diff options
author | Linus Walleij <triad@df.lth.se> | 2012-10-21 00:43:38 +0200 |
---|---|---|
committer | Linus Walleij <triad@df.lth.se> | 2012-10-21 00:43:38 +0200 |
commit | cbd399990f3f545635493bbe3583d385b165d2d7 (patch) | |
tree | ba1c3aeab7faa551a759b033e720dfec0668c540 /util | |
parent | 9cf4598b786dbdbab0b812aa9332ffe69cef6ba1 (diff) | |
download | libmtp-cbd399990f3f545635493bbe3583d385b165d2d7.tar.gz |
Probe around in sysfs in Linux, look if the device has three
endpoints, one bulk in, one bulk out, one interrupt in.
Unless it has this: do not proceed to open the device.
This should fix most lockups caused by libusb opening sensitive
devices to examine them: now we only do this if we really need
to take a closer look.
Signed-off-by: Linus Walleij <triad@df.lth.se>
Diffstat (limited to 'util')
-rw-r--r-- | util/mtp-probe.c | 240 |
1 files changed, 239 insertions, 1 deletions
diff --git a/util/mtp-probe.c b/util/mtp-probe.c index 3e52086..2bb8995 100644 --- a/util/mtp-probe.c +++ b/util/mtp-probe.c @@ -45,7 +45,238 @@ #include <stdio.h> #include <string.h> #include <syslog.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> #include <libmtp.h> +#include <regex.h> +#include <fcntl.h> + +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 main (int argc, char **argv) { @@ -66,7 +297,14 @@ int main (int argc, char **argv) syslog(LOG_INFO, "checking bus %d, device %d: \"%s\"\n", busno, devno, fname); - ret = LIBMTP_Check_Specific_Device(busno, devno); + ret = check_sysfs(fname); + /* + * This means that regular directory check either agrees that this may be a + * MTP device, or that it doesn't know (failed). In that case, kick the deeper + * check inside LIBMTP. + */ + if (ret != 0) + ret = LIBMTP_Check_Specific_Device(busno, devno); if (ret) { syslog(LOG_INFO, "bus: %d, device: %d was an MTP device\n", busno, devno); printf("1"); |