Custom udev rules

For our application we need a number of different usb-devices thus calling for custom udev rules. I have trawled through all topics regarding udev rules and have compiled the test app below. The app has been tested using resin ssh showing that the desired device is not generated. The device gets displayed as /dev/ttyUSB0 when plugged in before boot but does not show up as /dev/fc_serial as required.

Some udev rules seems to be run, as a rule targeting addition of any usb device is run.

The udev fc-serial.rule works as expected in regular raspbian jessie and my ubuntu 16.04 laptop.

The application is running on a RPI3.

Dockerfile

FROM resin/%%RESIN_MACHINE_NAME%%-buildpack-deps

MAINTAINER Mathias H Egeberg mhe@sometext.com

# Switch on systemd init system in container and set various other variables
ENV INITSYSTEM="on" \
    TERM="xterm" \
    PYTHONIOENCODING="UTF-8"

WORKDIR /usr

COPY ./udev-rules /etc/udev/rules.d/

COPY ./startup.sh /usr/startup.sh

CMD bash ./startup.sh

startup.sh

mount -t devtmpfs none /dev
udevd &
udevadm trigger
touch startup_success

udev-files/fc-serial.rule

SUBSYSTEM==“tty”, ATTRS{idVendor}==“0403”, ATTRS{idProduct}==“6001”, SYMLINK+=“fc_serial”

udev rule targeting addition of any usb device

ACTION=="add", SUBSYSTEMS=="usb", RUN+="/usr/bin/touch /usr/success"

Hi @mhe_lorenz
With the usb device attached, can you ssh into the running container (via the CLI or webterminal), run udevinfo -a -p $(udevinfo -q path -n /dev/tty/USB0) and post the output here?

@lucianbuzzo the command you ask for is not available, so here is the output of an equivalent:

root@51361fe:/usr# udevadm info -a -p  $(udevadm info -q path -n /dev/ttyUSB0)

Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.

  looking at device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/ttyUSB0/tty/ttyUSB0':
    KERNEL=="ttyUSB0"
    SUBSYSTEM=="tty"
    DRIVER==""

  looking at parent device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/ttyUSB0':
    KERNELS=="ttyUSB0"
    SUBSYSTEMS=="usb-serial"
    DRIVERS=="ftdi_sio"
    ATTRS{port_number}=="0"
    ATTRS{latency_timer}=="16"

  looking at parent device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0':
    KERNELS=="1-1.3:1.0"
    SUBSYSTEMS=="usb"
    DRIVERS=="ftdi_sio"
    ATTRS{interface}=="TTL232R"
    ATTRS{bInterfaceProtocol}=="ff"
    ATTRS{bInterfaceNumber}=="00"
    ATTRS{bInterfaceSubClass}=="ff"
    ATTRS{bInterfaceClass}=="ff"
    ATTRS{bAlternateSetting}==" 0"
    ATTRS{authorized}=="1"
    ATTRS{bNumEndpoints}=="02"
    ATTRS{supports_autosuspend}=="1"

  looking at parent device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3':
    KERNELS=="1-1.3"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{bDeviceClass}=="00"
    ATTRS{manufacturer}=="FTDI"
    ATTRS{bmAttributes}=="80"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{version}==" 2.00"
    ATTRS{devnum}=="4"
    ATTRS{bMaxPower}=="90mA"
    ATTRS{idProduct}=="6001"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{urbnum}=="16"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{maxchild}=="0"
    ATTRS{bcdDevice}=="0600"
    ATTRS{bMaxPacketSize0}=="8"
    ATTRS{idVendor}=="0403"
    ATTRS{product}=="TTL232R"
    ATTRS{speed}=="12"
    ATTRS{removable}=="removable"
    ATTRS{ltm_capable}=="no"
    ATTRS{serial}=="FTA6D6Q5"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{busnum}=="1"
    ATTRS{authorized}=="1"
    ATTRS{quirks}=="0x0"
    ATTRS{configuration}==""
    ATTRS{devpath}=="1.3"
    ATTRS{bDeviceProtocol}=="00"
    ATTRS{bNumInterfaces}==" 1"

  looking at parent device '/devices/platform/soc/3f980000.usb/usb1/1-1':
    KERNELS=="1-1"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{bDeviceClass}=="09"
    ATTRS{bmAttributes}=="e0"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{version}==" 2.00"
    ATTRS{devnum}=="2"
    ATTRS{bMaxPower}=="2mA"
    ATTRS{idProduct}=="9514"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{urbnum}=="37"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{maxchild}=="5"
    ATTRS{bcdDevice}=="0200"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{idVendor}=="0424"
    ATTRS{speed}=="480"
    ATTRS{removable}=="unknown"
    ATTRS{ltm_capable}=="no"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{busnum}=="1"
    ATTRS{authorized}=="1"
    ATTRS{quirks}=="0x0"
    ATTRS{configuration}==""
    ATTRS{devpath}=="1"
    ATTRS{bDeviceProtocol}=="02"
    ATTRS{bNumInterfaces}==" 1"

  looking at parent device '/devices/platform/soc/3f980000.usb/usb1':
    KERNELS=="usb1"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{bDeviceClass}=="09"
    ATTRS{manufacturer}=="Linux 4.9.50 dwc_otg_hcd"
    ATTRS{bmAttributes}=="e0"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{version}==" 2.00"
    ATTRS{devnum}=="1"
    ATTRS{bMaxPower}=="0mA"
    ATTRS{idProduct}=="0002"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{urbnum}=="25"
    ATTRS{bDeviceSubClass}=="00"
    ATTRS{maxchild}=="1"
    ATTRS{bcdDevice}=="0409"
    ATTRS{bMaxPacketSize0}=="64"
    ATTRS{idVendor}=="1d6b"
    ATTRS{product}=="DWC OTG Controller"
    ATTRS{speed}=="480"
    ATTRS{authorized_default}=="1"
    ATTRS{interface_authorized_default}=="1"
    ATTRS{removable}=="unknown"
    ATTRS{ltm_capable}=="no"
    ATTRS{serial}=="3f980000.usb"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{busnum}=="1"
    ATTRS{authorized}=="1"
    ATTRS{quirks}=="0x0"
    ATTRS{configuration}==""
    ATTRS{devpath}=="0"
    ATTRS{bDeviceProtocol}=="01"
    ATTRS{bNumInterfaces}==" 1"

  looking at parent device '/devices/platform/soc/3f980000.usb':
    KERNELS=="3f980000.usb"
    SUBSYSTEMS=="platform"
    DRIVERS=="dwc_otg"
    ATTRS{wr_reg_test}=="Time to write GNPTXFSIZ reg 10000000 times: 510 msecs (51 jiffies)"
    ATTRS{grxfsiz}=="GRXFSIZ = 0x00000306"
    ATTRS{srpcapable}=="SRPCapable = 0x1"
    ATTRS{buspower}=="Bus Power = 0x1"
    ATTRS{bussuspend}=="Bus Suspend = 0x0"
    ATTRS{hptxfsiz}=="HPTXFSIZ = 0x02000406"
    ATTRS{hnp}=="HstNegScs = 0x0"
    ATTRS{mode}=="Mode = 0x1"
    ATTRS{mode_ch_tim_en}=="Mode Change Ready Timer Enable = 0x0"
    ATTRS{hsic_connect}=="HSIC Connect = 0x1"
    ATTRS{gsnpsid}=="GSNPSID = 0x4f54280a"
    ATTRS{driver_override}=="(null)"
    ATTRS{hcd_frrem}=="HCD Dump Frame Remaining"
    ATTRS{gotgctl}=="GOTGCTL = 0x001c0001"
    ATTRS{gpvndctl}=="GPVNDCTL = 0x00000000"
    ATTRS{hnpcapable}=="HNPCapable = 0x1"
    ATTRS{spramdump}=="SPRAM Dump"
    ATTRS{regoffset}=="0xffffffff"
    ATTRS{gnptxfsiz}=="GNPTXFSIZ = 0x01000306"
    ATTRS{guid}=="GUID = 0x2708a000"
    ATTRS{regdump}=="Register Dump"
    ATTRS{hprt0}=="HPRT0 = 0x00001005"
    ATTRS{hcddump}=="HCD Dump"
    ATTRS{rem_wakeup_pwrdn}==""
    ATTRS{regvalue}=="invalid offset"
    ATTRS{gusbcfg}=="GUSBCFG = 0x20001700"
    ATTRS{fr_interval}=="Frame Interval = 0x1d4b"
    ATTRS{busconnected}=="Bus Connected = 0x1"
    ATTRS{remote_wakeup}=="Remote Wakeup Sig = 0 Enabled = 0 LPM Remote Wakeup = 0"
    ATTRS{devspeed}=="Device Speed = 0x0"
    ATTRS{rd_reg_test}=="Time to read GNPTXFSIZ reg 10000000 times: 1200 msecs (120 jiffies)"
    ATTRS{enumspeed}=="Device Enumeration Speed = 0x1"
    ATTRS{inv_sel_hsic}=="Invert Select HSIC = 0x0"
    ATTRS{ggpio}=="GGPIO = 0x00000000"
    ATTRS{srp}=="SesReqScs = 0x1"

  looking at parent device '/devices/platform/soc':
    KERNELS=="soc"
    SUBSYSTEMS=="platform"
    DRIVERS==""
    ATTRS{driver_override}=="(null)"

  looking at parent device '/devices/platform':
    KERNELS=="platform"
    SUBSYSTEMS==""
    DRIVERS==""

I have no idea whether this is related or not but you should perhaps know that I am also experiencing this issue with the same hardware: https://forums.balena.io/t/gsm-module-crash-on-boot/2418

any update here? I have several usb serial converters (the same model) that i need to predictably have enumerated using udev rules. i cant get this to work either.

I believe this is the PR that will resolve this issue: https://github.com/resin-os/meta-resin/pull/1206

@nathan Thanks for providing that PR.

This is now available in balenaOS, with documentation here: https://github.com/balena-os/meta-balena#udevrules

There is also an example of use here: https://github.com/balena-io-playground/gps-tracker

1 Like

@CameronDiver, this is indeed a great improvement that solves part of my initial problem. However, I need an option to edit the rules “runtime” from a container. Is this possible at the moment?

Hi, you can take a look at this project to achieve inserting udev rule into configuration: https://github.com/balena-io-playground/udev-rule-insert

It seems to do the job from my machine. So to achieve this from a container now, I would need to install the CLI in the container?

Although the udev rule was added the device also fell offline and stayed so for 10 minutes until I performed a manual reboot.

Hi @mhe-lorenz,

Apologies for the miscommunication, that project is primarily intended to update devices remotely (I don’t know that we’ve ever considered running it from a container!).

Taking a step back, would you mind describing a little bit more about how you need to configure these devices, and why that must be done from the container? Understanding a bit more about your use-case will better help us recommend solutions.

Editing runtime is probably not the right way to phrase it. Let me elaborate a bit on our thoughts.

What I would like to a achieve is a “self-contained” device that is capable of configuring itself while only interacting with the Balena Cloud. Since BalenaOS is build to serve containers I thought that would be the natural approach.

Our devices are connected to various hardware which is not under our control and thus we need to edit the udev rules. The devices are offline most of the time since they are powered by portable devices that is only turned on while in use.

Finally, we would like editing of the udev rules to versioned along with the rest of the application.

Hi,
Yeah this is something that we want to have in our product as a feature, but currently we don’t have support for this. Approaches that we where thinking about would be to add an api for this to the supervisor, which will allow your application to issue udev rules and trigger udev reloads on the device. Would this approach work for you?
I’m adding this to our internal feature request ticket, so that we can update this thread when we have some progress on it. The mentioned project from above is our current bridge gap solution for the problem.

A udev api for the supervisor sounds like a good solution, especially if it is possible to list, edit, add, and delete rules.

I am looking forward to updates once you get to this feature.