/*
* $Id: usbmouse.c,v 1.15 2001/12/27 10:37:41 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
* USB HIDBP Mouse support
*/
#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>
/*
* Version Information
*/
#define DRIVER_VERSION "v1.6"
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
#define DRIVER_DESC "USB HID Boot Protocol mouse driver"
#define DRIVER_LICENSE "GPL"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE(DRIVER_LICENSE);
/*
* 鼠标结构体,用于描述鼠标设备。
*/
struct usb_mouse
{
/* 鼠标设备的名称,包括生产厂商、产品类别、产品等信息 */
char name[128];
/* 设备节点名称 */
char phys[64];
/* USB 鼠标是一种 USB 设备,需要内嵌一个 USB 设备结构体来描述其 USB 属性 */
struct usb_device *usbdev;
/* USB 鼠标同时又是一种输入设备,需要内嵌一个输入设备结构体来描述其输入设备的属性 */
struct input_dev *dev;
/* URB 请求包结构体,用于传送数据 */
struct urb *irq;
/* 普通传输用的地址 */
signed char *data;
/* dma 传输用的地址 */
dma_addr_t data_dma;
};
/*
* urb 回调函数,在完成提交 urb 后,urb 回调函数将被调用。
* 此函数作为 usb_fill_int_urb 函数的形参,为构建的 urb 制定的回调函数。
*/
static void usb_mouse_irq(struct urb *urb)
{
/*
* urb 中的 context 指针用于为 USB 驱动程序保存一些数据。比如在这个回调函数的形参没有传递在 probe
* 中为 mouse 结构体分配的那块内存的地址指针,而又需要用到那块内存区域中的数据,context 指针则帮了
* 大忙了!
* 在填充 urb 时将 context 指针指向 mouse 结构体数据区,在这又创建一个局部 mouse 指针指向在 probe
* 函数中为 mouse 申请的那块内存,那块内存保存着非常重要数据。
* 当 urb 通过 USB core 提交给 hc 之后,如果结果正常,mouse->data 指向的内存区域将保存着鼠标的按键
* 和移动坐标信息,系统则依靠这些信息对鼠标的行为作出反应。
* mouse 中内嵌的 dev 指针,指向 input_dev 所属于的内存区域。
*/
struct usb_mouse *mouse = urb->context;
signed char *data = mouse->data;
struct input_dev *dev = mouse->dev;
int status;
/*
* status 值为 0 表示 urb 成功返回,直接跳出循环把鼠标事件报告给输入子系统。
* ECONNRESET 出错信息表示 urb 被 usb_unlink_urb 函数给 unlink 了,ENOENT 出错信息表示 urb 被
* usb_kill_urb 函数给 kill 了。usb_kill_urb 表示彻底结束 urb 的生命周期,而 usb_unlink_urb 则
* 是停止 urb,这个函数不等 urb 完全终止就会返回给回调函数。这在运行中断处理程序时或者等待某自旋锁
* 时非常有用,在这两种情况下是不能睡眠的,而等待一个 urb 完全停止很可能会出现睡眠的情况。
* ESHUTDOWN 这种错误表示 USB 主控制器驱动程序发生了严重的错误,或者提交完 urb 的一瞬间设备被拔出。
* 遇见除了以上三种错误以外的错误,将申请重传 urb。
*/
switch (urb->status)
{
case 0: /* success */
break;
case -ECONNRESET: /* unlink */
case -ENOENT:
case -ESHUTDOWN:
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit;
}
/*
* 向输入子系统汇报鼠标事件情况,以便作出反应。
* data 数组的第0个字节:bit 0、1、2、3、4分别代表左、右、中、SIDE、EXTRA键的按下情况;
* data 数组的第1个字节:表示鼠标的水平位移;
* data 数组的第2个字节:表示鼠标的垂直位移;
* data 数组的第3个字节:REL_WHEEL位移。
*/
input_report_key(dev, BTN_LEFT, data[0] & 0x01);
input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);
input_report_key(dev, BTN_SIDE, data[0] & 0x08);
input_report_key(dev, BTN_EXTRA, data[0] & 0x10);
input_report_rel(dev, REL_X, data[1]);
input_report_rel(dev, REL_Y, data[2]);
input_report_rel(dev, REL_WHEEL, data[3]);
/*
* 这里是用于事件同步。上面几行是一次完整的鼠标事件,包括按键信息、绝对坐标信息和滚轮信息,输入子
* 系统正是通过这个同步信号来在多个完整事件报告中区分每一次完整事件报告。示意如下:
* 按键信息 坐标位移信息 滚轮信息 EV_SYC | 按键信息 坐标位移信息 滚轮信息 EV_SYC ...
*/
input_sync(dev);
/*
* 系统需要周期性不断地获取鼠标的事件信息,因此在 urb 回调函数的末尾再次提交 urb 请求块,这样又会
* 调用新的回调函数,周而复始。
* 在回调函数中提交 urb 一定只能是 GFP_ATOMIC 优先级的,因为 urb 回调函数运行于中断上下文中,在提
* 交 urb 过程中可能会需要申请内存、保持信号量,这些操作或许会导致 USB core 睡眠,一切导致睡眠的行
* 为都是不允许的。
*/
resubmit:
status = usb_submit_urb (urb, GFP_ATOMIC);
if (status)
err ("can't resubmit intr, %s-%s/input0, status %d",
mouse->usbdev->bus->bus_name,
mouse->usbdev->devpath, status);
}
/*
* 打开鼠标设备时,开始提交在 probe 函数中构建的 urb,进入 urb 周期。
*/
static int usb_mouse_open(struct input_dev *dev)
{
struct usb_mouse *mouse = dev->private;
mouse->irq->dev = mouse->usbdev;
if (usb_submit_urb(mouse->irq, GFP_KERNEL))
return -EIO;
return 0;
}
/*
* 关闭鼠标设备时,结束 urb 生命周期。
*/
static void usb_mouse_close(struct input_dev *dev)
{
struct usb_mouse *mouse = dev->private;
usb_kill_urb(mouse->irq);
}
/*
* 驱动程序的探测函数
*/
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
/*
* 接口结构体包含于设备结构体中,interface_to_usbdev 是通过接口结构体获得它的设备结构体。
* usb_host_interface 是用于描述接口设置的结构体,内嵌在接口结构体 usb_interface 中。
* usb_endpoint_descriptor 是端点描述符结构体,内嵌在端点结构体 usb_host_endpoint 中,而端点
* 结构体内嵌在接口设置结构体中。
*/
struct usb_device *dev = interface_to_usbdev(intf);
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct usb_mouse *mouse;
struct input_dev *input_dev;
int pipe, maxp;
interface = intf->cur_altsetting;
/* 鼠标仅有一个 interrupt 类型的 in 端点,不满足此要求的设备均报错 */
if (interface->desc.bNumEndpoints != 1)
return -ENODEV;
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
/*
* 返回对应端点能够传输的最大的数据包,鼠标的返回的最大数据包为4个字节,数据包具体内容在 urb
* 回调函数中有详细说明。
*/
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
/* 为 mouse 设备结构体分配内存 */
mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);
/* input_dev */
input_dev = input_allocate_device();
if (!mouse || !input_dev)
goto fail1;
/*
* 申请内存空间用于数据传输,data 为指向该空间的地址,data_dma 则是这块内存空间的 dma 映射,
* 即这块内存空间对应的 dma 地址。在使用 dma 传输的情况下,则使用 data_dma 指向的 dma 区域,
* 否则使用 data 指向的普通内存区域进行传输。
* GFP_ATOMIC 表示不等待,GFP_KERNEL 是普通的优先级,可以睡眠等待,由于鼠标使用中断传输方式,
* 不允许睡眠状态,data 又是周期性获取鼠标事件的存储区,因此使用 GFP_ATOMIC 优先级,如果不能
* 分配到内存则立即返回 0。
*/
mouse->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse->data_dma);
if (!mouse->data)
goto fail1;
/*
* 为 urb 结构体申请内�
- 1
- 2
前往页