/* * detach-usb.c - a tool to detach a kernel module from a USB device * Copyright (C) 2007 Thomas Reitmayr * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* This program consists of parts of the libusb library, which * can be found at http://libusb.sourceforge.net/ */ #include #include #include #include #include #include #include #include #include #define USB_PATH1 "/dev/bus/usb" #define USB_PATH2 "/proc/bus/usb" #define USB_MAXDRIVERNAME 255 #define IOCTL_USB_IOCTL _IOWR('U', 18, struct usb_ioctl) #define IOCTL_USB_GETDRIVER _IOW('U', 8, struct usb_getdriver) #define IOCTL_USB_DISCONNECT _IO('U', 22) #define DEVICE_DESC_LENGTH 18 struct usb_ioctl { int ifno; /* interface 0..N ; negative numbers reserved */ int ioctl_code; /* MUST encode size + direction of data so the * macros in give correct values */ void *data; /* param buffer (in, or out) */ }; struct usb_getdriver { unsigned int interface; char driver[USB_MAXDRIVERNAME + 1]; }; int check_dir(char *dirname, unsigned vendor, unsigned product) { DIR *dir; struct dirent *entry; char filepath[PATH_MAX + 1]; int fd = -1; // snprintf(dirpath, PATH_MAX, "%s/%s", usb_path, bus->dirname); dir = opendir(dirname); if (!dir) return -1; while ((entry = readdir(dir))) { if (entry->d_name[0] == '.') continue; snprintf(filepath, sizeof(filepath) - 1, "%s/%s", dirname, entry->d_name); if (entry->d_type & DT_DIR) { fd = check_dir(filepath, vendor, product); } else { printf("checking %s\n", filepath); fd = open(filepath, O_RDWR); //ONLY); if (fd >= 0) { /* check the device descriptor */ unsigned char device_desc[DEVICE_DESC_LENGTH]; if (read(fd, (void *)device_desc, DEVICE_DESC_LENGTH) < 0) { /* read error */ close(fd); fd = -1; } else { /* compare IDs */ unsigned vendorid = (device_desc[9] << 8) | device_desc[8]; unsigned productid = (device_desc[11] << 8) | device_desc[10]; printf(" v=%04x, p=%04x\n", vendorid, productid); if ((vendorid != vendor) || (productid != product)) { close(fd); fd = -1; } } } } if (fd >= 0) break; } closedir(dir); return fd; } char *driver(int fd, unsigned interface) { struct usb_getdriver getdrv; static char drv[USB_MAXDRIVERNAME + 1]; int ret; getdrv.interface = interface; ret = ioctl(fd, IOCTL_USB_GETDRIVER, &getdrv); if (ret) { if (errno == ENODATA) { drv[0] = '\0'; } else { printf("Error in driver(): %s, %d\n", strerror(errno), errno); return NULL; } } else strncpy(drv, getdrv.driver, USB_MAXDRIVERNAME); return drv; } int detach(int fd, unsigned interface) { struct usb_ioctl command; int ret; command.ifno = interface; command.ioctl_code = IOCTL_USB_DISCONNECT; command.data = NULL; ret = ioctl(fd, IOCTL_USB_IOCTL, &command); if (ret) { printf("Error line __line__: %s\n", strerror(errno)); } return ret; } #define USAGE "Usage: detach-usb VENDOR PRODUCT INTERFACE\n" int main(int argc, char **argv) { if (argc < 4) { fputs("detach-usb: missing operands\n" USAGE, stderr); exit(EXIT_FAILURE); } unsigned vendor = strtoul(argv[1], NULL, 16); unsigned product = strtoul(argv[2], NULL, 16); unsigned interface = strtoul(argv[3], NULL, 16); int ret = 0; int fd = check_dir(USB_PATH1, vendor, product); if (fd < 0) fd = check_dir(USB_PATH2, vendor, product); if (fd < 0) { printf("could not find USB device\n"); ret = 1; } else { char *drv; printf("found USB device\n"); drv = driver(fd, interface); if (drv) { if (drv[0]) { if (detach(fd, interface) == 0) { printf("successfully detached driver %s\n", drv); } else { printf("could not detach driver %s\n", drv); ret = 2; } } else { printf("no driver attached\n"); } } else { /* error */ ret = 3; } close(fd); } return ret; }