文章目录
🌈应用层获取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与我们要找寻的一致:
c
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设备对应的主设备号,次设备号,设备名称、设备类型
等相关信息,如下图:
c
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为我们需要的设备节点:
c
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设备的配置,端点,端口
等信息。
🌈应用层代码实例
c
#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
运行如下:
c
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地址也是由上面应用程序获取,这里直接写死对应数值以便测试:
c
#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;
}
上述代码实际测试结果如下:
c
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