-----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 [] = {