开始写usb gadget驱动

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框架, 把这个设备配置起来...

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值