2 * Convert SW_TABLET_MODE events to SW_LID events for Android
4 * Copyright 2012 The Android-x86 Open Source Project
6 * Author: Stefan Seidel <android@stefanseidel.info>
8 * Licensed under GPLv2 or later
12 #define LOG_TAG "tablet-mode"
20 #include <cutils/log.h>
21 #include <linux/input.h>
22 #include <linux/uinput.h>
24 /* we must use this kernel-compatible implementation */
25 #define BITS_PER_LONG (sizeof(long) * 8)
26 #define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
27 #define OFF(x) ((x)%BITS_PER_LONG)
28 #define BIT(x) (1UL<<OFF(x))
29 #define LONG(x) ((x)/BITS_PER_LONG)
30 #define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
35 const char *dirname = "/dev/input";
37 if ((dir = opendir(dirname))) {
39 unsigned long caps[NBITS(SW_TABLET_MODE+1)];
40 while ((de = readdir(dir))) {
41 if (de->d_name[0] != 'e') // eventX
44 snprintf(name, PATH_MAX, "%s/%s", dirname, de->d_name);
45 fd = open(name, O_RDONLY);
47 ALOGE("could not open %s, %s", name, strerror(errno));
50 memset(caps, 0, sizeof(caps));
51 if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(caps)), caps) < 1) {
52 ALOGE("could not get device caps for %s, %s\n", name, strerror(errno));
55 if (test_bit(SW_TABLET_MODE, caps)) {
56 ALOGI("open %s(%s) ok", de->d_name, name);
66 void send_switch(int ufd, int state) {
67 struct input_event nev;
68 ALOGI("Tablet Mode Switch to %d\n", state);
69 memset(&nev, 0, sizeof(struct input_event));
73 write(ufd, &nev, sizeof(struct input_event));
75 nev.code = SYN_REPORT;
77 write(ufd, &nev, sizeof(struct input_event));
84 ALOGE("could not find any tablet mode switch, exiting.");
88 sleep(10); //wait some time or otherwise EventHub might not pick up our events correctly!?
90 int ufd = open("/dev/uinput", O_WRONLY | O_NDELAY);
92 struct uinput_user_dev ud;
93 memset(&ud, 0, sizeof(struct uinput_user_dev));
94 strcpy(ud.name, "Android Tablet Lid Switch");
95 write(ufd, &ud, sizeof(struct uinput_user_dev));
96 ioctl(ufd, UI_SET_EVBIT, EV_SW);
97 ioctl(ufd, UI_SET_SWBIT, SW_LID);
98 ioctl(ufd, UI_DEV_CREATE, 0);
100 ALOGE("could not open uinput device: %s", strerror(errno));
104 // send initial switch state
105 unsigned long sw_state[NBITS(SW_TABLET_MODE+1)];
106 memset(sw_state, 0, sizeof(sw_state));
107 if (ioctl(ifd, EVIOCGSW(sizeof(sw_state)), sw_state) >= 0) {
108 send_switch(ufd, test_bit(SW_TABLET_MODE, sw_state) ? 1 : 0);
111 // read events and pass them on modified
113 struct input_event iev;
114 size_t res = read(ifd, &iev, sizeof(struct input_event));
115 if (res < sizeof(struct input_event)) {
116 ALOGW("insufficient input data(%d)? fd=%d", res, ifd);
119 //LOGV("type=%d scancode=%d value=%d from fd=%d", iev.type, iev.code, iev.value, ifd);
120 if (iev.type == EV_SW && iev.code == SW_TABLET_MODE) {
121 send_switch(ufd, iev.value);