AndroidInputInverter
@Yaki @Alon
Target: invert input from secondary usb tablet on an Akai AK8455 tablet.
Status
pipeevents produces correct behavior of bottom tablet and starts on boot, and idc prevents two fingers clicks. Next do a kernel driver, main problem is finding the kernel source (version, patches, config) and install (which mtd partition) plus a fail safe work flow (i.e. how not to brick it).
Tablet orientation hack - we lock it in place, it takes a long time in boot to actually turn to the correct orientation. A lot of garbage running on boot makes boot very slow.
Source
pipeevents plus installation make rules (we use some adware adb running via wireless since the usb plug is not accessible, installation uses adb):
TODO
- [DONE via userspace - pipeevents, TODO via kernel] The touchpanel only produces clicks when using two fingers. See logs below. Also see android-porting group.
- Orientation is wrong for a given gravity - need to either change the output of the acceleration sensor via kernel change, or perhaps a configuration change
- getevent is very useful to debug this, it parses /dev/input/*
- We are not sure why the orientation locked with the panel, i.e. if the idc file addition fixed it. To figure out, do:
- dumpsys window input *without* the idc file
- dumpsys window input *with* the idc file
- compare.
- How to force rereading of the idc file without reset?
- open source adb via wifi application (apk)
pipeevents
Problem: tablet produces ""clicks"" only when we use two fingers, none when we use one
Tests: using getevent via adb (logs below) and comparing the events from the two tablet devices (/dev/input/event3 is the onboard one, /dev/input/event2 is the added bottom one - clearly depends on order of driver initialization, but stable as long as we don't touch kernel) the onboard produces MT events and the bottom produces older events that are confusing.
Solution: write a userspace app, based on android's toolbox sendevent and getevent, that produces fake MT events for every click read. At the same time disable the events being produces by setting the idc file to non internal.
Details:
- disable idc:
- edit /system/usr/idc/Vendor_2087_Product_0702.idc and set "device.internal=0"
- create a new program called pipeevents, build it with the NDK, set it to run on boot via /system/bin/preinstall.sh (since /init.rc seems to be rewritten - even though that's the correct solution for a rooted device, see init-process-and-initrc)
pipeevents.c
/* * Crude fix for second tablet single taps not working, requiring two taps. Instead * of fixing the driver, read events from the kernel (produced by the driver) and produce * fake events from the working tablet device file. * * The working tablet produces multi touch events, so we fake them. * * Used getevent -l and getevent without '-l' to get values of all constants and see * behavior of both tablets. * * Based on toolbox/sendevent.c and toolbox/getevent.c. */ /* * Building and debug deploying (not a daemon): * * Using android-ndk-r9c-linux-x86_64.tar.bz2 NDK * ROOT=<wherever you unpacked the NDK> * GCC=$ROOT/android-ndk-r9c/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc * SYSROOT=$ROOT/android-ndk-r9c/platforms/android-19/arch-arm/ * * $GCC --sysroot=$SYSROOT pipeevents.c -o pipeevents && adb push pipeevents /data/local/tmp/ * * Deploying: added to /system/bin/preinstall.sh * echo "starting pipeevents" * /data/local/tmp/pipeevents > /dev/null < /dev/null 2> /dev/null & */ #include <stdio.h> #include <fcntl.h> #include <sys/ioctl.h> #include <errno.h> //#define DEBUG #ifdef DEBUG #define debug printf #else #define debug(...) #endif // from <linux/input.h> struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value; }; #define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */ // end <linux/input.h> const char *SOURCE_DEV = "/dev/input/event2"; const char *DEST_DEV = "/dev/input/event3"; enum { ABS_X = 0, ABS_Y = 1, ABS_Z = 2, ABS_RX = 3, ABS_MISC = 0x28, ABS_MT_TOUCH_MAJOR = 0x30, ABS_MT_POSITION_X = 0x35, ABS_MT_POSITION_Y = 0x36, }; enum { SYN_REPORT = 0, SYN_MT_REPORT = 2 }; enum { BTN_TOUCH = 0x14a, BTN_TOOL_PEN = 0x140, }; enum { EV_ABS = 3, EV_SYN = 0, EV_MSC = 4, EV_KEY = 1, }; enum { EV_UP = 0, EV_DOWN = 1 }; static int write_event(int fd, int type, int code, int value) { int ret; struct input_event event; event.type = type; event.code = code; event.value = value; ret = write(fd, &event, sizeof(event)); if(ret < sizeof(event)) { fprintf(stderr, "write event failed, %s\n", strerror(errno)); return -1; } return 0; } /* Directionality: Source: Same Destination: ^ | <----- */ // getevent -p /dev/input/event3 - can do the ioctls (read the source) const int width = 960; const int height = 640; // getevent -p /dev/input/event2 - can do the ioctls (read the source) const int input_height = 2721; const int input_width = 4058; /* * offset between tablets - they are not exactly aligned: * * .____________________ * | top | * |--------------------| * | | * | | * |--------------------| * | bottom | * ---------------------. * */ const int output_offset_y = -100; static void write_xy_down(int fd, int x, int y) { write_event(fd, EV_ABS, ABS_MT_TOUCH_MAJOR, EV_DOWN); write_event(fd, EV_ABS, ABS_MT_POSITION_X, x); write_event(fd, EV_ABS, ABS_MT_POSITION_Y, y); write_event(fd, EV_SYN, SYN_MT_REPORT, 0); write_event(fd, EV_SYN, SYN_REPORT, 0); } static void write_up(int fd) { write_event(fd, EV_ABS, ABS_MT_TOUCH_MAJOR, EV_UP); write_event(fd, EV_SYN, SYN_MT_REPORT, 0); write_event(fd, EV_SYN, SYN_REPORT, 0); } int main(int argc, char *argv[]) { int i; int dest, source; int ret; int version; struct input_event event; int x, y; int key; // EV_DOWN / EV_UP int new_event = 0; dest = open(DEST_DEV, O_RDWR); if(dest < 0) { fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno)); return 1; } if (ioctl(dest, EVIOCGVERSION, &version)) { fprintf(stderr, "could not get driver version for %s, %s\n", argv[optind], strerror(errno)); return 1; } source = open(SOURCE_DEV, O_RDONLY); if(source < 0) { fprintf(stderr, "could not open %s, %s\n", argv[optind], strerror(errno)); return 1; } if (ioctl(source, EVIOCGVERSION, &version)) { fprintf(stderr, "could not get driver version for %s, %s\n", argv[optind], strerror(errno)); return 1; } while (1) { read(source, &event, sizeof(event)); switch (event.type) { /* * look for BTN_TOOL_PEN only, others are bogus - BTN_TOUCH appears * up/down even when dragging */ case EV_KEY: //printf("key: %d, %d\n", event.code, event.value); if (event.code != BTN_TOOL_PEN) { break; } if (key != event.value) { new_event = 1; } key = event.value; debug("got %s\n", event.value == EV_DOWN ? "down" : "up"); break; case EV_ABS: { switch (event.code) { case ABS_X: if (x != event.value) { new_event = 1; x = event.value * width / input_width; debug("x = %d\n", x); } break; case ABS_Y: if (y != event.value) { new_event = 1; y = event.value * height / input_height + output_offset_y; debug("y = %d\n", y); } break; } break; } case EV_SYN: { // only shows SYN_REPORT, no need to check if (new_event) { debug("new event\n"); new_event = 0; if (key == EV_DOWN) { write_xy_down(dest, x, y); } else { write_up(dest); } } break; } case EV_MSC: break; } } return 0; }
Research
Supporting our touch panel special events
- Resources
- Using toolbox's getevent and sendevent as a very crude proof of concept that this is all solvable in userspace
- but really would like the driver to produce the right events.
- libsensors.so ?
- adb shell ps | grep system_server
- adb shell cat /proc/150/maps | grep sensor
44e56000-44e58000 r-xp 00000000 5d:18 747 /system/lib/hw/sensors.exDroid.so 44e58000-44e59000 rw-p 00002000 5d:18 747 /system/lib/hw/sensors.exDroid.so 49a50000-49a62000 r-xp 00000000 5d:18 850 /system/lib/libsensorservice.so 49a62000-49a64000 rw-p 00012000 5d:18 850 /system/lib/libsensorservice.so
- Services: Sensor Service, Input Method Service ?
condo touch device isn't recognized as multi-touch. also, click is made only by two fingers - one marks the spot with no feedback, other fingers touches anywhere to make the first spot clicked. Ocassionally this behaviour changes to normal click by one finger, no idea why and how to keep that stable.
- here is a link to table of linux supported moulti-touch panel - http://lii-enac.fr/en/architecture/linux-input/multitouch-devices.html
- here is the condo driver, used in our transparent tablet - https://android.googlesource.com/kernel/tegra/+/457287905f43f5be9789214e1a25b514cd1d3e5a/drivers/hid/hid-cando.c
DONE
- using the idc file rotation of the tablet is enabled and it produces clicks that are always aligned with the screen rotation, see it's contents below.
- not sure all of the settings there are required.
Howto connect via adb to Akai from windows 8
Install PdaNet package, it includes drivers. Follow it's instructions (disconnect device before or during installation, connect it when it asks).
Afterwards "adb devices" should show up for instance:
List of devices attached 20080411413fc082 device
Turning on tcp/ip debugging via adb
http://stackoverflow.com/questions/2604727/how-can-i-connect-to-android-with-adb-over-tcp
su setprop service.adb.tcp.port 5555 stop adbd start adbd
Android input devices
How does android process a usb input device?
Android consults input device configuration files.
/system/usr/idc/
Vendor_2087_Product_0702.idc
# Copyright (C) 2010 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License.
# # Emulator keyboard configuration file #1. #
device.internal = 1
# Basic Parameters touch.deviceType = touchScreen touch.orientationAware = 2
# Size touch.size.calibration = diameter touch.size.scale = 1 touch.size.bias = 0 touch.size.isSummed = 0
# Pressure # Driver reports signal strength as pressure. # # A normal thumb touch typically registers about 200 signal strength # units although we don't expect these values to be accurate. touch.pressure.calibration = amplitude touch.pressure.scale = 0.005
# Orientation touch.orientation.calibration = none
Resources
- Systemcalls
- standalone toolchain use
- shell usage - tried a shell version of getevent -> sendevent, there was buffering i couldn't control.
- multitouch in linux
- downloading the android source
getevent traces
Single tap, doesn't produce the higher 'click' event
root@android:/ # getevent -lt /dev/input/event2 2658-945331: EV_KEY BTN_TOOL_PEN DOWN 2658-945354: EV_ABS ABS_MISC 00000002 2658-945362: EV_ABS ABS_X 00000282 2658-945369: EV_ABS ABS_Y 00000b15 2658-945405: EV_SYN SYN_REPORT 00000000 2658-955340: EV_MSC MSC_SCAN 000d0042 2658-955358: EV_KEY BTN_TOUCH DOWN 2658-955393: EV_MSC MSC_SCAN 000d0042 2658-955397: EV_KEY BTN_TOUCH UP 2658-955429: EV_SYN SYN_REPORT 00000000 2658-965327: EV_MSC MSC_SCAN 000d0042 2658-965342: EV_KEY BTN_TOUCH DOWN 2658-965364: EV_ABS ABS_X 00000288 2658-965371: EV_ABS ABS_Y 00000b11 2658-965379: EV_MSC MSC_SCAN 000d0042 2658-965382: EV_KEY BTN_TOUCH UP 2658-965413: EV_SYN SYN_REPORT 00000000 2658-975333: EV_MSC MSC_SCAN 000d0042 2658-975348: EV_KEY BTN_TOUCH DOWN 2658-975384: EV_MSC MSC_SCAN 000d0042 2658-975388: EV_KEY BTN_TOUCH UP 2658-975417: EV_SYN SYN_REPORT 00000000 2658-985363: EV_MSC MSC_SCAN 000d0042 2658-985378: EV_KEY BTN_TOUCH DOWN 2658-985413: EV_MSC MSC_SCAN 000d0042 2658-985417: EV_KEY BTN_TOUCH UP 2658-985447: EV_SYN SYN_REPORT 00000000 2658-995485: EV_KEY BTN_TOOL_PEN UP 2658-995608: EV_SYN SYN_REPORT 00000000
External tablet, double tap, produces click
# getevent -lt /dev/input/event2 2777-567013: EV_KEY BTN_TOOL_PEN DOWN 2777-567201: EV_ABS ABS_MISC 00000001 2777-567308: EV_ABS ABS_X 000002bf 2777-567407: EV_ABS ABS_Y 000009f7 2777-567919: EV_SYN SYN_REPORT 00000000 2777-576720: EV_MSC MSC_SCAN 000d0042 2777-576801: EV_KEY BTN_TOUCH DOWN 2777-577208: EV_MSC MSC_SCAN 000d0042 2777-577254: EV_KEY BTN_TOUCH UP 2777-577665: EV_SYN SYN_REPORT 00000000 2777-616766: EV_MSC MSC_SCAN 000d0042 2777-616858: EV_KEY BTN_TOUCH DOWN 2777-617137: EV_ABS ABS_X 000002cd 2777-617231: EV_ABS ABS_Y 000009d2 2777-617343: EV_MSC MSC_SCAN 000d0042 2777-617386: EV_KEY BTN_TOUCH UP 2777-617498: EV_KEY BTN_TOOL_RUBBER DOWN 2777-617671: EV_ABS ABS_Z 000001a9 2777-617760: EV_ABS ABS_RX 0000077c 2777-617872: EV_ABS 002a 00000002 2777-617918: EV_SYN SYN_REPORT 00000000 2777-626715: EV_MSC MSC_SCAN 000d0042 2777-626797: EV_KEY BTN_TOUCH DOWN 2777-627571: EV_SYN SYN_REPORT 00000000 2777-686340: EV_ABS ABS_X 000002b4 2777-686371: EV_ABS ABS_Y 000009c4 2777-686447: EV_SYN SYN_REPORT 00000000 2777-706393: EV_MSC MSC_SCAN 000d0042 2777-706427: EV_KEY BTN_TOUCH UP 2777-706442: EV_KEY BTN_TOOL_PEN UP 2777-706447: EV_KEY BTN_TOOL_RUBBER UP 2777-706526: EV_SYN SYN_REPORT 00000000
Builtin tablet, single tap, produces click
# getevent -lt /dev/input/event3 2737-190833: EV_ABS ABS_MT_TOUCH_MAJOR 00000001 2737-190935: EV_ABS ABS_MT_POSITION_X 00000069 2737-190986: EV_ABS ABS_MT_POSITION_Y 000000a2 2737-191030: EV_SYN SYN_MT_REPORT 00000000 2737-191067: EV_SYN SYN_REPORT 00000000 2737-249546: EV_ABS ABS_MT_TOUCH_MAJOR 00000001 2737-251284: EV_ABS ABS_MT_POSITION_X 00000069 2737-253841: EV_ABS ABS_MT_POSITION_Y 000000a2 2737-254038: EV_SYN SYN_MT_REPORT 00000000 2737-254195: EV_SYN SYN_REPORT 00000000 2737-283836: EV_ABS ABS_MT_TOUCH_MAJOR 00000001 2737-283869: EV_ABS ABS_MT_POSITION_X 00000069 2737-283880: EV_ABS ABS_MT_POSITION_Y 000000a2 2737-283891: EV_SYN SYN_MT_REPORT 00000000 2737-283902: EV_SYN SYN_REPORT 00000000 2737-321578: EV_ABS ABS_MT_TOUCH_MAJOR 00000000 2737-321709: EV_SYN SYN_MT_REPORT 00000000 2737-321736: EV_SYN SYN_REPORT 00000000
Both devices compared
# getevent -li /dev/input/event2 add device 1: /dev/input/event2 bus: 0003 vendor 2087 product 0702 version 0111 name: "Multi Touch Panel with Controller" location: "usb-sw_hcd_host0-1/input0" id: "" version: 1.0.1 events: KEY (0001): BTN_TOOL_PEN BTN_TOOL_RUBBER BTN_TOUCH ABS (0003): ABS_X : value 692, min 0, max 4095, fuzz 0, flat 0, resolution 0 ABS_Y : value 2500, min 0, max 4095, fuzz 0, flat 0, resolution 0 ABS_Z : value 425, min 0, max 4095, fuzz 0, flat 0, resolution 0 ABS_RX : value 1916, min 0, max 4095, fuzz 0, flat 0, resolution 0 ABS_MISC : value 1, min 0, max 1, fuzz 0, flat 0, resolution 0 0029 : value 3, min 0, max 1, fuzz 0, flat 0, resolution 0 002a : value 2, min 0, max 2, fuzz 0, flat 0, resolution 0 MSC (0004): MSC_SCAN input props: <none> root@android:/ # getevent -li /dev/input/event3 add device 1: /dev/input/event3 bus: 0019 vendor 0001 product 0002 version 0100 name: "zet6221_ts" location: "zet6221_touch/input0" id: "" version: 1.0.1 events: KEY (0001): KEY_HOME KEY_MENU KEY_BACK KEY_SEARCH ABS (0003): ABS_MT_TOUCH_MAJOR : value 0, min 0, max 1, fuzz 0, flat 0, resolution 0 ABS_MT_WIDTH_MAJOR : value 0, min 0, max 0, fuzz 0, flat 0, resolution 0 ABS_MT_POSITION_X : value 0, min 0, max 960, fuzz 0, flat 0, resolution 0 ABS_MT_POSITION_Y : value 0, min 0, max 640, fuzz 0, flat 0, resolution 0 input props: <none>