Jeff Griffin

Quart: OpenELEC branch commit breakdown

Posted on March 15, 2013

I've been putting off dealing with the documentation of all the snags I've run into thus far and decided to just take a look at the commit log and rationalize each one.  Some of these are fairly specific to my situation, while others may have broader implications.

Isolating and using SDL input

Once the gamecon module was added to the build, this was the first and largest hurdle to jump in getting gamepad input in OpenELEC for Raspberry Pi. Here's the situation:

the gamepad device is simply not utilized, or even logged as enabled, though running evtest shows that it's blocked. The first place you might look for a solution, understanding that you're using Linux window events, is LinuxInputDevices.cpp. Scrolling down and seeing the Linux input device type enumeration confirms the supposition that you are on the right track.

Your supposition, as it turns out, is bad and you should feel bad because the GetInfo method is going to be called to determine what type your little device falls into, and it will do this:

...since the button counting loop stopped at BTN_JOYSTICK (this is the index at witch joystick/gamepad buttons start), num_buttons is 0 and your gamepad is left as LI_DEVICE_NONE. Unfortunately the solution is not just to bump the index limit up on the loop, because there is no codepath in the application loop for CLinuxInputDevice into CButtonTranslator's TranslateJoystickString method.

There are only two paths to TranslateJoystickString in CApplication. One is the event server, the other uses SDL.  The latter seemed an easier, cleaner option to me.  Theoretically, it would just mean the inclusion of libSDL in the build.

This is from the graphic configuration script in the OpenELEC build system.

We start to see how a problem like this could happen, because right now it's relegated to users running XBMC in Linux without X, who require joystick support sans event server.  One could make an argument of course that you can use SDL input without the rest, which is what I did.

The solution was mostly just changing a few variables. I commented out the lines above and changed some defines in system.h.  The only real functional issue was that after XBMC's CLinuxInputDevice assigns LI_DEVICE_NONE to the gamepad devices it continues to hold onto block file descriptors, so when SDL uses evdev to grab the device, it's blocked as well.  This is addressed in SDL_input_only.patch, after the call to GetInfo.

"Sticky" buttons in SDL

This was absolutely infuriating, and took a good bit of GDB to figure out.  The failure mode is that a button will behave as though it is being held down, often after advancing a menu, so depending on the next focused item on each screen you may be in for a fun guided tour through the UI.  The offender this time was the SDL joystick class. As it turns out, evdev drivers can miss an event.  I get a sense that it happens when the system is taxed, so the situation is exacerbated on RPi, but certainly not unique to it.  When an event is missed evdev transmits a SYN_DROPPED event, at which point it can be polled to correct internal buffers.  Unfortunately there is no concept of SYN_DROPPED as of SDL 1.2.15.  Fortunately a contributor named Simon has proposed a patch, which was merged in last month, and addresses this issue with regard to axes, but not buttons.  I modified it to poll the button states and merged those changes back into the 1.2.15 joystick class with this patch.

Gamecon driver initialization

Originally I was going to fix these issues by using the js* devices in SDL and applying calibrations with jscal (I added linuxconsole package to my build in order to support it), but I really wanted the D-Pad to be supported properly as a HAT input, which is unique to evdev.  As a result, I made some changes to the initialization numbers in gamecon.  The problems are these:

1. Analog trigger axes come to rest at the low end of their value range.  As a result XBMC thinks that the triggers are always being pulled negative.  Creating a unattainable "false bottom" for analog triggers equidistant from the resting and max values prevents this.

2. The theoretical range for all Gamecube axes is 0-255.  These values are possible, but only when the controllers are taken out of their frames, because the plastic limits their range of motion.  The following is a quote from the Linux Kernel Documentation(italics added for emphasis):

input_set_abs_params(button_dev, ABS_X, 0, 255, 4, 8);

This setting would be appropriate for a joystick X axis, with the minimum of 0, maximum of 255 (which the joystick *must* be able to reach, no problem if it sometimes reports more, but it must be able to always reach the min and max values), with noise in the data up to +- 4, and with a center flat position of size 8.

I tested the four controllers I own and unfortunately, there's a good bit of variance between them.  The values I ended up with represent worst case ranges (ensuring all the axes of all four gamepads were well within reach of the mins and maxes) and midpoints that represent axes at rest within precision.

Even if these values do not perfectly suit all Gamecube gamepads, they should be an improvement over the unattainable 0-255.

That more-or-less covers it to where I am today.  I'm happy to say that XBMC is fully functional, and I'm using it in my bedroom now with no input device other than a Gamecube controller.  There are some things I left out that are very specific to my hardware build.  I can cover some of those things (joystick module initialization, keybinding xml, etc.) when I can write a proper Quart how-to.  In the meantime, I need to try and get some of these things patched in officially and make this easier to replicate.

Quart: (Re)Enter the Gamecube

Posted on March 14, 2013

In the last post I laid out the motivation for the little network appliance I've dubbed Quart.  The reason for the name is that I'm enclosing and operating a Raspberry Pi inside the nearly-quart-sized Nintendo Gamecube, which has been collecting dust in the basement for the last six years or so, since we replaced it with the Wii.

Why Gamecube?

After all, this system lived out a relatively short tenure as a video input in our household and doesn't evoke the same nostalgia as, say, Super Nintendo (or more in my case, Commodore 64).  In fact, the short answer to the question above is: We couldn't find the Super Nintendo.  But having decided on the Gamecube, I hit on a number of other great qualifications that make RPi and Gamecube a delightful abomination.

Cheap and Available

I hit on this already.  What else are you going to do with the bloody thing?  If you still own a Gamecube, you likely have a Wii as well.  The Wii is, of course, fully backward compatible.  So your Gamecube is either on your shelf, or the one at your local game trader; waiting to be sold for a pittance.

Relatively Small

We saw Nintendo and competitors take drastically different paths for the first time in this generation.  Sony and newcomer Microsoft came out with their enthusiast-oriented powerhouses, while Nintendo released the smaller, lower-powered, inexpensive Gamecube.  Admittedly it's still quite a bit larger than cases made to fit a Pi snugly, but it cleanly exposes the required controller ports, power and reset buttons.

Serviceable

Once you get past the four blasted security screws on the bottom, and start pulling it apart you may start noticing how well the various concerns are isolated and accessible.  We end up using precious little of the remaining guts, but the wheat is cleanly separated from the chaff.  More details on this later.

Buttons

Remember, one of the major requirements for this system is that all user functions will be available via the gamepad. Gamecube controllers have an ample number of inputs, and even sport some fairly modern concepts, like analog triggers, which make XBMC navigation a far more pleasurable experience.

Features

All four control ports are fed to GPIO and are functional using Markus Hiienkari's work with the original Linux gamecon driver by Vojtech Pavlik, and some modifications.

Runs OpenELEC, forked to support SDL gamepad input and modified gamecon driver.

Integrated power button and LED

Integrated reset button

Integrated classic power and AV cables

Standard male USB A cable with power leads spliced to the original Gamecube power adapter. Splice is concealed in a small, clasped component case.

Small Cat 5 extension surfaces below the chassis, and can be enclosed under the "Hi Speed Port" placeholder.