Module Name:    src
Committed By:   jakllsch
Date:           Sun Jan 13 01:15:03 UTC 2013

Modified Files:
        src/sys/dev/usb: files.usb usbdevices.config
Added Files:
        src/sys/dev/ic: si470x_reg.h
        src/sys/dev/usb: slurm.c

Log Message:
Add slurm(4), a radio(4) driver for USB FM radio modules based on the
Silicon Labs reference design.


To generate a diff of this commit:
cvs rdiff -u -r0 -r1.1 src/sys/dev/ic/si470x_reg.h
cvs rdiff -u -r1.126 -r1.127 src/sys/dev/usb/files.usb
cvs rdiff -u -r0 -r1.1 src/sys/dev/usb/slurm.c
cvs rdiff -u -r1.19 -r1.20 src/sys/dev/usb/usbdevices.config

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/usb/files.usb
diff -u src/sys/dev/usb/files.usb:1.126 src/sys/dev/usb/files.usb:1.127
--- src/sys/dev/usb/files.usb:1.126	Wed Jan  9 23:02:59 2013
+++ src/sys/dev/usb/files.usb	Sun Jan 13 01:15:02 2013
@@ -1,4 +1,4 @@
-#	$NetBSD: files.usb,v 1.126 2013/01/09 23:02:59 skrll Exp $
+#	$NetBSD: files.usb,v 1.127 2013/01/13 01:15:02 jakllsch Exp $
 #
 # Config file and device description for machine-independent USB code.
 # Included by ports that need it.  Ports that use it must provide
@@ -236,6 +236,11 @@ device	udsbr: radiodev
 attach	udsbr at usbdevif
 file	dev/usb/udsbr.c			udsbr
 
+# Silicon Labs USB radio module (FM)
+device	slurm: radiodev
+attach	slurm at usbifif
+file	dev/usb/slurm.c			slurm
+
 # TEMPerHUM HID
 device	uthum: hid, sysmon_envsys
 attach	uthum at uhidbus

Index: src/sys/dev/usb/usbdevices.config
diff -u src/sys/dev/usb/usbdevices.config:1.19 src/sys/dev/usb/usbdevices.config:1.20
--- src/sys/dev/usb/usbdevices.config:1.19	Wed Jan  9 23:02:59 2013
+++ src/sys/dev/usb/usbdevices.config	Sun Jan 13 01:15:02 2013
@@ -1,4 +1,4 @@
-#	$NetBSD: usbdevices.config,v 1.19 2013/01/09 23:02:59 skrll Exp $
+#	$NetBSD: usbdevices.config,v 1.20 2013/01/13 01:15:02 jakllsch Exp $
 #
 # This file contains all USB related configuration.
 # It is suitable for inclusion in a kernel config(5) file.
@@ -195,6 +195,10 @@ uyap* at uhub? port ?
 udsbr*	at uhub? port ?
 radio*	at udsbr?
 
+# Silicon Labs-based USB radio modules
+slurm*	at uhub? port ?
+radio*	at slurm?
+
 # USB Generic driver
 ugen*	at uhub? port ?
 

Added files:

Index: src/sys/dev/ic/si470x_reg.h
diff -u /dev/null src/sys/dev/ic/si470x_reg.h:1.1
--- /dev/null	Sun Jan 13 01:15:03 2013
+++ src/sys/dev/ic/si470x_reg.h	Sun Jan 13 01:15:02 2013
@@ -0,0 +1,112 @@
+/*	$NetBSD: si470x_reg.h,v 1.1 2013/01/13 01:15:02 jakllsch Exp $ */
+
+/*
+ * Copyright (c) 2012 Jonathan A. Kollasch
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _DEV_IC_SI470X_REG_H_
+#define _DEV_IC_SI470X_REG_H_
+
+#define __BIT16(x)		((uint16_t)__BIT(x))
+#define __BITS16(x, y)		((uint16_t)__BITS((x), (y)))
+
+#define SI470X_DEVICEID		0x00
+#define SI470X_PN		__BITS16(15, 12)
+#define SI470X_MFGID		__BITS16(11, 0)
+
+#define SI470X_CHIPID		0x01
+#define SI470X_REV		__BITS16(15, 10)
+#define SI470X_DEV		__BITS16(9, 6)
+#define SI470X_FIRMWARE		__BITS16(5, 0)
+
+#define SI470X_POWERCFG		0x02
+#define SI470X_DSMUTE		__BIT16(15)
+#define SI470X_DMUTE		__BIT16(14)
+#define SI470X_MONO		__BIT16(13)
+#define SI470X_RDSM		__BIT16(11)
+#define SI470X_SKMODE		__BIT16(10)
+#define SI470X_SEEKUP		__BIT16(9)
+#define SI470X_SEEK		__BIT16(8)
+#define SI470X_DISABLE		__BIT16(6)
+#define SI470X_ENABLE		__BIT16(0)
+
+#define SI470X_CHANNEL		0x03
+#define SI470X_TUNE		__BIT16(15)
+#define SI470X_CHAN		__BITS16(9, 0)
+
+#define SI470X_SYSCONFIG1	0x04
+#define SI470X_RDSIEN		__BIT16(15)
+#define SI470X_STCIEN		__BIT16(14)
+#define SI470X_RDS		__BIT16(12)
+#define SI470X_DE		__BIT16(11)
+#define SI470X_AGCD		__BIT16(10)
+#define SI470X_BLNDADJ		__BITS16(7, 6)
+#define SI470X_GPIO3		__BITS16(5, 4)
+#define SI470X_GPIO2		__BITS16(3, 2)
+#define SI470X_GPIO1		__BITS16(1, 0)
+
+#define SI470X_SYSCONFIG2	0x05
+#define SI470X_SEEKTH		__BITS16(15, 8)
+#define SI470X_BAND		__BITS16(7, 6)
+#define SI470X_SPACE		__BITS16(5, 4)
+#define SI470X_VOLUME		__BITS16(3, 0)
+
+#define SI470X_SYSCONFIG3	0x06
+#define SI470X_SMUTER		__BITS16(15, 14)
+#define SI470X_SMUTEA		__BITS16(13, 12)
+#define SI470X_VOLEXT		__BIT16(8)
+#define SI470X_SKSNR		__BITS16(7, 4)
+#define SI470X_SKCNT		__BITS16(3, 0)
+
+#define SI470X_TEST1		0x07
+#define SI470X_XOSCEN		__BIT16(15)
+#define SI470X_AHIZEN		__BIT16(14)
+
+#define SI470X_TEST2		0x08
+
+#define SI470X_BOOTCONFIG	0x09
+
+#define SI470X_STATUSRSSI	0x0a
+#define SI470X_RDSR		__BIT16(15)
+#define SI470X_STC		__BIT16(14)
+#define SI470X_SF_BL		__BIT16(13)
+#define SI470X_AFCRL		__BIT16(12)
+#define SI470X_RDSS		__BIT16(11)
+#define SI470X_BLERA		__BITS16(9, 10)
+#define SI470X_ST		__BIT16(8)
+#define SI470X_RSSI		__BITS16(7, 0)
+
+#define SI470X_READCHANNEL	0x0b
+#define SI470X_BLERB		__BITS16(15, 14)
+#define SI470X_BLERC		__BITS16(13, 12)
+#define SI470X_BLERD		__BITS16(11, 10)
+#define SI470X_READCHAN		__BITS16(9, 0)
+
+#define SI470X_RDSA		0x0c
+#define SI470X_RDSB		0x0d
+#define SI470X_RDSC		0x0e
+#define SI470X_RDSD		0x0f
+
+#endif /* _DEV_IC_SI470X_REG_H_ */

Index: src/sys/dev/usb/slurm.c
diff -u /dev/null src/sys/dev/usb/slurm.c:1.1
--- /dev/null	Sun Jan 13 01:15:03 2013
+++ src/sys/dev/usb/slurm.c	Sun Jan 13 01:15:02 2013
@@ -0,0 +1,379 @@
+/*	$NetBSD: slurm.c,v 1.1 2013/01/13 01:15:02 jakllsch Exp $ */
+
+/*
+ * Copyright (c) 2012 Jonathan A. Kollasch
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: slurm.c,v 1.1 2013/01/13 01:15:02 jakllsch Exp $");
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+#include <dev/ic/si470x_reg.h>
+
+#include <sys/radioio.h>
+#include <dev/radio_if.h>
+
+#ifdef SLURM_DEBUG
+int	slurmdebug = 0;
+#define DPRINTFN(n, x)	do { if (slurmdebug > (n)) printf x; } while (0)
+#else
+#define DPRINTFN(n, x)
+#endif
+
+#define DPRINTF(x) DPRINTFN(0, x)
+
+#define SI470X_VOLFACT (255 / __SHIFTOUT_MASK(SI470X_VOLUME))
+
+struct slurm_softc {
+	device_t		sc_dev;
+	usbd_device_handle	sc_udev;
+	usbd_interface_handle	sc_uif;
+	uint32_t		sc_band;
+	uint32_t		sc_space;
+};
+
+static const struct usb_devno slurm_devs[] = {
+	{ USB_VENDOR_ADS, USB_PRODUCT_ADS_RDX155 },
+};
+
+static int slurm_match(device_t, cfdata_t, void *);
+static void slurm_attach(device_t, device_t, void *);
+static int slurm_detach(device_t, int);
+
+static int slurm_get_info(void *, struct radio_info *);
+static int slurm_set_info(void *, struct radio_info *);
+static int slurm_search(void *, int);
+
+static usbd_status slurm_setreg(struct slurm_softc *, int, uint16_t);
+static usbd_status slurm_getreg(struct slurm_softc *, int, uint16_t *);
+
+static uint32_t slurm_si470x_get_freq(struct slurm_softc *, uint16_t);
+static void slurm_si470x_get_bandspace(struct slurm_softc *, uint16_t);
+static int slurm_si470x_get_info(uint16_t);
+static int slurm_si470x_get_mute(uint16_t);
+static int slurm_si470x_get_stereo(uint16_t);
+static int slurm_si470x_get_volume(uint16_t);
+
+static int slurm_si470x_search(struct slurm_softc *, int);
+
+static void slurm_si470x_set_freq(struct slurm_softc *, uint32_t);
+static void slurm_si470x_set_powercfg(struct slurm_softc *, int, int);
+static void slurm_si470x_set_volume(struct slurm_softc *, int);
+
+static const struct radio_hw_if slurm_radio = {
+	.get_info = slurm_get_info,
+	.set_info = slurm_set_info,
+	.search = slurm_search,
+};
+
+CFATTACH_DECL_NEW(slurm, sizeof(struct slurm_softc),
+    slurm_match, slurm_attach, slurm_detach, NULL);
+
+static int 
+slurm_match(device_t parent, cfdata_t match, void *aux)
+{
+	const struct usbif_attach_arg * const uaa = aux;
+
+	if (uaa->ifaceno != 2)
+		return UMATCH_NONE;
+
+	if (usb_lookup(slurm_devs, uaa->vendor, uaa->product) != NULL) {
+		return UMATCH_VENDOR_PRODUCT;
+	}
+
+	return UMATCH_NONE;
+}
+
+static void 
+slurm_attach(device_t parent, device_t self, void *aux)
+{
+	struct slurm_softc * const sc = device_private(self);
+	const struct usbif_attach_arg * const uaa = aux;
+
+	sc->sc_dev = self;
+	sc->sc_udev = uaa->device;
+	sc->sc_uif = uaa->iface;
+
+	aprint_normal("\n");
+	aprint_naive("\n");
+
+	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, sc->sc_dev);
+
+#ifdef SLURM_DEBUG
+	{
+		uint16_t val;
+		for (int i = 0; i < 16; i++) {
+			slurm_getreg(sc, i, &val);
+			device_printf(self, "%02x -> %04x\n", i, val);
+		}
+	}
+#endif
+
+	radio_attach_mi(&slurm_radio, sc, self);
+}
+
+static int 
+slurm_detach(device_t self, int flags)
+{
+	struct slurm_softc * const sc = device_private(self);
+	int rv = 0;
+
+	if ((rv = config_detach_children(self, flags)) != 0)
+		return rv;
+
+	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
+	    sc->sc_dev);
+
+	return (rv);
+}
+
+static int
+slurm_get_info(void *v, struct radio_info *ri)
+{
+	struct slurm_softc * const sc = v;
+	uint16_t powercfg, sysconfig2, readchannel, statusrssi;
+
+	slurm_getreg(sc, SI470X_POWERCFG, &powercfg);
+	slurm_getreg(sc, SI470X_SYSCONFIG2, &sysconfig2);
+	slurm_getreg(sc, SI470X_STATUSRSSI, &statusrssi);
+	slurm_getreg(sc, SI470X_READCHANNEL, &readchannel);
+
+	ri->mute = slurm_si470x_get_mute(powercfg);
+	ri->volume = slurm_si470x_get_volume(sysconfig2);
+	ri->stereo = slurm_si470x_get_stereo(powercfg);
+	ri->rfreq = 0;
+	ri->lock = 0;
+	slurm_si470x_get_bandspace(sc, sysconfig2);
+	ri->freq = slurm_si470x_get_freq(sc, readchannel);
+	ri->caps = RADIO_CAPS_DETECT_STEREO | RADIO_CAPS_DETECT_SIGNAL |
+		   RADIO_CAPS_SET_MONO | RADIO_CAPS_HW_SEARCH |
+		   RADIO_CAPS_HW_AFC | RADIO_CAPS_LOCK_SENSITIVITY;
+	ri->info = slurm_si470x_get_info(statusrssi);
+
+	return 0;
+}
+
+static int
+slurm_set_info(void *v, struct radio_info *ri)
+{
+	struct slurm_softc * const sc = v;
+
+	slurm_si470x_set_freq(sc, ri->freq);
+	slurm_si470x_set_powercfg(sc, ri->mute, ri->stereo);
+	slurm_si470x_set_volume(sc, ri->volume);
+
+	return 0;
+}
+
+static int
+slurm_search(void *v, int f)
+{
+	struct slurm_softc * const sc = v;
+	
+	return slurm_si470x_search(sc, f);
+}
+
+static usbd_status
+slurm_getreg(struct slurm_softc *sc, int reg, uint16_t *val)
+{
+	usbd_status status;
+	uint8_t s[3];
+
+	++reg;
+
+	s[0] = reg;
+	s[1] = s[2] = 0;
+
+	status = usbd_get_report(sc->sc_uif, UHID_FEATURE_REPORT,
+		reg, &s, sizeof(s));
+
+	*val = (s[1] << 8) | s[2];
+
+	return status;
+}
+
+static usbd_status
+slurm_setreg(struct slurm_softc *sc, int reg, uint16_t val)
+{
+	usbd_status status;
+	uint8_t s[3];
+
+	++reg;
+
+	s[0] = reg;
+	s[1] = (val >> 8) & 0xff;
+	s[2] = (val >> 0) & 0xff;
+
+	status = usbd_set_report(sc->sc_uif, UHID_FEATURE_REPORT,
+		reg, &s, sizeof(s));
+
+	return status;
+}
+
+static int
+slurm_si470x_await_stc(struct slurm_softc *sc)
+{
+	int i;
+	uint16_t statusrssi;
+
+	for (i = 50; i > 0; i--) {
+		usbd_delay_ms(sc->sc_udev, 2);
+		slurm_getreg(sc, SI470X_STATUSRSSI, &statusrssi);
+		if ((statusrssi & (SI470X_STC|SI470X_SF_BL)) != 0)
+			break;
+	}
+
+	if (i == 0)
+		return -1;
+	else
+		return 0;
+}
+
+static void
+slurm_si470x_get_bandspace(struct slurm_softc *sc, uint16_t sysconfig2)
+{
+	switch (__SHIFTOUT(sysconfig2, SI470X_SPACE)) {
+	default:
+	case 0:
+		sc->sc_space = 200;
+		break;
+	case 1:
+		sc->sc_space = 100;
+		break;
+	case 2:
+		sc->sc_space = 50;
+		break;
+	}
+
+	switch (__SHIFTOUT(sysconfig2, SI470X_BAND)) {
+	default:
+	case 0:
+		sc->sc_band = 87500;
+		break;
+	case 1:
+	case 2:
+		sc->sc_band = 76000;
+		break;
+	}
+}
+
+static uint32_t
+slurm_si470x_get_freq(struct slurm_softc *sc, uint16_t readchannel)
+{
+	readchannel = __SHIFTOUT(readchannel, SI470X_READCHAN);
+	return sc->sc_band + readchannel * sc->sc_space;
+}
+
+static int
+slurm_si470x_get_info(uint16_t statusrssi)
+{
+	return (__SHIFTOUT(statusrssi, SI470X_ST) ? RADIO_INFO_STEREO : 0)
+	    | (__SHIFTOUT(statusrssi, SI470X_AFCRL) ? 0 : RADIO_INFO_SIGNAL);
+}
+
+static int
+slurm_si470x_get_mute(uint16_t powercfg)
+{
+	return __SHIFTOUT(powercfg, SI470X_DMUTE) ? 0 : 1;
+}
+
+static int
+slurm_si470x_get_stereo(uint16_t powercfg)
+{
+	return __SHIFTOUT(powercfg, SI470X_MONO) ? 0 : 1;
+}
+
+static int
+slurm_si470x_get_volume(uint16_t sysconfig2)
+{
+	return __SHIFTOUT(sysconfig2, SI470X_VOLUME) * SI470X_VOLFACT;
+}
+
+static int
+slurm_si470x_search(struct slurm_softc *sc, int up)
+{
+	uint16_t powercfg;
+
+	slurm_getreg(sc, SI470X_POWERCFG, &powercfg);
+	powercfg &= ~(SI470X_SKMODE|SI470X_SEEKUP|SI470X_SEEK);
+	powercfg |= up ? SI470X_SEEKUP : 0;
+	slurm_setreg(sc, SI470X_POWERCFG, SI470X_SEEK|powercfg);
+	slurm_si470x_await_stc(sc);
+	slurm_setreg(sc, SI470X_POWERCFG, powercfg);
+
+	return 0;
+}
+
+static void
+slurm_si470x_set_freq(struct slurm_softc *sc, uint32_t freq)
+{
+	uint16_t channel;
+
+	channel = (freq - sc->sc_band) / sc->sc_space;
+
+	slurm_setreg(sc, SI470X_CHANNEL, SI470X_TUNE|channel);
+	slurm_si470x_await_stc(sc);
+	slurm_setreg(sc, SI470X_CHANNEL, channel);
+
+#ifdef SLURM_DEBUG
+	device_printf(sc->sc_dev, "%s 0a -> %04x after %d\n", __func__, val, i);
+#endif
+}
+
+static void
+slurm_si470x_set_powercfg(struct slurm_softc *sc, int mute, int stereo)
+{
+	uint16_t powercfg;
+
+	slurm_getreg(sc, SI470X_POWERCFG, &powercfg);
+	powercfg &= ~(SI470X_DMUTE|SI470X_MONO);
+	powercfg |= SI470X_DSMUTE;
+	powercfg |= mute ? 0 : SI470X_DMUTE;
+	powercfg |= stereo ? 0 : SI470X_MONO;
+	slurm_setreg(sc, SI470X_POWERCFG, powercfg);
+}
+
+static void
+slurm_si470x_set_volume(struct slurm_softc *sc, int volume)
+{
+	uint16_t sysconfig2;
+
+	slurm_getreg(sc, SI470X_SYSCONFIG2, &sysconfig2);
+	sysconfig2 &= ~SI470X_VOLUME;
+	sysconfig2 |= __SHIFTIN(volume / SI470X_VOLFACT, SI470X_VOLUME);
+	slurm_setreg(sc, SI470X_SYSCONFIG2, sysconfig2);
+}

Reply via email to