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
- Lines: 135
- Date:
Fri Sep 10 12:59:49 1999
- Orig file:
v2.3.17/linux/drivers/usb/keyboard.c
- Orig date:
Thu Aug 26 13:05:39 1999
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)