summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_ca.c177
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_ca.h51
2 files changed, 115 insertions, 113 deletions
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ca.c b/linux/drivers/media/dvb/dvb-core/dvb_ca.c
index 68b627500..51c0b4166 100644
--- a/linux/drivers/media/dvb/dvb-core/dvb_ca.c
+++ b/linux/drivers/media/dvb/dvb-core/dvb_ca.c
@@ -73,39 +73,6 @@ static u8* findstr(u8* haystack, int hlen, u8* needle, int nlen) {
/* ******************************************************************************** */
-/* Modified version of wait_event_interruptible so we can check for errors */
-
-#define __dvb_ca_wait_event_interruptible(wq, condition, ret) \
-do { \
- wait_queue_t __wait; \
- init_waitqueue_entry(&__wait, current); \
- \
- add_wait_queue(&wq, &__wait); \
- for (;;) { \
- set_current_state(TASK_INTERRUPTIBLE); \
- if (ret = condition) \
- break; \
- if (!signal_pending(current)) { \
- schedule(); \
- continue; \
- } \
- ret = -ERESTARTSYS; \
- break; \
- } \
- current->state = TASK_RUNNING; \
- remove_wait_queue(&wq, &__wait); \
-} while (0)
-
-#define dvb_ca_wait_event_interruptible(wq, condition) \
-({ \
- int __ret = 0; \
- if (!(condition)) \
- __dvb_ca_wait_event_interruptible(wq, condition, __ret); \
- __ret; \
-})
-
-
-/* ******************************************************************************** */
/* Functions for controlling access to slots */
/**
@@ -116,7 +83,7 @@ do { \
*
* @return 0 on success, <0 on failure.
* */
-int dvb_ca_slot_acquire(struct dvb_ca* ca, int slot) {
+static int dvb_ca_slot_acquire(struct dvb_ca* ca, int slot) {
int status;
if (status = down_interruptible(&ca->slot_info[slot].sem)) return status;
@@ -140,7 +107,7 @@ int dvb_ca_slot_acquire(struct dvb_ca* ca, int slot) {
*
* @return 0 on success, <0 on failure.
* */
-int dvb_ca_slot_release(struct dvb_ca* ca, int slot) {
+static int dvb_ca_slot_release(struct dvb_ca* ca, int slot) {
int status;
if (status = down_interruptible(&ca->slot_info[slot].sem)) return status;
@@ -161,7 +128,7 @@ int dvb_ca_slot_release(struct dvb_ca* ca, int slot) {
*
* @return 0 on success, <0 on failure.
* */
-int dvb_ca_slot_acquire_exclusive(struct dvb_ca* ca, int slot) {
+static int dvb_ca_slot_acquire_exclusive(struct dvb_ca* ca, int slot) {
int status;
while(1) {
@@ -198,7 +165,7 @@ int dvb_ca_slot_acquire_exclusive(struct dvb_ca* ca, int slot) {
*
* @return 0 on success, <0 on failure.
* */
-int dvb_ca_slot_release_exclusive(struct dvb_ca* ca, int slot) {
+static int dvb_ca_slot_release_exclusive(struct dvb_ca* ca, int slot) {
ca->slot_info[slot].usage_counter--;
up(&ca->slot_info[slot].sem);
return(0);
@@ -768,12 +735,16 @@ exit:
*/
static int dvb_ca_en50221_slot_init(struct dvb_ca* ca, int slot) {
int status;
+ int old_camchange_count;
dprintk ("%s\n", __FUNCTION__);
/* acquire the slot exclusively */
if (status = dvb_ca_slot_acquire_exclusive(ca, slot)) return status;
+ /* get the current number of reported CAM changes */
+ old_camchange_count = atomic_get(&ca->slot_info[slot].camchange_count);
+
/* reset slot */
ca->slot_reset(ca, slot);
@@ -789,6 +760,12 @@ static int dvb_ca_en50221_slot_init(struct dvb_ca* ca, int slot) {
return status;
}
+ /* check for a CAM change before bothering to initialise it */
+ if (atomic_get(&ca->camchange_count) != old_camchange_count) {
+ dvb_ca_slot_release_exclusive(ca, slot);
+ return -EIO;
+ }
+
/* perform CAM link protocol init */
if (status = dvb_ca_en50221_link_init(ca, slot)) {
dvb_ca_slot_release_exclusive(ca, slot);
@@ -797,8 +774,8 @@ static int dvb_ca_en50221_slot_init(struct dvb_ca* ca, int slot) {
/* OK, slot is now active */
INIT_LIST_HEAD(&ca->slot[info].connections);
- init_MUTEX(&ca->slot_info[slot].task_sem);
- ca->slot_info[slot].tasks = 0;
+ atomic_set(&ca->slot_info[slot].write_count, 0);
+ atomic_set(&ca->slot_info[slot].read_count, 0);
ca->slot_info[slot].cam_present = 1;
/* success */
@@ -848,10 +825,50 @@ static int dvb_ca_en50221_slot_shutdown(struct dvb_ca* ca, int slot) {
return 0;
}
+/**
+ * A CAMCHANGE IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_camchange_irq(struct dvb_ca* ca, int slot) {
+ atomic_inc(&ca->slot_info[slot].camchange_count);
+}
+
+/**
+ * A READDATA IRQ has occurred.
+ *
+ * @param ca CA instance.
+ * @param slot Slot concerned.
+ */
+void dvb_ca_en50221_readdata_irq(struct dvb_ca* ca, int slot) {
+ atomic_inc(&ca->slot_info[slot].read_count);
+}
+
+
/* ******************************************************************************** */
/* Monitoring thread */
+/**
+ * Wake up the DVB CA thread
+ */
+static void dvb_ca_wakeup(struct dvb_ca* ca) {
+ ca->wakeup = 1;
+ wake_up_interruptible(&ca->wait_queue);
+}
+
+/**
+ * Used by the CA thread to determine if an early wakeup is necessary
+ */
+static int dvb_ca_should_wakeup(struct dvb_ca* ca) {
+ if (ca->wakeup) {
+ ca->wakeup = 0;
+ return 1;
+ }
+ if (ca->exit) return 1;
+ return 0;
+}
/**
* Kernel thread which monitors CA slots for CAM changes, and performs data transfers.
@@ -862,6 +879,7 @@ static int dvb_ca_thread(void* data) {
struct dvb_ca_connection* tmp;
char name[15];
int slot;
+ int timeout;
unsigned long scan_connections = jiffies + (60*HZ);
dprintk ("%s\n", __FUNCTION__);
@@ -875,32 +893,49 @@ static int dvb_ca_thread(void* data) {
up (&ca->thread_sem); /* is locked when we enter the thread... */
/* sleep for a bit */
- interruptible_sleep_on_timeout(&ca->thread_queue, HZ/10);
- if (signal_pending(current)) break;
+ timeout = wait_event_interruptible_timeout(ca->wait_queue,0 != dvb_ca_should_wakeup(ca), HZ/10);
+ if ((-ERESTARTSYS == timeout) || (ca->exit)) {
+ /* got signal or quitting */
+ break;
+ }
if (down_interruptible(&ca->thread_sem)) break;
- if (ca->exit) break;
- /* go through all the slots looking for CAM changes */
+ /* go through all the slots processing them */
for(slot=0; slot < ca->slot_count; slot++) {
/* poll for CAM changes if there aren't any IRQs for that
- * OR: if we've had a CAM_CHANGE task set */
+ * OR: if we've have a >1 CAMCHANGE counter in IRQ mode */
if ((!(ca->flags & DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE)) ||
- (ca->slot_info[slot].tasks & DVB_CA_TASK_CAM_CHANGED)) {
-
- /* determine the hardware status of the CAM */
- int slot_status = ca->slot_status(ca, slot);
- int cam_present = (slot_status & DVB_CA_SLOT_CAM_PRESENT) ? 1: 0;
- int cam_changed = (slot_status & DVB_CA_SLOT_CAM_CHANGED) ? 1: 0;
- if (!cam_changed) {
- cam_changed = (cam_present != ca->slot_info[slot].cam_present);
- }
+ (atomic_get(ca->slot_info[slot].camchange_count))) {
+ // setup the flag for when we have no CAMCHANGE IRQ, and therefore no camchange_count
+ int no_irqs_flag = 0;
+ if (!(ca->flags & DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE)) no_irqs_flag = 1;
+
+ // In IRQ mode, we keep looping until no more CAM changes are reported.
+ while(atomic_get(ca->slot_info[slot].camchange_count) || no_irqs_flag) {
+ /* determine the hardware status of the CAM */
+ int slot_status = ca->slot_status(ca, slot);
+ int cam_present_now = (slot_status & DVB_CA_SLOT_CAM_PRESENT) ? 1: 0;
+ int cam_changed = (slot_status & DVB_CA_SLOT_CAM_CHANGED) ? 1: 0;
+ if (!cam_changed) {
+ cam_changed = (cam_present_now != ca->slot_info[slot].cam_present);
+ }
- /* if cam status has changed => update */
- if (cam_changed) {
- if (cam_present) dvb_ca_en50221_cam_slot_init(ca, slot);
- else dvb_ca_en50221_cam_slot_shutdown(ca, slot);
- }
+ /* if cam status has changed => update */
+ if (cam_changed) {
+ /* clear down an old CI slot */
+ if (ca->slot_info[slot].cam_present) dvb_ca_en50221_cam_slot_shutdown(ca, slot);
+
+ /* if a CAM is NOW present, initialise it */
+ if (cam_present_now) dvb_ca_en50221_cam_slot_init(ca, slot);
+ }
+
+ /* if we don't have a CAMCHANGE IRQ, exit the loop after one iteration */
+ if (no_irqs_flag) break;
+
+ /* we've handled one change in IRQ mode; decrement the counter */
+ atomic_dec(&ca->slot_info[slot].camchange_count);
+ }
}
/* do any reading and writing necessary */
@@ -1205,19 +1240,17 @@ int dvb_ca_buf_copy_from_cam(dvb_ca* ca, u8 *data, int len, u8 last_fragment, u8
#define DVB_CA_BUF_COPY_FROM_USER_CONDITION \
({ \
- int __status; \
- \
- if ((__status = dvb_ca_slot_acquire(ca, slot)) == 0) { \
+ if ((err = dvb_ca_slot_acquire(ca, slot)) == 0) { \
cacon = dvb_ca_connection_get(ca, slot, connection_id, 1); \
if (cacon == NULL) { \
- __status = -EIO; \
+ err = -EIO; \
dvb_ca_slot_release(ca, slot); \
} else { \
- __status = dvb_ringbuffer_free(&cacon->tx_buffer) >= (count+3) \
- if (!__status) dvb_ca_slot_release(ca, slot); \
+ err = dvb_ringbuffer_free(&cacon->tx_buffer) >= (count+3) \
+ if (!err) dvb_ca_slot_release(ca, slot); \
} \
} \
- __status; \
+ err; \
})
@@ -1238,6 +1271,7 @@ static ssize_t dvb_ca_buf_copy_from_user(dvb_ca* ca, struct file *file, const ch
dvb_ca_connection* cacon;
int status;
int write_pos;
+ int err;
/* Packets in the buffer have a 3 byte header as follows:
0: MSB of total packet size.
@@ -1273,7 +1307,7 @@ static ssize_t dvb_ca_buf_copy_from_user(dvb_ca* ca, struct file *file, const ch
/* wait for some space */
dvb_ca_slot_release(ca, slot);
- if ((status = dvb_ca_wait_event_interruptible(ca->write_queue, DVB_CA_BUF_COPY_FROM_USER_CONDITION)) < 0) {
+ if ((status = wait_event_interruptible(ca->write_queue, DVB_CA_BUF_COPY_FROM_USER_CONDITION)) < 0) {
/* slot is already released if the above failed */
return status;
}
@@ -1310,22 +1344,20 @@ static ssize_t dvb_ca_buf_copy_from_user(dvb_ca* ca, struct file *file, const ch
// at the one after, to give them equal priority.
#define DVB_CA_BUF_COPY_TO_USER_CONDITION \
({ \
- int __status; \
- \
for(slot=0; slot < ca->slot_count; slot++) { \
\
if (dvb_ca_slot_acquire(ca, slot) == 0) { \
\
/* go through each connection id to see if there is data available */ \
list_for_each_entry(cacon, &ca->slot_info[slot].connections, next) { \
- __status = dvb_ringbuffer_avail(&cacon->rx_buffer) >= 3; \
- if (__status) goto completed; \
+ err = dvb_ringbuffer_avail(&cacon->rx_buffer) >= 3; \
+ if (err) goto completed; \
} \
dvb_ca_slot_release(ca, slot); \
} \
} \
completed: \
- __status; \
+ err; \
})
@@ -1348,6 +1380,7 @@ static ssize_t dvb_ca_buf_copy_to_user(dvb_ca* ca, struct file *file, char *buf,
int packet_size;
int split;
int status;
+ int err;
/* Packets in the buffer have a 3 byte header as follows:
0: MSB of total packet size.
@@ -1367,7 +1400,7 @@ static ssize_t dvb_ca_buf_copy_to_user(dvb_ca* ca, struct file *file, char *buf,
return -EWOULDBLOCK;
/* wait for some data */
- if ((status = dvb_ca_wait_event_interruptible(ca->read_queue, DVB_CA_BUF_COPY_TO_USER_CONDITION)) < 0) {
+ if ((status = wait_event_interruptible(ca->read_queue, DVB_CA_BUF_COPY_TO_USER_CONDITION)) < 0) {
/* check for errors. No need to relase slot, as we won't have one */
return status;
}
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ca.h b/linux/drivers/media/dvb/dvb-core/dvb_ca.h
index 6b1d664d1..b3e3ce2d0 100644
--- a/linux/drivers/media/dvb/dvb-core/dvb_ca.h
+++ b/linux/drivers/media/dvb/dvb-core/dvb_ca.h
@@ -34,10 +34,6 @@
#define DVB_CA_FLAG_EN50221_IRQ_CAMCHANGE 2
#define DVB_CA_FLAG_EN50221_IRQ_READ 4
-#define DVB_CA_TASK_CAM_CHANGED 1
-#define DVB_CA_TASK_CAM_READ 2
-#define DVB_CA_TASK_CAM_WRITE 4
-
/* A connection id to a CAM */
@@ -77,9 +73,9 @@ struct dvb_ca_slot {
int usage_counter; /* current usage counter of this slot */
- atomic_t camchange;
- atomic_t write;
- atomic_t read;
+ atomic_t camchange_count;
+ atomic_t write_count;
+ atomic_t read_count;
list_head connections; /* list of dvb_ca_connection, one per connection_id */
};
@@ -145,50 +141,23 @@ struct dvb_ca {
/* ******************************************************************************** */
-/* Functions for controlling access to slots */
-
-/**
- * Safely increment the usage counter for a CA slot.
- *
- * @param ca CA instance.
- * @param slot Slot concerned.
- *
- * @return 0 on success, <0 on failure.
- * */
-extern int dvb_ca_slot_acquire(struct dvb_ca* ca, int slot);
-
+/* Functions for reporting EN50221 IRQ events */
/**
- * Safely decrement the usage counter for a CA slot.
+ * A CAMCHANGE IRQ has occurred.
*
* @param ca CA instance.
* @param slot Slot concerned.
- *
- * @return 0 on success, <0 on failure.
- * */
-extern int dvb_ca_slot_release(struct dvb_ca* ca, int slot);
-
-/**
- * Acquire a slot exclusively. The slot semaphore will be left locked on successful
- * exit of this function.
- *
- * @param ca CA instance.
- * @param slot Slot concerned.
- *
- * @return 0 on success, <0 on failure.
- * */
-extern int dvb_ca_slot_acquire_exclusive(struct dvb_ca* ca, int slot);
-
+ */
+void dvb_ca_en50221_camchange_irq(struct dvb_ca* ca, int slot);
/**
- * Release an exclusively owned slot. The slot semaphore will be unlocked by this function.
+ * A READDATA IRQ has occurred.
*
* @param ca CA instance.
* @param slot Slot concerned.
- *
- * @return 0 on success, <0 on failure.
- * */
-extern int dvb_ca_slot_release_exclusive(struct dvb_ca* ca, int slot);
+ */
+void dvb_ca_en50221_readdata_irq(struct dvb_ca* ca, int slot);