usb主机端class驱动比较简单, 利用usb_register_driver, 向usb core子系统注册相应的class驱动即可。
主要填充usb_driver数据结构, 主要的包括id_table为设备-驱动匹配的依据(如pid, vid或class, protocol等),
其次就是probe, disconnect等回调函数, probe即匹配驱动后的探测函数, 需要做class驱动的初始化,
比如, 鼠标: 向input device子系统注册设备, 描述event事件,
之后就是循环读取bulk in的数据, 根据协议 判断各个bit, 解析相应鼠标的位置 和动作。
另外如UVC Webcam等class驱动, usb设备匹配后, 主要是注册video_device设备, 实现相应的v4l2的各种ioctl操作。
下面是关于usb_gadget驱动, 即作为usb 从设备时, linux的gadget function驱动该如何写。
其实也很简单, 主要就是利用usb_gadget_register_driver(...最新linux换名字了.. usb_gadget_probe_driver)向usb gadget驱动框架注册gadget驱动。
填充usb_gadget_driver数据结构, 比较重要的有bind, reset, setup等函数。
* 注, class driver可参考usb-skeleton 驱动, function driver可参考g_zero驱动。
可惜, 由于g_zero是一个双配置, 多接口, 多端点的复合设备, 故最新版本Linux的zero.c使用了usb_composite_probe函数进行注册。其中有帮助的宏module_usb_composite_driver简化操作。
ok, 写一个gadget驱动跑跑看...
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/usb/gadget.h>
#include <linux/stacktrace.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("River");
/* endpoint descriptor */
static struct usb_endpoint_descriptor fs_sink_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType= USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT, // for pc host, it is out ep.
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static int g_loop_bind(struct usb_gadget *gadget,
struct usb_gadget_driver *driver) {
int ret = 0;
struct usb_ep *ep;
printk(KERN_INFO "g_loop bind callback, "
"bind driver to gadget!\n");
/* reset ep autoconfig state */
usb_ep_autoconfig_reset(gadget);
/* auto config the gadget ep */
ep = usb_ep_autoconfig(gadget, &fs_sink_desc);
if (!ep) {
printk(KERN_ERR "ep auto config failed. ret(%d)\n", ret);
ret = -ENOMEM;
goto config_fail;
}
printk(KERN_INFO "g_loop_bind succeed!\n");
return 0;
config_fail:
return ret;
}
static void g_loop_unbind(struct usb_gadget *gadget) {
/* reset ep autoconfig state */
usb_ep_autoconfig_reset(gadget);
printk(KERN_INFO "g_loop_unbind callback\n");
return;
}
/* ep0 setup callback, something udc doesn't do??
* for example, the no-standard/vendor specific
* control command?? Just let us see...
*/
static int g_loop_setup(struct usb_gadget *gadget,
const struct usb_ctrlrequest *ctl_req) {
printk(KERN_INFO "g_loop_setup callback\n");
dump_stack();
return 0;
}
static void g_loop_reset(struct usb_gadget *gadget) {
printk(KERN_INFO "g_loop_reset callback\n");
dump_stack();
return;
}
static void g_loop_disconnect(struct usb_gadget *gadget) {
printk(KERN_INFO "g_loop_disconnect callback\n");
return;
}
static struct usb_gadget_driver g_loop_driver = {
.function = "gadget_g_loop",
.max_speed = USB_SPEED_HIGH,
.bind = g_loop_bind,
.unbind = g_loop_unbind,
.setup = g_loop_setup,
.reset = g_loop_reset,
.disconnect = g_loop_disconnect,
};
static int __init g_loop_init(void) {
int ret = 0;
printk(KERN_INFO "g_loop_init in\n");
ret = usb_gadget_probe_driver(&g_loop_driver);
if (ret < 0) {
printk(KERN_ERR "usb gaget driver register failed, "
"ret(%d)\n", ret);
goto probe_failed;
}
printk(KERN_INFO "g_loop_init succeed\n");
return 0;
probe_failed:
return ret;
}
static void __exit g_loop_exit(void) {
printk(KERN_INFO "g_loop_exit in\n");
usb_gadget_unregister_driver(&g_loop_driver);
printk(KERN_INFO "g_loop_exit succeed\n");
return;
}
module_init(g_loop_init);
module_exit(g_loop_exit);
设备端log:
root@xxx:/mnt/module# insmod g_loop.ko
[ 803.757659] g_loop: loading out-of-tree module taints kernel.
[ 803.763672] g_loop_init in
[ 803.766377] driver->udc_name: (null)
[ 803.769948] no udc_name...
[ 803.772652] udc_bind_to_driver...
[ 803.775960] registering UDC driver [gadget_g_loop]
[ 803.780742] dwc3_gadget_set_speed
[ 803.784054] g_loop bind callback, bind driver to gadget!
[ 803.789366] g_loop_bind succeed!
[ 803.792590] dwc3_gadget_start
[ 803.796047] dwc3_gadget_pullup
[ 803.799109] g_loop_init succeed
...
[ 803.920487] Call trace:
[ 803.922934] [<ffffff8008088ad0>] dump_backtrace+0x0/0x350
[ 803.928320] [<ffffff8008088e34>] show_stack+0x14/0x20
[ 803.933364] [<ffffff8008a39344>] dump_stack+0x90/0xb4
[ 803.938408] [<ffffff8000ad4034>] g_loop_reset+0x14/0x28 [g_loop]
[ 803.944406] [<ffffff80086a6728>] usb_gadget_udc_reset+0x18/0x40
[ 803.950315] [<ffffff8008675db0>] dwc3_gadget_reset_interrupt+0x110/0x128
[ 803.957007] [<ffffff8008677f28>] dwc3_thread_interrupt+0x4e8/0x870
[ 803.963180] [<ffffff80080db6f0>] irq_thread_fn+0x28/0x68
[ 803.968482] [<ffffff80080db99c>] irq_thread+0x114/0x198
[ 803.973699] [<ffffff80080b7494>] kthread+0x12c/0x130
[ 803.978655] [<ffffff8008084a88>] ret_from_fork+0x10/0x18
[ 803.992688] g_loop_setup callback
...
[ 809.497983] g_loop_reset callback
dwc3设备控制器一直在那里向gic发reset中断...
为什么?? 看下主机端情况, win10上报未知USB设备(设备描述符请求失败)
嗯, 因为我代码就搭了个框架...
所有回调函数, 数据结构基本都是空的...
所以这个usb gadget是什么设备, 它的设备描述符, 配置项, 接口, 以及具体的功能我都没填...
无法识别也就好理解了...
* 注: 这里reset回调函数是由reset事件触发的, 具体中断号内核中是 25
root@xxx:/mnt/module# cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3
25: 16 0 0 0 GICv2 97 Level
26: 0 0 0 0 GICv2 101 Level dwc3_otg
具体设备树的中断号是65...
› › › dwc3_0: dwc3@fe200000 {
.....
› › › › interrupt-parent = <&gic>;
› › › › interrupts = <0 65 4>, <0 69 4>, <0 75 4>;
.....
根据zynqmp的datasheet... (datasheet中看gic有好几个, GIC0 ~ GIC4)
每个GIC有32个中断位, 对应如下:
65: GICP2[1] - USB0_Endpoint, 控制端点中断(66~68 是bulk, isoc, interrupt传输中断)
69: USB0_OTG (内核中断号26, 我暂时没触发, 反正跟otg有关, 可能需要otg线+drd设备一类的吧)
75: USB0_Wakeup (USB0 controller to wake-up PMU, usb0控制器去唤醒platform manage unit模块, 我也没触发...)
ok, 不管了, 先这样, 下面继续熟悉gadget框架, 把这个设备配置起来...