文章目录
- 1. 前言
- 2. 背景
- 3. USB Gadget 驱动
- 3.1 什么是 USB Gadget 驱动?
- 3.2 USB Gadget 驱动框架
- 3.3 USB 设备控制器(UDC) 驱动
- 3.3.1 USB 设备控制器(UDC) 驱动 概述
- 3.3.2 USB 设备控制器(UDC) 驱动示例
- 3.4 USB Gadget Function 驱动
- 3.5 USB Gadget 驱动
- 3.5.1 USB Gadget 驱动的加载
- 3.5.1.1 启动 UDC: 上拉 D+
- 3.5.1.2 设备枚举
- 3.5.2 USB Gadget 驱动数据交流过程
- 3.5.3 USB Gadget 驱动的卸载
1. 前言
限于作者能力水平,本文可能存在谬误,因此而给读者带来的损失,作者不做任何承诺。
2. 背景
本文所有分析基于 Linux 4.14
内核代码。
3. USB Gadget 驱动
3.1 什么是 USB Gadget 驱动?
USB 设备驱动,按照设备端关联的 USB 控制器
是工作在 主模式
还是 从模式
,分为 USB 设备主机侧驱动 (主模式)
,或者 USB 设备从机侧驱动 (从模式)
。同时,工作在 主模式
的 USB 控制器
,称为 USB 主机控制器 (UHC: USB Host Controller)
,工作在 从模式
的 USB 控制器
,称为 USB 设备控制器 (UDC: USB Device Controller)
。有的 USB 控制器
,只能工作在 主模式
或 从模式
中的某一种;而有的则既可以工作在 主模式
,也可以工作在 从模式
,模式通过 OTG
切换。当然,在同一时刻,USB 控制器
要么工作在 主模式
,要么工作在 从模式
。
本文的重点是 USB 设备从机侧驱动 (从模式)
,Linux 下将 USB 设备从机侧驱动
,称为 USB Gadget 驱动
。USB Gadget 驱动
是通过 USB 来模拟其它类型的设备,如 USB Gadget UAC 驱动
用来模拟声卡外设;USB Gadget Serial 驱动
用来模拟串口外设,等等等等。这里所谓模拟,是指通过 USB 来模拟这些设备的行为,而这些对于连接对端的 USB 主机
是透明的。对于 USB Gadget 驱动
,类似于譬如像 U 盘设备的固件,但它们并不完全等同,因为毕竟只是通过 USB 模拟设备行为。
3.2 USB Gadget 驱动框架
正如本小节中框图所示,USB Gadget 驱动
,包括 USB 设备控制器(UDC) 驱动
和 Gadget 功能(function)驱动
两大部分。其中 USB 设备控制器(UDC) 驱动
负责 USB 设备控制器(UDC)
和 主机侧 USB 控制器(UHC)
之间的数据传输;而 Gadget 功能驱动(function)
负责实现功能协议(如 UDC 等)。USB 设备控制器(UDC) 驱动
和 Gadget 功能驱动(function)
彼此之间也会进行数据交互。
在进一步对 USB Gadget 驱动
做更细节的描述前,我们通过下图,先让大家对 USB Gadget 驱动框架
做一个初步认识(图片来自于网络,具体出处已不可考):
实际上本文重点只涉及上图中右侧红框中的部分
,之所有列出左边部分,是想让大家对整个 USB 驱动框架有一个整体认识。左边的部分,不仅限于 Linux,可以是任何支持 USB 主机侧 系统,如 Windows, Mac OS 等其它系统。上图中我们列出的是 Linux 系统,Linux 系统本身既包含 USB 设备主机侧驱动
,又包含 USB 设备从机侧驱动(即 USB Gadget 驱动)
,所以 Linux 自身就形成一个完整的 USB 驱动框架闭环。
接下来内容,我们将按上图中右半部分
所示,逐个来讲述框架中的每个部分。
3.3 USB 设备控制器(UDC) 驱动
3.3.1 USB 设备控制器(UDC) 驱动 概述
所有的 USB Gadget 驱动
,最终都是通过 USB 设备控制器(UDC)
,和 主机侧的 USB 控制器(UHC)
进行交互;而且 UDC 是独占的
,一旦它被某个 USB Gadget 驱动 使用,直到该 USB Gadget 驱动 被卸载之前,其它的 USB Gadget 驱动 就不能使用它。
先来了解下 UDC
的硬件实现梗概 和 驱动代码组织。我们以常见的 双角色(既支持 主模式,又支持 从模式)
的 USB 控制器
为例,来了解 UDC
的硬件实现。绝大多数芯片厂商,不会自己实现 USB 控制器的所有部分,而是购买 USB 控制器 IP 库
,然后再以某个 IP 库为基础来实现 UDC 硬件。如 全志 H3 用 明导国际(Mentor Graphics)
的 MUSB
实现 UDC ,它的 硬件实现 和 驱动代码组织 梗概如下:
硬件 | 驱动-----------------|---------------------------------------------------UDC 的 厂商实现 | drivers/usb/musb/sunxi.c (UDC 厂商驱动部分)| | |V | VMUSB IP 库 | drivers/usb/musb/musb* (UDC MUSB IP 库驱动部分)| || V| drivers/usb/gadget/udc/core.c (UDC core 公共代码)
上面举例的是 全志 H3 对 MUSB 的实现,其它各个厂商对各类 USB IP 的实现类似。如 DesignWare
的 DWC3
也是经常被芯片厂商用来实现双角色 USB 控制器的 USB IP 库。DWC3 双角色 USB IP 的驱动实现于代码文件 drivers/usb/dwc3/dwc3*
中,而 UDC 驱动的其它部分类似于 全志 H3 的实现。对于单单支持 USB 设备控制器(UDC)
角色(从模式) 的驱动,代码实现于目录 drivers/usb/gadget/udc
下。
3.3.2 USB 设备控制器(UDC) 驱动示例
接下来,我们以 全志 H3 以 MUSB IP 库为基础,实现的 USB 设备控制器 驱动为例,来进行 UDC 驱动 的说明。先看下 MUSB 的 DTS 配置:
usb_otg: usb@01c19000 {compatible = "allwinner,sun8i-h3-musb";...phys = <&usbphy 0>; // 关联的 USB PHYphy-names = "usb";extcon = <&usbphy 0>;dr_mode = "otg"; // 设备模式配置为 OTG:即可以动态的切换 UHC 和 UDC 角色status = "okay";
};usbphy: phy@01c19400 {compatible = "allwinner,sun8i-h3-usb-phy";...// PG12 用于 OTG 角色检测usb0_id_det-gpios = <&pio 6 12 GPIO_ACTIVE_HIGH>; /* PG12 */usb0_vbus-supply = <®_usb0_vbus>;status = "okay";
};
DTS 配置的 usb_otg
指代 H3 实现的 双角色 USB 控制器
,usbphy
指代 USB 控制器使用的 USB PHY 设备
。先看 USB 控制器的驱动:
/* drivers/usb/musb/sunxi.c */
// MUSB 全志厂商驱动入口
static int sunxi_musb_probe(struct platform_device *pdev)
{struct musb_hdrc_platform_data pdata;struct platform_device_info pinfo;struct sunxi_glue *glue;...glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);switch (usb_get_dr_mode(&pdev->dev)) { // 获取 DTS 配置的模式...
#ifdef CONFIG_USB_MUSB_DUAL_ROLEcase USB_DR_MODE_OTG: // DTS 配置为 "otg" 模式,即双角色模式pdata.mode = MUSB_PORT_MODE_DUAL_ROLE;glue->phy_mode = PHY_MODE_USB_OTG;break;
#endif...}// 平台特定的接口: 初始化,寄存器读写,DMA, 模式设置, VBUS控制, ...pdata.platform_ops = &sunxi_musb_ops;if (!of_device_is_compatible(np, "allwinner,sun8i-h3-musb"))// 厂商硬件实现的端点(EndPoint)配置数据: 端点数目、方向(IN,OUT,IN&OUT), FIFO 缓冲深度 等pdata.config = &sunxi_musb_hdrc_config;else......memset(&pinfo, 0, sizeof(pinfo));pinfo.name = "musb-hdrc"; // MUSB IP 驱动匹配关键字...// 加载 MUSB IP 驱动: 厂商驱动 -> MUSB IP 驱动glue->musb_pdev = platform_device_register_full(&pinfo);...
}/* drivers/usb/musb/musb_core.c */
// MUSB IP 驱动入口,共实现厂商调用
static int musb_probe(struct platform_device *pdev)
{...return musb_init_controller(dev, irq, base);
}// 初始化 MUSB。这里只重点关注 从设模式 相关的初始化。
static int
musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
{struct musb *musb;musb = allocate_instance(dev, plat->config, ctrl);...musb->ops = plat->platform_ops; // 设置 MUSB 的厂商接口 (&sunxi_musb_ops)musb->port_mode = plat->mode;...status = musb_platform_init(musb); // 厂商特定初始化: sunxi_musb_init()musb->ops->init(musb) = sunxi_musb_init()...musb->isr = sunxi_musb_interrupt; // 厂商中断入口....../* setup musb parts of the core (especially endpoints) */// MUSB IP 核心初始化,特别是 USB 端点(EndPoint)数据初始化status = musb_core_init(plat->config->multipoint? MUSB_CONTROLLER_MHDRC: MUSB_CONTROLLER_HDRC, musb);...// 注册厂商初始化设定的中断处理接口 sunxi_musb_interrupt()// sunxi_musb_interrupt() -> musb_interrupt()// 厂商中断处理接口最终会调用 MUSB IP 的中断接口 musb_interrupt()if (request_irq(nIrq, musb->isr, IRQF_SHARED, dev_name(dev), musb)) {...}...// 按配置的模式, 初始化 MUSB IP 。// 再一次的,我们只关注 从设模式的相关初始化。switch (musb->port_mode) {...case MUSB_PORT_MODE_DUAL_ROLE: // 双角色模式, 我们只关注 从设模式 相关部分...// MUSB 从设模式 初始化.// 其中最重要的是会注册 UDC 对象到系统,其将和 USB Gadget Function 驱动组合到一起,// 形成一个完整的 USB Gadget 驱动.status = musb_gadget_setup(musb);...status = musb_platform_set_mode(musb, MUSB_OTG);break;}...
}// MUSB IP 核心初始化,特别是 USB 端点(EndPoint)数据初始化
static int musb_core_init(u16 musb_type, struct musb *musb)
{.../* configure ep0 */musb_configure_ep0(musb);/* discover endpoint configuration */musb->nr_endpoints = 1;musb->epmask = 1;// 端点数目、数据搜集 + 端点初始化(FIFO 配置等)if (musb->dyn_fifo)status = ep_config_from_table(musb);elsestatus = ep_config_from_hw(musb);...
}/* drivers/usb/musb/musb_gadget.c */
// MUSB 从设模式 初始化.
int musb_gadget_setup(struct musb *musb)
{...musb->g.ops = &musb_gadget_operations; // MUSB 从设模式 操作接口musb->g.max_speed = USB_SPEED_HIGH;musb->g.speed = USB_SPEED_UNKNOWN;musb->g.name = musb_driver_name; // "musb-hdrc"// MUSB 端点(EndPoint) 数据初始化,【包括】控制端点(EP0)musb_g_init_endpoints(musb);// 注册 UDC 设备对象 到系统status = usb_add_gadget_udc(musb->controller, &musb->g);...
}// 所有 MUSB 端点(EndPoint) 数据初始化,【包括】控制端点(EP0)
static inline void musb_g_init_endpoints(struct musb *musb)
{.../* initialize endpoint list just once */INIT_LIST_HEAD(&(musb->g.ep_list));for (epnum = 0, hw_ep = musb->endpoints;epnum < musb->nr_endpoints;epnum++, hw_ep++) {if (hw_ep->is_shared_fifo /* || !epnum */) { // 输入输出共享 FIFO,端点同时支持【输入&输出(IN & OUT)】,典型的如 EP0 端点init_peripheral_ep(musb, &hw_ep->ep_in, epnum, 0);count++;} else { // 只支持一个方向(IN 或 OUT)的端点if (hw_ep->max_packet_sz_tx) { // 只支持【输出】的端点: OUTinit_peripheral_ep(musb, &hw_ep->ep_in,epnum, 1);count++;}if (hw_ep->max_packet_sz_rx) { // 只支持【输入】的端点: INinit_peripheral_ep(musb, &hw_ep->ep_out,epnum, 0);count++;}}}
}// 初始化一个端点
static void
init_peripheral_ep(struct musb *musb, struct musb_ep *ep, u8 epnum, int is_in)
{...ep->current_epnum = epnum; // 端点编号...ep->is_in = is_in; // 标记端点方向:是输入(IN)还是出输出(OUT)INIT_LIST_HEAD(&ep->req_list); // 初始端点上数据请求包(usb_request)列表...// 配置端点接口和能力if (!epnum) { // 控制端点 EP0usb_ep_set_maxpacket_limit(&ep->end_point, 64);ep->end_point.caps.type_control = true;ep->end_point.ops = &musb_g_ep0_ops; // 控制端点 EP0 操作接口musb->g.ep0 = &ep->end_point;} else {if (is_in)usb_ep_set_maxpacket_limit(&ep->end_point, hw_ep->max_packet_sz_tx);elseusb_ep_set_maxpacket_limit(&ep->end_point, hw_ep->max_packet_sz_rx);ep->end_point.caps.type_iso = true;ep->end_point.caps.type_bulk = true;ep->end_point.caps.type_int = true;ep->end_point.ops = &musb_ep_ops; // 普通端点 操作接口list_add_tail(&ep->end_point.ep_list, &musb->g.ep_list);}// 修正端点方向设置if (!epnum || hw_ep->is_shared_fifo) {ep->end_point.caps.dir_in = true;ep->end_point.caps.dir_out = true;} else if (is_in)ep->end_point.caps.dir_in = true;elseep->end_point.caps.dir_out = true;
}
最后来看 UDC 设备对象的注册过程:
/* drivers/usb/gadget/udc/core.c */
int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
{return usb_add_gadget_udc_release(parent, gadget, NULL);
}int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,void (*release)(struct device *dev))
{struct usb_udc *udc;...udc = kzalloc(sizeof(*udc), GFP_KERNEL); // 分配一个 UDC 设备对象...list_add_tail(&udc->list, &udc_list); // 添加 UDC 设备对象到全局列表...usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);udc->vbus = true;/* pick up one of pending gadget drivers */// 可能发生的、将 UDC 绑定到某个 USB Gadget Function 驱动。// 这不是通常的情形,后续我们看后一种绑定时机的细节,它们逻辑上是一样的。ret = check_pending_gadget_drivers(udc);...return 0;
}
对于 UDC 驱动 的讨论,就先到这里,等到后面涉及到 UDC
和 USB Gadget Function
绑定的时候,再讨论剩余的相关细节。
3.4 USB Gadget Function 驱动
单独把 USB Gadget Function 驱动
拿出来讲,没有太多意义。关于 USB Gadget Function 驱动
的细节,我们将在 3.5 小节中一起讲述。
3.5 USB Gadget 驱动
本小节将讲述如何将 UDC 驱动
和 USB Gadget Function 驱动
组合到一起,形成一个完整的 USB Gadget 驱动
。
顺便提一下,USB Gadget 驱动代码组织在目录 drivers/usb/gadget
目录下:
drivers/usb/gadget/*.c, *.h: USB Gadget 驱动核心公共代码
drivers/usb/gadget/function/*.c, *.h: USB Gadget 驱动各功能(serial,UDC,...)代码
drivers/usb/gadget/legacy/*.c, *.h: USB Gadget 驱动
解下来,我们以 Gadget UAC 驱动
为例,来讲述 USB Gadget 驱动
的 记载、卸载,以及工作过程。其它功能的 USB Gadget 驱动
,除了协议特定部分,其它部分的流程是类似的,读者可参考本文自行分析。
3.5.1 USB Gadget 驱动的加载
3.5.1.1 启动 UDC: 上拉 D+
// 我们假设将 Gadget UAC 驱动编译程模块形式,即 g_audio.ko 。
// 当以 insmod g_audio.ko 指令加载模块时,将进入驱动入口 usb_composite_probe()/* drivers/usb/gadget/legacy/audio.c */
static struct usb_composite_driver audio_driver = {.name = "g_audio",.dev = &device_desc,.strings = audio_strings,.max_speed = USB_SPEED_HIGH,.bind = audio_bind,.unbind = audio_unbind,
};/** include/linux/usb/composite.h** #define module_usb_composite_driver(__usb_composite_driver) \* module_driver(__usb_composite_driver, usb_composite_probe, \* usb_composite_unregister)*/
// 定义 USB Gadget 驱动
module_usb_composite_driver(audio_driver);
在进一步讨论前,先看看 module_usb_composite_driver()
和 usb_composite_driver
的定义:
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);#define module_usb_composite_driver(__usb_composite_driver) \module_driver(__usb_composite_driver, usb_composite_probe, \usb_composite_unregister)
可以看到,module_usb_composite_driver()
定义了两个函数。具体到我们的 UAC 例子,就是定义了函数 audio_driver_init()
和 audio_driver_exit()
:
static int __init audio_driver_init(void)
{return usb_composite_probe(&audio_driver);
}
module_init(audio_driver_init);static void __exit audio_driver_exit(void)
{usb_composite_unregister(&audio_driver);
}
module_exit(audio_driver_exit);
再看下 usb_composite_driver
的定义:
struct usb_composite_driver {const char *name;const struct usb_device_descriptor *dev;struct usb_gadget_strings **strings;enum usb_device_speed max_speed;unsigned needs_serial:1;int (*bind)(struct usb_composite_dev *cdev); // audio_bind()int (*unbind)(struct usb_composite_dev *); // audio_unbind()void (*disconnect)(struct usb_composite_dev *);/* global suspend hooks */void (*suspend)(struct usb_composite_dev *);void (*resume)(struct usb_composite_dev *);struct usb_gadget_driver gadget_driver;
};struct usb_gadget_driver {char *function;enum usb_device_speed max_speed;int (*bind)(struct usb_gadget *gadget,struct usb_gadget_driver *driver); // composite_bind()void (*unbind)(struct usb_gadget *); // composite_unbind()int (*setup)(struct usb_gadget *,const struct usb_ctrlrequest *); // composite_setup()void (*disconnect)(struct usb_gadget *);void (*suspend)(struct usb_gadget *);void (*resume)(struct usb_gadget *);void (*reset)(struct usb_gadget *);struct device_driver driver;// 可以显式指定 USB Gadget 驱动关联的 UDC。// 但绝大多数情形都会将 udc_name 设为 NULL, 这指示直接使用系统中能找到的 UDC,// 因为通常系统中不会有超过一个 UDC 存在。char *udc_name;...
};
好,继续看 Gadget UAC 驱动的加载过程:
/* drivers/usb/gadget/composite.c */
int usb_composite_probe(struct usb_composite_driver *driver)
{struct usb_gadget_driver *gadget_driver;driver->gadget_driver = composite_driver_template;gadget_driver = &driver->gadget_driver;...return usb_gadget_probe_driver(gadget_driver); // 绑定 UDC
}/* drivers/usb/gadget/udc/core.c */
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
{...mutex_lock(&udc_lock);if (driver->udc_name) {...} else {list_for_each_entry(udc, &udc_list, list) {/* For now we take the first one */if (!udc->driver)goto found;}}...found:ret = udc_bind_to_driver(udc, driver); // 绑定 UDC 和 usb_gadget_drivermutex_unlock(&udc_lock);return ret;
}static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
{...// 绑定 UDC 和 usb_gadget_driverudc->driver = driver;udc->dev.driver = &driver->driver;udc->gadget->dev.driver = &driver->driver;// 间接触发 UAC 驱动的 bind:// composite_bind() -> audio_bind()ret = driver->bind(udc->gadget, driver);...status = composite->bind(cdev) = audio_bind()......ret = usb_gadget_udc_start(udc); /* 启动 UDC: 上电 */...usb_udc_connect_control(udc); /* D+ 上拉: 触发枚举过程 */...return 0;
}
重点看一下 audio_bind()
,看它是如何将 Gadget Fuction(UAC 1.0 Function)
绑定上来的:
/* drivers/usb/gadget/legacy/audio.c */
static struct usb_function_instance *fi_uac1;
static struct usb_function *f_uac1static int audio_bind(struct usb_composite_dev *cdev)
{struct f_uac1_legacy_opts *uac1_opts;fi_uac1 = usb_get_function_instance("uac1");try_get_usb_function_instance(name)...list_for_each_entry(fd, &func_list, list) {if (strcmp(name, fd->name)) // 按名字查找 function drivercontinue;...fi = fd->alloc_inst(); // f_audio_alloc_inst()if (IS_ERR(fi))module_put(fd->mod);elsefi->fd = fd;break;}...return fi;...status = usb_add_config(cdev, &audio_config_driver, audio_do_config);...status = usb_add_config_only(cdev, config); // 添加配置 @config...status = bind(config); // function 绑定:audio_do_config(), .........
}static int audio_do_config(struct usb_configuration *c)
{...f_uac1 = usb_get_function(fi_uac1); // 获取 functionf = fi->fd->alloc_func(fi); // f_audio_alloc()struct f_uac1 *uac1;...uac1 = kzalloc(sizeof(*uac1), GFP_KERNEL); // 分配 function 对象...// 设置 function 接口uac1->g_audio.func.name = "uac1_func";uac1->g_audio.func.bind = f_audio_bind;uac1->g_audio.func.unbind = f_audio_unbind;uac1->g_audio.func.set_alt = f_audio_set_alt;uac1->g_audio.func.get_alt = f_audio_get_alt;uac1->g_audio.func.setup = f_audio_setup; // 枚举阶段处理各种 UAC 协议特定的 setup 包uac1->g_audio.func.disable = f_audio_disable;uac1->g_audio.func.free_func = f_audio_free;return &uac1->g_audio.func;...f->fi = fi;return f;...status = usb_add_function(c, f_uac1); // 绑定 function...function->config = config;list_add_tail(&function->list, &config->functions);...if (function->bind) {// function 的初始化: 各类 USB 描述符设置 等等value = function->bind(config, function); // f_audio_bind(), ......}...
}
补充说明下,usb_get_function_instance()
查找的 Gadget Function 列表 func_list
是怎么构建的:
/* drivers/usb/gadget/function/f_uac1.c */
DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc);/* drivers/usb/gadget/function/f_serial.c */
DECLARE_USB_FUNCTION_INIT(gser, gser_alloc_inst, gser_alloc);// 更多 drivers/usb/gadget/function/f_*.c 注册的 function
#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)usb_function_register()...list_add_tail(&newf->list, &func_list);
到此,已经完成了 UDC 和 Function 的绑定 和 初始化过程。此时,UDC 已经触发了枚举过程,接下来看枚举期间发生了什么。
3.5.1.2 设备枚举
USB 的通信,总是从主机一侧开始
。当 USB 设备控制器收到 主机发过来的数据包 (UHC -> UDC),UDC 就是产生一个中断,所以我们的分析,从中断接口 sunxi_musb_interrupt()
开始:
/* drivers/usb/musb/sunxi.c */
static irqreturn_t sunxi_musb_interrupt(int irq, void *__hci)
{// 厂商特定的中断处理musb->int_usb = readb(musb->mregs + SUNXI_MUSB_INTRUSB);if (musb->int_usb)writeb(musb->int_usb, musb->mregs + SUNXI_MUSB_INTRUSB);...musb->int_tx = readw(musb->mregs + SUNXI_MUSB_INTRTX); // 读各端点 TX 中断状态if (musb->int_tx)writew(musb->int_tx, musb->mregs + SUNXI_MUSB_INTRTX);musb->int_rx = readw(musb->mregs + SUNXI_MUSB_INTRRX); // 读各端点 RX 中断状态if (musb->int_rx)writew(musb->int_rx, musb->mregs + SUNXI_MUSB_INTRRX);// MUSB IP 公共中断处理musb_interrupt(musb);...return IRQ_HANDLED;
}irqreturn_t musb_interrupt(struct musb *musb)
{...if (musb->int_usb)retval |= musb_stage0_irq(musb, musb->int_usb, devctl);// EP0 中断处理if (musb->int_tx & 1) {if (is_host_active(musb))retval |= musb_h_ep0_irq(musb);elseretval |= musb_g_ep0_irq(musb);/* we have just handled endpoint 0 IRQ, clear it */musb->int_tx &= ~BIT(0); // EP0 的事件处理了,清除 EP0 的位码}// 普通端点 TX 中断处理status = musb->int_tx;for_each_set_bit(epnum, &status, 16) {retval = IRQ_HANDLED;if (is_host_active(musb)) // 主机模式musb_host_tx(musb, epnum);else // 从设模式musb_g_tx(musb, epnum);}// 普通端点 TX 中断处理status = musb->int_rx;for_each_set_bit(epnum, &status, 16) {retval = IRQ_HANDLED;if (is_host_active(musb))musb_host_rx(musb, epnum);elsemusb_g_rx(musb, epnum);}return retval;
}
中段涉及的数据不光包含枚举或断开连接这些期间的 setup 包,还有其它数据的 RX, TX 传输。本小节讲述的枚举,所以只描述枚举相关的通信做简要说明:
sunxi_musb_interrupt()musb_interrupt()musb_g_ep0_irq()switch (musb->ep0_state) {...case MUSB_EP0_STAGE_SETUP:if (csr & MUSB_CSR0_RXPKTRDY) { /* 收包就绪 */struct usb_ctrlrequest setup;...musb_read_setup(musb, &setup); /* 从 FIFO 读取 setup 包的内容 */...handled = forward_to_driver(musb, &setup);musb->gadget_driver->setup(&musb->g, ctrlrequest) = composite_setup()...}...}/* drivers/usb/gadget/composite.c */
int
composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
{...// 非特定于 Gadget Function 的 setup 包处理switch (ctrl->bRequest) {case USB_REQ_GET_DESCRIPTOR: // 获取 USB 设备描述符...break;case USB_REQ_SET_CONFIGURATION: // 设置 USB 设备配置描述符...value = set_config(cdev, ctrl, w_value);...break;case USB_REQ_GET_CONFIGURATION: // 获取 USB 设备配置描述符...break;case USB_REQ_SET_INTERFACE:...value = f->set_alt(f, w_index, w_value); // f_audio_set_alt(), ......break;case USB_REQ_GET_INTERFACE:...value = f->get_alt ? f->get_alt(f, w_index) : 0; // f_audio_get_alt(), ......break;...default: // 特定于 Gadget Function 的 setup 包处理
unknown:...
try_fun_setup:if (f && f->setup)// 进行 Gadget Function 特定的 setup 包处理value = f->setup(f, ctrl); // f_audio_setup()else...goto done; // 特定于 Gadget Function 的 setup 包处理完毕,直接跳到函数末尾}/* respond with data transfer before status phase? */// 非特定于 Gadget Function 的 setup 包处理 回馈if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) {req->length = value;req->context = cdev;req->zero = value < w_length;value = composite_ep0_queue(cdev, req, GFP_ATOMIC);...}done:/* device either stalls (value < 0) or reports success */return value;
}
正确完成上述枚举过程,主机端接下来就可以和从设进行数据通信了。
3.5.2 USB Gadget 驱动数据交流过程
以 UAC 播放过程为例,来说明 USB Gadget 驱动 和 主机侧的 数据交流过程。在 Windows 开启播放器程序,它首先会发 SET_INTERFACE 给从设,以告知 USB Gadget 驱动做播放前的准备工作,以及启动对应的 USB 端点进行播放数据传输:
sunxi_musb_interrupt()...composite_setup()...f_audio_set_alt()...if (intf == uac1->as_out_intf) { // 准备处理播放数据uac1->as_out_alt = alt;if (alt)ret = u_audio_start_capture(&uac1->g_audio);...usb_ep_enable(ep); // 激活 USB 端点for (i = 0; i < params->req_number; i++) {// 分配 USB 数据包处理对象req = usb_ep_alloc_request(ep, GFP_ATOMIC);...req->complete = u_audio_iso_complete;...// 将 USB 数据包处理对象加入到 USB 端点if (usb_ep_queue(ep, prm->ureq[i].req, GFP_ATOMIC))...}else...}
接下来是收取播放数据包过程:
sunxi_musb_interrupt()...musb_g_giveback()...list_del(&req->list); // 含有数据的 usb_request 出 端点数据包队列...usb_gadget_giveback_request(&req->ep->end_point, &req->request)...req->complete(ep, req); // u_audio_iso_complete(), ...u_audio_iso_complete()...if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {...} else { // 拷贝 主机侧 发送的 播放数据 到 UAC 声卡 DMA 缓冲if (unlikely(pending < req->actual)) {memcpy(runtime->dma_area + hw_ptr, req->buf, pending);memcpy(runtime->dma_area, req->buf + pending, req->actual - pending);} else {memcpy(runtime->dma_area + hw_ptr, req->buf, req->actual);}...// usb_request 重新加入端点队列,以处理后续数据if (usb_ep_queue(ep, req, GFP_ATOMIC))...}
3.5.3 USB Gadget 驱动的卸载
仍然以 Gadget UAC 驱动的卸载为例,说明 USB Gadget 驱动 的卸载过程:
// 通过 rmmod g_audio 卸载模块
sys_delete_module()audio_driver_exit()usb_composite_unregister()usb_gadget_unregister_driver()usb_gadget_remove_driver()composite_unbind()__composite_unbind()remove_config()usb_remove_function()f_audio_unbind()