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