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 } 281