summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--linux/drivers/media/dvb/dvb-core/Makefile3
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_ca.c1928
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_ca.h248
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c1635
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h128
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_ksyms.c6
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.c127
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.h69
-rw-r--r--linux/drivers/media/dvb/ttpci/budget-ci.c382
-rw-r--r--linux/drivers/media/dvb/ttpci/budget-core.c229
-rw-r--r--linux/drivers/media/dvb/ttpci/budget.h3
-rw-r--r--linux/include/media/saa7146.h2
12 files changed, 2335 insertions, 2425 deletions
diff --git a/linux/drivers/media/dvb/dvb-core/Makefile b/linux/drivers/media/dvb/dvb-core/Makefile
index a58e89e8c..712778914 100644
--- a/linux/drivers/media/dvb/dvb-core/Makefile
+++ b/linux/drivers/media/dvb/dvb-core/Makefile
@@ -3,6 +3,7 @@
#
dvb-core-objs = dvbdev.o dmxdev.o dvb_demux.o dvb_filter.o \
- dvb_functions.o dvb_frontend.o dvb_i2c.o dvb_net.o dvb_ksyms.o dvb_ringbuffer.o
+ dvb_ca_en50221.o dvb_functions.o dvb_frontend.o \
+ dvb_i2c.o dvb_net.o dvb_ksyms.o dvb_ringbuffer.o
obj-$(CONFIG_DVB_CORE) += dvb-core.o
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ca.c b/linux/drivers/media/dvb/dvb-core/dvb_ca.c
deleted file mode 100644
index 51c0b4166..000000000
--- a/linux/drivers/media/dvb/dvb-core/dvb_ca.c
+++ /dev/null
@@ -1,1928 +0,0 @@
-/*
- * dvb_ca.c: generic DVB CA functions
- *
- * Copyright (C) 2004 Andrew de Quincey
- *
- * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- */
-
-/*
- * In the following, public functions always do locking of slots, private functions do not
- * necessarily do locking of slots - they may expect the caller to have locked */
-
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <asm/semaphore.h>
-
-#include "dvb_ca.h"
-#include "dvb_functions.h"
-
-static int dvb_ca_debug = 0;
-#define dprintk if (dvb_ca_debug) printk
-
-#define HOST_LINK_BUF_SIZE 0x200
-
-#define RX_BUFFER_SIZE 65540
-#define TX_BUFFER_SIZE 65540
-
-#define CTRLIF_DATA 0
-#define CTRLIF_COMMAND 1
-#define CTRLIF_STATUS 1
-#define CTRLIF_SIZE_LOW 2
-#define CTRLIF_SIZE_HIGH 3
-
-#define CMDREG_HC 1 /* Host control */
-#define CMDREG_SW 2 /* Size write */
-#define CMDREG_SR 4 /* Size read */
-#define CMDREG_RS 8 /* Reset interface */
-
-#define STATUSREG_RE 1 /* read error */
-#define STATUSREG_WE 2 /* write error */
-#define STATUSREG_FR 0x40 /* module free */
-#define STATUSREG_DA 0x80 /* data available */
-#define STATUSREG_TXERR (STATUSREG_RE|STATUSREG_WE) /* general transfer error */
-
-
-static int dvb_ca_en50221_link_init(struct dvb_ca* ca, int slot);
-
-
-static u8* findstr(u8* haystack, int hlen, u8* needle, int nlen) {
- int i;
-
- for(i=0; i<= hlen - nlen; i++) {
- if (!strncmp(haystack+i, needle, nlen)) return haystack+i;
- }
-
- return NULL;
-}
-
-
-/* ******************************************************************************** */
-/* Functions for controlling access to slots */
-
-/**
- * Safely increment the usage counter for a CA slot.
- *
- * @param ca CA instance.
- * @param slot Slot concerned.
- *
- * @return 0 on success, <0 on failure.
- * */
-static int dvb_ca_slot_acquire(struct dvb_ca* ca, int slot) {
- int status;
-
- if (status = down_interruptible(&ca->slot_info[slot].sem)) return status;
-
- if (!ca->slot_info[slot].cam_present) {
- up(&ca->slot_info[slot].sem);
- return -EIO;
- }
- ca->slot_info[slot].usage_counter++;
-
- up(&ca->slot_info[slot].sem);
- return 0;
-}
-
-
-/**
- * Safely decrement the usage counter for a CA slot.
- *
- * @param ca CA instance.
- * @param slot Slot concerned.
- *
- * @return 0 on success, <0 on failure.
- * */
-static int dvb_ca_slot_release(struct dvb_ca* ca, int slot) {
- int status;
-
- if (status = down_interruptible(&ca->slot_info[slot].sem)) return status;
-
- ca->slot_info[slot].usage_counter--;
-
- up(&ca->slot_info[slot].sem);
- return(0);
-}
-
-
-/**
- * Acquire a slot exclusively. The slot semaphore will be left locked on successful
- * exit of this function.
- *
- * @param ca CA instance.
- * @param slot Slot concerned.
- *
- * @return 0 on success, <0 on failure.
- * */
-static int dvb_ca_slot_acquire_exclusive(struct dvb_ca* ca, int slot) {
- int status;
-
- while(1) {
- /* lock the slot */
- if (status = down_interruptible(&ca->slot_info[slot].sem)) return status;
-
- /* if there is no CAM, exit straight away */
- if (!ca->slot_info[slot].cam_present) {
- up(&ca->slot_info[slot].sem);
- return -EIO;
- }
-
- /* if there are no other users of the slot, we've finished */
- if (ca->slot_info[slot].usage_counter == 0) {
- ca->slot_info[slot].usage_counter++;
- break;
- }
-
- /* sleep a bit and try again */
- up(&ca->slot_info[slot].sem);
- dvb_delay(1);
- }
-
- /* DO NOT UNLOCK! */
- return 0;
-}
-
-
-/**
- * Release an exclusively owned slot. The slot semaphore will be unlocked by this function.
- *
- * @param ca CA instance.
- * @param slot Slot concerned.
- *
- * @return 0 on success, <0 on failure.
- * */
-static int dvb_ca_slot_release_exclusive(struct dvb_ca* ca, int slot) {
- ca->slot_info[slot].usage_counter--;
- up(&ca->slot_info[slot].sem);
- return(0);
-}
-
-
-
-
-
-/* ******************************************************************************** */
-/* Functions for link level connection_ids */
-
-/**
- * Internal function to destroy and unlink a dvb_ca_connection structure.
- *
- * @param cacon The structure to destroy.
- * @param unlink If 1, it will be unlinked from the list.
- */
-static void dvb_ca_connection_destroy(struct dvb_ca_connection* cacon, int unlink) {
- /* unlink it from the list. */
- if (unlink)
- list_del(&cacon->connection);
-
- /* destroy buffers etc */
- if (cacon->rx_buffer.data) vfree(cacon->rx_buffer.data);
- if (cacon->tx_buffer.data) vfree(cacon->tx_buffer.data);
- vfree(cacon);
-}
-
-
-
-/**
- * Get or create a dvb_ca_connection structure for a particular slot/connection_id.
- *
- * @param ca CA device instance.
- * @param slot Slot id.
- * @param connection_id Connection id to retrieve structure for.
- * @param create If 1, and a connection struct was not found, a new one will be created.
- *
- * @return A dvb_ca_connection structure, or NULL.
- */
-static struct dvb_ca_connection* dvb_ca_connection_get(struct dvb_ca* ca, int slot, int connection_id, int create) {
- struct dvb_ca_connection* cacon;
- u8* mem;
-
- /* is there already a record for this connection_id */
- list_for_each_entry(cacon, &ca->slot_info[slot].connections, next) {
- /* if we found it, return it immediately */
- if (cacon->connection_id == connection_id) {
- cacon->last_used = jiffies;
- return cacon;
- }
- }
-
- /* if we get here, and we've been asked not to create a structure, just return NULL */
- if (!create) {
- return NULL;
- }
-
- /* did not find it => create a new connection */
- cacon = vmalloc(sizeof(struct dvb_ca_connection));
- if (cacon == NULL) {
- goto error;
- }
-
- /* setup the structure */
- memset(cacon, 0, sizeof(struct dvb_ca_connection));
- cacon->connection_id = connection_id;
- cacon->rx_partial_pkt = -1;
- cacon->tx_partial_pkt_size = -1;
- cacon->last_used = jiffies;
-
- if (!(mem = vmalloc(RX_BUFFER_SIZE))) {
- goto error;
- }
- dvb_ringbuffer_init(&cacon->rx_buffer, mem, RX_BUFFER_SIZE);
-
- if (!(mem = vmalloc(TX_BUFFER_SIZE))) {
- goto error;
- }
- dvb_ringbuffer_init(&cacon->tx_buffer, mem, TX_BUFFER_SIZE);
-
- /* success */
- list_add_tail(&cacon->next, &ca->slot_info[slot].connections);
- return cacon;
-
-error:
- if (cacon != NULL) {
- dvb_ca_connection_destroy(cacon, 0);
- }
- return NULL;
-}
-
-
-
-
-
-/* ******************************************************************************** */
-/* EN50221 physical interface functions */
-
-
-/**
- * Wait for flags to become set on the STATUS register on a CAM interface,
- * checking for errors and timeout.
- *
- * @param ca CA instance.
- * @param slot Slot on interface.
- * @param waitfor Flags to wait for.
- * @param timeout_ms Timeout in milliseconds.
- *
- * @return 0 on success, nonzero on error.
- */
-static int dvb_ca_en50221_wait_if_status(struct dvb_ca* ca, int slot, u8 waitfor, int timeout_ms) {
- unsigned long timeout;
-
- /* loop until timeout elapsed */
- timeout = jiffies + ((HZ * timeout_ms) / 1000);
- while(1) {
- /* read the status and check for error */
- int res = ca->read_cam_control(ca, slot, CTRLIF_STATUS);
- if (res < 0) return -EIO;
-
- /* if we got the flags, it was successful! */
- if (res & waitfor) {
- return 0;
- }
-
- /* check for timeout */
- if (time_after(jiffies,timeout)) {
- break;
- }
-
- /* wait for a bit */
- mdelay(100); // FIXME: is this timeout good? need to investigate
- }
-
- /* if we get here, we've timed out */
- return -ETIMEDOUT;
-}
-
-
-
-/**
- * Reset the CAM control interface.
- *
- * @param ca CA instance.
- * @param slot Slot id.
- *
- * @return 0 on success, nonzero on failure.
- */
-static int dvb_ca_en50221_reset_if(struct dvb_ca* ca, int slot) {
- int ret;
-
- /* reset the interface and wait for FR to be set */
- if (ret = ca->write_cam_control(ca, slot, CTRLIF_COMMAND, CMDREG_RS)) return ret;
- if (ret = dvb_ca_cam_wait_if_status(ca, slot, STATUSREG_FR, 2)) return ret;
-
- /* need to perform link initialisation again */
- return dvb_ca_en50221_link_init(ca, slot);
-}
-
-
-/**
- * Initialise the link layer connection to a CAM.
- *
- * @param ca CA instance.
- * @param slot Slot id.
- *
- * @return 0 on success, nonzero on failure.
- */
-static int dvb_ca_en50221_link_init(struct dvb_ca* ca, int slot) {
- int ret;
- int buf_size;
- u8 buf[2];
-
- /* reset the interface */
- if (ret = dvb_ca_en50221_reset_if(ca, slot)) return ret;
-
- /* set the host link buffer size temporarily. it will be overwritten with the
- * real negotiated size later. */
- ca->slot_info[i]->link_buf_size = 2;
-
- /* OK, setup to read the buffer size */
- if (ret = ca->write_cam_control(ca, slot, CTRLIF_COMMAND, CMDREG_SR)) return ret;
- if (ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, 2)) return ret;
-
- /* read the buffer size from the CAM */
- if ((ret = dvb_ca_en50221_read_data(ca, slot, buf, 2)) != 2) return ret;
-
- /* set everything back to 0 again */
- if (ret = ca->write_cam_control(ca, slot, CTRLIF_COMMAND, 0)) return ret;
-
- /* store it, and choose the minimum of our buffer and the CAM's buffer size */
- bufsize = (buf[0] << 8) | buf[1];
- if (bufsize > HOST_LINK_BUF_SIZE) bufsize = HOST_LINK_BUF_SIZE;
- ca->slot_info[i]->link_buf_size = bufsize;
-
- /* setup the buffer size in the link_buf */
- buf[0] = bufsize >> 8;
- buf[1] = bufsize & 0xff;
-
- /* OK, setup to write chosen buffer size to CAM */
- if (ret = ca->write_cam_control(ca, slot, CTRLIF_COMMAND, CMDREG_SW)) return ret;
- if (ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, 2)) return ret;
-
- /* write the buffer size to the CAM */
- if ((ret = dvb_ca_en50221_write_data(ca, slot, buf, 2)) != 2) return ret;
-
- /* set everything back to 0 again */
- if (ret = ca->write_cam_control(ca, slot, CTRLIF_COMMAND, 0)) return ret;
-
- /* success */
- return 0;
-}
-
-
-
-/**
- * Parse attribute memory of a CAM module, extracting Config register, and checking
- * it is a DVB CAM module.
- *
- * @param ca CA instance.
- * @param slot Slot id.
- *
- * @return 0 on success, <0 on failure.
- */
-static int dvb_ca_en50221_parse_attributes(struct dvb_ca* ca, int slot) {
- int address = 0;
- int tupleLength;
- int tupleType;
- char tuple[257];
- char* dvb_str;
- int i;
- int rasz;
- int got_device0a = 0;
- int got_device0c = 0;
- int got_vers1 = 0;
- int got_manfid = 0;
- int got_config = 0;
- int got_cftableentry = 0;
- int end_chain = 0;
-
-
- while(!end_chain) {
- /* grab the next tuple length and type */
- if ((tupleType = ca->read_attribute_mem(ca, slot, address++)) < 0) return tupleType;
- if ((tupleLength = ca->read_attribute_mem(ca, slot, address++)) < 0) return tupleLength;
-
- /* read in the whole tuple */
- for(i=0; i< tupleLength; i++) {
- tuple[i] = ca->read_attribute_mem(ca, slot, address+i);
- }
- address += tupleLength;
-
- /* deal with the types of tuple */
- switch(tupleType) {
- case 0x1D: // CISTPL_DEVICE_0A
- got_device0a = 1;
- break; /* don't bother parsing */
-
- case 0x1C: // CISTPL_DEVICE_0C
- got_device0c = 1;
- break; /* don't bother parsng */
-
- case 0x15: // CISTPL_VERS_1
- got_vers1 = 1;
- break; /* don't bother parsing */
-
- case 0x20: // CISTPL_MANFID
- /* check tuple length is valid */
- if (tupleLength != 4) break;
-
- /* OK, extract manid and devid */
- ca->slot_info[i].manid = (tuple[1] << 8) | tuple[0];
- ca->slot_info[i].devid = (tuple[3] << 8) | tuple[2];
-
- got_manfid = 1;
- break;
-
- case 0x1A: // CISTPL_CONFIG
- /* check tuple length is valid */
- if (tupleLength < 3) break;
-
- /* OK, grab length of configbase */
- rasz = tuple[0] & 3;
-
- /* check there is enough space for everything */
- if (tupleLength < (3 + rasz + 14)) break;
-
- /* extract the configbase */
- ca->slot_info[i].config_base = 0;
- for(i=0; i< rasz+1; i++) {
- ca->slot_info[i].config_base |= (tuple[2+i] << (8*i));
- }
-
- /* check it contains the correct DVB string */
- dvb_str = findstr(tuple, tupleLength, "DVB_CI_V", 8);
- if (dvb_str == NULL) break;
- if (tupleLength < ((dvb_str - tuple) + 12)) break;
-
- /* is it a version we support? */
- if (strncmp(dvb_str + 8, "1.00", 4)) {
- printk("dvb_ca: Unsupported DVB CAM module version %c%c%c%c\n",
- dvb_str[8], dvb_str[9], dvb_str[10], dvb_str[11]);
- return -EINVAL;
- }
-
- got_config = 1;
- break;
-
- case 0x1D: // CISTPL_CFTABLE_ENTRY
- if (tupleLength < (2+11+17)) break;
-
- /* get the config option */
- ca->slot_info[i].config_option = tuple[0] & 0x3f;
-
- /* does it contain an interrupt descriptor structure? */
- i = 1;
- if (tuple[0] & 0x80) i++;
- if (tuple[i] & 0x10) {
- ca->slot_info[i].irq_supported = 1;
- } else
- ca->slot_info[i].irq_supported = 0;
- }
-
- /* OK, check it contains the correct strings */
- if ((findstr(tuple, tupleLength, "DVB_HOST", 8) == NULL) ||
- (findstr(tuple, tupleLength, "DVB_CI_MODULE", 13) == NULL)) break;
-
- got_cftableentry = 1;
- break;
-
- case 0xFF: // end of chain
- end_chain = true;
- break;
-
- default: /* Unknown tuple type */
- /* just skip this tuple and move to the next one */
- break;
- }
- }
-
- // OK, did we get everything we were looking for?
- if (!got_device0a || !got_device0c || !got_vers1 || !got_manfid || !got_config || !got_cftablenentry) {
- printk("dvb_ca: Non-CAM PC card detected\n");
- return -EINVAL;
- }
-
- // success!
- return 0;
-}
-
-
-
-/**
- * Initialise a CAM module.
- *
- * @param ca CA instance.
- * @param slot Slot containing the CAM.
- */
-static void dvb_ca_en50221_init(struct dvb_ca* ca, int slot) {
- int tmp;
-
- /* reset the CAM module */
- tmp = ca->read_attribute_mem(ca, slot, ca->slot_info[slot].config_base);
- ca->write_attribute_mem(ca, slot, ca->slot_info[slot].config_base, tmp | 0x80);
- dvb_delay(1);
- ca->write_attribute_mem(ca, slot, ca->slot_info[slot].config_base, tmp);
- dvb_delay(1);
-
- /* set the config option */
- tmp = (tmp & 0x3f) | ca->slot_info[slot].config_option;
- ca->write_attribute_mem(ca, slot, ca->slot_info[slot].config_base, tmp);
-}
-
-
-/**
- * This function talks to an EN50221 CAM control interface. It reads a buffer of
- * data from the CAM. The data can either be stored in a supplied buffer, or
- * automatically be added to the correct place in the CA slot/connection buffer structures.
- *
- * @param ca CA instance.
- * @param slot Slot to read from.
- * @param buf If non-NULL, the data will be written to this buffer. If NULL,
- * the data will be added into the buffering system as a normal fragment.
- * @param count Size of buf. Ignored if buf is NULL.
- *
- * @return Number of bytes read, or < 0 on error
- */
-static int dvb_ca_en50221_read_data(struct dvb_ca* ca, int slot, u8* buf, int count) {
- int bytes_read;
- int status;
- u8 buf[HOST_LINK_BUF_SIZE];
- u8 last_fragment;
- u8 connection_id;
-
- /* if the CA device is not open, do not attempt to receive data from it */
- if (!ca->open) return -EIO;
-
- /* acquire the slot */
- if (status = dvb_ca_slot_acquire(ca, slot)) return status;
-
- /* reset the interface if there's been a tx error */
- if ((status = ca->read_cam_control(ca, slot, CTRLIF_STATUS)) < 0) goto exit;
- if (status & STATUSREG_TXERR) dvb_ca_en50221_reset_if(ca, slot);
-
- /* check if data is available */
- if ((status = ca->read_cam_control(ca, slot, CTRLIF_STATUS)) < 0) goto exit;
- if (!(status & STATUSREG_DA)) {
- /* no data */
- status = 0;
- goto exit;
- }
-
- /* read the amount of data */
- if ((status = ca->read_cam_control(ca, slot, CTRLIF_SIZE_HIGH)) < 0) goto exit;
- bytes_read = status << 8;
- if ((status = ca->read_cam_control(ca, slot, CTRLIF_SIZE_LOW)) < 0) goto exit;
- bytes_read |= status;
-
- /* check it will fit */
- if (bytes_read > ca->slot_info[i].link_buf_size) {
- printk("dvb_ca: CAM tried to send a buffer larger than the link buffer size!\n");
- status = -EIO;
- goto exit;
- }
-
- /* fill the buffer */
- for(i=0; i < bytes_read; i++) {
- /* read byte and check */
- if ((status = ca->read_cam_control(ca, slot, CTRLIF_DATA)) < 0) goto exit;
-
- /* OK, store it in the buffer */
- buf[i] = res;
-
- /* at this point, RE=1, DA=0 during the transfer */
- }
-
- /* check for read error (RE should now go to 0) */
- if ((status = ca->read_cam_control(ca, slot, CTRLIF_STATUS)) < 0) goto exit;
- if (status & STATUSREG_RE) {
- status = -EIO;
- goto exit;
- }
-
- /* check there are at least two bytes received! */
- if (bytes_read < 2) {
- printk("dvb_ca: CAM sent a buffer that was less than 2 bytes!\n");
- status = -EIO;
- goto exit;
- }
-
- /* extract information about this buffer */
- connection_id = buf[0];
- last_fragment = (buf[1] & 0x80) ? 0 : 1; // received bit clear => last fragment
-
- /* OK, add it to the receive buffer */
- if ((status = dvb_ca_buf_copy_from_cam(ca, buf+2, bytes_read-2, last_fragment, slot, connection_id)) < 0) {
- goto exit;
- }
-
- /* return the number of bytes read */
- status = bytes_read;
-
- exit:
- ca->write_cam_control(ca, slot, CTRLIF_COMMAND, 0);
- dvb_ca_slot_release(ca, slot);
- return status;
-}
-
-
-/**
- * This function talks to an EN50221 CAM control interface. It writes a buffer of data
- * to a CAM. The data to write will can either come from a supplied buffer, or from the
- * buffering system identified by the slot/connection_id.
- *
- * @param ca CA instance.
- * @param slot Slot to read from.
- * @param connection_id Connection id to write data from. Ignored if buf is non-NULL.
- * @param buf If non-NULL, the data in this buffer is treated as a complete link-level packet to
- * be written. If NULL, the data will be extracted from the buffering system for the slot/connection_id.
- * @param count Size of buf. Ignored if buf is NULL.
- *
- * @return Number of bytes written, or < 0 on error.
- */
-static int dvb_ca_en50221_write_data(struct dvb_ca* ca, int slot, int connection_id, u8* buf, int count) {
- int status;
- u8 buf[HOST_LINK_BUF_SIZE];
- u8 last_fragment;
- int bytes_write;
-
-
- /* if the CA device is not open, do not attempt to send data to it */
- if (!ca->open) return -EIO;
-
- /* acquire the slot */
- if (status = dvb_ca_slot_acquire(ca, slot)) return status;
-
- /* reset the interface if there's been a tx error */
- if ((status = ca->read_cam_control(ca, slot, CTRLIF_STATUS)) < 0) goto exit;
- if (status & STATUSREG_TXERR) dvb_cam_reset_if(ca, slot);
-
- /* check if interface does not have data available */
- if ((status = ca->read_cam_control(ca, slot, CTRLIF_STATUS)) < 0) goto exit;
- if (status & STATUSREG_DA) {
- status = -EAGAIN;
- goto exit;
- }
-
- /* OK, set HC bit */
- if (status = ca->write_cam_control(ca, slot, CTRLIF_COMMAND, CMDREG_HC)) goto exit;
-
- /* check if interface is free */
- if ((status = ca->read_cam_control(ca, slot, CTRLIF_STATUS)) < 0) goto exit;
- if (!(status & STATUSREG_FR)) {
- /* it wasn't free => try again later */
- res = -EAGAIN;
- goto exit;
- }
-
- /* grab some data to send */
- bytes_write = dvb_ca_buf_copy_to_cam(ca, buf + 2, ca->slot_info[i].link_buf_size - 2, &last_fragment, slot, connection_id);
- if (bytes_write <= 0) {
- res = bytes_write;
- goto exit;
- }
-
- /* setup the buffer header */
- buf[0] = connection_id;
- buf[1] = last_fragment ? 0 : 0x80;
- bytes_write += 2; // for the header
-
- /* send the amount of data */
- if (status = ca->write_cam_control(ca, slot, CTRLIF_SIZE_HIGH, bytes_write >> 8)) goto exit;
- if (status = ca->write_cam_control(ca, slot, CTRLIF_SIZE_LOW, bytes_write & 0xff)) goto exit;
-
- /* send the buffer */
- for(i=0; i < bytes_read; i++) {
- if (status = ca->write_cam_control(ca, slot, CTRLIF_DATA, ca->slot_info[slot].link_buf[i])) goto exit;
-
- /* At this point, WE=1, FR=0 during the transfer */
- }
-
- /* check for write error (WE should now be 0) */
- if ((status = ca->read_cam_control(ca, slot, CTRLIF_STATUS)) < 0) goto exit;
- if (status & STATUSREG_WE) {
- res = -EIO;
- goto exit;
- }
-
- /* return the number of bytes read */
- status = bytes_write;
-
-exit:
- ca->write_cam_control(ca, slot, CTRLIF_COMMAND, 0);
- dvb_ca_slot_release(ca, slot);
- return status;
-}
-
-
-
-/**
- * A CAM has been inserted => initialise it.
- *
- * @param ca CA instance.
- * @param slot Slot containing the CAM to initialise.
- */
-static int dvb_ca_en50221_slot_init(struct dvb_ca* ca, int slot) {
- int status;
- int old_camchange_count;
-
- dprintk ("%s\n", __FUNCTION__);
-
- /* acquire the slot exclusively */
- if (status = dvb_ca_slot_acquire_exclusive(ca, slot)) return status;
-
- /* get the current number of reported CAM changes */
- old_camchange_count = atomic_get(&ca->slot_info[slot].camchange_count);
-
- /* reset slot */
- ca->slot_reset(ca, slot);
-
- /* check it is a DVB cam and get hardware info */
- if (status = dvb_ca_en50221_parse_attributes(ca, slot)) {
- dvb_ca_slot_release_exclusive(ca, slot);
- return status;
- }
-
- /* setup CAM hardware */
- if (status = dvb_ca_en50221_init(ca, slot)) {
- dvb_ca_slot_release_exclusive(ca, slot);
- return status;
- }
-
- /* check for a CAM change before bothering to initialise it */
- if (atomic_get(&ca->camchange_count) != old_camchange_count) {
- dvb_ca_slot_release_exclusive(ca, slot);
- return -EIO;
- }
-
- /* perform CAM link protocol init */
- if (status = dvb_ca_en50221_link_init(ca, slot)) {
- dvb_ca_slot_release_exclusive(ca, slot);
- return status;
- }
-
- /* OK, slot is now active */
- INIT_LIST_HEAD(&ca->slot[info].connections);
- atomic_set(&ca->slot_info[slot].write_count, 0);
- atomic_set(&ca->slot_info[slot].read_count, 0);
- ca->slot_info[slot].cam_present = 1;
-
- /* success */
- dvb_ca_slot_release_exclusive(ca, slot);
- return 0;
-}
-
-
-/**
- * A CAM has been removed => shut it down.
- *
- * @param ca CA instance.
- * @param slot Slot to shut down.
- */
-static int dvb_ca_en50221_slot_shutdown(struct dvb_ca* ca, int slot) {
- struct dvb_ca_connection* cacon;
- struct dvb_ca_connection* tmp;
- int status;
-
- dprintk ("%s\n", __FUNCTION__);
-
- /* acquire the slot exclusively */
- if (status = dvb_ca_slot_acquire_exclusive(ca, slot)) return status;
-
- /* there is no longer a CAM present in this slot */
- ca->slot_info[slot].cam_present = 0;
-
- /* bypass the TS feed to that slot, then reset it */
- ca->slot_ts_bypass(ca, slot, 1);
- ca->slot_reset(ca, slot);
-
- /* flush the buffers on this slot */
- /* cannot use dvb_ca_buf_flush_buffers() here because it locks exclusively as well */
- list_for_each_entry_safe(cacon, tmp, &ca->slot_info[slot].connections, next) {
- dvb_ca_connection_destroy(cacon, 1);
- }
-
- /* finished modifying slot structures */
- dvb_ca_slot_release_exclusive(ca, slot);
-
- /* need to wake up all read and write processes to check if they're now
- trying to write to a defunct CAM */
- wake_up_interruptible(&ca->read_queue);
- wake_up_interruptible(&ca->write_queue);
-
- /* success */
- return 0;
-}
-
-/**
- * A CAMCHANGE IRQ has occurred.
- *
- * @param ca CA instance.
- * @param slot Slot concerned.
- */
-void dvb_ca_en50221_camchange_irq(struct dvb_ca* ca, int slot) {
- atomic_inc(&ca->slot_info[slot].camchange_count);
-}
-
-/**
- * A READDATA IRQ has occurred.
- *
- * @param ca CA instance.
- * @param slot Slot concerned.
- */
-void dvb_ca_en50221_readdata_irq(struct dvb_ca* ca, int slot) {
- atomic_inc(&ca->slot_info[slot].read_count);
-}
-
-
-
-/* ******************************************************************************** */
-/* Monitoring thread */
-
-/**
- * Wake up the DVB CA thread
- */
-static void dvb_ca_wakeup(struct dvb_ca* ca) {
- ca->wakeup = 1;
- wake_up_interruptible(&ca->wait_queue);
-}
-
-/**
- * Used by the CA thread to determine if an early wakeup is necessary
- */
-static int dvb_ca_should_wakeup(struct dvb_ca* ca) {
- if (ca->wakeup) {
- ca->wakeup = 0;
- return 1;
- }
- if (ca->exit) return 1;
- return 0;
-}
-
-/**
- * Kernel thread which monitors CA slots for CAM changes, and performs data transfers.
- */
-static int dvb_ca_thread(void* data) {
- struct dvb_ca *ca = (struct dvb_ca*) data;
- struct dvb_ca_connection* cacon;
- struct dvb_ca_connection* tmp;
- char name[15];
- int slot;
- int timeout;
- unsigned long scan_connections = jiffies + (60*HZ);
-
- dprintk ("%s\n", __FUNCTION__);
-
- /* setup kernel thread */
- snprintf(name, sizeof(name), "kdvb-ca");
- dvb_kernel_thread_setup(name);
-
- /* main loop */
- while(!ca->exit) {
- up (&ca->thread_sem); /* is locked when we enter the thread... */
-
- /* sleep for a bit */
- timeout = wait_event_interruptible_timeout(ca->wait_queue,0 != dvb_ca_should_wakeup(ca), HZ/10);
- if ((-ERESTARTSYS == timeout) || (ca->exit)) {
- /* got signal or quitting */
- break;
- }
- if (down_interruptible(&ca->thread_sem)) break;
-
- /* go through all the slots processing them */
- for(slot=0; slot < ca->slot_count; slot++) {
-
- /* poll for CAM changes if there aren't any IRQs for that
- * OR: if we've have a >1 CAMCHANGE counter in IRQ mode */
- if ((!(ca->flags & DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE)) ||
- (atomic_get(ca->slot_info[slot].camchange_count))) {
- // setup the flag for when we have no CAMCHANGE IRQ, and therefore no camchange_count
- int no_irqs_flag = 0;
- if (!(ca->flags & DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE)) no_irqs_flag = 1;
-
- // In IRQ mode, we keep looping until no more CAM changes are reported.
- while(atomic_get(ca->slot_info[slot].camchange_count) || no_irqs_flag) {
- /* determine the hardware status of the CAM */
- int slot_status = ca->slot_status(ca, slot);
- int cam_present_now = (slot_status & DVB_CA_SLOT_CAM_PRESENT) ? 1: 0;
- int cam_changed = (slot_status & DVB_CA_SLOT_CAM_CHANGED) ? 1: 0;
- if (!cam_changed) {
- cam_changed = (cam_present_now != ca->slot_info[slot].cam_present);
- }
-
- /* if cam status has changed => update */
- if (cam_changed) {
- /* clear down an old CI slot */
- if (ca->slot_info[slot].cam_present) dvb_ca_en50221_cam_slot_shutdown(ca, slot);
-
- /* if a CAM is NOW present, initialise it */
- if (cam_present_now) dvb_ca_en50221_cam_slot_init(ca, slot);
- }
-
- /* if we don't have a CAMCHANGE IRQ, exit the loop after one iteration */
- if (no_irqs_flag) break;
-
- /* we've handled one change in IRQ mode; decrement the counter */
- atomic_dec(&ca->slot_info[slot].camchange_count);
- }
- }
-
- /* do any reading and writing necessary */
- if (ca->open && (ca->slot_info[slot].cam_present)) {
- // FIXME: sort it out!
-
- /* if the CAM or the CI interface itself doesn't support IRQs, need to poll for reads */
- if ((!ca->slot_info[slot].irq_supported) || (!ca->flags & DVB_CA_FLAG_EN50221_IRQ_READ)) {
- /* if there is data to be read, read it! */
- while(dvb_ca_cam_read_data(ca, slot) > 0);
- }
- }
-
- /* tidy up any defunct connection structures every 60 seconds */
- if ((ca->slot_info[slot].cam_present) && (time_after(jiffies, scan_connections))) {
-
- if (status = dvb_ca_slot_acquire_exclusive(ca, slot)) break;
-
- list_for_each_entry_safe(cacon, tmp, &ca->slot_info[slot].connections, next) {
-
- /* tear down a connection structure if it hasn't been used for 120 seconds */
- unsigned long timeout = cacon->last_used + (120 * HZ);
- if (time_after(jiffies, timeout)) {
- dvb_ca_connection_destroy(cacon, 1);
- }
- }
-
- dvb_ca_slot_release_exclusive(ca, slot);
- }
- }
- }
-
- up(&ca->thread_sem);
- mb();
-
- /* completed */
- wake_up_interruptible (&ca->thread_queue);
- return(0);
-}
-
-
-
-
-/* ******************************************************************************** */
-/* Packet buffer functions */
-
-
-/**
- * Flush buffers associated with a particular CA slot.
- *
- * @param ca CA instance.
- * @param slot Slot whose buffers are to be to flushed.
- *
- * @return 0 on success, <0 on failure.
- */
-int dvb_ca_buf_flush_buffers(struct dvb_ca* ca, int slot) {
- struct dvb_ca_connection* cacon;
- struct dvb_ca_connection* tmp;
- int status;
-
- if (status = dvb_ca_slot_acquire_exclusive(ca, slot)) return status;
-
- /* just destroy all the connections for this slot */
- list_for_each_entry_safe(cacon, tmp, &ca->slot_info[slot].connections, next) {
- dvb_ca_connection_destroy(cacon, 1);
- }
-
- dvb_ca_slot_release_exclusive(ca, slot);
- return 0;
-}
-
-
-
-/**
- * This function does not talk to a CAM; it is a utility function for grabbing data from the
- * connection buffers in chunks suitable for use by hardware-dependent code.
- *
- * Copy data to be sent to the CAM from the cache into the
- * supplied buffer. This call is for lower layers to get data still to
- * be sent to the CAM. The buffer is filled by the layers which receive data
- * from userspace.
- *
- * @param ca CA instance.
- * @param dest Where to put the data.
- * @param len Size of destination buffer.
- * @param last_fragment. This will be set to 1 if this is the last fragment of a packet,
- * or 0 if there is still more data in this packet to be transmitted.
- * @param slot Slot concerned.
- * @param connection_id Connection id concerned.
- *
- * @return Number of bytes copied to dest, or <0 on error.
- */
-int dvb_ca_buf_copy_to_cam(dvb_ca* ca, u8 *dest, int len, u8* last_fragment, u8 slot, u8 connection_id)
-{
- struct dvb_ca_connection* cacon;
- int status;
-
- /* in the buffer, each packet has a 3 byte header as follows:
- 0: High byte of total packet size.
- 1: Low byte of total packet size.
- 2: Flag byte. 0=> packet incomplete. 1=> packet complete.
- These are all updated as data fragments are appended to the packet.
- */
-
-
- /* acquire the slot */
- if (status = dvb_ca_slot_acquire(ca, slot)) return status;
-
- /* get the connection struct for this slot/connection id */
- cacon = dvb_ca_connection_get(ca, slot, connection_id, 0);
-
- /* if we didn't find a connection struct, there is no data! */
- if (cacon == NULL) {
- dvb_ca_slot_release(ca, slot);
- *last_fragment = 1;
- return 0;
- }
-
- /* if there is no data, there is no point going on */
- if (dvb_ringbuffer_avail(&cacon->tx_buffer) == 0) {
- dvb_ca_slot_release(ca, slot);
- *last_fragment = 1;
- return 0;
- }
-
- /* if we're not in the middle of sending a packet... */
- if (cacon->tx_partial_pkt_size == -1);
- /* ensure there is actually enough data for a header */
- if (dvb_ringbuffer_avail(&cacon->tx_buffer) < 3) {
- dvb_ca_slot_release(ca, slot);
- *last_fragment = 1;
- return 0;
- }
-
- /* if the packet data is not available yet, exit straight away */
- if (DVB_RINGBUFFER_PEEK(&cacon->tx_buffer, 2) != 1) {
- dvb_ca_slot_release(ca, slot);
- *last_fragment = 1;
- return 0;
- }
-
- /* get the size of the next packet */
- cacon->tx_partial_pkt_size = DVB_RINGBUFFER_PEEK(&cacon->tx_buffer, 0) << 8;
- cacon->tx_partial_pkt_size |= DVB_RINGBUFFER_PEEK(&cacon->tx_buffer, 1);
- DVB_RINGBUFFER_SKIP(&cacon->tx_buffer, 3);
- }
-
- /* don't copy more data than is actually available */
- if (cacon->tx_partial_pkt_size < len) len = cacon->partial_pkt_size;
-
- /* copy the data into dest */
- dvb_ringbuffer_read(&cacon->tx_buffer, dest, len, 0);
-
- /* update the amount of consumed data */
- cacon->tx_partial_pkt_size -= len;
- if (cacon->tx_partial_pkt_size == 0) {
- /* whole packet consumed => move to next */
- cacon->tx_partial_pkt_size = -1;
-
- /* wake up the wait queues now writing is completed */
- wake_up_interruptible(&ca->read_queue);
- wake_up_interruptible(&ca->write_queue);
- }
-
- /* ok, return the amount of data copied this time */
- *last_fragment = (cacon->tx_partial_pkt_size == -1) ? 1 : 0;
- dvb_ca_slot_release(ca, slot);
- return len;
-}
-
-
-
-/**
- * This function does not talk to a CAM; it is a utility function for caching data retrieved
- * from a CAM by some hardware-dependent method and storing it in the connection buffers.
- *
- * Copy data recieved from the CAM into the cache. This call is for lower layers
- * to cache data received from the CAM. The buffer is emptied by the higher layers
- * which send data to userspace.
- *
- * @param ca CA instance.
- * @param data Buffer containing data.
- * @param len Number of bytes.
- * @param last_fragment 1 if this is the last fragment of a packet, or 0 if not.
- * @param slot Slot the data was received from.
- * @param connection_id Connection id the data was received from.
- *
- * @return 0 on success, or -1 if there was not enough space.
- * If -1 is returned, the packet will be automatically discarded. Any further data written
- * for this packet (last_fragment==0) will also be discarded until the next packet (i.e. the
- * write AFTER the write with last_fragment==1).
- */
-int dvb_ca_buf_copy_from_cam(dvb_ca* ca, u8 *data, int len, u8 last_fragment, u8 slot, u8 connection_id)
-{
- struct dvb_ca_connection* cacon;
- int total_length = 0;
- int status;
-
-
- /* in the buffer, each packet has a 3 byte header as follows:
- 0: High byte of total packet size.
- 1: Low byte of total packet size.
- 2: Flag byte. 0=> packet incomplete. 1=> packet complete.
- These are all updated as data fragments are appended to the packet.
- */
-
- /* acquire the slot */
- if (status = dvb_ca_slot_acquire(ca, slot)) return status;
-
- /* get the connection struct for this slot/connection id */
- cacon = dvb_ca_connection_get(ca, slot, connection_id, 1);
- if (cacon == NULL) {
- dvb_ca_slot_release(ca, slot);
- return -1;
- }
-
- /* check if we're discarding the current packet */
- if (cacon->rx_partial_pkt == -2) {
- /* if it is the last_fragment of a packet, set the flags appropriately */
- if (last_fragment) {
-
- /* we're NOT in the middle of a partial any more */
- cacon->rx_partial_pkt = -1;
- }
-
- /* indicate we're discarding this bit */
- dvb_ca_slot_release(ca, slot);
- return -1;
- } else if (cacon->rx_partial_pkt == -1) {
- /* check we've got enough space for the data + 3 byte header */
- if (dvb_ringbuffer_free(&cacon->rx_buffer) < (len + 3)) {
-
- /* if it is not the last fragment, we need to discard any other data written for this packet */
- if (!last_fragment) {
-
- /* set the rx_partial_pkt to indicate discarding in progress */
- cacon->rx_partial_pkt = -2;
- }
-
- /* indicate we're discarding */
- dvb_ca_slot_release(ca, slot);
- return -1;
- }
-
- /* record position partial packet starts at */
- cacon->rx_partial_pkt = cacon->rx_buffer.pwrite - cacon->rx_buffer.data;
-
- /* build the dummy (for the moment) packet header */
- DVB_RINGBUFFER_WRITE_BYTE(&cacon->rx_buffer, 0);
- DVB_RINGBUFFER_WRITE_BYTE(&cacon->rx_buffer, 0);
- DVB_RINGBUFFER_WRITE_BYTE(&cacon->rx_buffer, 0);
- } else {
- /* check we've got enough space for this fragment, if not, need to discard the current packet */
- if (dvb_ringbuffer_free(&cacon->rx_buffer) < len) {
- /* roll the write pointer back to the start of the packet being discarded */
- cacon->rx_buffer.pwrite = cacon->rx_buffer.data + cacon->rx_partial_pkt;
-
- /* mark the current packet to be discarded */
- cacon->rx_partial_pkt = -2;
-
- /* if it is the last_fragment of a packet, set the flags appropriately */
- if (last_fragment) {
- /* we're NOT in the middle of a packet */
- cacon->rx_partial_pkt = -1;
- }
-
- dvb_ca_slot_release(ca, slot);
- return -1;
- }
-
- /* get the current length of the partial packet */
- total_length = cacon->rx_buffer.data[(cacon->rx_partial_pkt + 0) % cacon->rx_buffer.size] << 8;
- total_length |= cacon->rx_buffer.data[(cacon->rx_partial_pkt + 1) % cacon->rx_buffer.size];
- }
-
- /* store the data */
- dvb_ringbuffer_write(&cacon->rx_buffer, data, len, 0);
-
- /* update the length of the packet in the packet header */
- total_length += len;
- cacon->rx_buffer.data[(cacon->rx_partial_pkt + 0) % cacon->rx_buffer.size] = total_length >> 8;
- cacon->rx_buffer.data[(cacon->rx_partial_pkt + 1) % cacon->rx_buffer.size] = (total_length & 0xff);
-
- /* if this is the last fragment, tidy up */
- if (last_fragment) {
- /* mark the packet as complete */
- cacon->rx_buffer.data[(cacon->rx_partial_pkt + 2) % cacon->rx_buffer.size] = 1;
-
- /* we no longer have a partial packet */
- cacon->rx_partial_pkt = -1;
-
- /* ok, wake up everything now reading is complete */
- wake_up_interruptible(&ca->read_queue);
- wake_up_interruptible(&ca->write_queue);
- }
-
- /* success */
- dvb_ca_slot_release(ca, slot);
- return 0;
-}
-
-
-#define DVB_CA_BUF_COPY_FROM_USER_CONDITION \
-({ \
- if ((err = dvb_ca_slot_acquire(ca, slot)) == 0) { \
- cacon = dvb_ca_connection_get(ca, slot, connection_id, 1); \
- if (cacon == NULL) { \
- err = -EIO; \
- dvb_ca_slot_release(ca, slot); \
- } else { \
- err = dvb_ringbuffer_free(&cacon->tx_buffer) >= (count+3) \
- if (!err) dvb_ca_slot_release(ca, slot); \
- } \
- } \
- err; \
-})
-
-
-/**
- * Function to copy a packet supplied by the user into the appropriate slot/connection's tx_buffer.
- *
- * @param ca CA instance.
- * @param file Associated file.
- * @param buf Source buffer (userspace).
- * @param count Size of buffer.
- * @param ppos Position in file (ignored).
- * @param slot Slot to write to.
- * @param connection_id Id of connection to write to.
- *
- * @return Number of bytes copied, or <0 on error.
- */
-static ssize_t dvb_ca_buf_copy_from_user(dvb_ca* ca, struct file *file, const char *buf, size_t count, loff_t *ppos, int slot, int connection_id) {
- dvb_ca_connection* cacon;
- int status;
- int write_pos;
- int err;
-
- /* Packets in the buffer have a 3 byte header as follows:
- 0: MSB of total packet size.
- 1: LSB of total packet size.
- 2: Flag byte. 0=> packet incomplete. 1=> packet complete.
- These are all updated as data fragments are appended to the packet.
- */
-
- /* acquire the slot */
- if (status = dvb_ca_slot_acquire(ca, slot)) return status;
-
- /* locate the connection to write to */
- cacon = dvb_ca_connection_get(ca, slot, connection_id, 1);
- if (cacon == NULL) {
- dvb_ca_slot_release(ca, slot);
- return -EIO;
- }
-
- /* check if the packet is WAY too big */
- if ((count + 3) > cacon->tx_buffer.size) {
- dvb_ca_slot_release(ca, slot);
- return -EINVAL;
- }
-
- /* OK, block if there isn't enough space at the moment */
- if (dvb_ringbuffer_free(&cacon->tx_buffer) < (count + 3)) {
-
- /* if we're in non blocking mode... */
- if (file->f_flags & O_NONBLOCK) {
- dvb_ca_slot_release(ca, slot);
- return -EWOULDBLOCK;
- }
-
- /* wait for some space */
- dvb_ca_slot_release(ca, slot);
- if ((status = wait_event_interruptible(ca->write_queue, DVB_CA_BUF_COPY_FROM_USER_CONDITION)) < 0) {
- /* slot is already released if the above failed */
- return status;
- }
-
- /* the wait was successful => slot is now reacquired */
- }
-
- /* write the packet header */
- write_pos = cacon->tx_buffer.pwrite - cacon->tx_buffer.data;
- DVB_RINGBUFFER_WRITE_BYTE(&cacon->tx_buffer, count >> 8);
- DVB_RINGBUFFER_WRITE_BYTE(&cacon->tx_buffer, count & 0xff);
- DVB_RINGBUFFER_WRITE_BYTE(&cacon->tx_buffer, 0);
-
- /* write the packet data to the buffer */
- if (dvb_ringbuffer_write(&cacon->tx_buffer, buf+3, count, 1) != count) {
-
- /* roll the write pointer back to the start of the packet being discarded */
- cacon->tx_buffer.pwrite = cacon->tx_buffer.data + write_pos;
-
- /* tell userspace */
- dvb_ca_slot_release(ca, slot);
- return -EFAULT;
- }
-
- /* mark the packet as available */
- cacon->tx_buffer.data[(write_pos + 2) % cacon->tx_buffer.size] = 1;
-
- /* return number of bytes written */
- dvb_ca_slot_release(ca, slot);
- return count;
-}
-
-// TODO: we should maybe remember the previous slot/connection and the next time start
-// at the one after, to give them equal priority.
-#define DVB_CA_BUF_COPY_TO_USER_CONDITION \
-({ \
- for(slot=0; slot < ca->slot_count; slot++) { \
- \
- if (dvb_ca_slot_acquire(ca, slot) == 0) { \
- \
- /* go through each connection id to see if there is data available */ \
- list_for_each_entry(cacon, &ca->slot_info[slot].connections, next) { \
- err = dvb_ringbuffer_avail(&cacon->rx_buffer) >= 3; \
- if (err) goto completed; \
- } \
- dvb_ca_slot_release(ca, slot); \
- } \
- } \
- completed: \
- err; \
-})
-
-
-/**
- * Function to copy a packet received from the CAM to a buffer supplied by the user.
- *
- * @param ca CA instance.
- * @param file Associated file.
- * @param buf Destination buffer (userspace).
- * @param count Size of buffer.
- * @param ppos Position in file (ignored).
- * @param _slot Updated to contain the slot received from.
- * @param connection_id Updated to contain the connection_id received from.
- *
- * @return Number of bytes copied, or <0 on error.
- */
-static ssize_t dvb_ca_buf_copy_to_user(dvb_ca* ca, struct file *file, char *buf, size_t count, loff_t *ppos, u8* _slot, u8* connection_id) {
- dvb_ca_connection* cacon;
- int slot;
- int packet_size;
- int split;
- int status;
- int err;
-
- /* Packets in the buffer have a 3 byte header as follows:
- 0: MSB of total packet size.
- 1: LSB of total packet size.
- 2: Flag byte. 0=> packet incomplete. 1=> packet complete.
- These are all updated as data fragments are appended to the packet.
- */
-
- /* check there is some space in the user-supplied destination buffer */
- if (count == 0) return 0;
-
- /* get a slot/connection to read from */
- if ((status = DVB_CA_BUF_COPY_TO_USER_CONDITION) == 0) {
-
- /* if we're in nonblocking mode, exit immediately */
- if (file->f_flags & O_NONBLOCK)
- return -EWOULDBLOCK;
-
- /* wait for some data */
- if ((status = wait_event_interruptible(ca->read_queue, DVB_CA_BUF_COPY_TO_USER_CONDITION)) < 0) {
- /* check for errors. No need to relase slot, as we won't have one */
- return status;
- }
- /* the wait was successful => slot is now acquired */
- }
-
- /* check for errors. No need to relase slot, as we won't have one */
- if (status < 0) return status;
-
- /* is the packet marked complete? */
- if (DVB_RINGBUFFER_PEEK(&cacon->rx_buffer, 2) != 1) {
- dvb_ca_slot_release(ca, slot);
- return 0;
- }
-
- /* get the packet's size */
- packet_size = DVB_RINGBUFFER_PEEK(&cacon->rx_buffer,0) << 8;
- packet_size |= DVB_RINGBUFFER_PEEK(&cacon->rx_buffer,1);
-
- /* is there enough space in the destination buffer */
- if (count < packet_size) {
- dvb_ca_slot_release(ca, slot);
- return -EINVAL;
- }
-
- /* Copy the data into the user buffer */
- /* Cannot use dvb_ringbuffer_read() here because we have to ignore the
- * three byte packet header. We can't just skip it beforehand, because
- * the read COULD -EFAULT, and we'd have to roll back. In the meantime,
- * something could have overwritten the packet header we skipped. */
- if ((cacon->rx_buffer.pread + 3 + packet_size) > cacon->rx_buffer.size) {
- split = cacon->rx_buffer.size - (cacon->rx_buffer.pread + 3);
- } else {
- split = 0;
- }
- if (split > 0) {
- if (copy_to_user(buf, cacon->rx_buffer.data + cacon->rx_buffer.pread + 3, split)) {
- dvb_ca_slot_release(ca, slot);
- return -EFAULT;
- }
- if (copy_to_user(buf+split, cacon->rx_buffer.data, packet_size - split)) {
- dvb_ca_slot_release(ca, slot);
- return -EFAULT;
- }
- } else {
- if (copy_to_user(buf, cacon->rx_buffer.data + cacon->rx_buffer.pread + 3, packet_size)) {
- dvb_ca_slot_release(ca, slot);
- return -EFAULT;
- }
- }
-
- /* the copy was successful => skip the packet header and the packet data */
- DVB_RINGBUFFER_SKIP(&cacon->rx_buffer, 3 + packet_size);
-
- /* return number of bytes read */
- *_slot = slot;
- *connection_id = cacon->connection_id;
- dvb_ca_slot_release(ca, slot);
- return packet_size;
-}
-
-
-
-
-/* ******************************************************************************** */
-/* File IO interface functions */
-
-
-
-
-/**
- * Implementation of file open syscall.
- *
- * @param inode Inode concerned.
- * @param file File concerned.
- *
- * @return 0 on success, <0 on failure.
- */
-int dvb_ca_io_open(struct inode *inode, struct file *file) {
- dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
- dvb_ca *ca=(dvb_ca*) dvbdev->priv;
- int i;
-
- int err=dvb_generic_open(inode, file);
- if (err<0)
- return err;
-
- ca->open = 1;
- return 0;
-}
-
-
-/**
- * Implementation of file close syscall.
- *
- * @param inode Inode concerned.
- * @param file File concerned.
- *
- * @return 0 on success, <0 on failure.
- */
-int dvb_ca_io_release(struct inode *inode, struct file *file) {
- dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
- dvb_ca *ca=(dvb_ca*) dvbdev->priv;
- int i;
-
- /* mark the CA device as closed */
- ca->open = 0;
-
- /* flush connection buffers */
- for(i=0; i< ca->slot_count; i++) {
- dvb_ca_buf_flush_buffers(ca, slot);
- }
-
- int err=dvb_generic_release(inode, file);
- if (err<0)
- return err;
- return 0;
-}
-
-
-/**
- * Implementation of poll() syscall.
- *
- * @param file File concerned.
- * @param wait poll wait table.
- *
- * @return Standard poll mask.
- */
-unsigned int dvb_ca_io_poll(struct file *file, poll_table *wait) {
- dvb_device_t* dvbdev = (dvb_device_t*) file->private_data;
- dvb_ca* ca = (dvb_ca*) dvbdev->priv;
- unsigned int mask=0;
- int slot;
- struct dvb_ca_connection* cacon;
-
-
- /* go through each slot looking to see if there is data available */
- for(slot=0; slot < ca->slot_count; slot++) {
-
- /* acquire the slot */
- dvb_ca_slot_acquire(ca, slot);
-
- /* go through each connection id to see if there is data available */
- list_for_each_entry(cacon, &ca->slot_info[slot].connections, next) {
-
- /* if the RX queue is NOT empty... */
- if (dvb_ringbuffer_avail(&cacon->rx_buffer) > 3) {
- /* and if the first packet on the RX buffer is marked as complete, data is ready to be read */
- if (DVB_RINGBUFFER_PEEK(&cacon->rx_buffer, 2) == 1)
- mask|= POLLIN;
- }
-
- /* is there data still to be written? */
- if (!dvb_ringbuffer_empty(&cacon->tx_buffer))
- mask|=POLLOUT;
- }
-
- /* finished with this slot now */
- dvb_ca_slot_release(ca, slot);
- }
-
- /* if there is something, return now */
- if (mask) return mask;
-
- /* wait for something to happen */
- poll_wait(file, &ca->read_queue, wait);
-
- /* see if there is some data available now */
- for(slot=0; slot < ca->slot_count; slot++) {
-
- /* acquire the slot */
- dvb_ca_slot_acquire(ca, slot);
-
- /* go through each connection id to see if there is data available */
- list_for_each_entry(cacon, &ca->slot_info[slot].connections, next) {
-
- /* if the RX queue is NOT empty... */
- if (dvb_ringbuffer_avail(&cacon->rx_buffer) > 3) {
- /* and if the first packet on the RX buffer is marked as complete, data is ready to be read */
- if (DVB_RINGBUFFER_PEEK(&cacon->rx_buffer, 2) == 1)
- mask|= POLLIN;
- }
-
- /* is there data still to be written? */
- if (!dvb_ringbuffer_empty(&cacon->tx_buffer))
- mask|=POLLOUT;
- }
-
- /* finished with this slot now */
- dvb_ca_slot_release(ca, slot);
- }
-
- return mask;
-}
-
-/**
- * Real ioctl implementation.
- * NOTE: CA_SEND_MSG/CA_GET_MSG ioctls have userspace buffers passed to them.
- *
- * @param inode Inode concerned.
- * @param file File concerned.
- * @param cmd IOCTL command.
- * @param arg Associated argument.
- *
- * @return 0 on success, <0 on error.
- */
-int dvb_ca_io_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *parg) {
- dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
- dvb_ca* ca=(dvb_ca*) dvbdev->priv;
- int err=0;
- int slot, connection_id;
- ca_msg_t msg;
- int msg_buf_offset = &msg.p.en50021.buf - &msg;
-
- switch (cmd) {
- case CA_RESET:
- // FIXME: update
- err=dvb_ci_ll_reset(&ci->wbuf, file, (int) parg, &ci->info[0]);
- break;
-
- case CA_GET_CAP:
- {
- if (ca->flags & DVB_CA_FLAG_EN50221) {
- ca_cap_t *cap = (ca_cap_t*) parg;
-
- cap->slot_num=ca->slot_count;
- cap->slot_type=CA_EN50221;
- cap->descr_num=0;
- cap->descr_type=0;
- }
- break;
- }
-
-
- case CA_GET_SLOT_INFO:
- {
- if (ca->flags & DVB_CA_FLAG_EN50221) {
- ca_slot_info_t *info=(ca_slot_info_t *)parg;
-
- if (info->num > ca->slot_count)
- return -EINVAL;
-
- info->type = CA_EN50221;
- info->flags = 0;
- if (ca->slot_info[info->num].cam_present)
- info->flags = CA_CI_MODULE_PRESENT | CA_CI_MODULE_READY;
- }
- break;
- }
-
- case CA_GET_MSG:
- if (ca->flags & DVB_CA_FLAG_EN50221) {
- if (copy_from_user(&msg, parg, msg_buf_offset)) return -EFAULT;
-
- err = dvb_ca_buf_copy_to_user(ca, file, parg + msg_buf_offset, msg.p.en50221.length, NULL, &slot, &connection_id);
- if (err >= 0) {
- msg.index = slot;
- msg.type = CA_EN50221;
- msg.p.en50221.connection_id = connection_id;
- msg.p.en50221.length = err;
- if (copy_to_user(&msg, parg, msg_buf_offset)) return -EFAULT;
- }
- }
- break;
-
- case CA_SEND_MSG:
- if ((ca->flags & DVB_CA_FLAG_EN50221) && (msg->type == CA_EN50221)) {
- if (copy_from_user(&msg, parg, msg_buf_offset)) return -EFAULT;
-
- err = dvb_ca_buf_copy_from_user(ca, file, parg + msg_buf_offset, msg.p.en50221.length, NULL,
- msg.index, msg.p.en50221.connection_id);
- }
- break;
-
- case CA_GET_DESCR_INFO:
- case CA_SET_DESCR:
- case CA_SET_PID:
- if (ca->flags & DVB_CA_FLAG_EN50221) {
- err=-EINVAL;
- }
- break;
-
- default:
- err=-EINVAL;
- break;
- }
-
- if (ca->ioctl)
- err=ca->ioctl(ca, cmd, parg);
- if (err<0 && err!=-EINVAL)
- return err;
- return 0;
-}
-
-
-/**
- * Wrapper for ioctl implementation.
- *
- * @param inode Inode concerned.
- * @param file File concerned.
- * @param cmd IOCTL command.
- * @param arg Associated argument.
- *
- * @return 0 on success, <0 on error.
- */
-int dvb_ca_io_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) {
- /* need to pass userspace buffer to these two */
- if ((cmd == CA_GET_MSG) || (cmd == CA_SEND_MSG)) {
- return dvb_ca_do_ioctl(inode, file, cmd, (void*) arg);
- }
-
- /* otherwise, just use the easy route */
- return generic_usercopy(inode, file, cmd, arg, dvb_ca_io_do_ioctl);
-}
-
-
-/**
- * Implementation of write() syscall.
- *
- * @param file File structure.
- * @param buf Source buffer.
- * @param count Size of source buffer.
- * @param ppos Position in file (ignored).
- *
- * @return Number of bytes read, or <0 on error.
- */
-ssize_t dvb_ca_io_write(struct file *file, const char *buf, size_t count, loff_t *ppos) {
- dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
- dvb_ca *ca=(dvb_ca*) dvbdev->priv;
- u8 hdr[2];
- u8 slot, connection_id;
-
- /* Incoming packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */
- if (count < 2) return -EINVAL;
-
- /* extract slot and connection id */
- if (copy_from_user(hdr, buf, 2)) return -EFAULT;
- slot = hdr[0];
- connection_id = hdr[1];
-
- /* copy the data from the buffer */
- status = dvb_ca_buf_copy_from_user(ca, file, buf+2, count-2, ppos, slot, connection_id);
- if (status < 0) return status;
-
- /* success */
- return status + 2;
-}
-
-
-/**
- * Implementation of read() syscall.
- *
- * @param file File structure.
- * @param buf Destination buffer.
- * @param count Size of destination buffer.
- * @param ppos Position in file (ignored).
- *
- * @return Number of bytes read, or <0 on error.
- */
-ssize_t dvb_ca_io_read(struct file *file, char *buf, size_t count, loff_t *ppos) {
- dvb_device_t *dvbdev=(dvb_device_t *) file->private_data;
- dvb_ca *ca=(dvb_ca*) dvbdev->priv;
- int status;
- u8 hdr[2];
- u8 slot, connection_id;
-
- /* Outgoing packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */
- if (count < 2) return -EINVAL;
-
- /* copy the data into the buffer */
- status = dvb_ca_buf_copy_to_user(ca, file, buf+2, count-2, ppos, &slot, &connection_id);
- if (status < 0) return status;
-
- /* set the slot and connection id */
- hdr[0] = slot;
- hdr[1] = connetion_id;
- if (copy_to_user(buf, hdr, 2)) return -EFAULT;
-
- /* success */
- return status + 2;
-}
-
-static struct file_operations dvb_ca_fops = {
- owner: THIS_MODULE,
- read: dvb_ca_io_read,
- write: dvb_ca_io_write,
- ioctl: dvb_ca_io_ioctl,
- open: dvb_ca_io_open,
- release: dvb_ca_io_release,
- poll: dvb_ca_io_poll,
-};
-
-static dvb_device_t dvbdev_ca = {
- priv: 0,
- users: 1,
- writers: 1,
- fops: &dvb_ca_fops,
-};
-
-
-
-
-
-/* ******************************************************************************** */
-/* Initialisation/shutdown functions */
-
-
-/**
- * Initialise a new DVB CA device.
- *
- * @param dvb_adapter DVB adapter to attach the new CA device to.
- * @param ca_dev The dvb_device_t to return.
- * @param ca The dvb_ca instance.
- *
- * @return 0 on success, nonzero on failure
- */
-int dvb_ca_init(dvb_adapter_t *dvb_adapter, dvb_device_t **ca_dev, struct dvb_ca* ca) {
- int ret;
-
- dprintk ("%s\n", __FUNCTION__);
-
- // FIXME: update
-
- /* make sure everything is within tolerances */
- if (ca->slot_count > DVB_CA_MAX_SLOTS) {
- return -EINVAL;
- }
-
- /* initialise the system parts */
- memset(&(ca->slot_info[0]), 0, sizeof(struct dvb_ca_slot_info) * DVB_CA_MAX_SLOTS);
- init_MUTEX(&ca->thread_sem);
- init_waitqueue_head(&ca->thread_queue);
- init_waitqueue_head(&ca->read_queue);
- init_waitqueue_head(&ca->write_queue);
- ca->exit = 0;
- ca->thread_pid = 0;
-
- /* register the DVB device */
- ret = dvb_register_device(dvb_adapter, ca_dev, &dvbdev_ca, ca, DVB_DEVICE_CA);
- return ret;
-
- /* create a kthread if necessary */
- if (ca->flags & DVB_CA_FLAG_EN50221) {
-
- if (signal_pending(current)) return -EINTR;
- if (down_interruptible (&ca->thread_sem)) return -EINTR;
-
- mb();
-
- /* create a kthread for monitoring this CA device */
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
- {
- /* bug in linux 2.4.x: kernel_thread() fails when the process calling
- * open() is run in the debugger (fixed in kernel 2.5). I hope this
- * workaround does not have any ugly side effects...
- */
- int pt = current->ptrace;
- current->ptrace = 0;
-#endif
-
- ret = kernel_thread (dvb_ca_thread, ca, 0);
-
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
- current->ptrace = pt;
- }
-#endif
-
- if (ret < 0) {
- printk("dvb_ca_init: failed to start kernel_thread (%d)\n", ret);
- up(&ca->thread_sem);
- return ret;
- }
- ca->thread_pid = ret;
- }
-
- /* done */
- return 0;
-}
-
-
-
-/**
- * Release a DVB CA device.
- *
- * @param ca_dev The dvb_device_t instance for the CA device.
- * @param ca The associated dvb_ca instance.
- */
-void dvb_ca_release(dvb_device_t *ca_dev, struct dvb_ca* ca) {
- dprintk ("%s %s\n", __FUNCTION__, ca->name);
-
- // FIXME: update
-
- /* shutdown the thread if there was one */
- if (ca->thread_pid) {
-
- /* tell the thread to stop */
- ca->exit = 1;
- mb();
-
- /* check if the thread is really alive */
- if (kill_proc(fe->thread_pid, 0, 1) == -ESRCH) {
- printk("dvb_ca_shutdown: thread PID %d already died\n", ca->thread_pid);
- /* make sure the mutex was not held by the thread */
- init_MUTEX (&ca->thread_sem);
- return;
- }
-
- wake_up_interruptible(&ca->thread_queue);
- interruptible_sleep_on(&ca->thread_queue);
-
- /* paranoia check */
- if (ca->thread_pid)
- printk("dvb_ca_shutdown: warning: thread PID %d won't exit\n", ca->thread_pid);
- }
-
- /* disable all CAM modules if we're an EN50221 style CAM */
- if (ca->flags & DVB_CA_FLAG_EN50221) {
- for(i=0; i< ca->slot_count; i++) {
- dvb_ca_en50221_slot_shutdown(ca, i);
- }
- }
-
- /* finally, unregister the device */
- dvb_unregister_device(ca_dev);
-}
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ca.h b/linux/drivers/media/dvb/dvb-core/dvb_ca.h
deleted file mode 100644
index b3e3ce2d0..000000000
--- a/linux/drivers/media/dvb/dvb-core/dvb_ca.h
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * dvb_ca.h: generic DVB CA functions
- *
- * Copyright (C) 2004 Andrew de Quincey
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * 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 Lesser General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef _DVB_CA_H_
-#define _DVB_CA_H_
-
-#include <linux/list.h>
-
-#include "dvbdev.h"
-
-#define DVB_CA_MAX_SLOTS 4
-
-#define DVB_CA_SLOTSTATUS_CAM_PRESENT 1
-#define DVB_CA_SLOTSTATUS_CAM_CHANGED 2
-
-#define DVB_CA_FLAG_EN50221 1
-#define DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE 2
-#define DVB_CA_FLAG_EN50221_IRQ_READ 4
-
-
-
-/* A connection id to a CAM */
-struct dvb_ca_connection {
-
- u8 connection_id; /* ID of this connection */
-
- struct dvb_ringbuffer rx_buffer; /* queue of data */
- struct dvb_ringbuffer tx_buffer; /* queue of data */
-
- int rx_partial_pkt; /* pointer to the position the current RX partial packet starts at, or -1 for none */
-
- int tx_partial_pkt_size; /* amount of data remaining from the current partial packet, or -1 for none */
-
- unsigned long last_used; /* last time in jiffies that this connection was used. */
-
- list_head next; /* next connection in list */
-};
-
-/* Information on a CA slot */
-struct dvb_ca_slot {
-
- u8 cam_present; /* is a CAM present in this slot or not? */
-
- u16 cam_manfid; /* manufacturer ID of the CAM */
- u16 cam_devid; /* device ID of the CAM */
-
- u32 config_base; /* base register of CAM config */
-
- u8 config_option; /* value to write into Config Control register */
-
- u8 irq_supported; /* if 1, the CAM supportes IRQs */
-
- int link_buf_size; /* size of the buffer to use when talking to the CAM */
-
- struct semaphore sem; /* semaphore for syncing access to slot structure */
-
- int usage_counter; /* current usage counter of this slot */
-
- atomic_t camchange_count;
- atomic_t write_count;
- atomic_t read_count;
-
- list_head connections; /* list of dvb_ca_connection, one per connection_id */
-};
-
-
-/* Structure describing a CA interface */
-struct dvb_ca {
-
- int index; //???
-
- dvb_adapter* adapter; //???
-
- /* Flags describing the interface (DVB_CA_FLAG_*) */
- u32 flags;
-
- /* number of slots supported by this CA interface */
- unsigned int slot_count;
-
- /* information on each slot */
- struct dvb_ca_slot slot_info[DVB_CA_MAX_SLOTS];
-
- /* functions for accessing attribute memory on the CAM */
- int (*read_attribute_mem)(struct dvb_ca* ca, int slot, int address);
- int (*write_attribute_mem)(struct dvb_ca* ca, int slot, int address, u8 value);
-
- /* functions for accessing the control interface on the CAM */
- int (*read_cam_control)(struct dvb_ca* ca, int slot, u8 address);
- int (*write_cam_control)(struct dvb_ca* ca, int slot, u8 address, u8 value);
-
- /* Functions for controlling slots */
- int (*slot_reset)(struct dvb_ca* ca, int slot);
- int (*slot_status)(struct dvb_ca* ca, int slot);
- int (*slot_ts_bypass)(struct dvb_ca* ca, int slot, int bypass);
-
- /* Hardware-specific IOCTL implementation. Set to NULL if you don't care */
- int (*ioctl)(void *, unsigned int cmd, void *parg);
-
-
- /* private data, used by caller */
- void* data;
-
- /* wait queues for read() and write() operations */
- wait_queue_head_t read_queue;
- wait_queue_head_t write_queue;
-
- /* PID of the monitoring thread */
- pid_t thread_pid;
-
- /* Semaphore for syncinc access when modifying driver structures */
- struct semaphore thread_sem;
-
- /* Wait queue used when shutting thread down */
- wait_queue_head_t thread_queue;
-
- /* Flag indicating when thread should exit */
- int exit;
-
- /* Flag indicating if the CA device is open */
- int open;
-};
-
-
-
-
-/* ******************************************************************************** */
-/* Functions for reporting EN50221 IRQ events */
-
-/**
- * A CAMCHANGE IRQ has occurred.
- *
- * @param ca CA instance.
- * @param slot Slot concerned.
- */
-void dvb_ca_en50221_camchange_irq(struct dvb_ca* ca, int slot);
-
-/**
- * A READDATA IRQ has occurred.
- *
- * @param ca CA instance.
- * @param slot Slot concerned.
- */
-void dvb_ca_en50221_readdata_irq(struct dvb_ca* ca, int slot);
-
-
-
-/* ******************************************************************************** */
-/* Packet buffer functions */
-
-
-/**
- * Flush buffers associated with a particular CA slot.
- *
- * @param ca CA instance.
- * @param slot Slot whose buffers are to be to flushed.
- *
- * @return 0 on success, <0 on failure.
- */
-extern int dvb_ca_buf_flush_buffers(struct dvb_ca* ca, int slot);
-
-
-/**
- * This function does not talk to a CAM; it is a utility function for grabbing data from the
- * connection buffers in chunks suitable for use by hardware-dependent code.
- *
- * Copy data to be sent to the CAM from the cache into the
- * supplied buffer. This call is for lower layers to get data still to
- * be sent to the CAM. The buffer is filled by the layers which receive data
- * from userspace.
- *
- * @param ca CA instance.
- * @param dest Where to put the data.
- * @param len Size of destination buffer.
- * @param last_fragment. This will be set to 1 if this is the last fragment of a packet,
- * or 0 if there is still more data in this packet to be transmitted.
- * @param slot Slot concerned.
- * @param connection_id Connection concerned.
- *
- * @return Number of bytes copied to dest, or <0 on error.
- */
-extern int dvb_ca_buf_copy_to_cam(dvb_ca* ca, u8 *dest, int len, u8* last_fragment, u8 slot, u8 connection_id);
-
-
-/**
- * This function does not talk to a CAM; it is a utility function for caching data retrieved
- * from a CAM by some hardware-dependent method and storing it in the connection buffers.
- *
- * Copy data recieved from the CAM into the cache. This call is for lower layers
- * to cache data received from the CAM. The buffer is emptied by the higher layers
- * which send data to userspace.
- *
- * @param ca CA instance.
- * @param data Buffer containing data.
- * @param len Number of bytes.
- * @param last_fragment 1 if this is the last fragment of a packet, or 0 if not.
- * @param slot Slot the data was received from.
- * @param connection_id Connection id the data was received from.
- *
- * @return 0 on success, or -1 if there was not enough space.
- * If -1 is returned, the packet will be automatically discarded. Any further data written
- * for this packet (last_fragment==0) will also be discarded until the next packet (i.e. the
- * write AFTER the write with last_fragment==1).
- */
-extern int dvb_ca_buf_copy_from_cam(dvb_ca* ca, u8 *data, int len, u8 last_fragment, u8 slot, u8 connection_id);
-
-
-/* ******************************************************************************** */
-/* Initialisation/shutdown functions */
-
-/**
- * Initialise a new DVB CA device.
- *
- * @param dvb_adapter DVB adapter to attach the new CA device to.
- * @param ca_dev The dvb_device_t to return.
- * @param ca The dvb_ca instance.
- *
- * @return 0 on success, nonzero on failure
- */
-extern int dvb_ca_init(dvb_adapter_t *dvb_adapter, dvb_device_t **ca_dev, struct dvb_ca* ca);
-
-/**
- * Release a DVB CA device.
- *
- * @param ca_dev The dvb_device_t instance for the CA device.
- * @param ca The associated dvb_ca instance.
- */
-extern void dvb_ca_release(dvb_device_t *ca_dev, struct dvb_ca* ca);
-
-
-
-#endif
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
new file mode 100644
index 000000000..02f40f5e0
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
@@ -0,0 +1,1635 @@
+/*
+ * dvb_ca.c: generic DVB functions for EN50221 CAM interfaces
+ *
+ * Copyright (C) 2004 Andrew de Quincey
+ *
+ * Parts of this file were based on sources as follows:
+ *
+ * Copyright (C) 2003 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * based on code:
+ *
+ * Copyright (C) 1999-2002 Ralph Metzler
+ * & Marcus Metzler for convergence integrated media GmbH
+ *
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <asm/semaphore.h>
+#include <asm/atomic.h>
+
+#include "dvb_ca_en50221.h"
+#include "dvb_functions.h"
+#include "dvb_ringbuffer.h"
+
+static int dvb_ca_en50221_debug = 0;
+#define dprintk if (dvb_ca_en50221_debug) printk
+
+#define INIT_TIMEOUT_SECS 5
+
+#define HOST_LINK_BUF_SIZE 0x200
+
+#define RX_BUFFER_SIZE 65535
+
+#define MAX_RX_PACKETS_PER_ITERATION 10
+
+#define CTRLIF_DATA 0
+#define CTRLIF_COMMAND 1
+#define CTRLIF_STATUS 1
+#define CTRLIF_SIZE_LOW 2
+#define CTRLIF_SIZE_HIGH 3
+
+#define CMDREG_HC 1 /* Host control */
+#define CMDREG_SW 2 /* Size write */
+#define CMDREG_SR 4 /* Size read */
+#define CMDREG_RS 8 /* Reset interface */
+#define CMDREG_FRIE 0x40 /* Enable FR interrupt */
+#define CMDREG_DAIE 0x80 /* Enable DA interrupt */
+#define IRQEN (CMDREG_FRIE|CMDREG_DAIE)
+
+#define STATUSREG_RE 1 /* read error */
+#define STATUSREG_WE 2 /* write error */
+#define STATUSREG_FR 0x40 /* module free */
+#define STATUSREG_DA 0x80 /* data available */
+#define STATUSREG_TXERR (STATUSREG_RE|STATUSREG_WE) /* general transfer error */
+
+
+#define DVB_CA_SLOTSTATE_NONE 0
+#define DVB_CA_SLOTSTATE_UNINITIALISED 1
+#define DVB_CA_SLOTSTATE_RUNNING 2
+#define DVB_CA_SLOTSTATE_INVALID 3
+#define DVB_CA_SLOTSTATE_WAITREADY 4
+#define DVB_CA_SLOTSTATE_VALIDATE 5
+#define DVB_CA_SLOTSTATE_WAITFR 6
+#define DVB_CA_SLOTSTATE_LINKINIT 7
+
+
+/* Information on a CA slot */
+struct dvb_ca_slot {
+
+ /* current state of the CAM */
+ int slot_state;
+
+ /* Number of CAMCHANGES that have occurred since last processing */
+ atomic_t camchange_count;
+
+ /* Type of last CAMCHANGE */
+ int camchange_type;
+
+ /* base address of CAM config */
+ u32 config_base;
+
+ /* value to write into Config Control register */
+ u8 config_option;
+
+ /* if 1, the CAM supports FR IRQs */
+ u8 fr_irq_supported:1;
+
+ /* if 1, the CAM supports DA IRQs */
+ u8 da_irq_supported:1;
+
+ /* size of the buffer to use when talking to the CAM */
+ int link_buf_size;
+
+ /* semaphore for syncing access to slot structure */
+ struct semaphore sem;
+
+ /* buffer for incoming packets */
+ struct dvb_ringbuffer rx_buffer;
+
+ /* timer used during various states of the slot */
+ unsigned long timeout;
+};
+
+/* Private CA-interface information */
+struct dvb_ca_private {
+
+ /* pointer back to the public data structure */
+ struct dvb_ca_en50221* pub;
+
+ /* the DVB device */
+ struct dvb_device *dvbdev;
+
+ /* Flags describing the interface (DVB_CA_FLAG_*) */
+ u32 flags;
+
+ /* number of slots supported by this CA interface */
+ unsigned int slot_count;
+
+ /* information on each slot */
+ struct dvb_ca_slot* slot_info;
+
+ /* wait queues for read() and write() operations */
+ wait_queue_head_t wait_queue;
+
+ /* PID of the monitoring thread */
+ pid_t thread_pid;
+
+ /* Wait queue used when shutting thread down */
+ wait_queue_head_t thread_queue;
+
+ /* Flag indicating when thread should exit */
+ int exit:1;
+
+ /* Flag indicating if the CA device is open */
+ int open:1;
+
+ /* Flag indicating the thread should wake up now */
+ int wakeup:1;
+
+ /* Delay the main thread should use */
+ unsigned long delay;
+
+ /* Slot to start looking for data to read from in the next user-space read operation */
+ int next_read_slot;
+};
+
+static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private* ca);
+static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebuf, int ecount);
+static int dvb_ca_en50221_write_data(struct dvb_ca_private* ca, int slot, u8* ebuf, int ecount);
+
+
+/**
+ * Safely find needle in haystack.
+ *
+ * @param haystack Buffer to look in.
+ * @param hlen Number of bytes in haystack.
+ * @param needle Buffer to find.
+ * @param nlen Number of bytes in needle.
+ * @return Pointer into haystack needle was found at, or NULL if not found.
+ */
+static u8* findstr(u8* haystack, int hlen, u8* needle, int nlen)
+{
+ int i;
+
+ if (hlen < nlen) return NULL;
+
+ for(i=0; i<= hlen - nlen; i++) {
+ if (!strncmp(haystack+i, needle, nlen)) return haystack+i;
+ }
+
+ return NULL;
+}
+
+
+
+/* ******************************************************************************** */
+/* EN50221 physical interface functions */
+
+
+/**
+ * Check CAM status.
+ */
+static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private* ca, int slot)
+{
+ int slot_status;
+ int status;
+ int cam_present_now;
+ int cam_changed;
+
+ /* IRQ mode */
+ if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE) {
+ return (atomic_read(&ca->slot_info[slot].camchange_count) != 0);
+ }
+
+ /* poll mode */
+ if ((status = down_interruptible(&ca->slot_info[slot].sem)) != 0) return status;
+ slot_status = ca->pub->poll_slot_status(ca->pub, slot);
+ up(&ca->slot_info[slot].sem);
+
+ cam_present_now = (slot_status & DVB_CA_EN50221_POLL_CAM_PRESENT) ? 1: 0;
+ cam_changed = (slot_status & DVB_CA_EN50221_POLL_CAM_CHANGED) ? 1: 0;
+ if (!cam_changed) {
+ int cam_present_old = (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE);
+ cam_changed = (cam_present_now != cam_present_old);
+ }
+
+ if (cam_changed) {
+ if (!cam_present_now) {
+ ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
+ } else {
+ ca->slot_info[slot].camchange_type = DVB_CA_EN50221_CAMCHANGE_INSERTED;
+ }
+ atomic_set(&ca->slot_info[slot].camchange_count, 1);
+ } else {
+ if ((ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) &&
+ (slot_status & DVB_CA_EN50221_POLL_CAM_READY)) {
+ // move to validate state if reset is completed
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE;
+ }
+ }
+
+ return cam_changed;
+}
+
+
+/**
+ * Wait for flags to become set on the STATUS register on a CAM interface,
+ * checking for errors and timeout.
+ *
+ * @param ca CA instance.
+ * @param slot Slot on interface.
+ * @param waitfor Flags to wait for.
+ * @param timeout_ms Timeout in milliseconds.
+ *
+ * @return 0 on success, nonzero on error.
+ */
+static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private* ca, int slot, u8 waitfor, int timeout_hz)
+{
+ unsigned long timeout;
+ unsigned long start;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* loop until timeout elapsed */
+ start = jiffies;
+ timeout = jiffies + timeout_hz;
+ while(1) {
+ /* read the status and check for error */
+ int res = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
+ if (res < 0) return -EIO;
+
+ /* if we got the flags, it was successful! */
+ if (res & waitfor) {
+ dprintk("%s succeeded timeout:%lu\n", __FUNCTION__, jiffies - start);
+ return 0;
+ }
+
+ /* check for timeout */
+ if (time_after(jiffies, timeout)) {
+ break;
+ }
+
+ /* wait for a bit */
+ dvb_delay(1);
+ }
+
+ dprintk("%s failed timeout:%lu\n", __FUNCTION__, jiffies - start);
+
+ /* if we get here, we've timed out */
+ return -ETIMEDOUT;
+}
+
+
+/**
+ * Initialise the link layer connection to a CAM.
+ *
+ * @param ca CA instance.
+ * @param slot Slot id.
+ *
+ * @return 0 on success, nonzero on failure.
+ */
+static int dvb_ca_en50221_link_init(struct dvb_ca_private* ca, int slot)
+{
+ int ret;
+ int buf_size;
+ u8 buf[2];
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* we'll be determining these during this function */
+ ca->slot_info[slot].fr_irq_supported = 0;
+ ca->slot_info[slot].da_irq_supported = 0;
+
+ /* reset the link interface. Note CAM IRQs are disabled */
+ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, CMDREG_RS)) != 0) return ret;
+ if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ/10)) != 0) return ret;
+
+ /* set the host link buffer size temporarily. it will be overwritten with the
+ * real negotiated size later. */
+ ca->slot_info[slot].link_buf_size = 2;
+
+ /* read the buffer size from the CAM */
+ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SR)) != 0) return ret;
+ if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ/10)) != 0) return ret;
+ if ((ret = dvb_ca_en50221_read_data(ca, slot, buf, 2)) != 2) return -EIO;
+ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0) return ret;
+
+ /* store it, and choose the minimum of our buffer and the CAM's buffer size */
+ buf_size = (buf[0] << 8) | buf[1];
+ if (buf_size > HOST_LINK_BUF_SIZE) buf_size = HOST_LINK_BUF_SIZE;
+ ca->slot_info[slot].link_buf_size = buf_size;
+ buf[0] = buf_size >> 8;
+ buf[1] = buf_size & 0xff;
+ dprintk("Chosen link buffer size of %i\n", buf_size);
+
+ /* write the buffer size to the CAM */
+ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SW)) != 0) return ret;
+ if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_FR, HZ/10)) != 0) return ret;
+ if ((ret = dvb_ca_en50221_write_data(ca, slot, buf, 2)) != 2) return -EIO;
+ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN)) != 0) return ret;
+
+ /* success */
+ return 0;
+}
+
+/**
+ * Read a tuple from attribute memory.
+ *
+ * @param ca CA instance.
+ * @param slot Slot id.
+ * @param address Address to read from. Updated.
+ * @param tupleType Tuple id byte. Updated.
+ * @param tupleLength Tuple length. Updated.
+ * @param tuple Dest buffer for tuple (must be 256 bytes). Updated.
+ *
+ * @return 0 on success, nonzero on error.
+ */
+static int dvb_ca_en50221_read_tuple(struct dvb_ca_private* ca, int slot,
+ int* address, int* tupleType, int* tupleLength, u8* tuple)
+{
+ int i;
+ int _tupleType;
+ int _tupleLength;
+ int _address = *address;
+
+ /* grab the next tuple length and type */
+ if ((_tupleType = ca->pub->read_attribute_mem(ca->pub, slot, _address)) < 0) return _tupleType;
+ if ((_tupleLength = ca->pub->read_attribute_mem(ca->pub, slot, _address+2)) < 0) return _tupleLength;
+ _address += 4;
+
+ dprintk("TUPLE type:0x%x length:%i\n", _tupleType, _tupleLength);
+
+ /* read in the whole tuple */
+ for(i=0; i< _tupleLength; i++) {
+ tuple[i] = ca->pub->read_attribute_mem(ca->pub, slot, _address + (i*2));
+ dprintk(" 0x%02x: 0x%02x %c\n", i, tuple[i] & 0xff, ((tuple[i] > 31) && (tuple[i] < 127)) ? tuple[i] : '.');
+ }
+ _address += (_tupleLength*2);
+
+ // success
+ *tupleType = _tupleType;
+ *tupleLength = _tupleLength;
+ *address = _address;
+ return 0;
+}
+
+
+/**
+ * Parse attribute memory of a CAM module, extracting Config register, and checking
+ * it is a DVB CAM module.
+ *
+ * @param ca CA instance.
+ * @param slot Slot id.
+ *
+ * @return 0 on success, <0 on failure.
+ */
+static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private* ca, int slot)
+{
+ int address = 0;
+ int tupleLength;
+ int tupleType;
+ u8 tuple[257];
+ char* dvb_str;
+ int rasz;
+ int status;
+ int got_cftableentry = 0;
+ int end_chain = 0;
+ int i;
+ u16 manfid = 0;
+ u16 devid = 0;
+
+
+ // CISTPL_DEVICE_0A
+ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status;
+ if (tupleType != 0x1D) return -EINVAL;
+
+
+
+ // CISTPL_DEVICE_0C
+ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status;
+ if (tupleType != 0x1C) return -EINVAL;
+
+
+
+ // CISTPL_VERS_1
+ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status;
+ if (tupleType != 0x15) return -EINVAL;
+
+
+
+ // CISTPL_MANFID
+ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status;
+ if (tupleType != 0x20) return -EINVAL;
+ if (tupleLength != 4) return -EINVAL;
+ manfid = (tuple[1] << 8) | tuple[0];
+ devid = (tuple[3] << 8) | tuple[2];
+
+
+
+ // CISTPL_CONFIG
+ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status;
+ if (tupleType != 0x1A) return -EINVAL;
+ if (tupleLength < 3) return -EINVAL;
+
+ /* extract the configbase */
+ rasz = tuple[0] & 3;
+ if (tupleLength < (3 + rasz + 14)) return -EINVAL;
+ ca->slot_info[slot].config_base = 0;
+ for(i=0; i< rasz+1; i++) {
+ ca->slot_info[slot].config_base |= (tuple[2+i] << (8*i));
+ }
+
+ /* check it contains the correct DVB string */
+ dvb_str = findstr(tuple, tupleLength, "DVB_CI_V", 8);
+ if (dvb_str == NULL) return -EINVAL;
+ if (tupleLength < ((dvb_str - (char*) tuple) + 12)) return -EINVAL;
+
+ /* is it a version we support? */
+ if (strncmp(dvb_str + 8, "1.00", 4)) {
+ printk("dvb_ca: Unsupported DVB CAM module version %c%c%c%c\n",
+ dvb_str[8], dvb_str[9], dvb_str[10], dvb_str[11]);
+ return -EINVAL;
+ }
+
+ /* process the CFTABLE_ENTRY tuples, and any after those */
+ while((!end_chain) && (address < 0x1000)) {
+ if ((status = dvb_ca_en50221_read_tuple(ca, slot, &address, &tupleType, &tupleLength, tuple)) < 0) return status;
+ switch(tupleType) {
+ case 0x1B: // CISTPL_CFTABLE_ENTRY
+ if (tupleLength < (2+11+17)) break;
+
+ /* if we've already parsed one, just use it */
+ if (got_cftableentry) break;
+
+ /* get the config option */
+ ca->slot_info[slot].config_option = tuple[0] & 0x3f;
+
+ /* does it contain an interrupt descriptor structure? */
+ i = 1;
+ if (tuple[0] & 0x80) i++;
+
+ /* OK, check it contains the correct strings */
+ if ((findstr(tuple, tupleLength, "DVB_HOST", 8) == NULL) ||
+ (findstr(tuple, tupleLength, "DVB_CI_MODULE", 13) == NULL)) break;
+
+ got_cftableentry = 1;
+ break;
+
+ case 0x14: // CISTPL_NO_LINK
+ break;
+
+ case 0xFF: // CISTPL_END
+ end_chain = 1;
+ break;
+
+ default: /* Unknown tuple type - just skip this tuple and move to the next one */
+ dprintk("dvb_ca: Skipping unknown tuple type:0x%x length:0x%x\n", tupleType, tupleLength);
+ break;
+ }
+ }
+
+ if ((address > 0x1000) || (!got_cftableentry)) return -EINVAL;
+
+ dprintk("Valid DVB CAM detected MANID:%x DEVID:%x CONFIGBASE:0x%x CONFIGOPTION:0x%x\n",
+ manfid, devid,
+ ca->slot_info[slot].config_base,
+ ca->slot_info[slot].config_option);
+
+ // success!
+ return 0;
+}
+
+
+/**
+ * Set CAM's configoption correctly.
+ *
+ * @param ca CA instance.
+ * @param slot Slot containing the CAM.
+ */
+static int dvb_ca_en50221_set_configoption(struct dvb_ca_private* ca, int slot)
+{
+ int configoption;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* set the config option */
+ ca->pub->write_attribute_mem(ca->pub, slot, ca->slot_info[slot].config_base, ca->slot_info[slot].config_option);
+
+ /* check it */
+ configoption = ca->pub->read_attribute_mem(ca->pub, slot, ca->slot_info[slot].config_base);
+ if ((configoption & 0x3f) != ca->slot_info[slot].config_option) {
+ return -EINVAL;
+ }
+
+ /* fine! */
+ return 0;
+
+}
+
+
+/**
+ * This function talks to an EN50221 CAM control interface. It reads a buffer of
+ * data from the CAM. The data can either be stored in a supplied buffer, or
+ * automatically be added to the slot's rx_buffer.
+ *
+ * @param ca CA instance.
+ * @param slot Slot to read from.
+ * @param ebuf If non-NULL, the data will be written to this buffer. If NULL,
+ * the data will be added into the buffering system as a normal fragment.
+ * @param ecount Size of ebuf. Ignored if ebuf is NULL.
+ *
+ * @return Number of bytes read, or < 0 on error
+ */
+static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebuf, int ecount)
+{
+ int bytes_read;
+ int status;
+ u8 buf[HOST_LINK_BUF_SIZE];
+ int i;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* acquire the slot */
+ if ((status = down_interruptible(&ca->slot_info[slot].sem)) != 0) return status;
+
+ /* check if we have space for at a link buf in the rx_buffer */
+ if (ebuf == NULL) {
+ if (dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer) <
+ (ca->slot_info[slot].link_buf_size + DVB_RINGBUFFER_PKTHDRSIZE)) {
+ status = -EAGAIN;
+ goto exit;
+ }
+ }
+
+ /* reset the interface if there's been a tx error */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit;
+ if (status & STATUSREG_TXERR) {
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exit;
+ }
+ if (!(status & STATUSREG_DA)) {
+ /* no data */
+ status = 0;
+ goto exit;
+ }
+
+ /* read the amount of data */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH)) < 0) goto exit;
+ bytes_read = status << 8;
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW)) < 0) goto exit;
+ bytes_read |= status;
+
+ /* check it will fit */
+ if (ebuf == NULL) {
+ if (bytes_read > ca->slot_info[slot].link_buf_size) {
+ printk("dvb_ca: CAM tried to send a buffer larger than the link buffer size!\n");
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exit;
+ }
+ if (bytes_read < 2) {
+ printk("dvb_ca: CAM sent a buffer that was less than 2 bytes!\n");
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exit;
+ }
+ } else {
+ if (bytes_read > ecount) {
+ printk("dvb_ca: CAM tried to send a buffer larger than the ecount size!\n");
+ status = -EIO;
+ goto exit;
+ }
+ }
+
+ /* fill the buffer */
+ for(i=0; i < bytes_read; i++) {
+ /* read byte and check */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_DATA)) < 0) goto exit;
+
+ /* OK, store it in the buffer */
+ buf[i] = status;
+ }
+
+ /* check for read error (RE should now go to 0) */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit;
+ if (status & STATUSREG_RE) {
+ status = -EIO;
+ goto exit;
+ }
+
+ /* OK, add it to the receive buffer, or copy into external buffer if supplied */
+ if (ebuf == NULL) {
+ dvb_ringbuffer_pkt_write(&ca->slot_info[slot].rx_buffer, buf, bytes_read, 0);
+ } else {
+ memcpy(ebuf, buf, bytes_read);
+ }
+ status = bytes_read;
+
+exit:
+ up(&ca->slot_info[slot].sem);
+ return status;
+}
+
+
+/**
+ * This function talks to an EN50221 CAM control interface. It writes a buffer of data
+ * to a CAM.
+ *
+ * @param ca CA instance.
+ * @param slot Slot to write to.
+ * @param ebuf The data in this buffer is treated as a complete link-level packet to
+ * be written.
+ * @param count Size of ebuf.
+ *
+ * @return Number of bytes written, or < 0 on error.
+ */
+static int dvb_ca_en50221_write_data(struct dvb_ca_private* ca, int slot, u8* buf, int bytes_write)
+{
+ int status;
+ int i;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+
+ // sanity check
+ if (bytes_write > ca->slot_info[slot].link_buf_size) return -EINVAL;
+
+ /* acquire the slot */
+ if ((status = down_interruptible(&ca->slot_info[slot].sem)) != 0) return status;
+
+ /* reset the interface if there's been a tx error */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit;
+ if (status & STATUSREG_TXERR) {
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ status = -EIO;
+ goto exitnowrite;
+ }
+
+ /* check if interface is actually waiting for us to read from it */
+ if (status & STATUSREG_DA) {
+ status = -EAGAIN;
+ goto exitnowrite;
+ }
+
+ /* OK, set HC bit */
+ if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_HC)) != 0) goto exit;
+
+ /* check if interface is still free */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit;
+ if (!(status & STATUSREG_FR)) {
+ /* it wasn't free => try again later */
+ status = -EAGAIN;
+ goto exit;
+ }
+
+ /* send the amount of data */
+ if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH, bytes_write >> 8)) != 0) goto exit;
+ if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW, bytes_write & 0xff)) != 0) goto exit;
+
+ /* send the buffer */
+ for(i=0; i < bytes_write; i++) {
+ if ((status = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_DATA, buf[i])) != 0) goto exit;
+ }
+
+ /* check for write error (WE should now be 0) */
+ if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) goto exit;
+ if (status & STATUSREG_WE) {
+ status = -EIO;
+ goto exit;
+ }
+ status = bytes_write;
+
+exit:
+ ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
+
+exitnowrite:
+ up(&ca->slot_info[slot].sem);
+ if (bytes_write >= 0) wake_up_interruptible(&ca->wait_queue);
+ return status;
+}
+
+
+
+/* ******************************************************************************** */
+/* EN50221 higher level functions */
+
+
+/**
+ * A CAM has been removed => shut it down.
+ *
+ * @param ca CA instance.
+ * @param slot Slot to shut down.
+ */
+static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private* ca, int slot)
+{
+ int status;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if ((status = down_interruptible(&ca->slot_info[slot].sem)) != 0) return status;
+ ca->pub->slot_shutdown(ca->pub, slot);
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
+ if (ca->slot_info[slot].rx_buffer.data) vfree(ca->slot_info[slot].rx_buffer.data);
+ ca->slot_info[slot].rx_buffer.data = NULL;
+ up(&ca->slot_info[slot].sem);
+
+ /* need to wake up all write processes to check if they're now
+ trying to write to a defunct CAM */
+ wake_up_interruptible(&ca->wait_queue);
+
+ dprintk("Slot %i shutdown\n", slot);
+
+ /* success */
+ return 0;
+}
+
+
+/**
+ * A CAMCHANGE IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ * @param change_type One of the DVB_CA_CAMCHANGE_* values.
+ */
+void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221* pubca, int slot, int change_type)
+{
+ struct dvb_ca_private* ca = (struct dvb_ca_private*) pubca->private;
+
+ dprintk("CAMCHANGE IRQ slot:%i change_type:%i\n", slot, change_type);
+
+ switch(change_type) {
+ case DVB_CA_EN50221_CAMCHANGE_REMOVED:
+ case DVB_CA_EN50221_CAMCHANGE_INSERTED:
+ break;
+
+ default:
+ return;
+ }
+
+ ca->slot_info[slot].camchange_type = change_type;
+ atomic_inc(&ca->slot_info[slot].camchange_count);
+ dvb_ca_en50221_thread_wakeup(ca);
+}
+
+
+/**
+ * A CAMREADY IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221* pubca, int slot)
+{
+ struct dvb_ca_private* ca = (struct dvb_ca_private*) pubca->private;
+
+ dprintk("CAMREADY IRQ slot:%i\n", slot);
+
+ if (ca->slot_info[slot].slot_state == DVB_CA_SLOTSTATE_WAITREADY) {
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_VALIDATE;
+ dvb_ca_en50221_thread_wakeup(ca);
+ }
+}
+
+
+/**
+ * An FR or DA IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221* pubca, int slot)
+{
+ struct dvb_ca_private* ca = (struct dvb_ca_private*) pubca->private;
+ int flags;
+
+ dprintk("FR/DA IRQ slot:%i\n", slot);
+
+ switch(ca->slot_info[slot].slot_state) {
+ case DVB_CA_SLOTSTATE_LINKINIT:
+ flags = ca->pub->read_cam_control(pubca, slot, CTRLIF_STATUS);
+ if (flags & STATUSREG_DA) {
+ dprintk("CAM supports DA IRQ\n");
+ ca->slot_info[slot].da_irq_supported = 1;
+ } else if (flags & STATUSREG_FR) {
+ // this is an else because some modules leave the FR bit
+ // set as well as DA during the link init sequence
+ dprintk("CAM supports FR IRQ\n");
+ ca->slot_info[slot].fr_irq_supported = 1;
+ }
+ break;
+
+ case DVB_CA_SLOTSTATE_RUNNING:
+ flags = ca->pub->read_cam_control(pubca, slot, CTRLIF_STATUS);
+ if (flags & STATUSREG_DA) {
+ dvb_ca_en50221_thread_wakeup(ca);
+ }
+ if (flags & STATUSREG_FR) {
+ wake_up_interruptible(&ca->wait_queue);
+ }
+ break;
+ }
+}
+
+
+
+/* ******************************************************************************** */
+/* EN50221 thread functions */
+
+/**
+ * Wake up the DVB CA thread
+ *
+ * @param ca CA instance.
+ */
+static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private* ca)
+{
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ ca->wakeup = 1;
+ mb();
+ wake_up_interruptible(&ca->thread_queue);
+}
+
+/**
+ * Used by the CA thread to determine if an early wakeup is necessary
+ *
+ * @param ca CA instance.
+ */
+static int dvb_ca_en50221_thread_should_wakeup(struct dvb_ca_private* ca)
+{
+ if (ca->wakeup) {
+ ca->wakeup = 0;
+ return 1;
+ }
+ if (ca->exit) return 1;
+ return 0;
+}
+
+
+/**
+ * Update the delay used by the thread.
+ *
+ * @param ca CA instance.
+ */
+static void dvb_ca_en50221_thread_update_delay(struct dvb_ca_private* ca)
+{
+ int delay;
+ int curdelay = 100000000;
+ int slot;
+
+ for(slot=0; slot < ca->slot_count; slot++) {
+ if (!ca->open) {
+ delay = HZ*60;
+ if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) delay = HZ/10;
+ } else {
+
+ switch(ca->slot_info[slot].slot_state) {
+ default:
+ case DVB_CA_SLOTSTATE_NONE:
+ case DVB_CA_SLOTSTATE_INVALID:
+ delay = HZ*60;
+ if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) delay = HZ/10;
+ break;
+
+ case DVB_CA_SLOTSTATE_UNINITIALISED:
+ case DVB_CA_SLOTSTATE_WAITREADY:
+ case DVB_CA_SLOTSTATE_VALIDATE:
+ case DVB_CA_SLOTSTATE_WAITFR:
+ case DVB_CA_SLOTSTATE_LINKINIT:
+ delay = HZ/10;
+ break;
+
+ case DVB_CA_SLOTSTATE_RUNNING:
+ delay = HZ*60;
+ if (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) delay = HZ/10;
+ if ((!ca->slot_info[slot].da_irq_supported) ||
+ (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_DA))) delay = HZ/100;
+ break;
+ }
+ }
+
+ if (delay < curdelay) curdelay = delay;
+ }
+
+ ca->delay = curdelay;
+}
+
+
+
+/**
+ * Kernel thread which monitors CA slots for CAM changes, and performs data transfers.
+ */
+static int dvb_ca_en50221_thread(void* data)
+{
+ struct dvb_ca_private *ca = (struct dvb_ca_private*) data;
+ char name[15];
+ int slot;
+ int flags;
+ int pktcount;
+ void* rxbuf;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* setup kernel thread */
+ snprintf(name, sizeof(name), "kdvb-ca-%i:%i", ca->dvbdev->adapter->num, ca->dvbdev->id);
+ dvb_kernel_thread_setup(name);
+
+ /* choose the correct initial delay */
+ dvb_ca_en50221_thread_update_delay(ca);
+
+ /* main loop */
+ while(!ca->exit) {
+ /* sleep for a bit */
+ if (!ca->wakeup) {
+ flags = wait_event_interruptible_timeout(ca->thread_queue, dvb_ca_en50221_thread_should_wakeup(ca), ca->delay);
+ if ((flags == -ERESTARTSYS) || ca->exit) {
+ /* got signal or quitting */
+ break;
+ }
+ }
+ ca->wakeup = 0;
+
+ /* go through all the slots processing them */
+ for(slot=0; slot < ca->slot_count; slot++) {
+
+ // check the cam status + deal with CAMCHANGEs
+ while(dvb_ca_en50221_check_camstatus(ca, slot)) {
+ /* clear down an old CI slot if necessary */
+ if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) dvb_ca_en50221_slot_shutdown(ca, slot);
+
+ /* if a CAM is NOW present, initialise it */
+ if (ca->slot_info[slot].camchange_type == DVB_CA_EN50221_CAMCHANGE_INSERTED) {
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_UNINITIALISED;
+ }
+
+ /* we've handled one CAMCHANGE */
+ dvb_ca_en50221_thread_update_delay(ca);
+ atomic_dec(&ca->slot_info[slot].camchange_count);
+ }
+
+ // CAM state machine
+ switch(ca->slot_info[slot].slot_state) {
+ case DVB_CA_SLOTSTATE_NONE:
+ case DVB_CA_SLOTSTATE_INVALID:
+ // no action needed
+ break;
+
+ case DVB_CA_SLOTSTATE_UNINITIALISED:
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_WAITREADY;
+ ca->pub->slot_reset(ca->pub, slot);
+ ca->slot_info[slot].timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
+ break;
+
+ case DVB_CA_SLOTSTATE_WAITREADY:
+ if (time_after(jiffies, ca->slot_info[slot].timeout)) {
+ printk("dvb_ca: PC card did not respond\n");
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+ break;
+ }
+ // no other action needed; will automatically change state when ready
+ break;
+
+ case DVB_CA_SLOTSTATE_VALIDATE:
+ if (dvb_ca_en50221_parse_attributes(ca, slot) != 0) {
+ printk("dvb_ca: Invalid PC card inserted\n");
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+ break;
+ }
+ if (dvb_ca_en50221_set_configoption(ca, slot) != 0) {
+ printk("dvb_ca: Unable to initialise CAM\n");
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+ break;
+ }
+
+ dprintk("DVB CAM validated successfully\n");
+
+ ca->slot_info[slot].timeout = jiffies + (INIT_TIMEOUT_SECS * HZ);
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_WAITFR;
+ ca->wakeup = 1;
+ break;
+
+ case DVB_CA_SLOTSTATE_WAITFR:
+ if (time_after(jiffies, ca->slot_info[slot].timeout)) {
+ printk("dvb_ca: DVB CAM did not respond\n");
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+ break;
+ }
+
+ flags = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS);
+ if (flags & STATUSREG_FR) {
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+ ca->wakeup = 1;
+ }
+ break;
+
+ case DVB_CA_SLOTSTATE_LINKINIT:
+ if (dvb_ca_en50221_link_init(ca, slot) != 0) {
+ printk("dvb_ca: DVB CAM link initialisation failed :(\n");
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+ break;
+ }
+
+ rxbuf = vmalloc(RX_BUFFER_SIZE);
+ if (rxbuf == NULL) {
+ printk("dvb_ca: Unable to allocate CAM rx buffer\n");
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+ break;
+ }
+ dvb_ringbuffer_init(&ca->slot_info[slot].rx_buffer, rxbuf, RX_BUFFER_SIZE);
+
+ ca->pub->slot_ts_enable(ca->pub, slot);
+ ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_RUNNING;
+ dvb_ca_en50221_thread_update_delay(ca);
+ printk("dvb_ca: DVB CAM detected and initialised successfully\n");
+ break;
+
+ case DVB_CA_SLOTSTATE_RUNNING:
+ if (!ca->open) break;
+
+ pktcount = 0;
+ while(dvb_ca_en50221_read_data(ca, slot, NULL, 0) > 0) {
+ if (!ca->open) break;
+
+ /* if a CAMCHANGE occurred at some point, do not do any more processing of this slot */
+ if (dvb_ca_en50221_check_camstatus(ca, slot)) {
+ // we dont want to sleep on the next iteration so we can handle the cam change
+ ca->wakeup = 1;
+ break;
+ }
+
+ /* check if we've hit our limit this time */
+ if (++pktcount >= MAX_RX_PACKETS_PER_ITERATION) {
+ // dont sleep; there is likely to be more data to read
+ ca->wakeup = 1;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ /* completed */
+ ca->thread_pid = 0;
+ mb();
+ wake_up_interruptible (&ca->thread_queue);
+ return 0;
+}
+
+
+
+/* ******************************************************************************** */
+/* EN50221 IO interface functions */
+
+/**
+ * Real ioctl implementation.
+ * NOTE: CA_SEND_MSG/CA_GET_MSG ioctls have userspace buffers passed to them.
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ * @param cmd IOCTL command.
+ * @param arg Associated argument.
+ *
+ * @return 0 on success, <0 on error.
+ */
+static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *parg)
+{
+ struct dvb_device* dvbdev=(struct dvb_device*) file->private_data;
+ struct dvb_ca_private* ca = (struct dvb_ca_private*) dvbdev->priv;
+ int err=0;
+ int slot;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ switch (cmd) {
+ case CA_RESET:
+ for(slot = 0; slot < ca->slot_count; slot++) {
+ if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_NONE) {
+ dvb_ca_en50221_slot_shutdown(ca, slot);
+ if (ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)
+ dvb_ca_en50221_camchange_irq(ca->pub, slot, DVB_CA_EN50221_CAMCHANGE_INSERTED);
+ }
+ }
+ ca->next_read_slot = 0;
+ dvb_ca_en50221_thread_wakeup(ca);
+ break;
+
+ case CA_GET_CAP:
+ {
+ struct ca_caps *caps = (struct ca_caps*) parg;
+
+ caps->slot_num=ca->slot_count;
+ caps->slot_type=CA_CI_LINK;
+ caps->descr_num=0;
+ caps->descr_type=0;
+ break;
+ }
+
+
+ case CA_GET_SLOT_INFO:
+ {
+ struct ca_slot_info *info=(struct ca_slot_info *)parg;
+
+ if ((info->num > ca->slot_count) || (info->num < 0))
+ return -EINVAL;
+
+ info->type = CA_CI_LINK;
+ info->flags = 0;
+ if ((ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_NONE) &&
+ (ca->slot_info[info->num].slot_state != DVB_CA_SLOTSTATE_INVALID)) {
+ info->flags = CA_CI_MODULE_PRESENT;
+ }
+ if (ca->slot_info[info->num].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
+ info->flags |= CA_CI_MODULE_READY;
+ }
+ break;
+ }
+
+ default:
+ err=-EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+
+/**
+ * Wrapper for ioctl implementation.
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ * @param cmd IOCTL command.
+ * @param arg Associated argument.
+ *
+ * @return 0 on success, <0 on error.
+ */
+static int dvb_ca_en50221_io_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ return dvb_usercopy(inode, file, cmd, arg, dvb_ca_en50221_io_do_ioctl);
+}
+
+
+/**
+ * Implementation of write() syscall.
+ *
+ * @param file File structure.
+ * @param buf Source buffer.
+ * @param count Size of source buffer.
+ * @param ppos Position in file (ignored).
+ *
+ * @return Number of bytes read, or <0 on error.
+ */
+static ssize_t dvb_ca_en50221_io_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+ struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+ struct dvb_ca_private *ca=(struct dvb_ca_private*) dvbdev->priv;
+ u8 slot, connection_id;
+ int status;
+ char fragbuf[HOST_LINK_BUF_SIZE];
+ int fragpos = 0;
+ int fraglen;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* Incoming packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */
+ if (count < 2) return -EINVAL;
+
+ /* extract slot & connection id */
+ if (copy_from_user(&slot, buf, 1)) return -EFAULT;
+ if (copy_from_user(&connection_id, buf+1, 1)) return -EFAULT;
+ buf+=2;
+ count-=2;
+
+ /* check if the slot is actually running */
+ if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) return -EINVAL;
+
+ /* fragment the packets & store in the buffer */
+ while(fragpos < count) {
+ fraglen = ca->slot_info[slot].link_buf_size - 2;
+ if ((count - fragpos) < fraglen) fraglen = count - fragpos;
+
+ fragbuf[0] = connection_id;
+ fragbuf[1] = ((fragpos + fraglen) < count) ? 0x80 : 0x00;
+ if ((status = copy_from_user(fragbuf+2, buf+fragpos, fraglen)) != 0) goto exit;
+
+ while(1) {
+ status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen+2);
+ if (status == (fraglen+2)) break;
+ if (status != -EAGAIN) goto exit;
+
+ dvb_delay(1);
+ }
+
+ fragpos += fraglen;
+ }
+ status = count;
+
+exit:
+ dvb_ca_en50221_thread_wakeup(ca);
+ return status;
+}
+
+
+/**
+ * Condition for waking up in dvb_ca_en50221_io_read_condition
+ */
+static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private* ca, int* result, int* _slot)
+{
+ int slot;
+ int slot_count = 0;
+ int idx;
+ int fraglen;
+ int connection_id = -1;
+ int found = 0;
+ u8 hdr[2];
+
+ slot = ca->next_read_slot;
+ while((slot_count < ca->slot_count) && (!found)) {
+ if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) goto nextslot;
+
+ if ((*result = down_interruptible(&ca->slot_info[slot].sem)) != 0) return 1;
+
+ idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
+ while(idx != -1) {
+ dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2, 0);
+ if (connection_id == -1) connection_id = hdr[0];
+ if ((hdr[0] == connection_id) && ((hdr[1] & 0x80) == 0)) {
+ *_slot = slot;
+ found = 1;
+ break;
+ }
+
+ idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen);
+ }
+
+ if (!found) up(&ca->slot_info[slot].sem);
+
+nextslot:
+ slot = (slot + 1) % ca->slot_count;
+ slot_count++;
+ }
+
+ ca->next_read_slot = slot;
+ return found;
+}
+
+
+/**
+ * Implementation of read() syscall.
+ *
+ * @param file File structure.
+ * @param buf Destination buffer.
+ * @param count Size of destination buffer.
+ * @param ppos Position in file (ignored).
+ *
+ * @return Number of bytes read, or <0 on error.
+ */
+static ssize_t dvb_ca_en50221_io_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+ struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+ struct dvb_ca_private *ca=(struct dvb_ca_private*) dvbdev->priv;
+ int status;
+ int result = 0;
+ u8 hdr[2];
+ int slot;
+ int connection_id = -1;
+ size_t idx, idx2;
+ int last_fragment = 0;
+ size_t fraglen;
+ int pktlen;
+ int dispose = 0;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* Outgoing packet has a 2 byte header. hdr[0] = slot_id, hdr[1] = connection_id */
+ if (count < 2) return -EINVAL;
+
+ /* wait for some data */
+ if ((status = dvb_ca_en50221_io_read_condition(ca, &result, &slot)) == 0) {
+
+ /* if we're in nonblocking mode, exit immediately */
+ if (file->f_flags & O_NONBLOCK) return -EWOULDBLOCK;
+
+ /* wait for some data */
+ status = wait_event_interruptible(ca->wait_queue, dvb_ca_en50221_io_read_condition(ca, &result, &slot));
+ }
+ if ((status < 0) || (result < 0)) {
+ if (result) return result;
+ return status;
+ }
+
+ idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
+ pktlen = 2;
+ do {
+ if (idx == -1) {
+ printk("dvb_ca: BUG: read packet ended before last_fragment encountered\n");
+ status = -EIO;
+ goto exit;
+ }
+
+ dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 0, hdr, 2, 0);
+ if (connection_id == -1) connection_id = hdr[0];
+ if (hdr[0] == connection_id) {
+ if (pktlen < count) {
+ if ((pktlen + fraglen - 2) > count) {
+ fraglen = count - pktlen;
+ } else {
+ fraglen -= 2;
+ }
+
+ if ((status = dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 2, buf + pktlen, fraglen, 1)) != 0) {
+ goto exit;
+ }
+ pktlen += fraglen;
+ }
+
+ if ((hdr[1] & 0x80) == 0) last_fragment = 1;
+ dispose = 1;
+ }
+
+ idx2 = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen);
+ if (dispose) dvb_ringbuffer_pkt_dispose(&ca->slot_info[slot].rx_buffer, idx);
+ idx = idx2;
+ dispose = 0;
+ } while (!last_fragment);
+
+ if ((status = copy_to_user(buf, &slot, 1)) != 0) goto exit;
+ if ((status = copy_to_user(buf+1, &connection_id, 1)) != 0) goto exit;
+ status = pktlen;
+
+exit:
+ up(&ca->slot_info[slot].sem);
+ return status;
+}
+
+
+/**
+ * Implementation of file open syscall.
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ *
+ * @return 0 on success, <0 on failure.
+ */
+static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev = (struct dvb_device *) file->private_data;
+ struct dvb_ca_private *ca = (struct dvb_ca_private*) dvbdev->priv;
+ int err;
+ int i;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ err=dvb_generic_open(inode, file);
+ if (err<0)
+ return err;
+
+ for(i=0; i< ca->slot_count; i++) {
+ if (ca->slot_info[i].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
+ dvb_ringbuffer_flush(&ca->slot_info[i].rx_buffer);
+ }
+ }
+
+ ca->open = 1;
+ dvb_ca_en50221_thread_update_delay(ca);
+ dvb_ca_en50221_thread_wakeup(ca);
+
+ return 0;
+}
+
+
+/**
+ * Implementation of file close syscall.
+ *
+ * @param inode Inode concerned.
+ * @param file File concerned.
+ *
+ * @return 0 on success, <0 on failure.
+ */
+static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)
+{
+ struct dvb_device *dvbdev=(struct dvb_device *) file->private_data;
+ struct dvb_ca_private *ca=(struct dvb_ca_private*) dvbdev->priv;
+ int err;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* mark the CA device as closed */
+ ca->open = 0;
+ dvb_ca_en50221_thread_update_delay(ca);
+
+ err=dvb_generic_release(inode, file);
+ if (err<0)
+ return err;
+ return 0;
+}
+
+
+/**
+ * Implementation of poll() syscall.
+ *
+ * @param file File concerned.
+ * @param wait poll wait table.
+ *
+ * @return Standard poll mask.
+ */
+static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table *wait)
+{
+ struct dvb_device* dvbdev = (struct dvb_device*) file->private_data;
+ struct dvb_ca_private* ca = (struct dvb_ca_private*) dvbdev->priv;
+ unsigned int mask=0;
+ int slot;
+ int result = 0;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
+ up(&ca->slot_info[slot].sem);
+ mask |= POLLIN;
+ }
+
+ /* if there is something, return now */
+ if (mask) return mask;
+
+ /* wait for something to happen */
+ poll_wait(file, &ca->wait_queue, wait);
+
+ if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
+ up(&ca->slot_info[slot].sem);
+ mask |= POLLIN;
+ }
+
+ return mask;
+}
+
+
+static struct file_operations dvb_ca_fops = {
+ owner: THIS_MODULE,
+ read: dvb_ca_en50221_io_read,
+ write: dvb_ca_en50221_io_write,
+ ioctl: dvb_ca_en50221_io_ioctl,
+ open: dvb_ca_en50221_io_open,
+ release: dvb_ca_en50221_io_release,
+ poll: dvb_ca_en50221_io_poll,
+};
+
+static struct dvb_device dvbdev_ca = {
+ priv: 0,
+ users: 1,
+ writers: 1,
+ fops: &dvb_ca_fops,
+};
+
+
+/* ******************************************************************************** */
+/* Initialisation/shutdown functions */
+
+
+/**
+ * Initialise a new DVB CA EN50221 interface device.
+ *
+ * @param dvb_adapter DVB adapter to attach the new CA device to.
+ * @param ca The dvb_ca instance.
+ * @param flags Flags describing the CA device (DVB_CA_FLAG_*).
+ * @param slot_count Number of slots supported.
+ *
+ * @return 0 on success, nonzero on failure
+ */
+int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221* pubca, int flags, int slot_count)
+{
+ int ret;
+ struct dvb_ca_private* ca = NULL;
+ int i;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (slot_count < 1) return -EINVAL;
+
+ /* initialise the system data */
+ if ((ca = (struct dvb_ca_private*) kmalloc(sizeof(struct dvb_ca_private), GFP_KERNEL)) == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ memset(ca, 0, sizeof(struct dvb_ca_private));
+ ca->pub = pubca;
+ ca->flags = flags;
+ ca->slot_count = slot_count;
+ if ((ca->slot_info = kmalloc(sizeof(struct dvb_ca_slot) * slot_count, GFP_KERNEL)) == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ memset(ca->slot_info, 0, sizeof(struct dvb_ca_slot) * slot_count);
+ init_waitqueue_head(&ca->wait_queue);
+ ca->thread_pid = 0;
+ init_waitqueue_head(&ca->thread_queue);
+ ca->exit = 0;
+ ca->open = 0;
+ ca->wakeup = 0;
+ ca->next_read_slot = 0;
+ pubca->private = ca;
+
+ /* register the DVB device */
+ ret = dvb_register_device(dvb_adapter, &ca->dvbdev, &dvbdev_ca, ca, DVB_DEVICE_CA);
+ if (ret) goto error;
+
+ /* now initialise each slot */
+ for(i=0; i< slot_count; i++) {
+ memset(&ca->slot_info[i], 0, sizeof(struct dvb_ca_slot));
+ ca->slot_info[i].slot_state = DVB_CA_SLOTSTATE_NONE;
+ atomic_set(&ca->slot_info[i].camchange_count, 0);
+ ca->slot_info[i].camchange_type = DVB_CA_EN50221_CAMCHANGE_REMOVED;
+ init_MUTEX(&ca->slot_info[i].sem);
+ }
+
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ goto error;
+ }
+ mb();
+
+ /* create a kthread for monitoring this CA device */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+ {
+ /* bug in linux 2.4.x: kernel_thread() fails when the process calling
+ * open() is run in the debugger (fixed in kernel 2.5). I hope this
+ * workaround does not have any ugly side effects...
+ */
+ int pt = current->ptrace;
+ current->ptrace = 0;
+#endif
+
+ ret = kernel_thread (dvb_ca_en50221_thread, ca, 0);
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+ current->ptrace = pt;
+ }
+#endif
+
+ if (ret < 0) {
+ printk("dvb_ca_init: failed to start kernel_thread (%d)\n", ret);
+ goto error;
+ }
+ ca->thread_pid = ret;
+ return 0;
+
+error:
+ if (ca != NULL) {
+ if (ca->dvbdev != NULL) dvb_unregister_device(ca->dvbdev);
+ if (ca->slot_info != NULL) kfree(ca->slot_info);
+ kfree(ca);
+ }
+ pubca->private = NULL;
+ return ret;
+}
+
+
+
+/**
+ * Release a DVB CA EN50221 interface device.
+ *
+ * @param ca_dev The dvb_device_t instance for the CA device.
+ * @param ca The associated dvb_ca instance.
+ */
+void dvb_ca_en50221_release(struct dvb_ca_en50221* pubca)
+{
+ struct dvb_ca_private* ca = (struct dvb_ca_private*) pubca->private;
+ int i;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* shutdown the thread if there was one */
+ if (ca->thread_pid) {
+ if (kill_proc(ca->thread_pid, 0, 1) == -ESRCH) {
+ printk("dvb_ca_release: thread PID %d already died\n", ca->thread_pid);
+ } else {
+ ca->exit = 1;
+ mb();
+ dvb_ca_en50221_thread_wakeup(ca);
+ wait_event_interruptible(ca->thread_queue, ca->thread_pid == 0);
+ }
+ }
+
+ for(i=0; i< ca->slot_count; i++) {
+ dvb_ca_en50221_slot_shutdown(ca, i);
+ }
+ kfree(ca->slot_info);
+ dvb_unregister_device(ca->dvbdev);
+ kfree(ca);
+ pubca->private = NULL;
+}
+
+MODULE_PARM(dvb_ca_en50221_debug,"i");
+
+MODULE_PARM_DESC(dvb_ca_en50221_debug, "enable verbose debug messages");
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
new file mode 100644
index 000000000..8458f7afd
--- /dev/null
+++ b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
@@ -0,0 +1,128 @@
+/*
+ * dvb_ca.h: generic DVB functions for EN50221 CA interfaces
+ *
+ * Copyright (C) 2004 Andrew de Quincey
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * 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 Lesser General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _DVB_CA_EN50221_H_
+#define _DVB_CA_EN50221_H_
+
+#include <linux/list.h>
+#include <linux/dvb/ca.h>
+
+#include "dvbdev.h"
+
+#define DVB_CA_EN50221_POLL_CAM_PRESENT 1
+#define DVB_CA_EN50221_POLL_CAM_CHANGED 2
+#define DVB_CA_EN50221_POLL_CAM_READY 4
+
+#define DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE 1
+#define DVB_CA_EN50221_FLAG_IRQ_FR 2
+#define DVB_CA_EN50221_FLAG_IRQ_DA 4
+
+#define DVB_CA_EN50221_CAMCHANGE_REMOVED 0
+#define DVB_CA_EN50221_CAMCHANGE_INSERTED 1
+
+
+
+/* Structure describing a CA interface */
+struct dvb_ca_en50221 {
+
+ /* functions for accessing attribute memory on the CAM */
+ int (*read_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address);
+ int (*write_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address, u8 value);
+
+ /* functions for accessing the control interface on the CAM */
+ int (*read_cam_control)(struct dvb_ca_en50221* ca, int slot, u8 address);
+ int (*write_cam_control)(struct dvb_ca_en50221* ca, int slot, u8 address, u8 value);
+
+ /* Functions for controlling slots */
+ int (*slot_reset)(struct dvb_ca_en50221* ca, int slot);
+ int (*slot_shutdown)(struct dvb_ca_en50221* ca, int slot);
+ int (*slot_ts_enable)(struct dvb_ca_en50221* ca, int slot);
+
+ /*
+ * Poll slot status.
+ * Only necessary if DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE is not set
+ */
+ int (*poll_slot_status)(struct dvb_ca_en50221* ca, int slot);
+
+ /* private data, used by caller */
+ void* data;
+
+ /* Opaque data used by the dvb_ca core. Do not modify! */
+ void* private;
+};
+
+
+
+
+/* ******************************************************************************** */
+/* Functions for reporting IRQ events */
+
+/**
+ * A CAMCHANGE IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ * @param change_type One of the DVB_CA_CAMCHANGE_* values
+ */
+void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221* pubca, int slot, int change_type);
+
+/**
+ * A CAMREADY IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221* pubca, int slot);
+
+/**
+ * An FR or a DA IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221* ca, int slot);
+
+
+
+/* ******************************************************************************** */
+/* Initialisation/shutdown functions */
+
+/**
+ * Initialise a new DVB CA device.
+ *
+ * @param dvb_adapter DVB adapter to attach the new CA device to.
+ * @param ca The dvb_ca instance.
+ * @param flags Flags describing the CA device (DVB_CA_EN50221_FLAG_*).
+ * @param slot_count Number of slots supported.
+ *
+ * @return 0 on success, nonzero on failure
+ */
+extern int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221* ca, int flags, int slot_count);
+
+/**
+ * Release a DVB CA device.
+ *
+ * @param ca The associated dvb_ca instance.
+ */
+extern void dvb_ca_en50221_release(struct dvb_ca_en50221* ca);
+
+
+
+#endif
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ksyms.c b/linux/drivers/media/dvb/dvb-core/dvb_ksyms.c
index 6b471c44c..558b3f41e 100644
--- a/linux/drivers/media/dvb/dvb-core/dvb_ksyms.c
+++ b/linux/drivers/media/dvb/dvb-core/dvb_ksyms.c
@@ -10,6 +10,7 @@
#include "dvb_frontend.h"
#include "dvb_net.h"
#include "dvb_filter.h"
+#include "dvb_ca_en50221.h"
EXPORT_SYMBOL(dvb_dmxdev_init);
EXPORT_SYMBOL(dvb_dmxdev_release);
@@ -49,3 +50,8 @@ EXPORT_SYMBOL(dvb_filter_pes2ts_init);
EXPORT_SYMBOL(dvb_filter_pes2ts);
EXPORT_SYMBOL(dvb_filter_get_ac3info);
+EXPORT_SYMBOL(dvb_ca_en50221_init);
+EXPORT_SYMBOL(dvb_ca_en50221_release);
+EXPORT_SYMBOL(dvb_ca_en50221_frda_irq);
+EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq);
+EXPORT_SYMBOL(dvb_ca_en50221_camready_irq);
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
index 1db637351..1ab5166fc 100644
--- a/linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
+++ b/linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
@@ -2,10 +2,11 @@
*
* dvb_ringbuffer.c: ring buffer implementation for the dvb driver
*
- * Copyright (C) 2003 Oliver Endriss
- *
- * based on code originally found in av7110.c:
- * Copyright (C) 1999-2002 Ralph Metzler
+ * Copyright (C) 2003 Oliver Endriss
+ * Copyright (C) 2004 Andrew de Quincey
+ *
+ * based on code originally found in av7110.c & dvb_ci.c:
+ * Copyright (C) 1999-2003 Ralph Metzler
* & Marcus Metzler for convergence integrated media GmbH
*
* This program is free software; you can redistribute it and/or
@@ -35,6 +36,8 @@
#include "dvb_ringbuffer.h"
+#define PKT_READY 0
+#define PKT_DISPOSED 1
void dvb_ringbuffer_init(struct dvb_ringbuffer *rbuf, void *data, size_t len)
@@ -60,7 +63,7 @@ int dvb_ringbuffer_empty(struct dvb_ringbuffer *rbuf)
ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf)
{
ssize_t free;
-
+
free = rbuf->pread - rbuf->pwrite;
if (free <= 0)
free += rbuf->size;
@@ -72,7 +75,7 @@ ssize_t dvb_ringbuffer_free(struct dvb_ringbuffer *rbuf)
ssize_t dvb_ringbuffer_avail(struct dvb_ringbuffer *rbuf)
{
ssize_t avail;
-
+
avail = rbuf->pwrite - rbuf->pread;
if (avail < 0)
avail += rbuf->size;
@@ -135,31 +138,127 @@ ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf,
{
size_t todo = len;
size_t split;
-
+
split = (rbuf->pwrite + len > rbuf->size) ? rbuf->size - rbuf->pwrite : 0;
if (split > 0) {
- if (!usermem)
+ if (!usermem)
memcpy(rbuf->data+rbuf->pwrite, buf, split);
else
- if (copy_from_user(rbuf->data+rbuf->pwrite,
+ if (copy_from_user(rbuf->data+rbuf->pwrite,
buf, split))
return -EFAULT;
buf += split;
todo -= split;
rbuf->pwrite = 0;
}
- if (!usermem)
+ if (!usermem)
memcpy(rbuf->data+rbuf->pwrite, buf, todo);
else
- if (copy_from_user(rbuf->data+rbuf->pwrite, buf, todo))
+ if (copy_from_user(rbuf->data+rbuf->pwrite, buf, todo))
return -EFAULT;
rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
- return len;
+ return len;
+}
+
+ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf, size_t len, int usermem)
+{
+ int status;
+ ssize_t oldpwrite = rbuf->pwrite;
+
+ DVB_RINGBUFFER_WRITE_BYTE(rbuf, len >> 8);
+ DVB_RINGBUFFER_WRITE_BYTE(rbuf, len & 0xff);
+ DVB_RINGBUFFER_WRITE_BYTE(rbuf, PKT_READY);
+ status = dvb_ringbuffer_write(rbuf, buf, len, usermem);
+
+ if (status < 0) rbuf->pwrite = oldpwrite;
+ return status;
}
+ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
+ int offset, u8* buf, size_t len, int usermem)
+{
+ size_t todo;
+ size_t split;
+ size_t pktlen;
+
+ pktlen = rbuf->data[idx] << 8;
+ pktlen |= rbuf->data[(idx + 1) % rbuf->size];
+ if (offset > pktlen) return -EINVAL;
+ if ((offset + len) > pktlen) len = pktlen - offset;
+
+ idx = (idx + DVB_RINGBUFFER_PKTHDRSIZE + offset) % rbuf->size;
+ todo = len;
+ split = ((idx + len) > rbuf->size) ? rbuf->size - idx : 0;
+ if (split > 0) {
+ if (!usermem)
+ memcpy(buf, rbuf->data+idx, split);
+ else
+ if (copy_to_user(buf, rbuf->data+idx, split))
+ return -EFAULT;
+ buf += split;
+ todo -= split;
+ idx = 0;
+ }
+ if (!usermem)
+ memcpy(buf, rbuf->data+idx, todo);
+ else
+ if (copy_to_user(buf, rbuf->data+idx, todo))
+ return -EFAULT;
+
+ return len;
+}
+
+void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx)
+{
+ size_t pktlen;
+
+ rbuf->data[(idx + 2) % rbuf->size] = PKT_DISPOSED;
+
+ // clean up disposed packets
+ while(dvb_ringbuffer_avail(rbuf) > DVB_RINGBUFFER_PKTHDRSIZE) {
+ if (DVB_RINGBUFFER_PEEK(rbuf, 2) == PKT_DISPOSED) {
+ pktlen = DVB_RINGBUFFER_PEEK(rbuf, 0) << 8;
+ pktlen |= DVB_RINGBUFFER_PEEK(rbuf, 1);
+ DVB_RINGBUFFER_SKIP(rbuf, pktlen + DVB_RINGBUFFER_PKTHDRSIZE);
+ } else {
+ // first packet is not disposed, so we stop cleaning now
+ break;
+ }
+ }
+}
+
+ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen)
+{
+ int consumed;
+ int curpktlen;
+ int curpktstatus;
+
+ if (idx == -1) idx = rbuf->pread;
+ consumed = (idx - rbuf->pread) % rbuf->size;
+
+ while((dvb_ringbuffer_avail(rbuf) - consumed) > DVB_RINGBUFFER_PKTHDRSIZE) {
+
+ curpktlen = rbuf->data[idx] << 8;
+ curpktlen |= rbuf->data[(idx + 1) % rbuf->size];
+ curpktstatus = rbuf->data[(idx + 2) % rbuf->size];
+
+ if (curpktstatus == PKT_READY) {
+ *pktlen = curpktlen;
+ return idx;
+ }
+
+ consumed += curpktlen + DVB_RINGBUFFER_PKTHDRSIZE;
+ idx = (idx + curpktlen + DVB_RINGBUFFER_PKTHDRSIZE) % rbuf->size;
+ }
+
+ // no packets available
+ return -1;
+}
+
+
EXPORT_SYMBOL(dvb_ringbuffer_init);
EXPORT_SYMBOL(dvb_ringbuffer_empty);
@@ -169,3 +268,7 @@ EXPORT_SYMBOL(dvb_ringbuffer_flush);
EXPORT_SYMBOL(dvb_ringbuffer_flush_spinlock_wakeup);
EXPORT_SYMBOL(dvb_ringbuffer_read);
EXPORT_SYMBOL(dvb_ringbuffer_write);
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_write);
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_read);
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_dispose);
+EXPORT_SYMBOL(dvb_ringbuffer_pkt_next);
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.h b/linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
index 50cd1a6d9..c72f6cb82 100644
--- a/linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
+++ b/linux/drivers/media/dvb/dvb-core/dvb_ringbuffer.h
@@ -2,10 +2,11 @@
*
* dvb_ringbuffer.h: ring buffer implementation for the dvb driver
*
- * Copyright (C) 2003 Oliver Endriss
- *
- * based on code originally found in av7110.c:
- * Copyright (C) 1999-2002 Ralph Metzler & Marcus Metzler
+ * Copyright (C) 2003 Oliver Endriss
+ * Copyright (C) 2004 Andrew de Quincey
+ *
+ * based on code originally found in av7110.c & dvb_ci.c:
+ * Copyright (C) 1999-2003 Ralph Metzler & Marcus Metzler
* for convergence integrated media GmbH
*
* This program is free software; you can redistribute it and/or
@@ -39,6 +40,8 @@ struct dvb_ringbuffer {
spinlock_t lock;
};
+#define DVB_RINGBUFFER_PKTHDRSIZE 3
+
/*
** Notes:
@@ -49,7 +52,7 @@ struct dvb_ringbuffer {
**
** *** write <buflen> bytes ***
** free = dvb_ringbuffer_free(rbuf);
-** if (free >= buflen)
+** if (free >= buflen)
** count = dvb_ringbuffer_write(rbuf, buffer, buflen, 0);
** else
** ...
@@ -61,7 +64,7 @@ struct dvb_ringbuffer {
** else
** ...
**
-** (2) If there is exactly one reader and one writer, there is no need
+** (2) If there is exactly one reader and one writer, there is no need
** to lock read or write operations.
** Two or more readers must be locked against each other.
** Flushing the buffer counts as a read operation.
@@ -96,13 +99,13 @@ extern void dvb_ringbuffer_flush_spinlock_wakeup(struct dvb_ringbuffer *rbuf);
/* advance read ptr by <num> bytes */
#define DVB_RINGBUFFER_SKIP(rbuf,num) \
(rbuf)->pread=((rbuf)->pread+(num))%(rbuf)->size
-
+
/*
-** read <len> bytes from ring buffer into <buf>
+** read <len> bytes from ring buffer into <buf>
** <usermem> specifies whether <buf> resides in user space
** returns number of bytes transferred or -EFAULT
*/
-extern ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf,
+extern ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf,
size_t len, int usermem);
@@ -120,4 +123,52 @@ extern ssize_t dvb_ringbuffer_read(struct dvb_ringbuffer *rbuf, u8 *buf,
extern ssize_t dvb_ringbuffer_write(struct dvb_ringbuffer *rbuf, const u8 *buf,
size_t len, int usermem);
+
+/**
+ * Write a packet into the ringbuffer.
+ *
+ * <rbuf> Ringbuffer to write to.
+ * <buf> Buffer to write.
+ * <len> Length of buffer (currently limited to 65535 bytes max).
+ * <usermem> Set to 1 if <buf> is in userspace.
+ * returns Number of bytes written, or -EFAULT, -ENOMEM, -EVINAL.
+ */
+extern ssize_t dvb_ringbuffer_pkt_write(struct dvb_ringbuffer *rbuf, u8* buf,
+ size_t len, int usermem);
+
+/**
+ * Read from a packet in the ringbuffer. Note: unlike dvb_ringbuffer_read(), this
+ * does NOT update the read pointer in the ringbuffer. You must use
+ * dvb_ringbuffer_pkt_dispose() to mark a packet as no longer required.
+ *
+ * <rbuf> Ringbuffer concerned.
+ * <idx> Packet index as returned by dvb_ringbuffer_pkt_next().
+ * <offset> Offset into packet to read from.
+ * <buf> Destination buffer for data.
+ * <len> Size of destination buffer.
+ * <usermem> Set to 1 if <buf> is in userspace.
+ * returns Number of bytes read, or -EFAULT.
+ */
+extern ssize_t dvb_ringbuffer_pkt_read(struct dvb_ringbuffer *rbuf, size_t idx,
+ int offset, u8* buf, size_t len, int usermem);
+
+/**
+ * Dispose of a packet in the ring buffer.
+ *
+ * <rbuf> Ring buffer concerned.
+ * <idx> Packet index as returned by dvb_ringbuffer_pkt_next().
+ */
+extern void dvb_ringbuffer_pkt_dispose(struct dvb_ringbuffer *rbuf, size_t idx);
+
+/**
+ * Get the index of the next packet in a ringbuffer.
+ *
+ * <rbuf> Ringbuffer concerned.
+ * <idx> Previous packet index, or -1 to return the first packet index.
+ * <pktlen> On success, will be updated to contain the length of the packet in bytes.
+ * returns Packet index (if >=0), or -1 if no packets available.
+ */
+extern ssize_t dvb_ringbuffer_pkt_next(struct dvb_ringbuffer *rbuf, size_t idx, size_t* pktlen);
+
+
#endif /* _DVB_RINGBUFFER_H_ */
diff --git a/linux/drivers/media/dvb/ttpci/budget-ci.c b/linux/drivers/media/dvb/ttpci/budget-ci.c
index db16c44e1..955ae3ffb 100644
--- a/linux/drivers/media/dvb/ttpci/budget-ci.c
+++ b/linux/drivers/media/dvb/ttpci/budget-ci.c
@@ -1,30 +1,30 @@
/*
- * budget-ci.c: driver for the SAA7146 based Budget DVB cards
+ * budget-ci.c: driver for the SAA7146 based Budget DVB cards
*
- * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
*
* msp430 IR support contributed by Jack Thomasson <jkt@Helius.COM>
* partially based on the Siemens DVB driver by Ralph+Marcus Metzler
*
* CI interface support (c) 2004 Andrew de Quincey <adq_dvb@lidskialf.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.
- *
+ *
*
* 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
+ * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
- *
+ *
*
* the project's page is at http://www.linuxtv.org/dvb/
*/
@@ -39,81 +39,96 @@
#include <linux/spinlock.h>
#include "dvb_functions.h"
+#include "dvb_ca_en50221.h"
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
#include "input_fake.h"
#endif
-#define DEBIADDR_IR 0x1234
-#define DEBIADDR_CICONTROL 0x0000
-#define DEBIADDR_CIVERSION 0x4000
-#define DEBIADDR_CIIRQCONTROL 0x4000
+#define DEBIADDR_IR 0x1234
+#define DEBIADDR_CICONTROL 0x0000
+#define DEBIADDR_CIVERSION 0x4000
+#define DEBIADDR_IO 0x1000
+#define DEBIADDR_ATTR 0x3000
+
+#define CICONTROL_RESET 0x01
+#define CICONTROL_ENABLETS 0x02
+#define CICONTROL_CAMDETECT 0x08
+#define DEBICICTL 0x00420000
+#define DEBICICAM 0x02420000
+#define SLOTSTATUS_NONE 1
+#define SLOTSTATUS_PRESENT 2
+#define SLOTSTATUS_RESET 4
+#define SLOTSTATUS_READY 8
+#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
struct budget_ci {
struct budget budget;
struct input_dev input_dev;
struct tasklet_struct msp430_irq_tasklet;
- spinlock_t debilock;
- int ci_present;
+ struct tasklet_struct ciintf_irq_tasklet;
+ spinlock_t debilock;
+ int slot_status;
+ struct dvb_ca_en50221 ca;
char ir_dev_name[50];
};
static u32 budget_debiread (struct budget_ci* budget_ci, u32 config, int addr, int count)
{
struct saa7146_dev *saa = budget_ci->budget.dev;
- u32 result = 0;
-
+ u32 result = 0;
+
if (count > 4 || count <= 0)
return 0;
- spin_lock(&budget_ci->debilock);
-
+ spin_lock(&budget_ci->debilock);
+
if (saa7146_wait_for_debi_done(saa) < 0) {
- spin_unlock(&budget_ci->debilock);
+ spin_unlock(&budget_ci->debilock);
return 0;
}
-
+
saa7146_write (saa, DEBI_COMMAND,
(count << 17) | 0x10000 | (addr & 0xffff));
saa7146_write(saa, DEBI_CONFIG, config);
- saa7146_write(saa, DEBI_PAGE, 0);
+ saa7146_write(saa, DEBI_PAGE, 0);
saa7146_write(saa, MC2, (2 << 16) | 2);
-
+
saa7146_wait_for_debi_done(saa);
result = saa7146_read(saa, 0x88);
result &= (0xffffffffUL >> ((4 - count) * 8));
- spin_unlock(&budget_ci->debilock);
+ spin_unlock(&budget_ci->debilock);
return result;
}
static u8 budget_debiwrite (struct budget_ci* budget_ci, u32 config, int addr, int count, u32 value)
{
struct saa7146_dev *saa = budget_ci->budget.dev;
-
+
if (count > 4 || count <= 0)
return 0;
- spin_lock(&budget_ci->debilock);
-
+ spin_lock(&budget_ci->debilock);
+
if (saa7146_wait_for_debi_done(saa) < 0) {
- spin_unlock(&budget_ci->debilock);
+ spin_unlock(&budget_ci->debilock);
return 0;
}
saa7146_write (saa, DEBI_COMMAND,
(count << 17) | 0x00000 | (addr & 0xffff));
saa7146_write(saa, DEBI_CONFIG, config);
- saa7146_write(saa, DEBI_PAGE, 0);
+ saa7146_write(saa, DEBI_PAGE, 0);
saa7146_write(saa, DEBI_AD, value);
saa7146_write(saa, MC2, (2 << 16) | 2);
-
+
saa7146_wait_for_debi_done(saa);
-
- spin_unlock(&budget_ci->debilock);
+
+ spin_unlock(&budget_ci->debilock);
return 0;
}
@@ -123,52 +138,52 @@ static u8 budget_debiwrite (struct budget_ci* budget_ci, u32 config, int addr, i
Hauppauge (from NOVA-CI-s box product)
i've taken a "middle of the road" approach and note the differences
*/
-static u16 key_map[64] = {
+static u16 key_map[64] = {
/* 0x0X */
KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8,
KEY_9,
KEY_ENTER,
KEY_RED,
- KEY_POWER, /* RADIO on Hauppauge */
+ KEY_POWER, /* RADIO on Hauppauge */
KEY_MUTE,
0,
- KEY_A, /* TV on Hauppauge */
+ KEY_A, /* TV on Hauppauge */
/* 0x1X */
KEY_VOLUMEUP, KEY_VOLUMEDOWN,
0, 0,
KEY_B,
0, 0, 0, 0, 0, 0, 0,
KEY_UP, KEY_DOWN,
- KEY_OPTION, /* RESERVED on Hauppauge */
+ KEY_OPTION, /* RESERVED on Hauppauge */
KEY_BREAK,
/* 0x2X */
KEY_CHANNELUP, KEY_CHANNELDOWN,
- KEY_PREVIOUS, /* Prev. Ch on Zenith, SOURCE on Hauppauge */
+ KEY_PREVIOUS, /* Prev. Ch on Zenith, SOURCE on Hauppauge */
0, KEY_RESTART, KEY_OK,
- KEY_CYCLEWINDOWS, /* MINIMIZE on Hauppauge */
+ KEY_CYCLEWINDOWS, /* MINIMIZE on Hauppauge */
0,
- KEY_ENTER, /* VCR mode on Zenith */
+ KEY_ENTER, /* VCR mode on Zenith */
KEY_PAUSE,
0,
KEY_RIGHT, KEY_LEFT,
0,
- KEY_MENU, /* FULL SCREEN on Hauppauge */
+ KEY_MENU, /* FULL SCREEN on Hauppauge */
0,
/* 0x3X */
KEY_SLOW,
- KEY_PREVIOUS, /* VCR mode on Zenith */
+ KEY_PREVIOUS, /* VCR mode on Zenith */
KEY_REWIND,
0,
KEY_FASTFORWARD,
KEY_PLAY, KEY_STOP,
KEY_RECORD,
- KEY_TUNER, /* TV/VCR on Zenith */
+ KEY_TUNER, /* TV/VCR on Zenith */
0,
KEY_C,
0,
KEY_EXIT,
KEY_POWER2,
- KEY_TUNER, /* VCR mode on Zenith */
+ KEY_TUNER, /* VCR mode on Zenith */
0,
};
@@ -185,7 +200,7 @@ static void msp430_ir_debounce (unsigned long data)
dev->rep[0] = 0;
dev->timer.expires = jiffies + HZ * 350 / 1000;
add_timer(&dev->timer);
- input_event(dev, EV_KEY, key_map[dev->repeat_key], 2); /* REPEAT */
+ input_event(dev, EV_KEY, key_map[dev->repeat_key], 2); /* REPEAT */
}
@@ -197,22 +212,22 @@ static void msp430_ir_interrupt (unsigned long data)
unsigned int code = budget_debiread(budget_ci, DEBINOSWAP, DEBIADDR_IR, 2) >> 8;
if (code & 0x40) {
- code &= 0x3f;
-
- if (timer_pending(&dev->timer)) {
- if (code == dev->repeat_key) {
- ++dev->rep[0];
- return;
- }
- del_timer(&dev->timer);
- input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
+ code &= 0x3f;
+
+ if (timer_pending(&dev->timer)) {
+ if (code == dev->repeat_key) {
+ ++dev->rep[0];
+ return;
+ }
+ del_timer(&dev->timer);
+ input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
}
if (!key_map[code]) {
- printk ("DVB (%s): no key for %02x!\n",
+ printk ("DVB (%s): no key for %02x!\n",
__FUNCTION__, code);
- return;
- }
+ return;
+ }
/* initialize debounce and repeat */
dev->repeat_key = code;
@@ -221,7 +236,7 @@ static void msp430_ir_interrupt (unsigned long data)
/* 350 milliseconds */
dev->timer.expires = jiffies + HZ * 350 / 1000;
/* MAKE */
- input_event(dev, EV_KEY, key_map[code], !0);
+ input_event(dev, EV_KEY, key_map[code], !0);
add_timer(&dev->timer);
}
}
@@ -249,7 +264,7 @@ static int msp430_ir_init (struct budget_ci *budget_ci)
saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_06);
- saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI);
+ saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI);
return 0;
}
@@ -269,73 +284,215 @@ static void msp430_ir_deinit (struct budget_ci *budget_ci)
input_unregister_device(dev);
}
+static int ciintf_read_attribute_mem(struct dvb_ca_en50221* ca, int slot, int address) {
+ struct budget_ci* budget_ci = (struct budget_ci*) ca->data;
+
+ if (slot != 0) return -EINVAL;
+
+ return budget_debiread(budget_ci, DEBICICAM, DEBIADDR_ATTR | (address & 0xfff), 1);
+}
+
+static int ciintf_write_attribute_mem(struct dvb_ca_en50221* ca, int slot, int address, u8 value) {
+ struct budget_ci* budget_ci = (struct budget_ci*) ca->data;
+
+ if (slot != 0) return -EINVAL;
+
+ return budget_debiwrite(budget_ci, DEBICICAM, DEBIADDR_ATTR | (address & 0xfff), 1, value);
+}
+
+static int ciintf_read_cam_control(struct dvb_ca_en50221* ca, int slot, u8 address) {
+ struct budget_ci* budget_ci = (struct budget_ci*) ca->data;
+
+ if (slot != 0) return -EINVAL;
+
+ return budget_debiread(budget_ci, DEBICICAM, DEBIADDR_IO | (address & 3), 1);
+}
+
+static int ciintf_write_cam_control(struct dvb_ca_en50221* ca, int slot, u8 address, u8 value) {
+ struct budget_ci* budget_ci = (struct budget_ci*) ca->data;
+
+ if (slot != 0) return -EINVAL;
+
+ return budget_debiwrite(budget_ci, DEBICICAM, DEBIADDR_IO | (address & 3), 1, value);
+}
+
+static int ciintf_slot_reset(struct dvb_ca_en50221* ca, int slot) {
+ struct budget_ci* budget_ci = (struct budget_ci*) ca->data;
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+
+ if (slot != 0) return -EINVAL;
+
+ // trigger on RISING edge during reset so we know when READY is re-asserted
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
+ budget_ci->slot_status = SLOTSTATUS_RESET;
+ budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, 0);
+ dvb_delay(1);
+ budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, CICONTROL_RESET);
+
+ return 0;
+}
+
+static int ciintf_slot_shutdown(struct dvb_ca_en50221* ca, int slot) {
+ if (slot != 0) return -EINVAL;
+
+ // no implementation necessary
+ return 0;
+}
+
+static int ciintf_slot_ts_enable(struct dvb_ca_en50221* ca, int slot) {
+ struct budget_ci* budget_ci = (struct budget_ci*) ca->data;
+ int tmp;
+
+ if (slot != 0) return -EINVAL;
+
+ tmp = budget_debiread(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1);
+ budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, tmp | CICONTROL_ENABLETS);
+ return 0;
+}
+
+
+static void ciintf_interrupt (unsigned long data)
+{
+ struct budget_ci *budget_ci = (struct budget_ci*) data;
+ struct saa7146_dev *saa = budget_ci->budget.dev;
+ unsigned int flags;
+
+ // ensure we don't get spurious IRQs during initialisation
+ if (!budget_ci->budget.ci_present) return;
+
+ flags = budget_debiread(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1);
+
+ // always set the GPIO mode back to "normal", in case the card is
+ // yanked at an inopportune moment
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO);
+
+ if (flags & CICONTROL_CAMDETECT) {
+
+ if (budget_ci->slot_status & SLOTSTATUS_NONE) {
+ // CAM insertion IRQ
+ budget_ci->slot_status = SLOTSTATUS_PRESENT;
+ dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, DVB_CA_EN50221_CAMCHANGE_INSERTED);
+
+ } else if (budget_ci->slot_status & SLOTSTATUS_RESET) {
+ // CAM ready (reset completed)
+ budget_ci->slot_status = SLOTSTATUS_READY;
+ dvb_ca_en50221_camready_irq(&budget_ci->ca, 0);
+
+ } else if (budget_ci->slot_status & SLOTSTATUS_READY) {
+ // FR/DA IRQ
+ dvb_ca_en50221_frda_irq(&budget_ci->ca, 0);
+ }
+ } else {
+ if (budget_ci->slot_status & SLOTSTATUS_OCCUPIED) {
+ budget_ci->slot_status = SLOTSTATUS_NONE;
+ dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, DVB_CA_EN50221_CAMCHANGE_REMOVED);
+ }
+ }
+}
+
static int ciintf_init(struct budget_ci* budget_ci)
{
struct saa7146_dev *saa = budget_ci->budget.dev;
+ int flags;
+ int result;
+
+ memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221));
+
+ // enable DEBI pins
+ saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800);
+
+ // test if it is there
+ if ((budget_debiread(budget_ci, DEBICICTL, DEBIADDR_CIVERSION, 1) & 0xa0) != 0xa0) {
+ result = -ENODEV;
+ goto error;
+ }
- // enable DEBI pins
- saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800);
-
- // test if it is there
- if ((budget_debiread(budget_ci, DEBICICTL, DEBIADDR_CIVERSION, 1) & 0xa0) != 0xa0) {
- saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
- return -ENODEV;
+ // determine whether a CAM is present or not
+ flags = budget_debiread(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1);
+ budget_ci->slot_status = SLOTSTATUS_NONE;
+ if (flags & CICONTROL_CAMDETECT) budget_ci->slot_status = SLOTSTATUS_PRESENT;
+
+
+ // register CI interface
+ budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem;
+ budget_ci->ca.write_attribute_mem = ciintf_write_attribute_mem;
+ budget_ci->ca.read_cam_control = ciintf_read_cam_control;
+ budget_ci->ca.write_cam_control = ciintf_write_cam_control;
+ budget_ci->ca.slot_reset = ciintf_slot_reset;
+ budget_ci->ca.slot_shutdown = ciintf_slot_shutdown;
+ budget_ci->ca.slot_ts_enable = ciintf_slot_ts_enable;
+ budget_ci->ca.data = budget_ci;
+ if ((result = dvb_ca_en50221_init(budget_ci->budget.dvb_adapter,
+ &budget_ci->ca,
+ DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE |
+ DVB_CA_EN50221_FLAG_IRQ_FR |
+ DVB_CA_EN50221_FLAG_IRQ_DA,
+ 1)) != 0) {
+ printk("budget_ci: CI interface detected, but initialisation failed.\n");
+ goto error;
}
- // reset CI interface
- budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, 0);
- dvb_delay(1);
- budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, 1);
+ // Setup CI slot IRQ
+ tasklet_init (&budget_ci->ciintf_irq_tasklet, ciintf_interrupt, (unsigned long) budget_ci);
+ saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO);
+ saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_03);
+ budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, CICONTROL_RESET);
- // clear any CI interrupt status
- budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CIIRQCONTROL, 1, 0xC0);
+ // success!
+ printk("budget_ci: CI interface initialised\n");
+ budget_ci->budget.ci_present = 1;
- // FIXME: register CI slot
+ // route TS data out to CI interface
+ saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI);
- // Setup CI slot IRQ
- saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO);
- saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_03);
- budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, 0xc1);
+ // forge a fake CI IRQ so the CAM state is setup correctly
+ flags = DVB_CA_EN50221_CAMCHANGE_REMOVED;
+ if (budget_ci->slot_status != SLOTSTATUS_NONE) flags = DVB_CA_EN50221_CAMCHANGE_INSERTED;
+ dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags);
- // FIXME: route TS data out to CI interface
-// saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI);
+ return 0;
- // success!
- printk("budget_ci: CI interface detected\n");
- budget_ci->ci_present = 1;
- return 0;
+error:
+ saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+ return result;
}
-static void ciintf_deinit(struct budget_ci* budget_ci)
+static void ciintf_deinit(struct budget_ci* budget_ci)
{
struct saa7146_dev *saa = budget_ci->budget.dev;
- // disable CI interrupts
- budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, 0x1);
- saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_03);
+ // disable CI interrupts
+ saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_03);
saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT);
+ tasklet_kill(&budget_ci->ciintf_irq_tasklet);
+ budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, 0);
+ dvb_delay(1);
+ budget_debiwrite(budget_ci, DEBICICTL, DEBIADDR_CICONTROL, 1, CICONTROL_RESET);
- // FIXME: disable TS data stream to CI interface
- saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT);
+ // disable TS data stream to CI interface
+ saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT);
- // disable DEBI pins again
- saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
+ // release the CA device
+ dvb_ca_en50221_release(&budget_ci->ca);
+
+ // disable DEBI pins
+ saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
}
static void budget_ci_irq (struct saa7146_dev *dev, u32 *isr)
{
- struct budget_ci *budget_ci = (struct budget_ci*) dev->ext_priv;
+ struct budget_ci *budget_ci = (struct budget_ci*) dev->ext_priv;
- DEB_EE(("dev: %p, budget_ci: %p\n", dev, budget_ci));
+ DEB_EE(("dev: %p, budget_ci: %p\n", dev, budget_ci));
- if (*isr & MASK_06)
- tasklet_schedule (&budget_ci->msp430_irq_tasklet);
+ if (*isr & MASK_06)
+ tasklet_schedule (&budget_ci->msp430_irq_tasklet);
- if (*isr & MASK_10)
+ if (*isr & MASK_10)
ttpci_budget_irq10_handler (dev, isr);
- if (*isr & MASK_03)
- printk("CI IRQ!!\n");
+ if ((*isr & MASK_03) && (budget_ci->budget.ci_present))
+ tasklet_schedule (&budget_ci->ciintf_irq_tasklet);
}
@@ -351,8 +508,8 @@ static int budget_ci_attach (struct saa7146_dev* dev,
DEB_EE(("budget_ci: %p\n", budget_ci));
- spin_lock_init(&budget_ci->debilock);
- budget_ci->ci_present = 0;
+ spin_lock_init(&budget_ci->debilock);
+ budget_ci->budget.ci_present = 0;
if ((err = ttpci_budget_init (&budget_ci->budget, dev, info))) {
kfree (budget_ci);
@@ -365,8 +522,9 @@ static int budget_ci_attach (struct saa7146_dev* dev,
(unsigned long) budget_ci);
msp430_ir_init (budget_ci);
-
- ciintf_init(budget_ci);
+
+ // UNCOMMENT TO TEST CI INTERFACE
+// ciintf_init(budget_ci);
return 0;
}
@@ -377,7 +535,9 @@ static int budget_ci_detach (struct saa7146_dev* dev)
{
struct budget_ci *budget_ci = (struct budget_ci*) dev->ext_priv;
struct saa7146_dev *saa = budget_ci->budget.dev;
- int err;
+ int err;
+
+ if (budget_ci->budget.ci_present) ciintf_deinit(budget_ci);
err = ttpci_budget_deinit (&budget_ci->budget);
@@ -385,11 +545,9 @@ static int budget_ci_detach (struct saa7146_dev* dev)
msp430_ir_deinit (budget_ci);
- if (budget_ci->ci_present) ciintf_deinit(budget_ci);
+ // disable frontend and CI interface
+ saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT);
- // disable frontend and CI interface
- saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT);
-
kfree (budget_ci);
return err;
@@ -397,17 +555,17 @@ static int budget_ci_detach (struct saa7146_dev* dev)
-static struct saa7146_extension budget_extension;
+static struct saa7146_extension budget_extension;
MAKE_BUDGET_INFO(ttbci, "TT-Budget/WinTV-NOVA-CI PCI", BUDGET_TT_HW_DISEQC);
-MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT);
+MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT);
static struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c),
MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100f),
- MAKE_EXTENSION_PCI(ttbt2, 0x13c2, 0x1011),
+ MAKE_EXTENSION_PCI(ttbt2, 0x13c2, 0x1011),
{
- .vendor = 0,
+ .vendor = 0,
}
};
@@ -415,7 +573,7 @@ MODULE_DEVICE_TABLE(pci, pci_tbl);
static struct saa7146_extension budget_extension = {
.name = "budget_ci dvb\0",
- .flags = 0,
+ .flags = 0,
.module = THIS_MODULE,
.pci_tbl = &pci_tbl[0],
@@ -424,10 +582,10 @@ static struct saa7146_extension budget_extension = {
.irq_mask = MASK_03 | MASK_06 | MASK_10,
.irq_func = budget_ci_irq,
-};
+};
-static int __init budget_ci_init(void)
+static int __init budget_ci_init(void)
{
return saa7146_register_extension(&budget_extension);
}
@@ -436,7 +594,7 @@ static int __init budget_ci_init(void)
static void __exit budget_ci_exit(void)
{
DEB_EE((".\n"));
- saa7146_unregister_extension(&budget_extension);
+ saa7146_unregister_extension(&budget_extension);
}
module_init(budget_ci_init);
diff --git a/linux/drivers/media/dvb/ttpci/budget-core.c b/linux/drivers/media/dvb/ttpci/budget-core.c
index 302a3495c..2b626a4f4 100644
--- a/linux/drivers/media/dvb/ttpci/budget-core.c
+++ b/linux/drivers/media/dvb/ttpci/budget-core.c
@@ -6,12 +6,12 @@
* Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
*
* Copyright (C) 1999-2002 Ralph Metzler
- * & Marcus Metzler for convergence integrated media GmbH
- *
+ * & Marcus Metzler for convergence integrated media GmbH
+ *
* 26feb2004 Support for FS Activy Card (Grundig tuner) by
- * Michael Dreher <michael@5dot1.de>,
- * Oliver Endriss <o.endriss@gmx.de>,
- * Andreas 'randy' Weinberger
+ * Michael Dreher <michael@5dot1.de>,
+ * Oliver Endriss <o.endriss@gmx.de>,
+ * Andreas 'randy' Weinberger
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -21,7 +21,7 @@
*
* 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
@@ -47,33 +47,33 @@ static int stop_ts_capture(struct budget *budget)
{
DEB_EE(("budget: %p\n",budget));
- if (--budget->feeding)
- return budget->feeding;
+ if (--budget->feeding)
+ return budget->feeding;
- saa7146_write(budget->dev, MC1, MASK_20); // DMA3 off
+ saa7146_write(budget->dev, MC1, MASK_20); // DMA3 off
IER_DISABLE(budget->dev, MASK_10);
- return 0;
+ return 0;
}
static int start_ts_capture (struct budget *budget)
{
- struct saa7146_dev *dev=budget->dev;
+ struct saa7146_dev *dev=budget->dev;
DEB_EE(("budget: %p\n",budget));
- if (budget->feeding)
- return ++budget->feeding;
+ if (budget->feeding)
+ return ++budget->feeding;
- saa7146_write(dev, MC1, MASK_20); // DMA3 off
+ saa7146_write(dev, MC1, MASK_20); // DMA3 off
- memset(budget->grabbing, 0x00, TS_HEIGHT*TS_WIDTH);
+ memset(budget->grabbing, 0x00, TS_HEIGHT*TS_WIDTH);
- saa7146_write(dev, PCI_BT_V1, 0x001c0000 |
- (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000));
+ saa7146_write(dev, PCI_BT_V1, 0x001c0000 |
+ (saa7146_read(dev, PCI_BT_V1) & ~0x001f0000));
- budget->tsf=0xff;
- budget->ttbp=0;
+ budget->tsf=0xff;
+ budget->ttbp=0;
/*
* Signal path on the Activy:
@@ -82,71 +82,77 @@ static int start_ts_capture (struct budget *budget)
*
* Since the tuner feeds 204 bytes packets into the SAA7146,
* DMA3 is configured to strip the trailing 16 FEC bytes:
- * Pitch: 188, NumBytes3: 188, NumLines3: 1024
+ * Pitch: 188, NumBytes3: 188, NumLines3: 1024
*/
- if (budget->card->type == BUDGET_FS_ACTIVY) {
- saa7146_write(dev, DD1_INIT, 0x04000000);
- saa7146_write(dev, MC2, (MASK_09 | MASK_25));
- saa7146_write(dev, BRS_CTRL, 0x00000000);
- } else {
- saa7146_write(dev, DD1_INIT, 0x02000600);
- saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
- saa7146_write(dev, BRS_CTRL, 0x60000000);
- }
-
- saa7146_write(dev, MC2, (MASK_08 | MASK_24));
- mdelay(10);
-
- saa7146_write(dev, BASE_ODD3, 0);
- saa7146_write(dev, BASE_EVEN3, 0);
- saa7146_write(dev, PROT_ADDR3, TS_WIDTH*TS_HEIGHT);
- saa7146_write(dev, BASE_PAGE3, budget->pt.dma |ME1|0x90);
-
- if (budget->card->type == BUDGET_FS_ACTIVY) {
- saa7146_write(dev, PITCH3, TS_WIDTH/2);
- saa7146_write(dev, NUM_LINE_BYTE3, ((TS_HEIGHT*2)<<16)|(TS_WIDTH/2));
- } else {
- saa7146_write(dev, PITCH3, TS_WIDTH);
- saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT<<16)|TS_WIDTH);
- }
-
- saa7146_write(dev, MC2, (MASK_04 | MASK_20));
- saa7146_write(dev, MC1, (MASK_04 | MASK_20)); // DMA3 on
+ if (budget->card->type == BUDGET_FS_ACTIVY) {
+ saa7146_write(dev, DD1_INIT, 0x04000000);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25));
+ saa7146_write(dev, BRS_CTRL, 0x00000000);
+ } else {
+ if (!budget->ci_present) {
+ saa7146_write(dev, DD1_INIT, 0x02000600);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+ saa7146_write(dev, BRS_CTRL, 0x60000000);
+ } else {
+ saa7146_write(dev, DD1_INIT, 0x06000200);
+ saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
+ saa7146_write(dev, BRS_CTRL, 0x00000000);
+ }
+ }
+
+ saa7146_write(dev, MC2, (MASK_08 | MASK_24));
+ mdelay(10);
+
+ saa7146_write(dev, BASE_ODD3, 0);
+ saa7146_write(dev, BASE_EVEN3, 0);
+ saa7146_write(dev, PROT_ADDR3, TS_WIDTH*TS_HEIGHT);
+ saa7146_write(dev, BASE_PAGE3, budget->pt.dma |ME1|0x90);
+
+ if (budget->card->type == BUDGET_FS_ACTIVY) {
+ saa7146_write(dev, PITCH3, TS_WIDTH/2);
+ saa7146_write(dev, NUM_LINE_BYTE3, ((TS_HEIGHT*2)<<16)|(TS_WIDTH/2));
+ } else {
+ saa7146_write(dev, PITCH3, TS_WIDTH);
+ saa7146_write(dev, NUM_LINE_BYTE3, (TS_HEIGHT<<16)|TS_WIDTH);
+ }
+
+ saa7146_write(dev, MC2, (MASK_04 | MASK_20));
+ saa7146_write(dev, MC1, (MASK_04 | MASK_20)); // DMA3 on
IER_ENABLE(budget->dev, MASK_10); // VPE
- return ++budget->feeding;
+ return ++budget->feeding;
}
static void vpeirq (unsigned long data)
{
- struct budget *budget = (struct budget*) data;
- u8 *mem = (u8 *)(budget->grabbing);
- u32 olddma = budget->ttbp;
- u32 newdma = saa7146_read(budget->dev, PCI_VDP3);
+ struct budget *budget = (struct budget*) data;
+ u8 *mem = (u8 *)(budget->grabbing);
+ u32 olddma = budget->ttbp;
+ u32 newdma = saa7146_read(budget->dev, PCI_VDP3);
- /* nearest lower position divisible by 188 */
- newdma -= newdma % 188;
+ /* nearest lower position divisible by 188 */
+ newdma -= newdma % 188;
- if (newdma >= TS_BUFLEN)
- return;
+ if (newdma >= TS_BUFLEN)
+ return;
budget->ttbp = newdma;
if(budget->feeding == 0 || newdma == olddma)
return;
- if (newdma > olddma) { /* no wraparound, dump olddma..newdma */
- dvb_dmx_swfilter_packets(&budget->demux,
- mem+olddma, (newdma-olddma) / 188);
- } else { /* wraparound, dump olddma..buflen and 0..newdma */
- dvb_dmx_swfilter_packets(&budget->demux,
- mem+olddma, (TS_BUFLEN-olddma) / 188);
- dvb_dmx_swfilter_packets(&budget->demux,
- mem, newdma / 188);
- }
+ if (newdma > olddma) { /* no wraparound, dump olddma..newdma */
+ dvb_dmx_swfilter_packets(&budget->demux,
+ mem+olddma, (newdma-olddma) / 188);
+ } else { /* wraparound, dump olddma..buflen and 0..newdma */
+ dvb_dmx_swfilter_packets(&budget->demux,
+ mem+olddma, (TS_BUFLEN-olddma) / 188);
+ dvb_dmx_swfilter_packets(&budget->demux,
+ mem, newdma / 188);
+ }
}
@@ -156,21 +162,21 @@ static void vpeirq (unsigned long data)
static int budget_start_feed(struct dvb_demux_feed *feed)
{
- struct dvb_demux *demux = feed->demux;
- struct budget *budget = (struct budget*) demux->priv;
+ struct dvb_demux *demux = feed->demux;
+ struct budget *budget = (struct budget*) demux->priv;
DEB_EE(("budget: %p\n",budget));
- if (!demux->dmx.frontend)
- return -EINVAL;
+ if (!demux->dmx.frontend)
+ return -EINVAL;
return start_ts_capture (budget);
}
static int budget_stop_feed(struct dvb_demux_feed *feed)
{
- struct dvb_demux *demux = feed->demux;
- struct budget *budget = (struct budget *) demux->priv;
+ struct dvb_demux *demux = feed->demux;
+ struct budget *budget = (struct budget *) demux->priv;
DEB_EE(("budget: %p\n",budget));
@@ -180,49 +186,49 @@ static int budget_stop_feed(struct dvb_demux_feed *feed)
static int budget_register(struct budget *budget)
{
- struct dvb_demux *dvbdemux = &budget->demux;
- int ret;
+ struct dvb_demux *dvbdemux = &budget->demux;
+ int ret;
DEB_EE(("budget: %p\n",budget));
- dvbdemux->priv = (void *) budget;
+ dvbdemux->priv = (void *) budget;
dvbdemux->filternum = 256;
- dvbdemux->feednum = 256;
- dvbdemux->start_feed = budget_start_feed;
- dvbdemux->stop_feed = budget_stop_feed;
- dvbdemux->write_to_decoder = NULL;
+ dvbdemux->feednum = 256;
+ dvbdemux->start_feed = budget_start_feed;
+ dvbdemux->stop_feed = budget_stop_feed;
+ dvbdemux->write_to_decoder = NULL;
- dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING |
- DMX_MEMORY_BASED_FILTERING);
+ dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING);
- dvb_dmx_init(&budget->demux);
+ dvb_dmx_init(&budget->demux);
- budget->dmxdev.filternum = 256;
- budget->dmxdev.demux = &dvbdemux->dmx;
- budget->dmxdev.capabilities = 0;
+ budget->dmxdev.filternum = 256;
+ budget->dmxdev.demux = &dvbdemux->dmx;
+ budget->dmxdev.capabilities = 0;
- dvb_dmxdev_init(&budget->dmxdev, budget->dvb_adapter);
+ dvb_dmxdev_init(&budget->dmxdev, budget->dvb_adapter);
- budget->hw_frontend.source = DMX_FRONTEND_0;
+ budget->hw_frontend.source = DMX_FRONTEND_0;
- ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend);
+ ret = dvbdemux->dmx.add_frontend(&dvbdemux->dmx, &budget->hw_frontend);
if (ret < 0)
- return ret;
-
- budget->mem_frontend.source = DMX_MEMORY_FE;
- ret=dvbdemux->dmx.add_frontend (&dvbdemux->dmx,
- &budget->mem_frontend);
- if (ret<0)
- return ret;
-
- ret=dvbdemux->dmx.connect_frontend (&dvbdemux->dmx,
- &budget->hw_frontend);
- if (ret < 0)
- return ret;
-
- dvb_net_init(budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx);
+ return ret;
+
+ budget->mem_frontend.source = DMX_MEMORY_FE;
+ ret=dvbdemux->dmx.add_frontend (&dvbdemux->dmx,
+ &budget->mem_frontend);
+ if (ret<0)
+ return ret;
+
+ ret=dvbdemux->dmx.connect_frontend (&dvbdemux->dmx,
+ &budget->hw_frontend);
+ if (ret < 0)
+ return ret;
+
+ dvb_net_init(budget->dvb_adapter, &budget->dvb_net, &dvbdemux->dmx);
return 0;
}
@@ -230,18 +236,18 @@ static int budget_register(struct budget *budget)
static void budget_unregister(struct budget *budget)
{
- struct dvb_demux *dvbdemux=&budget->demux;
+ struct dvb_demux *dvbdemux=&budget->demux;
DEB_EE(("budget: %p\n",budget));
dvb_net_release(&budget->dvb_net);
dvbdemux->dmx.close(&dvbdemux->dmx);
- dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->hw_frontend);
- dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->mem_frontend);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->hw_frontend);
+ dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, &budget->mem_frontend);
- dvb_dmxdev_release(&budget->dmxdev);
- dvb_dmx_release(&budget->demux);
+ dvb_dmxdev_release(&budget->dmxdev);
+ dvb_dmx_release(&budget->demux);
}
@@ -270,12 +276,12 @@ int ttpci_budget_init (struct budget *budget,
dvb_register_adapter(&budget->dvb_adapter, budget->card->name, THIS_MODULE);
/* set dd1 stream a & b */
- saa7146_write(dev, DD1_STREAM_B, 0x00000000);
+ saa7146_write(dev, DD1_STREAM_B, 0x00000000);
saa7146_write(dev, DD1_INIT, 0x02000000);
saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
/* the Siemens DVB needs this if you want to have the i2c chips
- get recognized before the main driver is loaded */
+ get recognized before the main driver is loaded */
if (bi->type != BUDGET_FS_ACTIVY)
saa7146_write(dev, GPIO_CTRL, 0x500000); /* GPIO 3 = 1 */
@@ -298,7 +304,7 @@ int ttpci_budget_init (struct budget *budget,
saa7146_write(dev, PCI_BT_V1, 0x001c0000);
/* upload all */
- saa7146_write(dev, GPIO_CTRL, 0x000000);
+ saa7146_write(dev, GPIO_CTRL, 0x000000);
tasklet_init (&budget->vpe_tasklet, vpeirq, (unsigned long) budget);
@@ -308,7 +314,7 @@ int ttpci_budget_init (struct budget *budget,
else
saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
- if (budget_register(budget) == 0) {
+ if (budget_register(budget) == 0) {
return 0;
}
err:
@@ -365,4 +371,3 @@ EXPORT_SYMBOL_GPL(budget_debug);
MODULE_PARM(budget_debug,"i");
MODULE_LICENSE("GPL");
-
diff --git a/linux/drivers/media/dvb/ttpci/budget.h b/linux/drivers/media/dvb/ttpci/budget.h
index 3c730423d..b0d315a0f 100644
--- a/linux/drivers/media/dvb/ttpci/budget.h
+++ b/linux/drivers/media/dvb/ttpci/budget.h
@@ -45,6 +45,8 @@ struct budget {
int fe_synced;
struct semaphore pid_mutex;
+
+ int ci_present;
u8 tsf;
u32 ttbp;
@@ -75,7 +77,6 @@ static struct saa7146_pci_extension_data x_var = { \
#define BUDGET_PATCH 3
#define BUDGET_FS_ACTIVY 4
-
extern int ttpci_budget_init (struct budget *budget,
struct saa7146_dev* dev,
struct saa7146_pci_extension_data *info);
diff --git a/linux/include/media/saa7146.h b/linux/include/media/saa7146.h
index e3d94070a..b3019ceb7 100644
--- a/linux/include/media/saa7146.h
+++ b/linux/include/media/saa7146.h
@@ -193,8 +193,6 @@ int saa7146_wait_for_debi_done(struct saa7146_dev *dev);
/* debi defines */
#define DEBINOSWAP 0x000e0000
-#define DEBICICTL 0x00420000
-#define DEBICICAM 0x02420000
/* define for the register programming sequencer (rps) */
#define CMD_NOP 0x00000000 /* No operation */