Linux USB 3.0驱动分析(十)——Gadget UAC2驱动分析

本文分析的是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 = -EBUSY;
        else
            goto found;
    } else {
        list_for_each_entry(udc, &udc_list, list) { //找到第一个udc
            /* For now we take the first one */
            if (!udc->driver) //没有绑定驱动,就说明找到了
                goto found;
        }
    }

    if (!driver->match_existing_only) { //加入到等待链表,udc注册会匹配这个等待的驱动
        list_add_tail(&driver->pending, &gadget_driver_pending_list);
        pr_info("udc-core: couldn't find an available UDC - added [%s] to list of pending drivers\n",
            driver->function);
        ret = 0;
    }
    mutex_unlock(&udc_lock);
    return ret;
found:
    ret = udc_bind_to_driver(udc, driver); //如果找到了,进行绑定
    mutex_unlock(&udc_lock);
    return ret;
}
从上面usb_gadget_probe_driver()函数的代码片段可知,它在遍历udc_list链表,找到一个udc(usb设备控制器驱动描述结构体)没有对应驱动的实例。
static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
{
    dev_dbg(&udc->dev, "registering UDC driver [%s]\n",
            driver->function);
    udc->driver = driver; //这里是usb_gadget_driver实例composite_driver_template
    udc->dev.driver = &driver->driver; //这里是usb_composite_driver实例audio_driver
    udc->gadget->dev.driver = &driver->driver;
    usb_gadget_udc_set_speed(udc, driver->max_speed);
    ret = driver->bind(udc->gadget, driver); //这里是调用composite_driver_template的composite_bind
    if (ret)
        goto err1;
    ret = usb_gadget_udc_start(udc); //这里会调用udc->gadget->ops->udc_start(udc->gadget, udc->driver),上一篇有分析
    if (ret) {
        driver->unbind(udc->gadget);
        goto err1;
    }
    //软件连接UDC。 usb_udc_connect_control -> usb_gadget_connect -> (gadget->ops->pullup)这里是调用dwc3_gadget_pullup
    usb_udc_connect_control(udc); //主要是是能D+或者D-的上拉,让host可以检测到

    kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); //通过发送uevent通知用户空间
    return 0;
}
主要是给udc相关的驱动数据结构赋值。既然有udc队列的遍历取出,必然有udc队列的添加,每注册一个udc驱动(usb_add_gadget_udc())就会把udc结构体添加到udc队列的尾部,上一篇udc相关分析有用到。
我们再来分析composite_driver_template的composite_bind函数,这里对复合设备做一些前期准备,然后调用复合设备的bind函数
 
static int composite_bind(struct usb_gadget *gadget,
        struct usb_gadget_driver *gdriver)
{
    cdev = kzalloc(sizeof *cdev, GFP_KERNEL); //分配usb_composite_dev结构体,表示一个复合usb gadget
	if (!cdev)
		return status;
    status = composite_dev_prepare(composite, cdev); //对复合设备的前期准备
    if (status)
        goto fail;
    /* composite gadget needs to assign strings for whole device (like
     * serial number), register function drivers, potentially update
     * power state and consumption, etc
     */
    status = composite->bind(cdev); //调用复合设备的bind函数,这里是audio_bind
    if (status < 0)
        goto fail;
    if (cdev->use_os_string) { //用来获取系统的描述符
        status = composite_os_desc_req_prepare(cdev, gadget->ep0);
        if (status)
            goto fail;
    }
    update_unchanged_dev_desc(&cdev->desc, composite->dev); //更新设备描述符
    /* has userspace failed to provide a serial number? */
    if (composite->needs_serial && !cdev->desc.iSerialNumber)
        WARNING(cdev, "userspace failed to provide iSerialNumber\n");

    INFO(cdev, "%s ready\n", composite->name);
    return 0;
}
对复合设备的前期准备composite_dev_prepare
主要是初始化端点0, ep0是设备控制传输用到的端点,尤其在设备枚举、属性配置上起关键作用,所以我们要首先初始化
int composite_dev_prepare(struct usb_composite_driver *composite,
        struct usb_composite_dev *cdev)
{
    /* preallocate control response and buffer */
    //调用ep->ops->alloc_request(ep, gfp_flags),也就是dwc3_gadget_ep_alloc_request,分配与此端点一起使用的请求对象
    cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); 
    if (!cdev->req)
        return -ENOMEM;

    cdev->req->buf = kmalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL); //分配内存
    if (!cdev->req->buf)
        goto fail;

    ret = device_create_file(&gadget->dev, &dev_attr_suspended);
    if (ret)
        goto fail_dev;
    cdev->req->complete = composite_setup_complete; //完成函数,在端点处理完一个usb request的时候调用
    cdev->req->context = cdev;
    gadget->ep0->driver_data = cdev;
    cdev->driver = composite;
    /*
     * As per USB compliance update, a device that is actively drawing
     * more than 100mA from USB must report itself as bus-powered in
     * the GetStatus(DEVICE) call.
     */
    if (CONFIG_USB_GADGET_VBUS_DRAW <= USB_SELF_POWER_VBUS_MAX_DRAW)
        usb_gadget_set_selfpowered(gadget);
    /* interface and string IDs start at zero via kzalloc.
     * we force endpoints to start unassigned; few controller
     * drivers will zero ep->driver_data.
     */
    usb_ep_autoconfig_reset(gadget);
    return 0;
}
复合设备的bind函数,这里是audio_bind
这里设备描述符相关在这里被初始化
static int audio_bind(struct usb_composite_dev *cdev)
{
#ifndef CONFIG_GADGET_UAC1
	fi_uac2 = usb_get_function_instance("uac2"); //默认是UAC2,搜索功能function实例,在f_hid中初始化,初始化接口位于usb/gadget/functions.c
	if (IS_ERR(fi_uac2))
		return PTR_ERR(fi_uac2);
#else
#ifndef CONFIG_GADGET_UAC1_LEGACY
	fi_uac1 = usb_get_function_instance("uac1");
#else
	fi_uac1 = usb_get_function_instance("uac1_legacy");
#endif
	if (IS_ERR(fi_uac1))
		return PTR_ERR(fi_uac1);
#endif
    
    status = usb_string_ids_tab(cdev, strings_dev); //批量分配未使用的字符串id
    if (status < 0)
        goto fail;
    device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; //这里设备描述符相关
    device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;

    if (gadget_is_otg(cdev->gadget) && !otg_desc[0]) { //如果是otg
        struct usb_descriptor_header *usb_desc;
        usb_desc = usb_otg_descriptor_alloc(cdev->gadget); //分配otg描述符
        if (!usb_desc)
            goto fail;
        usb_otg_descriptor_init(cdev->gadget, usb_desc); //初始化otg描述符,有三种协议:HNP,SRP,ADP
        otg_desc[0] = usb_desc;
        otg_desc[1] = NULL;
    }

    status = usb_add_config(cdev, &audio_config_driver, audio_do_config); //为复合层绑定,设备添加配置。
    if (status < 0)
        goto fail_otg_desc;
    usb_composite_overwrite_options(cdev, &coverwrite); //重新赋值设备描述符
    INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION);
    return 0;
}
在复合层绑定,进行配置的初始化,配置完成之后,这个ugadget就具有UAC的功能了。
int usb_add_config(struct usb_composite_dev *cdev,
        struct usb_configuration *config,
        int (*bind)(struct usb_configuration *))
{
    DBG(cdev, "adding config #%u '%s'/%p\n",
            config->bConfigurationValue,
            config->label, config);

    status = usb_add_config_only(cdev, config); //把config交给cdev管理
    if (status)
        goto done;

    status = bind(config);  //用到出入的函数指针,实际执行do_config, 往config中添加function
    if (status < 0) { //绑定config和function失败
        while (!list_empty(&config->functions)) {
            struct usb_function        *f;
            f = list_first_entry(&config->functions,
                    struct usb_function, list);
            list_del(&f->list);
            if (f->unbind) {
                DBG(cdev, "unbind function '%s'/%p\n",
                    f->name, f);
                f->unbind(config, f);
                /* may free memory for "f" */
            }
        }
        list_del(&config->list);
        config->cdev = NULL;
    } else { //绑定成功,后面都是调试打印
        unsigned    i;
        DBG(cdev, "cfg %d/%p speeds:%s%s%s%s\n",
            config->bConfigurationValue, config,
            config->superspeed_plus ? " superplus" : "",
            config->superspeed ? " super" : "",
            config->highspeed ? " high" : "",
            config->fullspeed
                ? (gadget_is_dualspeed(cdev->gadget)
                    ? " full"
                    : " full/low")
                : "");
        for (i = 0; i < MAX_CONFIG_INTERFACES; i++) { //这里这是用来打印调试
            struct usb_function    *f = config->interface[i];
            if (!f)
                continue;
            DBG(cdev, "  interface %d = %s/%p\n",
                i, f->name, f);
        }
    }
    /* set_alt(), or next bind(), sets up ep->claimed as needed */
    usb_ep_autoconfig_reset(cdev->gadget);
}
我们来看看关键的do_config函数
 
static int audio_do_config(struct usb_configuration *c)
{
    /* FIXME alloc iConfiguration string, set it in c->strings */
    if (gadget_is_otg(c->cdev->gadget)) { //如果是otg
        c->descriptors = otg_desc; //就赋值otg设置
        c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
    }
#ifdef CONFIG_GADGET_UAC1
    f_uac1 = usb_get_function(fi_uac1); //获取function, fi->fd->alloc_func(fi),也就是afunc_alloc
    if (IS_ERR(f_uac1)) {
        status = PTR_ERR(f_uac1);
        return status;
    }
    status = usb_add_function(c, f_uac1); ///向config中添加function
    if (status < 0) {
        usb_put_function(f_uac1);
        return status;
    }
#else
    f_uac2 = usb_get_function(fi_uac2);
    if (IS_ERR(f_uac2)) {
        status = PTR_ERR(f_uac2);
        return status;
    }
    status = usb_add_function(c, f_uac2);
    if (status < 0) {
        usb_put_function(f_uac2);
        return status;
    }
#endif
    return 0;
}
通过usb_add_dunction完成config和function的绑定
int usb_add_function(struct usb_configuration *config,
        struct usb_function *function)
{
    function->config = config;
    list_add_tail(&function->list, &config->functions);
    if (function->bind_deactivated) {
        value = usb_function_deactivate(function);
        if (value)
            goto done;
    }
    /* REVISIT *require* function->bind? */
    if (function->bind) {
        value = function->bind(config, function); //这里调用bind,对于uac2就是afunc_bind,前面一篇有分析
        if (value < 0) {
            list_del(&function->list);
            function->config = NULL;
        }
    } else
        value = 0;

    /* We allow configurations that don't work at both speeds.
     * If we run into a lowspeed Linux system, treat it the same
     * as full speed ... it's the function drivers that will need
     * to avoid bulk and ISO transfers.
     */
    if (!config->fullspeed && function->fs_descriptors)
        config->fullspeed = true;
    if (!config->highspeed && function->hs_descriptors)
        config->highspeed = true;
    if (!config->superspeed && function->ss_descriptors)
        config->superspeed = true;
    if (!config->superspeed_plus && function->ssp_descriptors)
        config->superspeed_plus = true;
done:
    if (value)
        DBG(config->cdev, "adding '%s'/%p --> %d\n",
                function->name, function, value);
    return value;
}
 
 
 
 
 
 
 
 
 
posted @ 2021-02-04 17:00  luoyuna  阅读(3134)  评论(0编辑  收藏  举报