9
0
Fork 0
barebox/drivers/input/usb_kbd.c

246 lines
7.0 KiB
C

/*
* USB keyboard driver for barebox
*
* (C) Copyright 2001 Denis Peter, MPL AG Switzerland
* (C) Copyright 2015 Peter Mamonov
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <common.h>
#include <init.h>
#include <clock.h>
#include <poller.h>
#include <usb/usb.h>
#include <string.h>
#include <dma.h>
#include <input/input.h>
/*
* NOTE: It's important for the NUM, CAPS, SCROLL-lock bits to be in this
* order. See usb_kbd_setled() function!
*/
#define USB_KBD_NUMLOCK (1 << 0)
#define USB_KBD_CAPSLOCK (1 << 1)
#define USB_KBD_SCROLLLOCK (1 << 2)
#define USB_KBD_CTRL (1 << 3)
#define USB_KBD_LEDMASK \
(USB_KBD_NUMLOCK | USB_KBD_CAPSLOCK | USB_KBD_SCROLLLOCK)
/*
* USB Keyboard reports are 8 bytes in boot protocol.
* Appendix B of HID Device Class Definition 1.11
*/
#define USB_KBD_BOOT_REPORT_SIZE 8
struct usb_kbd_pdata;
struct usb_kbd_pdata {
uint8_t *new;
uint8_t old[USB_KBD_BOOT_REPORT_SIZE];
struct poller_async poller;
struct usb_device *usbdev;
unsigned long intpipe;
int intpktsize;
int intinterval;
struct usb_endpoint_descriptor *ep;
int (*do_poll)(struct usb_kbd_pdata *);
struct input_device input;
};
static void usb_kbd_release_all_keys(struct usb_kbd_pdata *data)
{
int i;
for (i = 0; i <= KEY_MAX; i++)
input_report_key_event(&data->input, i, 0);
}
static int usb_kbd_int_poll(struct usb_kbd_pdata *data)
{
return usb_submit_int_msg(data->usbdev, data->intpipe, data->new,
data->intpktsize, data->intinterval);
}
static int usb_kbd_cnt_poll(struct usb_kbd_pdata *data)
{
struct usb_interface *iface = &data->usbdev->config.interface[0];
return usb_get_report(data->usbdev, iface->desc.bInterfaceNumber,
1, 0, data->new, USB_KBD_BOOT_REPORT_SIZE);
}
static const unsigned char usb_kbd_keycode[256] = {
0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38,
50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26,
27, 43, 43, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106,
105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71,
72, 73, 82, 83, 86,127,116,117,183,184,185,186,187,188,189,190,
191,192,193,194,134,138,130,132,128,129,131,137,133,135,136,113,
115,114, 0, 0, 0,121, 0, 89, 93,124, 92, 94, 95, 0, 0, 0,
122,123, 90, 91, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113,
150,158,159,128,136,177,178,176,142,152,173,140
};
static void usb_kbd_poll(void *arg)
{
struct usb_kbd_pdata *data = arg;
struct usb_device *usbdev = data->usbdev;
int ret, i;
ret = data->do_poll(data);
if (ret < 0)
usb_kbd_release_all_keys(data);
if (ret == -EAGAIN)
goto exit;
if (ret < 0) {
/* exit with noreturn */
dev_err(&usbdev->dev,
"usb_submit_int_msg() failed. Keyboard disconnect?\n");
return;
}
if (!memcmp(data->old, data->new, USB_KBD_BOOT_REPORT_SIZE))
goto exit;
pr_debug("%s: new report: %016llx\n", __func__, *((volatile uint64_t *)data->new));
for (i = 0; i < 8; i++)
input_report_key_event(&data->input, usb_kbd_keycode[i + 224], (data->new[0] >> i) & 1);
for (i = 2; i < 8; i++) {
if (data->old[i] > 3 && memscan(data->new + 2, data->old[i], 6) == data->new + 8) {
if (usb_kbd_keycode[data->old[i]])
input_report_key_event(&data->input, usb_kbd_keycode[data->old[i]], 0);
else
dev_err(&usbdev->dev,
"Unknown key (scancode %#x) released.\n",
data->old[i]);
}
if (data->new[i] > 3 && memscan(data->old + 2, data->new[i], 6) == data->old + 8) {
if (usb_kbd_keycode[data->new[i]])
input_report_key_event(&data->input, usb_kbd_keycode[data->new[i]], 1);
else
dev_err(&usbdev->dev,
"Unknown key (scancode %#x) pressed.\n",
data->new[i]);
}
}
memcpy(data->old, data->new, USB_KBD_BOOT_REPORT_SIZE);
exit:
poller_call_async(&data->poller, data->intinterval * MSECOND, usb_kbd_poll, data);
}
static int usb_kbd_probe(struct usb_device *usbdev,
const struct usb_device_id *id)
{
int ret;
struct usb_interface *iface = &usbdev->config.interface[0];
struct usb_kbd_pdata *data;
dev_info(&usbdev->dev, "USB keyboard found\n");
dev_dbg(&usbdev->dev, "Debug enabled\n");
ret = usb_set_protocol(usbdev, iface->desc.bInterfaceNumber, 0);
if (ret < 0)
return ret;
ret = usb_set_idle(usbdev, iface->desc.bInterfaceNumber, 1, 0);
if (ret < 0)
return ret;
data = xzalloc(sizeof(struct usb_kbd_pdata));
usbdev->drv_data = data;
data->new = dma_alloc(USB_KBD_BOOT_REPORT_SIZE);
data->usbdev = usbdev;
data->ep = &iface->ep_desc[0];
data->intpipe = usb_rcvintpipe(usbdev, data->ep->bEndpointAddress);
data->intpktsize = min(usb_maxpacket(usbdev, data->intpipe),
USB_KBD_BOOT_REPORT_SIZE);
data->intinterval = data->ep->bInterval;
/* test polling via interrupt endpoint */
data->do_poll = usb_kbd_int_poll;
ret = data->do_poll(data);
if (ret < 0) {
/* fall back to polling via control enpoint */
data->do_poll = usb_kbd_cnt_poll;
usb_set_idle(usbdev,
iface->desc.bInterfaceNumber, 0, 0);
ret = data->do_poll(data);
if (ret < 0) {
/* no luck */
dma_free(data->new);
free(data);
return ret;
} else
dev_dbg(&usbdev->dev, "poll keyboard via cont ep\n");
} else
dev_dbg(&usbdev->dev, "poll keyboard via int ep\n");
ret = input_device_register(&data->input);
if (ret) {
dev_err(&usbdev->dev, "can't register input\n");
return ret;
}
ret = poller_async_register(&data->poller);
if (ret) {
dev_err(&usbdev->dev, "can't setup poller\n");
return ret;
}
poller_call_async(&data->poller, data->intinterval * MSECOND, usb_kbd_poll, data);
return 0;
}
static void usb_kbd_disconnect(struct usb_device *usbdev)
{
struct usb_kbd_pdata *data = usbdev->drv_data;
usb_kbd_release_all_keys(data);
poller_async_unregister(&data->poller);
input_device_unregister(&data->input);
dma_free(data->new);
free(data);
}
static struct usb_device_id usb_kbd_usb_ids[] = {
{ USB_INTERFACE_INFO(3, 1, 1) }, // usb keyboard
{ }
};
static struct usb_driver usb_kbd_driver = {
.name = "usb-keyboard",
.id_table = usb_kbd_usb_ids,
.probe = usb_kbd_probe,
.disconnect = usb_kbd_disconnect,
};
static int __init usb_kbd_init(void)
{
return usb_driver_register(&usb_kbd_driver);
}
device_initcall(usb_kbd_init);