Module Name: src
Committed By: jmcneill
Date: Mon Jul 11 18:02:04 UTC 2011
Modified Files:
src/sys/dev/usb: files.usb
Added Files:
src/sys/dev/usb: emdtv.c emdtv_board.c emdtv_board.h emdtv_dtv.c
emdtv_i2c.c emdtv_ir.c emdtvreg.h emdtvvar.h
Log Message:
add emdtv(4), a dtv(4) driver for Empia Technology EM28XX DTV devices
To generate a diff of this commit:
cvs rdiff -u -r0 -r1.1 src/sys/dev/usb/emdtv.c src/sys/dev/usb/emdtv_board.c \
src/sys/dev/usb/emdtv_board.h src/sys/dev/usb/emdtv_dtv.c \
src/sys/dev/usb/emdtv_i2c.c src/sys/dev/usb/emdtv_ir.c \
src/sys/dev/usb/emdtvreg.h src/sys/dev/usb/emdtvvar.h
cvs rdiff -u -r1.107 -r1.108 src/sys/dev/usb/files.usb
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.107 src/sys/dev/usb/files.usb:1.108
--- src/sys/dev/usb/files.usb:1.107 Sun Jul 10 02:25:52 2011
+++ src/sys/dev/usb/files.usb Mon Jul 11 18:02:04 2011
@@ -1,4 +1,4 @@
-# $NetBSD: files.usb,v 1.107 2011/07/10 02:25:52 jmcneill Exp $
+# $NetBSD: files.usb,v 1.108 2011/07/11 18:02:04 jmcneill Exp $
#
# Config file and device description for machine-independent USB code.
# Included by ports that need it. Ports that use it must provide
@@ -158,6 +158,15 @@
file dev/usb/auvitek_i2c.c auvitek
file dev/usb/auvitek_video.c auvitek
+# Empia EM28xx
+device emdtv: dtvbus, irbus, i2cexec, lg3303, xc3028
+attach emdtv at usbdevif
+file dev/usb/emdtv.c emdtv
+file dev/usb/emdtv_board.c emdtv
+file dev/usb/emdtv_dtv.c emdtv
+file dev/usb/emdtv_i2c.c emdtv
+file dev/usb/emdtv_ir.c emdtv
+
#
# Misc
#
Added files:
Index: src/sys/dev/usb/emdtv.c
diff -u /dev/null src/sys/dev/usb/emdtv.c:1.1
--- /dev/null Mon Jul 11 18:02:04 2011
+++ src/sys/dev/usb/emdtv.c Mon Jul 11 18:02:04 2011
@@ -0,0 +1,474 @@
+/* $NetBSD: emdtv.c,v 1.1 2011/07/11 18:02:04 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2008, 2011 Jared D. McNeill <[email protected]>
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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: emdtv.c,v 1.1 2011/07/11 18:02:04 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/module.h>
+#include <sys/conf.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+
+#include <dev/usb/emdtvvar.h>
+#include <dev/usb/emdtvreg.h>
+
+static int emdtv_match(device_t, cfdata_t, void *);
+static void emdtv_attach(device_t, device_t, void *);
+static int emdtv_detach(device_t, int);
+static void emdtv_childdet(device_t, device_t);
+static int emdtv_activate(device_t, enum devact);
+
+static bool emdtv_read_eeprom(struct emdtv_softc *);
+static void emdtv_board_setup(struct emdtv_softc *);
+
+static void emdtv_default_board_init(struct emdtv_softc *);
+
+CFATTACH_DECL2_NEW(emdtv, sizeof(struct emdtv_softc),
+ emdtv_match, emdtv_attach, emdtv_detach, emdtv_activate,
+ NULL, emdtv_childdet);
+
+static const struct usb_devno emdtv_devices[] = {
+ { USB_VENDOR_AMD, USB_PRODUCT_AMD_TV_WONDER_600_USB },
+};
+
+int emdtv_debug_regs = 0;
+
+static int
+emdtv_match(device_t parent, cfdata_t match, void *opaque)
+{
+ struct usb_attach_arg *uaa = opaque;
+
+ return usb_lookup(emdtv_devices, uaa->vendor, uaa->product) != NULL ?
+ UMATCH_VENDOR_PRODUCT : UMATCH_NONE;
+}
+
+static void
+emdtv_attach(device_t parent, device_t self, void *opaque)
+{
+ struct emdtv_softc *sc = device_private(self);
+ struct usb_attach_arg *uaa = opaque;
+ usbd_device_handle dev = uaa->device;
+ usbd_status status;
+ char *devinfo;
+
+ devinfo = usbd_devinfo_alloc(dev, 0);
+ aprint_naive("\n");
+ aprint_normal(": %s\n", devinfo);
+ usbd_devinfo_free(devinfo);
+
+ sc->sc_dev = self;
+ sc->sc_udev = dev;
+
+ sc->sc_vendor = uaa->vendor;
+ sc->sc_product = uaa->product;
+
+ emdtv_i2c_attach(sc);
+
+ emdtv_read_eeprom(sc);
+
+ sc->sc_board = emdtv_board_lookup(sc->sc_vendor, sc->sc_product);
+ if (sc->sc_board == NULL) {
+ aprint_error_dev(sc->sc_dev,
+ "unsupported board 0x%04x:0x%04x\n",
+ sc->sc_vendor, sc->sc_product);
+ sc->sc_dying = true;
+ return;
+ }
+
+ emdtv_write_1(sc, 0x02, 0xa0, 0x23);
+ if (emdtv_read_1(sc, UR_GET_STATUS, 0x05) != 0) {
+ (void)emdtv_read_1(sc, 0x02, 0xa0);
+ if (emdtv_read_1(sc, 0x02, 0xa0) & 0x08)
+ aprint_debug_dev(sc->sc_dev,
+ "board requires manual gpio configuration\n");
+ }
+
+ emdtv_board_setup(sc);
+
+ emdtv_gpio_ctl(sc, EMDTV_GPIO_ANALOG_ON, false);
+ emdtv_gpio_ctl(sc, EMDTV_GPIO_TS1_ON, false);
+ usbd_delay_ms(sc->sc_udev, 100);
+ emdtv_gpio_ctl(sc, EMDTV_GPIO_ANALOG_ON, true);
+ emdtv_gpio_ctl(sc, EMDTV_GPIO_TUNER1_ON, true);
+ usbd_delay_ms(sc->sc_udev, 100);
+
+ status = usbd_set_config_no(sc->sc_udev, 1, 1);
+ if (status != USBD_NORMAL_COMPLETION) {
+ aprint_error_dev(sc->sc_dev, "couldn't set config no\n");
+ return;
+ }
+
+ status = usbd_device2interface_handle(sc->sc_udev, 0, &sc->sc_iface);
+ if (status != USBD_NORMAL_COMPLETION) {
+ aprint_error_dev(sc->sc_dev, "couldn't find iface handle\n");
+ return;
+ }
+
+ status = usbd_set_interface(sc->sc_iface, 1);
+ if (status != USBD_NORMAL_COMPLETION) {
+ aprint_error_dev(sc->sc_dev, "couldn't set interface\n");
+ return;
+ }
+
+ emdtv_dtv_attach(sc);
+ emdtv_ir_attach(sc);
+}
+
+static int
+emdtv_detach(device_t self, int flags)
+{
+ struct emdtv_softc *sc = device_private(self);
+ usbd_status status;
+
+ sc->sc_dying = true;
+
+ emdtv_ir_detach(sc, flags);
+ emdtv_dtv_detach(sc, flags);
+
+ if (sc->sc_iface != NULL) {
+ status = usbd_set_interface(sc->sc_iface, 0);
+ if (status != USBD_NORMAL_COMPLETION)
+ aprint_error_dev(sc->sc_dev,
+ "couldn't stop stream: %s\n", usbd_errstr(status));
+ }
+
+ if (sc->sc_lg3303)
+ lg3303_close(sc->sc_lg3303);
+
+ emdtv_i2c_detach(sc, flags);
+
+ return 0;
+}
+
+int
+emdtv_activate(device_t self, enum devact act)
+{
+ struct emdtv_softc *sc = device_private(self);
+
+ switch (act) {
+ case DVACT_DEACTIVATE:
+ sc->sc_dying = true;
+ break;
+ }
+
+ return 0;
+}
+
+static void
+emdtv_childdet(device_t self, device_t child)
+{
+ struct emdtv_softc *sc = device_private(self);
+
+ if (child == sc->sc_cirdev)
+ sc->sc_cirdev = NULL;
+ if (child == sc->sc_dtvdev)
+ sc->sc_dtvdev = NULL;
+}
+
+static bool
+emdtv_read_eeprom(struct emdtv_softc *sc)
+{
+ i2c_addr_t ee = EM28XX_I2C_ADDR_EEPROM;
+ uint8_t buf, *p = sc->sc_eeprom;
+ struct emdtv_eeprom *eeprom = (struct emdtv_eeprom *)sc->sc_eeprom;
+ int block, size = sizeof(sc->sc_eeprom);
+
+ if (iic_exec(&sc->sc_i2c, I2C_OP_READ, ee, NULL, 0, NULL, 0, 0))
+ return false;
+ buf = 0;
+ if (iic_exec(&sc->sc_i2c, I2C_OP_WRITE_WITH_STOP, ee, &buf, 1,
+ NULL, 0, 0))
+ return false;
+ while (size > 0) {
+ block = min(size, 16);
+ if (iic_exec(&sc->sc_i2c, I2C_OP_READ, ee, NULL, 0,
+ p, block, 0))
+ return false;
+ size -= block;
+ p += block;
+ }
+
+ aprint_normal_dev(sc->sc_dev,
+ "id 0x%08x vendor 0x%04x product 0x%04x\n",
+ eeprom->id, eeprom->vendor, eeprom->product);
+
+ sc->sc_vendor = eeprom->vendor;
+ sc->sc_product = eeprom->product;
+
+ return true;
+}
+
+static void
+emdtv_board_setup(struct emdtv_softc *sc)
+{
+ switch (sc->sc_vendor) {
+ case USB_VENDOR_EMPIA:
+ switch (sc->sc_product) {
+ case USB_PRODUCT_EMPIA_EM2883:
+ emdtv_write_1(sc, UR_GET_STATUS, EM28XX_XCLK_REG, 0x97);
+ emdtv_write_1(sc, UR_GET_STATUS, EM28XX_I2C_CLK_REG,
+ 0x40);
+ delay(10000);
+ emdtv_write_1(sc, UR_GET_STATUS, 0x08, 0x2d);
+ delay(10000);
+ break;
+ default:
+ aprint_normal_dev(sc->sc_dev,
+ "unknown EMPIA board 0x%04x/0x%04x\n",
+ sc->sc_vendor, sc->sc_product);
+ break;
+ }
+ break;
+ case USB_VENDOR_AMD:
+ switch (sc->sc_product) {
+ case USB_PRODUCT_AMD_TV_WONDER_600_USB:
+ emdtv_default_board_init(sc);
+ break;
+ default:
+ aprint_normal_dev(sc->sc_dev,
+ "unknown AMD board 0x%04x/0x%04x\n",
+ sc->sc_vendor, sc->sc_product);
+ }
+ break;
+ case USB_VENDOR_PINNACLE:
+ switch (sc->sc_product) {
+ case USB_PRODUCT_PINNACLE_PCTV800E:
+ emdtv_default_board_init(sc);
+ break;
+ default:
+ aprint_normal_dev(sc->sc_dev,
+ "unknown Pinnacle board 0x%04x/0x%04x\n",
+ sc->sc_vendor, sc->sc_product);
+ }
+ break;
+ default:
+ aprint_normal_dev(sc->sc_dev,
+ "unknown board 0x%04x:0x%04x\n",
+ sc->sc_vendor, sc->sc_product);
+ break;
+ }
+}
+
+/*
+ * Register read/write
+ */
+uint8_t
+emdtv_read_1(struct emdtv_softc *sc, uint8_t req, uint16_t index)
+{
+ uint8_t val;
+ emdtv_read_multi_1(sc, req, index, &val, 1);
+ return val;
+}
+
+void
+emdtv_write_1(struct emdtv_softc *sc, uint8_t req, uint16_t index, uint8_t val)
+{
+ emdtv_write_multi_1(sc, req, index, &val, 1);
+}
+
+void
+emdtv_read_multi_1(struct emdtv_softc *sc, uint8_t req, uint16_t index,
+ uint8_t *datap, uint16_t count)
+{
+ usb_device_request_t request;
+ usbd_status status;
+
+ request.bmRequestType = UT_READ_VENDOR_DEVICE;
+ request.bRequest = req;
+ USETW(request.wValue, 0x0000);
+ USETW(request.wIndex, index);
+ USETW(request.wLength, count);
+
+ status = usbd_do_request(sc->sc_udev, &request, datap);
+ if (status != USBD_NORMAL_COMPLETION)
+ aprint_error_dev(sc->sc_dev, "couldn't read %x/%x: %s\n",
+ req, index, usbd_errstr(status));
+
+ if (emdtv_debug_regs) {
+ int i;
+ printf("%s [%s] c0 %02x 00 00 %02x 00 01 00 <<<",
+ __func__, status == 0 ? " OK" : "NOK", req, index);
+ for (i = 0; status == 0 && i < count; i++)
+ printf(" %02x", datap[i]);
+ printf("\n");
+ }
+}
+
+void
+emdtv_write_multi_1(struct emdtv_softc *sc, uint8_t req, uint16_t index,
+ const uint8_t *datap, uint16_t count)
+{
+ usb_device_request_t request;
+ usbd_status status;
+
+ request.bmRequestType = UT_WRITE_VENDOR_DEVICE;
+ request.bRequest = req;
+ USETW(request.wValue, 0x0000);
+ USETW(request.wIndex, index);
+ USETW(request.wLength, count);
+
+ status = usbd_do_request(sc->sc_udev, &request, __UNCONST(datap));
+ if (status != USBD_NORMAL_COMPLETION)
+ aprint_error_dev(sc->sc_dev, "couldn't read %x/%x: %s\n",
+ req, index, usbd_errstr(status));
+
+ if (emdtv_debug_regs) {
+ int i;
+ printf("%s [%s] 40 %02x 00 00 %02x 00 %02x 00 >>>",
+ __func__, status == 0 ? " OK" : "NOK",
+ req, index, count);
+ for (i = 0; i < count; ++i)
+ printf(" %02x", datap[i]);
+ printf("\n");
+ }
+}
+
+bool
+emdtv_gpio_ctl(struct emdtv_softc *sc, emdtv_gpio_reg_t gpioreg, bool onoff)
+{
+ const struct emdtv_board *eb = sc->sc_board;
+ uint16_t gpio_value, reg;
+ uint8_t gpio;
+ uint8_t eeprom_offset = 0x3c;
+ uint8_t val;
+
+ if (sc->sc_board->eb_manual_gpio == false) {
+ val = eeprom_offset + gpioreg;
+ emdtv_write_1(sc, 0x03, 0xa0, val);
+ gpio_value = emdtv_read_1(sc, 0x02, 0xa0);
+ } else {
+ const struct emdtv_gpio_regs *r = &eb->eb_gpio_regs;
+ switch (gpioreg) {
+ case EMDTV_GPIO_TS1_ON:
+ gpio_value = r->ts1_on;
+ break;
+ case EMDTV_GPIO_ANALOG_ON:
+ gpio_value = r->a_on;
+ break;
+ case EMDTV_GPIO_TUNER1_ON:
+ gpio_value = r->t1_on;
+ break;
+ case EMDTV_GPIO_TUNER1_RESET:
+ gpio_value = r->t1_reset;
+ break;
+ case EMDTV_GPIO_DEMOD1_RESET:
+ gpio_value = r->d1_reset;
+ break;
+ default:
+ aprint_error_dev(sc->sc_dev,
+ "unknown gpio reg %d\n", gpioreg);
+ return false;
+ }
+ }
+
+ if ((gpio_value & 0x80) == 0) {
+ aprint_error_dev(sc->sc_dev,
+ "gpio reg %d not enabled\n", gpioreg);
+ return false;
+ }
+
+ reg = gpio_value & 0x10 ? 0x04 : 0x08;
+ gpio = emdtv_read_1(sc, UR_GET_STATUS, reg);
+ if ((gpio_value & 0x40) == 0) {
+ gpio &= ~((uint8_t)(1 << (gpio_value & 7)));
+
+ if (onoff)
+ gpio |= ((gpio_value >> 5) & 1) << (gpio_value & 7);
+ else
+ gpio |= (((gpio_value >> 5) & 1) ^ 1) <<
+ (gpio_value & 7);
+ emdtv_write_1(sc, UR_GET_STATUS, reg, gpio);
+ } else {
+ gpio &= ~((uint8_t)(1 << (gpio_value & 0xf)));
+
+ gpio |= ((gpio_value >> 5) & 1) << (gpio_value & 7);
+ emdtv_write_1(sc, UR_GET_STATUS, reg, gpio);
+ usbd_delay_ms(sc->sc_udev, 100);
+
+ gpio &= ~((uint8_t)(1 << (gpio_value & 0xf)));
+ gpio |= (((gpio_value >> 5) & 1) ^ 1) << (gpio_value & 7);
+ emdtv_write_1(sc, UR_GET_STATUS, reg, gpio);
+ usbd_delay_ms(sc->sc_udev, 100);
+ }
+
+ return true;
+}
+
+static void
+emdtv_default_board_init(struct emdtv_softc *sc)
+{
+ emdtv_write_1(sc, UR_GET_STATUS, EM28XX_XCLK_REG, 0x27);
+ emdtv_write_1(sc, UR_GET_STATUS, EM28XX_I2C_CLK_REG, 0x40);
+ emdtv_write_1(sc, UR_GET_STATUS, 0x08, 0xff);
+ emdtv_write_1(sc, UR_GET_STATUS, 0x04, 0x00);
+ usbd_delay_ms(sc->sc_udev, 100);
+ emdtv_write_1(sc, UR_GET_STATUS, 0x04, 0x08);
+ usbd_delay_ms(sc->sc_udev, 100);
+ emdtv_write_1(sc, UR_GET_STATUS, 0x08, 0xff);
+ usbd_delay_ms(sc->sc_udev, 50);
+ emdtv_write_1(sc, UR_GET_STATUS, 0x08, 0x2d);
+ usbd_delay_ms(sc->sc_udev, 50);
+ emdtv_write_1(sc, UR_GET_STATUS, 0x08, 0x3d);
+ //emdtv_write_1(sc, UR_GET_STATUS, 0x0f, 0xa7);
+ usbd_delay_ms(sc->sc_udev, 10);
+}
+
+MODULE(MODULE_CLASS_DRIVER, emdtv, "cir,dtv,lg3303,xc3028");
+
+#ifdef _MODULE
+#include "ioconf.c"
+#endif
+
+static int
+emdtv_modcmd(modcmd_t cmd, void *opaque)
+{
+ switch (cmd) {
+ case MODULE_CMD_INIT:
+#ifdef _MODULE
+ return config_init_component(cfdriver_ioconf_emdtv,
+ cfattach_ioconf_emdtv, cfdata_ioconf_emdtv);
+#else
+ return 0;
+#endif
+ case MODULE_CMD_FINI:
+#ifdef _MODULE
+ return config_fini_component(cfdriver_ioconf_emdtv,
+ cfattach_ioconf_emdtv, cfdata_ioconf_emdtv);
+#else
+ return 0;
+#endif
+ default:
+ return ENOTTY;
+ }
+}
Index: src/sys/dev/usb/emdtv_board.c
diff -u /dev/null src/sys/dev/usb/emdtv_board.c:1.1
--- /dev/null Mon Jul 11 18:02:04 2011
+++ src/sys/dev/usb/emdtv_board.c Mon Jul 11 18:02:04 2011
@@ -0,0 +1,91 @@
+/* $NetBSD: emdtv_board.c,v 1.1 2011/07/11 18:02:04 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2008 Jared D. McNeill <[email protected]>
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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: emdtv_board.c,v 1.1 2011/07/11 18:02:04 jmcneill Exp $");
+
+#include <sys/types.h>
+#include <sys/device.h>
+
+#include <dev/usb/usbdevs.h>
+#include <dev/usb/emdtvvar.h>
+
+static const struct emdtv_board emdtv_boards[] = {
+ {
+ .eb_name = "ATI TV Wonder 600 USB",
+ .eb_vendor = USB_VENDOR_AMD,
+ .eb_product = USB_PRODUCT_AMD_TV_WONDER_600_USB,
+ .eb_tuner = EMDTV_TUNER_XC3028L,
+ .eb_demod = EMDTV_DEMOD_LG3303,
+ .eb_manual_gpio = true,
+ .eb_gpio_regs = {
+ .ts1_on = EMDTV_GPIO_BIT_VAL(0, 0, 0),
+ .a_on = EMDTV_GPIO_BIT_VAL(1, 0, 0),
+ .t1_on = EMDTV_GPIO_BIT_VAL(6, 0, 0),
+ .t1_reset = EMDTV_GPIO_BIT_VAL(4, 0, 1),
+ .d1_reset = EMDTV_GPIO_BIT_VAL(19, 0, 1),
+ },
+ },
+ {
+ .eb_name = "Pinnacle PCTV HD Pro Stick 800e",
+ .eb_vendor = USB_VENDOR_PINNACLE,
+ .eb_product = USB_PRODUCT_PINNACLE_PCTV800E,
+ .eb_tuner = EMDTV_TUNER_XC3028,
+ .eb_demod = EMDTV_DEMOD_LG3303,
+ .eb_manual_gpio = true,
+ .eb_gpio_regs = {
+ .ts1_on = EMDTV_GPIO_BIT_VAL(0, 0, 0),
+ .a_on = EMDTV_GPIO_BIT_VAL(1, 0, 0),
+ .t1_on = EMDTV_GPIO_BIT_VAL(6, 0, 0),
+ .t1_reset = EMDTV_GPIO_BIT_VAL(4, 0, 1),
+ .d1_reset = EMDTV_GPIO_BIT_VAL(19, 0, 1),
+ },
+ },
+ {
+ .eb_name = "Empia Hybrid XS ATSC",
+ .eb_vendor = USB_VENDOR_EMPIA,
+ .eb_product = USB_PRODUCT_EMPIA_EM2883,
+ .eb_tuner = EMDTV_TUNER_XC5000,
+ .eb_demod = EMDTV_DEMOD_LG3304,
+ },
+};
+
+const struct emdtv_board *
+emdtv_board_lookup(uint16_t vendor, uint16_t product)
+{
+ const struct emdtv_board *eb;
+ unsigned int i;
+
+ for (i = 0; i < __arraycount(emdtv_boards); i++) {
+ eb = &emdtv_boards[i];
+ if (vendor == eb->eb_vendor && product == eb->eb_product)
+ return eb;
+ }
+
+ return NULL;
+}
Index: src/sys/dev/usb/emdtv_board.h
diff -u /dev/null src/sys/dev/usb/emdtv_board.h:1.1
--- /dev/null Mon Jul 11 18:02:04 2011
+++ src/sys/dev/usb/emdtv_board.h Mon Jul 11 18:02:04 2011
@@ -0,0 +1,74 @@
+/* $NetBSD: emdtv_board.h,v 1.1 2011/07/11 18:02:04 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2008 Jared D. McNeill <[email protected]>
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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_USB_EMDTV_BOARD_H
+#define _DEV_USB_EMDTV_BOARD_H
+
+typedef enum emdtv_tuner_type {
+ EMDTV_TUNER_XC3028L,
+ EMDTV_TUNER_XC3028,
+ EMDTV_TUNER_XC5000,
+} emdtv_tuner_type_t;
+
+typedef enum emdtv_demod_type {
+ EMDTV_DEMOD_LG3303,
+ EMDTV_DEMOD_LG3304,
+} emdtv_demod_type_t;
+
+typedef enum emdtv_gpio_reg {
+ EMDTV_GPIO_DEMOD1_RESET = 16,
+ EMDTV_GPIO_TUNER1_RESET = 17,
+ EMDTV_GPIO_TUNER1_ON = 23,
+ EMDTV_GPIO_ANALOG_ON = 24,
+ EMDTV_GPIO_TS1_ON = 25,
+} emdtv_gpio_reg_t;
+
+#define EMDTV_GPIO_BIT_VAL(reg, val, reset) \
+ ((reg) | 0x80 | (reset ? (1 << 6) : 0) | (val << 5))
+
+struct emdtv_gpio_regs {
+ uint16_t ts1_on;
+ uint16_t a_on;
+ uint16_t t1_on;
+ uint16_t t1_reset;
+ uint16_t d1_reset;
+};
+
+struct emdtv_board {
+ const char *eb_name;
+ uint16_t eb_vendor;
+ uint16_t eb_product;
+ emdtv_tuner_type_t eb_tuner;
+ emdtv_demod_type_t eb_demod;
+ bool eb_manual_gpio;
+ struct emdtv_gpio_regs eb_gpio_regs;
+};
+
+const struct emdtv_board *emdtv_board_lookup(uint16_t, uint16_t);
+
+#endif /* !_DEV_USB_EMDTV_BOARD_H */
Index: src/sys/dev/usb/emdtv_dtv.c
diff -u /dev/null src/sys/dev/usb/emdtv_dtv.c:1.1
--- /dev/null Mon Jul 11 18:02:04 2011
+++ src/sys/dev/usb/emdtv_dtv.c Mon Jul 11 18:02:04 2011
@@ -0,0 +1,399 @@
+/* $NetBSD: emdtv_dtv.c,v 1.1 2011/07/11 18:02:04 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2008, 2011 Jared D. McNeill <[email protected]>
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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: emdtv_dtv.c,v 1.1 2011/07/11 18:02:04 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+
+#include <dev/i2c/i2cvar.h>
+
+#include <dev/usb/emdtvvar.h>
+#include <dev/usb/emdtvreg.h>
+
+static void emdtv_dtv_get_devinfo(void *,
+ struct dvb_frontend_info *);
+static int emdtv_dtv_open(void *, int);
+static void emdtv_dtv_close(void *);
+static int emdtv_dtv_set_tuner(void *,
+ const struct dvb_frontend_parameters *);
+static fe_status_t emdtv_dtv_get_status(void *);
+static uint16_t emdtv_dtv_get_signal_strength(void *);
+static uint16_t emdtv_dtv_get_snr(void *);
+static int emdtv_dtv_start_transfer(void *);
+static int emdtv_dtv_stop_transfer(void *);
+
+static int emdtv_dtv_tuner_reset(void *);
+
+static void emdtv_dtv_isoc_startall(struct emdtv_softc *);
+static int emdtv_dtv_isoc_start(struct emdtv_softc *,
+ struct emdtv_isoc_xfer *);
+static void emdtv_dtv_isoc(usbd_xfer_handle, usbd_private_handle,
+ usbd_status);
+
+static const struct dtv_hw_if emdtv_dtv_if = {
+ .get_devinfo = emdtv_dtv_get_devinfo,
+ .open = emdtv_dtv_open,
+ .close = emdtv_dtv_close,
+ .set_tuner = emdtv_dtv_set_tuner,
+ .get_status = emdtv_dtv_get_status,
+ .get_signal_strength = emdtv_dtv_get_signal_strength,
+ .get_snr = emdtv_dtv_get_snr,
+ .start_transfer = emdtv_dtv_start_transfer,
+ .stop_transfer = emdtv_dtv_stop_transfer,
+};
+
+void
+emdtv_dtv_attach(struct emdtv_softc *sc)
+{
+ usb_endpoint_descriptor_t *ed;
+ usbd_status status;
+ struct dtv_attach_args daa;
+ int i;
+
+ for (i = 0; i < EMDTV_NXFERS; i++) {
+ sc->sc_ix[i].ix_altix = (i & 1) ?
+ &sc->sc_ix[i - 1] : &sc->sc_ix[i + 1];
+ sc->sc_ix[i].ix_sc = sc;
+ }
+
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface, 3);
+ if (ed == NULL) {
+ aprint_error_dev(sc->sc_dev, "couldn't find endpoint 3\n");
+ return;
+ }
+ sc->sc_isoc_maxpacketsize = UGETW(ed->wMaxPacketSize);
+ sc->sc_isoc_buflen = sc->sc_isoc_maxpacketsize * EMDTV_NFRAMES;
+
+ aprint_debug_dev(sc->sc_dev, "calling usbd_open_pipe, ep 0x%02x\n",
+ ed->bEndpointAddress);
+ status = usbd_open_pipe(sc->sc_iface,
+ ed->bEndpointAddress, USBD_EXCLUSIVE_USE,
+ &sc->sc_isoc_pipe);
+ if (status != USBD_NORMAL_COMPLETION) {
+ aprint_error_dev(sc->sc_dev, "couldn't open isoc pipe\n");
+ usbd_set_interface(sc->sc_iface, 0);
+ return;
+ }
+
+ emdtv_write_1(sc, UR_GET_STATUS, 0x48, 0x00);
+ emdtv_write_1(sc, UR_GET_STATUS, 0x12, 0x77);
+ usbd_delay_ms(sc->sc_udev, 6);
+
+ emdtv_gpio_ctl(sc, EMDTV_GPIO_ANALOG_ON, false);
+ emdtv_gpio_ctl(sc, EMDTV_GPIO_TS1_ON, true);
+ emdtv_gpio_ctl(sc, EMDTV_GPIO_TUNER1_ON, true);
+ emdtv_gpio_ctl(sc, EMDTV_GPIO_DEMOD1_RESET, true);
+ usbd_delay_ms(sc->sc_udev, 100);
+
+ daa.hw = &emdtv_dtv_if;
+ daa.priv = sc;
+ sc->sc_dtvdev = config_found_ia(sc->sc_dev, "dtvbus", &daa, dtv_print);
+}
+
+void
+emdtv_dtv_detach(struct emdtv_softc *sc, int flags)
+{
+ sc->sc_streaming = false;
+
+ if (sc->sc_dtvdev != NULL) {
+ config_detach(sc->sc_dtvdev, flags);
+ sc->sc_dtvdev = NULL;
+ }
+
+ if (sc->sc_xc3028)
+ xc3028_close(sc->sc_xc3028);
+ if (sc->sc_lg3303)
+ lg3303_close(sc->sc_lg3303);
+
+ if (sc->sc_isoc_pipe) {
+ usbd_abort_pipe(sc->sc_isoc_pipe);
+ usbd_close_pipe(sc->sc_isoc_pipe);
+ sc->sc_isoc_pipe = NULL;
+ }
+}
+
+static void
+emdtv_dtv_get_devinfo(void *priv, struct dvb_frontend_info *info)
+{
+ struct emdtv_softc *sc = priv;
+
+ memset(info, 0, sizeof(*info));
+ strlcpy(info->name, sc->sc_board->eb_name, sizeof(info->name));
+ info->type = FE_ATSC;
+ info->frequency_min = 54000000;
+ info->frequency_max = 858000000;
+ info->frequency_stepsize = 62500;
+ info->caps = FE_CAN_8VSB;
+}
+
+static int
+emdtv_dtv_open(void *priv, int flags)
+{
+ struct emdtv_softc *sc = priv;
+
+ if (sc->sc_dying)
+ return ENXIO;
+
+ switch (sc->sc_board->eb_tuner) {
+ case EMDTV_TUNER_XC3028L:
+ if (sc->sc_xc3028 == NULL) {
+ sc->sc_xc3028 = xc3028_open(sc->sc_dev,
+ &sc->sc_i2c, 0x61 << 1, emdtv_dtv_tuner_reset, sc,
+ XC3028L);
+ }
+ if (sc->sc_xc3028 == NULL) {
+ aprint_error_dev(sc->sc_dev, "couldn't open xc3028l\n");
+ return ENXIO;
+ }
+ break;
+ default:
+ aprint_error_dev(sc->sc_dev, "unsupported tuner (%d)\n",
+ sc->sc_board->eb_tuner);
+ return EIO;
+ }
+
+ switch (sc->sc_board->eb_demod) {
+ case EMDTV_DEMOD_LG3303:
+ if (sc->sc_lg3303 == NULL) {
+ sc->sc_lg3303 = lg3303_open(sc->sc_dev,
+ &sc->sc_i2c, 0x1c);
+ }
+ if (sc->sc_lg3303 == NULL) {
+ aprint_error_dev(sc->sc_dev, "couldn't open lg3303\n");
+ return ENXIO;
+ }
+ break;
+ default:
+ aprint_error_dev(sc->sc_dev, "unsupported demod (%d)\n",
+ sc->sc_board->eb_demod);
+ return EIO;
+ }
+
+ return 0;
+}
+
+static void
+emdtv_dtv_close(void *priv)
+{
+ return;
+}
+
+static int
+emdtv_dtv_set_tuner(void *priv, const struct dvb_frontend_parameters *params)
+{
+ struct emdtv_softc *sc = priv;
+ int error;
+
+ /* Setup demod */
+ error = ENXIO;
+ if (sc->sc_lg3303)
+ error = lg3303_set_modulation(sc->sc_lg3303,
+ params->u.vsb.modulation);
+ if (error)
+ return error;
+
+ /* Setup tuner */
+ error = ENXIO;
+ if (sc->sc_xc3028)
+ error = xc3028_tune_dtv(sc->sc_xc3028, params);
+
+ return error;
+}
+
+static fe_status_t
+emdtv_dtv_get_status(void *priv)
+{
+ struct emdtv_softc *sc = priv;
+
+ if (sc->sc_lg3303)
+ return lg3303_get_dtv_status(sc->sc_lg3303);
+
+ return 0;
+}
+
+uint16_t
+emdtv_dtv_get_signal_strength(void *priv)
+{
+ return 0;
+}
+
+uint16_t
+emdtv_dtv_get_snr(void *priv)
+{
+ return 0;
+}
+
+static int
+emdtv_dtv_start_transfer(void *priv)
+{
+ struct emdtv_softc *sc = priv;
+ int i, s;
+
+ s = splusb();
+
+ sc->sc_streaming = true;
+
+ aprint_debug_dev(sc->sc_dev, "allocating isoc xfers (pktsz %d)\n",
+ sc->sc_isoc_maxpacketsize);
+
+ for (i = 0; i < EMDTV_NXFERS; i++) {
+ sc->sc_ix[i].ix_xfer = usbd_alloc_xfer(sc->sc_udev);
+ sc->sc_ix[i].ix_buf = usbd_alloc_buffer(sc->sc_ix[i].ix_xfer,
+ sc->sc_isoc_buflen);
+ aprint_debug_dev(sc->sc_dev, " ix[%d] xfer %p buf %p\n",
+ i, sc->sc_ix[i].ix_xfer, sc->sc_ix[i].ix_buf);
+ }
+
+ aprint_debug_dev(sc->sc_dev, "starting isoc transactions\n");
+
+ emdtv_dtv_isoc_startall(sc);
+ splx(s);
+
+ return 0;
+}
+
+static int
+emdtv_dtv_stop_transfer(void *priv)
+{
+ struct emdtv_softc *sc = priv;
+ int i;
+
+ aprint_debug_dev(sc->sc_dev, "stopping stream\n");
+
+ sc->sc_streaming = false;
+
+ if (sc->sc_isoc_pipe != NULL)
+ usbd_abort_pipe(sc->sc_isoc_pipe);
+
+ for (i = 0; i < EMDTV_NXFERS; i++)
+ if (sc->sc_ix[i].ix_xfer) {
+ usbd_free_xfer(sc->sc_ix[i].ix_xfer);
+ sc->sc_ix[i].ix_xfer = NULL;
+ sc->sc_ix[i].ix_buf = NULL;
+ }
+
+ return 0;
+}
+
+static void
+emdtv_dtv_isoc_startall(struct emdtv_softc *sc)
+{
+ int i;
+
+ if (sc->sc_streaming == false || sc->sc_dying == true)
+ return;
+
+ for (i = 0; i < EMDTV_NXFERS; i += 2)
+ emdtv_dtv_isoc_start(sc, &sc->sc_ix[i]);
+}
+
+static int
+emdtv_dtv_isoc_start(struct emdtv_softc *sc, struct emdtv_isoc_xfer *ix)
+{
+ int i;
+
+ if (sc->sc_isoc_pipe == NULL)
+ return EIO;
+
+ for (i = 0; i < EMDTV_NFRAMES; i++)
+ ix->ix_frlengths[i] = sc->sc_isoc_maxpacketsize;
+
+ usbd_setup_isoc_xfer(ix->ix_xfer,
+ sc->sc_isoc_pipe,
+ ix,
+ ix->ix_frlengths,
+ EMDTV_NFRAMES,
+ USBD_NO_COPY | USBD_SHORT_XFER_OK,
+ emdtv_dtv_isoc);
+ usbd_transfer(ix->ix_xfer);
+
+ return 0;
+}
+
+static void
+emdtv_dtv_isoc(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status err)
+{
+ struct emdtv_isoc_xfer *ix = priv;
+ struct emdtv_softc *sc = ix->ix_sc;
+ struct dtv_payload payload;
+ usbd_pipe_handle isoc = sc->sc_isoc_pipe;
+ uint32_t len;
+ uint8_t *buf;
+ int i;
+
+ KASSERT(xfer == ix->ix_xfer);
+
+ if (sc->sc_dying)
+ return;
+
+ if (err) {
+ if (err == USBD_STALLED) {
+ usbd_clear_endpoint_stall_async(isoc);
+ goto resched;
+ }
+ return;
+ }
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
+
+ if (len == 0)
+ goto resched;
+
+ buf = usbd_get_buffer(xfer);
+ if (buf == NULL)
+ goto resched;
+
+ for (i = 0; i < EMDTV_NFRAMES; i++, buf += sc->sc_isoc_maxpacketsize) {
+ if (ix->ix_frlengths[i] == 0)
+ continue;
+ payload.data = buf;
+ payload.size = ix->ix_frlengths[i];
+ dtv_submit_payload(sc->sc_dtvdev, &payload);
+ }
+
+resched:
+ emdtv_dtv_isoc_start(sc, ix->ix_altix);
+}
+
+static int
+emdtv_dtv_tuner_reset(void *opaque)
+{
+ struct emdtv_softc *sc = opaque;
+ emdtv_gpio_ctl(sc, EMDTV_GPIO_TUNER1_RESET, true);
+ return 0;
+}
Index: src/sys/dev/usb/emdtv_i2c.c
diff -u /dev/null src/sys/dev/usb/emdtv_i2c.c:1.1
--- /dev/null Mon Jul 11 18:02:04 2011
+++ src/sys/dev/usb/emdtv_i2c.c Mon Jul 11 18:02:04 2011
@@ -0,0 +1,151 @@
+/* $NetBSD: emdtv_i2c.c,v 1.1 2011/07/11 18:02:04 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2008, 2010 Jared D. McNeill <[email protected]>
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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: emdtv_i2c.c,v 1.1 2011/07/11 18:02:04 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+
+#include <dev/i2c/i2cvar.h>
+
+#include <dev/usb/emdtvvar.h>
+#include <dev/usb/emdtvreg.h>
+
+static int emdtv_i2c_acquire_bus(void *, int);
+static void emdtv_i2c_release_bus(void *, int);
+static int emdtv_i2c_exec(void *, i2c_op_t, i2c_addr_t,
+ const void *, size_t, void *, size_t, int);
+
+static int emdtv_i2c_check(struct emdtv_softc *, i2c_addr_t);
+static int emdtv_i2c_recv(struct emdtv_softc *, i2c_addr_t,
+ uint8_t *, size_t);
+static int emdtv_i2c_send(struct emdtv_softc *, i2c_addr_t,
+ const uint8_t *, size_t, bool);
+
+int
+emdtv_i2c_attach(struct emdtv_softc *sc)
+{
+ mutex_init(&sc->sc_i2c_lock, MUTEX_DEFAULT, IPL_VM);
+ sc->sc_i2c.ic_cookie = sc;
+ sc->sc_i2c.ic_acquire_bus = emdtv_i2c_acquire_bus;
+ sc->sc_i2c.ic_release_bus = emdtv_i2c_release_bus;
+ sc->sc_i2c.ic_exec = emdtv_i2c_exec;
+
+ return 0;
+}
+
+int
+emdtv_i2c_detach(struct emdtv_softc *sc, int flags)
+{
+ mutex_destroy(&sc->sc_i2c_lock);
+
+ return 0;
+}
+
+static int
+emdtv_i2c_acquire_bus(void *opaque, int flags)
+{
+ struct emdtv_softc *sc = opaque;
+
+ mutex_enter(&sc->sc_i2c_lock);
+
+ return 0;
+}
+
+static int
+emdtv_i2c_exec(void *opaque, i2c_op_t op, i2c_addr_t addr,
+ const void *cmd, size_t cmdlen, void *vbuf, size_t buflen, int flags)
+{
+ struct emdtv_softc *sc = opaque;
+ int error;
+
+ if (I2C_OP_READ_P(op)) {
+ if (buflen == 0)
+ error = emdtv_i2c_check(sc, addr);
+ else
+ error = emdtv_i2c_recv(sc, addr, vbuf, buflen);
+ } else {
+ error = emdtv_i2c_send(sc, addr, cmd, cmdlen,
+ I2C_OP_STOP_P(op));
+ error = 0;
+ }
+
+ return error;
+}
+
+static void
+emdtv_i2c_release_bus(void *opaque, int flags)
+{
+ struct emdtv_softc *sc = opaque;
+
+ mutex_exit(&sc->sc_i2c_lock);
+}
+
+static int
+emdtv_i2c_check(struct emdtv_softc *sc, i2c_addr_t addr)
+{
+ emdtv_read_1(sc, EM28XX_UR_I2C, addr);
+ if (emdtv_read_1(sc, UR_GET_STATUS, EM28XX_REG_I2C_STATUS) != 0) {
+ device_printf(sc->sc_dev, "%s failed\n", __func__);
+ return ENXIO;
+ }
+ return 0;
+}
+
+static int
+emdtv_i2c_recv(struct emdtv_softc *sc, i2c_addr_t addr, uint8_t *datap,
+ size_t len)
+{
+ emdtv_read_multi_1(sc, EM28XX_UR_I2C, addr, datap, len);
+ if (emdtv_read_1(sc, UR_GET_STATUS, EM28XX_REG_I2C_STATUS) != 0) {
+ device_printf(sc->sc_dev, "%s failed\n", __func__);
+ return ENXIO;
+ }
+ return 0;
+}
+
+static int
+emdtv_i2c_send(struct emdtv_softc *sc, i2c_addr_t addr, const uint8_t *datap,
+ size_t len, bool stop)
+{
+ int off = (stop == false ? 1 : 0);
+ emdtv_write_multi_1(sc, EM28XX_UR_I2C + off, addr, datap, len);
+ if (emdtv_read_1(sc, UR_GET_STATUS, EM28XX_REG_I2C_STATUS) != 0) {
+ device_printf(sc->sc_dev, "%s failed\n", __func__);
+ return ENXIO;
+ }
+ return 0;
+}
Index: src/sys/dev/usb/emdtv_ir.c
diff -u /dev/null src/sys/dev/usb/emdtv_ir.c:1.1
--- /dev/null Mon Jul 11 18:02:04 2011
+++ src/sys/dev/usb/emdtv_ir.c Mon Jul 11 18:02:04 2011
@@ -0,0 +1,239 @@
+/* $NetBSD: emdtv_ir.c,v 1.1 2011/07/11 18:02:04 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2008 Jared D. McNeill <[email protected]>
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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: emdtv_ir.c,v 1.1 2011/07/11 18:02:04 jmcneill Exp $");
+
+#include <sys/select.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdevs.h>
+
+#include <dev/usb/emdtvvar.h>
+#include <dev/usb/emdtvreg.h>
+
+#include <dev/ir/ir.h>
+#include <dev/ir/cirio.h>
+#include <dev/ir/cirvar.h>
+
+static void emdtv_ir_intr(usbd_xfer_handle, usbd_private_handle,
+ usbd_status);
+static void emdtv_ir_worker(struct work *, void *);
+
+static int emdtv_ir_open(void *, int, int, struct proc *);
+static int emdtv_ir_close(void *, int, int, struct proc *);
+static int emdtv_ir_read(void *, struct uio *, int);
+static int emdtv_ir_write(void *, struct uio *, int);
+static int emdtv_ir_setparams(void *, struct cir_params *);
+
+static const struct cir_methods emdtv_ir_methods = {
+ .im_open = emdtv_ir_open,
+ .im_close = emdtv_ir_close,
+ .im_read = emdtv_ir_read,
+ .im_write = emdtv_ir_write,
+ .im_setparams = emdtv_ir_setparams,
+};
+
+void
+emdtv_ir_attach(struct emdtv_softc *sc)
+{
+ struct ir_attach_args ia;
+ usb_endpoint_descriptor_t *ed;
+ usbd_status status;
+ int err;
+
+ ed = usbd_interface2endpoint_descriptor(sc->sc_iface, 0);
+ if (ed == NULL)
+ return;
+
+ status = usbd_open_pipe_intr(sc->sc_iface, ed->bEndpointAddress,
+ USBD_EXCLUSIVE_USE, &sc->sc_intr_pipe, sc, &sc->sc_intr_buf, 1,
+ emdtv_ir_intr, USBD_DEFAULT_INTERVAL);
+ if (status != USBD_NORMAL_COMPLETION) {
+ aprint_error_dev(sc->sc_dev, "couldn't open intr pipe: %s\n",
+ usbd_errstr(status));
+ return;
+ }
+
+ mutex_init(&sc->sc_ir_mutex, MUTEX_DEFAULT, IPL_VM);
+
+ err = workqueue_create(&sc->sc_ir_wq, "emdtvir",
+ emdtv_ir_worker, sc, PRI_NONE, IPL_VM, 0);
+ if (err)
+ aprint_error_dev(sc->sc_dev, "couldn't create workqueue: %d\n",
+ err);
+
+ ia.ia_type = IR_TYPE_CIR;
+ ia.ia_methods = &emdtv_ir_methods;
+ ia.ia_handle = sc;
+
+ sc->sc_cirdev =
+ config_found_ia(sc->sc_dev, "irbus", &ia, ir_print);
+}
+
+void
+emdtv_ir_detach(struct emdtv_softc *sc, int flags)
+{
+ if (sc->sc_ir_wq != NULL)
+ workqueue_destroy(sc->sc_ir_wq);
+
+ if (sc->sc_intr_pipe != NULL) {
+ usbd_abort_pipe(sc->sc_intr_pipe);
+ usbd_close_pipe(sc->sc_intr_pipe);
+ sc->sc_intr_pipe = NULL;
+ }
+
+ mutex_enter(&sc->sc_ir_mutex);
+ mutex_exit(&sc->sc_ir_mutex);
+ mutex_destroy(&sc->sc_ir_mutex);
+
+ if (sc->sc_cirdev != NULL)
+ config_detach(sc->sc_cirdev, flags);
+}
+
+static void
+emdtv_ir_intr(usbd_xfer_handle xfer, usbd_private_handle priv,
+ usbd_status status)
+{
+ struct emdtv_softc *sc = priv;
+ uint32_t len;
+
+ usbd_get_xfer_status(xfer, NULL, NULL, &len, NULL);
+ if (status == USBD_CANCELLED)
+ return;
+
+ if (sc->sc_ir_wq)
+ workqueue_enqueue(sc->sc_ir_wq, &sc->sc_ir_work, NULL);
+}
+
+static void
+emdtv_ir_worker(struct work *wk, void *opaque)
+{
+ struct emdtv_softc *sc = opaque;
+ struct cir_softc *csc;
+ uint8_t evt[3];
+ int pos;
+
+ if (sc->sc_cirdev == NULL || sc->sc_dying == true ||
+ sc->sc_ir_open == false)
+ return;
+
+ emdtv_read_multi_1(sc, UR_GET_STATUS, EM28XX_REG_IR, evt, sizeof(evt));
+
+ csc = device_private(sc->sc_cirdev);
+
+ mutex_enter(&sc->sc_ir_mutex);
+ pos = (sc->sc_ir_ptr + sc->sc_ir_cnt) % EMDTV_CIR_BUFLEN;
+ memcpy(&sc->sc_ir_queue[pos], evt, sizeof(evt));
+ if (sc->sc_ir_cnt < EMDTV_CIR_BUFLEN - 1) {
+ ++sc->sc_ir_cnt;
+ ++csc->sc_rdframes;
+ }
+ selnotify(&csc->sc_rdsel, 0, 1);
+ mutex_exit(&sc->sc_ir_mutex);
+}
+
+/*
+ * cir(4)
+ */
+static int
+emdtv_ir_open(void *opaque, int flag, int mode, struct proc *p)
+{
+ struct emdtv_softc *sc = opaque;
+
+ if (sc->sc_ir_open == true)
+ return EBUSY;
+
+ sc->sc_ir_cnt = 0;
+ sc->sc_ir_ptr = EMDTV_CIR_BUFLEN - 1;
+ sc->sc_ir_open = true;
+
+ return 0;
+}
+
+static int
+emdtv_ir_close(void *opaque, int flag, int mode, struct proc *p)
+{
+ struct emdtv_softc *sc = opaque;
+
+ sc->sc_ir_open = false;
+
+ return 0;
+}
+
+static int
+emdtv_ir_read(void *opaque, struct uio *uio, int flag)
+{
+ struct emdtv_softc *sc = opaque;
+ struct cir_softc *csc;
+ int error = 0;
+
+ if (uio->uio_resid != 3) /* 3 byte protocol */
+ return EINVAL;
+
+ if (sc->sc_dying)
+ return EIO;
+
+ csc = device_private(sc->sc_cirdev);
+
+ mutex_enter(&sc->sc_ir_mutex);
+ if (sc->sc_ir_cnt == 0)
+ goto out;
+
+ error = uiomove(&sc->sc_ir_queue[sc->sc_ir_ptr], 3, uio);
+ sc->sc_ir_ptr++;
+ if (sc->sc_ir_ptr == EMDTV_CIR_BUFLEN)
+ sc->sc_ir_ptr = 0;
+ --sc->sc_ir_cnt;
+ --csc->sc_rdframes;
+ KASSERT(sc->sc_ir_cnt >= 0);
+
+out:
+ mutex_exit(&sc->sc_ir_mutex);
+ return error;
+}
+
+static int
+emdtv_ir_write(void *opaque, struct uio *uio, int flag)
+{
+ return EINVAL;
+}
+
+static int
+emdtv_ir_setparams(void *opaque, struct cir_params *cp)
+{
+ return 0;
+}
Index: src/sys/dev/usb/emdtvreg.h
diff -u /dev/null src/sys/dev/usb/emdtvreg.h:1.1
--- /dev/null Mon Jul 11 18:02:04 2011
+++ src/sys/dev/usb/emdtvreg.h Mon Jul 11 18:02:04 2011
@@ -0,0 +1,77 @@
+/* $NetBSD: emdtvreg.h,v 1.1 2011/07/11 18:02:04 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2008 Jared D. McNeill <[email protected]>
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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_USB_EMDTVREG_H
+#define _DEV_USB_EMDTVREG_H
+
+struct emdtv_eeprom {
+ uint32_t id; /* 0x9567eb1a */
+ uint16_t vendor;
+ uint16_t product;
+ uint16_t chipcfg;
+ uint16_t boardcfg;
+ uint16_t string[3];
+ uint8_t stringidx;
+} __attribute__((__packed__));
+
+#define EM28XX_I2C_ADDR_EEPROM 0xa0
+
+#define EM28XX_UR_I2C 0x02
+
+#define EM28XX_REG_I2C_STATUS 0x05
+
+#define EM28XX_I2C_CLK_REG 0x06
+#define EM28XX_XCLK_REG 0x0f
+
+#define EM28XX_REG_IR 0x45
+#define EM28XX_IR_RECORD 0x00
+#define EM28XX_IR_PAUSEPLAY 0x01
+#define EM28XX_IR_STOP 0x02
+#define EM28XX_IR_POWER 0x03
+#define EM28XX_IR_PREV 0x04
+#define EM28XX_IR_REWIND 0x05
+#define EM28XX_IR_FORWARD 0x06
+#define EM28XX_IR_NEXT 0x07
+#define EM28XX_IR_EPG 0x08
+#define EM28XX_IR_HOME 0x09
+#define EM28XX_IR_DVDMENU 0x0a
+#define EM28XX_IR_CHANNEL_UP 0x0b
+#define EM28XX_IR_BACK 0x0c
+#define EM28XX_IR_UP 0x0d
+#define EM28XX_IR_INFO 0x0e
+#define EM28XX_IR_CHANNEL_DOWN 0x0f
+#define EM28XX_IR_LEFT 0x10
+#define EM28XX_IR_OK 0x11
+#define EM28XX_IR_RIGHT 0x12
+#define EM28XX_IR_VOLUME_UP 0x13
+#define EM28XX_IR_LAST 0x14
+#define EM28XX_IR_DOWN 0x15
+#define EM28XX_IR_VOLUME_MUTE 0x16
+#define EM28XX_IR_VOLUME_DOWN 0x17
+
+#endif /* !_DEV_USB_EMDTVREG_H */
Index: src/sys/dev/usb/emdtvvar.h
diff -u /dev/null src/sys/dev/usb/emdtvvar.h:1.1
--- /dev/null Mon Jul 11 18:02:04 2011
+++ src/sys/dev/usb/emdtvvar.h Mon Jul 11 18:02:04 2011
@@ -0,0 +1,127 @@
+/* $NetBSD: emdtvvar.h,v 1.1 2011/07/11 18:02:04 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2008 Jared D. McNeill <[email protected]>
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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_USB_EMDTVVAR_H
+#define _DEV_USB_EMDTVVAR_H
+
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/workqueue.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+
+#include <dev/usb/emdtv_board.h>
+
+#include <dev/i2c/i2cvar.h>
+#include <dev/dtv/dtvif.h>
+
+#include <dev/i2c/lg3303var.h>
+#include <dev/i2c/xc3028var.h>
+
+#define EMDTV_EEPROM_LEN 256
+
+#define EMDTV_NXFERS 6
+#define EMDTV_NFRAMES 64
+
+#define EMDTV_CIR_BUFLEN 32
+
+struct emdtv_softc;
+
+struct emdtv_isoc_xfer {
+ struct emdtv_softc *ix_sc;
+ usbd_xfer_handle ix_xfer;
+ uint8_t *ix_buf;
+ uint16_t ix_frlengths[EMDTV_NFRAMES];
+ struct emdtv_isoc_xfer *ix_altix;
+};
+
+struct emdtv_softc {
+ device_t sc_dev;
+ usbd_device_handle sc_udev;
+
+ device_t sc_cirdev;
+ device_t sc_dtvdev;
+
+ uint16_t sc_vendor, sc_product;
+
+ const struct emdtv_board *sc_board;
+
+ struct lg3303 *sc_lg3303;
+ struct xc3028 *sc_xc3028;
+
+ struct i2c_controller sc_i2c;
+ kmutex_t sc_i2c_lock;
+
+ uint8_t sc_eeprom[EMDTV_EEPROM_LEN];
+
+ usbd_interface_handle sc_iface;
+
+ usbd_pipe_handle sc_isoc_pipe;
+ int sc_isoc_buflen;
+ int sc_isoc_maxpacketsize;
+ struct emdtv_isoc_xfer sc_ix[EMDTV_NXFERS];
+
+ usbd_pipe_handle sc_intr_pipe;
+ uint8_t sc_intr_buf;
+ struct workqueue *sc_ir_wq;
+ struct work sc_ir_work;
+ uint8_t sc_ir_keyid;
+
+ kmutex_t sc_ir_mutex;
+ uint8_t sc_ir_queue[EMDTV_CIR_BUFLEN][3];
+ int sc_ir_cnt;
+ int sc_ir_ptr;
+ bool sc_ir_open;
+
+ uint32_t sc_frequency;
+
+ bool sc_streaming;
+
+ bool sc_dying;
+};
+
+void emdtv_dtv_attach(struct emdtv_softc *);
+void emdtv_dtv_detach(struct emdtv_softc *, int);
+void emdtv_ir_attach(struct emdtv_softc *);
+void emdtv_ir_detach(struct emdtv_softc *, int);
+int emdtv_i2c_attach(struct emdtv_softc *);
+int emdtv_i2c_detach(struct emdtv_softc *, int);
+
+uint8_t emdtv_read_1(struct emdtv_softc *, uint8_t, uint16_t);
+void emdtv_read_multi_1(struct emdtv_softc *, uint8_t, uint16_t,
+ uint8_t *, uint16_t);
+void emdtv_write_1(struct emdtv_softc *, uint8_t, uint16_t, uint8_t);
+void emdtv_write_multi_1(struct emdtv_softc *, uint8_t, uint16_t,
+ const uint8_t *, uint16_t);
+
+bool emdtv_gpio_ctl(struct emdtv_softc *, emdtv_gpio_reg_t, bool);
+
+bool emdtv_i2c_transfer(void *, struct dvb_i2c_msg *, int);
+
+#endif /* !_DEV_USB_EMDTVVAR_H */