summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew de Quincy <devnull@localhost>2004-05-14 16:54:27 +0000
committerAndrew de Quincy <devnull@localhost>2004-05-14 16:54:27 +0000
commitef9a3acd7e1655a5ba1ec3d6bd8e12ed533405e8 (patch)
tree206f3174cbf95c6ca3b201ddb3a4c517d9d0cddd
parent568fc24f5c99af11dfb996ff251c4670d235143f (diff)
downloadmediapointer-dvb-s2-ef9a3acd7e1655a5ba1ec3d6bd8e12ed533405e8.tar.gz
mediapointer-dvb-s2-ef9a3acd7e1655a5ba1ec3d6bd8e12ed533405e8.tar.bz2
Fix for aston CAM read timeout problems
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c132
-rw-r--r--linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h3
2 files changed, 64 insertions, 71 deletions
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
index 106e4ab87..899c2495a 100644
--- a/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
+++ b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
@@ -34,7 +34,7 @@
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/delay.h>
-#include <asm/semaphore.h>
+#include <asm/rwsem.h>
#include <asm/atomic.h>
#include "dvb_ca_en50221.h"
@@ -108,7 +108,7 @@ struct dvb_ca_slot {
int link_buf_size;
/* semaphore for syncing access to slot structure */
- struct semaphore sem;
+ struct rw_semaphore sem;
/* buffer for incoming packets */
struct dvb_ringbuffer rx_buffer;
@@ -199,7 +199,6 @@ static u8* findstr(u8* haystack, int hlen, u8* needle, int nlen)
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;
@@ -209,9 +208,7 @@ static int dvb_ca_en50221_check_camstatus(struct dvb_ca_private* ca, int slot)
}
/* 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;
@@ -550,30 +547,27 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebu
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;
+ int buf_free;
+
+ down_read(&ca->slot_info[slot].sem);
+ buf_free = dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer);
+ up_read(&ca->slot_info[slot].sem);
+
+ if (buf_free < (ca->slot_info[slot].link_buf_size + DVB_RINGBUFFER_PKTHDRSIZE)) {
+ status = -EAGAIN;
+ goto exit;
}
}
- /* reset the interface if there's been a tx error */
+ /* check if there is data available */
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;
- }
+ /* 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;
@@ -587,19 +581,19 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebu
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;
+ 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;
+ 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;
+ goto exit;
}
}
@@ -612,29 +606,32 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private* ca, int slot, u8* ebu
buf[i] = status;
}
- /* check for read error (RE should now go to 0) */
+ /* 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) {
- status = -EIO;
- goto exit;
- }
+ 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 */
+ /* OK, add it to the receive buffer, or copy into external buffer if supplied */
if (ebuf == NULL) {
+ down_read(&ca->slot_info[slot].sem);
dvb_ringbuffer_pkt_write(&ca->slot_info[slot].rx_buffer, buf, bytes_read, 0);
+ up_read(&ca->slot_info[slot].sem);
} 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);
+ wake_up_interruptible(&ca->wait_queue);
}
-
- status = bytes_read;
+ status = bytes_read;
exit:
- up(&ca->slot_info[slot].sem);
return status;
}
@@ -662,22 +659,12 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private* ca, int slot, u8* bu
// 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 */
+ /* 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_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;
- }
+ 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;
@@ -685,10 +672,10 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private* ca, int slot, u8* bu
/* 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;
- }
+ /* 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;
@@ -702,16 +689,18 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private* ca, int slot, u8* bu
/* 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;
- }
+ 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);
exitnowrite:
- up(&ca->slot_info[slot].sem);
return status;
}
@@ -729,16 +718,14 @@ exitnowrite:
*/
static int dvb_ca_en50221_slot_shutdown(struct dvb_ca_private* ca, int slot)
{
- int status;
-
dprintk ("%s\n", __FUNCTION__);
- if ((status = down_interruptible(&ca->slot_info[slot].sem)) != 0) return status;
+ down_write(&ca->slot_info[slot].sem);
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);
+ up_write(&ca->slot_info[slot].sem);
/* need to wake up all processes to check if they're now
trying to write to a defunct CAM */
@@ -821,10 +808,7 @@ void dvb_ca_en50221_frda_irq(struct dvb_ca_en50221* pubca, int slot)
break;
case DVB_CA_SLOTSTATE_RUNNING:
- flags = ca->pub->read_cam_control(pubca, slot, CTRLIF_STATUS);
- if (flags & STATUSREG_DA) {
- dvb_ca_en50221_thread_wakeup(ca);
- }
+ if (ca->open) dvb_ca_en50221_read_data(ca, slot, NULL, 0);
break;
}
}
@@ -1053,7 +1037,11 @@ static int dvb_ca_en50221_thread(void* data)
case DVB_CA_SLOTSTATE_RUNNING:
if (!ca->open) break;
+
+ // no need to poll if the CAM supports IRQs
+ if (ca->slot_info[slot].da_irq_supported) break;
+ // poll mode
pktcount = 0;
while(dvb_ca_en50221_read_data(ca, slot, NULL, 0) > 0) {
if (!ca->open) break;
@@ -1266,7 +1254,7 @@ static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private* ca, int* resu
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;
+ down_read(&ca->slot_info[slot].sem);
idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, -1, &fraglen);
while(idx != -1) {
@@ -1281,7 +1269,7 @@ static int dvb_ca_en50221_io_read_condition(struct dvb_ca_private* ca, int* resu
idx = dvb_ringbuffer_pkt_next(&ca->slot_info[slot].rx_buffer, idx, &fraglen);
}
- if (!found) up(&ca->slot_info[slot].sem);
+ if (!found) up_read(&ca->slot_info[slot].sem);
nextslot:
slot = (slot + 1) % ca->slot_count;
@@ -1378,7 +1366,7 @@ static ssize_t dvb_ca_en50221_io_read(struct file *file, char *buf, size_t count
status = pktlen;
exit:
- up(&ca->slot_info[slot].sem);
+ up_read(&ca->slot_info[slot].sem);
return status;
}
@@ -1406,7 +1394,9 @@ static int dvb_ca_en50221_io_open(struct inode *inode, struct file *file)
for(i=0; i< ca->slot_count; i++) {
if (ca->slot_info[i].slot_state == DVB_CA_SLOTSTATE_RUNNING) {
+ down_write(&ca->slot_info[i].sem);
dvb_ringbuffer_flush(&ca->slot_info[i].rx_buffer);
+ up_write(&ca->slot_info[i].sem);
}
}
@@ -1464,7 +1454,7 @@ static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table *wait)
dprintk ("%s\n", __FUNCTION__);
if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
- up(&ca->slot_info[slot].sem);
+ up_read(&ca->slot_info[slot].sem);
mask |= POLLIN;
}
@@ -1475,7 +1465,7 @@ static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table *wait)
poll_wait(file, &ca->wait_queue, wait);
if (dvb_ca_en50221_io_read_condition(ca, &result, &slot) == 1) {
- up(&ca->slot_info[slot].sem);
+ up_read(&ca->slot_info[slot].sem);
mask |= POLLIN;
}
@@ -1559,7 +1549,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, struct dvb_ca_en50221*
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);
+ init_rwsem(&ca->slot_info[i].sem);
}
if (signal_pending(current)) {
diff --git a/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
index 8458f7afd..882ca41a2 100644
--- a/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
+++ b/linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h
@@ -42,6 +42,9 @@
/* Structure describing a CA interface */
struct dvb_ca_en50221 {
+ /* NOTE: the read_*, write_* and poll_slot_status functions must use locks as
+ * they may be called from several threads at once */
+
/* functions for accessing attribute memory on the CAM */
int (*read_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address);
int (*write_attribute_mem)(struct dvb_ca_en50221* ca, int slot, int address, u8 value);