diff options
author | Marc Balmer <mbalmer@cvs.openbsd.org> | 2008-07-05 12:11:52 +0000 |
---|---|---|
committer | Marc Balmer <mbalmer@cvs.openbsd.org> | 2008-07-05 12:11:52 +0000 |
commit | 8adbf99ba3a1501995eb4744071c45c9fd4e60d3 (patch) | |
tree | cb3922ac71b91b09b5823a93b425afdc3c1d64da /sys/dev/usb/udcf.c | |
parent | faae2e663a108bb0080dfb721de1ed15c2be5b55 (diff) |
Add support for the new Expert mouseCLOCK USB II that uses an FTDI
FT232R chip to interface the receiver (instead of the NetCologne
chip used on older models). Only the DCF77 and HBG receivers are
supported.
A sidenote: Gude ADS not only provides me with receivers and
documentation, but as a result of my feedback on their older receivers
the new ones now have proper USB product IDs to distinguish between
DCF77, HBG, and MSF.
Diffstat (limited to 'sys/dev/usb/udcf.c')
-rw-r--r-- | sys/dev/usb/udcf.c | 271 |
1 files changed, 197 insertions, 74 deletions
diff --git a/sys/dev/usb/udcf.c b/sys/dev/usb/udcf.c index 52079234dc5..3545c085485 100644 --- a/sys/dev/usb/udcf.c +++ b/sys/dev/usb/udcf.c @@ -1,7 +1,7 @@ -/* $OpenBSD: udcf.c,v 1.41 2007/11/23 16:23:11 mbalmer Exp $ */ +/* $OpenBSD: udcf.c,v 1.42 2008/07/05 12:11:51 mbalmer Exp $ */ /* - * Copyright (c) 2006, 2007 Marc Balmer <mbalmer@openbsd.org> + * Copyright (c) 2006, 2007, 2008 Marc Balmer <mbalmer@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -49,6 +49,10 @@ int udcfdebug = 0; #define UDCF_CTRL_IDX 0x33 #define UDCF_CTRL_VAL 0x98 +#define FT232R_RESET 0x00 /* reset USB request */ +#define FT232R_STATUS 0x05 /* get modem status USB request */ +#define FT232R_RI 0x40 /* ring indicator */ + #define DPERIOD1 ((long) 5 * 60) /* degrade OK -> WARN */ #define DPERIOD2 ((long) 15 * 60) /* degrade WARN -> CRIT */ @@ -84,6 +88,7 @@ struct udcf_softc { usb_device_request_t sc_req; + int sc_detect_ct; /* != 0: autodetect type */ int sc_clocktype; /* DCF77 or HBG */ int sc_sync; /* 1 during sync */ u_int64_t sc_mask; /* 64 bit mask */ @@ -91,6 +96,7 @@ struct udcf_softc { int sc_minute; int sc_level; time_t sc_last_mg; + int (*sc_signal)(struct udcf_softc *); time_t sc_current; /* current time */ time_t sc_next; /* time to become valid next */ @@ -136,6 +142,11 @@ void udcf_attach(struct device *, struct device *, void *); int udcf_detach(struct device *, int); int udcf_activate(struct device *, enum devact); +int udcf_nc_signal(struct udcf_softc *); +int udcf_nc_init_hw(struct udcf_softc *); +int udcf_ft232r_signal(struct udcf_softc *); +int udcf_ft232r_init_hw(struct udcf_softc *); + struct cfdriver udcf_cd = { NULL, "udcf", DV_DULL }; @@ -148,6 +159,13 @@ const struct cfattach udcf_ca = { udcf_activate, }; +static const struct usb_devno udcf_devs[] = { + { USB_VENDOR_GUDE, USB_PRODUCT_GUDE_DCF }, + { USB_VENDOR_FTDI, USB_PRODUCT_FTDI_DCF }, + { USB_VENDOR_FTDI, USB_PRODUCT_FTDI_HBG }, + { 0, 0 } +}; + int udcf_match(struct device *parent, void *match, void *aux) { @@ -156,9 +174,10 @@ udcf_match(struct device *parent, void *match, void *aux) if (uaa->iface != NULL) return UMATCH_NONE; - return uaa->vendor == USB_VENDOR_GUDE && - uaa->product == USB_PRODUCT_GUDE_DCF ? - UMATCH_VENDOR_PRODUCT : UMATCH_NONE; + if (usb_lookup(udcf_devs, uaa->vendor, uaa->product) == NULL) + return UMATCH_NONE; + + return UMATCH_VENDOR_PRODUCT; } void @@ -170,23 +189,41 @@ udcf_attach(struct device *parent, struct device *self, void *aux) usbd_interface_handle iface; struct timeval t; usbd_status err; - usb_device_request_t req; - uWord result; - int actlen; + + switch (uaa->product) { + case USB_PRODUCT_GUDE_DCF: + sc->sc_detect_ct = 1; + sc->sc_signal = udcf_nc_signal; + strlcpy(sc->sc_sensor.desc, "Unknown", + sizeof(sc->sc_sensor.desc)); + break; + case USB_PRODUCT_FTDI_DCF: + sc->sc_signal = udcf_ft232r_signal; + strlcpy(sc->sc_sensor.desc, clockname[CLOCK_DCF77], + sizeof(sc->sc_sensor.desc)); + break; + case USB_PRODUCT_FTDI_HBG: + sc->sc_signal = udcf_ft232r_signal; + strlcpy(sc->sc_sensor.desc, clockname[CLOCK_HBG], + sizeof(sc->sc_sensor.desc)); + break; + } usb_init_task(&sc->sc_task, udcf_probe, sc); usb_init_task(&sc->sc_bv_task, udcf_bv_probe, sc); usb_init_task(&sc->sc_mg_task, udcf_mg_probe, sc); usb_init_task(&sc->sc_sl_task, udcf_sl_probe, sc); - usb_init_task(&sc->sc_ct_task, udcf_ct_probe, sc); timeout_set(&sc->sc_to, udcf_intr, sc); timeout_set(&sc->sc_bv_to, udcf_bv_intr, sc); timeout_set(&sc->sc_mg_to, udcf_mg_intr, sc); timeout_set(&sc->sc_sl_to, udcf_sl_intr, sc); timeout_set(&sc->sc_it_to, udcf_it_intr, sc); - timeout_set(&sc->sc_ct_to, udcf_ct_intr, sc); + if (sc->sc_detect_ct) { + usb_init_task(&sc->sc_ct_task, udcf_ct_probe, sc); + timeout_set(&sc->sc_ct_to, udcf_ct_intr, sc); + } strlcpy(sc->sc_sensordev.xname, sc->sc_dev.dv_xname, sizeof(sc->sc_sensordev.xname)); @@ -194,7 +231,6 @@ udcf_attach(struct device *parent, struct device *self, void *aux) sc->sc_sensor.status = SENSOR_S_UNKNOWN; sc->sc_sensor.value = 0LL; sc->sc_sensor.flags = 0; - strlcpy(sc->sc_sensor.desc, "Unknown", sizeof(sc->sc_sensor.desc)); sensor_attach(&sc->sc_sensordev, &sc->sc_sensor); #ifdef UDCF_DEBUG @@ -208,7 +244,6 @@ udcf_attach(struct device *parent, struct device *self, void *aux) #endif sensordev_install(&sc->sc_sensordev); - sc->sc_udev = dev; if ((err = usbd_set_config_index(dev, 0, 1))) { DPRINTF(("%s: failed to set configuration, err=%s\n", @@ -237,33 +272,16 @@ udcf_attach(struct device *parent, struct device *self, void *aux) sc->sc_last = 0L; sc->sc_last_tv.tv_sec = 0L; - /* Prepare the USB request to probe the value */ - sc->sc_req.bmRequestType = UDCF_READ_REQ; - sc->sc_req.bRequest = 1; - USETW(sc->sc_req.wValue, 0); - USETW(sc->sc_req.wIndex, UDCF_READ_IDX); - USETW(sc->sc_req.wLength, 1); - - req.bmRequestType = UDCF_CTRL_REQ; - req.bRequest = 0; - USETW(req.wValue, 0); - USETW(req.wIndex, 0); - USETW(req.wLength, 0); - if ((err = usbd_do_request_flags(sc->sc_udev, &req, &result, - USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT))) { - DPRINTF(("failed to turn on power for receiver\n")); - goto fishy; - } - - req.bmRequestType = UDCF_CTRL_REQ; - req.bRequest = 0; - USETW(req.wValue, UDCF_CTRL_VAL); - USETW(req.wIndex, UDCF_CTRL_IDX); - USETW(req.wLength, 0); - if ((err = usbd_do_request_flags(sc->sc_udev, &req, &result, - USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT))) { - DPRINTF(("failed to turn on receiver\n")); - goto fishy; + switch (uaa->product) { + case USB_PRODUCT_GUDE_DCF: + if (udcf_nc_init_hw(sc)) + goto fishy; + break; + case USB_PRODUCT_FTDI_DCF: /* FALLTHROUGH */ + case USB_PRODUCT_FTDI_HBG: + if (udcf_ft232r_init_hw(sc)) + goto fishy; + break; } usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, @@ -297,9 +315,11 @@ udcf_attach(struct device *parent, struct device *self, void *aux) t.tv_sec = DPERIOD2; t_crit = tvtohz(&t); - t.tv_sec = 0L; - t.tv_usec = 250000L; - t_ct = tvtohz(&t); + if (sc->sc_detect_ct) { + t.tv_sec = 0L; + t.tv_usec = 250000L; + t_ct = tvtohz(&t); + } /* Give the receiver some slack to stabilize */ timeout_add(&sc->sc_to, t_wait); @@ -327,7 +347,8 @@ udcf_detach(struct device *self, int flags) timeout_del(&sc->sc_mg_to); timeout_del(&sc->sc_sl_to); timeout_del(&sc->sc_it_to); - timeout_del(&sc->sc_ct_to); + if (sc->sc_detect_ct) + timeout_del(&sc->sc_ct_to); /* Unregister the clock with the kernel */ sensordev_deinstall(&sc->sc_sensordev); @@ -335,7 +356,8 @@ udcf_detach(struct device *self, int flags) usb_rem_task(sc->sc_udev, &sc->sc_bv_task); usb_rem_task(sc->sc_udev, &sc->sc_mg_task); usb_rem_task(sc->sc_udev, &sc->sc_sl_task); - usb_rem_task(sc->sc_udev, &sc->sc_ct_task); + if (sc->sc_detect_ct) + usb_rem_task(sc->sc_udev, &sc->sc_ct_task); usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, &sc->sc_dev); @@ -383,26 +405,129 @@ udcf_ct_intr(void *xsc) } /* - * udcf_probe runs in a process context. If bit 0 is set, the transmitter - * emits at full power. During the low-power emission we decode a zero bit. + * initialize the Expert mouseCLOCK USB devices, they use a NetCologne + * chip to interface the receiver. Power must be supplied to the + * receiver and the receiver must be turned on. */ +int +udcf_nc_init_hw(struct udcf_softc *sc) +{ + usbd_status err; + usb_device_request_t req; + uWord result; + int actlen; + + /* Prepare the USB request to probe the value */ + sc->sc_req.bmRequestType = UDCF_READ_REQ; + sc->sc_req.bRequest = 1; + USETW(sc->sc_req.wValue, 0); + USETW(sc->sc_req.wIndex, UDCF_READ_IDX); + USETW(sc->sc_req.wLength, 1); + + req.bmRequestType = UDCF_CTRL_REQ; + req.bRequest = 0; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + if ((err = usbd_do_request_flags(sc->sc_udev, &req, &result, + USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT))) { + DPRINTF(("failed to turn on power for receiver\n")); + return -1; + } + + req.bmRequestType = UDCF_CTRL_REQ; + req.bRequest = 0; + USETW(req.wValue, UDCF_CTRL_VAL); + USETW(req.wIndex, UDCF_CTRL_IDX); + USETW(req.wLength, 0); + if ((err = usbd_do_request_flags(sc->sc_udev, &req, &result, + USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT))) { + DPRINTF(("failed to turn on receiver\n")); + return -1; + } + return 0; +} + +/* + * initialize the Expert mouseCLOCK USB II devices, they use an FTDI + * FT232R chip to interface the receiver. Only reset the chip. + */ +int +udcf_ft232r_init_hw(struct udcf_softc *sc) +{ + usbd_status err; + usb_device_request_t req; + + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = FT232R_RESET; + /* 0 resets the SIO */ + USETW(req.wValue,FT232R_RESET); + USETW(req.wIndex, 0); + USETW(req.wLength, 0); + err = usbd_do_request(sc->sc_udev, &req, NULL); + if (err) { + DPRINTF(("failed to reset ftdi\n")); + return -1; + } + return 0; +} + +/* + * return 1 during high-power-, 0 during low-power-emission + * If bit 0 is set, the transmitter emits at full power. + * During the low-power emission we decode a zero bit. + */ +int +udcf_nc_signal(struct udcf_softc *sc) +{ + int actlen; + unsigned char data; + + if (usbd_do_request_flags(sc->sc_udev, &sc->sc_req, &data, + USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT)) + /* This happens if we pull the receiver */ + return -1; + return data & 0x01; +} + +/* pick up the signal level through the FTDI FT232R chip */ +int +udcf_ft232r_signal(struct udcf_softc *sc) +{ + usb_device_request_t req; + int actlen; + u_int16_t data; + + req.bmRequestType = UT_READ_VENDOR_DEVICE; + req.bRequest = FT232R_STATUS; + USETW(req.wValue, 0); + USETW(req.wIndex, 0); + USETW(req.wLength, 2); + if (usbd_do_request_flags(sc->sc_udev, &req, &data, + USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT)) { + DPRINTFN(2, ("error reading ftdi modem status\n")); + return -1; + } + DPRINTFN(2, ("ftdi status 0x%04x\n", data)); + return data & FT232R_RI ? 0 : 1; +} + +/* udcf_probe runs in a process context. */ void udcf_probe(void *xsc) { struct udcf_softc *sc = xsc; struct timespec now; - unsigned char data; - int actlen; + int data; if (sc->sc_dying) return; - if (usbd_do_request_flags(sc->sc_udev, &sc->sc_req, &data, - USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT)) - /* This happens if we pull the receiver */ + data = sc->sc_signal(sc); + if (data == -1) return; - if (data & 0x01) { + if (data) { sc->sc_level = 1; timeout_add(&sc->sc_to, 1); return; @@ -411,13 +536,14 @@ udcf_probe(void *xsc) if (sc->sc_level == 0) return; - /* Begin of a second */ + /* the beginning of a second */ sc->sc_level = 0; if (sc->sc_minute == 1) { if (sc->sc_sync) { DPRINTF(("start collecting bits\n")); sc->sc_sync = 0; - if (sc->sc_sensor.status == SENSOR_S_UNKNOWN) + if (sc->sc_sensor.status == SENSOR_S_UNKNOWN && + sc->sc_detect_ct) sc->sc_clocktype = -1; } else { /* provide the timedelta */ @@ -428,7 +554,8 @@ udcf_probe(void *xsc) sc->sc_current) * 1000000000LL + now.tv_nsec; /* set the clocktype and make sensor valid */ - if (sc->sc_sensor.status == SENSOR_S_UNKNOWN) { + if (sc->sc_sensor.status == SENSOR_S_UNKNOWN && + sc->sc_detect_ct) { strlcpy(sc->sc_sensor.desc, sc->sc_clocktype ? clockname[CLOCK_HBG] : clockname[CLOCK_DCF77], @@ -448,13 +575,13 @@ udcf_probe(void *xsc) timeout_add(&sc->sc_to, t_sync); /* resync in 950 ms */ - /* No clock and bit detection during sync */ + /* no clock and bit detection during sync */ if (!sc->sc_sync) { /* detect bit value */ timeout_add(&sc->sc_bv_to, t_bv); /* detect clocktype */ - if (sc->sc_clocktype == -1) + if (sc->sc_detect_ct && sc->sc_clocktype == -1) timeout_add(&sc->sc_ct_to, t_ct); } timeout_add(&sc->sc_mg_to, t_mg); /* detect minute gap */ @@ -466,21 +593,19 @@ void udcf_bv_probe(void *xsc) { struct udcf_softc *sc = xsc; - int actlen; - unsigned char data; + int data; if (sc->sc_dying) return; - if (usbd_do_request_flags(sc->sc_udev, &sc->sc_req, &data, - USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT)) { - /* This happens if we pull the receiver */ + data = sc->sc_signal(sc); + if (data == -1) { DPRINTF(("bit detection failed\n")); return; - } + } - DPRINTFN(1, (data & 0x01 ? "0" : "1")); - if (!(data & 0x01)) + DPRINTFN(1, (data ? "0" : "1")); + if (!(data)) sc->sc_tbits |= sc->sc_mask; sc->sc_mask <<= 1; } @@ -513,7 +638,7 @@ udcf_mg_probe(void *xsc) goto cleanbits; } - /* Extract bits w/o parity */ + /* extract bits w/o parity */ m_bit = sc->sc_tbits & 1; r_bit = sc->sc_tbits >> 15 & 1; a1_bit = sc->sc_tbits >> 16 & 1; @@ -532,7 +657,7 @@ udcf_mg_probe(void *xsc) month_bits = sc->sc_tbits >> 45 & 0x1f; year_bits = sc->sc_tbits >> 50 & 0xff; - /* Validate time information */ + /* validate time information */ p1 = (parity >> (minute_bits & 0x0f) & 1) ^ (parity >> (minute_bits >> 4) & 1); @@ -658,25 +783,23 @@ udcf_it_intr(void *xsc) } } -/* detect clock type */ +/* detect clock type. used for older devices only. */ void udcf_ct_probe(void *xsc) { struct udcf_softc *sc = xsc; - int actlen; - unsigned char data; + int data; if (sc->sc_dying) return; - if (usbd_do_request_flags(sc->sc_udev, &sc->sc_req, &data, - USBD_SHORT_XFER_OK, &actlen, USBD_DEFAULT_TIMEOUT)) { - /* This happens if we pull the receiver */ + data = sc->sc_signal(sc); + if (data == -1) { DPRINTF(("clocktype detection failed\n")); return; } - sc->sc_clocktype = data & 0x01 ? 0 : 1; + sc->sc_clocktype = data ? 0 : 1; DPRINTF(("\nclocktype is %s\n", sc->sc_clocktype ? clockname[CLOCK_HBG] : clockname[CLOCK_DCF77])); } |