OSDN Git Service

aea8d97474716758e58f0b066c42821c3ae481aa
[android-x86/device-generic-common.git] / tablet-mode / tablet-mode.c
1 /**
2  * Convert SW_TABLET_MODE events to SW_LID events for Android
3  *
4  * Copyright 2012 The Android-x86 Open Source Project
5  *
6  * Author: Stefan Seidel <android@stefanseidel.info>
7  *
8  * Licensed under GPLv2 or later
9  *
10  **/
11
12 #define LOG_TAG "tablet-mode"
13
14 #include <sys/stat.h>
15 #include <poll.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 #include <dirent.h>
19 #include <cutils/log.h>
20 #include <linux/input.h>
21 #include <linux/uinput.h>
22
23 /* we must use this kernel-compatible implementation */
24 #define BITS_PER_LONG (sizeof(long) * 8)
25 #define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
26 #define OFF(x)  ((x)%BITS_PER_LONG)
27 #define BIT(x)  (1UL<<OFF(x))
28 #define LONG(x) ((x)/BITS_PER_LONG)
29 #define test_bit(bit, array)    ((array[LONG(bit)] >> OFF(bit)) & 1)
30
31 int openfd(void)
32 {
33         int fd;
34         const char *dirname = "/dev/input";
35         DIR *dir;
36         if ((dir = opendir(dirname))) {
37                 struct dirent *de;
38                 unsigned long caps[NBITS(SW_TABLET_MODE+1)];
39                 while ((de = readdir(dir))) {
40                         if (de->d_name[0] != 'e') // eventX
41                                 continue;
42                         char name[PATH_MAX];
43                         snprintf(name, PATH_MAX, "%s/%s", dirname, de->d_name);
44                         fd = open(name, O_RDONLY);
45                         if (fd < 0) {
46                                 ALOGE("could not open %s, %s", name, strerror(errno));
47                                 continue;
48                         }
49                         memset(caps, 0, sizeof(caps));
50                         if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(caps)), caps) < 1) {
51                                 ALOGE("could not get device caps for %s, %s\n", name, strerror(errno));
52                                 continue;
53                         }
54                         if (test_bit(SW_TABLET_MODE, caps)) {
55                                 ALOGI("open %s(%s) ok", de->d_name, name);
56                                 return fd;
57                         }
58                         close(fd);
59                 }
60                 closedir(dir);
61         }
62         return -1;
63 }
64
65 void send_switch(int ufd, int state) {
66         struct input_event nev;
67         ALOGI("Tablet Mode Switch to %d\n", state);
68         memset(&nev, 0, sizeof(struct input_event));
69         nev.type = EV_SW;
70         nev.code = SW_LID;
71         nev.value = !!state;
72         write(ufd, &nev, sizeof(struct input_event));
73         nev.type = EV_SYN;
74         nev.code = SYN_REPORT;
75         nev.value = 0;
76         write(ufd, &nev, sizeof(struct input_event));
77 }
78
79 int main(void)
80 {
81         int ifd = openfd();
82         if (ifd < 0) {
83                 ALOGE("could not find any tablet mode switch, exiting.");
84                 return -1;
85         }
86
87         sleep(10); //wait some time or otherwise EventHub might not pick up our events correctly!?
88
89         int ufd = open("/dev/uinput", O_WRONLY | O_NDELAY);
90         if (ufd >= 0) {
91                 struct uinput_user_dev ud;
92                 memset(&ud, 0, sizeof(struct uinput_user_dev));
93                 strcpy(ud.name, "Android Tablet Lid Switch");
94                 write(ufd, &ud, sizeof(struct uinput_user_dev));
95                 ioctl(ufd, UI_SET_EVBIT, EV_SW);
96                 ioctl(ufd, UI_SET_SWBIT, SW_LID);
97                 ioctl(ufd, UI_DEV_CREATE, 0);
98         } else {
99                 ALOGE("could not open uinput device: %s", strerror(errno));
100                 return -1;
101         }
102
103         // send initial switch state
104         unsigned long sw_state[NBITS(SW_TABLET_MODE+1)];
105         memset(sw_state, 0, sizeof(sw_state));
106         if (ioctl(ifd, EVIOCGSW(sizeof(sw_state)), sw_state) >= 0) {
107                 send_switch(ufd, test_bit(SW_TABLET_MODE, sw_state) ? 1 : 0);
108         }
109
110         // read events and pass them on modified
111         while (1) {
112                 struct input_event iev;
113                 size_t res = read(ifd, &iev, sizeof(struct input_event));
114                 if (res < sizeof(struct input_event)) {
115                         ALOGW("insufficient input data(%d)? fd=%d", res, ifd);
116                         continue;
117                 }
118                 //LOGV("type=%d scancode=%d value=%d from fd=%d", iev.type, iev.code, iev.value, ifd);
119                 if (iev.type == EV_SW && iev.code == SW_TABLET_MODE) {
120                         send_switch(ufd, iev.value);
121                 }
122         }
123
124         return 0;
125 }