// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> * 2005-2007 Takahiro Hirofuchi * Copyright (C) 2015-2016 Samsung Electronics * Igor Kotrasinski <i.kotrasinsk@samsung.com> * Krzysztof Opasiak <k.opasiak@samsung.com> */ #include <sys/types.h> #include <libudev.h> #include <errno.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <getopt.h> #include <netdb.h> #include <unistd.h> #include <dirent.h> #include <linux/usb/ch9.h> #include "usbip_common.h" #include "usbip_network.h" #include "usbip.h" static const char usbip_list_usage_string[] = "usbip list [-p|--parsable] <args>\n" " -p, --parsable Parsable list format\n" " -r, --remote=<host> List the exportable USB devices on <host>\n" " -l, --local List the local USB devices\n" " -d, --device List the local USB gadgets bound to usbip-vudc\n"; void usbip_list_usage(void) { printf("usage: %s", usbip_list_usage_string); } static int get_exported_devices(char *host, int sockfd) { char product_name[100]; char class_name[100]; struct op_devlist_reply reply; uint16_t code = OP_REP_DEVLIST; struct usbip_usb_device udev; struct usbip_usb_interface uintf; unsigned int i; int rc, j; int status; rc = usbip_net_send_op_common(sockfd, OP_REQ_DEVLIST, 0); if (rc < 0) { dbg("usbip_net_send_op_common failed"); return -1; } rc = usbip_net_recv_op_common(sockfd, &code, &status); if (rc < 0) { err("Exported Device List Request failed - %s\n", usbip_op_common_status_string(status)); return -1; } memset(&reply, 0, sizeof(reply)); rc = usbip_net_recv(sockfd, &reply, sizeof(reply)); if (rc < 0) { dbg("usbip_net_recv_op_devlist failed"); return -1; } PACK_OP_DEVLIST_REPLY(0, &reply); dbg("exportable devices: %d\n", reply.ndev); if (reply.ndev == 0) { info("no exportable devices found on %s", host); return 0; } printf("Exportable USB devices\n"); printf("======================\n"); printf(" - %s\n", host); for (i = 0; i < reply.ndev; i++) { memset(&udev, 0, sizeof(udev)); rc = usbip_net_recv(sockfd, &udev, sizeof(udev)); if (rc < 0) { dbg("usbip_net_recv failed: usbip_usb_device[%d]", i); return -1; } usbip_net_pack_usb_device(0, &udev); usbip_names_get_product(product_name, sizeof(product_name), udev.idVendor, udev.idProduct); usbip_names_get_class(class_name, sizeof(class_name), udev.bDeviceClass, udev.bDeviceSubClass, udev.bDeviceProtocol); printf("%11s: %s\n", udev.busid, product_name); printf("%11s: %s\n", "", udev.path); printf("%11s: %s\n", "", class_name); for (j = 0; j < udev.bNumInterfaces; j++) { rc = usbip_net_recv(sockfd, &uintf, sizeof(uintf)); if (rc < 0) { err("usbip_net_recv failed: usbip_usb_intf[%d]", j); return -1; } usbip_net_pack_usb_interface(0, &uintf); usbip_names_get_class(class_name, sizeof(class_name), uintf.bInterfaceClass, uintf.bInterfaceSubClass, uintf.bInterfaceProtocol); printf("%11s: %2d - %s\n", "", j, class_name); } printf("\n"); } return 0; } static int list_exported_devices(char *host) { int rc; int sockfd; sockfd = usbip_net_tcp_connect(host, usbip_port_string); if (sockfd < 0) { err("could not connect to %s:%s: %s", host, usbip_port_string, gai_strerror(sockfd)); return -1; } dbg("connected to %s:%s", host, usbip_port_string); rc = get_exported_devices(host, sockfd); if (rc < 0) { err("failed to get device list from %s", host); return -1; } close(sockfd); return 0; } static void print_device(const char *busid, const char *vendor, const char *product, bool parsable) { if (parsable) printf("busid=%s#usbid=%.4s:%.4s#", busid, vendor, product); else printf(" - busid %s (%.4s:%.4s)\n", busid, vendor, product); } static void print_product_name(char *product_name, bool parsable) { if (!parsable) printf(" %s\n", product_name); } static int list_devices(bool parsable) { struct udev *udev; struct udev_enumerate *enumerate; struct udev_list_entry *devices, *dev_list_entry; struct udev_device *dev; const char *path; const char *idVendor; const char *idProduct; const char *bConfValue; const char *bNumIntfs; const char *busid; char product_name[128]; int ret = -1; const char *devpath; /* Create libudev context. */ udev = udev_new(); /* Create libudev device enumeration. */ enumerate = udev_enumerate_new(udev); /* Take only USB devices that are not hubs and do not have * the bInterfaceNumber attribute, i.e. are not interfaces. */ udev_enumerate_add_match_subsystem(enumerate, "usb"); udev_enumerate_add_nomatch_sysattr(enumerate, "bDeviceClass", "09"); udev_enumerate_add_nomatch_sysattr(enumerate, "bInterfaceNumber", NULL); udev_enumerate_scan_devices(enumerate); devices = udev_enumerate_get_list_entry(enumerate); /* Show information about each device. */ udev_list_entry_foreach(dev_list_entry, devices) { path = udev_list_entry_get_name(dev_list_entry); dev = udev_device_new_from_syspath(udev, path); /* Ignore devices attached to vhci_hcd */ devpath = udev_device_get_devpath(dev); if (strstr(devpath, USBIP_VHCI_DRV_NAME)) { dbg("Skip the device %s already attached to %s\n", devpath, USBIP_VHCI_DRV_NAME); continue; } /* Get device information. */ idVendor = udev_device_get_sysattr_value(dev, "idVendor"); idProduct = udev_device_get_sysattr_value(dev, "idProduct"); bConfValue = udev_device_get_sysattr_value(dev, "bConfigurationValue"); bNumIntfs = udev_device_get_sysattr_value(dev, "bNumInterfaces"); busid = udev_device_get_sysname(dev); if (!idVendor || !idProduct || !bConfValue || !bNumIntfs) { err("problem getting device attributes: %s", strerror(errno)); goto err_out; } /* Get product name. */ usbip_names_get_product(product_name, sizeof(product_name), strtol(idVendor, NULL, 16), strtol(idProduct, NULL, 16)); /* Print information. */ print_device(busid, idVendor, idProduct, parsable); print_product_name(product_name, parsable); printf("\n"); udev_device_unref(dev); } ret = 0; err_out: udev_enumerate_unref(enumerate); udev_unref(udev); return ret; } static int list_gadget_devices(bool parsable) { int ret = -1; struct udev *udev; struct udev_enumerate *enumerate; struct udev_list_entry *devices, *dev_list_entry; struct udev_device *dev; const char *path; const char *driver; const struct usb_device_descriptor *d_desc; const char *descriptors; char product_name[128]; uint16_t idVendor; char idVendor_buf[8]; uint16_t idProduct; char idProduct_buf[8]; const char *busid; udev = udev_new(); enumerate = udev_enumerate_new(udev); udev_enumerate_add_match_subsystem(enumerate, "platform"); udev_enumerate_scan_devices(enumerate); devices = udev_enumerate_get_list_entry(enumerate); udev_list_entry_foreach(dev_list_entry, devices) { path = udev_list_entry_get_name(dev_list_entry); dev = udev_device_new_from_syspath(udev, path); driver = udev_device_get_driver(dev); /* We only have mechanism to enumerate gadgets bound to vudc */ if (driver == NULL || strcmp(driver, USBIP_DEVICE_DRV_NAME)) continue; /* Get device information. */ descriptors = udev_device_get_sysattr_value(dev, VUDC_DEVICE_DESCR_FILE); if (!descriptors) { err("problem getting device attributes: %s", strerror(errno)); goto err_out; } d_desc = (const struct usb_device_descriptor *) descriptors; idVendor = le16toh(d_desc->idVendor); sprintf(idVendor_buf, "0x%4x", idVendor); idProduct = le16toh(d_desc->idProduct); sprintf(idProduct_buf, "0x%4x", idVendor); busid = udev_device_get_sysname(dev); /* Get product name. */ usbip_names_get_product(product_name, sizeof(product_name), le16toh(idVendor), le16toh(idProduct)); /* Print information. */ print_device(busid, idVendor_buf, idProduct_buf, parsable); print_product_name(product_name, parsable); printf("\n"); udev_device_unref(dev); } ret = 0; err_out: udev_enumerate_unref(enumerate); udev_unref(udev); return ret; } int usbip_list(int argc, char *argv[]) { static const struct option opts[] = { { "parsable", no_argument, NULL, 'p' }, { "remote", required_argument, NULL, 'r' }, { "local", no_argument, NULL, 'l' }, { "device", no_argument, NULL, 'd' }, { NULL, 0, NULL, 0 } }; bool parsable = false; int opt; int ret = -1; if (usbip_names_init(USBIDS_FILE)) err("failed to open %s", USBIDS_FILE); for (;;) { opt = getopt_long(argc, argv, "pr:ld", opts, NULL); if (opt == -1) break; switch (opt) { case 'p': parsable = true; break; case 'r': ret = list_exported_devices(optarg); goto out; case 'l': ret = list_devices(parsable); goto out; case 'd': ret = list_gadget_devices(parsable); goto out; default: goto err_out; } } err_out: usbip_list_usage(); out: usbip_names_free(); return ret; }