aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/usbhid
diff options
context:
space:
mode:
authorWillem Penninckx <willem.penninckx@cs.kuleuven.be>2011-11-23 05:25:34 -0500
committerJiri Kosina <jkosina@suse.cz>2011-11-23 06:30:25 -0500
commitc196adf87514560f867492978ae350d4bbced0bd (patch)
tree9087524790420f16ed25c8a5245314d489bfc4af /drivers/hid/usbhid
parentf2c4826c685b1ad9afdcef3649e3e60a3348491c (diff)
HID: usbkbd: synchronize LED URB submission
usb_kbd_event() and usb_kbd_led() can be called concurrently, but they are not synchronized. They both readwrite kbd->leds, and usb_kbd_event() originally just checked the URB status field, while urb.h states that "It [status field] should not be examined before the URB is returned to the completion handler." To fix this unsynchronized behavior, this patch introduces a boolean representing whether the URB is submitted, and a spinlock. Signed-off-by: Willem Penninckx <willem.penninckx@cs.kuleuven.be> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/usbhid')
-rw-r--r--drivers/hid/usbhid/usbkbd.c63
1 files changed, 58 insertions, 5 deletions
diff --git a/drivers/hid/usbhid/usbkbd.c b/drivers/hid/usbhid/usbkbd.c
index 065817329f03..052346f3ccb4 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] = {
64 150,158,159,128,136,177,178,176,142,152,173,140 64 150,158,159,128,136,177,178,176,142,152,173,140
65}; 65};
66 66
67
68/**
69 * struct usb_kbd - state of each attached keyboard
70 * @dev: input device associated with this keyboard
71 * @usbdev: usb device associated with this keyboard
72 * @old: data received in the past from the @irq URB representing which
73 * keys were pressed. By comparing with the current list of keys
74 * that are pressed, we are able to see key releases.
75 * @irq: URB for receiving a list of keys that are pressed when a
76 * new key is pressed or a key that was pressed is released.
77 * @led: URB for sending LEDs (e.g. numlock, ...)
78 * @newleds: data that will be sent with the @led URB representing which LEDs
79 should be on
80 * @name: Name of the keyboard. @dev's name field points to this buffer
81 * @phys: Physical path of the keyboard. @dev's phys field points to this
82 * buffer
83 * @new: Buffer for the @irq URB
84 * @cr: Control request for @led URB
85 * @leds: Buffer for the @led URB
86 * @new_dma: DMA address for @irq URB
87 * @leds_dma: DMA address for @led URB
88 * @leds_lock: spinlock that protects @leds, @newleds, and @led_urb_submitted
89 * @led_urb_submitted: indicates whether @led is in progress, i.e. it has been
90 * submitted and its completion handler has not returned yet
91 * without resubmitting @led
92 */
67struct usb_kbd { 93struct usb_kbd {
68 struct input_dev *dev; 94 struct input_dev *dev;
69 struct usb_device *usbdev; 95 struct usb_device *usbdev;
@@ -78,6 +104,10 @@ struct usb_kbd {
78 unsigned char *leds; 104 unsigned char *leds;
79 dma_addr_t new_dma; 105 dma_addr_t new_dma;
80 dma_addr_t leds_dma; 106 dma_addr_t leds_dma;
107
108 spinlock_t leds_lock;
109 bool led_urb_submitted;
110
81}; 111};
82 112
83static void usb_kbd_irq(struct urb *urb) 113static void usb_kbd_irq(struct urb *urb)
@@ -136,44 +166,66 @@ resubmit:
136static int usb_kbd_event(struct input_dev *dev, unsigned int type, 166static int usb_kbd_event(struct input_dev *dev, unsigned int type,
137 unsigned int code, int value) 167 unsigned int code, int value)
138{ 168{
169 unsigned long flags;
139 struct usb_kbd *kbd = input_get_drvdata(dev); 170 struct usb_kbd *kbd = input_get_drvdata(dev);
140 171
141 if (type != EV_LED) 172 if (type != EV_LED)
142 return -1; 173 return -1;
143 174
175 spin_lock_irqsave(&kbd->leds_lock, flags);
144 kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) | 176 kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
145 (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) | 177 (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) |
146 (!!test_bit(LED_NUML, dev->led)); 178 (!!test_bit(LED_NUML, dev->led));
147 179
148 if (kbd->led->status == -EINPROGRESS) 180 if (kbd->led_urb_submitted){
181 spin_unlock_irqrestore(&kbd->leds_lock, flags);
149 return 0; 182 return 0;
183 }
150 184
151 if (*(kbd->leds) == kbd->newleds) 185 if (*(kbd->leds) == kbd->newleds){
186 spin_unlock_irqrestore(&kbd->leds_lock, flags);
152 return 0; 187 return 0;
188 }
153 189
154 *(kbd->leds) = kbd->newleds; 190 *(kbd->leds) = kbd->newleds;
191
155 kbd->led->dev = kbd->usbdev; 192 kbd->led->dev = kbd->usbdev;
156 if (usb_submit_urb(kbd->led, GFP_ATOMIC)) 193 if (usb_submit_urb(kbd->led, GFP_ATOMIC))
157 pr_err("usb_submit_urb(leds) failed\n"); 194 pr_err("usb_submit_urb(leds) failed\n");
158 195 else
196 kbd->led_urb_submitted = true;
197
198 spin_unlock_irqrestore(&kbd->leds_lock, flags);
199
159 return 0; 200 return 0;
160} 201}
161 202
162static void usb_kbd_led(struct urb *urb) 203static void usb_kbd_led(struct urb *urb)
163{ 204{
205 unsigned long flags;
164 struct usb_kbd *kbd = urb->context; 206 struct usb_kbd *kbd = urb->context;
165 207
166 if (urb->status) 208 if (urb->status)
167 hid_warn(urb->dev, "led urb status %d received\n", 209 hid_warn(urb->dev, "led urb status %d received\n",
168 urb->status); 210 urb->status);
169 211
170 if (*(kbd->leds) == kbd->newleds) 212 spin_lock_irqsave(&kbd->leds_lock, flags);
213
214 if (*(kbd->leds) == kbd->newleds){
215 kbd->led_urb_submitted = false;
216 spin_unlock_irqrestore(&kbd->leds_lock, flags);
171 return; 217 return;
218 }
172 219
173 *(kbd->leds) = kbd->newleds; 220 *(kbd->leds) = kbd->newleds;
221
174 kbd->led->dev = kbd->usbdev; 222 kbd->led->dev = kbd->usbdev;
175 if (usb_submit_urb(kbd->led, GFP_ATOMIC)) 223 if (usb_submit_urb(kbd->led, GFP_ATOMIC)){
176 hid_err(urb->dev, "usb_submit_urb(leds) failed\n"); 224 hid_err(urb->dev, "usb_submit_urb(leds) failed\n");
225 kbd->led_urb_submitted = false;
226 }
227 spin_unlock_irqrestore(&kbd->leds_lock, flags);
228
177} 229}
178 230
179static int usb_kbd_open(struct input_dev *dev) 231static int usb_kbd_open(struct input_dev *dev)
@@ -252,6 +304,7 @@ static int usb_kbd_probe(struct usb_interface *iface,
252 304
253 kbd->usbdev = dev; 305 kbd->usbdev = dev;
254 kbd->dev = input_dev; 306 kbd->dev = input_dev;
307 spin_lock_init(&kbd->leds_lock);
255 308
256 if (dev->manufacturer) 309 if (dev->manufacturer)
257 strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name)); 310 strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));