1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2015-2016 Samsung Electronics |
4 | * Igor Kotrasinski <i.kotrasinsk@samsung.com> |
5 | * Krzysztof Opasiak <k.opasiak@samsung.com> |
6 | * |
7 | * Refactored from usbip_host_driver.c, which is: |
8 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> |
9 | * 2005-2007 Takahiro Hirofuchi |
10 | */ |
11 | |
12 | #include <sys/types.h> |
13 | #include <sys/stat.h> |
14 | #include <fcntl.h> |
15 | |
16 | #include <errno.h> |
17 | #include <unistd.h> |
18 | |
19 | #include <libudev.h> |
20 | |
21 | #include "usbip_common.h" |
22 | #include "usbip_host_common.h" |
23 | #include "list.h" |
24 | #include "sysfs_utils.h" |
25 | |
26 | extern struct udev *udev_context; |
27 | |
28 | static int32_t read_attr_usbip_status(struct usbip_usb_device *udev) |
29 | { |
30 | char status_attr_path[SYSFS_PATH_MAX]; |
31 | int size; |
32 | int fd; |
33 | int length; |
34 | char status[2] = { 0 }; |
35 | int value = 0; |
36 | |
37 | size = snprintf(status_attr_path, sizeof(status_attr_path), |
38 | "%s/usbip_status" , udev->path); |
39 | if (size < 0 || (unsigned int)size >= sizeof(status_attr_path)) { |
40 | err("usbip_status path length %i >= %lu or < 0" , size, |
41 | (long unsigned)sizeof(status_attr_path)); |
42 | return -1; |
43 | } |
44 | |
45 | |
46 | fd = open(status_attr_path, O_RDONLY); |
47 | if (fd < 0) { |
48 | err("error opening attribute %s" , status_attr_path); |
49 | return -1; |
50 | } |
51 | |
52 | length = read(fd, status, 1); |
53 | if (length < 0) { |
54 | err("error reading attribute %s" , status_attr_path); |
55 | close(fd); |
56 | return -1; |
57 | } |
58 | |
59 | value = atoi(status); |
60 | close(fd); |
61 | return value; |
62 | } |
63 | |
64 | static |
65 | struct usbip_exported_device *usbip_exported_device_new( |
66 | struct usbip_host_driver *hdriver, const char *sdevpath) |
67 | { |
68 | struct usbip_exported_device *edev = NULL; |
69 | struct usbip_exported_device *edev_old; |
70 | size_t size; |
71 | int i; |
72 | |
73 | edev = calloc(1, sizeof(struct usbip_exported_device)); |
74 | |
75 | edev->sudev = |
76 | udev_device_new_from_syspath(udev_context, sdevpath); |
77 | if (!edev->sudev) { |
78 | err("udev_device_new_from_syspath: %s" , sdevpath); |
79 | goto err; |
80 | } |
81 | |
82 | if (hdriver->ops.read_device(edev->sudev, &edev->udev) < 0) |
83 | goto err; |
84 | |
85 | edev->status = read_attr_usbip_status(udev: &edev->udev); |
86 | if (edev->status < 0) |
87 | goto err; |
88 | |
89 | /* reallocate buffer to include usb interface data */ |
90 | size = sizeof(struct usbip_exported_device) + |
91 | edev->udev.bNumInterfaces * sizeof(struct usbip_usb_interface); |
92 | |
93 | edev_old = edev; |
94 | edev = realloc(edev, size); |
95 | if (!edev) { |
96 | edev = edev_old; |
97 | dbg("realloc failed" ); |
98 | goto err; |
99 | } |
100 | |
101 | for (i = 0; i < edev->udev.bNumInterfaces; i++) { |
102 | /* vudc does not support reading interfaces */ |
103 | if (!hdriver->ops.read_interface) |
104 | break; |
105 | hdriver->ops.read_interface(&edev->udev, i, &edev->uinf[i]); |
106 | } |
107 | |
108 | return edev; |
109 | err: |
110 | if (edev->sudev) |
111 | udev_device_unref(edev->sudev); |
112 | if (edev) |
113 | free(edev); |
114 | |
115 | return NULL; |
116 | } |
117 | |
118 | static int refresh_exported_devices(struct usbip_host_driver *hdriver) |
119 | { |
120 | struct usbip_exported_device *edev; |
121 | struct udev_enumerate *enumerate; |
122 | struct udev_list_entry *devices, *dev_list_entry; |
123 | struct udev_device *dev; |
124 | const char *path; |
125 | |
126 | enumerate = udev_enumerate_new(udev_context); |
127 | udev_enumerate_add_match_subsystem(enumerate, hdriver->udev_subsystem); |
128 | udev_enumerate_scan_devices(enumerate); |
129 | |
130 | devices = udev_enumerate_get_list_entry(enumerate); |
131 | |
132 | udev_list_entry_foreach(dev_list_entry, devices) { |
133 | path = udev_list_entry_get_name(dev_list_entry); |
134 | dev = udev_device_new_from_syspath(udev_context, |
135 | path); |
136 | if (dev == NULL) |
137 | continue; |
138 | |
139 | /* Check whether device uses usbip driver. */ |
140 | if (hdriver->ops.is_my_device(dev)) { |
141 | edev = usbip_exported_device_new(hdriver, sdevpath: path); |
142 | if (!edev) { |
143 | dbg("usbip_exported_device_new failed" ); |
144 | continue; |
145 | } |
146 | |
147 | list_add(new: &edev->node, head: &hdriver->edev_list); |
148 | hdriver->ndevs++; |
149 | } |
150 | } |
151 | |
152 | return 0; |
153 | } |
154 | |
155 | static void usbip_exported_device_destroy(struct list_head *devs) |
156 | { |
157 | struct list_head *i, *tmp; |
158 | struct usbip_exported_device *edev; |
159 | |
160 | list_for_each_safe(i, tmp, devs) { |
161 | edev = list_entry(i, struct usbip_exported_device, node); |
162 | list_del(entry: i); |
163 | free(edev); |
164 | } |
165 | } |
166 | |
167 | int usbip_generic_driver_open(struct usbip_host_driver *hdriver) |
168 | { |
169 | int rc; |
170 | |
171 | udev_context = udev_new(); |
172 | if (!udev_context) { |
173 | err("udev_new failed" ); |
174 | return -1; |
175 | } |
176 | |
177 | rc = refresh_exported_devices(hdriver); |
178 | if (rc < 0) |
179 | goto err; |
180 | return 0; |
181 | err: |
182 | udev_unref(udev_context); |
183 | return -1; |
184 | } |
185 | |
186 | void usbip_generic_driver_close(struct usbip_host_driver *hdriver) |
187 | { |
188 | if (!hdriver) |
189 | return; |
190 | |
191 | usbip_exported_device_destroy(devs: &hdriver->edev_list); |
192 | |
193 | udev_unref(udev_context); |
194 | } |
195 | |
196 | int usbip_generic_refresh_device_list(struct usbip_host_driver *hdriver) |
197 | { |
198 | int rc; |
199 | |
200 | usbip_exported_device_destroy(devs: &hdriver->edev_list); |
201 | |
202 | hdriver->ndevs = 0; |
203 | INIT_LIST_HEAD(list: &hdriver->edev_list); |
204 | |
205 | rc = refresh_exported_devices(hdriver); |
206 | if (rc < 0) |
207 | return -1; |
208 | |
209 | return 0; |
210 | } |
211 | |
212 | int usbip_export_device(struct usbip_exported_device *edev, int sockfd) |
213 | { |
214 | char attr_name[] = "usbip_sockfd" ; |
215 | char sockfd_attr_path[SYSFS_PATH_MAX]; |
216 | int size; |
217 | char sockfd_buff[30]; |
218 | int ret; |
219 | |
220 | if (edev->status != SDEV_ST_AVAILABLE) { |
221 | dbg("device not available: %s" , edev->udev.busid); |
222 | switch (edev->status) { |
223 | case SDEV_ST_ERROR: |
224 | dbg("status SDEV_ST_ERROR" ); |
225 | ret = ST_DEV_ERR; |
226 | break; |
227 | case SDEV_ST_USED: |
228 | dbg("status SDEV_ST_USED" ); |
229 | ret = ST_DEV_BUSY; |
230 | break; |
231 | default: |
232 | dbg("status unknown: 0x%x" , edev->status); |
233 | ret = -1; |
234 | } |
235 | return ret; |
236 | } |
237 | |
238 | /* only the first interface is true */ |
239 | size = snprintf(sockfd_attr_path, sizeof(sockfd_attr_path), "%s/%s" , |
240 | edev->udev.path, attr_name); |
241 | if (size < 0 || (unsigned int)size >= sizeof(sockfd_attr_path)) { |
242 | err("exported device path length %i >= %lu or < 0" , size, |
243 | (long unsigned)sizeof(sockfd_attr_path)); |
244 | return -1; |
245 | } |
246 | |
247 | size = snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n" , sockfd); |
248 | if (size < 0 || (unsigned int)size >= sizeof(sockfd_buff)) { |
249 | err("socket length %i >= %lu or < 0" , size, |
250 | (long unsigned)sizeof(sockfd_buff)); |
251 | return -1; |
252 | } |
253 | |
254 | ret = write_sysfs_attribute(attr_path: sockfd_attr_path, new_value: sockfd_buff, |
255 | len: strlen(sockfd_buff)); |
256 | if (ret < 0) { |
257 | err("write_sysfs_attribute failed: sockfd %s to %s" , |
258 | sockfd_buff, sockfd_attr_path); |
259 | return ret; |
260 | } |
261 | |
262 | info("connect: %s" , edev->udev.busid); |
263 | |
264 | return ret; |
265 | } |
266 | |
267 | struct usbip_exported_device *usbip_generic_get_device( |
268 | struct usbip_host_driver *hdriver, int num) |
269 | { |
270 | struct list_head *i; |
271 | struct usbip_exported_device *edev; |
272 | int cnt = 0; |
273 | |
274 | list_for_each(i, &hdriver->edev_list) { |
275 | edev = list_entry(i, struct usbip_exported_device, node); |
276 | if (num == cnt) |
277 | return edev; |
278 | cnt++; |
279 | } |
280 | |
281 | return NULL; |
282 | } |
283 | |