xref: /openbsd-src/sys/dev/usb/umbg.c (revision 81508fe356eb7772a68118f65f91723ce5261d7d)
1*81508fe3Sjsg /*	$OpenBSD: umbg.c,v 1.29 2024/05/23 03:21:09 jsg Exp $ */
22c06c139Smbalmer 
32c06c139Smbalmer /*
42c06c139Smbalmer  * Copyright (c) 2007 Marc Balmer <mbalmer@openbsd.org>
52c06c139Smbalmer  *
62c06c139Smbalmer  * Permission to use, copy, modify, and distribute this software for any
72c06c139Smbalmer  * purpose with or without fee is hereby granted, provided that the above
82c06c139Smbalmer  * copyright notice and this permission notice appear in all copies.
92c06c139Smbalmer  *
102c06c139Smbalmer  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
112c06c139Smbalmer  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
122c06c139Smbalmer  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
132c06c139Smbalmer  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
142c06c139Smbalmer  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
152c06c139Smbalmer  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
162c06c139Smbalmer  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
172c06c139Smbalmer  */
182c06c139Smbalmer 
192c06c139Smbalmer #include <sys/param.h>
202c06c139Smbalmer #include <sys/systm.h>
212c06c139Smbalmer #include <sys/device.h>
222c06c139Smbalmer #include <sys/time.h>
232c06c139Smbalmer #include <sys/sensors.h>
24b047b92cStedu #include <sys/timeout.h>
252c06c139Smbalmer 
262c06c139Smbalmer #include <dev/usb/usb.h>
272c06c139Smbalmer #include <dev/usb/usbdi.h>
282c06c139Smbalmer #include <dev/usb/usbdevs.h>
292c06c139Smbalmer 
302c06c139Smbalmer #ifdef UMBG_DEBUG
312c06c139Smbalmer #define DPRINTFN(n, x)	do { if (umbgdebug > (n)) printf x; } while (0)
322c06c139Smbalmer int umbgdebug = 0;
332c06c139Smbalmer #else
342c06c139Smbalmer #define DPRINTFN(n, x)
352c06c139Smbalmer #endif
362c06c139Smbalmer #define DPRINTF(x)	DPRINTFN(0, x)
372c06c139Smbalmer 
382c06c139Smbalmer #ifdef UMBG_DEBUG
392c06c139Smbalmer #define TRUSTTIME	((long) 60)
402c06c139Smbalmer #else
412c06c139Smbalmer #define TRUSTTIME	((long) 12 * 60 * 60)	/* degrade OK > WARN > CRIT */
422c06c139Smbalmer #endif
432c06c139Smbalmer 
442c06c139Smbalmer struct umbg_softc {
452c06c139Smbalmer 	struct device		sc_dev;		/* base device */
46ab0b1be7Smglocker 	struct usbd_device	*sc_udev;	/* USB device */
47ab0b1be7Smglocker 	struct usbd_interface	*sc_iface;	/* data interface */
482c06c139Smbalmer 
492c06c139Smbalmer 	int			sc_bulkin_no;
50ab0b1be7Smglocker 	struct usbd_pipe	*sc_bulkin_pipe;
512c06c139Smbalmer 	int			sc_bulkout_no;
52ab0b1be7Smglocker 	struct usbd_pipe	*sc_bulkout_pipe;
532c06c139Smbalmer 
542c06c139Smbalmer 	struct timeout		sc_to;		/* get time from device */
552c06c139Smbalmer 	struct usb_task		sc_task;
562c06c139Smbalmer 
572c06c139Smbalmer 	struct timeout		sc_it_to;	/* invalidate sensor */
582c06c139Smbalmer 
592c06c139Smbalmer 	usb_device_request_t	sc_req;
602c06c139Smbalmer 
612c06c139Smbalmer 	struct ksensor		sc_timedelta;	/* timedelta */
622c06c139Smbalmer 	struct ksensor		sc_signal;	/* signal quality and status */
632c06c139Smbalmer 	struct ksensordev	sc_sensordev;
642c06c139Smbalmer };
652c06c139Smbalmer 
662c06c139Smbalmer struct mbg_time {
672c06c139Smbalmer 	u_int8_t		hundreds;
682c06c139Smbalmer 	u_int8_t		sec;
692c06c139Smbalmer 	u_int8_t		min;
702c06c139Smbalmer 	u_int8_t		hour;
712c06c139Smbalmer 	u_int8_t		mday;
722c06c139Smbalmer 	u_int8_t		wday;	/* 1 (monday) - 7 (sunday) */
732c06c139Smbalmer 	u_int8_t		mon;
742c06c139Smbalmer 	u_int8_t		year;	/* 0 - 99 */
752c06c139Smbalmer 	u_int8_t		status;
762c06c139Smbalmer 	u_int8_t		signal;
772c06c139Smbalmer 	int8_t			utc_off;
782c06c139Smbalmer };
792c06c139Smbalmer 
802c06c139Smbalmer struct mbg_time_hr {
81fede6f61Smbalmer 	u_int32_t		sec;		/* always UTC */
822c06c139Smbalmer 	u_int32_t		frac;		/* fractions of second */
83fede6f61Smbalmer 	int32_t			utc_off;	/* informal only, in seconds */
842c06c139Smbalmer 	u_int16_t		status;
852c06c139Smbalmer 	u_int8_t		signal;
862c06c139Smbalmer };
872c06c139Smbalmer 
882c06c139Smbalmer /* mbg_time.status bits */
892c06c139Smbalmer #define MBG_FREERUN		0x01	/* clock running on xtal */
902c06c139Smbalmer #define MBG_DST_ENA		0x02	/* DST enabled */
912c06c139Smbalmer #define MBG_SYNC		0x04	/* clock synced at least once */
922c06c139Smbalmer #define MBG_DST_CHG		0x08	/* DST change announcement */
932c06c139Smbalmer #define MBG_UTC			0x10	/* special UTC firmware is installed */
942c06c139Smbalmer #define MBG_LEAP		0x20	/* announcement of a leap second */
952c06c139Smbalmer #define MBG_IFTM		0x40	/* current time was set from host */
969c0b2136Smbalmer #define MBG_INVALID		0x80	/* time invalid, batt. was disconn. */
972c06c139Smbalmer 
982c06c139Smbalmer /* commands */
992c06c139Smbalmer #define MBG_GET_TIME		0x00
1002c06c139Smbalmer #define MBG_GET_SYNC_TIME	0x02
1012c06c139Smbalmer #define MBG_GET_TIME_HR		0x03
1022c06c139Smbalmer #define MBG_SET_TIME		0x10
1032c06c139Smbalmer #define MBG_GET_TZCODE		0x32
1042c06c139Smbalmer #define MBG_SET_TZCODE		0x33
1052c06c139Smbalmer #define MBG_GET_FW_ID_1		0x40
1062c06c139Smbalmer #define MBG_GET_FW_ID_2		0x41
1072c06c139Smbalmer #define MBG_GET_SERNUM		0x42
1082c06c139Smbalmer 
1092c06c139Smbalmer /* timezone codes (for MBG_{GET|SET}_TZCODE) */
1102c06c139Smbalmer #define MBG_TZCODE_CET_CEST	0x00
1112c06c139Smbalmer #define MBG_TZCODE_CET		0x01
1122c06c139Smbalmer #define MBG_TZCODE_UTC		0x02
1132c06c139Smbalmer #define MBG_TZCODE_EET_EEST	0x03
1142c06c139Smbalmer 
1152c06c139Smbalmer /* misc. constants */
1162c06c139Smbalmer #define MBG_FIFO_LEN		16
1172c06c139Smbalmer #define MBG_ID_LEN		(2 * MBG_FIFO_LEN + 1)
1182c06c139Smbalmer #define MBG_BUSY		0x01
1192c06c139Smbalmer #define MBG_SIG_BIAS		55
1202c06c139Smbalmer #define MBG_SIG_MAX		68
1212c06c139Smbalmer #define NSECPERSEC		1000000000LL	/* nanoseconds per second */
1222c06c139Smbalmer #define HRDIVISOR		0x100000000LL	/* for hi-res timestamp */
1232c06c139Smbalmer 
1242c06c139Smbalmer static int t_wait, t_trust;
1252c06c139Smbalmer 
1262c06c139Smbalmer void umbg_intr(void *);
1272c06c139Smbalmer void umbg_it_intr(void *);
1282c06c139Smbalmer 
1292c06c139Smbalmer int umbg_match(struct device *, void *, void *);
1302c06c139Smbalmer void umbg_attach(struct device *, struct device *, void *);
1312c06c139Smbalmer int umbg_detach(struct device *, int);
1322c06c139Smbalmer 
1332c06c139Smbalmer void umbg_task(void *);
1342c06c139Smbalmer 
1352c06c139Smbalmer int umbg_read(struct umbg_softc *, u_int8_t cmd, char *buf, size_t len,
1362c06c139Smbalmer     struct timespec *tstamp);
1372c06c139Smbalmer 
1382c06c139Smbalmer struct cfdriver umbg_cd = {
1392c06c139Smbalmer 	NULL, "umbg", DV_DULL
1402c06c139Smbalmer };
1412c06c139Smbalmer 
1422c06c139Smbalmer const struct cfattach umbg_ca = {
143cf8c8cdaSmpi 	sizeof(struct umbg_softc), umbg_match, umbg_attach, umbg_detach
1442c06c139Smbalmer };
1452c06c139Smbalmer 
1462c06c139Smbalmer int
umbg_match(struct device * parent,void * match,void * aux)1472c06c139Smbalmer umbg_match(struct device *parent, void *match, void *aux)
1482c06c139Smbalmer {
1492c06c139Smbalmer 	struct usb_attach_arg *uaa = aux;
1502c06c139Smbalmer 
151c0d38480Smpi 	if (uaa->iface == NULL)
1522c06c139Smbalmer 		return UMATCH_NONE;
1532c06c139Smbalmer 
154dd567292Ssthen 	return uaa->vendor == USB_VENDOR_MEINBERG && (
155dd567292Ssthen 	    uaa->product == USB_PRODUCT_MEINBERG_USB5131 ||
156dd567292Ssthen 	    uaa->product == USB_PRODUCT_MEINBERG_DCF600USB) ?
1572c06c139Smbalmer 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
1582c06c139Smbalmer }
1592c06c139Smbalmer 
1602c06c139Smbalmer void
umbg_attach(struct device * parent,struct device * self,void * aux)1612c06c139Smbalmer umbg_attach(struct device *parent, struct device *self, void *aux)
1622c06c139Smbalmer {
1632c06c139Smbalmer 	struct umbg_softc *sc = (struct umbg_softc *)self;
1642c06c139Smbalmer 	struct usb_attach_arg *uaa = aux;
165ab0b1be7Smglocker 	struct usbd_device *dev = uaa->device;
166ab0b1be7Smglocker 	struct usbd_interface *iface = uaa->iface;
1672c06c139Smbalmer 	struct mbg_time tframe;
1682c06c139Smbalmer 	usb_endpoint_descriptor_t *ed;
1692c06c139Smbalmer 	usbd_status err;
1702c06c139Smbalmer 	int signal;
171dd567292Ssthen 	const char *desc;
1722c06c139Smbalmer #ifdef UMBG_DEBUG
1732c06c139Smbalmer 	char fw_id[MBG_ID_LEN];
1742c06c139Smbalmer #endif
1758881fd2fSmbalmer 	sc->sc_udev = dev;
1768881fd2fSmbalmer 
1778881fd2fSmbalmer 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
1788881fd2fSmbalmer 	    sizeof(sc->sc_sensordev.xname));
1798881fd2fSmbalmer 
1808881fd2fSmbalmer 	sc->sc_timedelta.type = SENSOR_TIMEDELTA;
1818881fd2fSmbalmer 	sc->sc_timedelta.status = SENSOR_S_UNKNOWN;
182dd567292Ssthen 
183dd567292Ssthen 	switch (uaa->product) {
184dd567292Ssthen 	case USB_PRODUCT_MEINBERG_DCF600USB:
185dd567292Ssthen 		desc = "DCF600USB";
186dd567292Ssthen 		break;
187dd567292Ssthen 	case USB_PRODUCT_MEINBERG_USB5131:
188dd567292Ssthen 		desc = "USB5131";
189dd567292Ssthen 		break;
190dd567292Ssthen 	default:
191dd567292Ssthen 		desc = "Unspecified Radio clock";
192dd567292Ssthen 	}
193dd567292Ssthen 	strlcpy(sc->sc_timedelta.desc, desc,
1948881fd2fSmbalmer 	    sizeof(sc->sc_timedelta.desc));
1958881fd2fSmbalmer 	sensor_attach(&sc->sc_sensordev, &sc->sc_timedelta);
1968881fd2fSmbalmer 
1978881fd2fSmbalmer 	sc->sc_signal.type = SENSOR_PERCENT;
1988881fd2fSmbalmer 	strlcpy(sc->sc_signal.desc, "Signal", sizeof(sc->sc_signal.desc));
1998881fd2fSmbalmer 	sensor_attach(&sc->sc_sensordev, &sc->sc_signal);
2008881fd2fSmbalmer 	sensordev_install(&sc->sc_sensordev);
2018881fd2fSmbalmer 
202c33449aaSjakemsr 	usb_init_task(&sc->sc_task, umbg_task, sc, USB_TASK_TYPE_GENERIC);
2038881fd2fSmbalmer 	timeout_set(&sc->sc_to, umbg_intr, sc);
2048881fd2fSmbalmer 	timeout_set(&sc->sc_it_to, umbg_it_intr, sc);
2052c06c139Smbalmer 
2062c06c139Smbalmer 	if ((err = usbd_device2interface_handle(dev, 0, &iface))) {
2079c0b2136Smbalmer 		printf("%s: failed to get interface, err=%s\n",
2089c0b2136Smbalmer 		    sc->sc_dev.dv_xname, usbd_errstr(err));
2092c06c139Smbalmer 		goto fishy;
2102c06c139Smbalmer 	}
2112c06c139Smbalmer 
2122c06c139Smbalmer 	ed = usbd_interface2endpoint_descriptor(iface, 0);
2132c06c139Smbalmer 	sc->sc_bulkin_no = ed->bEndpointAddress;
2142c06c139Smbalmer 	ed = usbd_interface2endpoint_descriptor(iface, 1);
2152c06c139Smbalmer 	sc->sc_bulkout_no = ed->bEndpointAddress;
2162c06c139Smbalmer 
2172c06c139Smbalmer 	sc->sc_iface = iface;
2182c06c139Smbalmer 
2192c06c139Smbalmer 	err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no,
2202c06c139Smbalmer 	    USBD_EXCLUSIVE_USE, &sc->sc_bulkin_pipe);
2212c06c139Smbalmer 	if (err) {
2222c06c139Smbalmer 		printf("%s: open rx pipe failed: %s\n", sc->sc_dev.dv_xname,
2232c06c139Smbalmer 		    usbd_errstr(err));
2242c06c139Smbalmer 		goto fishy;
2252c06c139Smbalmer 	}
2262c06c139Smbalmer 
2272c06c139Smbalmer 	err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no,
2282c06c139Smbalmer 	    USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
2292c06c139Smbalmer 	if (err) {
2302c06c139Smbalmer 		printf("%s: open tx pipe failed: %s\n", sc->sc_dev.dv_xname,
2312c06c139Smbalmer 		    usbd_errstr(err));
2322c06c139Smbalmer 		goto fishy;
2332c06c139Smbalmer 	}
2342c06c139Smbalmer 
2352c06c139Smbalmer 	printf("%s: ", sc->sc_dev.dv_xname);
2362c06c139Smbalmer 	if (umbg_read(sc, MBG_GET_TIME, (char *)&tframe,
2372c06c139Smbalmer 	    sizeof(struct mbg_time), NULL)) {
2382c06c139Smbalmer 		sc->sc_signal.status = SENSOR_S_CRIT;
2392c06c139Smbalmer 		printf("unknown status");
2402c06c139Smbalmer 	} else {
2412c06c139Smbalmer 		sc->sc_signal.status = SENSOR_S_OK;
2422c06c139Smbalmer 		signal = tframe.signal - MBG_SIG_BIAS;
2432c06c139Smbalmer 		if (signal < 0)
2442c06c139Smbalmer 			signal = 0;
2452c06c139Smbalmer 		else if (signal > MBG_SIG_MAX)
2462c06c139Smbalmer 			signal = MBG_SIG_MAX;
2472c06c139Smbalmer 		sc->sc_signal.value = signal;
2482c06c139Smbalmer 
2492c06c139Smbalmer 		if (tframe.status & MBG_SYNC)
2502c06c139Smbalmer 			printf("synchronized");
2512c06c139Smbalmer 		else
2522c06c139Smbalmer 			printf("not synchronized");
2532c06c139Smbalmer 		if (tframe.status & MBG_FREERUN) {
2542c06c139Smbalmer 			sc->sc_signal.status = SENSOR_S_WARN;
2552c06c139Smbalmer 			printf(", freerun");
2562c06c139Smbalmer 		}
2572c06c139Smbalmer 		if (tframe.status & MBG_IFTM)
2582c06c139Smbalmer 			printf(", time set from host");
2592c06c139Smbalmer 	}
2602c06c139Smbalmer #ifdef UMBG_DEBUG
2612c06c139Smbalmer 	if (umbg_read(sc, MBG_GET_FW_ID_1, fw_id, MBG_FIFO_LEN, NULL) ||
2622c06c139Smbalmer 	    umbg_read(sc, MBG_GET_FW_ID_2, &fw_id[MBG_FIFO_LEN], MBG_FIFO_LEN,
2632c06c139Smbalmer 	    NULL))
2642c06c139Smbalmer 		printf(", firmware unknown");
2652c06c139Smbalmer 	else {
2662c06c139Smbalmer 		fw_id[MBG_ID_LEN - 1] = '\0';
2672c06c139Smbalmer 		printf(", firmware %s", fw_id);
2682c06c139Smbalmer 	}
2692c06c139Smbalmer #endif
2702c06c139Smbalmer 	printf("\n");
2712c06c139Smbalmer 
272e15e1c8cSblambert 	t_wait = 5;
2732c06c139Smbalmer 
274e15e1c8cSblambert 	t_trust = TRUSTTIME;
2752c06c139Smbalmer 
2762c06c139Smbalmer 	usb_add_task(sc->sc_udev, &sc->sc_task);
2772c06c139Smbalmer 	return;
2782c06c139Smbalmer 
2792c06c139Smbalmer fishy:
2807b043b62Sjakemsr 	usbd_deactivate(sc->sc_udev);
2812c06c139Smbalmer }
2822c06c139Smbalmer 
2832c06c139Smbalmer int
umbg_detach(struct device * self,int flags)2842c06c139Smbalmer umbg_detach(struct device *self, int flags)
2852c06c139Smbalmer {
2862c06c139Smbalmer 	struct umbg_softc *sc = (struct umbg_softc *)self;
2872c06c139Smbalmer 	usbd_status err;
2882c06c139Smbalmer 
289c7438bddSjakemsr 	if (timeout_initialized(&sc->sc_to))
2902c06c139Smbalmer 		timeout_del(&sc->sc_to);
291c7438bddSjakemsr 	if (timeout_initialized(&sc->sc_it_to))
2922c06c139Smbalmer 		timeout_del(&sc->sc_it_to);
2932c06c139Smbalmer 
294289f8943Sderaadt 	usb_rem_task(sc->sc_udev, &sc->sc_task);
295289f8943Sderaadt 
2962c06c139Smbalmer 	if (sc->sc_bulkin_pipe != NULL) {
2972c06c139Smbalmer 		err = usbd_close_pipe(sc->sc_bulkin_pipe);
2982c06c139Smbalmer 		if (err)
2992c06c139Smbalmer 			printf("%s: close rx pipe failed: %s\n",
3002c06c139Smbalmer 			    sc->sc_dev.dv_xname, usbd_errstr(err));
3012c06c139Smbalmer 		sc->sc_bulkin_pipe = NULL;
3022c06c139Smbalmer 	}
3032c06c139Smbalmer 	if (sc->sc_bulkout_pipe != NULL) {
3042c06c139Smbalmer 		err = usbd_close_pipe(sc->sc_bulkout_pipe);
3052c06c139Smbalmer 		if (err)
3062c06c139Smbalmer 			printf("%s: close tx pipe failed: %s\n",
3072c06c139Smbalmer 			    sc->sc_dev.dv_xname, usbd_errstr(err));
3082c06c139Smbalmer 		sc->sc_bulkout_pipe = NULL;
3092c06c139Smbalmer 	}
3102c06c139Smbalmer 
3112c06c139Smbalmer 	/* Unregister the clock with the kernel */
3122c06c139Smbalmer 	sensordev_deinstall(&sc->sc_sensordev);
3132c06c139Smbalmer 
3142c06c139Smbalmer 	return 0;
3152c06c139Smbalmer }
3162c06c139Smbalmer 
3172c06c139Smbalmer void
umbg_intr(void * xsc)3182c06c139Smbalmer umbg_intr(void *xsc)
3192c06c139Smbalmer {
3202c06c139Smbalmer 	struct umbg_softc *sc = xsc;
3212c06c139Smbalmer 	usb_add_task(sc->sc_udev, &sc->sc_task);
3222c06c139Smbalmer }
3232c06c139Smbalmer 
3242c06c139Smbalmer /* umbg_task_hr() read a high resolution timestamp from the device. */
3252c06c139Smbalmer void
umbg_task(void * arg)3262c06c139Smbalmer umbg_task(void *arg)
3272c06c139Smbalmer {
3282c06c139Smbalmer 	struct umbg_softc *sc = (struct umbg_softc *)arg;
3292c06c139Smbalmer 	struct mbg_time_hr tframe;
3302c06c139Smbalmer 	struct timespec tstamp;
3312c06c139Smbalmer 	int64_t tlocal, trecv;
3322c06c139Smbalmer 	int signal;
3332c06c139Smbalmer 
3347b043b62Sjakemsr 	if (usbd_is_dying(sc->sc_udev))
3352c06c139Smbalmer 		return;
3362c06c139Smbalmer 
3372c06c139Smbalmer 	if (umbg_read(sc, MBG_GET_TIME_HR, (char *)&tframe, sizeof(tframe),
3382c06c139Smbalmer 	    &tstamp)) {
3392c06c139Smbalmer 		sc->sc_signal.status = SENSOR_S_CRIT;
3402c06c139Smbalmer 		goto bail_out;
3412c06c139Smbalmer 	}
3422c06c139Smbalmer 	if (tframe.status & MBG_INVALID) {
3432c06c139Smbalmer 		sc->sc_signal.status = SENSOR_S_CRIT;
3442c06c139Smbalmer 		goto bail_out;
3452c06c139Smbalmer 	}
3462c06c139Smbalmer 
3472c06c139Smbalmer 	tlocal = tstamp.tv_sec * NSECPERSEC + tstamp.tv_nsec;
348fede6f61Smbalmer 	trecv = letoh32(tframe.sec) * NSECPERSEC +
3492c06c139Smbalmer 	    (letoh32(tframe.frac) * NSECPERSEC >> 32);
3502c06c139Smbalmer 
3512c06c139Smbalmer 	sc->sc_timedelta.value = tlocal - trecv;
3522c06c139Smbalmer 	if (sc->sc_timedelta.status == SENSOR_S_UNKNOWN ||
3532c06c139Smbalmer 		!(letoh16(tframe.status) & MBG_FREERUN)) {
3542c06c139Smbalmer 		sc->sc_timedelta.status = SENSOR_S_OK;
355e15e1c8cSblambert 		timeout_add_sec(&sc->sc_it_to, t_trust);
3562c06c139Smbalmer 	}
3572c06c139Smbalmer 
3582c06c139Smbalmer 	sc->sc_timedelta.tv.tv_sec = tstamp.tv_sec;
3592c06c139Smbalmer 	sc->sc_timedelta.tv.tv_usec = tstamp.tv_nsec / 1000;
3602c06c139Smbalmer 
3612c06c139Smbalmer 	signal = tframe.signal - MBG_SIG_BIAS;
3622c06c139Smbalmer 	if (signal < 0)
3632c06c139Smbalmer 		signal = 0;
3642c06c139Smbalmer 	else if (signal > MBG_SIG_MAX)
3652c06c139Smbalmer 		signal = MBG_SIG_MAX;
3662c06c139Smbalmer 
3672c06c139Smbalmer 	sc->sc_signal.value = signal * 100000 / MBG_SIG_MAX;
3682c06c139Smbalmer 	sc->sc_signal.status = letoh16(tframe.status) & MBG_FREERUN ?
3692c06c139Smbalmer 	    SENSOR_S_WARN : SENSOR_S_OK;
3702c06c139Smbalmer 	sc->sc_signal.tv.tv_sec = sc->sc_timedelta.tv.tv_sec;
3712c06c139Smbalmer 	sc->sc_signal.tv.tv_usec = sc->sc_timedelta.tv.tv_usec;
3722c06c139Smbalmer 
3732c06c139Smbalmer bail_out:
374e15e1c8cSblambert 	timeout_add_sec(&sc->sc_to, t_wait);
3752c06c139Smbalmer 
3762c06c139Smbalmer }
3772c06c139Smbalmer 
3782c06c139Smbalmer /* send a command and read back results */
3792c06c139Smbalmer int
umbg_read(struct umbg_softc * sc,u_int8_t cmd,char * buf,size_t len,struct timespec * tstamp)3802c06c139Smbalmer umbg_read(struct umbg_softc *sc, u_int8_t cmd, char *buf, size_t len,
3812c06c139Smbalmer     struct timespec *tstamp)
3822c06c139Smbalmer {
3832c06c139Smbalmer 	usbd_status err;
384ab0b1be7Smglocker 	struct usbd_xfer *xfer;
3852c06c139Smbalmer 
3862c06c139Smbalmer 	xfer = usbd_alloc_xfer(sc->sc_udev);
3872c06c139Smbalmer 	if (xfer == NULL) {
3882c06c139Smbalmer 		DPRINTF(("%s: alloc xfer failed\n", sc->sc_dev.dv_xname));
3892c06c139Smbalmer 		return -1;
3902c06c139Smbalmer 	}
3912c06c139Smbalmer 
3922c06c139Smbalmer 	usbd_setup_xfer(xfer, sc->sc_bulkout_pipe, NULL, &cmd, sizeof(cmd),
393aa88c704Smpi 	    USBD_SHORT_XFER_OK | USBD_SYNCHRONOUS, USBD_DEFAULT_TIMEOUT, NULL);
3942c06c139Smbalmer 	if (tstamp)
3952c06c139Smbalmer 		nanotime(tstamp);
396aa88c704Smpi 	err = usbd_transfer(xfer);
3972c06c139Smbalmer 	if (err) {
3982c06c139Smbalmer 		DPRINTF(("%s: sending of command failed: %s\n",
3992c06c139Smbalmer 		    sc->sc_dev.dv_xname, usbd_errstr(err)));
4002c06c139Smbalmer 		usbd_free_xfer(xfer);
4012c06c139Smbalmer 		return -1;
4022c06c139Smbalmer 	}
4032c06c139Smbalmer 
4042c06c139Smbalmer 	usbd_setup_xfer(xfer, sc->sc_bulkin_pipe, NULL, buf, len,
405aa88c704Smpi 	    USBD_SHORT_XFER_OK | USBD_SYNCHRONOUS, USBD_DEFAULT_TIMEOUT, NULL);
4062c06c139Smbalmer 
407aa88c704Smpi 	err = usbd_transfer(xfer);
4082c06c139Smbalmer 	usbd_free_xfer(xfer);
4092c06c139Smbalmer 	if (err) {
4102c06c139Smbalmer 		DPRINTF(("%s: reading data failed: %s\n",
4112c06c139Smbalmer 		    sc->sc_dev.dv_xname, usbd_errstr(err)));
4122c06c139Smbalmer 		return -1;
4132c06c139Smbalmer 	}
4142c06c139Smbalmer 	return 0;
4152c06c139Smbalmer }
4162c06c139Smbalmer 
4172c06c139Smbalmer void
umbg_it_intr(void * xsc)4182c06c139Smbalmer umbg_it_intr(void *xsc)
4192c06c139Smbalmer {
4202c06c139Smbalmer 	struct umbg_softc *sc = xsc;
4212c06c139Smbalmer 
4227b043b62Sjakemsr 	if (usbd_is_dying(sc->sc_udev))
4232c06c139Smbalmer 		return;
4242c06c139Smbalmer 
4252c06c139Smbalmer 	if (sc->sc_timedelta.status == SENSOR_S_OK) {
4262c06c139Smbalmer 		sc->sc_timedelta.status = SENSOR_S_WARN;
4272c06c139Smbalmer 		/*
4282c06c139Smbalmer 		 * further degrade in TRUSTTIME seconds if the clocks remains
4292c06c139Smbalmer 		 * free running.
4302c06c139Smbalmer 		 */
431e15e1c8cSblambert 		timeout_add_sec(&sc->sc_it_to, t_trust);
4322c06c139Smbalmer 	} else
4332c06c139Smbalmer 		sc->sc_timedelta.status = SENSOR_S_CRIT;
4342c06c139Smbalmer }
435