summaryrefslogtreecommitdiffstats
path: root/drivers/hid/usbhid/usbkbd.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-01-10 10:48:28 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2012-01-10 10:48:28 -0800
commitf62f61917d72c1fb0101ad405664f6fc868d676b (patch)
treecedeb85a8d1c97dc3be0b667734b0fe4e0dd84b6 /drivers/hid/usbhid/usbkbd.c
parentd04baa157d1b35cbd27c87b4a13111d9675b61f3 (diff)
parent420174afdc7023c000e5b5b1b6fe9e028470c713 (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid: (68 commits) hid-input/battery: add FEATURE quirk hid-input/battery: remove battery_val hid-input/battery: power-supply type really *is* a battery hid-input/battery: make the battery setup common for INPUTs and FEATUREs hid-input/battery: deal with both FEATURE and INPUT report batteries hid-input/battery: add quirks for battery hid-input/battery: remove apparently redundant kmalloc hid-input: add support for HID devices reporting Battery Strength HID: hid-multitouch: add support 9 new Xiroku devices HID: multitouch: add support for 3M 32" HID: multitouch: add support of Atmel multitouch panels HID: usbhid: defer LED setting to a workqueue HID: usbhid: hid-core: submit queued urbs before suspend HID: usbhid: remove LED_ON HID: emsff: use symbolic name instead of hardcoded PID constant HID: Enable HID_QUIRK_MULTI_INPUT for Trio Linker Plus II HID: Kconfig: fix syntax HID: introduce proper dependency of HID_BATTERY on POWER_SUPPLY HID: multitouch: support PixArt optical touch screen HID: make parser more verbose about parsing errors by default ... Fix up rename/delete conflict in drivers/hid/hid-hyperv.c (removed in staging, moved in this branch) and similarly for the rules for same file in drivers/staging/hv/{Kconfig,Makefile}.
Diffstat (limited to 'drivers/hid/usbhid/usbkbd.c')
-rw-r--r--drivers/hid/usbhid/usbkbd.c64
1 files changed, 59 insertions, 5 deletions
diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c
index bc445d7e3bf..796086980f4 100644
--- a/drivers/hid/usbhid/usbkbd.c
+++ b/drivers/hid/usbhid/usbkbd.c
@@ -64,6 +64,32 @@ static const unsigned char usb_kbd_keycode[256] = {
150,158,159,128,136,177,178,176,142,152,173,140
};
+
+/**
+ * struct usb_kbd - state of each attached keyboard
+ * @dev: input device associated with this keyboard
+ * @usbdev: usb device associated with this keyboard
+ * @old: data received in the past from the @irq URB representing which
+ * keys were pressed. By comparing with the current list of keys
+ * that are pressed, we are able to see key releases.
+ * @irq: URB for receiving a list of keys that are pressed when a
+ * new key is pressed or a key that was pressed is released.
+ * @led: URB for sending LEDs (e.g. numlock, ...)
+ * @newleds: data that will be sent with the @led URB representing which LEDs
+ should be on
+ * @name: Name of the keyboard. @dev's name field points to this buffer
+ * @phys: Physical path of the keyboard. @dev's phys field points to this
+ * buffer
+ * @new: Buffer for the @irq URB
+ * @cr: Control request for @led URB
+ * @leds: Buffer for the @led URB
+ * @new_dma: DMA address for @irq URB
+ * @leds_dma: DMA address for @led URB
+ * @leds_lock: spinlock that protects @leds, @newleds, and @led_urb_submitted
+ * @led_urb_submitted: indicates whether @led is in progress, i.e. it has been
+ * submitted and its completion handler has not returned yet
+ * without resubmitting @led
+ */
struct usb_kbd {
struct input_dev *dev;
struct usb_device *usbdev;
@@ -78,6 +104,10 @@ struct usb_kbd {
unsigned char *leds;
dma_addr_t new_dma;
dma_addr_t leds_dma;
+
+ spinlock_t leds_lock;
+ bool led_urb_submitted;
+
};
static void usb_kbd_irq(struct urb *urb)
@@ -136,44 +166,66 @@ resubmit:
static int usb_kbd_event(struct input_dev *dev, unsigned int type,
unsigned int code, int value)
{
+ unsigned long flags;
struct usb_kbd *kbd = input_get_drvdata(dev);
if (type != EV_LED)
return -1;
+ spin_lock_irqsave(&kbd->leds_lock, flags);
kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
(!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) |
(!!test_bit(LED_NUML, dev->led));
- if (kbd->led->status == -EINPROGRESS)
+ if (kbd->led_urb_submitted){
+ spin_unlock_irqrestore(&kbd->leds_lock, flags);
return 0;
+ }
- if (*(kbd->leds) == kbd->newleds)
+ if (*(kbd->leds) == kbd->newleds){
+ spin_unlock_irqrestore(&kbd->leds_lock, flags);
return 0;
+ }
*(kbd->leds) = kbd->newleds;
+
kbd->led->dev = kbd->usbdev;
if (usb_submit_urb(kbd->led, GFP_ATOMIC))
pr_err("usb_submit_urb(leds) failed\n");
-
+ else
+ kbd->led_urb_submitted = true;
+
+ spin_unlock_irqrestore(&kbd->leds_lock, flags);
+
return 0;
}
static void usb_kbd_led(struct urb *urb)
{
+ unsigned long flags;
struct usb_kbd *kbd = urb->context;
if (urb->status)
hid_warn(urb->dev, "led urb status %d received\n",
urb->status);
- if (*(kbd->leds) == kbd->newleds)
+ spin_lock_irqsave(&kbd->leds_lock, flags);
+
+ if (*(kbd->leds) == kbd->newleds){
+ kbd->led_urb_submitted = false;
+ spin_unlock_irqrestore(&kbd->leds_lock, flags);
return;
+ }
*(kbd->leds) = kbd->newleds;
+
kbd->led->dev = kbd->usbdev;
- if (usb_submit_urb(kbd->led, GFP_ATOMIC))
+ if (usb_submit_urb(kbd->led, GFP_ATOMIC)){
hid_err(urb->dev, "usb_submit_urb(leds) failed\n");
+ kbd->led_urb_submitted = false;
+ }
+ spin_unlock_irqrestore(&kbd->leds_lock, flags);
+
}
static int usb_kbd_open(struct input_dev *dev)
@@ -252,6 +304,7 @@ static int usb_kbd_probe(struct usb_interface *iface,
kbd->usbdev = dev;
kbd->dev = input_dev;
+ spin_lock_init(&kbd->leds_lock);
if (dev->manufacturer)
strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
@@ -334,6 +387,7 @@ static void usb_kbd_disconnect(struct usb_interface *intf)
if (kbd) {
usb_kill_urb(kbd->irq);
input_unregister_device(kbd->dev);
+ usb_kill_urb(kbd->led);
usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
kfree(kbd);
}