首页 > 其他分享 >USB gadget驱动框架(三)

USB gadget驱动框架(三)

时间:2024-09-04 10:25:31浏览次数:5  
标签:function USB 框架 gadget driver udc cdev usb

gadget驱动框架(三)

usb_udc与usb_gadget_driver的绑定

usb_udc与usb_gadget_driver,在注册的时候分别被添加到udc_list和gadget_driver_pending_list中,无论这两者先后顺序如何,都将会动态的去识别及匹配到具体设备中,绑定过程如下:

源码:drivers/usb/gadget/udc/core.c

/* ------------------------------------------------------------------------- */

static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
{
    int ret;

    dev_dbg(&udc->dev, "registering UDC driver [%s]\n",
            driver->function);

    udc->driver = driver;
    udc->dev.driver = &driver->driver;
    udc->gadget->dev.driver = &driver->driver;

    usb_gadget_udc_set_speed(udc, driver->max_speed);

    //调用usb_gadget_driver的bind函数,实体为:composite_driver_template.bind
    //具体实现在drivers/usb/gadget/composite.c的composite_bind
    ret = driver->bind(udc->gadget, driver);
    if (ret)
        goto err1;

    //调用usb_gadget的udc_start函数,需要udc驱动者实现
    //主要功能是启动udc等
    //源码:drivers/usb/gadget/udc/s3c2410_udc.c的s3c2410_udc_start
    ret = usb_gadget_udc_start(udc);
    if (ret) {
        driver->unbind(udc->gadget);
        goto err1;
    }

    //调用usb_gadget的pullup函数,上拉或者下拉DP
    usb_udc_connect_control(udc);

    kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
    return 0;
err1:
    if (ret != -EISNAM)
        dev_err(&udc->dev, "failed to start %s: %d\n",
            udc->driver->function, ret);
    udc->driver = NULL;
    udc->dev.driver = NULL;
    udc->gadget->dev.driver = NULL;
    return ret;
}

usb_udc_connect_control

源码:drivers/usb/gadget/udc/core.c

static void usb_udc_connect_control(struct usb_udc *udc)
{
    //该变量在usb_add_gadget_udc_release创建usb_udc时置为true,
    if (udc->vbus)
        usb_gadget_connect(udc->gadget);
    else
        usb_gadget_disconnect(udc->gadget);
}

/**
 * usb_gadget_connect - software-controlled connect to USB host
 * @gadget:the peripheral being connected
 *
 * Enables the D+ (or potentially D-) pullup.  The host will start
 * enumerating this gadget when the pullup is active and a VBUS session
 * is active (the link is powered).  This pullup is always enabled unless
 * usb_gadget_disconnect() has been used to disable it.
 *
 * Returns zero on success, else negative errno.
 */
int usb_gadget_connect(struct usb_gadget *gadget)
{
    int ret = 0;

    if (!gadget->ops->pullup) {
        ret = -EOPNOTSUPP;
        goto out;
    }

    if (gadget->deactivated) {
        /*
         * If gadget is deactivated we only save new state.
         * Gadget will be connected automatically after activation.
         */
        gadget->connected = true;
        goto out;
    }

    //调用usb_gadget的udc_start函数,需要udc驱动者实现
    //功能:上拉DP,让host检测到设备的插入
    //源码:drivers/usb/gadget/udc/s3c2410_udc.c的s3c2410_udc_pullup
    ret = gadget->ops->pullup(gadget, 1);
    if (!ret)
        gadget->connected = 1;

out:
    trace_usb_gadget_connect(gadget, ret);

    return ret;
}


/**
 * usb_gadget_disconnect - software-controlled disconnect from USB host
 * @gadget:the peripheral being disconnected
 *
 * Disables the D+ (or potentially D-) pullup, which the host may see
 * as a disconnect (when a VBUS session is active).  Not all systems
 * support software pullup controls.
 *
 * Returns zero on success, else negative errno.
 */
int usb_gadget_disconnect(struct usb_gadget *gadget)
{
    int ret = 0;

    if (!gadget->ops->pullup) {
        ret = -EOPNOTSUPP;
        goto out;
    }

    if (gadget->deactivated) {
        /*
         * If gadget is deactivated we only save new state.
         * Gadget will stay disconnected after activation.
         */
        gadget->connected = false;
        goto out;
    }

    //调用usb_gadget的udc_start函数,需要udc驱动者实现
    //功能:下拉DP,让host检测到设备的拔出
    //源码:drivers/usb/gadget/udc/s3c2410_udc.c的s3c2410_udc_pullup
    ret = gadget->ops->pullup(gadget, 0);
    if (!ret)
        gadget->connected = 0;

out:
    trace_usb_gadget_disconnect(gadget, ret);

    return ret;
}

usb_gadget_driver的bind与ubind

主要功能是创建usb_composite_dev,及调用usb_composite_driver->bind函数

源码:drivers/usb/gadget/composite.c

static int composite_bind(struct usb_gadget *gadget,
        struct usb_gadget_driver *gdriver)
{
    struct usb_composite_dev    *cdev;
    struct usb_composite_driver    *composite = to_cdriver(gdriver);
    int                status = -ENOMEM;

    //创建usb_composite_dev
    cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
    if (!cdev)
        return status;

    spin_lock_init(&cdev->lock);
    cdev->gadget = gadget;
    set_gadget_data(gadget, cdev);
    //初始化usb_composite_dev的configs链表
    INIT_LIST_HEAD(&cdev->configs);
    //初始化usb_composite_dev的gstrings链表
    INIT_LIST_HEAD(&cdev->gstrings);

    //初始化usb_composite_dev.req等
    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
     */
    //回调usb_composite_driver的bind函数,
    //实例为gsrial_driver.gsbind,
    //源码:drivers/usb/gadget/legacy/serial.c
    //主要功能为:初始化设备描述符、字符串描述符等
    status = composite->bind(cdev);
    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;

fail:
    __composite_unbind(gadget, false);
    return status;
}

static void composite_unbind(struct usb_gadget *gadget)
{
    __composite_unbind(gadget, true);
}

static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver)
{
    struct usb_composite_dev    *cdev = get_gadget_data(gadget);
    struct usb_gadget_strings    *gstr = cdev->driver->strings[0];
    struct usb_string        *dev_str = gstr->strings;

    /* composite_disconnect() must already have been called
     * by the underlying peripheral controller driver!
     * so there's no i/o concurrency that could affect the
     * state protected by cdev->lock.
     */
    WARN_ON(cdev->config);

    while (!list_empty(&cdev->configs)) {
        struct usb_configuration    *c;
        c = list_first_entry(&cdev->configs,
                struct usb_configuration, list);
        remove_config(cdev, c);
    }
    if (cdev->driver->unbind && unbind_driver)
        cdev->driver->unbind(cdev);

    composite_dev_cleanup(cdev);

    if (dev_str[USB_GADGET_MANUFACTURER_IDX].s == cdev->def_manufacturer)
        dev_str[USB_GADGET_MANUFACTURER_IDX].s = "";

    kfree(cdev->def_manufacturer);
    kfree(cdev);
    set_gadget_data(gadget, NULL);
}

usb_composite_driver的bind与ubind

源码:

static int gs_bind(struct usb_composite_dev *cdev)
{
    int            status;

    /* Allocate string descriptor numbers ... note that string
     * contents can be overridden by the composite_dev glue.
     */

    status = usb_string_ids_tab(cdev, strings_dev);
    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;
    status = strings_dev[STRING_DESCRIPTION_IDX].id;
    serial_config_driver.iConfiguration = status;

    //otg设备的话,则需要做otg描述符的初始化,本次使用的非otg,暂不分析
    if (gadget_is_otg(cdev->gadget)) {
        if (!otg_desc[0]) {
            struct usb_descriptor_header *usb_desc;

            usb_desc = usb_otg_descriptor_alloc(cdev->gadget);
            if (!usb_desc) {
                status = -ENOMEM;
                goto fail;
            }
            usb_otg_descriptor_init(cdev->gadget, usb_desc);
            otg_desc[0] = usb_desc;
            otg_desc[1] = NULL;
        }
        serial_config_driver.descriptors = otg_desc;
        serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
    }

    /* register our configuration */
    if (use_acm) {//acm function,kernel默认将use_acm置为true
        status  = serial_register_ports(cdev, &serial_config_driver,
                "acm");
        usb_ep_autoconfig_reset(cdev->gadget);
    } else if (use_obex)//obex function,kernel默认将use_acm置为false
        status = serial_register_ports(cdev, &serial_config_driver,
                "obex");
    else {//gser function
        status = serial_register_ports(cdev, &serial_config_driver,
                "gser");
    }
    if (status < 0)
        goto fail1;

    usb_composite_overwrite_options(cdev, &coverwrite);
    INFO(cdev, "%s\n", GS_VERSION_NAME);

    return 0;
fail1:
    kfree(otg_desc[0]);
    otg_desc[0] = NULL;
fail:
    return status;
}


static int gs_unbind(struct usb_composite_dev *cdev)
{
    int i;

    for (i = 0; i < n_ports; i++) {
        usb_put_function(f_serial[i]);
        usb_put_function_instance(fi_serial[i]);
    }

    kfree(otg_desc[0]);
    otg_desc[0] = NULL;

    return 0;
}

serial_register_ports的function的bind

static int serial_register_ports(struct usb_composite_dev *cdev,
        struct usb_configuration *c, const char *f_name)
{
    int i;
    int ret;

    //通过bConfigurationValue判断当前config,是否在cdev->configs链表中
    //在,则报错;不在,则添加到链表中
    ret = usb_add_config_only(cdev, c);
    if (ret)
        goto out;

    for (i = 0; i < n_ports; i++) {

        //根据f_name匹配相关的usb_function_instance
        //源码:drivers/usb/gadget/functions.c
        fi_serial[i] = usb_get_function_instance(f_name);
        if (IS_ERR(fi_serial[i])) {
            ret = PTR_ERR(fi_serial[i]);
            goto fail;
        }

        //调用usb_function_instance的alloc_func申请usb_function
        //源码:drivers/usb/gadget/function/f_acm.c
        f_serial[i] = usb_get_function(fi_serial[i]);
        if (IS_ERR(f_serial[i])) {
            ret = PTR_ERR(f_serial[i]);
            goto err_get_func;
        }

        //将申请到的usb_function添加到config->functions链表中
        //源码:drivers/usb/gadget/composite.c
        ret = usb_add_function(c, f_serial[i]);
        if (ret)
            goto err_add_func;
    }

    return 0;

err_add_func:
    usb_put_function(f_serial[i]);
err_get_func:
    usb_put_function_instance(fi_serial[i]);

fail:
    i--;
    while (i >= 0) {
        usb_remove_function(c, f_serial[i]);
        usb_put_function(f_serial[i]);
        usb_put_function_instance(fi_serial[i]);
        i--;
    }
out:
    return ret;
}


static struct usb_function_instance *try_get_usb_function_instance(const char *name)
{
    struct usb_function_driver *fd;
    struct usb_function_instance *fi;

    fi = ERR_PTR(-ENOENT);
    mutex_lock(&func_lock);
    //name="acm",遍历func_list,查找"acm"所对应的usb_function_driver
    list_for_each_entry(fd, &func_list, list) {

        if (strcmp(name, fd->name))
            continue;

        if (!try_module_get(fd->mod)) {
            fi = ERR_PTR(-EBUSY);
            break;
        }
        //调用usb_function_driver.alloc_inst申请usb_function_instance
        fi = fd->alloc_inst();
        if (IS_ERR(fi))
            module_put(fd->mod);
        else
            fi->fd = fd;
        break;
    }
    mutex_unlock(&func_lock);
    return fi;
}

struct usb_function_instance *usb_get_function_instance(const char *name)
{
    struct usb_function_instance *fi;
    int ret;
    //name="acm",通过该name获取usb_function_instance
    fi = try_get_usb_function_instance(name);
    if (!IS_ERR(fi))
        return fi;
    ret = PTR_ERR(fi);
    if (ret != -ENOENT)
        return fi;
    ret = request_module("usbfunc:%s", name);
    if (ret < 0)
        return ERR_PTR(ret);
    return try_get_usb_function_instance(name);
}

struct usb_function *usb_get_function(struct usb_function_instance *fi)
{
    struct usb_function *f;
    //调用usb_function_instance的alloc_func申请usb_function
    f = fi->fd->alloc_func(fi);
    if (IS_ERR(f))
        return f;
    f->fi = fi;
    return f;
}
EXPORT_SYMBOL_GPL(usb_get_function);

usb_configuration与usb_functionn的bind

/**
 * usb_add_function() - add a function to a configuration
 * @config: the configuration
 * @function: the function being added
 * Context: single threaded during gadget setup
 *
 * After initialization, each configuration must have one or more
 * functions added to it.  Adding a function involves calling its @bind()
 * method to allocate resources such as interface and string identifiers
 * and endpoints.
 *
 * This function returns the value of the function's bind(), which is
 * zero for success else a negative errno value.
 */
int usb_add_function(struct usb_configuration *config,
        struct usb_function *function)
{
    int    value = -EINVAL;

    DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n",
            function->name, function,
            config->label, config);

    if (!function->set_alt || !function->disable)
        goto done;

    function->config = config;
    //将function添加到config->functions链表中
    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);
        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;
}

小结

到此已分析完usb_udc、usb_gadget_driver的绑定过程,及usb_function的匹配过程。。下一节将详细介绍usb_function的注册过程及相关函数的分析。另外一个方面,udc控制器已将DP上拉,udc endpoint0已使能,静待插入host完成枚举

标签:function,USB,框架,gadget,driver,udc,cdev,usb
From: https://www.cnblogs.com/linhaostudy/p/18395958

相关文章

  • LSTM+transformer+稀疏注意力机制(ASSA)时间序列预测(pytorch框架)
    LSTM+transformer+稀疏注意力机制transformer,LSTM,ASSA注意力首发原创!纯个人手打代码,自己研究的创新点,超级新。可以发刊,先发先的,高精度代码。需知:好的创新性模型可以事半功倍。目前太多流水paper,都是旧模型,老师已经审美疲劳,很难发好一点的刊,这种模型很新,让paper审核老师眼......
  • USB gadget functionfs
    FunctionFS(FunctionFilesystem)是LinuxUSBGadget框架的一部分,专门用于从用户空间实现和控制自定义的USB功能。它提供了一种文件系统接口,使用户能够在用户空间中直接定义USB设备的接口、端点和描述符,并管理USB数据的传输。FunctionFS常用于需要用户空间控制的复杂U......
  • USB gadget configfs
    概述USBLinuxGadget是一种具有UDC(USB设备控制器)的设备,可以连接到USB主机,以扩展其附加功能,如串口或大容量存储能力。一个gadget被它的主机视为一组配置,每个配置都包含一些接口,从gadget的角度来看,这些接口被称为功能,每个功能代表一个串行连接或一个SCSI磁盘。Linux提供了许......
  • 第二章 快速上手Django框架
    1.终端安装pipinstalldjango==3.2C:\Python39 -python.exe -Scripts -pip.exe -django-admin.exe -Lib -re.py -random.py -site-pakages -django==3.2 ...2.命令行创建项目cd指定目录django-adminstartproject项目名mysite......
  • Spring框架简述
    何为Spring?        Spring是分层的JavaEE应用full-stack轻量级开源框架,主要以IOC(反转控制)和AOP(面向切面编程)为核心。提供了展现层SpringMVC和持久层SpringJDBCTemplate以及业务层事务管理等众多的企业级应用技。Spring框架有哪些核心技术SpringIOC(InversionofCo......
  • Scrapy:Python网络爬虫框架详解
    网络爬虫作为获取互联网数据的重要手段之一,在数据挖掘和信息抽取领域发挥着重要作用。Scrapy是一个快速的、高层次的web抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,从联系跟踪、数据挖掘到历史存档等。Scrapy的主要特点异步处理:Scrapy基......
  • PyTorch:Python深度学习框架使用详解
    PyTorch是一个开源的机器学习库,广泛用于计算机视觉和自然语言处理领域。它由Facebook的AI研究团队开发,因其动态计算图、易用性以及与Python的紧密集成而受到开发者的青睐。PyTorch的主要特点动态计算图:PyTorch的计算图在运行时构建,使得模型的修改和调试更加灵活。自动微分......
  • LangChain4j系列:带你入门LangChain4j框架
    LangChain4j框架是什么?LangChain4j于2023年初在ChatGPT的炒作中开始开发。思想来源于Python和JavaScriptLLM库,并加入创新思想,开发一款Java语言版本的LLMs库。LangChain4j的目标是简化Java应用程序集成LLMs所以LangChain4j就是一个通过抽象统一API、提供便捷可用......
  • Openwrt中挂载NTFS格式USB硬盘无法正常显示中文目录及文件的解决办法
    在试图挂载硬盘使用alist作视频站的时候,我发现Openwrt访问自动挂载上的USB硬盘目录时,只能看得到英文的目录和文件。这就是固件自动挂载不太灵光的地方了自动挂载是Openwrt导航栏-系统-挂载点的[自动挂载磁盘]选项与[挂载已连接的设备]按钮。平常不想动手敲命令的时候固然很方便,但......
  • 基于 Selenium 的 Python 自动化测试框架
    SeleniumBase:功能全面的浏览器自动化框架。该项目是基于Selenium的Python自动化测试框架,集成了爬虫、自动化测试和生成报告等多种功能。它提供了丰富的示例,并且独特的UC模式,可以帮助开发者在进行浏览器自动化操作时避免被检测出来。fromseleniumbaseimportBaseCaseBaseCa......