blob: 6aa8b7e8fb980b60c0ccc19d99e695c0135ba75c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * Copyright (c) 1999-2001 Vojtech Pavlik
3 *
4 * USB HIDBP Keyboard support
5 */
6
7/*
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
Dmitry Torokhov05f091a2005-05-29 02:29:01 -050010 * the Free Software Foundation; either version 2 of the License, or
Linus Torvalds1da177e2005-04-16 15:20:36 -070011 * (at your option) any later version.
Dmitry Torokhov05f091a2005-05-29 02:29:01 -050012 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070013 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
Dmitry Torokhov05f091a2005-05-29 02:29:01 -050017 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070018 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Dmitry Torokhov05f091a2005-05-29 02:29:01 -050021 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070022 * Should you need to contact me, the author, you can do so either by
23 * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
24 * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
25 */
26
27#include <linux/kernel.h>
28#include <linux/slab.h>
29#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/init.h>
David Brownellae0dadc2006-06-13 10:04:34 -070031#include <linux/usb/input.h>
Michael Opdenacker4ef2e232007-02-21 22:51:25 +010032#include <linux/hid.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
34/*
35 * Version Information
36 */
37#define DRIVER_VERSION ""
38#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
39#define DRIVER_DESC "USB HID Boot Protocol keyboard driver"
40#define DRIVER_LICENSE "GPL"
41
42MODULE_AUTHOR(DRIVER_AUTHOR);
43MODULE_DESCRIPTION(DRIVER_DESC);
44MODULE_LICENSE(DRIVER_LICENSE);
45
Ming Leia44ebcc2008-06-08 16:15:16 +080046static const unsigned char usb_kbd_keycode[256] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070047 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
48 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
49 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
50 27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
51 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
52 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
53 72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
54 191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
55 115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
56 122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
57 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
58 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
59 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
61 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
62 150,158,159,128,136,177,178,176,142,152,173,140
63};
64
65struct usb_kbd {
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -050066 struct input_dev *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 struct usb_device *usbdev;
68 unsigned char old[8];
69 struct urb *irq, *led;
70 unsigned char newleds;
71 char name[128];
72 char phys[64];
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
74 unsigned char *new;
75 struct usb_ctrlrequest *cr;
76 unsigned char *leds;
77 dma_addr_t cr_dma;
78 dma_addr_t new_dma;
79 dma_addr_t leds_dma;
80};
81
David Howells7d12e782006-10-05 14:55:46 +010082static void usb_kbd_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -070083{
84 struct usb_kbd *kbd = urb->context;
85 int i;
86
87 switch (urb->status) {
88 case 0: /* success */
89 break;
90 case -ECONNRESET: /* unlink */
91 case -ENOENT:
92 case -ESHUTDOWN:
93 return;
94 /* -EPIPE: should clear the halt */
95 default: /* error */
96 goto resubmit;
97 }
98
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 for (i = 0; i < 8; i++)
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500100 input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
102 for (i = 2; i < 8; i++) {
103
104 if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
105 if (usb_kbd_keycode[kbd->old[i]])
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500106 input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 else
108 info("Unknown key (scancode %#x) released.", kbd->old[i]);
109 }
110
111 if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
112 if (usb_kbd_keycode[kbd->new[i]])
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500113 input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 else
115 info("Unknown key (scancode %#x) pressed.", kbd->new[i]);
116 }
117 }
118
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500119 input_sync(kbd->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
121 memcpy(kbd->old, kbd->new, 8);
122
123resubmit:
Christoph Lameter54e6ecb2006-12-06 20:33:16 -0800124 i = usb_submit_urb (urb, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 if (i)
Jiri Kosina58037eb2007-05-30 15:07:13 +0200126 err_hid ("can't resubmit intr, %s-%s/input0, status %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 kbd->usbdev->bus->bus_name,
128 kbd->usbdev->devpath, i);
129}
130
Adrian Bunkbe5e33832005-04-22 15:07:00 -0700131static int usb_kbd_event(struct input_dev *dev, unsigned int type,
132 unsigned int code, int value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133{
Dmitry Torokhove0712982007-05-09 10:17:31 +0200134 struct usb_kbd *kbd = input_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135
136 if (type != EV_LED)
137 return -1;
138
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
140 (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) |
141 (!!test_bit(LED_NUML, dev->led));
142
143 if (kbd->led->status == -EINPROGRESS)
144 return 0;
145
146 if (*(kbd->leds) == kbd->newleds)
147 return 0;
148
149 *(kbd->leds) = kbd->newleds;
150 kbd->led->dev = kbd->usbdev;
151 if (usb_submit_urb(kbd->led, GFP_ATOMIC))
Jiri Kosina58037eb2007-05-30 15:07:13 +0200152 err_hid("usb_submit_urb(leds) failed");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153
154 return 0;
155}
156
David Howells7d12e782006-10-05 14:55:46 +0100157static void usb_kbd_led(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158{
159 struct usb_kbd *kbd = urb->context;
160
161 if (urb->status)
162 warn("led urb status %d received", urb->status);
Dmitry Torokhov05f091a2005-05-29 02:29:01 -0500163
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 if (*(kbd->leds) == kbd->newleds)
165 return;
166
167 *(kbd->leds) = kbd->newleds;
168 kbd->led->dev = kbd->usbdev;
169 if (usb_submit_urb(kbd->led, GFP_ATOMIC))
Jiri Kosina58037eb2007-05-30 15:07:13 +0200170 err_hid("usb_submit_urb(leds) failed");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171}
172
173static int usb_kbd_open(struct input_dev *dev)
174{
Dmitry Torokhove0712982007-05-09 10:17:31 +0200175 struct usb_kbd *kbd = input_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 kbd->irq->dev = kbd->usbdev;
Dmitry Torokhov65cde542005-05-29 02:29:38 -0500178 if (usb_submit_urb(kbd->irq, GFP_KERNEL))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180
181 return 0;
182}
183
184static void usb_kbd_close(struct input_dev *dev)
185{
Dmitry Torokhove0712982007-05-09 10:17:31 +0200186 struct usb_kbd *kbd = input_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187
Dmitry Torokhov65cde542005-05-29 02:29:38 -0500188 usb_kill_urb(kbd->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189}
190
191static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
192{
193 if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))
194 return -1;
195 if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))
196 return -1;
Christoph Lameter54e6ecb2006-12-06 20:33:16 -0800197 if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 return -1;
Christoph Lameter54e6ecb2006-12-06 20:33:16 -0800199 if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 return -1;
Christoph Lameter54e6ecb2006-12-06 20:33:16 -0800201 if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 return -1;
203
204 return 0;
205}
206
207static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)
208{
Mariusz Kozlowski4ba0b2e2006-11-08 15:35:58 +0100209 usb_free_urb(kbd->irq);
210 usb_free_urb(kbd->led);
Dmitry Torokhov6675c5b2007-05-03 01:04:52 -0400211 usb_buffer_free(dev, 8, kbd->new, kbd->new_dma);
212 usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma);
213 usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214}
215
Dmitry Torokhov05f091a2005-05-29 02:29:01 -0500216static int usb_kbd_probe(struct usb_interface *iface,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 const struct usb_device_id *id)
218{
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500219 struct usb_device *dev = interface_to_usbdev(iface);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 struct usb_host_interface *interface;
221 struct usb_endpoint_descriptor *endpoint;
222 struct usb_kbd *kbd;
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500223 struct input_dev *input_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 int i, pipe, maxp;
Dmitry Torokhov5d6341c2007-04-04 10:40:57 +0200225 int error = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226
227 interface = iface->cur_altsetting;
228
229 if (interface->desc.bNumEndpoints != 1)
230 return -ENODEV;
231
232 endpoint = &interface->endpoint[0].desc;
Luiz Fernando N. Capitulinoa20c3142006-10-26 13:02:59 -0300233 if (!usb_endpoint_is_int_in(endpoint))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 return -ENODEV;
235
Jiri Kosinab0e66822007-12-17 16:11:27 +0100236#ifdef CONFIG_USB_HID
Pascal Terjan9f6b3722007-11-26 14:03:52 +0100237 if (usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
238 le16_to_cpu(dev->descriptor.idProduct))
239 & HID_QUIRK_IGNORE) {
240 return -ENODEV;
241 }
Jiri Kosinab0e66822007-12-17 16:11:27 +0100242#endif
Pascal Terjan9f6b3722007-11-26 14:03:52 +0100243
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
245 maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
246
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500247 kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);
248 input_dev = input_allocate_device();
249 if (!kbd || !input_dev)
250 goto fail1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500252 if (usb_kbd_alloc_mem(dev, kbd))
253 goto fail2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254
255 kbd->usbdev = dev;
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500256 kbd->dev = input_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500258 if (dev->manufacturer)
259 strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
260
261 if (dev->product) {
262 if (dev->manufacturer)
263 strlcat(kbd->name, " ", sizeof(kbd->name));
264 strlcat(kbd->name, dev->product, sizeof(kbd->name));
265 }
266
267 if (!strlen(kbd->name))
268 snprintf(kbd->name, sizeof(kbd->name),
269 "USB HIDBP Keyboard %04x:%04x",
270 le16_to_cpu(dev->descriptor.idVendor),
271 le16_to_cpu(dev->descriptor.idProduct));
272
273 usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
274 strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));
275
276 input_dev->name = kbd->name;
277 input_dev->phys = kbd->phys;
278 usb_to_input_id(dev, &input_dev->id);
Dmitry Torokhove0712982007-05-09 10:17:31 +0200279 input_dev->dev.parent = &iface->dev;
280
281 input_set_drvdata(input_dev, kbd);
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500282
Jiri Slaby7b19ada2007-10-18 23:40:32 -0700283 input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
284 BIT_MASK(EV_REP);
285 input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
286 BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) |
287 BIT_MASK(LED_KANA);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288
289 for (i = 0; i < 255; i++)
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500290 set_bit(usb_kbd_keycode[i], input_dev->keybit);
291 clear_bit(0, input_dev->keybit);
Dmitry Torokhov05f091a2005-05-29 02:29:01 -0500292
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500293 input_dev->event = usb_kbd_event;
294 input_dev->open = usb_kbd_open;
295 input_dev->close = usb_kbd_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296
297 usb_fill_int_urb(kbd->irq, dev, pipe,
298 kbd->new, (maxp > 8 ? 8 : maxp),
299 usb_kbd_irq, kbd, endpoint->bInterval);
300 kbd->irq->transfer_dma = kbd->new_dma;
301 kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
302
303 kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
304 kbd->cr->bRequest = 0x09;
305 kbd->cr->wValue = cpu_to_le16(0x200);
306 kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
307 kbd->cr->wLength = cpu_to_le16(1);
308
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
310 (void *) kbd->cr, kbd->leds, 1,
311 usb_kbd_led, kbd);
312 kbd->led->setup_dma = kbd->cr_dma;
313 kbd->led->transfer_dma = kbd->leds_dma;
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500314 kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315
Dmitry Torokhov5d6341c2007-04-04 10:40:57 +0200316 error = input_register_device(kbd->dev);
317 if (error)
318 goto fail2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319
320 usb_set_intfdata(iface, kbd);
321 return 0;
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500322
Dmitry Torokhov5d6341c2007-04-04 10:40:57 +0200323fail2:
324 usb_kbd_free_mem(dev, kbd);
325fail1:
326 input_free_device(input_dev);
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500327 kfree(kbd);
Dmitry Torokhov5d6341c2007-04-04 10:40:57 +0200328 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329}
330
331static void usb_kbd_disconnect(struct usb_interface *intf)
332{
333 struct usb_kbd *kbd = usb_get_intfdata (intf);
Dmitry Torokhov05f091a2005-05-29 02:29:01 -0500334
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 usb_set_intfdata(intf, NULL);
336 if (kbd) {
337 usb_kill_urb(kbd->irq);
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500338 input_unregister_device(kbd->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
340 kfree(kbd);
341 }
342}
343
344static struct usb_device_id usb_kbd_id_table [] = {
Michael Opdenacker4ef2e232007-02-21 22:51:25 +0100345 { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
346 USB_INTERFACE_PROTOCOL_KEYBOARD) },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 { } /* Terminating entry */
348};
349
350MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);
351
352static struct usb_driver usb_kbd_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 .name = "usbkbd",
354 .probe = usb_kbd_probe,
355 .disconnect = usb_kbd_disconnect,
356 .id_table = usb_kbd_id_table,
357};
358
359static int __init usb_kbd_init(void)
360{
361 int result = usb_register(&usb_kbd_driver);
362 if (result == 0)
363 info(DRIVER_VERSION ":" DRIVER_DESC);
364 return result;
365}
366
367static void __exit usb_kbd_exit(void)
368{
369 usb_deregister(&usb_kbd_driver);
370}
371
372module_init(usb_kbd_init);
373module_exit(usb_kbd_exit);