diff options
-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])); } |