2

I built a PPS-capable GPS clock on my Raspberry Pi, and to get it to work, I needed to use udev to create symlinks to the hardware serial port and the kernel GPIO PPS on /dev/gps0 and /dev/gpspps0. It doesn't have to be 0, I just need the two numbers to be the same so that NTP can pick up the PPS signal. I created the following udev rules file to do this:

# Symlink /dev/ttyAMA0 to /dev/gps0
KERNEL=="ttyAMA0", SUBSYSTEM=="tty", DRIVER=="", SYMLINK+="gps0"
# Symlink /dev/pps0 to /dev/gpspps0
KERNEL=="pps0", SUBSYSTEM=="pps", DRIVER=="", SYMLINK+="gpspps0"

On its own, this behaves just fine. The symlinks are created and NTP happily acquires a PPS lock on the device. The following devices are generated:

pi@srv-GPiS-00:~ $ ls -al /dev/gps*
lrwxrwxrwx 1 root root 7 Aug 12 16:31 /dev/gps0 -> ttyAMA0
lrwxrwxrwx 1 root root 4 Aug 12 16:31 /dev/gpspps0 -> pps0

However, if a USB GPS that is processed by 60-gpsd.rules is plugged in at boot time, the symlink gps0 is overwritten to point to ttyUSB0, as shown here:

pi@srv-GPiS-00:~ $ ls -al /dev/gps*
lrwxrwxrwx 1 root root 7 Aug 12 16:45 /dev/gps0 -> ttyUSB0
lrwxrwxrwx 1 root root 4 Aug 12 16:45 /dev/gpspps0 -> pps0

(before you ask why I have two GPSes plugged into the same computer, I'm trying to debug and calibrate the second GPS against my known-working system)

Unplugging the device allows the symlink to return to ttyAMA0. Interestingly enough, when the second GPS is plugged back in, udev has a secondary behavior and puts it at ttyUSB1 and acquires the symlink gps1 for it:

pi@srv-GPiS-00:~ $ ls -al /dev/gps*
lrwxrwxrwx 1 root root 7 Aug 12 16:48 /dev/gps0 -> ttyAMA0
lrwxrwxrwx 1 root root 7 Aug 12 16:48 /dev/gps1 -> ttyUSB1
lrwxrwxrwx 1 root root 4 Aug 12 16:45 /dev/gpspps0 -> pps0

I would like the system to have this secondary behavior so that NTP cannot be interfered with if I reboot the system with the second GPS attached.

I have tried changing the filename of my udev rules to 50 from 90 to no effect, and I have also tried specifying gps%n/gpspps%n in my rules file instead of gps0/gpspps0(like in 60-gpsd.rules), which results in completely eliminating the secondary behavior. I also know of the := operator for udev which sets a parameter finally, but I do not know how this interacts with two devices vying for the same symlink, and I also do not want to clobber any other symlink rules from other files.

Granted, yes, I could hardcode my device to be gps3/gpspps3, which I believe is the maximum device number supported by NTP. However, I want to figure out why udev is doing what it's doing. I would also like to avoid removing/disabling the 60-gpsd.rules file, since that is duct tape and the behavior would not be stable across many OS upgrades.

Any thoughts?

Additional info:

pi@srv-GPiS-00:~ $ udevadm info --name=/dev/ttyAMA0 --attribute-walk
...
  looking at device '/devices/platform/soc/3f201000.serial/tty/ttyAMA0':
    KERNEL=="ttyAMA0"
    SUBSYSTEM=="tty"
    DRIVER==""

  looking at parent device '/devices/platform/soc/3f201000.serial':
    KERNELS=="3f201000.serial"
    SUBSYSTEMS=="amba"
    DRIVERS=="uart-pl011"
    ATTRS{id}=="00241011"
    ATTRS{irq0}=="87"
    ATTRS{driver_override}==""

  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==""

pi@srv-GPiS-00:~ $ udevadm info --name=/dev/ttyUSB1 --attribute-walk
...
  looking at device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.4/1-1.4:1.0/ttyUSB1/tty/ttyUSB1':
    KERNEL=="ttyUSB1"
    SUBSYSTEM=="tty"
    DRIVER==""

  looking at parent device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.4/1-1.4:1.0/ttyUSB1':
    KERNELS=="ttyUSB1"
    SUBSYSTEMS=="usb-serial"
    DRIVERS=="garmin_gps"
    ATTRS{port_number}=="0"

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

  looking at parent device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.4':
    KERNELS=="1-1.4"
    SUBSYSTEMS=="usb"
    DRIVERS=="usb"
    ATTRS{bDeviceClass}=="ff"
    ATTRS{bmAttributes}=="c0"
    ATTRS{bConfigurationValue}=="1"
    ATTRS{version}==" 1.10"
    ATTRS{devnum}=="7"
    ATTRS{bMaxPower}=="0mA"
    ATTRS{idProduct}=="0003"
    ATTRS{avoid_reset_quirk}=="0"
    ATTRS{urbnum}=="3412"
    ATTRS{bDeviceSubClass}=="ff"
    ATTRS{maxchild}=="0"
    ATTRS{bcdDevice}=="0001"
    ATTRS{bMaxPacketSize0}=="8"
    ATTRS{idVendor}=="091e"
    ATTRS{speed}=="12"
    ATTRS{removable}=="removable"
    ATTRS{ltm_capable}=="no"
    ATTRS{bNumConfigurations}=="1"
    ATTRS{busnum}=="1"
    ATTRS{authorized}=="1"
    ATTRS{quirks}=="0x0"
    ATTRS{configuration}==""
    ATTRS{devpath}=="1.4"
    ATTRS{bDeviceProtocol}=="ff"
    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}=="81"
    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.35-v7+ 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: 690 msecs (69 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: 940 msecs (94 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==""
1
  • You should look for some other rule that is creating the links, as your rule cannot create gps1. There are rules in (at least) /etc/udev/rules.d/ and /lib/udev/rules.d/. Run udevadm monitor and hot-plug the gps device to see what is happening.
    – meuh
    Aug 13, 2017 at 17:24

0

You must log in to answer this question.

Browse other questions tagged .