usb: usbmon: Read text within supplied buffer size
[pandora-kernel.git] / drivers / usb / mon / mon_text.c
index 682bf77..108dcc5 100644 (file)
@@ -82,6 +82,8 @@ struct mon_reader_text {
 
        wait_queue_head_t wait;
        int printf_size;
+       size_t printf_offset;
+       size_t printf_togo;
        char *printf_buf;
        struct mutex printf_lock;
 
@@ -373,75 +375,103 @@ err_alloc:
        return rc;
 }
 
-/*
- * For simplicity, we read one record in one system call and throw out
- * what does not fit. This means that the following does not work:
- *   dd if=/dbg/usbmon/0t bs=10
- * Also, we do not allow seeks and do not bother advancing the offset.
- */
+static ssize_t mon_text_copy_to_user(struct mon_reader_text *rp,
+    char __user * const buf, const size_t nbytes)
+{
+       const size_t togo = min(nbytes, rp->printf_togo);
+
+       if (copy_to_user(buf, &rp->printf_buf[rp->printf_offset], togo))
+               return -EFAULT;
+       rp->printf_togo -= togo;
+       rp->printf_offset += togo;
+       return togo;
+}
+
+/* ppos is not advanced since the llseek operation is not permitted. */
 static ssize_t mon_text_read_t(struct file *file, char __user *buf,
-                               size_t nbytes, loff_t *ppos)
+    size_t nbytes, loff_t *ppos)
 {
        struct mon_reader_text *rp = file->private_data;
        struct mon_event_text *ep;
        struct mon_text_ptr ptr;
+       ssize_t ret;
 
-       ep = mon_text_read_wait(rp, file);
-       if (IS_ERR(ep))
-               return PTR_ERR(ep);
        mutex_lock(&rp->printf_lock);
-       ptr.cnt = 0;
-       ptr.pbuf = rp->printf_buf;
-       ptr.limit = rp->printf_size;
-
-       mon_text_read_head_t(rp, &ptr, ep);
-       mon_text_read_statset(rp, &ptr, ep);
-       ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
-           " %d", ep->length);
-       mon_text_read_data(rp, &ptr, ep);
-
-       if (copy_to_user(buf, rp->printf_buf, ptr.cnt))
-               ptr.cnt = -EFAULT;
+
+       if (rp->printf_togo == 0) {
+
+               ep = mon_text_read_wait(rp, file);
+               if (IS_ERR(ep)) {
+                       mutex_unlock(&rp->printf_lock);
+                       return PTR_ERR(ep);
+               }
+               ptr.cnt = 0;
+               ptr.pbuf = rp->printf_buf;
+               ptr.limit = rp->printf_size;
+
+               mon_text_read_head_t(rp, &ptr, ep);
+               mon_text_read_statset(rp, &ptr, ep);
+               ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
+                   " %d", ep->length);
+               mon_text_read_data(rp, &ptr, ep);
+
+               rp->printf_togo = ptr.cnt;
+               rp->printf_offset = 0;
+
+               kmem_cache_free(rp->e_slab, ep);
+       }
+
+       ret = mon_text_copy_to_user(rp, buf, nbytes);
        mutex_unlock(&rp->printf_lock);
-       kmem_cache_free(rp->e_slab, ep);
-       return ptr.cnt;
+       return ret;
 }
 
+/* ppos is not advanced since the llseek operation is not permitted. */
 static ssize_t mon_text_read_u(struct file *file, char __user *buf,
-                               size_t nbytes, loff_t *ppos)
+    size_t nbytes, loff_t *ppos)
 {
        struct mon_reader_text *rp = file->private_data;
        struct mon_event_text *ep;
        struct mon_text_ptr ptr;
+       ssize_t ret;
 
-       ep = mon_text_read_wait(rp, file);
-       if (IS_ERR(ep))
-               return PTR_ERR(ep);
        mutex_lock(&rp->printf_lock);
-       ptr.cnt = 0;
-       ptr.pbuf = rp->printf_buf;
-       ptr.limit = rp->printf_size;
 
-       mon_text_read_head_u(rp, &ptr, ep);
-       if (ep->type == 'E') {
-               mon_text_read_statset(rp, &ptr, ep);
-       } else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) {
-               mon_text_read_isostat(rp, &ptr, ep);
-               mon_text_read_isodesc(rp, &ptr, ep);
-       } else if (ep->xfertype == USB_ENDPOINT_XFER_INT) {
-               mon_text_read_intstat(rp, &ptr, ep);
-       } else {
-               mon_text_read_statset(rp, &ptr, ep);
+       if (rp->printf_togo == 0) {
+
+               ep = mon_text_read_wait(rp, file);
+               if (IS_ERR(ep)) {
+                       mutex_unlock(&rp->printf_lock);
+                       return PTR_ERR(ep);
+               }
+               ptr.cnt = 0;
+               ptr.pbuf = rp->printf_buf;
+               ptr.limit = rp->printf_size;
+
+               mon_text_read_head_u(rp, &ptr, ep);
+               if (ep->type == 'E') {
+                       mon_text_read_statset(rp, &ptr, ep);
+               } else if (ep->xfertype == USB_ENDPOINT_XFER_ISOC) {
+                       mon_text_read_isostat(rp, &ptr, ep);
+                       mon_text_read_isodesc(rp, &ptr, ep);
+               } else if (ep->xfertype == USB_ENDPOINT_XFER_INT) {
+                       mon_text_read_intstat(rp, &ptr, ep);
+               } else {
+                       mon_text_read_statset(rp, &ptr, ep);
+               }
+               ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
+                   " %d", ep->length);
+               mon_text_read_data(rp, &ptr, ep);
+
+               rp->printf_togo = ptr.cnt;
+               rp->printf_offset = 0;
+
+               kmem_cache_free(rp->e_slab, ep);
        }
-       ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
-           " %d", ep->length);
-       mon_text_read_data(rp, &ptr, ep);
 
-       if (copy_to_user(buf, rp->printf_buf, ptr.cnt))
-               ptr.cnt = -EFAULT;
+       ret = mon_text_copy_to_user(rp, buf, nbytes);
        mutex_unlock(&rp->printf_lock);
-       kmem_cache_free(rp->e_slab, ep);
-       return ptr.cnt;
+       return ret;
 }
 
 static struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp,