diff options
-rw-r--r-- | linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.c | 132 | ||||
-rw-r--r-- | linux/drivers/media/dvb/dvb-core/dvb_ca_en50221.h | 3 |
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); |