linux 卸载 usbmouse,8 Linux usbmouse设备驱动程序

前一章节对linux内核中USB驱动程序的框架进行了分析,这一节以USB鼠标为对象,编写USB鼠标驱动程序。

实验内容:编写USB鼠标设备驱动程序。并将USB鼠标左键定义为"L"功能,右键定义为"S"功能,中间滚轮键定义为"ENTER"功能,方便测试。

参考内核中/driver/hid/usbhid/usbmouse.c文件。

从入口函数usbmouse_as_key_init开始。按照之前编写字符驱动程序的惯例,入口函数中需要实现usb_driver结构体的分配,配置、注册以及和硬件相关的操作。

那么,首先需要定义一个usb_driver结构体。

2c34dd5d37e84d1f36f5b9681ca3cea9.png

其中probe函数是整个驱动程序的重点,后面再讲。disconnect函数是当设备断开连接时调用,后面再讲。

id_table用于保存usb设备的id信息,其结构体定义如下:

f7951eea9a0a995f936836736d20d0b9.png

这里usbmouse_as_key_id_table的定义如下:

ba988b47fa117b46842c47fe5fabaf0e.png

定义中使用宏USB_INTERFACE_INFO,具体定义如下:

35574839dd19d3d3d892417ca2a1995d.png

对宏USB_INTERFACE_INFO进行展开,usbmouse_as_key_id_table [] ={

{

.match_flags = USB_DEVICE_ID_MATCH_INT_INFO,

.bInterfaceClass = USB_INTERFACE_CLASS_HID,

.bInterfaceSubClass = USB_INTERFACE_SUBCLASS_BOOT,

.bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE },

},即我们编写的USB鼠标驱动的支持接口类是USB_INTERFACE_CLASS_HID:0x03,支持的接口子类是USB_INTERFACE_SUBCLASS_BOOT:0x01,接口协议为USB_INTERFACE_PROTOCOL_MOUSE:0x03。

probe函数是整个驱动的核心,实现设备的分配、设置、注册以及硬件相关的操作。

(1)分配输入设备

这里对USB鼠标是按照按键类输入事件进行处理的,因此需要分配一个输入设备uk_dev。

static struct input_dev *uk_dev;

uk_dev = input_allocate_device();

(2)设置事件

USB鼠标产生按键类事件,并且支持长按重复操作。左键按下对应"L"功能,邮件按下对应"S"功能,中键对应"ENTER"功能。

(3)注册

调用input_register_device函数注册输入设备uk_dev。

(4)与硬件相关的操作

USB设备驱动的硬件操作与之前的LCD、按键等有所区别,不是直接操作寄存器的,这里是调用USB总线驱动程序的接口来实现数据的访问。

urb是linux内核中USB驱动程序中实现数据传输的一种数据结构,全称USB request block,其定义如下:

structurb

{

/* private: usb core and host controller only fields in the urb */

structkref kref;       /* reference count of the URB */

spinlock_t lock;        /* lock for the URB */

void*hcpriv;           /* private data for host controller */

atomic_t use_count;     /* concurrent submissions counter */

u8 reject;          /* submissions will fail */

/* public: documented fields in the urb that can be used by drivers */

structlist_head urb_list;  /* list head for use by the urb's

* current owner */

structusb_device *dev;     /* (in) pointer to associated device */

unsigned intpipe;      /* (in) pipe information */

intstatus;         /* (return) non-ISO status */

unsigned inttransfer_flags;    /* (in) URB_SHORT_NOT_OK | ...*/

void*transfer_buffer;      /* (in) associated data buffer */

dma_addr_t transfer_dma;    /* (in) dma addr for transfer_buffer */

inttransfer_buffer_length; /* (in) data buffer length */

intactual_length;      /* (return) actual transfer length */

unsigned char*setup_packet;    /* (in) setup packet (control only) */

dma_addr_t setup_dma;       /* (in) dma addr for setup_packet */

intstart_frame;        /* (modify) start frame (ISO) */

intnumber_of_packets;      /* (in) number of ISO packets */

intinterval;           /* (modify) transfer interval

* (INT/ISO) */

interror_count;        /* (return) number of ISO errors */

void*context;          /* (in) context for completion */

usb_complete_t complete;    /* (in) completion routine */

structusb_iso_packet_descriptor iso_frame_desc[0];

/* (in) ISO ONLY */

};

使用urb实现数据传输的过程如下:

分配一个urb结构体

调用usb_alloc_urb函数,分配一个uk_urb结构体空间,并初始化结构体。

设置urb结构体

USB2.0中定义了控制、中断、批量、同步四种传输方式。Linux内核中对应这四种传输方式定义了对应的urb的接口函数。

static inline void usb_fill_control_urb (struct urb *urb,

struct usb_device *dev,

unsigned int pipe,

unsigned char *setup_packet,

void *transfer_buffer,

int buffer_length,

usb_complete_t complete_fn,

void *context)

static inline void usb_fill_bulk_urb (struct urb *urb,

struct usb_device *dev,

unsigned int pipe,

void *transfer_buffer,

int buffer_length,

usb_complete_t complete_fn,

void *context)

static inline void usb_fill_int_urb (struct urb *urb,

struct usb_device *dev,

unsigned int pipe,

void *transfer_buffer,

int buffer_length,

usb_complete_t complete_fn,

void *context,

int interval)

usb鼠标因其数据量少,对传输的实时性要求较高,因此选用中断传输的方式。这里重点讲解usb_fill_int_urb函数。

urb:需要设置的urb结构体;

dev:urb要发送到的usb设备;

pipe:urb要被发送到的USB设备的特定端点,使用usb_rcvintpipe函数或者usb_sndintpipe函数创建端点;

transfer_buffer:指向发送数据或者接收数据的虚拟缓冲区;

buffer_length:transfer_buffer数据缓冲区的长度;

complete_fn:指向urb完成时被调用的完成处理函数;

context:完成处理函数的上下文;

interval:urb调度的间隔时间。

提交urb

调用usb_submit_urb函数,将urb提交到usb主机控制器驱动程序。

在urb完成处理函数usbmouse_as_key_irq中执行如下操作:

(1)判断按键是否是否发生变化,若变化,则上传对应的按键事件。

(2)重新提交urb。

usbmouse_as_key_disconnect函数中完成如下操作:

调用usb_kill_urb函数杀死urb;

调用usb_free_urb函数释放urb空间;

调用usb_buffer_free释放缓冲区;

调用input_unregister_device,卸载设备;

调用input_free_device,释放dev设备。

代码如下:

#include 

#include 

#include 

#include 

#include 

#include 

staticstructinput_dev *uk_dev;

staticchar* usb_buf;

staticdma_addr_t usb_buf_phys;

staticintlen;

staticstructurb *uk_urb;

staticstructusb_device_id usbmouse_as_key_id_table [] = {

{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,

USB_INTERFACE_PROTOCOL_MOUSE) },

};

staticvoidusbmouse_as_key_irq(structurb *urb)

{

staticunsigned charpre_val ;

#if 0

inti;

staticintcnt = 0;

printk("data cnt %d:",++cnt);

for(i = 0; i 

{

printk("%02x", usb_buf[i]);

}

printk("\n");

#endif

/*USB鼠标数据含义

* data[0]: bit0-左键,1-按下,0-松开

*          bit1-右键,1-按下,0-松开

*          bit2-中键,1-按下,0-松开

*/

if((pre_val & (1<<0)) != (usb_buf[0] & (1<<0)))

{

input_event(uk_dev, EV_KEY, KEY_L, (usb_buf[0] & (1<<0))?1:0);

input_sync(uk_dev);

}

if((pre_val & (1<<1)) != (usb_buf[0] & (1<<1)))

{

input_event(uk_dev, EV_KEY, KEY_S, (usb_buf[0] & (1<<1))?1:0);

input_sync(uk_dev);

}

if((pre_val & (1<<2)) != (usb_buf[0] & (1<<2)))

{

input_event(uk_dev, EV_KEY, KEY_ENTER, (usb_buf[0] & (1<<2))?1:0);

input_sync(uk_dev);

}

pre_val = usb_buf[0];

/*重新提交urb */

usb_submit_urb(uk_urb, GFP_KERNEL);

}

staticintusbmouse_as_key_probe(structusb_interface *intf, conststructusb_device_id *id)

{

structusb_device *dev = interface_to_usbdev(intf);

structusb_host_interface *interface;

structusb_endpoint_descriptor *endpoint;

intpipe;

interface = intf->cur_altsetting;

endpoint = &interface->endpoint[0].desc;

/* a、分配一个input_dev */

uk_dev = input_allocate_device();

/* b、设置*/

/* b.1能产生哪类事件*/

set_bit(EV_KEY, uk_dev->evbit);

set_bit(EV_REP, uk_dev->evbit);

/* b.2能产生哪些事件*/

set_bit(KEY_L, uk_dev->keybit);

set_bit(KEY_S, uk_dev->keybit);

set_bit(KEY_ENTER, uk_dev->keybit);

/* c、注册*/

input_register_device(uk_dev);

/* d、硬件相关的操作*/

/*数据传输三要素:源、目的、长度*/

/*源:  */

pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

/*长度*/

len = endpoint->wMaxPacketSize;

/*目的*/

usb_buf = usb_buffer_alloc(dev, len, GFP_ATOMIC, &usb_buf_phys);

/*分配urb: usb request block */

uk_urb = usb_alloc_urb(0, GFP_KERNEL);

/*使用"数据传输三要素"设置urb */

usb_fill_int_urb(uk_urb, dev, pipe, usb_buf, len, usbmouse_as_key_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);

return0;

}

staticvoidusbmouse_as_key_disconnect(structusb_interface *intf)

{

structusb_device *dev = interface_to_usbdev(intf);

usb_kill_urb(uk_urb);

usb_free_urb(uk_urb);

usb_buffer_free(dev, len, usb_buf, usb_buf_phys);

input_unregister_device(uk_dev);

input_free_device(uk_dev);

}

staticstructusb_driver usbmouse_as_key_driver = {

.name       = "usbmouse_as_key",

.probe      = usbmouse_as_key_probe,

.disconnect = usbmouse_as_key_disconnect,

.id_table   = usbmouse_as_key_id_table,

};

staticintusbmouse_as_key_init()

{

usb_register(&usbmouse_as_key_driver);

return0;

}

staticvoidusbmouse_as_key_exit()

{

usb_deregister(&usbmouse_as_key_driver);

}

module_init(usbmouse_as_key_init);

module_exit(usbmouse_as_key_exit);

MODULE_LICENSE("GPL");

测试截图如下:

1df72d457bf0d62bce0f133c214d8373.png

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值