本文共 5877 字,大约阅读时间需要 19 分钟。
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#include #include #include 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] [] dump_backtrace+0x0/0x350[ 803.928320] [ ] show_stack+0x14/0x20[ 803.933364] [ ] dump_stack+0x90/0xb4[ 803.938408] [ ] g_loop_reset+0x14/0x28 [g_loop][ 803.944406] [ ] usb_gadget_udc_reset+0x18/0x40[ 803.950315] [ ] dwc3_gadget_reset_interrupt+0x110/0x128[ 803.957007] [ ] dwc3_thread_interrupt+0x4e8/0x870[ 803.963180] [ ] irq_thread_fn+0x28/0x68[ 803.968482] [ ] irq_thread+0x114/0x198[ 803.973699] [ ] kthread+0x12c/0x130[ 803.978655] [ ] 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框架, 把这个设备配置起来...
转载地址:http://qicrj.baihongyu.com/