--- printer.c-orig	Sun May 13 12:24:21 2001
+++ printer.c	Sun May 13 13:03:04 2001
@@ -81,6 +81,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 */
@@ -243,18 +244,13 @@
 
 	usblp->used = 0;
 
+	down (&usblp->sem);
 	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);
-
+	up (&usblp->sem);
 	return 0;
 }
 
@@ -272,21 +268,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 +306,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,16 +323,19 @@
 			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;
 		}
 
+done:
+	up (&usblp->sem);
 	return 0;
 }
 
@@ -332,25 +344,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 +408,8 @@
 		usb_submit_urb(&usblp->writeurb);
 	}
 
+done:
+	up (&usblp->sem);
 	return count;
 }
 
@@ -392,20 +420,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 +457,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 +475,8 @@
 		usb_submit_urb(&usblp->readurb);
 	}
 
+done:
+	up (&usblp->sem);
 	return count;
 }
 
@@ -530,6 +579,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);
@@ -612,10 +662,11 @@
 	struct usblp *usblp = ptr;
 
 	if (!usblp || !usblp->dev) {
-		err("disconnect on nonexisting interface");
-		return;
+		err("disconnect on disconnected interface");
+		BUG ();
 	}
 
+	down (&usblp->sem);
 	usblp->dev = NULL;
 
 	usb_unlink_urb(&usblp->writeurb);
