Hi, this is a cleanup of printer.c. It removes sleep_on and relatives, kills module removal races and fixes return values.
It compiles, but I lack the hardware to test. Regards Oliver --- printer.c.alt Thu Nov 29 16:56:25 2001 +++ printer.c Sat Dec 1 13:14:29 2001 @@ -20,6 +20,7 @@ * v0.7 - fixed bulk-IN read and poll (David Paschal, [EMAIL PROTECTED]) * v0.8 - add devfs support * v0.9 - fix unplug-while-open paths + * v0.10 - remove sleep_on and add module reference counting ([EMAIL PROTECTED]) */ /* @@ -98,11 +99,14 @@ unsigned int quirks; /* quirks flags */ unsigned char used; /* True if open */ unsigned char bidir; /* interface is bidirectional */ + unsigned char readable; /* True if data was read */ + unsigned char writeable; /* True if data was written */ unsigned char *device_id_string; /* IEEE 1284 DEVICE ID string (ptr) */ /* first 2 bytes are (big-endian) length */ }; extern devfs_handle_t usb_devfs_handle; /* /dev/usb dir. */ +static DECLARE_WAIT_QUEUE_HEAD(usblp_waiters); static struct usblp *usblp_table[USBLP_MINORS]; @@ -148,10 +152,10 @@ usblp_ctrl_msg(usblp, USBLP_REQ_RESET, USB_DIR_OUT, USB_RECIP_OTHER, 0, NULL, 0) /* - * URB callback. + * URB callbacks. */ -static void usblp_bulk(struct urb *urb) +static void usblp_read_callback(struct urb *urb) { struct usblp *usblp = urb->context; @@ -159,12 +163,28 @@ return; if (urb->status) - warn("usblp%d: nonzero read/write bulk status received: %d", + warn("usblp%d: nonzero read bulk status received: %d", usblp->minor, urb->status); + usblp->readable = 1; wake_up_interruptible(&usblp->wait); } +static void usblp_write_callback(struct urb *urb) +{ + struct usblp *usblp = urb->context; + + if (!usblp || !usblp->dev || !usblp->used) + return; + + if (urb->status) + warn("usblp%d: nonzero write bulk status received: %d", + usblp->minor, urb->status); + + usblp->writeable = 1; + wake_up_interruptible(&usblp->wait); +} + /* * Get and print printer errors. */ @@ -208,6 +228,7 @@ if (minor < 0 || minor >= USBLP_MINORS) return -ENODEV; + MOD_INC_USE_COUNT; lock_kernel(); usblp = usblp_table[minor]; @@ -238,6 +259,8 @@ usblp->writeurb.transfer_buffer_length = 0; usblp->writeurb.status = 0; + usblp->readable = 0; + usblp->writeable = 1; /* in the beginning we can write */ if (usblp->bidir) { usblp->readcount = 0; @@ -250,6 +273,8 @@ } out: unlock_kernel(); + if (retval < 0) + MOD_DEC_USE_COUNT; return retval; } @@ -279,6 +304,7 @@ } else /* finish cleanup from disconnect */ usblp_cleanup (usblp); unlock_kernel(); + MOD_DEC_USE_COUNT; return 0; } @@ -371,31 +397,46 @@ { struct usblp *usblp = file->private_data; int timeout, err = 0, writecount = 0; + DECLARE_WAITQUEUE(wait, current); while (writecount < count) { - // FIXME: only use urb->status inside completion - // callbacks; this way is racey... - if (usblp->writeurb.status == -EINPROGRESS) { + if(!usblp->dev) + return -ENODEV; + + if (!usblp->writeable) { if (file->f_flags & O_NONBLOCK) - return -EAGAIN; + return writecount ? writecount : -EAGAIN; timeout = USBLP_WRITE_TIMEOUT; - while (timeout && usblp->writeurb.status == -EINPROGRESS) { - - if (signal_pending(current)) - return writecount ? writecount : -EINTR; - timeout = interruptible_sleep_on_timeout(&usblp->wait, timeout); + add_wait_queue(&usblp_waiters, &wait); + for (;;) { + if (!timeout) { + remove_wait_queue(&usblp_waiters, &wait); + return writecount ? writecount : -EIO; + } + + set_current_state(TASK_INTERRUPTIBLE); + + if (!usblp->writeable) { + if (signal_pending(current)) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&usblp_waiters, &wait); + return writecount ? writecount : +-EINTR; + } + + timeout = schedule_timeout(timeout); + } else { + break; /* We can now write */ + } } + remove_wait_queue(&usblp_waiters, &wait); + set_current_state(TASK_RUNNING); } down (&usblp->sem); - if (!usblp->dev) { - up (&usblp->sem); - return -ENODEV; - } if (usblp->writeurb.status != 0) { if (usblp->quirks & USBLP_QUIRK_BIDIR) { @@ -428,8 +469,10 @@ if (copy_from_user(usblp->writeurb.transfer_buffer, buffer + writecount, usblp->writeurb.transfer_buffer_length)) return -EFAULT; + usblp->writeable = 0; usblp->writeurb.dev = usblp->dev; - usb_submit_urb(&usblp->writeurb); + if (0 > usb_submit_urb(&usblp->writeurb)) + count = -EIO; up (&usblp->sem); } @@ -439,6 +482,7 @@ static ssize_t usblp_read(struct file *file, char *buffer, size_t count, loff_t *ppos) { struct usblp *usblp = file->private_data; + DECLARE_WAITQUEUE(wait, current); if (!usblp->bidir) return -EINVAL; @@ -449,30 +493,38 @@ goto done; } - if (usblp->readurb.status == -EINPROGRESS) { + if (!usblp->readable) { if (file->f_flags & O_NONBLOCK) { count = -EAGAIN; goto done; } - // FIXME: only use urb->status inside completion - // callbacks; this way is racey... - while (usblp->readurb.status == -EINPROGRESS) { + add_wait_queue(&usblp_waiters, &wait); + for (;;) { if (signal_pending(current)) { count = -EINTR; + remove_wait_queue(&usblp_waiters, &wait); goto done; } - up (&usblp->sem); - interruptible_sleep_on(&usblp->wait); - down (&usblp->sem); + if (!usblp->dev) { /* We drop the lock below, thus we need to recheck */ + count = -ENODEV; + remove_wait_queue(&usblp_waiters, &wait); + goto done; + } + set_current_state(TASK_INTERRUPTIBLE); + if (!usblp->readable) { + up (&usblp->sem); + schedule(); + down (&usblp->sem); + } else { + break; + } } + remove_wait_queue(&usblp_waiters, &wait); + set_current_state(TASK_RUNNING); } - if (!usblp->dev) { - count = -ENODEV; - goto done; - } if (usblp->readurb.status) { err("usblp%d: error %d reading from printer", @@ -495,6 +547,7 @@ if ((usblp->readcount += count) == usblp->readurb.actual_length) { usblp->readcount = 0; usblp->readurb.dev = usblp->dev; + usblp->readable = 0; usb_submit_urb(&usblp->readurb); } @@ -554,6 +607,8 @@ char *buf; char name[6]; + MOD_INC_USE_COUNT; + /* If a bidirectional interface exists, use it. */ for (i = 0; i < dev->actconfig->interface[ifnum].num_altsetting; i++) { @@ -580,26 +635,26 @@ if ((epwrite->bEndpointAddress & 0x80) == 0x80) { if (interface->bNumEndpoints == 1) - return NULL; + goto err_out; epwrite = interface->endpoint + 1; epread = bidir ? interface->endpoint + 0 : NULL; } if ((epwrite->bEndpointAddress & 0x80) == 0x80) - return NULL; + goto err_out; if (bidir && (epread->bEndpointAddress & 0x80) != 0x80) - return NULL; + goto err_out; for (minor = 0; minor < USBLP_MINORS && usblp_table[minor]; minor++); if (usblp_table[minor]) { err("no more free usblp devices"); - return NULL; + goto err_out; } if (!(usblp = kmalloc(sizeof(struct usblp), GFP_KERNEL))) { err("out of memory"); - return NULL; + goto err_out; } memset(usblp, 0, sizeof(struct usblp)); init_MUTEX (&usblp->sem); @@ -625,22 +680,22 @@ if (!(buf = kmalloc(USBLP_BUF_SIZE * (bidir ? 2 : 1), GFP_KERNEL))) { err("out of memory"); kfree(usblp); - return NULL; + goto err_out; } if (!(usblp->device_id_string = kmalloc(DEVICE_ID_SIZE, GFP_KERNEL))) { err("out of memory"); kfree(usblp); kfree(buf); - return NULL; + goto err_out; } FILL_BULK_URB(&usblp->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), - buf, 0, usblp_bulk, usblp); + buf, 0, usblp_write_callback, usblp); if (bidir) FILL_BULK_URB(&usblp->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), - buf + USBLP_BUF_SIZE, USBLP_BUF_SIZE, usblp_bulk, usblp); + buf + USBLP_BUF_SIZE, USBLP_BUF_SIZE, usblp_read_callback, +usblp); /* Get the device_id string if possible. FIXME: Could make this kmalloc(length). */ err = usblp_get_id(usblp, 0, usblp->device_id_string, DEVICE_ID_SIZE - 1); @@ -675,7 +730,12 @@ info("usblp%d: USB %sdirectional printer dev %d if %d alt %d", minor, bidir ? "Bi" : "Uni", dev->devnum, ifnum, alts); + MOD_DEC_USE_COUNT; return usblp_table[minor] = usblp; + +err_out: + MOD_DEC_USE_COUNT; + return NULL; } static void usblp_disconnect(struct usb_device *dev, void *ptr) @@ -687,6 +747,7 @@ BUG (); } + MOD_INC_USE_COUNT; down (&usblp->sem); lock_kernel(); usblp->dev = NULL; @@ -700,6 +761,7 @@ else /* cleanup later, on close */ up (&usblp->sem); unlock_kernel(); + MOD_DEC_USE_COUNT; } static struct usb_device_id usblp_ids [] = { _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel