net: usb: qcusbnet: Add support for poll/select
authorBenoit Goby <benoit@android.com>
Mon, 28 Feb 2011 22:27:22 +0000 (14:27 -0800)
committerBenoit Goby <benoit@android.com>
Thu, 3 Mar 2011 02:24:26 +0000 (18:24 -0800)
Change-Id: I974c3e0337b3c4eebd821919966979e670c93b6b
Signed-off-by: Benoit Goby <benoit@android.com>
drivers/net/usb/qcusbnet/qmidevice.c

index f125697f208e7e5380c102a29f1e769fc789968c..dab417ddae39c3f590bddf58158d4c909dcbd261 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "qmidevice.h"
 #include "qcusbnet.h"
+#include <linux/poll.h>
 
 struct readreq {
        struct list_head node;
@@ -39,6 +40,7 @@ struct client {
        struct list_head reads;
        struct list_head notifies;
        struct list_head urbs;
+       wait_queue_head_t read_wait;
 };
 
 struct urbsetup {
@@ -75,6 +77,7 @@ static ssize_t devqmi_read(struct file *file, char __user *buf, size_t size,
                           loff_t *pos);
 static ssize_t devqmi_write(struct file *file, const char __user *buf,
                            size_t size, loff_t *pos);
+static unsigned int devqmi_poll(struct file *file, poll_table *wait);
 
 static bool qmi_ready(struct qcusbnet *dev, u16 timeout);
 static void wds_callback(struct qcusbnet *dev, u16 cid, void *data);
@@ -95,6 +98,7 @@ static const struct file_operations devqmi_fops = {
        .unlocked_ioctl = devqmi_ioctl,
        .open  = devqmi_open,
        .flush = devqmi_close,
+       .poll  = devqmi_poll,
 };
 
 #ifdef CONFIG_SMP
@@ -200,6 +204,8 @@ static void read_callback(struct urb *urb)
                                return;
                        }
 
+                       wake_up_interruptible(&client->read_wait);
+
                        DBG("Creating new readListEntry for client 0x%04X, TID %x\n",
                            cid, tid);
 
@@ -683,6 +689,7 @@ static int client_alloc(struct qcusbnet *dev, u8 type)
        INIT_LIST_HEAD(&client->reads);
        INIT_LIST_HEAD(&client->notifies);
        INIT_LIST_HEAD(&client->urbs);
+       init_waitqueue_head(&client->read_wait);
        spin_unlock_irqrestore(&dev->qmi.clients_lock, flags);
        return cid;
 }
@@ -1224,6 +1231,49 @@ static ssize_t devqmi_write(struct file *file, const char __user * buf,
        return status;
 }
 
+static unsigned int devqmi_poll(struct file *file, poll_table *wait)
+{
+       struct qmihandle *handle = (struct qmihandle *)file->private_data;
+       struct client *client;
+       unsigned int mask = 0;
+       unsigned int flags;
+
+       if (!handle) {
+               DBG("Bad file data\n");
+               return -EBADF;
+       }
+
+       if (!device_valid(handle->dev)) {
+               DBG("Invalid device! Updating f_ops\n");
+               file->f_op = file->f_dentry->d_inode->i_fop;
+               return -ENXIO;
+       }
+
+       if (handle->cid == (u16)-1) {
+               DBG("Client ID must be set before polling 0x%04X\n",
+                         handle->cid);
+               return -EBADR;
+       }
+
+       spin_lock_irqsave(&handle->dev->qmi.clients_lock, flags);
+
+       client = client_bycid(handle->dev, handle->cid);
+       if (!client) {
+               DBG("Could not find matching client ID 0x%04X\n", handle->cid);
+               spin_unlock_irqrestore(&handle->dev->qmi.clients_lock, flags);
+               return -ENXIO;
+       }
+
+       poll_wait(file, &client->read_wait, wait);
+
+       if (!list_empty(&client->reads))
+               mask |= POLLIN | POLLRDNORM;
+
+       spin_unlock_irqrestore(&handle->dev->qmi.clients_lock, flags);
+
+       return mask;
+}
+
 int qc_register(struct qcusbnet *dev)
 {
        int result;