2017. 2. 23. 21:59 devel/개념
Linux Input Event Driver
Linux Input Event Driver
주의
내공 부족으로 정확하지 않는 내용이 다수 있을 수 있음.
Subsystem이라고 부를 정도로 정말 거대하다.
0. The Input Subsystem
출처 : Essential Linux Device Drivers
hw -> Transfer Layer{spi, i2c, ….} -> input driver -> i2c core api -> input event driver -> Evdev Interface -> application
내가 코드 보며 이해한 바로는 이런데 실제로는 어떨련지 모르겠다.
굳이 input driver와 input event driver를 나누지 않고 하나로 합쳐서 작성된 것도 있다.
1. The Evdev Interface
include/linux/input.h
input subsystem의 자료형 및 정의가 있다
struct input_event {
struct timeval time; //timestamp
__u16 type;
__u16 code;
__s32 value; //event value
}
이 구조체 형태로 /dev/input/ 디렉토리에 이벤트 값이 전달 된다.
주의 : 한 회에 한 종류 값만 전달 된다.
무슨 의미인고 하니 read로 읽을 때 이벤트 당 1개씩 값이 전달 된다는 이야기
예를 들어 (x, y) 좌표에 (12, 34) 클릭이 발생한 경우
data#1 - 클릭 발생
type = EV_KEY,
code = BTN_TOUCH,
value = 1
data#2 - x 좌표
type = EV_ABS,
code = ABS_X,
value = 12
data#3 - y 좌표
type = EV_ABS,
code = ABS_Y,
value = 34
data#4 - 이벤트 끝
type = EV_SYN, //event1 finished
code = SYN_REPORT,
value = 0
EV_SYN으로 하나의 이벤트 전송 종료를 알린다.
이벤트 리포팅을 위해
static inline void input_report_key(struct input_dev *dev, unsigned int code, int value);
...
static inline void input_sync(struct input_dev *dev);
여러 이벤트 종류에 따라 각기 다양하게 함수들이 있다.
사용 예시
drivers/input/joystick/a3d.c
static void a3d_read(struct a3d *a3d, unsigned char *data)
{
...
input_report_key(dev, BTN_RIGHT, data[2] & 1);
input_report_key(dev, BTN_LEFT, data[3] & 2);
...
input_report_abs(dev, ABS_X, ....);
...
input_sync(dev);
}
data를 얻어와서 event 를 evdev로 보내는 모습
이 드라이버의 경우 data는 별도의 input driver에게 얻는다.
struct input_dev {
...
struct input_id id;
unsigned long evbit[...];
unsigned long keybit[...];
...
int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);
...
}
엄청나게 거대하므로 각각 역할은 커널소스의 주석을 직접 확인하기 바란다.
2. Example Source
drivers/input/keyboard/pxa27x_keypad.c
이 코드가 가장 보기 쉬웠어요
module_platform_driver
-> probe
-> request_mem_region, ioremap
-> input_allocate_device, input_dev 설정, request_irq
-> input_register_device
irq_handler - mmio read, input_event, input_report_key, input_sync
대략 이런 흐름인 것 같다.
3. Linux input event read Application code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
#include <sys/time.h>
#include <string.h>
int main()
{
int fd, ret;
int x, y;
const char* evdPath = "/dev/input/event3";
struct input_event iev[3];
fd = open(evdPath, O_RDONLY);
if(fd < 0) {
perror("error");
return -1;
}
while(1) {
ret = read(fd, iev, sizeof(struct input_event)*3);
if(ret < 0) {
perror("error");
break;
}
if(iev[0].type == EV_REL && iev[0].code == REL_X)
x = iev[0].value;
if(idev[1].type == EV_REL && iev[1].code == REL_Y)
y = iev[1].value;
printf("x:%d, y:%d\n", x, y);
printf("%hu, %hu, %d\n", iev[0].type, iev[0].code, iev[0].value);
printf("%hu, %hu, %d\n", iev[1].type, iev[1].code, iev[1].value);
printf("%hu, %hu, %d\n", iev[2].type, iev[2].code, iev[2].value);
}
close(fd);
return 0;
}
RUN
4, 3
2, 0, 4
2, 1, 3
0, 0, 0
...
별 설명할 꺼리가 없다……
다만 왜 event3로 생성이 되는지 그건 잘 모르겠다.
4. virtual input event driver code
이름은 거창한데 사실 별거 없다
hw 에서 인터럽트 혹은 데이터를 안 받았는데 그냥 데이터 들어왔다고 evdev에게 이벤트를 알리는 것이다.
const char *pdev_name = "ietest";
struct platform_device *pdev;
struct ev_test {
struct input_dev *input_dev;
struct task_struct *task;
};
int read_data_from_sensor(void)
{
static int roll = 0;
return roll++%2 ? 3 : 4;
}
void set_input_device_property(struct input_dev *dev)
{
set_bit(EV_REL, dev->evbit);
set_bit(REL_X, dev->relbit);
set_bit(REL_Y, dev->relbit);
}
void send_event_msg_to_evdev(struct input_dev *dev)
{
input_report_rel(dev, REL_X, read_data_from_sensor());
input_report_rel(dev, REL_Y, read_data_from_sensor());
input_sync(dev);
}
static int hw_fake_int_handler(void *arg)
{
struct ev_test *event_ctrl = (struct ev_test*) arg;
struct input_dev *idev = event_ctrl->input_dev;
while(!kthread_sould_stop()) //check stop signal
{
send_event_msg_to_evdev(idev);
msleep_interruptible(300);
}
return 0;
}
/* REMOVED ERROR CHECKING ROUTINE */
static int inputevent_test_init(void)
{
int ret;
struct ev_test *event_ctrl;
struct task_struct *task;
struct input_dev *idev;
/* platform or i2c or spi or serio or etc ... */
pdev = platform_device_register_simple(pdev_name, -1, NULL, 0);
event_ctrl = kzalloc((sizeof(struct ev_test)), GFP_KERNEL);
idev = input_allocate_device();
set_input_device_property(idev);
input_register_device(idev);
task = kthread_run(hw_fake_int_handler, event_ctrl,
"ieint_%s_#%d", pdev_name, 1);
event_ctrl->input_dev = idev;
event_ctrl->task = task;
dev_set_drvdata(&pdev->dev, event_ctrl);
return 0;
}
static void inputevent_test_exit(void)
{
struct ev_test *event_ctrl = (struct ev_test*)dev_get_drvdata(&pdev->dev);
kthread_stop(event_ctrl->task);
input_unregister_device(pdev);
}
module_init(inputevent_test_init);
module_exit(inputevent_test_exit);
이 코드에서는 pdev를 가상으로 만들었지만 실제로는 여러 통신 방식을 통해 probe 되고 거기에 interrupt handler를 등록해서 input event 를 보내는 식으로 처리하는 것 같다
인터럽트 핸들러 대신 가상으로 커널 쓰레드를 만들어서 계속 좌표를 보내도록 개발했다
기타 github에 올려본 예제
'devel > 개념' 카테고리의 다른 글
STMF4 시리즈 부트로더, iap 개발 팁 (1) | 2017.04.03 |
---|---|
Linux Kernel DMA (0) | 2017.03.02 |
golang 고랭지 농업이 아니라 go 언어 (0) | 2016.12.01 |
gpio pull down switch 연결 (0) | 2016.11.16 |
도요타 캠리 급발진 버그 분석 보고서 (0) | 2016.11.10 |