Merge branch 'llseek' of git://git.kernel.org/pub/scm/linux/kernel/git/arnd/bkl
[linux-2.6.git] / drivers / media / dvb / dvb-core / dvb_ca_en50221.c
index 98ee167..4d0646d 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/delay.h>
 #include <linux/spinlock.h>
 #include <linux/sched.h>
+#include <linux/smp_lock.h>
 #include <linux/kthread.h>
 
 #include "dvb_ca_en50221.h"
@@ -93,6 +94,9 @@ struct dvb_ca_slot {
        /* current state of the CAM */
        int slot_state;
 
+       /* mutex used for serializing access to one CI slot */
+       struct mutex slot_lock;
+
        /* Number of CAMCHANGES that have occurred since last processing */
        atomic_t camchange_count;
 
@@ -711,14 +715,20 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * b
        dprintk("%s\n", __func__);
 
 
-       // sanity check
+       /* 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 */
+       /* it is possible we are dealing with a single buffer implementation,
+          thus if there is data available for read or if there is even a read
+          already in progress, we do nothing but awake the kernel thread to
+          process the data if necessary. */
        if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0)
                goto exitnowrite;
        if (status & (STATUSREG_DA | STATUSREG_RE)) {
+               if (status & STATUSREG_DA)
+                       dvb_ca_en50221_thread_wakeup(ca);
+
                status = -EAGAIN;
                goto exitnowrite;
        }
@@ -987,6 +997,8 @@ static int dvb_ca_en50221_thread(void *data)
                /* go through all the slots processing them */
                for (slot = 0; slot < ca->slot_count; slot++) {
 
+                       mutex_lock(&ca->slot_info[slot].slot_lock);
+
                        // check the cam status + deal with CAMCHANGEs
                        while (dvb_ca_en50221_check_camstatus(ca, slot)) {
                                /* clear down an old CI slot if necessary */
@@ -1122,7 +1134,7 @@ static int dvb_ca_en50221_thread(void *data)
 
                        case DVB_CA_SLOTSTATE_RUNNING:
                                if (!ca->open)
-                                       continue;
+                                       break;
 
                                // poll slots for data
                                pktcount = 0;
@@ -1146,6 +1158,8 @@ static int dvb_ca_en50221_thread(void *data)
                                }
                                break;
                        }
+
+                       mutex_unlock(&ca->slot_info[slot].slot_lock);
                }
        }
 
@@ -1168,7 +1182,7 @@ 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,
+static int dvb_ca_en50221_io_do_ioctl(struct file *file,
                                      unsigned int cmd, void *parg)
 {
        struct dvb_device *dvbdev = file->private_data;
@@ -1181,6 +1195,7 @@ static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file,
        switch (cmd) {
        case CA_RESET:
                for (slot = 0; slot < ca->slot_count; slot++) {
+                       mutex_lock(&ca->slot_info[slot].slot_lock);
                        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)
@@ -1188,6 +1203,7 @@ static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file,
                                                                     slot,
                                                                     DVB_CA_EN50221_CAMCHANGE_INSERTED);
                        }
+                       mutex_unlock(&ca->slot_info[slot].slot_lock);
                }
                ca->next_read_slot = 0;
                dvb_ca_en50221_thread_wakeup(ca);
@@ -1240,10 +1256,10 @@ static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file,
  *
  * @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 long dvb_ca_en50221_io_ioctl(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(file, cmd, arg, dvb_ca_en50221_io_do_ioctl);
 }
 
 
@@ -1296,8 +1312,11 @@ static ssize_t dvb_ca_en50221_io_write(struct file *file,
 
                fragbuf[0] = connection_id;
                fragbuf[1] = ((fragpos + fraglen) < count) ? 0x80 : 0x00;
-               if ((status = copy_from_user(fragbuf + 2, buf + fragpos, fraglen)) != 0)
+               status = copy_from_user(fragbuf + 2, buf + fragpos, fraglen);
+               if (status) {
+                       status = -EFAULT;
                        goto exit;
+               }
 
                timeout = jiffies + HZ / 2;
                written = 0;
@@ -1308,7 +1327,9 @@ static ssize_t dvb_ca_en50221_io_write(struct file *file,
                                goto exit;
                        }
 
+                       mutex_lock(&ca->slot_info[slot].slot_lock);
                        status = dvb_ca_en50221_write_data(ca, slot, fragbuf, fraglen + 2);
+                       mutex_unlock(&ca->slot_info[slot].slot_lock);
                        if (status == (fraglen + 2)) {
                                written = 1;
                                break;
@@ -1470,8 +1491,11 @@ static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user * buf,
 
        hdr[0] = slot;
        hdr[1] = connection_id;
-       if ((status = copy_to_user(buf, hdr, 2)) != 0)
+       status = copy_to_user(buf, hdr, 2);
+       if (status) {
+               status = -EFAULT;
                goto exit;
+       }
        status = pktlen;
 
 exit:
@@ -1590,14 +1614,15 @@ static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table * wait)
 EXPORT_SYMBOL(dvb_ca_en50221_init);
 
 
-static struct file_operations dvb_ca_fops = {
+static const 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,
+       .unlocked_ioctl = dvb_ca_en50221_io_ioctl,
        .open = dvb_ca_en50221_io_open,
        .release = dvb_ca_en50221_io_release,
        .poll = dvb_ca_en50221_io_poll,
+       .llseek = noop_llseek,
 };
 
 static struct dvb_device dvbdev_ca = {
@@ -1664,6 +1689,7 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
                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;
+               mutex_init(&ca->slot_info[i].slot_lock);
        }
 
        if (signal_pending(current)) {