00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #include "linux-joystick.h"
00036
00037 #ifdef WIN32
00038 #error "Can't compile linux-joystick module under Windows"
00039 #endif // WIN32
00040
00041 #include <linux/joystick.h>
00042 #include <sstream>
00043 #include <iostream>
00044
00045 #include "perf/perf.h"
00046 #include "util/file.h"
00047 #include "wave-nix/wave-nix.h"
00048
00049
00050 namespace gamepad {
00051
00052
00053
00054 #define s_devicePath "/dev/input/"
00055 #define s_devicePrefix "js"
00056
00057 const int s_maxName = 80;
00058
00059 static const char * s_prefix = "LinuxJoystick";
00060
00061 static SetString s_devicePaths;
00062
00063
00064
00065
00066
00067
00068
00069
00070 static bool
00071 test_bit
00072 (
00073 IN int bit,
00074 IN const byte_t * array
00075 )
00076 {
00077 ASSERT(bit >= 0, "Bad bit: %d", bit);
00078 ASSERT(array, "null");
00079
00080 int offset = bit / 8;
00081 byte_t b = array[offset];
00082 byte_t mask = 1 << (bit % 8);
00083 return b & mask;
00084 }
00085
00086
00087
00088 #define DUMP_EVBIT(name, desc) { \
00089 if (test_bit(EV_ ##name , pb)) { DPRINTF(" %s", desc); } \
00090 }
00091
00092 static void
00093 dumpCapabilities
00094 (
00095 IN const byte_t * pb
00096 )
00097 throw()
00098 {
00099 ASSERT(pb, "null");
00100
00101 DPRINTF("Device capabilities:");
00102 DUMP_EVBIT(SYN, "Synchronizes");
00103 DUMP_EVBIT(KEY, "Buttons/keys");
00104 DUMP_EVBIT(REL, "Relative input");
00105 DUMP_EVBIT(ABS, "Absolute input");
00106 DUMP_EVBIT(MSC, "Miscellaneous");
00107 DUMP_EVBIT(SW, "Switch");
00108 DUMP_EVBIT(LED, "LEDs");
00109 DUMP_EVBIT(SND, "Sound");
00110 DUMP_EVBIT(REP, "Repeat events");
00111 DUMP_EVBIT(FF, "Force Feedback");
00112 DUMP_EVBIT(PWR, "Power management");
00113 DUMP_EVBIT(FF_STATUS, "Force feedback status");
00114 }
00115
00116
00117
00118 static int
00119 getIndex
00120 (
00121 IN int value,
00122 IN const int * map,
00123 IN int size
00124 )
00125 {
00126 THROW(value >= 0, "Bad input index value: " << value);
00127 ASSERT(map, "null");
00128 ASSERT(size > 0, "Bad map size: %d", size);
00129
00130 for (int i = 0; i < size; ++i) {
00131 if (value == map[i])
00132 return i;
00133 }
00134
00135
00136
00137 DPRINTF("WARNING: input index not found in map? %d", value);
00138
00139 return -1;
00140 }
00141
00142
00143
00144 static inline void
00145 diagnoseError
00146 (
00147 void
00148 )
00149 {
00150 dumpErrorInfo(std::cerr, errno);
00151 }
00152
00153
00154
00155 static bool
00156 isJoystickEntry
00157 (
00158 IN smart_fd& fd
00159 )
00160 {
00161 ASSERT(fd, "invalid");
00162
00163
00164 dword_t version;
00165 if (ioctl(fd, JSIOCGVERSION, &version)) {
00166 diagnoseError();
00167 DPRINTF(" ...unable to retrieve joystick version");
00168 return false;
00169 }
00170 DPRINTF("Version = 0x%08x", version);
00171 int16_t majorV = version >> 16;
00172 byte_t minorV = (version >> 8) & 0xff;
00173 byte_t patchV = version & 0xff;
00174 DPRINTF(" major:%d minor:%d patch:%d", majorV, minorV, patchV);
00175
00176
00177 char name[s_maxName];
00178 int nchars = ioctl(fd, JSIOCGNAME(s_maxName), &name);
00179 if (nchars < 1 || nchars > s_maxName) {
00180 diagnoseError();
00181 DPRINTF(" ...failed to get joystick name");
00182 return false;
00183 }
00184 DPRINTF("Joystick name: '%s'", name);
00185
00186
00187 static const int s_minAxes = 2;
00188 int numAxes = 0;
00189 if (ioctl(fd, JSIOCGAXES, &numAxes)) {
00190 diagnoseError();
00191 DPRINTF(" ...failed to retrieve axis count");
00192 return false;
00193 }
00194 DPRINTF("Number of axes: %d", numAxes);
00195 if (numAxes < s_minAxes) {
00196 DPRINTF(" ...not enough axes (want %d, only found %d)",
00197 s_minAxes, numAxes);
00198 return false;
00199 }
00200
00201
00202 int numButtons = 0;
00203 if (ioctl(fd, JSIOCGBUTTONS, &numButtons)) {
00204 diagnoseError();
00205 DPRINTF(" ...failed to retrieve button count");
00206 return false;
00207 }
00208 DPRINTF("Number of buttons: %d", numButtons);
00209 if (numButtons < 1) {
00210 DPRINTF(" ...does not contain any buttons?");
00211 return false;
00212 }
00213
00214
00215 return true;
00216 }
00217
00218
00219
00220 const char *
00221 getJoystickIndex
00222 (
00223 IN const char * path
00224 )
00225 {
00226 ASSERT(path, "null");
00227
00228 const char * key = s_devicePath s_devicePrefix;
00229
00230 const char * p = strstr(path, key);
00231 THROW(p, "Bad joystick device path: " << path);
00232
00233 p += strlen(key);
00234
00235 DPRINTF("path: '%s' --> index '%s'", path, p);
00236 return p;
00237 }
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248 class LJDevice : public SourceDevice {
00249 public:
00250
00251 LJDevice(void) throw();
00252 ~LJDevice(void) throw();
00253
00254
00255 void initialize(IN const char * path);
00256
00257
00258 const char * getPublicName(void) throw() { return m_name.c_str(); }
00259 const char * getUniqueId(void) throw() { return m_uid.c_str(); }
00260 eDeviceState getState(void) throw();
00261 int getNumPots(void) throw() { return m_nPots; }
00262 int getNumButtons(void) throw() { return m_nButtons; }
00263 void poll(void) throw();
00264 const pot_value_t& getPotValue(IN int potIndex);
00265 eButton getButtonValue(IN int buttonIndex);
00266
00267 private:
00268
00269
00270 std::string m_path;
00271 std::string m_name;
00272 std::string m_uid;
00273 smart_fd m_fd;
00274 byte_t m_eventTypes[(EV_MAX + 7) / 8];
00275 int m_nPots;
00276 int m_nButtons;
00277 std::vector<byte_t> m_button;
00278 std::vector<pot_value_t> m_pot;
00279 };
00280
00281
00282 LJDevice::LJDevice(void)
00283 throw()
00284 {
00285 m_nPots = 0;
00286 m_nButtons = 0;
00287 }
00288
00289
00290 LJDevice::~LJDevice(void)
00291 throw()
00292 {
00293 }
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303 void
00304 LJDevice::initialize
00305 (
00306 IN const char * path
00307 )
00308 {
00309 perf::Timer timer("LinuxInputDevice::initialize()");
00310 ASSERT(path, "null");
00311
00312 DPRINTF("Opening linux joystick source device: '%s'", path);
00313
00314
00315 m_path = path;
00316
00317
00318 THROW(m_fd.open(path, O_RDONLY),
00319 "Failed to open linux joystick device: " << path);
00320
00321
00322 char buffer[s_maxName];
00323 int nchars = ioctl(m_fd, JSIOCGNAME(s_maxName), &buffer);
00324 THROW(nchars > 0 || nchars <= s_maxName,
00325 "Failed to acquire linux joystick device name");
00326 m_name = buffer;
00327 DPRINTF(" public name: '%s'", m_name.c_str());
00328
00329
00330 std::ostringstream out;
00331 out << s_prefix << ",";
00332 out << m_name << ",";
00333 out << "idx:" << getJoystickIndex(path);
00334
00335 m_uid = out.str();
00336 DPRINTF(" unique ID: '%s'", m_uid.c_str());
00337
00338
00339 if (ioctl(m_fd, JSIOCGBUTTONS, &m_nButtons)) {
00340 diagnoseError();
00341 THROW(false, "Failed to acquire button count");
00342 }
00343 DPRINTF("Found %d buttons on device", m_nButtons);
00344 m_button.resize(m_nButtons, eButtonUp);
00345
00346
00347 if (ioctl(m_fd, JSIOCGAXES, &m_nPots)) {
00348 diagnoseError();
00349 THROW(false, "Failed to acquire axis count");
00350 }
00351 DPRINTF("Found %d potentiometers on device", m_nPots);
00352 m_pot.resize(m_nPots);
00353
00354
00355 THROW(!fcntl(m_fd, F_SETFL, O_NONBLOCK),
00356 "Failed to set linux joystick device to nonblocking i/o");
00357 }
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367 eDeviceState
00368 LJDevice::getState
00369 (
00370 void
00371 )
00372 throw()
00373 {
00374 if (s_devicePaths.end() == s_devicePaths.find(m_path.c_str())) {
00375
00376 return eDevice_Detached;
00377 }
00378
00379
00380 return eDevice_Attached;
00381 }
00382
00383
00384
00385 void
00386 LJDevice::poll
00387 (
00388 void
00389 )
00390 throw()
00391 {
00392 ASSERT(m_fd, "need to open device first");
00393
00394
00395 for (int i = 0; i < m_nButtons; ++i) {
00396 m_button[i] &= ~eButtonWasPushed;
00397 m_button[i] &= ~eButtonWasReleased;
00398 }
00399
00400
00401 struct js_event event;
00402 while (true) {
00403 int len = read(m_fd, &event, sizeof(event));
00404 if (!len) {
00405 break;
00406 }
00407 if (len < 0) {
00408
00409 if (EAGAIN == errno)
00410 break;
00411
00412
00413 diagnoseError();
00414 DPRINTF("Failed to read from joystick!");
00415 break;
00416 }
00417
00418 int idx = event.number;
00419 switch (event.type) {
00420 case JS_EVENT_INIT | JS_EVENT_AXIS:
00421 case JS_EVENT_AXIS:
00422 if (idx < 0 || idx >= m_nPots)
00423 break;
00424 m_pot[idx].update(event.value);
00425 break;
00426
00427 case JS_EVENT_INIT | JS_EVENT_BUTTON:
00428 case JS_EVENT_BUTTON:
00429 if (idx < 0 || idx >= m_nButtons)
00430 break;
00431 m_button[idx] =
00432 updateButton(m_button[idx], event.value);
00433 break;
00434
00435 default:
00436 DPRINTF("Unrecognized joystick event: %d", event.type);
00437 }
00438 }
00439 }
00440
00441
00442
00443 const pot_value_t&
00444 LJDevice::getPotValue
00445 (
00446 IN int potIndex
00447 )
00448 {
00449 THROW(potIndex >= 0 && potIndex < m_nPots,
00450 "Bad pot index: " << potIndex);
00451 return m_pot[potIndex];
00452 }
00453
00454
00455
00456 eButton
00457 LJDevice::getButtonValue
00458 (
00459 IN int buttonIndex
00460 )
00461 {
00462 THROW(buttonIndex >= 0 && buttonIndex < m_nButtons,
00463 "Bad button index: " << buttonIndex);
00464 return (eButton) m_button[buttonIndex];
00465 }
00466
00467
00468
00469 static void
00470 refreshLinuxJoystickDevicePaths
00471 (
00472 void
00473 )
00474 {
00475 perf::Timer timer("refreshLinuxJoystickDevicePaths");
00476
00477
00478 smart_dir dir;
00479 dir.open(s_devicePath);
00480 ASSERT(dir, "should have a valid open directory now");
00481
00482
00483 int prefixLength = strlen(s_devicePrefix);
00484 SetString newSet;
00485 struct dirent entry;
00486 while (dir.getNextEntry(entry)) {
00487
00488 if (strncmp(s_devicePrefix, entry.d_name, prefixLength)) {
00489 continue;
00490 }
00491
00492 std::string path;
00493 appendPath(s_devicePath, entry.d_name, path);
00494
00495
00496 if (s_devicePaths.end() != s_devicePaths.find(path.c_str())) {
00497
00498 newSet.insert(path);
00499 continue;
00500 }
00501
00502
00503 smart_fd fd;
00504 if (fd.open(path.c_str(), O_RDONLY) && isJoystickEntry(fd)) {
00505 newSet.insert(path);
00506 }
00507 }
00508
00509
00510 s_devicePaths = newSet;
00511 }
00512
00513
00514
00515 smart_ptr<SourceDevice>
00516 getLinuxJoystickSourceDevice
00517 (
00518 IN const char * path
00519 )
00520 {
00521 ASSERT(path, "null");
00522
00523
00524 smart_ptr<LJDevice> local = new LJDevice;
00525 ASSERT(local, "out of memory");
00526 local->initialize(path);
00527
00528
00529 return local;
00530 }
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541 class Factory : public SourceDeviceFactory {
00542 public:
00543
00544 ~Factory(void) throw() { }
00545
00546
00547 void initialize(void) { }
00548
00549
00550 const char * getName(void) const throw()
00551 { return "Linux Joystick Factory"; }
00552 int getCount(void);
00553 smart_ptr<SourceDevice> getSourceDevice(IN int index);
00554
00555 private:
00556
00557 typedef std::map<std::string, smart_ptr<SourceDevice> > device_map_t;
00558
00559
00560 device_map_t m_devices;
00561 };
00562
00563
00564
00565 int
00566 Factory::getCount
00567 (
00568 void
00569 )
00570 {
00571
00572 refreshLinuxJoystickDevicePaths();
00573
00574
00575 VecString badPaths;
00576 for (device_map_t::iterator i = m_devices.begin(); i != m_devices.end();
00577 ++i) {
00578 const char * path = i->first.c_str();
00579 if (s_devicePaths.end() == s_devicePaths.find(path)) {
00580
00581 badPaths.push_back(path);
00582 }
00583 }
00584 for (VecString::iterator i = badPaths.begin(); i != badPaths.end();
00585 ++i) {
00586 device_map_t::iterator iBad = m_devices.find(*i);
00587 ASSERT(m_devices.end() != iBad, "Just saw this in map!");
00588 m_devices.erase(iBad);
00589 }
00590 ASSERT(m_devices.size() <= s_devicePaths.size(),
00591 "Should have devices for a subset of paths!");
00592
00593
00594 return s_devicePaths.size();
00595 }
00596
00597
00598
00599 smart_ptr<SourceDevice>
00600 Factory::getSourceDevice
00601 (
00602 IN int index
00603 )
00604 {
00605
00606 const char * path = NULL;
00607 for (SetString::const_iterator i = s_devicePaths.begin();
00608 i != s_devicePaths.end(); ++i, --index) {
00609 if (!index) {
00610 path = i->c_str();
00611 break;
00612 }
00613 }
00614 if (!path) {
00615 DPRINTF("Invalid source device index: %d", index);
00616 return NULL;
00617 }
00618
00619
00620 device_map_t::iterator i = m_devices.find(path);
00621 if (m_devices.end() != i) {
00622 return i->second;
00623 }
00624
00625
00626 smart_ptr<SourceDevice> device =
00627 getLinuxJoystickSourceDevice(path);
00628 ASSERT(device, "failed to create linux input source device");
00629 m_devices[path] = device;
00630 return device;
00631 }
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641 smart_ptr<SourceDeviceFactory>
00642 getLinuxJoystickSourceDeviceFactory
00643 (
00644 void
00645 )
00646 {
00647 smart_ptr<Factory> local = new Factory;
00648 ASSERT(local, "out of memory");
00649
00650 local->initialize();
00651
00652 return local;
00653 }
00654
00655
00656
00657 };
00658