xref: /openbsd/sys/dev/usb/umbg.c (revision 4bdff4be)
1 /*	$OpenBSD: umbg.c,v 1.28 2022/07/02 08:50:42 visa Exp $ */
2 
3 /*
4  * Copyright (c) 2007 Marc Balmer <mbalmer@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/kernel.h>
22 #include <sys/conf.h>
23 #include <sys/device.h>
24 #include <sys/time.h>
25 #include <sys/sensors.h>
26 #include <sys/timeout.h>
27 
28 #include <dev/usb/usb.h>
29 #include <dev/usb/usbdi.h>
30 #include <dev/usb/usbdi_util.h>
31 #include <dev/usb/usbdevs.h>
32 
33 #ifdef UMBG_DEBUG
34 #define DPRINTFN(n, x)	do { if (umbgdebug > (n)) printf x; } while (0)
35 int umbgdebug = 0;
36 #else
37 #define DPRINTFN(n, x)
38 #endif
39 #define DPRINTF(x)	DPRINTFN(0, x)
40 
41 #ifdef UMBG_DEBUG
42 #define TRUSTTIME	((long) 60)
43 #else
44 #define TRUSTTIME	((long) 12 * 60 * 60)	/* degrade OK > WARN > CRIT */
45 #endif
46 
47 struct umbg_softc {
48 	struct device		sc_dev;		/* base device */
49 	struct usbd_device	*sc_udev;	/* USB device */
50 	struct usbd_interface	*sc_iface;	/* data interface */
51 
52 	int			sc_bulkin_no;
53 	struct usbd_pipe	*sc_bulkin_pipe;
54 	int			sc_bulkout_no;
55 	struct usbd_pipe	*sc_bulkout_pipe;
56 
57 	struct timeout		sc_to;		/* get time from device */
58 	struct usb_task		sc_task;
59 
60 	struct timeout		sc_it_to;	/* invalidate sensor */
61 
62 	usb_device_request_t	sc_req;
63 
64 	struct ksensor		sc_timedelta;	/* timedelta */
65 	struct ksensor		sc_signal;	/* signal quality and status */
66 	struct ksensordev	sc_sensordev;
67 };
68 
69 struct mbg_time {
70 	u_int8_t		hundreds;
71 	u_int8_t		sec;
72 	u_int8_t		min;
73 	u_int8_t		hour;
74 	u_int8_t		mday;
75 	u_int8_t		wday;	/* 1 (monday) - 7 (sunday) */
76 	u_int8_t		mon;
77 	u_int8_t		year;	/* 0 - 99 */
78 	u_int8_t		status;
79 	u_int8_t		signal;
80 	int8_t			utc_off;
81 };
82 
83 struct mbg_time_hr {
84 	u_int32_t		sec;		/* always UTC */
85 	u_int32_t		frac;		/* fractions of second */
86 	int32_t			utc_off;	/* informal only, in seconds */
87 	u_int16_t		status;
88 	u_int8_t		signal;
89 };
90 
91 /* mbg_time.status bits */
92 #define MBG_FREERUN		0x01	/* clock running on xtal */
93 #define MBG_DST_ENA		0x02	/* DST enabled */
94 #define MBG_SYNC		0x04	/* clock synced at least once */
95 #define MBG_DST_CHG		0x08	/* DST change announcement */
96 #define MBG_UTC			0x10	/* special UTC firmware is installed */
97 #define MBG_LEAP		0x20	/* announcement of a leap second */
98 #define MBG_IFTM		0x40	/* current time was set from host */
99 #define MBG_INVALID		0x80	/* time invalid, batt. was disconn. */
100 
101 /* commands */
102 #define MBG_GET_TIME		0x00
103 #define MBG_GET_SYNC_TIME	0x02
104 #define MBG_GET_TIME_HR		0x03
105 #define MBG_SET_TIME		0x10
106 #define MBG_GET_TZCODE		0x32
107 #define MBG_SET_TZCODE		0x33
108 #define MBG_GET_FW_ID_1		0x40
109 #define MBG_GET_FW_ID_2		0x41
110 #define MBG_GET_SERNUM		0x42
111 
112 /* timezone codes (for MBG_{GET|SET}_TZCODE) */
113 #define MBG_TZCODE_CET_CEST	0x00
114 #define MBG_TZCODE_CET		0x01
115 #define MBG_TZCODE_UTC		0x02
116 #define MBG_TZCODE_EET_EEST	0x03
117 
118 /* misc. constants */
119 #define MBG_FIFO_LEN		16
120 #define MBG_ID_LEN		(2 * MBG_FIFO_LEN + 1)
121 #define MBG_BUSY		0x01
122 #define MBG_SIG_BIAS		55
123 #define MBG_SIG_MAX		68
124 #define NSECPERSEC		1000000000LL	/* nanoseconds per second */
125 #define HRDIVISOR		0x100000000LL	/* for hi-res timestamp */
126 
127 static int t_wait, t_trust;
128 
129 void umbg_intr(void *);
130 void umbg_it_intr(void *);
131 
132 int umbg_match(struct device *, void *, void *);
133 void umbg_attach(struct device *, struct device *, void *);
134 int umbg_detach(struct device *, int);
135 
136 void umbg_task(void *);
137 
138 int umbg_read(struct umbg_softc *, u_int8_t cmd, char *buf, size_t len,
139     struct timespec *tstamp);
140 
141 struct cfdriver umbg_cd = {
142 	NULL, "umbg", DV_DULL
143 };
144 
145 const struct cfattach umbg_ca = {
146 	sizeof(struct umbg_softc), umbg_match, umbg_attach, umbg_detach
147 };
148 
149 int
150 umbg_match(struct device *parent, void *match, void *aux)
151 {
152 	struct usb_attach_arg *uaa = aux;
153 
154 	if (uaa->iface == NULL)
155 		return UMATCH_NONE;
156 
157 	return uaa->vendor == USB_VENDOR_MEINBERG && (
158 	    uaa->product == USB_PRODUCT_MEINBERG_USB5131 ||
159 	    uaa->product == USB_PRODUCT_MEINBERG_DCF600USB) ?
160 	    UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
161 }
162 
163 void
164 umbg_attach(struct device *parent, struct device *self, void *aux)
165 {
166 	struct umbg_softc *sc = (struct umbg_softc *)self;
167 	struct usb_attach_arg *uaa = aux;
168 	struct usbd_device *dev = uaa->device;
169 	struct usbd_interface *iface = uaa->iface;
170 	struct mbg_time tframe;
171 	usb_endpoint_descriptor_t *ed;
172 	usbd_status err;
173 	int signal;
174 	const char *desc;
175 #ifdef UMBG_DEBUG
176 	char fw_id[MBG_ID_LEN];
177 #endif
178 	sc->sc_udev = dev;
179 
180 	strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname,
181 	    sizeof(sc->sc_sensordev.xname));
182 
183 	sc->sc_timedelta.type = SENSOR_TIMEDELTA;
184 	sc->sc_timedelta.status = SENSOR_S_UNKNOWN;
185 
186 	switch (uaa->product) {
187 	case USB_PRODUCT_MEINBERG_DCF600USB:
188 		desc = "DCF600USB";
189 		break;
190 	case USB_PRODUCT_MEINBERG_USB5131:
191 		desc = "USB5131";
192 		break;
193 	default:
194 		desc = "Unspecified Radio clock";
195 	}
196 	strlcpy(sc->sc_timedelta.desc, desc,
197 	    sizeof(sc->sc_timedelta.desc));
198 	sensor_attach(&sc->sc_sensordev, &sc->sc_timedelta);
199 
200 	sc->sc_signal.type = SENSOR_PERCENT;
201 	strlcpy(sc->sc_signal.desc, "Signal", sizeof(sc->sc_signal.desc));
202 	sensor_attach(&sc->sc_sensordev, &sc->sc_signal);
203 	sensordev_install(&sc->sc_sensordev);
204 
205 	usb_init_task(&sc->sc_task, umbg_task, sc, USB_TASK_TYPE_GENERIC);
206 	timeout_set(&sc->sc_to, umbg_intr, sc);
207 	timeout_set(&sc->sc_it_to, umbg_it_intr, sc);
208 
209 	if ((err = usbd_device2interface_handle(dev, 0, &iface))) {
210 		printf("%s: failed to get interface, err=%s\n",
211 		    sc->sc_dev.dv_xname, usbd_errstr(err));
212 		goto fishy;
213 	}
214 
215 	ed = usbd_interface2endpoint_descriptor(iface, 0);
216 	sc->sc_bulkin_no = ed->bEndpointAddress;
217 	ed = usbd_interface2endpoint_descriptor(iface, 1);
218 	sc->sc_bulkout_no = ed->bEndpointAddress;
219 
220 	sc->sc_iface = iface;
221 
222 	err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkin_no,
223 	    USBD_EXCLUSIVE_USE, &sc->sc_bulkin_pipe);
224 	if (err) {
225 		printf("%s: open rx pipe failed: %s\n", sc->sc_dev.dv_xname,
226 		    usbd_errstr(err));
227 		goto fishy;
228 	}
229 
230 	err = usbd_open_pipe(sc->sc_iface, sc->sc_bulkout_no,
231 	    USBD_EXCLUSIVE_USE, &sc->sc_bulkout_pipe);
232 	if (err) {
233 		printf("%s: open tx pipe failed: %s\n", sc->sc_dev.dv_xname,
234 		    usbd_errstr(err));
235 		goto fishy;
236 	}
237 
238 	printf("%s: ", sc->sc_dev.dv_xname);
239 	if (umbg_read(sc, MBG_GET_TIME, (char *)&tframe,
240 	    sizeof(struct mbg_time), NULL)) {
241 		sc->sc_signal.status = SENSOR_S_CRIT;
242 		printf("unknown status");
243 	} else {
244 		sc->sc_signal.status = SENSOR_S_OK;
245 		signal = tframe.signal - MBG_SIG_BIAS;
246 		if (signal < 0)
247 			signal = 0;
248 		else if (signal > MBG_SIG_MAX)
249 			signal = MBG_SIG_MAX;
250 		sc->sc_signal.value = signal;
251 
252 		if (tframe.status & MBG_SYNC)
253 			printf("synchronized");
254 		else
255 			printf("not synchronized");
256 		if (tframe.status & MBG_FREERUN) {
257 			sc->sc_signal.status = SENSOR_S_WARN;
258 			printf(", freerun");
259 		}
260 		if (tframe.status & MBG_IFTM)
261 			printf(", time set from host");
262 	}
263 #ifdef UMBG_DEBUG
264 	if (umbg_read(sc, MBG_GET_FW_ID_1, fw_id, MBG_FIFO_LEN, NULL) ||
265 	    umbg_read(sc, MBG_GET_FW_ID_2, &fw_id[MBG_FIFO_LEN], MBG_FIFO_LEN,
266 	    NULL))
267 		printf(", firmware unknown");
268 	else {
269 		fw_id[MBG_ID_LEN - 1] = '\0';
270 		printf(", firmware %s", fw_id);
271 	}
272 #endif
273 	printf("\n");
274 
275 	t_wait = 5;
276 
277 	t_trust = TRUSTTIME;
278 
279 	usb_add_task(sc->sc_udev, &sc->sc_task);
280 	return;
281 
282 fishy:
283 	usbd_deactivate(sc->sc_udev);
284 }
285 
286 int
287 umbg_detach(struct device *self, int flags)
288 {
289 	struct umbg_softc *sc = (struct umbg_softc *)self;
290 	usbd_status err;
291 
292 	if (timeout_initialized(&sc->sc_to))
293 		timeout_del(&sc->sc_to);
294 	if (timeout_initialized(&sc->sc_it_to))
295 		timeout_del(&sc->sc_it_to);
296 
297 	usb_rem_task(sc->sc_udev, &sc->sc_task);
298 
299 	if (sc->sc_bulkin_pipe != NULL) {
300 		err = usbd_close_pipe(sc->sc_bulkin_pipe);
301 		if (err)
302 			printf("%s: close rx pipe failed: %s\n",
303 			    sc->sc_dev.dv_xname, usbd_errstr(err));
304 		sc->sc_bulkin_pipe = NULL;
305 	}
306 	if (sc->sc_bulkout_pipe != NULL) {
307 		err = usbd_close_pipe(sc->sc_bulkout_pipe);
308 		if (err)
309 			printf("%s: close tx pipe failed: %s\n",
310 			    sc->sc_dev.dv_xname, usbd_errstr(err));
311 		sc->sc_bulkout_pipe = NULL;
312 	}
313 
314 	/* Unregister the clock with the kernel */
315 	sensordev_deinstall(&sc->sc_sensordev);
316 
317 	return 0;
318 }
319 
320 void
321 umbg_intr(void *xsc)
322 {
323 	struct umbg_softc *sc = xsc;
324 	usb_add_task(sc->sc_udev, &sc->sc_task);
325 }
326 
327 /* umbg_task_hr() read a high resolution timestamp from the device. */
328 void
329 umbg_task(void *arg)
330 {
331 	struct umbg_softc *sc = (struct umbg_softc *)arg;
332 	struct mbg_time_hr tframe;
333 	struct timespec tstamp;
334 	int64_t tlocal, trecv;
335 	int signal;
336 
337 	if (usbd_is_dying(sc->sc_udev))
338 		return;
339 
340 	if (umbg_read(sc, MBG_GET_TIME_HR, (char *)&tframe, sizeof(tframe),
341 	    &tstamp)) {
342 		sc->sc_signal.status = SENSOR_S_CRIT;
343 		goto bail_out;
344 	}
345 	if (tframe.status & MBG_INVALID) {
346 		sc->sc_signal.status = SENSOR_S_CRIT;
347 		goto bail_out;
348 	}
349 
350 	tlocal = tstamp.tv_sec * NSECPERSEC + tstamp.tv_nsec;
351 	trecv = letoh32(tframe.sec) * NSECPERSEC +
352 	    (letoh32(tframe.frac) * NSECPERSEC >> 32);
353 
354 	sc->sc_timedelta.value = tlocal - trecv;
355 	if (sc->sc_timedelta.status == SENSOR_S_UNKNOWN ||
356 		!(letoh16(tframe.status) & MBG_FREERUN)) {
357 		sc->sc_timedelta.status = SENSOR_S_OK;
358 		timeout_add_sec(&sc->sc_it_to, t_trust);
359 	}
360 
361 	sc->sc_timedelta.tv.tv_sec = tstamp.tv_sec;
362 	sc->sc_timedelta.tv.tv_usec = tstamp.tv_nsec / 1000;
363 
364 	signal = tframe.signal - MBG_SIG_BIAS;
365 	if (signal < 0)
366 		signal = 0;
367 	else if (signal > MBG_SIG_MAX)
368 		signal = MBG_SIG_MAX;
369 
370 	sc->sc_signal.value = signal * 100000 / MBG_SIG_MAX;
371 	sc->sc_signal.status = letoh16(tframe.status) & MBG_FREERUN ?
372 	    SENSOR_S_WARN : SENSOR_S_OK;
373 	sc->sc_signal.tv.tv_sec = sc->sc_timedelta.tv.tv_sec;
374 	sc->sc_signal.tv.tv_usec = sc->sc_timedelta.tv.tv_usec;
375 
376 bail_out:
377 	timeout_add_sec(&sc->sc_to, t_wait);
378 
379 }
380 
381 /* send a command and read back results */
382 int
383 umbg_read(struct umbg_softc *sc, u_int8_t cmd, char *buf, size_t len,
384     struct timespec *tstamp)
385 {
386 	usbd_status err;
387 	struct usbd_xfer *xfer;
388 
389 	xfer = usbd_alloc_xfer(sc->sc_udev);
390 	if (xfer == NULL) {
391 		DPRINTF(("%s: alloc xfer failed\n", sc->sc_dev.dv_xname));
392 		return -1;
393 	}
394 
395 	usbd_setup_xfer(xfer, sc->sc_bulkout_pipe, NULL, &cmd, sizeof(cmd),
396 	    USBD_SHORT_XFER_OK | USBD_SYNCHRONOUS, USBD_DEFAULT_TIMEOUT, NULL);
397 	if (tstamp)
398 		nanotime(tstamp);
399 	err = usbd_transfer(xfer);
400 	if (err) {
401 		DPRINTF(("%s: sending of command failed: %s\n",
402 		    sc->sc_dev.dv_xname, usbd_errstr(err)));
403 		usbd_free_xfer(xfer);
404 		return -1;
405 	}
406 
407 	usbd_setup_xfer(xfer, sc->sc_bulkin_pipe, NULL, buf, len,
408 	    USBD_SHORT_XFER_OK | USBD_SYNCHRONOUS, USBD_DEFAULT_TIMEOUT, NULL);
409 
410 	err = usbd_transfer(xfer);
411 	usbd_free_xfer(xfer);
412 	if (err) {
413 		DPRINTF(("%s: reading data failed: %s\n",
414 		    sc->sc_dev.dv_xname, usbd_errstr(err)));
415 		return -1;
416 	}
417 	return 0;
418 }
419 
420 void
421 umbg_it_intr(void *xsc)
422 {
423 	struct umbg_softc *sc = xsc;
424 
425 	if (usbd_is_dying(sc->sc_udev))
426 		return;
427 
428 	if (sc->sc_timedelta.status == SENSOR_S_OK) {
429 		sc->sc_timedelta.status = SENSOR_S_WARN;
430 		/*
431 		 * further degrade in TRUSTTIME seconds if the clocks remains
432 		 * free running.
433 		 */
434 		timeout_add_sec(&sc->sc_it_to, t_trust);
435 	} else
436 		sc->sc_timedelta.status = SENSOR_S_CRIT;
437 }
438