As someone who works with a lot of development boards, most of which come with a USB-to-serial chip of some kind, I deal with an annoying problem pretty often in Linux (currently, Ubuntu 16.04). When I plug the development board into my computer, I’m blocked from doing anything with the USB serial port it creates. The first error I usually get is that permission was denied:
$ cat /dev/ttyUSB0 cat: /dev/ttyUSB0: Permission denied
This is a common problem which is easy to solve — the problem is that the port’s permissions and ownership don’t allow me to access it:
$ ls -l /dev/ttyUSB0 crw-rw---- 1 root dialout 188, 0 Oct 25 13:45 /dev/ttyUSB0
I usually just add myself to the dialout group after I install Ubuntu to get around this issue. But…this isn’t what this blog post is about. The problem is that after I fix this, log out, log back in, and plug the board back into my computer, I run into this next problem:
$ cat /dev/ttyUSB0 cat: /dev/ttyUSB0: Device or resource busy
What’s interesting about this problem is that if I wait for 15-30 seconds and try again, it works. The problem goes away on its own. I did some sleuthing with the “lsof” command and figured out why this happens. The reason is that ModemManager opens the port when you plug it in, checking to see if a modem is attached to the serial port. I personally find this really annoying.
Luckily, it’s pretty easy to fix with a udev rule. udev already comes with several default rules which prevent ModemManager from looking at some USB serial devices (see the files /lib/udev/rules.d/77-mm-usb-device-blacklist.rules and /lib/udev/rules.d/77-mm-usb-serial-adapters-greylist.rules). Following the pattern in these files, you can create your own blacklist rule which you can put in /etc/udev/rules.d. In this example, I’m going to prevent an imaginary USB-to-serial adapter with USB vendor ID 0x1234 and product ID 0x5678 from being checked by ModemManager.
Create the file /etc/udev/rules.d/99-sample-usb-blacklist.rules and enter the following content, all on a single line:
ACTION=="add", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="1234", ATTRS{idProduct}=="5678", ENV{ID_MM_DEVICE_IGNORE}="1"
That’s all there is to it. This rule checks for a device in the subsystem “usb” with a DEVTYPE of “usb_device” and a matching USB PID/VID combination. If it finds a matching device, it sets an environment variable called ID_MM_DEVICE_IGNORE to 1, which will tell ModemManager to ignore it. Reload the udev rules with the following command:
sudo udevadm control --reload-rules
Now unplug and replug your development board (or USB-to-serial adapter or whatever it is) and you should notice that ModemManager didn’t interfere.
Luckily, this problem is not as common today as it used to be. Modern versions of ModemManager come with a default rule that “greylists” all USB devices with the FTDI vendor ID so they are only checked if you manually scan for modems–see the 77-mm-usb-serial-adapters-greylist.rules file I mentioned earlier. Most development boards that I’ve seen use FTDI chips, so that rule takes care of them. I still occasionally run into this problem with non-FTDI chips or older versions of Ubuntu, though, and the rule pattern I gave above should fix it.
Note that the ID_MM_DEVICE_IGNORE environment variable needs to be set on the USB device, and not the tty device. In other words, your rule needs to say SUBSYSTEM==”usb” and not SUBSYSTEM==”tty”. I have spent an embarrassing amount of time trying to troubleshoot a rule that didn’t work because of this requirement. If you already have another rule that sets custom permissions on the tty device, you need to create a separate rule to add the ID_MM_DEVICE_IGNORE environment variable to the actual USB device rather than the tty device.