summaryrefslogtreecommitdiffstats
path: root/qemu/roms/openbios/drivers/usbhid.c
blob: a423278a85def4f5ab0f0a32636bd9a74bd3f15a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
Project: Base System Functionality Testing Project (functest)
Project Creation Date: January 20, 2015
Project Category: Integration & Testing
Lifecycle State: Incubation
Primary Contact: Jose Lausuch (jose.lausuch@ericsson.com)
Project Lead: Jose lausuch (jose.lausuch@ericsson.com)
Jira Project Name: Base System Functionality Testing Project
Jira Project Prefix: FUNCTEST
Mailing list tag: [functest]
IRC: Server:freenode.net Channel:#opnfv-functest
Repository: functest

Committers:
Morgan Richomme <morgan.richomme@orange.com>
Jose Lausuch <jose.lausuch@ericsson.com>
Cedric Ollivier <cedric.ollivier@orange.com>
Helen Yao <yaohelan@huawei.com>
Serena Feng <feng.xiaowei@zte.com.cn>
Juha Kosonen <juha.kosonen@nokia.com>
Valentin Boucher <valentin.boucher@orange.com>
Viktor Tikkanen <viktor.tikkanen@nokia.com>
Mei Mei <meimei@huawei.com>
Linda Wang <wangwulin@huawei.com>

Additional contributors:
Georgios Paraskevopoulos <georgepar.91@gmail.com>
Romanos Skiadas <rom.skiad@gmail.com>
Michael Polenchuk <mpolenchuk@mirantis.com>
Cristina Pauna <cristina.pauna@enea.com>
Matthew Li <matthew.lijun@huawei.com>
Steven Pisarski <s.pisarski@cablelabs.com>

Link to TSC approval of the project: http://meetbot.opnfv.org/meetings/opnfv-meeting/2015/opnfv-meeting.2015-01-20-14.57.html

Link(s) to approval of additional committers:

http://lists.opnfv.org/pipermail/opnfv-tech-discuss/2015-April/001971.html
http://ircbot.wl.linuxfoundation.org/meetings/opnfv-testperf/2015/opnfv-testperf.2015-09-29-13.00.html
http://ircbot.wl.linuxfoundation.org/meetings/opnfv-testperf/2016/opnfv-testperf.2016-03-01-08.00.html
http://ircbot.wl.linuxfoundation.org/meetings/opnfv-functest/2016/opnfv-functest.2016-10-11-08.01.html
04' href='#n404'>404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
/*
 * Driver for HID devices ported from CoreBoot
 *
 * Copyright (C) 2014 BALATON Zoltan
 *
 * This file was part of the libpayload project.
 *
 * Copyright (C) 2008-2010 coresystems GmbH
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "config.h"
#include "libopenbios/bindings.h"
#include <libc/string.h>
#include "libc/byteorder.h"
#include "libc/vsprintf.h"
#include "drivers/usb.h"
#include "usb.h"

DECLARE_UNNAMED_NODE(usb_kbd, INSTALL_OPEN, sizeof(int));

static void
keyboard_open(int *idx)
{
	RET(-1);
}

static void
keyboard_close(int *idx)
{
}

static void keyboard_read(void);

NODE_METHODS( usb_kbd ) = {
	{ "open",		keyboard_open		},
	{ "close",		keyboard_close		},
	{ "read",               keyboard_read		},
};

#ifdef CONFIG_USB_DEBUG
static const char *boot_protos[3] = { "(none)", "keyboard", "mouse" };
#endif
typedef enum { hid_proto_boot = 0, hid_proto_report = 1 } hid_proto;
enum { GET_REPORT = 0x1, GET_IDLE = 0x2, GET_PROTOCOL = 0x3, SET_REPORT =
		0x9, SET_IDLE = 0xa, SET_PROTOCOL = 0xb
};

typedef union {
	struct {
		u8 modifiers;
		u8 repeats;
		u8 keys[6];
	};
	u8 buffer[8];
} usb_hid_keyboard_event_t;

typedef struct {
	void* queue;
	hid_descriptor_t *descriptor;

	usb_hid_keyboard_event_t previous;
	int lastkeypress;
	int repeat_delay;
} usbhid_inst_t;

#define HID_INST(dev) ((usbhid_inst_t*)(dev)->data)

static void
usb_hid_destroy (usbdev_t *dev)
{
	if (HID_INST(dev)->queue) {
		int i;
		for (i = 0; i <= dev->num_endp; i++) {
			if (dev->endpoints[i].endpoint == 0)
				continue;
			if (dev->endpoints[i].type != INTERRUPT)
				continue;
			if (dev->endpoints[i].direction != IN)
				continue;
			break;
		}
		dev->controller->destroy_intr_queue(
				&dev->endpoints[i], HID_INST(dev)->queue);
		HID_INST(dev)->queue = NULL;
	}
	free (dev->data);
}

/* keybuffer is global to all USB keyboards */
static int keycount;
#define KEYBOARD_BUFFER_SIZE 16
static short keybuffer[KEYBOARD_BUFFER_SIZE];

const char *countries[36][2] = {
	{ "unknown", "us" },
	{ "Arabic", "ae" },
	{ "Belgian", "be" },
	{ "Canadian-Bilingual", "ca" },
	{ "Canadian-French", "ca" },
	{ "Czech Republic", "cz" },
	{ "Danish", "dk" },
	{ "Finnish", "fi" },
	{ "French", "fr" },
	{ "German", "de" },
	{ "Greek", "gr" },
	{ "Hebrew", "il" },
	{ "Hungary", "hu" },
	{ "International (ISO)", "iso" },
	{ "Italian", "it" },
	{ "Japan (Katakana)", "jp" },
	{ "Korean", "us" },
	{ "Latin American", "us" },
	{ "Netherlands/Dutch", "nl" },
	{ "Norwegian", "no" },
	{ "Persian (Farsi)", "ir" },
	{ "Poland", "pl" },
	{ "Portuguese", "pt" },
	{ "Russia", "ru" },
	{ "Slovakia", "sl" },
	{ "Spanish", "es" },
	{ "Swedish", "se" },
	{ "Swiss/French", "ch" },
	{ "Swiss/German", "ch" },
	{ "Switzerland", "ch" },
	{ "Taiwan", "tw" },
	{ "Turkish-Q", "tr" },
	{ "UK", "uk" },
	{ "US", "us" },
	{ "Yugoslavia", "yu" },
	{ "Turkish-F", "tr" },
	/* 36 - 255: Reserved */
};



struct layout_maps {
	const char *country;
	const short map[4][0x80];
};

static const struct layout_maps *map;

#define KEY_BREAK     0x101  /* Not on PC KBD */
#define KEY_DOWN      0x102  /* Down arrow key */
#define KEY_UP        0x103  /* Up arrow key */
#define KEY_LEFT      0x104  /* Left arrow key */
#define KEY_RIGHT     0x105  /* Right arrow key */
#define KEY_HOME      0x106  /* home key */
#define KEY_BACKSPACE 0x107  /* not on pc */
#define KEY_F0        0x108  /* function keys; 64 reserved */
#define KEY_F(n)      (KEY_F0 + (n))

#define KEY_DC        0x14a  /* delete character */
#define KEY_IC        0x14b  /* insert char or enter ins mode */

#define KEY_NPAGE     0x152  /* next page */
#define KEY_PPAGE     0x153  /* previous page */

#define KEY_ENTER     0x157  /* enter or send (unreliable) */

#define KEY_PRINT     0x15a  /* print/copy */

#define KEY_END       0x166  /* end key */

static const struct layout_maps keyboard_layouts[] = {
// #ifdef CONFIG_PC_KEYBOARD_LAYOUT_US
{ .country = "us", .map = {
	{ /* No modifier */
	-1, -1, -1, -1, 'a', 'b', 'c', 'd',
	'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
	/* 0x10 */
	'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
	'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
	/* 0x20 */
	'3', '4', '5', '6', '7', '8', '9', '0',
	'\n', '\e', '\b', '\t', ' ', '-', '=', '[',
	/* 0x30 */
	']', '\\', -1, ';', '\'', '`', ',', '.',
	'/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
	/* 0x40 */
	KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */,
	KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
	/* 50 */
	KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
	KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME,
	/* 60 */
	KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1,
	/* 70 */
	-1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1,
	 },
	{ /* Shift modifier */
	-1, -1, -1, -1, 'A', 'B', 'C', 'D',
	'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
	/* 0x10 */
	'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
	'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
	/* 0x20 */
	'#', '$', '%', '^', '&', '*', '(', ')',
	'\n', '\e', '\b', '\t', ' ', '_', '+', '{',
	/* 0x30 */
	'}', '|', -1, ':', '"', '~', '<', '>',
	'?', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
	/* 0x40 */
	KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */,
	KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
	/* 50 */
	KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
	KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME,
	/* 60 */
	KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1,
	/* 70 */
	-1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1,
	 },
	{ /* Alt */
	-1, -1, -1, -1, 'a', 'b', 'c', 'd',
	'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
	/* 0x10 */
	'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
	'u', 'v', 'w', 'x', 'y', 'z', '1', '2',
	/* 0x20 */
	'3', '4', '5', '6', '7', '8', '9', '0',
	'\n', '\e', '\b', '\t', ' ', '-', '=', '[',
	/* 0x30 */
	']', '\\', -1, ';', '\'', '`', ',', '.',
	'/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
	/* 0x40 */
	KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */,
	KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
	/* 50 */
	KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
	KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME,
	/* 60 */
	KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1,
	/* 70 */
	-1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1,
	 },
	{ /* Shift+Alt modifier */
	-1, -1, -1, -1, 'A', 'B', 'C', 'D',
	'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
	/* 0x10 */
	'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
	'U', 'V', 'W', 'X', 'Y', 'Z', '!', '@',
	/* 0x20 */
	'#', '$', '%', '^', '&', '*', '(', ')',
	'\n', '\e', '\b', '\t', ' ', '-', '=', '[',
	/* 0x30 */
	']', '\\', -1, ':', '\'', '`', ',', '.',
	'/', -1 /* CapsLk */, KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_F(5), KEY_F(6),
	/* 0x40 */
	KEY_F(7), KEY_F(8), KEY_F(9), KEY_F(10), KEY_F(11), KEY_F(12), KEY_PRINT, -1 /* ScrLk */,
	KEY_BREAK, KEY_IC, KEY_HOME, KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
	/* 50 */
	KEY_LEFT, KEY_DOWN, KEY_UP, -1 /*NumLck*/, '/', '*', '-' /* = ? */, '+',
	KEY_ENTER, KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, -1, KEY_RIGHT, KEY_HOME,
	/* 60 */
	KEY_UP, KEY_PPAGE, -1, KEY_DC, -1 /* < > | */, -1 /* Win Key Right */, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1,
	/* 70 */
	-1, -1, -1, -1, -1, -1, -1, -1,
	-1, -1, -1, -1, -1, -1, -1, -1,
	 }
}},
//#endif
};

#define MOD_SHIFT    (1 << 0)
#define MOD_ALT      (1 << 1)
#define MOD_CTRL     (1 << 2)

static void usb_hid_keyboard_queue(int ch) {
	/* ignore key presses if buffer full */
	if (keycount < KEYBOARD_BUFFER_SIZE)
		keybuffer[keycount++] = ch;
}

#define KEYBOARD_REPEAT_MS	30
#define INITIAL_REPEAT_DELAY	10
#define REPEAT_DELAY		 2

static void
usb_hid_process_keyboard_event(usbhid_inst_t *const inst,
		const usb_hid_keyboard_event_t *const current)
{
	const usb_hid_keyboard_event_t *const previous = &inst->previous;

	int i, keypress = 0, modifiers = 0;

	if (current->modifiers & 0x01) /* Left-Ctrl */   modifiers |= MOD_CTRL;
	if (current->modifiers & 0x02) /* Left-Shift */  modifiers |= MOD_SHIFT;
	if (current->modifiers & 0x04) /* Left-Alt */    modifiers |= MOD_ALT;
	if (current->modifiers & 0x08) /* Left-GUI */    ;
	if (current->modifiers & 0x10) /* Right-Ctrl */  modifiers |= MOD_CTRL;
	if (current->modifiers & 0x20) /* Right-Shift */ modifiers |= MOD_SHIFT;
	if (current->modifiers & 0x40) /* Right-AltGr */ modifiers |= MOD_ALT;
	if (current->modifiers & 0x80) /* Right-GUI */   ;

	/* Did the event change at all? */
	if (inst->lastkeypress &&
			!memcmp(current, previous, sizeof(*current))) {
		/* No. Then it's a key repeat event. */
		if (inst->repeat_delay) {
			inst->repeat_delay--;
		} else {
			usb_hid_keyboard_queue(inst->lastkeypress);
			inst->repeat_delay = REPEAT_DELAY;
		}

		return;
	}

	inst->lastkeypress = 0;

	for (i=0; i<6; i++) {
		int j;
		int skip = 0;
		// No more keys? skip
		if (current->keys[i] == 0)
			return;

		for (j=0; j<6; j++) {
			if (current->keys[i] == previous->keys[j]) {
				skip = 1;
				break;
			}
		}
		if (skip)
			continue;


		/* Mask off MOD_CTRL */
		keypress = map->map[modifiers & 0x03][current->keys[i]];

		if (modifiers & MOD_CTRL) {
			switch (keypress) {
			case 'a' ... 'z':
				keypress &= 0x1f;
				break;
			default:
				continue;
			}
		}

		if (keypress == -1) {
			/* Debug: Print unknown keys */
			usb_debug ("usbhid: <%x> %x [ %x %x %x %x %x %x ] %d\n",
				current->modifiers, current->repeats,
			current->keys[0], current->keys[1],
			current->keys[2], current->keys[3],
			current->keys[4], current->keys[5], i);

			/* Unknown key? Try next one in the queue */
			continue;
		}

		usb_hid_keyboard_queue(keypress);

		/* Remember for authentic key repeat */
		inst->lastkeypress = keypress;
		inst->repeat_delay = INITIAL_REPEAT_DELAY;
	}
}

static void
usb_hid_poll (usbdev_t *dev)
{
	usb_hid_keyboard_event_t current;
	const u8 *buf;

	while ((buf=dev->controller->poll_intr_queue (HID_INST(dev)->queue))) {
		memcpy(&current.buffer, buf, 8);
		usb_hid_process_keyboard_event(HID_INST(dev), &current);
		HID_INST(dev)->previous = current;
	}
}

static void
usb_hid_set_idle (usbdev_t *dev, interface_descriptor_t *interface, u16 duration)
{
	dev_req_t dr;
	dr.data_dir = host_to_device;
	dr.req_type = class_type;
	dr.req_recp = iface_recp;
	dr.bRequest = SET_IDLE;
	dr.wValue = __cpu_to_le16((duration >> 2) << 8);
	dr.wIndex = __cpu_to_le16(interface->bInterfaceNumber);
	dr.wLength = 0;
	dev->controller->control (dev, OUT, sizeof (dev_req_t), &dr, 0, 0);
}

static void
usb_hid_set_protocol (usbdev_t *dev, interface_descriptor_t *interface, hid_proto proto)
{
	dev_req_t dr;
	dr.data_dir = host_to_device;
	dr.req_type = class_type;
	dr.req_recp = iface_recp;
	dr.bRequest = SET_PROTOCOL;
	dr.wValue = __cpu_to_le16(proto);
	dr.wIndex = __cpu_to_le16(interface->bInterfaceNumber);
	dr.wLength = 0;
	dev->controller->control (dev, OUT, sizeof (dev_req_t), &dr, 0, 0);
}

static int usb_hid_set_layout (const char *country)
{
	/* FIXME should be per keyboard */
	int i;

	for (i=0; i<sizeof(keyboard_layouts)/sizeof(keyboard_layouts[0]); i++) {
		if (strncmp(keyboard_layouts[i].country, country,
					strlen(keyboard_layouts[i].country)))
			continue;

		/* Found, changing keyboard layout */
		map = &keyboard_layouts[i];
		usb_debug("  Keyboard layout '%s'\n", map->country);
		return 0;
	}

	usb_debug("  Keyboard layout '%s' not found, using '%s'\n",
			country, map->country);

	/* Nothing found, not changed */
	return -1;
}

void
usb_hid_init (usbdev_t *dev)
{
	configuration_descriptor_t *cd = (configuration_descriptor_t*)dev->configuration;
	interface_descriptor_t *interface = (interface_descriptor_t*)(((char *) cd) + cd->bLength);

	if (interface->bInterfaceSubClass == hid_subclass_boot) {
		u8 countrycode = 0;
		usb_debug ("  supports boot interface..\n");
		usb_debug ("  it's a %s\n",
			boot_protos[interface->bInterfaceProtocol]);
		switch (interface->bInterfaceProtocol) {
		case hid_boot_proto_keyboard:
			dev->data = malloc (sizeof (usbhid_inst_t));
			if (!dev->data) {
				printk("Not enough memory for USB HID device.\n");
				return;
                        }
			memset(&HID_INST(dev)->previous, 0x00,
			       sizeof(HID_INST(dev)->previous));
			usb_debug ("  configuring...\n");
			usb_hid_set_protocol(dev, interface, hid_proto_boot);
			usb_hid_set_idle(dev, interface, KEYBOARD_REPEAT_MS);
			usb_debug ("  activating...\n");
#if 0
			HID_INST (dev)->descriptor =
				(hid_descriptor_t *)
					get_descriptor(dev, gen_bmRequestType
					(device_to_host, standard_type, iface_recp),
					0x21, 0, 0);
			countrycode = HID_INST(dev)->descriptor->bCountryCode;
#endif
			/* 35 countries defined: */
			if (countrycode > 35)
				countrycode = 0;
			usb_debug ("  Keyboard has %s layout (country code %02x)\n",
					countries[countrycode][0], countrycode);

			/* Set keyboard layout accordingly */
			usb_hid_set_layout(countries[countrycode][1]);

			// only add here, because we only support boot-keyboard HID devices
			dev->destroy = usb_hid_destroy;
			dev->poll = usb_hid_poll;
			int i;
			for (i = 0; i <= dev->num_endp; i++) {
				if (dev->endpoints[i].endpoint == 0)
					continue;
				if (dev->endpoints[i].type != INTERRUPT)
					continue;
				if (dev->endpoints[i].direction != IN)
					continue;
				break;
			}
			usb_debug ("  found endpoint %x for interrupt-in\n", i);
			/* 20 buffers of 8 bytes, for every 10 msecs */
			HID_INST(dev)->queue = dev->controller->create_intr_queue (&dev->endpoints[i], 8, 20, 10);
			keycount = 0;
			usb_debug ("  configuration done.\n");
			break;
		default:
			usb_debug("NOTICE: HID interface protocol %d%s not supported.\n",
				 interface->bInterfaceProtocol,
				 (interface->bInterfaceProtocol == hid_boot_proto_mouse ?
				 " (USB mouse)" : ""));
			break;
		}
	}
}

static int usbhid_havechar (void)
{
	return (keycount != 0);
}

static int usbhid_getchar (void)
{
	short ret;

	if (keycount == 0)
		return 0;
	ret = keybuffer[0];
	memmove(keybuffer, keybuffer + 1, --keycount);

	return (int)ret;
}

/* ( addr len -- actual ) */
static void keyboard_read(void)
{
	char *addr;
	int len, key, i;

	usb_poll();
	len=POP();
	addr=(char *)cell2pointer(POP());

	for (i = 0; i < len; i++) {
	    if (!usbhid_havechar())
			break;
		key = usbhid_getchar();
		*addr++ = (char)key;
	}
	PUSH(i);
}

void ob_usb_hid_add_keyboard(const char *path)
{
	char name[128];
	phandle_t aliases;

	snprintf(name, sizeof(name), "%s/keyboard", path);
	usb_debug("Found keyboard at %s\n", name);
	REGISTER_NAMED_NODE(usb_kbd, name);

	push_str(name);
	fword("find-device");

	push_str("keyboard");
	fword("device-type");

	aliases = find_dev("/aliases");
	set_property(aliases, "adb-keyboard", name, strlen(name) + 1);
}