Hi, we had the following problem in libmtp: - We have to autoprobe every plugged-in device to figure out if it is MTP or not. These devices are numerous and we can no longer count on users reporting each and every VID+PID out there - Class codes cannot be used - Have to look for devices with interfaces with certain endpoints: - 1 BULK in - 1 BULK out - 1 INTERRUPT in - Then we do deeper probing. - However some devices bug out on libusb_open() which is in turn needed to use libusb to figure out if the interface has these BULK and INTERRUPT characteristics So since I cannot use libusb_open() I have added code like below to libmtp to instead inspect sysfs *before* starting any libusb-based business. It is run from udev and udev provides the sysfs node and that is passed to check_sysfs(). So: - Is this a good idea? - What is the scary business that libusb_open() does that makes a lot of substandard USB devices totally freak out? enum ep_type { OTHER_EP, BULK_OUT_EP, BULK_IN_EP, INTERRUPT_IN_EP, INTERRUPT_OUT_EP, }; static enum ep_type get_ep_type(char *path) { char pbuf[FILENAME_MAX];enum ep_type { OTHER_EP, BULK_OUT_EP, BULK_IN_EP, INTERRUPT_IN_EP, INTERRUPT_OUT_EP, }; static enum ep_type get_ep_type(char *path) { char pbuf[FILENAME_MAX]; int len = strlen(path); int fd; char buf[128]; int bread; int is_out = 0; int is_in = 0; int is_bulk = 0; int is_interrupt = 0; int i; strcpy(pbuf, path); pbuf[len++] = '/'; /* Check the type */ strncpy(pbuf + len, "type", FILENAME_MAX - len); pbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */ fd = open(pbuf, O_RDONLY); if (fd < 0) return OTHER_EP; bread = read(fd, buf, sizeof(buf)); close(fd); if (bread < 2) return OTHER_EP; for (i = 0; i < bread; i++) if(buf[i] == 0x0d || buf[i] == 0x0a) buf[i] = '\0'; if (!strcmp(buf, "Bulk")) is_bulk = 1; if (!strcmp(buf, "Interrupt")) is_interrupt = 1; /* Check the direction */ strncpy(pbuf + len, "direction", FILENAME_MAX - len); pbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */ fd = open(pbuf, O_RDONLY); if (fd < 0) return OTHER_EP; bread = read(fd, buf, sizeof(buf)); close(fd); if (bread < 2) return OTHER_EP; for (i = 0; i < bread; i++) if(buf[i] == 0x0d || buf[i] == 0x0a) buf[i] = '\0'; if (!strcmp(buf, "in")) is_in = 1; if (!strcmp(buf, "out")) is_out = 1; if (is_bulk && is_in) return BULK_IN_EP; if (is_bulk && is_out) return BULK_OUT_EP; if (is_interrupt && is_in) return INTERRUPT_IN_EP; if (is_interrupt && is_out) return INTERRUPT_OUT_EP; return OTHER_EP; } static int has_3_ep(char *path) { char pbuf[FILENAME_MAX]; int len = strlen(path); int fd; char buf[128]; int bread; strcpy(pbuf, path); pbuf[len++] = '/'; strncpy(pbuf + len, "bNumEndpoints", FILENAME_MAX - len); pbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */ fd = open(pbuf, O_RDONLY); if (fd < 0) return -1; /* Read all contents to buffer */ bread = read(fd, buf, sizeof(buf)); close(fd); if (bread < 2) return 0; /* 0x30, 0x33 = "03", maybe we should parse it? */ if (buf[0] == 0x30 && buf[1] == 0x33) return 1; return 0; } static int check_interface(char *sysfspath) { char dirbuf[FILENAME_MAX]; int len = strlen(sysfspath); DIR *dir; struct dirent *dent; regex_t r; int ret; int bulk_out_ep_found = 0; int bulk_in_ep_found = 0; int interrupt_in_ep_found = 0; ret = has_3_ep(sysfspath); if (ret <= 0) return ret; /* Yes it has three endpoints ... look even closer! */ dir = opendir(sysfspath); if (!dir) return -1; strcpy(dirbuf, sysfspath); dirbuf[len++] = '/'; /* Check for dirs that identify endpoints */ ret = regcomp(&r, "^ep_[0-9a-f]+$", REG_EXTENDED | REG_NOSUB); if (ret) return -1; while ((dent = readdir(dir))) { struct stat st; /* No need to check those beginning with a period */ if (dent->d_name[0] == '.') continue; strncpy(dirbuf + len, dent->d_name, FILENAME_MAX - len); dirbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */ ret = lstat(dirbuf, &st); if (ret) continue; if (S_ISDIR(st.st_mode) && !regexec(&r, dent->d_name, 0, 0, 0)) { enum ep_type ept; ept = get_ep_type(dirbuf); if (ept == BULK_OUT_EP) bulk_out_ep_found = 1; else if (ept == BULK_IN_EP) bulk_in_ep_found = 1; else if (ept == INTERRUPT_IN_EP) interrupt_in_ep_found = 1; } } regfree(&r); closedir(dir); /* * If this is fulfilled the interface is an MTP candidate */ if (bulk_out_ep_found && bulk_in_ep_found && interrupt_in_ep_found) { return 1; } return 0; } static int check_sysfs(char *sysfspath) { char dirbuf[FILENAME_MAX]; int len = strlen(sysfspath); DIR *dir; struct dirent *dent; regex_t r; int ret; int look_closer = 0; dir = opendir(sysfspath); if (!dir) return -1; strcpy(dirbuf, sysfspath); dirbuf[len++] = '/'; /* Check for dirs that identify interfaces */ ret = regcomp(&r, "^[0-9]+-[0-9]+\\:[0-9]+\\.[0-9]+$", REG_EXTENDED | REG_NOSUB); if (ret) return -1; while ((dent = readdir(dir))) { struct stat st; int ret; /* No need to check those beginning with a period */ if (dent->d_name[0] == '.') continue; strncpy(dirbuf + len, dent->d_name, FILENAME_MAX - len); dirbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */ ret = lstat(dirbuf, &st); if (ret) continue; /* Look closer at dirs that may be interfaces */ if (S_ISDIR(st.st_mode)) { if (!regexec(&r, dent->d_name, 0, 0, 0)) if (check_interface(dirbuf) > 0) /* potential MTP interface! */ look_closer = 1; } } regfree(&r); closedir(dir); return look_closer; } int len = strlen(path); int fd; char buf[128]; int bread; int is_out = 0; int is_in = 0; int is_bulk = 0; int is_interrupt = 0; int i; strcpy(pbuf, path); pbuf[len++] = '/'; /* Check the type */ strncpy(pbuf + len, "type", FILENAME_MAX - len); pbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */ fd = open(pbuf, O_RDONLY); if (fd < 0) return OTHER_EP; bread = read(fd, buf, sizeof(buf)); close(fd); if (bread < 2) return OTHER_EP; for (i = 0; i < bread; i++) if(buf[i] == 0x0d || buf[i] == 0x0a) buf[i] = '\0'; if (!strcmp(buf, "Bulk")) is_bulk = 1; if (!strcmp(buf, "Interrupt")) is_interrupt = 1; /* Check the direction */ strncpy(pbuf + len, "direction", FILENAME_MAX - len); pbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */ fd = open(pbuf, O_RDONLY); if (fd < 0) return OTHER_EP; bread = read(fd, buf, sizeof(buf)); close(fd); if (bread < 2) return OTHER_EP; for (i = 0; i < bread; i++) if(buf[i] == 0x0d || buf[i] == 0x0a) buf[i] = '\0'; if (!strcmp(buf, "in")) is_in = 1; if (!strcmp(buf, "out")) is_out = 1; if (is_bulk && is_in) return BULK_IN_EP; if (is_bulk && is_out) return BULK_OUT_EP; if (is_interrupt && is_in) return INTERRUPT_IN_EP; if (is_interrupt && is_out) return INTERRUPT_OUT_EP; return OTHER_EP; } static int has_3_ep(char *path) { char pbuf[FILENAME_MAX]; int len = strlen(path); int fd; char buf[128]; int bread; strcpy(pbuf, path); pbuf[len++] = '/'; strncpy(pbuf + len, "bNumEndpoints", FILENAME_MAX - len); pbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */ fd = open(pbuf, O_RDONLY); if (fd < 0) return -1; /* Read all contents to buffer */ bread = read(fd, buf, sizeof(buf)); close(fd); if (bread < 2) return 0; /* 0x30, 0x33 = "03", maybe we should parse it? */ if (buf[0] == 0x30 && buf[1] == 0x33) return 1; return 0; } static int check_interface(char *sysfspath) { char dirbuf[FILENAME_MAX]; int len = strlen(sysfspath); DIR *dir; struct dirent *dent; regex_t r; int ret; int bulk_out_ep_found = 0; int bulk_in_ep_found = 0; int interrupt_in_ep_found = 0; ret = has_3_ep(sysfspath); if (ret <= 0) return ret; /* Yes it has three endpoints ... look even closer! */ dir = opendir(sysfspath); if (!dir) return -1; strcpy(dirbuf, sysfspath); dirbuf[len++] = '/'; /* Check for dirs that identify endpoints */ ret = regcomp(&r, "^ep_[0-9a-f]+$", REG_EXTENDED | REG_NOSUB); if (ret) return -1; while ((dent = readdir(dir))) { struct stat st; /* No need to check those beginning with a period */ if (dent->d_name[0] == '.') continue; strncpy(dirbuf + len, dent->d_name, FILENAME_MAX - len); dirbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */ ret = lstat(dirbuf, &st); if (ret) continue; if (S_ISDIR(st.st_mode) && !regexec(&r, dent->d_name, 0, 0, 0)) { enum ep_type ept; ept = get_ep_type(dirbuf); if (ept == BULK_OUT_EP) bulk_out_ep_found = 1; else if (ept == BULK_IN_EP) bulk_in_ep_found = 1; else if (ept == INTERRUPT_IN_EP) interrupt_in_ep_found = 1; } } regfree(&r); closedir(dir); /* * If this is fulfilled the interface is an MTP candidate */ if (bulk_out_ep_found && bulk_in_ep_found && interrupt_in_ep_found) { return 1; } return 0; } static int check_sysfs(char *sysfspath) { char dirbuf[FILENAME_MAX]; int len = strlen(sysfspath); DIR *dir; struct dirent *dent; regex_t r; int ret; int look_closer = 0; dir = opendir(sysfspath); if (!dir) return -1; strcpy(dirbuf, sysfspath); dirbuf[len++] = '/'; /* Check for dirs that identify interfaces */ ret = regcomp(&r, "^[0-9]+-[0-9]+\\:[0-9]+\\.[0-9]+$", REG_EXTENDED | REG_NOSUB); if (ret) return -1; while ((dent = readdir(dir))) { struct stat st; int ret; /* No need to check those beginning with a period */ if (dent->d_name[0] == '.') continue; strncpy(dirbuf + len, dent->d_name, FILENAME_MAX - len); dirbuf[FILENAME_MAX - 1] = '\0'; /* Sentinel */ ret = lstat(dirbuf, &st); if (ret) continue; /* Look closer at dirs that may be interfaces */ if (S_ISDIR(st.st_mode)) { if (!regexec(&r, dent->d_name, 0, 0, 0)) if (check_interface(dirbuf) > 0) /* potential MTP interface! */ look_closer = 1; } } regfree(&r); closedir(dir); return look_closer; } Yours, Linus Walleij -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html