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