diff options
author | Holger Waechtler <devnull@localhost> | 2003-03-17 20:38:28 +0000 |
---|---|---|
committer | Holger Waechtler <devnull@localhost> | 2003-03-17 20:38:28 +0000 |
commit | 8dd52d823f399f39d02e35cbd32da30175d0429b (patch) | |
tree | 03b99d56119d2d3e38c3673ceb187d6744a1b132 /linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c | |
parent | d8f67b42a822587316ffab72e131d9566bd6dd33 (diff) | |
download | mediapointer-dvb-s2-8dd52d823f399f39d02e35cbd32da30175d0429b.tar.gz mediapointer-dvb-s2-8dd52d823f399f39d02e35cbd32da30175d0429b.tar.bz2 |
ttusb driver revival, thanks to tmb for getting this working!!!
Diffstat (limited to 'linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c')
-rw-r--r-- | linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c | 1108 |
1 files changed, 1108 insertions, 0 deletions
diff --git a/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c new file mode 100644 index 000000000..a982e1afb --- /dev/null +++ b/linux/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c @@ -0,0 +1,1108 @@ +/* + * TTUSB DVB driver + * + * Copyright (c) 2002 Holger Waechtler <holger@convergence.de> + * Copyright (c) 2003 Felix Domke <tmbinc@gmx.net> + * + * 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. + */ +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/wait.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/delay.h> +#include <linux/time.h> +#include <asm/semaphore.h> +#include <asm/errno.h> + +#include "dvb_frontend.h" +#include "dmxdev.h" +#include "dvb_demux.h" +#include "dvb_net.h" + +#include <linux/dvb/frontend.h> +#include <linux/dvb/dmx.h> +#include <linux/pci.h> +#include "compat.h" + +/* + TTUSB_HWSECTIONS: + the DSP supports filtering in hardware, however, since the "muxstream" + is a bit braindead (no matching channel masks or no matching filter mask), + we won't support this - yet. it doesn't event support negative filters, + so the best way is maybe to keep TTUSB_HWSECTIONS undef'd and just + parse TS data. USB bandwith will be a problem when having large + datastreams, especially for dvb-net, but hey, that's not my problem. + + TTUSB_DISEQC, TTUSB_TONE: + let the STC do the diseqc/tone stuff. this isn't support at least with + my TTUSB, so let it undef'd unless you want to implement another + frontend. never tested. + + DEBUG: + define it to > 3 for really hardcore debugging. you probably don't want + this if the device is loading at all. +*/ + +static int debug = 1; + +#define dprintk(x...) do { if (debug) printk(KERN_DEBUG x); } while (0) + +#define ISO_BUF_COUNT 5 +#define FRAMES_PER_ISO_BUF 4 +#define ISO_FRAME_SIZE 912 +#define TTUSB_MAXCHANNEL 32 +#ifdef TTUSB_HWSECTIONS +#define TTUSB_MAXFILTER 16 /* ??? */ +#endif + +struct ttusb { + struct dvb_demux dvb_demux; + dmxdev_t dmxdev; + dvb_net_t dvbnet; + + /* our semaphore, for channel allocation/deallocation */ + struct semaphore sem; + + struct dvb_adapter *adapter; + struct usb_device *dev; + + int disconnecting; + int iso_streaming; + + unsigned int bulk_out_pipe; + unsigned int bulk_in_pipe; + unsigned int isoc_in_pipe; + + void *iso_buffer; + dma_addr_t iso_dma_handle; + + wait_queue_head_t wq; + + struct urb *iso_urb [ISO_BUF_COUNT]; + + int last_channel; + int last_filter; + + u8 c; /* transaction counter, wraps around... */ + fe_sec_tone_mode_t tone; + fe_sec_voltage_t voltage; + + int mux_state; // 0..2 - MuxSyncWord, 3 - nMuxPacks, 4 - muxpack + u8 mux_npacks; + u8 muxpack[256+4]; + int muxpack_ptr, muxpack_len; + + int insync; + + u8 last_result[32]; + + struct ttusb_channel + { + struct ttusb *ttusb; + struct dvb_demux_feed *dvbdmxfeed; + + int active; + int id; + int pid; + int type; /* 1 - TS, 2 - Filter */ +#ifdef TTUSB_HWSECTIONS + int filterstate[TTUSB_MAXFILTER]; /* 0: not busy, 1: busy */ +#endif + } channel[TTUSB_MAXCHANNEL]; +}; + +/* ugly workaround ... don't know why it's neccessary to read */ +/* all result codes. */ + +#define DEBUG 0 +static +int ttusb_cmd (struct ttusb *ttusb, + const u8 *data, int len) +{ + int actual_len; + int err; +#if DEBUG >= 3 + int i; + + printk(">"); + for (i=0; i<len; ++i) + printk(" %02x", data[i]); + printk("\n"); +#endif + err = usb_bulk_msg (ttusb->dev, ttusb->bulk_out_pipe, + (u8*)data, len, &actual_len, HZ); + if (err != 0) + { + dprintk ("%s: usb_bulk_msg(send) failed, err == %i!\n", + __FUNCTION__, err); + return err; + } + if (actual_len != len) + { + dprintk("%s: only wrote %d of %d bytes\n", __FUNCTION__, actual_len, len); + return -1; + } + + err = usb_bulk_msg (ttusb->dev, ttusb->bulk_in_pipe, + ttusb->last_result, 32, &actual_len, HZ); + if (err != 0) + { + printk("%s: failed, receive error %d\n", __FUNCTION__, err); + return err; + } +#if DEBUG >= 3 + actual_len = ttusb->last_result[3]+4; + printk("<"); + for (i=0; i<actual_len; ++i) + printk(" %02x", ttusb->last_result[i]); + printk("\n"); +#endif + return 0; +} + +static +int ttusb_result (struct ttusb *ttusb, + u8 *data, int len) +{ + memcpy(data, ttusb->last_result, len); + return 0; +} + +static +int ttusb_i2c_msg (struct ttusb *ttusb, + u8 addr, u8 *snd_buf, u8 snd_len, u8 *rcv_buf, u8 rcv_len) +{ + u8 b [0x28]; + u8 id = ++ttusb->c; + int i, err; + + if (snd_len > 0x28 - 7 || rcv_len > 0x20 - 7) + return -EINVAL; + + b [0] = 0xaa; + b [1] = id; + b [2] = 0x31; + b [3] = snd_len + 3; + b [4] = addr << 1; + b [5] = snd_len; + b [6] = rcv_len; + + for (i=0; i<snd_len; i++) + b [7+i] = snd_buf [i]; + + err = ttusb_cmd (ttusb, b, snd_len + 7); + + if (err) + return -EREMOTEIO; + + if (rcv_len > 0) { + err = ttusb_result (ttusb, b, 0x20); + + if (err || b [0] != 0x55 || b [1] != id) + { + dprintk ("%s: usb_bulk_msg(recv) failed, err == %i, id == %02x, b == ", __FUNCTION__, err, id); + return -EREMOTEIO; + } + + for (i=0; i<rcv_len; i++) + rcv_buf [i] = b [7+i]; + } + + return rcv_len; +} + +static +int ttusb_i2c_xfer (struct dvb_i2c_bus *i2c, const struct i2c_msg msg[], int num) +{ + struct ttusb *ttusb = i2c->data; + int i = 0; + int inc; + + if (down_interruptible (&ttusb->sem) < 0) + return -EAGAIN; + + while (i<num) { + u8 addr, snd_len, rcv_len, *snd_buf, *rcv_buf; + int err; + + if (num > i+1 && (msg[i+1].flags & I2C_M_RD)) { + addr = msg[i].addr; + snd_buf = msg[i].buf; + snd_len = msg[i].len; + rcv_buf = msg[i+1].buf; + rcv_len = msg[i+1].len; + inc = 2; + } else { + addr = msg[i].addr; + snd_buf = msg[i].buf; + snd_len = msg[i].len; + rcv_buf = NULL; + rcv_len = 0; + inc = 1; + } + + err = ttusb_i2c_msg (ttusb, addr, + snd_buf, snd_len, + rcv_buf, rcv_len); + + if (err < rcv_len) { + printk ("%s: i == %i\n", __FUNCTION__, i); + break; + } + + i += inc; + } + + up (&ttusb->sem); + return i; +} + + +#include "dvb-ttusb-dspbootcode.h" + +static +int ttusb_boot_dsp (struct ttusb *ttusb) +{ + int i, err; + u8 b [40]; + + /* BootBlock */ + b [0] = 0xaa; + b [2] = 0x13; + b [3] = 16; + + /* upload dsp code in 16 byte steps (36 didn't work for me ...) */ + /* 32 is max packet size, no messages should be splitted. */ + for (i=0; i<sizeof(dsp_bootcode); i+=16) { + memcpy (&b[4], &dsp_bootcode[i], 16); + + b [1] = ++ttusb->c; + + err = ttusb_cmd (ttusb, b, 20); + if (err) + goto done; + } + + /* last block ... */ + b [1] = ++ttusb->c; + b [2] = 0x13; + b [3] = 0; + + err = ttusb_cmd (ttusb, b, 4); + if (err) + goto done; + + /* BootEnd */ + b [1] = ++ttusb->c; + b [2] = 0x14; + b [3] = 0; + + err = ttusb_cmd (ttusb, b, 4); + +done: + if (err) { + dprintk ("%s: usb_bulk_msg() failed, return value %i!\n", + __FUNCTION__, err); + } + + return err; +} + + +static +int ttusb_set_channel (struct ttusb *ttusb, int chan_id, int filter_type, + int pid) +{ + int err; + /* SetChannel */ + u8 b [] = { 0xaa, ++ttusb->c, 0x22, 4, chan_id, filter_type, + (pid >> 8) & 0xff, pid & 0xff}; + + err = ttusb_cmd (ttusb, b, sizeof(b)); + return err; +} + + +static +int ttusb_del_channel (struct ttusb *ttusb, int channel_id) +{ + int err; + /* DelChannel */ + u8 b [] = { 0xaa, ++ttusb->c, 0x23, 1, channel_id }; + + err = ttusb_cmd (ttusb, b, sizeof(b)); + return err; +} + +#ifdef TTUSB_HWSECTIONS +static +int ttusb_set_filter (struct ttusb *ttusb, int filter_id, int associated_chan, + u8 filter [8], u8 mask [8]) +{ + int err; + /* SetFilter */ + u8 b [] = { 0xaa, 0, 0x24, 0x1a, filter_id, associated_chan, + filter[0], filter[1], filter[2], filter[3], + filter[4], filter[5], filter[6], filter[7], + filter[8], filter[9], filter[10], filter[11], + mask[0], mask[1], mask[2], mask[3], + mask[4], mask[5], mask[6], mask[7], + mask[8], mask[9], mask[10], mask[11]}; + + err = ttusb_cmd (ttusb, b, sizeof(b)); + return err; +} + + +static +int ttusb_del_filter (struct ttusb *ttusb, int filter_id) +{ + int err; + /* DelFilter */ + u8 b [] = { 0xaa, ++ttusb->c, 0x25, 1, filter_id }; + + err = ttusb_cmd (ttusb, b, sizeof(b)); + return err; +} +#endif + +static +int ttusb_init_controller (struct ttusb *ttusb) +{ + u8 b0 [] = { 0xaa, ++ttusb->c, 0x15, 1, 0 }; + u8 b1 [] = { 0xaa, ++ttusb->c, 0x15, 1, 1 }; + u8 b2 [] = { 0xaa, ++ttusb->c, 0x32, 1, 0 }; + /* i2c write read: 5 bytes, addr 0x10, 0x02 bytes write, 1 bytes read. */ + u8 b3 [] = { 0xaa, ++ttusb->c, 0x31, 5, 0x10, 0x02, 0x01, 0x00, 0x1e }; + u8 b4 [] = { 0x55, ttusb->c, 0x31, 4, 0x10, 0x02, 0x01, 0x00, 0x1e }; + + u8 get_version [] = {0xaa, ++ttusb->c, 0x17, 5, 0, 0, 0, 0, 0}; + int err; + + /* reset board */ + err = ttusb_cmd (ttusb, b0, sizeof (b0)); + if (err) + return err; + + /* reset board (again?) */ + err = ttusb_cmd (ttusb, b1, sizeof (b1)); + + if (err) + return err; + + ttusb_boot_dsp (ttusb); + + /* set i2c bit rate */ + err = ttusb_cmd (ttusb, b2, sizeof (b2)); + if (err) + return err; + + err = ttusb_cmd (ttusb, b3, sizeof (b3)); + if (err) + return err; + + err = ttusb_result(ttusb, b4, sizeof(b4)); + + err = ttusb_cmd (ttusb, get_version, sizeof (get_version)); + if (err) + return err; + + err = ttusb_result(ttusb, get_version, sizeof(get_version)); + if (err) + return err; + dprintk("%s: stc-version: %c%c%c%c%c\n", __FUNCTION__, + get_version[4], get_version[5], get_version[6], + get_version[7], get_version[8]); + if (memcmp(get_version+4, "V 0.0", 5) && + memcmp(get_version+4, "V 1.1", 5)) + { + printk("%s: unknown STC version %c%c%c%c%c, please report!\n", + __FUNCTION__, + get_version[4], get_version[5], get_version[6], + get_version[7], get_version[8]); + } + return 0; +} + +#ifdef TTUSB_DISEQC +static +int ttusb_send_diseqc (struct ttusb *ttusb, const struct dvb_diseqc_master_cmd *cmd) +{ + u8 b [12] = { 0xaa, ++ttusb->c, 0x18}; + + int err; + + b[3] = 4 + 2 + cmd->msg_len; + b[4] = 0xFF; /* send diseqc master, not burst */ + b[5] = cmd->msg_len; + + memcpy(b+5, cmd->msg, cmd->msg_len); + + /* Diseqc */ + err = ttusb_cmd (ttusb, b, 4 + b[3]); + if (err) { + dprintk ("%s: usb_bulk_msg() failed, return value %i!\n", + __FUNCTION__, err); + } + + return err; +} +#endif + +static +int ttusb_update_lnb (struct ttusb *ttusb) +{ + u8 b [] = { 0xaa, ++ttusb->c, 0x16, 5, /*power:*/ 1, + ttusb->voltage == SEC_VOLTAGE_18 ? 0 : 1, + ttusb->tone == SEC_TONE_ON ? 1 : 0, 1, 1 }; + int err; + + /* SetLNB */ + err = ttusb_cmd (ttusb, b, sizeof (b)); + if (err) { + dprintk ("%s: usb_bulk_msg() failed, return value %i!\n", + __FUNCTION__, err); + } + + return err; +} + +static +int ttusb_set_voltage (struct ttusb *ttusb, fe_sec_voltage_t voltage) +{ + ttusb->voltage = voltage; + return ttusb_update_lnb(ttusb); +} + +#ifdef TTUSB_TONE +static +int ttusb_set_tone (struct ttusb *ttusb, fe_sec_tone_mode_t tone) +{ + ttusb->tone = tone; + return ttusb_update_lnb(ttusb); +} +#endif + +static +int ttusb_lnb_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg) +{ + struct ttusb *ttusb = fe->i2c->data; + + switch (cmd) { + case FE_SET_VOLTAGE: + return ttusb_set_voltage (ttusb, (fe_sec_voltage_t) arg); +#ifdef TTUSB_TONE + case FE_SET_TONE: + return ttusb_set_tone (ttusb, (fe_sec_tone_mode_t) arg); +#endif +#ifdef TTUSB_DISEQC + case FE_DISEQC_SEND_MASTER_CMD: + return ttusb_send_diseqc(ttusb, (struct dvb_diseqc_master_cmd *)arg); +#endif + default: + return -EOPNOTSUPP; + }; +} + + +#if 0 +static +void ttusb_set_led_freq (struct ttusb *ttusb, u8 freq) +{ + u8 b [] = { 0xaa, ++ttusb->c, 0x19, 1, freq }; + int err, actual_len; + + err = ttusb_cmd (ttusb, b, sizeof (b)); + if (err) { + dprintk ("%s: usb_bulk_msg() failed, return value %i!\n", + __FUNCTION__, err); + } +} +#endif + + +/*****************************************************************************/ + +#ifdef TTUSB_HWSECTIONS +static void ttusb_handle_ts_data(struct ttusb_channel *channel, const u8 *data, int len); +static void ttusb_handle_sec_data(struct ttusb_channel *channel, const u8 *data, int len); +#endif + +static +void ttusb_process_muxpack(struct ttusb *ttusb, const u8 *muxpack) +{ + if (muxpack[0] & 0x80) + { +#ifdef TTUSB_HWSECTIONS + /* section data */ + int pusi=muxpack[0]&0x40; + int channel=muxpack[0]&0x1F; + int payload=muxpack[1]; + const u8 *data=muxpack+2; + /* check offset flag */ + if (muxpack[0] & 0x20) + data++; + + ttusb_handle_sec_data(ttusb->channel+channel, data, payload); + data+=payload; + + if ((!!(ttusb->muxpack[0] & 0x20)) ^ !!(ttusb->muxpack[1]&1)) + data++; +#warning TODO: pusi + printk("cc: %04x\n", (data[0]<<8)|data[1]); +#endif + } else if (muxpack[0] == 0x47) + { +#ifdef TTUSB_HWSECTIONS + /* we have TS data here! */ + int pid=((muxpack[1]&0x0F)<<8)|muxpack[2]; + int channel; + for (channel = 0; channel < TTUSB_MAXCHANNEL; ++channel) + if (ttusb->channel[channel].active && (pid == ttusb->channel[channel].pid)) + ttusb_handle_ts_data(ttusb->channel+channel, muxpack, 188); +#endif + dvb_dmx_swfilter_packets(&ttusb->dvb_demux, muxpack, 188); + } else if (muxpack[0] != 0) + printk("illegal muxpack type %02x\n", muxpack[0]); +} + +static +void ttusb_process_frame(struct ttusb *ttusb, u8 *data, int len) +{ + int maxwork=1024; + while (len) + { + if (! (maxwork--)) + { + printk("%s: too much work\n", __FUNCTION__); + break; + } + switch (ttusb->mux_state) + { + case 0: + case 1: + case 2: + len--; + if (*data++ == 0xAA) + ++ttusb->mux_state; + else + { + ttusb->mux_state=0; +#if DEBUG > 3 + if (ttusb->insync) + printk("%02x ", data[-1]); +#else + if (ttusb->insync) + { + printk("%s: lost sync.\n", __FUNCTION__); + ttusb->insync=0; + } +#endif + } + break; + case 3: + ttusb->insync=1; + len--; + ttusb->mux_npacks = *data++; + ++ttusb->mux_state; + ttusb->muxpack_ptr=0; + ttusb->muxpack_len=2; /* maximum bytes, until we know the length */ + break; + case 4: + { + int avail; + avail=len; + if (avail > (ttusb->muxpack_len - ttusb->muxpack_ptr)) + avail = ttusb->muxpack_len - ttusb->muxpack_ptr; + memcpy(ttusb->muxpack + ttusb->muxpack_ptr, data, avail); + ttusb->muxpack_ptr += avail; + if (ttusb->muxpack_ptr > 260) + BUG(); + data+=avail; + len-=avail; + /* determine length */ + if (ttusb->muxpack_ptr == 2) + { + if (ttusb->muxpack[0] & 0x80) + { + ttusb->muxpack_len = ttusb->muxpack[1]+2; + if (ttusb->muxpack[0] & 0x20) + ttusb->muxpack_len++; + if ((!!(ttusb->muxpack[0] & 0x20)) ^ !!(ttusb->muxpack[1]&1)) + ttusb->muxpack_len++; + ttusb->muxpack_len += 4; + } else if (ttusb->muxpack[0] == 0x47) + ttusb->muxpack_len = 188+4; + else if (ttusb->muxpack[0] == 0x00) + ttusb->muxpack_len = ttusb->muxpack[1]+2+4; + else + { + dprintk("%s: invalid state: first byte is %x\n", + __FUNCTION__, ttusb->muxpack[0]); + ttusb->mux_state=0; + } + } + + /* if length is valid and we reached the end, goto next muxpack */ + if ((ttusb->muxpack_ptr >= 2) && (ttusb->muxpack_ptr == ttusb->muxpack_len)) + { + ttusb_process_muxpack(ttusb, ttusb->muxpack); + ttusb->muxpack_ptr=0; + ttusb->muxpack_len=2; /* maximum bytes, until we know the length */ + + /* no muxpacks left? return to search-sync state */ + if (! ttusb->mux_npacks--) + { + ttusb->mux_state=0; + break; + } + } + break; + } + default: + BUG(); + break; + } + } +} + + +static +void ttusb_iso_irq (struct urb *urb) +{ + struct ttusb *ttusb = urb->context; + + if (! ttusb->iso_streaming) + return; +#if 0 + printk ("%s: (write urb %i) status %d, errcount == %d, length == %i\n", + __FUNCTION__, ttusb->write_urb, + urb->status, urb->error_count, urb->actual_length); +#endif + + if (! urb->status) + { + int i; + for (i=0; i < FRAMES_PER_ISO_BUF; ++i) + { + struct iso_packet_descriptor *d; + d = &urb->iso_frame_desc[i]; + ttusb_process_frame(ttusb, urb->transfer_buffer + d->offset, d->actual_length); + } + } + + wake_up_interruptible (&ttusb->wq); +} + + +static +void ttusb_free_iso_urbs (struct ttusb *ttusb) +{ + int i; + + for (i=0; i<ISO_BUF_COUNT; i++) + if (ttusb->iso_urb[i]) + usb_free_urb (ttusb->iso_urb[i]); + + pci_free_consistent (NULL, + ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF * ISO_BUF_COUNT, + ttusb->iso_buffer, ttusb->iso_dma_handle); +} + + +static +int ttusb_alloc_iso_urbs (struct ttusb *ttusb) +{ + int i; + + ttusb->iso_buffer = pci_alloc_consistent (NULL, + ISO_FRAME_SIZE * FRAMES_PER_ISO_BUF * ISO_BUF_COUNT, + &ttusb->iso_dma_handle); + + memset (ttusb->iso_buffer, 0, 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_free_iso_urbs (ttusb); + return -ENOMEM; + } + + ttusb->iso_urb[i] = urb; + } + + return 0; +} + + + +static +void ttusb_stop_iso_xfer (struct ttusb *ttusb) +{ + int i; + + for (i=0; i<ISO_BUF_COUNT; i++) + usb_unlink_urb (ttusb->iso_urb[i]); + + ttusb->iso_streaming = 0; +} + + +static +int ttusb_start_iso_xfer (struct ttusb *ttusb) +{ + int i, j, err, buffer_offset = 0; + + if (ttusb->iso_streaming) { + printk ("%s: iso xfer already running!\n", __FUNCTION__); + return 0; + } + + ttusb->insync=0; + ttusb->mux_state=0; + + for (i=0; i<ISO_BUF_COUNT; i++) { + int frame_offset = 0; + struct urb *urb = ttusb->iso_urb[i]; + + urb->dev = ttusb->dev; + urb->context = ttusb; + urb->complete = ttusb_iso_irq; + urb->pipe = ttusb->isoc_in_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 = ttusb->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; + } + } + + for (i=0; i<ISO_BUF_COUNT; i++) { + int next = (i + 1) % ISO_BUF_COUNT; + ttusb->iso_urb[i]->next = ttusb->iso_urb[next]; + } + + for (i=0; i<ISO_BUF_COUNT; i++) { +//?????? usb_claim_bandwidth (ttusb->dev, ttusb->iso_urb[i], ??, 1); + if ((err = usb_submit_urb (ttusb->iso_urb[i]))) { + ttusb_stop_iso_xfer (ttusb); + printk ("%s: failed urb submission (%i: err = %i)!\n", + __FUNCTION__, i, err); + return err; + } + } + + ttusb->iso_streaming = 1; + + return 0; +} + +#ifdef TTUSB_HWSECTIONS +static +void ttusb_handle_ts_data(struct ttusb_channel *channel, const u8 *data, int len) +{ + struct dvb_demux_feed *dvbdmxfeed = channel->dvbdmxfeed; + + dvbdmxfeed->cb.ts(data, len, 0, 0, &dvbdmxfeed->feed.ts, 0); +} + +static +void ttusb_handle_sec_data(struct ttusb_channel *channel, const u8 *data, int len) +{ +// struct dvb_demux_feed *dvbdmxfeed = channel->dvbdmxfeed; +#error TODO: handle ugly stuff +// dvbdmxfeed->cb.sec(data, len, 0, 0, &dvbdmxfeed->feed.sec, 0); +} +#endif + +static +struct ttusb_channel * ttusb_channel_allocate(struct ttusb *ttusb) +{ + int i; + if (down_interruptible(&ttusb->sem)) + return 0; + /* lock! */ + for (i=0; i<TTUSB_MAXCHANNEL; ++i) + if (! ttusb->channel[i].active) + { + ttusb->channel[i].active=1; + up(&ttusb->sem); + return ttusb->channel+i; + } + up(&ttusb->sem); + return 0; +} + +static +int ttusb_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ +// struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct ttusb *ttusb = (struct ttusb *)dvbdmxfeed->demux; + struct ttusb_channel *channel; + + printk("ttusb_start_feed\n"); + +// if (!dvbdmx->dmx.frontend) +// return -EINVAL; + + /* since we're on USB-1.1, we do not support full TS delivery. */ + if (dvbdmxfeed->pid == 0x2000) + return -EINVAL; + + switch(dvbdmxfeed->type) { + case DMX_TYPE_TS: + break; + case DMX_TYPE_SEC: + break; + default: + return -EINVAL; + } + + if (dvbdmxfeed->type == DMX_TYPE_TS) + { + switch(dvbdmxfeed->pes_type) { + case DMX_TS_PES_VIDEO: + case DMX_TS_PES_AUDIO: + case DMX_TS_PES_TELETEXT: + case DMX_TS_PES_PCR: +// return -EINVAL; + case DMX_TS_PES_OTHER: + channel=ttusb_channel_allocate(ttusb); + break; + default: + return -EINVAL; + } + } else + { + channel=ttusb_channel_allocate(ttusb); + } + + if (!channel) + return -EBUSY; + + dvbdmxfeed->priv = channel; + channel->dvbdmxfeed = dvbdmxfeed; + + channel->pid = dvbdmxfeed->pid; + +#ifdef TTUSB_HWSECTIONS + if (dvbdmxfeed->type == DMX_TYPE_TS) + { + channel->type=1; + } else if (dvbdmxfeed->type == DMX_TYPE_SEC) + { + channel->type=2; +#error TODO: allocate filters + } +#else + channel->type=1; +#endif + + ttusb_set_channel (ttusb, channel->id, channel->type, channel->pid); + return 0; +} + +static int ttusb_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct ttusb_channel *channel=(struct ttusb_channel *)dvbdmxfeed->priv; + ttusb_del_channel(channel->ttusb, channel->id); + channel->active = 0; + return 0; +} + +static +int ttusb_setup_interfaces (struct ttusb *ttusb) +{ + usb_set_configuration (ttusb->dev, 1); + usb_set_interface (ttusb->dev, 1, 1); + + ttusb->bulk_out_pipe = usb_sndbulkpipe(ttusb->dev, 1); + ttusb->bulk_in_pipe = usb_rcvbulkpipe(ttusb->dev, 1); + ttusb->isoc_in_pipe = usb_rcvisocpipe(ttusb->dev, 2); + + return 0; +} + + +static +void* ttusb_probe (struct usb_device *udev, unsigned int ifnum, + const struct usb_device_id *id) +{ + struct ttusb *ttusb; + int result, channel; + + if (ifnum != 0) + return NULL; + + dprintk ("%s: TTUSB DVB connected\n", __FUNCTION__); + + if (!(ttusb = kmalloc (sizeof (struct ttusb), GFP_KERNEL))) + return NULL; + + +// MOD_INC_USE_COUNT; + + memset (ttusb, 0, sizeof(struct ttusb)); + + for (channel=0; channel < TTUSB_MAXCHANNEL; ++channel) + { + ttusb->channel[channel].id=channel; + ttusb->channel[channel].ttusb=ttusb; + } + + ttusb->dev = udev; + ttusb->c = 0; + ttusb->mux_state = 0; + sema_init (&ttusb->sem, 0); + init_waitqueue_head (&ttusb->wq); + + ttusb_setup_interfaces (ttusb); + + ttusb_alloc_iso_urbs (ttusb); + if (ttusb_init_controller (ttusb)) + printk("ttusb_init_controller: error\n"); + + up (&ttusb->sem); + + dvb_register_adapter (&ttusb->adapter, + "Technotrend/Hauppauge Nova-USB"); + + dvb_register_i2c_bus (ttusb_i2c_xfer, ttusb, ttusb->adapter, 0); + dvb_add_frontend_ioctls (ttusb->adapter, ttusb_lnb_ioctl, NULL, ttusb); + + memset(&ttusb->dvb_demux, 0, sizeof(ttusb->dvb_demux)); + ttusb->dvb_demux.dmx.vendor = "Technotrend/Hauppauge"; + ttusb->dvb_demux.dmx.model = "USB-Nova"; + ttusb->dvb_demux.dmx.id = "xyz"; + ttusb->dvb_demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; + + ttusb->dvb_demux.priv = 0; +#ifdef TTUSB_HWSECTIONS + ttusb->dvb_demux.filternum = TTUSB_MAXFILTER; +#else + ttusb->dvb_demux.filternum = 32; +#endif + ttusb->dvb_demux.feednum = TTUSB_MAXCHANNEL; + ttusb->dvb_demux.start_feed = ttusb_start_feed; + ttusb->dvb_demux.stop_feed = ttusb_stop_feed; + ttusb->dvb_demux.write_to_decoder = 0; + + if ((result = dvb_dmx_init(&ttusb->dvb_demux)) < 0) { + printk("ttusb_dvb: dvb_dmx_init failed (errno = %d)\n", result); + goto err; + } + +//FIXME dmxdev (nur WAS?) + ttusb->dmxdev.filternum = ttusb->dvb_demux.filternum; + ttusb->dmxdev.demux = &ttusb->dvb_demux.dmx; + ttusb->dmxdev.capabilities = 0; + + if ((result = dvb_dmxdev_init(&ttusb->dmxdev, ttusb->adapter)) < 0) { + printk("ttusb_dvb: dvb_dmxdev_init failed (errno = %d)\n", result); + dvb_dmx_release(&ttusb->dvb_demux); + goto err; + } + + if (dvb_net_init(ttusb->adapter, &ttusb->dvbnet, &ttusb->dvb_demux.dmx)) + { + printk("ttusb_dvb: dvb_net_init failed!\n"); + } + +err: + ttusb_start_iso_xfer (ttusb); + return (void*) ttusb; +} + + +static +void ttusb_disconnect (struct usb_device *udev, void *data) +{ + struct ttusb *ttusb = data; + + ttusb->disconnecting = 1; + wake_up_interruptible (&ttusb->wq); + + ttusb_stop_iso_xfer (ttusb); + + ttusb->dvb_demux.dmx.close(&ttusb->dvb_demux.dmx); + dvb_net_release(&ttusb->dvbnet); + dvb_dmxdev_release(&ttusb->dmxdev); + dvb_dmx_release(&ttusb->dvb_demux); + + dvb_unregister_i2c_bus (ttusb_i2c_xfer, ttusb->adapter, 0); + dvb_unregister_adapter (ttusb->adapter); + + ttusb_free_iso_urbs (ttusb); + + kfree (ttusb); + +// MOD_DEC_USE_COUNT; + + dprintk ("%s: TTUSB DVB disconnected\n", __FUNCTION__); +} + + + +static struct usb_device_id ttusb_table [] = { + { USB_DEVICE(0xb48, 0x1003) }, + { USB_DEVICE(0xb48, 0x1004) }, /* to be confirmed ???? */ + { USB_DEVICE(0xb48, 0x1005) }, /* to be confirmed ???? */ + { } +}; + +MODULE_DEVICE_TABLE(usb, ttusb_table); + + +static +struct usb_driver ttusb_driver = { + name: "Technotrend/Hauppauge USB-Nova", + probe: ttusb_probe, + disconnect: ttusb_disconnect, + id_table: ttusb_table, +}; + + + +static +int __init ttusb_init(void) +{ + int err; + + if ((err = usb_register(&ttusb_driver)) < 0) { + printk ("%s: usb_register failed! Error number %d", + __FILE__, err); + return -1; + } + + return 0; +} + + +static +void __exit ttusb_exit (void) +{ + usb_deregister(&ttusb_driver); +} + + +module_init (ttusb_init); +module_exit (ttusb_exit); + +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Debug or not"); + +MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>"); +MODULE_DESCRIPTION("TTUSB DVB Driver"); +MODULE_LICENSE("GPL"); + |