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完成枚举

USB gadget驱动框架(三)的更多相关文章

  1. USB摄像头驱动框架分析(五)

    一.USB摄像头驱动框架如下所示:1.构造一个usb_driver2.设置   probe:        2.1. 分配video_device:video_device_alloc        ...

  2. USB gadget 驱动 printer.c 分析

    1. modprobe g_printer idVendor=0x0525 idProduct=0xa4a8 modprobe后面也可以加模块参数 2. prn_example从stdout获取数据然 ...

  3. USB摄像头驱动框架分析

    usb摄像头驱动程序,里面涉及硬件的操作.比如说,想设置亮度的时候,需要把亮度的参数发给硬件.去得到真正视频数据的时候,需要访问硬件得到数据.usb摄像头驱动程序框架与虚拟摄像头驱动程序的框架是一样的 ...

  4. I2C驱动框架(三)

    参考:I2C子系统之platform_device初始化——smdk2440_machine_init() I2C驱动框架还应用了另一种总线-设备-驱动模型,平台设备总线platform_bus_ty ...

  5. Linux USB ECM Gadget 驱动介绍

    ​1 USB ECM介绍 USB ECM,属于USB-IF定义的CDC(Communication Device Class)下的一个子类:Ethernet Networking Control Mo ...

  6. Linux下USB驱动框架分析【转】

    转自:http://blog.csdn.net/brucexu1978/article/details/17583407 版权声明:本文为博主原创文章,未经博主允许不得转载. http://www.c ...

  7. Linux usb子系统(一) _写一个usb鼠标驱动

    USB总线是一种典型的热插拔的总线标准,由于其优异的性能几乎成为了当下大小设备中的标配. USB的驱动可以分为3类:SoC的USB控制器的驱动,主机端USB设备的驱动,设备上的USB Gadget驱动 ...

  8. I2C驱动框架(四)

    参考:I2C子系统之platform_driver初始化——I2C_adap_s3c_init() 在完成platform_device的添加之后,i2c子系统将进行platform_driver的注 ...

  9. linux usb总线驱动(一)

    目录 linux usb总线驱动框架 USB 介绍 传输类型 控制器接口 2440接口 基本流程 alloc_dev choose_address hub_port_init usb_get_devi ...

  10. Linux 串口、usb转串口驱动分析(2-2) 【转】

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=26807463&id=4186852 Linux 串口.usb转 ...

随机推荐

  1. php 后台执行进程

    一些业务需要长期在后台执行进程 下面用thinkphp5.1做演示 在command文件夹下建立进程 cd 到 lunar_php项目根目录 执行 php think hello 如果想后台执行 no ...

  2. browsermob-proxy-2.1.4启动失败,报错ProxyServerError: The Browsermob-Proxy server process failed to start

    报错信息:ProxyServerError: The Browsermob-Proxy server process failed to start. Check <_io.TextIOWrap ...

  3. 解决SpringMVC/SpringBoot @RequestBody无法注入基本数据类型

    我们都知道SpringMVC使用 @RequestBody 注解可以接收请求content-type 为 application/json 格式的消息体.但是我们必须使用实体对象,Map或者直接用St ...

  4. Nuxt.js必读:轻松掌握运行时配置与 useRuntimeConfig

    title: Nuxt.js必读:轻松掌握运行时配置与 useRuntimeConfig date: 2024/7/29 updated: 2024/7/29 author: cmdragon exc ...

  5. Windows11重置后出现Windows.old文件夹无法删除,报错C:\Windows.old\WINDOWS\System32\WDI - 目录不是空的。Win11系统Windows.old能删除吗?Windows.old怎么删

    问题: Windows11重置后出现Windows.old文件夹无法删除,报错C:\Windows.old\WINDOWS\System32\WDI - 目录不是空的. 网上的各种方法均不奏效: ht ...

  6. 如何为华为超算平台设置cuda路径

    在提交主机上修改.bashrc文件: 第一种: 使用运行主机上的cuda环境: # CUDAexport PATH=/usr/local/cuda-11.4/bin:$PATHexport LD_LI ...

  7. Correct the classpath of your application so that it contains a single, compatible version of xxx报错解决

    1.背景 有时候引入包有冲突,比如在Maven项目中的不同模块多次重复引入等 这里遇到的问题是重复映入了如下包: <dependency> <groupId>com.baomi ...

  8. springboot接口入参下划线转驼峰以及返回参数驼峰转下划线实现

    1.背景 在实际开发中,通常来说java里面是使用驼峰的命名规则; 但是有时候在对接其他三方平台的接口时,要求使用下划线的命名规则,这时候就涉及到如何让自己的接口满足三方平台的下划线; 实现方式有 1 ...

  9. 如何让您的 .NET应用程序更智能-- 请参加 8.20 的 .NET Conf -- Focus on AI

    Microsoft 将于 2024 年 8 月 20 日举办免费的 .NET Conf: Focus on AI.该虚拟活动为开发人员提供了如何集成 .NET 和 AI 以增强应用程序开发和用户体验的 ...

  10. 你要了解的2种AI思维链

    我们使用的AI助手,一般是经过了预训练和微调这2个步骤,尽管训练出的模型能回答许多通用类问题,但是在遇到复杂问题时还是束手无策. 直到有人提出了思维链方式,才解决了模型在面对复杂问题时的推理能力. 1 ...