summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Binkert <nate@cvs.openbsd.org>2002-07-09 18:37:05 +0000
committerNathan Binkert <nate@cvs.openbsd.org>2002-07-09 18:37:05 +0000
commitf3fabe0b52340a7b09180f04b0802909327106c4 (patch)
tree38c8ddaa462a337aee03cad5e4a6ccd2196c368d
parent3b96bc2d1e2e4f9a9c127915e2aba05e670a3b61 (diff)
driver for D-Link DSB-R100 FM Radio
From NetBSD
-rw-r--r--sys/dev/usb/files.usb8
-rw-r--r--sys/dev/usb/udsbr.c279
2 files changed, 286 insertions, 1 deletions
diff --git a/sys/dev/usb/files.usb b/sys/dev/usb/files.usb
index 9bdc4909c9b..e06ba89fe33 100644
--- a/sys/dev/usb/files.usb
+++ b/sys/dev/usb/files.usb
@@ -1,4 +1,4 @@
-# $OpenBSD: files.usb,v 1.27 2002/06/11 07:49:56 nate Exp $
+# $OpenBSD: files.usb,v 1.28 2002/07/09 18:37:04 nate Exp $
# $NetBSD: files.usb,v 1.16 2000/02/14 20:29:54 augustss Exp $
#
# Config file and device description for machine-independent USB code.
@@ -110,6 +110,12 @@ device uyap: ezload
attach uyap at uhub
file dev/usb/uyap.c uyap
+# D-Link DSB-R100 FM radio
+device udsbr: radiobus
+attach udsbr at uhub
+file dev/usb/udsbr.c udsbr
+
+
# Ethernet adapters
# ADMtek AN986 Pegasus
device aue: ether, ifnet, mii, ifmedia
diff --git a/sys/dev/usb/udsbr.c b/sys/dev/usb/udsbr.c
new file mode 100644
index 00000000000..1ded0778657
--- /dev/null
+++ b/sys/dev/usb/udsbr.c
@@ -0,0 +1,279 @@
+/* $OpenBSD: udsbr.c,v 1.3 2002/07/09 18:37:04 nate Exp $ */
+/* $NetBSD: udsbr.c,v 1.6 2002/02/12 10:51:49 tron Exp $ */
+
+/*
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (lennart@augustsson.net).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Driver for the D-Link DSB-R100 FM radio.
+ * I apologize for the magic hex constants, but this is what happens
+ * when you have to reverse engineer the driver.
+ * Parts of the code borrowed from Linux and parts from Warner Losh's
+ * FreeBSD driver.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+
+#include <sys/radioio.h>
+#include <dev/radio_if.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+
+#include <dev/usb/usbdevs.h>
+
+#ifdef UDSBR_DEBUG
+#define DPRINTF(x) if (udsbrdebug) logprintf x
+#define DPRINTFN(n,x) if (udsbrdebug>(n)) logprintf x
+int udsbrdebug = 0;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+#define UDSBR_CONFIG_NO 1
+
+Static int udsbr_get_info(void *, struct radio_info *);
+Static int udsbr_set_info(void *, struct radio_info *);
+
+struct radio_hw_if udsbr_hw_if = {
+ NULL, /* open */
+ NULL, /* close */
+ udsbr_get_info,
+ udsbr_set_info,
+ NULL
+};
+
+struct udsbr_softc {
+ USBBASEDEVICE sc_dev;
+ usbd_device_handle sc_udev;
+
+ char sc_mute;
+ char sc_vol;
+ u_int32_t sc_freq;
+
+ struct device *sc_child;
+
+ char sc_dying;
+};
+
+Static int udsbr_req(struct udsbr_softc *sc, int ureq, int value,
+ int index);
+Static void udsbr_start(struct udsbr_softc *sc);
+Static void udsbr_stop(struct udsbr_softc *sc);
+Static void udsbr_setfreq(struct udsbr_softc *sc, int freq);
+Static int udsbr_status(struct udsbr_softc *sc);
+
+USB_DECLARE_DRIVER(udsbr);
+
+USB_MATCH(udsbr)
+{
+ USB_MATCH_START(udsbr, uaa);
+
+ DPRINTFN(50,("udsbr_match\n"));
+
+ if (uaa->iface != NULL)
+ return (UMATCH_NONE);
+
+ if (uaa->vendor != USB_VENDOR_CYPRESS ||
+ uaa->product != USB_PRODUCT_CYPRESS_FMRADIO)
+ return (UMATCH_NONE);
+ return (UMATCH_VENDOR_PRODUCT);
+}
+
+USB_ATTACH(udsbr)
+{
+ USB_ATTACH_START(udsbr, sc, uaa);
+ usbd_device_handle dev = uaa->device;
+ char devinfo[1024];
+ usbd_status err;
+
+ DPRINTFN(10,("udsbr_attach: sc=%p\n", sc));
+
+ usbd_devinfo(dev, 0, devinfo);
+ USB_ATTACH_SETUP;
+ printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo);
+
+ err = usbd_set_config_no(dev, UDSBR_CONFIG_NO, 1);
+ if (err) {
+ printf("%s: setting config no failed\n",
+ USBDEVNAME(sc->sc_dev));
+ USB_ATTACH_ERROR_RETURN;
+ }
+
+ sc->sc_udev = dev;
+
+ DPRINTFN(10, ("udsbr_attach: %p\n", sc->sc_udev));
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+
+ sc->sc_child = radio_attach_mi(&udsbr_hw_if, sc, USBDEV(sc->sc_dev));
+
+ USB_ATTACH_SUCCESS_RETURN;
+}
+
+USB_DETACH(udsbr)
+{
+ USB_DETACH_START(udsbr, sc);
+ int rv = 0;
+
+ if (sc->sc_child != NULL)
+ rv = config_detach(sc->sc_child, flags);
+
+ usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
+ USBDEV(sc->sc_dev));
+
+ return (rv);
+}
+
+int
+udsbr_activate(device_ptr_t self, enum devact act)
+{
+ struct udsbr_softc *sc = (struct udsbr_softc *)self;
+ int rv = 0;
+
+ switch (act) {
+ case DVACT_ACTIVATE:
+ return (EOPNOTSUPP);
+ break;
+
+ case DVACT_DEACTIVATE:
+ sc->sc_dying = 1;
+ if (sc->sc_child != NULL)
+ rv = config_deactivate(sc->sc_child);
+ break;
+ }
+ return (rv);
+}
+
+int
+udsbr_req(struct udsbr_softc *sc, int ureq, int value, int index)
+{
+ usb_device_request_t req;
+ usbd_status err;
+ u_char data;
+
+ DPRINTFN(1,("udsbr_req: ureq=0x%02x value=0x%04x index=0x%04x\n",
+ ureq, value, index));
+ req.bmRequestType = UT_READ_VENDOR_DEVICE;
+ req.bRequest = ureq;
+ USETW(req.wValue, value);
+ USETW(req.wIndex, index);
+ USETW(req.wLength, 1);
+ err = usbd_do_request(sc->sc_udev, &req, &data);
+ if (err) {
+ printf("%s: request failed err=%d\n", USBDEVNAME(sc->sc_dev),
+ err);
+ }
+ return !(data & 1);
+}
+
+void
+udsbr_start(struct udsbr_softc *sc)
+{
+ (void)udsbr_req(sc, 0x00, 0x0000, 0x00c7);
+ (void)udsbr_req(sc, 0x02, 0x0001, 0x0000);
+}
+
+void
+udsbr_stop(struct udsbr_softc *sc)
+{
+ (void)udsbr_req(sc, 0x00, 0x0016, 0x001c);
+ (void)udsbr_req(sc, 0x02, 0x0000, 0x0000);
+}
+
+void
+udsbr_setfreq(struct udsbr_softc *sc, int freq)
+{
+ DPRINTF(("udsbr_setfreq: setfreq=%d\n", freq));
+ /*
+ * Freq now is in Hz. We need to convert it to the frequency
+ * that the radio wants. This frequency is 10.7MHz above
+ * the actual frequency. We then need to convert to
+ * units of 12.5kHz. We add one to the IFM to make rounding
+ * easier.
+ */
+ freq = (freq * 1000 + 10700001) / 12500;
+ (void)udsbr_req(sc, 0x01, (freq >> 8) & 0xff, freq & 0xff);
+ (void)udsbr_req(sc, 0x00, 0x0096, 0x00b7);
+ usbd_delay_ms(sc->sc_udev, 240); /* wait for signal to settle */
+}
+
+int
+udsbr_status(struct udsbr_softc *sc)
+{
+ return (udsbr_req(sc, 0x00, 0x0000, 0x0024));
+}
+
+
+int
+udsbr_get_info(void *v, struct radio_info *ri)
+{
+ struct udsbr_softc *sc = v;
+
+ ri->mute = sc->sc_mute;
+ ri->volume = sc->sc_vol ? 255 : 0;
+ ri->caps = RADIO_CAPS_DETECT_STEREO;
+ ri->rfreq = 0;
+ ri->lock = 0;
+ ri->freq = sc->sc_freq;
+ ri->info = udsbr_status(sc) ? RADIO_INFO_STEREO : 0;
+
+ return (0);
+}
+
+int
+udsbr_set_info(void *v, struct radio_info *ri)
+{
+ struct udsbr_softc *sc = v;
+
+ sc->sc_mute = ri->mute != 0;
+ sc->sc_vol = ri->volume != 0;
+ sc->sc_freq = ri->freq;
+ udsbr_setfreq(sc, sc->sc_freq);
+
+ if (sc->sc_mute || sc->sc_vol == 0)
+ udsbr_stop(sc);
+ else
+ udsbr_start(sc);
+
+ return (0);
+}