11.32Sriastrad/* $NetBSD: udsbr.c,v 1.32 2023/05/10 00:12:36 riastradh Exp $ */ 21.1Saugustss 31.1Saugustss/* 41.1Saugustss * Copyright (c) 2002 The NetBSD Foundation, Inc. 51.1Saugustss * All rights reserved. 61.1Saugustss * 71.1Saugustss * This code is derived from software contributed to The NetBSD Foundation 81.3Saugustss * by Lennart Augustsson (lennart@augustsson.net). 91.1Saugustss * 101.1Saugustss * Redistribution and use in source and binary forms, with or without 111.1Saugustss * modification, are permitted provided that the following conditions 121.1Saugustss * are met: 131.1Saugustss * 1. Redistributions of source code must retain the above copyright 141.1Saugustss * notice, this list of conditions and the following disclaimer. 151.1Saugustss * 2. Redistributions in binary form must reproduce the above copyright 161.1Saugustss * notice, this list of conditions and the following disclaimer in the 171.1Saugustss * documentation and/or other materials provided with the distribution. 181.1Saugustss * 191.1Saugustss * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 201.1Saugustss * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 211.1Saugustss * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 221.1Saugustss * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 231.1Saugustss * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 241.1Saugustss * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 251.1Saugustss * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 261.1Saugustss * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 271.1Saugustss * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 281.1Saugustss * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 291.1Saugustss * POSSIBILITY OF SUCH DAMAGE. 301.1Saugustss */ 311.1Saugustss 321.2Saugustss/* 331.2Saugustss * Driver for the D-Link DSB-R100 FM radio. 341.3Saugustss * I apologize for the magic hex constants, but this is what happens 351.2Saugustss * when you have to reverse engineer the driver. 361.2Saugustss * Parts of the code borrowed from Linux and parts from Warner Losh's 371.2Saugustss * FreeBSD driver. 381.2Saugustss */ 391.2Saugustss 401.1Saugustss#include <sys/cdefs.h> 411.32Sriastrad__KERNEL_RCSID(0, "$NetBSD: udsbr.c,v 1.32 2023/05/10 00:12:36 riastradh Exp $"); 421.25Sskrll 431.25Sskrll#ifdef _KERNEL_OPT 441.25Sskrll#include "opt_usb.h" 451.25Sskrll#endif 461.1Saugustss 471.1Saugustss#include <sys/param.h> 481.1Saugustss#include <sys/systm.h> 491.1Saugustss#include <sys/kernel.h> 501.1Saugustss#include <sys/device.h> 511.1Saugustss 521.1Saugustss#include <sys/radioio.h> 531.1Saugustss#include <dev/radio_if.h> 541.1Saugustss 551.1Saugustss#include <dev/usb/usb.h> 561.1Saugustss#include <dev/usb/usbdi.h> 571.1Saugustss#include <dev/usb/usbdi_util.h> 581.21Smrg#include <dev/usb/usbdivar.h> 591.1Saugustss 601.1Saugustss#include <dev/usb/usbdevs.h> 611.1Saugustss 621.1Saugustss#ifdef UDSBR_DEBUG 631.18Sdyoung#define DPRINTF(x) if (udsbrdebug) printf x 641.18Sdyoung#define DPRINTFN(n,x) if (udsbrdebug>(n)) printf x 651.1Saugustssint udsbrdebug = 0; 661.1Saugustss#else 671.1Saugustss#define DPRINTF(x) 681.1Saugustss#define DPRINTFN(n,x) 691.1Saugustss#endif 701.1Saugustss 711.1Saugustss#define UDSBR_CONFIG_NO 1 721.1Saugustss 731.5SaugustssStatic int udsbr_get_info(void *, struct radio_info *); 741.5SaugustssStatic int udsbr_set_info(void *, struct radio_info *); 751.1Saugustss 761.29Smaxvstatic const struct radio_hw_if udsbr_hw_if = { 771.1Saugustss NULL, /* open */ 781.1Saugustss NULL, /* close */ 791.1Saugustss udsbr_get_info, 801.1Saugustss udsbr_set_info, 811.1Saugustss NULL 821.1Saugustss}; 831.1Saugustss 841.1Saugustssstruct udsbr_softc { 851.26Sskrll device_t sc_dev; 861.23Sskrll struct usbd_device * sc_udev; 871.1Saugustss 881.1Saugustss char sc_mute; 891.1Saugustss char sc_vol; 901.23Sskrll uint32_t sc_freq; 911.1Saugustss 921.15Scube device_t sc_child; 931.1Saugustss 941.1Saugustss char sc_dying; 951.1Saugustss}; 961.1Saugustss 971.23SskrllStatic int udsbr_req(struct udsbr_softc *, int, int, 981.23Sskrll int); 991.23SskrllStatic void udsbr_start(struct udsbr_softc *); 1001.23SskrllStatic void udsbr_stop(struct udsbr_softc *); 1011.23SskrllStatic void udsbr_setfreq(struct udsbr_softc *, int); 1021.23SskrllStatic int udsbr_status(struct udsbr_softc *); 1031.1Saugustss 1041.29Smaxvstatic int udsbr_match(device_t, cfdata_t, void *); 1051.29Smaxvstatic void udsbr_attach(device_t, device_t, void *); 1061.29Smaxvstatic void udsbr_childdet(device_t, device_t); 1071.29Smaxvstatic int udsbr_detach(device_t, int); 1081.29Smaxvstatic int udsbr_activate(device_t, enum devact); 1091.27Smrg 1101.15ScubeCFATTACH_DECL2_NEW(udsbr, sizeof(struct udsbr_softc), udsbr_match, 1111.13Sdyoung udsbr_attach, udsbr_detach, udsbr_activate, NULL, udsbr_childdet); 1121.1Saugustss 1131.29Smaxvstatic int 1141.18Sdyoungudsbr_match(device_t parent, cfdata_t match, void *aux) 1151.1Saugustss{ 1161.18Sdyoung struct usb_attach_arg *uaa = aux; 1171.1Saugustss 1181.1Saugustss DPRINTFN(50,("udsbr_match\n")); 1191.1Saugustss 1201.23Sskrll if (uaa->uaa_vendor != USB_VENDOR_CYPRESS || 1211.23Sskrll uaa->uaa_product != USB_PRODUCT_CYPRESS_FMRADIO) 1221.23Sskrll return UMATCH_NONE; 1231.23Sskrll return UMATCH_VENDOR_PRODUCT; 1241.1Saugustss} 1251.1Saugustss 1261.29Smaxvstatic void 1271.18Sdyoungudsbr_attach(device_t parent, device_t self, void *aux) 1281.1Saugustss{ 1291.18Sdyoung struct udsbr_softc *sc = device_private(self); 1301.18Sdyoung struct usb_attach_arg *uaa = aux; 1311.23Sskrll struct usbd_device * dev = uaa->uaa_device; 1321.10Saugustss char *devinfop; 1331.1Saugustss usbd_status err; 1341.1Saugustss 1351.1Saugustss DPRINTFN(10,("udsbr_attach: sc=%p\n", sc)); 1361.1Saugustss 1371.15Scube sc->sc_dev = self; 1381.15Scube 1391.16Splunky aprint_naive("\n"); 1401.16Splunky aprint_normal("\n"); 1411.16Splunky 1421.10Saugustss devinfop = usbd_devinfo_alloc(dev, 0); 1431.15Scube aprint_normal_dev(self, "%s\n", devinfop); 1441.10Saugustss usbd_devinfo_free(devinfop); 1451.1Saugustss 1461.1Saugustss err = usbd_set_config_no(dev, UDSBR_CONFIG_NO, 1); 1471.1Saugustss if (err) { 1481.22Sskrll aprint_error_dev(self, "failed to set configuration" 1491.22Sskrll ", err=%s\n", usbd_errstr(err)); 1501.18Sdyoung return; 1511.1Saugustss } 1521.1Saugustss 1531.1Saugustss sc->sc_udev = dev; 1541.1Saugustss 1551.1Saugustss DPRINTFN(10, ("udsbr_attach: %p\n", sc->sc_udev)); 1561.1Saugustss 1571.24Smsaitoh usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev); 1581.1Saugustss 1591.18Sdyoung sc->sc_child = radio_attach_mi(&udsbr_hw_if, sc, sc->sc_dev); 1601.1Saugustss 1611.18Sdyoung return; 1621.1Saugustss} 1631.1Saugustss 1641.29Smaxvstatic void 1651.13Sdyoungudsbr_childdet(device_t self, device_t child) 1661.13Sdyoung{ 1671.13Sdyoung} 1681.13Sdyoung 1691.29Smaxvstatic int 1701.18Sdyoungudsbr_detach(device_t self, int flags) 1711.1Saugustss{ 1721.18Sdyoung struct udsbr_softc *sc = device_private(self); 1731.32Sriastrad int error; 1741.32Sriastrad 1751.32Sriastrad error = config_detach_children(self, flags); 1761.32Sriastrad if (error) 1771.32Sriastrad return error; 1781.1Saugustss 1791.28Smaxv if (sc->sc_udev != NULL) 1801.28Smaxv usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, 1811.28Smaxv sc->sc_dev); 1821.1Saugustss 1831.32Sriastrad return 0; 1841.1Saugustss} 1851.1Saugustss 1861.29Smaxvstatic int 1871.18Sdyoungudsbr_activate(device_t self, enum devact act) 1881.1Saugustss{ 1891.15Scube struct udsbr_softc *sc = device_private(self); 1901.1Saugustss 1911.1Saugustss switch (act) { 1921.1Saugustss case DVACT_DEACTIVATE: 1931.1Saugustss sc->sc_dying = 1; 1941.17Sdyoung return 0; 1951.17Sdyoung default: 1961.17Sdyoung return EOPNOTSUPP; 1971.1Saugustss } 1981.1Saugustss} 1991.1Saugustss 2001.1Saugustssint 2011.1Saugustssudsbr_req(struct udsbr_softc *sc, int ureq, int value, int index) 2021.1Saugustss{ 2031.1Saugustss usb_device_request_t req; 2041.1Saugustss usbd_status err; 2051.1Saugustss u_char data; 2061.1Saugustss 2071.31Schristos DPRINTFN(1,("udsbr_req: ureq=0x%02x value=0x%04x index=0x%04x\n", 2081.4Saugustss ureq, value, index)); 2091.1Saugustss req.bmRequestType = UT_READ_VENDOR_DEVICE; 2101.1Saugustss req.bRequest = ureq; 2111.1Saugustss USETW(req.wValue, value); 2121.1Saugustss USETW(req.wIndex, index); 2131.1Saugustss USETW(req.wLength, 1); 2141.1Saugustss err = usbd_do_request(sc->sc_udev, &req, &data); 2151.1Saugustss if (err) { 2161.15Scube aprint_error_dev(sc->sc_dev, "request failed err=%d\n", err); 2171.1Saugustss } 2181.1Saugustss return !(data & 1); 2191.1Saugustss} 2201.1Saugustss 2211.1Saugustssvoid 2221.1Saugustssudsbr_start(struct udsbr_softc *sc) 2231.1Saugustss{ 2241.1Saugustss (void)udsbr_req(sc, 0x00, 0x0000, 0x00c7); 2251.1Saugustss (void)udsbr_req(sc, 0x02, 0x0001, 0x0000); 2261.1Saugustss} 2271.1Saugustss 2281.1Saugustssvoid 2291.1Saugustssudsbr_stop(struct udsbr_softc *sc) 2301.1Saugustss{ 2311.1Saugustss (void)udsbr_req(sc, 0x00, 0x0016, 0x001c); 2321.1Saugustss (void)udsbr_req(sc, 0x02, 0x0000, 0x0000); 2331.1Saugustss} 2341.1Saugustss 2351.1Saugustssvoid 2361.1Saugustssudsbr_setfreq(struct udsbr_softc *sc, int freq) 2371.1Saugustss{ 2381.1Saugustss DPRINTF(("udsbr_setfreq: setfreq=%d\n", freq)); 2391.23Sskrll /* 2401.23Sskrll * Freq now is in Hz. We need to convert it to the frequency 2411.23Sskrll * that the radio wants. This frequency is 10.7MHz above 2421.23Sskrll * the actual frequency. We then need to convert to 2431.23Sskrll * units of 12.5kHz. We add one to the IFM to make rounding 2441.23Sskrll * easier. 2451.23Sskrll */ 2461.23Sskrll freq = (freq * 1000 + 10700001) / 12500; 2471.1Saugustss (void)udsbr_req(sc, 0x01, (freq >> 8) & 0xff, freq & 0xff); 2481.1Saugustss (void)udsbr_req(sc, 0x00, 0x0096, 0x00b7); 2491.4Saugustss usbd_delay_ms(sc->sc_udev, 240); /* wait for signal to settle */ 2501.1Saugustss} 2511.1Saugustss 2521.1Saugustssint 2531.1Saugustssudsbr_status(struct udsbr_softc *sc) 2541.1Saugustss{ 2551.23Sskrll return udsbr_req(sc, 0x00, 0x0000, 0x0024); 2561.1Saugustss} 2571.1Saugustss 2581.1Saugustss 2591.1Saugustssint 2601.1Saugustssudsbr_get_info(void *v, struct radio_info *ri) 2611.1Saugustss{ 2621.1Saugustss struct udsbr_softc *sc = v; 2631.1Saugustss 2641.1Saugustss ri->mute = sc->sc_mute; 2651.1Saugustss ri->volume = sc->sc_vol ? 255 : 0; 2661.1Saugustss ri->caps = RADIO_CAPS_DETECT_STEREO; 2671.1Saugustss ri->rfreq = 0; 2681.1Saugustss ri->lock = 0; 2691.1Saugustss ri->freq = sc->sc_freq; 2701.1Saugustss ri->info = udsbr_status(sc) ? RADIO_INFO_STEREO : 0; 2711.1Saugustss 2721.23Sskrll return 0; 2731.1Saugustss} 2741.1Saugustss 2751.1Saugustssint 2761.1Saugustssudsbr_set_info(void *v, struct radio_info *ri) 2771.1Saugustss{ 2781.1Saugustss struct udsbr_softc *sc = v; 2791.1Saugustss 2801.1Saugustss sc->sc_mute = ri->mute != 0; 2811.1Saugustss sc->sc_vol = ri->volume != 0; 2821.1Saugustss sc->sc_freq = ri->freq; 2831.1Saugustss udsbr_setfreq(sc, sc->sc_freq); 2841.1Saugustss 2851.1Saugustss if (sc->sc_mute || sc->sc_vol == 0) 2861.1Saugustss udsbr_stop(sc); 2871.1Saugustss else 2881.1Saugustss udsbr_start(sc); 2891.1Saugustss 2901.23Sskrll return 0; 2911.1Saugustss} 292