summaryrefslogtreecommitdiffstats
path: root/drivers/usb/class/usblp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/class/usblp.c')
-rw-r--r--drivers/usb/class/usblp.c116
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);
OpenPOWER on IntegriCloud