作业帮 > 体裁作文 > 教育资讯

鼠标驱动

来源:学生作业帮助网 编辑:作业帮 时间:2024/09/23 22:39:49 体裁作文
鼠标驱动体裁作文

篇一:USB鼠标驱动详解

一个 USB 总线引出两个重要的链表,一个为 USB 设备链表,一个为 USB 驱动链表。设备链表包含各种系统中的 USB 设备以及这些设备的所有接口,驱动链表包含 USB 设备驱动程序(usb device driver)和 USB 驱动程序(usb driver)。 USB 设备驱动程序(usb device driver)和 USB 驱动程序(usb driver)的区别是什么?

USB 设备驱动程序包含 USB 设备的一些通用特性,将与所有 USB 设备相匹配。在 USB core 定义了:struct usb_device_driver usb_generic_driver。usb_generic_driver 是 USB 子系统中唯一的一个设备驱动程序对象。而 USB 驱动程序则是与接口相匹配,接口是一个完成特定功能的端点的集合。

设备是如何添加到设备链表上去的?

在设备插入 USB 控制器之后,USB core 即会将设备在系统中注册,添加到 USB 设备链表上去。

USB 设备驱动程序(usb device driver)是如何添加到驱动链表上去的?

在系统启动注册 USB core 时,USB 设备驱动程序即将被注册,也就添加到驱动链表上去了。

接口是如何添加到设备链表上去的?

在 USB 设备驱动程序和 USB 设备的匹配之后,USB core 会对设备进行配置,分析设备的结构之后会将设备所有接口都添加到设备链表上去。比如鼠标设备中有一个接口,USB core 对鼠标设备配置后,会将这个接口添加到设备链表上去。

USB 驱动程序(usb driver)是如何添加到驱动链表上去的?

在每个 USB 驱动程序的被注册时,USB 驱动程序即会添加到驱动链表上去。比如鼠标驱动程序,usb_mouse_init 函数将通过 usb_register(&usb_mouse_driver) 将鼠标驱动程序注册到 USB core 中,然后就添加到驱动链表中去了。其中 usb_mouse_driver 是描述鼠标驱动程序的结构体。

已配置状态(configured status)之后话

当鼠标的设备、接口都添加到设备链表,并且鼠标驱动程序也添加到驱动链表上去了,系统就进入一种叫做已配置(configured)的状态。要达到已配置状态,将经历复杂的过程,USB core 为 USB 设备奉献着无怨无悔。在这个过程中,系统将会建立起该设备的的设备、配置、接口、设置、端点的描述信息,它们分别被 usb_device、usb_configuration、usb_interface、usb_host_interface、 usb_host_endpoint 结构体描述。

设备达到已配置状态后,首先当然就要进行 USB 驱动程序和相应接口的配对,对于鼠标设备来说则是鼠标驱动程序和鼠标中的接口的配对。USB core 会调用 usb_device_match 函数,通过比较设备中的接口信息和 USB 驱动程序中的 id_table,来初步决定该 USB 驱动程序是不是跟相应接口相匹配。通过这一道关卡后,USB core 会认为这个设备应该由这个驱动程序负责。

然而,仅仅这一步是不够的,接着,将会调用 USB 驱动程序中的 probe 函数对相应接口进行进一步检查。如果该驱动程序确实适合设备接口,对设备做一些初始化工作,分配 urb 准备数据传输。

当鼠标设备在用户空间打开时,将提交 probe 函数构建的 urb 请求块,urb 将开始为传送数据而忙碌了。urb 请求块就像一个装东西的“袋子”,USB 驱动程序把“空袋子”提交给 USB core,然后再交给主控制器,主控制器把数据放入这个“袋子”后再将装满数据的“袋子”通过 USB core 交还给 USB 驱动程序,这样一次数据传输就完成了。

以下是完全注释后的鼠标驱动程序代码 usbmouse.c

1. /*

2. * $Id: usbmouse.c,v 1.15 2001/12/27 10:37:41 vojtech Exp $

3. *

4. * Copyright (c) 1999-2001 Vojtech Pavlik

5. *

6. * USB HIDBP Mouse support

7. */

8. #include

9. #include

10. #include

11. #include

12. #include

13. #include

14. /*

15. * Version Information

16. */

17. #define DRIVER_VERSION "v1.6"

18. #define DRIVER_AUTHOR "Vojtech Pavlik "

19. #define DRIVER_DESC "USB HID Boot Protocol mouse driver"

20. #define DRIVER_LICENSE "GPL"

21. MODULE_AUTHOR(DRIVER_AUTHOR);

22. MODULE_DESCRIPTION(DRIVER_DESC);

23. MODULE_LICENSE(DRIVER_LICENSE);

24. /*

25. * 鼠标结构体,用于描述鼠标设备。

26. */

27. struct usb_mouse

28. {

29. /* 鼠标设备的名称,包括生产厂商、产品类别、产品等信息 */

30. char name[128];

31. /* 设备节点名称 */

32. char phys[64];

33. /* USB 鼠标是一种 USB 设备,需要内嵌一个 USB 设备结构体来描述其 USB 属性 */

34. struct usb_device *usbdev;

35. /* USB 鼠标同时又是一种输入设备,需要内嵌一个输入设备结构体来描述其输入设备的属性 */

36. struct input_dev *dev;

37. /* URB 请求包结构体,用于传送数据 */

38. struct urb *irq;

39. /* 普通传输用的地址 */

40. signed char *data;

41. /* dma 传输用的地址 */

42. dma_addr_t data_dma;

43. };

44. /*

45. * urb 回调函数,在完成提交 urb 后,urb 回调函数将被调用。

46. * 此函数作为 usb_fill_int_urb 函数的形参,为构建的 urb 制定的回调函数。

47. */

48. static void usb_mouse_irq(struct urb *urb)

49. {

50. /*

51. * urb 中的 context 指针用于为 USB 驱动程序保存一些数据。比如在这个回调函数的形参没有传递在 probe

52. * 中为 mouse 结构体分配的那块内存的地址指针,而又需要用到那块内存区域中的数据,context 指针则帮了

53. * 大忙了!

54. * 在填充 urb 时将 context 指针指向 mouse 结构体数据区,在这又创建一个局部 mouse 指针指向在 probe

55. * 函数中为 mouse 申请的那块内存,那块内存保存着非常重要数据。

56. * 当 urb 通过 USB core 提交给 hc 之后,如果结果正常,mouse->data 指向的内存区域将保存着鼠标的按键

57. * 和移动坐标信息,系统则依靠这些信息对鼠标的行为作出反应。

58. * mouse 中内嵌的 dev 指针,指向 input_dev 所属于的内存区域。

59. */

60. struct usb_mouse *mouse = urb->context;

61. signed char *data = mouse->data;

62. struct input_dev *dev = mouse->dev;

63. int status;

64. /*

65. * status 值为 0 表示 urb 成功返回,直接跳出循环把鼠标事件报告给输入子系统。

66. * ECONNRESET 出错信息表示 urb 被 usb_unlink_urb 函数给 unlink 了,ENOENT 出错信息表示 urb 被

67. * usb_kill_urb 函数给 kill 了。usb_kill_urb 表示彻底结束 urb 的生命周期,而 usb_unlink_urb 则

68. * 是停止 urb,这个函数不等 urb 完全终止就会返回给回调函数。这在运行中断处理程序时或者等待某自旋锁

69. * 时非常有用,在这两种情况下是不能睡眠的,而等待一个 urb 完全停止很可能会出现睡眠的情况。

70. * ESHUTDOWN 这种错误表示 USB 主控制器驱动程序发生了严重的错误,或者提交完 urb 的一瞬间设备被拔出。

71. * 遇见除了以上三种错误以外的错误,将申请重传 urb。

72. */

73. switch (urb->status)

74. {

75. case 0: /* success */

76. break;

77. case -ECONNRESET: /* unlink */

78. case -ENOENT:

79. case -ESHUTDOWN:

80. return;

81. /* -EPIPE: should clear the halt */

82. default: /* error */

83. goto resubmit;

84. }

85. /*

86. * 向输入子系统汇报鼠标事件情况,以便作出反应。

87. * data 数组的第0个字节:bit 0、1、2、3、4分别代表左、右、中、SIDE、EXTRA键的按下情况;

88. * data 数组的第1个字节:表示鼠标的水平位移;

89. * data 数组的第2个字节:表示鼠标的垂直位移;

90. * data 数组的第3个字节:REL_WHEEL位移。

91. */

92. input_report_key(dev, BTN_LEFT, data[0] & 0x01);

93. input_report_key(dev, BTN_RIGHT, data[0] & 0x02);

94. input_report_key(dev, BTN_MIDDLE, data[0] & 0x04);

95. input_report_key(dev, BTN_SIDE, data[0] & 0x08);

96. input_report_key(dev, BTN_EXTRA, data[0] & 0x10);

97. input_report_rel(dev, REL_X, data[1]);

98. input_report_rel(dev, REL_Y, data[2]);

99. input_report_rel(dev, REL_WHEEL, data[3]);

100. /*

101. * 这里是用于事件同步。上面几行是一次完整的鼠标事件,包括按键信息、绝对坐标信息和滚轮信息,输入子

102. * 系统正是通过这个同步信号来在多个完整事件报告中区分每一次完整事件报告。示意如下:

103. * 按键信息 坐标位移信息 滚轮信息 EV_SYC | 按键信息 坐标位移信息 滚轮信息 EV_SYC ...

104. */

105. input_sync(dev);

106. /*

107. * 系统需要周期性不断地获取鼠标的事件信息,因此在 urb 回调函数的末尾再次提交 urb 请求块,这样又会

108. * 调用新的回调函数,周而复始。

109. * 在回调函数中提交 urb 一定只能是 GFP_ATOMIC 优先级的,因为 urb 回调函数运行于中断上下文中,在提

110. * 交 urb 过程中可能会需要申请内存、保持信号量,这些操作或许会导致 USB core 睡眠,一切导致睡眠的行

111. * 为都是不允许的。

112. */

113. resubmit:

114. status = usb_submit_urb (urb, GFP_ATOMIC);

115. if (status)

116. err ("can't resubmit intr, %s-%s/input0, status %d",

117. mouse->usbdev->bus->bus_name,

118. mouse->usbdev->devpath, status);

119. }

120. /*

121. * 打开鼠标设备时,开始提交在 probe 函数中构建的 urb,进入 urb 周期。

122. */

123. static int usb_mouse_open(struct input_dev *dev)

124. {

125. struct usb_mouse *mouse = dev->private;

126. mouse->irq->dev = mouse->usbdev;

127. if (usb_submit_urb(mouse->irq, GFP_KERNEL))

128. return -EIO;

129. return 0;

130. }

131. /*

132. * 关闭鼠标设备时,结束 urb 生命周期。

133. */

134. static void usb_mouse_close(struct input_dev *dev)

135. {

136. struct usb_mouse *mouse = dev->private;

137. usb_kill_urb(mouse->irq);

138. }

139. /*

140. * 驱动程序的探测函数

141. */

142. static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_id *id)

143. {

144. /*

145. * 接口结构体包含于设备结构体中,interface_to_usbdev 是通过接口结构体获得它的设备结构体。 146. * usb_host_interface 是用于描述接口设置的结构体,内嵌在接口结构体 usb_interface 中。

147. * usb_endpoint_descriptor 是端点描述符结构体,内嵌在端点结构体 usb_host_endpoint 中,而端点 148. * 结构体内嵌在接口设置结构体中。

149. */

150. struct usb_device *dev = interface_to_usbdev(intf);

151. struct usb_host_interface *interface;

152. struct usb_endpoint_descriptor *endpoint;

153. struct usb_mouse *mouse;

154. struct input_dev *input_dev;

155. int pipe, maxp;

156. interface = intf->cur_altsetting;

157. /* 鼠标仅有一个 interrupt 类型的 in 端点,不满足此要求的设备均报错 */

158. if (interface->desc.bNumEndpoints != 1)

159. return -ENODEV;

160. endpoint = &interface->endpoint[0].desc;

161. if (!usb_endpoint_is_int_in(endpoint))

162. return -ENODEV;

163. /*

164. * 返回对应端点能够传输的最大的数据包,鼠标的返回的最大数据包为4个字节,数据包具体内容在 urb 165. * 回调函数中有详细说明。

166. */

167. pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);

168. maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));

169. /* 为 mouse 设备结构体分配内存 */

170. mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);

171. /* input_dev */

172. input_dev = input_allocate_device();

173. if (!mouse || !input_dev)

174. goto fail1;

175. /*

176. * 申请内存空间用于数据传输,data 为指向该空间的地址,data_dma 则是这块内存空间的 dma 映射, 177. * 即这块内存空间对应的 dma 地址。在使用 dma 传输的情况下,则使用 data_dma 指向的 dma 区域, 178. * 否则使用 data 指向的普通内存区域进行传输。

篇二:C语言综合用C语言写的鼠标驱动程序

//C语言综合:用C语言写的鼠标驱动程序

#include "dos.h"

#include "bios.h"

#include "malloc.h"

#include "stdlib.h"

#include "stdio.h"

#include "graphics.h""

#define R 15

/*鼠标的形态*/

void initgr(void) // BGI初始化

{

int gd = DETECT, gm = 0; // 和gd = VGA,gm = VGAHI是同样效果

registerbgidriver(EGAVGA_driver); // 注册BGI驱动后可以不需要.BGI文件的支持运行

initgraph(&gd, &gm, """");

}

/*获取鼠标位置和按键,key=1是左键,key=2是右键*/

void getmouse(int *x,int *y,int *key)

{

union REGS inregs,outregs;

inregs.x.ax=3; //获取鼠标位置和状态也可以用3

int86(0x33,&inregs,&outregs); //中断调用

*x=outregs.x.cx; //cx寄存器中存的是横坐标 *y=outregs.x.dx; //dx寄存器中存的是列坐标 *key=outregs.x.bx; //bx寄存器是按键状态 }

void visbilemouse()

{

union REGS inregs,outregs;

inregs.x.ax=0x01; /*显示鼠标*/

int86(0x33,&inregs,&outregs);

}

/*按键后,返回当前鼠标的x,y和按键状态,知道按键后才返回*/

void mouse(int *x,int *y,int *z) //画鼠标是利用将一个空矩形存入内存中,然后再在该空矩形中画鼠标形状

{

int a=0,b=0,c=0,a_old=0,b_old=0; //a,b的值可以随便

int *ball; //定义指向内存的存储图形的指针

ball=malloc(imagesize(a,b,a+R,b+R)); //返回矩形的大小

getimage(a,b,a+R,b+R,ball); //第一次将图形(一个空矩形)存入到内存中,内存中存入的是一个空矩形

/*setcolor(GREEN);

setlinestyle(0,0,1);

line(a,b,a+R,b+R/2);

line(a,b,a+R/2,b+R);

line(a+R,b+R/2,a+R/2,b+R);

line(a+R*3/4,b+R*3/4,a+R,b+R); 同样可要可不要,画鼠标*/

while(c==0) //直到按键后才返回,不按键一直循环

{

getmouse(&a,&b,&c); //a,为横坐标,b,为列坐标,c为按键状态

if(a<0) a=0; //保证鼠标左边不出界 if(b<0) b=0; //保证上面不出界 if(a>getmaxx()-R) a=getmaxx()-R; //保证右边不出界 if(b>getmaxy()-R) b=getmaxy()-R; //保证下边不出界 if(a!=a_old || b!=b_old) //当鼠标移动时 {

putimage(a_old,b_old,ball,0); //在a_old,b_old输出图形,用来擦掉原来的鼠标

getimage(a,b,a+R,b+R,ball); //这条语句是将目前鼠标所在位置的图形存贮到ball里面试着看着有无这条语句的区别

/*不需要在获取了*/

setcolor(GREEN);

setlinestyle(0,0,1);

line(a,b,a+R,b+R/2);

line(a,b,a+R/2,b+R);

line(a+R,b+R/2,a+R/2,b+R);

line(a+R*3/4,b+R*3/4,a+R,b+R); //画鼠标 }

a_old=a;b_old=b;

} /*while()结束*/

*x=a;*y=b;*z=c; //返回按键后鼠标的位置

putimage(a,b,ball,0); //将鼠标擦掉,因为存在ball里面的是一个背景为缺省状态下的一个空矩形

free(ball);

}

/*主要思想是通过getimage,putimage,imagesize图形的存贮一个当前的图形来

擦拭掉以前的鼠标图形,也可以用清屏部分屏幕来实现*/ void main()

{

int x,y,z;

initgr();

visbilemouse();

do

{

setcolor(WHITE);

rectangle(175,8,220,25);

outtextxy(180,10,""exit"");

mouse(&x,&y,&z);

if(z==1)

{

setlinestyle(0,0,1);

setcolor(12);

circle(x,y,1);

}

if(z==2)

{

setfillstyle(1,BLACK);

bar(0,0,getmaxx(),getmaxy()); }

鼠标驱动

}while(x<175 || x>220 || y<8 || y>25); closegraph();

}

篇三:无线鼠标键盘未能成功安装设备驱动程序而无反应解决方法

无线鼠标键盘未能成功安装设备驱动程序而无反应解决方法 成功的解决办法:(1)从目录C:\Windows\System32\DriverStore\FileRepository\usb.inf_amd64_neutral_*********'中复制文件,usb.inf和usb.PNF,粘贴到C:\Windows\inf目录中;(2)在设备管理器-未知设备中找到UsbReceiver,右键菜单选择更新驱动程序;(3)在对话框中选择浏览计算机以查找驱动程序软件(4)浏览位置输入C:\Windows\inf安装完毕后,UsbReceiver将分别变成以下设备:(1)GenericUSBHub(2)HID-compliantdevice

篇四:当U盘、读卡器、或鼠标等移动设备插入电脑的usb接口后,遇到未成功安装驱动程序时,该怎么办

当U盘、读卡器、或鼠标等移动设备插入电脑的usb接口后,遇到未成功安装驱动程序

时,该怎么办

贵州省仁怀市喜头镇共和小学 周万权

当你用U盘或读卡器等移动设备在电脑中(指该台电脑第一次插入)拷贝文件时你会发现:首先听到电脑提示音,紧接着在电脑的右下角工具栏里会出现“正在安装驱动程序,请稍等??”这样的提示,此时,电脑正在为你的设备安装驱动程序,等待数十秒钟后,一般情况下,即可成功安装驱动程序。安装完后,就可以把电脑中你所需要的文件拷贝在你的移动设备上了。无独有偶,当U盘、读卡器、或鼠标等移动磁盘插入电脑的usb接口后,听到提示音,等待数十秒后,却显示“未成功安装驱动程序”时,怎么办?

笔者经验发现:当遇到未成功安装驱动程序时,系统不会自动重新安装,你的U盘或读卡器等移动设备在该usb接口暂时不能使用了。要么换别的usb接口,换接口后系统当然会重新自动安装,安装成功后能正常使用,在别的usb接口同样能正常使用。唯独在起先未成功安装的那个usb接口仍然不能读出你的这个移动设备。怎么办?只有靠你手动安装驱动程序了。那怎么手动安装驱动程序呢?打开“控制面板”,→点击“设备和打印机”。这时,将你的移动设备插入未成功安装的那个usb接口,在“打印机和传真”下面的“设备”中,你会看到你插入的移动设备图标。再右击显示的这个图标,→点击“属性”,→再点击“属性”中的“硬件”。在“设备功能”“名称”中,选中你

的那个移动设备,→再点击“属性”。依次点击→“驱动程序”、→“更新驱动程序”、→选择“浏览计算机以查找驱动程序软件(手动查找并安装驱动程序软件)”、→点击“下一步”页面显示“已安装适合设备的最佳驱动程序软件”。最后关闭窗口,大功告成。你的移动设备在该usb接口便可使用了。

2014年6月5日

篇五:键盘鼠标驱动

Linux操作系统课程报告

串行端口程序设计

姓 名:连炳发

班 级:集成电路设计与集成系统

学 号:1015251030

指导教师:李国刚

一、实验目的

了解 PS/2 键盘鼠标协议和接口;

了解模块驱动方法,掌握交叉编译的概念及方法,学习鼠标驱动的方法。

二、实验内容

加载 PS/2 驱动模块,观察键盘鼠标的接管情况。

三、预备知识

C 语言的基础知识。

Linux 的基本操作。

了模块驱动的基本命令。

四、实验设备及工具

硬件:UP-NETARM2410-S 嵌入式实验仪,PC 机Pentumn500 以上, 硬盘40G 以上,内存大于 128M。

软件:PC 机操作系统REDHAT LINUX 9.0 +MINICOM + AMRLINUX 开发环境

五、实验原理

1、PS/2 协议

PS/2 设备接口广泛用于现代的鼠标和键盘,它由IBM 开发,现在是大多数键盘、鼠标与P C机通讯的标准协议。物理上的PS/2 接口是两类连接器的一种:5 脚的DIN 或6 脚的mini -DIN。这两种连接器(在电气特性上)是十分类似的,实际上两者只有一点不同那就是管 脚的排列。这就意味着这两种连接器可以很容易用一种简单的硬件连线的适配器来转换。 DIN 标准是由德国标准化组织建立的。PC 键盘可以有6 脚的mini-DIN 或5 脚的DIN 连接器。

具有6 脚的mini-DIN 的键盘通常叫做“PS/2”键盘,而那些有5 脚的DIN 的叫做“AT”设 备(“XT”键盘也是用5 脚DIN,但它们非常古老并且多年前就不再生产了)。所以现代的 为PC 生产的键盘不是PS/2,AT 就是USB 的。鼠标流行着大量的形状和大小、接口,最流 行的类型可能算是PS/2 鼠标,现在USB 鼠标渐渐开始流行起来了。

每种连接器的引脚定义如图5.4.1 所示:在刚才提到的连接器上有四个有趣的管脚:地、 +5V、数据和时钟。主机提供+5V,并且键盘/鼠标的地连接到主机的地上。数据和时钟都 是集电极开路的,这就意味着它们通常保持高电平而且很容易下拉到地(逻辑0)。任何你 连接到PS/2 鼠标、键盘或主机的设备在时钟和数据线上要有一个大的上拉电阻。把线拉低

就置“0”,让线上浮成高电平就置“1”。参考图5.4.2 中数据和时钟线的一般接口结构。 PS/2 鼠标和键盘履行一种双向同步通信协议。换句话说,每次数据线上发送一位数据并且 没在时钟线上发一个脉冲就被读入。键盘/鼠标可以发送数据到主机,而主机也可以发送数 据到设备,但主机总是在总线上有优先权,它可以在任何时候抑制来自键盘/鼠标的通信, 只要把时钟位拉低即可。

图5.4.1 连接器的引脚定义

图5.4.2 数据和时钟线的一般接口结构

2、模块

模块(module)是在内核空间运行的程序,实际上是一种目标对象文件,没有链接,不能 独立运行,但是可以装载到系统中作为内核的一部分运行,从而可以动态扩充内核的功能。 模块最主要的用处就是用来实现设备驱动程序。

Linux 下对于一个硬件的驱动,可以有两种方式:直接加载到内核代码中,启动内核时就 会驱动此硬件设备。另一种就是以模块方式,编译生成一个.o 文件。当应用程序需要时再 加载进内核空间运行。所以我们所说的一个硬件的驱动程序,通常指的就是一个驱动模块。 对于一个设备,它可以在/dev 下面存在一个对应的逻辑设备节点,这个节点以文件的形式 存在,但它不是普通意义上的文件,它是设备文件,更确切的说,它是设备节点。这个节 点是通过mknod 命令建立的,其中指定了主设备号和次设备号。主设备号表明了某一类设 备,一般对应着确定的驱动程序;次设备号一般是区分不同属性,例如不同的使用方法, 不同的位置,不同的操作。这个设备号是从/proc/devices 文件中获得的,所以一般是先 有驱动程序在内核中,才有设备节点在目录中。这个设备号(特指主设备号)的主要作用, 就是声明设备所使用的驱动程序。驱动程序和设备号是一一对应的,当你打开一个设备文 件时,操作系统就已经知道这个设备所对应的驱动程序。

对于一个硬件,Linux 是这样来进行驱动的:首先,我们必须提供一个.o 的驱动模块文件 (这里我们只说明模块方式,其实内核方式是类似的)。我们要使用这个驱动程序,首先要 加载运行它(insmod *.o)。这样驱动就会根据自己的类型(字符设备类型或块设备类型, 例如鼠标就是字符设备而硬盘就是块设备)向系统注册,注册成功系统会反馈一个主设备 号,这个主设备号就是系统对它的唯一标识(例如硬盘块设备在/proc/devices 中显示的 主设备号为3 ,我们用ls -l /dev/had 看到的主设备就肯定是3)。驱动就是根据此主设 备号来创建一个一般放置在/dev 目录下的设备文件(mknod 命令用来创建它,它必须用主 设备号这个参数)。在我们要访问此硬件时,就可以对设备文件通过open、read、write 等命令进行。而驱动就会接收到相应的read、write 操作而根据自己的模块中的相应函数 进行了。

3、Linux 键盘驱动的工作原理

首先让我们通过以下的结构图5.4.3 来了解一下用户从终端的击键是如何工作的: _____________ _________ _________

/ \ put_queue | |receive_buf| |tty_read

/handle_scancode \-------->|tty_queue|---------->|tty_ldisc|------->

\ / | | |buffer |

\_____________/ |_________| |_________|

_________ ____________

| |sys_read| |

--->|/dev/ttyX|------->|user process|

| | | |

|_________| |____________|

图5.4.3 键盘结构图

首先,当你输入一个键盘值的时候,键盘将会发送相应的scancodes 给键盘驱动。一个独

立的击键可以产生一个六个scancodes 的队列。

键盘驱动中的handle_scancode()函数解析scancodes 流并通过kdb_translate()函数里的 转换表(translation-table)将击键事件和键的释放事件(key release events)转换成 连续的keycode。

比如,'a'的keycode 是30。击键’a'的时候便会产生keycode 30。释放a 键的时候会产 生keycode 158(128+30)。

然后,这些keycode 通过对keymap 的查询被转换成相应key 符号。这步是一个相当 复杂的过程。

以上操作之后,获得的字符被送入raw tty 队列--tty_flip_buffer。

receive_buf()函数周期性的从tty_flip_buffer 中获得字符,然后把这些字符送入tty read 队列。

当用户进程需要得到用户的输入的时候,它会在进程的标准输入(stdin)调用read()函 数。sys_read()函数调用定义在相应的tty 设备(如/dev/tty0)的file_operations 结构 中指向tty_read 的read()函数来读取字符并且返回给用户进程。

file_operations 是文件操作结构,定义了文件操作行为的成员,结构如下,很容易理解: struct file_operations {

struct module *owner;

loff_t (*llseek) (struct file *, loff_t, int);

ssize_t (*read) (struct file *, char *, size_t, loff_t *);<----这是本文提到的read 函数

ssize_t (*write) (struct file *, const char *, size_t, loff_t *);>---这是本文提到的write 函数 int (*readdir) (struct file *, void *, filldir_t);

unsigned int (*poll) (struct file *, struct poll_table_struct *);

int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);

int (*mmap) (struct file *, struct vm_area_struct *);

int (*open) (struct inode *, struct file *);

int (*flush) (struct file *);

int (*release) (struct inode *, struct file *);

int (*fsync) (struct file *, struct dentry *, int datasync);

int (*fasync) (int, struct file *, int);

int (*lock) (struct file *, int, struct file_lock *);

ssize_t (*readv) (struct file *, const struct iovec *, unsigned long, loff_t *);

ssize_t (*writev) (struct file *, const struct iovec *, unsigned long, loff_t *);

ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);

unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);

};

我们知道unix 系统中设备也是文件,所以tty 设备我们也可以进行文件操作。

键盘驱动器可以有如下4 种模式:

- scancode(RAW 模式):应用程序取得输入的scancode。这种模式通常用于应用程序实现 自己的键盘驱动器,比如X11 程序。

- keycode(MEDIUMRAW 模式):应用程序取得key 的击键和释放行为(通过keycode 来鉴 别这两种行为)信息。

- ASCII(XLATE 模式):应用程序取得keymap 定义的字符,该字符是8 位编码的。

- Unicode(UNICODE 模式):此模式唯一和ASCII 模式不同之处就是UNICODE 模式允许用 户将自己的10 进制值编写成UTF8 的unicode 字符,如十进制的数可以编写成Ascii_0 到 Ascii_9,或者用户16 进制的值可以用Hex_0 到Hex_9 来代表。一个keymap 可以产生出一 系列UTF8 的序列。

六、程序分析

分析源代码 Ps2_kbd_mouse.c

使用者在键盘上按一个键,就产生一个中断请求,CPU 在响应中断时进入键盘的中断服务程 序keyboard_interrupt()。人们往往以为键盘是很简单的设备,但实际上键盘的结构和操 作都不简单。我们以字符”A”为例来说明键盘的操作。当在键盘上按下一个键时,键盘立 即就向母板发出一个字节的代码,称为”键盘扫描码” 。具体位置取决于键的位置,”A” 键的键盘扫描码为0x1c。母板上的键盘接口在接收这个字节以后就把它转换成一种”系统 扫描码”,并将其存入控制器的内部缓冲区,然后便向CPU 发出一个中断请求。对于”A”键, 其系统扫描码为0x1e。当CPU 从键盘接口的数据寄存器读时,读出的就是系统扫描码。然 后,当放开”A”时,键盘又要向母板发出键盘扫描码,而这次是两个字节,第一个是0xf0, 表示是放开一个键;然后是0x1c,表示是哪一个键.同样母板上的键盘接口也要把它转换成 系统扫描码、也要向CPU 发出中断请求,但是系统扫描码仍然是一个字节,只是在0x1e 的基 础上把最高位设成1,变成0x9e。这样,在CPU 从键盘接口读出的单字节系统扫描码中,其最 高位表示按下或放开,而低7 位则与具体的键对应。不光对普通的字符键是如此,对功能键 和控制键也是一样。例如,左右两个Shift 键的系统扫描码分别为0x2a 和0x36。这样,以 输入一个小写的”a”为例,CPU 实际上可能要发生4 次中断、要依次从键盘接口读出4 个 字节的系统扫描码,例如0x36,0x1e,0x9e,0xb6。对于把这个序列解释成什么,那就是软件 的事了;如果解释成ASCII 码,而键盘又没有锁定在大写状态,那就是”a” 。由此可见,键 盘接口向CPU 发出中断请求并不意味着从键盘接收到了一个字符,而只是意味着键盘上发 生了某种事件。此外,上面只是一般而言,实际上还有不少例外。其中最重要的是所谓”扩 充键” 。早期的PC 键盘上只有83 个键,后来扩充到了101 键或104 键,例如右边的Ctrl 键就是一个扩充键。当按下或放开扩充键时,键盘扫描码和系统扫描码都以一个0xe0 字节 开头,所以按下右边Ctrl 键时的键盘扫描码是[0xe0,0x14];放开时为[0xe0,0xf0,0x14]; 相应的系统扫描码则为[0xe0,0x1d]以及[0xe0,0x9d] 。

事件 键盘扫描码 系统扫描码

按下”A”键 0x1c 0x1e

放开”A”键 0xf0,0x1c 0x9e

按下右Shift 键 0x59 0x36

放开右Shift 键 0xf0,0x59 0xb6

按下右Ctrl 键 0xe0,0x14 0xe0,0x1d

体裁作文