Using a Teensy to send USB HID data to a linux system.

sagreen83

Active member
I'm attempting to use a TeensyLC to emulate an existing device on a Linux Mint system. I'm pretty new to USB HID communication so its likely that my questions will not be the smartest questions ever asked :) I have a lot of questions in making this device work, but I'll start with the following.

I have compiled a simple program using the Arudino IDE with the USB Type set to "Keyboard". The program is uploaded to the Teensy, and the teensy is plugged into the Linux system.

To see what is being published by the USB device, I use a program on linux called "evtest" this program lets you select a USB device that it sees plugged in, and it reports all of the events that device is capable of producing.

I have attached 3 files.

1 - evtest.txt - This file is what I see when I invoke evtest. It displays all of the USB devices that it finds, and allows you to pick one to query.
2 - evdev1.txt - This is the output of evtest when I select device 4
3 - evdev2.txt - This is the output of evtest when I select device 5.

Questions: (I have many, but I will start with these).
1) Why are 2 devices found when evtest is invoked? (I only have "keyboard" selected as the USB type)
2) Why are the resulting outputs different between the 2 devices?
3) Device 5 has a lot of ABS events that I would like to send, but it seems to me that the only way to send ABS events is using the Joystick USB type. None of these seem to be exposed in usb_keyboard.h. Is there a way that I can access these ABS events using the Keyboard USB type?
4)Why are the 2 devices reporting as the same name? "Teensyduino Keyboard". I dont see a way to distinguish between the 2 on the linux side when polling devices. Clearly they are each publishing a different list of events.

Thanks in advance for your response,
Scott...
 

Attachments

  • evtest.txt
    896 bytes · Views: 27
  • evdev1.txt
    4.3 KB · Views: 26
  • evdev2.txt
    7.9 KB · Views: 49
With most of the USB Types there is some form of logical Serial driver.
The USB Types with the name Serial in them setup a standard Serial interface and will show up like /dev/ttyACMx...
Those without that name typically have another semi HID called SEREMU (Serial Emulator) which allows you to output debug stuff like Serial.print("debug"); and
connect it up to Arduino Serial Monitor.

Plus: Keyboard is also sort of split up:

a) Standard normal keys (HID keyboard boot mode) Which handles the standard simple keys like A-Z 0-9, the modifiers like shift, control...

b) Support for other keys, like media keys, power and the like. This is handled on another endpoint in the device.

https://www.pjrc.com/teensy/td_keyboard.html - sort of shows some of the split but not much of these details. More details are in the USB HID documents.

As for Mouse stuff: https://www.pjrc.com/teensy/td_mouse.html
There is moveTo which may or may not work with linux? The page mentioned here is pretty old, so not sure current state with linux.
 
With most of the USB Types there is some form of logical Serial driver.
The USB Types with the name Serial in them setup a standard Serial interface and will show up like /dev/ttyACMx...
Those without that name typically have another semi HID called SEREMU (Serial Emulator) which allows you to output debug stuff like Serial.print("debug"); and
connect it up to Arduino Serial Monitor.

Plus: Keyboard is also sort of split up:

a) Standard normal keys (HID keyboard boot mode) Which handles the standard simple keys like A-Z 0-9, the modifiers like shift, control...

b) Support for other keys, like media keys, power and the like. This is handled on another endpoint in the device.

https://www.pjrc.com/teensy/td_keyboard.html - sort of shows some of the split but not much of these details. More details are in the USB HID documents.

As for Mouse stuff: https://www.pjrc.com/teensy/td_mouse.html
There is moveTo which may or may not work with linux? The page mentioned here is pretty old, so not sure current state with linux.

Ok, I understand the split endpoints, but how about question #3 above. How do you send ABS events from the Teensy when selecting the keyboard as the USB type. Take a look at the attached files and you will see that the teensy is telling the Linux system that one of its endpoints has a lot of ABS events available. The Joystick will product ABS events via the sliders or X,Y and Z calls, but these do not seem to be available when the USB type is set to keyboard.

Scott...
 
Ok, I understand the split endpoints, but how about question #3 above. How do you send ABS events from the Teensy when selecting the keyboard as the USB type. Take a look at the attached files and you will see that the teensy is telling the Linux system that one of its endpoints has a lot of ABS events available. The Joystick will product ABS events via the sliders or X,Y and Z calls, but these do not seem to be available when the USB type is set to keyboard.

Scott...

Sorry, I have no idea what you mean by ABS events associated with a keyboard? Keyboards send keys...
Now if you instead choose usb type of: Keyboard + mouse + joystick

You can then send either of the two other types of messages. So when you wish to send a mouse position, you woud do something like: Mouse.moveTo(100, 100);
https://www.pjrc.com/teensy/td_mouse.html

If you want to set the slider you would do something like: Joystick.sliderLeft(512);
https://www.pjrc.com/teensy/td_joystick.html
 
Sorry, I have no idea what you mean by ABS events associated with a keyboard? Keyboards send keys...
Now if you instead choose usb type of: Keyboard + mouse + joystick

You can then send either of the two other types of messages. So when you wish to send a mouse position, you woud do something like: Mouse.moveTo(100, 100);
https://www.pjrc.com/teensy/td_mouse.html

If you want to set the slider you would do something like: Joystick.sliderLeft(512);
https://www.pjrc.com/teensy/td_joystick.html

Ok..

EVDEV is a python library on Linux that reads USB HID events and posts these events to your python program. These events are broken up into groups of similar events. KEY (keyboard buttons), ABS (variable input 1-1024 like joysticks), and REL (mouse wheels).
https://python-evdev.readthedocs.io/en/latest/

EVTEST is a program that sets on top of EVDEV and tells you the events that the USB HID device is capable of sending.

The code fragment below is the result of running EVTEST on a teensylc using keyboard as the USB type. It seems that the Teensy is telling linux that, as a keyboard, its capable of sending data that would create KEY, REL and ABS events on the python side. My question is how do you do this when the USB type is set to keyboard?

Note: When I have usb type set to Keyboard, the compiler complains when I try to send joystick or mouse commands as referenced in the documents you mention above.


Code:
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x16c0 product 0x4d0 version 0x111
Input device name: "Teensyduino Keyboard"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 1 (KEY_ESC)
    Event code 28 (KEY_ENTER)
    Event code 74 (KEY_KPMINUS)
    Event code 78 (KEY_KPPLUS)
    Event code 103 (KEY_UP)
    Event code 105 (KEY_LEFT)
    Event code 106 (KEY_RIGHT)
    Event code 108 (KEY_DOWN)
    Event code 110 (KEY_INSERT)
    Event code 111 (KEY_DELETE)
    Event code 113 (KEY_MUTE)
    Event code 114 (KEY_VOLUMEDOWN)
    Event code 115 (KEY_VOLUMEUP)
    Event code 116 (KEY_POWER)
    Event code 119 (KEY_PAUSE)
    Event code 128 (KEY_STOP)
    Event code 130 (KEY_PROPS)
    Event code 131 (KEY_UNDO)
    Event code 133 (KEY_COPY)
    Event code 134 (KEY_OPEN)
    Event code 135 (KEY_PASTE)
    Event code 136 (KEY_FIND)
    Event code 137 (KEY_CUT)
    Event code 138 (KEY_HELP)
    Event code 139 (KEY_MENU)
    Event code 140 (KEY_CALC)
    Event code 142 (KEY_SLEEP)
    Event code 143 (KEY_WAKEUP)
    Event code 144 (KEY_FILE)
    Event code 148 (KEY_PROG1)
    Event code 150 (KEY_WWW)
    Event code 152 (KEY_SCREENLOCK)
    Event code 155 (KEY_MAIL)
    Event code 156 (KEY_BOOKMARKS)
    Event code 158 (KEY_BACK)
    Event code 159 (KEY_FORWARD)
    Event code 161 (KEY_EJECTCD)
    Event code 163 (KEY_NEXTSONG)
    Event code 164 (KEY_PLAYPAUSE)
    Event code 165 (KEY_PREVIOUSSONG)
    Event code 166 (KEY_STOPCD)
    Event code 167 (KEY_RECORD)
    Event code 168 (KEY_REWIND)
    Event code 169 (KEY_PHONE)
    Event code 171 (KEY_CONFIG)
    Event code 172 (KEY_HOMEPAGE)
    Event code 173 (KEY_REFRESH)
    Event code 174 (KEY_EXIT)
    Event code 176 (KEY_EDIT)
    Event code 177 (KEY_SCROLLUP)
    Event code 178 (KEY_SCROLLDOWN)
    Event code 181 (KEY_NEW)
    Event code 182 (KEY_REDO)python -m evdev.evtest
    Event code 206 (KEY_CLOSE)
    Event code 207 (KEY_PLAY)
    Event code 208 (KEY_FASTFORWARD)
    Event code 209 (KEY_BASSBOOST)
    Event code 210 (KEY_PRINT)
    Event code 212 (KEY_CAMERA)
    Event code 216 (KEY_CHAT)
    Event code 217 (KEY_SEARCH)
    Event code 219 (KEY_FINANCE)
    Event code 223 (KEY_CANCEL)
    Event code 224 (KEY_BRIGHTNESSDOWN)
    Event code 225 (KEY_BRIGHTNESSUP)
    Event code 228 (KEY_KBDILLUMTOGGLE)
    Event code 231 (KEY_SEND)
    Event code 232 (KEY_REPLY)
    Event code 233 (KEY_FORWARDMAIL)
    Event code 234 (KEY_SAVE)
    Event code 235 (KEY_DOCUMENTS)
    Event code 240 (KEY_UNKNOWN)
    Event code 241 (KEY_VIDEO_NEXT)
    Event code 244 (KEY_BRIGHTNESS_ZERO)
    Event code 256 (BTN_0)
    Event code 314 (BTN_SELECT)
    Event code 315 (BTN_START)
    Event code 353 (KEY_SELECT)
    Event code 354 (KEY_GOTO)
    Event code 356 (KEY_POWER2)
    Event code 358 (KEY_INFO)
    Event code 362 (KEY_PROGRAM)
    Event code 366 (KEY_PVR)
    Event code 370 (KEY_SUBTITLE)
    Event code 372 (KEY_ZOOM)
    Event code 374 (KEY_KEYBOARD)
    Event code 376 (KEY_PC)
    Event code 377 (KEY_TV)
    Event code 378 (KEY_TV2)
    Event code 379 (KEY_VCR)
    Event code 380 (KEY_VCR2)
    Event code 381 (KEY_SAT)
    Event code 383 (KEY_CD)
    Event code 384 (KEY_TAPE)
    Event code 386 (KEY_TUNER)
    Event code 387 (KEY_PLAYER)
    Event code 389 (KEY_DVD)
    Event code 392 (KEY_AUDIO)
    Event code 393 (KEY_VIDEO)
    Event code 396 (KEY_MEMO)
    Event code 397 (KEY_CALENDAR)
    Event code 398 (KEY_RED)
    Event code 399 (KEY_GREEN)
    Event code 400 (KEY_YELLOW)
    Event code 401 (KEY_BLUE)
    Event code 402 (KEY_CHANNELUP)
    Event code 403 (KEY_CHANNELDOWN)
    Event code 405 (KEY_LAST)
    Event code 407 (KEY_NEXT)
    Event code 408 (KEY_RESTART)
    Event code 409 (KEY_SLOW)
    Event code 410 (KEY_SHUFFLE)
    Event code 412 (KEY_PREVIOUS)
    Event code 416 (KEY_VIDEOPHONE)
    Event code 417 (KEY_GAMES)
    Event code 418 (KEY_ZOOMIN)
    Event code 419 (KEY_ZOOMOUT)
    Event code 420 (KEY_ZOOMRESET)
    Event code 421 (KEY_WORDPROCESSOR)
    Event code 422 (KEY_EDITOR)
    Event code 423 (KEY_SPREADSHEET)
    Event code 424 (KEY_GRAPHICSEDITOR)
    Event code 425 (KEY_PRESENTATION)
    Event code 426 (KEY_DATABASE)
    Event code 427 (KEY_NEWS)
    Event code 428 (KEY_VOICEMAIL)
    Event code 429 (KEY_ADDRESSBOOK)
    Event code 430 (KEY_MESSENGER)
    Event code 431 (KEY_DISPLAYTOGGLE)
    Event code 432 (KEY_SPELLCHECK)
    Event code 433 (KEY_LOGOFF)
    Event code 438 (KEY_CONTEXT_MENU)
    Event code 439 (KEY_MEDIA_REPEAT)
    Event code 442 (?)
    Event code 576 (?)
    Event code 577 (?)
    Event code 578 (?)
    Event code 579 (?)
    Event code 580 (?)
    Event code 581 (?)
    Event code 582 (?)
    Event code 592 (?)
    Event code 593 (?)
  Event type 2 (EV_REL)
    Event code 6 (REL_HWHEEL)
  Event type 3 (EV_ABS)
    Event code 0 (ABS_X)
      Value      0
      Min        0
      Max      183
    Event code 1 (ABS_Y)
      Value      0
      Min        0
      Max      183
    Event code 2 (ABS_Z)
      Value      0
      Min        0
      Max      183
    Event code 3 (ABS_RX)
      Value      0
      Min        0
      Max      183
    Event code 4 (ABS_RY)
      Value      0
      Min        0
      Max      183
    Event code 5 (ABS_RZ)
      Value      0
      Min        0
      Max      183
    Event code 6 (ABS_THROTTLE)
      Value      0
      Min        0
      Max      183
    Event code 7 (ABS_RUDDER)
      Value      0
      Min        0
      Max      183
    Event code 8 (ABS_WHEEL)
      Value      0
      Min        0
      Max      183
    Event code 16 (ABS_HAT0X)
      Value      0
      Min        0
      Max      183
    Event code 17 (ABS_HAT0Y)
      Value      0
      Min       -1
      Max        1
    Event code 18 (ABS_HAT1X)
      Value      0
      Min       -1
      Max        1
    Event code 32 (ABS_VOLUME)
      Value      0
      Min        0
      Max      668
    Event code 40 (ABS_MISC)
      Value      0
      Min        0
      Max      183
    Event code 41 (?)
      Value      0
      Min        0
      Max      183
    Event code 42 (?)
      Value      0
      Min        0
      Max      183
    Event code 43 (?)
      Value      0
      Min        0
      Max      183
    Event code 44 (?)
      Value      0
      Min        0
      Max      183
    Event code 45 (?)
      Value      0
      Min        0
      Max      183
    Event code 46 (?)
      Value      0
      Min        0
      Max      183
    Event code 47 (ABS_MT_SLOT)
      Value      0
      Min        0
      Max      183
    Event code 48 (ABS_MT_TOUCH_MAJOR)
      Value      0
      Min        0
      Max      183
    Event code 49 (ABS_MT_TOUCH_MINOR)
      Value      0
      Min        0
      Max      183
    Event code 50 (ABS_MT_WIDTH_MAJOR)
      Value      0
      Min        0
      Max      183
    Event code 51 (ABS_MT_WIDTH_MINOR)
      Value      0
      Min        0
      Max      183
    Event code 52 (ABS_MT_ORIENTATION)
      Value      0
      Min        0
      Max      183
    Event code 53 (ABS_MT_POSITION_X)
      Value      0
      Min        0
      Max      183
    Event code 54 (ABS_MT_POSITION_Y)
      Value      0
      Min        0
      Max      183
    Event code 55 (ABS_MT_TOOL_TYPE)
      Value      0
      Min        0
      Max      183
    Event code 56 (ABS_MT_BLOB_ID)
      Value      0
      Min        0
      Max      183
    Event code 57 (ABS_MT_TRACKING_ID)
      Value      0
      Min        0
      Max      183
    Event code 58 (ABS_MT_PRESSURE)
      Value      0
      Min        0
      Max      183
    Event code 59 (ABS_MT_DISTANCE)
      Value      0
      Min        0
      Max      183
    Event code 60 (ABS_MT_TOOL_X)
      Value      0
      Min        0
      Max      183
    Event code 61 (ABS_MT_TOOL_Y)
      Value      0
      Min        0
      Max      183
    Event code 62 (?)
      Value      0
      Min        0
      Max      183
    Event code 63 (?)
      Value      0
      Min        0
      Max      183
  Event type 4 (EV_MSC)
    Event code 4 (MSC_SCAN)
Properties:
Testing ... (interrupt to exit)
 
Last edited:
Sorry I am not sure what else I can tell you:
The USB Keyboard link I linked to earlier, shows the key codes that are understood by the USB Keyboard code. The code on press and release will figure out by the codes specified if it goes out the normal key type HID or if it goes out the other one.

Again if you want Mouse or Joystick type events, you need to change the USB Type to include those.

Good luck.
 
Sorry I am not sure what else I can tell you:
The USB Keyboard link I linked to earlier, shows the key codes that are understood by the USB Keyboard code. The code on press and release will figure out by the codes specified if it goes out the normal key type HID or if it goes out the other one.

Again if you want Mouse or Joystick type events, you need to change the USB Type to include those.

Good luck.

Thanks..

My problem is that the device that I am trying to emulate has a single endpoint that exposes events that cross a keyboard, joystick and mouse. With the teensy each of these is a different endpoint, and EVDEV treats them as separate event streams. What I am trying to do is create a device that is plug and play and doesnt require changes to the host software. i.e. it emulates the original device and fakes out the host to think my device is the same device.

I've been able to hack the core usb_desc.h to change the MANUFACTURER_NAME and PRODUCT_NAME to be the same as the device I am trying to emulate. That gets me past the device check in the host python code. The problem is that the host code iterates across all endpoints that have the desired name, looking for specific capabilities. When it gets to the first one in a "serial/keyboard/mouse/joystick" USB TYPE, it does not find any ABS capabilities and ignores the device completely. In the short term I would have been able to get past this if each of the endpoints had a different name like "Teensyduino Mouse", "Teensyduino Keyboard" etc, but it doesnt appear that there is any way to have each of the endpoints have a different product name.

Is it possible to create a rawhid device that publishes the set of events I am trying to replicate? If so, are there any examples beyond the rawhid example that is on the site?

The code fragment below are the set of events that I need from a single endpoint.

Code:
    codes = {
        'BTN_0': "button.feedhold",
        'BTN_1': "button.cycle-start",
        'BTN_2': "button.hold2run",
        'BTN_3': "switch.mode-select",
        'BTN_4': "switch.defeatured-mode",
        'BTN_5': "button.pendant-1",
        'BTN_6': "button.pendant-2",
        'ABS_RX':"axis-select",
        'ABS_RY':'step-select',
        'ABS_X':'feed-override',
        'ABS_Y':'rpm-override',
        'ABS_Z':'rapid-override',
        'ABS_MISC':"pendant-knob",
        'ABS_WHEEL':'jog-wheel',
        'ABS_GAS':'jog-wheel-2',
        'LED_NUML': "led.ready",
        'LED_CAPSL': "led.board",
        'LED_SCROLLL': "led.red",
        'LED_COMPOSE': "led.green",
        'LED_KANA': "led.blue"
    }

Scott...
 
I've done lots of Linux Input Subsystem stuff, including uinput (synthesizing input devices). (My very first microcontroller project was a large arcade controller based on Teensy 2.0++, which I used to play SuperTux and online Flash games.)

My problem is that the device that I am trying to emulate has a single endpoint that exposes events that cross a keyboard, joystick and mouse.
This is very uncommon, because not all operating systems are as flexible as Linux is. On other operating systems, you may even need custom drivers to support such a combined device.

Indeed, Teensyduino creates a separate endpoint for each different type of device exactly because it is basically necessary to support all operating systems without requiring any driver support.

If you look at the Teensy core, you'll find that the exact USB endpoint configuration depends on the combined device type that is supported. For HID devices, it is in usb_hid/ or in usb_serial_hid/, depending on whether you also expose a serial interface or not.

What you need to do, is create a similar one, say usb_hid_sagreen83/, that exposes the correct USB endpoint; use the existing ones as a guide. Then, modify teensy/usb.c so that when in your own code you #define USB_HID_SAGREEN83, this new USB HID implementation is chosen (overriding the type selection otherwise, but choose the one that best matches your use case), say
Code:
#if defined(USB_HID_SAGREEN83)
#include "../usb_hid_sagreen83/usb.c"
#elif defined(USB_SERIAL)
#include "../usb_serial/usb.c"
#elif defined(USB_HID)
#include "../usb_hid/usb.c"
#elif defined(USB_SERIAL_HID)
#include "../usb_serial_hid/usb.c"
#elif defined(USB_DISK) || defined(USB_DISK_SDFLASH)
#include "../usb_disk/usb.c"
#elif defined(USB_MIDI)
#include "../usb_midi/usb.c"
#elif defined(USB_RAWHID)
#include "../usb_rawhid/usb.c"
#elif defined(USB_FLIGHTSIM)
#include "../usb_flightsim/usb.c"
#endif

Alternatively, if you want to keep the Teensy LC firmware compatible with unmodified Teensyduino, you can let the Teensy LC expose the three sub-devices (with standard Teensy Vendor:product), and use a simple userspace daemon in Linux to grab them, and synthesize the target device (with custon Vendor:product) from their events using uinput.
This is easier (because uinput is a simple, robust kernel interface exposed via character devices), but does require that the daemon runs whenever the input device is to be used. It is completely transparent/basically undetectable to any application using the event interfaces, as it is true kernel-level device interface.

Personally, I would choose my own approach based on where, how, and who uses the device. I constantly modify my Linux systems to accommodate whatever workflow seems most effective to me; it is just a tool for me. But, if the device was to be used by other people, perhaps on different machines, then the Teensyduino new-HID-type addition would probably make most sense.

Now, I am not that familiar with the Teensy HID internals that I could reliably say whether RawHID can do the same, or whether this is the most sensible way to do this in Teensyduino. I am only describing how I would do it. That new usb_hid_sagreen83/ directory and the modified teensy/usb.c would be stored separately, so that whenever I update Teensyduino, I can add the directory and add the changes on top of teensy/usb.c[/URL].

Other members here like KurtE, defragster, and others are much more familiar with Teensyduino innards than I am –– I too ask them for advice now and then. I myself am more of a Linux side person, so do regard their advice as more likely to be helpful than mine. But, if you need any help with the Linux input side, I can easily whip up a proper daemon to do the uinput synthesis, if you like.
 
What you need to do, is create a similar one, say usb_hid_sagreen83/, that exposes the correct USB endpoint; use the existing ones as a guide. Then, modify teensy/usb.c so that when in your own code you #define USB_HID_SAGREEN83, this new USB HID implementation is chosen (overriding the type selection otherwise, but choose the one that best matches your use case), say
Code:
#if defined(USB_HID_SAGREEN83)
#include "../usb_hid_sagreen83/usb.c"
#elif defined(USB_SERIAL)
#include "../usb_serial/usb.c"
#elif defined(USB_HID)
#include "../usb_hid/usb.c"
#elif defined(USB_SERIAL_HID)
#include "../usb_serial_hid/usb.c"
#elif defined(USB_DISK) || defined(USB_DISK_SDFLASH)
#include "../usb_disk/usb.c"
#elif defined(USB_MIDI)
#include "../usb_midi/usb.c"
#elif defined(USB_RAWHID)
#include "../usb_rawhid/usb.c"
#elif defined(USB_FLIGHTSIM)
#include "../usb_flightsim/usb.c"
#endif


Thank you for the great writeup! I'll poke around and see what I can break :)

Note: As I've been hacking I noticed that usb_serial_hid/usb.c has the following comment which based on your writeup, must be incorrect now unless your writeup was specific to Teensy 2.0.

Code:
// This file is used only for Teensy 2.0 and Teensy++ 2.0

Thanks,
Scott...
 
There are 3 different sets of USB sources, the one you are mentioning for Teensy 2.x

The sources for Teensy LC and 3.x are in cores\teensy3
So for example, the descriptors are defined in cores\teensy3\usb_desc.h and usb_desc.c

The sources for Teensy 4.x including Micromod are in cores\teensy4
 
There are 3 different sets of USB sources, the one you are mentioning for Teensy 2.x

The sources for Teensy LC and 3.x are in cores\teensy3
So for example, the descriptors are defined in cores\teensy3\usb_desc.h and usb_desc.c

The sources for Teensy 4.x including Micromod are in cores\teensy4

Thanks a ton!

I am making some progress. Using Nominal Animal's writeup, I've narrowed my endpoints down to a single Joystick, and the host application is now making it past the ABS test.

Next question:
Referencing the code fragment below, It appears that the button collection for the Joystick is starting at code 288 [BTN_TRIGGER]. I'd like this to start at 255 [BTN_0]. Is it possible to change the codes that are sent from each button?

Given the fact that I'm only starting to understand HID communication, its possible that this is established on the host side, not the client side.

Code:
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 288 (BTN_TRIGGER)
    Event code 289 (BTN_THUMB)
    Event code 290 (BTN_THUMB2)
    Event code 291 (BTN_TOP)
    Event code 292 (BTN_TOP2)
    Event code 293 (BTN_PINKIE)
    Event code 294 (BTN_BASE)
    Event code 295 (BTN_BASE2)
    Event code 296 (BTN_BASE3)
    Event code 297 (BTN_BASE4)
    Event code 298 (BTN_BASE5)
    Event code 299 (BTN_BASE6)
    Event code 300 (?)
 
There are 3 different sets of USB sources, the one you are mentioning for Teensy 2.x
Ouch. Apologies for the confusion! And thanks, KurtE, for the correction.

It appears that the button collection for the Joystick is starting at code 288 [BTN_TRIGGER]. I'd like this to start at 255 [BTN_0].
The Input Event Codes are grouped per device type, see /usr/include/linux/input-event-codes.h, and are specific to the Linux Input Subsystem.
(Thus, as you suspected, are determined on the Linux host side, based on the HID device type.)

BTN_0 (0x100 = 256) through BTN_9 (0x109 = 265) are reserved for touchpads.

BTN_MOUSE (0x110 = 272) through BTN_TASK (0x117 = 279) are reserved for mice.

BTN_JOYSTICK (0x120 = 288) through BTN_DEAD (0x12F = 303) are reserved for joystics, but are immediately followed by those for gamepads, BTN_SOUTH (0x130 = 304) through BTN_THUMBR (0x13e = 318).
 
Ouch. Apologies for the confusion! And thanks, KurtE, for the correction.


The Input Event Codes are grouped per device type, see /usr/include/linux/input-event-codes.h, and are specific to the Linux Input Subsystem.
(Thus, as you suspected, are determined on the Linux host side, based on the HID device type.)

BTN_0 (0x100 = 256) through BTN_9 (0x109 = 265) are reserved for touchpads.

BTN_MOUSE (0x110 = 272) through BTN_TASK (0x117 = 279) are reserved for mice.

BTN_JOYSTICK (0x120 = 288) through BTN_DEAD (0x12F = 303) are reserved for joystics, but are immediately followed by those for gamepads, BTN_SOUTH (0x130 = 304) through BTN_THUMBR (0x13e = 318).

Thank you. It doesnt appear that there is any way to emulate a touchpad without some major mods. The touchpad falls into the Digitizer category.

Scott...
 
I'm making some interesting progress...

I do not possess a version of the device that I am trying to emulate, so I grabbed another random device, that I do have, to emulate so that I can use the actual device against my Teensy clone to see how all of this works.

I modified KEYMEDIA_INTERFACE to replicate the device that I am now trying to emulate. I compiled with the USB type = "Keyboard". My modifications are as follows...

Code:
#ifdef KEYMEDIA_INTERFACE
static uint8_t keymedia_report_desc[] = {
        0x05, 0x0C,                     // Usage Page (Consumer)
        0x09, 0x01,                     // Usage (Consumer Controls)
        0xA1, 0x01,                     // Collection (Application)
        0xA1, 0x02,                     // Collection (Application)
        0x05, 0x01,                     //   Usage Page (Generic Desktop) 
        0x09, 0x38,                     //   Usage (Wheel)    
        0x15, 0x80,                     //   Logical Minimum (-127)
        0x25, 0x7F,               	//   Logical Maximum (127)
        0x66, 0x00, 0x00,                 
        0x75, 0x08,                     //   Report Size (8)
        0x95, 0x01,                     //   Report Count (1)
        0x81, 0x06,                     //   Input (Data, Variable)
        
        0x09, 0x37,                     //   Usage (Dial)  
        0x15, 0x00,                     //   Logical Minimum (0)
        0x26, 0xFF, 0x00,          	//   Logical Maximum (255)               
        0x66, 0x00, 0x00,
        0x75, 0x08,                     //   Report Size (8)
        0x95, 0x01,                     //   Report Count (1)
        0x81, 0x2E,                     //   Input (Data, Variable)
 
	0x95, 0x01,                     //   Report Count (1)
	0x75, 0x08,                     //   Report Size (8)
	0x81, 0x01,                     //   Input (Data, Array)
         
        0x05, 0x09,                     //   Usage Page (Buttons)  
        0x19, 0x01,                     //   Usage Minimum (Button #1)        
	0x29, 0x0D,                     //   Usage Maximum (Button #13)       
	0x15, 0x00,                     //   Logical Minimum (0)
	0x25, 0x01,               	//   Logical Maximum (1)        
	0x35, 0x00,                     //   Physical Minimum (0)
        0x45, 0x01,               	//   Physical Maximum (1)
 	0x75, 0x01,                     //   Report Size (1)
	0x95, 0x0D,                     //   Report Count (0x0D) 		
	0x81, 0x02,                     //   Input (Data, Variable)
		
	0x95, 0x01,                     //   Report Count (0x0D) 		   
 	0x75, 0x03,                     //   Report Size (1)
	0x81, 0x01,                     //   Input (Constant, Array) 		
        0xC0,                           // End Collection
        0xC0                            // End Collection        
};
#endif

When I run evtest on the hacked device I get the following which is exactly the same as the device that I am trying to emulate...

Code:
Input driver version is 1.0.1
Input device ID: bus 0x3 vendor 0x16c0 product 0x4d0 version 0x111
Input device name: "Teensyduino Keyboard"
Supported events:
  Event type 0 (EV_SYN)
  Event type 1 (EV_KEY)
    Event code 256 (BTN_0)
    Event code 257 (BTN_1)
    Event code 258 (BTN_2)
    Event code 259 (BTN_3)
    Event code 260 (BTN_4)
    Event code 261 (BTN_5)
    Event code 262 (BTN_6)
    Event code 263 (BTN_7)
    Event code 264 (BTN_8)
    Event code 265 (BTN_9)
    Event code 266 (?)
    Event code 267 (?)
    Event code 268 (?)
  Event type 2 (EV_REL)
    Event code 7 (REL_DIAL)
    Event code 8 (REL_WHEEL)
  Event type 4 (EV_MSC)
    Event code 4 (MSC_SCAN)
Properties:
Testing ... (interrupt to exit)


I have no idea yet how to modify the rest of the code to support this new report descriptor. Any advice?

Interesting that I am getting BTN_0 - BTN_9 results and its not a touchpad. Not sure what to make of this yet.

Scott...
 
Interesting that I am getting BTN_0 - BTN_9 results and its not a touchpad. Not sure what to make of this yet.
Ah, now I see. You are using the USB HID Button Page for the buttons; those too map to BTN_0 onwards in the Linux Input subsystem. Your usage report describes the format of the events :).

However, I noticed some of your comments were incorrect:
Code:
static uint8_t keymedia_report_desc[] = {
        0x05, 0x0C,                     // Usage Page (Consumer)
        0x09, 0x01,                     // Usage (Consumer Controls)
        0xA1, 0x01,                     // Collection (Application)
        0xA1, 0x02,                     // Collection (Application)
        0x05, 0x01,                     //   Usage Page (Generic Desktop) 
        0x09, 0x38,                     //   Usage (Wheel)    
        0x15, 0x80,                     //   Logical Minimum (-128)  (FIXED)
        0x25, 0x7F,               	//   Logical Maximum (127)
        0x66, 0x00, 0x00,                 
        0x75, 0x08,                     //   Report Size (8)
        0x95, 0x01,                     //   Report Count (1)
        0x81, 0x06,                     //   Input (Data, Variable, Relative)   (FIXED)
        
        0x09, 0x37,                     //   Usage (Dial)  
        0x15, 0x00,                     //   Logical Minimum (0)
        0x26, 0xFF, 0x00,          	//   Logical Maximum (255)               
        0x66, 0x00, 0x00,
        0x75, 0x08,                     //   Report Size (8)
        0x95, 0x01,                     //   Report Count (1)
        0x81, 0x2E,                     //   Input (Data, Variable, Relative, Wrap)   (FIXED)
 
	0x95, 0x01,                     //   Report Count (1)
	0x75, 0x08,                     //   Report Size (8)
	0x81, 0x01,                     //   Input (Constant, Array)  (FIXED)
         
        0x05, 0x09,                     //   Usage Page (Buttons)  
        0x19, 0x01,                     //   Usage Minimum (Button #1)        
	0x29, 0x0D,                     //   Usage Maximum (Button #13)       
	0x15, 0x00,                     //   Logical Minimum (0)
	0x25, 0x01,               	//   Logical Maximum (1)        
	0x35, 0x00,                     //   Physical Minimum (0)
        0x45, 0x01,               	//   Physical Maximum (1)
 	0x75, 0x01,                     //   Report Size (1)
	0x95, 0x0D,                     //   Report Count (0x0D) 		
	0x81, 0x02,                     //   Input (Data, Variable)
		
	0x95, 0x01,                     //   Report Count (1)  (FIXED)
 	0x75, 0x03,                     //   Report Size (3)
	0x81, 0x01,                     //   Input (Constant, Array)   (FIXED)

        0xC0,                           // End Collection
        0xC0                            // End Collection        
};
#endif
see HID 1.11, 6.2.2.4 Main Items (PDF).

The above tells us that the USB report consists of five bytes. The first byte is the wheel, second is the dial, third is reserved and zero, and fourth and fifth are the button states.

(I assume you have obtained an USB capture from the actual device, using Wireshark or similar, and constructed the report to match? If not, that is a very good idea to do, to match the original device.)

I do believe you'll need a function to generate the report yourself, adapting it from teensy3/usb_keyboard.c:usb_keymedia_send(). Something like
Code:
int generate_event(int8_t wheel, uint8_t dial, uint16_t buttons)
{
    uint32_t wait_count=0;
    usb_packet_t *tx_packet;

    while (1) {
        if (!usb_configuration) {
            return -1;
        }
        if (usb_tx_packet_count(KEYMEDIA_ENDPOINT) < TX_PACKET_LIMIT) {
            tx_packet = usb_malloc();
            if (tx_packet) break;
        }
        if (++wait_count > TX_TIMEOUT || transmit_previous_timeout) {
            transmit_previous_timeout = 1;
            return -1;
        }
        yield();
    }
    transmit_previous_timeout = 0;

    *(tx_packet->buf + 0) = (uint8_t)wheel;
    *(tx_packet->buf + 1) = dial;
    *(tx_packet->buf + 2) = 0;
    *(tx_packet->buf + 3) = buttons & 0xFF;
    *(tx_packet->buf + 4) = (buttons >> 8) & 0x1F;
    tx_packet->len = 5;

    usb_tx(KEYMEDIA_ENDPOINT, tx_packet);
    return 0;
}
noting that you only need to generate an event whenever at least one of the values (wheel, dial, or button states) change.
 
(I assume you have obtained an USB capture from the actual device, using Wireshark or similar, and constructed the report to match? If not, that is a very good idea to do, to match the original device.)

Thanks for the feedback, and thanks for the code!. That is exactly what I did. I used this utility.

https://www.hhdsoftware.com/device-monitoring-studio

I'll check out wireshark. This one seems to be pretty nice but seems a bit expensive.

Scott...
 
Last edited:
Out of curiosity, what is involved in adding another USB type selection to the Arduino->Tools menu USB type dropdown? Would be nice to just make another USB type and not hack one of the existing ones.

Scott...
 
I'm making great progress with this device. I am now emulating an off the shelf USB device with a TeensyLC in which the host does not know the difference between my Teensy Device and the purchased device.

I have created a new entry in the Arduino Drop down that allows me to select my new device as the USB type. Is there any advice on how to do this in a way that doesnt require a lot of work when a new version of the Teensy Core libraries are distributed? I've created a new usb_mydevice.c and usb_mydevice.h in cores/teensy3 as well as the changes needed in usb_desc.c and usb_desc.h

Scott...
 
Back
Top