1 1.4 skrll /* $NetBSD: slurm.c,v 1.4 2019/01/22 06:47:20 skrll Exp $ */ 2 1.1 jakllsch 3 1.1 jakllsch /* 4 1.1 jakllsch * Copyright (c) 2012 Jonathan A. Kollasch 5 1.1 jakllsch * All rights reserved. 6 1.1 jakllsch * 7 1.1 jakllsch * Redistribution and use in source and binary forms, with or without 8 1.1 jakllsch * modification, are permitted provided that the following conditions 9 1.1 jakllsch * are met: 10 1.1 jakllsch * 1. Redistributions of source code must retain the above copyright 11 1.1 jakllsch * notice, this list of conditions and the following disclaimer. 12 1.1 jakllsch * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 jakllsch * notice, this list of conditions and the following disclaimer in the 14 1.1 jakllsch * documentation and/or other materials provided with the distribution. 15 1.1 jakllsch * 16 1.1 jakllsch * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 1.1 jakllsch * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 1.1 jakllsch * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 1.1 jakllsch * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 20 1.1 jakllsch * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 1.1 jakllsch * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 1.1 jakllsch * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 1.1 jakllsch * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 1.1 jakllsch * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 1.1 jakllsch * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26 1.1 jakllsch * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 1.1 jakllsch */ 28 1.1 jakllsch 29 1.1 jakllsch #include <sys/cdefs.h> 30 1.4 skrll __KERNEL_RCSID(0, "$NetBSD: slurm.c,v 1.4 2019/01/22 06:47:20 skrll Exp $"); 31 1.1 jakllsch 32 1.1 jakllsch #include <sys/param.h> 33 1.1 jakllsch #include <sys/proc.h> 34 1.1 jakllsch #include <sys/systm.h> 35 1.1 jakllsch #include <sys/kernel.h> 36 1.1 jakllsch #include <sys/device.h> 37 1.1 jakllsch #include <sys/conf.h> 38 1.1 jakllsch 39 1.1 jakllsch #include <dev/usb/usb.h> 40 1.1 jakllsch #include <dev/usb/usbdi.h> 41 1.1 jakllsch #include <dev/usb/usbdivar.h> 42 1.1 jakllsch #include <dev/usb/usbdi_util.h> 43 1.1 jakllsch #include <dev/usb/usbdevs.h> 44 1.1 jakllsch #include <dev/ic/si470x_reg.h> 45 1.1 jakllsch 46 1.1 jakllsch #include <sys/radioio.h> 47 1.1 jakllsch #include <dev/radio_if.h> 48 1.1 jakllsch 49 1.1 jakllsch #ifdef SLURM_DEBUG 50 1.1 jakllsch int slurmdebug = 0; 51 1.1 jakllsch #define DPRINTFN(n, x) do { if (slurmdebug > (n)) printf x; } while (0) 52 1.1 jakllsch #else 53 1.1 jakllsch #define DPRINTFN(n, x) 54 1.1 jakllsch #endif 55 1.1 jakllsch 56 1.1 jakllsch #define DPRINTF(x) DPRINTFN(0, x) 57 1.1 jakllsch 58 1.1 jakllsch #define SI470X_VOLFACT (255 / __SHIFTOUT_MASK(SI470X_VOLUME)) 59 1.1 jakllsch 60 1.1 jakllsch struct slurm_softc { 61 1.1 jakllsch device_t sc_dev; 62 1.2 skrll struct usbd_device * sc_udev; 63 1.2 skrll struct usbd_interface * sc_uif; 64 1.1 jakllsch uint32_t sc_band; 65 1.1 jakllsch uint32_t sc_space; 66 1.1 jakllsch }; 67 1.1 jakllsch 68 1.1 jakllsch static const struct usb_devno slurm_devs[] = { 69 1.1 jakllsch { USB_VENDOR_ADS, USB_PRODUCT_ADS_RDX155 }, 70 1.1 jakllsch }; 71 1.1 jakllsch 72 1.1 jakllsch static int slurm_match(device_t, cfdata_t, void *); 73 1.1 jakllsch static void slurm_attach(device_t, device_t, void *); 74 1.1 jakllsch static int slurm_detach(device_t, int); 75 1.1 jakllsch 76 1.1 jakllsch static int slurm_get_info(void *, struct radio_info *); 77 1.1 jakllsch static int slurm_set_info(void *, struct radio_info *); 78 1.1 jakllsch static int slurm_search(void *, int); 79 1.1 jakllsch 80 1.1 jakllsch static usbd_status slurm_setreg(struct slurm_softc *, int, uint16_t); 81 1.1 jakllsch static usbd_status slurm_getreg(struct slurm_softc *, int, uint16_t *); 82 1.1 jakllsch 83 1.1 jakllsch static uint32_t slurm_si470x_get_freq(struct slurm_softc *, uint16_t); 84 1.1 jakllsch static void slurm_si470x_get_bandspace(struct slurm_softc *, uint16_t); 85 1.1 jakllsch static int slurm_si470x_get_info(uint16_t); 86 1.1 jakllsch static int slurm_si470x_get_mute(uint16_t); 87 1.1 jakllsch static int slurm_si470x_get_stereo(uint16_t); 88 1.1 jakllsch static int slurm_si470x_get_volume(uint16_t); 89 1.1 jakllsch 90 1.1 jakllsch static int slurm_si470x_search(struct slurm_softc *, int); 91 1.1 jakllsch 92 1.1 jakllsch static void slurm_si470x_set_freq(struct slurm_softc *, uint32_t); 93 1.1 jakllsch static void slurm_si470x_set_powercfg(struct slurm_softc *, int, int); 94 1.1 jakllsch static void slurm_si470x_set_volume(struct slurm_softc *, int); 95 1.1 jakllsch 96 1.1 jakllsch static const struct radio_hw_if slurm_radio = { 97 1.1 jakllsch .get_info = slurm_get_info, 98 1.1 jakllsch .set_info = slurm_set_info, 99 1.1 jakllsch .search = slurm_search, 100 1.1 jakllsch }; 101 1.1 jakllsch 102 1.1 jakllsch CFATTACH_DECL_NEW(slurm, sizeof(struct slurm_softc), 103 1.1 jakllsch slurm_match, slurm_attach, slurm_detach, NULL); 104 1.1 jakllsch 105 1.2 skrll static int 106 1.1 jakllsch slurm_match(device_t parent, cfdata_t match, void *aux) 107 1.1 jakllsch { 108 1.2 skrll const struct usbif_attach_arg * const uiaa = aux; 109 1.1 jakllsch 110 1.2 skrll if (uiaa->uiaa_ifaceno != 2) 111 1.1 jakllsch return UMATCH_NONE; 112 1.1 jakllsch 113 1.2 skrll if (usb_lookup(slurm_devs, uiaa->uiaa_vendor, uiaa->uiaa_product) != NULL) { 114 1.1 jakllsch return UMATCH_VENDOR_PRODUCT; 115 1.1 jakllsch } 116 1.1 jakllsch 117 1.1 jakllsch return UMATCH_NONE; 118 1.1 jakllsch } 119 1.1 jakllsch 120 1.2 skrll static void 121 1.1 jakllsch slurm_attach(device_t parent, device_t self, void *aux) 122 1.1 jakllsch { 123 1.1 jakllsch struct slurm_softc * const sc = device_private(self); 124 1.2 skrll const struct usbif_attach_arg * const uiaa = aux; 125 1.1 jakllsch 126 1.1 jakllsch sc->sc_dev = self; 127 1.2 skrll sc->sc_udev = uiaa->uiaa_device; 128 1.2 skrll sc->sc_uif = uiaa->uiaa_iface; 129 1.1 jakllsch 130 1.1 jakllsch aprint_normal("\n"); 131 1.1 jakllsch aprint_naive("\n"); 132 1.1 jakllsch 133 1.1 jakllsch usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev); 134 1.1 jakllsch 135 1.1 jakllsch #ifdef SLURM_DEBUG 136 1.1 jakllsch { 137 1.1 jakllsch uint16_t val; 138 1.1 jakllsch for (int i = 0; i < 16; i++) { 139 1.1 jakllsch slurm_getreg(sc, i, &val); 140 1.1 jakllsch device_printf(self, "%02x -> %04x\n", i, val); 141 1.1 jakllsch } 142 1.1 jakllsch } 143 1.1 jakllsch #endif 144 1.1 jakllsch 145 1.1 jakllsch radio_attach_mi(&slurm_radio, sc, self); 146 1.1 jakllsch } 147 1.1 jakllsch 148 1.2 skrll static int 149 1.1 jakllsch slurm_detach(device_t self, int flags) 150 1.1 jakllsch { 151 1.1 jakllsch struct slurm_softc * const sc = device_private(self); 152 1.1 jakllsch int rv = 0; 153 1.1 jakllsch 154 1.1 jakllsch if ((rv = config_detach_children(self, flags)) != 0) 155 1.1 jakllsch return rv; 156 1.1 jakllsch 157 1.3 msaitoh usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, sc->sc_dev); 158 1.1 jakllsch 159 1.4 skrll return rv; 160 1.1 jakllsch } 161 1.1 jakllsch 162 1.1 jakllsch static int 163 1.1 jakllsch slurm_get_info(void *v, struct radio_info *ri) 164 1.1 jakllsch { 165 1.1 jakllsch struct slurm_softc * const sc = v; 166 1.1 jakllsch uint16_t powercfg, sysconfig2, readchannel, statusrssi; 167 1.1 jakllsch 168 1.1 jakllsch slurm_getreg(sc, SI470X_POWERCFG, &powercfg); 169 1.1 jakllsch slurm_getreg(sc, SI470X_SYSCONFIG2, &sysconfig2); 170 1.1 jakllsch slurm_getreg(sc, SI470X_STATUSRSSI, &statusrssi); 171 1.1 jakllsch slurm_getreg(sc, SI470X_READCHANNEL, &readchannel); 172 1.1 jakllsch 173 1.1 jakllsch ri->mute = slurm_si470x_get_mute(powercfg); 174 1.1 jakllsch ri->volume = slurm_si470x_get_volume(sysconfig2); 175 1.1 jakllsch ri->stereo = slurm_si470x_get_stereo(powercfg); 176 1.1 jakllsch ri->rfreq = 0; 177 1.1 jakllsch ri->lock = 0; 178 1.1 jakllsch slurm_si470x_get_bandspace(sc, sysconfig2); 179 1.1 jakllsch ri->freq = slurm_si470x_get_freq(sc, readchannel); 180 1.1 jakllsch ri->caps = RADIO_CAPS_DETECT_STEREO | RADIO_CAPS_DETECT_SIGNAL | 181 1.1 jakllsch RADIO_CAPS_SET_MONO | RADIO_CAPS_HW_SEARCH | 182 1.1 jakllsch RADIO_CAPS_HW_AFC | RADIO_CAPS_LOCK_SENSITIVITY; 183 1.1 jakllsch ri->info = slurm_si470x_get_info(statusrssi); 184 1.1 jakllsch 185 1.1 jakllsch return 0; 186 1.1 jakllsch } 187 1.1 jakllsch 188 1.1 jakllsch static int 189 1.1 jakllsch slurm_set_info(void *v, struct radio_info *ri) 190 1.1 jakllsch { 191 1.1 jakllsch struct slurm_softc * const sc = v; 192 1.1 jakllsch 193 1.1 jakllsch slurm_si470x_set_freq(sc, ri->freq); 194 1.1 jakllsch slurm_si470x_set_powercfg(sc, ri->mute, ri->stereo); 195 1.1 jakllsch slurm_si470x_set_volume(sc, ri->volume); 196 1.1 jakllsch 197 1.1 jakllsch return 0; 198 1.1 jakllsch } 199 1.1 jakllsch 200 1.1 jakllsch static int 201 1.1 jakllsch slurm_search(void *v, int f) 202 1.1 jakllsch { 203 1.1 jakllsch struct slurm_softc * const sc = v; 204 1.2 skrll 205 1.1 jakllsch return slurm_si470x_search(sc, f); 206 1.1 jakllsch } 207 1.1 jakllsch 208 1.1 jakllsch static usbd_status 209 1.1 jakllsch slurm_getreg(struct slurm_softc *sc, int reg, uint16_t *val) 210 1.1 jakllsch { 211 1.1 jakllsch usbd_status status; 212 1.1 jakllsch uint8_t s[3]; 213 1.1 jakllsch 214 1.1 jakllsch ++reg; 215 1.1 jakllsch 216 1.1 jakllsch s[0] = reg; 217 1.1 jakllsch s[1] = s[2] = 0; 218 1.1 jakllsch 219 1.1 jakllsch status = usbd_get_report(sc->sc_uif, UHID_FEATURE_REPORT, 220 1.1 jakllsch reg, &s, sizeof(s)); 221 1.1 jakllsch 222 1.1 jakllsch *val = (s[1] << 8) | s[2]; 223 1.1 jakllsch 224 1.1 jakllsch return status; 225 1.1 jakllsch } 226 1.1 jakllsch 227 1.1 jakllsch static usbd_status 228 1.1 jakllsch slurm_setreg(struct slurm_softc *sc, int reg, uint16_t val) 229 1.1 jakllsch { 230 1.1 jakllsch usbd_status status; 231 1.1 jakllsch uint8_t s[3]; 232 1.1 jakllsch 233 1.1 jakllsch ++reg; 234 1.1 jakllsch 235 1.1 jakllsch s[0] = reg; 236 1.1 jakllsch s[1] = (val >> 8) & 0xff; 237 1.1 jakllsch s[2] = (val >> 0) & 0xff; 238 1.1 jakllsch 239 1.1 jakllsch status = usbd_set_report(sc->sc_uif, UHID_FEATURE_REPORT, 240 1.1 jakllsch reg, &s, sizeof(s)); 241 1.1 jakllsch 242 1.1 jakllsch return status; 243 1.1 jakllsch } 244 1.1 jakllsch 245 1.1 jakllsch static int 246 1.1 jakllsch slurm_si470x_await_stc(struct slurm_softc *sc) 247 1.1 jakllsch { 248 1.1 jakllsch int i; 249 1.1 jakllsch uint16_t statusrssi; 250 1.1 jakllsch 251 1.1 jakllsch for (i = 50; i > 0; i--) { 252 1.1 jakllsch usbd_delay_ms(sc->sc_udev, 2); 253 1.1 jakllsch slurm_getreg(sc, SI470X_STATUSRSSI, &statusrssi); 254 1.1 jakllsch if ((statusrssi & (SI470X_STC|SI470X_SF_BL)) != 0) 255 1.1 jakllsch break; 256 1.1 jakllsch } 257 1.1 jakllsch 258 1.1 jakllsch if (i == 0) 259 1.1 jakllsch return -1; 260 1.1 jakllsch else 261 1.1 jakllsch return 0; 262 1.1 jakllsch } 263 1.1 jakllsch 264 1.1 jakllsch static void 265 1.1 jakllsch slurm_si470x_get_bandspace(struct slurm_softc *sc, uint16_t sysconfig2) 266 1.1 jakllsch { 267 1.1 jakllsch switch (__SHIFTOUT(sysconfig2, SI470X_SPACE)) { 268 1.1 jakllsch default: 269 1.1 jakllsch case 0: 270 1.1 jakllsch sc->sc_space = 200; 271 1.1 jakllsch break; 272 1.1 jakllsch case 1: 273 1.1 jakllsch sc->sc_space = 100; 274 1.1 jakllsch break; 275 1.1 jakllsch case 2: 276 1.1 jakllsch sc->sc_space = 50; 277 1.1 jakllsch break; 278 1.1 jakllsch } 279 1.1 jakllsch 280 1.1 jakllsch switch (__SHIFTOUT(sysconfig2, SI470X_BAND)) { 281 1.1 jakllsch default: 282 1.1 jakllsch case 0: 283 1.1 jakllsch sc->sc_band = 87500; 284 1.1 jakllsch break; 285 1.1 jakllsch case 1: 286 1.1 jakllsch case 2: 287 1.1 jakllsch sc->sc_band = 76000; 288 1.1 jakllsch break; 289 1.1 jakllsch } 290 1.1 jakllsch } 291 1.1 jakllsch 292 1.1 jakllsch static uint32_t 293 1.1 jakllsch slurm_si470x_get_freq(struct slurm_softc *sc, uint16_t readchannel) 294 1.1 jakllsch { 295 1.1 jakllsch readchannel = __SHIFTOUT(readchannel, SI470X_READCHAN); 296 1.1 jakllsch return sc->sc_band + readchannel * sc->sc_space; 297 1.1 jakllsch } 298 1.1 jakllsch 299 1.1 jakllsch static int 300 1.1 jakllsch slurm_si470x_get_info(uint16_t statusrssi) 301 1.1 jakllsch { 302 1.1 jakllsch return (__SHIFTOUT(statusrssi, SI470X_ST) ? RADIO_INFO_STEREO : 0) 303 1.1 jakllsch | (__SHIFTOUT(statusrssi, SI470X_AFCRL) ? 0 : RADIO_INFO_SIGNAL); 304 1.1 jakllsch } 305 1.1 jakllsch 306 1.1 jakllsch static int 307 1.1 jakllsch slurm_si470x_get_mute(uint16_t powercfg) 308 1.1 jakllsch { 309 1.1 jakllsch return __SHIFTOUT(powercfg, SI470X_DMUTE) ? 0 : 1; 310 1.1 jakllsch } 311 1.1 jakllsch 312 1.1 jakllsch static int 313 1.1 jakllsch slurm_si470x_get_stereo(uint16_t powercfg) 314 1.1 jakllsch { 315 1.1 jakllsch return __SHIFTOUT(powercfg, SI470X_MONO) ? 0 : 1; 316 1.1 jakllsch } 317 1.1 jakllsch 318 1.1 jakllsch static int 319 1.1 jakllsch slurm_si470x_get_volume(uint16_t sysconfig2) 320 1.1 jakllsch { 321 1.1 jakllsch return __SHIFTOUT(sysconfig2, SI470X_VOLUME) * SI470X_VOLFACT; 322 1.1 jakllsch } 323 1.1 jakllsch 324 1.1 jakllsch static int 325 1.1 jakllsch slurm_si470x_search(struct slurm_softc *sc, int up) 326 1.1 jakllsch { 327 1.1 jakllsch uint16_t powercfg; 328 1.1 jakllsch 329 1.1 jakllsch slurm_getreg(sc, SI470X_POWERCFG, &powercfg); 330 1.1 jakllsch powercfg &= ~(SI470X_SKMODE|SI470X_SEEKUP|SI470X_SEEK); 331 1.1 jakllsch powercfg |= up ? SI470X_SEEKUP : 0; 332 1.1 jakllsch slurm_setreg(sc, SI470X_POWERCFG, SI470X_SEEK|powercfg); 333 1.1 jakllsch slurm_si470x_await_stc(sc); 334 1.1 jakllsch slurm_setreg(sc, SI470X_POWERCFG, powercfg); 335 1.1 jakllsch 336 1.1 jakllsch return 0; 337 1.1 jakllsch } 338 1.1 jakllsch 339 1.1 jakllsch static void 340 1.1 jakllsch slurm_si470x_set_freq(struct slurm_softc *sc, uint32_t freq) 341 1.1 jakllsch { 342 1.1 jakllsch uint16_t channel; 343 1.1 jakllsch 344 1.1 jakllsch channel = (freq - sc->sc_band) / sc->sc_space; 345 1.1 jakllsch 346 1.1 jakllsch slurm_setreg(sc, SI470X_CHANNEL, SI470X_TUNE|channel); 347 1.1 jakllsch slurm_si470x_await_stc(sc); 348 1.1 jakllsch slurm_setreg(sc, SI470X_CHANNEL, channel); 349 1.1 jakllsch 350 1.1 jakllsch #ifdef SLURM_DEBUG 351 1.1 jakllsch device_printf(sc->sc_dev, "%s 0a -> %04x after %d\n", __func__, val, i); 352 1.1 jakllsch #endif 353 1.1 jakllsch } 354 1.1 jakllsch 355 1.1 jakllsch static void 356 1.1 jakllsch slurm_si470x_set_powercfg(struct slurm_softc *sc, int mute, int stereo) 357 1.1 jakllsch { 358 1.1 jakllsch uint16_t powercfg; 359 1.1 jakllsch 360 1.1 jakllsch slurm_getreg(sc, SI470X_POWERCFG, &powercfg); 361 1.1 jakllsch powercfg &= ~(SI470X_DMUTE|SI470X_MONO); 362 1.1 jakllsch powercfg |= SI470X_DSMUTE; 363 1.1 jakllsch powercfg |= mute ? 0 : SI470X_DMUTE; 364 1.1 jakllsch powercfg |= stereo ? 0 : SI470X_MONO; 365 1.1 jakllsch slurm_setreg(sc, SI470X_POWERCFG, powercfg); 366 1.1 jakllsch } 367 1.1 jakllsch 368 1.1 jakllsch static void 369 1.1 jakllsch slurm_si470x_set_volume(struct slurm_softc *sc, int volume) 370 1.1 jakllsch { 371 1.1 jakllsch uint16_t sysconfig2; 372 1.1 jakllsch 373 1.1 jakllsch slurm_getreg(sc, SI470X_SYSCONFIG2, &sysconfig2); 374 1.1 jakllsch sysconfig2 &= ~SI470X_VOLUME; 375 1.1 jakllsch sysconfig2 |= __SHIFTIN(volume / SI470X_VOLFACT, SI470X_VOLUME); 376 1.1 jakllsch slurm_setreg(sc, SI470X_SYSCONFIG2, sysconfig2); 377 1.1 jakllsch } 378