udsbr.c revision 1.2
11.2Saugustss/* $NetBSD: udsbr.c,v 1.2 2002/01/02 03:44:56 augustss Exp $ */ 21.1Saugustss 31.1Saugustss/* 41.1Saugustss * Copyright (c) 2002 The NetBSD Foundation, Inc. 51.1Saugustss * All rights reserved. 61.1Saugustss * 71.1Saugustss * This code is derived from software contributed to The NetBSD Foundation 81.1Saugustss * by Lennart Augustsson (lennart@augustsson.net) at 91.1Saugustss * Carlstedt Research & Technology. 101.1Saugustss * 111.1Saugustss * Redistribution and use in source and binary forms, with or without 121.1Saugustss * modification, are permitted provided that the following conditions 131.1Saugustss * are met: 141.1Saugustss * 1. Redistributions of source code must retain the above copyright 151.1Saugustss * notice, this list of conditions and the following disclaimer. 161.1Saugustss * 2. Redistributions in binary form must reproduce the above copyright 171.1Saugustss * notice, this list of conditions and the following disclaimer in the 181.1Saugustss * documentation and/or other materials provided with the distribution. 191.1Saugustss * 3. All advertising materials mentioning features or use of this software 201.1Saugustss * must display the following acknowledgement: 211.1Saugustss * This product includes software developed by the NetBSD 221.1Saugustss * Foundation, Inc. and its contributors. 231.1Saugustss * 4. Neither the name of The NetBSD Foundation nor the names of its 241.1Saugustss * contributors may be used to endorse or promote products derived 251.1Saugustss * from this software without specific prior written permission. 261.1Saugustss * 271.1Saugustss * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 281.1Saugustss * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 291.1Saugustss * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 301.1Saugustss * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 311.1Saugustss * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 321.1Saugustss * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 331.1Saugustss * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 341.1Saugustss * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 351.1Saugustss * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 361.1Saugustss * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 371.1Saugustss * POSSIBILITY OF SUCH DAMAGE. 381.1Saugustss */ 391.1Saugustss 401.2Saugustss/* 411.2Saugustss * Driver for the D-Link DSB-R100 FM radio. 421.2Saugustss * I apologize for the magix hex constants, but this is what happens 431.2Saugustss * when you have to reverse engineer the driver. 441.2Saugustss * Parts of the code borrowed from Linux and parts from Warner Losh's 451.2Saugustss * FreeBSD driver. 461.2Saugustss */ 471.2Saugustss 481.1Saugustss#include <sys/cdefs.h> 491.2Saugustss__KERNEL_RCSID(0, "$NetBSD: udsbr.c,v 1.2 2002/01/02 03:44:56 augustss Exp $"); 501.1Saugustss 511.1Saugustss#include <sys/param.h> 521.1Saugustss#include <sys/systm.h> 531.1Saugustss#include <sys/kernel.h> 541.1Saugustss#include <sys/device.h> 551.1Saugustss 561.1Saugustss#include <sys/radioio.h> 571.1Saugustss#include <dev/radio_if.h> 581.1Saugustss 591.1Saugustss#include <dev/usb/usb.h> 601.1Saugustss#include <dev/usb/usbdi.h> 611.1Saugustss#include <dev/usb/usbdi_util.h> 621.1Saugustss 631.1Saugustss#include <dev/usb/usbdevs.h> 641.1Saugustss 651.1Saugustss#ifdef UDSBR_DEBUG 661.1Saugustss#define DPRINTF(x) if (udsbrdebug) logprintf x 671.1Saugustss#define DPRINTFN(n,x) if (udsbrdebug>(n)) logprintf x 681.1Saugustssint udsbrdebug = 0; 691.1Saugustss#else 701.1Saugustss#define DPRINTF(x) 711.1Saugustss#define DPRINTFN(n,x) 721.1Saugustss#endif 731.1Saugustss 741.1Saugustss#define UDSBR_CONFIG_NO 1 751.1Saugustss 761.1Saugustssint udsbr_get_info(void *, struct radio_info *); 771.1Saugustssint udsbr_set_info(void *, struct radio_info *); 781.1Saugustssint udsbr_search(void *, int); 791.1Saugustss 801.1Saugustssstruct radio_hw_if udsbr_hw_if = { 811.1Saugustss NULL, /* open */ 821.1Saugustss NULL, /* close */ 831.1Saugustss udsbr_get_info, 841.1Saugustss udsbr_set_info, 851.1Saugustss NULL 861.1Saugustss}; 871.1Saugustss 881.1Saugustssstruct udsbr_softc { 891.1Saugustss USBBASEDEVICE sc_dev; 901.1Saugustss usbd_device_handle sc_udev; 911.1Saugustss 921.1Saugustss char sc_mute; 931.1Saugustss char sc_vol; 941.1Saugustss u_int32_t sc_freq; 951.1Saugustss 961.1Saugustss struct device *sc_child; 971.1Saugustss 981.1Saugustss char sc_dying; 991.1Saugustss}; 1001.1Saugustss 1011.1Saugustssint udsbr_req(struct udsbr_softc *sc, int ureq, int value, int index); 1021.1Saugustssvoid udsbr_start(struct udsbr_softc *sc); 1031.1Saugustssvoid udsbr_stop(struct udsbr_softc *sc); 1041.1Saugustssvoid udsbr_setfreq(struct udsbr_softc *sc, int freq); 1051.1Saugustssint udsbr_status(struct udsbr_softc *sc); 1061.1Saugustss 1071.1SaugustssUSB_DECLARE_DRIVER(udsbr); 1081.1Saugustss 1091.1SaugustssUSB_MATCH(udsbr) 1101.1Saugustss{ 1111.1Saugustss USB_MATCH_START(udsbr, uaa); 1121.1Saugustss 1131.1Saugustss DPRINTFN(50,("udsbr_match\n")); 1141.1Saugustss 1151.1Saugustss if (uaa->iface != NULL) 1161.1Saugustss return (UMATCH_NONE); 1171.1Saugustss 1181.1Saugustss if (uaa->vendor != USB_VENDOR_CYPRESS || 1191.1Saugustss uaa->product != USB_PRODUCT_CYPRESS_FMRADIO) 1201.1Saugustss return (UMATCH_NONE); 1211.1Saugustss return (UMATCH_VENDOR_PRODUCT); 1221.1Saugustss} 1231.1Saugustss 1241.1SaugustssUSB_ATTACH(udsbr) 1251.1Saugustss{ 1261.1Saugustss USB_ATTACH_START(udsbr, sc, uaa); 1271.1Saugustss usbd_device_handle dev = uaa->device; 1281.1Saugustss char devinfo[1024]; 1291.1Saugustss usbd_status err; 1301.1Saugustss 1311.1Saugustss DPRINTFN(10,("udsbr_attach: sc=%p\n", sc)); 1321.1Saugustss 1331.1Saugustss usbd_devinfo(dev, 0, devinfo); 1341.1Saugustss USB_ATTACH_SETUP; 1351.1Saugustss printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo); 1361.1Saugustss 1371.1Saugustss err = usbd_set_config_no(dev, UDSBR_CONFIG_NO, 1); 1381.1Saugustss if (err) { 1391.1Saugustss printf("%s: setting config no failed\n", 1401.1Saugustss USBDEVNAME(sc->sc_dev)); 1411.1Saugustss USB_ATTACH_ERROR_RETURN; 1421.1Saugustss } 1431.1Saugustss 1441.1Saugustss sc->sc_udev = dev; 1451.1Saugustss 1461.1Saugustss DPRINTFN(10, ("udsbr_attach: %p\n", sc->sc_udev)); 1471.1Saugustss 1481.1Saugustss usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, 1491.1Saugustss USBDEV(sc->sc_dev)); 1501.1Saugustss 1511.1Saugustss sc->sc_child = radio_attach_mi(&udsbr_hw_if, sc, USBDEV(sc->sc_dev)); 1521.1Saugustss 1531.1Saugustss USB_ATTACH_SUCCESS_RETURN; 1541.1Saugustss} 1551.1Saugustss 1561.1SaugustssUSB_DETACH(udsbr) 1571.1Saugustss{ 1581.1Saugustss USB_DETACH_START(udsbr, sc); 1591.1Saugustss int rv = 0; 1601.1Saugustss 1611.1Saugustss if (sc->sc_child != NULL) 1621.1Saugustss rv = config_detach(sc->sc_child, flags); 1631.1Saugustss 1641.1Saugustss usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, 1651.1Saugustss USBDEV(sc->sc_dev)); 1661.1Saugustss 1671.1Saugustss return (rv); 1681.1Saugustss} 1691.1Saugustss 1701.1Saugustssint 1711.1Saugustssudsbr_activate(device_ptr_t self, enum devact act) 1721.1Saugustss{ 1731.1Saugustss struct udsbr_softc *sc = (struct udsbr_softc *)self; 1741.1Saugustss int rv = 0; 1751.1Saugustss 1761.1Saugustss switch (act) { 1771.1Saugustss case DVACT_ACTIVATE: 1781.1Saugustss return (EOPNOTSUPP); 1791.1Saugustss break; 1801.1Saugustss 1811.1Saugustss case DVACT_DEACTIVATE: 1821.1Saugustss sc->sc_dying = 1; 1831.1Saugustss if (sc->sc_child != NULL) 1841.1Saugustss rv = config_deactivate(sc->sc_child); 1851.1Saugustss break; 1861.1Saugustss } 1871.1Saugustss return (rv); 1881.1Saugustss} 1891.1Saugustss 1901.1Saugustssint 1911.1Saugustssudsbr_req(struct udsbr_softc *sc, int ureq, int value, int index) 1921.1Saugustss{ 1931.1Saugustss usb_device_request_t req; 1941.1Saugustss usbd_status err; 1951.1Saugustss u_char data; 1961.1Saugustss 1971.1Saugustss req.bmRequestType = UT_READ_VENDOR_DEVICE; 1981.1Saugustss req.bRequest = ureq; 1991.1Saugustss USETW(req.wValue, value); 2001.1Saugustss USETW(req.wIndex, index); 2011.1Saugustss USETW(req.wLength, 1); 2021.1Saugustss err = usbd_do_request(sc->sc_udev, &req, &data); 2031.1Saugustss if (err) { 2041.1Saugustss printf("%s: request failed err=%d\n", USBDEVNAME(sc->sc_dev), 2051.1Saugustss err); 2061.1Saugustss } 2071.1Saugustss return !(data & 1); 2081.1Saugustss} 2091.1Saugustss 2101.1Saugustssvoid 2111.1Saugustssudsbr_start(struct udsbr_softc *sc) 2121.1Saugustss{ 2131.1Saugustss (void)udsbr_req(sc, 0x00, 0x0000, 0x00c7); 2141.1Saugustss (void)udsbr_req(sc, 0x02, 0x0001, 0x0000); 2151.1Saugustss} 2161.1Saugustss 2171.1Saugustssvoid 2181.1Saugustssudsbr_stop(struct udsbr_softc *sc) 2191.1Saugustss{ 2201.1Saugustss (void)udsbr_req(sc, 0x00, 0x0016, 0x001c); 2211.1Saugustss (void)udsbr_req(sc, 0x02, 0x0000, 0x0000); 2221.1Saugustss} 2231.1Saugustss 2241.1Saugustssvoid 2251.1Saugustssudsbr_setfreq(struct udsbr_softc *sc, int freq) 2261.1Saugustss{ 2271.1Saugustss DPRINTF(("udsbr_setfreq: setfreq=%d\n", freq)); 2281.2Saugustss /* 2291.2Saugustss * Freq now is in Hz. We need to convert it to the frequency 2301.2Saugustss * that the radio wants. This frequency is 10.7MHz above 2311.2Saugustss * the actual frequency. We then need to convert to 2321.2Saugustss * units of 12.5kHz. We add one to the IFM to make rounding 2331.2Saugustss * easier. 2341.2Saugustss */ 2351.2Saugustss freq = (freq + 10700001) / 12500; 2361.1Saugustss (void)udsbr_req(sc, 0x01, (freq >> 8) & 0xff, freq & 0xff); 2371.1Saugustss (void)udsbr_req(sc, 0x00, 0x0096, 0x00b7); 2381.1Saugustss} 2391.1Saugustss 2401.1Saugustssint 2411.1Saugustssudsbr_status(struct udsbr_softc *sc) 2421.1Saugustss{ 2431.2Saugustss usbd_delay_ms(sc->sc_udev, 240); /* XXX wait for signal to settle */ 2441.1Saugustss return (udsbr_req(sc, 0x00, 0x0000, 0x0024)); 2451.1Saugustss} 2461.1Saugustss 2471.1Saugustss 2481.1Saugustssint 2491.1Saugustssudsbr_get_info(void *v, struct radio_info *ri) 2501.1Saugustss{ 2511.1Saugustss struct udsbr_softc *sc = v; 2521.1Saugustss 2531.1Saugustss ri->mute = sc->sc_mute; 2541.1Saugustss ri->volume = sc->sc_vol ? 255 : 0; 2551.1Saugustss ri->caps = RADIO_CAPS_DETECT_STEREO; 2561.1Saugustss ri->rfreq = 0; 2571.1Saugustss ri->lock = 0; 2581.1Saugustss ri->freq = sc->sc_freq; 2591.1Saugustss ri->info = udsbr_status(sc) ? RADIO_INFO_STEREO : 0; 2601.1Saugustss 2611.1Saugustss return (0); 2621.1Saugustss} 2631.1Saugustss 2641.1Saugustssint 2651.1Saugustssudsbr_set_info(void *v, struct radio_info *ri) 2661.1Saugustss{ 2671.1Saugustss struct udsbr_softc *sc = v; 2681.1Saugustss 2691.1Saugustss sc->sc_mute = ri->mute != 0; 2701.1Saugustss sc->sc_vol = ri->volume != 0; 2711.1Saugustss sc->sc_freq = ri->freq; 2721.1Saugustss udsbr_setfreq(sc, sc->sc_freq); 2731.1Saugustss 2741.1Saugustss if (sc->sc_mute || sc->sc_vol == 0) 2751.1Saugustss udsbr_stop(sc); 2761.1Saugustss else 2771.1Saugustss udsbr_start(sc); 2781.1Saugustss 2791.1Saugustss return (0); 2801.1Saugustss} 281