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