1 /* $NetBSD: udsbr.c,v 1.16 2009/09/23 19:07:19 plunky Exp $ */
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Lennart Augustsson (lennart@augustsson.net).
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * Driver for the D-Link DSB-R100 FM radio.
34 * I apologize for the magic hex constants, but this is what happens
35 * when you have to reverse engineer the driver.
36 * Parts of the code borrowed from Linux and parts from Warner Losh's
40 #include <sys/cdefs.h>
41 __KERNEL_RCSID(0, "$NetBSD: udsbr.c,v 1.16 2009/09/23 19:07:19 plunky Exp $");
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/kernel.h>
46 #include <sys/device.h>
48 #include <sys/radioio.h>
49 #include <dev/radio_if.h>
51 #include <dev/usb/usb.h>
52 #include <dev/usb/usbdi.h>
53 #include <dev/usb/usbdi_util.h>
55 #include <dev/usb/usbdevs.h>
58 #define DPRINTF(x) if (udsbrdebug) logprintf x
59 #define DPRINTFN(n,x) if (udsbrdebug>(n)) logprintf x
66 #define UDSBR_CONFIG_NO 1
68 Static
int udsbr_get_info(void *, struct radio_info
*);
69 Static
int udsbr_set_info(void *, struct radio_info
*);
71 const struct radio_hw_if udsbr_hw_if
= {
81 usbd_device_handle sc_udev
;
92 Static
int udsbr_req(struct udsbr_softc
*sc
, int ureq
, int value
,
94 Static
void udsbr_start(struct udsbr_softc
*sc
);
95 Static
void udsbr_stop(struct udsbr_softc
*sc
);
96 Static
void udsbr_setfreq(struct udsbr_softc
*sc
, int freq
);
97 Static
int udsbr_status(struct udsbr_softc
*sc
);
99 int udsbr_match(device_t
, cfdata_t
, void *);
100 void udsbr_attach(device_t
, device_t
, void *);
101 void udsbr_childdet(device_t
, device_t
);
102 int udsbr_detach(device_t
, int);
103 int udsbr_activate(device_t
, enum devact
);
104 extern struct cfdriver udsbr_cd
;
105 CFATTACH_DECL2_NEW(udsbr
, sizeof(struct udsbr_softc
), udsbr_match
,
106 udsbr_attach
, udsbr_detach
, udsbr_activate
, NULL
, udsbr_childdet
);
110 USB_MATCH_START(udsbr
, uaa
);
112 DPRINTFN(50,("udsbr_match\n"));
114 if (uaa
->vendor
!= USB_VENDOR_CYPRESS
||
115 uaa
->product
!= USB_PRODUCT_CYPRESS_FMRADIO
)
116 return (UMATCH_NONE
);
117 return (UMATCH_VENDOR_PRODUCT
);
122 USB_ATTACH_START(udsbr
, sc
, uaa
);
123 usbd_device_handle dev
= uaa
->device
;
127 DPRINTFN(10,("udsbr_attach: sc=%p\n", sc
));
134 devinfop
= usbd_devinfo_alloc(dev
, 0);
135 aprint_normal_dev(self
, "%s\n", devinfop
);
136 usbd_devinfo_free(devinfop
);
138 err
= usbd_set_config_no(dev
, UDSBR_CONFIG_NO
, 1);
140 aprint_error_dev(self
, "setting config no failed\n");
141 USB_ATTACH_ERROR_RETURN
;
146 DPRINTFN(10, ("udsbr_attach: %p\n", sc
->sc_udev
));
148 usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH
, sc
->sc_udev
,
151 sc
->sc_child
= radio_attach_mi(&udsbr_hw_if
, sc
, USBDEV(sc
->sc_dev
));
153 USB_ATTACH_SUCCESS_RETURN
;
157 udsbr_childdet(device_t self
, device_t child
)
163 USB_DETACH_START(udsbr
, sc
);
166 if (sc
->sc_child
!= NULL
)
167 rv
= config_detach(sc
->sc_child
, flags
);
169 usbd_add_drv_event(USB_EVENT_DRIVER_DETACH
, sc
->sc_udev
,
176 udsbr_activate(device_ptr_t self
, enum devact act
)
178 struct udsbr_softc
*sc
= device_private(self
);
181 case DVACT_DEACTIVATE
:
190 udsbr_req(struct udsbr_softc
*sc
, int ureq
, int value
, int index
)
192 usb_device_request_t req
;
196 DPRINTFN(1,("udsbr_req: ureq=0x%02x value=0x%04x index=0x%04x\n",
197 ureq
, value
, index
));
198 req
.bmRequestType
= UT_READ_VENDOR_DEVICE
;
200 USETW(req
.wValue
, value
);
201 USETW(req
.wIndex
, index
);
202 USETW(req
.wLength
, 1);
203 err
= usbd_do_request(sc
->sc_udev
, &req
, &data
);
205 aprint_error_dev(sc
->sc_dev
, "request failed err=%d\n", err
);
211 udsbr_start(struct udsbr_softc
*sc
)
213 (void)udsbr_req(sc
, 0x00, 0x0000, 0x00c7);
214 (void)udsbr_req(sc
, 0x02, 0x0001, 0x0000);
218 udsbr_stop(struct udsbr_softc
*sc
)
220 (void)udsbr_req(sc
, 0x00, 0x0016, 0x001c);
221 (void)udsbr_req(sc
, 0x02, 0x0000, 0x0000);
225 udsbr_setfreq(struct udsbr_softc
*sc
, int freq
)
227 DPRINTF(("udsbr_setfreq: setfreq=%d\n", freq
));
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
235 freq
= (freq
* 1000 + 10700001) / 12500;
236 (void)udsbr_req(sc
, 0x01, (freq
>> 8) & 0xff, freq
& 0xff);
237 (void)udsbr_req(sc
, 0x00, 0x0096, 0x00b7);
238 usbd_delay_ms(sc
->sc_udev
, 240); /* wait for signal to settle */
242 udsbr_status(struct udsbr_softc
*sc
)
244 return (udsbr_req(sc
, 0x00, 0x0000, 0x0024));
249 udsbr_get_info(void *v
, struct radio_info
*ri
)
251 struct udsbr_softc
*sc
= v
;
253 ri
->mute
= sc
->sc_mute
;
254 ri
->volume
= sc
->sc_vol
? 255 : 0;
255 ri
->caps
= RADIO_CAPS_DETECT_STEREO
;
258 ri
->freq
= sc
->sc_freq
;
259 ri
->info
= udsbr_status(sc
) ? RADIO_INFO_STEREO
: 0;
265 udsbr_set_info(void *v
, struct radio_info
*ri
)
267 struct udsbr_softc
*sc
= v
;
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
);
274 if (sc
->sc_mute
|| sc
->sc_vol
== 0)