blob: a948605564fbd8ca63ceadfb2e6f2431678de433 [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;
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 dma_addr_t new_dma;
78 dma_addr_t leds_dma;
79};
80
David Howells7d12e782006-10-05 14:55:46 +010081static void usb_kbd_irq(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -070082{
83 struct usb_kbd *kbd = urb->context;
84 int i;
85
86 switch (urb->status) {
87 case 0: /* success */
88 break;
89 case -ECONNRESET: /* unlink */
90 case -ENOENT:
91 case -ESHUTDOWN:
92 return;
93 /* -EPIPE: should clear the halt */
94 default: /* error */
95 goto resubmit;
96 }
97
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 for (i = 0; i < 8; i++)
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -050099 input_report_key(kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100
101 for (i = 2; i < 8; i++) {
102
103 if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) {
104 if (usb_kbd_keycode[kbd->old[i]])
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500105 input_report_key(kbd->dev, usb_kbd_keycode[kbd->old[i]], 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 else
Greg Kroah-Hartmanddbe3242008-10-12 00:14:23 +0200107 dev_info(&urb->dev->dev,
108 "Unknown key (scancode %#x) released.\n", kbd->old[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 }
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
Greg Kroah-Hartmanddbe3242008-10-12 00:14:23 +0200115 dev_info(&urb->dev->dev,
116 "Unknown key (scancode %#x) released.\n", kbd->new[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 }
118 }
119
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500120 input_sync(kbd->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121
122 memcpy(kbd->old, kbd->new, 8);
123
124resubmit:
Christoph Lameter54e6ecb2006-12-06 20:33:16 -0800125 i = usb_submit_urb (urb, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 if (i)
Jiri Kosina58037eb2007-05-30 15:07:13 +0200127 err_hid ("can't resubmit intr, %s-%s/input0, status %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 kbd->usbdev->bus->bus_name,
129 kbd->usbdev->devpath, i);
130}
131
Adrian Bunkbe5e33832005-04-22 15:07:00 -0700132static int usb_kbd_event(struct input_dev *dev, unsigned int type,
133 unsigned int code, int value)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134{
Dmitry Torokhove0712982007-05-09 10:17:31 +0200135 struct usb_kbd *kbd = input_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136
137 if (type != EV_LED)
138 return -1;
139
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 kbd->newleds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) |
141 (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) |
142 (!!test_bit(LED_NUML, dev->led));
143
144 if (kbd->led->status == -EINPROGRESS)
145 return 0;
146
147 if (*(kbd->leds) == kbd->newleds)
148 return 0;
149
150 *(kbd->leds) = kbd->newleds;
151 kbd->led->dev = kbd->usbdev;
152 if (usb_submit_urb(kbd->led, GFP_ATOMIC))
Jiri Kosina58037eb2007-05-30 15:07:13 +0200153 err_hid("usb_submit_urb(leds) failed");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154
155 return 0;
156}
157
David Howells7d12e782006-10-05 14:55:46 +0100158static void usb_kbd_led(struct urb *urb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159{
160 struct usb_kbd *kbd = urb->context;
161
162 if (urb->status)
From: Greg Kroah-Hartman7d89fe12008-10-12 00:25:51 +0200163 dev_warn(&urb->dev->dev, "led urb status %d received\n",
164 urb->status);
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500165
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 if (*(kbd->leds) == kbd->newleds)
167 return;
168
169 *(kbd->leds) = kbd->newleds;
170 kbd->led->dev = kbd->usbdev;
171 if (usb_submit_urb(kbd->led, GFP_ATOMIC))
Jiri Kosina58037eb2007-05-30 15:07:13 +0200172 err_hid("usb_submit_urb(leds) failed");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173}
174
175static int usb_kbd_open(struct input_dev *dev)
176{
Dmitry Torokhove0712982007-05-09 10:17:31 +0200177 struct usb_kbd *kbd = input_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 kbd->irq->dev = kbd->usbdev;
Dmitry Torokhov65cde542005-05-29 02:29:38 -0500180 if (usb_submit_urb(kbd->irq, GFP_KERNEL))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182
183 return 0;
184}
185
186static void usb_kbd_close(struct input_dev *dev)
187{
Dmitry Torokhove0712982007-05-09 10:17:31 +0200188 struct usb_kbd *kbd = input_get_drvdata(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189
Dmitry Torokhov65cde542005-05-29 02:29:38 -0500190 usb_kill_urb(kbd->irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191}
192
193static int usb_kbd_alloc_mem(struct usb_device *dev, struct usb_kbd *kbd)
194{
195 if (!(kbd->irq = usb_alloc_urb(0, GFP_KERNEL)))
196 return -1;
197 if (!(kbd->led = usb_alloc_urb(0, GFP_KERNEL)))
198 return -1;
Daniel Mack997ea582010-04-12 13:17:25 +0200199 if (!(kbd->new = usb_alloc_coherent(dev, 8, GFP_ATOMIC, &kbd->new_dma)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 return -1;
Alan Stern0ede76f2010-03-05 15:10:17 -0500201 if (!(kbd->cr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 return -1;
Daniel Mack997ea582010-04-12 13:17:25 +0200203 if (!(kbd->leds = usb_alloc_coherent(dev, 1, GFP_ATOMIC, &kbd->leds_dma)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 return -1;
205
206 return 0;
207}
208
209static void usb_kbd_free_mem(struct usb_device *dev, struct usb_kbd *kbd)
210{
Mariusz Kozlowski4ba0b2e2006-11-08 15:35:58 +0100211 usb_free_urb(kbd->irq);
212 usb_free_urb(kbd->led);
Daniel Mack997ea582010-04-12 13:17:25 +0200213 usb_free_coherent(dev, 8, kbd->new, kbd->new_dma);
Alan Stern0ede76f2010-03-05 15:10:17 -0500214 kfree(kbd->cr);
Daniel Mack997ea582010-04-12 13:17:25 +0200215 usb_free_coherent(dev, 1, kbd->leds, kbd->leds_dma);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216}
217
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500218static int usb_kbd_probe(struct usb_interface *iface,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 const struct usb_device_id *id)
220{
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500221 struct usb_device *dev = interface_to_usbdev(iface);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 struct usb_host_interface *interface;
223 struct usb_endpoint_descriptor *endpoint;
224 struct usb_kbd *kbd;
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500225 struct input_dev *input_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 int i, pipe, maxp;
Dmitry Torokhov5d6341c2007-04-04 10:40:57 +0200227 int error = -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228
229 interface = iface->cur_altsetting;
230
231 if (interface->desc.bNumEndpoints != 1)
232 return -ENODEV;
233
234 endpoint = &interface->endpoint[0].desc;
Luiz Fernando N. Capitulinoa20c3142006-10-26 13:02:59 -0300235 if (!usb_endpoint_is_int_in(endpoint))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 return -ENODEV;
237
238 pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
239 maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
240
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500241 kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);
242 input_dev = input_allocate_device();
243 if (!kbd || !input_dev)
244 goto fail1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500246 if (usb_kbd_alloc_mem(dev, kbd))
247 goto fail2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
249 kbd->usbdev = dev;
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500250 kbd->dev = input_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500252 if (dev->manufacturer)
253 strlcpy(kbd->name, dev->manufacturer, sizeof(kbd->name));
254
255 if (dev->product) {
256 if (dev->manufacturer)
257 strlcat(kbd->name, " ", sizeof(kbd->name));
258 strlcat(kbd->name, dev->product, sizeof(kbd->name));
259 }
260
261 if (!strlen(kbd->name))
262 snprintf(kbd->name, sizeof(kbd->name),
263 "USB HIDBP Keyboard %04x:%04x",
264 le16_to_cpu(dev->descriptor.idVendor),
265 le16_to_cpu(dev->descriptor.idProduct));
266
267 usb_make_path(dev, kbd->phys, sizeof(kbd->phys));
Márton Németh6236dfa2009-11-23 08:26:38 -0800268 strlcat(kbd->phys, "/input0", sizeof(kbd->phys));
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500269
270 input_dev->name = kbd->name;
271 input_dev->phys = kbd->phys;
272 usb_to_input_id(dev, &input_dev->id);
Dmitry Torokhove0712982007-05-09 10:17:31 +0200273 input_dev->dev.parent = &iface->dev;
274
275 input_set_drvdata(input_dev, kbd);
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500276
Jiri Slaby7b19ada2007-10-18 23:40:32 -0700277 input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
278 BIT_MASK(EV_REP);
279 input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
280 BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_COMPOSE) |
281 BIT_MASK(LED_KANA);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282
283 for (i = 0; i < 255; i++)
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500284 set_bit(usb_kbd_keycode[i], input_dev->keybit);
285 clear_bit(0, input_dev->keybit);
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500286
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500287 input_dev->event = usb_kbd_event;
288 input_dev->open = usb_kbd_open;
289 input_dev->close = usb_kbd_close;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290
291 usb_fill_int_urb(kbd->irq, dev, pipe,
292 kbd->new, (maxp > 8 ? 8 : maxp),
293 usb_kbd_irq, kbd, endpoint->bInterval);
294 kbd->irq->transfer_dma = kbd->new_dma;
295 kbd->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
296
297 kbd->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
298 kbd->cr->bRequest = 0x09;
299 kbd->cr->wValue = cpu_to_le16(0x200);
300 kbd->cr->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
301 kbd->cr->wLength = cpu_to_le16(1);
302
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 usb_fill_control_urb(kbd->led, dev, usb_sndctrlpipe(dev, 0),
304 (void *) kbd->cr, kbd->leds, 1,
305 usb_kbd_led, kbd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 kbd->led->transfer_dma = kbd->leds_dma;
Alan Stern0ede76f2010-03-05 15:10:17 -0500307 kbd->led->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
Dmitry Torokhov5d6341c2007-04-04 10:40:57 +0200309 error = input_register_device(kbd->dev);
310 if (error)
311 goto fail2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312
313 usb_set_intfdata(iface, kbd);
Alan Stern3d615102010-04-02 13:21:58 -0400314 device_set_wakeup_enable(&dev->dev, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 return 0;
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500316
Dmitry Torokhov5d6341c2007-04-04 10:40:57 +0200317fail2:
318 usb_kbd_free_mem(dev, kbd);
319fail1:
320 input_free_device(input_dev);
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500321 kfree(kbd);
Dmitry Torokhov5d6341c2007-04-04 10:40:57 +0200322 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323}
324
325static void usb_kbd_disconnect(struct usb_interface *intf)
326{
327 struct usb_kbd *kbd = usb_get_intfdata (intf);
Dmitry Torokhov05f091ab2005-05-29 02:29:01 -0500328
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 usb_set_intfdata(intf, NULL);
330 if (kbd) {
331 usb_kill_urb(kbd->irq);
Dmitry Torokhovc5b7c7c2005-09-15 02:01:47 -0500332 input_unregister_device(kbd->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333 usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
334 kfree(kbd);
335 }
336}
337
338static struct usb_device_id usb_kbd_id_table [] = {
Michael Opdenacker4ef2e232007-02-21 22:51:25 +0100339 { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT,
340 USB_INTERFACE_PROTOCOL_KEYBOARD) },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 { } /* Terminating entry */
342};
343
344MODULE_DEVICE_TABLE (usb, usb_kbd_id_table);
345
346static struct usb_driver usb_kbd_driver = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 .name = "usbkbd",
348 .probe = usb_kbd_probe,
349 .disconnect = usb_kbd_disconnect,
350 .id_table = usb_kbd_id_table,
351};
352
353static int __init usb_kbd_init(void)
354{
355 int result = usb_register(&usb_kbd_driver);
356 if (result == 0)
Greg Kroah-Hartmanddbe3242008-10-12 00:14:23 +0200357 printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
358 DRIVER_DESC "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 return result;
360}
361
362static void __exit usb_kbd_exit(void)
363{
364 usb_deregister(&usb_kbd_driver);
365}
366
367module_init(usb_kbd_init);
368module_exit(usb_kbd_exit);