fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / media / dvb / dvb-core / dvb_ca_en50221.c
index 88ed3b9..2a03bf5 100644 (file)
 #include <linux/slab.h>
 #include <linux/list.h>
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/vmalloc.h>
 #include <linux/delay.h>
-#include <asm/semaphore.h>
-#include <asm/atomic.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
 
 #include "dvb_ca_en50221.h"
-#include "dvb_functions.h"
 #include "dvb_ringbuffer.h"
 
-static int dvb_ca_en50221_debug = 0;
+static int dvb_ca_en50221_debug;
+
+module_param_named(cam_debug, dvb_ca_en50221_debug, int, 0644);
+MODULE_PARM_DESC(cam_debug, "enable verbose debug messages");
+
 #define dprintk if (dvb_ca_en50221_debug) printk
 
-#define INIT_TIMEOUT_SECS 5
+#define INIT_TIMEOUT_SECS 10
 
 #define HOST_LINK_BUF_SIZE 0x200
 
@@ -58,19 +62,19 @@ static int dvb_ca_en50221_debug = 0;
 #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 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_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 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
@@ -86,83 +90,80 @@ static int dvb_ca_en50221_debug = 0;
 /* Information on a CA slot */
 struct dvb_ca_slot {
 
-        /* current state of the CAM */
-        int slot_state;
+       /* current state of the CAM */
+       int slot_state;
 
-        /* Number of CAMCHANGES that have occurred since last processing */
-        atomic_t camchange_count;
+       /* Number of CAMCHANGES that have occurred since last processing */
+       atomic_t camchange_count;
 
-        /* Type of last CAMCHANGE */
-        int camchange_type;
+       /* Type of last CAMCHANGE */
+       int camchange_type;
 
-        /* base address of CAM config */
-        u32 config_base;
+       /* base address of CAM config */
+       u32 config_base;
 
-        /* value to write into Config Control register */
-        u8 config_option;
+       /* value to write into Config Control register */
+       u8 config_option;
 
-        /* if 1, the CAM supports DA IRQs */
-        u8 da_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;
+       /* 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;
 
-        /* buffer for incoming packets */
-        struct dvb_ringbuffer rx_buffer;
-
-        /* timer used during various states of the slot */
-        unsigned long timeout;
+       /* 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;
+       /* pointer back to the public data structure */
+       struct dvb_ca_en50221 *pub;
 
-        /* the DVB device */
-        struct dvb_device *dvbdev;
+       /* the DVB device */
+       struct dvb_device *dvbdev;
 
-        /* Flags describing the interface (DVB_CA_FLAG_*) */
-        u32 flags;
+       /* Flags describing the interface (DVB_CA_FLAG_*) */
+       u32 flags;
 
-        /* number of slots supported by this CA interface */
-        unsigned int slot_count;
+       /* number of slots supported by this CA interface */
+       unsigned int slot_count;
 
-        /* information on each slot */
-        struct dvb_ca_slot* slot_info;
+       /* information on each slot */
+       struct dvb_ca_slot *slot_info;
 
-        /* wait queues for read() and write() operations */
-        wait_queue_head_t wait_queue;
+       /* wait queues for read() and write() operations */
+       wait_queue_head_t wait_queue;
 
-        /* PID of the monitoring thread */
-        pid_t thread_pid;
+       /* PID of the monitoring thread */
+       pid_t thread_pid;
 
-        /* Wait queue used when shutting thread down */
-        wait_queue_head_t thread_queue;
+       /* Wait queue used when shutting thread down */
+       wait_queue_head_t thread_queue;
 
-        /* Flag indicating when thread should exit */
-        int exit:1;
+       /* Flag indicating when thread should exit */
+       unsigned int exit:1;
 
-        /* Flag indicating if the CA device is open */
-        int open:1;
+       /* Flag indicating if the CA device is open */
+       unsigned int open:1;
 
-        /* Flag indicating the thread should wake up now */
-        int wakeup:1;
+       /* Flag indicating the thread should wake up now */
+       unsigned int wakeup:1;
 
-        /* Delay the main thread should use */
-        unsigned long delay;
+       /* 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;
+       /* 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_privateca);
-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);
+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);
 
 
 /**
@@ -174,17 +175,19 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private* ca, int slot, u8* eb
  * @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)
+static u8 *findstr(u8 * haystack, int hlen, u8 * needle, int nlen)
 {
-        int i;
+       int i;
 
-        if (hlen < nlen) return NULL;
+       if (hlen < nlen)
+               return NULL;
 
-        for(i=0; i<= hlen - nlen; i++) {
-                  if (!strncmp(haystack+i, needle, nlen)) return haystack+i;
-        }
+       for (i = 0; i <= hlen - nlen; i++) {
+               if (!strncmp(haystack + i, needle, nlen))
+                       return haystack + i;
+       }
 
-        return NULL;
+       return NULL;
 }
 
 
@@ -196,46 +199,43 @@ static u8* findstr(u8* haystack, int hlen, u8* needle, int nlen)
 /**
  * Check CAM status.
  */
-static int dvb_ca_en50221_check_camstatus(struct dvb_ca_privateca, int slot)
+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;
+       int slot_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 */
+       slot_status = ca->pub->poll_slot_status(ca->pub, slot, ca->open);
+
+       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;
 }
 
 
@@ -250,40 +250,42 @@ static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private* ca, int slot)
  *
  * @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)
+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;
+       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 */
+               msleep(1);
+       }
+
+       dprintk("%s failed timeout:%lu\n", __FUNCTION__, jiffies - start);
+
+       /* if we get here, we've timed out */
+       return -ETIMEDOUT;
 }
 
 
@@ -295,47 +297,52 @@ static int dvb_ca_en50221_wait_if_status(struct dvb_ca_private* ca, int slot, u8
  *
  * @return 0 on success, nonzero on failure.
  */
-static int dvb_ca_en50221_link_init(struct dvb_ca_privateca, int slot)
+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].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;
+       int ret;
+       int buf_size;
+       u8 buf[2];
+
+       dprintk("%s\n", __FUNCTION__);
+
+       /* we'll be determining these during this function */
+       ca->slot_info[slot].da_irq_supported = 0;
+
+       /* 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;
 }
 
 /**
@@ -350,33 +357,44 @@ static int dvb_ca_en50221_link_init(struct dvb_ca_private* ca, int slot)
  *
  * @return 0 on success, nonzero on error.
  */
-static int dvb_ca_en50221_read_tuple(struct dvb_ca_privateca, int slot,
-                                     int* address, int* tupleType, int* tupleLength, u8* tuple)
+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;
+       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 (_tupleType == 0xff) {
+               dprintk("END OF CHAIN TUPLE type:0x%x\n", _tupleType);
+               *address += 2;
+               *tupleType = _tupleType;
+               *tupleLength = 0;
+               return 0;
+       }
+       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;
 }
 
 
@@ -389,116 +407,141 @@ static int dvb_ca_en50221_read_tuple(struct dvb_ca_private* ca, int slot,
  *
  * @return 0 on success, <0 on failure.
  */
-static int dvb_ca_en50221_parse_attributes(struct dvb_ca_privateca, int slot)
+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;
-
-                        /* 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;
-                }
-        }
+       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 adapter %d: Unsupported DVB CAM module version %c%c%c%c\n",
+                      ca->dvbdev->adapter->num, 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;
+
+                       /* 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;
+       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);
+       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;
+       // success!
+       return 0;
 }
 
 
@@ -508,22 +551,24 @@ static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private* ca, int slot)
  * @param ca CA instance.
  * @param slot Slot containing the CAM.
  */
-static int dvb_ca_en50221_set_configoption(struct dvb_ca_privateca, int slot)
+static int dvb_ca_en50221_set_configoption(struct dvb_ca_private *ca, int slot)
 {
-        int configoption;
+       int configoption;
 
-        dprintk ("%s\n", __FUNCTION__);
+       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);
+       /* 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);
-        dprintk("Set configoption 0x%x, read configoption 0x%x\n", 
+       /* check it */
+       configoption = ca->pub->read_attribute_mem(ca->pub, slot, ca->slot_info[slot].config_base);
+       dprintk("Set configoption 0x%x, read configoption 0x%x\n",
                ca->slot_info[slot].config_option, configoption & 0x3f);
 
-        /* fine! */
-        return 0;
+       /* fine! */
+       return 0;
 
 }
 
@@ -541,101 +586,114 @@ static int dvb_ca_en50221_set_configoption(struct dvb_ca_private* ca, int slot)
  *
  * @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)
+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 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);
-        }
-
-        /* wake up readers when a last_fragment is received */
-        if ((buf[1] & 0x80) == 0x00) {
-                wake_up_interruptible(&ca->wait_queue);
-        }
-
-        status = bytes_read;
+       int bytes_read;
+       int status;
+       u8 buf[HOST_LINK_BUF_SIZE];
+       int i;
+
+       dprintk("%s\n", __FUNCTION__);
+
+       /* check if we have space for a link buf in the rx_buffer */
+       if (ebuf == NULL) {
+               int buf_free;
+
+               if (ca->slot_info[slot].rx_buffer.data == NULL) {
+                       status = -EIO;
+                       goto exit;
+               }
+               buf_free = dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer);
+
+               if (buf_free < (ca->slot_info[slot].link_buf_size + DVB_RINGBUFFER_PKTHDRSIZE)) {
+                       status = -EAGAIN;
+                       goto exit;
+               }
+       }
+
+       /* check if there is data available */
+       if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
+               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 adapter %d: CAM tried to send a buffer larger than the link buffer size (%i > %i)!\n",
+                              ca->dvbdev->adapter->num, bytes_read, ca->slot_info[slot].link_buf_size);
+                       ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+                       status = -EIO;
+                       goto exit;
+               }
+               if (bytes_read < 2) {
+                       printk("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n",
+                              ca->dvbdev->adapter->num);
+                       ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+                       status = -EIO;
+                       goto exit;
+               }
+       } else {
+               if (bytes_read > ecount) {
+                       printk("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n",
+                              ca->dvbdev->adapter->num);
+                       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 be 0) */
+       if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
+               goto exit;
+       if (status & STATUSREG_RE) {
+               ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+               status = -EIO;
+               goto exit;
+       }
+
+       /* OK, add it to the receive buffer, or copy into external buffer if supplied */
+       if (ebuf == NULL) {
+               if (ca->slot_info[slot].rx_buffer.data == NULL) {
+                       status = -EIO;
+                       goto exit;
+               }
+               dvb_ringbuffer_pkt_write(&ca->slot_info[slot].rx_buffer, buf, bytes_read);
+       } else {
+               memcpy(ebuf, buf, bytes_read);
+       }
+
+       dprintk("Received CA packet for slot %i connection id 0x%x last_frag:%i size:0x%x\n", slot,
+               buf[0], (buf[1] & 0x80) == 0, bytes_read);
+
+       /* wake up readers when a last_fragment is received */
+       if ((buf[1] & 0x80) == 0x00) {
+               wake_up_interruptible(&ca->wait_queue);
+       }
+       status = bytes_read;
 
 exit:
-        up(&ca->slot_info[slot].sem);
-        return status;
+       return status;
 }
 
 
@@ -651,69 +709,73 @@ exit:
  *
  * @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)
+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 exitnowrite;
-        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;
+       int status;
+       int i;
+
+       dprintk("%s\n", __FUNCTION__);
+
+
+       // sanity check
+       if (bytes_write > ca->slot_info[slot].link_buf_size)
+               return -EINVAL;
+
+       /* check if interface is actually waiting for us to read from it, or if a read is in progress */
+       if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
+               goto exitnowrite;
+       if (status & (STATUSREG_DA | STATUSREG_RE)) {
+               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) {
+               ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT;
+               status = -EIO;
+               goto exit;
+       }
+       status = bytes_write;
+
+       dprintk("Wrote CA packet for slot %i, connection id 0x%x last_frag:%i size:0x%x\n", slot,
+               buf[0], (buf[1] & 0x80) == 0, bytes_write);
 
 exit:
-        ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
+       ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN);
 
 exitnowrite:
-        up(&ca->slot_info[slot].sem);
-        return status;
+       return status;
 }
+EXPORT_SYMBOL(dvb_ca_en50221_camchange_irq);
 
 
 
@@ -727,28 +789,23 @@ exitnowrite:
  * @param ca CA instance.
  * @param slot Slot to shut down.
  */
-static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_privateca, int slot)
+static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private *ca, int slot)
 {
-        int status;
+       dprintk("%s\n", __FUNCTION__);
 
-        dprintk ("%s\n", __FUNCTION__);
+       ca->pub->slot_shutdown(ca->pub, slot);
+       ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
 
-        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 processes to check if they're now
+          trying to write to a defunct CAM */
+       wake_up_interruptible(&ca->wait_queue);
 
-        /* need to wake up all 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);
 
-        dprintk("Slot %i shutdown\n", slot);
-
-        /* success */
-        return 0;
+       /* success */
+       return 0;
 }
+EXPORT_SYMBOL(dvb_ca_en50221_camready_irq);
 
 
 /**
@@ -758,25 +815,26 @@ static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private* ca, int slot)
  * @param slot Slot concerned.
  * @param change_type One of the DVB_CA_CAMCHANGE_* values.
  */
-void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221pubca, int slot, int change_type)
+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;
+       struct dvb_ca_private *ca = pubca->private;
 
-        dprintk("CAMCHANGE IRQ slot:%i change_type:%i\n", slot, change_type);
+       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;
+       switch (change_type) {
+       case DVB_CA_EN50221_CAMCHANGE_REMOVED:
+       case DVB_CA_EN50221_CAMCHANGE_INSERTED:
+               break;
 
-        default:
-                return;
-        }
+       default:
+               return;
+       }
 
-        ca->slot_info[slot].camchange_type = change_type;
-        atomic_inc(&ca->slot_info[slot].camchange_count);
-        dvb_ca_en50221_thread_wakeup(ca);
+       ca->slot_info[slot].camchange_type = change_type;
+       atomic_inc(&ca->slot_info[slot].camchange_count);
+       dvb_ca_en50221_thread_wakeup(ca);
 }
+EXPORT_SYMBOL(dvb_ca_en50221_frda_irq);
 
 
 /**
@@ -785,16 +843,16 @@ void dvb_ca_en50221_camchange_irq(struct dvb_ca_en50221* pubca, int slot, int ch
  * @param ca CA instance.
  * @param slot Slot concerned.
  */
-void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221pubca, int slot)
+void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221 *pubca, int slot)
 {
-        struct dvb_ca_private* ca = (struct dvb_ca_private*) pubca->private;
+       struct dvb_ca_private *ca = pubca->private;
 
-        dprintk("CAMREADY IRQ slot:%i\n", slot);
+       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);
-        }
+       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);
+       }
 }
 
 
@@ -804,29 +862,27 @@ void dvb_ca_en50221_camready_irq(struct dvb_ca_en50221* pubca, int slot)
  * @param ca CA instance.
  * @param slot Slot concerned.
  */
-void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221pubca, int slot)
+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;
+       struct dvb_ca_private *ca = pubca->private;
+       int flags;
 
-        dprintk("FR/DA IRQ slot:%i\n", slot);
+       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;
+       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;
                }
-                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);
-                }
-                break;
-        }
+               break;
+
+       case DVB_CA_SLOTSTATE_RUNNING:
+               if (ca->open)
+                       dvb_ca_en50221_thread_wakeup(ca);
+               break;
+       }
 }
 
 
@@ -839,14 +895,14 @@ void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221* pubca, int slot)
  *
  * @param ca CA instance.
  */
-static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_privateca)
+static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca)
 {
 
-        dprintk ("%s\n", __FUNCTION__);
+       dprintk("%s\n", __FUNCTION__);
 
-        ca->wakeup = 1;
-        mb();
-        wake_up_interruptible(&ca->thread_queue);
+       ca->wakeup = 1;
+       mb();
+       wake_up_interruptible(&ca->thread_queue);
 }
 
 /**
@@ -854,15 +910,16 @@ static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private* ca)
  *
  * @param ca CA instance.
  */
-static int dvb_ca_en50221_thread_should_wakeup(struct dvb_ca_privateca)
+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;
+       if (ca->wakeup) {
+               ca->wakeup = 0;
+               return 1;
+       }
+       if (ca->exit)
+               return 1;
+
+       return 0;
 }
 
 
@@ -871,49 +928,50 @@ static int dvb_ca_en50221_thread_should_wakeup(struct dvb_ca_private* ca)
  *
  * @param ca CA instance.
  */
-static void dvb_ca_en50221_thread_update_delay(struct dvb_ca_privateca)
+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++) {
-                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->open) {
-                                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;
+       int delay;
+       int curdelay = 100000000;
+       int slot;
+
+       for (slot = 0; slot < ca->slot_count; slot++) {
+               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->open) {
+                               if ((!ca->slot_info[slot].da_irq_supported) ||
+                                   (!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_DA))) {
+                                       delay = HZ / 10;
+                               }
+                       }
+                       break;
+               }
+
+               if (delay < curdelay)
+                       curdelay = delay;
+       }
+
+       ca->delay = curdelay;
 }
 
 
@@ -921,167 +979,213 @@ static void dvb_ca_en50221_thread_update_delay(struct dvb_ca_private* ca)
 /**
  * Kernel thread which monitors CA slots for CAM changes, and performs data transfers.
  */
-static int dvb_ca_en50221_thread(voiddata)
+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;
-                                       dvb_ca_en50221_thread_update_delay(ca);
-                                        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;
-                                       dvb_ca_en50221_thread_update_delay(ca);
-                                        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;
-                                       dvb_ca_en50221_thread_update_delay(ca);
-                                        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;
-                                       dvb_ca_en50221_thread_update_delay(ca);
-                                        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;
-                                       dvb_ca_en50221_thread_update_delay(ca);
-                                        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;
-                                       dvb_ca_en50221_thread_update_delay(ca);
-                                        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;
+       struct dvb_ca_private *ca = data;
+       char name[15];
+       int slot;
+       int flags;
+       int status;
+       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);
+
+       lock_kernel();
+       daemonize(name);
+       sigfillset(&current->blocked);
+       unlock_kernel();
+
+       /* 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 adaptor %d: PC card did not respond :(\n",
+                                              ca->dvbdev->adapter->num);
+                                       ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+                                       dvb_ca_en50221_thread_update_delay(ca);
+                                       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) {
+                                       /* we need this extra check for annoying interfaces like the budget-av */
+                                       if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) &&
+                                           (ca->pub->poll_slot_status)) {
+                                               int status = ca->pub->poll_slot_status(ca->pub, slot, 0);
+                                               if (!(status & DVB_CA_EN50221_POLL_CAM_PRESENT)) {
+                                                       ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
+                                                       dvb_ca_en50221_thread_update_delay(ca);
+                                                       break;
+                                               }
+                                       }
+
+                                       printk("dvb_ca adapter %d: Invalid PC card inserted :(\n",
+                                              ca->dvbdev->adapter->num);
+                                       ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+                                       dvb_ca_en50221_thread_update_delay(ca);
+                                       break;
+                               }
+                               if (dvb_ca_en50221_set_configoption(ca, slot) != 0) {
+                                       printk("dvb_ca adapter %d: Unable to initialise CAM :(\n",
+                                              ca->dvbdev->adapter->num);
+                                       ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+                                       dvb_ca_en50221_thread_update_delay(ca);
+                                       break;
+                               }
+                               if (ca->pub->write_cam_control(ca->pub, slot,
+                                                              CTRLIF_COMMAND, CMDREG_RS) != 0) {
+                                       printk("dvb_ca adapter %d: Unable to reset CAM IF\n",
+                                              ca->dvbdev->adapter->num);
+                                       ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+                                       dvb_ca_en50221_thread_update_delay(ca);
+                                       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 adapter %d: DVB CAM did not respond :(\n",
+                                              ca->dvbdev->adapter->num);
+                                       ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+                                       dvb_ca_en50221_thread_update_delay(ca);
+                                       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) {
+                                       /* we need this extra check for annoying interfaces like the budget-av */
+                                       if ((!(ca->flags & DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE)) &&
+                                           (ca->pub->poll_slot_status)) {
+                                               int status = ca->pub->poll_slot_status(ca->pub, slot, 0);
+                                               if (!(status & DVB_CA_EN50221_POLL_CAM_PRESENT)) {
+                                                       ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_NONE;
+                                                       dvb_ca_en50221_thread_update_delay(ca);
+                                                       break;
+                                               }
+                                       }
+
+                                       printk("dvb_ca adapter %d: DVB CAM link initialisation failed :(\n", ca->dvbdev->adapter->num);
+                                       ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+                                       dvb_ca_en50221_thread_update_delay(ca);
+                                       break;
+                               }
+
+                               if (ca->slot_info[slot].rx_buffer.data == NULL) {
+                                       rxbuf = vmalloc(RX_BUFFER_SIZE);
+                                       if (rxbuf == NULL) {
+                                               printk("dvb_ca adapter %d: Unable to allocate CAM rx buffer :(\n", ca->dvbdev->adapter->num);
+                                               ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID;
+                                               dvb_ca_en50221_thread_update_delay(ca);
+                                               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 adapter %d: DVB CAM detected and initialised successfully\n", ca->dvbdev->adapter->num);
+                               break;
+
+                       case DVB_CA_SLOTSTATE_RUNNING:
+                               if (!ca->open)
+                                       continue;
+
+                               // poll slots for data
+                               pktcount = 0;
+                               while ((status = 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;
 }
 
 
@@ -1100,65 +1204,65 @@ static int dvb_ca_en50221_thread(void* data)
  *
  * @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)
+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;
+       struct dvb_device *dvbdev = file->private_data;
+       struct dvb_ca_private *ca = 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 = 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 = 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;
 }
 
 
@@ -1172,9 +1276,10 @@ static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file, un
  *
  * @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)
+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);
+       return dvb_usercopy(inode, file, cmd, arg, dvb_ca_en50221_io_do_ioctl);
 }
 
 
@@ -1188,108 +1293,125 @@ static int dvb_ca_en50221_io_ioctl(struct inode *inode, struct file *file, unsig
  *
  * @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)
+static ssize_t dvb_ca_en50221_io_write(struct file *file,
+                                      const char __user * 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;
-        unsigned long timeout;
-        int written;
-
-        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;
-
-               timeout = jiffies + HZ/2;
-               written = 0;
-                while(!time_after(jiffies, timeout)) {
-                        status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen+2);
-                       if (status == (fraglen+2)) {
-                              written = 1;
-                              break;
+       struct dvb_device *dvbdev = file->private_data;
+       struct dvb_ca_private *ca = dvbdev->priv;
+       u8 slot, connection_id;
+       int status;
+       char fragbuf[HOST_LINK_BUF_SIZE];
+       int fragpos = 0;
+       int fraglen;
+       unsigned long timeout;
+       int written;
+
+       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;
+
+               timeout = jiffies + HZ / 2;
+               written = 0;
+               while (!time_after(jiffies, timeout)) {
+                       /* check the CAM hasn't been removed/reset in the meantime */
+                       if (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_RUNNING) {
+                               status = -EIO;
+                               goto exit;
                        }
-                        if (status != -EAGAIN) goto exit;
 
-                        dvb_delay(1);
-                }
-               if (!written) {
-                       status = -EIO;
-                       goto exit;
+                       status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen + 2);
+                       if (status == (fraglen + 2)) {
+                               written = 1;
+                               break;
+                       }
+                       if (status != -EAGAIN)
+                               goto exit;
+
+                       msleep(1);
+               }
+               if (!written) {
+                       status = -EIO;
+                       goto exit;
                }
 
-                fragpos += fraglen;
-        }
-        status = count + 2;
+               fragpos += fraglen;
+       }
+       status = count + 2;
 
 exit:
-        return status;
+       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)
+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);
+       int slot;
+       int slot_count = 0;
+       int idx;
+       size_t 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 (ca->slot_info[slot].rx_buffer.data == NULL) {
+                       return 0;
+               }
+
+               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);
+               }
 
 nextslot:
-                slot = (slot + 1) % ca->slot_count;
-                slot_count++;
-        }
+               slot = (slot + 1) % ca->slot_count;
+               slot_count++;
+       }
 
-        ca->next_read_slot = slot;
-        return found;
+       ca->next_read_slot = slot;
+       return found;
 }
 
 
@@ -1303,83 +1425,93 @@ nextslot:
  *
  * @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)
+static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user * 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);
-
-        hdr[0] = slot;
-        hdr[1] = connection_id;
-        if ((status = copy_to_user(buf, hdr, 2)) != 0) goto exit;
-        status = pktlen;
+       struct dvb_device *dvbdev = file->private_data;
+       struct dvb_ca_private *ca = 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 adapter %d: BUG: read packet ended before last_fragment encountered\n", ca->dvbdev->adapter->num);
+                       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);
+
+       hdr[0] = slot;
+       hdr[1] = connection_id;
+       if ((status = copy_to_user(buf, hdr, 2)) != 0)
+               goto exit;
+       status = pktlen;
 
 exit:
-        up(&ca->slot_info[slot].sem);
-        return status;
+       return status;
 }
 
 
@@ -1393,28 +1525,36 @@ exit:
  */
 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;
+       struct dvb_device *dvbdev = file->private_data;
+       struct dvb_ca_private *ca = dvbdev->priv;
+       int err;
+       int i;
 
-        dprintk ("%s\n", __FUNCTION__);
+       dprintk("%s\n", __FUNCTION__);
 
-        err=dvb_generic_open(inode, file);
-        if (err<0)
-                return err;
+       if (!try_module_get(ca->pub->owner))
+               return -EIO;
 
-        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);
-                }
-        }
+       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) {
+                       if (ca->slot_info[i].rx_buffer.data != NULL) {
+                               /* it is safe to call this here without locks because
+                                * ca->open == 0. Data is not read in this case */
+                               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);
+       ca->open = 1;
+       dvb_ca_en50221_thread_update_delay(ca);
+       dvb_ca_en50221_thread_wakeup(ca);
 
-        return 0;
+       return 0;
 }
 
 
@@ -1428,20 +1568,21 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
  */
 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;
+       struct dvb_device *dvbdev = file->private_data;
+       struct dvb_ca_private *ca = dvbdev->priv;
+       int err = 0;
+
+       dprintk("%s\n", __FUNCTION__);
+
+       /* mark the CA device as closed */
+       ca->open = 0;
+       dvb_ca_en50221_thread_update_delay(ca);
 
-        dprintk ("%s\n", __FUNCTION__);
+       err = dvb_generic_release(inode, file);
 
-        /* mark the CA device as closed */
-        ca->open = 0;
-        dvb_ca_en50221_thread_update_delay(ca);
+       module_put(ca->pub->owner);
 
-        err=dvb_generic_release(inode, file);
-        if (err<0)
-                return err;
-        return 0;
+       return 0;
 }
 
 
@@ -1453,52 +1594,52 @@ static int dvb_ca_en50221_io_release(struct inode *inode, struct file *file)
  *
  * @return Standard poll mask.
  */
-static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table *wait)
+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;
+       struct dvb_device *dvbdev = file->private_data;
+       struct dvb_ca_private *ca = dvbdev->priv;
+       unsigned int mask = 0;
+       int slot;
+       int result = 0;
 
-        dprintk ("%s\n", __FUNCTION__);
+       dprintk("%s\n", __FUNCTION__);
 
-        if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
-                up(&ca->slot_info[slot].sem);
-                mask |= POLLIN;
-        }
+       if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
+               mask |= POLLIN;
+       }
 
-        /* if there is something, return now */
-        if (mask) return mask;
+       /* if there is something, return now */
+       if (mask)
+               return mask;
 
-        /* wait for something to happen */
-        poll_wait(file, &ca->wait_queue, wait);
+       /* 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;
-        }
+       if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
+               mask |= POLLIN;
+       }
 
-        return mask;
+       return mask;
 }
+EXPORT_SYMBOL(dvb_ca_en50221_init);
 
 
 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,
+       .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,
-        readers: 1,
-        writers: 1,
-        fops: &dvb_ca_fops,
+       .priv = NULL,
+       .users = 1,
+       .readers = 1,
+       .writers = 1,
+       .fops = &dvb_ca_fops,
 };
 
 
@@ -1516,76 +1657,80 @@ static struct dvb_device dvbdev_ca = {
  *
  * @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 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 */
-        ret = kernel_thread (dvb_ca_en50221_thread, ca, 0);
-        if (ret < 0) {
-                printk("dvb_ca_init: failed to start kernel_thread (%d)\n", ret);
-                goto error;
-        }
-        ca->thread_pid = ret;
-        return 0;
+       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 = kzalloc(sizeof(struct dvb_ca_private), GFP_KERNEL)) == NULL) {
+               ret = -ENOMEM;
+               goto error;
+       }
+       ca->pub = pubca;
+       ca->flags = flags;
+       ca->slot_count = slot_count;
+       if ((ca->slot_info = kcalloc(slot_count, sizeof(struct dvb_ca_slot), GFP_KERNEL)) == NULL) {
+               ret = -ENOMEM;
+               goto error;
+       }
+       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;
+       }
+
+       if (signal_pending(current)) {
+               ret = -EINTR;
+               goto error;
+       }
+       mb();
+
+       /* create a kthread for monitoring this CA device */
+
+       ret = kernel_thread(dvb_ca_en50221_thread, ca, 0);
+
+       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;
+       if (ca != NULL) {
+               if (ca->dvbdev != NULL)
+                       dvb_unregister_device(ca->dvbdev);
+               kfree(ca->slot_info);
+               kfree(ca);
+       }
+       pubca->private = NULL;
+       return ret;
 }
+EXPORT_SYMBOL(dvb_ca_en50221_release);
 
 
 
@@ -1595,34 +1740,32 @@ error:
  * @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_en50221pubca)
+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;
+       struct dvb_ca_private *ca = 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 adapter %d: thread PID %d already died\n",
+                              ca->dvbdev->adapter->num, 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);
+               vfree(ca->slot_info[i].rx_buffer.data);
+       }
+       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");