blob: 0caaafe018438db68b33acc4ac0d695c717d4058 [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 Torokhov05f091ab2005-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 Torokhov05f091ab2005-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 Torokhov05f091ab2005-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 Torokhov05f091ab2005-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 Torokhov05f091ab2005-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 Torokhov05f091ab2005-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
236 pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
237 maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
238
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500239 kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);
240 input_dev = input_allocate_device();
241 if (!kbd || !input_dev)
242 goto fail1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500244 if (usb_kbd_alloc_mem(dev, kbd))
245 goto fail2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246
247 kbd->usbdev = dev;
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500248 kbd->dev = input_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500250 if (dev->manufacturer)
251 strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
252
253 if (dev->product) {
254 if (dev->manufacturer)
255 strlcat(kbd->name, " ", sizeof(kbd->name));
256 strlcat(kbd->name, dev->product, sizeof(kbd->name));
257 }
258
259 if (!strlen(kbd->name))
260 snprintf(kbd->name, sizeof(kbd->name),
261 "USB HIDBP Keyboard %04x:%04x",
262 le16_to_cpu(dev->descriptor.idVendor),
263 le16_to_cpu(dev->descriptor.idProduct));
264
265 usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
266 strlcpy(kbd->phys, "/input0", sizeof(kbd->phys));
267
268 input_dev->name = kbd->name;
269 input_dev->phys = kbd->phys;
270 usb_to_input_id(dev, &input_dev->id);
Dmitry Torokhove0712982007-05-09 10:17:31 +0200271 input_dev->dev.parent = &iface->dev;
272
273 input_set_drvdata(input_dev, kbd);
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500274
Jiri Slaby7b19ada2007-10-18 23:40:32 -0700275 input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
276 BIT_MASK(EV_REP);
277 input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
278 BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) |
279 BIT_MASK(LED_KANA);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280
281 for (i = 0; i < 255; i++)
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500282 set_bit(usb_kbd_keycode[i], input_dev->keybit);
283 clear_bit(0, input_dev->keybit);
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500284
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500285 input_dev->event = usb_kbd_event;
286 input_dev->open = usb_kbd_open;
287 input_dev->close = usb_kbd_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288
289 usb_fill_int_urb(kbd->irq, dev, pipe,
290 kbd->new, (maxp > 8 ? 8 : maxp),
291 usb_kbd_irq, kbd, endpoint->bInterval);
292 kbd->irq->transfer_dma = kbd->new_dma;
293 kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
294
295 kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
296 kbd->cr->bRequest = 0x09;
297 kbd->cr->wValue = cpu_to_le16(0x200);
298 kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
299 kbd->cr->wLength = cpu_to_le16(1);
300
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301 usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
302 (void *) kbd->cr, kbd->leds, 1,
303 usb_kbd_led, kbd);
304 kbd->led->setup_dma = kbd->cr_dma;
305 kbd->led->transfer_dma = kbd->leds_dma;
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500306 kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307
Dmitry Torokhov5d6341c2007-04-04 10:40:57 +0200308 error = input_register_device(kbd->dev);
309 if (error)
310 goto fail2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311
312 usb_set_intfdata(iface, kbd);
313 return 0;
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500314
Dmitry Torokhov5d6341c2007-04-04 10:40:57 +0200315fail2:
316 usb_kbd_free_mem(dev, kbd);
317fail1:
318 input_free_device(input_dev);
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500319 kfree(kbd);
Dmitry Torokhov5d6341c2007-04-04 10:40:57 +0200320 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321}
322
323static void usb_kbd_disconnect(struct usb_interface *intf)
324{
325 struct usb_kbd *kbd = usb_get_intfdata (intf);
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500326
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327 usb_set_intfdata(intf, NULL);
328 if (kbd) {
329 usb_kill_urb(kbd->irq);
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500330 input_unregister_device(kbd->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
332 kfree(kbd);
333 }
334}
335
336static struct usb_device_id usb_kbd_id_table [] = {
Michael Opdenacker4ef2e232007-02-21 22:51:25 +0100337 { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
338 USB_INTERFACE_PROTOCOL_KEYBOARD) },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 { } /* Terminating entry */
340};
341
342MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);
343
344static struct usb_driver usb_kbd_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 .name = "usbkbd",
346 .probe = usb_kbd_probe,
347 .disconnect = usb_kbd_disconnect,
348 .id_table = usb_kbd_id_table,
349};
350
351static int __init usb_kbd_init(void)
352{
353 int result = usb_register(&usb_kbd_driver);
354 if (result == 0)
355 info(DRIVER_VERSION ":" DRIVER_DESC);
356 return result;
357}
358
359static void __exit usb_kbd_exit(void)
360{
361 usb_deregister(&usb_kbd_driver);
362}
363
364module_init(usb_kbd_init);
365module_exit(usb_kbd_exit);