Allow the choice of PPS signal captured by uart(4) to be runtime-configured,

eliminating the need to build a custom kernel to use the CTS signal.

The historical UART_PPS_ON_CTS kernel option is still honored, but now it
can be overridden at runtime using a tunable to configure all uart devices
(hw.uart.pps_mode) or specific devices (dev.uart.#.pps_mode).  The per-
device config is both a tunable and a writable sysctl.

This syncs the PPS capabilities of uart(4) with the enhancements recently
recently added to ucom(4) for capturing from USB serial devices.

Relnotes:	yes
This commit is contained in:
Ian Lepore 2015-08-10 20:08:09 +00:00
parent 9138b6e04a
commit 196d3019a8
3 changed files with 145 additions and 16 deletions

View File

@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd March 12, 2008
.Dd August 10, 2015
.Dt UART 4
.Os
.Sh NAME
@ -151,6 +151,39 @@ SCC: serial communications controllers supported by the
device driver.
.El
.\"
.Sh Pulse Per Second (PPS) Timing Interface
The
.Nm
driver can capture PPS timing information as defined in RFC 2783.
The API, accessed via
.Xr ioctl 8 ,
is available on the tty device.
To use the PPS capture feature with
.Xr ntpd 8 ,
symlink the tty device to
.Va /dev/pps0.
.Pp
The
.Va hw.uart.pps_mode
tunable configures the PPS capture mode for all uart devices;
it can be set in
.Xr loader.conf 5 .
The
.Va dev.uart.0.pps_mode
sysctl configures the PPS capture mode for a specific uart device;
it can be set in
.Xr loader.conf 5
or
.Xr sysctl.conf 5 .
The following capture modes are available:
.Bl -tag -compact -offset "mmmm" -width "mmmm"
.It 0
Capture disabled.
.It 1
Capture pulses on the CTS line.
.It 2
Capture pulses on the DCD line (default).
.El
.Sh FILES
.Bl -tag -width ".Pa /dev/ttyu?.init" -compact
.It Pa /dev/ttyu?

View File

@ -48,14 +48,6 @@
#define UART_STAT_OVERRUN 0x0400
#define UART_STAT_PARERR 0x0800
#ifdef UART_PPS_ON_CTS
#define UART_SIG_DPPS SER_DCTS
#define UART_SIG_PPS SER_CTS
#else
#define UART_SIG_DPPS SER_DDCD
#define UART_SIG_PPS SER_DCD
#endif
/* UART_IOCTL() requests */
#define UART_IOCTL_BREAK 1
#define UART_IOCTL_IFLOW 2
@ -120,6 +112,7 @@ struct uart_softc {
/* Pulse capturing support (PPS). */
struct pps_state sc_pps;
int sc_pps_mode;
/* Upper layer data. */
void *sc_softih;

View File

@ -70,6 +70,111 @@ static int uart_force_poll;
SYSCTL_INT(_debug, OID_AUTO, uart_force_poll, CTLFLAG_RDTUN, &uart_force_poll,
0, "Force UART polling");
#define PPS_MODE_DISABLED 0
#define PPS_MODE_CTS 1
#define PPS_MODE_DCD 2
static inline int
uart_pps_signal(int pps_mode)
{
switch(pps_mode)
{
case PPS_MODE_CTS:
return (SER_CTS);
case PPS_MODE_DCD:
return (SER_DCD);
}
return (0);
}
static inline int
uart_pps_mode_valid(int pps_mode)
{
switch(pps_mode)
{
case PPS_MODE_DISABLED:
case PPS_MODE_CTS:
case PPS_MODE_DCD:
return (true);
}
return (false);
}
static const char *
uart_pps_mode_name(int pps_mode)
{
switch(pps_mode)
{
case PPS_MODE_DISABLED:
return ("disabled");
case PPS_MODE_CTS:
return ("CTS");
case PPS_MODE_DCD:
return ("DCD");
}
return ("invalid");
}
static int
uart_pps_mode_sysctl(SYSCTL_HANDLER_ARGS)
{
struct uart_softc *sc;
int err, tmp;
sc = arg1;
tmp = sc->sc_pps_mode;
err = sysctl_handle_int(oidp, &tmp, 0, req);
if (err != 0 || req->newptr == NULL)
return (err);
if (!uart_pps_mode_valid(tmp))
return (EINVAL);
sc->sc_pps_mode = tmp;
return(0);
}
static void
uart_pps_init(struct uart_softc *sc)
{
struct sysctl_ctx_list *ctx;
struct sysctl_oid *tree;
ctx = device_get_sysctl_ctx(sc->sc_dev);
tree = device_get_sysctl_tree(sc->sc_dev);
/*
* The historical default for pps capture mode is either DCD or CTS,
* depending on the UART_PPS_ON_CTS kernel option. Start with that,
* then try to fetch the tunable that overrides the mode for all uart
* devices, then try to fetch the sysctl-tunable that overrides the mode
* for one specific device.
*/
#ifdef UART_PPS_ON_CTS
sc->sc_pps_mode = PPS_MODE_CTS;
#else
sc->sc_pps_mode = PPS_MODE_DCD;
#endif
TUNABLE_INT_FETCH("hw.uart.pps_mode", &sc->sc_pps_mode);
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "pps_mode",
CTLTYPE_INT | CTLFLAG_RWTUN, sc, 0, uart_pps_mode_sysctl, "I",
"pulse capturing mode - 0/1/2 - disabled/CTS/DCD");
if (!uart_pps_mode_valid(sc->sc_pps_mode)) {
device_printf(sc->sc_dev,
"Invalid pps_mode %d configured; disabling PPS capture\n",
sc->sc_pps_mode);
sc->sc_pps_mode = PPS_MODE_DISABLED;
} else if (bootverbose) {
device_printf(sc->sc_dev, "PPS capture mode %d (%s)\n",
sc->sc_pps_mode, uart_pps_mode_name(sc->sc_pps_mode));
}
sc->sc_pps.ppscap = PPS_CAPTUREBOTH;
sc->sc_pps.driver_mtx = uart_tty_getlock(sc);
sc->sc_pps.driver_abi = PPS_ABI_VERSION;
pps_init_abi(&sc->sc_pps);
}
void
uart_add_sysdev(struct uart_devinfo *di)
{
@ -211,14 +316,15 @@ static __inline int
uart_intr_sigchg(void *arg)
{
struct uart_softc *sc = arg;
int new, old, sig;
int new, old, pps_sig, sig;
sig = UART_GETSIG(sc);
if (sc->sc_pps.ppsparam.mode & PPS_CAPTUREBOTH) {
if (sig & UART_SIG_DPPS) {
pps_sig = uart_pps_signal(sc->sc_pps_mode);
if (sig & SER_DELTA(pps_sig)) {
pps_capture(&sc->sc_pps);
pps_event(&sc->sc_pps, (sig & UART_SIG_PPS) ?
pps_event(&sc->sc_pps, (sig & pps_sig) ?
PPS_CAPTUREASSERT : PPS_CAPTURECLEAR);
}
}
@ -571,10 +677,7 @@ uart_bus_attach(device_t dev)
} else {
if ((error = uart_tty_attach(sc)) != 0)
goto fail;
sc->sc_pps.ppscap = PPS_CAPTUREBOTH;
sc->sc_pps.driver_mtx = uart_tty_getlock(sc);
sc->sc_pps.driver_abi = PPS_ABI_VERSION;
pps_init_abi(&sc->sc_pps);
uart_pps_init(sc);
}
if (sc->sc_sysdev != NULL)