注意:本篇讲的鼠标驱动仅能实现鼠标左右键跟滑轮这三个按键类似button的功能,按下左键则打出"l",右键打出“s”,滑轮打出“enter”。如果要实现正常的鼠标驱动,参考内核的鼠标驱动,修改input的一些参数即可。
一、写驱动的步骤(新手稍微看下即可,内容有点搞)
1、复制头文件;
2、写入口函数,出口函数,再加上协议;
3、分配注册usb_driver结构体(拷别人的),
static struct usb_driver usb_mk_driver = {};
在init中注册该结构体:usb_register(&usb_mk_driver);
在exit中注销该结构体:usb_deregister(&usb_mk_driver);
4、写id_table函数,static struct usb_device_id usb_mk_id_table [] = {},只有满足id_table中的各种类别,子类号,协议,才能调用;
5、写probe函数:static int usb_mk_probe(struct usb_interface *intf, const struct usb_device_id *id);
6、写disconnect函数:static void usb_mk_disconnect(struct usb_interface *intf)
7、如果想在开发板插入usb设备的时候打印设备信息(可以不用),可以在probe中加入:
printk("VID = 0x%x, PID = 0x%x\n", dev->descriptor.idVendor, dev->descriptor.idProduct);
printk("USB VERS = 0x%x, PID = 0x%x\n", dev->descriptor.bcdUSB);
8、probe之后要进一步判断你这个是不是鼠标:
1、分配一个input_dev结构体:在头文件下定义;static struct input_dev *mk_dev; 在probe内:mk_dev = input_allocate_device();
2、设置:
1、能产生哪类事件
set_bit(EV_KEY, mk_dev->evbit);
set_bit(EV_REP, mk_dev->evbit);
2、能产生哪些事件
set_bit(KEY_L, mk_dev->keybit);
set_bit(KEY_S, mk_dev->keybit);
set_bit(KEY_ENTER, mk_dev->keybit);
3、注册:input_register_device(mk_dev);
4、设置:数据传输3要素: 源, 目的, 长度
先从usbmouse.c中拷入几行代码:
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
int pipe;
endpoint = &interface->endpoint[0].desc;
源:usb设备的某个端点
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
目的:需要设置缓冲区buffer:
buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &buf_phys);
在头文件下定义一个缓冲区:
static char *buf;
static dma_addr_t buf_phys;
长度:static int len(作为全局变量,等会有用)
len = endpoint->wMaxPacketSize;
5、怎么用那三要素呢?
在probe中;
1、分配一个urb,mk_urb = usb_alloc_urb(0, GFP_KERNEL);
在头文件下定义urb:static struct urb *mk_urb;
2、使用三要素填充urb:
usb_fill_int_urb(mk_urb, dev, pipe, buf,
len,
uk_callback, NULL, endpoint->bInterval);
mk_urb->transfer_dma = buf_phys;
mk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
3、使用URB:usb_submit_urb(mk_urb, GFP_KERNEL);
6、要写一个中断函数,原因是usb控制器收到从usb传来的数据后,需要一个中断来通知cpu有信号啦!static void uk_callback(struct urb *urb)
在中断函数中定义一些唧唧歪歪的东西后看,重新提交urb:
usb_submit_urb(mk_urb, GFP_KERNEL);
7,提交urb后当然还要做杀掉urb,在disconnect中杀掉它!usb_kill_urb(mk_urb);
顺便再disconnect中注销或卸载其他的函数;
8、在中断函数里上报事件。
**************************************************************************************************************
二、驱动程序
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
#include <linux/hid.h>
#include <linux/input.h>
/* 参考drivers\hid\usbhid\usbmouse.c
*/
static struct input_dev *mk_dev;
static int len;
static char *buf; //定义一个缓冲区
static dma_addr_t buf_phys; //dma_addr实际上是一个物理地址
static struct urb *mk_urb;
static struct usb_device_id usb_mk_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
/*只要它的类是class_hid,子类是boot,协议是mouse,那么就可以匹配*/
*{USB_DEVICE(0x46d, 0xc52f)或者直接指定自己的usb设备只支持这种厂家的设备。*/
{ } /* Terminating entry */
};
/* 当USB主机控制器获得鼠标数据后,
* 会调用这个函数
*/
static void uk_callback(struct urb *urb)//这是一个中断函数!
{
int i;
static char pre_val;
#if 0 //这一段都用不到了,因为if = 0 ;这一段的作用就是用来看鼠标按下之后都显示什么数值,最后通过这些数值来设置上报事件。
printk("Get datas:\n");
for (i = 0; i < len; i++)
{
printk("%02x ", buf[i]);
}
printk("\n");
#endif
/* 鼠标数据含义:
* buf[0]: bit0-左键, 0-松开, 1-按下
* bit1-右键, 0-松开, 1-按下
* bit2-中键, 0-松开, 1-按下
* buf[1],buf[2]构成一个整数, 表示X方向的相对位移
* >0 : 右移
* <0 : 左移
* buf[3],buf[4]构成一个整数, 表示Y方向的相对位移
* >0 : 下移
* <0 : 上移
* buf[6]: 滚轮
*/
/* 确定按键值 */
/* 上报数据 */
if ((pre_val & (1<<0)) != (buf[0] & (1<<0))) //如果上次数据的bit0不等于这次数据的bit0,那么就是左键发生变化
{
/* 左键按下或松开 */
input_event(mk_dev, EV_KEY, KEY_L, (buf[0] & (1<<0)) ? 1 : 0);//如果buf0=1的话那就是按下,既是1,否则为0;
input_sync(mk_dev);
}
if ((pre_val & (1<<1)) != (buf[0] & (1<<1)))
{
/* 右键按下或松开 */
input_event(mk_dev, EV_KEY, KEY_S, (buf[0] & (1<<1)) ? 1 : 0);
input_sync(mk_dev);
}
if ((pre_val & (1<<2)) != (buf[0] & (1<<2)))
{
/* 中键按下或松开 */
input_event(mk_dev, EV_KEY, KEY_ENTER, (buf[0] & (1<<2)) ? 1 : 0);
input_sync(mk_dev);
}
pre_val = buf[0];
/* 重新提交URB */
usb_submit_urb(mk_urb, GFP_KERNEL);
}
/*interface是指接口,一个usb设备可能有多个逻辑接口,这个逻辑接口就是用下面的usb_interface来表示的*/
static int usb_mk_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;
static int first = 1;
if (!first)
return -EIO;
first = 0;
/* 每一个设备都有端点0
* interface->endpoint[]数组里放"除了端点0外的其他端点"
* interface->endpoint[0]表示"除端点0外的第1个端点"
* interface->endpoint[1]表示"除端点0外的第2个端点"
*/
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
/* 1. 分配inputd_dev */
mk_dev = input_allocate_device();
/* 2. 设置 */
/* 2.1 能产生哪类事件 */
set_bit(EV_KEY, mk_dev->evbit); //按键类事件
set_bit(EV_REP, mk_dev->evbit); //重复类事件,例如一直按着L,则会显示LLLLLLL。。。
/* 2.2 能产生这类事件里的哪些事件 */
set_bit(KEY_L, mk_dev->keybit);
set_bit(KEY_S, mk_dev->keybit);
set_bit(KEY_ENTER, mk_dev->keybit);
/* 3. 注册 */
input_register_device(mk_dev);
/* 4. 硬件相关的操作:
* 对于GPIO按键, 是request_irq, 在中断处理函数里上报按键
* 对于USB设备, 是使用"USB主机驱动程序提供的函数"发起USB传输获得数据
*/
/* 数据传输3要素: 源, 目的, 长度 */
/* A. 源: USB设备的某个端点 */
/* ((PIPE_INTERRUPT << 30) | (dev->devnum << 8) | (endpoint << 15) | USB_DIR_IN) */
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);//pipe是源,源是一个整数,这个整数里有端点的类型,端点的方向
/* C. 长度: 这个端点描述符的wMaxPacketSize */
len = endpoint->wMaxPacketSize;
/* B. 目的: 分配buffer */
buf = usb_alloc_coherent(dev, len, GFP_ATOMIC, &buf_phys);//从usb_buffer_alloc换成usb_alloc_coherent
/* D. 怎么使用这3要素 ? */
/* 分配URB: USB Reqeust Block ,usb请求块*/
mk_urb = usb_alloc_urb(0, GFP_KERNEL);
/* 用3要素填充URB
*实际上usb设备没有中断cpu的能力,但是电脑的usb主机有中断cpu的能力,所以usb主机不断查询,有信号便中断cpu。
*/
usb_fill_int_urb(mk_urb, dev, pipe, buf,
len,
uk_callback, NULL, endpoint->bInterval);//bInterval:查询频率,uk_callback是一个中断函数;
mk_urb->transfer_dma = buf_phys; //得到数据后要往某个内存里写,它没那么聪明,需要一个物理地址
mk_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;//还有设置某些标记,这标记什么意思伟哥也不懂
/* 使用URB ,看看别人怎么做*/
usb_submit_urb(mk_urb, GFP_KERNEL);//提交urb
return 0;
}
static void usb_mk_disconnect(struct usb_interface *intf)
{
struct usb_device *dev = interface_to_usbdev(intf);
printk("disconnect usb mouse!!!!!\n");
usb_kill_urb(mk_urb);
usb_free_urb(mk_urb);
usb_free_coherent(dev,len, buf, buf_phys);//应该是usb_free_coherent
input_unregister_device(mk_dev);
input_free_device(mk_dev);
}
/* 1. 分配usb_driver */
/* 2. 设置 */
static struct usb_driver usb_mk_driver = {
.name = "usbmk",
.probe = usb_mk_probe,
.disconnect = usb_mk_disconnect,
.id_table = usb_mk_id_table,
};
static int usb_mk_init(void)
{
/* 3. 注册 */
usb_register(&usb_mk_driver);
return 0;
}
static void usb_mk_exit(void)
{
usb_deregister(&usb_mk_driver);
}
module_init(usb_mk_init);
module_exit(usb_mk_exit);
MODULE_LICENSE("GPL");