2009年4月13日星期一

linux下面开发PS2驱动过程

PS2键盘鼠标驱动开发文档

-------以及USBHID keyboard/mouse验证

 

 

 内容简介: 键盘鼠标作为基本的输入输出设备,特别是键盘更是与终端有关联,以下将简要分析其设备接口及终端输入相关处理机制, 以及驱动开发中的硬件相关配置部分.

 

目录索引:

 一.        PS2键盘鼠标驱动的硬件相关部分.

二.        PS2鼠标驱动的软件层次.

三.        USBHID keyboard/mouse验证.


一.  PS2键盘鼠标驱动的硬件相关部分.

 

文件: linux-2.4.21\drivers\char\ Ep93xx_w83977_keyb.c

同类参照文件: linux-2.4.21\drivers\char\ pc_keyb.c

 

基于w83977的键盘及鼠标设备,是完全与标准的8042键盘兼容的,因此本驱动基本直接从8042的标准驱动pc_keyb.c来进行修改,当中须要修改的地方相当的少,主要集中如下几点:

 

1.       中断的设置.

edb9302_enable_eint1()

ü         键盘的中断为EGPIO[0].

ü         鼠标的中断为EGPIO[3].

 

有关这一部分,也是必须进行相关改动的地方,主要是配置w83977的设备以及设备GPIO中断等,在这相关设备的初始化函数当中调用,主要包括如下几个简单的函数:

 

此处须要修改的地方如下:

 

[1]. pckbd_init_hw函数当中加入如下键盘GPIO中断设置函数:

// houhh 20070809, init the keyboard gpio int...

w83697uart_set_gpio(GPIO_INT_A, GPIO_KEYB_BITPOS);

 

[2]. psaux_init函数的mouse中断设备及w83977内部中部设备:

// houhh 20070809...

w83697uart_set_gpio(GPIO_INT_A, GPIO_MOUSE_BITPOS);

w83977mouse_set();

 

 

2.       键盘设备的读写, 其读写具体定义如下,与具体的平台相台,在w883977当中键盘的I/O定义如下:

         #define EDB9302_VIRT_8042                IO_BASE83977_VIRT

         #define kbd_read_input()                         __raw_readb(EDB9302_VIRT_8042 + 0x60)

         #define kbd_read_status()                       __raw_readb(EDB9302_VIRT_8042 + 0x64)

         #define kbd_write_output(val)                __raw_writeb(val, EDB9302_VIRT_8042 + 0x60)

         #define kbd_write_command(val)           __raw_writeb(val, EDB9302_VIRT_8042 + 0x64)

         #define kbd_request_irq(handler)            request_irq(IRQ_GPIO, handler, SA_SHIRQ, \

                                                                     "keyboard", "kbd"); \

                                                                           edb9302_enable_eint1(1);    

此处须要修改的地方如下:

 

         [1]. 键盘与鼠标数据读写均建立在此四个宏之基础上,其实是两个宏, 这其实是本驱动中修改最多的部分了J,构成与底层硬件通信的最基础.

       [2]. edb9302_enable_eint1(1) 是打开设备相应的GPIO中断位使能, 也是必须修改成与自己设备相关的部分.

 

3.       中断处理

 

须要修改的地方基本已经在前面的1/2点中指出来了, 一共是四处修改,此时即已经完成了驱动,下面具体了解一下有关中断的处理:

 

keyboard_interrupt()是中断处理程序,虽然他的名字叫做keyboard什么的,但是其实它是键盘与鼠标两个中断的入口,因为他们俩共用一个系统中断向量. 叫断到来时根据不同的GPIO位来判断应该处理什么中断,但实际上处理时是根据读写具体设备的相关状态是判断处理的,具体如下处理在handle_kbd_event()函数当中:

 

static unsigned char handle_kbd_event(void)

{

                   {

                            if (status & KBD_STAT_MOUSE_OBF)

                                     handle_mouse_event(scancode);

                            else

                                     handle_keyboard_event(scancode);

                   }

                   status = kbd_read_status();

         }

         if (!work)

                   printk(KERN_ERR "pc_keyb: controller jammed (0x%02X).\n", status);

         return status;

}

 

根据硬件寄存器读取的状态,进行区别是应该处理什么中断, 具体的硬件寄存器的状态可以参见手册,这里因为没作任何修改,因此不进行说明了, 至于具体的中断内部的处理流程参看源码.


二.  PS2鼠标驱动的软件层次.

 

1. 键盘设备初始化部分.

 

void __init pckbd_init_hw(void)

 

这个初始化的调用者有必要指出来,请看keyboard.h文件中有如下定义:

 

#define kbd_setkeycode                 pckbd_setkeycode

#define kbd_getkeycode                 pckbd_getkeycode

#define kbd_translate                      pckbd_translate

#define kbd_unexpected_up           pckbd_unexpected_up

#define kbd_leds                             pckbd_leds

#define kbd_init_hw                       pckbd_init_hw

#define kbd_sysrq_xlate                 pckbd_sysrq_xlate

 

kbd_init_hw调用者是上一层的键盘共用部分代码kbd_init, 该函数位于 keyboard.c文件, 调用层次关系如下

         chr_dev_init()àtty_init()àkdb_init()àkbd_init_hw();

 

__initcall(chr_dev_init); __initcall机制,在系统初始化完成后调用.

 

在此函数的调用中,完成键盘硬件的初始化以及检测配置等,最后就是安装键盘中断处理程序,键盘中断使能.

 

另外如果已经打开了鼠标支持选项, 则还会调用mouse初始化程序.

#if defined CONFIG_W83977_PSMOUSE

         psaux_init();

#endif

 

2. 鼠标设备初始化部分.

 

上面已经提到了鼠标设备如果打开了,则是在键盘设备当中附带初始化的,鼠标设备与键盘初始化时有个不同的地方,那就是要建立一个设备节点如下:

 

static struct miscdevice psaux_mouse = {

       PSMOUSE_MINOR, "psaux", &psaux_fops

};

       if ((retval = misc_register(&psaux_mouse)))

              return retval;

 

MISC类型为杂类设备,其主设备号为10, 因此这个ps2 mouse的设备节点为/dev/psaux, 主设备号为10, 次设备号为1; 这里应该讲讲为何键盘设备没有设备节点了,这也是开始时我比较困惑的问题,但经实际源码的分析,得知键盘设备其实就是针对当前终端的,在一开始初始化时就已经完成硬件设备初始化,也挂上了中断服务程序并打开了中断,而且在键盘输入时,直接就是针对当前的TTY设备, 因此键盘设备并不须要一个设备节点来打开它,如果你要读取键盘设备的数据,其实就是读取当前终端即可,这一点要弄清楚.

 

既然有moues设备节点,那就有打开设备以及打开设备号的相应文件操作,具体在如下定义,关于每一项并不做具体说明,可能见源码.

struct file_operations psaux_fops = {

       read:              read_aux,

       write:      write_aux,

       poll:        aux_poll,

       open:       open_aux,

       release:    release_aux,

       fasync:    fasync_aux,

};

 

3.  键盘数据的接收.

 

刚才在上面也讲到了键盘数据没有对应文件结构,并不是通过普通的文件读写操作来处理的,那么他具体是如何处理键盘接收的?现在进一点来看keyboard.c这个公用部分的键盘数据接收机制:

 

首先列出具体的键盘中断的处理代码,我们将分两部分来讲解以下三个问题:

 

[1]. 按下弹起这个过程是如何接收的?

[2]. 重复键盘是如何实现的?

[3]. 键盘功能灯是如何处理的?

 

static inline void handle_keyboard_event(unsigned char scancode)

{

#ifdef CONFIG_VT

         kbd_exists = 1;

         if (do_acknowledge(scancode))

                   handle_scancode(scancode, !(scancode & 0x80));

#endif                                    

         tasklet_schedule(&keyboard_tasklet);

}      

 

带着如上的三个问题,来看上述的中断处理,结论如下:

1.        handle_scancode()带这个公共部分负责完成一次按下与弹起的工作,将键盘最终通过调用put_queue(scancode);来将按键交给当前终端缓冲区, 查看这个函数的代码,我们会发觉与串口的处理相当类似,也是首先将中断中收到的数据插入一个flip缓冲区,然后再启动终端的一个tasklet来完成缓冲区数据的取走加工,这当然是异步的.

 

 

void put_queue(int ch)

{

         if (tty) {

                   tty_insert_flip_char(tty, ch, 0);

                   con_schedule_flip(tty);

         }

}

 

2.        对于后面的两个问题,其实是一并处理的,用的机制当然也是我们已经反复提到的tasklet机制,如果对这个机制还不太了解,可以参考” UART串口驱动开发文档.doc”驱动一文件中的详细关于“软中断的机制的原理介绍”, 至此我应该可以深刻的体会软中断做为一种异步机制,在内核应用广泛程度了!

 

处理键盘按键不放,以及键盘功能灯的bh函数为static void kbd_bh(unsigned long dummy), 这个函数的具体代码不详细贴出,它完成的工作如下:

 

[1]. 调用kbd_processkeycode(kbd_repeatkeycode, 0, 1), 以完成须要处理重复按键产生的情形.

[2]  处理当前终端变化的情况, 如果终端变化后才按键数据送向新的终端.

[3]. 如果键盘功能灯的状态有变,即进行响应, 如此软件即可以设置功能灯的状态了.


三.  USBHID keyboard/mouse验证.

 

这个驱证在驱动方面没有任何的代码修改,主要涉及到两个方面:

 

1.       参照DG930x-linux下面的readme.txt来打开USB模块,以及打开Input core模块,并打开键盘鼠标设备的支持,这在readme文档中已经有详细的说明了.

2.       参照minigui的配置文档MiniGUI.cfg的说明,打开输入设备的配置.

 

[1]. USB MOUSE配制:

# IAL engine

ial_engine=console

#ial_engine=dummy

mdev=/dev/input/mice

mtype=IMPS2

 

[2]. PS2 MOUSE配制:

# IAL engine

ial_engine=console

#ial_engine=dummy

mdev=/dev/psaux

mtype=IMPS2

 

3.       参照建立mouseusb设备节点名/dev/input/mice.

4.       编译minigui时,如果要移动窗体必须打开可移动窗体的配制选项, 这此配置在提交的minigui当中已经全部修改完成,可以参照配置文件buildlib-ep9302.

没有评论:

发表评论