Linux下USB 输入子系统的学习(以鼠标驱动为例)
usb 1-1.2: new low speed USB device using musb_hdrc and address 3
usb 1-1.2: Product: USB Optical Mouse
input: USB HID v1.11 Mouse [USB Optical Mouse] on usb-musb_hdrc-1.2
//drivers/usb/input/hid-core.c
>hid_probe()
{
printk(": USB HID v%x.%02x %s [%s] on %s\n",
hid->version >> 8, hid->version & 0xff, c, hid->name, path);
}
//drivers/input/mouse/sermouse.c---->sermouse_connect()
printk(KERN_INFO "input: %s on %s\n", sermouse_protocols[sermouse->type], serio->phys);
/proc/bus/input # cat devices
I: Bus=0003 Vendor=0461 Product=4d15 Version=0200
N: Name="USB Optical Mouse"
P: Phys=usb-musb_hdrc-1.1/input0
H: Handlers=mouse1
B: EV=f
B: KEY=70000 0 0 0 0 0 0 0 0
B: REL=103
B: ABS=100 0
驱动层负责和底层的硬件设备打交道,
将底层硬件对用户输入的响应转换为标准的输入事件以后再向上发送给Input Core。
驱动层通过调用Input_register_device函数和
Input_unregister_device函数来向输入子系统中注册和注销输入设备---
/drivers/us/input/usbmouse.c
usb_mouse_probe->
input_register_device(&mouse->dev)>
usb_mouse_disconnect>
input_unregister_device(&mouse->dev);
usb_mouse_irq()>
input_report_key>
input_event>//drivers/input/input.c
input_sync>//每次终端数据发送完成后都会跟随发送一个同步事件,便与鼠标、按键数据及时更新到响应的全局变量
//输入子系统层的操作--drivers/input/input.c
open /dev/input/mouse0>
mousedev_open()> //drivers/input/mousedev.c /*打开鼠标类输入设备*/
input_open_device()>
input_handle->dev->open>
//驱动层物理设备的操作---drivers/usb/input/usbmouse.c
usb_mouse_open()
注册Input Handler
驱动层只是把输入设备注册到输入子系统中,在驱动层的代码中本身并不创建设备结点。
应用程序用来与设备打交道的设备结点的创建由Event Handler层调用Input core中的函数来实现。
而在创建具体的设备节点之前,Event Handler层需要先注册一类设备的输入事件处理函数及相关接口
static int __init mousedev_init(void){//注册输入事件处理、连接、断开、设备(文件)操作函数 input_register_handler(&mousedev_handler);//创建输入设备节点devfs_mk_cdev(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX), S_IFCHR|S_IRUGO|S_IWUSR, "input/mice");}
static struct input_handler mousedev_handler = {
.event = mousedev_event,//REL(相对坐标)、按键、ABS(绝对坐标)、SYNC等事件处理
.connect = mousedev_connect,
.disconnect = mousedev_disconnect,
.fops = &mousedev_fops,//鼠标打开、poll、读写等操作
.minor = MOUSEDEV_MINOR_BASE,
.name = "mousedev",
.id_table = mousedev_ids,
};
下面集中分析mousedev_event事件处理:分析数据事件结构体linux/input.h中有定义,这个文件还定义了标准按键的编码等struct input_event { struct timeval time; //按键时间 __u16 type; //类型,在下面有定义 __u16 code; //要模拟成什么按键 __s32 value;//是按下还是释放};
code:
事
件的代码.如果事件的类型代码是EV_KEY,该代码code为设备键盘代码.代码植0~127为键盘上的按键代码,0x110~0x116
为鼠标上按键代码,其中0x110(BTN_ LEFT)为鼠标左键,0x111(BTN_RIGHT)为鼠标右键,0x112(BTN_
MIDDLE)为鼠标中键.其它代码含义请参看include/linux/input.h文件. 如果事件的类型代码是EV_REL,code值表示轨迹的类型.如指示鼠标的X轴方向REL_X(代码为0x00),指示鼠标的Y轴方向REL_Y(代码为0x01),指示鼠标中轮子方向REL_WHEEL(代码为0x08).
type:
EV_SYN,同步事件
EV_KEY,键盘
EV_REL,相对坐标
EV_ABS,绝对坐标
value:
事件的值.如果事件的类型代码是EV_KEY,当按键按下时值为1,松开时值为0;如果事件的类型代码是EV_ REL,value的正数值和负数值分别代表两个不同方向的值.
mousedev_event()
{
//以相对坐标移动事件为例:
case EV_REL:
mousedev_rel_event(mousedev, code, value);
break;
}
mousedev_rel_event(struct mousedev *mousedev, unsigned int code, int value)
{
//code区分X Y 轴、滚轮移动
//value代表正负移动量
switch (code) {
case REL_X: mousedev->packet.dx += value; break;
case REL_Y: mousedev->packet.dy -= value; break;
case REL_WHEEL: mousedev->packet.dz -= value; break;
}
}
分析mousedev中的packet:struct mousedev_hw_data { int dx, dy, dz; int x, y; int abs_event; unsigned long buttons;};
struct mousedev_hw_data packet;
更新mousedev中的packet数据,然后触摸屏输入、鼠标输入分别使用touch、EV_SYN方式调用mousedev_notify_readers()将其更新到mousedev_list中,这就是为什么每次按键、移动事件都跟随一个同步事件的原因
//屏幕分辨率的设置:
#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X
#define CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024
#endif
#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y
#define CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768
#endif
其他参考链接:http://blog.csdn.net/wind20/archive/2008/04/25/2327510.aspx
http://blog.csdn.net/chenzhixin/archive/2008/03/12/2173530.aspx
测试应用程序代码:
#include
#include
#include
#include
#include
#define USB_MOUSE ("/dev/input/mouse0")
struct pollfd mypoll;
int main(int argc, char *argv[])
{
int mouseFd;
struct input_event buff;
if ((mouseFd = open(USB_MOUSE, O_RDONLY)) == -1) {
printf("Failed to open /dev/input/mouse0\n");
return -1;
}
mypoll.fd = mouseFd;
mypoll.events = POLLIN;
while(1)
{
if(poll( &mypoll, 1, 10) > 0)
{
unsigned char data[4] ={0};
/*
data的数据格式:
data0:00xx 1xxx ----低三位是按键值---左中右分别为01 02 04, 第4/5位分别代表x、y移动方向,右上方x/y>0,左下方xy<0
data1:取值范围-127~127,代表x轴移动偏移量
data2:取值范围-127~127,代表y轴移动偏移量
*/
usleep(50000);
read(mouseFd, data, 4); //MOUSEDEV_EMUL_PS2方式每次采样数据为3个字节,多读不会出错,只返回成功读取的数据数
printf("mouse data=%02x%02x%02x%02x\n", data[0],data[1], data[2], data[3]);
}
}
close(mouseFd);
return 0;
}
usb 1-1.2: new low speed USB device using musb_hdrc and address 3
usb 1-1.2: Product: USB Optical Mouse
input: USB HID v1.11 Mouse [USB Optical Mouse] on usb-musb_hdrc-1.2
//drivers/usb/input/hid-core.c
>hid_probe()
{
printk(": USB HID v%x.%02x %s [%s] on %s\n",
hid->version >> 8, hid->version & 0xff, c, hid->name, path);
}
//drivers/input/mouse/sermouse.c---->sermouse_connect()
printk(KERN_INFO "input: %s on %s\n", sermouse_protocols[sermouse->type], serio->phys);
/proc/bus/input # cat devices
I: Bus=0003 Vendor=0461 Product=4d15 Version=0200
N: Name="USB Optical Mouse"
P: Phys=usb-musb_hdrc-1.1/input0
H: Handlers=mouse1
B: EV=f
B: KEY=70000 0 0 0 0 0 0 0 0
B: REL=103
B: ABS=100 0
驱动层负责和底层的硬件设备打交道,
将底层硬件对用户输入的响应转换为标准的输入事件以后再向上发送给Input Core。
驱动层通过调用Input_register_device函数和
Input_unregister_device函数来向输入子系统中注册和注销输入设备---
/drivers/us/input/usbmouse.c
usb_mouse_probe->
input_register_device(&mouse->dev)>
usb_mouse_disconnect>
input_unregister_device(&mouse->dev);
usb_mouse_irq()>
input_report_key>
input_event>//drivers/input/input.c
input_sync>//每次终端数据发送完成后都会跟随发送一个同步事件,便与鼠标、按键数据及时更新到响应的全局变量
//输入子系统层的操作--drivers/input/input.c
open /dev/input/mouse0>
mousedev_open()> //drivers/input/mousedev.c /*打开鼠标类输入设备*/
input_open_device()>
input_handle->dev->open>
//驱动层物理设备的操作---drivers/usb/input/usbmouse.c
usb_mouse_open()
注册Input Handler
驱动层只是把输入设备注册到输入子系统中,在驱动层的代码中本身并不创建设备结点。
应用程序用来与设备打交道的设备结点的创建由Event Handler层调用Input core中的函数来实现。
而在创建具体的设备节点之前,Event Handler层需要先注册一类设备的输入事件处理函数及相关接口
static int __init mousedev_init(void){//注册输入事件处理、连接、断开、设备(文件)操作函数 input_register_handler(&mousedev_handler);//创建输入设备节点devfs_mk_cdev(MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX), S_IFCHR|S_IRUGO|S_IWUSR, "input/mice");}
static struct input_handler mousedev_handler = {
.event = mousedev_event,//REL(相对坐标)、按键、ABS(绝对坐标)、SYNC等事件处理
.connect = mousedev_connect,
.disconnect = mousedev_disconnect,
.fops = &mousedev_fops,//鼠标打开、poll、读写等操作
.minor = MOUSEDEV_MINOR_BASE,
.name = "mousedev",
.id_table = mousedev_ids,
};
下面集中分析mousedev_event事件处理:分析数据事件结构体linux/input.h中有定义,这个文件还定义了标准按键的编码等struct input_event { struct timeval time; //按键时间 __u16 type; //类型,在下面有定义 __u16 code; //要模拟成什么按键 __s32 value;//是按下还是释放};
code:
事
件的代码.如果事件的类型代码是EV_KEY,该代码code为设备键盘代码.代码植0~127为键盘上的按键代码,0x110~0x116
为鼠标上按键代码,其中0x110(BTN_ LEFT)为鼠标左键,0x111(BTN_RIGHT)为鼠标右键,0x112(BTN_
MIDDLE)为鼠标中键.其它代码含义请参看include/linux/input.h文件. 如果事件的类型代码是EV_REL,code值表示轨迹的类型.如指示鼠标的X轴方向REL_X(代码为0x00),指示鼠标的Y轴方向REL_Y(代码为0x01),指示鼠标中轮子方向REL_WHEEL(代码为0x08).
type:
EV_SYN,同步事件
EV_KEY,键盘
EV_REL,相对坐标
EV_ABS,绝对坐标
value:
事件的值.如果事件的类型代码是EV_KEY,当按键按下时值为1,松开时值为0;如果事件的类型代码是EV_ REL,value的正数值和负数值分别代表两个不同方向的值.
mousedev_event()
{
//以相对坐标移动事件为例:
case EV_REL:
mousedev_rel_event(mousedev, code, value);
break;
}
mousedev_rel_event(struct mousedev *mousedev, unsigned int code, int value)
{
//code区分X Y 轴、滚轮移动
//value代表正负移动量
switch (code) {
case REL_X: mousedev->packet.dx += value; break;
case REL_Y: mousedev->packet.dy -= value; break;
case REL_WHEEL: mousedev->packet.dz -= value; break;
}
}
分析mousedev中的packet:struct mousedev_hw_data { int dx, dy, dz; int x, y; int abs_event; unsigned long buttons;};
struct mousedev_hw_data packet;
更新mousedev中的packet数据,然后触摸屏输入、鼠标输入分别使用touch、EV_SYN方式调用mousedev_notify_readers()将其更新到mousedev_list中,这就是为什么每次按键、移动事件都跟随一个同步事件的原因
//屏幕分辨率的设置:
#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X
#define CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024
#endif
#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y
#define CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768
#endif
其他参考链接:http://blog.csdn.net/wind20/archive/2008/04/25/2327510.aspx
http://blog.csdn.net/chenzhixin/archive/2008/03/12/2173530.aspx
测试应用程序代码:
#include
#include
#include
#include
#include
#define USB_MOUSE ("/dev/input/mouse0")
struct pollfd mypoll;
int main(int argc, char *argv[])
{
int mouseFd;
struct input_event buff;
if ((mouseFd = open(USB_MOUSE, O_RDONLY)) == -1) {
printf("Failed to open /dev/input/mouse0\n");
return -1;
}
mypoll.fd = mouseFd;
mypoll.events = POLLIN;
while(1)
{
if(poll( &mypoll, 1, 10) > 0)
{
unsigned char data[4] ={0};
/*
data的数据格式:
data0:00xx 1xxx ----低三位是按键值---左中右分别为01 02 04, 第4/5位分别代表x、y移动方向,右上方x/y>0,左下方xy<0
data1:取值范围-127~127,代表x轴移动偏移量
data2:取值范围-127~127,代表y轴移动偏移量
*/
usleep(50000);
read(mouseFd, data, 4); //MOUSEDEV_EMUL_PS2方式每次采样数据为3个字节,多读不会出错,只返回成功读取的数据数
printf("mouse data=%02x%02x%02x%02x\n", data[0],data[1], data[2], data[3]);
}
}
close(mouseFd);
return 0;
}