summaryrefslogtreecommitdiffstats
path: root/drivers/usb/dc2xx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/dc2xx.c')
-rw-r--r--drivers/usb/dc2xx.c134
1 files changed, 80 insertions, 54 deletions
diff --git a/drivers/usb/dc2xx.c b/drivers/usb/dc2xx.c
index bb960a4af..67a55ab46 100644
--- a/drivers/usb/dc2xx.c
+++ b/drivers/usb/dc2xx.c
@@ -43,6 +43,7 @@
* added timeouts to bulk_msg calls. Minor updates, docs.
* 03 Nov, 1999 -- update for 2.3.25 kernel API changes.
* 08 Jan, 2000 .. multiple camera support
+ * 12 Aug, 2000 .. add some real locking, remove an Oops
*
* Thanks to: the folk who've provided USB product IDs, sent in
* patches, and shared their sucesses!
@@ -60,7 +61,6 @@
#include <linux/module.h>
#undef DEBUG
#include <linux/usb.h>
-#include <linux/smp_lock.h>
@@ -114,7 +114,7 @@ struct camera_state {
int outEP; /* write endpoint */
const struct camera *info; /* DC-240, etc */
int subminor; /* which minor dev #? */
- int isActive; /* I/O taking place? */
+ struct semaphore sem; /* locks this struct */
/* this is non-null iff the device is open */
char *buf; /* buffer for I/O */
@@ -127,21 +127,25 @@ struct camera_state {
/* Support multiple cameras, possibly of different types. */
static struct camera_state *minor_data [MAX_CAMERAS];
+/* make this an rwlock if contention becomes an issue */
+static DECLARE_MUTEX (state_table_mutex);
static ssize_t camera_read (struct file *file,
char *buf, size_t len, loff_t *ppos)
{
struct camera_state *camera;
int retries;
+ int retval = 0;
if (len > MAX_PACKET_SIZE)
return -EINVAL;
camera = (struct camera_state *) file->private_data;
- if (!camera->dev)
+ down (&camera->sem);
+ if (!camera->dev) {
+ up (&camera->sem);
return -ENODEV;
- if (camera->isActive++)
- return -EBUSY;
+ }
/* Big reads are common, for image downloading. Smaller ones
* are also common (even "directory listing" commands don't
@@ -150,37 +154,33 @@ static ssize_t camera_read (struct file *file,
*/
for (retries = 0; retries < MAX_READ_RETRY; retries++) {
int count;
- int result;
if (signal_pending (current)) {
- camera->isActive = 0;
- return -EINTR;
- }
- if (!camera->dev) {
- camera->isActive = 0;
- return -ENODEV;
+ retval = -EINTR;
+ break;
}
- result = usb_bulk_msg (camera->dev,
+ retval = usb_bulk_msg (camera->dev,
usb_rcvbulkpipe (camera->dev, camera->inEP),
camera->buf, len, &count, HZ*10);
- dbg ("read (%d) - 0x%x %d", len, result, count);
+ dbg ("read (%d) - 0x%x %d", len, retval, count);
- if (!result) {
+ if (!retval) {
if (copy_to_user (buf, camera->buf, count))
- return -EFAULT;
- camera->isActive = 0;
- return count;
+ retval = -EFAULT;
+ else
+ retval = count;
+ break;
}
- if (result != USB_ST_TIMEOUT)
+ if (retval != USB_ST_TIMEOUT)
break;
interruptible_sleep_on_timeout (&camera->wait, RETRY_TIMEOUT);
dbg ("read (%d) - retry", len);
}
- camera->isActive = 0;
- return -EIO;
+ up (&camera->sem);
+ return retval;
}
static ssize_t camera_write (struct file *file,
@@ -193,10 +193,11 @@ static ssize_t camera_write (struct file *file,
return -EINVAL;
camera = (struct camera_state *) file->private_data;
- if (!camera->dev)
+ down (&camera->sem);
+ if (!camera->dev) {
+ up (&camera->sem);
return -ENODEV;
- if (camera->isActive++)
- return -EBUSY;
+ }
/* most writes will be small: simple commands, sometimes with
* parameters. putting images (like borders) into the camera
@@ -224,18 +225,13 @@ static ssize_t camera_write (struct file *file,
bytes_written = -EINTR;
goto done;
}
- if (!camera->dev) {
- if (!bytes_written)
- bytes_written = -ENODEV;
- goto done;
- }
result = usb_bulk_msg (camera->dev,
usb_sndbulkpipe (camera->dev, camera->outEP),
obuf, thistime, &count, HZ*10);
if (result)
- dbg ("write USB err - %x", result);
+ dbg ("write USB err - %d", result);
if (count) {
obuf += count;
@@ -264,49 +260,69 @@ static ssize_t camera_write (struct file *file,
buf += copy_size;
}
done:
- camera->isActive = 0;
+ up (&camera->sem);
dbg ("wrote %d", bytes_written);
return bytes_written;
}
static int camera_open (struct inode *inode, struct file *file)
{
- struct camera_state *camera;
+ struct camera_state *camera = NULL;
int subminor;
+ int value = 0;
+ down (&state_table_mutex);
subminor = MINOR (inode->i_rdev) - USB_CAMERA_MINOR_BASE;
if (subminor < 0 || subminor >= MAX_CAMERAS
|| !(camera = minor_data [subminor])) {
+ up (&state_table_mutex);
return -ENODEV;
}
+ down (&camera->sem);
+ up (&state_table_mutex);
+
+ if (camera->buf) {
+ value = -EBUSY;
+ goto done;
+ }
if (!(camera->buf = (char *) kmalloc (MAX_PACKET_SIZE, GFP_KERNEL))) {
- return -ENOMEM;
+ value = -ENOMEM;
+ goto done;
}
- dbg ("open");
+ dbg ("open #%d", subminor);
- camera->isActive = 0;
file->private_data = camera;
- return 0;
+done:
+ up (&camera->sem);
+ return value;
}
static int camera_release (struct inode *inode, struct file *file)
{
- struct camera_state *camera;
+ struct camera_state *camera;
+ int subminor;
camera = (struct camera_state *) file->private_data;
- kfree (camera->buf);
+ down (&state_table_mutex);
+ down (&camera->sem);
+
+ if (camera->buf) {
+ kfree (camera->buf);
+ camera->buf = 0;
+ }
+ subminor = camera->subminor;
/* If camera was unplugged with open file ... */
- lock_kernel();
if (!camera->dev) {
- minor_data [camera->subminor] = NULL;
+ minor_data [subminor] = NULL;
kfree (camera);
}
- unlock_kernel();
+ up (&camera->sem);
+ up (&state_table_mutex);
- dbg ("close");
+ dbg ("close #%d", subminor);
return 0;
}
@@ -333,7 +349,7 @@ static void * camera_probe(struct usb_device *dev, unsigned int ifnum)
struct usb_interface_descriptor *interface;
struct usb_endpoint_descriptor *endpoint;
int direction, ep;
- struct camera_state *camera;
+ struct camera_state *camera = NULL;
/* Is it a supported camera? */
for (i = 0; i < sizeof (cameras) / sizeof (struct camera); i++) {
@@ -368,27 +384,30 @@ static void * camera_probe(struct usb_device *dev, unsigned int ifnum)
/* select "subminor" number (part of a minor number) */
+ down (&state_table_mutex);
for (i = 0; i < MAX_CAMERAS; i++) {
if (!minor_data [i])
break;
}
if (i >= MAX_CAMERAS) {
info ("Ignoring additional USB Camera");
- return NULL;
+ up (&state_table_mutex);
+ goto bye;
}
/* allocate & init camera state */
camera = minor_data [i] = kmalloc (sizeof *camera, GFP_KERNEL);
if (!camera) {
err ("no memory!");
- return NULL;
+ up (&state_table_mutex);
+ goto bye;
}
+
+ init_MUTEX (&camera->sem);
camera->info = camera_info;
camera->subminor = i;
- camera->isActive = 0;
camera->buf = NULL;
init_waitqueue_head (&camera->wait);
- info ("USB Camera #%d connected", camera->subminor);
/* get input and output endpoints (either order) */
@@ -417,19 +436,19 @@ static void * camera_probe(struct usb_device *dev, unsigned int ifnum)
goto error;
}
-
- if (usb_set_configuration (dev, dev->config[0].bConfigurationValue)) {
- err ("Failed usb_set_configuration");
- goto error;
- }
+ info ("USB Camera #%d connected", camera->subminor);
camera->dev = dev;
- return camera;
+ usb_inc_dev_use (dev);
+ goto bye;
error:
minor_data [camera->subminor] = NULL;
kfree (camera);
- return NULL;
+ camera = NULL;
+bye:
+ up (&state_table_mutex);
+ return camera;
}
static void camera_disconnect(struct usb_device *dev, void *ptr)
@@ -437,6 +456,9 @@ static void camera_disconnect(struct usb_device *dev, void *ptr)
struct camera_state *camera = (struct camera_state *) ptr;
int subminor = camera->subminor;
+ down (&state_table_mutex);
+ down (&camera->sem);
+
/* If camera's not opened, we can clean up right away.
* Else apps see a disconnect on next I/O; the release cleans.
*/
@@ -447,6 +469,10 @@ static void camera_disconnect(struct usb_device *dev, void *ptr)
camera->dev = NULL;
info ("USB Camera #%d disconnected", subminor);
+ usb_dec_dev_use (dev);
+
+ up (&camera->sem);
+ up (&state_table_mutex);
}
static /* const */ struct usb_driver camera_driver = {