Commit 6455aafe authored by Michael Hunold's avatar Michael Hunold Committed by Linus Torvalds

[PATCH] DVB: Revamp of the TTUSB-DEC driver

 - Alter hotplug firmware naming to fit in with dvb standard.
 - Use the hotplug firmware loader for 2.6 kernels instead of compiling
   the firmware into the module. 
 - Integrate frontend into ttusb_dec module and remove pseudo-i2c bits,
   move ttusb_dec header into source file.
 - Rudimentary section filter support (enough for scan).
parent 6569e964
...@@ -9,6 +9,7 @@ Supported: ...@@ -9,6 +9,7 @@ Supported:
Linux Kernels 2.4 and 2.6 Linux Kernels 2.4 and 2.6
Video Streaming Video Streaming
Audio Streaming Audio Streaming
Section Filters
Channel Zapping Channel Zapping
Hotplug firmware loader under 2.6 kernels Hotplug firmware loader under 2.6 kernels
...@@ -16,14 +17,10 @@ In Progress: ...@@ -16,14 +17,10 @@ In Progress:
DEC3000-s DEC3000-s
To Do: To Do:
Section data
Teletext streams
Tuner status information Tuner status information
DVB network interface DVB network interface
Streaming video PC->DEC Streaming video PC->DEC
Note: Since section data can not be retreived yet, scan apps will not work.
Getting the Firmware Getting the Firmware
-------------------- --------------------
Currently, the driver only works with v2.15a of the firmware. The firmwares Currently, the driver only works with v2.15a of the firmware. The firmwares
...@@ -46,7 +43,7 @@ mv STB_PC_S.bin /etc/dvb/dec3000s.bin ...@@ -46,7 +43,7 @@ mv STB_PC_S.bin /etc/dvb/dec3000s.bin
Hotplug Firmware Loading for 2.6 kernels Hotplug Firmware Loading for 2.6 kernels
---------------------------------------- ----------------------------------------
For 2.6 kernels the firmware is loaded at the point that the driver module is For 2.6 kernels the firmware is loaded at the point that the driver module is
loaded. See linux/Documentation/dvb/FIRMWARE for more information. loaded. See linux/Documentation/dvb/firmware.txt for more information.
mv STB_PC_T.bin /usr/lib/hotplug/firmware/dec2000t.bin mv STB_PC_T.bin /usr/lib/hotplug/firmware/dvb-ttusb-dec-2000t-2.15a.fw
mv STB_PC_S.bin /usr/lib/hotplug/firmware/dec3000s.bin mv STB_PC_S.bin /usr/lib/hotplug/firmware/dvb-ttusb-dec-3000s-2.15a.fw
...@@ -13,15 +13,6 @@ config DVB_TTUSB_DEC ...@@ -13,15 +13,6 @@ config DVB_TTUSB_DEC
The DEC devices require firmware in order to boot into a mode in The DEC devices require firmware in order to boot into a mode in
which they are slaves to the PC. See which they are slaves to the PC. See
linux/Documentation/dvb/FIRMWARE for details. linux/Documentation/dvb/ttusb-dec.txt for details.
The firmware can be obtained and put into the default
locations as follows:
wget http://hauppauge.lightpath.net/de/dec215a.exe
unzip -j dec215a.exe Software/Oem/STB/App/Boot/STB_PC_T.bin
mv STB_PC_T.bin /usr/lib/hotplug/firmware/dec2000t.bin
unzip -j dec215a.exe Software/Oem/STB/App/Boot/STB_PC_S.bin
mv STB_PC_S.bin /usr/lib/hotplug/firmware/dec3000s.bin
Say Y if you own such a device and want to use it. Say Y if you own such a device and want to use it.
/*
* 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 <linux/init.h>
#include <linux/module.h>
#include "dvb_frontend.h"
#include "dvb_functions.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, void **data)
{
dprintk("%s\n", __FUNCTION__);
return dvb_register_frontend(dec2000_frontend_ioctl, i2c, NULL,
&dec2000_frontend_info);
}
static void dec2000_frontend_detach(struct dvb_i2c_bus *i2c, void *data)
{
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");
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main (int argc, char **argv)
{
unsigned char buf[8];
unsigned int i, count, bytes = 0;
int fd;
if (argc != 3) {
fprintf (stderr, "\n\tusage: %s <ucode.bin> <array_name>\n\n",
argv[0]);
return -1;
}
fd = open (argv[1], O_RDONLY);
printf ("\n#include <asm/types.h>\n\nu8 %s [] __initdata = {",
argv[2]);
while ((count = read (fd, buf, 8)) > 0) {
printf ("\n\t");
for (i=0;i<count;i++, bytes++)
printf ("0x%02x, ", buf[i]);
}
printf ("\n};\n\n");
close (fd);
return 0;
}
...@@ -60,13 +60,18 @@ static int debug = 0; ...@@ -60,13 +60,18 @@ static int debug = 0;
#define LOF_HI 10600000 #define LOF_HI 10600000
#define LOF_LO 9750000 #define LOF_LO 9750000
enum ttusb_model { enum ttusb_dec_model {
TTUSB_DEC2000T, TTUSB_DEC2000T,
TTUSB_DEC3000S TTUSB_DEC3000S
}; };
enum ttusb_dec_packet_type {
PACKET_AV_PES,
PACKET_SECTION
};
struct ttusb_dec { struct ttusb_dec {
enum ttusb_model model; enum ttusb_dec_model model;
char *model_name; char *model_name;
char *firmware_name; char *firmware_name;
...@@ -98,10 +103,14 @@ struct ttusb_dec { ...@@ -98,10 +103,14 @@ struct ttusb_dec {
int iso_stream_count; int iso_stream_count;
struct semaphore iso_sem; struct semaphore iso_sem;
u8 av_pes[MAX_AV_PES_LENGTH + 4]; u8 packet[MAX_AV_PES_LENGTH + 4];
int av_pes_state; enum ttusb_dec_packet_type packet_type;
int av_pes_length; int packet_state;
int av_pes_payload_length; int packet_length;
int packet_payload_length;
int av_pes_stream_count;
int filter_stream_count;
struct dvb_filter_pes2ts a_pes2ts; struct dvb_filter_pes2ts a_pes2ts;
struct dvb_filter_pes2ts v_pes2ts; struct dvb_filter_pes2ts v_pes2ts;
...@@ -114,6 +123,9 @@ struct ttusb_dec { ...@@ -114,6 +123,9 @@ struct ttusb_dec {
struct tasklet_struct urb_tasklet; struct tasklet_struct urb_tasklet;
spinlock_t urb_frame_list_lock; spinlock_t urb_frame_list_lock;
struct list_head filter_info_list;
spinlock_t filter_info_list_lock;
int active; /* Loaded successfully */ int active; /* Loaded successfully */
}; };
...@@ -123,6 +135,12 @@ struct urb_frame { ...@@ -123,6 +135,12 @@ struct urb_frame {
struct list_head urb_frame_list; struct list_head urb_frame_list;
}; };
struct filter_info {
u8 stream_id;
struct dvb_demux_filter *filter;
struct list_head filter_info_list;
};
static struct dvb_frontend_info dec2000t_frontend_info = { static struct dvb_frontend_info dec2000t_frontend_info = {
.name = "TechnoTrend/Hauppauge DEC2000-t Frontend", .name = "TechnoTrend/Hauppauge DEC2000-t Frontend",
.type = FE_OFDM, .type = FE_OFDM,
...@@ -179,8 +197,8 @@ static int ttusb_dec_send_command(struct ttusb_dec *dec, const u8 command, ...@@ -179,8 +197,8 @@ static int ttusb_dec_send_command(struct ttusb_dec *dec, const u8 command,
printk("\n"); printk("\n");
} }
result = usb_bulk_msg(dec->udev, dec->command_pipe, b, sizeof(b), result = usb_bulk_msg(dec->udev, dec->command_pipe, b,
&actual_len, HZ); sizeof(b), &actual_len, HZ);
if (result) { if (result) {
printk("%s: command bulk message failed: error %d\n", printk("%s: command bulk message failed: error %d\n",
...@@ -189,8 +207,8 @@ static int ttusb_dec_send_command(struct ttusb_dec *dec, const u8 command, ...@@ -189,8 +207,8 @@ static int ttusb_dec_send_command(struct ttusb_dec *dec, const u8 command,
return result; return result;
} }
result = usb_bulk_msg(dec->udev, dec->result_pipe, c, sizeof(c), result = usb_bulk_msg(dec->udev, dec->result_pipe, c,
&actual_len, HZ); sizeof(c), &actual_len, HZ);
if (result) { if (result) {
printk("%s: result bulk message failed: error %d\n", printk("%s: result bulk message failed: error %d\n",
...@@ -227,8 +245,9 @@ static int ttusb_dec_av_pes2ts_cb(void *priv, unsigned char *data) ...@@ -227,8 +245,9 @@ static int ttusb_dec_av_pes2ts_cb(void *priv, unsigned char *data)
static void ttusb_dec_set_pids(struct ttusb_dec *dec) static void ttusb_dec_set_pids(struct ttusb_dec *dec)
{ {
u8 b[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, u8 b[] = { 0x00, 0x00, 0x00, 0x00,
0xff, 0xff }; 0x00, 0x00, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff };
u16 pcr = htons(dec->pid[DMX_PES_PCR]); u16 pcr = htons(dec->pid[DMX_PES_PCR]);
u16 audio = htons(dec->pid[DMX_PES_AUDIO]); u16 audio = htons(dec->pid[DMX_PES_AUDIO]);
...@@ -253,34 +272,13 @@ static void ttusb_dec_set_pids(struct ttusb_dec *dec) ...@@ -253,34 +272,13 @@ static void ttusb_dec_set_pids(struct ttusb_dec *dec)
static void ttusb_dec_process_av_pes(struct ttusb_dec * dec, u8 * av_pes, static void ttusb_dec_process_av_pes(struct ttusb_dec * dec, u8 * av_pes,
int length) int length)
{ {
int i; if (length < 8) {
u16 csum = 0; printk("%s: packet too short - discarding\n", __FUNCTION__);
u8 c;
if (length < 16) {
printk("%s: packet too short.\n", __FUNCTION__);
return; return;
} }
for (i = 0; i < length; i += 2) { if (length > 8 + MAX_AV_PES_LENGTH) {
csum ^= le16_to_cpup((u16 *)(av_pes + i)); printk("%s: packet too long - discarding\n", __FUNCTION__);
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; return;
} }
...@@ -297,16 +295,14 @@ static void ttusb_dec_process_av_pes(struct ttusb_dec * dec, u8 * av_pes, ...@@ -297,16 +295,14 @@ static void ttusb_dec_process_av_pes(struct ttusb_dec * dec, u8 * av_pes,
&av_pes[12], prebytes); &av_pes[12], prebytes);
dvb_filter_pes2ts(&dec->v_pes2ts, dec->v_pes, dvb_filter_pes2ts(&dec->v_pes2ts, dec->v_pes,
dec->v_pes_length + prebytes, dec->v_pes_length + prebytes, 1);
1);
} }
if (av_pes[5] & 0x10) { if (av_pes[5] & 0x10) {
dec->v_pes[7] = 0x80; dec->v_pes[7] = 0x80;
dec->v_pes[8] = 0x05; dec->v_pes[8] = 0x05;
dec->v_pes[9] = 0x21 | dec->v_pes[9] = 0x21 | ((av_pes[8] & 0xc0) >> 5);
((av_pes[8] & 0xc0) >> 5);
dec->v_pes[10] = ((av_pes[8] & 0x3f) << 2) | dec->v_pes[10] = ((av_pes[8] & 0x3f) << 2) |
((av_pes[9] & 0xc0) >> 6); ((av_pes[9] & 0xc0) >> 6);
dec->v_pes[11] = 0x01 | dec->v_pes[11] = 0x01 |
...@@ -314,18 +310,17 @@ static void ttusb_dec_process_av_pes(struct ttusb_dec * dec, u8 * av_pes, ...@@ -314,18 +310,17 @@ static void ttusb_dec_process_av_pes(struct ttusb_dec * dec, u8 * av_pes,
((av_pes[10] & 0x80) >> 6); ((av_pes[10] & 0x80) >> 6);
dec->v_pes[12] = ((av_pes[10] & 0x7f) << 1) | dec->v_pes[12] = ((av_pes[10] & 0x7f) << 1) |
((av_pes[11] & 0xc0) >> 7); ((av_pes[11] & 0xc0) >> 7);
dec->v_pes[13] = 0x01 | dec->v_pes[13] = 0x01 | ((av_pes[11] & 0x7f) << 1);
((av_pes[11] & 0x7f) << 1);
memcpy(&dec->v_pes[14], &av_pes[12 + prebytes], memcpy(&dec->v_pes[14], &av_pes[12 + prebytes],
length - 16 - prebytes); length - 12 - prebytes);
dec->v_pes_length = 14 + length - 16 - prebytes; dec->v_pes_length = 14 + length - 12 - prebytes;
} else { } else {
dec->v_pes[7] = 0x00; dec->v_pes[7] = 0x00;
dec->v_pes[8] = 0x00; dec->v_pes[8] = 0x00;
memcpy(&dec->v_pes[9], &av_pes[8], length - 12); memcpy(&dec->v_pes[9], &av_pes[8], length - 8);
dec->v_pes_length = 9 + length - 12; dec->v_pes_length = 9 + length - 8;
} }
dec->v_pes_postbytes = postbytes; dec->v_pes_postbytes = postbytes;
...@@ -349,7 +344,7 @@ static void ttusb_dec_process_av_pes(struct ttusb_dec * dec, u8 * av_pes, ...@@ -349,7 +344,7 @@ static void ttusb_dec_process_av_pes(struct ttusb_dec * dec, u8 * av_pes,
} }
case 0x02: /* MainAudioStream */ case 0x02: /* MainAudioStream */
dvb_filter_pes2ts(&dec->a_pes2ts, &av_pes[8], length - 12, dvb_filter_pes2ts(&dec->a_pes2ts, &av_pes[8], length - 8,
av_pes[5] & 0x10); av_pes[5] & 0x10);
break; break;
...@@ -357,93 +352,194 @@ static void ttusb_dec_process_av_pes(struct ttusb_dec * dec, u8 * av_pes, ...@@ -357,93 +352,194 @@ static void ttusb_dec_process_av_pes(struct ttusb_dec * dec, u8 * av_pes,
printk("%s: unknown AV_PES type: %02x.\n", __FUNCTION__, printk("%s: unknown AV_PES type: %02x.\n", __FUNCTION__,
av_pes[2]); av_pes[2]);
break; break;
}
}
static void ttusb_dec_process_filter(struct ttusb_dec *dec, u8 *packet,
int length)
{
struct list_head *item;
struct filter_info *finfo;
struct dvb_demux_filter *filter = NULL;
unsigned long flags;
u8 sid;
sid = packet[1];
spin_lock_irqsave(&dec->filter_info_list_lock, flags);
for (item = dec->filter_info_list.next; item != &dec->filter_info_list;
item = item->next) {
finfo = list_entry(item, struct filter_info, filter_info_list);
if (finfo->stream_id == sid) {
filter = finfo->filter;
break;
}
}
spin_unlock_irqrestore(&dec->filter_info_list_lock, flags);
if (filter)
filter->feed->cb.sec(&packet[2], length - 2, NULL, 0,
&filter->filter, DMX_OK);
}
static void ttusb_dec_process_packet(struct ttusb_dec *dec)
{
int i;
u16 csum = 0;
if (dec->packet_length % 2) {
printk("%s: odd sized packet - discarding\n", __FUNCTION__);
return;
}
for (i = 0; i < dec->packet_length; i += 2)
csum ^= ((dec->packet[i] << 8) + dec->packet[i + 1]);
if (csum) {
printk("%s: checksum failed - discarding\n", __FUNCTION__);
return;
}
switch (dec->packet_type) {
case PACKET_AV_PES:
if (dec->av_pes_stream_count)
ttusb_dec_process_av_pes(dec, dec->packet,
dec->packet_payload_length);
break;
case PACKET_SECTION:
if (dec->filter_stream_count)
ttusb_dec_process_filter(dec, dec->packet,
dec->packet_payload_length);
break;
}
}
static void swap_bytes(u8 *b, int length)
{
u8 c;
length -= length % 2;
for (; length; b += 2, length -= 2) {
c = *b;
*b = *(b + 1);
*(b + 1) = c;
} }
} }
static void ttusb_dec_process_urb_frame(struct ttusb_dec * dec, u8 * b, static void ttusb_dec_process_urb_frame(struct ttusb_dec * dec, u8 * b,
int length) int length)
{ {
swap_bytes(b, length);
while (length) { while (length) {
switch (dec->av_pes_state) { switch (dec->packet_state) {
case 0: case 0:
case 1: case 1:
case 2:
if (*b++ == 0xaa)
dec->packet_state++;
else
dec->packet_state = 0;
length--;
break;
case 3: case 3:
if (*b++ == 0xaa) { if (*b++ == 0x00) {
dec->av_pes_state++; dec->packet_state++;
if (dec->av_pes_state == 4) dec->packet_length = 0;
dec->av_pes_length = 0;
} else { } else {
dec->av_pes_state = 0; dec->packet_state = 0;
} }
length--; length--;
break; break;
case 2: case 4:
if (*b++ == 0x00) { dec->packet[dec->packet_length++] = *b++;
dec->av_pes_state++;
if (dec->packet_length == 3) {
if (dec->packet[0] == 'A' &&
dec->packet[1] == 'V') {
dec->packet_type = PACKET_AV_PES;
dec->packet_state++;
} else if (dec->packet[0] == 'S') {
dec->packet_type = PACKET_SECTION;
dec->packet_state++;
} else { } else {
dec->av_pes_state = 0; dec->packet_state = 0;
}
} }
length--; length--;
break; break;
case 4: case 5:
dec->av_pes[dec->av_pes_length++] = *b++; dec->packet[dec->packet_length++] = *b++;
if (dec->av_pes_length == 8) { if (dec->packet_type == PACKET_AV_PES &&
dec->av_pes_state++; dec->packet_length == 8) {
dec->av_pes_payload_length = le16_to_cpup( dec->packet_state++;
(u16 *)(dec->av_pes + 6)); dec->packet_payload_length = 8 +
(dec->packet[6] << 8) +
dec->packet[7];
} else if (dec->packet_type == PACKET_SECTION &&
dec->packet_length == 5) {
dec->packet_state++;
dec->packet_payload_length = 5 +
((dec->packet[3] & 0x0f) << 8) +
dec->packet[4];
} }
length--; length--;
break; break;
case 5: { case 6: {
int remainder = dec->av_pes_payload_length + int remainder = dec->packet_payload_length -
8 - dec->av_pes_length; dec->packet_length;
if (length >= remainder) { if (length >= remainder) {
memcpy(dec->av_pes + dec->av_pes_length, memcpy(dec->packet + dec->packet_length,
b, remainder); b, remainder);
dec->av_pes_length += remainder; dec->packet_length += remainder;
b += remainder; b += remainder;
length -= remainder; length -= remainder;
dec->av_pes_state++; dec->packet_state++;
} else { } else {
memcpy(&dec->av_pes[dec->av_pes_length], memcpy(&dec->packet[dec->packet_length],
b, length); b, length);
dec->av_pes_length += length; dec->packet_length += length;
length = 0; length = 0;
} }
break; break;
} }
case 6: case 7: {
dec->av_pes[dec->av_pes_length++] = *b++; int tail = 4;
dec->packet[dec->packet_length++] = *b++;
if (dec->packet_type == PACKET_SECTION &&
dec->packet_payload_length % 2)
tail++;
if (dec->av_pes_length == if (dec->packet_length ==
8 + dec->av_pes_payload_length + 4) { dec->packet_payload_length + tail) {
ttusb_dec_process_av_pes(dec, dec->av_pes, ttusb_dec_process_packet(dec);
dec->av_pes_length); dec->packet_state = 0;
dec->av_pes_state = 0;
} }
length--; length--;
break; break;
}
default: default:
printk("%s: illegal packet state encountered.\n", printk("%s: illegal packet state encountered.\n",
__FUNCTION__); __FUNCTION__);
dec->av_pes_state = 0; dec->packet_state = 0;
} }
} }
} }
...@@ -561,12 +657,8 @@ static void ttusb_dec_stop_iso_xfer(struct ttusb_dec *dec) ...@@ -561,12 +657,8 @@ static void ttusb_dec_stop_iso_xfer(struct ttusb_dec *dec)
dec->iso_stream_count--; dec->iso_stream_count--;
if (!dec->iso_stream_count) { if (!dec->iso_stream_count) {
u8 b0[] = { 0x00 };
for (i = 0; i < ISO_BUF_COUNT; i++) for (i = 0; i < ISO_BUF_COUNT; i++)
usb_unlink_urb(dec->iso_urb[i]); usb_unlink_urb(dec->iso_urb[i]);
ttusb_dec_send_command(dec, 0x81, sizeof(b0), b0, NULL, NULL);
} }
up(&dec->iso_sem); up(&dec->iso_sem);
...@@ -594,10 +686,6 @@ static int ttusb_dec_start_iso_xfer(struct ttusb_dec *dec) ...@@ -594,10 +686,6 @@ static int ttusb_dec_start_iso_xfer(struct ttusb_dec *dec)
return -EAGAIN; return -EAGAIN;
if (!dec->iso_stream_count) { if (!dec->iso_stream_count) {
u8 b0[] = { 0x05 };
ttusb_dec_send_command(dec, 0x80, sizeof(b0), b0, NULL, NULL);
ttusb_dec_setup_urbs(dec); ttusb_dec_setup_urbs(dec);
for (i = 0; i < ISO_BUF_COUNT; i++) { for (i = 0; i < ISO_BUF_COUNT; i++) {
...@@ -616,7 +704,7 @@ static int ttusb_dec_start_iso_xfer(struct ttusb_dec *dec) ...@@ -616,7 +704,7 @@ static int ttusb_dec_start_iso_xfer(struct ttusb_dec *dec)
} }
} }
dec->av_pes_state = 0; dec->packet_state = 0;
dec->v_pes_postbytes = 0; dec->v_pes_postbytes = 0;
} }
...@@ -627,34 +715,14 @@ static int ttusb_dec_start_iso_xfer(struct ttusb_dec *dec) ...@@ -627,34 +715,14 @@ static int ttusb_dec_start_iso_xfer(struct ttusb_dec *dec)
return 0; return 0;
} }
static int ttusb_dec_start_feed(struct dvb_demux_feed *dvbdmxfeed) static int ttusb_dec_start_ts_feed(struct dvb_demux_feed *dvbdmxfeed)
{ {
struct dvb_demux *dvbdmx = dvbdmxfeed->demux; struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
struct ttusb_dec *dec = dvbdmx->priv; struct ttusb_dec *dec = dvbdmx->priv;
u8 b0[] = { 0x05 };
dprintk("%s\n", __FUNCTION__); dprintk("%s\n", __FUNCTION__);
if (!dvbdmx->dmx.frontend)
return -EINVAL;
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:"); dprintk(" ts_type:");
if (dvbdmxfeed->ts_type & TS_DECODER) if (dvbdmxfeed->ts_type & TS_DECODER)
...@@ -704,22 +772,151 @@ static int ttusb_dec_start_feed(struct dvb_demux_feed *dvbdmxfeed) ...@@ -704,22 +772,151 @@ static int ttusb_dec_start_feed(struct dvb_demux_feed *dvbdmxfeed)
} }
ttusb_dec_send_command(dec, 0x80, sizeof(b0), b0, NULL, NULL);
dec->av_pes_stream_count++;
ttusb_dec_start_iso_xfer(dec); ttusb_dec_start_iso_xfer(dec);
return 0; return 0;
} }
static int ttusb_dec_stop_feed(struct dvb_demux_feed *dvbdmxfeed) static int ttusb_dec_start_sec_feed(struct dvb_demux_feed *dvbdmxfeed)
{ {
struct ttusb_dec *dec = dvbdmxfeed->demux->priv; struct ttusb_dec *dec = dvbdmxfeed->demux->priv;
u8 b0[] = { 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00 };
u16 pid;
u8 c[COMMAND_PACKET_SIZE];
int c_length;
int result;
struct filter_info *finfo;
unsigned long flags;
u8 x = 1;
dprintk("%s\n", __FUNCTION__); dprintk("%s\n", __FUNCTION__);
pid = htons(dvbdmxfeed->pid);
memcpy(&b0[0], &pid, 2);
memcpy(&b0[4], &x, 1);
memcpy(&b0[5], &dvbdmxfeed->filter->filter.filter_value[0], 1);
result = ttusb_dec_send_command(dec, 0x60, sizeof(b0), b0,
&c_length, c);
if (!result) {
if (c_length == 2) {
if (!(finfo = kmalloc(sizeof(struct filter_info),
GFP_ATOMIC)))
return -ENOMEM;
finfo->stream_id = c[1];
finfo->filter = dvbdmxfeed->filter;
spin_lock_irqsave(&dec->filter_info_list_lock, flags);
list_add_tail(&finfo->filter_info_list,
&dec->filter_info_list);
spin_unlock_irqrestore(&dec->filter_info_list_lock,
flags);
dvbdmxfeed->priv = finfo;
dec->filter_stream_count++;
ttusb_dec_start_iso_xfer(dec);
return 0;
}
return -EAGAIN;
} else
return result;
}
static int ttusb_dec_start_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
dprintk("%s\n", __FUNCTION__);
if (!dvbdmx->dmx.frontend)
return -EINVAL;
dprintk(" pid: 0x%04X\n", dvbdmxfeed->pid);
switch (dvbdmxfeed->type) {
case DMX_TYPE_TS:
return ttusb_dec_start_ts_feed(dvbdmxfeed);
break;
case DMX_TYPE_SEC:
return ttusb_dec_start_sec_feed(dvbdmxfeed);
break;
default:
dprintk(" type: unknown (%d)\n", dvbdmxfeed->type);
return -EINVAL;
}
}
static int ttusb_dec_stop_ts_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct ttusb_dec *dec = dvbdmxfeed->demux->priv;
u8 b0[] = { 0x00 };
ttusb_dec_send_command(dec, 0x81, sizeof(b0), b0, NULL, NULL);
dec->av_pes_stream_count--;
ttusb_dec_stop_iso_xfer(dec); ttusb_dec_stop_iso_xfer(dec);
return 0; return 0;
} }
static int ttusb_dec_stop_sec_feed(struct dvb_demux_feed *dvbdmxfeed)
{
struct ttusb_dec *dec = dvbdmxfeed->demux->priv;
u8 b0[] = { 0x00, 0x00 };
struct filter_info *finfo = (struct filter_info *)dvbdmxfeed->priv;
unsigned long flags;
b0[1] = finfo->stream_id;
spin_lock_irqsave(&dec->filter_info_list_lock, flags);
list_del(&finfo->filter_info_list);
spin_unlock_irqrestore(&dec->filter_info_list_lock, flags);
kfree(finfo);
ttusb_dec_send_command(dec, 0x62, sizeof(b0), b0, NULL, NULL);
dec->filter_stream_count--;
ttusb_dec_stop_iso_xfer(dec);
return 0;
}
static int ttusb_dec_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
{
dprintk("%s\n", __FUNCTION__);
switch (dvbdmxfeed->type) {
case DMX_TYPE_TS:
return ttusb_dec_stop_ts_feed(dvbdmxfeed);
break;
case DMX_TYPE_SEC:
return ttusb_dec_stop_sec_feed(dvbdmxfeed);
break;
}
return 0;
}
static void ttusb_dec_free_iso_urbs(struct ttusb_dec *dec) static void ttusb_dec_free_iso_urbs(struct ttusb_dec *dec)
{ {
int i; int i;
...@@ -802,8 +999,9 @@ static void ttusb_dec_init_usb(struct ttusb_dec *dec) ...@@ -802,8 +999,9 @@ static void ttusb_dec_init_usb(struct ttusb_dec *dec)
static int ttusb_dec_boot_dsp(struct ttusb_dec *dec) static int ttusb_dec_boot_dsp(struct ttusb_dec *dec)
{ {
int i, j, actual_len, result, size, trans_count; int i, j, actual_len, result, size, trans_count;
u8 b0[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, u8 b0[] = { 0x00, 0x00, 0x00, 0x00,
0x00 }; 0x00, 0x00, 0x00, 0x00,
0x00, 0x00 };
u8 b1[] = { 0x61 }; u8 b1[] = { 0x61 };
u8 b[ARM_PACKET_SIZE]; u8 b[ARM_PACKET_SIZE];
u8 *firmware = NULL; u8 *firmware = NULL;
...@@ -1067,9 +1265,11 @@ static int ttusb_dec_2000t_frontend_ioctl(struct dvb_frontend *fe, unsigned int ...@@ -1067,9 +1265,11 @@ static int ttusb_dec_2000t_frontend_ioctl(struct dvb_frontend *fe, unsigned int
case FE_SET_FRONTEND: { case FE_SET_FRONTEND: {
struct dvb_frontend_parameters *p = struct dvb_frontend_parameters *p =
(struct dvb_frontend_parameters *)arg; (struct dvb_frontend_parameters *)arg;
u8 b[] = { 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, u8 b[] = { 0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff, 0x00, 0x00, 0x00, 0xff }; 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0xff,
0x00, 0x00, 0x00, 0xff };
u32 freq; u32 freq;
dprintk("%s: FE_SET_FRONTEND\n", __FUNCTION__); dprintk("%s: FE_SET_FRONTEND\n", __FUNCTION__);
...@@ -1112,8 +1312,8 @@ static int ttusb_dec_2000t_frontend_ioctl(struct dvb_frontend *fe, unsigned int ...@@ -1112,8 +1312,8 @@ static int ttusb_dec_2000t_frontend_ioctl(struct dvb_frontend *fe, unsigned int
return 0; return 0;
} }
static int ttusb_dec_3000s_frontend_ioctl(struct dvb_frontend *fe, unsigned int cmd, static int ttusb_dec_3000s_frontend_ioctl(struct dvb_frontend *fe,
void *arg) unsigned int cmd, void *arg)
{ {
struct ttusb_dec *dec = fe->data; struct ttusb_dec *dec = fe->data;
...@@ -1165,12 +1365,16 @@ static int ttusb_dec_3000s_frontend_ioctl(struct dvb_frontend *fe, unsigned int ...@@ -1165,12 +1365,16 @@ static int ttusb_dec_3000s_frontend_ioctl(struct dvb_frontend *fe, unsigned int
case FE_SET_FRONTEND: { case FE_SET_FRONTEND: {
struct dvb_frontend_parameters *p = struct dvb_frontend_parameters *p =
(struct dvb_frontend_parameters *)arg; (struct dvb_frontend_parameters *)arg;
u8 b[] = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, u8 b[] = { 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00 }; 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0d,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00 };
u32 freq; u32 freq;
u32 sym_rate; u32 sym_rate;
u32 band; u32 band;
...@@ -1183,7 +1387,8 @@ static int ttusb_dec_3000s_frontend_ioctl(struct dvb_frontend *fe, unsigned int ...@@ -1183,7 +1387,8 @@ static int ttusb_dec_3000s_frontend_ioctl(struct dvb_frontend *fe, unsigned int
p->u.qam.symbol_rate); p->u.qam.symbol_rate);
dprintk(" inversion->%d\n", p->inversion); dprintk(" inversion->%d\n", p->inversion);
freq = htonl(p->frequency * 1000 + (dec->hi_band ? LOF_HI : LOF_LO)); freq = htonl(p->frequency * 1000 +
(dec->hi_band ? LOF_HI : LOF_LO));
memcpy(&b[4], &freq, sizeof(u32)); memcpy(&b[4], &freq, sizeof(u32));
sym_rate = htonl(p->u.qam.symbol_rate); sym_rate = htonl(p->u.qam.symbol_rate);
memcpy(&b[12], &sym_rate, sizeof(u32)); memcpy(&b[12], &sym_rate, sizeof(u32));
...@@ -1265,6 +1470,24 @@ static void ttusb_dec_exit_frontend(struct ttusb_dec *dec) ...@@ -1265,6 +1470,24 @@ static void ttusb_dec_exit_frontend(struct ttusb_dec *dec)
dvb_unregister_frontend(dec->frontend_ioctl, &dec->i2c_bus); dvb_unregister_frontend(dec->frontend_ioctl, &dec->i2c_bus);
} }
static void ttusb_dec_init_filters(struct ttusb_dec *dec)
{
INIT_LIST_HEAD(&dec->filter_info_list);
dec->filter_info_list_lock = SPIN_LOCK_UNLOCKED;
}
static void ttusb_dec_exit_filters(struct ttusb_dec *dec)
{
struct list_head *item;
struct filter_info *finfo;
while ((item = dec->filter_info_list.next) != &dec->filter_info_list) {
finfo = list_entry(item, struct filter_info, filter_info_list);
list_del(&finfo->filter_info_list);
kfree(finfo);
}
}
static int ttusb_dec_probe(struct usb_interface *intf, static int ttusb_dec_probe(struct usb_interface *intf,
const struct usb_device_id *id) const struct usb_device_id *id)
{ {
...@@ -1288,13 +1511,13 @@ static int ttusb_dec_probe(struct usb_interface *intf, ...@@ -1288,13 +1511,13 @@ static int ttusb_dec_probe(struct usb_interface *intf,
case 0x1006: case 0x1006:
dec->model = TTUSB_DEC3000S; dec->model = TTUSB_DEC3000S;
dec->model_name = "DEC3000-s"; dec->model_name = "DEC3000-s";
dec->firmware_name = "dec3000s.bin"; dec->firmware_name = "dvb-ttusb-dec-3000s-2.15a.fw";
break; break;
case 0x1008: case 0x1008:
dec->model = TTUSB_DEC2000T; dec->model = TTUSB_DEC2000T;
dec->model_name = "DEC2000-t"; dec->model_name = "DEC2000-t";
dec->firmware_name = "dec2000t.bin"; dec->firmware_name = "dvb-ttusb-dec-2000t-2.15a.fw";
break; break;
} }
...@@ -1308,6 +1531,7 @@ static int ttusb_dec_probe(struct usb_interface *intf, ...@@ -1308,6 +1531,7 @@ static int ttusb_dec_probe(struct usb_interface *intf,
ttusb_dec_init_dvb(dec); ttusb_dec_init_dvb(dec);
ttusb_dec_init_frontend(dec); ttusb_dec_init_frontend(dec);
ttusb_dec_init_v_pes(dec); ttusb_dec_init_v_pes(dec);
ttusb_dec_init_filters(dec);
ttusb_dec_init_tasklet(dec); ttusb_dec_init_tasklet(dec);
dec->active = 1; dec->active = 1;
...@@ -1327,6 +1551,7 @@ static void ttusb_dec_disconnect(struct usb_interface *intf) ...@@ -1327,6 +1551,7 @@ static void ttusb_dec_disconnect(struct usb_interface *intf)
if (dec->active) { if (dec->active) {
ttusb_dec_exit_tasklet(dec); ttusb_dec_exit_tasklet(dec);
ttusb_dec_exit_filters(dec);
ttusb_dec_exit_usb(dec); ttusb_dec_exit_usb(dec);
ttusb_dec_exit_frontend(dec); ttusb_dec_exit_frontend(dec);
ttusb_dec_exit_dvb(dec); ttusb_dec_exit_dvb(dec);
......
/*
* 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 <asm/semaphore.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include "dmxdev.h"
#include "dvb_demux.h"
#include "dvb_filter.h"
#include "dvb_i2c.h"
#include "dvb_net.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
struct ttusb_dec {
/* DVB bits */
struct dvb_adapter *adapter;
struct dmxdev dmxdev;
struct dvb_demux demux;
struct dmx_frontend frontend;
struct dvb_i2c_bus *i2c_bus;
struct dvb_net dvb_net;
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;
struct semaphore usb_sem;
void *iso_buffer;
dma_addr_t iso_dma_handle;
struct urb *iso_urb[ISO_BUF_COUNT];
int iso_stream_count;
struct semaphore iso_sem;
u8 av_pes[MAX_AV_PES_LENGTH + 4];
int av_pes_state;
int av_pes_length;
int av_pes_payload_length;
struct dvb_filter_pes2ts a_pes2ts;
struct dvb_filter_pes2ts v_pes2ts;
u8 v_pes[16 + MAX_AV_PES_LENGTH];
int v_pes_length;
int v_pes_postbytes;
struct list_head urb_frame_list;
struct tasklet_struct urb_tasklet;
spinlock_t urb_frame_list_lock;
};
struct urb_frame {
u8 data[ISO_FRAME_SIZE];
int length;
struct list_head urb_frame_list;
};
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment