patch-2.3.18 linux/drivers/usb/keyboard.c

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

diff -u --recursive --new-file v2.3.17/linux/drivers/usb/keyboard.c linux/drivers/usb/keyboard.c
@@ -1,3 +1,11 @@
+/*
+	Fixes:
+	1999/09/04 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+		Handle states in usb_kbd_irq
+	1999/09/06 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+		rmmod usb-keyboard doesn't crash the system anymore: the irq
+		handlers are correctly released with usb_release_irq
+*/
 #include <linux/kernel.h>
 #include <linux/malloc.h>
 #include <linux/string.h>
@@ -31,6 +39,8 @@
     unsigned char repeat_key;
     struct timer_list repeat_timer;
     struct list_head list;
+    unsigned int irqpipe;
+    void *irq_handler; /* host controller's IRQ transfer handle */
 };
 
 extern unsigned char usb_kbd_map[];
@@ -97,8 +107,22 @@
     struct usb_keyboard *kbd = (struct usb_keyboard*) dev_id;
     unsigned long *down = (unsigned long*) buffer;
 
-    if(kbd->down[0] != down[0] || kbd->down[1] != down[1])
-    {
+    /*
+     * USB_ST_NOERROR is the normal case.
+     * USB_ST_REMOVED occurs if keyboard disconnected
+     * On other states, ignore
+     */
+
+    switch (state) {
+	case USB_ST_REMOVED:
+	case USB_ST_INTERNALERROR:
+        	printk(KERN_DEBUG "%s(%d): Suspending\n", __FILE__, __LINE__);
+		return 0; /* disable */
+	case USB_ST_NOERROR: break;
+		default: return 1; /* ignore */
+    }
+
+    if(kbd->down[0] != down[0] || kbd->down[1] != down[1]) {
         unsigned char *olddown, *newdown;
         unsigned char modsdelta, key;
         int i;
@@ -172,7 +196,8 @@
     struct usb_interface_descriptor *interface;
     struct usb_endpoint_descriptor *endpoint;
     struct usb_keyboard *kbd;
-
+    int ret;
+    
     if (dev->descriptor.bNumConfigurations < 1)
 	return -1;
 
@@ -202,11 +227,14 @@
         usb_set_protocol(dev, 0);
         usb_set_idle(dev, 0, 0);
         
-        usb_request_irq(dev,
-                        usb_rcvctrlpipe(dev, endpoint->bEndpointAddress),
-                        usb_kbd_irq,
-                        endpoint->bInterval,
-                        kbd);
+	kbd->irqpipe = usb_rcvctrlpipe(dev, endpoint->bEndpointAddress);
+	ret = usb_request_irq(dev, kbd->irqpipe,
+				usb_kbd_irq, endpoint->bInterval,
+				kbd, &kbd->irq_handler);
+	if (ret) {
+		printk(KERN_INFO "usb-keyboard failed usb_request_irq (0x%x)\n", ret);
+		goto probe_err;
+	}
 
         list_add(&kbd->list, &usb_kbd_list);
 	
@@ -223,12 +251,13 @@
 usb_kbd_disconnect(struct usb_device *dev)
 {
     struct usb_keyboard *kbd = (struct usb_keyboard*) dev->private;
-    if(kbd)
+    if (kbd)
     {
-        dev->private = NULL;
-        list_del(&kbd->list);
-        del_timer(&kbd->repeat_timer);
-        kfree(kbd);
+	usb_release_irq(dev, kbd->irq_handler, kbd->irqpipe);
+	dev->private = NULL;
+	list_del(&kbd->list);
+	del_timer(&kbd->repeat_timer);
+	kfree(kbd);
     }
 
     printk(KERN_INFO "USB HID boot protocol keyboard removed.\n");
@@ -240,6 +269,30 @@
     return 0;
 }
 
+void usb_kbd_cleanup(void)
+{
+	struct list_head *cur, *head = &usb_kbd_list;
+
+	cur = head->next;
+
+	while (cur != head) {
+		struct usb_keyboard *kbd = list_entry(cur, struct usb_keyboard, list);
+
+		cur = cur->next;
+
+                list_del(&kbd->list);
+                INIT_LIST_HEAD(&kbd->list);
+
+		if (kbd->irq_handler) {
+			usb_release_irq(kbd->dev, kbd->irq_handler, kbd->irqpipe);
+			/* never keep a reference to a released IRQ! */
+			kbd->irq_handler = NULL;
+		}
+	}
+
+	usb_deregister(&usb_kbd_driver);
+}
+
 #ifdef MODULE
 int init_module(void)
 {
@@ -248,7 +301,7 @@
 
 void cleanup_module(void)
 {
-	usb_deregister(&usb_kbd_driver);
+	usb_kbd_cleanup();
 }
 #endif
 

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