AndroidInputInverter

From TAMI
Jump to: navigation, search

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

  1. disable idc:
    1. edit /system/usr/idc/Vendor_2087_Product_0702.idc and set "device.internal=0"
  2. 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

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.

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

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>