【记录】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. 分配/设置usb_driver */
static struct usb_driver usbmouse_driver = {
.name = "usbmouse_",
.probe = usbmouse_probe,
.disconnect = usbmouse_disconnect,
.id_table = usbmouse_id_table,
};
static int usbmouse_init(void)
{
/* 2. 注册 */
usb_register(&usbmouse_driver);
return 0;
}
static void usbmouse_exit(void)
{
usb_deregister(&usbmouse_driver);
}
module_init(usbmouse_init);
module_exit(usbmouse_exit);
MODULE_LICENSE("GPL");复制代码
USB设备驱动的匹配之通过id_table,和platfrom平台驱动匹配方式(设备名字匹配的)不同,这里是想匹配鼠标,所以在id_table 里添加
static struct usb_device_id usbmouse_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
{ } /* Terminating entry */
};复制代码这样,usb总线驱动识别出了鼠标,id_table匹配上了,就会调用相应的probe函数
在函数之外定义几个结构体,做全局调用
static char *usb_buf;
static dma_addr_t usb_buf_phys;
static int len;
static struct urb *uk_urb;复制代码
static int usbmouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
int pipe;
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
/* 数据传输3要素: 源,目的,长度 */
/* 源: USB设备的某个端点 */
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
/* 长度: */
len = endpoint->wMaxPacketSize;
/* 目的: */
usb_buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &usb_buf_phys);
/* 使用"3要素" */
/* 分配usb request block */
uk_urb = usb_alloc_urb(0, GFP_KERNEL);
/* 使用"3要素设置urb" */
usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_irq, NULL, endpoint->bInterval);
uk_urb->transfer_dma = usb_buf_phys;
uk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* 使用URB */
usb_submit_urb(uk_urb, GFP_KERNEL);
return 0;
}复制代码当中还要对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了
QQ截图20160817001830.png (68.72 KB, 下载次数: 0)
2016-8-17 11:15 上传
重新烧写或者网络升级,加载这个最简单的驱动程序,插上鼠标,移动,按下左右键
QQ截图20160816224944.png (66.04 KB, 下载次数: 0)
2016-8-17 11:15 上传
知道这些数值的意义,就很好办事了,这个最简单的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);
在对应的“中断程序”里添加 上报事件
switch (urb->status) {
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
input_report_key(uk_dev, BTN_LEFT, usb_buf[1] & 0x01);
input_report_key(uk_dev, BTN_RIGHT, usb_buf[1] & 0x02);
input_report_key(uk_dev, BTN_MIDDLE, usb_buf[1] & 0x04);
input_report_key(uk_dev, BTN_SIDE, usb_buf[1] & 0x08);
input_report_key(uk_dev, BTN_EXTRA, usb_buf[1] & 0x10);
input_report_rel(uk_dev, REL_X, usb_buf[3]);
input_report_rel(uk_dev, REL_Y, usb_buf[4]);
input_report_rel(uk_dev, REL_WHEEL, usb_buf[2]);
input_sync(dev);复制代码重新编译,重新加载,而这里特别要注意usb_buf数组里的值与鼠标的操作一定要对应起来,这个需要自己测出来(在一张图片里可以看到)。插上鼠标,能移动,有反应,拔掉,又插上,没有问题(有问题,操作不正当,就看看数组里的值是不是对应的),这才像usb鼠标嘛
最后,官方有这个鼠标完整的例子,在/drivers/hid/usbhid/usbmouse.c,要注意,data数组里的值是不是对应的。