[0] https://blog.benjojo.co.uk/post/userspace-usb-drivers
[1] https://sourceforge.net/projects/glider-kernel/files/EDI%20H...
i too suffer from immediate references to tell me i know nothing, but we are all the same, reading the same stuff, even right here...
I think the biggest source of complexity for device drivers is the expectations that come with the design of the operating system the driver should run on. If there are e.g. strong expectations that device drivers don't block for certain operations or keep certain locks for extended amounts of time, then the driver may gain a lot of complexity from trying to play nice. Modern optimized desktop and server operating system are somewhat guilty of forcing that kind of complexity onto device drivers.
[1] https://github.com/torvalds/linux/blob/master/drivers/tty/se...
It can be. Not all devices will use memory-mapped I/O. USB devices, for example, use message passing. Messages are packets called USB Request Blocks, or URBs.
https://www.kernel.org/doc/html/latest/driver-api/usb/URB.ht...
A USB device driver is just a program that sends and receives theses messages. There are standardized device classes which enable generic drivers. Human interface devices, for example, will most likely be handled by a generic driver which can deal with any device of that class.
Hardware manufacturers can still add custom functionality on top of a standard interface, usually through control messages. A USB keyboard with LEDs, for example. All the key events are handled by generic drivers, I only had to figure out the LED control interface.
I used wireshark to reverse engineer it. Captured the USB traffic, saw a lot of control packets and correlated all of them with the proprietary manufacturer software's functions. With this data, it was simple to write a user space Linux driver for my keyboard.
https://wiki.wireshark.org/USB
The manufacturer's program would send a control message containing a payload, essentially bytes instructing the hardware what to do. For example, to set the color of a specific LED, my program sends this buffer to the hardware:
const unsigned char report[] = {
0xCC, /* Some kind of prefix? Always present in all payloads. */
0x01, /* Set LED color command. */
led, /* LED index, some keys have multiple LEDs. */
r, g, b, /* RGB8 color of the LED. */
0x7F /* Some kind of suffix? Always present in all payloads. */
};
hid_send_feature_report(keyboard, report, sizeof(report));
To turn off all LEDs and clear all configuration: const unsigned char report[] = {
0xCC,
0x00, 0x0C,
0x00, 0x00, 0x00,
0x7F
};
> I've rather fallen into the trap of thinking of kernel code, including drivers, as being some sort of deep magic beyond human comprehensionThere certainly is a lot of deep magic. Often because dealing with hardware is an ugly business. I've read Linux drivers which actually work around literal defects in the hardware. For example, I remember a bit of special code for a button that inexplicably sent two press events every time it was pushed. I wouldn't even have known that was the case were it not for the extremely helpful comments left by the developer.
In my case, I discovered the proprietary driver from the manufacturer was intercepting all of my keystrokes in order to support a special mode where LEDs light up when you press a key. It boggles my mind to this day, why couldn't they have done that in hardware?
Software is cheaper and more flexible, I suppose.