一个鼠标驱动程序的分析(/driver/input/mouse/amimouse.c)
一:Input设备驱动,(我称为虚拟输入设备)//drivers/input/input.c文件
input设备是一种字符设备在模块初始化时创建设备类"input",注册Input字符设备,input的操作函数只有Open函数。当打开特定设备时才将特定的设备操作函数
static struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
函数调用如下:
“/input/input . c”
input_init()//这是虚拟输入设备的入口,既模块加载时调用
{
class_simple_create(THIS_MODULE,"input");//创建设备类
input_proc_init()//创建proc下的文件节点
retval = register_chrdev(INPUT_MAJOR, "input", &input_fops);//注册字符设备驱动程序,在系统字符设备数组中添加一个字符设备,主设备号为INPUT—MAJOR,操作函数集为input_fops,在特殊文件打开时会根据文件的节点结构INODE中的主设备号在系统字符设备数组中搜索主设备号相同的字符设备驱动程序的操作函数集并将此操作函数集付给FILE结构的操作函数集指针f_ops并执行该函数集中的打开操作。。对于本input类设备即为input_fops中的input_open_file
devfs_mk_dir("input")//在设备目录"/dev"下创建devfs文件系统的"input"目录,以后的具体输入设备也将在这个目录下建立特殊设备文件,当打开该特殊文件时即对设备进行操作
}
当打开具体文件时既执行input_ipen_file此函数会寻到具体设备的文件操作函数集并付给file->f_op(这是一个文件的操作函数集,当系统打开一个文件时即产生一个file结构对该文件的操作都通过file中的f_opes 如读取即调用FILE->f_op->read等
二:输入驱动器,输入驱动器是指一类的输入设备(比如鼠标输入驱动器,键盘输入驱动器等等)这里说的是鼠标输入驱动器。。(我也称之为虚拟鼠标驱动器,因为他并不完成真正的硬件相关的鼠标驱动,真正的硬件IO驱动要在device中完成)他的描述结构是:
struct input_handler {
void *private;//私有数据
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);//响应输入事件的函数
struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id);//连接设备的函数
void (*disconnect)(struct input_handle *handle);//断开设备
struct file_operations *fops;//文件操作函数集
int minor;//次设备号
char *name;//设备名
.....
};
static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
static struct input_handler mousedev_handler = {
.event = mousedev_event,//鼠标事件回调函数
.connect = mousedev_connect,//连接device
.disconnect = mousedev_disconnect,
.fops = &mousedev_fops,//文件操作函数集
.minor = MOUSEDEV_MINOR_BASE,//次设备号基数
.name = "mousedev",//设备名
.id_table = mousedev_ids,//本驱动支持的设备ID
};
他的入口是:
“/input/mousedev . c”
static int __init mousedev_init(void)
{
input_register_handler(&mousedev_handler);//在虚拟输入设备中注册鼠标输入驱动器
memset(&mousedev_mix, 0, sizeof(struct mousedev));
INIT_LIST_HEAD(&mousedev_mix.list);
init_waitqueue_head(&mousedev_mix.wait);
mousedev_table[MOUSEDEV_MIX] = &mousedev_mix;//给设备数组一个默认的鼠标device
mousedev_mix.exist = 1;
mousedev_mix.minor = MOUSEDEV_MIX;
devfs_mk_cdev(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX),
S_IFCHR|S_IRUGO|S_IWUSR, "input/mice");//建立input/mice文件
class_simple_device_add(input_class, MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX),
NULL, "mice");//在input_class设备类中增加本设备驱动程序
printk(KERN_INFO "mice: PS/2 mouse device common for all mice/n");
return 0;
}
虚拟鼠标设备驱动器就提供了各种操作的操作集函数,如read,write,ioctrl,等
static struct file_operations mousedev_fops = {
.owner = THIS_MODULE,
.read = mousedev_read,
.write = mousedev_write,
.poll = mousedev_poll,
.open = mousedev_open,
.release = mousedev_release,
.fasync = mousedev_fasync,
};
“/input/input . c”
void input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;//具体设备描述结构
struct input_handle *handle;//设备句柄
struct input_device_id *id;//设备ID
if (!handler) return;
INIT_LIST_HEAD(&handler->h_list);//初始化设备驱动器的链表
if (handler->fops != NULL)
input_table[handler->minor >> 5] = handler;//把驱动器按次设备号放到驱动器数组里,虚拟设备支持最多8个次设备
list_add_tail(&handler->node, &input_handler_list);//加入驱动器链表
list_for_each_entry(dev, &input_dev_list, node)
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
if ((id = input_match_device(handler->id_table, dev)))//用当前所有的输入设备于驱动器适配
if ((handle = handler->connect(handler, dev, id)))//适配成功与驱动器连接
input_link_handle(handle);//连接成功将设备句柄加到设备的句柄链表中
}
我们先假设注册驱动器时还没有一个输入设备在链表中,即注册时不需要进行驱动器适配。在后面设备注册时再讲适配连接
这样就完成了一个驱动器的注册
三,具体设备。。
这里完成的是真实与硬件交互的设备程序。。设备的描述结构是input_dev
struct input_dev {
void *private;//私有数据
char *name;//
......
int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
int (*accept)(struct input_dev *dev, struct file *file);
int (*flush)(struct input_dev *dev, struct file *file);
int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect);
int (*erase_effect)(struct input_dev *dev, int effect_id);//都是些回调函数了
struct input_handle *grab;//设备句柄
struct device *dev;//通用设备结构
struct list_head h_list;//句柄链表input_link_handle函数将设备句柄加入到链表中。dev响应中断时如果没有自己专用的event函数就遍历handle列表找到handle指向的input_handler的evnt函数调用
struct list_head node;
};
struct input_handle {
void *private;//私有数据
int open;
char *name;
struct input_dev *dev;//设备
struct input_handler *handler;//句柄拥有者驱动器
struct list_head d_node;
struct list_head h_node;
};
它的入口是:
“/input/mouse/amimouse . c”
static int __init amimouse_init(void)
{
if (!MACH_IS_AMIGA || !AMIGAHW_PRESENT(AMI_MOUSE))
return -ENODEV;
amimouse_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
amimouse_dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
amimouse_dev.keybit[LONG(BTN_LEFT)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
amimouse_dev.open = amimouse_open;
amimouse_dev.close = amimouse_close;
amimouse_dev.name = amimouse_name;
amimouse_dev.phys = amimouse_phys;
amimouse_dev.id.bustype = BUS_AMIGA;
amimouse_dev.id.vendor = 0x0001;
amimouse_dev.id.product = 0x0002;
amimouse_dev.id.version = 0x0100;
input_register_device(&amimouse_dev);//注册设备
printk(KERN_INFO "input: %s at joy0dat/n", amimouse_name);
return 0;
}
“/input/input . c”
void input_register_device(struct input_dev *dev)
{
struct input_handle *handle;
struct input_handler *handler;
struct input_device_id *id;
/*
* If delay and period are pre-set by the driver, then autorepeating
* is handled by the driver itself and we don't do it in input.c.
*/
init_timer(&dev->timer);//初始化设备的定时器,对鼠标没什么用mousedev里没对他做什么,对键盘有用
if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) {
dev->timer.data = (long) dev;
dev->timer.function = input_repeat_key;
dev->rep[REP_DELAY] = 250;
dev->rep[REP_PERIOD] = 33;
}
INIT_LIST_HEAD(&dev->h_list);//初始化设备的句柄链表
list_add_tail(&dev->node, &input_dev_list);
list_for_each_entry(handler, &input_handler_list, node)//遍历驱动器列表
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
if ((id = input_match_device(handler->id_table, dev)))//找到与设备相配的驱动器,用id_table适配
if ((handle = handler->connect(handler, dev, id)))//适配成功与驱动器连接// 这里是mouse驱动器mousedev的connect函数是mousedev_connect
input_link_handle(handle);//把Mousedev返回input_handle(注意不是input_handler)加到dev的handle连表中//这样dev响应中断时如果没有自己专用的event函数就遍历handle列表找到handle指向的input_handler的evnt函数调用
#ifdef CONFIG_HOTPLUG
input_call_hotplug("add", dev);
#endif
#ifdef CONFIG_PROC_FS
input_devices_state++;
wake_up(&input_devices_poll_wait);
#endif
}
看看驱动器是怎么连接设备的,他申请一个mousedev结构用来保存鼠标设备信息。将设备和设备句柄及input_handler联系起来放在mousedev_table指针数组中并给每个mousedev分配一个ID minor进行唯一标志
struct mousedev {
int exist;//设备是否存在
int open;//
int minor;
char name[16];
<