Cockpit: Handling raw data
The goal for today is to get to the point where we can receive events from the device in our driver and basically understand what we are getting.
In it's current state, even though the driver does execute the probe
function, it does not seem to be receing any raw events. The reason is that probe
does not even come close to handling everything it should. It needs to first receive and parse a report descriptor which explains the data that are going to be sent by the device. Remember how evtest
could give us a list of all the input options? It can do that by reading the report descriptor and analyzing it. Thankfully we are not doing anything special with it so far and we can just use the default parsing function offered by the kernel.
Probe
static int probe(struct hid_device *hdev, const struct hid_device_id *id) {
printk(KERN_INFO "winwing_ufc1_devdrv: probed\n");
int ret = hid_parse(hdev);
if (ret) {
printk(KERN_ERR "winwing_ufc1_devdrv: parse failed");
return ret;
}
ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
if (ret) {
printk(KERN_ERR "winwing_ufc1_devdrv: hw start failed");
return ret;
}
ret = hid_hw_open(hdev);
if (ret) {
printk(KERN_ERR "winwing_ufc1_devdrv: hw open failed");
hid_hw_stop(hdev);
return ret;
}
return 0;
}
static void remove(struct hid_device *hdev) {
hid_hw_close(hdev);
hid_hw_stop(hdev);
}
The descriptor is parsed with hid_parse
. After that we need to "start" the device. This initializes hardware buffers and connects to the device. HID_CONNECT_HIDRAW
is specified, instead of the standard HID_CONNECT_DEFAULT
as the mode in order to avoid having events be sent to the default HID driver. The purpose of hid_hw_open
is to tell the device that we are finally ready to receive events.
To go along with that, I've also added the remove
function which takes care of signaling to the device that we will stop receiving events (hid_hw_close
) and to clean up the buffers (hid_hw_stop
). This is passed to the module struct in order for the kernel to call it when appropriate.
Raw events
At this point, our raw_events
function is being called hundreds of times per second and I honestly have no clue why. So I modified it to print the data and maybe that will give us an idea. Here is the update raw_events
function:
static int raw_event(struct hid_device *hdev,
struct hid_report *report,
u8 *raw_data,
int size) {
printk(KERN_INFO "winwing_ufc1_devdrv: data:");
for (int i = 0; i < size; i++) {
printk(KERN_CONT " %02x", raw_data[i]);
}
printk(KERN_CONT "\n");
return 0;
}
Nothing really to explain here so let's look at the data:
$ dmesg -w
[22210.301743] winwing_ufc1_devdrv: data: 01 00 00 00 00 80 00 36 01 76 0d 00 00 00 00 00 00
[22210.311749] winwing_ufc1_devdrv: data: 01 00 00 00 00 80 00 36 01 76 0d 00 00 00 00 00 00
[22210.322045] winwing_ufc1_devdrv: data: 01 00 00 00 00 80 00 36 01 76 0d 00 00 00 00 00 00
[22210.331743] winwing_ufc1_devdrv: data: 01 00 00 00 00 80 00 36 01 76 0d 00 00 00 00 00 00
[22210.341884] winwing_ufc1_devdrv: data: 01 00 00 00 00 80 00 36 01 76 0d 00 00 00 00 00 00
We are looking things from a different perspective than before, when we were using evtest
. This array is essentially the state of all buttons given to us at the same time. When I press a button, a single bit changes in that array and when I release it, it gets reverted. We don't have to care about handling sync packets or parsing the USB data. Switches and knobs are also tracked in that same array. We are looking at a higher level representation because HID still does some of the heavy lifting for us, and I am now very thankful for it.
Next steps
From here on out it's a matter of understanding the mapping between buttons and the bits that change in this array. Once that is done and properly handled then this is half the work done for the kernel level driver. The other half is going to be LEDs which I still have little clue how to do properly...
We didn't cover a lot of ground today as I am slowly getting back into this project but the path now seems clearer and I am excited to keep working on this!