Commit | Line | Data |
---|---|---|
0945b4fe | 1 | /* |
756d6726 | 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/>. | |
0945b4fe TH |
17 | */ |
18 | ||
19 | #include <sys/types.h> | |
20 | #include <sys/stat.h> | |
021aed84 | 21 | #include <fcntl.h> |
4fd83e84 | 22 | |
23 | #include <errno.h> | |
0945b4fe TH |
24 | #include <unistd.h> |
25 | ||
021aed84 VM |
26 | #include <libudev.h> |
27 | ||
099f79fa | 28 | #include "usbip_common.h" |
756d6726 | 29 | #include "usbip_host_driver.h" |
021aed84 VM |
30 | #include "list.h" |
31 | #include "sysfs_utils.h" | |
0945b4fe | 32 | |
f2fb62b3 | 33 | #undef PROGNAME |
34 | #define PROGNAME "libusbip" | |
35 | ||
756d6726 | 36 | struct usbip_host_driver *host_driver; |
021aed84 | 37 | struct udev *udev_context; |
0945b4fe | 38 | |
35dd0c2d | 39 | static int32_t read_attr_usbip_status(struct usbip_usb_device *udev) |
0945b4fe | 40 | { |
021aed84 VM |
41 | char status_attr_path[SYSFS_PATH_MAX]; |
42 | int fd; | |
43 | int length; | |
44 | char status; | |
0945b4fe | 45 | int value = 0; |
0945b4fe | 46 | |
021aed84 VM |
47 | snprintf(status_attr_path, SYSFS_PATH_MAX, "%s/usbip_status", |
48 | udev->path); | |
0945b4fe | 49 | |
021aed84 VM |
50 | if ((fd = open(status_attr_path, O_RDONLY)) < 0) { |
51 | err("error opening attribute %s", status_attr_path); | |
0945b4fe TH |
52 | return -1; |
53 | } | |
54 | ||
021aed84 VM |
55 | length = read(fd, &status, 1); |
56 | if (length < 0) { | |
57 | err("error reading attribute %s", status_attr_path); | |
58 | close(fd); | |
0945b4fe TH |
59 | return -1; |
60 | } | |
61 | ||
021aed84 | 62 | value = atoi(&status); |
0945b4fe TH |
63 | |
64 | return value; | |
65 | } | |
66 | ||
021aed84 VM |
67 | static |
68 | struct usbip_exported_device *usbip_exported_device_new(const char *sdevpath) | |
0945b4fe TH |
69 | { |
70 | struct usbip_exported_device *edev = NULL; | |
696c9c7f | 71 | struct usbip_exported_device *edev_old; |
756d6726 | 72 | size_t size; |
73 | int i; | |
0945b4fe | 74 | |
021aed84 | 75 | edev = calloc(1, sizeof(struct usbip_exported_device)); |
0945b4fe | 76 | |
021aed84 | 77 | edev->sudev = udev_device_new_from_syspath(udev_context, sdevpath); |
0945b4fe | 78 | if (!edev->sudev) { |
021aed84 | 79 | err("udev_device_new_from_syspath: %s", sdevpath); |
0945b4fe TH |
80 | goto err; |
81 | } | |
82 | ||
83 | read_usb_device(edev->sudev, &edev->udev); | |
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 */ | |
021aed84 | 90 | size = sizeof(struct usbip_exported_device) + edev->udev.bNumInterfaces * |
35dd0c2d | 91 | sizeof(struct usbip_usb_interface); |
756d6726 | 92 | |
696c9c7f | 93 | edev_old = edev; |
756d6726 | 94 | edev = realloc(edev, size); |
0945b4fe | 95 | if (!edev) { |
696c9c7f | 96 | edev = edev_old; |
25567a39 | 97 | dbg("realloc failed"); |
0945b4fe TH |
98 | goto err; |
99 | } | |
100 | ||
756d6726 | 101 | for (i = 0; i < edev->udev.bNumInterfaces; i++) |
0945b4fe TH |
102 | read_usb_interface(&edev->udev, i, &edev->uinf[i]); |
103 | ||
104 | return edev; | |
0945b4fe | 105 | err: |
021aed84 VM |
106 | if (edev->sudev) |
107 | udev_device_unref(edev->sudev); | |
0945b4fe TH |
108 | if (edev) |
109 | free(edev); | |
756d6726 | 110 | |
0945b4fe TH |
111 | return NULL; |
112 | } | |
113 | ||
0945b4fe TH |
114 | static int refresh_exported_devices(void) |
115 | { | |
756d6726 | 116 | struct usbip_exported_device *edev; |
021aed84 VM |
117 | struct udev_enumerate *enumerate; |
118 | struct udev_list_entry *devices, *dev_list_entry; | |
119 | struct udev_device *dev; | |
120 | const char *path; | |
21c5e840 | 121 | const char *driver; |
021aed84 VM |
122 | |
123 | enumerate = udev_enumerate_new(udev_context); | |
124 | udev_enumerate_add_match_subsystem(enumerate, "usb"); | |
125 | udev_enumerate_scan_devices(enumerate); | |
126 | ||
127 | devices = udev_enumerate_get_list_entry(enumerate); | |
128 | ||
129 | udev_list_entry_foreach(dev_list_entry, devices) { | |
130 | path = udev_list_entry_get_name(dev_list_entry); | |
131 | dev = udev_device_new_from_syspath(udev_context, path); | |
21c5e840 SK |
132 | if (dev == NULL) |
133 | continue; | |
021aed84 VM |
134 | |
135 | /* Check whether device uses usbip-host driver. */ | |
21c5e840 SK |
136 | driver = udev_device_get_driver(dev); |
137 | if (driver != NULL && !strcmp(driver, USBIP_HOST_DRV_NAME)) { | |
021aed84 VM |
138 | edev = usbip_exported_device_new(path); |
139 | if (!edev) { | |
140 | dbg("usbip_exported_device_new failed"); | |
141 | continue; | |
142 | } | |
143 | ||
144 | list_add(&edev->node, &host_driver->edev_list); | |
145 | host_driver->ndevs++; | |
0945b4fe | 146 | } |
0945b4fe TH |
147 | } |
148 | ||
0945b4fe TH |
149 | return 0; |
150 | } | |
151 | ||
021aed84 | 152 | static void usbip_exported_device_destroy(void) |
0945b4fe | 153 | { |
021aed84 VM |
154 | struct list_head *i, *tmp; |
155 | struct usbip_exported_device *edev; | |
0945b4fe | 156 | |
021aed84 VM |
157 | list_for_each_safe(i, tmp, &host_driver->edev_list) { |
158 | edev = list_entry(i, struct usbip_exported_device, node); | |
159 | list_del(i); | |
160 | free(edev); | |
0945b4fe | 161 | } |
756d6726 | 162 | } |
0945b4fe | 163 | |
756d6726 | 164 | int usbip_host_driver_open(void) |
165 | { | |
166 | int rc; | |
0945b4fe | 167 | |
021aed84 VM |
168 | udev_context = udev_new(); |
169 | if (!udev_context) { | |
170 | err("udev_new failed"); | |
0945b4fe TH |
171 | return -1; |
172 | } | |
173 | ||
021aed84 | 174 | host_driver = calloc(1, sizeof(*host_driver)); |
0945b4fe | 175 | |
021aed84 VM |
176 | host_driver->ndevs = 0; |
177 | INIT_LIST_HEAD(&host_driver->edev_list); | |
0945b4fe | 178 | |
756d6726 | 179 | rc = refresh_exported_devices(); |
180 | if (rc < 0) | |
021aed84 | 181 | goto err_free_host_driver; |
0945b4fe TH |
182 | |
183 | return 0; | |
184 | ||
756d6726 | 185 | err_free_host_driver: |
186 | free(host_driver); | |
187 | host_driver = NULL; | |
0945b4fe | 188 | |
021aed84 VM |
189 | udev_unref(udev_context); |
190 | ||
0945b4fe TH |
191 | return -1; |
192 | } | |
193 | ||
756d6726 | 194 | void usbip_host_driver_close(void) |
0945b4fe | 195 | { |
756d6726 | 196 | if (!host_driver) |
0945b4fe TH |
197 | return; |
198 | ||
021aed84 | 199 | usbip_exported_device_destroy(); |
0945b4fe | 200 | |
756d6726 | 201 | free(host_driver); |
202 | host_driver = NULL; | |
021aed84 VM |
203 | |
204 | udev_unref(udev_context); | |
0945b4fe TH |
205 | } |
206 | ||
756d6726 | 207 | int usbip_host_refresh_device_list(void) |
0945b4fe | 208 | { |
756d6726 | 209 | int rc; |
210 | ||
021aed84 | 211 | usbip_exported_device_destroy(); |
756d6726 | 212 | |
213 | host_driver->ndevs = 0; | |
021aed84 | 214 | INIT_LIST_HEAD(&host_driver->edev_list); |
756d6726 | 215 | |
216 | rc = refresh_exported_devices(); | |
217 | if (rc < 0) | |
218 | return -1; | |
219 | ||
220 | return 0; | |
221 | } | |
222 | ||
223 | int usbip_host_export_device(struct usbip_exported_device *edev, int sockfd) | |
224 | { | |
225 | char attr_name[] = "usbip_sockfd"; | |
021aed84 | 226 | char sockfd_attr_path[SYSFS_PATH_MAX]; |
0945b4fe TH |
227 | char sockfd_buff[30]; |
228 | int ret; | |
229 | ||
0945b4fe | 230 | if (edev->status != SDEV_ST_AVAILABLE) { |
25567a39 | 231 | dbg("device not available: %s", edev->udev.busid); |
756d6726 | 232 | switch (edev->status) { |
233 | case SDEV_ST_ERROR: | |
234 | dbg("status SDEV_ST_ERROR"); | |
235 | break; | |
236 | case SDEV_ST_USED: | |
237 | dbg("status SDEV_ST_USED"); | |
238 | break; | |
239 | default: | |
240 | dbg("status unknown: 0x%x", edev->status); | |
0945b4fe TH |
241 | } |
242 | return -1; | |
243 | } | |
244 | ||
245 | /* only the first interface is true */ | |
021aed84 | 246 | snprintf(sockfd_attr_path, sizeof(sockfd_attr_path), "%s/%s", |
b7945b77 | 247 | edev->udev.path, attr_name); |
0945b4fe | 248 | |
0945b4fe | 249 | snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd); |
756d6726 | 250 | |
021aed84 VM |
251 | ret = write_sysfs_attribute(sockfd_attr_path, sockfd_buff, |
252 | strlen(sockfd_buff)); | |
0945b4fe | 253 | if (ret < 0) { |
021aed84 VM |
254 | err("write_sysfs_attribute failed: sockfd %s to %s", |
255 | sockfd_buff, sockfd_attr_path); | |
256 | return ret; | |
0945b4fe TH |
257 | } |
258 | ||
021aed84 | 259 | info("connect: %s", edev->udev.busid); |
0945b4fe TH |
260 | |
261 | return ret; | |
262 | } | |
263 | ||
756d6726 | 264 | struct usbip_exported_device *usbip_host_get_device(int num) |
0945b4fe | 265 | { |
021aed84 | 266 | struct list_head *i; |
0945b4fe | 267 | struct usbip_exported_device *edev; |
756d6726 | 268 | int cnt = 0; |
0945b4fe | 269 | |
021aed84 VM |
270 | list_for_each(i, &host_driver->edev_list) { |
271 | edev = list_entry(i, struct usbip_exported_device, node); | |
756d6726 | 272 | if (num == cnt) |
0945b4fe TH |
273 | return edev; |
274 | else | |
756d6726 | 275 | cnt++; |
0945b4fe TH |
276 | } |
277 | ||
278 | return NULL; | |
279 | } |