patch-2.3.17 linux/drivers/usb/mouse.c

Next file: linux/drivers/usb/ohci.c
Previous file: linux/drivers/usb/inits.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.16/linux/drivers/usb/mouse.c linux/drivers/usb/mouse.c
@@ -4,6 +4,12 @@
  *
  * Brad Keryan 4/3/1999
  *
+ * version 0.30? Paul Ashton 1999/08/19 - Fixed behaviour on mouse
+ * disconnect and suspend/resume. Added module parameter "force=1"
+ * to allow opening of the mouse driver before mouse has been plugged
+ * in (enables consistent XF86Config settings). Fixed module use count.
+ * Documented missing blocking/non-blocking read handling (not fixed).
+ * 
  * version 0.20: Linus rewrote read_mouse() to do PS/2 and do it
  * correctly. Events are added together, not queued, to keep the rodent sober.
  *
@@ -49,6 +55,7 @@
 	int present; /* this mouse is plugged in */
 	int active; /* someone is has this mouse's device open */
 	int ready; /* the mouse has changed state since the last read */
+	int suspended; /* mouse disconnected */
 	wait_queue_head_t wait; /* for polling */
 	struct fasync_struct *fasync;
 	/* later, add a list here to support multiple mice */
@@ -65,12 +72,38 @@
 
 spinlock_t usb_mouse_lock = SPIN_LOCK_UNLOCKED;
 
+static int force=0; /* allow the USB mouse to be opened even if not there (yet) */
+MODULE_PARM(force,"i");
+
 static int mouse_irq(int state, void *__buffer, int len, void *dev_id)
 {
 	signed char *data = __buffer;
 	/* finding the mouse is easy when there's only one */
 	struct mouse_state *mouse = &static_mouse_state; 
 
+	if (state)
+		printk(KERN_DEBUG "%s(%d):state %d, bp %p, len %d, dp %p\n",
+			__FILE__, __LINE__, state, __buffer, len, dev_id);
+
+	/*
+	 * USB_ST_NOERROR is the normal case.
+	 * USB_ST_REMOVED occurs if mouse disconnected or suspend/resume
+	 * USB_ST_INTERNALERROR occurs if system suspended then mouse removed
+	 *    followed by resume. On UHCI could then occur every second
+	 * In both cases, suspend the mouse
+	 * On other states, ignore
+	 */
+	switch (state) {
+	case USB_ST_REMOVED:
+	case USB_ST_INTERNALERROR: 
+		printk(KERN_DEBUG "%s(%d): Suspending\n",
+			__FILE__, __LINE__);
+		mouse->suspended = 1;
+		return 0; /* disable */
+	case USB_ST_NOERROR: break;
+	default: return 1; /* ignore */
+	}	
+
 	/* if a mouse moves with no one listening, do we care? no */
 	if(!mouse->active)
 		return 1;
@@ -110,9 +143,11 @@
 
 	fasync_mouse(-1, file, 0);
 
+	printk(KERN_DEBUG "%s(%d): MOD_DEC\n", __FILE__, __LINE__);
 	MOD_DEC_USE_COUNT;
 
 	if (--mouse->active == 0) {
+		mouse->suspended = 0;
 		/* stop polling the mouse while its not in use */
 	    	usb_release_irq(mouse->dev, mouse->irq_handle);
 		/* never keep a reference to a released IRQ! */
@@ -126,15 +161,38 @@
 {
 	struct mouse_state *mouse = &static_mouse_state;
 
+	printk(KERN_DEBUG "%s(%d): open_mouse\n", __FILE__, __LINE__);
+	/*
+	 * First open may fail since mouse_probe() may get called after this
+	 * if module load is in response to the open
+	 * mouse_probe() sets mouse->present. This open can be delayed by
+	 * specifying force=1 in module load
+	 * This helps if you want to insert the USB mouse after starting X
+	 */
 	if (!mouse->present)
-		return -EINVAL;
+	{
+		if (force) /* always load the driver even if no mouse (yet) */
+		{
+			printk(KERN_DEBUG "%s(%d): forced open\n",
+				__FILE__, __LINE__);
+			mouse->suspended = 1;
+		}
+		else
+			return -EINVAL;
+	}
+
+	/* prevent the driver from being unloaded while its in use */
+	printk(KERN_DEBUG "%s(%d): MOD_INC\n", __FILE__, __LINE__);
+	/* Increment use count even if already active */
+	MOD_INC_USE_COUNT;
+
 	if (mouse->active++)
 		return 0;
 	/* flush state */
 	mouse->buttons = mouse->dx = mouse->dy = mouse->dz = 0;
 
-	/* prevent the driver from being unloaded while its in use */
-	MOD_INC_USE_COUNT;
+	if (!mouse->present) /* only get here if force == 1 */
+		return 0;
 
 	/* start the usb controller's polling of the mouse */
 	mouse->irq_handle = usb_request_irq(mouse->dev, usb_rcvctrlpipe(mouse->dev, mouse->bEndpointAddress), mouse_irq, mouse->bInterval, NULL);
@@ -160,6 +218,10 @@
 	static int state = 0;
 	struct mouse_state *mouse = &static_mouse_state;
 
+	/*
+	 * FIXME - Other mouse drivers handle blocking and nonblocking reads
+	 * differently here...
+	 */
 	if (count) {
 		mouse->ready = 0;
 		switch (state) {
@@ -304,6 +366,19 @@
 	mouse->bInterval = endpoint->bInterval;
 
 	mouse->present = 1;
+
+	/* This appears to let USB mouse survive disconnection and */
+	/* APM suspend/resume */
+	if (mouse->suspended)
+	{
+		printk(KERN_DEBUG "%s(%d): mouse resume\n", __FILE__, __LINE__);
+		/* restart the usb controller's polling of the mouse */
+		mouse->irq_handle = usb_request_irq(mouse->dev,
+			usb_rcvctrlpipe(mouse->dev, mouse->bEndpointAddress),
+			mouse_irq, mouse->bInterval, NULL);
+		mouse->suspended = 0;
+	}
+
 	return 0;
 }
 
@@ -315,7 +390,6 @@
 	if (mouse->present) {
 	    	usb_release_irq(mouse->dev, mouse->irq_handle);
 		/* never keep a reference to a released IRQ! */
-		mouse->irq_handle = NULL;
 	}
 
 	mouse->irq_handle = NULL;
@@ -336,7 +410,7 @@
 {
 	struct mouse_state *mouse = &static_mouse_state;
 
-	mouse->present = mouse->active = 0;
+	mouse->present = mouse->active = mouse->suspended = 0;
 	mouse->irq_handle = NULL;
 	init_waitqueue_head(&mouse->wait);
 	mouse->fasync = NULL;

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