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