最简ARM LINUX USB 驱动

自己板子是插上鼠标后,没有反应,只有在插上鼠标,板子重新上电,鼠标才有作用,这实在是不解,好像板子是有鼠标USB驱动,而USB驱动是支持热拔插的,不应该出现这种情况的,出现了,就想着解决。  首先必须的知道USB插上设备之后,内核做了哪些工作。(这一步很重要)
插上鼠标,终端打印了这些话,
  new full speed USB device using xxxx
拔开鼠标,也会打印
  USB disconnect
所以,这些话是在哪打印的呢? 通过全局搜索,发现在hub_port_init函数里面,再玩上找会找到hub_port_connect_change函数,这个函数就是重点了。


在这里需要先了解USB的驱动框架,这里不做过多的阐述,都是别人的话,具体可以参考别人写的Linux书,每一本都会讲到USB,USB的基础概念,框架,各个结构体......
提出其中对我有用的地方,就是USB设备一插上了之后,就会引起中断,USB总线驱动就会发现设备,给新设备分配地址(choose_address(udev) ), 告诉USB设备(hub_set_address ), 发出命令获取描述符(usb_get_device_descriptor(udev, 8),usb_get_configuration(udev) ),最后 device_add。把device放入usb_bus_type的dev链表  从usb_bus_type的driver链表里取出usb_driver, 把usb_interface和usb_driver的id_table  如果能匹配,调用usb_driver的probe
所以,做这个USB开发的,就只需要 分配/设置usb_driver结构体,做好.probe函数。就行了。这就是USB的驱动框架了。内核都跟你做好了准备工作了,分工明确,做驱动开发也可以很轻松。

1),开始写一个最简单的USB驱动程序
1,注册
2,分配设置usb_driver结构体

  1. /* 1. 分配/设置usb_driver */
  2. static struct usb_driver usbmouse_driver = {
  3.         .name                = "usbmouse_",
  4.         .probe                = usbmouse_probe,
  5.         .disconnect        = usbmouse_disconnect,
  6.         .id_table        = usbmouse_id_table,
  7. };


  8. static int usbmouse_init(void)
  9. {
  10.         /* 2. 注册 */
  11.         usb_register(&usbmouse_driver);
  12.         return 0;
  13. }

  14. static void usbmouse_exit(void)
  15. {
  16.         usb_deregister(&usbmouse_driver);        
  17. }

  18. module_init(usbmouse_init);
  19. module_exit(usbmouse_exit);

  20. MODULE_LICENSE("GPL");
复制代码

USB设备驱动的匹配之通过id_table,和platfrom平台驱动匹配方式(设备名字匹配的)不同,这里是想匹配鼠标,所以在id_table 里添加

  1. static struct usb_device_id usbmouse_id_table [] = {
  2.         { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
  3.                 USB_INTERFACE_PROTOCOL_MOUSE) },
  4.         { }        /* Terminating entry */
  5. };
复制代码
这样,usb总线驱动识别出了鼠标,id_table匹配上了,就会调用相应的probe函数
在函数之外定义几个结构体,做全局调用
  1. static char *usb_buf;
  2. static dma_addr_t usb_buf_phys;
  3. static int len;
  4. static struct urb *uk_urb;
复制代码
  1. static int usbmouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
  2. {
  3.         struct usb_device *dev = interface_to_usbdev(intf);
  4.         struct usb_host_interface *interface;
  5.         struct usb_endpoint_descriptor *endpoint;
  6.         int pipe;
  7.         
  8.         interface = intf->cur_altsetting;
  9.         endpoint = &interface->endpoint[0].desc;
  10.         /* 数据传输3要素: 源,目的,长度 */
  11.         /* 源: USB设备的某个端点 */
  12.         pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

  13.         /* 长度: */
  14.         len = endpoint->wMaxPacketSize;

  15.         /* 目的: */
  16.         usb_buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &usb_buf_phys);

  17.         /* 使用"3要素" */
  18.         /* 分配usb request block */
  19.         uk_urb = usb_alloc_urb(0, GFP_KERNEL);
  20.         /* 使用"3要素设置urb" */
  21.         usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_irq, NULL, endpoint->bInterval);
  22.         uk_urb->transfer_dma = usb_buf_phys;
  23.         uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
  24.         /* 使用URB */
  25.         usb_submit_urb(uk_urb, GFP_KERNEL);
  26.         return 0;
  27. }
复制代码
当中还要对 usbmouse_irq函数做处理,咋一看是中断函数,其实不是中断,因为USB通信是主从关系,从机设备是没有主动打断主机的能力,只有主机查询,而主机有USB专门的控制器,由它查询,查询到不同就会给CPU中断,这个一定要理解。
static void usbmouse_as_key_irq(struct urb *urb)
{
        static unsigned char pre_val;
        int i;
        static int cnt = 0;
        printk("data cnt %d: ", ++cnt);
        for (i = 0; i < len; i++)
        {
                printk("%02x ", usb_buf);
        }
        printk("\n");

}

这样算是最简单的USB鼠标驱动程序了,把原先的鼠标驱动程序在内核里去掉,make menuconfig,就OK了
 
重新烧写或者网络升级,加载这个最简单的驱动程序,插上鼠标,移动,按下左右键
 

知道这些数值的意义,就很好办事了,这个最简单的USB驱动程序,只不过是内核的自娱自乐罢了,真正要让鼠标有意义起来就得让应用层知道啊,那就要使用输入子系统了,而输入子系统做过记录,就不在累述,输入子系统就几个步骤
1,分配一个input_dev
2,设置能产生哪类事件还有这类事件的哪些事件(有点绕口)
3,注册
4,在对应的“中断程序”里,上报事件,
在函数外定义
static struct input_dev *uk_dev;  //全局使用
在probe函数里添加
     /* 分配一个input_dev */
        uk_dev = input_allocate_device();

        uk_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
        uk_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
                BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
        uk_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
        uk_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
                BIT_MASK(BTN_EXTRA);
        uk_dev->relbit[0] |= BIT_MASK(REL_WHEEL);
        /* 注册 */
        input_register_device(uk_dev);


在对应的“中断程序”里添加 上报事件
     
  1. switch (urb->status) {
  2.         case 0:                        /* success */
  3.                 break;
  4.         case -ECONNRESET:        /* unlink */
  5.         case -ENOENT:
  6.         case -ESHUTDOWN:
  7.                 return;
  8.         /* -EPIPE:  should clear the halt */
  9.         default:                /* error */
  10.                 goto resubmit;
  11.         }

  12.         input_report_key(uk_dev, BTN_LEFT,   usb_buf[1] & 0x01);
  13.         input_report_key(uk_dev, BTN_RIGHT,  usb_buf[1] & 0x02);
  14.         input_report_key(uk_dev, BTN_MIDDLE, usb_buf[1] & 0x04);
  15.         input_report_key(uk_dev, BTN_SIDE,   usb_buf[1] & 0x08);
  16.         input_report_key(uk_dev, BTN_EXTRA,  usb_buf[1] & 0x10);

  17.         input_report_rel(uk_dev, REL_X,     usb_buf[3]);
  18.         input_report_rel(uk_dev, REL_Y,     usb_buf[4]);
  19.         input_report_rel(uk_dev, REL_WHEEL, usb_buf[2]);

  20.         input_sync(dev);
复制代码
重新编译,重新加载,而这里特别要注意usb_buf数组里的值与鼠标的操作一定要对应起来,这个需要自己测出来(在一张图片里可以看到)。  插上鼠标,能移动,有反应,拔掉,又插上,没有问题(有问题,操作不正当,就看看数组里的值是不是对应的),这才像usb鼠标嘛


最后,官方有这个鼠标完整的例子,在/drivers/hid/usbhid/usbmouse.c,要注意,data数组里的值是不是对应的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值