Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=7f477358e2384c54b190cc3b6ce28277050a041b
Commit:     7f477358e2384c54b190cc3b6ce28277050a041b
Parent:     ca337db6f92a32347172a20b96f6f51b7bb63b7c
Author:     Pete Zaitcev <[EMAIL PROTECTED]>
AuthorDate: Mon Jul 23 01:58:15 2007 -0700
Committer:  Greg Kroah-Hartman <[EMAIL PROTECTED]>
CommitDate: Fri Oct 12 14:54:58 2007 -0700

    usblp: Implement the ENOSPC convention
    
    This patch implements a mode when a printer returns ENOSPC when it runs
    out of paper. The default remains the same as before. An application which
    wishes to use this function has to enable it explicitly with an ioctl
    LPABORT.
    
    This is done on a request by our (Fedora) CUPS guy, Tim Waugh. The API is
    similar enough to the lp0's one that CUPS works with both (but see below),
    but it's has some differences.
    
    Most importantly, the abort mode is persistent in case of lp0: once tunelp
    was run your cat fill blow up until you reboot or run tunelp again. For
    usblp, I made it so the abort mode is only in effect as long as device
    is open. This way you can mix and match CUPS and cat(1) freely and nothing
    bad happens even if you run out of paper. It is also safer in the face
    of any unexpected crashes.
    
    It has to be noted that mixing LPABORT and O_NONBLOCK is not advised.
    It probably does not do what you want: instead of returning -ENOSPC
    it will always return -EAGAIN (because it would otherwise block while
    waiting for the paper). Applications which use O_NONBLOCK should continue
    to use LPGETSTATUS like before.
    
    Finally, CUPS actually requires patching to take full advantage of this.
    It has several components; those which invoke LPABORT work, but some of
    them need the ioctl added. This is completely compatible, you can mix
    old CUPS and new kernels or vice versa.
    
    Signed-off-by: Pete Zaitcev <[EMAIL PROTECTED]>
    Signed-off-by: Greg Kroah-Hartman <[EMAIL PROTECTED]>
---
 drivers/usb/class/usblp.c |   65 +++++++++++++++++++++++++++------------------
 1 files changed, 39 insertions(+), 26 deletions(-)

diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index 5192cd9..30d5a13 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -69,7 +69,6 @@
 #define USBLP_DEVICE_ID_SIZE   1024
 
 /* ioctls: */
-#define LPGETSTATUS            0x060b          /* same as in drivers/char/lp.c 
*/
 #define IOCNR_GET_DEVICE_ID            1
 #define IOCNR_GET_PROTOCOLS            2
 #define IOCNR_SET_PROTOCOL             3
@@ -159,10 +158,12 @@ struct usblp {
        int                     wstatus;        /* bytes written or error */
        int                     rstatus;        /* bytes ready or error */
        unsigned int            quirks;                 /* quirks flags */
+       unsigned int            flags;                  /* mode flags */
        unsigned char           used;                   /* True if open */
        unsigned char           present;                /* True if not 
disconnected */
        unsigned char           bidir;                  /* interface is 
bidirectional */
        unsigned char           sleeping;               /* interface is 
suspended */
+       unsigned char           no_paper;               /* Paper Out happened */
        unsigned char           *device_id_string;      /* IEEE 1284 DEVICE ID 
string (ptr) */
                                                        /* first 2 bytes are 
(big-endian) length */
 };
@@ -325,6 +326,7 @@ static void usblp_bulk_write(struct urb *urb)
                usblp->wstatus = status;
        else
                usblp->wstatus = urb->actual_length;
+       usblp->no_paper = 0;
        usblp->wcomplete = 1;
        wake_up(&usblp->wwait);
        spin_unlock(&usblp->lock);
@@ -411,18 +413,10 @@ static int usblp_open(struct inode *inode, struct file 
*file)
                goto out;
 
        /*
-        * TODO: need to implement LP_ABORTOPEN + O_NONBLOCK as in 
drivers/char/lp.c ???
-        * This is #if 0-ed because we *don't* want to fail an open
-        * just because the printer is off-line.
+        * We do not implement LP_ABORTOPEN/LPABORTOPEN for two reasons:
+        *  - We do not want persistent state which close(2) does not clear
+        *  - It is not used anyway, according to CUPS people
         */
-#if 0
-       if ((retval = usblp_check_status(usblp, 0))) {
-               retval = retval > 1 ? -EIO : -ENOSPC;
-               goto out;
-       }
-#else
-       retval = 0;
-#endif
 
        retval = usb_autopm_get_interface(intf);
        if (retval < 0)
@@ -463,6 +457,8 @@ static int usblp_release(struct inode *inode, struct file 
*file)
 {
        struct usblp *usblp = file->private_data;
 
+       usblp->flags &= ~LP_ABORT;
+
        mutex_lock (&usblp_mutex);
        usblp->used = 0;
        if (usblp->present) {
@@ -486,7 +482,7 @@ static unsigned int usblp_poll(struct file *file, struct 
poll_table_struct *wait
        poll_wait(file, &usblp->wwait, wait);
        spin_lock_irqsave(&usblp->lock, flags);
        ret = ((!usblp->bidir || !usblp->rcomplete) ? 0 : POLLIN  | POLLRDNORM)
-                              | (!usblp->wcomplete ? 0 : POLLOUT | POLLWRNORM);
+          | ((usblp->no_paper || usblp->wcomplete) ? POLLOUT | POLLWRNORM : 0);
        spin_unlock_irqrestore(&usblp->lock, flags);
        return ret;
 }
@@ -675,6 +671,13 @@ static long usblp_ioctl(struct file *file, unsigned int 
cmd, unsigned long arg)
                                        retval = -EFAULT;
                                break;
 
+                       case LPABORT:
+                               if (arg)
+                                       usblp->flags |= LP_ABORT;
+                               else
+                                       usblp->flags &= ~LP_ABORT;
+                               break;
+
                        default:
                                retval = -ENOTTY;
                }
@@ -730,6 +733,7 @@ static ssize_t usblp_write(struct file *file, const char 
__user *buffer, size_t
                if ((rv = usb_submit_urb(writeurb, GFP_KERNEL)) < 0) {
                        usblp->wstatus = 0;
                        spin_lock_irq(&usblp->lock);
+                       usblp->no_paper = 0;
                        usblp->wcomplete = 1;
                        wake_up(&usblp->wwait);
                        spin_unlock_irq(&usblp->lock);
@@ -747,12 +751,17 @@ static ssize_t usblp_write(struct file *file, const char 
__user *buffer, size_t
                                /* Presume that it's going to complete well. */
                                writecount += transfer_length;
                        }
+                       if (rv == -ENOSPC) {
+                               spin_lock_irq(&usblp->lock);
+                               usblp->no_paper = 1;    /* Mark for poll(2) */
+                               spin_unlock_irq(&usblp->lock);
+                               writecount += transfer_length;
+                       }
                        /* Leave URB dangling, to be cleaned on close. */
                        goto collect_error;
                }
 
                if (usblp->wstatus < 0) {
-                       usblp_check_status(usblp, 0);
                        rv = -EIO;
                        goto collect_error;
                }
@@ -838,32 +847,36 @@ done:
  * when O_NONBLOCK is set. So, applications setting O_NONBLOCK must use
  * select(2) or poll(2) to wait for the buffer to drain before closing.
  * Alternatively, set blocking mode with fcntl and issue a zero-size write.
- *
- * Old v0.13 code had a non-functional timeout for wait_event(). Someone forgot
- * to check the return code for timeout expiration, so it had no effect.
- * Apparently, it was intended to check for error conditons, such as out
- * of paper. It is going to return when we settle things with CUPS. XXX
  */
 static int usblp_wwait(struct usblp *usblp, int nonblock)
 {
        DECLARE_WAITQUEUE(waita, current);
        int rc;
+       int err = 0;
 
        add_wait_queue(&usblp->wwait, &waita);
        for (;;) {
+               set_current_state(TASK_INTERRUPTIBLE);
                if (mutex_lock_interruptible(&usblp->mut)) {
                        rc = -EINTR;
                        break;
                }
-               set_current_state(TASK_INTERRUPTIBLE);
-               if ((rc = usblp_wtest(usblp, nonblock)) < 0) {
-                       mutex_unlock(&usblp->mut);
-                       break;
-               }
+               rc = usblp_wtest(usblp, nonblock);
                mutex_unlock(&usblp->mut);
-               if (rc == 0)
+               if (rc <= 0)
                        break;
-               schedule();
+
+               if (usblp->flags & LP_ABORT) {
+                       if (schedule_timeout(msecs_to_jiffies(5000)) == 0) {
+                               err = usblp_check_status(usblp, err);
+                               if (err == 1) { /* Paper out */
+                                       rc = -ENOSPC;
+                                       break;
+                               }
+                       }
+               } else {
+                       schedule();
+               }
        }
        set_current_state(TASK_RUNNING);
        remove_wait_queue(&usblp->wwait, &waita);
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to