Android Kernel 가상 마우스 드라이버 구현하기

개요

가상 마우스 개요

가상 마우스 동작 방식은 다음과 같다.

App이 마우스 동작의 좌표 정보를 sysfs노드인 /sys/devices/platform/virmouse/vmevent를 거쳐 가상 마우스 드라이버(virmouse.c)에 통지한다.

가상 마우스 드라이버는 evdev를 거쳐 이동 정보를 사용자 영역으로 보낸다.

evdev란 범용 입력 이벤트 드라이버를 뜻한다.

evdev가 생섯하는 각 이벤트 패킷은 include/linux/input.h에 정의되어 있다.

gpm(general-purpose mouse)은 X 서버의 도움 없이 텍스트 환경에서 마우스 사용을 가능하게 하는 서버다.

gpm이 evdev 메시지를 이해하므로 가상 마우스 드라이버는 gpm과 직접 의사소통이 가능하다.

초기화

초기화 과정에서 가상마우스 드라이버는 자신을 입력장치 드라이버로 등록한다.

이를 위해 먼저 핵심 API인 input_allocate_device()를 사용해 input_dev 구조체를 할당한다.

1
2
3
4
5
6
/* Allocate an input device data structure */
virmouse_input_dev = input_allocate_device();
if (!virmouse_input_dev) {
printk("Bad input_allocate_device()\n");
return -ENOMEM;
}

그리고 상대적인 이벤트를 생성하는 주체가 가상 마우스라고 선언한다.

1
2
/* 이벤트 형식은 EV_REL */
set_bit(EV_REL, virmouse_input_dev->evbit);

다음으로 가상 마우스가 만드는 이벤트 코드를 선언한다.

1
2
3
set_bit(REL_X, virmouse_input_dev->relbit); //상대적인 x 움직임
set_bit(REL_Y, virmouse_input_dev->relbit); //상대적인 y 움직임
set_bit(REL_WHEEL, virmouse_input_dev->relbit); //휠 움직임

다음은 가상마우스가 만드는 버튼 클릭이다.

1
2
3
4
set_bit(EV_KEY, virmouse_input_dev->evbit); //이벤트 형식은 EV_KEY
set_bit(BTN_LEFT, virmouse_input_dev->keybit); //좌클릭
set_bit(BTN_MIDDLE, virmouse_input_dev->keybit); //가운데 클릭
set_bit(BTN_RIGHT, virmouse_input_dev->keybit); //우클릭

최종적으로 다음과 같이 등록한다.

1
2
/* Register with the input subsystem */
ret = input_register_device(virmouse_input_dev);

write

write_virmouse()/sys/devices/platform/virmouse/vmevent에 붙어있는 sysfs의 store() 메소드이다.
App이 x y key값을 쓸때 다음 작업을 수행한다.

x,y값 입력시 상대적 좌표 이동값을 입력한다.

1
2
3
input_report_rel(virmouse_input_dev, REL_X, tmpx);
input_report_rel(virmouse_input_dev, REL_Y, tmpy);
input_sync(virmouse_input_dev);

key입력시(여기선 좌클릭) key입력을 구현한다.

좌,우,가운에 클릭은 클릭시 1, 때었을때 0값이 들어간다.

그러므로 가상 마우스 클릭을 구현하려면 1,0파라미터 값이 순차적으로 들어가야 한다.

1
2
3
4
5
input_report_key(virmouse_input_dev, BTN_LEFT, 1);
input_sync(virmouse_input_dev);
msleep(10);
input_report_key(virmouse_input_dev, BTN_LEFT, 0);
input_sync(virmouse_input_dev);

이후 input_sync()가 이 이벤트가 끝났음을 가리킨다.

입력 하위시스템은 입력된 이벤트를 evdev 패킷 하나로 모아 /dev/input/eventX를 거쳐 밖으로 내보낸다.

X는 가상 마우스 드라이버에 할당된 인터페이스 번호를 의미한다.

최종 구현 소스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*
* A Virtual Mouse Driver to send fake events from userspace.
*/
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/pci.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/delay.h>

struct input_dev *virmouse_input_dev;
static struct platform_device *virmouse_dev; /* Device structure */

/* Sysfs method to input simulated coordinates */
static ssize_t write_virmouse(struct device *dev,
struct device_attribute *attr,
const char *buffer, size_t count)
{
int x, y, key, tmpx, tmpy, i;

/* parsing input data */
sscanf(buffer, "%d%d%d", &x, &y, &key);

/* Report relative coordinates */

printk ("virmouse_event!!: X:%d Y:%d %d\n", x, y, key);

/* Report key event */
if (key == 4) {
input_report_rel(virmouse_input_dev, REL_WHEEL, y);
}else if(key==1){
input_report_key(virmouse_input_dev, BTN_LEFT, 1);
input_sync(virmouse_input_dev);
msleep(10);
input_report_key(virmouse_input_dev, BTN_LEFT, 0);
input_sync(virmouse_input_dev);
}
else if (key==2){
input_report_key(virmouse_input_dev, BTN_MIDDLE, 1);
input_sync(virmouse_input_dev);
msleep(10);
input_report_key(virmouse_input_dev, BTN_MIDDLE, 0);
input_sync(virmouse_input_dev);
}
else if (key==3){
input_report_key(virmouse_input_dev, BTN_RIGHT, 1);
input_sync(virmouse_input_dev);
msleep(10);
input_report_key(virmouse_input_dev, BTN_RIGHT, 0);
input_sync(virmouse_input_dev);
}
else if(key==0){
tmpx = x/3;
tmpy = y/3;
for(i = 0; i<3; i++)
{
input_report_rel(virmouse_input_dev, REL_X, tmpx);
input_report_rel(virmouse_input_dev, REL_Y, tmpy);
input_sync(virmouse_input_dev);
msleep(5);
}
}

return count;

}

/* Attach the sysfs write method */
DEVICE_ATTR(vmevent, 0644, NULL, write_virmouse);

/* Attribute Descriptor */
static struct attribute *virmouse_attrs[] = {
&dev_attr_vmevent.attr,
NULL
};

/* Attribute group */
static struct attribute_group virmouse_attr_group = {
.attrs = virmouse_attrs,
};

/* Driver Initializing */
int __init virmouse_init(void)
{
int ret = 0;
/* Register a platform device */
virmouse_dev = platform_device_register_simple("virmouse", -1, NULL, 0);
if (IS_ERR(virmouse_dev)){
printk ("virmouse_init: error\n");
return PTR_ERR(virmouse_dev);
}

/* Create a sysfs node to read simulated coordinates */
ret = sysfs_create_group(&virmouse_dev->dev.kobj, &virmouse_attr_group);

/* Allocate an input device data structure */
virmouse_input_dev = input_allocate_device();
if (!virmouse_input_dev) {
printk("Bad input_allocate_device()\n");
return -ENOMEM;
}

/* Announce that the virtual mouse will generate relative coordinates */
set_bit(EV_REL, virmouse_input_dev->evbit);
set_bit(REL_X, virmouse_input_dev->relbit);
set_bit(REL_Y, virmouse_input_dev->relbit);
set_bit(REL_WHEEL, virmouse_input_dev->relbit);


/* Announce key event */
set_bit(EV_KEY, virmouse_input_dev->evbit);
set_bit(BTN_LEFT, virmouse_input_dev->keybit);
set_bit(BTN_MIDDLE, virmouse_input_dev->keybit);
set_bit(BTN_RIGHT, virmouse_input_dev->keybit);
set_bit(BTN_0, virmouse_input_dev->keybit);


/* Register with the input subsystem */
ret = input_register_device(virmouse_input_dev);

/* print messages in the dmesg */
printk("Virtual Mouse Driver Initialized.\n");

return ret;
}

/* Driver Uninitializing */
void virmouse_uninit(void)
{
/* Unregister from the input subsystem */
input_unregister_device(virmouse_input_dev);

/* Remove sysfs node */
sysfs_remove_group(&virmouse_dev->dev.kobj, &virmouse_attr_group);

/* Unregister driver */
platform_device_unregister(virmouse_dev);

return;
}

module_init(virmouse_init);
module_exit(virmouse_uninit);
공유하기