18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2011 matt mooney <mfm@muteddisk.com> 48c2ecf20Sopenharmony_ci * 2005-2007 Takahiro Hirofuchi 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <libudev.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <errno.h> 108c2ecf20Sopenharmony_ci#include <stdio.h> 118c2ecf20Sopenharmony_ci#include <string.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <getopt.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "usbip_common.h" 168c2ecf20Sopenharmony_ci#include "utils.h" 178c2ecf20Sopenharmony_ci#include "usbip.h" 188c2ecf20Sopenharmony_ci#include "sysfs_utils.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic const char usbip_unbind_usage_string[] = 218c2ecf20Sopenharmony_ci "usbip unbind <args>\n" 228c2ecf20Sopenharmony_ci " -b, --busid=<busid> Unbind " USBIP_HOST_DRV_NAME ".ko from " 238c2ecf20Sopenharmony_ci "device on <busid>\n"; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_civoid usbip_unbind_usage(void) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci printf("usage: %s", usbip_unbind_usage_string); 288c2ecf20Sopenharmony_ci} 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic int unbind_device(char *busid) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci char bus_type[] = "usb"; 338c2ecf20Sopenharmony_ci int rc, ret = -1; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci char unbind_attr_name[] = "unbind"; 368c2ecf20Sopenharmony_ci char unbind_attr_path[SYSFS_PATH_MAX]; 378c2ecf20Sopenharmony_ci char rebind_attr_name[] = "rebind"; 388c2ecf20Sopenharmony_ci char rebind_attr_path[SYSFS_PATH_MAX]; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci struct udev *udev; 418c2ecf20Sopenharmony_ci struct udev_device *dev; 428c2ecf20Sopenharmony_ci const char *driver; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci /* Create libudev context. */ 458c2ecf20Sopenharmony_ci udev = udev_new(); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci /* Check whether the device with this bus ID exists. */ 488c2ecf20Sopenharmony_ci dev = udev_device_new_from_subsystem_sysname(udev, "usb", busid); 498c2ecf20Sopenharmony_ci if (!dev) { 508c2ecf20Sopenharmony_ci err("device with the specified bus ID does not exist"); 518c2ecf20Sopenharmony_ci goto err_close_udev; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* Check whether the device is using usbip-host driver. */ 558c2ecf20Sopenharmony_ci driver = udev_device_get_driver(dev); 568c2ecf20Sopenharmony_ci if (!driver || strcmp(driver, "usbip-host")) { 578c2ecf20Sopenharmony_ci err("device is not bound to usbip-host driver"); 588c2ecf20Sopenharmony_ci goto err_close_udev; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* Unbind device from driver. */ 628c2ecf20Sopenharmony_ci snprintf(unbind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s", 638c2ecf20Sopenharmony_ci SYSFS_MNT_PATH, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME, 648c2ecf20Sopenharmony_ci USBIP_HOST_DRV_NAME, unbind_attr_name); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci rc = write_sysfs_attribute(unbind_attr_path, busid, strlen(busid)); 678c2ecf20Sopenharmony_ci if (rc < 0) { 688c2ecf20Sopenharmony_ci err("error unbinding device %s from driver", busid); 698c2ecf20Sopenharmony_ci goto err_close_udev; 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* Notify driver of unbind. */ 738c2ecf20Sopenharmony_ci rc = modify_match_busid(busid, 0); 748c2ecf20Sopenharmony_ci if (rc < 0) { 758c2ecf20Sopenharmony_ci err("unable to unbind device on %s", busid); 768c2ecf20Sopenharmony_ci goto err_close_udev; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* Trigger new probing. */ 808c2ecf20Sopenharmony_ci snprintf(rebind_attr_path, sizeof(unbind_attr_path), "%s/%s/%s/%s/%s/%s", 818c2ecf20Sopenharmony_ci SYSFS_MNT_PATH, SYSFS_BUS_NAME, bus_type, SYSFS_DRIVERS_NAME, 828c2ecf20Sopenharmony_ci USBIP_HOST_DRV_NAME, rebind_attr_name); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci rc = write_sysfs_attribute(rebind_attr_path, busid, strlen(busid)); 858c2ecf20Sopenharmony_ci if (rc < 0) { 868c2ecf20Sopenharmony_ci err("error rebinding"); 878c2ecf20Sopenharmony_ci goto err_close_udev; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci ret = 0; 918c2ecf20Sopenharmony_ci info("unbind device on busid %s: complete", busid); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cierr_close_udev: 948c2ecf20Sopenharmony_ci udev_device_unref(dev); 958c2ecf20Sopenharmony_ci udev_unref(udev); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return ret; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ciint usbip_unbind(int argc, char *argv[]) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci static const struct option opts[] = { 1038c2ecf20Sopenharmony_ci { "busid", required_argument, NULL, 'b' }, 1048c2ecf20Sopenharmony_ci { NULL, 0, NULL, 0 } 1058c2ecf20Sopenharmony_ci }; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci int opt; 1088c2ecf20Sopenharmony_ci int ret = -1; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci for (;;) { 1118c2ecf20Sopenharmony_ci opt = getopt_long(argc, argv, "b:", opts, NULL); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (opt == -1) 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci switch (opt) { 1178c2ecf20Sopenharmony_ci case 'b': 1188c2ecf20Sopenharmony_ci ret = unbind_device(optarg); 1198c2ecf20Sopenharmony_ci goto out; 1208c2ecf20Sopenharmony_ci default: 1218c2ecf20Sopenharmony_ci goto err_out; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cierr_out: 1268c2ecf20Sopenharmony_ci usbip_unbind_usage(); 1278c2ecf20Sopenharmony_ciout: 1288c2ecf20Sopenharmony_ci return ret; 1298c2ecf20Sopenharmony_ci} 130