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