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 */

Reply via email to