本文分析的是linux-5.4.3
一.Gadget Audio设备驱动分析
drivers/usb/gadget/legacy/audio.c
因为项目的问题,了解usb音频设备的工作原理,为啥它能让PC识别成“speak”或者“mic”,以及你能够播放录音。
主要涉及下面两个层次:
Gadget功能驱动层: 最主要的结构是struct usb_composite_driver,这个结构在这层定义,并且实现结构中的各个函数。
USB设备层: 最主要的数据结构是struct usb_composite_dev与usb_gadget_driver。前一个代表一个USB复合设备,而后一个是Gadget驱动,与UDC层交互。
这边主要是usb结构中的设备和配置相关
首先我们来看初始化流程,主要是注册audio_driver这个复合设备驱动
static struct usb_composite_driver audio_driver = {
.name = "g_audio",
.dev = &device_desc, //这是一个设备描述符
.strings = audio_strings, //一个给定语言的字符串集合,字符串描述符
.max_speed = USB_SPEED_HIGH, //设备速度
.bind = audio_bind, //注册绑定回调函数
.unbind = audio_unbind,
};
/* 设置#define module_usb_composite_driver(__usb_composite_driver) \
module_driver(__usb_composite_driver, usb_composite_probe, \
usb_composite_unregister) */
//module_usb_composite_driver是一个宏定义用usb_composite_probe注册复合驱动程序
module_usb_composite_driver(audio_driver);
我们再来看看usb_composite_probe里面具体做了什么,主要是赋值了composite_driver_template,这个是所有复合设备共有的
static const struct usb_gadget_driver composite_driver_template = {
.bind= composite_bind,
.unbind= composite_unbind,
.setup= composite_setup,
.reset= composite_disconnect,
.disconnect= composite_disconnect,
.suspend= composite_suspend,
.resume= composite_resume,
.driver= {
.owner= THIS_MODULE,
},
};
int usb_composite_probe(struct usb_composite_driver *driver) //注册复合设备
{
struct usb_gadget_driver *gadget_driver;
if (!driver || !driver->dev || !driver->bind)
return -EINVAL;
if (!driver->name)
driver->name = "composite";
driver->gadget_driver = composite_driver_template; //赋值一个usb_gadget_driver实例,应该只有一个
gadget_driver = &driver->gadget_driver;
gadget_driver->function = (char *) driver->name;
gadget_driver->driver.name = driver->name;
gadget_driver->max_speed = driver->max_speed;
return usb_gadget_probe_driver(gadget_driver); //继续调用
}
后面继续调用usb_gadget_probe_driver,进行注册。
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
{
mutex_lock(&udc_lock);
if (driver->udc_name) { //如果有udc的名字,就直接遍历比较
list_for_each_entry(udc, &udc_list, list) {
ret = strcmp(driver->udc_name, dev_name(&udc->dev));
if (!ret)
break;
}
if (ret)
ret = -ENODEV;
else if (udc->driver) //已经有匹配的driver,正在忙
ret &#