blob: b2fd0b00de9276504a1e87f31ebb068eaa8cc755 [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
Greg Kroah-Hartmanddbe3242008-10-12 00:14:23 +0200108 dev_info(&urb->dev->dev,
109 "Unknown key (scancode %#x) released.\n", kbd->old[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 }
111
112 if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) {
113 if (usb_kbd_keycode[kbd->new[i]])
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500114 input_report_key(kbd->dev, usb_kbd_keycode[kbd->new[i]], 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 else
Greg Kroah-Hartmanddbe3242008-10-12 00:14:23 +0200116 dev_info(&urb->dev->dev,
117 "Unknown key (scancode %#x) released.\n", kbd->new[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 }
119 }
120
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500121 input_sync(kbd->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122
123 memcpy(kbd->old, kbd->new, 8);
124
125resubmit:
Christoph Lameter54e6ecb2006-12-06 20:33:16 -0800126 i = usb_submit_urb (urb, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 if (i)
Jiri Kosina58037eb2007-05-30 15:07:13 +0200128 err_hid ("can't resubmit intr, %s-%s/input0, status %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 kbd->usbdev->bus->bus_name,
130 kbd->usbdev->devpath, i);
131}
132
Adrian Bunkbe5e33832005-04-22 15:07:00 -0700133static int usb_kbd_event(struct input_dev *dev, unsigned int type,
134 unsigned int code, int value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135{
Dmitry Torokhove0712982007-05-09 10:17:31 +0200136 struct usb_kbd *kbd = input_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137
138 if (type != EV_LED)
139 return -1;
140
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
142 (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) |
143 (!!test_bit(LED_NUML, dev->led));
144
145 if (kbd->led->status == -EINPROGRESS)
146 return 0;
147
148 if (*(kbd->leds) == kbd->newleds)
149 return 0;
150
151 *(kbd->leds) = kbd->newleds;
152 kbd->led->dev = kbd->usbdev;
153 if (usb_submit_urb(kbd->led, GFP_ATOMIC))
Jiri Kosina58037eb2007-05-30 15:07:13 +0200154 err_hid("usb_submit_urb(leds) failed");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155
156 return 0;
157}
158
David Howells7d12e782006-10-05 14:55:46 +0100159static void usb_kbd_led(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160{
161 struct usb_kbd *kbd = urb->context;
162
163 if (urb->status)
From: Greg Kroah-Hartman7d89fe12008-10-12 00:25:51 +0200164 dev_warn(&urb->dev->dev, "led urb status %d received\n",
165 urb->status);
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500166
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 if (*(kbd->leds) == kbd->newleds)
168 return;
169
170 *(kbd->leds) = kbd->newleds;
171 kbd->led->dev = kbd->usbdev;
172 if (usb_submit_urb(kbd->led, GFP_ATOMIC))
Jiri Kosina58037eb2007-05-30 15:07:13 +0200173 err_hid("usb_submit_urb(leds) failed");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174}
175
176static int usb_kbd_open(struct input_dev *dev)
177{
Dmitry Torokhove0712982007-05-09 10:17:31 +0200178 struct usb_kbd *kbd = input_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 kbd->irq->dev = kbd->usbdev;
Dmitry Torokhov65cde542005-05-29 02:29:38 -0500181 if (usb_submit_urb(kbd->irq, GFP_KERNEL))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183
184 return 0;
185}
186
187static void usb_kbd_close(struct input_dev *dev)
188{
Dmitry Torokhove0712982007-05-09 10:17:31 +0200189 struct usb_kbd *kbd = input_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190
Dmitry Torokhov65cde542005-05-29 02:29:38 -0500191 usb_kill_urb(kbd->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192}
193
194static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
195{
196 if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))
197 return -1;
198 if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))
199 return -1;
Christoph Lameter54e6ecb2006-12-06 20:33:16 -0800200 if (!(kbd->new = usb_buffer_alloc(dev, 8, GFP_ATOMIC, &kbd->new_dma)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 return -1;
Christoph Lameter54e6ecb2006-12-06 20:33:16 -0800202 if (!(kbd->cr = usb_buffer_alloc(dev, sizeof(struct usb_ctrlrequest), GFP_ATOMIC, &kbd->cr_dma)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 return -1;
Christoph Lameter54e6ecb2006-12-06 20:33:16 -0800204 if (!(kbd->leds = usb_buffer_alloc(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 return -1;
206
207 return 0;
208}
209
210static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)
211{
Mariusz Kozlowski4ba0b2e2006-11-08 15:35:58 +0100212 usb_free_urb(kbd->irq);
213 usb_free_urb(kbd->led);
Dmitry Torokhov6675c5b2007-05-03 01:04:52 -0400214 usb_buffer_free(dev, 8, kbd->new, kbd->new_dma);
215 usb_buffer_free(dev, sizeof(struct usb_ctrlrequest), kbd->cr, kbd->cr_dma);
216 usb_buffer_free(dev, 1, kbd->leds, kbd->leds_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217}
218
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500219static int usb_kbd_probe(struct usb_interface *iface,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 const struct usb_device_id *id)
221{
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500222 struct usb_device *dev = interface_to_usbdev(iface);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 struct usb_host_interface *interface;
224 struct usb_endpoint_descriptor *endpoint;
225 struct usb_kbd *kbd;
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500226 struct input_dev *input_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 int i, pipe, maxp;
Dmitry Torokhov5d6341c2007-04-04 10:40:57 +0200228 int error = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229
230 interface = iface->cur_altsetting;
231
232 if (interface->desc.bNumEndpoints != 1)
233 return -ENODEV;
234
235 endpoint = &interface->endpoint[0].desc;
Luiz Fernando N. Capitulinoa20c3142006-10-26 13:02:59 -0300236 if (!usb_endpoint_is_int_in(endpoint))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 return -ENODEV;
238
239 pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
240 maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
241
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500242 kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);
243 input_dev = input_allocate_device();
244 if (!kbd || !input_dev)
245 goto fail1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500247 if (usb_kbd_alloc_mem(dev, kbd))
248 goto fail2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
250 kbd->usbdev = dev;
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500251 kbd->dev = input_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500253 if (dev->manufacturer)
254 strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
255
256 if (dev->product) {
257 if (dev->manufacturer)
258 strlcat(kbd->name, " ", sizeof(kbd->name));
259 strlcat(kbd->name, dev->product, sizeof(kbd->name));
260 }
261
262 if (!strlen(kbd->name))
263 snprintf(kbd->name, sizeof(kbd->name),
264 "USB HIDBP Keyboard %04x:%04x",
265 le16_to_cpu(dev->descriptor.idVendor),
266 le16_to_cpu(dev->descriptor.idProduct));
267
268 usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
Márton Németh6236dfa2009-11-23 08:26:38 -0800269 strlcat(kbd->phys, "/input0", sizeof(kbd->phys));
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500270
271 input_dev->name = kbd->name;
272 input_dev->phys = kbd->phys;
273 usb_to_input_id(dev, &input_dev->id);
Dmitry Torokhove0712982007-05-09 10:17:31 +0200274 input_dev->dev.parent = &iface->dev;
275
276 input_set_drvdata(input_dev, kbd);
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500277
Jiri Slaby7b19ada2007-10-18 23:40:32 -0700278 input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
279 BIT_MASK(EV_REP);
280 input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
281 BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) |
282 BIT_MASK(LED_KANA);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283
284 for (i = 0; i < 255; i++)
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500285 set_bit(usb_kbd_keycode[i], input_dev->keybit);
286 clear_bit(0, input_dev->keybit);
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500287
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500288 input_dev->event = usb_kbd_event;
289 input_dev->open = usb_kbd_open;
290 input_dev->close = usb_kbd_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291
292 usb_fill_int_urb(kbd->irq, dev, pipe,
293 kbd->new, (maxp > 8 ? 8 : maxp),
294 usb_kbd_irq, kbd, endpoint->bInterval);
295 kbd->irq->transfer_dma = kbd->new_dma;
296 kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
297
298 kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
299 kbd->cr->bRequest = 0x09;
300 kbd->cr->wValue = cpu_to_le16(0x200);
301 kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
302 kbd->cr->wLength = cpu_to_le16(1);
303
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
305 (void *) kbd->cr, kbd->leds, 1,
306 usb_kbd_led, kbd);
307 kbd->led->setup_dma = kbd->cr_dma;
308 kbd->led->transfer_dma = kbd->leds_dma;
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500309 kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
Dmitry Torokhov5d6341c2007-04-04 10:40:57 +0200311 error = input_register_device(kbd->dev);
312 if (error)
313 goto fail2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314
315 usb_set_intfdata(iface, kbd);
Alan Stern3d615102010-04-02 13:21:58 -0400316 device_set_wakeup_enable(&dev->dev, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 return 0;
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500318
Dmitry Torokhov5d6341c2007-04-04 10:40:57 +0200319fail2:
320 usb_kbd_free_mem(dev, kbd);
321fail1:
322 input_free_device(input_dev);
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500323 kfree(kbd);
Dmitry Torokhov5d6341c2007-04-04 10:40:57 +0200324 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325}
326
327static void usb_kbd_disconnect(struct usb_interface *intf)
328{
329 struct usb_kbd *kbd = usb_get_intfdata (intf);
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500330
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 usb_set_intfdata(intf, NULL);
332 if (kbd) {
333 usb_kill_urb(kbd->irq);
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500334 input_unregister_device(kbd->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
336 kfree(kbd);
337 }
338}
339
340static struct usb_device_id usb_kbd_id_table [] = {
Michael Opdenacker4ef2e232007-02-21 22:51:25 +0100341 { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
342 USB_INTERFACE_PROTOCOL_KEYBOARD) },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 { } /* Terminating entry */
344};
345
346MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);
347
348static struct usb_driver usb_kbd_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 .name = "usbkbd",
350 .probe = usb_kbd_probe,
351 .disconnect = usb_kbd_disconnect,
352 .id_table = usb_kbd_id_table,
353};
354
355static int __init usb_kbd_init(void)
356{
357 int result = usb_register(&usb_kbd_driver);
358 if (result == 0)
Greg Kroah-Hartmanddbe3242008-10-12 00:14:23 +0200359 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
360 DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 return result;
362}
363
364static void __exit usb_kbd_exit(void)
365{
366 usb_deregister(&usb_kbd_driver);
367}
368
369module_init(usb_kbd_init);
370module_exit(usb_kbd_exit);