events.cpp

Go to the documentation of this file.
00001 /*
00002  * events.cpp
00003  *
00004  * Copyright (C) 2009  Thomas A. Vaughan
00005  * All rights reserved.
00006  *
00007  * Uses the event devices to read/write joysticks (/dev/input/eventX)
00008  */
00009 
00010 // includes --------------------------------------------------------------------
00011 #include <linux/input.h>
00012 #include <iostream>
00013 #include <time.h>
00014 
00015 #include "common/common.h"
00016 #include "perf/perf.h"
00017 #include "util/file.h"
00018 
00019 
00020 ////////////////////////////////////////////////////////////////////////////////
00021 //
00022 //      static helper methods
00023 //
00024 ////////////////////////////////////////////////////////////////////////////////
00025 
00026 
00027 #define CASE( XYZ ) if (test_bit( XYZ , eventTypes)) { DPRINTF("%s ", #XYZ ); }
00028 
00029 
00030 static bool
00031 test_bit
00032 (
00033 IN int bit,
00034 IN const byte_t * array
00035 )
00036 throw()
00037 {
00038         ASSERT(bit >= 0, "bad bit: %d", bit);
00039         ASSERT(array, "null");
00040 
00041         int offset = bit / 8;
00042         const byte_t * p = array + offset;
00043         int mask = 1 << (bit % 8);
00044         return !!(*p & mask);
00045 }
00046 
00047 
00048 
00049 static int
00050 getIndex
00051 (
00052 IN int code,
00053 IN const int * array
00054 )
00055 {
00056         ASSERT(code >= 0, "bad code: %d", code);
00057         ASSERT(array, "null");
00058 
00059         int i = 0;
00060         while (true) {
00061                 if (*array == code)
00062                         return i;
00063                 ++i;
00064                 ++array;
00065         }
00066 }
00067 
00068 
00069 
00070 static void
00071 doRumble
00072 (
00073 IN smart_fd& fd,
00074 IN int value,
00075 IN int big
00076 )
00077 {
00078         static int eid = -1;
00079         if (value) {
00080                 // turn on rumble...
00081                 if (-1 != eid)
00082                         return; // already rumbling!
00083 
00084                 // upload and turn on rumble!
00085                 ff_effect e;
00086                 e.type = FF_RUMBLE;
00087                 e.id = -1;
00088                 uint16_t strength = 0xe000;
00089                 e.u.rumble.strong_magnitude = (big) ? strength : 0;
00090                 e.u.rumble.weak_magnitude = (!big) ? strength : 0;
00091                 e.replay.length = 2000;
00092                 e.replay.delay = 0;
00093 
00094                 if (ioctl(fd, EVIOCSFF, &e) < 0) {
00095                         DPRINTF("Failed to upload rumble");
00096                         return;
00097                 }
00098                 eid = e.id;
00099 
00100                 input_event play;
00101                 play.type = EV_FF;
00102                 play.code = eid;
00103                 play.value = 1;
00104                 if (write(fd, (const void *) &play, sizeof(play)) <= 0) {
00105                         DPRINTF("Failed to play rumble");
00106                         return;
00107                 }
00108         } else {
00109                 // disable rumbling...
00110                 if (-1 == eid)
00111                         return;         // not rumbling right now
00112 
00113                 // turn off and delete rumble
00114                 input_event stop;
00115                 stop.type = EV_FF;
00116                 stop.code = eid;
00117                 stop.value = 0;
00118                 if (write(fd, (const void *) &stop, sizeof(stop)) <= 0) {
00119                         DPRINTF("Failed to stop rumble");
00120                         return;
00121                 }
00122 
00123                 if (ioctl(fd, EVIOCRMFF, eid) < 0) {
00124                         DPRINTF("Failed to remove rumble");
00125                         return;
00126                 }
00127                 eid = -1;
00128         }
00129 }
00130 
00131  
00132 
00133 static void
00134 doTest
00135 (
00136 IN const char * path
00137 )
00138 {
00139         THROW(path, "null device path");
00140 
00141         DPRINTF("path: %s", path);
00142         smart_fd fd;
00143         THROW(fd.open(path, O_RDWR), "Failed to open device path: " << path);
00144 
00145         // get the name of the device
00146         const int bufsize = 1024;
00147         char devname[bufsize];
00148         THROW(ioctl(fd, EVIOCGNAME(bufsize), devname) >= 0,
00149             "Failed to acquire device name");
00150         DPRINTF("Device name: '%s'", devname);
00151 
00152         char uniq[bufsize];
00153         if (ioctl(fd, EVIOCGUNIQ(bufsize), uniq) < 0) {
00154                 DPRINTF("Failed to acquire unique device ID");
00155         } else {
00156                 DPRINTF("Unique ID: '%s'", uniq);
00157         }
00158 
00159         // get the device info
00160         struct input_id iid;
00161         THROW(ioctl(fd, EVIOCGID, &iid) >= 0,
00162             "Failed to acquire device id");
00163         DPRINTF("Bus type: %08x = %d", iid.bustype, iid.bustype);
00164         DPRINTF("Vendor:   %08x = %d", iid.vendor, iid.vendor);
00165         DPRINTF("Product:  %08x = %d", iid.product, iid.product);
00166         DPRINTF("Version:  %08x = %d", iid.version, iid.version);
00167 
00168         // query force-feedback capabilities
00169         byte_t features[(FF_MAX + 7) / 8];
00170         THROW(ioctl(fd, EVIOCGBIT(EV_FF, sizeof(features)), features) >= 0,
00171             "Failed to query for force-feedback capabilities");
00172 
00173         printf("\nList of force feedback effects: ");
00174         if (test_bit(FF_RUMBLE, features))   printf("Rumble ");
00175         if (test_bit(FF_PERIODIC, features)) printf("Periodic ");
00176         if (test_bit(FF_CONSTANT, features)) printf("Constant ");
00177         if (test_bit(FF_SPRING, features))   printf("Spring ");
00178         if (test_bit(FF_FRICTION, features)) printf("Friction ");
00179 //      if (test_bit(FF_DAMPER, features))   printf("Damper ");
00180 //      if (test_bit(FF_INERTIA, features))  printf("Inertia ");
00181 //      if (test_bit(FF_RAMP, features))     printf("Ramp ");
00182         printf("\n");
00183 
00184         int n_effects; /* Number of effects the device can play at the same time */
00185         THROW(ioctl(fd, EVIOCGEFFECTS, &n_effects) >= 0,
00186             "Failed to query number of simultaneous force-feedback effects");
00187         DPRINTF("Can play %d simultaneous force-feedback effects", n_effects);
00188 
00189         // determine capabilities
00190         byte_t eventTypes[(EV_MAX + 7) / 8];
00191         THROW(ioctl(fd, EVIOCGBIT(0, sizeof(eventTypes)), eventTypes) >= 0,
00192             "Failed to retrieve event types");
00193 
00194         // list events
00195         printf("Events: ");
00196         CASE(EV_SYN)
00197         CASE(EV_KEY)
00198         CASE(EV_REL)
00199         CASE(EV_ABS)
00200         CASE(EV_MSC)
00201         CASE(EV_SW)
00202         CASE(EV_LED)
00203         CASE(EV_SND)
00204         CASE(EV_REP)
00205         CASE(EV_FF)
00206         CASE(EV_PWR)
00207         CASE(EV_FF_STATUS)
00208 
00209         // first, look up buttons
00210         const int nMax = 128;
00211         THROW(test_bit(EV_KEY, eventTypes),
00212             "Device reports no buttons/keys?");
00213         int nButtons = 0;
00214         byte_t keyStats[(KEY_MAX + 7) / 8];
00215         THROW(ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyStats)), keyStats) >= 0,
00216             "Failed to retrieve button types");
00217         printf("Buttons:");
00218         int btnIndex[nMax];
00219         for (int i = 0; i < KEY_MAX; ++i) {
00220                 if (test_bit(i, keyStats)) {
00221                         printf(" %d", i);
00222                         btnIndex[nButtons] = i;
00223                         nButtons++;
00224                 }
00225         }
00226         printf("\n");
00227         DPRINTF("Found %d buttons total", nButtons);
00228 
00229         // look up absolute axes
00230         THROW(test_bit(EV_ABS, eventTypes),
00231             "Device reports no absolute axes?");
00232         int nAxes = 0;
00233         byte_t axisStats[(ABS_MAX + 7) / 8];
00234         THROW(ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(axisStats)), axisStats) >= 0,
00235             "Failed to retrieve absolute axis types");
00236         printf("Axes:");
00237         int axisIndex[nMax];
00238         for (int i = 0; i < ABS_MAX; ++i) {
00239                 if (test_bit(i, axisStats)) {
00240                         printf(" %d", i);
00241                         axisIndex[nAxes] = i;
00242                         nAxes++;
00243                 }
00244         }
00245         printf("\n");
00246         DPRINTF("Found %d axes total", nAxes);
00247 
00248         // set to non-blocking now
00249         THROW(!fcntl(fd, F_SETFL, O_NONBLOCK),
00250             "Failed to set device path as nonblocking");
00251 
00252         // keep polling
00253         const int desiredHz = 100;
00254         int msSleep = 1000 / desiredHz;
00255         DPRINTF("Want to run at %d Hz", desiredHz);
00256         DPRINTF("  That means polling every %d milliseconds", msSleep);
00257         long nsSleep = 1000 * 1000 * msSleep;
00258         DPRINTF("  Which is %ld nanoseconds", nsSleep);
00259         struct timespec ts;
00260         ts.tv_sec = 0;
00261         ts.tv_nsec = nsSleep;
00262 
00263         // keep state here
00264         THROW(nAxes <= nMax, "Too many axes!");
00265         THROW(nButtons <= nMax, "Too many buttons!");
00266         short axes[nMax];
00267         byte_t buttons[nMax];
00268         memset(axes, 0, sizeof(axes));
00269         memset(buttons, 0, sizeof(buttons));
00270 
00271         // loop
00272         printf("\n");
00273         for (;;) {
00274                 nanosleep(&ts, NULL);
00275 
00276                 // poll events
00277                 int nEvents = 0;
00278                 struct input_event events[32];
00279                 while (true) {
00280                         int len = read(fd, events, sizeof(events));
00281                         if (len <= 0)
00282                                 break;          // end of events
00283 
00284                         // parse events
00285                         nEvents = len / sizeof(input_event);
00286                         for (int i = 0; i < nEvents; ++i) {
00287                                 int idx;
00288                                 switch(events[i].type) {
00289                                 case EV_KEY:
00290                                         idx = getIndex(events[i].code, btnIndex);
00291                                 //      DPRINTF("%d --> index %d", events[i].code, idx);
00292                                 //      DPRINTF("button %d: %d", idx, events[i].value);
00293                                         buttons[idx] = events[i].value;
00294                                         if (idx < 2) {
00295                                                 // button zero or one!  toggle rumble
00296                                                 doRumble(fd, events[i].value, idx);
00297                                         }
00298                                         break;
00299 
00300                                 case EV_ABS:
00301                                         idx = getIndex(events[i].code, axisIndex);
00302                                         axes[idx] = events[i].value;
00303                                         break;
00304                                 }
00305                         }
00306                 }
00307 
00308                 // write out state
00309                 printf("\r");
00310                 printf("%02d", nEvents);
00311                 for (int i = 0; i < nAxes; ++i) {
00312                         printf(" %6d", axes[i]);
00313                 }
00314                 printf(":");
00315                 for (int i = 0; i < nButtons; ++i) {
00316                         if (buttons[i]) {
00317                                 printf(" b%02d", i);
00318                 //              buttons[i] = 0;
00319                         }
00320                 }
00321                 printf("            ");
00322         }
00323 }
00324 
00325 
00326 
00327 ////////////////////////////////////////////////////////////////////////////////
00328 //
00329 //      entry point
00330 //
00331 ////////////////////////////////////////////////////////////////////////////////
00332 
00333 int
00334 main
00335 (
00336 IN int argc,
00337 IN const char * argv[]
00338 )
00339 {
00340         int retval = 0;
00341         try {
00342                 perf::Timer timer("overall timer");
00343 
00344                 THROW(2 == argc, "Usage: linux-joy-events <event-device-path>"
00345                     << "\nAn example path is /dev/input/event8");
00346                 const char * path = argv[1];
00347 
00348                 doTest(path);
00349 
00350         } catch (std::exception& e) {
00351                 DPRINTF("EXCEPTION: %s", e.what());
00352                 retval = 1;
00353         }
00354 
00355         perf::dumpTimingSummary(std::cerr);
00356 
00357         return retval;
00358 }
00359