Subject: kern/27201: Add (partial) PPS support to "ucom" driver
To: None <gnats-bugs@gnats.NetBSD.org>
From: None <paul@Plectere.com>
List: netbsd-bugs
Date: 10/10/2004 03:06:15
>Number:         27201
>Category:       kern
>Synopsis:       The "ucom" driver doesn't support PPS, most serial drivers do.
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Sun Oct 10 10:07:01 UTC 2004
>Closed-Date:
>Last-Modified:
>Originator:     Paul Shupak
>Release:        NetBSD 2.99.9
>Organization:
	
>Environment:
	
	
System: NetBSD cobalt 2.99.9 NetBSD 2.99.9 (COBALT-$Revision: 1.4 $) #730: Fri Oct  8 22:00:40 PDT 2004  root@svcs:/sys/arch/i386/compile/COBALT i386
Architecture: i386
Machine: i386
>Description:
	The "ucom" driver doesn't have any of the `standard' PPS support/glue code.
>How-To-Repeat:
	Examine the IOCTL list in the code and then compar to other serial drivers (e.g.
	sys/ic/com.c).  Or just try to use PPS on a "ucom" attached GPS.
>Fix:
	
	The following patch allow "ntpd" to specify PPS, but it still doesn't seem
to quite work (to much interrupt delay variation probably), but "ntpd" no longer crashes
is PPS is requested.
--------------------------------------------------------------------------------
% cvs diff -c sys/dev/udb/ucom.c

Index: ucom.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/ucom.c,v
retrieving revision 1.53
diff -c -r1.53 ucom.c
*** ucom.c	13 Sep 2004 12:55:49 -0000	1.53
--- ucom.c	10 Oct 2004 09:57:20 -0000
***************
*** 52,57 ****
--- 52,58 ----
  #include <sys/file.h>
  #include <sys/select.h>
  #include <sys/proc.h>
+ #include <sys/timepps.h>
  #include <sys/vnode.h>
  #include <sys/device.h>
  #include <sys/poll.h>
***************
*** 133,138 ****
--- 134,146 ----
  #if defined(__NetBSD__) && NRND > 0
  	rndsource_element_t	sc_rndsource;	/* random source */
  #endif
+ 	/* PPS signal on DCD, with or without inkernel clock disciplining */
+ 	u_char	sc_ppsmask;			/* pps signal mask */
+ 	u_char	sc_ppsassert;			/* pps leading edge */
+ 	u_char	sc_ppsclear;			/* pps trailing edge */
+ 	pps_info_t ppsinfo;
+ 	pps_params_t ppsparam;
+ 
  };
  
  dev_type_open(ucomopen);
***************
*** 149,154 ****
--- 157,168 ----
  	ucomstop, ucomtty, ucompoll, nommap, ttykqfilter, D_TTY
  };
  
+ static int ppscap =
+ 	PPS_TSFMT_TSPEC |
+ 	PPS_CAPTUREASSERT |
+ 	PPS_CAPTURECLEAR |
+ 	PPS_OFFSETASSERT | PPS_OFFSETCLEAR;
+ 
  Static void	ucom_cleanup(struct ucom_softc *);
  Static void	ucom_hwiflow(struct ucom_softc *);
  Static int	ucomparam(struct tty *, struct termios *);
***************
*** 299,304 ****
--- 313,322 ----
  		ucom_dtr(sc, 0);
  		(void)tsleep(sc, TTIPRI, ttclos, hz);
  	}
+ 
+ 	/* Turn off PPS capture on last close. */
+ 	sc->sc_ppsmask = 0;
+ 	sc->ppsparam.mode = 0;
  }
  
  int
***************
*** 361,366 ****
--- 379,387 ----
  				splx(s);
  				return (error);
  			}
+ 			/* Clear PPS capture state on first open. */
+ 			sc->sc_ppsmask = 0;
+ 			sc->ppsparam.mode = 0;
  		}
  
  		ucom_status_change(sc);
***************
*** 673,678 ****
--- 694,812 ----
  		*(int *)data = ucom_to_tiocm(sc);
  		break;
  
+ 	case PPS_IOC_CREATE:
+ 		break;
+ 
+ 	case PPS_IOC_DESTROY:
+ 		break;
+ 
+ 	case PPS_IOC_GETPARAMS: {
+ 		pps_params_t *pp;
+ 		pp = (pps_params_t *)data;
+ 		*pp = sc->ppsparam;
+ 		break;
+ 	}
+ 
+ 	case PPS_IOC_SETPARAMS: {
+ 	  	pps_params_t *pp;
+ 		int mode;
+ 		pp = (pps_params_t *)data;
+ 		if (pp->mode & ~ppscap) {
+ 			error = EINVAL;
+ 			break;
+ 		}
+ 		sc->ppsparam = *pp;
+ 	 	/* 
+ 		 * Compute msr masks from user-specified timestamp state.
+ 		 */
+ 		mode = sc->ppsparam.mode;
+ 		switch (mode & PPS_CAPTUREBOTH) {
+ 		case 0:
+ 			sc->sc_ppsmask = 0;
+ 			break;
+ 	
+ 		case PPS_CAPTUREASSERT:
+ 			sc->sc_ppsmask = UMSR_DCD;
+ 			sc->sc_ppsassert = UMSR_DCD;
+ 			sc->sc_ppsclear = -1;
+ 			break;
+ 	
+ 		case PPS_CAPTURECLEAR:
+ 			sc->sc_ppsmask = UMSR_DCD;
+ 			sc->sc_ppsassert = -1;
+ 			sc->sc_ppsclear = 0;
+ 			break;
+ 
+ 		case PPS_CAPTUREBOTH:
+ 			sc->sc_ppsmask = UMSR_DCD;
+ 			sc->sc_ppsassert = UMSR_DCD;
+ 			sc->sc_ppsclear = 0;
+ 			break;
+ 
+ 		default:
+ 			error = EINVAL;
+ 			break;
+ 		}
+ 		break;
+ 	}
+ 
+ 	case PPS_IOC_GETCAP:
+ 		*(int*)data = ppscap;
+ 		break;
+ 
+ 	case PPS_IOC_FETCH: {
+ 		pps_info_t *pi;
+ 		pi = (pps_info_t *)data;
+ 		*pi = sc->ppsinfo;
+ 		break;
+ 	}
+ 
+ #ifdef PPS_SYNC
+ 	case PPS_IOC_KCBIND: {
+ 		int edge = (*(int *)data) & PPS_CAPTUREBOTH;
+ 
+ 		if (edge == 0) {
+ 			/*
+ 			 * remove binding for this source; ignore
+ 			 * the request if this is not the current
+ 			 * hardpps source
+ 			 */
+ 			if (pps_kc_hardpps_source == sc) {
+ 				pps_kc_hardpps_source = NULL;
+ 				pps_kc_hardpps_mode = 0;
+ 			}
+ 		} else {
+ 			/*
+ 			 * bind hardpps to this source, replacing any
+ 			 * previously specified source or edges
+ 			 */
+ 			pps_kc_hardpps_source = sc;
+ 			pps_kc_hardpps_mode = edge;
+ 		}
+ 		break;
+ 	}
+ #endif /* PPS_SYNC */
+ 
+ 	case TIOCDCDTIMESTAMP:	/* XXX old, overloaded  API used by xntpd v3 */
+ 		/*
+ 		 * Some GPS clocks models use the falling rather than
+ 		 * rising edge as the on-the-second signal. 
+ 		 * The old API has no way to specify PPS polarity.
+ 		 */
+ 		sc->sc_ppsmask = UMSR_DCD;
+ #ifndef PPS_TRAILING_EDGE
+ 		sc->sc_ppsassert = UMSR_DCD;
+ 		sc->sc_ppsclear = -1;
+ 		TIMESPEC_TO_TIMEVAL((struct timeval *)data, 
+ 		    &sc->ppsinfo.assert_timestamp);
+ #else
+ 		sc->sc_ppsassert = -1;
+ 		sc->sc_ppsclear = 0;
+ 		TIMESPEC_TO_TIMEVAL((struct timeval *)data, 
+ 		    &sc->ppsinfo.clear_timestamp);
+ #endif
+ 		break;
+ 
  	default:
  		error = EPASSTHROUGH;
  		break;
***************
*** 788,796 ****
  		old_msr = sc->sc_msr;
  		sc->sc_methods->ucom_get_status(sc->sc_parent, sc->sc_portno,
  		    &sc->sc_lsr, &sc->sc_msr);
! 		if (ISSET((sc->sc_msr ^ old_msr), UMSR_DCD))
  			(*tp->t_linesw->l_modem)(tp,
  			    ISSET(sc->sc_msr, UMSR_DCD));
  	} else {
  		sc->sc_lsr = 0;
  		sc->sc_msr = 0;
--- 922,978 ----
  		old_msr = sc->sc_msr;
  		sc->sc_methods->ucom_get_status(sc->sc_parent, sc->sc_portno,
  		    &sc->sc_lsr, &sc->sc_msr);
! 		if (ISSET((sc->sc_msr ^ old_msr), UMSR_DCD)) {
  			(*tp->t_linesw->l_modem)(tp,
  			    ISSET(sc->sc_msr, UMSR_DCD));
+ 			/*
+ 			 * Pulse-per-second (PSS) signals on edge of DCD?
+ 			 * Process these even if line discipline is ignoring DCD
+ 			 */
+ 			if (UMSR_DCD & sc->sc_ppsmask) {
+ 				struct timeval tv;
+ 				if ((sc->sc_msr & sc->sc_ppsmask) == sc->sc_ppsassert) {
+ 					/* XXX nanotime() */
+ 					microtime(&tv);
+ 					TIMEVAL_TO_TIMESPEC(&tv, 
+ 					    &sc->ppsinfo.assert_timestamp);
+ 					if (sc->ppsparam.mode & PPS_OFFSETASSERT) {
+ 						timespecadd(&sc->ppsinfo.assert_timestamp,
+ 						    &sc->ppsparam.assert_offset,
+ 							    &sc->ppsinfo.assert_timestamp);
+ 					}
+ 
+ #ifdef PPS_SYNC
+ 					if (pps_kc_hardpps_source == sc &&
+ 					    pps_kc_hardpps_mode & PPS_CAPTUREASSERT) {
+ 						hardpps(&tv, tv.tv_usec);
+ 					}
+ #endif
+ 					sc->ppsinfo.assert_sequence++;
+ 					sc->ppsinfo.current_mode = sc->ppsparam.mode;
+ 
+ 				} else if ((sc->sc_msr & sc->sc_ppsmask) == sc->sc_ppsclear) {
+ 					/* XXX nanotime() */
+ 					microtime(&tv);
+ 					TIMEVAL_TO_TIMESPEC(&tv, 
+ 					    &sc->ppsinfo.clear_timestamp);
+ 					if (sc->ppsparam.mode & PPS_OFFSETCLEAR) {
+ 						timespecadd(&sc->ppsinfo.clear_timestamp,
+ 						    &sc->ppsparam.clear_offset,
+ 						    &sc->ppsinfo.clear_timestamp);
+ 					}
+ 
+ #ifdef PPS_SYNC
+ 					if (pps_kc_hardpps_source == sc &&
+ 					    pps_kc_hardpps_mode & PPS_CAPTURECLEAR) {
+ 						hardpps(&tv, tv.tv_usec);
+ 					}
+ #endif
+ 					sc->ppsinfo.clear_sequence++;
+ 					sc->ppsinfo.current_mode = sc->ppsparam.mode;
+ 				}
+ 			}
+ 		}
  	} else {
  		sc->sc_lsr = 0;
  		sc->sc_msr = 0;
>Release-Note:
>Audit-Trail:
>Unformatted: