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;
}
 
 
 
 
 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM