Linux USB Gadget 框架中数据结构体之间的关系
1. 什么是 USB Gadget 框架?
简单来说,USB Gadget 框架 是 Linux 内核中一套允许设备作为 USB 从设备(或称为“外设”)运行的软件基础设施。
- USB Host(主机): 通常是你的电脑、手机或充电器。它控制总线,为设备供电。我们电脑上的 USB 口就是 Host 口。
- USB Gadget(外设): 通常是 U 盘、键盘、鼠标、打印机等。它们响应主机的请求。
当你的嵌入式设备(如树莓派、智能手机、IoT 设备)通过 USB 线连接到电脑时,USB Gadget 框架就决定了这个嵌入式设备在电脑眼中“是什么”。它可以让你的开发板在电脑上显示为一个 U 盘、一个网卡、一个串口,或者多种功能的复合设备。
核心思想: 它提供了一个标准化的、可扩展的软件接口,让开发者能够在不关心底层 USB 控制器的具体硬件差异的情况下,配置和实现各种 USB 设备功能。
2. 核心组件与层次结构
USB Gadget 框架采用分层的架构,如下图所示(概念图):
+---------------------------------------------------+
| Gadget Function Drivers |
| (e.g., f_acm.ko, f_serial.ko, f_mass_storage.ko |
+---------------------------------------------------+
| Gadget API (UDC Core) |
| (drivers/usb/gadget/udc/core.c) |
+---------------------------------------------------+
| USB Device Controller (UDC) Driver |
| (e.g., dwc3.ko, udc-core.c, chip-specific) |
+---------------------------------------------------+
| Hardware (USB Device Controller) |
+---------------------------------------------------+
让我们自底向上地理解这些组件:
2.1 USB 设备控制器(UDC)硬件
这是 SoC 或微控制器内部的一个物理硬件模块,负责处理底层的 USB 协议、电气信号和数据处理。例如,DWC3。
2.2 UDC 驱动
这是直接操作 UDC 硬件的内核驱动程序。它负责:
- 初始化硬件。
- 管理 USB 端点(Endpoints)。
- 处理 USB 请求块(URB)。
- 向 Gadget 核心层注册自己,表明“我这个硬件控制器可以被用来做 Gadget”。
关键结构体: struct usb_udc 和 struct usb_gadget_ops(包含一系列操作硬件的函数指针)。
2.3 Gadget API / UDC 核心层
这是框架的核心,位于 drivers/usb/gadget/udc/core.c。它充当 UDC 驱动和上层功能驱动的“粘合剂”。它提供了一组统一的 API,使得功能驱动无需关心底层是哪种 UDC 硬件。
2.4 Gadget Function 驱动
这是实现具体 USB 设备功能的驱动程序。例如:
- f_acm: 实现 USB CDC 串行(ACM)功能驱动程序。
- f_serial: 实现通用 USB 串行功能驱动程序。
- f_mass_storage: 实现大容量存储设备(U盘)的驱动程序。
关键结构体: struct usb_function 和 struct usb_configuration。
一个 Function 驱动定义了:
- USB 设备描述符: 告诉主机“我是什么”。
- 接口 和 端点: 实现功能所需的通信管道。
- 数据处理回调函数: 当主机发送或请求数据时应该做什么。
3. USB Gadget 数据结构之间的关系
- 分析所用代码基于linux kernel5.4
先上个根据分析结果绘制的数据结构关系图:
3.1 注册 Gadget Function 的驱动usb_function_driver
在数据结构关系图靠下的位置有个struct list_head类型的全局变量 func_list 。func_list 是全局usb_function_driver实例链表,用于管理所有已注册的 usb_function_driver实例。
以 f_acm 为例看下usb_function_driver如何注册到 func_list
drivers/usb/gadget/function/f_acm.c
DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func);
DECLARE_USB_FUNCTION_INIT宏定义实现:
include/linux/usb/composite.h
#define DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc) \
static struct usb_function_driver _name ## usb_func = { \
.name = __stringify(_name), \
.mod = THIS_MODULE, \
.alloc_inst = _inst_alloc, \
.alloc_func = _func_alloc, \
}; \
MODULE_ALIAS("usbfunc:"__stringify(_name));
#define DECLARE_USB_FUNCTION_INIT(_name, _inst_alloc, _func_alloc) \
DECLARE_USB_FUNCTION(_name, _inst_alloc, _func_alloc) \
static int __init _name ## mod_init(void) \
{ \
return usb_function_register(&_name ## usb_func); \
} \
static void __exit _name ## mod_exit(void) \
{ \
usb_function_unregister(&_name ## usb_func); \
} \
module_init(_name ## mod_init); \
module_exit(_name ## mod_exit)
所以DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func);展开后:
static struct usb_function_driver acmusb_func = {
.name = __stringify(acm),
.mod = THIS_MODULE,
.alloc_inst = acm_alloc_instance,
.alloc_func = acm_alloc_func,
};
MODULE_ALIAS("usbfunc:"__stringify(acm));
static int __init acmmod_init(void)
{
return usb_function_register(&acmusb_func);
}
static void __exit acmmod_exit(void)
{
usb_function_unregister(&acmusb_func);
}
module_init(acmmod_init);
module_exit(acmmod_exit);
kernel 启动后, 调用 acmmod_init() --> usb_function_register(&acmusb_func) 直接把 acmusb_func : usb_function_driver 注册到 func_list.
所有 usb_function_driver 注册完成后, func_list 链表内容示例如下:
func_list (全局 USB 函数驱动链表)
│
├── usb_function_driver (acm)
│ ├── name = "acm"
│ ├── mod = f_acm.ko 模块
│ ├── alloc_inst = acm_alloc_instance()
│ ├── alloc_func = acm_alloc_func()
│ └── list (链表节点)
│
├── usb_function_driver (gser)
│ ├── name = "gser"
│ ├── mod = f_serial.ko 模块
│ ├── alloc_inst = gser_alloc_inst()
│ ├── alloc_func = gser_alloc()
│ └── list
│
└── usb_function_driver (mass_storage)
├── name = "mass_storage"
├── mod = f_mass_storage.ko 模块
├── alloc_inst = fsg_alloc_inst()
├── alloc_func = fsg_alloc()
└── list
3.2 用户空间通过 configfs 配置 USB Gadget 的步骤说明
从 Linux 3.11 开始,引入了 USB Gadget ConfigFS。它通过一个虚拟文件系统 (/config/usb-gadget) 允许用户在用户空间动态地、无需编译地创建和配置 USB Gadget。
3.2.1 挂载 configfs 虚拟文件系统
mount configfs none /config
- 内核虽然支持 configfs,但默认不挂载
- 挂载后才能在 /config 目录下看到配置接口
- 通过文件操作(创建目录/文件、写入配置)来与内核交互
3.2.2 创建 gadget
命令:
mkdir /config/usb_gadget/g1 0770 shell shell
代码:
drivers/usb/gadget/configfs.c
static struct config_group *gadgets_make(
struct config_group *group,
const char *name)
{
struct gadget_info *gi;
gi = kzalloc(sizeof(*gi), GFP_KERNEL);
gi->composite.bind = configfs_do_nothing;
gi->composite.gadget_driver = configfs_driver_template;
gi->composite.gadget_driver.function = kstrdup(name, GFP_KERNEL);
gi->composite.name = gi->composite.gadget_driver.function;
}
static struct configfs_group_operations gadgets_ops = {
.make_group = &gadgets_make,
.drop_item = &gadgets_drop,
};
static const struct config_item_type gadgets_type = {
.ct_group_ops = &gadgets_ops,
.ct_owner = THIS_MODULE,
};
static struct configfs_subsystem gadget_subsys = {
.su_group = {
.cg_item = {
.ci_namebuf = "usb_gadget", // 在 /config/ 下的目录名
.ci_type = &gadgets_type, // 绑定类型
},
},
.su_mutex = __MUTEX_INITIALIZER(gadget_subsys.su_mutex),
};
static int __init gadget_cfs_init(void)
{
int ret;
config_group_init(&gadget_subsys.su_group); // 初始化组结构
ret = configfs_register_subsystem(&gadget_subsys); // 注册子系统
return ret;
}
module_init(gadget_cfs_init);
- configfs 的初始化函数
gadget_cfs_init注册 "usb_gadget" 子系统到 configfs - gadget_subsys.cg_item.ci_type 绑定类型 gadgets_type
- gadgets_type 的 ct_group_ops 配置了操作函数 gadgets_ops
- make_group: 当用户在 /config/usb_gadget/ 下创建新目录时调用的函数
- drop_item: 当用户删除该目录时调用的清理函数
当创建目录 /config/usb_gadget/g1 时,内核会调用 gadgets_make():
- 创建代表 g1 的 struct gadget_info
- 初始化 usb_composite_driver
- 初始化 usb_composite_dev
- 将 configfs_driver_template 赋值给 gi->composite.gadget_driver
3.2.3 创建配置
命令:
mkdir /config/usb_gadget/g1/configs/b.1 0770 shell shell
代码:
drivers/usb/gadget/configfs.c
/* 定义配置描述符的组操作 */
static struct configfs_group_operations config_desc_ops = {
.make_group = &config_desc_make, // 创建子组时调用
.drop_item = &config_desc_drop, // 删除子组时调用
};
/* 定义配置描述符类型 */
static const struct config_item_type config_desc_type = {
.ct_group_ops = &config_desc_ops, // 绑定组操作
.ct_owner = THIS_MODULE,
};
static struct config_group *gadgets_make(
struct config_group *group,
const char *name)
{
/* 初始化子目录"configs" */
config_group_init_type_name(&gi->configs_group, "configs",
&config_desc_type);
}
函数 config_desc_make():
static struct config_group *config_desc_make(
struct config_group *group,
const char *name)
{
struct gadget_info *gi;
struct config_usb_cfg *cfg;
gi = container_of(group, struct gadget_info, configs_group);
cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
cfg->c.label = kstrdup(buf, GFP_KERNEL); //'b'
cfg->c.bConfigurationValue = num; // 1
ret = usb_add_config_only(&gi->cdev, &cfg->c);
}
因此在 configs 目录下创建子目录时会调用 config_desc_make():
- 创建代表 b.1 的 struct config_usb_cfg
- 初始化 config_usb_cfg 里的 usb_configuration
usb_add_config_only():
- 将步骤 3.2.2 中的 usb_composite_dev 赋值给 usb_configuration.cdev
- 将 usb_configuration 加入到步骤 3.2.2 中的 usb_composite_dev.configs
经过 usb_add_config_only() 后:
- gadget_info 可以通过 usb_composite_dev 遍历 usb_configuration 和 config_usb_cfg 实例。
- config_usb_cfg 也可以通过 usb_configuration.cdev 取得对应的 gadget_info 实例, 例如 config_usb_cfg_link() 里的操作。
3.2.4 创建 function
命令:
mkdir /config/usb_gadget/g1/functions/acm.gs0 0770 shell shell
代码:
drivers/usb/gadget/configfs.c
static struct configfs_group_operations functions_ops = {
.make_group = &function_make,
.drop_item = &function_drop,
};
static const struct config_item_type functions_type = {
.ct_group_ops = &functions_ops,
.ct_owner = THIS_MODULE,
};
static struct config_group *gadgets_make(
struct config_group *group,
const char *name)
{
config_group_init_type_name(&gi->functions_group, "functions",
&functions_type);
}
在 functions 目录下创建子目录时会调用 function_make():
static struct config_group *function_make(
struct config_group *group,
const char *name)
{
struct gadget_info *gi;
struct usb_function_instance *fi;
char buf[MAX_NAME_LEN];
char *func_name;
char *instance_name;
int ret;
/* name = acm.gs0 */
ret = snprintf(buf, MAX_NAME_LEN, "%s", name);
func_name = buf;
instance_name = strchr(func_name, '.');
*instance_name = '\0'; // func_name = acm
instance_name++; // instance_name = gs0
fi = usb_get_function_instance(func_name);
gi = container_of(group, struct gadget_info, functions_group);
list_add_tail(&fi->cfs_list, &gi->available_func);
}
获取 usb_function_instance 实例的代码:
drivers/usb/gadget/functions.c
struct usb_function_instance *usb_get_function_instance(const char *name)
{
return try_get_usb_function_instance(name);
}
static struct usb_function_instance *try_get_usb_function_instance(const char *name)
{
struct usb_function_driver *fd;
struct usb_function_instance *fi;
list_for_each_entry(fd, &func_list, list) {
if (strcmp(name, fd->name))
continue;
fi = fd->alloc_inst();
fi->fd = fd;
break;
}
return fi;
}
try_get_usb_function_instance():
- 根据传入的 name 从 3.1 中的 func_list 找出已注册的 usb_function_driver
- 调用 alloc_inst() 创建 usb_function_instance
- acm 对应的 alloc_inst() = acm_alloc_instance(), acm_alloc_instance() 会创建 f_serial_opts 和 usb_function_instance实例
- usb_function_driver 赋值给 usb_function_instance.fd
- 返回 usb_function_instance 直至 function_make
function_make() 获取到 usb_function_instance 后:
- 将 usb_function_instance 关联至 gadget_info.available_func
3.2.5 链接 function 到配置
命令:
symlink /config/usb_gadget/g1/functions/acm.gs0 /config/usb_gadget/g1/configs/b.1/f5
代码:
drivers/usb/gadget/configfs.c
static struct configfs_item_operations gadget_config_item_ops = {
.allow_link = config_usb_cfg_link,
.drop_link = config_usb_cfg_unlink,
};
static const struct config_item_type gadget_config_type = {
.ct_item_ops = &gadget_config_item_ops,
};
static struct config_group *config_desc_make(
struct config_group *group,
const char *name)
{
config_group_init_type_name(&cfg->group, name,
&gadget_config_type);
}
上面代码配置了 configs 的子目录(如 b.1) 下执行 symlink 操作会调用的函数:config_usb_cfg_link()
drivers/usb/gadget/configfs.c
/*
* usb_cfg_ci 代表 configs/b.1/f5
* usb_func_ci 代表 functions/acm.gs0
*/
static int config_usb_cfg_link(
struct config_item *usb_cfg_ci,
struct config_item *usb_func_ci)
{
struct config_usb_cfg *cfg = to_config_usb_cfg(usb_cfg_ci);
struct usb_composite_dev *cdev = cfg->c.cdev;
struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev);
struct config_group *group = to_config_group(usb_func_ci);
/* 获取 acm 对应的 usb_function_instance */
struct usb_function_instance *fi = container_of(group,
struct usb_function_instance, group);
struct usb_function_instance *a_fi;
struct usb_function *f;
/* 创建 acm 对应的 usb_funcrion */
f = usb_get_function(fi);
/* usb_funcrion 绑定到 config_usb_cfg.func_list */
list_add_tail(&f->list, &cfg->func_list);
}
usb_get_function() 调用 usb_function_driver.alloc_func 创建 usb_function:
drivers/usb/gadget/functions.c
struct usb_function *usb_get_function(struct usb_function_instance *fi)
{
struct usb_function *f;
f = fi->fd->alloc_func(fi);
if (IS_ERR(f))
return f;
f->fi = fi;
return f;
}
对 acm 来说, fi->fd->alloc_func = acm_alloc_func:
drivers/usb/gadget/function/f_acm.c
/*
struct f_acm {
struct gserial port;
}
struct gserial {
struct usb_function func;
}
*/
static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
{
struct f_serial_opts *opts;
struct f_acm *acm;
acm = kzalloc(sizeof(*acm), GFP_KERNEL);
acm->port.func.name = "acm";
acm->port.func.strings = acm_strings;
/* descriptors are per-instance copies */
acm->port.func.bind = acm_bind;
acm->port.func.set_alt = acm_set_alt;
acm->port.func.setup = acm_setup;
opts = container_of(fi, struct f_serial_opts, func_inst);
acm->port_num = opts->port_num;
return &acm->port.func;
}
总结 config_usb_cfg_link() 做的事情:
- 创建 usb_function 实例,
- 将 usb_function 实例添加到 config_usb_cfg.func_list
3.2.6. 启用 UDC
命令:
write /config/usb_gadget/g1/UDC ${sys.usb.controller} //"20000000.dwc3"
调用链:
用户空间
↓
echo "20000000.dwc3" > /config/usb_gadget/g1/UDC
↓
gadget_dev_desc_UDC_store()
↓
usb_gadget_probe_driver(gadget_info.composite.gadget_driver) //传入步骤3.2.2 的 configfs_driver_template
↓
从全局变量 udc_list 找出 "20000000.dwc3" 的 usb_udc
↓
udc_bind_to_driver()
↓
driver->bind() = configfs_composite_bind()
↓
遍历 gadget_info.cdev.configs 里所有 usb_configuration (如: configs/b.1)
↓
取出包含 usb_configuration 的 config_usb_cfg
↓
遍历 config_usb_cfg.func_list 中的 usb_function (如: acm.gs0)
↓
usb_add_function() 绑定 function 到 usb_configuration.functions
↓
执行 function->bind() (如: acm_bind())
↓
usb_gadget_udc_start() 启动 udc
↓
USB 设备就绪
启用 UDC 后,主机端就会检测到一个新的 USB 设备(如: acm)。