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 | |
da87dba8 WF |
50 | fd = open(status_attr_path, O_RDONLY); |
51 | if (fd < 0) { | |
021aed84 | 52 | err("error opening attribute %s", status_attr_path); |
0945b4fe TH |
53 | return -1; |
54 | } | |
55 | ||
021aed84 VM |
56 | length = read(fd, &status, 1); |
57 | if (length < 0) { | |
58 | err("error reading attribute %s", status_attr_path); | |
59 | close(fd); | |
0945b4fe TH |
60 | return -1; |
61 | } | |
62 | ||
021aed84 | 63 | value = atoi(&status); |
0945b4fe TH |
64 | |
65 | return value; | |
66 | } | |
67 | ||
021aed84 VM |
68 | static |
69 | struct usbip_exported_device *usbip_exported_device_new(const char *sdevpath) | |
0945b4fe TH |
70 | { |
71 | struct usbip_exported_device *edev = NULL; | |
696c9c7f | 72 | struct usbip_exported_device *edev_old; |
756d6726 | 73 | size_t size; |
74 | int i; | |
0945b4fe | 75 | |
021aed84 | 76 | edev = calloc(1, sizeof(struct usbip_exported_device)); |
0945b4fe | 77 | |
021aed84 | 78 | edev->sudev = udev_device_new_from_syspath(udev_context, sdevpath); |
0945b4fe | 79 | if (!edev->sudev) { |
021aed84 | 80 | err("udev_device_new_from_syspath: %s", sdevpath); |
0945b4fe TH |
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 */ | |
25da2290 WF |
91 | size = sizeof(struct usbip_exported_device) + |
92 | edev->udev.bNumInterfaces * sizeof(struct usbip_usb_interface); | |
756d6726 | 93 | |
696c9c7f | 94 | edev_old = edev; |
756d6726 | 95 | edev = realloc(edev, size); |
0945b4fe | 96 | if (!edev) { |
696c9c7f | 97 | edev = edev_old; |
25567a39 | 98 | dbg("realloc failed"); |
0945b4fe TH |
99 | goto err; |
100 | } | |
101 | ||
756d6726 | 102 | for (i = 0; i < edev->udev.bNumInterfaces; i++) |
0945b4fe TH |
103 | read_usb_interface(&edev->udev, i, &edev->uinf[i]); |
104 | ||
105 | return edev; | |
0945b4fe | 106 | err: |
021aed84 VM |
107 | if (edev->sudev) |
108 | udev_device_unref(edev->sudev); | |
0945b4fe TH |
109 | if (edev) |
110 | free(edev); | |
756d6726 | 111 | |
0945b4fe TH |
112 | return NULL; |
113 | } | |
114 | ||
0945b4fe TH |
115 | static int refresh_exported_devices(void) |
116 | { | |
756d6726 | 117 | struct usbip_exported_device *edev; |
021aed84 VM |
118 | struct udev_enumerate *enumerate; |
119 | struct udev_list_entry *devices, *dev_list_entry; | |
120 | struct udev_device *dev; | |
121 | const char *path; | |
21c5e840 | 122 | const char *driver; |
021aed84 VM |
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); | |
21c5e840 SK |
133 | if (dev == NULL) |
134 | continue; | |
021aed84 VM |
135 | |
136 | /* Check whether device uses usbip-host driver. */ | |
21c5e840 SK |
137 | driver = udev_device_get_driver(dev); |
138 | if (driver != NULL && !strcmp(driver, USBIP_HOST_DRV_NAME)) { | |
021aed84 VM |
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++; | |
0945b4fe | 147 | } |
0945b4fe TH |
148 | } |
149 | ||
0945b4fe TH |
150 | return 0; |
151 | } | |
152 | ||
021aed84 | 153 | static void usbip_exported_device_destroy(void) |
0945b4fe | 154 | { |
021aed84 VM |
155 | struct list_head *i, *tmp; |
156 | struct usbip_exported_device *edev; | |
0945b4fe | 157 | |
021aed84 VM |
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); | |
0945b4fe | 162 | } |
756d6726 | 163 | } |
0945b4fe | 164 | |
756d6726 | 165 | int usbip_host_driver_open(void) |
166 | { | |
167 | int rc; | |
0945b4fe | 168 | |
021aed84 VM |
169 | udev_context = udev_new(); |
170 | if (!udev_context) { | |
171 | err("udev_new failed"); | |
0945b4fe TH |
172 | return -1; |
173 | } | |
174 | ||
021aed84 | 175 | host_driver = calloc(1, sizeof(*host_driver)); |
0945b4fe | 176 | |
021aed84 VM |
177 | host_driver->ndevs = 0; |
178 | INIT_LIST_HEAD(&host_driver->edev_list); | |
0945b4fe | 179 | |
756d6726 | 180 | rc = refresh_exported_devices(); |
181 | if (rc < 0) | |
021aed84 | 182 | goto err_free_host_driver; |
0945b4fe TH |
183 | |
184 | return 0; | |
185 | ||
756d6726 | 186 | err_free_host_driver: |
187 | free(host_driver); | |
188 | host_driver = NULL; | |
0945b4fe | 189 | |
021aed84 VM |
190 | udev_unref(udev_context); |
191 | ||
0945b4fe TH |
192 | return -1; |
193 | } | |
194 | ||
756d6726 | 195 | void usbip_host_driver_close(void) |
0945b4fe | 196 | { |
756d6726 | 197 | if (!host_driver) |
0945b4fe TH |
198 | return; |
199 | ||
021aed84 | 200 | usbip_exported_device_destroy(); |
0945b4fe | 201 | |
756d6726 | 202 | free(host_driver); |
203 | host_driver = NULL; | |
021aed84 VM |
204 | |
205 | udev_unref(udev_context); | |
0945b4fe TH |
206 | } |
207 | ||
756d6726 | 208 | int usbip_host_refresh_device_list(void) |
0945b4fe | 209 | { |
756d6726 | 210 | int rc; |
211 | ||
021aed84 | 212 | usbip_exported_device_destroy(); |
756d6726 | 213 | |
214 | host_driver->ndevs = 0; | |
021aed84 | 215 | INIT_LIST_HEAD(&host_driver->edev_list); |
756d6726 | 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"; | |
021aed84 | 227 | char sockfd_attr_path[SYSFS_PATH_MAX]; |
0945b4fe TH |
228 | char sockfd_buff[30]; |
229 | int ret; | |
230 | ||
0945b4fe | 231 | if (edev->status != SDEV_ST_AVAILABLE) { |
25567a39 | 232 | dbg("device not available: %s", edev->udev.busid); |
756d6726 | 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); | |
0945b4fe TH |
242 | } |
243 | return -1; | |
244 | } | |
245 | ||
246 | /* only the first interface is true */ | |
021aed84 | 247 | snprintf(sockfd_attr_path, sizeof(sockfd_attr_path), "%s/%s", |
b7945b77 | 248 | edev->udev.path, attr_name); |
0945b4fe | 249 | |
0945b4fe | 250 | snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd); |
756d6726 | 251 | |
021aed84 VM |
252 | ret = write_sysfs_attribute(sockfd_attr_path, sockfd_buff, |
253 | strlen(sockfd_buff)); | |
0945b4fe | 254 | if (ret < 0) { |
021aed84 VM |
255 | err("write_sysfs_attribute failed: sockfd %s to %s", |
256 | sockfd_buff, sockfd_attr_path); | |
257 | return ret; | |
0945b4fe TH |
258 | } |
259 | ||
021aed84 | 260 | info("connect: %s", edev->udev.busid); |
0945b4fe TH |
261 | |
262 | return ret; | |
263 | } | |
264 | ||
756d6726 | 265 | struct usbip_exported_device *usbip_host_get_device(int num) |
0945b4fe | 266 | { |
021aed84 | 267 | struct list_head *i; |
0945b4fe | 268 | struct usbip_exported_device *edev; |
756d6726 | 269 | int cnt = 0; |
0945b4fe | 270 | |
021aed84 VM |
271 | list_for_each(i, &host_driver->edev_list) { |
272 | edev = list_entry(i, struct usbip_exported_device, node); | |
756d6726 | 273 | if (num == cnt) |
0945b4fe TH |
274 | return edev; |
275 | else | |
756d6726 | 276 | cnt++; |
0945b4fe TH |
277 | } |
278 | ||
279 | return NULL; | |
280 | } |