Hi,
here is the improved patch.
On module unloading: probe and disconnect run under BKL, thus should be
safe.
Regards
Oliver
--- printer.c.alt Thu Nov 29 16:56:25 2001
+++ printer.c Sun Dec 2 01:46:40 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,6 +99,8 @@
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 */
};
@@ -148,10 +151,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 +162,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 +227,7 @@
if (minor < 0 || minor >= USBLP_MINORS)
return -ENODEV;
+ MOD_INC_USE_COUNT;
lock_kernel();
usblp = usblp_table[minor];
@@ -238,6 +258,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 +272,8 @@
}
out:
unlock_kernel();
+ if (retval < 0)
+ MOD_DEC_USE_COUNT;
return retval;
}
@@ -279,6 +303,7 @@
} else /* finish cleanup from disconnect */
usblp_cleanup (usblp);
unlock_kernel();
+ MOD_DEC_USE_COUNT;
return 0;
}
@@ -371,31 +396,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->wait, &wait);
+ for (;;) {
+ if (!timeout) {
+ remove_wait_queue(&usblp->wait, &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->wait,
+&wait);
+ return writecount ? writecount :
+-EINTR;
+ }
+
+ timeout = schedule_timeout(timeout);
+ } else {
+ break; /* We can now write */
+ }
}
+ remove_wait_queue(&usblp->wait, &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 +468,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 +481,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 +492,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->wait, &wait);
+ for (;;) {
if (signal_pending(current)) {
count = -EINTR;
+ remove_wait_queue(&usblp->wait, &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->wait, &wait);
+ goto done;
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!usblp->readable) {
+ up (&usblp->sem);
+ schedule();
+ down (&usblp->sem);
+ } else {
+ break;
+ }
}
+ remove_wait_queue(&usblp->wait, &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 +546,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 +606,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 +634,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 +679,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 +729,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 +746,7 @@
BUG ();
}
+ MOD_INC_USE_COUNT;
down (&usblp->sem);
lock_kernel();
usblp->dev = NULL;
@@ -700,6 +760,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