Leo Bodnar BU0836A controller & Linux

Questions and Answers

Does the controller work under Linux?

Yes. The kernel creates a regular joystick device node for it, such as /dev/input/js1.


Do multiple controllers work at the same time?

Yes, but read on ...


Does the kernel recognize and remember multiple controllers individually?

Yes and no. Each of the controllers has its own individual serial number, and both usb and input subsystem make this number available. The number isn't made part of the joystick identifier as returned by the JSIOCGNAME ioctl() function, though. The kernel only returns the generic product name »Leo Bodnar BU0836A Interface« for all attached BU0836A devices. There's no way to distinguish different BU0836 devices by their joystick name.


Is there a fix for the identification problem?

A simple kernel patch fixes the problem (tested under 2.6.33). It adds iSerial to the device name, so that each BU0836A appears under an individual name, e.g. »Leo Bodnar BU0836A Interface A10345«. This is done for all HID devices (USB mice, keyboards, ...), which hasn't led to any problems here. Most HID devices probably don't set iSerial anyway, so there's no change for them. And even those that do don't seem to care about their »name« at all.

--- a/drivers/hid/usbhid/hid-core.c     2010-03-04 02:49:38.676850618 +0100
+++ b/drivers/hid/usbhid/hid-core.c     2010-03-04 03:03:43.269006052 +0100
@@ -1131,6 +1131,12 @@
                strlcat(hid->name, dev->product, sizeof(hid->name));
        }
 
+       if (dev->serial) {
+               if (hid->name[0])
+                       strlcat(hid->name, " ", sizeof(hid->name));
+               strlcat(hid->name, dev->serial, sizeof(hid->name));
+       }
+
        if (!strlen(hid->name))
                snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
                         le16_to_cpu(dev->descriptor.idVendor),

Has this patch been submitted to the kernel developers?

Yes, but so far there wasn't much interest. The reasoning was something like (paraphrased): »The joystick interface sucks anyway. Just use another interface.« Of course, this is of no help if you are forced to use the joystick interface, for which one reason might be that it's the only interface supporting calibration.


Is there a way to fix the identification problem without having to patch the kernel?

In one of the next answers you find a link to a bu0836 configuration program. This also contains a preload library that modifies the kernel's answer to requests for joystick names. Just set LD_PRELOAD to library js_serial_preload.so before you run the joystick using application:

$ LD_PRELOAD=/usr/local/lib/js_serial_preload.so fgfs --aircraft=bo105 --airport=lowi

This simple library monitors all ioctl() function calls and catches JSIOCGNAME requests, whose answer it replaces by a name including the serial number. This name is retrieved from the associated events device. The preload library relies on proper joystick symlinks being set under /dev/input/by-id/, so if it yields an error message, check out the next question.


Why are there different device symlinks under /dev/input/by-id/?

Normally, the system creates symbolic links (»symlinks«) under /dev/input/by-id/ for every joystick, one ending with -joystick and linking to the joystick device, and one ending with -event-joystick and linking to the event device. Ideally, the same happens with BU0836 controllers, as shown in the green lines in this example:

$ ls -l /dev/input/by-id/|grep BU0836|cut -d' ' -f8-
usb-Leo_Bodnar_BU0836A_Interface_A12300-joystick -> ../js1
usb-Leo_Bodnar_BU0836A_Interface_A12300-event-joystick -> ../event5
usb-Leo_Bodnar_BU0836A_Interface_A12301-event-if00 -> ../event6

In some cases you might just get one single symlink ending with -event-if00, as shown in the red line. This is not the kernel's fault, but caused by udev's joystick detection heuristics. udev (by way of its helper program /lib/udev/input_id) only treats input devices as joysticks, if they provide an absolute x-axis, y-axis, and at least one of trigger button, a-button or 1-button. But you don't have to bother hardware-configuring your BU0836 like that, and can just give udev a little lesson instead: create a file /etc/udev/rules.d/00-local.rules containing this line:

SUBSYSTEMS=="usb", ATTRS{manufacturer}=="Leo Bodnar", ATTRS{product}=="BU0836*", ENV{ID_INPUT_JOYSTICK}:="1"

This will tell udev that all BU0836 devices are in fact joysticks, no matter which axes and buttons they have. Change the manufacturer and product strings accordingly if you use a custom device. The following command will tell you more about which properties are available to identify device /dev/input/js1:

$ /sbin/udevadm info -q all --attribute-walk -n /dev/input/js1

Is there a way to set zoom/invert/encoder mode under Linux?

The applications »BU0836 configuration.exe« and »BU0836_encoders.exe« for Microsoft® Windows® don't yet work under Wine. This is due to Wine not fully supporting generic USB-devices. There is a Free implementation of Bodnar's tools for Linux/Unix available here: http://gitorious.org/bu0836/ (man-page). The following image shows its status screen:


What does lsusb show about a BU0836A device?

$ lsusb -s2:2 -v

Bus 002 Device 002: ID 16c0:05ba VOTI 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0        64
  idVendor           0x16c0 VOTI
  idProduct          0x05ba 
  bcdDevice            1.22
  iManufacturer           1 Leo Bodnar
  iProduct                2 BU0836A Interface
  iSerial                 3 A10345
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           34
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.10
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength     103
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0020  1x 32 bytes
        bInterval               4
Device Status:     0x0000
  (Bus Powered)

What does a BU0836A's HID report (descriptor #34) look like?

For a device with two axes activated, it looks something like the following, whereby the first number is an offset, followed by the actual code and an interpretation. Note, how offsets 14 to 24 simply replace 09 by 15 as a placeholder for unused axes. That way the record length remains the same no matter how many axes are used. Also note that offset 97 says that the device expects 17 bytes control input. This is one address offset byte and 16 data bytes.

  0: 05 01              Usage Page 'Generic Desktop Controls'
  2: 09 04              Usage 'Joystick'
  4: a1 01              Collection 'Application (mouse, keyboard)'
  6: 09 01                      Usage 'Pointer'
  8: a1 00                      Collection 'Physical (group of axes)'
 10: 09 30                              Usage 'X'
 12: 09 31                              Usage 'Y'
 14: 15 32                              Logical Minimum = 50
 16: 15 33                              Logical Minimum = 51
 18: 15 34                              Logical Minimum = 52
 20: 15 35                              Logical Minimum = 53
 22: 15 37                              Logical Minimum = 55
 24: 15 36                              Logical Minimum = 54
 26: 15 00                              Logical Minimum = 0
 28: 26 ff 0f                           Logical Maximum = 4095
 31: 75 10                              Report Size = 16
 33: 95 02                              Report Count = 2
 35: 81 02                              Input data *var abs lin pref-state null-pos bit-field
 37: c0                         End Collection
 38: 05 09                      Usage Page 'Button'
 40: 19 01                      Usage Minimum = 1
 42: 29 20                      Usage Maximum = 32
 44: 15 00                      Logical Minimum = 0
 46: 25 01                      Logical Maximum = 1
 48: 75 01                      Report Size = 1
 50: 95 20                      Report Count = 32
 52: 81 02                      Input data *var abs lin pref-state null-pos bit-field
 54: 05 01                      Usage Page 'Generic Desktop Controls'
 56: 09 39                      Usage 'Hat switch'
 58: 25 07                      Logical Maximum = 7
 60: 35 00                      Physical Minimum = 0
 62: 46 3b 01                   Physical Maximum = 315
 65: 55 00                      Unit Exponent = 0
 67: 65 44                      Unit = System(English-Rotation) Length(Degrees)
 69: 75 04                      Report Size = 4
 71: 95 01                      Report Count = 1
 73: 81 42                      Input data *var abs lin pref-state null-pos bit-field
 75: 65 00                      Unit = None
 77: 95 01                      Report Count = 1
 79: 75 04                      Report Size = 4
 81: 81 03                      Input *const *var abs lin pref-state null-pos bit-field
 83: a1 02                      Collection 'Logical (interrelated data)'
 85: 06 00 ff                           Usage Page 'Vendor-defined'
 88: 09 01                              Usage 1
 90: 15 00                              Logical Minimum = 0
 92: 26 ff 00                           Logical Maximum = 255
 95: 75 08                              Report Size = 8
 97: 95 11                              Report Count = 17
 99: b1 02                              Feature data *var abs lin pref-state null-pos non-vol bit-field
101: c0                         End Collection
102: c0                 End Collection

What does the controller tell about its current configuration?

Each USB/HID request for a feature report returns 17 bytes of data (one line in the data block below), of which the first byte is an address offset for the following 16 bytes, which are a segment of the controller's EEPROM. The reports come in order, but don't necessarily start with report 00. After report f0 follows another 00 report and the sequence repeats. All in all this makes 256 data bytes.

00 03 0a 00 00 00 00 00 00 00 00 00 00 01 00 00 00
10 00 00 00 00 00 00 00 00 00 00 06 00 00 00 00 00
20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
80 04 60 00 00 00 00 00 00 88 77 04 00 00 00 00 00
90 c3 f5 03 3b 03 c0 82 28 00 72 0c 00 03 82 11 00
a0 3f 00 0f 00 a0 00 70 fc 09 03 00 00 00 00 00 00
b0 00 00 00 00 0f 00 00 00 00 00 00 00 00 00 00 00
c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Some of the EEPROM addresses and their meaning are listed below. Note that the zoom factor is an 8-bit number, not just a boolean.

Offset Type Meaning 0b uint8_t bitmap for axis inversion 0c uint8_t axis autodiscovery: 0 = off, 1 = on 0d uint8_t bitmap for axis shut-off: 1 = force off (only if autodiscovery is off) 0e-15 uint8_t[8] zoom values (0 = "Normal", 198 = "Zoom") 16 uint16_t bit0 of the rotary encoder bitmap (Network Endian) 18 uint16_t bit1 of the rotary encoder bitmap (Network Endian) bit0 and bit1 together describe the encoder type for each of the 16 button pairs: 0 = "OFF", 1 = "1:1", 2 = "1:2", 3 = "1:4" 1a uint8_t pulse width; range 1–11 (multiplicated with 8 gives width in ms, so 8 to 88 ms, default: 6, that is 48 ms)

Writing to the EEPROM is done by sending a feature report of two bytes length. The first byte is the address offset, and the second the data byte to write.


Which chip does the BU0836A device use?

A programmable Microchip® PIC18LF2553-I/SP.

Why does lsusb list VOTI as vendor?

VOTI (Van Ooijen Technische Informatica) owns vendor id 0x16c0 and sold the right to use product ids 0x05b40x05bd and 0x27740x27d7 to Leo Bodnar.



Last change: Tue Sep 27 19:38:21 CEST 2011
Most of what is written on this page is also true for Bodnar's BU0836, BU0836X, BU0836LC, as well as some of his other devices.
If you have more questions or information, please write to  < melchior : franz # gmail : com >.