Saturday, August 17, 2013

Why an MCP2210 Driver?

Well, I think I can call this latest commit an official first beta, although it's still missing real GPIO support, although you can currently manipulate it from user space via the userpsace utility program.  However, it is indeed incomplete since I intend to connect the MCP2210's GPIOs to Linux's GPIO subsystem, as I have with the SPI functionality.

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:
  1. Smaller CPU & memory footprint by eliminate HID drivers, APIs and libraries from the equation.
  2. Faster throughput by removing userspace from most operations (as well as the above).
  3. Facilitate integration with Linux's SPI and GPIO subsystems and enable the use of Linux SPI protocol drivers.
  4. Autonomous self-configuration.
So let's examine these points in greater detail.

Smaller Footprint

The 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).

Component .config Context Dependencies Interface License
usbcore USB kernel none n/a GPL v2
USB Host Driver (varies) kernel usbcore intra-kernel GPL v2
input INPUT kernel none n/a GPL v2
hidcore HID kernel input 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:
Component .config Context Dependencies Interface License
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
Component .config Context Dependencies Interface License
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 Throughput

With 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 subsystems

Finally, 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-Configuration

So 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.


Revision History:
  • 2013, Aug 18: Fixed typos, spelling & grammar. Added Autonomous Self-Configuration section.

Friday, June 21, 2013

MCP2210 Linux Driver

Hello World,

Hah! I've always wanted to do that.  So I'm developing a Linux driver for the MCP2210 USB-to-SPI bridge based loosely off of Mathew King's demo driver.  Currently, it will send & receive responses for most control messages (chip settings, SPI settings, etc.), user EEPROM reads & writes and SPI messages.  However, aside from SPI messages, there is currently no interface for these (outside of hacking the driver).  SPI devices are exposed via the driver of your specification, of which I've only tested spidev, but the configuration is still hard-coded (with a fake_config() function). The plan is to expose the remaining functionality via ioctls or some such.

Without knowing the board wiring in advance, auto-configuration of SPI devices is impossible.  The MCP2210 can be configured with a start-up configuration that specifies what each pin is used for, but can only store the complete communication settings for one SPI device.  However, it offers 256 bytes of user EEPROM that is perfect for just such a thing.  Thus, I have cobbled together an auto-configuration scheme which I dub Creek -- because if it doesn't work, you're up one.  This is a simple bit-frigging mechanism that tosses unimportant precision on values and allows you to store the complete SPI config (minus protocol driver name, etc.) in under 5 bytes. As an alternative, I will provide some type of configuration ability from userland via ioctls.

I don't have this code posted anywhere just yet, but I should have it to that point by the end of June or early July and will also be submitting to LKML for initial review (and having it torn to pieces, most likely) in roughly the same time-frame.