patch-2.4.10 linux/drivers/usb/se401.c

Next file: linux/drivers/usb/se401.h
Previous file: linux/drivers/usb/scanner.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.9/linux/drivers/usb/se401.c linux/drivers/usb/se401.c
@@ -25,7 +25,7 @@
  * 	- Jeroen Vreeken
  */
 
-static const char version[] = "0.22";
+static const char version[] = "0.23";
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -66,6 +66,7 @@
 
 MODULE_AUTHOR("Jeroen Vreeken <pe1rxq@amsat.org>");
 MODULE_DESCRIPTION("SE401 USB Camera Driver");
+MODULE_LICENSE("GPL");
 MODULE_PARM(flickerless, "i");
 MODULE_PARM_DESC(flickerless, "Net frequency to adjust exposure time to (0/50/60)");
 MODULE_PARM(video_nr, "i");
@@ -1034,8 +1035,8 @@
 			}
 		}
 	}
-	
-	if (se401->frame[framenr].grabstate==FRAME_DONE) 
+
+	if (se401->frame[framenr].grabstate==FRAME_DONE)
 		if (se401->enhance)
 			enhance_picture(se401->frame[framenr].data, se401->cheight*se401->cwidth*3);
 	return 0;
@@ -1054,32 +1055,27 @@
 	struct usb_se401 *se401 = (struct usb_se401 *)dev;
 	int err = 0;
 
+	/* we are called with the BKL held */
 	MOD_INC_USE_COUNT;
-	down(&se401->lock);
 
+	se401->user=1;
 	se401->fbuf=rvmalloc(se401->maxframesize * SE401_NUMFRAMES);
 	if(!se401->fbuf) err=-ENOMEM;
 
         if (err) {
 		MOD_DEC_USE_COUNT;
-		up(&se401->lock);
-	        return err;
+		se401->user = 0;
 	}
-	
-	se401->user=1;
 
-	up(&se401->lock);
-	
-	return 0;
+	return err;
 }
 
 static void se401_close(struct video_device *dev)
 {
+	/* called with BKL held */
         struct usb_se401 *se401 = (struct usb_se401 *)dev;
 	int i;
 
-	down(&se401->lock);
-
 	for (i=0; i<SE401_NUMFRAMES; i++)
 		se401->frame[i].grabstate=FRAME_UNUSED;
 	if (se401->streaming)
@@ -1087,9 +1083,8 @@
 
 	rvfree(se401->fbuf, se401->maxframesize * SE401_NUMFRAMES);
 	se401->user=0;
-	up(&se401->lock);
 
-        if (!se401->dev) {
+        if (se401->removed) {
                 video_unregister_device(&se401->vdev);
 		kfree(se401->width);
 		kfree(se401->height);
@@ -1205,7 +1200,7 @@
 			return -EINVAL;
 		if (se401_set_size(se401, vw.width, vw.height))
 			return -EINVAL;
-		
+
 		return 0;
         }
 	case VIDIOCGWIN:
@@ -1280,7 +1275,7 @@
 
 		if(frame <0 || frame >= SE401_NUMFRAMES)
 			return -EINVAL;
-			
+
 		ret=se401_newframe(se401, frame);
 		se401->frame[frame].grabstate=FRAME_UNUSED;
 		return ret;
@@ -1350,12 +1345,11 @@
 
 	ret=se401_newframe(se401, 0);
 
-	if (!ret) {
-		copy_to_user(buf, se401->frame[0].data, realcount);
-	} else {
-		realcount=ret;
-	}
 	se401->frame[0].grabstate=FRAME_UNUSED;
+	if (ret)
+		return ret;	
+	if (copy_to_user(buf, se401->frame[0].data, realcount))
+		return -EFAULT;
 
 	return realcount;
 }
@@ -1368,7 +1362,7 @@
 	unsigned long page, pos;
 
 	down(&se401->lock);
-	
+
 	if (se401->dev == NULL) {
 		up(&se401->lock);
 		return -EIO;
@@ -1487,7 +1481,7 @@
 		info("int urb burned down");
 		return 1;
 	}
-	
+
         /* Flash the led */
         se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0);
         se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0);
@@ -1555,33 +1549,45 @@
 
 	info("firmware version: %02x", dev->descriptor.bcdDevice & 255);
 
-        if (se401_init(se401))
+        if (se401_init(se401)) {
+		kfree(se401);
 		return NULL;
+	}
+
 	memcpy(&se401->vdev, &se401_template, sizeof(se401_template));
 	memcpy(se401->vdev.name, se401->camera_name, strlen(se401->camera_name));
+	init_waitqueue_head(&se401->wq);
+	init_MUTEX(&se401->lock);
+	wmb();
+
 	if (video_register_device(&se401->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
+		kfree(se401);
 		err("video_register_device failed");
 		return NULL;
 	}
 	info("registered new video device: video%d", se401->vdev.minor);
 
-	init_waitqueue_head(&se401->wq);
-	init_MUTEX(&se401->lock);
-
         return se401;
 }
 
 static void se401_disconnect(struct usb_device *dev, void *ptr)
 {
-	int i;
 	struct usb_se401 *se401 = (struct usb_se401 *) ptr;
 
+	lock_kernel();
 	/* We don't want people trying to open up the device */
-	if (!se401->user)
-              video_unregister_device(&se401->vdev);
+	if (!se401->user){
+		video_unregister_device(&se401->vdev);
+		usb_se401_remove_disconnected(se401);
+	} else {
+		se401->removed = 1;
+	}
+	unlock_kernel();
+}
 
-        usb_driver_release_interface(&se401_driver,
-                &se401->dev->actconfig->interface[se401->iface]);
+static inline void usb_se401_remove_disconnected (struct usb_se401 *se401)
+{
+	int i;
 
         se401->dev = NULL;
         se401->frame[0].grabstate = FRAME_ERROR;
@@ -1589,8 +1595,7 @@
 
 	se401->streaming = 0;
 
-	if (waitqueue_active(&se401->wq))
-                wake_up_interruptible(&se401->wq);
+	wake_up_interruptible(&se401->wq);
 
 	for (i=0; i<SE401_NUMSBUF; i++) if (se401->urb[i]) {
 		se401->urb[i]->next = NULL;
@@ -1613,14 +1618,10 @@
 #endif
 
         /* Free the memory */
-        if (!se401->user) {
-		kfree(se401->width);
-		kfree(se401->height);
-                kfree(se401);
-                se401 = NULL;
-        }
+	kfree(se401->width);
+	kfree(se401->height);
+	kfree(se401);
 }
-
 
 static struct usb_driver se401_driver = {
         name:		"se401",

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)