linux内核usb驱动开发,【记录】ARM-Linux开发之USB驱动鼠标控制

【记录】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了

a26d678767e8b431ecd7a25f1936bdc5.gif

QQ截图20160817001830.png (68.72 KB, 下载次数: 0)

2016-8-17 11:15 上传

重新烧写或者网络升级,加载这个最简单的驱动程序,插上鼠标,移动,按下左右键

a26d678767e8b431ecd7a25f1936bdc5.gif

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数组里的值是不是对应的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值