Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4a280e3d authored by jp abgrall's avatar jp abgrall Committed by Gerrit Code Review
Browse files

Merge "Don't use control requests to read device serial numbers."

parents 6ed51b8a bd446c76
Loading
Loading
Loading
Loading
+103 −78
Original line number Diff line number Diff line
@@ -75,10 +75,18 @@ struct usb_handle
    unsigned char ep_out;
};

/* True if name isn't a valid name for a USB device in /sys/bus/usb/devices.
 * Device names are made up of numbers, dots, and dashes, e.g., '7-1.5'.
 * We reject interfaces (e.g., '7-1.5:1.0') and host controllers (e.g. 'usb1').
 * The name must also start with a digit, to disallow '.' and '..'
 */
static inline int badname(const char *name)
{
    while(*name) {
        if(!isdigit(*name++)) return 1;
    if (!isdigit(*name))
      return 1;
    while(*++name) {
        if(!isdigit(*name) && *name != '.' && *name != '-')
            return 1;
    }
    return 0;
}
@@ -95,7 +103,8 @@ static int check(void *_desc, int len, unsigned type, int size)
    return 0;
}

static int filter_usb_device(int fd, char *ptr, int len, int writable,
static int filter_usb_device(int fd, char* sysfs_name,
                             char *ptr, int len, int writable,
                             ifc_match_func callback,
                             int *ept_in_id, int *ept_out_id, int *ifc_id)
{
@@ -131,69 +140,35 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable,
    info.dev_protocol = dev->bDeviceProtocol;
    info.writable = writable;

    // read device serial number (if there is one)
    info.serial_number[0] = 0;
    if (dev->iSerialNumber) {
        struct usbdevfs_ctrltransfer  ctrl;
        // Keep it short enough because some bootloaders are borked if the URB len is > 255
        // 128 is too big by 1.
        __u16 buffer[127];

        memset(buffer, 0, sizeof(buffer));

        ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
        ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
        ctrl.wValue = (USB_DT_STRING << 8) | dev->iSerialNumber;
        //language ID (en-us) for serial number string
        ctrl.wIndex = 0x0409;
        ctrl.wLength = sizeof(buffer);
        ctrl.data = buffer;
        ctrl.timeout = 50;

        result = ioctl(fd, USBDEVFS_CONTROL, &ctrl);
        if (result > 0) {
            int i;
            // skip first word, and copy the rest to the serial string, changing shorts to bytes.
            result /= 2;
            for (i = 1; i < result; i++)
                info.serial_number[i - 1] = buffer[i];
            info.serial_number[i - 1] = 0;
        }
    }

    /* We need to get a path that represents a particular port on a particular
     * hub.  We are passed an fd that was obtained by opening an entry under
     * /dev/bus/usb.  Unfortunately, the names of those entries change each
     * time devices are plugged and unplugged.  So how to get a repeatable
     * path?  udevadm provided the inspiration.  We can get the major and
     * minor of the device file, read the symlink that can be found here:
     *   /sys/dev/char/<major>:<minor>
     * and then use the last element of that path.  As a concrete example, I
     * have an Android device at /dev/bus/usb/001/027 so working with bash:
     *   $ ls -l /dev/bus/usb/001/027
     *   crw-rw-r-- 1 root plugdev 189, 26 Apr  9 11:03 /dev/bus/usb/001/027
     *   $ ls -l /sys/dev/char/189:26
     *   lrwxrwxrwx 1 root root 0 Apr  9 11:03 /sys/dev/char/189:26 ->
     *           ../../devices/pci0000:00/0000:00:1a.7/usb1/1-4/1-4.2/1-4.2.3
     * So our device_path would be 1-4.2.3 which says my device is connected
     * to port 3 of a hub on port 2 of a hub on port 4 of bus 1 (per
     * http://www.linux-usb.org/FAQ.html).
    snprintf(info.device_path, sizeof(info.device_path), "usb:%s", sysfs_name);

    /* Read device serial number (if there is one).
     * We read the serial number from sysfs, since it's faster and more
     * reliable than issuing a control pipe read, and also won't
     * cause problems for devices which don't like getting descriptor
     * requests while they're in the middle of flashing.
     */
    info.device_path[0] = '\0';
    result = fstat(fd, &st);
    if (!result && S_ISCHR(st.st_mode)) {
        char cdev[128];
        char link[256];
        char *slash;
        ssize_t link_len;
        snprintf(cdev, sizeof(cdev), "/sys/dev/char/%d:%d",
                 major(st.st_rdev), minor(st.st_rdev));
        link_len = readlink(cdev, link, sizeof(link) - 1);
        if (link_len > 0) {
            link[link_len] = '\0';
            slash = strrchr(link, '/');
            if (slash)
                snprintf(info.device_path, sizeof(info.device_path), "usb:%s", slash+1);
    info.serial_number[0] = '\0';
    if (dev->iSerialNumber) {
        char path[80];
        int fd;

        snprintf(path, sizeof(path),
                 "/sys/bus/usb/devices/%s/serial", sysfs_name);
        path[sizeof(path) - 1] = '\0';

        fd = open(path, O_RDONLY);
        if (fd >= 0) {
            int chars_read = read(fd, info.serial_number,
                                  sizeof(info.serial_number) - 1);
            close(fd);

            if (chars_read <= 0)
                info.serial_number[0] = '\0';
            else if (info.serial_number[chars_read - 1] == '\n') {
                // strip trailing newline
                info.serial_number[chars_read - 1] = '\0';
            }
        }
    }

@@ -241,14 +216,73 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable,
    return -1;
}

static int read_sysfs_string(const char *sysfs_name, const char *sysfs_node,
                             char* buf, int bufsize)
{
    char path[80];
    int fd, n;

    snprintf(path, sizeof(path),
             "/sys/bus/usb/devices/%s/%s", sysfs_name, sysfs_node);
    path[sizeof(path) - 1] = '\0';

    fd = open(path, O_RDONLY);
    if (fd < 0)
        return -1;

    n = read(fd, buf, bufsize - 1);
    close(fd);

    if (n < 0)
        return -1;

    buf[n] = '\0';

    return n;
}

static int read_sysfs_number(const char *sysfs_name, const char *sysfs_node)
{
    char buf[16];
    int value;

    if (read_sysfs_string(sysfs_name, sysfs_node, buf, sizeof(buf)) < 0)
        return -1;

    if (sscanf(buf, "%d", &value) != 1)
        return -1;

    return value;
}

/* Given the name of a USB device in sysfs, get the name for the same
 * device in devfs. Returns 0 for success, -1 for failure.
 */
static int convert_to_devfs_name(const char* sysfs_name,
                                 char* devname, int devname_size)
{
    int busnum, devnum;

    busnum = read_sysfs_number(sysfs_name, "busnum");
    if (busnum < 0)
        return -1;

    devnum = read_sysfs_number(sysfs_name, "devnum");
    if (devnum < 0)
        return -1;

    snprintf(devname, devname_size, "/dev/bus/usb/%03d/%03d", busnum, devnum);
    return 0;
}

static usb_handle *find_usb_device(const char *base, ifc_match_func callback)
{
    usb_handle *usb = 0;
    char busname[64], devname[64];
    char devname[64];
    char desc[1024];
    int n, in, out, ifc;

    DIR *busdir, *devdir;
    DIR *busdir;
    struct dirent *de;
    int fd;
    int writable;
@@ -259,15 +293,7 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback)
    while((de = readdir(busdir)) && (usb == 0)) {
        if(badname(de->d_name)) continue;

        sprintf(busname, "%s/%s", base, de->d_name);
        devdir = opendir(busname);
        if(devdir == 0) continue;

//        DBG("[ scanning %s ]\n", busname);
        while((de = readdir(devdir)) && (usb == 0)) {

            if(badname(de->d_name)) continue;
            sprintf(devname, "%s/%s", busname, de->d_name);
        if(!convert_to_devfs_name(de->d_name, devname, sizeof(devname))) {

//            DBG("[ scanning %s ]\n", devname);
            writable = 1;
@@ -282,7 +308,7 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback)

            n = read(fd, desc, sizeof(desc));

            if(filter_usb_device(fd, desc, n, writable, callback,
            if(filter_usb_device(fd, de->d_name, desc, n, writable, callback,
                                 &in, &out, &ifc) == 0) {
                usb = calloc(1, sizeof(usb_handle));
                strcpy(usb->fname, devname);
@@ -301,7 +327,6 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback)
                close(fd);
            }
        }
        closedir(devdir);
    }
    closedir(busdir);

@@ -431,5 +456,5 @@ int usb_close(usb_handle *h)

usb_handle *usb_open(ifc_match_func callback)
{
    return find_usb_device("/dev/bus/usb", callback);
    return find_usb_device("/sys/bus/usb/devices", callback);
}