UVC设备驱动之描述符分析
come from : https://blog.****.net/qingkongyeyue/article/details/76204405
1.一般的USB设备有设备描述符、配置描述符、接口描述符、端点描述符四种描述符。特定的USB设备还有它自己独特的描述符。
USB设备、配置、接口、端点与驱动之间的关系。
一个设备可能有多个配置,一个配置可以拥有多种接口(功能),每个接口(功能)对应一个USB驱动。
例如:
我们要为一个电视机增加一个USB接口,可以通过接入笔记本来播放笔记本中各种视频或者音乐。那么电视机就是一个USB设备。
若电视剧具备视频、音频两种功能,那么就可以使用两个接口来对应视频、音频功能。这两个接口使用两个USB驱动来编写,一个对应视频子系统、一个对应音频子系统。
对于端点,视频需要传输图像数据、分配率控制等可以通过两个端点来负责传输。音频需要传输音频、音量、音效等数据,可以通过三个端点来负责传输。
2.驱动程序中先判断id_tables数组,查看插入的这个USB设备是否可以用当前这个驱动程序来驱动。在UVC驱动中这个数组定义为:
static struct usb_device_id myuvc_ids[] = {
/* Generic USB Video Class */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, /* VideoControl Interface */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 2, 0) }, /* VideoStreaming Interface */
{}
};
构造一类设备的宏: USB_INTERFACE_INFO(cl, sc, pr)
cl: blnterfaceClass value 类
sc: blnterfaceSubClass value 子类
pr: blnterfaceProtocol value 所遵循的协议
参考USB规范UVC 1.5 Class specification.pdf
表示有两个接口,分别为VC和VS。如果符合数组中的一项则会调用驱动中的probe函数。因此加载驱动后插入USB命令可dmesg后发现probe中的打印信息打印了2遍,说明的确执行了2遍probe,有两个接口。
3.插入USB后用lsusb命令可得到设备信息。如ID 1e4e:0102 为设备ID。用lsusb -h查看帮助;用lsusb -v -d 1e4e来查看四个描述符的具体内容。之后参考lsusb命令的源码来分析如何获取这些描述符的具体内容。
-d用于指定厂家ID
4.在probe函数中定义struct usb_device *dev=interface_to_usbdev(intf);来获得设备结构体,
定义struct usb_device_descriptor *descriptor=&dev->descriptor;来获得设备描述符结构体。
而interface_to_usbdev(intf)之所以能直接通过接口来获得设备结构体和设备描述符结构体的信息是因为插上设备后USB总线驱动程序已经读取设备描述符信息并保存起来了。
5.配置描述符获得是通过设备描述符结构体的,通过for循环来获取:
Struct usb_host_config *hostconfig;
Struct usb_config_descriptor *config;
For(i=0;i<descriptor->bNumConfigurations;i++)
{
hostconfig=&dev->config[i];
Config=&hostconfig->desc;
}
//因为dev中的config是一个数组,其中config数组某一项的desc成员中存放着配置描述符的具体内容。
6.IAD接口联合体描述符中描述的是videocontrol interface接口和videostream interface接口的个数和序号等接口信息。使用方法: struct usb_interface_assoc_descriptor *assoc_desc;
assoc_desc=hostconfig->intf_assoc[0];//从配置描述符结构体中取出接口描述符数组的第一项。
打印后发现assoc_desc->bInterfaceCount=2;说明有2个接口,验证了videocontrol interface和videostream interface这两个接口。
7.至于接口描述符结构体,则可从probe函数传入的参数intf中获得。一个接口有多个设置,用intf->num_altsetting可以得到数目。Struct usb_interface_descriptor *interface;
For(j=0;j<intf->altsetting[j];j++)
{
Interface=intf->altsetting[j].desc;
}
//interface中存放的是具体的接口描述符信息。
打印interface中的信息就可以看见videocontrol interface中的所有设置和videostream interface 中的所有设置了。
8.以上这些描述符结构体都是UVC规范所要求的必须添加的。至于每个ioctl所调用的不同命令则有相对应得其他描述符,在上一个设置描述符中可得到:
Unsigned char *buf;
Buf=intf->cur_altsetting->extra;//都放在buf中。
Unsigned long buflen=intf->cur_altsetting->extralen;//长度
Unsigned long desc_len;//一个描述符的长度
Int k=0;
While(k<buflen)
{
Desc_len=buffer[k];
For(l=0;l<desc_len;l++)
{
Printf(“%02x”,buffer[k+l]);
}
}
//注:每个描述符的第一个数据都是这个描述符的长度。
最后我们能在videocontrol interface中的自定义描述符中找到UVC的控制操作以及数据的处理流程等详细信息。
9.这些自定义的描述符在buf中的存放先后位置是固定的第一个是head描述符。
10.端点描述符:struct usb_endpoint_desc *endpoint;
For(m=0;m<interface->bNumEndpoints;m++)
{
//interface由上面所得
Endpoint=intf->altsetting[m].endpoint[m].desc;
}//表示获得当前接口的第几个设置的第几个端点的描述符。