Line data Source code
1 : /* $OpenBSD: udsbr.c,v 1.27 2016/11/06 12:58:01 mpi Exp $ */
2 : /* $NetBSD: udsbr.c,v 1.7 2002/07/11 21:14:27 augustss Exp $ */
3 :
4 : /*
5 : * Copyright (c) 2002 The NetBSD Foundation, Inc.
6 : * All rights reserved.
7 : *
8 : * This code is derived from software contributed to The NetBSD Foundation
9 : * by Lennart Augustsson (lennart@augustsson.net).
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 : *
20 : * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 : * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 : * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 : * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 : * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 : * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 : * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 : * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 : * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 : * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 : * POSSIBILITY OF SUCH DAMAGE.
31 : */
32 :
33 : /*
34 : * Driver for the D-Link DSB-R100 FM radio.
35 : * I apologize for the magic hex constants, but this is what happens
36 : * when you have to reverse engineer the driver.
37 : * Parts of the code borrowed from Linux and parts from Warner Losh's
38 : * FreeBSD driver.
39 : */
40 :
41 : #include <sys/param.h>
42 : #include <sys/systm.h>
43 : #include <sys/device.h>
44 :
45 : #include <sys/radioio.h>
46 : #include <dev/radio_if.h>
47 :
48 : #include <dev/usb/usb.h>
49 : #include <dev/usb/usbdi.h>
50 : #include <dev/usb/usbdi_util.h>
51 :
52 : #include <dev/usb/usbdevs.h>
53 :
54 : #ifdef UDSBR_DEBUG
55 : #define DPRINTF(x) do { if (udsbrdebug) printf x; } while (0)
56 : #define DPRINTFN(n,x) do { if (udsbrdebug>(n)) printf x; } while (0)
57 : int udsbrdebug = 0;
58 : #else
59 : #define DPRINTF(x)
60 : #define DPRINTFN(n,x)
61 : #endif
62 :
63 : #define UDSBR_CONFIG_NO 1
64 :
65 : int udsbr_get_info(void *, struct radio_info *);
66 : int udsbr_set_info(void *, struct radio_info *);
67 :
68 : struct radio_hw_if udsbr_hw_if = {
69 : NULL, /* open */
70 : NULL, /* close */
71 : udsbr_get_info,
72 : udsbr_set_info,
73 : NULL
74 : };
75 :
76 : struct udsbr_softc {
77 : struct device sc_dev;
78 : struct usbd_device *sc_udev;
79 :
80 : char sc_mute;
81 : char sc_vol;
82 : u_int32_t sc_freq;
83 :
84 : struct device *sc_child;
85 : };
86 :
87 : int udsbr_req(struct udsbr_softc *sc, int ureq, int value, int index);
88 : void udsbr_start(struct udsbr_softc *sc);
89 : void udsbr_stop(struct udsbr_softc *sc);
90 : void udsbr_setfreq(struct udsbr_softc *sc, int freq);
91 : int udsbr_status(struct udsbr_softc *sc);
92 :
93 : int udsbr_match(struct device *, void *, void *);
94 : void udsbr_attach(struct device *, struct device *, void *);
95 : int udsbr_detach(struct device *, int);
96 : int udsbr_activate(struct device *, int);
97 :
98 : struct cfdriver udsbr_cd = {
99 : NULL, "udsbr", DV_DULL
100 : };
101 :
102 : const struct cfattach udsbr_ca = {
103 : sizeof(struct udsbr_softc),
104 : udsbr_match,
105 : udsbr_attach,
106 : udsbr_detach,
107 : udsbr_activate,
108 : };
109 :
110 : int
111 0 : udsbr_match(struct device *parent, void *match, void *aux)
112 : {
113 0 : struct usb_attach_arg *uaa = aux;
114 :
115 : DPRINTFN(50,("udsbr_match\n"));
116 :
117 0 : if (uaa->iface == NULL || uaa->configno != UDSBR_CONFIG_NO)
118 0 : return (UMATCH_NONE);
119 :
120 0 : if (uaa->vendor != USB_VENDOR_CYPRESS ||
121 0 : uaa->product != USB_PRODUCT_CYPRESS_FMRADIO)
122 0 : return (UMATCH_NONE);
123 0 : return (UMATCH_VENDOR_PRODUCT);
124 0 : }
125 :
126 : void
127 0 : udsbr_attach(struct device *parent, struct device *self, void *aux)
128 : {
129 0 : struct udsbr_softc *sc = (struct udsbr_softc *)self;
130 0 : struct usb_attach_arg *uaa = aux;
131 0 : struct usbd_device *dev = uaa->device;
132 :
133 0 : sc->sc_udev = dev;
134 0 : sc->sc_child = radio_attach_mi(&udsbr_hw_if, sc, &sc->sc_dev);
135 0 : }
136 :
137 : int
138 0 : udsbr_detach(struct device *self, int flags)
139 : {
140 0 : struct udsbr_softc *sc = (struct udsbr_softc *)self;
141 : int rv = 0;
142 :
143 0 : if (sc->sc_child != NULL)
144 0 : rv = config_detach(sc->sc_child, flags);
145 :
146 0 : return (rv);
147 : }
148 :
149 : int
150 0 : udsbr_activate(struct device *self, int act)
151 : {
152 0 : struct udsbr_softc *sc = (struct udsbr_softc *)self;
153 : int rv = 0;
154 :
155 0 : switch (act) {
156 : case DVACT_DEACTIVATE:
157 0 : if (sc->sc_child != NULL)
158 0 : rv = config_deactivate(sc->sc_child);
159 : break;
160 : }
161 0 : return (rv);
162 : }
163 :
164 : int
165 0 : udsbr_req(struct udsbr_softc *sc, int ureq, int value, int index)
166 : {
167 0 : usb_device_request_t req;
168 : usbd_status err;
169 0 : u_char data;
170 :
171 : DPRINTFN(1,("udsbr_req: ureq=0x%02x value=0x%04x index=0x%04x\n",
172 : ureq, value, index));
173 0 : req.bmRequestType = UT_READ_VENDOR_DEVICE;
174 0 : req.bRequest = ureq;
175 0 : USETW(req.wValue, value);
176 0 : USETW(req.wIndex, index);
177 0 : USETW(req.wLength, 1);
178 0 : err = usbd_do_request(sc->sc_udev, &req, &data);
179 0 : if (err) {
180 0 : printf("%s: request failed err=%d\n", sc->sc_dev.dv_xname,
181 : err);
182 0 : }
183 0 : return !(data & 1);
184 0 : }
185 :
186 : void
187 0 : udsbr_start(struct udsbr_softc *sc)
188 : {
189 0 : (void)udsbr_req(sc, 0x00, 0x0000, 0x00c7);
190 0 : (void)udsbr_req(sc, 0x02, 0x0001, 0x0000);
191 0 : }
192 :
193 : void
194 0 : udsbr_stop(struct udsbr_softc *sc)
195 : {
196 0 : (void)udsbr_req(sc, 0x00, 0x0016, 0x001c);
197 0 : (void)udsbr_req(sc, 0x02, 0x0000, 0x0000);
198 0 : }
199 :
200 : void
201 0 : udsbr_setfreq(struct udsbr_softc *sc, int freq)
202 : {
203 : DPRINTF(("udsbr_setfreq: setfreq=%d\n", freq));
204 : /*
205 : * Freq now is in Hz. We need to convert it to the frequency
206 : * that the radio wants. This frequency is 10.7MHz above
207 : * the actual frequency. We then need to convert to
208 : * units of 12.5kHz. We add one to the IFM to make rounding
209 : * easier.
210 : */
211 0 : freq = (freq * 1000 + 10700001) / 12500;
212 0 : (void)udsbr_req(sc, 0x01, (freq >> 8) & 0xff, freq & 0xff);
213 0 : (void)udsbr_req(sc, 0x00, 0x0096, 0x00b7);
214 0 : usbd_delay_ms(sc->sc_udev, 240); /* wait for signal to settle */
215 0 : }
216 :
217 : int
218 0 : udsbr_status(struct udsbr_softc *sc)
219 : {
220 0 : return (udsbr_req(sc, 0x00, 0x0000, 0x0024));
221 : }
222 :
223 :
224 : int
225 0 : udsbr_get_info(void *v, struct radio_info *ri)
226 : {
227 0 : struct udsbr_softc *sc = v;
228 :
229 0 : ri->mute = sc->sc_mute;
230 0 : ri->volume = sc->sc_vol ? 255 : 0;
231 0 : ri->caps = RADIO_CAPS_DETECT_STEREO;
232 0 : ri->rfreq = 0;
233 0 : ri->lock = 0;
234 0 : ri->freq = sc->sc_freq;
235 0 : ri->info = udsbr_status(sc) ? RADIO_INFO_STEREO : 0;
236 :
237 0 : return (0);
238 : }
239 :
240 : int
241 0 : udsbr_set_info(void *v, struct radio_info *ri)
242 : {
243 0 : struct udsbr_softc *sc = v;
244 :
245 0 : sc->sc_mute = ri->mute != 0;
246 0 : sc->sc_vol = ri->volume != 0;
247 0 : sc->sc_freq = ri->freq;
248 0 : udsbr_setfreq(sc, sc->sc_freq);
249 :
250 0 : if (sc->sc_mute || sc->sc_vol == 0)
251 0 : udsbr_stop(sc);
252 : else
253 0 : udsbr_start(sc);
254 :
255 0 : return (0);
256 : }
|