?? 最近新學(xué)linux對(duì)一個(gè)鼠標(biāo)驅(qū)動(dòng)的分析.cpp
字號(hào):
一個(gè)鼠標(biāo)驅(qū)動(dòng)程序的分析(/driver/input/mouse/amimouse.c)
一:Input設(shè)備驅(qū)動(dòng),(我稱為虛擬輸入設(shè)備)//drivers/input/input.c文件
input設(shè)備是一種字符設(shè)備在模塊初始化時(shí)創(chuàng)建設(shè)備類"input",注冊(cè)Input字符設(shè)備,input的操作函數(shù)只有Open函數(shù)。當(dāng)打開特
定設(shè)備時(shí)才將特定的設(shè)備操作函數(shù)
static struct file_operations input_fops = {
.owner = THIS_MODULE,
.open = input_open_file,
};
函數(shù)調(diào)用如下:
“/input/input . c”
input_init()//這是虛擬輸入設(shè)備的入口,既模塊加載時(shí)調(diào)用
{
class_simple_create(THIS_MODULE,"input");//創(chuàng)建設(shè)備類
input_proc_init()//創(chuàng)建proc下的文件節(jié)點(diǎn)
retval = register_chrdev(INPUT_MAJOR, "input", &input_fops);//注冊(cè)字符設(shè)備驅(qū)動(dòng)程序,在系統(tǒng)字符設(shè)備數(shù)組中添加一個(gè)字符設(shè)
備,主設(shè)備號(hào)為INPUT—MAJOR,操作函數(shù)集為input_fops,在特殊文件打開時(shí)會(huì)根據(jù)文件的節(jié)點(diǎn)結(jié)構(gòu)INODE中的主設(shè)備
號(hào)在系統(tǒng)字符設(shè)備數(shù)組中搜索主設(shè)備號(hào)相同的字符設(shè)備驅(qū)動(dòng)程序的操作函數(shù)集并將此操作函數(shù)集付給FILE結(jié)構(gòu)的操
作函數(shù)集指針f_ops并執(zhí)行該函數(shù)集中的打開操作。。對(duì)于本input類設(shè)備即為input_fops中的input_open_file
devfs_mk_dir("input")//在設(shè)備目錄"/dev"下創(chuàng)建devfs文件系統(tǒng)的"input"目錄,以后的具體輸入設(shè)備也將在這個(gè)目錄下建立
特殊設(shè)備文件,當(dāng)打開該特殊文件時(shí)即對(duì)設(shè)備進(jìn)行操作
}
當(dāng)打開具體文件時(shí)既執(zhí)行input_ipen_file
此函數(shù)會(huì)尋到具體設(shè)備的文件操作函數(shù)集并付給file->f_op(這是一個(gè)文件的操作函數(shù)集,當(dāng)系統(tǒng)打開一個(gè)文件時(shí)即產(chǎn)生一個(gè)file結(jié)構(gòu)
對(duì)該文件的操作都通過file中的f_opes 如讀取即調(diào)用FILE->f_op->read等
二:輸入驅(qū)動(dòng)器,輸入驅(qū)動(dòng)器是指一類的輸入設(shè)備(比如鼠標(biāo)輸入驅(qū)動(dòng)器,鍵盤輸入驅(qū)動(dòng)器等等)
這里說的是鼠標(biāo)輸入驅(qū)動(dòng)器。。(我也稱之為虛擬鼠標(biāo)驅(qū)動(dòng)器,因?yàn)樗⒉煌瓿烧嬲挠布嚓P(guān)的鼠標(biāo)驅(qū)動(dòng),
真正的硬件IO驅(qū)動(dòng)要在device中完成)他的描述結(jié)構(gòu)是
struct input_handler {
void *private;//私有數(shù)據(jù)
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);//響應(yīng)輸入事件的函數(shù)
struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id);//連接設(shè)備的函數(shù)
void (*disconnect)(struct input_handle *handle);//斷開設(shè)備
struct file_operations *fops;//文件操作函數(shù)集
int minor;//次設(shè)備號(hào)
char *name;//設(shè)備名
.....
};
static struct mousedev *mousedev_table[MOUSEDEV_MINORS];
static struct input_handler mousedev_handler = {
.event = mousedev_event,//鼠標(biāo)事件回調(diào)函數(shù)
.connect = mousedev_connect,//連接device
.disconnect = mousedev_disconnect,
.fops = &mousedev_fops,//文件操作函數(shù)集
.minor = MOUSEDEV_MINOR_BASE,//次設(shè)備號(hào)基數(shù)
.name = "mousedev",//設(shè)備名
.id_table = mousedev_ids,//本驅(qū)動(dòng)支持的設(shè)備ID
};
他的入口是:
“/input/mousedev . c”
static int __init mousedev_init(void)
{
input_register_handler(&mousedev_handler);//在虛擬輸入設(shè)備中注冊(cè)鼠標(biāo)輸入驅(qū)動(dòng)器
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;//給設(shè)備數(shù)組一個(gè)默認(rèn)的鼠標(biāo)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設(shè)備類中增加本設(shè)備驅(qū)動(dòng)程序
printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
return 0;
}
虛擬鼠標(biāo)設(shè)備驅(qū)動(dòng)器就提供了各種操作的操作集函數(shù),如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;//具體設(shè)備描述結(jié)構(gòu)
struct input_handle *handle;//設(shè)備句柄
struct input_device_id *id;//設(shè)備ID
if (!handler) return;
INIT_LIST_HEAD(&handler->h_list);//初始化設(shè)備驅(qū)動(dòng)器的鏈表
if (handler->fops != NULL)
input_table[handler->minor >> 5] = handler;//把驅(qū)動(dòng)器按次設(shè)備號(hào)放到驅(qū)動(dòng)器數(shù)組里,虛擬設(shè)備支持最多8個(gè)次設(shè)備
list_add_tail(&handler->node, &input_handler_list);//加入驅(qū)動(dòng)器鏈表
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)))//用當(dāng)前所有的輸入設(shè)備于驅(qū)動(dòng)器適配
if ((handle = handler->connect(handler, dev, id)))//適配成功與驅(qū)動(dòng)器連接
input_link_handle(handle);//連接成功將設(shè)備句柄加到設(shè)備的句柄鏈表中
}
我們先假設(shè)注冊(cè)驅(qū)動(dòng)器時(shí)還沒有一個(gè)輸入設(shè)備在鏈表中,即注冊(cè)時(shí)不需要進(jìn)行驅(qū)動(dòng)器適配。在后面設(shè)備注冊(cè)時(shí)再講適配
連接
這樣就完成了一個(gè)驅(qū)動(dòng)器的注冊(cè)
三,具體設(shè)備。。
這里完成的是真實(shí)與硬件交互的設(shè)備程序。。設(shè)備的描述結(jié)構(gòu)是input_dev
struct input_dev {
void *private;//私有數(shù)據(jù)
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);//都是些回調(diào)函數(shù)了
struct input_handle *grab;//設(shè)備句柄
struct device *dev;//通用設(shè)備結(jié)構(gòu)
struct list_head h_list;//句柄鏈表input_link_handle函數(shù)將設(shè)備句柄加入到鏈表中。dev響應(yīng)中斷時(shí)如果沒有自己專用的event函數(shù)就遍歷handle列表找到handle指向的input_handler的evnt函數(shù)調(diào)用
struct list_head node;
};
struct input_handle {
void *private;//私有數(shù)據(jù)
int open;
char *name;
struct input_dev *dev;//設(shè)備
struct input_handler *handler;//句柄擁有者驅(qū)動(dòng)器
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);//注冊(cè)設(shè)備
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);//初始化設(shè)備的定時(shí)器,對(duì)鼠標(biāo)沒什么用mousedev里沒對(duì)他做什么,對(duì)鍵盤有用
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);//初始化設(shè)備的句柄鏈表
list_add_tail(&dev->node, &input_dev_list);
list_for_each_entry(handler, &input_handler_list, node)//遍歷驅(qū)動(dòng)器列表
if (!handler->blacklist || !input_match_device(handler->blacklist, dev))
if ((id = input_match_device(handler->id_table, dev)))//找到與設(shè)備相配的驅(qū)動(dòng)器,用id_table適配
if ((handle = handler->connect(handler, dev, id)))//適配成功與驅(qū)動(dòng)器連接// 這里是mouse驅(qū)動(dòng)器
mousedev的connect函數(shù)是mousedev_connect
input_link_handle(handle);//把Mousedev返回input_handle(注意不是input_handler)加到dev的handle連表中
//這樣dev響應(yīng)中斷時(shí)如果沒有自己專用的event函數(shù)就遍歷handle列表找到handle指向的input_handler的evnt函數(shù)調(diào)用
#ifdef CONFIG_HOTPLUG
input_call_hotplug("add", dev);
#endif
#ifdef CONFIG_PROC_FS
input_devices_state++;
wake_up(&input_devices_poll_wait);
#endif
}
看看驅(qū)動(dòng)器是怎么連接設(shè)備的,他申請(qǐng)一個(gè)mousedev結(jié)構(gòu)用來保存鼠標(biāo)設(shè)備信息。將設(shè)備和設(shè)備句柄及input_handler聯(lián)系起來
放在mousedev_table指針數(shù)組中并給每個(gè)mousedev分配一個(gè)ID minor進(jìn)行唯一標(biāo)志
struct mousedev {
int exist;//設(shè)備是否存在
int open;//
int minor;
char name[16];
wait_queue_head_t wait;//等待隊(duì)列
struct list_head list;//打開mousdev的文件的列表
struct input_handle handle;//設(shè)備句柄-
struct mousedev_hw_data packet;
unsigned int pkt_count;
int old_x[4], old_y[4];
int frac_dx, frac_dy;
unsigned long touch;
};
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/mousedev . c”
static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
{
struct mousedev *mousedev;
int minor = 0;
for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++);
if (minor == MOUSEDEV_MINORS) {
printk(KERN_ERR "mousedev: no more free mousedev devices\n");
return NULL;
}
if (!(mousedev = kmalloc(sizeof(struct mousedev), GFP_KERNEL)))
return NULL;
memset(mousedev, 0, sizeof(struct mousedev));
INIT_LIST_HEAD(&mousedev->list);
init_waitqueue_head(&mousedev->wait);
mousedev->minor = minor;
mousedev->exist = 1;
mousedev->handle.dev = dev;
mousedev->handle.name = mousedev->name;
mousedev->handle.handler = handler;
sprintf(mousedev->name, "mouse%d", minor);
if (mousedev_mix.open)
input_open_device(&mousedev->handle);//看默認(rèn)的mousedev是否需要打開,如果需要就打開設(shè)備。
mousedev_table[minor] = mousedev;
devfs_mk_cdev(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor),
S_IFCHR|S_IRUGO|S_IWUSR, "input/mouse%d", minor);建立設(shè)備文件
class_simple_device_add(input_class,
MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor),
dev->dev, "mouse%d", minor);
return &mousedev->handle;
}
所有的注冊(cè)已經(jīng)完成
四:設(shè)備控制。。。。
打開設(shè)備。
當(dāng)系統(tǒng)打開一個(gè)設(shè)備時(shí) 如"/dev/input/mouse0",虛擬文件系統(tǒng)根據(jù)文件節(jié)點(diǎn)INODE中的主設(shè)備
號(hào)在系統(tǒng)字符設(shè)備數(shù)組中搜索主設(shè)備號(hào)相同的字符設(shè)備驅(qū)動(dòng)程序的操作函數(shù)集并將此操作函數(shù)集付給FILE結(jié)構(gòu)的操
作函數(shù)集指針f_ops并執(zhí)行該函數(shù)集中的打開操作。。對(duì)于input類設(shè)備即為input_fops中的input_open_file
“/input/input . c”
static int input_open_file(struct inode *inode, struct file *file)
{
struct input_handler *handler = input_table[iminor(inode) >> 5];//根據(jù)次設(shè)備號(hào)找到驅(qū)動(dòng)器這里我們就假設(shè)是打開
鼠標(biāo)既找到了mousedev_handler
struct file_operations *old_fops, *new_fops = NULL;
int err;
/* No load-on-demand here? */
if (!handler || !(new_fops = fops_get(handler->fops)))//得到輸入驅(qū)動(dòng)器的操作函數(shù)集//輸入驅(qū)動(dòng)器。可以是一個(gè)鼠標(biāo)輸入驅(qū)動(dòng)器
//或者一個(gè)鍵盤輸入驅(qū)動(dòng)器,這里分析的是鼠標(biāo)
return -ENODEV;
/*
* That's _really_ odd. Usually NULL ->open means "nothing special",
* not "no device". Oh, well...
*/
if (!new_fops->open) {//如果輸入驅(qū)動(dòng)器沒有打開操作函數(shù),既設(shè)備文件無法完成打開操作
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -