The setup: a Linkit Board (Smart 7688 Duo) running OpenWRT and a generic keyboard connected on the USB host.

The goal: dumping the signal we get from the /dev/input/event0 and send it through the native UART of the board to the Atmega32u4. So this Atmega acts like a keyboard to the machine it is connected.

What I get so far: there is no difficult to get the signals from the keyboard on OpenWRT and sending it to the atmega via serial. But make the Atmega act like a real (real!) keyboard is the real issue.

I use a python script based on this answer to simplify visualization of the data and I can easily send it to the Arduino IDE console via serial. So with a simple Keyboard.press() statement on a arduino sketch I reproduce the keystroke in the machine what the ATmega is acting like a HID.

Seems pretty straightforward, and it is when you just need to mimic a simple keystroke. But it turns out very tricky to reproduce all the caracteritics of an actual keyboard, like simultaneous keypresses, modifiers, caps lock, etc.

Does anyone ever tried something similar?

PS.: My idea is to modify the Arduino keyboard class to be able to send KeyReports the way I want with the raw data received from OpenWRT, but as I said, turns out very complicated. I just don't wanna make if statements for every kind of data received and tell the Atmega to perform the matching key. I think there is a way to simply mirror the signals.


Thanks a lot to @dirkt for put so much effort and time helping. It was a very productive discussion and I learned a lot. You got close to the answer I need when told that the report we got from /dev/hidraw0 will implement all special keys ont he other side.

So reformulating the question with the gathered information: How do I get the report from the keyboard in the format: [modifier, reserved, key, key, key, key, key, key]

The /dev/hidraw0 returns the key and the event (1: press 0: release 2: hold) but I don't see how to get the modifier byte. (lets forget the reserved for now)

closed as unclear what you're asking by Rui F Ribeiro, dirkt, schily, msp9011, countermode Sep 5 '18 at 9:27

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • 1
    Could you please describe exactly what your problem is? I'm not familiar with the Smart Duo, I'm not entirely sure where the ATmega is wired up, and which signals you want to create in which direction. Forwarding /dev/input/event0 to the UART is trivial. Having whatever is behind the UART act as a input device isn't trivial, but not difficult, have a look at uinput. I'm not sure how HID comes in, and I'm not sure where the problem with key reports is. – dirkt Sep 4 '18 at 11:44
  • The Smart Duo is almost quite like the Arduino Yun, but in Yun the MPU is the Atheros and int he Smart is the MT7688. – Diego Dyan Sep 4 '18 at 11:53
  • There is two chips in the board: the MPU (MT7688) and the MCU (ATMEGA32U4) and they are linked via UART by the /dev/ttyS0. As far as I know, the key report have the format [modifier, reserved, key, key, key, key, key, key], e.g. six keys strokes at same time and one modifiers (shift, ctrl, alt). But for example: what about two modifiers? Or the caps lock? Or mapping the numeric pad? Seens trivial at the beggining, but gets complicated when we try to implement. – Diego Dyan Sep 4 '18 at 12:00
  • I supressed the information: the ATMEGA32u4 could act as a HID device by his nature. It is very used in this kind of aplication, but I am trying to make something more advanced. – Diego Dyan Sep 4 '18 at 12:03
  • 1
    The problem is: from /dev/input/event0, you are not getting the raw HID reports, but already-processed Linux input events. This page has a nice concise description of USB HID keyboard report structure. Tools like wireshark can also capture and analyze USB traffic: you may want to capture the USB traffic from a real keyboard at a very low level, and then examine the results in order to mimic it. – telcoM Sep 4 '18 at 12:15

Partial answer:

Let's sort out some stuff first.

If I understand you correctly, OpenWRT is running on the MPU (MT7688). The MPU sees the MCU (ATmega) behind the UART, and the MPU has an USB controller to which a generic USB HID keyboard is connected.

In the kernel, key presses then travel through the following layers:

keyboard -> USB -> HID -> input

So the keyboard sends USB packets as "interrupts", which contain HID report data. The HID data is described in the HID descriptor, and for keyboards, usually you have the one modifier byte and up to six simultaneous keypresses. But other formats are possible. You can read raw hid reports from /dev/hidrawX. The HID reports are converted by to kernel input layer events, which contain a single keypress/release, and those are the ones you read from /dev/input/event0.

Nothing of this has anything to do with the UART, or the ATmega behind it.

Now there are ways to connect a HID device via other busses to the kernel layer, instead of via USB. For example over I2C, or over the UART.

Is that what you want? This again has nothing to do with the presence of the USB-keyboard events on /dev/input/event0.

Another option is to invent your own private protocol, whatever you fancy, run one side on the the ATmega, and run a little C program that registers a new input-layer device via uinput, reads the UART, and then sends the input layer event.

Is that what you want? Again it has nothing to do with the presence of USB-keyboard events on /dev/input/event0, or with forwarding those events to the UART.

Or do you want to take the keyboard events from the MPU, send them to the ATmega, process them there, then send them back and have them act as real input events? That's of course possible, though then you have to ask yourself why you don't do this processing on the MPU in the first place.

So please describe your actual goal. At the moment, it's a bit unclear. You can fiddle with the Linux input layer, you can fiddle with the Linux HID layer, you can inject events into both layers, and do everything you want. The formats are not particularly difficult. You just must be specific about what you want.

Modify the Arduino keyboard class to be able to send KeyReports the way I want with the raw data received from OpenWRT

doesn't make a lot of sense. There is no "Arduino keyboard class" in the kernel layer. If you are using a an "Arduino keyboard class" as part of your development system, please link to it. And it probably won't help you a lot, you'll need to understand the other formats (HID report, input layer event) as well.


So to recap (the next time you ask a problem, please describe it in a similar way. Also have a look at the problem with XY questions):

You want to have an intermediary device between a USB keyboard and some other computer, where the other computer should just see an ordinary keyboard, and on the device you can do keylogging, trigger other events etc. (You still haven't specified if it is necessary that this intermediary device is actual hardware, or if it is also possible to install something on the PC that will either help with the task, or do the extra processing of the keyboard events.)

For that, you wanted to use a Linkit board. The MPU on the Linkit board acts as USB host to the keyboard. It is connected via an UART to the MCU (an ATmega), which itself can act as a USB HID device, which in turn can be connected to the other PC.

To act as a USB HID device, the MCU needs to do three things: It has to have a HID descriptor which describes the reports, it needs to send out HID keyboard press reports, and it needs to react to HID reports from the PC to set keyboard LEDs etc. I'll assume you have a library for the ATmega which handles all that for you.

The simplest approach is to just copy the HID descriptor from the real USB keyboard. You can find the HID descriptor in human-readable form in /sys/kernel/debug/hid/DEVICE_ID/rdesc, where DEVICE_ID identifies your keyboard. Either parse the hexadecimal at the beginning of it, or find out if there are ioctl's where you can get this information directly.

Then you need small program on the MPU which uses an ad-hoc protocol to communicate with the ATmega. It has to do the following: (1) If an USB hid device connects, read and parse the HID descriptor, send HID descriptor to ATmega. (2) Now enter a loop and forward HID reports from /dev/hidrawN, where N is the number for the keyboard via UART, and also write HID reports received from the UART to /dev/hidrawN. (3) Send an "exit" message if the USB keyboard disconnects.

On the ATmega, you need a small program which that implements the other side, using the available HID library.

You can choose on which side to implement the key logging etc.

Simultaneous keypresses, caps lock etc. are all automatically handled by forwarding the HID reports.

  • My goal is to read the same USB device in two diferent computers, one of them is the MT7688. Because the particularities of the USB comunication, I tought the simplest way to do that is the way I describe: receive the HID information in the MPU and forward the information through an MCU who has native HID interface, but you and @telcoM pointed out a very good point: I didn't know the data I read on dev/input/event0 was already converted to be show as events. I tought it was raw data. – Diego Dyan Sep 4 '18 at 14:20
  • Perhaps I am running away from the Linux/Unix theme when I said "Arduino keyboard class" because I was referencing the Arduino IDE and its way to send reports. Thanks for the orientation, but maybe you are right: it has nothing to do with forwarding HID data through UART. Can you suggest another way to achieve it? – Diego Dyan Sep 4 '18 at 14:20
  • What is the other computer where you want to read the USB keyboard? Is it connected to the ATmega? If yes, how? Via USB, and the ATmega acts as HID device for it? Does the MT7688 need to do anything with the keyboard events? (If you just want to connect the USB keyboard to one device, and access it from a different device, have a look at usbip. Much simpler than trying to process HID reports and using an ATmega as USB device). – dirkt Sep 4 '18 at 17:09
  • The other computer is a regular PC (Win or Linux). Yes, it is connected to Atmega via USB and the Atmega is acting like a HID for it. The scenario I am trying to build could be described as a keylogger: where the data goes to diferent ways: on the PC side it needs to act like a regular USB keyboard and in the MPU side, (in my case), I decided what to do with it (logging, trigger some others events, etc). – Diego Dyan Sep 4 '18 at 19:18
  • I had the idea to do with this setup because the MT7688 has the USB host and the ATMega32U could act like a HID, and they are on the same board and comunicating by native UART. But its getting far away from a Linux question and becoming an Arduino issue, because after all your explanation I can see that all will be solved if I was good enough to write a arduino sketch that reads every possiblity of input. In other words, I need to write a driver for it. – Diego Dyan Sep 4 '18 at 19:23

Not the answer you're looking for? Browse other questions tagged or ask your own question.