Commit | Line | Data |
---|---|---|
1ccea77e | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
3391ba0e KO |
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 | |
3391ba0e KO |
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 | 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]; | |
e5dfa3f9 | 31 | int size; |
3391ba0e KO |
32 | int fd; |
33 | int length; | |
e325808c | 34 | char status[2] = { 0 }; |
3391ba0e KO |
35 | int value = 0; |
36 | ||
e5dfa3f9 JD |
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 | ||
3391ba0e KO |
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 | ||
e325808c | 52 | length = read(fd, status, 1); |
3391ba0e KO |
53 | if (length < 0) { |
54 | err("error reading attribute %s", status_attr_path); | |
55 | close(fd); | |
56 | return -1; | |
57 | } | |
58 | ||
e325808c | 59 | value = atoi(status); |
3391ba0e KO |
60 | |
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(&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, path); | |
142 | if (!edev) { | |
143 | dbg("usbip_exported_device_new failed"); | |
144 | continue; | |
145 | } | |
146 | ||
147 | list_add(&edev->node, &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(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(&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(&hdriver->edev_list); | |
201 | ||
202 | hdriver->ndevs = 0; | |
203 | INIT_LIST_HEAD(&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]; | |
e5dfa3f9 | 216 | int size; |
3391ba0e KO |
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"); | |
c207a10d | 225 | ret = ST_DEV_ERR; |
3391ba0e KO |
226 | break; |
227 | case SDEV_ST_USED: | |
228 | dbg("status SDEV_ST_USED"); | |
c207a10d | 229 | ret = ST_DEV_BUSY; |
3391ba0e KO |
230 | break; |
231 | default: | |
232 | dbg("status unknown: 0x%x", edev->status); | |
c207a10d | 233 | ret = -1; |
3391ba0e | 234 | } |
c207a10d | 235 | return ret; |
3391ba0e KO |
236 | } |
237 | ||
238 | /* only the first interface is true */ | |
e5dfa3f9 JD |
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 | } | |
3391ba0e | 246 | |
e5dfa3f9 JD |
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 | } | |
3391ba0e KO |
253 | |
254 | ret = write_sysfs_attribute(sockfd_attr_path, sockfd_buff, | |
255 | 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 | } |