udsbr.c revision 1.2
1/* $NetBSD: udsbr.c,v 1.2 2002/01/02 03:44:56 augustss Exp $ */ 2 3/* 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Lennart Augustsson (lennart@augustsson.net) at 9 * Carlstedt Research & Technology. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40/* 41 * Driver for the D-Link DSB-R100 FM radio. 42 * I apologize for the magix hex constants, but this is what happens 43 * when you have to reverse engineer the driver. 44 * Parts of the code borrowed from Linux and parts from Warner Losh's 45 * FreeBSD driver. 46 */ 47 48#include <sys/cdefs.h> 49__KERNEL_RCSID(0, "$NetBSD: udsbr.c,v 1.2 2002/01/02 03:44:56 augustss Exp $"); 50 51#include <sys/param.h> 52#include <sys/systm.h> 53#include <sys/kernel.h> 54#include <sys/device.h> 55 56#include <sys/radioio.h> 57#include <dev/radio_if.h> 58 59#include <dev/usb/usb.h> 60#include <dev/usb/usbdi.h> 61#include <dev/usb/usbdi_util.h> 62 63#include <dev/usb/usbdevs.h> 64 65#ifdef UDSBR_DEBUG 66#define DPRINTF(x) if (udsbrdebug) logprintf x 67#define DPRINTFN(n,x) if (udsbrdebug>(n)) logprintf x 68int udsbrdebug = 0; 69#else 70#define DPRINTF(x) 71#define DPRINTFN(n,x) 72#endif 73 74#define UDSBR_CONFIG_NO 1 75 76int udsbr_get_info(void *, struct radio_info *); 77int udsbr_set_info(void *, struct radio_info *); 78int udsbr_search(void *, int); 79 80struct radio_hw_if udsbr_hw_if = { 81 NULL, /* open */ 82 NULL, /* close */ 83 udsbr_get_info, 84 udsbr_set_info, 85 NULL 86}; 87 88struct udsbr_softc { 89 USBBASEDEVICE sc_dev; 90 usbd_device_handle sc_udev; 91 92 char sc_mute; 93 char sc_vol; 94 u_int32_t sc_freq; 95 96 struct device *sc_child; 97 98 char sc_dying; 99}; 100 101int udsbr_req(struct udsbr_softc *sc, int ureq, int value, int index); 102void udsbr_start(struct udsbr_softc *sc); 103void udsbr_stop(struct udsbr_softc *sc); 104void udsbr_setfreq(struct udsbr_softc *sc, int freq); 105int udsbr_status(struct udsbr_softc *sc); 106 107USB_DECLARE_DRIVER(udsbr); 108 109USB_MATCH(udsbr) 110{ 111 USB_MATCH_START(udsbr, uaa); 112 113 DPRINTFN(50,("udsbr_match\n")); 114 115 if (uaa->iface != NULL) 116 return (UMATCH_NONE); 117 118 if (uaa->vendor != USB_VENDOR_CYPRESS || 119 uaa->product != USB_PRODUCT_CYPRESS_FMRADIO) 120 return (UMATCH_NONE); 121 return (UMATCH_VENDOR_PRODUCT); 122} 123 124USB_ATTACH(udsbr) 125{ 126 USB_ATTACH_START(udsbr, sc, uaa); 127 usbd_device_handle dev = uaa->device; 128 char devinfo[1024]; 129 usbd_status err; 130 131 DPRINTFN(10,("udsbr_attach: sc=%p\n", sc)); 132 133 usbd_devinfo(dev, 0, devinfo); 134 USB_ATTACH_SETUP; 135 printf("%s: %s\n", USBDEVNAME(sc->sc_dev), devinfo); 136 137 err = usbd_set_config_no(dev, UDSBR_CONFIG_NO, 1); 138 if (err) { 139 printf("%s: setting config no failed\n", 140 USBDEVNAME(sc->sc_dev)); 141 USB_ATTACH_ERROR_RETURN; 142 } 143 144 sc->sc_udev = dev; 145 146 DPRINTFN(10, ("udsbr_attach: %p\n", sc->sc_udev)); 147 148 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, 149 USBDEV(sc->sc_dev)); 150 151 sc->sc_child = radio_attach_mi(&udsbr_hw_if, sc, USBDEV(sc->sc_dev)); 152 153 USB_ATTACH_SUCCESS_RETURN; 154} 155 156USB_DETACH(udsbr) 157{ 158 USB_DETACH_START(udsbr, sc); 159 int rv = 0; 160 161 if (sc->sc_child != NULL) 162 rv = config_detach(sc->sc_child, flags); 163 164 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, 165 USBDEV(sc->sc_dev)); 166 167 return (rv); 168} 169 170int 171udsbr_activate(device_ptr_t self, enum devact act) 172{ 173 struct udsbr_softc *sc = (struct udsbr_softc *)self; 174 int rv = 0; 175 176 switch (act) { 177 case DVACT_ACTIVATE: 178 return (EOPNOTSUPP); 179 break; 180 181 case DVACT_DEACTIVATE: 182 sc->sc_dying = 1; 183 if (sc->sc_child != NULL) 184 rv = config_deactivate(sc->sc_child); 185 break; 186 } 187 return (rv); 188} 189 190int 191udsbr_req(struct udsbr_softc *sc, int ureq, int value, int index) 192{ 193 usb_device_request_t req; 194 usbd_status err; 195 u_char data; 196 197 req.bmRequestType = UT_READ_VENDOR_DEVICE; 198 req.bRequest = ureq; 199 USETW(req.wValue, value); 200 USETW(req.wIndex, index); 201 USETW(req.wLength, 1); 202 err = usbd_do_request(sc->sc_udev, &req, &data); 203 if (err) { 204 printf("%s: request failed err=%d\n", USBDEVNAME(sc->sc_dev), 205 err); 206 } 207 return !(data & 1); 208} 209 210void 211udsbr_start(struct udsbr_softc *sc) 212{ 213 (void)udsbr_req(sc, 0x00, 0x0000, 0x00c7); 214 (void)udsbr_req(sc, 0x02, 0x0001, 0x0000); 215} 216 217void 218udsbr_stop(struct udsbr_softc *sc) 219{ 220 (void)udsbr_req(sc, 0x00, 0x0016, 0x001c); 221 (void)udsbr_req(sc, 0x02, 0x0000, 0x0000); 222} 223 224void 225udsbr_setfreq(struct udsbr_softc *sc, int freq) 226{ 227 DPRINTF(("udsbr_setfreq: setfreq=%d\n", freq)); 228 /* 229 * Freq now is in Hz. We need to convert it to the frequency 230 * that the radio wants. This frequency is 10.7MHz above 231 * the actual frequency. We then need to convert to 232 * units of 12.5kHz. We add one to the IFM to make rounding 233 * easier. 234 */ 235 freq = (freq + 10700001) / 12500; 236 (void)udsbr_req(sc, 0x01, (freq >> 8) & 0xff, freq & 0xff); 237 (void)udsbr_req(sc, 0x00, 0x0096, 0x00b7); 238} 239 240int 241udsbr_status(struct udsbr_softc *sc) 242{ 243 usbd_delay_ms(sc->sc_udev, 240); /* XXX wait for signal to settle */ 244 return (udsbr_req(sc, 0x00, 0x0000, 0x0024)); 245} 246 247 248int 249udsbr_get_info(void *v, struct radio_info *ri) 250{ 251 struct udsbr_softc *sc = v; 252 253 ri->mute = sc->sc_mute; 254 ri->volume = sc->sc_vol ? 255 : 0; 255 ri->caps = RADIO_CAPS_DETECT_STEREO; 256 ri->rfreq = 0; 257 ri->lock = 0; 258 ri->freq = sc->sc_freq; 259 ri->info = udsbr_status(sc) ? RADIO_INFO_STEREO : 0; 260 261 return (0); 262} 263 264int 265udsbr_set_info(void *v, struct radio_info *ri) 266{ 267 struct udsbr_softc *sc = v; 268 269 sc->sc_mute = ri->mute != 0; 270 sc->sc_vol = ri->volume != 0; 271 sc->sc_freq = ri->freq; 272 udsbr_setfreq(sc, sc->sc_freq); 273 274 if (sc->sc_mute || sc->sc_vol == 0) 275 udsbr_stop(sc); 276 else 277 udsbr_start(sc); 278 279 return (0); 280} 281