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