diff options
Diffstat (limited to 'tools/usb/usbip/libsrc/usbip_host_driver.c')
-rw-r--r-- | tools/usb/usbip/libsrc/usbip_host_driver.c | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/tools/usb/usbip/libsrc/usbip_host_driver.c b/tools/usb/usbip/libsrc/usbip_host_driver.c new file mode 100644 index 000000000000..bef08d5c44e8 --- /dev/null +++ b/tools/usb/usbip/libsrc/usbip_host_driver.c | |||
@@ -0,0 +1,280 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> | ||
3 | * 2005-2007 Takahiro Hirofuchi | ||
4 | * | ||
5 | * This program is free software: you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation, either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
17 | */ | ||
18 | |||
19 | #include <sys/types.h> | ||
20 | #include <sys/stat.h> | ||
21 | #include <fcntl.h> | ||
22 | |||
23 | #include <errno.h> | ||
24 | #include <unistd.h> | ||
25 | |||
26 | #include <libudev.h> | ||
27 | |||
28 | #include "usbip_common.h" | ||
29 | #include "usbip_host_driver.h" | ||
30 | #include "list.h" | ||
31 | #include "sysfs_utils.h" | ||
32 | |||
33 | #undef PROGNAME | ||
34 | #define PROGNAME "libusbip" | ||
35 | |||
36 | struct usbip_host_driver *host_driver; | ||
37 | struct udev *udev_context; | ||
38 | |||
39 | static int32_t read_attr_usbip_status(struct usbip_usb_device *udev) | ||
40 | { | ||
41 | char status_attr_path[SYSFS_PATH_MAX]; | ||
42 | int fd; | ||
43 | int length; | ||
44 | char status; | ||
45 | int value = 0; | ||
46 | |||
47 | snprintf(status_attr_path, SYSFS_PATH_MAX, "%s/usbip_status", | ||
48 | udev->path); | ||
49 | |||
50 | fd = open(status_attr_path, O_RDONLY); | ||
51 | if (fd < 0) { | ||
52 | err("error opening attribute %s", status_attr_path); | ||
53 | return -1; | ||
54 | } | ||
55 | |||
56 | length = read(fd, &status, 1); | ||
57 | if (length < 0) { | ||
58 | err("error reading attribute %s", status_attr_path); | ||
59 | close(fd); | ||
60 | return -1; | ||
61 | } | ||
62 | |||
63 | value = atoi(&status); | ||
64 | |||
65 | return value; | ||
66 | } | ||
67 | |||
68 | static | ||
69 | struct usbip_exported_device *usbip_exported_device_new(const char *sdevpath) | ||
70 | { | ||
71 | struct usbip_exported_device *edev = NULL; | ||
72 | struct usbip_exported_device *edev_old; | ||
73 | size_t size; | ||
74 | int i; | ||
75 | |||
76 | edev = calloc(1, sizeof(struct usbip_exported_device)); | ||
77 | |||
78 | edev->sudev = udev_device_new_from_syspath(udev_context, sdevpath); | ||
79 | if (!edev->sudev) { | ||
80 | err("udev_device_new_from_syspath: %s", sdevpath); | ||
81 | goto err; | ||
82 | } | ||
83 | |||
84 | read_usb_device(edev->sudev, &edev->udev); | ||
85 | |||
86 | edev->status = read_attr_usbip_status(&edev->udev); | ||
87 | if (edev->status < 0) | ||
88 | goto err; | ||
89 | |||
90 | /* reallocate buffer to include usb interface data */ | ||
91 | size = sizeof(struct usbip_exported_device) + | ||
92 | edev->udev.bNumInterfaces * sizeof(struct usbip_usb_interface); | ||
93 | |||
94 | edev_old = edev; | ||
95 | edev = realloc(edev, size); | ||
96 | if (!edev) { | ||
97 | edev = edev_old; | ||
98 | dbg("realloc failed"); | ||
99 | goto err; | ||
100 | } | ||
101 | |||
102 | for (i = 0; i < edev->udev.bNumInterfaces; i++) | ||
103 | read_usb_interface(&edev->udev, i, &edev->uinf[i]); | ||
104 | |||
105 | return edev; | ||
106 | err: | ||
107 | if (edev->sudev) | ||
108 | udev_device_unref(edev->sudev); | ||
109 | if (edev) | ||
110 | free(edev); | ||
111 | |||
112 | return NULL; | ||
113 | } | ||
114 | |||
115 | static int refresh_exported_devices(void) | ||
116 | { | ||
117 | struct usbip_exported_device *edev; | ||
118 | struct udev_enumerate *enumerate; | ||
119 | struct udev_list_entry *devices, *dev_list_entry; | ||
120 | struct udev_device *dev; | ||
121 | const char *path; | ||
122 | const char *driver; | ||
123 | |||
124 | enumerate = udev_enumerate_new(udev_context); | ||
125 | udev_enumerate_add_match_subsystem(enumerate, "usb"); | ||
126 | udev_enumerate_scan_devices(enumerate); | ||
127 | |||
128 | devices = udev_enumerate_get_list_entry(enumerate); | ||
129 | |||
130 | udev_list_entry_foreach(dev_list_entry, devices) { | ||
131 | path = udev_list_entry_get_name(dev_list_entry); | ||
132 | dev = udev_device_new_from_syspath(udev_context, path); | ||
133 | if (dev == NULL) | ||
134 | continue; | ||
135 | |||
136 | /* Check whether device uses usbip-host driver. */ | ||
137 | driver = udev_device_get_driver(dev); | ||
138 | if (driver != NULL && !strcmp(driver, USBIP_HOST_DRV_NAME)) { | ||
139 | edev = usbip_exported_device_new(path); | ||
140 | if (!edev) { | ||
141 | dbg("usbip_exported_device_new failed"); | ||
142 | continue; | ||
143 | } | ||
144 | |||
145 | list_add(&edev->node, &host_driver->edev_list); | ||
146 | host_driver->ndevs++; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | static void usbip_exported_device_destroy(void) | ||
154 | { | ||
155 | struct list_head *i, *tmp; | ||
156 | struct usbip_exported_device *edev; | ||
157 | |||
158 | list_for_each_safe(i, tmp, &host_driver->edev_list) { | ||
159 | edev = list_entry(i, struct usbip_exported_device, node); | ||
160 | list_del(i); | ||
161 | free(edev); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | int usbip_host_driver_open(void) | ||
166 | { | ||
167 | int rc; | ||
168 | |||
169 | udev_context = udev_new(); | ||
170 | if (!udev_context) { | ||
171 | err("udev_new failed"); | ||
172 | return -1; | ||
173 | } | ||
174 | |||
175 | host_driver = calloc(1, sizeof(*host_driver)); | ||
176 | |||
177 | host_driver->ndevs = 0; | ||
178 | INIT_LIST_HEAD(&host_driver->edev_list); | ||
179 | |||
180 | rc = refresh_exported_devices(); | ||
181 | if (rc < 0) | ||
182 | goto err_free_host_driver; | ||
183 | |||
184 | return 0; | ||
185 | |||
186 | err_free_host_driver: | ||
187 | free(host_driver); | ||
188 | host_driver = NULL; | ||
189 | |||
190 | udev_unref(udev_context); | ||
191 | |||
192 | return -1; | ||
193 | } | ||
194 | |||
195 | void usbip_host_driver_close(void) | ||
196 | { | ||
197 | if (!host_driver) | ||
198 | return; | ||
199 | |||
200 | usbip_exported_device_destroy(); | ||
201 | |||
202 | free(host_driver); | ||
203 | host_driver = NULL; | ||
204 | |||
205 | udev_unref(udev_context); | ||
206 | } | ||
207 | |||
208 | int usbip_host_refresh_device_list(void) | ||
209 | { | ||
210 | int rc; | ||
211 | |||
212 | usbip_exported_device_destroy(); | ||
213 | |||
214 | host_driver->ndevs = 0; | ||
215 | INIT_LIST_HEAD(&host_driver->edev_list); | ||
216 | |||
217 | rc = refresh_exported_devices(); | ||
218 | if (rc < 0) | ||
219 | return -1; | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd) | ||
225 | { | ||
226 | char attr_name[] = "usbip_sockfd"; | ||
227 | char sockfd_attr_path[SYSFS_PATH_MAX]; | ||
228 | char sockfd_buff[30]; | ||
229 | int ret; | ||
230 | |||
231 | if (edev->status != SDEV_ST_AVAILABLE) { | ||
232 | dbg("device not available: %s", edev->udev.busid); | ||
233 | switch (edev->status) { | ||
234 | case SDEV_ST_ERROR: | ||
235 | dbg("status SDEV_ST_ERROR"); | ||
236 | break; | ||
237 | case SDEV_ST_USED: | ||
238 | dbg("status SDEV_ST_USED"); | ||
239 | break; | ||
240 | default: | ||
241 | dbg("status unknown: 0x%x", edev->status); | ||
242 | } | ||
243 | return -1; | ||
244 | } | ||
245 | |||
246 | /* only the first interface is true */ | ||
247 | snprintf(sockfd_attr_path, sizeof(sockfd_attr_path), "%s/%s", | ||
248 | edev->udev.path, attr_name); | ||
249 | |||
250 | snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd); | ||
251 | |||
252 | ret = write_sysfs_attribute(sockfd_attr_path, sockfd_buff, | ||
253 | strlen(sockfd_buff)); | ||
254 | if (ret < 0) { | ||
255 | err("write_sysfs_attribute failed: sockfd %s to %s", | ||
256 | sockfd_buff, sockfd_attr_path); | ||
257 | return ret; | ||
258 | } | ||
259 | |||
260 | info("connect: %s", edev->udev.busid); | ||
261 | |||
262 | return ret; | ||
263 | } | ||
264 | |||
265 | struct usbip_exported_device *usbip_host_get_device(int num) | ||
266 | { | ||
267 | struct list_head *i; | ||
268 | struct usbip_exported_device *edev; | ||
269 | int cnt = 0; | ||
270 | |||
271 | list_for_each(i, &host_driver->edev_list) { | ||
272 | edev = list_entry(i, struct usbip_exported_device, node); | ||
273 | if (num == cnt) | ||
274 | return edev; | ||
275 | else | ||
276 | cnt++; | ||
277 | } | ||
278 | |||
279 | return NULL; | ||
280 | } | ||