当前位置: 首页 > news >正文

USB 驱动开发 --- Gadget 驱动框架梳理

编译链接

#----》 linux_5.10/drivers/usb/gadget/Makefileobj-$(CONFIG_USB_LIBCOMPOSITE)    += libcomposite.o
libcomposite-y            := usbstring.o config.o epautoconf.o
libcomposite-y            += composite.o functions.o configfs.o u_f.oobj-$(CONFIG_USB_GADGET)    += udc/ function/ legacy/

可知:

  • libcomposite 框架包含: composite、functions、configfs 等几个组件。
    ​ 注:类似 libphy框架,可再跟进比较。

  • gadget 框架包含: udc、function、legacy 三个主要组成。

    • legacy, 传统的 USB 设备功能配置方,主要依赖于 gadgetfs 接口;
    • function, 更加现代化和结构化的 USB 设备功能配置方式,它依赖于 configfs 接口;

注: Milkv Dus 内核配置文件cvitek_sg2000_milkv_duos_glibc_arm64_sd_defconfig中:CONFIG_USB_LIBCOMPOSITE=y,``CONFIG_USB_GADGET=y`,说明两个模块都内嵌到内核镜像中。

源码跟读

gadget 框架

Legacy,zero 驱动

对于 Legacy 编程模式,选择 zero 驱动跟读。

查看依赖关系

#----> linux_5.10/drivers/usb/gadget/legacy/Kconfigconfig USB_ZEROtristate "Gadget Zero (DEVELOPMENT)"select USB_LIBCOMPOSITE											// 勾选 USB_LIBCOMPOSITE、 USB_F_SS_LBselect USB_F_SS_LB

可知: Legacy 模式下的 Zere 驱动依赖 libcomposite 框架与 SS_LB Function 驱动。

...
g_zero-y               := zero.o
obj-$(CONFIG_USB_ZERO) += g_zero.o

查看配置状态

$ grep -wrn "CONFIG_USB_ZERO" linux_5.10/build/sg2000_milkv_duos_glibc_arm64_sd/.config
2643:# CONFIG_USB_ZERO is not set

Zero 驱动未纳入编译,修改如下:

diff --git a/build/boards/cv181x/sg2000_milkv_duos_glibc_arm64_sd/sg2000_milkv_duos_glibc_arm64_sd_defconfig b/build/boards/cv181x/sg2000_milkv_duos_glibc_arm64_sd/sg2000_milkv_duos_glibc_arm64_sd_defconfig
index d532054e6..361978f37 100644
--- a/build/boards/cv181x/sg2000_milkv_duos_glibc_arm64_sd/sg2000_milkv_duos_glibc_arm64_sd_defconfig
+++ b/build/boards/cv181x/sg2000_milkv_duos_glibc_arm64_sd/sg2000_milkv_duos_glibc_arm64_sd_defconfig
@@ -27,3 +27,4 @@ CONFIG_TARGET_PACKAGE_MTD-UTILS=y# CONFIG_TARGET_PACKAGE_RSYSLOG is not setCONFIG_TARGET_PACKAGE_BUSYBOX_SYSLOGD_SCRIPT=yCONFIG_TARGET_PACKAGE_NTP=y
+CONFIG_USB_ZERO=m

使用bear工具重新生成clangd依赖的 compile_commands.json ,附加处理:

sed -i /-mabi=lp64/d compile_commands.json 

由此可实现VScode + Clangd 插件下的代码跳转功能。跟读源码:

//----> linux_5.10/drivers/usb/gadget/legacy/zero.cmodule_init(zero_driver_init);        // 内核模块初始化入口module_usb_composite_driver(zero_driver);							// 宏展开内容如下:
static int __init zero_driver_init(void) {return usb_composite_probe(&zero_driver);static struct usb_composite_driver zero_driver = {.name        = "zero",.dev         = &device_desc,.strings     = dev_strings,.max_speed   = USB_SPEED_SUPER,.bind        = zero_bind,										// Bind.unbind      = zero_unbind,.suspend     = zero_suspend,.resume      = zero_resume,
};//----> linux_5.10/drivers/usb/gadget/composite.c/*** usb_composite_probe() - register a composite driver...*/ 
int usb_composite_probe(struct usb_composite_driver *driver) {...driver->gadget_driver = composite_driver_template;              // 复制模板驱动(默认方法)gadget_driver = &driver->gadget_driver;                    gadget_driver->function    =  (char *) driver->name;            // 根据 usb_composite_driver 定义针对性的修改: function、driver.name、max_speedgadget_driver->driver.name = driver->name;gadget_driver->max_speed   = driver->max_speed;return usb_gadget_probe_driver(gadget_driver);                	// 将 usb_composite_driver 转换为 usb_gadget_driver 对象进行 probestatic 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,},
};//----> linux_5.10/drivers/usb/gadget/udc/core.c
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)struct         *udc = NULL;...mutex_lock(&udc_lock);                                          // 加锁保护,临界资源:udc_list、gadget_driver_pending_listif (driver->udc_name) { ... }                                   // 如果指定了目标 UDC 硬件,进行特殊 probeelse {list_for_each_entry(udc, &udc_list, list) {            		// 遍历 udc 设备链表,如果第一个没有匹配到驱动的 udc 设备if (!udc->driver)goto found;    if (!driver->match_existing_only) {    ... }              		// 如果没有找到未匹配驱动的 UDC 且 未限定只适配已存在 UDC,则将当前驱动加入 gadget_driver_pending_list 链表, 等待下一次 probelist_add_tail(&driver->pending, &gadget_driver_pending_list);...return ret;found:ret = udc_bind_to_driver(udc, driver);                        	// 尝试绑定 UDC 设备 与 usb_gaget_driver...
}//----> linux_5.10/drivers/usb/gadget/udc/core.cstatic int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver) {...udc->driver             = driver;								// 实际指向:composite_driver_templateudc->dev.driver         = &driver->driver;udc->gadget->dev.driver = &driver->driver;    usb_gadget_udc_set_speed(udc, driver->max_speed);ret = driver->bind(udc->gadget, driver);                    	// Bind    阶段,实际指向 composite_driver_template 模板驱动中的 bind 方法:composite_bind...ret = usb_gadget_udc_start(udc);								// Stasrt  阶段,实际指向 UDC 设备 OPS 能力集中的 udc_start 方法:return udc->gadget->ops->udc_start(udc->gadget, udc->driver);...usb_udc_connect_control(udc);									// Connect 阶段,实际指向 UDC 设备 OPS 能力集中的 pullup 方法,控制上拉电阻连接if (udc->vbus)	usb_gadget_connect(udc->gadget);ret = gadget->ops->pullup(gadget, 1);... // OTG 相关,暂且忽略kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);return 0;
err1:...return ret;
}//----> linux_5.10/drivers/usb/gadget/composite.cstatic int composite_bind(struct usb_gadget *gadget, struct usb_gadget_driver *gdriver) {struct usb_composite_dev	*cdev;...cdev = kzalloc(sizeof *cdev, GFP_KERNEL);                       // 申请 usb_composite_dev 设备struct usb_composite_driver	*composite = to_cdriver(gdriver);	// 实际指向 zero_driver 驱动, usb_composite_driver...spin_lock_init(&cdev->lock);cdev->gadget = gadget;set_gadget_data(gadget, cdev);INIT_LIST_HEAD(&cdev->configs);INIT_LIST_HEAD(&cdev->gstrings);status = composite_dev_prepare(composite, cdev) {struct usb_gadget *gadget = cdev->gadget;	cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);  // request 申请,对象 与 内存struct usb_request *req = ep->ops->alloc_request(ep, gfp_flags);...return req;cdev->req->buf = kmalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL);  ret = device_create_file(&gadget->dev, &dev_attr_suspended);// 创建 suspended 节点cdev->req->complete = composite_setup_complete;             // request 补充,用于 complete 处理cdev->req->context  = cdev;gadget->ep0->driver_data = cdev;							// ep0 驱动私有数据cdev->driver = composite;                                   // 引用 zero_driver, 关联 usb_cmposite_dev 与 usb_composite_driver... // VBUS Drawn, 电流控制相关usb_ep_autoconfig_reset(gadget);return 0;}		...status = composite->bind(cdev);									// 实际指向 zero_driver 驱动中的 bind 方法:zero_bind, 可调整 cdev.use_os_string 等成员以影响后续流程...if (cdev->use_os_string) {                                      // 在 composite->bing 阶段被定义。status = composite_os_desc_req_prepare(cdev, gadget->ep0);...update_unchanged_dev_desc(&cdev->desc, composite->dev);         // 更新描述符, 从 zero_driver => cdev;...return 0;fail:__composite_unbind(gadget, false);return status;	
}	

Legacy zero 驱动在模块入口 module_init 主动调用 usb_composite_probe(&zero_driver) 尝试与 UDC 设备绑定。
绑定由 udc_bind_to_driver 实现,过程又可分为三个步骤:

  1. Bind,gadget驱动私有的 bind 实现
    • Legacy 模式下由 composite_bind 代为实现,Function下
  2. Stasrt,配置 UDC,绑定 UDC 设备与 gadget 驱动;
  3. Connect,发起 UDC 设备连接;

回到 zero_driver 驱动,查看 bind 阶段的实现

//----> linux_5.10/drivers/usb/gadget/legacy/zero.cstatic int zero_bind(struct usb_composite_dev *cdev) {...status = usb_string_ids_tab(cdev, strings_dev);                 // 更新 String 类描述符,主要涉及 ID 相关信息...device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;    // VID、PID、SN号等基本信息更新device_desc.iProduct      = strings_dev[USB_GADGET_PRODUCT_IDX].id;device_desc.iSerialNumber = strings_dev[USB_GADGET_SERIAL_IDX].id;	...timer_setup(&autoresume_timer, zero_autoresume, 0);             // 自动唤醒定时器初始化func_inst_ss = usb_get_function_instance("SourceSink");         // Function 申请,目标:SourceSinkss_opts =  container_of(func_inst_ss, struct f_ss_opts, func_inst);	// Super-Speed 特性定义ss_opts->pattern       = gzero_options.pattern;ss_opts->isoc_interval = gzero_options.isoc_interval;	...func_ss = usb_get_function(func_inst_ss);						// Function 实例化,目标:SourceSink... // 次要 Function Loopback 实例化过程,忽略... // 唤醒相关if (gadget_is_otg(cdev->gadget)) {                              // OTG 相关...if (loopdefault) { ...											// 默认第一功能为 Sourcesink,其次 Loopbackelse {usb_add_config_only(cdev, &sourcesink_driver);usb_add_config_only(cdev, &loopback_driver);				// 添加 cdev 至配置表:sourcesink_driver、 loopback_driver... // cdev->configs 重复项检查config->cdev = cdev;									// config 关联 cdevlist_add_tail(&config->list, &cdev->configs);			// cdev 关联 configsINIT_LIST_HEAD(&config->functions);config->next_interface_id = 0;							// interface 索引号memset(config->interface, 0, sizeof(config->interface));//   接口内容清空		status = usb_add_function(&sourcesink_driver, func_ss);         // 向 Sourcesink 添加 Function 实例... // 次要 Function Loopback 添加过程,忽略usb_ep_autoconfig_reset(cdev->gadget);                          // 更新端点配置	usb_composite_overwrite_options(cdev, &coverwrite);             // 更新 optionsINFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname);return 0;	err_free_otg_desc:	...return status;
}	static struct usb_configuration sourcesink_driver = {.label                  = "source/sink",.setup                  = ss_config_setup,.bConfigurationValue    = 3,.bmAttributes           = USB_CONFIG_ATT_SELFPOWER,/* .iConfiguration      = DYNAMIC */
};

匹配步骤:

  1. 按名字查找已添加到 func_list 链表中的 Function;
  2. 查找到目标 function 则调用其驱动中的私有实例化函数指针(alloc_inst)创建实例;

涉及

  • functions 核心API,如: usb_get_function_instance、usb_get_function
  • composite 核心 API,如:usb_add_function;

具体 function 需要静态声明 function, 如 SourceSink 设备驱动

//----> linux_5.10/drivers/usb/gadget/functions.cstruct usb_function_instance *usb_get_function_instance(const char *name) {struct usb_function_driver *fd;struct usb_function_instance *fi;...fi = try_get_usb_function_instance(name);                       // 获取 function 实例mutex_lock(&func_lock);list_for_each_entry(fd, &func_list, list) {					// 遍历 func_list 链表if (strcmp(name, fd->name))								//   使用名字查找目标 Function, 当前涉及:SourceSink、Loopbackcontinue;		...fi = fd->alloc_inst();									// 调用具体 function 的私有 alloc_inst 实现,如:source_sink_alloc_instif (IS_ERR(fi)) ... elsefi->fd = fd;										// 引用 function_driver 驱动break;......return try_get_usb_function_instance(name);
}	struct usb_function *usb_get_function(struct usb_function_instance *fi) {...f = fi->fd->alloc_func(fi);                                     // 调用具体 function 的私有 alloc_func 实现,如:source_sink_alloc_func...f->fi = fi;                                                     // 引用 function 实例return f;
}

function 驱动在 module_init 阶段先使用 usb_function_register 将自己添加至 func_list 链表。

function 私有实现:xxx.alloc_inst()、xxx.alloc_func(usb_function_instance), 以 SourceSink 为例:

//----> linux_5.10/drivers/usb/gadget/function/f_sourcesink.cDECLARE_USB_FUNCTION(SourceSink, source_sink_alloc_inst, source_sink_alloc_func);.name = "SourceSink",				....alloc_inst = source_sink_alloc_inst,.alloc_func = source_sink_alloc_func,			
}static struct usb_function_instance *source_sink_alloc_inst(void) {struct f_ss_opts *ss_opts;ss_opts = kzalloc(sizeof(*ss_opts), GFP_KERNEL);                // 申请 struct f_ss_opts 对象...ss_opts->func_inst.free_func_inst = source_sink_free_instance;ss_opts->isoc_interval            = GZERO_ISOC_INTERVAL;	... // ss_opts 其他属性设定config_group_init_type_name(&ss_opts->func_inst.group, "", &ss_func_type);  // configfs 相关属性创建,如:bulk_buflenreturn &ss_opts->func_inst;                                     // 返回 function 实例(struct usb_function_instance),后面估计需要使用类 contains_of 方式转换到 ss_opts
}	static struct configfs_attribute *ss_attrs[] = {&f_ss_opts_attr_pattern,&f_ss_opts_attr_isoc_interval,&f_ss_opts_attr_isoc_maxpacket,&f_ss_opts_attr_isoc_mult,&f_ss_opts_attr_isoc_maxburst,&f_ss_opts_attr_bulk_buflen,&f_ss_opts_attr_bulk_qlen,&f_ss_opts_attr_iso_qlen,NULL,
};static struct usb_function *source_sink_alloc_func(struct usb_function_instance *fi) {struct f_sourcesink     *ss;...ss = kzalloc(sizeof(*ss), GFP_KERNEL);                          // 申请 soursink 实例...ss_opts =  container_of(fi, struct f_ss_opts, func_inst);		// 转换为 f_ss_opts 实例...ss->pattern       = ss_opts->pattern;ss->isoc_interval = ss_opts->isoc_interval;	... // 由 ss_opts 转录到 ss;ss->function.name      = "source/sink";ss->function.bind      = sourcesink_bind;ss->function.setup     = sourcesink_setup;ss->function.strings   = sourcesink_strings;ss->function.free_func = sourcesink_free_func;... // 其下 function  初始化return &ss->function;                                           // 返回 function 实例(struct usb_function),后面估计需要使用类 contains_of 方式转换到 f_ss
}	
  • ss_opts 主要与 configfs 交互,关键对象:struct config_item;
  • ss 主要与 composite 交互,与 ss_opts 仅在初始化 alloc_func 阶段完成转录;

composite 核心 API:usb_add_function;

//----> linux_5.10/drivers/usb/gadget/composite.cint usb_add_function(struct usb_configuration *config, struct usb_function *function) {...function->config = config;list_add_tail(&function->list, &config->functions);             // function 记录到链表: if (function->bind_deactivated) {                               // bind 过程中不回应枚举value = usb_function_deactivate(function);                  ...if (function->bind) {                                           // function 的私有 bind 实现value = function->bind(config, function);                   //   调用具体 function 的私有 ;bind 实现,如:source_sink_alloc_func...if (!config->fullspeed && function->fs_descriptors)             // Full、High、Super、Super-Plus Speed 特性设定config->fullspeed = true;if (!config->highspeed && function->hs_descriptors)config->highspeed = true;...return value;
}	

至此,probe 的 bind 阶段告一段落。接下来是 start、connect 阶段

//----> linux_5.10/drivers/usb/gadget/udc/core.cstatic inline int usb_gadget_udc_start(struct usb_udc *udc) {return udc->gadget->ops->udc_start(udc->gadget, udc->driver);   // 实际指向 UDC 驱动 ops 操作集的中 .udc_start 方法,对应:dwc2_hsotg_udc_start//----> linux_5.10/drivers/usb/dwc2/gadget.cstatic const struct usb_gadget_ops dwc2_hsotg_gadget_ops = {.udc_start      = dwc2_hsotg_udc_start,.pullup         = dwc2_hsotg_pullup,...
};static int dwc2_hsotg_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver) {struct dwc2_hsotg *hsotg = to_hsotg(gadget);                    // dadget 设备转换为 私有对象... // 驱动合法性检查,含:非空、max_speed、driver.setup 方法非空driver->driver.bus = NULL;hsotg->driver             = driver;								// DWC2 绑定 usb_gadget_driver hsotg->gadget.dev.of_node = hsotg->dev->of_node;hsotg->gadget.speed       = USB_SPEED_UNKNOWN;	if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) {                 // Duoble-Role, 即角色定义,当前做 PERIPHERAL 即 Deviceret = dwc2_lowlevel_hw_enable(hsotg);int ret = __dwc2_lowlevel_hw_enable(hsotg);ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);			ret = devm_add_action_or_reset(&pdev->dev, __dwc2_disable_regulators, hsotg);				if (hsotg->clk) {ret = clk_prepare_enable(hsotg->clk);				if (hsotg->uphy) {ret = usb_phy_init(hsotg->uphy);					} else if (hsotg->plat && hsotg->plat->phy_init) {ret = hsotg->plat->phy_init(pdev, hsotg->plat->phy_type);					} else {ret = phy_power_on(hsotg->phy);		if (ret == 0)ret = phy_init(hsotg->phy);}if (!IS_ERR_OR_NULL(hsotg->uphy))otg_set_peripheral(hsotg->uphy->otg, &hsotg->gadget);if (dwc2_hw_is_device(hsotg)) {dwc2_hsotg_init(hsotg);dwc2_hsotg_core_init_disconnected(hsotg, false);hsotg->enabled = 0;spin_unlock_irqrestore(&hsotg->lock, flags);gadget->sg_supported = using_desc_dma(hsotg);return ret;
}					static void usb_udc_connect_control(struct usb_udc *udc) {if (udc->vbus)usb_gadget_connect(udc->gadget);elseusb_gadget_disconnect(udc->gadget);int usb_gadget_connect(struct usb_gadget *gadget) {		... 															// 如果不支持上拉电阻配置,则直接退出if (gadget->deactivated) {                                      // 如果设置了 deaactivated, 在 activation 之后 gadget 将会自动连接gadget->connected = true;goto out;ret = gadget->ops->pullup(gadget, 1);                           // 上拉电阻配置,具体由 UDC 驱动实现
out:trace_usb_gadget_connect(gadget, ret);							// 发起连接return ret;
}	static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on) {	struct dwc2_hsotg *hsotg = to_hsotg(gadget);...if (is_on) {hsotg->enabled = 1;dwc2_hsotg_core_init_disconnected(hsotg, false);/* Enable ACG feature in device mode,if supported */dwc2_enable_acg(hsotg);					// active clock gating featuredwc2_hsotg_core_connect(hsotg);	dwc2_clear_bit(hsotg, DCTL, DCTL_SFTDISCON);		...
Function,sourcesink 驱动

对于 Function 编程模式,选择 loop 驱动跟读。
已知 Function 编程模式主要依赖 configfs 实现,所以参考内核文档``先展示使用示例:

  • linux_5.10/Documentation/usb/gadget-testing.rst
  • linux_5.10/Documentation/usb/gadget_configfs.rst

Kconfig 配置

config USB_CONFIGFS_F_LB_SSbool "Loopback and sourcesink function (for testing)"depends on USB_CONFIGFSselect USB_F_SS_LB												# 选中 USB_F_SS_LB

对应 Makefile

#----> linux_5.10/drivers/usb/gadget/function/Makefile...
usb_f_ss_lb-y             := f_loopback.o f_sourcesink.o
obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o

f_ss_lb Function 由 sourcesink + loopbak 两个组成,与 Legacy 模式相同,所以源码跟踪着重关注入口与使用差异

//-----> linux_5.10/drivers/usb/gadget/function/f_sourcesink.cmodule_init(sslb_modinit);static int __init sslb_modinit(void) {ret = usb_function_register(&SourceSinkusb_func);				// 调用 libcomposite API:usb_function_register 向 composite 注册 usb_gadget_driver;ret = lb_modinit();												// 一并完成 loopback 初始化return usb_function_register(&Loopbackusb_func);	...int usb_function_register(struct usb_function_driver *newf) {struct usb_function_driver *fd;...mutex_lock(&func_lock);list_for_each_entry(fd, &func_list, list) {						// 遍历 func_list 链表,如果查找到同名驱动则退出if (!strcmp(fd->name, newf->name))							goto out;...list_add_tail(&newf->list, &func_list);							// 添加至 func_list 链表DECLARE_USB_FUNCTION(, source_sink_alloc_inst, source_sink_alloc_func);		// 宏展开如下
static struct usb_function_driver SourceSinkusb_func = {.name       = "SourceSink",				.mod        = THIS_MODULE,					.alloc_inst = source_sink_alloc_inst,.alloc_func = source_sink_alloc_func,	
};								
MODULE_ALIAS("usbfunc:""SourceSink");

f_ss_lb Function 需要依托 configfs 实例化,参考内核文档 `` 测试步骤如下:

  1. 使用 configfs 在用户空间创建 gadget function;
  2. 使用测试用例 test-usb 测试;

使用 configfs 创建 gadget 设备示例如下:

# 加载 f_ss_lb 驱动
insmod /mnt/system/ko/usb_f_ss_lb.ko# 变量声明
CVI_DIR=/tmp/usb
CVI_GADGET=$CVI_DIR/usb_gadget/cvitek
CVI_FUNC=$CVI_GADGET/functions
MANUFACTURER="Cvitek"
PRODUCT="USB Com Port"
SERIAL="0123456789"
VID=0x3346
PID=0x1003
CLASS=SourceSink
FUNC_NUM=0# 环境初始化
mkdir $CVI_DIR# configfs 挂载
mount none $CVI_DIR -t configfs# 创建 Gadget 设备
mkdir $CVI_GADGET# Gadget 设备初始化
echo $VID >$CVI_GADGET/idVendor
echo $PID >$CVI_GADGET/idProduct# 信息初始化
mkdir $CVI_GADGET/strings/0x409
echo $MANUFACTURER>$CVI_GADGET/strings/0x409/manufacturer
echo $PRODUCT>     $CVI_GADGET/strings/0x409/product
echo $SERIAL>      $CVI_GADGET/strings/0x409/serialnumber# 创建并关联 configuration 与 function 
mkdir $CVI_GADGET/configs/c.1
mkdir $CVI_GADGET/functions/$CLASS.usb$FUNC_NUM
ln -s $CVI_FUNC/$CLASS.usb$FUNC_NUM $CVI_GADGET/configs/c.1# 配置 configuration
mkdir $CVI_GADGET/configs/c.1/strings/0x409
echo "config1">    $CVI_GADGET/configs/c.1/strings/0x409/configuration
echo 120>          $CVI_GADGET/configs/c.1/MaxPower# 使能 Gadget 设备
UDC=`ls /sys/class/udc/ | awk '{print $1}'`
echo $UDC>       $CVI_GADGET/UDCecho 4340000.usb > /tmp/usb/usb_gadget/cvitek/UDC

注:

  • 可配置信息参考内核文档,路径:Documentation/ABI/*/configfs-usb-gadget*
  • Shell 执行异常,$UDC 结果明文写入 $CVI_GADGET/UDC 节点,如:echo 4340000.usb > /tmp/usb/usb_gadget/cvitek/UDC

configfs 入口

//----> linux_5.10/drivers/usb/gadget/configfs.cmodule_init(gadget_cfs_init);static int __init gadget_cfs_init(void) {config_group_init(&gadget_subsys.su_group);						// 主要是链表初始化,暂且忽略ret = configfs_register_subsystem(&gadget_subsys);return ret;
}static struct configfs_subsystem gadget_subsys = {.su_group = {.cg_item = {.ci_namebuf = "usb_gadget",.ci_type = &gadgets_type,}...
};static const struct config_item_type gadgets_type = {.ct_group_ops   = &gadgets_ops,	...
};static struct configfs_group_operations gadgets_ops = {.make_group     = &gadgets_make,...
};static struct config_group *gadgets_make(struct config_group *group, const char *name) {struct gadget_info *gi;gi = kzalloc(sizeof(*gi), GFP_KERNEL);...config_group_init_type_name(&gi->group, name, &gadget_root_type);config_group_init_type_name(&gi->functions_group, "functions", &functions_type);configfs_add_default_group(&gi->functions_group, &gi->group);config_group_init_type_name(&gi->configs_group, "configs", &config_desc_type);configfs_add_default_group(&gi->configs_group, &gi->group);config_group_init_type_name(&gi->strings_group, "strings", &gadget_strings_strings_type);configfs_add_default_group(&gi->strings_group, &gi->group);config_group_init_type_name(&gi->os_desc_group, "os_desc", &os_desc_type);configfs_add_default_group(&gi->os_desc_group, &gi->group);gi->composite.bind      = configfs_do_nothing;									// composite 驱动,接口方法实现gi->composite.unbind    = configfs_do_nothing;gi->composite.suspend   = NULL;gi->composite.resume    = NULL;gi->composite.max_speed = USB_SPEED_SUPER;...composite_init_dev(&gi->cdev);gi->cdev.desc.bLength         = USB_DT_DEVICE_SIZE;								// composite 设备,描述符初始化gi->cdev.desc.bDescriptorType = USB_DT_DEVICE;gi->cdev.desc.bcdDevice       = cpu_to_le16(get_default_bcdDevice());			//     bcdDevice 默认由 get_default_bcdDevice(), 即内核版本 LINUX_VERSION_CODE 第 0,2 Byte 决定gi->composite.gadget_driver          = configfs_driver_template;				// gadget 驱动,接口方法默认使用:configfs_driver_templategi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL);gi->composite.name                   = gi->composite.gadget_driver.function;...
}static const struct usb_gadget_driver configfs_driver_template = {.bind           = configfs_composite_bind,.unbind         = configfs_composite_unbind,.setup          = configfs_composite_setup,.reset          = configfs_composite_disconnect,.disconnect     = configfs_composite_disconnect,...
};

function 创建实现,命令 mkdir $CVI_GADGET/functions/$CLASS.usb$FUNC_NUM,源码跟读:

static const struct config_item_type functions_type = {.ct_group_ops   = &functions_ops,.ct_owner       = THIS_MODULE,
};static struct configfs_group_operations functions_ops = {.make_group     = &function_make,.drop_item      = &function_drop,
};static struct config_group *function_make(struct config_group *group, const char *name) {char buf[MAX_NAME_LEN];char *func_name;char *instance_name;	...ret = snprintf(buf, MAX_NAME_LEN, "%s", name);									// ...func_name = buf;	instance_name = strchr(func_name, '.');	fi = usb_get_function_instance(func_name);						// 调用 usb_get_function_instance 创建 funcion 实例...
}

上述示例中,最关键一步为"使能 Gadget 设备",查找相关实现:

$ grep -wrn "UDC" linux_5.10/drivers/usb/gadget/
linux_5.10/drivers/usb/gadget/configfs.c:336:CONFIGFS_ATTR(gadget_dev_desc_, UDC);

源码跟读

//----> linux_5.10/drivers/usb/gadget/configfs.cCONFIGFS_ATTR(gadget_dev_desc_, UDC);								// configfs 属性节点 UDCstatic struct configfs_attribute *gadget_root_attrs[] = {&gadget_dev_desc_attr_bcdUSB,&gadget_dev_desc_attr_UDC,										// UDC 节点引用...static ssize_t gadget_dev_desc_UDC_show(struct config_item *item, char *page) {	... 	// UDC Read 实现,暂且忽略static ssize_t gadget_dev_desc_UDC_store(struct config_item *item, const char *page, size_t len) {struct gadget_info *gi = to_gadget_info(item);...name = kstrdup(page, GFP_KERNEL);... // 有效性检查及字符串处理mutex_lock(&gi->lock);if (!strlen(name)) {											// 输入空字符串 "" 则使用 unregister_gadget 将当前 Gadet 设备注销ret = unregister_gadget(gi);...} else {...gi->composite.gadget_driver.udc_name = name;ret = usb_gadget_probe_driver(&gi->composite.gadget_driver);// 使用 usb_gadget_probe_driver 尝试与 gadget 设备匹配......return ret;
}	

此外调用的 usb_gadget_probe_driver 与上文跟读的 Lgacy zero 驱动相似(都是主动调用尝试与 UDC 设备绑定),所以后续的流程一致不做重复跟读。

内核还提供了在 Host 端的对应测试用例 testusb 。
在此不做展开,testusb 源码路径:linux_5.10/tools/usb/testusb.c;

对比
  • 驱动入口,直接入口都是 usb_gadget_probe_driver,前置路径不同

    • Legacy zere 驱动,使用宏 module_usb_composite_driver 定义模块入口并调用 usb_gadget_probe_driver
    • Function f_ss_lb 驱动,由 configfs 生成的 Gadget 设备中 UDC 节点 write 方法调用 usb_gadget_probe_driver
  • 驱动对象,两者都是继承的 usb_gadget_driver,封装程度不同

    • Legacy zero 驱动对象为 usb_composite_driver;
    • Function f_ss_lb 驱动对象为 usb_function_driver;
  • 接口方法,两者都基于 composite_{bind、setup, …} 连接上层

    • Legacy zero 借助 usb_gadget_driver 驱动模板(composite_driver_template) 修改后传入 usb_gadget_probe_driver 以接入 libcomposite 框架;
    • Function f_ss_lb 借助 usb_gadget_driver 驱动模板(configfs_driver_template) 修改后传入 usb_gadget_probe_driver 以接入 libcomposite 框架;

    如: composite_setup 在处理完基本功能外不会尝试执行上层的 setup 方法,包含:usb_function.setup、usb_configuration.setup;

总结
  1. Legacy zero 的方法更贴近 libcomposite 框架的(直接调用相关API),手段灵活,但开发难度相对较高;
  2. Function 集成度更高,可使用 configfs 方式高效开发、调试,但可配置内容受 function 实现的内容限定;

UDC 驱动

udc_list 链表由 USB 控制器以平台设备(of)接入系统并与驱动 probe 过程中注册到 USB composite driver 框架

#----> linux_5.10/drivers/usb/dwc2/platform.cmodule_platform_driver(dwc2_platform_driver);static struct platform_driver dwc2_platform_driver = {.driver = {.of_match_table = dwc2_of_match_table,...},.probe    = dwc2_driver_probe,...
};static int dwc2_driver_probe(struct platform_device *dev)struct dwc2_hsotg *hsotg;...hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL);	// 申请 hsotg 对象(也是 usb_gadget),DWC2 驱动中 dwc2_hsotg 结构体包含 usb_gadget 结构hsotg->dev = &dev->dev;	                                        // 关联 平台设备... // DMA 相关初始化hsotg->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res); // IO 寄存器映射retval = dwc2_lowlevel_hw_init(hsotg);                          // 硬件底层初始化hsotg->irq = platform_get_irq(dev, 0);                          // 中断初始化retval = devm_request_irq(hsotg->dev, hsotg->irq, dwc2_handle_common_intr, IRQF_SHARED, dev_name(hsotg->dev), hsotg);hsotg->vbus_supply = devm_regulator_get_optional(hsotg->dev, "vbus");   // VBUS 初始化retval = dwc2_lowlevel_hw_enable(hsotg);                        // 底层硬件使能hsotg->needs_byte_swap = dwc2_check_core_endianness(hsotg);		// 数据流大小端转换配置retval = dwc2_get_dr_mode(hsotg);                               // USB 控制器角色配置... // 其他配置retval = dwc2_core_reset(hsotg, false);                         // 核心复位retval = dwc2_get_hwparams(hsotg);                              // 硬件参数更新dwc2_force_dr_mode(hsotg);                                      // USB 控制器角色切换retval = dwc2_init_params(hsotg);                               // 硬件参数更新... // 其他配置
#if IS_ENABLED(CONFIG_USB_ROLE_SWITCH)	retval = dwc2_drd_init(hsotg);	                                // 双角色功能初始化
#endif	if (hsotg->dr_mode != USB_DR_MODE_HOST) {                       // Gadget 初始化retval = dwc2_gadget_init(hsotg);  ...hsotg->gadget_enabled = 1;									// 	标记 gadget_enabled = 1... // 其他唤醒相关配置... // Host 初始化platform_set_drvdata(dev, hsotg);hsotg->hibernated = 0;dwc2_debugfs_init(hsotg);if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)dwc2_lowlevel_hw_disable(hsotg);							// Gadget 失能#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)if (hsotg->gadget_enabled) {retval = usb_add_gadget_udc(hsotg->dev, &hsotg->gadget);	// 添加当前 udc 至 udc_list 链表, DWC2 驱动中 dwc2_hsotg 结构体包含 usb_gadget 结构... // proc 相关节点创建return 0;
}	//----> linux_5.10/drivers/usb/dwc2/gadget.cint dwc2_gadget_init(struct dwc2_hsotg *hsotg) {...hsotg->gadget.max_speed = USB_SPEED_HIGH;						// UDC(DWC2) 初始化配置hsotg->gadget.ops       = &dwc2_hsotg_gadget_ops;hsotg->gadget.name      = dev_name(dev);if (hsotg->dr_mode == USB_DR_MODE_OTG)hsotg->gadget.is_otg = 1;else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)hsotg->op_state = OTG_STATE_B_PERIPHERAL;... // 其他配置ret = dwc2_hsotg_hw_cfg(hsotg);...hsotg->num_of_eps = hsotg->hw_params.num_dev_ep;			// UDC 控制器支持的端点数量hsotg->num_of_eps++;										// ep0 对象申请hsotg->eps_in[0]  = devm_kzalloc(hsotg->dev, sizeof(struct dwc2_hsotg_ep), GFP_KERNEL);		hsotg->eps_out[0] = hsotg->eps_in[0];						// IN & OUT 端点使用同一 端点对象cfg = hsotg->hw_params.dev_ep_dirs;for (i = 1, cfg >>= 2; i < hsotg->num_of_eps; i++, cfg >>= 2) {ep_type = cfg & 3;if (!(ep_type & 2)) { ...								// IN 端点创建if (!(ep_type & 1)) {									// OUT 端点创建hsotg->fifo_mem = hsotg->hw_params.total_fifo_size;			// FIFO 信息初始化hsotg->dedicated_fifos = hsotg->hw_params.en_multiple_tx_fifo;return 0;}hsotg->ctrl_buff = devm_kzalloc(hsotg->dev, DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);	// 申请 ctrl_buffhsotg->ep0_buff  = devm_kzalloc(hsotg->dev, DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);	// 申请 ep0_buff... // DMA 相关ret = devm_request_irq(hsotg->dev, hsotg->irq, dwc2_hsotg_irq, IRQF_SHARED, dev_name(hsotg->dev), hsotg);		// 中断申请{共享中断,},回调函数:dwc2_hsotg_irqINIT_LIST_HEAD(&hsotg->gadget.ep_list);hsotg->gadget.ep0 = &hsotg->eps_out[0]->ep;hsotg->ctrl_req = dwc2_hsotg_ep_alloc_request(&hsotg->eps_out[0]->ep, GFP_KERNEL);			struct dwc2_hsotg_req *req = kzalloc(sizeof(*req), flags);	// 申请 request 内存,用于 ep0INIT_LIST_HEAD(&req->queue);return &req->req;											// 由 dwc2_hsotg_req 封闭 usb_request}		for (epnum = 0; epnum < hsotg->num_of_eps; epnum++) {			// 遍历所有 端点,初始化 IN & OUT 端点if (hsotg->eps_in[epnum])dwc2_hsotg_initep(hsotg, hsotg->eps_in[epnum], epnum, 1);		if (hsotg->eps_out[epnum])dwc2_hsotg_initep(hsotg, hsotg->eps_out[epnum], epnum, 0);	dwc2_hsotg_dump(hsotg);											// DWC2 寄存器信息打印return 0;
}	int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) {return usb_add_gadget_udc_release(parent, gadget, NULL);usb_initialize_gadget(parent, gadget, release);dev_set_name(&gadget->dev, "gadget");...device_initialize(&gadget->dev);						// 添加 gadget 设备 Device 对象ret = usb_add_gadget(gadget);int usb_add_gadget(struct usb_gadget *gadget) {struct usb_udc		*udc;...udc = kzalloc(sizeof(*udc), GFP_KERNEL);						// udc 设备申请device_initialize(&udc->dev);									// udc 设备实例化udc->dev.release = usb_udc_release;udc->dev.class   = udc_class;udc->dev.groups  = usb_udc_attr_groups;udc->dev.parent  = gadget->dev.parent;ret = dev_set_name(&udc->dev, "%s", kobject_name(&gadget->dev.parent->kobj));	ret = device_add(&gadget->dev);									// 添加 Gadget 设备,可触发 sysfs uevent 事件udc->gadget = gadget;											// 建立 Gadget 与 udc 关系映射gadget->udc = udc;mutex_lock(&udc_lock);list_add_tail(&udc->list, &udc_list);							// 添加当前 udc 至 udc_list 链表ret = device_add(&udc->dev);									// 添加 UDC 设备usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);udc->vbus = true;												// 置位 UDC 设备 VBUS/* pick up one of pending gadget drivers */ret = check_pending_gadget_drivers(udc);						// 检查当前 UDC 设备是否有匹配的 gadget_driver
中断相关

DWC2 控制器驱动使用 devm_request_irq 申请了两个中断(共享)

  • dwc2_handle_common_intr,会话类型中断,probe 阶段 ;
  • dwc2_hsotg_irq, 数据收发中断,probe.gadget_init 阶段;
//----> linux_5.10/drivers/usb/dwc2/gadget.cstatic irqreturn_t dwc2_hsotg_irq(int irq, void *pw) {...if (!dwc2_is_device_mode(hsotg))								// 仅当 Device 模式时处理中断return IRQ_NONE;spin_lock(&hsotg->lock);										// 加锁
irq_retry:gintsts = dwc2_readl(hsotg, GINTSTS);							// 读取中断寄存器 状态 与 掩码gintmsk = dwc2_readl(hsotg, GINTMSK);	gintsts &= gintmsk;if (gintsts & ... 												// 其他类型中断 if (gintsts & GINTSTS_ENUMDONE) {								// 枚举中断 dwc2_writel(hsotg, GINTSTS_ENUMDONE, GINTSTS);dwc2_hsotg_irq_enumdone(hsotg);	if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) {				// IN&OUT 中断 ...for (ep = 0; ep < hsotg->num_of_eps && daint_out; ep++, daint_out >>= 1) {if (daint_out & 1) dwc2_hsotg_epint(hsotg, ep, 0);for (ep = 0; ep < hsotg->num_of_eps  && daint_in; ep++, daint_in >>= 1) {if (daint_in & 1)   dwc2_hsotg_epint(hsotg, ep, 1);... // 其他中断类型处理if (gintsts & IRQ_RETRY_MASK && --retry_count > 0)				// 如果中断类型涉及 FIFO,进行 重试goto irq_retry;	... // 其他处理return IRQ_HANDLED;	
}static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, int dir_in) {...ints = dwc2_gadget_read_ep_interrupts(hsotg, idx, dir_in);		// 读中断状态dwc2_writel(hsotg, ints, epint_reg);							// 清中断 if (ints & DXEPINT_XFERCOMPL) {									// 收发完成中断 if (using_desc_dma(hsotg) && hs_ep->isochronous) {			//  ISO 异步包...} else if (dir_in) {		if (hs_ep->isochronous && hs_ep->interval > 1) ... 		// iso 包处理dwc2_hsotg_complete_in(hsotg, hs_ep);					// IN 包 complete 处理if (idx == 0 && !hs_ep->req)							// ep0 端点0 的包dwc2_hsotg_enqueue_setup(hsotg);					// IN 包 enqueue  处理} else if (using_dma(hsotg)) {...dwc2_hsotg_handle_outdone(hsotg, idx);					// OUT 包处理}if (ints & ...	// 其他中断类型处理
}//----> linux_5.10/drivers/usb/dwc2/core_intr.cirqreturn_t dwc2_handle_common_intr(int irq, void *dev) {struct dwc2_hsotg *hsotg = dev;...spin_lock(&hsotg->lock);if (!dwc2_is_controller_alive(hsotg)) { ...                     // 检查 dwc 控制器是否工作正常if (dwc2_is_device_mode(hsotg))                                 // Device 模式下,读取 帧数hsotg->frame_number = (dwc2_readl(hsotg, DSTS) & DSTS_SOFFN_MASK) >> DSTS_SOFFN_SHIFT;	else ...gintsts = dwc2_read_common_intr(hsotg);                         // 读取中断状态if (hsotg->hibernated) { ...                                    // hiberante 处理	if (gintsts & GINTSTS_SESSREQINT)                               // 会话请求型 中断 dwc2_handle_session_req_intr(hsotg);if (gintsts & GINTSTS_PRTINT) {                                 // 端口打印型 中断if (dwc2_is_device_mode(hsotg)) {dwc2_handle_usb_port_intr(hsotg);retval = IRQ_HANDLED;			... // 其他中断类型处理			
out:spin_unlock(&hsotg->lock);return retval;
}				static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) {...dwc2_writel(hsotg, GINTSTS_SESSREQINT, GINTSTS);if (dwc2_is_device_mode(hsotg)) {	if (hsotg->lx_state == DWC2_L2) {if (hsotg->in_ppd) {ret = dwc2_exit_partial_power_down(hsotg, 0, true);	if (hsotg->params.power_down == DWC2_POWER_DOWN_PARAM_NONE && hsotg->bus_suspended)dwc2_gadget_exit_clock_gating(hsotg, 0);				dwc2_hsotg_disconnect(hsotg);...
}	
数据包收发
//----> linux_5.10/drivers/usb/dwc2/gadget.c/*** dwc2_hsotg_enqueue_setup - start a request for EP0 packets...* Enqueue a request on EP0 if necessary to received any SETUP packets* received from the host.** 如果需要,可以向 EP0 端点 提交一个请求以便接收来自 Host 的 SETIUP 包。**/ 
static void dwc2_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg)		// 空 req 入列struct usb_request *req = hsotg->ctrl_req;		struct dwc2_hsotg_req *hs_req = our_req(req);...req->zero     = 0;req->length   = 8;req->buf      = hsotg->ctrl_buff;req->complete = dwc2_hsotg_complete_setup;						// complete 处理hsotg->eps_out[0]->dir_in   = 0;hsotg->eps_out[0]->send_zlp = 0;hsotg->ep0_state            = DWC2_EP0_SETUP;ret = dwc2_hsotg_ep_queue(&hsotg->eps_out[0]->ep, req, GFP_ATOMIC);	...
}static void dwc2_hsotg_complete_setup(struct usb_ep *ep, struct usb_request *req) {struct dwc2_hsotg_ep *hs_ep = our_ep(ep);struct dwc2_hsotg *hsotg = hs_ep->parent;...spin_lock(&hsotg->lock);if (req->actual == 0)					dwc2_hsotg_enqueue_setup(hsotg);							// 对 空包 继续等待elsedwc2_hsotg_process_control(hsotg, req->buf);				// 对 非空包 上交处理static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags) {struct dwc2_hsotg_req *hs_req = our_req(req);struct dwc2_hsotg_ep *hs_ep = our_ep(ep);struct dwc2_hsotg *hs = hs_ep->parent;...first = list_empty(&hs_ep->queue);list_add_tail(&hs_req->queue, &hs_ep->queue);...if (first) {...if (hs_ep->target_frame != TARGET_FRAME_INITIAL)dwc2_hsotg_start_req(hs, hs_ep, hs_req, false);}return 0;
}	/*** dwc2_hsotg_process_control - process a control request* * The controller has received the SETUP phase of a control request, and* needs to work out what to do next (and whether to pass it on to the* gadget driver).* * UDC 已经收到 SETUP 阶段的控制包请求,并判断后续动作(是否需要将其上报给 Gadget 驱动)*/
static void dwc2_hsotg_process_control(struct dwc2_hsotg *hsotg, struct usb_ctrlrequest *ctrl) {struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0];...if (ctrl->wLength == 0) {										// 包类型确认ep0->dir_in = 1;hsotg->ep0_state = DWC2_EP0_STATUS_IN;} else if (ctrl->bRequestType & USB_DIR_IN) {ep0->dir_in = 1;hsotg->ep0_state = DWC2_EP0_DATA_IN;} else {ep0->dir_in = 0;hsotg->ep0_state = DWC2_EP0_DATA_OUT;}	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {switch (ctrl->bRequest) {case USB_REQ_SET_ADDRESS: ...hsotg->connected = 1;dcfg  = dwc2_readl(hsotg, DCFG);dcfg &= ~DCFG_DEVADDR_MASK;dcfg |= (le16_to_cpu(ctrl->wValue) << DCFG_DEVADDR_SHIFT) & DCFG_DEVADDR_MASK;dwc2_writel(hsotg, dcfg, DCFG);ret = dwc2_hsotg_send_reply(hsotg, ep0, NULL, 0);		// 发送回应包return;... // 其他case,含:USB_REQ_GET_STATUS、USB_REQ_CLEAR_FEATURE、USB_REQ_SET_FEATUREif (ret == 0 && hsotg->driver) {spin_unlock(&hsotg->lock);ret = hsotg->driver->setup(&hsotg->gadget, ctrl);			// 将其他类型包上报给 Gadget 驱动处理(无论 Legace、Fuction模式,最终都是 composite_setup 再到 上层 setup)...
}/*** dwc2_hsotg_start_req - start a USB request from an endpoint's queue...* Start the given request running by setting the endpoint registers* appropriately, and writing any data to the FIFOs.*/
static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg, struct dwc2_hsotg_ep *hs_ep, struct dwc2_hsotg_req *hs_req, bool continuing) {struct usb_request *ureq = &hs_req->req;...dma_reg    = dir_in ? DIEPDMA(index)  : DOEPDMA(index);epctrl_reg = dir_in ? DIEPCTL(index)  : DOEPCTL(index);epsize_reg = dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index);...length = ureq->length - ureq->actual;... // zlp(zero length packet) 包处理/* store the request as the current one we're doing */hs_ep->req = hs_req;											// 使用当前控制器的 reqif (using_desc_dma(hsotg)) { ...								// desc_dma 相关} else {dwc2_writel(hsotg, epsize, epsize_reg);						// 写数据...if (hs_ep->isochronous && hs_ep->interval == 1) { ...			// 同步传输,包号更新处理ctrl |= DXEPCTL_EPENA;	/* ensure ep enabled */			if (!(index == 0 && hsotg->ep0_state == DWC2_EP0_SETUP))ctrl |= DXEPCTL_CNAK;	/* clear NAK set by core */dwc2_writel(hsotg, ctrl, epctrl_reg);							// ctrl 相关配置hs_ep->size_loaded = length;hs_ep->last_load   = ureq->actual;  							// 更新端点 FIFO信息if (dir_in && !using_dma(hsotg)) {								// 非 DMA 方式则直接向 FIFO 写入数据hs_ep->fifo_load = 0;dwc2_hsotg_write_fifo(hsotg, hs_ep, hs_req);		dwc2_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 1);	// 使能端点中断
}

摘要:

  • UDC 作为 Gadget 初始化时,一并负责了 ep0 端点的初始化;
  • DWC2 控制器状态参考枚举 dwc2_lx_state 可知: DWC2_L2 为 Suspend 状态;
  • 使用 DECLARE_USB_FUNCTION 声明作用
    • MODULE_ALIAS(“usbfunc:”“SourceSink”),对应 modprobe 时的 request_module(“usbfunc:%s”, name);
    • usb_get_function_instance 时通过 .name 匹配目标驱动,使用 .alloc_inst 实例化;

http://www.mrgr.cn/news/82434.html

相关文章:

  • 07-ArcGIS For JavaScript--隐藏参数qualitySettings(memory和lod控制)
  • 【2025最新计算机毕业设计】基于Spring Boot+Vue影院购票系统(高质量源码,提供文档,免费部署到本地)
  • Azkaban其二,具体使用以及告警设置
  • 光隔离探头特点和应用领域
  • 互联网视频和新媒体领域 - MCN 极简理解
  • VS2015中使用boost库函数时报错问题解决error C4996 ‘std::_Copy_impl‘
  • 动态规划<八> 完全背包问题及其余背包问题
  • 国内Ubuntu环境Docker部署CosyVoice
  • 国内Ubuntu环境Docker部署Stable Diffusion入坑记录
  • 多模态论文笔记——Coca
  • 多模态论文笔记——CogVLM和CogVLM2(副)
  • redis的集群模式与ELK基础
  • 如何从文档创建 RAG 评估数据集
  • .Net Core配置系统
  • U8G2库使用案例(stm32)
  • 计算机网络原理(谢希仁第八版)第4章课后习题答案
  • Java-list均分分割到多个子列表
  • Unity+Hybridclr发布WebGL记录
  • [Hive]七 Hive 内核
  • springboot3+vue项目实践-黑马
  • 大模型WebUI:Gradio全解系列10——Additional Features:补充特性(下)
  • 【开源社区openEuler实践】qemu
  • UML之泛化、特化和继承
  • YOLO11改进 | 卷积模块 | ECCV2024 小波卷积
  • Linux下部署Redis集群 - 一主二从三哨兵模式
  • mysql 事物隔离级别 与mvcc