Module Name:    src
Committed By:   jmcneill
Date:           Sat Jul  9 15:00:45 UTC 2011

Modified Files:
        src/sys/dev/i2c: au8522.c au8522var.h xc5k.c xc5kreg.h xc5kvar.h
        src/sys/dev/usb: auvitek.c auvitek_board.c auvitek_i2c.c
            auvitek_video.c auvitekvar.h
Added Files:
        src/sys/dev/i2c: au8522mod.h au8522mod_8vsb.h au8522mod_qam256.h
            au8522mod_qam64.h
        src/sys/dev/usb: auvitek_dtv.c

Log Message:
add digital capture support


To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 src/sys/dev/i2c/au8522.c
cvs rdiff -u -r0 -r1.1 src/sys/dev/i2c/au8522mod.h \
    src/sys/dev/i2c/au8522mod_8vsb.h src/sys/dev/i2c/au8522mod_qam256.h \
    src/sys/dev/i2c/au8522mod_qam64.h
cvs rdiff -u -r1.2 -r1.3 src/sys/dev/i2c/au8522var.h src/sys/dev/i2c/xc5k.c
cvs rdiff -u -r1.1 -r1.2 src/sys/dev/i2c/xc5kreg.h src/sys/dev/i2c/xc5kvar.h
cvs rdiff -u -r1.3 -r1.4 src/sys/dev/usb/auvitek.c \
    src/sys/dev/usb/auvitek_video.c
cvs rdiff -u -r1.2 -r1.3 src/sys/dev/usb/auvitek_board.c \
    src/sys/dev/usb/auvitekvar.h
cvs rdiff -u -r0 -r1.1 src/sys/dev/usb/auvitek_dtv.c
cvs rdiff -u -r1.1 -r1.2 src/sys/dev/usb/auvitek_i2c.c

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/i2c/au8522.c
diff -u src/sys/dev/i2c/au8522.c:1.3 src/sys/dev/i2c/au8522.c:1.4
--- src/sys/dev/i2c/au8522.c:1.3	Thu May 26 23:42:05 2011
+++ src/sys/dev/i2c/au8522.c	Sat Jul  9 15:00:43 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: au8522.c,v 1.3 2011/05/26 23:42:05 jmcneill Exp $ */
+/* $NetBSD: au8522.c,v 1.4 2011/07/09 15:00:43 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2010 Jared D. McNeill <jmcne...@invisible.ca>
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: au8522.c,v 1.3 2011/05/26 23:42:05 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: au8522.c,v 1.4 2011/07/09 15:00:43 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -41,10 +41,13 @@
 #include <sys/kmem.h>
 #include <sys/module.h>
 
+#include <dev/dtv/dtvio.h>
+
 #include <dev/i2c/i2cvar.h>
 
 #include <dev/i2c/au8522reg.h>
 #include <dev/i2c/au8522var.h>
+#include <dev/i2c/au8522mod.h>
 
 static int	au8522_reset(struct au8522 *);
 static int	au8522_read_1(struct au8522 *, uint16_t, uint8_t *);
@@ -187,8 +190,31 @@
 	return 0;
 }
 
+static int
+au8522_set_if(struct au8522 *au)
+{
+	uint8_t ifinit[3];
+	unsigned int n;
+
+	switch (au->if_freq) {
+	case 6000000:	/* 6MHz */
+		ifinit[0] = 0xfb;
+		ifinit[1] = 0x8e;
+		ifinit[2] = 0x39;
+		break;
+	default:
+		aprint_error_dev(au->parent, "au8522: unsupported if freq %dHz\n", au->if_freq);
+		return EINVAL;
+	}
+
+	for (n = 0; n < __arraycount(ifinit); n++)
+		au8522_write_1(au, 0x80b5 + n, ifinit[n]);
+
+	return 0;
+}
+
 struct au8522 *
-au8522_open(device_t parent, i2c_tag_t i2c, i2c_addr_t addr)
+au8522_open(device_t parent, i2c_tag_t i2c, i2c_addr_t addr, unsigned int if_freq)
 {
 	struct au8522 *au;
 
@@ -198,6 +224,8 @@
 	au->parent = parent;
 	au->i2c = i2c;
 	au->i2c_addr = addr;
+	au->current_modulation = -1;
+	au->if_freq = if_freq;
 
 	if (au8522_reset(au))
 		goto failed;
@@ -271,6 +299,90 @@
 	}
 }
 
+int
+au8522_set_modulation(struct au8522 *au, fe_modulation_t modulation)
+{
+	const struct au8522_modulation_table *modtab = NULL;
+	size_t modtablen;
+	unsigned int n;
+
+	switch (modulation) {
+	case VSB_8:
+		modtab = au8522_modulation_8vsb;
+		modtablen = __arraycount(au8522_modulation_8vsb);
+		break;
+	case QAM_64:
+		modtab = au8522_modulation_qam64;
+		modtablen = __arraycount(au8522_modulation_qam64);
+		break;
+	case QAM_256:
+		modtab = au8522_modulation_qam256;
+		modtablen = __arraycount(au8522_modulation_qam256);
+		break;
+	default:
+		return EINVAL;
+	}
+
+	for (n = 0; n < modtablen; n++)
+		au8522_write_1(au, modtab[n].reg, modtab[n].val);
+
+	au8522_set_if(au);
+
+	au->current_modulation = modulation;
+
+	return 0;
+}
+
+void
+au8522_set_gate(struct au8522 *au, bool onoff)
+{
+	au8522_write_1(au, AU8522_REG_TUNERCTL, onoff ? AU8522_TUNERCTL_EN : 0);
+}
+
+fe_status_t
+au8522_get_dtv_status(struct au8522 *au)
+{
+	fe_status_t status = 0;
+	uint8_t val;
+
+	//printf("%s: current_modulation = %d\n", __func__,
+	//    au->current_modulation);
+
+	switch (au->current_modulation) {
+	case VSB_8:
+		if (au8522_read_1(au, 0x4088, &val))
+			return 0;
+		if ((val & 0x03) == 0x03) {
+			status |= FE_HAS_SIGNAL;
+			status |= FE_HAS_CARRIER;
+			status |= FE_HAS_VITERBI;
+		}
+		break;
+	case QAM_64:
+	case QAM_256:
+		if (au8522_read_1(au, 0x4541, &val))
+			return 0;
+		if (val & 0x80) {
+			status |= FE_HAS_VITERBI;
+		}
+		if (val & 0x20) {
+			status |= FE_HAS_SIGNAL;
+			status |= FE_HAS_CARRIER;
+		}
+		break;
+	default:
+		break;
+	}
+
+	if (status & FE_HAS_VITERBI) {
+		status |= FE_HAS_SYNC;
+		status |= FE_HAS_LOCK;
+	}
+
+	//printf("%s: status = 0x%x\n", __func__, status);
+	return status;
+}
+
 MODULE(MODULE_CLASS_DRIVER, au8522, NULL);
 
 static int

Index: src/sys/dev/i2c/au8522var.h
diff -u src/sys/dev/i2c/au8522var.h:1.2 src/sys/dev/i2c/au8522var.h:1.3
--- src/sys/dev/i2c/au8522var.h:1.2	Thu May 26 23:42:05 2011
+++ src/sys/dev/i2c/au8522var.h	Sat Jul  9 15:00:44 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: au8522var.h,v 1.2 2011/05/26 23:42:05 jmcneill Exp $ */
+/* $NetBSD: au8522var.h,v 1.3 2011/07/09 15:00:44 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2010 Jared D. McNeill <jmcne...@invisible.ca>
@@ -30,11 +30,16 @@
 #define _AU8522VAR_H
 
 #include <dev/i2c/i2cvar.h>
+#include <dev/dtv/dtvio.h>
 
 struct au8522 {
 	device_t	parent;
 	i2c_tag_t	i2c;
 	i2c_addr_t	i2c_addr;
+
+	unsigned int	if_freq;
+
+	fe_modulation_t	current_modulation;
 };
 
 typedef enum {
@@ -51,7 +56,7 @@
 	AU8522_AINPUT_SIF,
 } au8522_ainput_t;
 
-struct au8522 *	au8522_open(device_t, i2c_tag_t, i2c_addr_t);
+struct au8522 *	au8522_open(device_t, i2c_tag_t, i2c_addr_t, unsigned int);
 void		au8522_close(struct au8522 *);
 
 void		au8522_enable(struct au8522 *, bool);
@@ -59,5 +64,8 @@
 				 au8522_vinput_t, au8522_ainput_t);
 int		au8522_get_signal(struct au8522 *);
 void		au8522_set_audio(struct au8522 *, bool);
+int		au8522_set_modulation(struct au8522 *, fe_modulation_t);
+void		au8522_set_gate(struct au8522 *, bool);
+fe_status_t	au8522_get_dtv_status(struct au8522 *);
 
 #endif /* !_AU8522VAR_H */
Index: src/sys/dev/i2c/xc5k.c
diff -u src/sys/dev/i2c/xc5k.c:1.2 src/sys/dev/i2c/xc5k.c:1.3
--- src/sys/dev/i2c/xc5k.c:1.2	Tue Dec 28 00:11:50 2010
+++ src/sys/dev/i2c/xc5k.c	Sat Jul  9 15:00:44 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: xc5k.c,v 1.2 2010/12/28 00:11:50 jmcneill Exp $ */
+/* $NetBSD: xc5k.c,v 1.3 2011/07/09 15:00:44 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2010 Jared D. McNeill <jmcne...@invisible.ca>
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: xc5k.c,v 1.2 2010/12/28 00:11:50 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: xc5k.c,v 1.3 2011/07/09 15:00:44 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -51,6 +51,9 @@
 #define	XC5K_FIRMWARE_DRVNAME	"xc5k"
 #define	XC5K_FIRMWARE_IMGNAME	"dvb-fe-xc5000-1.6.114.fw"
 
+#define	XC5K_FREQ_MIN		1000000
+#define	XC5K_FREQ_MAX		1023000000
+
 static kmutex_t xc5k_firmware_lock;
 
 static int	xc5k_reset(struct xc5k *);
@@ -126,7 +129,7 @@
 xc5k_firmware_open(struct xc5k *xc)
 {
 	firmware_handle_t fwh;
-	uint16_t product_id;
+	uint16_t product_id, xcversion, xcbuild;
 	uint8_t *fw = NULL;
 	size_t fwlen;
 	int error;
@@ -156,6 +159,18 @@
 	aprint_normal_dev(xc->parent, "xc5k: loading firmware '%s/%s'\n",
 	    XC5K_FIRMWARE_DRVNAME, XC5K_FIRMWARE_IMGNAME);
 	error = xc5k_firmware_upload(xc, fw, fwlen);
+	if (!error) {
+		xc5k_read_2(xc, XC5K_REG_VERSION, &xcversion);
+		xc5k_read_2(xc, XC5K_REG_BUILD, &xcbuild);
+		if (!error)
+			aprint_normal_dev(xc->parent,
+			    "xc5k: hw %d.%d, fw %d.%d.%d\n",
+			    (xcversion >> 12) & 0xf,
+			    (xcversion >> 8) & 0xf,
+			    (xcversion >> 4) & 0xf,
+			    xcversion & 0xf,
+			    xcbuild);
+	}
 
 done:
 	if (fw)
@@ -228,7 +243,8 @@
 
 struct xc5k *
 xc5k_open(device_t parent, i2c_tag_t i2c, i2c_addr_t addr,
-    xc5k_reset_cb reset, void *reset_priv)
+    xc5k_reset_cb reset, void *reset_priv, unsigned int if_freq,
+    fe_type_t fe_type)
 {
 	struct xc5k *xc;
 	uint16_t product_id;
@@ -241,6 +257,8 @@
 	xc->i2c_addr = addr;
 	xc->reset = reset;
 	xc->reset_priv = reset_priv;
+	xc->if_freq = if_freq;
+	xc->fe_type = fe_type;
 
 	if (xc5k_read_2(xc, XC5K_REG_PRODUCT_ID, &product_id))
 		goto failed;
@@ -275,7 +293,7 @@
 }
 
 int
-xc5k_tune(struct xc5k *xc, struct xc5k_params *params)
+xc5k_tune_video(struct xc5k *xc, struct xc5k_params *params)
 {
 	uint16_t amode, vmode;
 	uint16_t lock, freq;
@@ -298,9 +316,11 @@
 		return EIO;
 	if (xc5k_write_2(xc, XC5K_REG_AUDIO_MODE, amode))
 		return EIO;
+	if (xc5k_write_2(xc, XC5K_REG_OUTAMP, XC5K_OUTAMP_ANALOG))
+		return EIO;
 	freq = (params->frequency * 62500) / 15625;
 #ifdef XC5K_DEBUG
-	printf("xc5k_tune: frequency=%u (%u Hz)\n", params->frequency,
+	printf("xc5k_tune_video: frequency=%u (%u Hz)\n", params->frequency,
 	    params->frequency * 62500);
 	printf("           freq=%u\n", freq);
 #endif
@@ -312,7 +332,7 @@
 		if (xc5k_read_2(xc, XC5K_REG_LOCK, &lock))
 			return EIO;
 #ifdef XC5K_DEBUG
-		printf("xc5k_tune: lock=0x%04x\n", lock);
+		printf("xc5k_tune_video: lock=0x%04x\n", lock);
 #endif
 		if (lock == 1)
 			break;
@@ -322,6 +342,98 @@
 	return 0;
 }
 
+int
+xc5k_tune_dtv(struct xc5k *xc, const struct dvb_frontend_parameters *params)
+{
+	uint16_t amode, vmode;
+	uint32_t freq, ifout;
+	int signal_source;
+	fe_modulation_t modulation;
+
+	if (xc->fe_type == FE_ATSC)
+		modulation = params->u.vsb.modulation;
+	else if (xc->fe_type == FE_QAM)
+		modulation = params->u.qam.modulation;
+	else
+		return EINVAL;
+
+	switch (modulation) {
+	case VSB_8:
+	case VSB_16:
+		signal_source = XC5K_SIGNAL_SOURCE_AIR;
+		switch (xc->fe_type) {
+		case FE_ATSC:
+			amode = XC5K_AUDIO_MODE_DTV6;
+			vmode = XC5K_VIDEO_MODE_DTV6;
+			freq = params->frequency - 1750000;
+			break;
+		default:
+			return EINVAL;
+		}
+		break;
+	case QAM_16:
+	case QAM_32:
+	case QAM_64:
+	case QAM_128:
+	case QAM_256:
+		signal_source = XC5K_SIGNAL_SOURCE_CABLE;
+		switch (xc->fe_type) {
+		case FE_ATSC:
+			amode = XC5K_AUDIO_MODE_DTV6;
+			vmode = XC5K_VIDEO_MODE_DTV6;
+			freq = params->frequency - 1750000;
+			break;
+		case FE_QAM:
+			amode = XC5K_AUDIO_MODE_DTV78;
+			vmode = XC5K_VIDEO_MODE_DTV78;
+			freq = params->frequency - 2750000;
+			break;
+		default:
+			return EINVAL;
+		}
+		break;
+	default:
+		return EINVAL;
+	}
+
+	if (freq > XC5K_FREQ_MAX || freq < XC5K_FREQ_MIN)
+		return ERANGE;
+
+	if (xc5k_write_2(xc, XC5K_REG_SIGNAL_SOURCE, signal_source))
+		return EIO;
+	if (xc5k_write_2(xc, XC5K_REG_VIDEO_MODE, vmode))
+		return EIO;
+	if (xc5k_write_2(xc, XC5K_REG_AUDIO_MODE, amode))
+		return EIO;
+	ifout = ((xc->if_freq / 1000) * 1024) / 1000;
+	if (xc5k_write_2(xc, XC5K_REG_IF_OUT, ifout))
+		return EIO;
+	if (xc5k_write_2(xc, XC5K_REG_OUTAMP, XC5K_OUTAMP_DIGITAL))
+		return EIO;
+	freq = (uint16_t)(freq / 15625);
+	if (xc5k_write_2(xc, XC5K_REG_FINER_FREQ, freq))
+		return EIO;
+
+	return 0;
+}
+
+fe_status_t
+xc5k_get_status(struct xc5k *xc)
+{
+	uint16_t lock_status;
+	fe_status_t festatus = 0;
+
+	if (xc5k_read_2(xc, XC5K_REG_LOCK, &lock_status))
+		return 0;
+	if (lock_status & XC5K_LOCK_LOCKED) {
+		festatus |= FE_HAS_LOCK;
+		if ((lock_status & XC5K_LOCK_NOSIGNAL) == 0)
+			festatus |= FE_HAS_SIGNAL;
+	}
+
+	return festatus;
+}
+
 MODULE(MODULE_CLASS_DRIVER, xc5k, NULL);
 
 static int
@@ -329,7 +441,7 @@
 {
 	switch (cmd) {
 	case MODULE_CMD_INIT:
-		mutex_init(&xc5k_firmware_lock, MUTEX_DEFAULT, IPL_VM);
+		mutex_init(&xc5k_firmware_lock, MUTEX_DEFAULT, IPL_NONE);
 		return 0;
 	case MODULE_CMD_FINI:
 		mutex_destroy(&xc5k_firmware_lock);

Index: src/sys/dev/i2c/xc5kreg.h
diff -u src/sys/dev/i2c/xc5kreg.h:1.1 src/sys/dev/i2c/xc5kreg.h:1.2
--- src/sys/dev/i2c/xc5kreg.h:1.1	Mon Dec 27 15:42:11 2010
+++ src/sys/dev/i2c/xc5kreg.h	Sat Jul  9 15:00:44 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: xc5kreg.h,v 1.1 2010/12/27 15:42:11 jmcneill Exp $ */
+/* $NetBSD: xc5kreg.h,v 1.2 2011/07/09 15:00:44 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2010 Jared D. McNeill <jmcne...@invisible.ca>
@@ -33,13 +33,24 @@
 #define	XC5K_REG_INIT		0x00
 #define	XC5K_REG_VIDEO_MODE	0x01
 #define	 XC5K_VIDEO_MODE_BTSC		0x8028
+#define	 XC5K_VIDEO_MODE_DTV6		0x8002
+#define	 XC5K_VIDEO_MODE_DTV7		0x8007
+#define	 XC5K_VIDEO_MODE_DTV8		0x800b
+#define	 XC5K_VIDEO_MODE_DTV78		0x801b
 #define	XC5K_REG_AUDIO_MODE	0x02
 #define	 XC5K_AUDIO_MODE_BTSC		0x0400
+#define	 XC5K_AUDIO_MODE_DTV6		0x00c0
+#define	 XC5K_AUDIO_MODE_DTV7		0x00c0
+#define	 XC5K_AUDIO_MODE_DTV8		0x00c0
+#define	 XC5K_AUDIO_MODE_DTV78		0x00c0
 #define	XC5K_REG_RF_FREQ	0x03
 #define	XC5K_REG_D_CODE		0x04
 #define	XC5K_REG_IF_OUT		0x05
 #define	XC5K_REG_SEEK_MODE	0x07
 #define	XC5K_REG_POWER_DOWN	0x0a
+#define	XC5K_REG_OUTAMP		0x0b
+#define	 XC5K_OUTAMP_DIGITAL		0x008a
+#define	 XC5K_OUTAMP_ANALOG	 	0x0009
 #define	XC5K_REG_SIGNAL_SOURCE	0x0d
 #define	 XC5K_SIGNAL_SOURCE_AIR		0
 #define	 XC5K_SIGNAL_SOURCE_CABLE	1
@@ -54,6 +65,8 @@
 #define	XC5K_REG_FRAME_LINES	0x02
 #define	XC5K_REG_HSYNC_FREQ	0x03
 #define	XC5K_REG_LOCK		0x04
+#define	 XC5K_LOCK_LOCKED		0x0001
+#define	 XC5K_LOCK_NOSIGNAL		0x0002
 #define	XC5K_REG_FREQ_ERR	0x05
 #define	XC5K_REG_SNR		0x06
 #define	XC5K_REG_VERSION	0x07
Index: src/sys/dev/i2c/xc5kvar.h
diff -u src/sys/dev/i2c/xc5kvar.h:1.1 src/sys/dev/i2c/xc5kvar.h:1.2
--- src/sys/dev/i2c/xc5kvar.h:1.1	Mon Dec 27 15:42:11 2010
+++ src/sys/dev/i2c/xc5kvar.h	Sat Jul  9 15:00:44 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: xc5kvar.h,v 1.1 2010/12/27 15:42:11 jmcneill Exp $ */
+/* $NetBSD: xc5kvar.h,v 1.2 2011/07/09 15:00:44 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2010 Jared D. McNeill <jmcne...@invisible.ca>
@@ -30,6 +30,7 @@
 #define _XC5KVAR_H
 
 #include <dev/i2c/i2cvar.h>
+#include <dev/dtv/dtvio.h>
 #include <dev/video_if.h>
 
 typedef int (*xc5k_reset_cb)(void *);
@@ -41,6 +42,9 @@
 
 	xc5k_reset_cb	reset;
 	void		*reset_priv;
+
+	unsigned int	if_freq;
+	fe_type_t	fe_type;
 };
 
 struct xc5k_params {
@@ -50,8 +54,10 @@
 };
 
 struct xc5k *	xc5k_open(device_t, i2c_tag_t, i2c_addr_t,
-			  xc5k_reset_cb, void *);
+			  xc5k_reset_cb, void *, unsigned int, fe_type_t);
 void		xc5k_close(struct xc5k *);
-int		xc5k_tune(struct xc5k *, struct xc5k_params *);
+int		xc5k_tune_video(struct xc5k *, struct xc5k_params *);
+int		xc5k_tune_dtv(struct xc5k *, const struct dvb_frontend_parameters *);
+fe_status_t	xc5k_get_status(struct xc5k *);
 
 #endif /* !_XC5KVAR_H */

Index: src/sys/dev/usb/auvitek.c
diff -u src/sys/dev/usb/auvitek.c:1.3 src/sys/dev/usb/auvitek.c:1.4
--- src/sys/dev/usb/auvitek.c:1.3	Tue Dec 28 04:02:33 2010
+++ src/sys/dev/usb/auvitek.c	Sat Jul  9 15:00:44 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: auvitek.c,v 1.3 2010/12/28 04:02:33 jmcneill Exp $ */
+/* $NetBSD: auvitek.c,v 1.4 2011/07/09 15:00:44 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2010 Jared D. McNeill <jmcne...@invisible.ca>
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: auvitek.c,v 1.3 2010/12/28 04:02:33 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: auvitek.c,v 1.4 2011/07/09 15:00:44 jmcneill Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -115,7 +115,9 @@
 
 	sc->sc_dying = sc->sc_running = 0;
 
-	mutex_init(&sc->sc_subdev_lock, MUTEX_DEFAULT, IPL_VM);
+	mutex_init(&sc->sc_subdev_lock, MUTEX_DEFAULT, IPL_NONE);
+	mutex_init(&sc->sc_ab.ab_lock, MUTEX_DEFAULT, IPL_USB);
+	cv_init(&sc->sc_ab.ab_cv, "auvitekbulk");
 
 	err = usbd_set_config_index(dev, 0, 1);
 	if (err) {
@@ -123,14 +125,23 @@
 		    usbd_errstr(err));
 		return;
 	}
-	err = usbd_device2interface_handle(dev, 0, &sc->sc_iface);
+	err = usbd_device2interface_handle(dev, 0, &sc->sc_isoc_iface);
+	if (err) {
+		aprint_error_dev(self, "couldn't get interface handle: %s\n",
+		    usbd_errstr(err));
+		return;
+	}
+	err = usbd_device2interface_handle(dev, 3, &sc->sc_bulk_iface);
 	if (err) {
 		aprint_error_dev(self, "couldn't get interface handle: %s\n",
 		    usbd_errstr(err));
 		return;
 	}
 
-	err = usbd_set_interface(sc->sc_iface, AUVITEK_XFER_ALTNO);
+	sc->sc_ax.ax_sc = sc->sc_ab.ab_sc = sc;
+	sc->sc_ax.ax_endpt = sc->sc_ab.ab_endpt = -1;
+
+	err = usbd_set_interface(sc->sc_isoc_iface, AUVITEK_XFER_ALTNO);
 	if (err) {
 		aprint_error_dev(self, "couldn't set interface: %s\n",
 		    usbd_errstr(err));
@@ -138,13 +149,11 @@
 	}
 
 	nep = 0;
-	usbd_endpoint_count(sc->sc_iface, &nep);
-	sc->sc_ax.ax_sc = sc;
-	sc->sc_ax.ax_endpt = -1;
+	usbd_endpoint_count(sc->sc_isoc_iface, &nep);
 	for (i = 0; i < nep; i++) {
 		int dir, type;
 
-		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
+		ed = usbd_interface2endpoint_descriptor(sc->sc_isoc_iface, i);
 		if (ed == NULL) {
 			aprint_error_dev(self,
 			    "couldn't read endpoint descriptor %d\n", i);
@@ -154,21 +163,22 @@
 		dir = UE_GET_DIR(ed->bEndpointAddress);
 		type = UE_GET_XFERTYPE(ed->bmAttributes);
 
-		if (dir == UE_DIR_IN && type == UE_ISOCHRONOUS) {
+		if (dir == UE_DIR_IN && type == UE_ISOCHRONOUS &&
+		    sc->sc_ax.ax_endpt == -1) {
 			sc->sc_ax.ax_endpt = ed->bEndpointAddress;
 			sc->sc_ax.ax_maxpktlen =
 			    UE_GET_SIZE(UGETW(ed->wMaxPacketSize)) *
 			    (UE_GET_TRANS(UGETW(ed->wMaxPacketSize)) + 1);
-			break;
 		}
 	}
-	err = usbd_set_interface(sc->sc_iface, 0);
+
+	err = usbd_set_interface(sc->sc_isoc_iface, 0);
 	if (err) {
 		aprint_error_dev(self, "couldn't set interface: %s\n",
 		    usbd_errstr(err));
-		sc->sc_dying = 1;
 		return;
 	}
+
 	if (sc->sc_ax.ax_endpt == -1) {
 		aprint_error_dev(self, "couldn't find isoc endpoint\n");
 		sc->sc_dying = 1;
@@ -183,11 +193,60 @@
 	aprint_debug_dev(self, "isoc endpoint 0x%02x size %d\n",
 	    sc->sc_ax.ax_endpt, sc->sc_ax.ax_maxpktlen);
 
+	nep = 0;
+	usbd_endpoint_count(sc->sc_bulk_iface, &nep);
+	for (i = 0; i < nep; i++) {
+		int dir, type;
+
+		ed = usbd_interface2endpoint_descriptor(sc->sc_bulk_iface, i);
+		if (ed == NULL) {
+			aprint_error_dev(self,
+			    "couldn't read endpoint descriptor %d\n", i);
+			continue;
+		}
+
+		dir = UE_GET_DIR(ed->bEndpointAddress);
+		type = UE_GET_XFERTYPE(ed->bmAttributes);
+
+		if (dir == UE_DIR_IN && type == UE_BULK &&
+		    sc->sc_ab.ab_endpt == -1) {
+			sc->sc_ab.ab_endpt = ed->bEndpointAddress;
+		}
+	}
+
+	if (sc->sc_ab.ab_endpt == -1) {
+		aprint_error_dev(self, "couldn't find bulk endpoint\n");
+		sc->sc_dying = 1;
+		return;
+	}
+
+	for (i = 0; i < AUVITEK_NBULK_XFERS; i++) {
+		sc->sc_ab.ab_bx[i].bx_sc = sc;
+		sc->sc_ab.ab_bx[i].bx_xfer = usbd_alloc_xfer(sc->sc_udev);
+		if (sc->sc_ab.ab_bx[i].bx_xfer == NULL) {
+			aprint_error_dev(self, "couldn't allocate xfer\n");
+			sc->sc_dying = 1;
+			return;
+		}
+		sc->sc_ab.ab_bx[i].bx_buffer = usbd_alloc_buffer(
+		    sc->sc_ab.ab_bx[i].bx_xfer, AUVITEK_BULK_BUFLEN);
+		if (sc->sc_ab.ab_bx[i].bx_buffer == NULL) {
+			aprint_error_dev(self,
+			    "couldn't allocate xfer buffer\n");
+			sc->sc_dying = 1;
+			return;
+		}
+	}
+
+	aprint_debug_dev(self, "bulk endpoint 0x%02x size %d\n",
+	    sc->sc_ab.ab_endpt, AUVITEK_BULK_BUFLEN);
+
 	auvitek_board_init(sc);
 
 	auvitek_i2c_attach(sc);
 
-	sc->sc_au8522 = au8522_open(self, &sc->sc_i2c, 0x8e >> 1);
+	sc->sc_au8522 = au8522_open(self, &sc->sc_i2c, 0x8e >> 1,
+	    auvitek_board_get_if_frequency(sc));
 	if (sc->sc_au8522 == NULL) {
 		aprint_error_dev(sc->sc_dev, "couldn't initialize decoder\n");
 		sc->sc_dying = 1;
@@ -196,17 +255,20 @@
 
 	auvitek_video_attach(sc);
 	auvitek_audio_attach(sc);
+	auvitek_dtv_attach(sc);
 }
 
 static int
 auvitek_detach(device_t self, int flags)
 {
 	struct auvitek_softc *sc = device_private(self);
+	unsigned int i;
 
 	sc->sc_dying = 1;
 
 	pmf_device_deregister(self);
 
+	auvitek_dtv_detach(sc, flags);
 	auvitek_audio_detach(sc, flags);
 	auvitek_video_detach(sc, flags);
 
@@ -219,6 +281,13 @@
 
 	mutex_destroy(&sc->sc_subdev_lock);
 
+	for (i = 0; i < AUVITEK_NBULK_XFERS; i++) {
+		if (sc->sc_ab.ab_bx[i].bx_xfer)
+			usbd_free_xfer(sc->sc_ab.ab_bx[i].bx_xfer);
+	}
+	cv_destroy(&sc->sc_ab.ab_cv);
+	mutex_destroy(&sc->sc_ab.ab_lock);
+
 	return 0;
 }
 
@@ -242,6 +311,8 @@
 	struct auvitek_softc *sc = device_private(self);
 
 	auvitek_video_childdet(sc, child);
+	auvitek_audio_childdet(sc, child);
+	auvitek_dtv_childdet(sc, child);
 }
 
 uint8_t
@@ -287,7 +358,7 @@
 		    usbd_errstr(err));
 }
 
-MODULE(MODULE_CLASS_DRIVER, auvitek, "au8522,xc5k");
+MODULE(MODULE_CLASS_DRIVER, auvitek, "au8522,xc5k,dtv");
 
 #ifdef _MODULE
 #include "ioconf.c"
Index: src/sys/dev/usb/auvitek_video.c
diff -u src/sys/dev/usb/auvitek_video.c:1.3 src/sys/dev/usb/auvitek_video.c:1.4
--- src/sys/dev/usb/auvitek_video.c:1.3	Thu May 26 23:42:39 2011
+++ src/sys/dev/usb/auvitek_video.c	Sat Jul  9 15:00:45 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: auvitek_video.c,v 1.3 2011/05/26 23:42:39 jmcneill Exp $ */
+/* $NetBSD: auvitek_video.c,v 1.4 2011/07/09 15:00:45 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2010 Jared D. McNeill <jmcne...@invisible.ca>
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: auvitek_video.c,v 1.3 2011/05/26 23:42:39 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: auvitek_video.c,v 1.4 2011/07/09 15:00:45 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -100,9 +100,8 @@
 					     uint8_t *, uint32_t);
 static void		auvitek_videobuf_weave(struct auvitek_softc *,
 					       uint8_t *, uint32_t);
-static int		auvitek_tuner_reset(void *);
 
-static const struct video_hw_if auvitek_hw_if = {
+static const struct video_hw_if auvitek_video_if = {
 	.open = auvitek_open,
 	.close = auvitek_close,
 	.get_devname = auvitek_get_devname,
@@ -133,7 +132,7 @@
 {
 	snprintf(sc->sc_businfo, sizeof(sc->sc_businfo), "usb:%08x",
 	    sc->sc_udev->cookie.cookie);
-	sc->sc_videodev = video_attach_mi(&auvitek_hw_if, sc->sc_dev);
+	sc->sc_videodev = video_attach_mi(&auvitek_video_if, sc->sc_dev);
 
 	return (sc->sc_videodev != NULL);
 }
@@ -167,7 +166,9 @@
 	mutex_enter(&sc->sc_subdev_lock);
 	if (sc->sc_xc5k == NULL) {
 		sc->sc_xc5k = xc5k_open(sc->sc_dev, &sc->sc_i2c, 0xc2 >> 1, 
-		    auvitek_tuner_reset, sc);
+		    auvitek_board_tuner_reset, sc,
+		    auvitek_board_get_if_frequency(sc),
+		    FE_ATSC);
 	}
 	mutex_exit(&sc->sc_subdev_lock);
 
@@ -525,7 +526,7 @@
 	params.frequency = vf->frequency;
 	if (sc->sc_au8522)
 		au8522_set_audio(sc->sc_au8522, false);
-	error = xc5k_tune(sc->sc_xc5k, &params);
+	error = xc5k_tune_video(sc->sc_xc5k, &params);
 	if (sc->sc_au8522)
 		au8522_set_audio(sc->sc_au8522, true);
 	if (error)
@@ -548,7 +549,7 @@
 	usbd_status err;
 	int i;
 
-	err = usbd_set_interface(sc->sc_iface, AUVITEK_XFER_ALTNO);
+	err = usbd_set_interface(sc->sc_isoc_iface, AUVITEK_XFER_ALTNO);
 	if (err != USBD_NORMAL_COMPLETION) {
 		aprint_error_dev(sc->sc_dev, "couldn't set altno %d: %s\n",
 		    AUVITEK_XFER_ALTNO, usbd_errstr(err));
@@ -562,7 +563,7 @@
 
 	ax->ax_nframes = nframes;
 	ax->ax_uframe_len = uframe_len;
-	for (i = 0; i < AUVITEK_NXFERS; i++) {
+	for (i = 0; i < AUVITEK_NISOC_XFERS; i++) {
 		struct auvitek_isoc *isoc = &ax->ax_i[i];
 		isoc->i_ax = ax;
 		isoc->i_frlengths =
@@ -570,7 +571,7 @@
 			KM_SLEEP);
 	}
 
-	err = usbd_open_pipe(sc->sc_iface, ax->ax_endpt,
+	err = usbd_open_pipe(sc->sc_isoc_iface, ax->ax_endpt,
 	    USBD_EXCLUSIVE_USE, &ax->ax_pipe);
 	if (err != USBD_NORMAL_COMPLETION) {
 		aprint_error_dev(sc->sc_dev, "couldn't open pipe: %s\n",
@@ -578,7 +579,7 @@
 		return EIO;
 	}
 
-	for (i = 0; i < AUVITEK_NXFERS; i++) {
+	for (i = 0; i < AUVITEK_NISOC_XFERS; i++) {
 		struct auvitek_isoc *isoc = &ax->ax_i[i];
 
 		isoc->i_xfer = usbd_alloc_xfer(sc->sc_udev);
@@ -613,7 +614,7 @@
 		ax->ax_pipe = NULL;
 	}
 
-	for (i = 0; i < AUVITEK_NXFERS; i++) {
+	for (i = 0; i < AUVITEK_NISOC_XFERS; i++) {
 		struct auvitek_isoc *isoc = &ax->ax_i[i];
 		if (isoc->i_xfer != NULL) {
 			usbd_free_buffer(isoc->i_xfer);
@@ -628,7 +629,7 @@
 	}
 
 	usbd_delay_ms(sc->sc_udev, 1000);
-	err = usbd_set_interface(sc->sc_iface, 0);
+	err = usbd_set_interface(sc->sc_isoc_iface, 0);
 	if (err != USBD_NORMAL_COMPLETION) {
 		aprint_error_dev(sc->sc_dev,
 		    "couldn't set zero bw interface: %s\n",
@@ -649,7 +650,7 @@
 	ax->ax_av.av_eb = ax->ax_av.av_ob = 0;
 	ax->ax_av.av_stride = 720 * 2;
 
-	for (i = 0; i < AUVITEK_NXFERS; i++) {
+	for (i = 0; i < AUVITEK_NISOC_XFERS; i++) {
 		error = auvitek_isoc_start1(&ax->ax_i[i]);
 		if (error)
 			return error;
@@ -797,11 +798,3 @@
 		resid -= wlen;
 	}
 }
-
-static int
-auvitek_tuner_reset(void *priv)
-{
-	struct auvitek_softc *sc = priv;
-
-	return auvitek_board_tuner_reset(sc);
-}

Index: src/sys/dev/usb/auvitek_board.c
diff -u src/sys/dev/usb/auvitek_board.c:1.2 src/sys/dev/usb/auvitek_board.c:1.3
--- src/sys/dev/usb/auvitek_board.c:1.2	Tue Dec 28 04:02:33 2010
+++ src/sys/dev/usb/auvitek_board.c	Sat Jul  9 15:00:44 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: auvitek_board.c,v 1.2 2010/12/28 04:02:33 jmcneill Exp $ */
+/* $NetBSD: auvitek_board.c,v 1.3 2011/07/09 15:00:44 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2010 Jared D. McNeill <jmcne...@invisible.ca>
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: auvitek_board.c,v 1.2 2010/12/28 04:02:33 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: auvitek_board.c,v 1.3 2011/07/09 15:00:44 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -97,8 +97,9 @@
 }
 
 int
-auvitek_board_tuner_reset(struct auvitek_softc *sc)
+auvitek_board_tuner_reset(void *priv)
 {
+	struct auvitek_softc *sc = priv;
 	uint8_t val;
 
 	switch (sc->sc_board) {
@@ -117,3 +118,9 @@
 
 	return 0;
 }
+
+unsigned int
+auvitek_board_get_if_frequency(struct auvitek_softc *sc)
+{
+	return 6000000;	/* 6MHz */
+}
Index: src/sys/dev/usb/auvitekvar.h
diff -u src/sys/dev/usb/auvitekvar.h:1.2 src/sys/dev/usb/auvitekvar.h:1.3
--- src/sys/dev/usb/auvitekvar.h:1.2	Tue Dec 28 04:02:33 2010
+++ src/sys/dev/usb/auvitekvar.h	Sat Jul  9 15:00:45 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: auvitekvar.h,v 1.2 2010/12/28 04:02:33 jmcneill Exp $ */
+/* $NetBSD: auvitekvar.h,v 1.3 2011/07/09 15:00:45 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2010 Jared D. McNeill <jmcne...@invisible.ca>
@@ -30,6 +30,8 @@
 #define _AUVITEKVAR_H
 
 #include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/kthread.h>
 
 #include <dev/usb/usb.h>
 #include <dev/usb/usbdi.h>
@@ -47,8 +49,10 @@
 	AUVITEK_BOARD_HVR_950Q,
 };
 
-#define	AUVITEK_NXFERS		8
+#define	AUVITEK_NISOC_XFERS	16
+#define	AUVITEK_NBULK_XFERS	1
 #define AUVITEK_XFER_ALTNO	5
+#define	AUVITEK_BULK_BUFLEN	58658	/* BDA driver uses the same */
 
 struct auvitek_isoc {
 	struct auvitek_xfer	*i_ax;
@@ -69,7 +73,7 @@
 	int			ax_endpt;
 	uint16_t		ax_maxpktlen;
 	usbd_pipe_handle	ax_pipe;
-	struct auvitek_isoc	ax_i[AUVITEK_NXFERS];
+	struct auvitek_isoc	ax_i[AUVITEK_NISOC_XFERS];
 	uint32_t		ax_nframes;
 	uint32_t		ax_uframe_len;
 	uint8_t			ax_frinfo;
@@ -77,15 +81,32 @@
 	struct auvitek_videobuf	ax_av;
 };
 
+struct auvitek_bulk_xfer {
+	struct auvitek_softc	*bx_sc;
+	usbd_xfer_handle	bx_xfer;
+	uint8_t			*bx_buffer;
+};
+
+struct auvitek_bulk {
+	struct auvitek_softc	*ab_sc;
+	int			ab_endpt;
+	usbd_pipe_handle	ab_pipe;
+	struct auvitek_bulk_xfer ab_bx[AUVITEK_NBULK_XFERS];
+	bool			ab_running;
+	kmutex_t		ab_lock;
+	kcondvar_t		ab_cv;
+};
+
 struct auvitek_softc {
 	device_t		sc_dev;
-	device_t		sc_videodev, sc_audiodev;
+	device_t		sc_videodev, sc_dtvdev, sc_audiodev;
 	struct i2c_controller	sc_i2c;
 	kmutex_t		sc_i2c_lock;
 
 	usbd_device_handle	sc_udev;
 	int			sc_uport;
-	usbd_interface_handle	sc_iface;
+	usbd_interface_handle	sc_isoc_iface;
+	usbd_interface_handle	sc_bulk_iface;
 
 	char			sc_running;
 	char			sc_dying;
@@ -102,6 +123,7 @@
 	uint32_t		sc_curfreq;
 
 	struct auvitek_xfer	sc_ax;
+	struct auvitek_bulk	sc_ab;
 
 	char			sc_businfo[32];
 };
@@ -117,7 +139,8 @@
 
 /* auvitek_board.c */
 void	auvitek_board_init(struct auvitek_softc *);
-int	auvitek_board_tuner_reset(struct auvitek_softc *);
+int	auvitek_board_tuner_reset(void *);
+unsigned int auvitek_board_get_if_frequency(struct auvitek_softc *);
 
 /* auvitek_i2c.c */
 int	auvitek_i2c_attach(struct auvitek_softc *);
@@ -128,4 +151,9 @@
 int	auvitek_video_detach(struct auvitek_softc *, int);
 void	auvitek_video_childdet(struct auvitek_softc *, device_t);
 
+/* auvitek_dtv.c */
+int	auvitek_dtv_attach(struct auvitek_softc *);
+int	auvitek_dtv_detach(struct auvitek_softc *, int);
+void	auvitek_dtv_childdet(struct auvitek_softc *, device_t);
+
 #endif /* !_AUVITEKVAR_H */

Index: src/sys/dev/usb/auvitek_i2c.c
diff -u src/sys/dev/usb/auvitek_i2c.c:1.1 src/sys/dev/usb/auvitek_i2c.c:1.2
--- src/sys/dev/usb/auvitek_i2c.c:1.1	Mon Dec 27 15:42:11 2010
+++ src/sys/dev/usb/auvitek_i2c.c	Sat Jul  9 15:00:45 2011
@@ -1,4 +1,4 @@
-/* $NetBSD: auvitek_i2c.c,v 1.1 2010/12/27 15:42:11 jmcneill Exp $ */
+/* $NetBSD: auvitek_i2c.c,v 1.2 2011/07/09 15:00:45 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2010 Jared D. McNeill <jmcne...@invisible.ca>
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: auvitek_i2c.c,v 1.1 2010/12/27 15:42:11 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: auvitek_i2c.c,v 1.2 2011/07/09 15:00:45 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -121,7 +121,7 @@
 	uint8_t v;
 	unsigned int i;
 
-	KASSERT(mutex_owned(&sc->sc_i2c_lock));
+	//KASSERT(mutex_owned(&sc->sc_i2c_lock));
 
 	auvitek_write_1(sc, AU0828_REG_I2C_MBMODE, 1);
 	auvitek_write_1(sc, AU0828_REG_I2C_CLKDIV, sc->sc_i2c_clkdiv);
@@ -160,7 +160,7 @@
 	uint8_t v;
 	unsigned int i, fifolen;
 
-	KASSERT(mutex_owned(&sc->sc_i2c_lock));
+	//KASSERT(mutex_owned(&sc->sc_i2c_lock));
 
 	auvitek_write_1(sc, AU0828_REG_I2C_MBMODE, 1);
 	auvitek_write_1(sc, AU0828_REG_I2C_CLKDIV, sc->sc_i2c_clkdiv);

Added files:

Index: src/sys/dev/i2c/au8522mod.h
diff -u /dev/null src/sys/dev/i2c/au8522mod.h:1.1
--- /dev/null	Sat Jul  9 15:00:45 2011
+++ src/sys/dev/i2c/au8522mod.h	Sat Jul  9 15:00:43 2011
@@ -0,0 +1,41 @@
+/* $NetBSD: au8522mod.h,v 1.1 2011/07/09 15:00:43 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2011 Jared D. McNeill <jmcne...@invisible.ca>
+ * 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 _AU8522MOD_H
+#define _AU8522MOD_H
+
+struct au8522_modulation_table {
+	uint16_t	reg;
+	uint8_t		val;
+};
+
+#include <dev/i2c/au8522mod_8vsb.h>
+#include <dev/i2c/au8522mod_qam64.h>
+#include <dev/i2c/au8522mod_qam256.h>
+
+#endif /* !_AU8522MOD_H */
Index: src/sys/dev/i2c/au8522mod_8vsb.h
diff -u /dev/null src/sys/dev/i2c/au8522mod_8vsb.h:1.1
--- /dev/null	Sat Jul  9 15:00:46 2011
+++ src/sys/dev/i2c/au8522mod_8vsb.h	Sat Jul  9 15:00:43 2011
@@ -0,0 +1,57 @@
+/* $NetBSD: au8522mod_8vsb.h,v 1.1 2011/07/09 15:00:43 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2011 Jared D. McNeill <jmcne...@invisible.ca>
+ * 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.
+ */
+
+static const struct au8522_modulation_table au8522_modulation_8vsb[] = {
+	{ 0x8090, 0x84 },
+	{ 0x4092, 0x11 },
+	{ 0x2005, 0x00 },
+	{ 0x8091, 0x80 },
+	{ 0x80a3, 0x0c },
+	{ 0x80a4, 0xe8 },
+	{ 0x8081, 0xc4 },
+	{ 0x80a5, 0x40 },
+	{ 0x80a7, 0x40 },
+	{ 0x80a6, 0x67 },
+	{ 0x8262, 0x20 },
+	{ 0x821c, 0x30 },
+	{ 0x80d8, 0x1a },
+	{ 0x8227, 0xa0 },
+	{ 0x8121, 0xff },
+	{ 0x80a8, 0xf0 },
+	{ 0x80a9, 0x05 },
+	{ 0x80aa, 0x77 },
+	{ 0x80ab, 0xf0 },
+	{ 0x80ac, 0x05 },
+	{ 0x80ad, 0x77 },
+	{ 0x80ae, 0x41 },
+	{ 0x80af, 0x66 },
+	{ 0x821b, 0xcc },
+	{ 0x821d, 0x80 },
+	{ 0x80a4, 0xe8 },
+	{ 0x8231, 0x13 },
+};
Index: src/sys/dev/i2c/au8522mod_qam256.h
diff -u /dev/null src/sys/dev/i2c/au8522mod_qam256.h:1.1
--- /dev/null	Sat Jul  9 15:00:46 2011
+++ src/sys/dev/i2c/au8522mod_qam256.h	Sat Jul  9 15:00:43 2011
@@ -0,0 +1,102 @@
+/* $NetBSD: au8522mod_qam256.h,v 1.1 2011/07/09 15:00:43 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2011 Jared D. McNeill <jmcne...@invisible.ca>
+ * 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.
+ */
+
+static const struct au8522_modulation_table au8522_modulation_qam256[] = {
+	{ 0x80a3, 0x09 },
+	{ 0x80a4, 0x00 },
+	{ 0x8081, 0xc4 },
+	{ 0x80a5, 0x40 },
+	{ 0x80aa, 0x77 },
+	{ 0x80ad, 0x77 },
+	{ 0x80a6, 0x67 },
+	{ 0x8262, 0x20 },
+	{ 0x821c, 0x30 },
+	{ 0x80b8, 0x3e },
+	{ 0x80b9, 0xf0 },
+	{ 0x80ba, 0x01 },
+	{ 0x80bb, 0x18 },
+	{ 0x80bc, 0x50 },
+	{ 0x80bd, 0x00 },
+	{ 0x80be, 0xea },
+	{ 0x80bf, 0xef },
+	{ 0x80c0, 0xfc },
+	{ 0x80c1, 0xbd },
+	{ 0x80c2, 0x1f },
+	{ 0x80c3, 0xfc },
+	{ 0x80c4, 0xdd },
+	{ 0x80c5, 0xaf },
+	{ 0x80c6, 0x00 },
+	{ 0x80c7, 0x38 },
+	{ 0x80c8, 0x30 },
+	{ 0x80c9, 0x05 },
+	{ 0x80ca, 0x4a },
+	{ 0x80cb, 0xd0 },
+	{ 0x80cc, 0x01 },
+	{ 0x80cd, 0xd9 },
+	{ 0x80ce, 0x6f },
+	{ 0x80cf, 0xf9 },
+	{ 0x80d0, 0x70 },
+	{ 0x80d1, 0xdf },
+	{ 0x80d2, 0xf7 },
+	{ 0x80d3, 0xc2 },
+	{ 0x80d4, 0xdf },
+	{ 0x80d5, 0x02 },
+	{ 0x80d6, 0x9a },
+	{ 0x80d7, 0xd0 },
+	{ 0x8250, 0x0d },
+	{ 0x8251, 0xcd },
+	{ 0x8252, 0xe0 },
+	{ 0x8253, 0x05 },
+	{ 0x8254, 0xa7 },
+	{ 0x8255, 0xff },
+	{ 0x8256, 0xed },
+	{ 0x8257, 0x5b },
+	{ 0x8258, 0xae },
+	{ 0x8259, 0xe6 },
+	{ 0x825a, 0x3d },
+	{ 0x825b, 0x0f },
+	{ 0x825c, 0x0d },
+	{ 0x825d, 0xea },
+	{ 0x825e, 0xf2 },
+	{ 0x825f, 0x51 },
+	{ 0x8260, 0xf5 },
+	{ 0x8261, 0x06 },
+	{ 0x821a, 0x00 },
+	{ 0x8546, 0x40 },
+	{ 0x8210, 0x26 },
+	{ 0x8211, 0xf6 },
+	{ 0x8212, 0x84 },
+	{ 0x8213, 0x02 },
+	{ 0x8502, 0x01 },
+	{ 0x8121, 0x04 },
+	{ 0x8122, 0x04 },
+	{ 0x852e, 0x10 },
+	{ 0x80a4, 0xca },
+	{ 0x80a7, 0x40 },
+	{ 0x8526, 0x01 },
+};
Index: src/sys/dev/i2c/au8522mod_qam64.h
diff -u /dev/null src/sys/dev/i2c/au8522mod_qam64.h:1.1
--- /dev/null	Sat Jul  9 15:00:46 2011
+++ src/sys/dev/i2c/au8522mod_qam64.h	Sat Jul  9 15:00:44 2011
@@ -0,0 +1,102 @@
+/* $NetBSD: au8522mod_qam64.h,v 1.1 2011/07/09 15:00:44 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2011 Jared D. McNeill <jmcne...@invisible.ca>
+ * 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.
+ */
+
+static const struct au8522_modulation_table au8522_modulation_qam64[] = {
+	{ 0x00a3, 0x09 },
+	{ 0x00a4, 0x00 },
+	{ 0x0081, 0xc4 },
+	{ 0x00a5, 0x40 },
+	{ 0x00aa, 0x77 },
+	{ 0x00ad, 0x77 },
+	{ 0x00a6, 0x67 },
+	{ 0x0262, 0x20 },
+	{ 0x021c, 0x30 },
+	{ 0x00b8, 0x3e },
+	{ 0x00b9, 0xf0 },
+	{ 0x00ba, 0x01 },
+	{ 0x00bb, 0x18 },
+	{ 0x00bc, 0x50 },
+	{ 0x00bd, 0x00 },
+	{ 0x00be, 0xea },
+	{ 0x00bf, 0xef },
+	{ 0x00c0, 0xfc },
+	{ 0x00c1, 0xbd },
+	{ 0x00c2, 0x1f },
+	{ 0x00c3, 0xfc },
+	{ 0x00c4, 0xdd },
+	{ 0x00c5, 0xaf },
+	{ 0x00c6, 0x00 },
+	{ 0x00c7, 0x38 },
+	{ 0x00c8, 0x30 },
+	{ 0x00c9, 0x05 },
+	{ 0x00ca, 0x4a },
+	{ 0x00cb, 0xd0 },
+	{ 0x00cc, 0x01 },
+	{ 0x00cd, 0xd9 },
+	{ 0x00ce, 0x6f },
+	{ 0x00cf, 0xf9 },
+	{ 0x00d0, 0x70 },
+	{ 0x00d1, 0xdf },
+	{ 0x00d2, 0xf7 },
+	{ 0x00d3, 0xc2 },
+	{ 0x00d4, 0xdf },
+	{ 0x00d5, 0x02 },
+	{ 0x00d6, 0x9a },
+	{ 0x00d7, 0xd0 },
+	{ 0x0250, 0x0d },
+	{ 0x0251, 0xcd },
+	{ 0x0252, 0xe0 },
+	{ 0x0253, 0x05 },
+	{ 0x0254, 0xa7 },
+	{ 0x0255, 0xff },
+	{ 0x0256, 0xed },
+	{ 0x0257, 0x5b },
+	{ 0x0258, 0xae },
+	{ 0x0259, 0xe6 },
+	{ 0x025a, 0x3d },
+	{ 0x025b, 0x0f },
+	{ 0x025c, 0x0d },
+	{ 0x025d, 0xea },
+	{ 0x025e, 0xf2 },
+	{ 0x025f, 0x51 },
+	{ 0x0260, 0xf5 },
+	{ 0x0261, 0x06 },
+	{ 0x021a, 0x00 },
+	{ 0x0546, 0x40 },
+	{ 0x0210, 0xc7 },
+	{ 0x0211, 0xaa },
+	{ 0x0212, 0xab },
+	{ 0x0213, 0x02 },
+	{ 0x0502, 0x00 },
+	{ 0x0121, 0x04 },
+	{ 0x0122, 0x04 },
+	{ 0x052e, 0x10 },
+	{ 0x00a4, 0xca },
+	{ 0x00a7, 0x40 },
+	{ 0x0526, 0x01 },
+};

Index: src/sys/dev/usb/auvitek_dtv.c
diff -u /dev/null src/sys/dev/usb/auvitek_dtv.c:1.1
--- /dev/null	Sat Jul  9 15:00:46 2011
+++ src/sys/dev/usb/auvitek_dtv.c	Sat Jul  9 15:00:44 2011
@@ -0,0 +1,345 @@
+/* $NetBSD: auvitek_dtv.c,v 1.1 2011/07/09 15:00:44 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2011 Jared D. McNeill <jmcne...@invisible.ca>
+ * 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.
+ */
+
+/*
+ * Auvitek AU0828 USB controller (Digital TV function)
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: auvitek_dtv.c,v 1.1 2011/07/09 15:00:44 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/bus.h>
+
+#include <dev/usb/usb.h>
+#include <dev/usb/usbdi.h>
+#include <dev/usb/usbdivar.h>
+#include <dev/usb/usbdi_util.h>
+#include <dev/usb/usbdevs.h>
+
+#include <dev/dtv/dtvif.h>
+
+#include <dev/usb/auvitekreg.h>
+#include <dev/usb/auvitekvar.h>
+
+static void		auvitek_dtv_get_devinfo(void *,
+			    struct dvb_frontend_info *);
+static int		auvitek_dtv_open(void *, int);
+static void		auvitek_dtv_close(void *);
+static int		auvitek_dtv_set_tuner(void *,
+			    const struct dvb_frontend_parameters *);
+static fe_status_t	auvitek_dtv_get_status(void *);
+static uint16_t		auvitek_dtv_get_signal_strength(void *);
+static uint16_t		auvitek_dtv_get_snr(void *);
+static int		auvitek_dtv_start_transfer(void *);
+static int		auvitek_dtv_stop_transfer(void *);
+
+static int		auvitek_dtv_init_pipes(struct auvitek_softc *);
+static int		auvitek_dtv_close_pipes(struct auvitek_softc *);
+
+static int		auvitek_dtv_bulk_start(struct auvitek_softc *);
+static int		auvitek_dtv_bulk_start1(struct auvitek_bulk_xfer *);
+static void		auvitek_dtv_bulk_cb(usbd_xfer_handle,
+					    usbd_private_handle,
+					    usbd_status);
+
+static const struct dtv_hw_if auvitek_dtv_if = {
+	.get_devinfo = auvitek_dtv_get_devinfo,
+	.open = auvitek_dtv_open,
+	.close = auvitek_dtv_close,
+	.set_tuner = auvitek_dtv_set_tuner,
+	.get_status = auvitek_dtv_get_status,
+	.get_signal_strength = auvitek_dtv_get_signal_strength,
+	.get_snr = auvitek_dtv_get_snr,
+	.start_transfer = auvitek_dtv_start_transfer,
+	.stop_transfer = auvitek_dtv_stop_transfer,
+};
+
+int
+auvitek_dtv_attach(struct auvitek_softc *sc)
+{
+	struct dtv_attach_args daa;
+
+	daa.hw = &auvitek_dtv_if;
+	daa.priv = sc;
+	sc->sc_dtvdev = config_found_ia(sc->sc_dev, "dtvbus", &daa, dtv_print);
+
+	return (sc->sc_dtvdev != NULL);
+}
+
+int
+auvitek_dtv_detach(struct auvitek_softc *sc, int flags)
+{
+	if (sc->sc_dtvdev != NULL) {
+		config_detach(sc->sc_dtvdev, flags);
+		sc->sc_dtvdev = NULL;
+	}
+
+	return 0;
+}
+
+void
+auvitek_dtv_childdet(struct auvitek_softc *sc, device_t child)
+{
+	if (sc->sc_dtvdev == child)
+		sc->sc_dtvdev = NULL;
+}
+
+static void
+auvitek_dtv_get_devinfo(void *priv, struct dvb_frontend_info *info)
+{
+	struct auvitek_softc *sc = priv;
+
+	memset(info, 0, sizeof(*info));
+	strlcpy(info->name, sc->sc_descr, sizeof(info->name));
+	info->type = FE_ATSC;
+	info->frequency_min = 54000000;
+	info->frequency_max = 858000000;
+	info->frequency_stepsize = 62500;
+	info->caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB;
+}
+
+static int
+auvitek_dtv_open(void *priv, int flags)
+{
+	struct auvitek_softc *sc = priv;
+
+	if (sc->sc_dying)
+		return EIO;
+
+	mutex_enter(&sc->sc_subdev_lock);
+	if (sc->sc_xc5k == NULL) {
+		sc->sc_xc5k = xc5k_open(sc->sc_dev, &sc->sc_i2c, 0xc2 >> 1, 
+		    auvitek_board_tuner_reset, sc,
+		    auvitek_board_get_if_frequency(sc),
+		    FE_ATSC);
+	}
+	mutex_exit(&sc->sc_subdev_lock);
+
+	if (sc->sc_xc5k == NULL)
+		return ENXIO;
+
+	return auvitek_dtv_init_pipes(sc);
+}
+
+static void
+auvitek_dtv_close(void *priv)
+{
+	struct auvitek_softc *sc = priv;
+
+	auvitek_dtv_stop_transfer(sc);
+	auvitek_dtv_close_pipes(sc);
+}
+
+static int
+auvitek_dtv_set_tuner(void *priv, const struct dvb_frontend_parameters *params)
+{
+	struct auvitek_softc *sc = priv;
+	int error;
+
+	error = au8522_set_modulation(sc->sc_au8522, params->u.vsb.modulation);
+	if (error)
+		return error;
+
+	delay(100000);
+
+	au8522_set_gate(sc->sc_au8522, true);
+	error = xc5k_tune_dtv(sc->sc_xc5k, params);
+	au8522_set_gate(sc->sc_au8522, false);
+
+	return error;
+}
+
+fe_status_t
+auvitek_dtv_get_status(void *priv)
+{
+	struct auvitek_softc *sc = priv;
+
+	return au8522_get_dtv_status(sc->sc_au8522);
+}
+
+uint16_t
+auvitek_dtv_get_signal_strength(void *priv)
+{
+	return 0;	/* TODO */
+}
+
+uint16_t
+auvitek_dtv_get_snr(void *priv)
+{
+	return 0;	/* TODO */
+}
+
+static int
+auvitek_dtv_start_transfer(void *priv)
+{
+	struct auvitek_softc *sc = priv;
+	int s;
+
+	if (sc->sc_ab.ab_running) {
+		return 0;
+	}
+
+	auvitek_write_1(sc, 0x608, 0x90);
+	auvitek_write_1(sc, 0x609, 0x72);
+	auvitek_write_1(sc, 0x60a, 0x71);
+	auvitek_write_1(sc, 0x60b, 0x01);
+
+	sc->sc_ab.ab_running = true;
+
+	s = splusb();
+	auvitek_dtv_bulk_start(sc);
+	splx(s);
+
+	return 0;
+}
+
+static int
+auvitek_dtv_stop_transfer(void *priv)
+{
+	struct auvitek_softc *sc = priv;
+
+	sc->sc_ab.ab_running = false;
+
+	auvitek_write_1(sc, 0x608, 0x00);
+	auvitek_write_1(sc, 0x609, 0x00);
+	auvitek_write_1(sc, 0x60a, 0x00);
+	auvitek_write_1(sc, 0x60b, 0x00);
+
+	return 0;
+}
+
+static int
+auvitek_dtv_init_pipes(struct auvitek_softc *sc)
+{
+	usbd_status err;
+
+	err = usbd_open_pipe(sc->sc_bulk_iface, sc->sc_ab.ab_endpt,
+	    USBD_EXCLUSIVE_USE, &sc->sc_ab.ab_pipe);
+	if (err) {
+		aprint_error_dev(sc->sc_dev, "couldn't open bulk-in pipe: %s\n",
+		    usbd_errstr(err));
+		return ENOMEM;
+	}
+
+	return 0;
+}
+
+static int
+auvitek_dtv_close_pipes(struct auvitek_softc *sc)
+{
+	if (sc->sc_ab.ab_pipe != NULL) {
+		usbd_abort_pipe(sc->sc_ab.ab_pipe);
+		usbd_close_pipe(sc->sc_ab.ab_pipe);
+		sc->sc_ab.ab_pipe = NULL;
+	}
+
+	return 0;
+}
+
+static void
+auvitek_dtv_bulk_cb(usbd_xfer_handle xfer, usbd_private_handle priv,
+    usbd_status status)
+{
+	struct auvitek_bulk_xfer *bx = priv;
+	struct auvitek_softc *sc = bx->bx_sc;
+	struct auvitek_bulk *ab = &sc->sc_ab;
+	struct dtv_payload payload;
+	uint32_t xferlen;
+
+	if (ab->ab_running == false)
+		return;
+
+	usbd_get_xfer_status(xfer, NULL, NULL, &xferlen, NULL);
+
+	//printf("%s: status=%d xferlen=%u\n", __func__, status, xferlen);
+
+	if (status != USBD_NORMAL_COMPLETION) {
+		printf("%s: USB error (%s)\n", __func__, usbd_errstr(status));
+		if (status == USBD_STALLED) {
+			usbd_clear_endpoint_stall_async(ab->ab_pipe);
+			goto next;
+		}
+		if (status == USBD_SHORT_XFER) {
+			goto next;
+		}
+		return;
+	}
+
+	if (xferlen == 0) {
+		printf("%s: 0-length xfer\n", __func__);
+		goto next;
+	}
+
+	payload.data = bx->bx_buffer;
+	payload.size = xferlen;
+	dtv_submit_payload(sc->sc_dtvdev, &payload);
+
+next:
+	auvitek_dtv_bulk_start1(bx);
+}
+
+static int
+auvitek_dtv_bulk_start(struct auvitek_softc *sc)
+{
+	int i, error;
+
+	for (i = 0; i < AUVITEK_NBULK_XFERS; i++) {
+		error = auvitek_dtv_bulk_start1(&sc->sc_ab.ab_bx[i]);
+		if (error)
+			return error;
+	}
+
+	return 0;
+}
+
+static int
+auvitek_dtv_bulk_start1(struct auvitek_bulk_xfer *bx)
+{
+	struct auvitek_softc *sc = bx->bx_sc;
+	struct auvitek_bulk *ab = &sc->sc_ab;
+	int err;
+
+	usbd_setup_xfer(bx->bx_xfer, ab->ab_pipe, bx,
+	    bx->bx_buffer, AUVITEK_BULK_BUFLEN,
+	    //USBD_SHORT_XFER_OK|USBD_NO_COPY, USBD_NO_TIMEOUT,
+	    USBD_NO_COPY, 100,
+	    auvitek_dtv_bulk_cb);
+	err = usbd_transfer(bx->bx_xfer);
+	if (err != USBD_IN_PROGRESS) {
+		aprint_error_dev(sc->sc_dev, "USB error: %s\n",
+		    usbd_errstr(err));
+		return ENODEV;
+	}
+
+	return 0;
+}

Reply via email to