diff options
Diffstat (limited to 'drivers/usb/class/usblp.c')
-rw-r--r-- | drivers/usb/class/usblp.c | 116 |
1 files changed, 70 insertions, 46 deletions
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 5192cd9356de..ad632f2d6f94 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -28,6 +28,7 @@ * v0.12 - add hpoj.sourceforge.net ioctls (David Paschal) * v0.13 - alloc space for statusbuf (<status> not on stack); * use usb_buffer_alloc() for read buf & write buf; + * none - Maintained in Linux kernel after v0.13 */ /* @@ -69,7 +70,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 @@ -115,7 +115,7 @@ MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:H #define USBLP_MINORS 16 #define USBLP_MINOR_BASE 0 -#define USBLP_WRITE_TIMEOUT (5000) /* 5 seconds */ +#define USBLP_CTL_TIMEOUT 5000 /* 5 seconds */ #define USBLP_FIRST_PROTOCOL 1 #define USBLP_LAST_PROTOCOL 3 @@ -159,10 +159,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 */ }; @@ -259,7 +261,7 @@ static int usblp_ctrl_msg(struct usblp *usblp, int request, int type, int dir, i retval = usb_control_msg(usblp->dev, dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0), - request, type | dir | recip, value, index, buf, len, USBLP_WRITE_TIMEOUT); + request, type | dir | recip, value, index, buf, len, USBLP_CTL_TIMEOUT); dbg("usblp_control_msg: rq: 0x%02x dir: %d recip: %d value: %d idx: %d len: %#x result: %d", request, !!dir, recip, value, index, len, retval); return retval < 0 ? retval : 0; @@ -325,13 +327,11 @@ 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); - /* XXX Use usb_setup_bulk_urb when available. Talk to Marcel. */ - kfree(urb->transfer_buffer); - urb->transfer_buffer = NULL; /* Not refcounted, so to be safe... */ usb_free_urb(urb); } @@ -346,16 +346,17 @@ static int usblp_check_status(struct usblp *usblp, int err) unsigned char status, newerr = 0; int error; - error = usblp_read_status (usblp, usblp->statusbuf); - if (error < 0) { + mutex_lock(&usblp->mut); + if ((error = usblp_read_status(usblp, usblp->statusbuf)) < 0) { + mutex_unlock(&usblp->mut); if (printk_ratelimit()) printk(KERN_ERR "usblp%d: error %d reading printer status\n", usblp->minor, error); return 0; } - status = *usblp->statusbuf; + mutex_unlock(&usblp->mut); if (~status & LP_PERRORP) newerr = 3; @@ -411,18 +412,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 +456,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) { @@ -485,8 +480,8 @@ static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait poll_wait(file, &usblp->rwait, 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); + ret = ((usblp->bidir && usblp->rcomplete) ? POLLIN | POLLRDNORM : 0) | + ((usblp->no_paper || usblp->wcomplete) ? POLLOUT | POLLWRNORM : 0); spin_unlock_irqrestore(&usblp->lock, flags); return ret; } @@ -675,6 +670,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; } @@ -684,10 +686,30 @@ done: return retval; } +static struct urb *usblp_new_writeurb(struct usblp *usblp, int transfer_length) +{ + struct urb *urb; + char *writebuf; + + if ((writebuf = kmalloc(transfer_length, GFP_KERNEL)) == NULL) + return NULL; + if ((urb = usb_alloc_urb(0, GFP_KERNEL)) == NULL) { + kfree(writebuf); + return NULL; + } + + usb_fill_bulk_urb(urb, usblp->dev, + usb_sndbulkpipe(usblp->dev, + usblp->protocol[usblp->current_protocol].epwrite->bEndpointAddress), + writebuf, transfer_length, usblp_bulk_write, usblp); + urb->transfer_flags |= URB_FREE_BUFFER; + + return urb; +} + static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) { struct usblp *usblp = file->private_data; - char *writebuf; struct urb *writeurb; int rv; int transfer_length; @@ -708,17 +730,11 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t transfer_length = USBLP_BUF_SIZE; rv = -ENOMEM; - if ((writebuf = kmalloc(USBLP_BUF_SIZE, GFP_KERNEL)) == NULL) - goto raise_buf; - if ((writeurb = usb_alloc_urb(0, GFP_KERNEL)) == NULL) + if ((writeurb = usblp_new_writeurb(usblp, transfer_length)) == NULL) goto raise_urb; - usb_fill_bulk_urb(writeurb, usblp->dev, - usb_sndbulkpipe(usblp->dev, - usblp->protocol[usblp->current_protocol].epwrite->bEndpointAddress), - writebuf, transfer_length, usblp_bulk_write, usblp); usb_anchor_urb(writeurb, &usblp->urbs); - if (copy_from_user(writebuf, + if (copy_from_user(writeurb->transfer_buffer, buffer + writecount, transfer_length)) { rv = -EFAULT; goto raise_badaddr; @@ -730,6 +746,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 +764,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; } @@ -771,8 +793,6 @@ raise_badaddr: usb_unanchor_urb(writeurb); usb_free_urb(writeurb); raise_urb: - kfree(writebuf); -raise_buf: raise_wait: collect_error: /* Out of raise sequence */ mutex_unlock(&usblp->wmut); @@ -838,32 +858,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); |