162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2015-2016 Samsung Electronics 462306a36Sopenharmony_ci * Igor Kotrasinski <i.kotrasinsk@samsung.com> 562306a36Sopenharmony_ci * Krzysztof Opasiak <k.opasiak@samsung.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Refactored from usbip_host_driver.c, which is: 862306a36Sopenharmony_ci * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> 962306a36Sopenharmony_ci * 2005-2007 Takahiro Hirofuchi 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <sys/types.h> 1362306a36Sopenharmony_ci#include <sys/stat.h> 1462306a36Sopenharmony_ci#include <fcntl.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <errno.h> 1762306a36Sopenharmony_ci#include <unistd.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <libudev.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "usbip_common.h" 2262306a36Sopenharmony_ci#include "usbip_host_common.h" 2362306a36Sopenharmony_ci#include "list.h" 2462306a36Sopenharmony_ci#include "sysfs_utils.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciextern struct udev *udev_context; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int32_t read_attr_usbip_status(struct usbip_usb_device *udev) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci char status_attr_path[SYSFS_PATH_MAX]; 3162306a36Sopenharmony_ci int size; 3262306a36Sopenharmony_ci int fd; 3362306a36Sopenharmony_ci int length; 3462306a36Sopenharmony_ci char status[2] = { 0 }; 3562306a36Sopenharmony_ci int value = 0; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci size = snprintf(status_attr_path, sizeof(status_attr_path), 3862306a36Sopenharmony_ci "%s/usbip_status", udev->path); 3962306a36Sopenharmony_ci if (size < 0 || (unsigned int)size >= sizeof(status_attr_path)) { 4062306a36Sopenharmony_ci err("usbip_status path length %i >= %lu or < 0", size, 4162306a36Sopenharmony_ci (long unsigned)sizeof(status_attr_path)); 4262306a36Sopenharmony_ci return -1; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci fd = open(status_attr_path, O_RDONLY); 4762306a36Sopenharmony_ci if (fd < 0) { 4862306a36Sopenharmony_ci err("error opening attribute %s", status_attr_path); 4962306a36Sopenharmony_ci return -1; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci length = read(fd, status, 1); 5362306a36Sopenharmony_ci if (length < 0) { 5462306a36Sopenharmony_ci err("error reading attribute %s", status_attr_path); 5562306a36Sopenharmony_ci close(fd); 5662306a36Sopenharmony_ci return -1; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci value = atoi(status); 6062306a36Sopenharmony_ci close(fd); 6162306a36Sopenharmony_ci return value; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic 6562306a36Sopenharmony_cistruct usbip_exported_device *usbip_exported_device_new( 6662306a36Sopenharmony_ci struct usbip_host_driver *hdriver, const char *sdevpath) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct usbip_exported_device *edev = NULL; 6962306a36Sopenharmony_ci struct usbip_exported_device *edev_old; 7062306a36Sopenharmony_ci size_t size; 7162306a36Sopenharmony_ci int i; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci edev = calloc(1, sizeof(struct usbip_exported_device)); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci edev->sudev = 7662306a36Sopenharmony_ci udev_device_new_from_syspath(udev_context, sdevpath); 7762306a36Sopenharmony_ci if (!edev->sudev) { 7862306a36Sopenharmony_ci err("udev_device_new_from_syspath: %s", sdevpath); 7962306a36Sopenharmony_ci goto err; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (hdriver->ops.read_device(edev->sudev, &edev->udev) < 0) 8362306a36Sopenharmony_ci goto err; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci edev->status = read_attr_usbip_status(&edev->udev); 8662306a36Sopenharmony_ci if (edev->status < 0) 8762306a36Sopenharmony_ci goto err; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* reallocate buffer to include usb interface data */ 9062306a36Sopenharmony_ci size = sizeof(struct usbip_exported_device) + 9162306a36Sopenharmony_ci edev->udev.bNumInterfaces * sizeof(struct usbip_usb_interface); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci edev_old = edev; 9462306a36Sopenharmony_ci edev = realloc(edev, size); 9562306a36Sopenharmony_ci if (!edev) { 9662306a36Sopenharmony_ci edev = edev_old; 9762306a36Sopenharmony_ci dbg("realloc failed"); 9862306a36Sopenharmony_ci goto err; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci for (i = 0; i < edev->udev.bNumInterfaces; i++) { 10262306a36Sopenharmony_ci /* vudc does not support reading interfaces */ 10362306a36Sopenharmony_ci if (!hdriver->ops.read_interface) 10462306a36Sopenharmony_ci break; 10562306a36Sopenharmony_ci hdriver->ops.read_interface(&edev->udev, i, &edev->uinf[i]); 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return edev; 10962306a36Sopenharmony_cierr: 11062306a36Sopenharmony_ci if (edev->sudev) 11162306a36Sopenharmony_ci udev_device_unref(edev->sudev); 11262306a36Sopenharmony_ci if (edev) 11362306a36Sopenharmony_ci free(edev); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return NULL; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic int refresh_exported_devices(struct usbip_host_driver *hdriver) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct usbip_exported_device *edev; 12162306a36Sopenharmony_ci struct udev_enumerate *enumerate; 12262306a36Sopenharmony_ci struct udev_list_entry *devices, *dev_list_entry; 12362306a36Sopenharmony_ci struct udev_device *dev; 12462306a36Sopenharmony_ci const char *path; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci enumerate = udev_enumerate_new(udev_context); 12762306a36Sopenharmony_ci udev_enumerate_add_match_subsystem(enumerate, hdriver->udev_subsystem); 12862306a36Sopenharmony_ci udev_enumerate_scan_devices(enumerate); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci devices = udev_enumerate_get_list_entry(enumerate); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci udev_list_entry_foreach(dev_list_entry, devices) { 13362306a36Sopenharmony_ci path = udev_list_entry_get_name(dev_list_entry); 13462306a36Sopenharmony_ci dev = udev_device_new_from_syspath(udev_context, 13562306a36Sopenharmony_ci path); 13662306a36Sopenharmony_ci if (dev == NULL) 13762306a36Sopenharmony_ci continue; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Check whether device uses usbip driver. */ 14062306a36Sopenharmony_ci if (hdriver->ops.is_my_device(dev)) { 14162306a36Sopenharmony_ci edev = usbip_exported_device_new(hdriver, path); 14262306a36Sopenharmony_ci if (!edev) { 14362306a36Sopenharmony_ci dbg("usbip_exported_device_new failed"); 14462306a36Sopenharmony_ci continue; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci list_add(&edev->node, &hdriver->edev_list); 14862306a36Sopenharmony_ci hdriver->ndevs++; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return 0; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void usbip_exported_device_destroy(struct list_head *devs) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct list_head *i, *tmp; 15862306a36Sopenharmony_ci struct usbip_exported_device *edev; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci list_for_each_safe(i, tmp, devs) { 16162306a36Sopenharmony_ci edev = list_entry(i, struct usbip_exported_device, node); 16262306a36Sopenharmony_ci list_del(i); 16362306a36Sopenharmony_ci free(edev); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ciint usbip_generic_driver_open(struct usbip_host_driver *hdriver) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci int rc; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci udev_context = udev_new(); 17262306a36Sopenharmony_ci if (!udev_context) { 17362306a36Sopenharmony_ci err("udev_new failed"); 17462306a36Sopenharmony_ci return -1; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci rc = refresh_exported_devices(hdriver); 17862306a36Sopenharmony_ci if (rc < 0) 17962306a36Sopenharmony_ci goto err; 18062306a36Sopenharmony_ci return 0; 18162306a36Sopenharmony_cierr: 18262306a36Sopenharmony_ci udev_unref(udev_context); 18362306a36Sopenharmony_ci return -1; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_civoid usbip_generic_driver_close(struct usbip_host_driver *hdriver) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci if (!hdriver) 18962306a36Sopenharmony_ci return; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci usbip_exported_device_destroy(&hdriver->edev_list); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci udev_unref(udev_context); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ciint usbip_generic_refresh_device_list(struct usbip_host_driver *hdriver) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci int rc; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci usbip_exported_device_destroy(&hdriver->edev_list); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci hdriver->ndevs = 0; 20362306a36Sopenharmony_ci INIT_LIST_HEAD(&hdriver->edev_list); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci rc = refresh_exported_devices(hdriver); 20662306a36Sopenharmony_ci if (rc < 0) 20762306a36Sopenharmony_ci return -1; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ciint usbip_export_device(struct usbip_exported_device *edev, int sockfd) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci char attr_name[] = "usbip_sockfd"; 21562306a36Sopenharmony_ci char sockfd_attr_path[SYSFS_PATH_MAX]; 21662306a36Sopenharmony_ci int size; 21762306a36Sopenharmony_ci char sockfd_buff[30]; 21862306a36Sopenharmony_ci int ret; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (edev->status != SDEV_ST_AVAILABLE) { 22162306a36Sopenharmony_ci dbg("device not available: %s", edev->udev.busid); 22262306a36Sopenharmony_ci switch (edev->status) { 22362306a36Sopenharmony_ci case SDEV_ST_ERROR: 22462306a36Sopenharmony_ci dbg("status SDEV_ST_ERROR"); 22562306a36Sopenharmony_ci ret = ST_DEV_ERR; 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci case SDEV_ST_USED: 22862306a36Sopenharmony_ci dbg("status SDEV_ST_USED"); 22962306a36Sopenharmony_ci ret = ST_DEV_BUSY; 23062306a36Sopenharmony_ci break; 23162306a36Sopenharmony_ci default: 23262306a36Sopenharmony_ci dbg("status unknown: 0x%x", edev->status); 23362306a36Sopenharmony_ci ret = -1; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci return ret; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci /* only the first interface is true */ 23962306a36Sopenharmony_ci size = snprintf(sockfd_attr_path, sizeof(sockfd_attr_path), "%s/%s", 24062306a36Sopenharmony_ci edev->udev.path, attr_name); 24162306a36Sopenharmony_ci if (size < 0 || (unsigned int)size >= sizeof(sockfd_attr_path)) { 24262306a36Sopenharmony_ci err("exported device path length %i >= %lu or < 0", size, 24362306a36Sopenharmony_ci (long unsigned)sizeof(sockfd_attr_path)); 24462306a36Sopenharmony_ci return -1; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci size = snprintf(sockfd_buff, sizeof(sockfd_buff), "%d\n", sockfd); 24862306a36Sopenharmony_ci if (size < 0 || (unsigned int)size >= sizeof(sockfd_buff)) { 24962306a36Sopenharmony_ci err("socket length %i >= %lu or < 0", size, 25062306a36Sopenharmony_ci (long unsigned)sizeof(sockfd_buff)); 25162306a36Sopenharmony_ci return -1; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci ret = write_sysfs_attribute(sockfd_attr_path, sockfd_buff, 25562306a36Sopenharmony_ci strlen(sockfd_buff)); 25662306a36Sopenharmony_ci if (ret < 0) { 25762306a36Sopenharmony_ci err("write_sysfs_attribute failed: sockfd %s to %s", 25862306a36Sopenharmony_ci sockfd_buff, sockfd_attr_path); 25962306a36Sopenharmony_ci return ret; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci info("connect: %s", edev->udev.busid); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return ret; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistruct usbip_exported_device *usbip_generic_get_device( 26862306a36Sopenharmony_ci struct usbip_host_driver *hdriver, int num) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct list_head *i; 27162306a36Sopenharmony_ci struct usbip_exported_device *edev; 27262306a36Sopenharmony_ci int cnt = 0; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci list_for_each(i, &hdriver->edev_list) { 27562306a36Sopenharmony_ci edev = list_entry(i, struct usbip_exported_device, node); 27662306a36Sopenharmony_ci if (num == cnt) 27762306a36Sopenharmony_ci return edev; 27862306a36Sopenharmony_ci cnt++; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci return NULL; 28262306a36Sopenharmony_ci} 283