1/* 2 * Copyright (C) 2005-2007 Takahiro Hirofuchi 3 */ 4 5#include "usbip_common.h" 6#include "vhci_driver.h" 7#include <limits.h> 8#include <netdb.h> 9#include <libudev.h> 10#include "sysfs_utils.h" 11 12#undef PROGNAME 13#define PROGNAME "libusbip" 14 15struct usbip_vhci_driver *vhci_driver; 16struct udev *udev_context; 17 18static struct usbip_imported_device * 19imported_device_init(struct usbip_imported_device *idev, char *busid) 20{ 21 struct udev_device *sudev; 22 23 sudev = udev_device_new_from_subsystem_sysname(udev_context, 24 "usb", busid); 25 if (!sudev) { 26 dbg("udev_device_new_from_subsystem_sysname failed: %s", busid); 27 goto err; 28 } 29 read_usb_device(sudev, &idev->udev); 30 udev_device_unref(sudev); 31 32 return idev; 33 34err: 35 return NULL; 36} 37 38 39 40static int parse_status(const char *value) 41{ 42 int ret = 0; 43 char *c; 44 45 46 for (int i = 0; i < vhci_driver->nports; i++) 47 memset(&vhci_driver->idev[i], 0, sizeof(vhci_driver->idev[i])); 48 49 50 /* skip a header line */ 51 c = strchr(value, '\n'); 52 if (!c) 53 return -1; 54 c++; 55 56 while (*c != '\0') { 57 int port, status, speed, devid; 58 unsigned long socket; 59 char lbusid[SYSFS_BUS_ID_SIZE]; 60 61 ret = sscanf(c, "%d %d %d %x %lx %31s\n", 62 &port, &status, &speed, 63 &devid, &socket, lbusid); 64 65 if (ret < 5) { 66 dbg("sscanf failed: %d", ret); 67 BUG(); 68 } 69 70 dbg("port %d status %d speed %d devid %x", 71 port, status, speed, devid); 72 dbg("socket %lx lbusid %s", socket, lbusid); 73 74 75 /* if a device is connected, look at it */ 76 { 77 struct usbip_imported_device *idev = &vhci_driver->idev[port]; 78 79 idev->port = port; 80 idev->status = status; 81 82 idev->devid = devid; 83 84 idev->busnum = (devid >> 16); 85 idev->devnum = (devid & 0x0000ffff); 86 87 if (idev->status != VDEV_ST_NULL 88 && idev->status != VDEV_ST_NOTASSIGNED) { 89 idev = imported_device_init(idev, lbusid); 90 if (!idev) { 91 dbg("imported_device_init failed"); 92 return -1; 93 } 94 } 95 } 96 97 98 /* go to the next line */ 99 c = strchr(c, '\n'); 100 if (!c) 101 break; 102 c++; 103 } 104 105 dbg("exit"); 106 107 return 0; 108} 109 110static int refresh_imported_device_list(void) 111{ 112 const char *attr_status; 113 114 attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device, 115 "status"); 116 if (!attr_status) { 117 err("udev_device_get_sysattr_value failed"); 118 return -1; 119 } 120 121 return parse_status(attr_status); 122} 123 124static int get_nports(void) 125{ 126 char *c; 127 int nports = 0; 128 const char *attr_status; 129 130 attr_status = udev_device_get_sysattr_value(vhci_driver->hc_device, 131 "status"); 132 if (!attr_status) { 133 err("udev_device_get_sysattr_value failed"); 134 return -1; 135 } 136 137 /* skip a header line */ 138 c = strchr(attr_status, '\n'); 139 if (!c) 140 return 0; 141 c++; 142 143 while (*c != '\0') { 144 /* go to the next line */ 145 c = strchr(c, '\n'); 146 if (!c) 147 return nports; 148 c++; 149 nports += 1; 150 } 151 152 return nports; 153} 154 155/* 156 * Read the given port's record. 157 * 158 * To avoid buffer overflow we will read the entire line and 159 * validate each part's size. The initial buffer is padded by 4 to 160 * accommodate the 2 spaces, 1 newline and an additional character 161 * which is needed to properly validate the 3rd part without it being 162 * truncated to an acceptable length. 163 */ 164static int read_record(int rhport, char *host, unsigned long host_len, 165 char *port, unsigned long port_len, char *busid) 166{ 167 int part; 168 FILE *file; 169 char path[PATH_MAX+1]; 170 char *buffer, *start, *end; 171 char delim[] = {' ', ' ', '\n'}; 172 int max_len[] = {(int)host_len, (int)port_len, SYSFS_BUS_ID_SIZE}; 173 size_t buffer_len = host_len + port_len + SYSFS_BUS_ID_SIZE + 4; 174 175 buffer = malloc(buffer_len); 176 if (!buffer) 177 return -1; 178 179 snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport); 180 181 file = fopen(path, "r"); 182 if (!file) { 183 err("fopen"); 184 free(buffer); 185 return -1; 186 } 187 188 if (fgets(buffer, buffer_len, file) == NULL) { 189 err("fgets"); 190 free(buffer); 191 fclose(file); 192 return -1; 193 } 194 fclose(file); 195 196 /* validate the length of each of the 3 parts */ 197 start = buffer; 198 for (part = 0; part < 3; part++) { 199 end = strchr(start, delim[part]); 200 if (end == NULL || (end - start) > max_len[part]) { 201 free(buffer); 202 return -1; 203 } 204 start = end + 1; 205 } 206 207 if (sscanf(buffer, "%s %s %s\n", host, port, busid) != 3) { 208 err("sscanf"); 209 free(buffer); 210 return -1; 211 } 212 213 free(buffer); 214 215 return 0; 216} 217 218/* ---------------------------------------------------------------------- */ 219 220int usbip_vhci_driver_open(void) 221{ 222 udev_context = udev_new(); 223 if (!udev_context) { 224 err("udev_new failed"); 225 return -1; 226 } 227 228 vhci_driver = calloc(1, sizeof(struct usbip_vhci_driver)); 229 230 /* will be freed in usbip_driver_close() */ 231 vhci_driver->hc_device = 232 udev_device_new_from_subsystem_sysname(udev_context, 233 USBIP_VHCI_BUS_TYPE, 234 USBIP_VHCI_DRV_NAME); 235 if (!vhci_driver->hc_device) { 236 err("udev_device_new_from_subsystem_sysname failed"); 237 goto err; 238 } 239 240 vhci_driver->nports = get_nports(); 241 242 dbg("available ports: %d", vhci_driver->nports); 243 244 if (refresh_imported_device_list()) 245 goto err; 246 247 return 0; 248 249err: 250 udev_device_unref(vhci_driver->hc_device); 251 252 if (vhci_driver) 253 free(vhci_driver); 254 255 vhci_driver = NULL; 256 257 udev_unref(udev_context); 258 259 return -1; 260} 261 262 263void usbip_vhci_driver_close(void) 264{ 265 if (!vhci_driver) 266 return; 267 268 udev_device_unref(vhci_driver->hc_device); 269 270 free(vhci_driver); 271 272 vhci_driver = NULL; 273 274 udev_unref(udev_context); 275} 276 277 278int usbip_vhci_refresh_device_list(void) 279{ 280 281 if (refresh_imported_device_list()) 282 goto err; 283 284 return 0; 285err: 286 dbg("failed to refresh device list"); 287 return -1; 288} 289 290 291int usbip_vhci_get_free_port(void) 292{ 293 for (int i = 0; i < vhci_driver->nports; i++) { 294 if (vhci_driver->idev[i].status == VDEV_ST_NULL) 295 return i; 296 } 297 298 return -1; 299} 300 301int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid, 302 uint32_t speed) { 303 char buff[200]; /* what size should be ? */ 304 char attach_attr_path[SYSFS_PATH_MAX]; 305 char attr_attach[] = "attach"; 306 const char *path; 307 int ret; 308 309 snprintf(buff, sizeof(buff), "%u %d %u %u", 310 port, sockfd, devid, speed); 311 dbg("writing: %s", buff); 312 313 path = udev_device_get_syspath(vhci_driver->hc_device); 314 snprintf(attach_attr_path, sizeof(attach_attr_path), "%s/%s", 315 path, attr_attach); 316 dbg("attach attribute path: %s", attach_attr_path); 317 318 ret = write_sysfs_attribute(attach_attr_path, buff, strlen(buff)); 319 if (ret < 0) { 320 dbg("write_sysfs_attribute failed"); 321 return -1; 322 } 323 324 dbg("attached port: %d", port); 325 326 return 0; 327} 328 329static unsigned long get_devid(uint8_t busnum, uint8_t devnum) 330{ 331 return (busnum << 16) | devnum; 332} 333 334/* will be removed */ 335int usbip_vhci_attach_device(uint8_t port, int sockfd, uint8_t busnum, 336 uint8_t devnum, uint32_t speed) 337{ 338 int devid = get_devid(busnum, devnum); 339 340 return usbip_vhci_attach_device2(port, sockfd, devid, speed); 341} 342 343int usbip_vhci_detach_device(uint8_t port) 344{ 345 char detach_attr_path[SYSFS_PATH_MAX]; 346 char attr_detach[] = "detach"; 347 char buff[200]; /* what size should be ? */ 348 const char *path; 349 int ret; 350 351 snprintf(buff, sizeof(buff), "%u", port); 352 dbg("writing: %s", buff); 353 354 path = udev_device_get_syspath(vhci_driver->hc_device); 355 snprintf(detach_attr_path, sizeof(detach_attr_path), "%s/%s", 356 path, attr_detach); 357 dbg("detach attribute path: %s", detach_attr_path); 358 359 ret = write_sysfs_attribute(detach_attr_path, buff, strlen(buff)); 360 if (ret < 0) { 361 dbg("write_sysfs_attribute failed"); 362 return -1; 363 } 364 365 dbg("detached port: %d", port); 366 367 return 0; 368} 369 370int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev) 371{ 372 char product_name[100]; 373 char host[NI_MAXHOST] = "unknown host"; 374 char serv[NI_MAXSERV] = "unknown port"; 375 char remote_busid[SYSFS_BUS_ID_SIZE]; 376 int ret; 377 int read_record_error = 0; 378 379 if (idev->status == VDEV_ST_NULL || idev->status == VDEV_ST_NOTASSIGNED) 380 return 0; 381 382 ret = read_record(idev->port, host, sizeof(host), serv, sizeof(serv), 383 remote_busid); 384 if (ret) { 385 err("read_record"); 386 read_record_error = 1; 387 } 388 389 printf("Port %02d: <%s> at %s\n", idev->port, 390 usbip_status_string(idev->status), 391 usbip_speed_string(idev->udev.speed)); 392 393 usbip_names_get_product(product_name, sizeof(product_name), 394 idev->udev.idVendor, idev->udev.idProduct); 395 396 printf(" %s\n", product_name); 397 398 if (!read_record_error) { 399 printf("%10s -> usbip://%s:%s/%s\n", idev->udev.busid, 400 host, serv, remote_busid); 401 printf("%10s -> remote bus/dev %03d/%03d\n", " ", 402 idev->busnum, idev->devnum); 403 } else { 404 printf("%10s -> unknown host, remote port and remote busid\n", 405 idev->udev.busid); 406 printf("%10s -> remote bus/dev %03d/%03d\n", " ", 407 idev->busnum, idev->devnum); 408 } 409 410 return 0; 411} 412