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 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 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 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 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 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 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 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