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