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):
 * https://gitorious.org/android-second-tablet-hacks

= 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:


 * 1) disable idc:
 * 2) edit /system/usr/idc/Vendor_2087_Product_0702.idc and set "device.internal=0"
 * 3) 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= * 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 & */ //#define DEBUG // from  struct input_event { struct timeval time; __u16 type; __u16 code; __s32 value; }; // end  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; }
 * 1) include 
 * 2) include 
 * 3) include 
 * 4) include 
 * 1) ifdef DEBUG
 * 2) define debug printf
 * 3) else
 * 4) define debug(...)
 * 5) endif
 * 1) define EVIOCGVERSION		_IOR('E', 0x01, int)			/* get driver version */

= Research =

Supporting our touch panel special events
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
 * Resources
 * http://www.opersys.com/blog/extending-android-hal
 * http://www.opersys.com/downloads/cc-slides/embedded-android/embedded-android-131104.pdf
 * https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
 * 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


 * 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?
 * http://source.android.com/devices/tech/input/overview.html

Android consults input device configuration files.

/system/usr/idc/

Vendor_2087_Product_0702.idc
# # #
 * 1) Copyright (C) 2010 The Android Open Source Project
 * 1) Licensed under the Apache License, Version 2.0 (the "License");
 * 2) you may not use this file except in compliance with the License.
 * 3) You may obtain a copy of the License at
 * 1)      http://www.apache.org/licenses/LICENSE-2.0
 * 1) Unless required by applicable law or agreed to in writing, software
 * 2) distributed under the License is distributed on an "AS IS" BASIS,
 * 3) WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * 4) See the License for the specific language governing permissions and
 * 5) limitations under the License.

# #
 * 1) Emulator keyboard configuration file #1.

device.internal = 1

touch.deviceType = touchScreen touch.orientationAware = 2
 * 1) Basic Parameters

touch.size.calibration = diameter touch.size.scale = 1 touch.size.bias = 0 touch.size.isSummed = 0
 * 1) Size

# touch.pressure.calibration = amplitude touch.pressure.scale = 0.005
 * 1) Pressure
 * 2) Driver reports signal strength as pressure.
 * 1) A normal thumb touch typically registers about 200 signal strength
 * 2) units although we don't expect these values to be accurate.

touch.orientation.calibration = none
 * 1) Orientation

Resources

 * https://groups.google.com/forum/#!forum/android-porting


 * Systemcalls
 * x86 - http://docs.cs.up.ac.za/programming/asm/derick_tut/syscalls.html
 * arm - http://pandeyece.blogspot.co.il/2012/12/linux-system-call-for-arm-architecture.html
 * standalone toolchain use


 * shell usage - tried a shell version of getevent -> sendevent, there was buffering i couldn't control.
 * https://www.mirbsd.org/htman/i386/man1/sh.htm


 * multitouch in linux
 * https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt


 * downloading the android source
 * http://source.android.com/source/downloading.html


 * Android course from opersys, very good overview, android as an embedded linux platform
 * http://www.opersys.com/training/embedded-android
 * adding a new HAL (not required, input is not HAL, but might be interesting later) https://github.com/opersys/opersys-hal-hw

= 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
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
 * 1) getevent -lt /dev/input/event2

Builtin tablet, single tap, produces click
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
 * 1) getevent -lt /dev/input/event3

Both devices compared
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: 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:
 * 1) getevent -li /dev/input/event2