编译链接
#----》 linux_5.10/drivers/usb/gadget/Makefile
obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
libcomposite-y := usbstring.o config.o epautoconf.o
libcomposite-y += composite.o functions.o configfs.o u_f.o
obj-$(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/Kconfig
config USB_ZERO
tristate "Gadget Zero (DEVELOPMENT)"
select USB_LIBCOMPOSITE // 勾选 USB_LIBCOMPOSITE、 USB_F_SS_LB
select 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 set
CONFIG_TARGET_PACKAGE_BUSYBOX_SYSLOGD_SCRIPT=y
CONFIG_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.c
module_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_speed
gadget_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 对象进行 probe
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,
},
};
//----> 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_list
if (driver->udc_name) { ... } // 如果指定了目标 UDC 硬件,进行特殊 probe
else {
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 链表, 等待下一次 probe
list_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.c
static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver) {
...
udc->driver = driver; // 实际指向:composite_driver_template
udc->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.c
static 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
实现,过程又可分为三个步骤:
- Bind,gadget驱动私有的 bind 实现
- Legacy 模式下由 composite_bind 代为实现,Function下
- Stasrt,配置 UDC,绑定 UDC 设备与 gadget 驱动;
- Connect,发起 UDC 设备连接;
回到 zero_driver 驱动,查看 bind 阶段的实现
//----> linux_5.10/drivers/usb/gadget/legacy/zero.c
static 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 申请,目标:SourceSink
ss_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,其次 Loopback
else {
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 关联 cdev
list_add_tail(&config->list, &cdev->configs); // cdev 关联 configs
INIT_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); // 更新 options
INFO(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 */
};
匹配步骤:
- 按名字查找已添加到 func_list 链表中的 Function;
- 查找到目标 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.c
struct 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、Loopback
continue;
...
fi = fd->alloc_inst(); // 调用具体 function 的私有 alloc_inst 实现,如:source_sink_alloc_inst
if (IS_ERR(fi)) ... else
fi->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.c
DECLARE_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_buflen
return &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.c
int 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.c
static 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.c
static 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 即 Device
ret = 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);
else
usb_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 feature
dwc2_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_SS
bool "Loopback and sourcesink function (for testing)"
depends on USB_CONFIGFS
select 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.c
module_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 实例化,参考内核文档 `` 测试步骤如下:
- 使用 configfs 在用户空间创建 gadget function;
- 使用测试用例
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/UDC
echo 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.c
module_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_template
gi->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.c
CONFIGFS_ATTR(gadget_dev_desc_, UDC); // configfs 属性节点 UDC
static 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
;
- Legacy zere 驱动,使用宏 module_usb_composite_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;
- Legacy zero 借助 usb_gadget_driver 驱动模板(composite_driver_template) 修改后传入
总结
- Legacy zero 的方法更贴近 libcomposite 框架的(直接调用相关API),手段灵活,但开发难度相对较高;
- Function 集成度更高,可使用 configfs 方式高效开发、调试,但可配置内容受 function 实现的内容限定;
UDC 驱动
udc_list 链表由 USB 控制器以平台设备(of)接入系统并与驱动 probe 过程中注册到 USB composite driver 框架
#----> linux_5.10/drivers/usb/dwc2/platform.c
module_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.c
int 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_buff
hsotg->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_irq
INIT_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 内存,用于 ep0
INIT_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.c
static 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.c
irqreturn_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); // 对 空包 继续等待
else
dwc2_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_FEATURE
if (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; // 使用当前控制器的 req
if (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
实例化;