[PATCH] USB mouse suspend/resume fix

Paul Ashton (lk@mailandnews.com)
16 Aug 1999 15:05:32 +0100


The USB mouse driver oopses when my laptop suspend/resumes as well as when
I disconnect the mouse. The problem is due to a null pointer dereference
in mouse_irq when state == USB_ST_REMOVED. The following patch allows
the mouse to function following a resume or reconnect. It also fixes
an issue with module use count not incrementing if driver already in
use.

Although this works, I'm not sure its the right thing to do. Should
the driver really suspend the use of the mouse when it is disconnected,
or should it close it and require /dev/usbmouse to be reopened? If
so, that will mean the X server and gpm would need to be recoded to
expect that to happen. Alternatively a virtual mouse driver could
interface to a userspace program that multiplexes access to the
real mouse drivers and handles them disappearing...

Lastly, if I insert the mouse driver automatically with the following
modifications to conf.modules, then the first open of /dev/usbmouse
will fail due to mouse_probe() being called after open_mouse() has
returned EINVAL due to mouse->present being false. Can someone
point me to a better method of inserting the mouse driver automatically,
or can the driver be fixed to handle this?

alias char-major-10-32 mouse
pre-install mouse /sbin/modprobe usb-uhci

Paul

--- /tmp/mouse.c Sun Aug 15 22:28:00 1999
+++ linux/drivers/usb/mouse.c Mon Aug 16 00:51:14 1999
@@ -49,6 +49,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 */
@@ -71,6 +72,17 @@
/* 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);
+
+ if (state == USB_ST_REMOVED)
+ {
+ printk(KERN_DEBUG "%s(%d): USB_ST_REMOVED\n",
+ __FILE__, __LINE__);
+ mouse->suspended = 1;
+ return 0;
+ }
/* if a mouse moves with no one listening, do we care? no */
if(!mouse->active)
return 1;
@@ -110,9 +122,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,16 +140,26 @@
{
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 needs to be delayed. how?
+ */
+
if (!mouse->present)
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;
-
/* 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);

@@ -304,6 +328,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;
}

@@ -336,7 +373,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;

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/