summaryrefslogtreecommitdiff
path: root/linux/drivers
diff options
context:
space:
mode:
authorAlex Woods <devnull@localhost>2003-03-30 13:13:16 +0000
committerAlex Woods <devnull@localhost>2003-03-30 13:13:16 +0000
commit6d3197dd77c4f3b8e6477e9f0db4c65d66b41d4c (patch)
treeb9b84b675e6e8c3bf7f915eaa2768431aa968203 /linux/drivers
parentce8a79b18f4f8a4d7d95b01bf417f9b4ce3734ec (diff)
downloadmediapointer-dvb-s2-6d3197dd77c4f3b8e6477e9f0db4c65d66b41d4c.tar.gz
mediapointer-dvb-s2-6d3197dd77c4f3b8e6477e9f0db4c65d66b41d4c.tar.bz2
Initial checkin.
Channel zapping, video streaming, and audio streaming functional.
Diffstat (limited to 'linux/drivers')
-rw-r--r--linux/drivers/media/dvb/ttusb-dec/dec2000_frontend.c193
-rw-r--r--linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c1044
-rw-r--r--linux/drivers/media/dvb/ttusb-dec/ttusb_dec.h83
3 files changed, 1320 insertions, 0 deletions
diff --git a/linux/drivers/media/dvb/ttusb-dec/dec2000_frontend.c b/linux/drivers/media/dvb/ttusb-dec/dec2000_frontend.c
new file mode 100644
index 000000000..05adad0f3
--- /dev/null
+++ b/linux/drivers/media/dvb/ttusb-dec/dec2000_frontend.c
@@ -0,0 +1,193 @@
+/*
+ * TTUSB DEC-2000-t Frontend
+ *
+ * Copyright (C) 2003 Alex Woods <linux-dvb@giblets.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "dvb_frontend.h"
+
+static int debug = 0;
+
+#define dprintk if (debug) printk
+
+static struct dvb_frontend_info dec2000_frontend_info = {
+
+ name: "TechnoTrend/Hauppauge DEC-2000-t Frontend",
+ type: FE_OFDM,
+ frequency_min: 51000000,
+ frequency_max: 858000000,
+ frequency_stepsize: 62500,
+ caps: FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO,
+
+};
+
+static int
+dec2000_frontend_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
+{
+
+ dprintk("%s\n", __FUNCTION__);
+
+ switch (cmd) {
+
+ case FE_GET_INFO:
+ dprintk("%s: FE_GET_INFO\n", __FUNCTION__);
+ memcpy(arg, &dec2000_frontend_info,
+ sizeof (struct dvb_frontend_info));
+ break;
+
+ case FE_READ_STATUS:{
+ fe_status_t *status = (fe_status_t *) arg;
+ dprintk("%s: FE_READ_STATUS\n", __FUNCTION__);
+ *status =
+ FE_HAS_SIGNAL | FE_HAS_VITERBI | FE_HAS_SYNC |
+ FE_HAS_CARRIER | FE_HAS_LOCK;
+ break;
+ }
+
+ case FE_READ_BER:{
+ u32 *ber = (u32 *) arg;
+ dprintk("%s: FE_READ_BER\n", __FUNCTION__);
+ *ber = 0;
+ return -ENOSYS;
+ break;
+ }
+
+ case FE_READ_SIGNAL_STRENGTH:{
+ dprintk("%s: FE_READ_SIGNAL_STRENGTH\n", __FUNCTION__);
+ *(s32 *) arg = 0xFF;
+ return -ENOSYS;
+ break;
+ }
+
+ case FE_READ_SNR:
+ dprintk("%s: FE_READ_SNR\n", __FUNCTION__);
+ *(s32 *) arg = 0;
+ return -ENOSYS;
+ break;
+
+ case FE_READ_UNCORRECTED_BLOCKS:
+ dprintk("%s: FE_READ_UNCORRECTED_BLOCKS\n", __FUNCTION__);
+ *(u32 *) arg = 0;
+ return -ENOSYS;
+ break;
+
+ case FE_SET_FRONTEND:{
+ struct dvb_frontend_parameters *p =
+ (struct dvb_frontend_parameters *) arg;
+ u8 b[] =
+ { 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff,
+ 0x00, 0x00, 0x00, 0xff };
+ u32 freq;
+ struct i2c_msg msg = { addr: 0x71, flags: 0, len:20 };
+
+ dprintk("%s: FE_SET_FRONTEND\n", __FUNCTION__);
+
+ dprintk(" frequency->%d\n", p->frequency);
+ dprintk(" symbol_rate->%d\n",
+ p->u.qam.symbol_rate);
+ dprintk(" inversion->%d\n", p->inversion);
+
+ freq = htonl(p->frequency / 1000);
+ memcpy(&b[4], &freq, sizeof (int));
+ msg.buf = b;
+ fe->i2c->xfer(fe->i2c, &msg, 1);
+
+ break;
+ }
+
+ case FE_GET_FRONTEND:
+ dprintk("%s: FE_GET_FRONTEND\n", __FUNCTION__);
+ break;
+
+ case FE_SLEEP:
+ dprintk("%s: FE_SLEEP\n", __FUNCTION__);
+ return -ENOSYS;
+ break;
+
+ case FE_INIT:
+ dprintk("%s: FE_INIT\n", __FUNCTION__);
+ break;
+
+ case FE_RESET:
+ dprintk("%s: FE_RESET\n", __FUNCTION__);
+ break;
+
+ default:
+ dprintk("%s: unknown IOCTL (0x%X)\n", __FUNCTION__, cmd);
+ return -EINVAL;
+
+ }
+
+ return 0;
+
+}
+
+static int
+dec2000_frontend_attach(struct dvb_i2c_bus *i2c)
+{
+
+ dprintk("%s\n", __FUNCTION__);
+
+ dvb_register_frontend(dec2000_frontend_ioctl, i2c, NULL,
+ &dec2000_frontend_info);
+
+ return 0;
+
+}
+
+static void
+dec2000_frontend_detach(struct dvb_i2c_bus *i2c)
+{
+
+ dprintk("%s\n", __FUNCTION__);
+
+ dvb_unregister_frontend(dec2000_frontend_ioctl, i2c);
+
+}
+
+static int __init
+dec2000_frontend_init(void)
+{
+
+ return dvb_register_i2c_device(THIS_MODULE, dec2000_frontend_attach,
+ dec2000_frontend_detach);
+
+}
+
+static void __exit
+dec2000_frontend_exit(void)
+{
+
+ dvb_unregister_i2c_device(dec2000_frontend_attach);
+
+}
+
+module_init(dec2000_frontend_init);
+module_exit(dec2000_frontend_exit);
+
+MODULE_DESCRIPTION("TechnoTrend/Hauppauge DEC-2000-t Frontend");
+MODULE_AUTHOR("Alex Woods <linux-dvb@giblets.org");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug level");
diff --git a/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c
new file mode 100644
index 000000000..0457657f4
--- /dev/null
+++ b/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c
@@ -0,0 +1,1044 @@
+/*
+ * TTUSB DEC Driver
+ *
+ * Copyright (C) 2003 Alex Woods <linux-dvb@giblets.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include "ttusb_dec.h"
+#include "dvb_frontend.h"
+
+static int debug = 0;
+
+#define dprintk if (debug) printk
+
+static int
+ttusb_dec_send_command(ttusb_dec_t * dec, const u8 command, const u8 params[],
+ int param_length, u8 cmd_result[])
+{
+
+ int result, actual_len, i;
+ u8 b[COMMAND_PACKET_SIZE + 4];
+ u8 c[COMMAND_PACKET_SIZE + 4];
+
+ dprintk("%s\n", __FUNCTION__);
+
+ b[0] = 0xaa;
+ b[1] = ++dec->trans_count;
+ b[2] = command;
+ b[3] = param_length;
+
+ if (params)
+ memcpy(&b[4], params, param_length);
+
+ if (debug) {
+
+ printk("%s: command: ", __FUNCTION__);
+ for (i = 0; i < param_length + 4; i++)
+ printk("0x%02X ", b[i]);
+ printk("\n");
+
+ }
+
+ result =
+ usb_bulk_msg(dec->udev, dec->command_pipe, b, sizeof (b),
+ &actual_len, HZ);
+
+ if (result < 0)
+ return result;
+
+ result =
+ usb_bulk_msg(dec->udev, dec->result_pipe, c, sizeof (c),
+ &actual_len, HZ);
+
+ if (result < 0)
+ return result;
+ else {
+
+ if (debug) {
+
+ printk("%s: result: ", __FUNCTION__);
+ for (i = 0; i < actual_len; i++)
+ printk("0x%02X ", c[i]);
+ printk("\n");
+
+ }
+
+ if (cmd_result && c[3] > 0)
+ memcpy(cmd_result, &c[4], c[3]);
+
+ return c[3];
+
+ }
+
+}
+
+static void
+ttusb_dec_set_pids(ttusb_dec_t * dec)
+{
+
+ u8 b[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff };
+
+ u16 pcr = htons(dec->pid[DMX_PES_PCR]);
+ u16 audio = htons(dec->pid[DMX_PES_AUDIO]);
+ u16 video = htons(dec->pid[DMX_PES_VIDEO]);
+
+ dprintk("%s\n", __FUNCTION__);
+
+ memcpy(&b[0], &pcr, 2);
+ memcpy(&b[2], &audio, 2);
+ memcpy(&b[4], &video, 2);
+
+ ttusb_dec_send_command(dec, 0x50, b, sizeof (b), NULL);
+
+}
+
+static int
+ttusb_dec_i2c_master_xfer(struct dvb_i2c_bus *i2c, const struct i2c_msg msgs[],
+ int num)
+{
+
+ int result, i;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ for (i = 0; i < num; i++)
+ if ((result =
+ ttusb_dec_send_command(i2c->data, msgs[i].addr,
+ msgs[i].buf, msgs[i].len,
+ NULL)) < 0)
+ return result;
+
+ return 0;
+
+}
+
+static void
+ttusb_dec_process_av_pes(ttusb_dec_t * dec, u8 * av_pes, int length)
+{
+
+ int i;
+ u16 csum = 0;
+ u8 c;
+
+ if (length < 16) {
+
+ printk("%s: packet too short.\n", __FUNCTION__);
+ return;
+
+ }
+
+ for (i = 0; i < length; i += 2) {
+
+ csum ^= le16_to_cpup((u16 *) (av_pes + i));
+ c = av_pes[i];
+ av_pes[i] = av_pes[i + 1];
+ av_pes[i + 1] = c;
+
+ }
+
+ if (csum) {
+
+ printk("%s: checksum failed.\n", __FUNCTION__);
+ return;
+
+ }
+
+ if (length > 8 + MAX_AV_PES_LENGTH + 4) {
+
+ printk("%s: packet too long.\n", __FUNCTION__);
+ return;
+
+ }
+
+ if (!(av_pes[0] == 'A' && av_pes[1] == 'V')) {
+
+ printk("%s: invalid AV_PES packet.\n", __FUNCTION__);
+ return;
+
+ }
+
+ switch (av_pes[2]) {
+
+ case 0x01:{ /* VideoStream */
+ int prebytes = av_pes[5] & 0x03;
+ int postbytes = (av_pes[5] & 0x0c) >> 2;
+ u16 v_pes_payload_length;
+
+ if (dec->v_pes_postbytes > 0
+ && dec->v_pes_postbytes == prebytes) {
+
+ memcpy(&dec->v_pes[dec->v_pes_length],
+ &av_pes[12], prebytes);
+
+ dvb_filter_pes2ts(&dec->v_pes2ts, dec->v_pes,
+ dec->v_pes_length + prebytes);
+
+ }
+
+ if (av_pes[5] & 0x10) {
+
+ dec->v_pes[7] = 0x80;
+ dec->v_pes[8] = 0x05;
+
+ dec->v_pes[9] =
+ 0x21 | ((av_pes[8] & 0xc0) >> 5);
+ dec->v_pes[10] =
+ ((av_pes[8] & 0x3f) << 2) |
+ ((av_pes[9] & 0xc0) >> 6);
+ dec->v_pes[11] =
+ 0x01 | ((av_pes[9] & 0x3f) << 2) |
+ ((av_pes[10] & 0x80) >> 6);
+ dec->v_pes[12] =
+ ((av_pes[10] & 0x7f) << 1) |
+ ((av_pes[11] & 0xc0) >> 7);
+ dec->v_pes[13] =
+ 0x01 | ((av_pes[11] & 0x7f) << 1);
+
+ memcpy(&dec->v_pes[14], &av_pes[12 + prebytes],
+ length - 16 - prebytes);
+ dec->v_pes_length = 14 + length - 16 - prebytes;
+
+ } else {
+
+ dec->v_pes[7] = 0x00;
+ dec->v_pes[8] = 0x00;
+
+ memcpy(&dec->v_pes[9], &av_pes[8], length - 12);
+ dec->v_pes_length = 9 + length - 12;
+
+ }
+
+ dec->v_pes_postbytes = postbytes;
+
+ if (dec->v_pes[9 + dec->v_pes[8]] == 0x00
+ && dec->v_pes[10 + dec->v_pes[8]] == 0x00
+ && dec->v_pes[11 + dec->v_pes[8]] == 0x01)
+ dec->v_pes[6] = 0x84;
+ else
+ dec->v_pes[6] = 0x80;
+
+ v_pes_payload_length =
+ htons(dec->v_pes_length - 6 + postbytes);
+ memcpy(&dec->v_pes[4], &v_pes_payload_length, 2);
+
+ if (postbytes == 0)
+ dvb_filter_pes2ts(&dec->v_pes2ts, dec->v_pes,
+ dec->v_pes_length);
+
+ break;
+
+ }
+
+ case 0x02: /* MainAudioStream */
+ dvb_filter_pes2ts(&dec->a_pes2ts, &av_pes[8], length - 12);
+ break;
+
+ default:
+ printk("%s: unknown AV_PES type (%02x).\n", __FUNCTION__,
+ av_pes[2]);
+ break;
+
+ }
+
+}
+
+static void
+ttusb_dec_process_urb_frame(ttusb_dec_t * dec, u8 * b, int length)
+{
+
+ while (length) {
+
+ switch (dec->av_pes_state) {
+
+ case 0:
+ case 1:
+ case 3:
+ if (*b++ == 0xaa) {
+
+ dec->av_pes_state++;
+ if (dec->av_pes_state == 4)
+ dec->av_pes_length = 0;
+
+ } else {
+
+ dec->av_pes_state = 0;
+
+ }
+
+ length--;
+ break;
+
+ case 2:
+ if (*b++ == 0x00) {
+
+ dec->av_pes_state++;
+
+ } else {
+
+ dec->av_pes_state = 0;
+
+ }
+
+ length--;
+ break;
+
+ case 4:
+ dec->av_pes[dec->av_pes_length++] = *b++;
+
+ if (dec->av_pes_length == 8) {
+
+ dec->av_pes_state++;
+ dec->av_pes_payload_length =
+ le16_to_cpup((u16 *) (dec->av_pes + 6));
+
+ }
+
+ length--;
+ break;
+
+ case 5:{
+
+ int remainder =
+ dec->av_pes_payload_length + 8 -
+ dec->av_pes_length;
+
+ if (length >= remainder) {
+
+ memcpy(dec->av_pes + dec->av_pes_length,
+ b, remainder);
+ dec->av_pes_length += remainder;
+ b += remainder;
+ length -= remainder;
+ dec->av_pes_state++;
+
+ } else {
+ memcpy(&dec->av_pes[dec->av_pes_length],
+ b, length);
+ dec->av_pes_length += length;
+ length = 0;
+
+ }
+ break;
+
+ }
+
+ case 6:
+ dec->av_pes[dec->av_pes_length++] = *b++;
+
+ if (dec->av_pes_length ==
+ 8 + dec->av_pes_payload_length + 4) {
+
+ ttusb_dec_process_av_pes(dec, dec->av_pes,
+ dec->av_pes_length);
+ dec->av_pes_state = 0;
+
+ }
+
+ length--;
+ break;
+
+ default:
+ printk("%s: illegal packet state encountered.\n",
+ __FUNCTION__);
+ dec->av_pes_state = 0;
+
+ }
+
+ }
+
+}
+
+static void
+ttusb_dec_process_urb(struct urb *urb)
+{
+
+ ttusb_dec_t *dec = urb->context;
+
+ if (!dec->iso_streaming)
+ return;
+
+ if (!urb->status) {
+
+ int i;
+
+ for (i = 0; i < FRAMES_PER_ISO_BUF; i++) {
+
+ struct iso_packet_descriptor *d;
+ u8 *b;
+ int length;
+
+ d = &urb->iso_frame_desc[i];
+ b = urb->transfer_buffer + d->offset;
+ length = d->actual_length;
+
+ ttusb_dec_process_urb_frame(dec, b, length);
+
+ }
+
+ } else {
+
+ dprintk("%s: urb error: %d\n", __FUNCTION__, urb->status);
+
+ }
+
+}
+
+static void
+ttusb_dec_setup_urbs(ttusb_dec_t * dec)
+{
+
+ int i, j, buffer_offset = 0;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ for (i = 0; i < ISO_BUF_COUNT; i++) {
+
+ int frame_offset = 0;
+ struct urb *urb = dec->iso_urb[i];
+
+ urb->dev = dec->udev;
+ urb->context = dec;
+ urb->complete = ttusb_dec_process_urb;
+ urb->pipe = dec->stream_pipe;
+ urb->transfer_flags = USB_ISO_ASAP;
+ urb->number_of_packets = FRAMES_PER_ISO_BUF;
+ urb->transfer_buffer_length =
+ ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF;
+ urb->transfer_buffer = dec->iso_buffer + buffer_offset;
+ buffer_offset += ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF;
+
+ for (j = 0; j < FRAMES_PER_ISO_BUF; j++) {
+
+ urb->iso_frame_desc[j].offset = frame_offset;
+ urb->iso_frame_desc[j].length = ISO_FRAME_SIZE;
+ frame_offset += ISO_FRAME_SIZE;
+
+ }
+
+ }
+
+}
+
+static void
+ttusb_dec_stop_iso_xfer(ttusb_dec_t * dec)
+{
+
+ int i;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ if (dec->iso_streaming)
+ for (i = 0; i < ISO_BUF_COUNT; i++)
+ usb_unlink_urb(dec->iso_urb[i]);
+
+ dec->iso_streaming = 0;
+
+}
+
+static int
+ttusb_dec_av_pes2ts_cb(void *priv, unsigned char *data)
+{
+
+ struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *) priv;
+
+ dvbdmxfeed->cb.ts(data, 188, 0, 0, &dvbdmxfeed->feed.ts, DMX_OK);
+
+ return 0;
+
+}
+
+static int
+ttusb_dec_start_iso_xfer(ttusb_dec_t * dec)
+{
+
+ int i, result;
+ u8 b0[] = { 0x05 };
+
+ dprintk("%s\n", __FUNCTION__);
+
+ ttusb_dec_send_command(dec, 0x80, b0, sizeof (b0), NULL);
+
+ if (dec->iso_streaming) {
+
+ printk("%s: iso xfer already running!\n", __FUNCTION__);
+ return 0;
+
+ }
+
+ ttusb_dec_setup_urbs(dec);
+
+ for (i = 0; i < ISO_BUF_COUNT; i++) {
+
+ if ((result = usb_submit_urb(dec->iso_urb[i]))) {
+
+ ttusb_dec_stop_iso_xfer(dec);
+ printk("%s: failed urb submission (%i: errno = %i)!\n",
+ __FUNCTION__, i, result);
+ return result;
+
+ }
+
+ }
+
+ if (!dec->interface) {
+
+ usb_set_interface(dec->udev, 0, 8); /* Can't do this at mod init time - weird */
+ dec->interface = 8;
+
+ }
+
+ dec->iso_streaming = 1;
+ dec->av_pes_state = 0;
+ dec->v_pes_postbytes = 0;
+
+ dvb_filter_pes2ts_init(&dec->a_pes2ts, dec->pid[DMX_PES_AUDIO],
+ ttusb_dec_av_pes2ts_cb, dec->demux.feed);
+ dvb_filter_pes2ts_init(&dec->v_pes2ts, dec->pid[DMX_PES_VIDEO],
+ ttusb_dec_av_pes2ts_cb, dec->demux.feed);
+
+ return 0;
+
+}
+
+static int
+ttusb_dec_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+
+ struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ ttusb_dec_t *dec = dvbdmx->priv;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ if (!dvbdmx->dmx.frontend)
+ return -EINVAL;
+
+ if (dec->iso_streaming)
+ ttusb_dec_stop_iso_xfer(dec);
+
+ dprintk(" pid: 0x%04X\n", dvbdmxfeed->pid);
+
+ switch (dvbdmxfeed->type) {
+
+ case DMX_TYPE_TS:
+ dprintk(" type: DMX_TYPE_TS\n");
+ break;
+
+ case DMX_TYPE_SEC:
+ dprintk(" type: DMX_TYPE_SEC\n");
+ break;
+
+ default:
+ dprintk(" type: unknown (%d)\n", dvbdmxfeed->type);
+ return -EINVAL;
+
+ }
+
+ dprintk(" ts_type:");
+
+ if (dvbdmxfeed->ts_type & TS_DECODER)
+ dprintk(" TS_DECODER");
+
+ if (dvbdmxfeed->ts_type & TS_PACKET)
+ dprintk(" TS_PACKET");
+
+ if (dvbdmxfeed->ts_type & TS_PAYLOAD_ONLY)
+ dprintk(" TS_PAYLOAD_ONLY");
+
+ dprintk("\n");
+
+ switch (dvbdmxfeed->pes_type) {
+
+ case DMX_TS_PES_VIDEO:
+ dprintk(" pes_type: DMX_TS_PES_VIDEO\n");
+ dec->pid[DMX_PES_PCR] = dvbdmxfeed->pid;
+ dec->pid[DMX_PES_VIDEO] = dvbdmxfeed->pid;
+ ttusb_dec_set_pids(dec);
+ break;
+
+ case DMX_TS_PES_AUDIO:
+ dprintk(" pes_type: DMX_TS_PES_AUDIO\n");
+ dec->pid[DMX_PES_AUDIO] = dvbdmxfeed->pid;
+ ttusb_dec_set_pids(dec);
+ break;
+
+ case DMX_TS_PES_TELETEXT:
+ dec->pid[DMX_PES_TELETEXT] = dvbdmxfeed->pid;
+ dprintk(" pes_type: DMX_TS_PES_TELETEXT\n");
+ break;
+
+ case DMX_TS_PES_PCR:
+ dprintk(" pes_type: DMX_TS_PES_PCR\n");
+ dec->pid[DMX_PES_PCR] = dvbdmxfeed->pid;
+ ttusb_dec_set_pids(dec);
+ break;
+
+ case DMX_TS_PES_OTHER:
+ dprintk(" pes_type: DMX_TS_PES_OTHER\n");
+ break;
+
+ default:
+ dprintk(" pes_type: unknown (%d)\n", dvbdmxfeed->pes_type);
+ return -EINVAL;
+
+ }
+
+ ttusb_dec_start_iso_xfer(dec);
+
+ return 0;
+
+}
+
+static int
+ttusb_dec_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+
+ ttusb_dec_t *dec = dvbdmxfeed->demux->priv;
+ u8 b0[] = { 0x00 };
+
+ dprintk("%s\n", __FUNCTION__);
+
+ ttusb_dec_stop_iso_xfer(dec);
+
+ ttusb_dec_send_command(dec, 0x81, b0, sizeof (b0), NULL);
+
+ return 0;
+
+}
+
+static void
+ttusb_dec_free_iso_urbs(ttusb_dec_t * dec)
+{
+
+ int i;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ for (i = 0; i < ISO_BUF_COUNT; i++)
+ if (dec->iso_urb[i])
+ usb_free_urb(dec->iso_urb[i]);
+
+ pci_free_consistent(NULL,
+ ISO_FRAME_SIZE * (FRAMES_PER_ISO_BUF *
+ ISO_BUF_COUNT), dec->iso_buffer,
+ dec->iso_dma_handle);
+
+}
+
+static int
+ttusb_dec_alloc_iso_urbs(ttusb_dec_t * dec)
+{
+
+ int i;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ dec->iso_buffer =
+ pci_alloc_consistent(NULL,
+ ISO_FRAME_SIZE * (FRAMES_PER_ISO_BUF *
+ ISO_BUF_COUNT),
+ &dec->iso_dma_handle);
+
+ memset(dec->iso_buffer, 0,
+ sizeof (ISO_FRAME_SIZE * (FRAMES_PER_ISO_BUF * ISO_BUF_COUNT)));
+
+ for (i = 0; i < ISO_BUF_COUNT; i++) {
+
+ struct urb *urb;
+
+ if (!(urb = usb_alloc_urb(FRAMES_PER_ISO_BUF))) {
+
+ ttusb_dec_free_iso_urbs(dec);
+ return -ENOMEM;
+
+ }
+
+ dec->iso_urb[i] = urb;
+
+ }
+
+ ttusb_dec_setup_urbs(dec);
+
+ for (i = 0; i < ISO_BUF_COUNT; i++) {
+
+ int next = (i + 1) % ISO_BUF_COUNT;
+ dec->iso_urb[i]->next = dec->iso_urb[next];
+
+ }
+
+ return 0;
+
+}
+
+static void
+ttusb_dec_init_v_pes(ttusb_dec_t * dec)
+{
+
+ dprintk("%s\n", __FUNCTION__);
+
+ dec->v_pes[0] = 0x00;
+ dec->v_pes[1] = 0x00;
+ dec->v_pes[2] = 0x01;
+ dec->v_pes[3] = 0xe0;
+
+}
+
+static void
+ttusb_dec_init_usb(ttusb_dec_t * dec)
+{
+
+ dprintk("%s\n", __FUNCTION__);
+
+ usb_set_configuration(dec->udev, 1);
+ usb_set_interface(dec->udev, 0, 0);
+
+ dec->command_pipe = usb_sndbulkpipe(dec->udev, COMMAND_PIPE);
+ dec->result_pipe = usb_rcvbulkpipe(dec->udev, RESULT_PIPE);
+ dec->stream_pipe = usb_rcvisocpipe(dec->udev, STREAM_PIPE);
+
+ ttusb_dec_alloc_iso_urbs(dec);
+
+}
+
+#include "dsp_dec2000.h"
+
+static int
+ttusb_dec_boot_dsp(ttusb_dec_t * dec)
+{
+
+ int i, j, actual_len, result, size, trans_count;
+ u8 b0[] =
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x59, 0x61, 0x00 };
+ u8 b1[] = { 0x61 };
+ u8 b[ARM_PACKET_SIZE];
+ u8 c[COMMAND_PACKET_SIZE];
+ u32 dsp_length = htonl(sizeof (dsp_dec2000));
+
+ dprintk("%s\n", __FUNCTION__);
+
+ memcpy(b0, &dsp_length, 4);
+
+ result = ttusb_dec_send_command(dec, 0x41, b0, sizeof (b0), c);
+
+ if (result < 0)
+ return result;
+
+ trans_count = 0;
+ j = 0;
+
+ for (i = 0; i < sizeof (dsp_dec2000); i += COMMAND_PACKET_SIZE) {
+
+ size = sizeof (dsp_dec2000) - i;
+ if (size > COMMAND_PACKET_SIZE)
+ size = COMMAND_PACKET_SIZE;
+
+ b[j + 0] = 0xaa;
+ b[j + 1] = trans_count++;
+ b[j + 2] = 0xf0;
+ b[j + 3] = size;
+ memcpy(&b[j + 4], &dsp_dec2000[i], size);
+
+ j += COMMAND_PACKET_SIZE + 4;
+
+ if (j >= ARM_PACKET_SIZE) {
+
+ result =
+ usb_bulk_msg(dec->udev, dec->command_pipe, b,
+ ARM_PACKET_SIZE, &actual_len, HZ / 10);
+ j = 0;
+
+ } else if (size < COMMAND_PACKET_SIZE) {
+
+ result =
+ usb_bulk_msg(dec->udev, dec->command_pipe, b,
+ j - COMMAND_PACKET_SIZE + size,
+ &actual_len, HZ / 10);
+
+ }
+
+ }
+
+ result = ttusb_dec_send_command(dec, 0x43, b1, sizeof (b1), c);
+
+ return 0;
+
+}
+
+static void
+ttusb_dec_init_stb(ttusb_dec_t * dec)
+{
+
+ u8 c[COMMAND_PACKET_SIZE];
+ int result;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ usb_set_configuration(dec->udev, 1);
+
+ result = ttusb_dec_send_command(dec, 0x08, NULL, 0, c);
+
+ if (result == 0x10)
+ ttusb_dec_boot_dsp(dec);
+
+}
+
+static int
+ttusb_dec_init_dvb(ttusb_dec_t * dec)
+{
+
+ int result;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ if ((result = dvb_register_adapter(&dec->adapter, "dec2000")) < 0) {
+
+ printk("%s: dvb_register_adapter failed (errno = %d)\n",
+ __FUNCTION__, result);
+
+ return result;
+
+ }
+
+ if (!
+ (dec->i2c_bus =
+ dvb_register_i2c_bus(ttusb_dec_i2c_master_xfer, dec, dec->adapter,
+ 0))) {
+
+ printk("%s: dvb_register_i2c_bus failed\n", __FUNCTION__);
+
+ dvb_unregister_adapter(dec->adapter);
+
+ return -ENOMEM;
+
+ }
+
+ dec->demux.dmx.vendor = "Technotrend/Hauppauge";
+ dec->demux.dmx.model = "DEC-2000-t";
+ dec->demux.dmx.id = "demux0_0";
+ dec->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING;
+
+ dec->demux.priv = (void *) dec;
+ dec->demux.filternum = 31;
+ dec->demux.feednum = 31;
+ dec->demux.start_feed = ttusb_dec_start_feed;
+ dec->demux.stop_feed = ttusb_dec_stop_feed;
+ dec->demux.write_to_decoder = NULL;
+
+ if ((result = dvb_dmx_init(&dec->demux)) < 0) {
+
+ printk("%s: dvb_dmx_init failed (errno = %d)\n", __FUNCTION__,
+ result);
+
+ dvb_unregister_i2c_bus(ttusb_dec_i2c_master_xfer, dec->adapter,
+ 0);
+ dvb_unregister_adapter(dec->adapter);
+
+ return result;
+
+ }
+
+ dec->dmxdev.filternum = 32;
+ dec->dmxdev.demux = &dec->demux.dmx;
+ dec->dmxdev.capabilities = 0;
+
+ if ((result = dvb_dmxdev_init(&dec->dmxdev, dec->adapter)) < 0) {
+
+ printk("%s: dvb_dmxdev_init failed (errno = %d)\n",
+ __FUNCTION__, result);
+
+ dvb_dmx_release(&dec->demux);
+ dvb_unregister_i2c_bus(ttusb_dec_i2c_master_xfer, dec->adapter,
+ 0);
+ dvb_unregister_adapter(dec->adapter);
+
+ return result;
+
+ }
+
+ dec->frontend.id = "tda10045";
+ dec->frontend.vendor = "TechnoTrend/Hauppauge";
+ dec->frontend.model = "DEC";
+ dec->frontend.source = DMX_FRONTEND_0;
+
+ if ((result =
+ dec->demux.dmx.add_frontend(&dec->demux.dmx,
+ &dec->frontend)) < 0) {
+
+ printk("%s: dvb_dmx_init failed (errno = %d)\n", __FUNCTION__,
+ result);
+
+ dvb_dmxdev_release(&dec->dmxdev);
+ dvb_dmx_release(&dec->demux);
+ dvb_unregister_i2c_bus(ttusb_dec_i2c_master_xfer, dec->adapter,
+ 0);
+ dvb_unregister_adapter(dec->adapter);
+
+ return result;
+
+ }
+
+ if ((result =
+ dec->demux.dmx.connect_frontend(&dec->demux.dmx,
+ &dec->frontend)) < 0) {
+
+ printk("%s: dvb_dmx_init failed (errno = %d)\n", __FUNCTION__,
+ result);
+
+ dec->demux.dmx.remove_frontend(&dec->demux.dmx, &dec->frontend);
+ dvb_dmxdev_release(&dec->dmxdev);
+ dvb_dmx_release(&dec->demux);
+ dvb_unregister_i2c_bus(ttusb_dec_i2c_master_xfer, dec->adapter,
+ 0);
+ dvb_unregister_adapter(dec->adapter);
+
+ return result;
+
+ }
+
+ return 0;
+
+}
+
+static void
+ttusb_dec_exit_dvb(ttusb_dec_t * dec)
+{
+
+ dprintk("%s\n", __FUNCTION__);
+
+ dec->demux.dmx.close(&dec->demux.dmx);
+ dec->demux.dmx.remove_frontend(&dec->demux.dmx, &dec->frontend);
+ dvb_dmxdev_release(&dec->dmxdev);
+ dvb_dmx_release(&dec->demux);
+ dvb_unregister_i2c_bus(ttusb_dec_i2c_master_xfer, dec->adapter, 0);
+ dvb_unregister_adapter(dec->adapter);
+
+}
+
+static void *
+ttusb_dec_probe(struct usb_device *udev, unsigned int ifnum,
+ const struct usb_device_id *id)
+{
+
+ ttusb_dec_t *dec;
+
+ dprintk("%s\n", __FUNCTION__);
+
+ if (ifnum != 0)
+ return NULL;
+
+ if (!(dec = kmalloc(sizeof (ttusb_dec_t), GFP_KERNEL))) {
+
+ printk("%s: couldn't allocate memory.\n", __FUNCTION__);
+ return NULL;
+
+ }
+
+ memset(dec, 0, sizeof (ttusb_dec_t));
+
+ dec->udev = udev;
+
+ ttusb_dec_init_usb(dec);
+
+ ttusb_dec_init_stb(dec);
+
+ ttusb_dec_init_dvb(dec);
+
+ ttusb_dec_init_v_pes(dec);
+
+ return (void *) dec;
+
+}
+
+static void
+ttusb_dec_disconnect(struct usb_device *udev, void *data)
+{
+
+ dprintk("%s\n", __FUNCTION__);
+
+ ttusb_dec_t *dec = data;
+
+ ttusb_dec_stop_iso_xfer(dec);
+
+ ttusb_dec_exit_dvb(dec);
+
+ ttusb_dec_free_iso_urbs(dec);
+
+ kfree(dec);
+
+}
+
+static struct usb_device_id ttusb_dec_table[] = {
+
+ {USB_DEVICE(0x0b48, 0x1006)}, /* Unconfirmed */
+ {USB_DEVICE(0x0b48, 0x1007)}, /* Unconfirmed */
+ {USB_DEVICE(0x0b48, 0x1008)}, /* DEC 2000 t */
+ {}
+
+};
+
+static struct usb_driver ttusb_dec_driver = {
+
+ name: DRIVER_NAME,
+ probe: ttusb_dec_probe,
+ disconnect: ttusb_dec_disconnect,
+ id_table: ttusb_dec_table,
+
+};
+
+static int __init
+ttusb_dec_init(void)
+{
+
+ int result;
+
+ if ((result = usb_register(&ttusb_dec_driver)) < 0) {
+
+ printk("%s: initialisation failed (Error %d).\n", __FUNCTION__,
+ result);
+ return result;
+
+ }
+
+ return 0;
+
+}
+
+static void __exit
+ttusb_dec_exit(void)
+{
+
+ usb_deregister(&ttusb_dec_driver);
+
+}
+
+module_init(ttusb_dec_init);
+module_exit(ttusb_dec_exit);
+
+MODULE_AUTHOR("Alex Woods <linux-dvb@giblets.org>");
+MODULE_DESCRIPTION(DRIVER_NAME);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, ttusb_dec_table);
+
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug level");
diff --git a/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.h b/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.h
new file mode 100644
index 000000000..d66edadc6
--- /dev/null
+++ b/linux/drivers/media/dvb/ttusb-dec/ttusb_dec.h
@@ -0,0 +1,83 @@
+/*
+ * TTUSB DEC Driver
+ *
+ * Copyright (C) 2003 Alex Woods <linux-dvb@giblets.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _TTUSB_DEC_H
+#define _TTUSB_DEC_H
+
+#include "dmxdev.h"
+#include "dvb_demux.h"
+#include "dvb_filter.h"
+#include "dvb_i2c.h"
+
+#define DRIVER_NAME "TechnoTrend/Hauppauge DEC USB"
+
+#define COMMAND_PIPE 0x03
+#define RESULT_PIPE 0x84
+#define STREAM_PIPE 0x88
+
+#define COMMAND_PACKET_SIZE 0x3c
+#define ARM_PACKET_SIZE 0x1000
+
+#define ISO_BUF_COUNT 0x04
+#define FRAMES_PER_ISO_BUF 0x04
+#define ISO_FRAME_SIZE 0x0380
+
+#define MAX_AV_PES_LENGTH 6144
+
+typedef struct ttusb_dec_s {
+
+ /* DVB bits */
+ struct dvb_adapter *adapter;
+ dmxdev_t dmxdev;
+ struct dvb_demux demux;
+ dmx_frontend_t frontend;
+ struct dvb_i2c_bus *i2c_bus;
+
+ u16 pid[DMX_PES_OTHER];
+
+ /* USB bits */
+ struct usb_device *udev;
+ u8 trans_count;
+ unsigned int command_pipe;
+ unsigned int result_pipe;
+ unsigned int stream_pipe;
+ int interface;
+
+ void *iso_buffer;
+ dma_addr_t iso_dma_handle;
+ struct urb *iso_urb[ISO_BUF_COUNT];
+ int iso_streaming;
+
+ u8 av_pes[MAX_AV_PES_LENGTH + 4];
+ int av_pes_state;
+ int av_pes_length;
+ int av_pes_payload_length;
+
+ dvb_filter_pes2ts_t a_pes2ts;
+ dvb_filter_pes2ts_t v_pes2ts;
+
+ u8 v_pes[16 + MAX_AV_PES_LENGTH];
+ int v_pes_length;
+ int v_pes_postbytes;
+
+} ttusb_dec_t;
+
+#endif