From e29a9532f576b7f7d0cd304cad5b2cc0c86f9645 Mon Sep 17 00:00:00 2001 From: skeezix Date: Sun, 14 Mar 2010 22:14:02 -0400 Subject: [PATCH] Added my own evdev handler for dpad/dpadbuttons/nubs, works fantastic --- Makefile | 9 +- include/pnd_device.h | 8 ++ include/pnd_io_evdev.h | 69 ++++++++++ include/pnd_io_ioctl.h | 9 ++ lib/pnd_io_evdev.c | 300 +++++++++++++++++++++++++++++++++++++++++ lib/pnd_io_gpio.c | 2 + lib/pnd_io_ioctl.c | 21 +++ test/evdevtest.c | 75 +++++++++++ 8 files changed, 490 insertions(+), 3 deletions(-) create mode 100644 include/pnd_io_evdev.h create mode 100644 include/pnd_io_ioctl.h create mode 100644 lib/pnd_io_evdev.c create mode 100644 lib/pnd_io_ioctl.c create mode 100644 test/evdevtest.c diff --git a/Makefile b/Makefile index bd8e912..eb73f4d 100644 --- a/Makefile +++ b/Makefile @@ -21,12 +21,12 @@ LIB = libpnd.a SOLIB = libpnd.so.1 # canonicle name SOLIB1 = libpnd.so.1.0.1 # versioned name XMLOBJ = lib/tinyxml/tinystr.o lib/tinyxml/tinyxml.o lib/tinyxml/tinyxmlerror.o lib/tinyxml/tinyxmlparser.o -ALLOBJ = pnd_conf.o pnd_container.o pnd_discovery.o pnd_pxml.o pnd_notify.o pnd_locate.o pnd_tinyxml.o pnd_pndfiles.o pnd_apps.o pnd_utility.o pnd_desktop.o pnd_io_gpio.o pnd_logger.o pnd_dbusnotify.o pnd_device.o +ALLOBJ = pnd_conf.o pnd_container.o pnd_discovery.o pnd_pxml.o pnd_notify.o pnd_locate.o pnd_tinyxml.o pnd_pndfiles.o pnd_apps.o pnd_utility.o pnd_desktop.o pnd_io_gpio.o pnd_io_ioctl.o pnd_io_evdev.o pnd_logger.o pnd_dbusnotify.o pnd_device.o -all: ${SOLIB} ${LIB} conftest discotest notifytest pndnotifyd rawpxmltest pndvalidator loggertest dbusnotifytest pnd_run pndevmapperd pnd_info evtest mmenu mmwrapper +all: ${SOLIB} ${LIB} conftest discotest evdevtest notifytest pndnotifyd rawpxmltest pndvalidator loggertest dbusnotifytest pnd_run pndevmapperd pnd_info evtest mmenu mmwrapper clean: - ${RM} -f ${ALLOBJ} ${XMLOBJ} ${LIB} ${SOLIB1} locatetest.o bin/locatetest conftest.o bin/conftest discotest.o bin/discotest dbusnotifytest.o bin/dbusnotifytest loggertest.o bin/loggertest bin/notifytest notifytest.o bin/rawpxmltest rawpxmltest.o bin/pnd_run pnd_run.o pnd_info.o bin/pnd_info bin/pndevmapperd pndevmapperd.o bin/pndnotifyd pndnotifyd.o ${SOLIB} testdata/dotdesktop/*.desktop testdata/menu/*.desktop testdata/apps/*.pnd testdata/dotdesktop/*.png deployment/usr/lib/libpnd* deployment/usr/bin/pndnotifyd deployment/usr/bin/pnd_run deployment/usr/bin/pnd_info deployment/usr/pandora/scripts/* deployment/etc/sudoers deployment/etc/init.d/pndnotifyd bin/pndvalidator pndvalidator.o deployment/usr/bin/pndevmapperd testdata/menuicons/* evtest.o bin/evtest bin/mmenu bin/mmwrapper mmenu.o mmwrapper.o deployment/usr/bin/mmenu deployment/usr/bin/mmwrapper mmcache.o mmui.o mmcat.o + ${RM} -f ${ALLOBJ} ${XMLOBJ} ${LIB} ${SOLIB1} locatetest.o bin/locatetest conftest.o bin/conftest discotest.o evdevtest.o bin/evdevtest bin/discotest dbusnotifytest.o bin/dbusnotifytest loggertest.o bin/loggertest bin/notifytest notifytest.o bin/rawpxmltest rawpxmltest.o bin/pnd_run pnd_run.o pnd_info.o bin/pnd_info bin/pndevmapperd pndevmapperd.o bin/pndnotifyd pndnotifyd.o ${SOLIB} testdata/dotdesktop/*.desktop testdata/menu/*.desktop testdata/apps/*.pnd testdata/dotdesktop/*.png deployment/usr/lib/libpnd* deployment/usr/bin/pndnotifyd deployment/usr/bin/pnd_run deployment/usr/bin/pnd_info deployment/usr/pandora/scripts/* deployment/etc/sudoers deployment/etc/init.d/pndnotifyd bin/pndvalidator pndvalidator.o deployment/usr/bin/pndevmapperd testdata/menuicons/* evtest.o bin/evtest bin/mmenu bin/mmwrapper mmenu.o mmwrapper.o deployment/usr/bin/mmenu deployment/usr/bin/mmwrapper mmcache.o mmui.o mmcat.o ${RM} -rf deployment/media deployment/etc/pandora/mmenu find . -name "*~*" -exec rm {} \; -print @@ -114,6 +114,9 @@ conftest: conftest.o ${LIB} discotest: discotest.o ${LIB} ${CC} -lstdc++ -o bin/discotest discotest.o libpnd.a +evdevtest: evdevtest.o ${LIB} + ${CC} -lstdc++ -o bin/evdevtest evdevtest.o -static libpnd.a + notifytest: notifytest.o ${LIB} ${CC} -lstdc++ -o bin/notifytest notifytest.o libpnd.a diff --git a/include/pnd_device.h b/include/pnd_device.h index d0b32f5..acd1298 100644 --- a/include/pnd_device.h +++ b/include/pnd_device.h @@ -31,6 +31,14 @@ extern "C" { #define PND_DEVICE_LED_BT "/sys/class/leds/pandora::bluetooth" #define PND_DEVICE_LED_SUFFIX_BRIGHTNESS "/brightness" +// device names +#define PND_EVDEV_NUB1 "vsense66" +#define PND_EVDEV_NUB2 "vsense67" +#define PND_EVDEV_KEYPAD "omap_twl4030keypad" +#define PND_EVDEV_GPIO "gpio-keys" +#define PND_EVDEV_TS "ADS784x Touchscreen" +#define PND_EVDEV_POWER "triton2-pwrbutton" + /* utility */ unsigned char pnd_device_open_write_close ( char *name, char *v ); diff --git a/include/pnd_io_evdev.h b/include/pnd_io_evdev.h new file mode 100644 index 0000000..3503a9d --- /dev/null +++ b/include/pnd_io_evdev.h @@ -0,0 +1,69 @@ + +#ifndef h_pnd_io_evdev_h +#define h_pnd_io_evdev_h + +// some handy routines for open/close/monitor of the evdev controller devices .. +// ie: so you can watch the analogs and d-pads pretty easily, for when you don't +// want to rely on SDL or whatever +// +// this technique works fine for nubs, d-pads, even keyboard and various buttons +// this API may only handle some parts for now, but you coudl easily duplicate some of the +// code to extend for adding keyboard. +// +// dpad, nubs, start/select/pandora, triggers all work fine as of Mar 2010. +// + +// can get analog nubs, d-pads, or even keyboard A-Z type keys here +// some special ones like Power are on other devices than you'd expect, but all good + +typedef enum { + pnd_evdev_dpads = 0, // for d-pad and d-pad-buttons + pnd_evdev_nub1, + pnd_evdev_nub2, + pnd_evdev_power, + pnd_evdev_max +} pnd_evdev_e; + +unsigned char pnd_evdev_open ( pnd_evdev_e device ); // returns 0 on error, >0 success +void pnd_evdev_close ( pnd_evdev_e device ); +void pnd_evdev_closeall ( void ); +int pnd_evdev_get_fd ( unsigned char handle ); // obtain actual fd from handle +int pnd_evdev_open_by_name ( char *devname ); // internal but handy; see device names in pnd_device.h + +typedef enum { + pnd_evdev_left = (1<<0), // these are bitmask; ex: (pnd_evdev_left | pnd_evdev_up) + pnd_evdev_right = 1<<1, + pnd_evdev_up = 1<<2, + pnd_evdev_down = 1<<3, + pnd_evdev_x = 1<<4, + pnd_evdev_y = 1<<5, + pnd_evdev_a = 1<<6, + pnd_evdev_b = 1<<7, + pnd_evdev_ltrigger = 1<<8, + pnd_evdev_rtrigger = 1<<9, + pnd_evdev_start = 1<<10, + pnd_evdev_select = 1<<11, + pnd_evdev_pandora = 1<<12 +} pnd_evdev_dpad_e; + +typedef struct { + int x; + int y; +} pnd_nubstate_t; + +// catchup() - catch up any pending events +// return 0 if something weird happened, like device error; in that case, closeall and reopen? +// return 1 if looks like state is all up to date now (ie: no more events) +unsigned char pnd_evdev_catchup ( unsigned char blockp ); // will do all open devices + +// fetch dpad state -- a mask of what buttons are pressed currently +// return -1 if device not open +int pnd_evdev_dpad_state ( pnd_evdev_e device ); // returns bitmask of pnd_evdev_dpad_e + +// try to obtain X/Y axis for the requested nub +// r_nubstate best not be null or the behaviour is undefined. (Well, it is defined .. *catch fire*) +// return 1 when state is copied over +// return -1 when device not opened +int pnd_evdev_nub_state ( pnd_evdev_e nubdevice, pnd_nubstate_t *r_nubstate ); + +#endif diff --git a/include/pnd_io_ioctl.h b/include/pnd_io_ioctl.h new file mode 100644 index 0000000..5ed7b1d --- /dev/null +++ b/include/pnd_io_ioctl.h @@ -0,0 +1,9 @@ +#ifndef h_pnd_io_ioctl_h +#define h_pnd_io_ioctl_h + +// this is a simple 'is key pressed?' type routine, using standard ioctl; if you don't want to +// monitor the events and just ewant to see whats up, this can be handy. +// returns -1 on error, 0 for 'not down', and >0 for 'is down' +int pnd_is_key_down ( int fd, int key ); + +#endif diff --git a/lib/pnd_io_evdev.c b/lib/pnd_io_evdev.c new file mode 100644 index 0000000..c57392e --- /dev/null +++ b/lib/pnd_io_evdev.c @@ -0,0 +1,300 @@ + +#include +#include +#include +#include +#include /* abs() */ +#include /* struct input_event */ +#include +#include +#include + +#include "pnd_device.h" +#include "pnd_io_evdev.h" + +typedef struct { + int fd; + // state info + union { + pnd_nubstate_t nub; + unsigned int buttons; + } state; +} pnd_evmap_t; +static pnd_evmap_t evmap [ pnd_evdev_max ]; +static unsigned char evmap_first = 1; +static pnd_evdev_e evmapback [ 10 ]; // map fd back to which device + +unsigned char pnd_evdev_open ( pnd_evdev_e device ) { + + // if first, reset global struct + if ( evmap_first ) { + evmap_first = 0; // don't repeat + unsigned char i; + for ( i = 0; i < pnd_evdev_max; i++ ) { + evmap [ i ].fd = -1; // not opened + } + } // if first + + // do it + switch ( device ) { + + case pnd_evdev_dpads: + if ( ( evmap [ pnd_evdev_dpads ].fd = pnd_evdev_open_by_name ( PND_EVDEV_GPIO ) ) >= 0 ) { + evmapback [ evmap [ pnd_evdev_dpads ].fd ] = pnd_evdev_dpads; + return ( 1 ); + } + break; + + case pnd_evdev_nub1: + if ( ( evmap [ pnd_evdev_nub1 ].fd = pnd_evdev_open_by_name ( PND_EVDEV_NUB1 ) ) >= 0 ) { + evmapback [ evmap [ pnd_evdev_nub1 ].fd ] = pnd_evdev_nub1; + return ( 1 ); + } + break; + + case pnd_evdev_nub2: + if ( ( evmap [ pnd_evdev_nub2 ].fd = pnd_evdev_open_by_name ( PND_EVDEV_NUB2 ) ) >= 0 ) { + evmapback [ evmap [ pnd_evdev_nub2 ].fd ] = pnd_evdev_nub2; + return ( 1 ); + } + break; + + case pnd_evdev_power: + if ( ( evmap [ pnd_evdev_power ].fd = pnd_evdev_open_by_name ( PND_EVDEV_POWER ) ) >= 0 ) { + evmapback [ evmap [ pnd_evdev_power ].fd ] = pnd_evdev_power; + return ( 1 ); + } + break; + + case pnd_evdev_max: + return ( 0 ); // shutup gcc + } // switch + + // error opening, or not supported in this bit of code, or wtf + return ( 0 ); +} + +void pnd_evdev_close ( pnd_evdev_e device ) { + // if open + if ( evmap [ device ].fd >= 0 ) { + close ( evmap [ device ].fd ); // close it + evmap [ device ].fd = -1; // flag as closed + } + return; +} + +void pnd_evdev_closeall ( void ) { + unsigned char i; + + for ( i = 0; i < pnd_evdev_max; i++ ) { + pnd_evdev_close ( i ); + } // for + + return; +} + +int pnd_evdev_get_fd ( unsigned char device ) { + return ( evmap [ device ].fd ); +} + +int pnd_evdev_open_by_name ( char *devname ) { + int fd; + char fname [ 64 ]; + char name[256] = { 0, }; + unsigned char id; + + // keep opening event# devices until we run out + for ( id = 0; ; id++ ) { + + // set temp filename + snprintf ( fname, sizeof ( fname ), "/dev/input/event%i", id ); + + // try open, or bail out if no more + if ( ( fd = open ( fname, O_RDONLY | O_NDELAY ) ) < 0 ) { + return ( -1 ); + } + + // fetch the devices name + if ( ioctl (fd, EVIOCGNAME(sizeof(name)), name ) < 0 ) { + close ( fd ); // couldn't get the name?! + continue; // pretty odd to not have a name, but maybe the next device does.. + } + + // are we done? + if ( strcmp ( name, devname ) == 0 ) { + return ( fd ); + } + + // next! + close ( fd ); + } + + return ( -1 ); // couldn't find it +} + +unsigned char pnd_evdev_catchup ( unsigned char blockp ) { + fd_set fdset; + int maxfd = -99; + int i; + + // if not blocking, we want a zero duration timeout + struct timeval tv; + struct timeval *ptv; + bzero ( &tv, sizeof(struct timeval) ); // set to 0s, ie: return immediately + + if ( blockp ) { + ptv = NULL; + } else { + ptv = &tv; + } + + // clear watch fd's + FD_ZERO ( &fdset ); + + // for all our open evdev's, flag them in the watch fd list + for ( i = 0; i < pnd_evdev_max; i++ ) { + if ( evmap [ i ].fd >= 0 ) { + // set the fd into the select set + FD_SET( evmap [ i ].fd, &fdset ); + // select(2) needs to know the highest fd + if ( evmap [ i ].fd > maxfd ) { + maxfd = evmap [ i ].fd; + } // if + } // if + } // for + + if ( maxfd < 0 ) { + return ( 0 ); // nothing to do + } + + int ret; + + ret = select ( maxfd + 1, &fdset, NULL, NULL, ptv ); + + if ( ret < 0 ) { + return ( 0 ); // something bad + } else if ( ret == 0 ) { + return ( 1 ); // all good, nothing here + } + + // all good, and something in the queue.. + for ( i = 0; i < pnd_evdev_max; i++ ) { + // if open.. + if ( evmap [ i ].fd >= 0 ) { + // if pending in the queue + if ( FD_ISSET ( evmap [ i ].fd, &fdset ) ) { + + int rd, fd; + int j; + struct input_event ev[64]; + + fd = evmap [ i ].fd; + + // pull events from the queue; max 64 events + rd = read ( fd, ev, sizeof(struct input_event) * 64 ); + if ( rd < (int) sizeof(struct input_event) ) { + // less than a whole event-struct? bad! + break; + } + + // for each event in the pulled events, parse it + for (j = 0; j < rd / sizeof(struct input_event); j++ ) { + + // SYN, ignore + if ( ev[j].type == EV_SYN ) { + continue; + + } else if ( ev[j].type == EV_KEY ) { + // KEY event -- including dpads + + // store val for key + // keycode: ev[j].code -> keycode, see linux/input.h + // state val: ev[j].value -> 0keyup, 1keydown + +#if 0 // as of mid-March; notaz did a recent keycode change so had to refigure it out + printf ( "evdev\tkey %d\tvalue %d\n", ev[j].code, ev[j].value ); + // a 102 b 107 x 109 y 104 + // ltrigger 54 rtrigger 97 + // start 56 select 29 pandora 139 +#endif + + unsigned int state = evmap [ pnd_evdev_dpads ].state.buttons; + + switch ( ev[j].code ) { + + case KEY_UP: state &= ~pnd_evdev_up; if ( ev[j].value ) state |= pnd_evdev_up; break; + case KEY_DOWN: state &= ~pnd_evdev_down; if ( ev[j].value ) state |= pnd_evdev_down; break; + case KEY_LEFT: state &= ~pnd_evdev_left; if ( ev[j].value ) state |= pnd_evdev_left; break; + case KEY_RIGHT: state &= ~pnd_evdev_right; if ( ev[j].value ) state |= pnd_evdev_right; break; + case KEY_PAGEDOWN: /*KEY_X*/ state &= ~pnd_evdev_x; if ( ev[j].value ) state |= pnd_evdev_x; break; + case KEY_PAGEUP: /*KEY_Y*/ state &= ~pnd_evdev_y; if ( ev[j].value ) state |= pnd_evdev_y; break; + case KEY_HOME: /*KEY_A*/ state &= ~pnd_evdev_a; if ( ev[j].value ) state |= pnd_evdev_a; break; + case KEY_END: /*KEY_B*/ state &= ~pnd_evdev_b; if ( ev[j].value ) state |= pnd_evdev_b; break; + + case KEY_RIGHTSHIFT: // ltrigger + state &= ~pnd_evdev_ltrigger; if ( ev[j].value ) state |= pnd_evdev_ltrigger; break; + case KEY_RIGHTCTRL: // rtrigger + state &= ~pnd_evdev_rtrigger; if ( ev[j].value ) state |= pnd_evdev_rtrigger; break; + + // start + // select + // pandora + case KEY_LEFTALT: state &= ~pnd_evdev_start; if ( ev[j].value ) state |= pnd_evdev_start; break; + case KEY_LEFTCTRL: state &= ~pnd_evdev_select; if ( ev[j].value ) state |= pnd_evdev_select; break; + case KEY_MENU: state &= ~pnd_evdev_pandora; if ( ev[j].value ) state |= pnd_evdev_pandora; break; + + } // switch + + evmap [ pnd_evdev_dpads ].state.buttons = state; + + } else if ( ev[j].type == EV_SW ) { + // SWITCH event + + } else if ( ev[j].type == EV_ABS ) { + // ABS .. ie: nub + + // vsense66 -> nub1 (left) + // vsense67 -> nub2 (right) + pnd_nubstate_t *pn = NULL; + if ( evmapback [ fd ] == pnd_evdev_nub1 ) { + pn = &( evmap [ pnd_evdev_nub1 ].state.nub ); + } else { + pn = &( evmap [ pnd_evdev_nub2 ].state.nub ); + } + + if ( ev[j].code == ABS_X ) { + pn -> x = ev[j].value; + } else if ( ev[j].code == ABS_Y ) { + pn -> y = ev[j].value; + } else { + //printf("unexpected EV_ABS code: %i\n", ev[i].code); + } + + } else { + // unknown? + continue; + } // event type? + + } // for each pulled event + + } // if pending + } // if open + } // for all device.. + + return ( 1 ); +} + +int pnd_evdev_dpad_state ( pnd_evdev_e device ) { + if ( evmap [ device ].fd < 0 ) { + return ( -1 ); + } + return ( evmap [ device ].state.buttons ); +} + +int pnd_evdev_nub_state ( pnd_evdev_e nubdevice, pnd_nubstate_t *r_nubstate ) { + if ( evmap [ nubdevice ].fd < 0 ) { + return ( -1 ); + } + memcpy ( r_nubstate, &(evmap [ nubdevice ].state.nub), sizeof(pnd_nubstate_t) ); + return ( 1 ); +} diff --git a/lib/pnd_io_gpio.c b/lib/pnd_io_gpio.c index 23105e5..d36c2ab 100755 --- a/lib/pnd_io_gpio.c +++ b/lib/pnd_io_gpio.c @@ -1,4 +1,6 @@ +// this is pulled from cpasjuste and/or pickle + #if defined (_PANDORA) || !defined (EMULATOR) /* cribbed from pnd_keytypes.h so as to make it unnecessary */ diff --git a/lib/pnd_io_ioctl.c b/lib/pnd_io_ioctl.c new file mode 100644 index 0000000..c501842 --- /dev/null +++ b/lib/pnd_io_ioctl.c @@ -0,0 +1,21 @@ + +#include +#include +#include +#include "pnd_io_ioctl.h" + +int pnd_is_key_down ( int fd, int key ) { + unsigned int size = KEY_MAX / 8 + 1; + unsigned char buf [ size ]; + bzero ( buf, size ); + + if ( ioctl ( fd, EVIOCGKEY(size), buf ) < 0 ) { + return ( -1 ); // error + } + + if ( buf [ key / 8 ] & ( 1<<(key%8) ) ) { + return ( 1 ); // down + } + + return ( 0 ); // not down +} diff --git a/test/evdevtest.c b/test/evdevtest.c new file mode 100644 index 0000000..1772679 --- /dev/null +++ b/test/evdevtest.c @@ -0,0 +1,75 @@ + +#include +#include +#include + +#include "pnd_device.h" +#include "pnd_io_evdev.h" + +int main ( void ) { + + if ( ! pnd_evdev_open ( pnd_evdev_dpads ) ) { + printf ( "Couldn't open dpads\n" ); + exit ( 0 ); + } + + if ( ! pnd_evdev_open ( pnd_evdev_nub1 ) ) { + printf ( "Couldn't open nub1\n" ); + exit ( 0 ); + } + + if ( ! pnd_evdev_open ( pnd_evdev_nub2 ) ) { + printf ( "Couldn't open nub2\n" ); + exit ( 0 ); + } + + while ( 1 ) { + + // poll and process + if ( ! pnd_evdev_catchup ( 1 /* block */ ) ) { + printf ( "Couldn't catch up events\n" ); + exit ( 0 ); + } + + // check state + int s; + + s = pnd_evdev_dpad_state ( pnd_evdev_dpads ); + + if ( s & pnd_evdev_up ) { printf ( "d-pad up\n" ); } + if ( s & pnd_evdev_down ) { printf ( "d-pad down\n" ); } + if ( s & pnd_evdev_left ) { printf ( "d-pad left\n" ); } + if ( s & pnd_evdev_right ) { printf ( "d-pad right\n" ); } + + if ( s & pnd_evdev_x ) { printf ( "d-pad x\n" ); } + if ( s & pnd_evdev_y ) { printf ( "d-pad y\n" ); } + if ( s & pnd_evdev_a ) { printf ( "d-pad a\n" ); } + if ( s & pnd_evdev_b ) { printf ( "d-pad b\n" ); } + if ( s & pnd_evdev_ltrigger ) { printf ( "d-pad ltrigger\n" ); } + if ( s & pnd_evdev_rtrigger ) { printf ( "d-pad rtrigger\n" ); } + + if ( s & pnd_evdev_start ) { printf ( "d-pad start\n" ); } + if ( s & pnd_evdev_select ) { printf ( "d-pad select\n" ); } + if ( s & pnd_evdev_pandora ) { printf ( "d-pad pandora\n" ); } + + pnd_nubstate_t ns; + + pnd_evdev_nub_state ( pnd_evdev_nub1, &ns ); + if ( ns.x || ns.y ) { + printf ( "ns1 x / y: %d / %d\n", ns.x, ns.y ); + } + + pnd_evdev_nub_state ( pnd_evdev_nub2, &ns ); + if ( ns.x || ns.y ) { + printf ( "ns2 x / y: %d / %d\n", ns.x, ns.y ); + } + + // sleep + //usleep ( 500000 ); + + } // while + + pnd_evdev_closeall(); + + return ( 0 ); +}