文章目录
- 🌈应用层获取USB设备信息总体思路
- 🌈应用层代码实例
- 🌈实例测试
- 🌈应用层通过endpoint进行数据读写
🌈应用层获取USB设备信息总体思路
应用层可以打开USB设备的节点,读取包括USB设备的配置,端口,端点等信息。具体来说对于USB设备均存在对应的VID PID
,其中VID指的是Vendor ID(厂商识别码),用于唯一标识USB设备的制造商。PID指的是Product ID(产品识别码),用于唯一标识USB设备的产品型号。通过VID和PID,可以确定一个具体的USB设备,因为每个设备的VID和PID是唯一的,可以帮助系统识别和与设备进行交互。
如果上位机正常识别USB设备,我们可以通过对应节点信息/sys/bus/usb/devices
按照文件目录去搜索,如果目录中idVendor和idProduct
与我们要搜索的一致,可以判定为我们需要找的设备节点。如下图1-4目录中的idVendor和idProduct与我们要找寻的一致:
root@Vostro:/sys/bus/usb/devices/1-4# ls -al
total 0
drwxr-xr-x 12 root root 0 Aug 31 20:55 .
drwxr-xr-x 8 root root 0 Aug 10 11:55 ..
drwxr-xr-x 6 root root 0 Aug 31 20:55 1-4:1.0
drwxr-xr-x 6 root root 0 Aug 31 20:55 1-4:1.1
drwxr-xr-x 7 root root 0 Aug 31 20:55 1-4:1.2
drwxr-xr-x 7 root root 0 Aug 31 20:55 1-4:1.3
drwxr-xr-x 7 root root 0 Aug 31 20:55 1-4:1.4
drwxr-xr-x 7 root root 0 Aug 31 20:55 1-4:1.5
drwxr-xr-x 7 root root 0 Aug 31 20:55 1-4:1.6
-rw-r--r-- 1 root root 4096 Aug 31 20:55 authorized
-rw-r--r-- 1 root root 4096 Aug 31 20:55 avoid_reset_quirk
-r--r--r-- 1 root root 4096 Aug 31 20:55 bcdDevice
-rw-r--r-- 1 root root 4096 Aug 31 20:55 bConfigurationValue
-r--r--r-- 1 root root 4096 Aug 31 20:55 bDeviceClass
-r--r--r-- 1 root root 4096 Aug 31 20:55 bDeviceProtocol
-r--r--r-- 1 root root 4096 Aug 31 20:55 bDeviceSubClass
-r--r--r-- 1 root root 4096 Aug 31 20:55 bmAttributes
-r--r--r-- 1 root root 4096 Aug 31 20:55 bMaxPacketSize0
-r--r--r-- 1 root root 4096 Aug 31 20:55 bMaxPower
-r--r--r-- 1 root root 4096 Aug 31 20:55 bNumConfigurations
-r--r--r-- 1 root root 4096 Aug 31 20:55 bNumInterfaces
-r--r--r-- 1 root root 4096 Aug 31 20:55 busnum
-r--r--r-- 1 root root 4096 Aug 31 20:55 configuration
-r--r--r-- 1 root root 65553 Aug 31 20:55 descriptors
-r--r--r-- 1 root root 4096 Aug 31 20:55 dev
-r--r--r-- 1 root root 4096 Aug 31 20:55 devnum
-r--r--r-- 1 root root 4096 Aug 31 20:55 devpath
lrwxrwxrwx 1 root root 0 Aug 31 20:55 driver -> ../../../../../bus/usb/drivers/usb
drwxr-xr-x 3 root root 0 Aug 31 20:55 ep_00
lrwxrwxrwx 1 root root 0 Aug 31 20:55 firmware_node -> ../../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:4c/device:4d/device:51
-r--r--r-- 1 root root 4096 Aug 31 20:55 idProduct
-r--r--r-- 1 root root 4096 Aug 31 20:55 idVendor
-r--r--r-- 1 root root 4096 Aug 31 20:55 ltm_capable
-r--r--r-- 1 root root 4096 Aug 31 20:55 manufacturer
-r--r--r-- 1 root root 4096 Aug 31 20:55 maxchild
drwxr-xr-x 2 root root 0 Aug 31 20:55 physical_location
lrwxrwxrwx 1 root root 0 Aug 31 20:55 port -> ../1-0:1.0/usb1-port4
drwxr-xr-x 2 root root 0 Aug 31 20:55 power
-r--r--r-- 1 root root 4096 Aug 31 20:55 product
-r--r--r-- 1 root root 4096 Aug 31 20:55 quirks
-r--r--r-- 1 root root 4096 Aug 31 20:55 removable
--w------- 1 root root 4096 Aug 31 20:55 remove
-r--r--r-- 1 root root 4096 Aug 31 20:55 rx_lanes
-r--r--r-- 1 root root 4096 Aug 31 20:55 serial
-r--r--r-- 1 root root 4096 Aug 31 20:55 speed
lrwxrwxrwx 1 root root 0 Aug 31 20:55 subsystem -> ../../../../../bus/usb
-r--r--r-- 1 root root 4096 Aug 31 20:55 tx_lanes
-rw-r--r-- 1 root root 4096 Aug 31 20:55 uevent
-r--r--r-- 1 root root 4096 Aug 31 20:55 urbnum
-r--r--r-- 1 root root 4096 Aug 31 20:55 version
在找到对应目录后,读取该目录下面的uevent节点
,会获取到该USB设备对应的主设备号,次设备号,设备名称、设备类型
等相关信息,如下图:
root@Vostro:/sys/bus/usb/devices/1-4# cat uevent
MAJOR=189
MINOR=115
DEVNAME=bus/usb/001/116
DEVTYPE=usb_device
DRIVER=usb
PRODUCT=2cb7/1/100
TYPE=0/0/0
BUSNUM=001
DEVNUM=116
这里我们需要获取的是DEVNAME=bus/usb/001/116
这个信息,该信息表示在/dev设备目录下/dev/bus/usb/001/116
为该USB设备的节点,我们后续读取USB设备信息以及数据读写操作都是通过这个节点进行的,如下图我们可以看到/dev/bus/usb/001/
目录下有多个USB设备,其中116为我们需要的设备节点:
root@Vostro:/sys/bus/usb/devices/1-4# cd /dev/bus/usb/001/
root@Vostro:/dev/bus/usb/001# ls -al
total 0
drwxr-xr-x 2 root root 120 Sep 1 17:59 .
drwxr-xr-x 4 root root 80 Aug 10 11:55 ..
crw-rw-rw- 1 root root 189, 0 Aug 10 11:56 001
crw-rw-rw- 1 root root 189, 2 Aug 10 11:56 003
crw-rw-rw- 1 root root 189, 3 Aug 10 11:56 004
crw-rw-rw- 1 root root 189, 115 Aug 31 20:55 116
找到该节点后,通过应用层的open函数
打开设备,通过read函数
读取并解析设备的相关信息,即可获得USB设备的配置,端点,端口
等信息。
🌈应用层代码实例
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <linux/usbdevice_fs.h>
#include <linux/usb/ch9.h>#define BUF_SIZE 512
#define MAX_PATH_LEN 512
#define USB_DIR_BASE "/sys/bus/usb/devices"typedef struct
{int idVendor;int idProduct;int InterfaceNum;int usbdev;char portname[BUF_SIZE];char syspath[BUF_SIZE];char busname[BUF_SIZE];int bulk_ep_in;int bulk_ep_out;int wMaxPacketSize;int usb_need_zero_package;int (* write)(const void *handle, void *pbuf, int size, int portnum);int (* read)(const void *handle, void *pbuf, int size, int portnum);
} s_usbdev_t;s_usbdev_t udev;int strStartsWith(const char *str, const char *match_str)
{for ( ; *str != '\0' && *match_str != '\0'; str++, match_str++) {if (*str != *match_str) {return 0;}}return *match_str == '\0';
}static int get_usbsys_val(const char *sys_filename, int base)
{char buff[64] = {0};int ret_val = -1;int fd = open(sys_filename, O_RDONLY);if (fd < 0) {return -1;}if (read(fd, buff, sizeof(buff)) <= 0) {printf("read:%s failed\n", sys_filename);}else {ret_val = strtoul(buff, NULL, base);}close(fd);return ret_val;
}static int get_busname_by_uevent(const char *uevent, char *busname)
{FILE *fp = NULL;char line[BUF_SIZE] = {0};int MAJOR = 0, MINOR = 0;char DEVTYPE[64] = {0}, PRODUCT[64] = {0};fp = fopen(uevent, "r");if (fp == NULL) {printf("fopen %s failed, errno:%d(%s)\n", uevent, errno, strerror(errno));return -1;}while (fgets(line, sizeof(line), fp)){if (line[strlen(line) - 1] == '\n' || line[strlen(line) - 1] == '\r') {line[strlen(line) - 1] = 0;}if (strStartsWith(line, "MAJOR=")){MAJOR = atoi(&line[strlen("MAJOR=")]);}else if (strStartsWith(line, "MINOR=")){MINOR = atoi(&line[strlen("MINOR=")]);}else if (strStartsWith(line, "DEVICE=")){strncpy(busname, &line[strlen("DEVICE=")], MAX_PATH_LEN);}else if (strStartsWith(line, "DEVNAME=")){strncpy(busname, &line[strlen("DEVNAME=")], MAX_PATH_LEN);}else if (strStartsWith(line, "DEVTYPE=")){strncpy(DEVTYPE, &line[strlen("DEVTYPE=")], sizeof(DEVTYPE));}else if (strStartsWith(line, "PRODUCT=")){strncpy(PRODUCT, &line[strlen("PRODUCT=")], sizeof(PRODUCT));}}fclose(fp);if (MAJOR != 189 || MINOR == 0 || busname[0] == 0|| DEVTYPE[0] == 0 || PRODUCT[0] == 0|| strStartsWith(DEVTYPE, "usb_device") == 0) {return -1;}return 0;
}int usb_dev_open(int usb_vid, int usb_pid)
{char devdesc[MAX_PATH_LEN*2] = {0};char devname[MAX_PATH_LEN+128] = {0};size_t desc_length = 0, len = 0;int bInterfaceNumber = 0;DIR *usbdir = NULL;struct dirent *dent = NULL;int idVendor = 0, idProduct = 0;int bNumInterfaces = 0, bConfigurationValue = 0;char sys_filename[MAX_PATH_LEN] = {0};usbdir = opendir("/sys/bus/usb/devices");if (usbdir == NULL) {return -1;}while ((dent = readdir(usbdir)) != NULL){if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0) {continue;}snprintf(sys_filename, sizeof(sys_filename), "%s/%s/idVendor", USB_DIR_BASE, dent->d_name);if ((idVendor = get_usbsys_val(sys_filename, 16)) <= 0) {continue;}snprintf(sys_filename, sizeof(sys_filename), "%s/%s/idProduct", USB_DIR_BASE, dent->d_name);if ((idProduct = get_usbsys_val(sys_filename, 16)) <= 0) {continue;}snprintf(sys_filename, sizeof(sys_filename), "%s/%s/bConfigurationValue", USB_DIR_BASE, dent->d_name);if ((bConfigurationValue = get_usbsys_val(sys_filename, 10)) <= 0) {continue;}snprintf(sys_filename, sizeof(sys_filename), "%s/%s/bNumInterfaces", USB_DIR_BASE, dent->d_name);if ((bNumInterfaces = get_usbsys_val(sys_filename, 10)) <= 0) {continue;}if((idVendor == usb_vid)&&(idProduct == usb_pid)){udev.idVendor = idVendor;udev.idProduct = idProduct;printf("----------------------------------\n");printf("idVendor: %04x\n", udev.idVendor);printf("idProduct: %04x\n", udev.idProduct);printf("bNumInterfaces: %d\n", bNumInterfaces);printf("bConfigurationValue: %d\n", bConfigurationValue);snprintf(sys_filename, sizeof(sys_filename), "%s/%s/uevent", USB_DIR_BASE, dent->d_name);get_busname_by_uevent(sys_filename, udev.busname);printf("busname: %s\n", udev.busname);printf("----------------------------------\n");break;}usleep(10000);}if (usbdir) {closedir(usbdir);usbdir = NULL;}snprintf(devname, sizeof(devname), "/dev/%s", udev.busname);if (access(devname, F_OK | R_OK| W_OK)) {printf("access %s failed, errno:%d(%s)\n", devname, errno, strerror(errno));return -1;}udev.usbdev = open(devname, O_RDWR | O_NOCTTY);if (udev.usbdev < 0) {printf("open %s failed, errno:%d(%s)\n", devname, errno, strerror(errno));return -1;}printf("[%s] OK.\n", devname);desc_length = read(udev.usbdev, devdesc, sizeof(devdesc));printf("desc_length is %zu,read length is %zu\r\n", sizeof(devdesc), desc_length);for (len=0; len<desc_length;){struct usb_descriptor_header *h = (struct usb_descriptor_header *)(devdesc + len);if (h->bLength == sizeof(struct usb_device_descriptor) && h->bDescriptorType == USB_DT_DEVICE){struct usb_device_descriptor *device = (struct usb_device_descriptor *)h;printf("P: idVendor: %04x idProduct:%04x\n", device->idVendor, device->idProduct);}else if (h->bLength == sizeof(struct usb_config_descriptor) && h->bDescriptorType == USB_DT_CONFIG){struct usb_config_descriptor *config = (struct usb_config_descriptor *)h;printf("C: bNumInterfaces: %d\n", config->bNumInterfaces);}else if (h->bLength == sizeof(struct usb_interface_descriptor) && h->bDescriptorType == USB_DT_INTERFACE){struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)h;printf("I: If#= %d Alt= %d #EPs= %d Cls=%02x Sub=%02x Prot=%02x\n",interface->bInterfaceNumber, interface->bAlternateSetting, interface->bNumEndpoints,interface->bInterfaceClass, interface->bInterfaceSubClass, interface->bInterfaceProtocol);bInterfaceNumber = interface->bInterfaceNumber;}else if (h->bLength == USB_DT_ENDPOINT_SIZE && h->bDescriptorType == USB_DT_ENDPOINT){struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)h;if ( (endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK){if (endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {udev.bulk_ep_in = endpoint->bEndpointAddress;printf("bulk_ep_in:0x%02X\n", udev.bulk_ep_in);} else {udev.bulk_ep_out = endpoint->bEndpointAddress;printf("bulk_ep_out:0x%02X\n", udev.bulk_ep_out);}udev.wMaxPacketSize = endpoint->wMaxPacketSize;printf("wMaxPacketSize:%d\n", endpoint->wMaxPacketSize);}}len += h->bLength;}return 0;
}int main(int argc, char * * argv)
{int vid_hex,pid_hex;if(argc !=3 ){printf("Usage: usb_read_device_info USB_VID USB_PID \r\n");return -1;}sscanf(argv[1], "%x", &vid_hex);sscanf(argv[2], "%x", &pid_hex);printf("USB VID %4x, PID is %4x\r\n", vid_hex, pid_hex);usb_dev_open(vid_hex,pid_hex);
}
🌈实例测试
插入一个VID PID为2cb7 0001的USB设备,运行上面的测试程序(编译过程省略),main函数入口参数传入VID 2CB7及 PID 0001
运行如下:
book@Vostro:~/Joy/Test$ sudo ./USB_read_device_info 2cb7 0001
USB VID 2cb7, PID is 1
----------------------------------
idVendor: 2cb7
idProduct: 0001
bNumInterfaces: 7
bConfigurationValue: 1
busname: bus/usb/001/116
----------------------------------
[/dev/bus/usb/001/116] OK.
desc_length is 1024,read length is 221
P: idVendor: 2cb7 idProduct:0001
C: bNumInterfaces: 7
I: If#= 0 Alt= 0 #EPs= 1 Cls=02 Sub=06 Prot=00
I: If#= 1 Alt= 0 #EPs= 0 Cls=0a Sub=00 Prot=00
I: If#= 1 Alt= 1 #EPs= 2 Cls=0a Sub=00 Prot=00
bulk_ep_in:0x81
wMaxPacketSize:512
bulk_ep_out:0x01
wMaxPacketSize:512
I: If#= 2 Alt= 0 #EPs= 2 Cls=ff Sub=ff Prot=ff
bulk_ep_in:0x82
wMaxPacketSize:512
bulk_ep_out:0x02
wMaxPacketSize:512
I: If#= 3 Alt= 0 #EPs= 2 Cls=ff Sub=ff Prot=ff
bulk_ep_in:0x83
wMaxPacketSize:512
bulk_ep_out:0x03
wMaxPacketSize:512
I: If#= 4 Alt= 0 #EPs= 2 Cls=ff Sub=ff Prot=ff
bulk_ep_in:0x84
wMaxPacketSize:512
bulk_ep_out:0x04
wMaxPacketSize:512
I: If#= 5 Alt= 0 #EPs= 2 Cls=ff Sub=ff Prot=ff
bulk_ep_in:0x85
wMaxPacketSize:512
bulk_ep_out:0x05
wMaxPacketSize:512
I: If#= 6 Alt= 0 #EPs= 2 Cls=ff Sub=42 Prot=01
bulk_ep_in:0x86
wMaxPacketSize:512
bulk_ep_out:0x06
wMaxPacketSize:512
可以看到通过解析read函数读取的相关信息,打印出了该USB设备的configuration、interface、endpoint
等信息。
🌈应用层通过endpoint进行数据读写
使用USB的endpoint
进行数据读写,需要了解每个endpoint的地址,并且endpoint为单向的
,对于USB设备每interface
下面可以有多个endpoint
,这些endpoint可以是输入,也可以是输出,下面实例的设备节点USB_DEV_PATH
是由上面应用程序获取,通讯具体的endpoint地址也是由上面应用程序获取,这里直接写死对应数值以便测试:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/usbdevice_fs.h>// USB设备节点路径,可通过上部分应用层实例代码获取某VID PID 对应的usb设备节点
#define USB_DEV_PATH "/dev/bus/usb/001/018" int usbfs_bulk_write(int fd, char *data, size_t dataLen)
{int ret;struct usbdevfs_urb urb_write;struct usbdevfs_urb *urb = NULL;memset(&urb_write, 0, sizeof(urb_write));urb_write.type = USBDEVFS_URB_TYPE_BULK;urb_write.endpoint = 0x05;urb_write.status = -1;urb_write.buffer = (void *)data;urb_write.buffer_length = dataLen;urb_write.usercontext = &urb_write;printf("endpoint is %d, buffer_length is %d\r\n", urb_write.endpoint, urb_write.buffer_length);do {ret = ioctl(fd, USBDEVFS_SUBMITURB, &urb_write);} while ((ret < 0) && (errno == EINTR));if (ret != 0) {printf("USBDEVFS_SUBMITURB failed, ret: %d, errno:%d(%s)\n", ret, errno, strerror(errno));return -1;}do {urb = NULL;ret = ioctl(fd, USBDEVFS_REAPURB, &urb);} while ((ret < 0) && (errno == EINTR));if (ret == 0 && urb && urb->status == 0 && urb->actual_length) {return urb->actual_length;}return -1;
}int usbfs_bulk_read(int fd, char *data, size_t dataLen)
{int ret = -1;struct usbdevfs_bulktransfer bulk;bulk.ep = 0x85;bulk.len = dataLen;bulk.data = data;bulk.timeout = 3000;do {ret = ioctl(fd, USBDEVFS_BULK, &bulk);} while ((ret < 0) && (errno == EINTR));return ret;
}int main()
{// 准备要写入的数据unsigned char data[] = {0x01,0xaa,0xaa,0xaa,0x01,0x55,0x73,0x01,0x14,0x00,0x00,0x00,0x06,0x67,0xbb,0xbb,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x44,0x09,0x7e};size_t dataLen = sizeof(data);int ret;int fd = open(USB_DEV_PATH, O_RDWR | O_NOCTTY); // 打开USB设备文件描述符if (fd == -1) {perror("Failed to open USB device");return -1;}ret = usbfs_bulk_write(fd, data, dataLen);printf("usbfs_bulk_write ret is %d\r\n", ret);ret = usbfs_bulk_read(fd, data, dataLen);printf("usbfs_bulk_read ret is %d\r\n", ret);return 0;
}
上述代码实际测试结果如下:
root:/home/Joy/Test# ./usb_bulk_write_read
endpoint is 5, buffer_length is 35
usbfs_bulk_write ret is 35
usbfs_bulk_read ret is 10