am 4a280e3d: Merge "Don\'t use control requests to read device serial numbers."
* commit '4a280e3dbe002bb5419ec010f89cdc158244e435': Don't use control requests to read device serial numbers.
This commit is contained in:
commit
edeed28bd3
1 changed files with 103 additions and 78 deletions
|
|
@ -75,10 +75,18 @@ struct usb_handle
|
||||||
unsigned char ep_out;
|
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)
|
static inline int badname(const char *name)
|
||||||
{
|
{
|
||||||
while(*name) {
|
if (!isdigit(*name))
|
||||||
if(!isdigit(*name++)) return 1;
|
return 1;
|
||||||
|
while(*++name) {
|
||||||
|
if(!isdigit(*name) && *name != '.' && *name != '-')
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +103,8 @@ static int check(void *_desc, int len, unsigned type, int size)
|
||||||
return 0;
|
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,
|
ifc_match_func callback,
|
||||||
int *ept_in_id, int *ept_out_id, int *ifc_id)
|
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.dev_protocol = dev->bDeviceProtocol;
|
||||||
info.writable = writable;
|
info.writable = writable;
|
||||||
|
|
||||||
// read device serial number (if there is one)
|
snprintf(info.device_path, sizeof(info.device_path), "usb:%s", sysfs_name);
|
||||||
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));
|
/* Read device serial number (if there is one).
|
||||||
|
* We read the serial number from sysfs, since it's faster and more
|
||||||
ctrl.bRequestType = USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE;
|
* reliable than issuing a control pipe read, and also won't
|
||||||
ctrl.bRequest = USB_REQ_GET_DESCRIPTOR;
|
* cause problems for devices which don't like getting descriptor
|
||||||
ctrl.wValue = (USB_DT_STRING << 8) | dev->iSerialNumber;
|
* requests while they're in the middle of flashing.
|
||||||
//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).
|
|
||||||
*/
|
*/
|
||||||
info.device_path[0] = '\0';
|
info.serial_number[0] = '\0';
|
||||||
result = fstat(fd, &st);
|
if (dev->iSerialNumber) {
|
||||||
if (!result && S_ISCHR(st.st_mode)) {
|
char path[80];
|
||||||
char cdev[128];
|
int fd;
|
||||||
char link[256];
|
|
||||||
char *slash;
|
snprintf(path, sizeof(path),
|
||||||
ssize_t link_len;
|
"/sys/bus/usb/devices/%s/serial", sysfs_name);
|
||||||
snprintf(cdev, sizeof(cdev), "/sys/dev/char/%d:%d",
|
path[sizeof(path) - 1] = '\0';
|
||||||
major(st.st_rdev), minor(st.st_rdev));
|
|
||||||
link_len = readlink(cdev, link, sizeof(link) - 1);
|
fd = open(path, O_RDONLY);
|
||||||
if (link_len > 0) {
|
if (fd >= 0) {
|
||||||
link[link_len] = '\0';
|
int chars_read = read(fd, info.serial_number,
|
||||||
slash = strrchr(link, '/');
|
sizeof(info.serial_number) - 1);
|
||||||
if (slash)
|
close(fd);
|
||||||
snprintf(info.device_path, sizeof(info.device_path), "usb:%s", slash+1);
|
|
||||||
|
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;
|
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)
|
static usb_handle *find_usb_device(const char *base, ifc_match_func callback)
|
||||||
{
|
{
|
||||||
usb_handle *usb = 0;
|
usb_handle *usb = 0;
|
||||||
char busname[64], devname[64];
|
char devname[64];
|
||||||
char desc[1024];
|
char desc[1024];
|
||||||
int n, in, out, ifc;
|
int n, in, out, ifc;
|
||||||
|
|
||||||
DIR *busdir, *devdir;
|
DIR *busdir;
|
||||||
struct dirent *de;
|
struct dirent *de;
|
||||||
int fd;
|
int fd;
|
||||||
int writable;
|
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)) {
|
while((de = readdir(busdir)) && (usb == 0)) {
|
||||||
if(badname(de->d_name)) continue;
|
if(badname(de->d_name)) continue;
|
||||||
|
|
||||||
sprintf(busname, "%s/%s", base, de->d_name);
|
if(!convert_to_devfs_name(de->d_name, devname, sizeof(devname))) {
|
||||||
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);
|
|
||||||
|
|
||||||
// DBG("[ scanning %s ]\n", devname);
|
// DBG("[ scanning %s ]\n", devname);
|
||||||
writable = 1;
|
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));
|
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) {
|
&in, &out, &ifc) == 0) {
|
||||||
usb = calloc(1, sizeof(usb_handle));
|
usb = calloc(1, sizeof(usb_handle));
|
||||||
strcpy(usb->fname, devname);
|
strcpy(usb->fname, devname);
|
||||||
|
|
@ -301,7 +327,6 @@ static usb_handle *find_usb_device(const char *base, ifc_match_func callback)
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
closedir(devdir);
|
|
||||||
}
|
}
|
||||||
closedir(busdir);
|
closedir(busdir);
|
||||||
|
|
||||||
|
|
@ -431,5 +456,5 @@ int usb_close(usb_handle *h)
|
||||||
|
|
||||||
usb_handle *usb_open(ifc_match_func callback)
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue