The GNSS Subsystem - Johan Hovold, Hovold Consulting AB [Open Source Summit EU 2018]

GNSS (Global Navigation Satellite Subsystem) receivers are currently managed in userspace. With serdev, it is possible to move them into the kernel.

Why move it in the kernel? For power management, but also to simplify the driver architecture.

There are about 5.8G GNSS receivers today, 8G in 2021.

An important parameter of a GNSS receiver is the time to first fix (TTFF): when turning on the receiver, how long before you get a position. Cold start, warm start and hot start scenarios.

I/O interface of GNSS is typically UART (or USB-ACM), also I2C, MMIO, SDIO, even rpmsg. You get reports (positions, time, satellites in view) and can send control. Additionally there are enable and reset signals.

Protocols both for reports and control. Main standard is NMEA 0183, general protocol for communication between navigation equipment. Standard is proprietary but widely reverse-engineered. In addition to NMEA usually also vendor protocols, switch at runtime with a command. NMEA is an ASCII protocol with a checksum. Different messages for different information. GGA is the main sentence, gives position and time and velocity and number of satellites. However, none of the reports are complete, e.g. error information is not in GGA message. Specification doesn’t say anything about synchronisation between messages (although most are timestamped).

Traditionally in Linux GNSS is done in userspace by gpsd or Android location services. Mainly UART interface, custom hacks for non-UART interfaces. Thus, description lives in userspace, e.g. identify the port to open. Mostly goes through the TTY layer. gpsd tries to autodetect: probe serial ports, try different baud rates, try to recognize different vendor protocols, … Power management can be done through UART (DTR) or with sideband GPIOs or with control commands.

In GTA04 (OpenMoko), teh SiRFstar GPS receiver has an onoff input but no wakeup output signal. So to determine the power state, you have to monitor the data channel and toggle the onoff if no data is coming. This can be done better in the kernel, and with serdev it’s finally possible to do that. But that requires a userspace interface for GNSS devices.

The GNSS subsystem should abstract the I/O interface but still know about the protocols. It should do device description (device tree) and discovery (ACPI). It should be able to do power management.

Moving everythign in the kernel has some issues. Proprietary protocol can give legal issues, or only a userspace binary is available for parsing. String parsing in the kernel is risky. There are a ton of device-dependent features and quirks to support. It’s hard to generalise the protocols to a single userspace API, and it would still require new userspace services. Also some conversions would need floating point. Therefore, it was decided to keep the protocol handling in userspace, and move just the device management and I/O interface abstraction in the kernel. This way, current userspace (talking to gpsd) remains compatible, and currently supported protocols stay supported. It is also easier to move drivers into the kernel one by one. It was merged in 4.19.

User API is /sys/class/gnss/gnss0. type attribute gives the type of uevent that is available. on a character device. Reading from the chardev is a pollable read with a 4K buffer. DT describes needed power supplies and reset lines. Driver interface consumes these to allocate resources. Driver operations are open, close and write_raw (for control); driver is responsible for calling the read function when new reports are received.

Because many of the drivers are based on serdev, there is a generic serial gnss device driver. Then only the set_power callback needs to be implemented.

Currently drivers merged for SiRFstar and u-blox.

A major limitation at the moment is line speed change. NMEA is supposed to start up in 4800 bauds, then you can have a control command to change the speed. But the control is done in userspace while the serial management is done in the kernel.

Hotplugging of USB devices is no problem, but serdev doesn’t support hotplugging at the moment (actually serdev isn’t even enabled for USB serial).

Some GNSS receivers are integrated in a modem. These can then use A-GPS to reduce TTFF. However, modems are managed entirely in userspace, and through several interfaces to the kernel: tty, socket, CDC chardev. For example, it’s possible that you have to power on the receiver over the modem port but that the NMEA comes out on a different serial port.

This could be handled through userspace drivers, like is done already with uhid and uinput. So the ugnss driver would allow userspace to inject gnss messages back into the kernel. That way only one interface is needed for gpsd (always the gnss chardev interface). That’s also a way to emulate a receiver for testing.

Possible extensions are PPS support (time pulse pin on a GPIO), control of the low-noise amplifiers. It may still be worth consideration to move protocol handling into the kernel as well.

Multiple interfaces (e.g. 2 serial ports) to the receiver is not supported at the moment.