I have published the code on github. As is usual, you can clone it with the command:
git clone https://github.com/daniel-santos/mcp2210-linux
I have covered build instructions in the README.md (from the main repo web page, just scroll down). From this point forward, I will keep the master branch for good, tested stable features only and create new branches for new features to keep development going steadily while also keeping a good usable driver and userspace utility.
A lot of time has passed and changes made since my original post back in June, so I want to talk a little about the motivation for the driver and it's design and I'll cover the more practical use of the driver, configuration and using the userspace utility program in a later post.
Why?Now why write a driver for the MCP2210 when it already works without one? Well that's a very good and fair question. As I've discussed elsewhere, a more mature solution already exists via Kerry Wong's MCP2210 Library. The reasons for a custom driver are:
- Smaller CPU & memory footprint by eliminate HID drivers, APIs and libraries from the equation.
- Faster throughput by removing userspace from most operations (as well as the above).
- Facilitate integration with Linux's SPI and GPIO subsystems and enable the use of Linux SPI protocol drivers.
- Autonomous self-configuration.
Smaller FootprintThe table below illustrates what the software stack looks like using Kerry Wong's MCP2210-Library. There is nothing inherently wrong with this library, he's just created a nifty solution to a problem while working with the limitations of the underlying device & driver implementation (or lack of driver). In the following tables the "Interface" specifies the interface that the component has with its dependency(ies).
|USB Host Driver||(varies)||kernel||usbcore||intra-kernel||GPL v2|
|hidraw||HIDRAW||kernel||input, hidcore||intra-kernel||GPL v2|
|hid-generic||HID_GENERIC||kernel||input, hidcore||intra-kernel||GPLv 2|
|libusb||n/a||user||hid-generic, hidraw(?)||not completely sure||LGPL v2.1|
|hidapi||n/a||user||libusb||native / C ABI||GPL v3, BSD or HIDAPI|
|MCP2210 Library||n/a||user||hidapi||native / C ABI||Apache v2.0|
|application||n/a||user||MCP2210 Library||native / C++ ABI||any|
At first glance, much of this list may seem extraneous. Who isn't going to have the input or HID layers on their system!?. However, these large subsystems do not normally need to be present on headless or embedded systems and requiring them can add an extra burden to such projects.
Now lets compare this to the mcp2210 driver's software stack, examining various circumstances:
SPI from userspace via spidev driver:
|usbcore||same as above|
|USB Host Driver||same as above|
|spi (core API)||SPI||kernel||none||n/a||GPL v2|
|mcp2210||MCP2210||kernel||spi, usbcore||intra-kernel||GPL v2|
|spidev||SPIDEV||kernel||spi, mcp2210||intra-kernel||GPL v2|
|application||n/a||user||spidev||ioctl with /dev/spidev node||any|
SPI using an spi protocol driver
|usbcore||same as above|
|USB Host Driver||same as above|
|spi (core API)||same as above|
|mcp2210||same as above|
|your spi protocol driver||varies||kernel||spi, mcp2210||intra-kernel||GPL v2|
|application (optional)||n/a||user||spi protocol driver||varies||any|
Now I'm not even knocking Microchip's decision to implement this as a usbhid device. They apparently did so in order to enable their device to be used in the greatest number of places with the least amount of trouble. Since any USB compliant operating system will immediately recognize and enumerate the device as a generic HID device and expose it to userspace, no driver is needed at all. Writing, maintaining, installing and updating userspace libraries is far easier than drivers. However, the MCP2210 is indeed not a human interface device and . The HID APIs and protocol is ill-suited for it. For example, using HID reports in Linux requires approximately 4k of memory to send a single 64-byte message
Faster ThroughputWith the current mechanisms, SPI operations have a long round trip to make through many layers of software. Many layers allocate memory and copy buffers for each message exchanged with the device. When transmitting large, high-speed SPI messages, this can result in a significant slow down and/or increased CPU utilization.
Also, the moment we leave an atomic context, our process becomes eligible for preemption. This is actually a good thing from a system standpoint, but when we're trying to get maximal throughput, any other load on the CPU will greatly hamper communications.
When using the new driver, we can submit a new request to the device as soon as we receive a response from the USB host controller for the previous request without the possibility of preemption, since it all occurs in interrupt context. It is a very lightweight operation (if we're at full speed). If the SPI device is communicating at a lower speed, however, then we have to defer submission of the next request to give the MCP2210 time to finish transferring 60 bytes, so that does require an additional timer (soft-IRQ) to keep the system responsive to other I/O and CPU needs.
Integration With the Linux SPI and GPIO subsystemsFinally, this new driver builds upon years development and refining of some of the highest quality SPI and GPIO subsystems in the world. An SPI protocol driver can know how to communicate directly with a particular peripheral and not care at all about the SPI master that is facilitating the communications -- this is the most ideal mechanism for communicating with SPI devices in Linux. This also opens the door to using a vast array of mature spi protocol drivers that now exist both within and outside of the kernel tree.
Autonomous Self-ConfigurationSo now that we have integration with the Linux SPI & GPIO layers (the later which isn't completed), we can use the user area of the on-board EEPROM to store configuration data that the MCP2210 doesn't have a way to offer the host computer. We do this by reading the first 4 bytes of the user-EEPROM area to see if it matches a "magic" number. If it does, then the remainder is read and decoded to tell the driver what SPI devices are on the board and which spi protocol drivers to use for them.
This final step enables a solution that requires no information from userland, yet will not mistakenly attempt to auto-configure an MCP2210-based device that isn't designed for it. The end result yields a mechanism to have any arbitrary SPI device with an SPI protocol driver to be automatically detected and probed, just because it's connected to an MCP2210 with this data stored in its EEPROM.
- 2013, Aug 18: Fixed typos, spelling & grammar. Added Autonomous Self-Configuration section.