diff options
author | Alex Woods <devnull@localhost> | 2003-03-30 13:13:16 +0000 |
---|---|---|
committer | Alex Woods <devnull@localhost> | 2003-03-30 13:13:16 +0000 |
commit | 6d3197dd77c4f3b8e6477e9f0db4c65d66b41d4c (patch) | |
tree | b9b84b675e6e8c3bf7f915eaa2768431aa968203 /linux/drivers | |
parent | ce8a79b18f4f8a4d7d95b01bf417f9b4ce3734ec (diff) | |
download | mediapointer-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.c | 193 | ||||
-rw-r--r-- | linux/drivers/media/dvb/ttusb-dec/ttusb_dec.c | 1044 | ||||
-rw-r--r-- | linux/drivers/media/dvb/ttusb-dec/ttusb_dec.h | 83 |
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 |