分类: LINUX
2013-09-07 19:05:53
/*
* Copyright (c) 1999-2001 Vojtech Pavlik
*
* USB HIDBP Mouse support
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include
#include
#include
#include
#include
#include
/* for apple IDs */
#ifdef CONFIG_USB_HID_MODULE
#include "../hid-ids.h"
#endif
/*
* Version Information
*/
#define DRIVER_VERSION "v1.6"
#define DRIVER_AUTHOR "Vojtech Pavlik <>"
#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]; /* 设备节点名称 */
struct usb_device *usbdev; /* USB 鼠标是一种 USB 设备,需要内嵌一个 USB 设备结构体来描述其 USB 属性 */
struct input_dev *dev; /* USB 鼠标同时又是一种输入设备,需要内嵌一个输入设备结构体来描述其输入设备的属性 */
struct urb *irq; /* URB 请求包结构体,用于传送数据 */
signed char *data; /* 普通传输用的地址 */
dma_addr_t data_dma; /* dma 传输用的地址 */
};
static void usb_mouse_irq(struct urb *urb) /* 完成函数,在urb传输结束后被调用*/
{
struct usb_mouse *mouse = urb->context; /*urb->context指向我们在中断urb初始化中传递给void *context */
signed char *data = mouse->data; /* 获取数据缓冲区的首地址*/
struct input_dev *dev = mouse->dev; /*获取输入设备结构体数据 */
int status;
switch (urb->status) {
case 0: /* success */ /* struct urb ->status=0表示发送成功*/
break;
case -ECONNRESET: /* unlink */ /* urb被usb_unlink_urb调用解开链接,urb的transfer_flags变量被设置为UEB_ASYNC_UNLINK */
case -ENOENT: /* urb被usb_unlink_urb调用终止*/
case -ESHUTDOWN: /* USB主控制器驱动程序发生严重 错误,设备已经被禁止,或者从系统脱离,而urb在设备被移除之后提交*/
return;
/* -EPIPE: should clear the halt */
default: /* error */
goto resubmit; /* 如果发送错误则重新发送数据*/
}
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]);
input_sync(dev);
resubmit:
status = usb_submit_urb (urb, GFP_ATOMIC); /* 系统需要周期性不断地获取鼠标的事件信息,
*因此在 urb 回调函数的末尾再次提交 urb 请求块,
*这样又调用新的回调函数,周而复始。
*在回调函数中提交 urb 一定只能是 GFP_ATOMIC 优先级的,
*因为 urb 回调函数运行于中断上下文中,
*在提交 urb 过程中可能会需要申请内存、保持信号量,
*这些操作或许会导致 USB core 睡眠,一切导致睡眠
*的行为都是不允许的。
*/
if (status)
err ("can't resubmit intr, %s-%s/input0, status %d",
mouse->usbdev->bus->bus_name,
mouse->usbdev->devpath, status);
}
static int usb_mouse_open(struct input_dev *dev)
{
struct usb_mouse *mouse = input_get_drvdata(dev); /* 从设备的私有数据区获取设备描述结构体,可通过nput_set_drvdata 保存数据*/
mouse->irq->dev = mouse->usbdev; /* 初始化struct urb的dev,它是 urb所发送的目标,该变量在urb可以被发送到USB核心之前必须有USB驱动程序初始化 */
if (usb_submit_urb(mouse->irq, GFP_KERNEL)) /* 提交urb(把数据从批量端口发出)*/
return -EIO;
return 0;
}
static void usb_mouse_close(struct input_dev *dev)
{
struct usb_mouse *mouse = input_get_drvdata(dev);
usb_kill_urb(mouse->irq); /* 取消urb(终止一个已经被提交到USB核心的urb,通常是在设备从系统中断开时调用该函数)*/
}
static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf); /* 接口结构体包含于设备结构体中,interface_to_usbdev 是通过接口结构体获得它的设备结构体
* 实际上interface_to_usbdev 是通过container_of实现的
*/
struct usb_host_interface *interface; /* 指向描述接口设置的结构体 */
struct usb_endpoint_descriptor *endpoint; /* 指向端点描述结构体,包含真正的端点信息 */
struct usb_mouse *mouse;
struct input_dev *input_dev;
int pipe, maxp;
int error = -ENOMEM;
interface = intf->cur_altsetting; /* 指向altsetting数组内部的指针,表示该接口的当前活动设置(指向altsetting 数组中的一个)即struct usb_host_interface*/
if (interface->desc.bNumEndpoints != 1) /* 如果该接口使用的端点数(,不包括端点0 )不等于1,则返回错误
* 鼠标仅有一个 interrupt 类型的 in 端点,不满足此要求的设备均报错
*/
return -ENODEV;
endpoint = &interface->endpoint[0].desc; /* 指向端点1 描述结构体(端点0不包括在内)*/
if (!usb_endpoint_is_int_in(endpoint)) /* 如果不是中断输入类型则返回错误*/
return -ENODEV;
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); /* 指定 USB设备的指定端点号设置为一个中断IN端点*/
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); /*返回对应端点能够传输的最大的数据包,
* 鼠标的返回的最大数据包为4个字节,数据包具体内容在 urb回调函数中有详细说明
*/
mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL); /* 为鼠标描述结构分配内存*/
input_dev = input_allocate_device(); /* 分配输入设备*/
if (!mouse || !input_dev)
goto fail1;
mouse->data = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &mouse->data_dma); /* 创建一个DMA缓冲区来以最高效的方式发送数据到设备*/
if (!mouse->data) /* data_dma 用来保存buffer的DMA地址,data 保存首地址 */
goto fail1;
mouse->irq = usb_alloc_urb(0, GFP_KERNEL); /* 创建struct urb结构*/
if (!mouse->irq)
goto fail2;
/* 填充 usb 设备结构体和输入设备结构体 */
mouse->usbdev = dev; /* USB设备描述结构=interface_to_usbdev(intf) */
mouse->dev = input_dev;
/* 获取鼠标设备的名称 */
if (dev->manufacturer) /* 如果有制造商*/
strlcpy(mouse->name, dev->manufacturer, sizeof(mouse->name));
if (dev->product) {
if (dev->manufacturer)
strlcat(mouse->name, " ", sizeof(mouse->name));
strlcat(mouse->name, dev->product, sizeof(mouse->name));
}
if (!strlen(mouse->name))
snprintf(mouse->name, sizeof(mouse->name),
"USB HIDBP Mouse %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
/* 填充鼠标设备结构体中的节点名。usb_make_path 用来获取 USB 设备在 Sysfs 中的路径,格式为:usb-usb 总线号-路径名 */
usb_make_path(dev, mouse->phys, sizeof(mouse->phys));
strlcat(mouse->phys, "/input0", sizeof(mouse->phys));
input_dev->name = mouse->name; /* 将鼠标设备的名称赋给鼠标设备内嵌的输入子系统结构体 */
input_dev->phys = mouse->phys; /* 将鼠标设备的设备节点名赋给鼠标设备内嵌的输入子系统结构体 */
usb_to_input_id(dev, &input_dev->id); /* input_dev 中的 input_id 结构体,用来存储厂商、设备类型和设备的编号
,这个函数是将设备描述符中的编号赋给内嵌的输入子系统结构体 */
input_dev->dev.parent = &intf->dev;
/* 能产生按键类事件和相对位移类事件 */
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
/* 能产生按键类事件中鼠标类事件、左键、中键、右键 */
input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
/* 能产生相对位移类事件中的 x方向的相对位移类事件, y方向的相对位移类事件 */
input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
/* 能产生按键类事件中的鼠标事件中的其他按键类事件( 有的鼠标有 ) */
input_dev->keybit[BIT_WORD(BTN_MOUSE)] |= BIT_MASK(BTN_SIDE) |
BIT_MASK(BTN_EXTRA);
/* 能产生相对位移事件中的滚轮类事件 */
input_dev->relbit[0] |= BIT_MASK(REL_WHEEL);
/* input_dev 的 private 数据项用于表示当前输入设备的种类,这里将鼠标结构体对象赋给它 */
input_set_drvdata(input_dev, mouse); /* 将保存为设备的私有数据,可通过input_get_drvdata 获取数据 */
input_dev->open = usb_mouse_open; /* 填充输入设备打开函数指针 */
input_dev->close = usb_mouse_close; /* 填充输入设备关闭函数指针 */
usb_fill_int_urb(mouse->irq, dev, pipe, mouse->data,
(maxp > 8 ? 8 : maxp),
usb_mouse_irq, mouse, endpoint->bInterval); /* 中断urb初始化*/
mouse->irq->transfer_dma = mouse->data_dma;
mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; /* URB_NO_TRANSFER_DMA_MAP:当urb包含一个即将传输的DMA缓冲区时应该设置该位,
* USB核心使用transfer_dma变量所指向的缓冲区,而不是 transfer_buffer变量所指向的
*/
error = input_register_device(mouse->dev); /* 注册输入设备*/
if (error)
goto fail3;
usb_set_intfdata(intf, mouse); /* 把数据指针保存到接口设备中,该函数接受一个指向任意数据类型的指针,
把它保存到struct usb_interface结构体中以方便后面的访问*/
return 0;
fail3:
usb_free_urb(mouse->irq);
fail2:
usb_buffer_free(dev, 8, mouse->data, mouse->data_dma);
fail1:
input_free_device(input_dev);
kfree(mouse);
return error;
}
static void usb_mouse_disconnect(struct usb_interface *intf)
{
struct usb_mouse *mouse = usb_get_intfdata (intf);
usb_set_intfdata(intf, NULL); /* 将接口指针设置为空*/
if (mouse) {
usb_kill_urb(mouse->irq); /* 取消urb */
input_unregister_device(mouse->dev); /* 卸载输入设备*/
usb_free_urb(mouse->irq); /* 释放struct urb结构,驱动程序必须调用该函数来告诉USB核心驱动程序已经使用完urb,
* 该函数调用之后,urb结构体就消失,驱动程序不能再访问它
*/
usb_buffer_free(interface_to_usbdev(intf), 8, mouse->data, mouse->data_dma); /* 释放DMA缓冲区*/
kfree(mouse); /* 释放鼠标描述结构体*/
}
}
/* usb_device_id 结构体用于表示该驱动程序所支持的设备,
* USB_INTERFACE_INFO 可以用来匹配特定类型的 这个宏的参数意思为 (类别, 子类别, 协议)。
* USB_INTERFACE_CLASS_HID 表示是一种 HID (Human Interface Device),即人机交互设备类别;
* USB_INTERFACE_SUBCLASS_BOOT 是子类别,表示是一种 boot 阶段使用的 HID;
* USB_INTERFACE_PROTOCOL_MOUSE 表示是鼠标设备,遵循鼠标的协议。
*/
static struct usb_device_id usb_mouse_id_table [] = {
{ USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
USB_INTERFACE_PROTOCOL_MOUSE) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table); /* 向系统注册id_table,对于USB设备来说第一个必须是usb,*/
//这个宏用来让运行在用户空间的程序知道这个驱动程序能够支持的设备
static struct usb_driver usb_mouse_driver = { /* usb驱动操作函数*/
.name = "usbmouse",
.probe = usb_mouse_probe, /* 探测函数,在设备接入系统时调用*/
.disconnect = usb_mouse_disconnect, /* 当设备被拔出或驱动程序被卸载时调用*/
.id_table = usb_mouse_id_table, /* USB核心通过比较id_table来确定是否支持该设备*/
};
static int __init usb_mouse_init(void)
{
int retval = usb_register(&usb_mouse_driver); /* 注册*/
if (retval == 0)
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
DRIVER_DESC "\n");
return retval;
}
static void __exit usb_mouse_exit(void)
{
usb_deregister(&usb_mouse_driver); /* 注销*/
}
module_init(usb_mouse_init);
module_exit(usb_mouse_exit);