-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
I forgot to attach David's patch; here it is.
Anders
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.5 (SunOS)
Comment: For info see http://www.gnupg.org
iD8DBQE7BLZMWyfD6jrb5n4RAln4AKCV0uocmfser9vsWlE8NpdhmObr5wCg2fk9
67RPClSJWEJquk5NG012rjg=
=7iXA
-----END PGP SIGNATURE-----
--- linux/drivers/usb/printer.c-orig Sun May 13 14:09:35 2001
+++ linux/drivers/usb/printer.c Tue May 15 10:02:59 2001
@@ -19,6 +19,7 @@
* v0.6 - never time out
* v0.7 - fixed bulk-IN read and poll (David Paschal, [EMAIL PROTECTED])
* v0.8 - add devfs support
+ * v0.9 - fix disconnect-while-open paths
*/
/*
@@ -81,6 +82,7 @@
struct usblp {
struct usb_device *dev; /* USB device */
devfs_handle_t devfs; /* devfs device */
+ struct semaphore sem; /* locks this struct,
+especially "dev" */
struct urb readurb, writeurb; /* The urbs */
wait_queue_head_t wait; /* Zzzzz ... */
int readcount; /* Counter for reads */
@@ -237,24 +239,30 @@
return retval;
}
+static void usblp_cleanup (struct usblp *usblp)
+{
+ devfs_unregister (usblp->devfs);
+ usblp_table [usblp->minor] = NULL;
+ info ("usblp%d: removed", usblp->minor);
+
+ kfree (usblp->writeurb.transfer_buffer);
+ kfree (usblp->device_id_string);
+ kfree (usblp);
+}
+
static int usblp_release(struct inode *inode, struct file *file)
{
struct usblp *usblp = file->private_data;
+ down (&usblp->sem);
usblp->used = 0;
-
if (usblp->dev) {
if (usblp->bidir)
- usb_unlink_urb(&usblp->readurb);
- usb_unlink_urb(&usblp->writeurb);
- return 0;
- }
-
- devfs_unregister(usblp->devfs);
- usblp_table[usblp->minor] = NULL;
- kfree(usblp->device_id_string);
- kfree(usblp);
-
+ usb_unlink_urb (&usblp->readurb);
+ usb_unlink_urb (&usblp->writeurb);
+ up (&usblp->sem);
+ } else /* finish cleanup from disconnect */
+ usblp_cleanup (usblp);
return 0;
}
@@ -272,21 +280,31 @@
struct usblp *usblp = file->private_data;
int length, err;
unsigned char status;
+ int retval = 0;
+
+ down (&usblp->sem);
+ if (!usblp->dev) {
+ retval = -ENODEV;
+ goto done;
+ }
if (_IOC_TYPE(cmd) == 'P') /* new-style ioctl number */
switch (_IOC_NR(cmd)) {
case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */
- if (_IOC_DIR(cmd) != _IOC_READ)
- return -EINVAL;
+ if (_IOC_DIR(cmd) != _IOC_READ) {
+ retval = -EINVAL;
+ goto done;
+ }
err = usblp_get_id(usblp, 0, usblp->device_id_string,
DEVICE_ID_SIZE - 1);
if (err < 0) {
dbg ("usblp%d: error = %d reading IEEE-1284
Device ID string",
usblp->minor, err);
usblp->device_id_string[0] =
usblp->device_id_string[1] = '\0';
- return -EIO;
+ retval = -EIO;
+ goto done;
}
length = (usblp->device_id_string[0] << 8) +
usblp->device_id_string[1]; /* big-endian */
@@ -300,13 +318,16 @@
if (length > _IOC_SIZE(cmd)) length = _IOC_SIZE(cmd);
/* truncate */
- if (copy_to_user((unsigned char *) arg,
usblp->device_id_string, (unsigned long) length))
- return -EFAULT;
+ if (copy_to_user((unsigned char *) arg,
+ usblp->device_id_string, (unsigned
+long) length)) {
+ retval = -EFAULT;
+ goto done;
+ }
break;
default:
- return -EINVAL;
+ retval = -EINVAL;
}
else /* old-style ioctl value */
switch (cmd) {
@@ -314,17 +335,20 @@
case LPGETSTATUS:
if (usblp_read_status(usblp, &status)) {
err("usblp%d: failed reading printer status",
usblp->minor);
- return -EIO;
+ retval = -EIO;
+ goto done;
}
if (copy_to_user ((unsigned char *)arg, &status, 1))
- return -EFAULT;
+ retval = -EFAULT;
break;
default:
- return -EINVAL;
+ retval = -EINVAL;
}
- return 0;
+done:
+ up (&usblp->sem);
+ return retval;
}
static ssize_t usblp_write(struct file *file, const char *buffer, size_t count,
loff_t *ppos)
@@ -332,25 +356,39 @@
struct usblp *usblp = file->private_data;
int timeout, err = 0, writecount = 0;
+ down (&usblp->sem);
+ if (!usblp->dev) {
+ count = -ENODEV;
+ goto done;
+ }
+
while (writecount < count) {
+ // FIXME: testing urb->status outside completion
+ // callbacks is racey...
if (usblp->writeurb.status == -EINPROGRESS) {
- if (file->f_flags & O_NONBLOCK)
- return -EAGAIN;
+ if (file->f_flags & O_NONBLOCK) {
+ count = -EAGAIN;
+ goto done;
+ }
timeout = USBLP_WRITE_TIMEOUT;
while (timeout && usblp->writeurb.status == -EINPROGRESS) {
- if (signal_pending(current))
- return writecount ? writecount : -EINTR;
+ if (signal_pending(current)) {
+ count = writecount ? writecount : -EINTR;
+ goto done;
+ }
timeout = interruptible_sleep_on_timeout(&usblp->wait,
timeout);
}
}
- if (!usblp->dev)
- return -ENODEV;
+ if (!usblp->dev) {
+ count = -ENODEV;
+ goto done;
+ }
if (usblp->writeurb.status) {
if (usblp->quirks & USBLP_QUIRK_BIDIR) {
@@ -382,6 +420,8 @@
usb_submit_urb(&usblp->writeurb);
}
+done:
+ up (&usblp->sem);
return count;
}
@@ -392,20 +432,36 @@
if (!usblp->bidir)
return -EINVAL;
+ down (&usblp->sem);
+ if (!usblp->dev) {
+ count = -ENODEV;
+ goto done;
+ }
+
if (usblp->readurb.status == -EINPROGRESS) {
- if (file->f_flags & O_NONBLOCK)
- return -EAGAIN;
+ if (file->f_flags & O_NONBLOCK) {
+ count = -EAGAIN;
+ goto done;
+ }
+ // FIXME: testing urb->status outside completion
+ // callbacks is racey...
while (usblp->readurb.status == -EINPROGRESS) {
- if (signal_pending(current))
- return -EINTR;
+ if (signal_pending(current)) {
+ count = -EINTR;
+ goto done;
+ }
+ up (&usblp->sem);
interruptible_sleep_on(&usblp->wait);
+ down (&usblp->sem);
}
}
- if (!usblp->dev)
- return -ENODEV;
+ if (!usblp->dev) {
+ count = -ENODEV;
+ goto done;
+ }
if (usblp->readurb.status) {
err("usblp%d: error %d reading from printer",
@@ -413,14 +469,17 @@
usblp->readurb.dev = usblp->dev;
usblp->readcount = 0;
usb_submit_urb(&usblp->readurb);
- return -EIO;
+ count = -EIO;
+ goto done;
}
count = count < usblp->readurb.actual_length - usblp->readcount ?
count : usblp->readurb.actual_length - usblp->readcount;
- if (copy_to_user(buffer, usblp->readurb.transfer_buffer + usblp->readcount,
count))
- return -EFAULT;
+ if (copy_to_user(buffer, usblp->readurb.transfer_buffer + usblp->readcount,
+count)) {
+ count = -EFAULT;
+ goto done;
+ }
if ((usblp->readcount += count) == usblp->readurb.actual_length) {
usblp->readcount = 0;
@@ -428,6 +487,8 @@
usb_submit_urb(&usblp->readurb);
}
+done:
+ up (&usblp->sem);
return count;
}
@@ -530,6 +591,7 @@
return NULL;
}
memset(usblp, 0, sizeof(struct usblp));
+ init_MUTEX (&usblp->sem);
/* lookup quirks for this printer */
quirks = usblp_quirks(dev->descriptor.idVendor, dev->descriptor.idProduct);
@@ -592,14 +654,14 @@
sprintf(name, "lp%d", minor);
- /* Create with perms=664 */
+ /* Create with perms=660 */
usblp->devfs = devfs_register(usb_devfs_handle, name,
DEVFS_FL_DEFAULT, USB_MAJOR,
USBLP_MINOR_BASE + minor,
S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP |
S_IWGRP, &usblp_fops, NULL);
if (usblp->devfs == NULL)
- err("usblp%d: device node registration failed", minor);
+ err("usblp%d: devfs node registration failed", minor);
info("usblp%d: USB %sdirectional printer dev %d if %d alt %d",
minor, bidir ? "Bi" : "Uni", dev->devnum, ifnum, alts);
@@ -612,25 +674,21 @@
struct usblp *usblp = ptr;
if (!usblp || !usblp->dev) {
- err("disconnect on nonexisting interface");
- return;
+ err("bogus disconnect");
+ BUG ();
}
+ down (&usblp->sem);
usblp->dev = NULL;
usb_unlink_urb(&usblp->writeurb);
if (usblp->bidir)
usb_unlink_urb(&usblp->readurb);
- kfree(usblp->writeurb.transfer_buffer);
-
- if (usblp->used) return;
-
- kfree(usblp->device_id_string);
-
- devfs_unregister(usblp->devfs);
- usblp_table[usblp->minor] = NULL;
- kfree(usblp);
+ if (!usblp->used)
+ usblp_cleanup (usblp);
+ else /* cleanup later, on close */
+ up (&usblp->sem);
}
static struct usb_device_id usblp_ids [] = {