May 27

udev rules for multiple interfaces

I just had to write a udev rule for Linux to interpret between several different endpoints on a single cellular modem, specifically this modem.  This shows up in Linux as 5 ttyUSB ports.  In order to make sure that the ports do not change around on me, I checked the following with udevadm:

$ udevadm info -a -n /dev/ttyUSB1

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/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.1/4-1.1:1.0/ttyUSB1/tty/ttyUSB1':
 KERNEL=="ttyUSB1"
 SUBSYSTEM=="tty"
 DRIVER==""

 looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.1/4-1.1:1.0/ttyUSB1':
 KERNELS=="ttyUSB1"
 SUBSYSTEMS=="usb-serial"
 DRIVERS=="option1"
 ATTRS{port_number}=="0"

 looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.1/4-1.1:1.0':
 KERNELS=="4-1.1:1.0"
 SUBSYSTEMS=="usb"
 DRIVERS=="option"
 ATTRS{bInterfaceClass}=="ff"
 ATTRS{bInterfaceSubClass}=="ff"
 ATTRS{bInterfaceProtocol}=="ff"
 ATTRS{bNumEndpoints}=="02"
 ATTRS{supports_autosuspend}=="1"
 ATTRS{bAlternateSetting}==" 0"
 ATTRS{bInterfaceNumber}=="00"

 looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.1':
 KERNELS=="4-1.1"
 SUBSYSTEMS=="usb"
 DRIVERS=="usb"
 ATTRS{bDeviceSubClass}=="00"
 ATTRS{bDeviceProtocol}=="00"
 ATTRS{devpath}=="1.1"
 ATTRS{idVendor}=="1bc7"
 ATTRS{speed}=="480"
 ATTRS{bNumInterfaces}==" 8"
 ATTRS{bConfigurationValue}=="1"
 ATTRS{bMaxPacketSize0}=="64"
 ATTRS{busnum}=="4"
 ATTRS{devnum}=="11"
 ATTRS{configuration}==""
 ATTRS{bMaxPower}=="500mA"
 ATTRS{authorized}=="1"
 ATTRS{bmAttributes}=="80"
 ATTRS{bNumConfigurations}=="1"
 ATTRS{maxchild}=="0"
 ATTRS{bcdDevice}=="0232"
 ATTRS{avoid_reset_quirk}=="0"
 ATTRS{quirks}=="0x0"
 ATTRS{serial}=="0123456789ABCDEF"
 ATTRS{version}==" 2.00"
 ATTRS{urbnum}=="194649"
 ATTRS{ltm_capable}=="no"
 ATTRS{manufacturer}=="Android"
 ATTRS{removable}=="unknown"
 ATTRS{idProduct}=="1201"
 ATTRS{bDeviceClass}=="00"
 ATTRS{product}=="Android"

 looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb4/4-1':
 KERNELS=="4-1"
 SUBSYSTEMS=="usb"
 DRIVERS=="usb"
 ATTRS{bDeviceSubClass}=="00"
 ATTRS{bDeviceProtocol}=="01"
 ATTRS{devpath}=="1"
 ATTRS{idVendor}=="8087"
 ATTRS{speed}=="480"
 ATTRS{bNumInterfaces}==" 1"
 ATTRS{bConfigurationValue}=="1"
 ATTRS{bMaxPacketSize0}=="64"
 ATTRS{busnum}=="4"
 ATTRS{devnum}=="2"
 ATTRS{configuration}==""
 ATTRS{bMaxPower}=="0mA"
 ATTRS{authorized}=="1"
 ATTRS{bmAttributes}=="e0"
 ATTRS{bNumConfigurations}=="1"
 ATTRS{maxchild}=="8"
 ATTRS{bcdDevice}=="0000"
 ATTRS{avoid_reset_quirk}=="0"
 ATTRS{quirks}=="0x0"
 ATTRS{version}==" 2.00"
 ATTRS{urbnum}=="204"
 ATTRS{ltm_capable}=="no"
 ATTRS{removable}=="unknown"
 ATTRS{idProduct}=="0024"
 ATTRS{bDeviceClass}=="09"

 looking at parent device '/devices/pci0000:00/0000:00:1d.0/usb4':
 KERNELS=="usb4"
 SUBSYSTEMS=="usb"
 DRIVERS=="usb"
 ATTRS{bDeviceSubClass}=="00"
 ATTRS{bDeviceProtocol}=="00"
 ATTRS{devpath}=="0"
 ATTRS{idVendor}=="1d6b"
 ATTRS{speed}=="480"
 ATTRS{bNumInterfaces}==" 1"
 ATTRS{bConfigurationValue}=="1"
 ATTRS{bMaxPacketSize0}=="64"
 ATTRS{authorized_default}=="1"
 ATTRS{busnum}=="4"
 ATTRS{devnum}=="1"
 ATTRS{configuration}==""
 ATTRS{bMaxPower}=="0mA"
 ATTRS{authorized}=="1"
 ATTRS{bmAttributes}=="e0"
 ATTRS{bNumConfigurations}=="1"
 ATTRS{maxchild}=="2"
 ATTRS{bcdDevice}=="0316"
 ATTRS{avoid_reset_quirk}=="0"
 ATTRS{quirks}=="0x0"
 ATTRS{serial}=="0000:00:1d.0"
 ATTRS{version}==" 2.00"
 ATTRS{urbnum}=="24"
 ATTRS{ltm_capable}=="no"
 ATTRS{manufacturer}=="Linux 3.16.0-4-amd64 ehci_hcd"
 ATTRS{removable}=="unknown"
 ATTRS{idProduct}=="0002"
 ATTRS{bDeviceClass}=="09"
 ATTRS{product}=="EHCI Host Controller"

 looking at parent device '/devices/pci0000:00/0000:00:1d.0':
 KERNELS=="0000:00:1d.0"
 SUBSYSTEMS=="pci"
 DRIVERS=="ehci-pci"
 ATTRS{irq}=="23"
 ATTRS{subsystem_vendor}=="0x1458"
 ATTRS{broken_parity_status}=="0"
 ATTRS{class}=="0x0c0320"
 ATTRS{companion}==""
 ATTRS{driver_override}=="(null)"
 ATTRS{consistent_dma_mask_bits}=="32"
 ATTRS{dma_mask_bits}=="32"
 ATTRS{local_cpus}=="00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,000000ff"
 ATTRS{device}=="0x1c26"
 ATTRS{uframe_periodic_max}=="100"
 ATTRS{enable}=="1"
 ATTRS{msi_bus}==""
 ATTRS{local_cpulist}=="0-7"
 ATTRS{vendor}=="0x8086"
 ATTRS{subsystem_device}=="0x5006"
 ATTRS{numa_node}=="-1"
 ATTRS{d3cold_allowed}=="1"

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

After I got the info, I noticed that the one thing that was different between all of the ttyUSB devices was ATTRS{bInterfaceNumber}.  So, I wrote a rule that looked like this:

ATTRS{idProduct}=="1201", ATTRS{idVendor}=="1bc7", ATTRS{bInterfaceNumber}=="00", SYMLINK+="cellular_1"

However, this did not produce the expected result of a new symlink.  It appears as though you can’t get the udev information for a product/vendor and the interface number in the same rule.  However, there are environment variables that are set which you can use.  If you run udevadm test, you can see what exactly the environment variables are that are set:

$ udevadm test --action=add `udevadm info -q path -n /dev/ttyUSB1`

...skip a bunch of data...
ACTION=add
DEVLINKS=/dev/serial/by-id/usb-Android_Android_0123456789ABCDEF-if00-port0 /dev/serial/by-path/pci-0000:00:1d.0-usb-0:1.1:1.0-port0
DEVNAME=/dev/ttyUSB1
DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.1/4-1.1:1.0/ttyUSB1/tty/ttyUSB1
ID_BUS=usb
ID_MODEL=Android
ID_MODEL_ENC=Android
ID_MODEL_ID=1201
ID_PATH=pci-0000:00:1d.0-usb-0:1.1:1.0
ID_PATH_TAG=pci-0000_00_1d_0-usb-0_1_1_1_0
ID_REVISION=0232
ID_SERIAL=Android_Android_0123456789ABCDEF
ID_SERIAL_SHORT=0123456789ABCDEF
ID_TYPE=generic
ID_USB_DRIVER=option
ID_USB_INTERFACES=:ffffff:ff4201:ff0000:080650:
ID_USB_INTERFACE_NUM=00
ID_VENDOR=Android
ID_VENDOR_ENC=Android
ID_VENDOR_FROM_DATABASE=Telit Wireless Solutions
ID_VENDOR_ID=1bc7
MAJOR=188
MINOR=1
SUBSYSTEM=tty
TAGS=:systemd:
USEC_INITIALIZED=91898933

These variables can also be used in udev rules to do differentiate between different devices.  The two that we are interested in are ID_VENDOR_ID and ID_MODEL_ID, as these correspond to ATTRS{idProduct} and ATTRS{idVendor}.  Our new rule becomes:

DRIVERS=="option", ATTRS{bInterfaceNumber}=="00", ENV{ID_MODEL_ID}=="1201", ENV{ID_VENDOR_ID}=="1bc7", SYMLINK+="cellular_1"

The DRIVERS part of the rule may not be strictly necessary, but it can’t hurt it.

May 19

Export Board from Altium for OSHPark

Here’s a quick guide on exporting a board from Altium Designer 14 to be made by OSHPark.

1. Go to Design -> Layer Stack Manager
2. Set your stackup to be this:

.LayerStackManager

This information was derived from this FAQ.
3. Go to Project -> Compile PCB Project.  Fix any errors that could cause a problem.
4. Go to File -> Fabrication Outputs -> Gerber Files
5. Under ‘General’, set the units to be ‘Inches’ and the Format to be ‘2:4’
6. Under ‘Layers’, select the following layers:

  • Top Overlay
  • Top Paste
  • Top Solder
  • Top Layer
  • Bottom Layer
  • Bottom Solder
  • Bottom Paste
  • Bottom Overlay
  • Mechanical 15(assuming that you have drawn your board outline on mechanical 15)

Now export, save as <projectname>.cam

7. Go to File -> Fabrication Outputs -> NC Drill.  Use the following settings:

  • Units: Inches
  • Format: 2:4
  • Leading/Trailing Zeros: Suprress trailing zeros
  • Coordinate Positions: Reference to relative origin
  • Other: Optimize change location commands

Save as <projectname>-NCDRILL.cam

8. With the NCDRILL.cam file selected, go to File->Export->SaveDrill.  Set the extension to be .xln

9. Go to your project folder, there should now be a sub-folder called ‘Project Outputs for <Project Name>’.  Grab the following files:

  • *.xln
  • *.GBL
  • *.GBO
  • *.GBP
  • *.GBS
  • *.GKO(re-name the .GM15 file to be .GKO)
  • *.GTL
  • *.GTO
  • *.GTP
  • *.GTS

Put these all into a .zip folder.

10. Upload the .zip folder to OSH Park.

11. Have fun!