| File: | dev/usb/udcf.c |
| Warning: | line 326, column 7 Although the value stored to 'err' is used in the enclosing expression, the value is never actually read from 'err' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: udcf.c,v 1.65 2022/07/02 08:50:42 visa Exp $ */ |
| 2 | |
| 3 | /* |
| 4 | * Copyright (c) 2006, 2007, 2008 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/device.h> |
| 23 | #include <sys/time.h> |
| 24 | #include <sys/sensors.h> |
| 25 | #include <sys/timeout.h> |
| 26 | |
| 27 | #include <dev/usb/usb.h> |
| 28 | #include <dev/usb/usbdi.h> |
| 29 | #include <dev/usb/usbdi_util.h> |
| 30 | #include <dev/usb/usbdevs.h> |
| 31 | |
| 32 | #ifdef UDCF_DEBUG |
| 33 | #define DPRINTFN(n, x) do { if (udcfdebug > (n)) printf x; } while (0) |
| 34 | int udcfdebug = 0; |
| 35 | #else |
| 36 | #define DPRINTFN(n, x) |
| 37 | #endif |
| 38 | #define DPRINTF(x) DPRINTFN(0, x) |
| 39 | |
| 40 | #define UDCF_READ_IDX0x1f 0x1f |
| 41 | |
| 42 | #define UDCF_CTRL_IDX0x33 0x33 |
| 43 | #define UDCF_CTRL_VAL0x98 0x98 |
| 44 | |
| 45 | #define FT232R_RESET0x00 0x00 /* reset USB request */ |
| 46 | #define FT232R_STATUS0x05 0x05 /* get modem status USB request */ |
| 47 | #define FT232R_RI0x40 0x40 /* ring indicator */ |
| 48 | |
| 49 | /* max. skew of received time diff vs. measured time diff in percent. */ |
| 50 | #define MAX_SKEW5 5 |
| 51 | |
| 52 | #define CLOCK_DCF77"DCF77" "DCF77" |
| 53 | |
| 54 | struct udcf_softc { |
| 55 | struct device sc_dev; /* base device */ |
| 56 | struct usbd_device *sc_udev; /* USB device */ |
| 57 | struct usbd_interface *sc_iface; /* data interface */ |
| 58 | |
| 59 | struct timeout sc_to; |
| 60 | struct usb_task sc_task; |
| 61 | |
| 62 | struct timeout sc_bv_to; /* bit-value detect */ |
| 63 | struct timeout sc_db_to; /* debounce */ |
| 64 | struct timeout sc_mg_to; /* minute-gap detect */ |
| 65 | struct timeout sc_sl_to; /* signal-loss detect */ |
| 66 | struct timeout sc_it_to; /* invalidate time */ |
| 67 | struct usb_task sc_bv_task; |
| 68 | struct usb_task sc_mg_task; |
| 69 | struct usb_task sc_sl_task; |
| 70 | |
| 71 | usb_device_request_t sc_req; |
| 72 | |
| 73 | int sc_sync; /* 1 during sync */ |
| 74 | u_int64_t sc_mask; /* 64 bit mask */ |
| 75 | u_int64_t sc_tbits; /* Time bits */ |
| 76 | int sc_minute; |
| 77 | int sc_level; |
| 78 | time_t sc_last_mg; |
| 79 | int (*sc_signal)(struct udcf_softc *); |
| 80 | |
| 81 | time_t sc_current; /* current time */ |
| 82 | time_t sc_next; /* time to become valid next */ |
| 83 | time_t sc_last; |
| 84 | int sc_nrecv; /* consecutive valid times */ |
| 85 | struct timeval sc_last_tv; /* uptime of last valid time */ |
| 86 | struct ksensor sc_sensor; |
| 87 | #ifdef UDCF_DEBUG |
| 88 | struct ksensor sc_skew; /* recv vs local skew */ |
| 89 | #endif |
| 90 | struct ksensordev sc_sensordev; |
| 91 | }; |
| 92 | |
| 93 | /* timeouts in milliseconds: */ |
| 94 | #define T_BV150 150 /* bit value detection (150ms) */ |
| 95 | #define T_SYNC950 950 /* sync (950ms) */ |
| 96 | #define T_MG1500 1500 /* minute gap detection (1500ms) */ |
| 97 | #define T_MGSYNC450 450 /* resync after a minute gap (450ms) */ |
| 98 | #define T_SL3000 3000 /* detect signal loss (3sec) */ |
| 99 | #define T_WAIT5000 5000 /* wait (5sec) */ |
| 100 | #define T_WARN300000 300000 /* degrade sensor status to warning (5min) */ |
| 101 | #define T_CRIT900000 900000 /* degrade sensor status to critical (15min) */ |
| 102 | |
| 103 | void udcf_intr(void *); |
| 104 | void udcf_probe(void *); |
| 105 | |
| 106 | void udcf_bv_intr(void *); |
| 107 | void udcf_mg_intr(void *); |
| 108 | void udcf_sl_intr(void *); |
| 109 | void udcf_it_intr(void *); |
| 110 | void udcf_bv_probe(void *); |
| 111 | void udcf_mg_probe(void *); |
| 112 | void udcf_sl_probe(void *); |
| 113 | |
| 114 | int udcf_match(struct device *, void *, void *); |
| 115 | void udcf_attach(struct device *, struct device *, void *); |
| 116 | int udcf_detach(struct device *, int); |
| 117 | |
| 118 | int udcf_nc_signal(struct udcf_softc *); |
| 119 | int udcf_nc_init_hw(struct udcf_softc *); |
| 120 | int udcf_ft232r_signal(struct udcf_softc *); |
| 121 | int udcf_ft232r_init_hw(struct udcf_softc *); |
| 122 | |
| 123 | struct cfdriver udcf_cd = { |
| 124 | NULL((void *)0), "udcf", DV_DULL |
| 125 | }; |
| 126 | |
| 127 | const struct cfattach udcf_ca = { |
| 128 | sizeof(struct udcf_softc), udcf_match, udcf_attach, udcf_detach, |
| 129 | }; |
| 130 | |
| 131 | static const struct usb_devno udcf_devs[] = { |
| 132 | { USB_VENDOR_GUDE0x0959, USB_PRODUCT_GUDE_DCF0xdcf7 }, |
| 133 | { USB_VENDOR_FTDI0x0403, USB_PRODUCT_FTDI_DCF0xe88a } |
| 134 | }; |
| 135 | |
| 136 | int |
| 137 | udcf_match(struct device *parent, void *match, void *aux) |
| 138 | { |
| 139 | struct usb_attach_arg *uaa = aux; |
| 140 | |
| 141 | if (uaa->iface == NULL((void *)0)) |
| 142 | return UMATCH_NONE0; |
| 143 | |
| 144 | return (usb_lookup(udcf_devs, uaa->vendor, uaa->product)usbd_match_device((const struct usb_devno *)(udcf_devs), sizeof (udcf_devs) / sizeof ((udcf_devs)[0]), sizeof ((udcf_devs)[0 ]), (uaa->vendor), (uaa->product)) != NULL((void *)0) ? |
| 145 | UMATCH_VENDOR_PRODUCT13 : UMATCH_NONE0); |
| 146 | } |
| 147 | |
| 148 | void |
| 149 | udcf_attach(struct device *parent, struct device *self, void *aux) |
| 150 | { |
| 151 | struct udcf_softc *sc = (struct udcf_softc *)self; |
| 152 | struct usb_attach_arg *uaa = aux; |
| 153 | struct usbd_device *dev = uaa->device; |
| 154 | struct usbd_interface *iface; |
| 155 | usbd_status err; |
| 156 | |
| 157 | switch (uaa->product) { |
| 158 | case USB_PRODUCT_GUDE_DCF0xdcf7: |
| 159 | sc->sc_signal = udcf_nc_signal; |
| 160 | strlcpy(sc->sc_sensor.desc, "DCF77", |
| 161 | sizeof(sc->sc_sensor.desc)); |
| 162 | break; |
| 163 | case USB_PRODUCT_FTDI_DCF0xe88a: |
| 164 | sc->sc_signal = udcf_ft232r_signal; |
| 165 | strlcpy(sc->sc_sensor.desc, "DCF77", |
| 166 | sizeof(sc->sc_sensor.desc)); |
| 167 | break; |
| 168 | } |
| 169 | |
| 170 | usb_init_task(&sc->sc_task, udcf_probe, sc, USB_TASK_TYPE_GENERIC)((&sc->sc_task)->fun = (udcf_probe), (&sc->sc_task )->arg = (sc), (&sc->sc_task)->type = (0), (& sc->sc_task)->state = 0x0); |
| 171 | usb_init_task(&sc->sc_bv_task, udcf_bv_probe, sc, USB_TASK_TYPE_GENERIC)((&sc->sc_bv_task)->fun = (udcf_bv_probe), (&sc ->sc_bv_task)->arg = (sc), (&sc->sc_bv_task)-> type = (0), (&sc->sc_bv_task)->state = 0x0); |
| 172 | usb_init_task(&sc->sc_mg_task, udcf_mg_probe, sc, USB_TASK_TYPE_GENERIC)((&sc->sc_mg_task)->fun = (udcf_mg_probe), (&sc ->sc_mg_task)->arg = (sc), (&sc->sc_mg_task)-> type = (0), (&sc->sc_mg_task)->state = 0x0); |
| 173 | usb_init_task(&sc->sc_sl_task, udcf_sl_probe, sc, USB_TASK_TYPE_GENERIC)((&sc->sc_sl_task)->fun = (udcf_sl_probe), (&sc ->sc_sl_task)->arg = (sc), (&sc->sc_sl_task)-> type = (0), (&sc->sc_sl_task)->state = 0x0); |
| 174 | |
| 175 | timeout_set(&sc->sc_to, udcf_intr, sc); |
| 176 | timeout_set(&sc->sc_bv_to, udcf_bv_intr, sc); |
| 177 | timeout_set(&sc->sc_mg_to, udcf_mg_intr, sc); |
| 178 | timeout_set(&sc->sc_sl_to, udcf_sl_intr, sc); |
| 179 | timeout_set(&sc->sc_it_to, udcf_it_intr, sc); |
| 180 | |
| 181 | strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, |
| 182 | sizeof(sc->sc_sensordev.xname)); |
| 183 | |
| 184 | sc->sc_sensor.type = SENSOR_TIMEDELTA; |
| 185 | sc->sc_sensor.status = SENSOR_S_UNKNOWN; |
| 186 | sensor_attach(&sc->sc_sensordev, &sc->sc_sensor); |
| 187 | |
| 188 | #ifdef UDCF_DEBUG |
| 189 | sc->sc_skew.type = SENSOR_TIMEDELTA; |
| 190 | sc->sc_skew.status = SENSOR_S_UNKNOWN; |
| 191 | strlcpy(sc->sc_skew.desc, "local clock skew", |
| 192 | sizeof(sc->sc_skew.desc)); |
| 193 | sensor_attach(&sc->sc_sensordev, &sc->sc_skew); |
| 194 | #endif |
| 195 | sensordev_install(&sc->sc_sensordev); |
| 196 | |
| 197 | sc->sc_udev = dev; |
| 198 | if ((err = usbd_device2interface_handle(dev, 0, &iface))) { |
| 199 | DPRINTF(("%s: failed to get interface, err=%s\n", |
| 200 | sc->sc_dev.dv_xname, usbd_errstr(err))); |
| 201 | goto fishy; |
| 202 | } |
| 203 | |
| 204 | sc->sc_iface = iface; |
| 205 | |
| 206 | sc->sc_level = 0; |
| 207 | sc->sc_minute = 0; |
| 208 | sc->sc_last_mg = 0L; |
| 209 | |
| 210 | sc->sc_sync = 1; |
| 211 | |
| 212 | sc->sc_current = 0L; |
| 213 | sc->sc_next = 0L; |
| 214 | sc->sc_nrecv = 0; |
| 215 | sc->sc_last = 0L; |
| 216 | sc->sc_last_tv.tv_sec = 0L; |
| 217 | |
| 218 | switch (uaa->product) { |
| 219 | case USB_PRODUCT_GUDE_DCF0xdcf7: |
| 220 | if (udcf_nc_init_hw(sc)) |
| 221 | goto fishy; |
| 222 | break; |
| 223 | case USB_PRODUCT_FTDI_DCF0xe88a: |
| 224 | if (udcf_ft232r_init_hw(sc)) |
| 225 | goto fishy; |
| 226 | break; |
| 227 | } |
| 228 | |
| 229 | /* Give the receiver some slack to stabilize */ |
| 230 | timeout_add_msec(&sc->sc_to, T_WAIT5000); |
| 231 | |
| 232 | /* Detect signal loss */ |
| 233 | timeout_add_msec(&sc->sc_sl_to, T_WAIT5000 + T_SL3000); |
| 234 | |
| 235 | DPRINTF(("synchronizing\n")); |
| 236 | return; |
| 237 | |
| 238 | fishy: |
| 239 | DPRINTF(("udcf_attach failed\n")); |
| 240 | usbd_deactivate(sc->sc_udev); |
| 241 | } |
| 242 | |
| 243 | int |
| 244 | udcf_detach(struct device *self, int flags) |
| 245 | { |
| 246 | struct udcf_softc *sc = (struct udcf_softc *)self; |
| 247 | |
| 248 | if (timeout_initialized(&sc->sc_to)((&sc->sc_to)->to_flags & 0x04)) |
| 249 | timeout_del(&sc->sc_to); |
| 250 | if (timeout_initialized(&sc->sc_bv_to)((&sc->sc_bv_to)->to_flags & 0x04)) |
| 251 | timeout_del(&sc->sc_bv_to); |
| 252 | if (timeout_initialized(&sc->sc_mg_to)((&sc->sc_mg_to)->to_flags & 0x04)) |
| 253 | timeout_del(&sc->sc_mg_to); |
| 254 | if (timeout_initialized(&sc->sc_sl_to)((&sc->sc_sl_to)->to_flags & 0x04)) |
| 255 | timeout_del(&sc->sc_sl_to); |
| 256 | if (timeout_initialized(&sc->sc_it_to)((&sc->sc_it_to)->to_flags & 0x04)) |
| 257 | timeout_del(&sc->sc_it_to); |
| 258 | |
| 259 | /* Unregister the clock with the kernel */ |
| 260 | sensordev_deinstall(&sc->sc_sensordev); |
| 261 | usb_rem_task(sc->sc_udev, &sc->sc_task); |
| 262 | usb_rem_task(sc->sc_udev, &sc->sc_bv_task); |
| 263 | usb_rem_task(sc->sc_udev, &sc->sc_mg_task); |
| 264 | usb_rem_task(sc->sc_udev, &sc->sc_sl_task); |
| 265 | |
| 266 | return 0; |
| 267 | } |
| 268 | |
| 269 | /* udcf_intr runs in an interrupt context */ |
| 270 | void |
| 271 | udcf_intr(void *xsc) |
| 272 | { |
| 273 | struct udcf_softc *sc = xsc; |
| 274 | usb_add_task(sc->sc_udev, &sc->sc_task); |
| 275 | } |
| 276 | |
| 277 | /* bit value detection */ |
| 278 | void |
| 279 | udcf_bv_intr(void *xsc) |
| 280 | { |
| 281 | struct udcf_softc *sc = xsc; |
| 282 | usb_add_task(sc->sc_udev, &sc->sc_bv_task); |
| 283 | } |
| 284 | |
| 285 | /* minute gap detection */ |
| 286 | void |
| 287 | udcf_mg_intr(void *xsc) |
| 288 | { |
| 289 | struct udcf_softc *sc = xsc; |
| 290 | usb_add_task(sc->sc_udev, &sc->sc_mg_task); |
| 291 | } |
| 292 | |
| 293 | /* signal loss detection */ |
| 294 | void |
| 295 | udcf_sl_intr(void *xsc) |
| 296 | { |
| 297 | struct udcf_softc *sc = xsc; |
| 298 | usb_add_task(sc->sc_udev, &sc->sc_sl_task); |
| 299 | } |
| 300 | |
| 301 | /* |
| 302 | * initialize the Expert mouseCLOCK USB devices, they use a NetCologne |
| 303 | * chip to interface the receiver. Power must be supplied to the |
| 304 | * receiver and the receiver must be turned on. |
| 305 | */ |
| 306 | int |
| 307 | udcf_nc_init_hw(struct udcf_softc *sc) |
| 308 | { |
| 309 | usbd_status err; |
| 310 | usb_device_request_t req; |
| 311 | uWord result; |
| 312 | int actlen; |
| 313 | |
| 314 | /* Prepare the USB request to probe the value */ |
| 315 | sc->sc_req.bmRequestType = UT_READ_VENDOR_DEVICE(0x80 | 0x40 | 0x00); |
| 316 | sc->sc_req.bRequest = 1; |
| 317 | USETW(sc->sc_req.wValue, 0)(*(u_int16_t *)(sc->sc_req.wValue) = (0)); |
| 318 | USETW(sc->sc_req.wIndex, UDCF_READ_IDX)(*(u_int16_t *)(sc->sc_req.wIndex) = (0x1f)); |
| 319 | USETW(sc->sc_req.wLength, 1)(*(u_int16_t *)(sc->sc_req.wLength) = (1)); |
| 320 | |
| 321 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE(0x00 | 0x40 | 0x00); |
| 322 | req.bRequest = 0; |
| 323 | USETW(req.wValue, 0)(*(u_int16_t *)(req.wValue) = (0)); |
| 324 | USETW(req.wIndex, 0)(*(u_int16_t *)(req.wIndex) = (0)); |
| 325 | USETW(req.wLength, 0)(*(u_int16_t *)(req.wLength) = (0)); |
| 326 | if ((err = usbd_do_request_flags(sc->sc_udev, &req, &result, |
Although the value stored to 'err' is used in the enclosing expression, the value is never actually read from 'err' | |
| 327 | USBD_SHORT_XFER_OK0x04, &actlen, USBD_DEFAULT_TIMEOUT5000))) { |
| 328 | DPRINTF(("failed to turn on power for receiver\n")); |
| 329 | return -1; |
| 330 | } |
| 331 | |
| 332 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE(0x00 | 0x40 | 0x00); |
| 333 | req.bRequest = 0; |
| 334 | USETW(req.wValue, UDCF_CTRL_VAL)(*(u_int16_t *)(req.wValue) = (0x98)); |
| 335 | USETW(req.wIndex, UDCF_CTRL_IDX)(*(u_int16_t *)(req.wIndex) = (0x33)); |
| 336 | USETW(req.wLength, 0)(*(u_int16_t *)(req.wLength) = (0)); |
| 337 | if ((err = usbd_do_request_flags(sc->sc_udev, &req, &result, |
| 338 | USBD_SHORT_XFER_OK0x04, &actlen, USBD_DEFAULT_TIMEOUT5000))) { |
| 339 | DPRINTF(("failed to turn on receiver\n")); |
| 340 | return -1; |
| 341 | } |
| 342 | return 0; |
| 343 | } |
| 344 | |
| 345 | /* |
| 346 | * initialize the Expert mouseCLOCK USB II devices, they use an FTDI |
| 347 | * FT232R chip to interface the receiver. Only reset the chip. |
| 348 | */ |
| 349 | int |
| 350 | udcf_ft232r_init_hw(struct udcf_softc *sc) |
| 351 | { |
| 352 | usbd_status err; |
| 353 | usb_device_request_t req; |
| 354 | |
| 355 | req.bmRequestType = UT_WRITE_VENDOR_DEVICE(0x00 | 0x40 | 0x00); |
| 356 | req.bRequest = FT232R_RESET0x00; |
| 357 | /* 0 resets the SIO */ |
| 358 | USETW(req.wValue,FT232R_RESET)(*(u_int16_t *)(req.wValue) = (0x00)); |
| 359 | USETW(req.wIndex, 0)(*(u_int16_t *)(req.wIndex) = (0)); |
| 360 | USETW(req.wLength, 0)(*(u_int16_t *)(req.wLength) = (0)); |
| 361 | err = usbd_do_request(sc->sc_udev, &req, NULL((void *)0)); |
| 362 | if (err) { |
| 363 | DPRINTF(("failed to reset ftdi\n")); |
| 364 | return -1; |
| 365 | } |
| 366 | return 0; |
| 367 | } |
| 368 | |
| 369 | /* |
| 370 | * return 1 during high-power-, 0 during low-power-emission |
| 371 | * If bit 0 is set, the transmitter emits at full power. |
| 372 | * During the low-power emission we decode a zero bit. |
| 373 | */ |
| 374 | int |
| 375 | udcf_nc_signal(struct udcf_softc *sc) |
| 376 | { |
| 377 | int actlen; |
| 378 | unsigned char data; |
| 379 | |
| 380 | if (usbd_do_request_flags(sc->sc_udev, &sc->sc_req, &data, |
| 381 | USBD_SHORT_XFER_OK0x04, &actlen, USBD_DEFAULT_TIMEOUT5000)) |
| 382 | /* This happens if we pull the receiver */ |
| 383 | return -1; |
| 384 | return data & 0x01; |
| 385 | } |
| 386 | |
| 387 | /* pick up the signal level through the FTDI FT232R chip */ |
| 388 | int |
| 389 | udcf_ft232r_signal(struct udcf_softc *sc) |
| 390 | { |
| 391 | usb_device_request_t req; |
| 392 | int actlen; |
| 393 | u_int16_t data; |
| 394 | |
| 395 | req.bmRequestType = UT_READ_VENDOR_DEVICE(0x80 | 0x40 | 0x00); |
| 396 | req.bRequest = FT232R_STATUS0x05; |
| 397 | USETW(req.wValue, 0)(*(u_int16_t *)(req.wValue) = (0)); |
| 398 | USETW(req.wIndex, 0)(*(u_int16_t *)(req.wIndex) = (0)); |
| 399 | USETW(req.wLength, 2)(*(u_int16_t *)(req.wLength) = (2)); |
| 400 | if (usbd_do_request_flags(sc->sc_udev, &req, &data, |
| 401 | USBD_SHORT_XFER_OK0x04, &actlen, USBD_DEFAULT_TIMEOUT5000)) { |
| 402 | DPRINTFN(2, ("error reading ftdi modem status\n")); |
| 403 | return -1; |
| 404 | } |
| 405 | DPRINTFN(2, ("ftdi status 0x%04x\n", data)); |
| 406 | return data & FT232R_RI0x40 ? 0 : 1; |
| 407 | } |
| 408 | |
| 409 | /* udcf_probe runs in a process context. */ |
| 410 | void |
| 411 | udcf_probe(void *xsc) |
| 412 | { |
| 413 | struct udcf_softc *sc = xsc; |
| 414 | struct timespec now; |
| 415 | int data; |
| 416 | |
| 417 | if (usbd_is_dying(sc->sc_udev)) |
| 418 | return; |
| 419 | |
| 420 | data = sc->sc_signal(sc); |
| 421 | if (data == -1) |
| 422 | return; |
| 423 | |
| 424 | if (data) { |
| 425 | sc->sc_level = 1; |
| 426 | timeout_add(&sc->sc_to, 1); |
| 427 | return; |
| 428 | } |
| 429 | |
| 430 | if (sc->sc_level == 0) |
| 431 | return; |
| 432 | |
| 433 | /* the beginning of a second */ |
| 434 | sc->sc_level = 0; |
| 435 | if (sc->sc_minute == 1) { |
| 436 | if (sc->sc_sync) { |
| 437 | DPRINTF(("start collecting bits\n")); |
| 438 | sc->sc_sync = 0; |
| 439 | } else { |
| 440 | /* provide the timedelta */ |
| 441 | microtime(&sc->sc_sensor.tv); |
| 442 | nanotime(&now); |
| 443 | sc->sc_current = sc->sc_next; |
| 444 | sc->sc_sensor.value = (int64_t)(now.tv_sec - |
| 445 | sc->sc_current) * 1000000000LL + now.tv_nsec; |
| 446 | |
| 447 | sc->sc_sensor.status = SENSOR_S_OK; |
| 448 | |
| 449 | /* |
| 450 | * if no valid time information is received |
| 451 | * during the next 5 minutes, the sensor state |
| 452 | * will be degraded to SENSOR_S_WARN |
| 453 | */ |
| 454 | timeout_add_msec(&sc->sc_it_to, T_WARN300000); |
| 455 | } |
| 456 | sc->sc_minute = 0; |
| 457 | } |
| 458 | |
| 459 | timeout_add_msec(&sc->sc_to, T_SYNC950); /* resync in 950 ms */ |
| 460 | |
| 461 | /* no clock and bit detection during sync */ |
| 462 | if (!sc->sc_sync) { |
| 463 | /* detect bit value */ |
| 464 | timeout_add_msec(&sc->sc_bv_to, T_BV150); |
| 465 | } |
| 466 | timeout_add_msec(&sc->sc_mg_to, T_MG1500); /* detect minute gap */ |
| 467 | timeout_add_msec(&sc->sc_sl_to, T_SL3000); /* detect signal loss */ |
| 468 | } |
| 469 | |
| 470 | /* detect the bit value */ |
| 471 | void |
| 472 | udcf_bv_probe(void *xsc) |
| 473 | { |
| 474 | struct udcf_softc *sc = xsc; |
| 475 | int data; |
| 476 | |
| 477 | if (usbd_is_dying(sc->sc_udev)) |
| 478 | return; |
| 479 | |
| 480 | data = sc->sc_signal(sc); |
| 481 | if (data == -1) { |
| 482 | DPRINTF(("bit detection failed\n")); |
| 483 | return; |
| 484 | } |
| 485 | |
| 486 | DPRINTFN(1, (data ? "0" : "1")); |
| 487 | if (!(data)) |
| 488 | sc->sc_tbits |= sc->sc_mask; |
| 489 | sc->sc_mask <<= 1; |
| 490 | } |
| 491 | |
| 492 | /* detect the minute gap */ |
| 493 | void |
| 494 | udcf_mg_probe(void *xsc) |
| 495 | { |
| 496 | struct udcf_softc *sc = xsc; |
| 497 | struct clock_ymdhms ymdhm; |
| 498 | struct timeval monotime; |
| 499 | int tdiff_recv, tdiff_local; |
| 500 | int skew; |
| 501 | int minute_bits, hour_bits, day_bits; |
| 502 | int month_bits, year_bits, wday; |
| 503 | int p1, p2, p3; |
| 504 | int p1_bit, p2_bit, p3_bit; |
| 505 | int r_bit, a1_bit, a2_bit, z1_bit, z2_bit; |
| 506 | int s_bit, m_bit; |
| 507 | u_int32_t parity = 0x6996; |
| 508 | |
| 509 | if (sc->sc_sync) { |
| 510 | sc->sc_minute = 1; |
| 511 | goto cleanbits; |
| 512 | } |
| 513 | |
| 514 | if (gettime() - sc->sc_last_mg < 57) { |
| 515 | DPRINTF(("\nunexpected gap, resync\n")); |
| 516 | sc->sc_sync = sc->sc_minute = 1; |
| 517 | goto cleanbits; |
| 518 | } |
| 519 | |
| 520 | /* extract bits w/o parity */ |
| 521 | m_bit = sc->sc_tbits & 1; |
| 522 | r_bit = sc->sc_tbits >> 15 & 1; |
| 523 | a1_bit = sc->sc_tbits >> 16 & 1; |
| 524 | z1_bit = sc->sc_tbits >> 17 & 1; |
| 525 | z2_bit = sc->sc_tbits >> 18 & 1; |
| 526 | a2_bit = sc->sc_tbits >> 19 & 1; |
| 527 | s_bit = sc->sc_tbits >> 20 & 1; |
| 528 | p1_bit = sc->sc_tbits >> 28 & 1; |
| 529 | p2_bit = sc->sc_tbits >> 35 & 1; |
| 530 | p3_bit = sc->sc_tbits >> 58 & 1; |
| 531 | |
| 532 | minute_bits = sc->sc_tbits >> 21 & 0x7f; |
| 533 | hour_bits = sc->sc_tbits >> 29 & 0x3f; |
| 534 | day_bits = sc->sc_tbits >> 36 & 0x3f; |
| 535 | wday = (sc->sc_tbits >> 42) & 0x07; |
| 536 | month_bits = sc->sc_tbits >> 45 & 0x1f; |
| 537 | year_bits = sc->sc_tbits >> 50 & 0xff; |
| 538 | |
| 539 | /* validate time information */ |
| 540 | p1 = (parity >> (minute_bits & 0x0f) & 1) ^ |
| 541 | (parity >> (minute_bits >> 4) & 1); |
| 542 | |
| 543 | p2 = (parity >> (hour_bits & 0x0f) & 1) ^ |
| 544 | (parity >> (hour_bits >> 4) & 1); |
| 545 | |
| 546 | p3 = (parity >> (day_bits & 0x0f) & 1) ^ |
| 547 | (parity >> (day_bits >> 4) & 1) ^ |
| 548 | ((parity >> wday) & 1) ^ (parity >> (month_bits & 0x0f) & 1) ^ |
| 549 | (parity >> (month_bits >> 4) & 1) ^ |
| 550 | (parity >> (year_bits & 0x0f) & 1) ^ |
| 551 | (parity >> (year_bits >> 4) & 1); |
| 552 | |
| 553 | if (m_bit == 0 && s_bit == 1 && p1 == p1_bit && p2 == p2_bit && |
| 554 | p3 == p3_bit && (z1_bit ^ z2_bit)) { |
| 555 | |
| 556 | /* Decode time */ |
| 557 | if ((ymdhm.dt_year = 2000 + FROMBCD(year_bits)(((year_bits) >> 4) * 10 + ((year_bits) & 0xf))) > 2037) { |
| 558 | DPRINTF(("year out of range, resync\n")); |
| 559 | sc->sc_sync = 1; |
| 560 | goto cleanbits; |
| 561 | } |
| 562 | ymdhm.dt_min = FROMBCD(minute_bits)(((minute_bits) >> 4) * 10 + ((minute_bits) & 0xf)); |
| 563 | ymdhm.dt_hour = FROMBCD(hour_bits)(((hour_bits) >> 4) * 10 + ((hour_bits) & 0xf)); |
| 564 | ymdhm.dt_day = FROMBCD(day_bits)(((day_bits) >> 4) * 10 + ((day_bits) & 0xf)); |
| 565 | ymdhm.dt_mon = FROMBCD(month_bits)(((month_bits) >> 4) * 10 + ((month_bits) & 0xf)); |
| 566 | ymdhm.dt_sec = 0; |
| 567 | |
| 568 | sc->sc_next = clock_ymdhms_to_secs(&ymdhm); |
| 569 | getmicrouptime(&monotime); |
| 570 | |
| 571 | /* convert to coordinated universal time */ |
| 572 | sc->sc_next -= z1_bit ? 7200 : 3600; |
| 573 | |
| 574 | DPRINTF(("\n%02d.%02d.%04d %02d:%02d:00 %s", |
| 575 | ymdhm.dt_day, ymdhm.dt_mon, ymdhm.dt_year, |
| 576 | ymdhm.dt_hour, ymdhm.dt_min, z1_bit ? "CEST" : "CET")); |
| 577 | DPRINTF((r_bit ? ", call bit" : "")); |
| 578 | DPRINTF((a1_bit ? ", dst chg ann." : "")); |
| 579 | DPRINTF((a2_bit ? ", leap sec ann." : "")); |
| 580 | DPRINTF(("\n")); |
| 581 | |
| 582 | if (sc->sc_last) { |
| 583 | tdiff_recv = sc->sc_next - sc->sc_last; |
| 584 | tdiff_local = monotime.tv_sec - sc->sc_last_tv.tv_sec; |
| 585 | skew = abs(tdiff_local - tdiff_recv); |
| 586 | #ifdef UDCF_DEBUG |
| 587 | if (sc->sc_skew.status == SENSOR_S_UNKNOWN) |
| 588 | sc->sc_skew.status = SENSOR_S_CRIT; |
| 589 | sc->sc_skew.value = skew * 1000000000LL; |
| 590 | getmicrotime(&sc->sc_skew.tv); |
| 591 | #endif |
| 592 | DPRINTF(("local = %d, recv = %d, skew = %d\n", |
| 593 | tdiff_local, tdiff_recv, skew)); |
| 594 | |
| 595 | if (skew && skew * 100LL / tdiff_local > MAX_SKEW5) { |
| 596 | DPRINTF(("skew out of tolerated range\n")); |
| 597 | goto cleanbits; |
| 598 | } else { |
| 599 | if (sc->sc_nrecv < 2) { |
| 600 | sc->sc_nrecv++; |
| 601 | DPRINTF(("got frame %d\n", |
| 602 | sc->sc_nrecv)); |
| 603 | } else { |
| 604 | DPRINTF(("data is valid\n")); |
| 605 | sc->sc_minute = 1; |
| 606 | } |
| 607 | } |
| 608 | } else { |
| 609 | DPRINTF(("received the first frame\n")); |
| 610 | sc->sc_nrecv = 1; |
| 611 | } |
| 612 | |
| 613 | /* record the time received and when it was received */ |
| 614 | sc->sc_last = sc->sc_next; |
| 615 | sc->sc_last_tv.tv_sec = monotime.tv_sec; |
| 616 | } else { |
| 617 | DPRINTF(("\nparity error, resync\n")); |
| 618 | sc->sc_sync = sc->sc_minute = 1; |
| 619 | } |
| 620 | |
| 621 | cleanbits: |
| 622 | timeout_add_msec(&sc->sc_to, T_MGSYNC450); /* re-sync in 450 ms */ |
| 623 | sc->sc_last_mg = gettime(); |
| 624 | sc->sc_tbits = 0LL; |
| 625 | sc->sc_mask = 1LL; |
| 626 | } |
| 627 | |
| 628 | /* detect signal loss */ |
| 629 | void |
| 630 | udcf_sl_probe(void *xsc) |
| 631 | { |
| 632 | struct udcf_softc *sc = xsc; |
| 633 | |
| 634 | if (usbd_is_dying(sc->sc_udev)) |
| 635 | return; |
| 636 | |
| 637 | DPRINTF(("no signal\n")); |
| 638 | sc->sc_sync = 1; |
| 639 | timeout_add_msec(&sc->sc_to, T_WAIT5000); |
| 640 | timeout_add_msec(&sc->sc_sl_to, T_WAIT5000 + T_SL3000); |
| 641 | } |
| 642 | |
| 643 | /* invalidate timedelta (called in an interrupt context) */ |
| 644 | void |
| 645 | udcf_it_intr(void *xsc) |
| 646 | { |
| 647 | struct udcf_softc *sc = xsc; |
| 648 | |
| 649 | if (usbd_is_dying(sc->sc_udev)) |
| 650 | return; |
| 651 | |
| 652 | if (sc->sc_sensor.status == SENSOR_S_OK) { |
| 653 | sc->sc_sensor.status = SENSOR_S_WARN; |
| 654 | /* |
| 655 | * further degrade in 15 minutes if we dont receive any new |
| 656 | * time information |
| 657 | */ |
| 658 | timeout_add_msec(&sc->sc_it_to, T_CRIT900000); |
| 659 | } else { |
| 660 | sc->sc_sensor.status = SENSOR_S_CRIT; |
| 661 | sc->sc_nrecv = 0; |
| 662 | } |
| 663 | } |