Project update 5 of 21
The μArt has a broad OS support covering Linux, Windows and MacOS. But as you are probably aware, drivers are just like any other software in that they can be sometimes… suboptimal. Many weeks ago, I noticed the Linux driver for the μArt had some omissions, so I set forth to fill in the gaps - successfully. Hence I can state that the μArt is one of the cooler projects that not only contributed to open-source drivers, but made sure those changes will be readily available in standard distributions by the time the product ships.
Our UART adapter’s protocol bridging function is fulfilled by Silicon Labs’ CP2102N. This is a relatively new lineup of three chips that replace a large part of the vendor’s older solutions, for example the well-known CP2102. When I say "relatively new", I mean these new products have been on the market for more than two years now. That should be enough time for a vendor to put out drivers for all systems they claim official support for, right?
Well, yes, in the sense that the drivers to use the UART functionality have been there right from the beginning on any of the above operating systems. That means, you could have taken a μArt a couple of months ago, plugged it into an old Linux system, and it would have worked. That of course covers about 95% of the user-base, but still, if you looked hard enough, you may had noticed some "restrictions". Recognizing this, I started implementing the missing bits and pieces, and the result is a driver that surpasses even the "improved" one on the vendor’s own website, and maybe more importantly, it has been already accepted into the mainline Linux kernel.
Following is a discussion of the original weaknesses and the improvements I’ve made.
The CP2102N (and in consequence the μArt) is advertised as a UART bridge that is not only fast, but is able to use almost any baudrate. In fact, the CP2102N datasheet claims this device can generate most baudrates in its range with only an error of 1% - which is plenty good, as you can generally have 3-4x this error before you start experiencing problems. You can imagine how disappointed I was during testing when I realized the device did not only have problems using its highest advertised baudrate, but when using uncommon baudrates in the supported range it magically fell back to a standard 9600. This I did not experience under Windows, so I immediately knew it must be a problem in the Linux driver. Thankfully I have a strong programming background, so who would I be if I did not try to implement perfect baudrate support for Linux too - that’s the whole point of Linux being open-source after all, to be able to do that, right?
The culprit was found shortly after. It turns out older devices like the CP2102 (without the N-suffix) could only use a small set of baudrates and weren’t nearly as flexible in this regard. The SiLabs Application Note 205 lists all baudrates that the old devices support in a short-ish table, and any value not in that table is considered invalid and to result in undefined behavior. The Linux driver implemented that table and correctly made sure that if any other (that is, non-supported) rate was requested, nothing "undefined" would come of it by falling back to a supported one. That was all fine and dandy, except somebody forgot to adjust the driver to the CP2102N which was not constrained to the values in this table anymore. Needless to say, as a result the driver artifically restricted the new flexible IC only to the values supported by its predecessors. This has been corrected by the μArt project, and now you can use any baudrate under Linux just like you can under Windows.
With that, the UART functionality under Linux was whole, at least as far as I could tell. But the device in use also had GPIOs, and again, I couldn’t help but notice I could not set or read the GPIOs under Linux. "Okay", I thought to myself, "one problem down, one to go, so let’s tackle it too". Especially since (in case you didn’t know) the GPIO function under Linux is a lot cooler than in Windows, because in contrast to Microsoft’s child, there’s not only a whole standardized framework for it at all, but Linux allows you to use the GPIOs even if the serial device has already been opened in another application. This means you can use any standard utility, or even your Bash, to control GPIO pins without having to write or modify a serial terminal program, and also let them run and work in parallel, which is really usefull. This is not the case under Windows (this is a Windows limitation in general), so I saw a lot of value in implementing GPIO support under Linux correctly.
Needless to say, I investigated, and the mainline driver in Linux (the "cp210x" module) did not have any GPIO support for this device. SiLabs’ website had a modified driver claiming GPIO support that you could compile yourself from source code - and so its user-friendliness flew right out the window - , but that wasn’t the only problem with it. Beside being out of date anyway, it turns out SiLabs implemented their GPIO support in a completely custom and proprietary way that did not use any of the standard Linux GPIO kernel infrastructure. Most uncool about that is even if you took the effort of compiling the vendor’s driver yourself, it made it completely impossible to use the GPIOs from standard applications, and in turn made you lose the usefull advantage compared to Windows systems I talked about a little above. Unsurprisingly, when they tried to submit it to Linux for mainline inclusion years ago, they’ve been utterly rejected because of this, and were asked to reimplement it correctly, which they did not. They probably thought some fool would come about who’d do it for them anyway - that fool turned out to be me.
What complicated matters is that the CP2102N isn’t the vendor’s only UART bridge with GPIOs but there are actually a couple of others as well, none having GPIO drivers in Linux of course, with the exception of one, and that one happened to be the odd one working slightly differently from the rest. This means I couldn’t just "simply add code for the CP2102N", but I had to do it in a future-proof way that allowed other "standard" chips to be easily added later, but keep full support for the odd one that was already in the kernel, integrate it in a nice way with the new infrastructure, and all this without being able to test the odd one on actual hardware. Another problem was that in order to determine plausible pin directions upon device attach, I had to get some information out of the chip that was nowhere documented, so I had to rely on sniffing USB traffic under Windows and reverse-engineer a small part of the protocol.
There have been a few revisions for the patch until it satisfied the reviews, but in the end I pulled it through, and the CP2102N - and so the μArt too - now has GPIO support as deemed right by the kernel developers. The changes for this, just like for the flexible baudrate support, have been accepted and are already in Linux.
… but in a completely positive and constructive way. I should probably call it "unforeseen positive side-effects" instead of "fallout". Me adding full baudrate support caused a small ripple effect and also motivated the subtree maintainer to clean up the baudrate handling of other Silicon Labs devices in general. As a result, the CP2101, CP2102, CP2103 and CP2105 received a fix avoiding some undefined behavior. Furthermore, the CP2105 and CP2104 also received support for all baudrates not constrained to the AN205 table anymore. Lastly, it is now more or less trivial to add GPIO support for any of the other existing devices from the same vendor.
Another lengthy update, but I hope it wasn’t too boring for at least some of you. TL;DR, as part of the μArt project I made significant contributions to the standard Linux driver for SiLabs UART bridges, contributions that many other devices beside the μArt will be able to make good use of. Though indirectly, but even ICs not in the μArt got positively affected, and all these changes have been officially accepted into Linux and will be part of your usual desktop distributions by the time you receive your rewards.
A big-big Thank You goes to Johan (usb-serial subtree maintainer) who not only supported me through this process, but helped me a lot to get these changes integrated in time and not be late. It usually takes a couple of months from "being accepted into mainline" to "being there on the user’s system", so it was vital to get this done earlier this summer so that you can use the new driver when the product ships. For the interested, Johan’s tree (with my patches) can be found at: https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial.git/log/?h=usb-next