diff options
Diffstat (limited to 'qemu/roms/ipxe/src/drivers/usb/usbkbd.c')
-rw-r--r-- | qemu/roms/ipxe/src/drivers/usb/usbkbd.c | 509 |
1 files changed, 0 insertions, 509 deletions
diff --git a/qemu/roms/ipxe/src/drivers/usb/usbkbd.c b/qemu/roms/ipxe/src/drivers/usb/usbkbd.c deleted file mode 100644 index ea94f2e63..000000000 --- a/qemu/roms/ipxe/src/drivers/usb/usbkbd.c +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - * - * You can also choose to distribute this program under the terms of - * the Unmodified Binary Distribution Licence (as given in the file - * COPYING.UBDL), provided that you have satisfied its requirements. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <assert.h> -#include <ipxe/console.h> -#include <ipxe/keys.h> -#include <ipxe/usb.h> -#include "usbkbd.h" - -/** @file - * - * USB keyboard driver - * - */ - -/** List of USB keyboards */ -static LIST_HEAD ( usb_keyboards ); - -/****************************************************************************** - * - * Keyboard map - * - ****************************************************************************** - */ - -/** - * Map USB keycode to iPXE key - * - * @v keycode Keycode - * @v modifiers Modifiers - * @ret key iPXE key - * - * Key codes are defined in the USB HID Usage Tables Keyboard/Keypad - * page. - */ -static unsigned int usbkbd_map ( unsigned int keycode, - unsigned int modifiers ) { - unsigned int key; - - if ( keycode < USBKBD_KEY_A ) { - /* Not keys */ - key = 0; - } else if ( keycode <= USBKBD_KEY_Z ) { - /* Alphabetic keys */ - key = ( keycode - USBKBD_KEY_A + 'a' ); - if ( modifiers & USBKBD_CTRL ) { - key -= ( 'a' - CTRL_A ); - } else if ( modifiers & USBKBD_SHIFT ) { - key -= ( 'a' - 'A' ); - } - } else if ( keycode <= USBKBD_KEY_0 ) { - /* Numeric key row */ - if ( modifiers & USBKBD_SHIFT ) { - key = "!@#$%^&*()" [ keycode - USBKBD_KEY_1 ]; - } else { - key = ( ( ( keycode - USBKBD_KEY_1 + 1 ) % 10 ) + '0' ); - } - } else if ( keycode <= USBKBD_KEY_SPACE ) { - /* Unmodifiable keys */ - static const uint8_t unmodifable[] = - { LF, ESC, BACKSPACE, TAB, ' ' }; - key = unmodifable[ keycode - USBKBD_KEY_ENTER ]; - } else if ( keycode <= USBKBD_KEY_SLASH ) { - /* Punctuation keys */ - if ( modifiers & USBKBD_SHIFT ) { - key = "_+{}|~:\"~<>?" [ keycode - USBKBD_KEY_MINUS ]; - } else { - key = "-=[]\\#;'`,./" [ keycode - USBKBD_KEY_MINUS ]; - } - } else if ( keycode <= USBKBD_KEY_UP ) { - /* Special keys */ - static const uint16_t special[] = { - 0, 0, 0, 0, 0, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, - KEY_F10, KEY_F11, KEY_F12, 0, 0, 0, KEY_IC, KEY_HOME, - KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, - KEY_LEFT, KEY_DOWN, KEY_UP - }; - key = special[ keycode - USBKBD_KEY_CAPSLOCK ]; - } else { - key = 0; - } - - return key; -} - -/****************************************************************************** - * - * Keyboard buffer - * - ****************************************************************************** - */ - -/** - * Insert keypress into keyboard buffer - * - * @v kbd USB keyboard - * @v keycode Keycode - * @v modifiers Modifiers - */ -static void usbkbd_produce ( struct usb_keyboard *kbd, unsigned int keycode, - unsigned int modifiers ) { - unsigned int key; - - /* Map to iPXE key */ - key = usbkbd_map ( keycode, modifiers ); - - /* Do nothing if this keycode has no corresponding iPXE key */ - if ( ! key ) { - DBGC ( kbd, "KBD %s has no key for keycode %#02x:%#02x\n", - kbd->name, modifiers, keycode ); - return; - } - - /* Check for buffer overrun */ - if ( usbkbd_fill ( kbd ) >= USBKBD_BUFSIZE ) { - DBGC ( kbd, "KBD %s buffer overrun (key %#02x)\n", - kbd->name, key ); - return; - } - - /* Insert into buffer */ - kbd->key[ ( kbd->prod++ ) % USBKBD_BUFSIZE ] = key; - DBGC2 ( kbd, "KBD %s key %#02x produced\n", kbd->name, key ); -} - -/** - * Consume character from keyboard buffer - * - * @v kbd USB keyboard - * @ret character Character - */ -static unsigned int usbkbd_consume ( struct usb_keyboard *kbd ) { - static char buf[] = "\x1b[xx~"; - char *tmp = &buf[2]; - unsigned int key; - unsigned int character; - unsigned int ansi_n; - unsigned int len; - - /* Sanity check */ - assert ( usbkbd_fill ( kbd ) > 0 ); - - /* Get current keypress */ - key = kbd->key[ kbd->cons % USBKBD_BUFSIZE ]; - - /* If this is a straightforward key, just consume and return it */ - if ( key < KEY_MIN ) { - kbd->cons++; - DBGC2 ( kbd, "KBD %s key %#02x consumed\n", kbd->name, key ); - return key; - } - - /* Construct ANSI sequence */ - ansi_n = KEY_ANSI_N ( key ); - if ( ansi_n ) - tmp += sprintf ( tmp, "%d", ansi_n ); - *(tmp++) = KEY_ANSI_TERMINATOR ( key ); - *tmp = '\0'; - len = ( tmp - buf ); - assert ( len < sizeof ( buf ) ); - if ( kbd->subcons == 0 ) { - DBGC2 ( kbd, "KBD %s key %#02x consumed as ^[%s\n", - kbd->name, key, &buf[1] ); - } - - /* Extract character from ANSI sequence */ - assert ( kbd->subcons < len ); - character = buf[ kbd->subcons++ ]; - - /* Consume key if applicable */ - if ( kbd->subcons == len ) { - kbd->cons++; - kbd->subcons = 0; - } - - return character; -} - -/****************************************************************************** - * - * Keyboard report - * - ****************************************************************************** - */ - -/** - * Check for presence of keycode in report - * - * @v report Keyboard report - * @v keycode Keycode (must be non-zero) - * @ret has_keycode Keycode is present in report - */ -static int usbkbd_has_keycode ( struct usb_keyboard_report *report, - unsigned int keycode ) { - unsigned int i; - - /* Check for keycode */ - for ( i = 0 ; i < ( sizeof ( report->keycode ) / - sizeof ( report->keycode[0] ) ) ; i++ ) { - if ( report->keycode[i] == keycode ) - return keycode; - } - - return 0; -} - -/** - * Handle keyboard report - * - * @v kbd USB keyboard - * @v new New keyboard report - */ -static void usbkbd_report ( struct usb_keyboard *kbd, - struct usb_keyboard_report *new ) { - struct usb_keyboard_report *old = &kbd->report; - unsigned int keycode; - unsigned int i; - - /* Check if current key has been released */ - if ( kbd->keycode && ! usbkbd_has_keycode ( new, kbd->keycode ) ) { - DBGC2 ( kbd, "KBD %s keycode %#02x released\n", - kbd->name, kbd->keycode ); - kbd->keycode = 0; - } - - /* Decrement auto-repeat hold-off timer, if applicable */ - if ( kbd->holdoff ) - kbd->holdoff--; - - /* Check if a new key has been pressed */ - for ( i = 0 ; i < ( sizeof ( new->keycode ) / - sizeof ( new->keycode[0] ) ) ; i++ ) { - - /* Ignore keys present in the previous report */ - keycode = new->keycode[i]; - if ( ( keycode == 0 ) || usbkbd_has_keycode ( old, keycode ) ) - continue; - DBGC2 ( kbd, "KBD %s keycode %#02x pressed\n", - kbd->name, keycode ); - - /* Insert keypress into keyboard buffer */ - usbkbd_produce ( kbd, keycode, new->modifiers ); - - /* Record as most recent keycode */ - kbd->keycode = keycode; - - /* Start auto-repeat hold-off timer */ - kbd->holdoff = USBKBD_HOLDOFF; - } - - /* Insert auto-repeated keypress into keyboard buffer, if applicable */ - if ( kbd->keycode && ! kbd->holdoff ) - usbkbd_produce ( kbd, kbd->keycode, new->modifiers ); - - /* Record report */ - memcpy ( old, new, sizeof ( *old ) ); -} - -/****************************************************************************** - * - * Interrupt endpoint - * - ****************************************************************************** - */ - -/** - * Complete interrupt transfer - * - * @v ep USB endpoint - * @v iobuf I/O buffer - * @v rc Completion status code - */ -static void usbkbd_complete ( struct usb_endpoint *ep, - struct io_buffer *iobuf, int rc ) { - struct usb_keyboard *kbd = container_of ( ep, struct usb_keyboard, - hid.in ); - struct usb_keyboard_report *report; - - /* Ignore packets cancelled when the endpoint closes */ - if ( ! ep->open ) - goto drop; - - /* Ignore packets with errors */ - if ( rc != 0 ) { - DBGC ( kbd, "KBD %s interrupt IN failed: %s\n", - kbd->name, strerror ( rc ) ); - goto drop; - } - - /* Ignore underlength packets */ - if ( iob_len ( iobuf ) < sizeof ( *report ) ) { - DBGC ( kbd, "KBD %s underlength report:\n", kbd->name ); - DBGC_HDA ( kbd, 0, iobuf->data, iob_len ( iobuf ) ); - goto drop; - } - report = iobuf->data; - - /* Handle keyboard report */ - usbkbd_report ( kbd, report ); - - drop: - /* Recycle I/O buffer */ - usb_recycle ( &kbd->hid.in, iobuf ); -} - -/** Interrupt endpoint operations */ -static struct usb_endpoint_driver_operations usbkbd_operations = { - .complete = usbkbd_complete, -}; - -/****************************************************************************** - * - * USB interface - * - ****************************************************************************** - */ - -/** - * Probe device - * - * @v func USB function - * @v config Configuration descriptor - * @ret rc Return status code - */ -static int usbkbd_probe ( struct usb_function *func, - struct usb_configuration_descriptor *config ) { - struct usb_device *usb = func->usb; - struct usb_keyboard *kbd; - int rc; - - /* Allocate and initialise structure */ - kbd = zalloc ( sizeof ( *kbd ) ); - if ( ! kbd ) { - rc = -ENOMEM; - goto err_alloc; - } - kbd->name = func->name; - kbd->bus = usb->port->hub->bus; - usbhid_init ( &kbd->hid, func, &usbkbd_operations, NULL ); - usb_refill_init ( &kbd->hid.in, sizeof ( kbd->report ), - USBKBD_INTR_MAX_FILL ); - - /* Describe USB human interface device */ - if ( ( rc = usbhid_describe ( &kbd->hid, config ) ) != 0 ) { - DBGC ( kbd, "KBD %s could not describe: %s\n", - kbd->name, strerror ( rc ) ); - goto err_describe; - } - DBGC ( kbd, "KBD %s using %s (len %zd)\n", - kbd->name, usb_endpoint_name ( &kbd->hid.in ), kbd->hid.in.mtu ); - - /* Set boot protocol */ - if ( ( rc = usbhid_set_protocol ( usb, func->interface[0], - USBHID_PROTOCOL_BOOT ) ) != 0 ) { - DBGC ( kbd, "KBD %s could not set boot protocol: %s\n", - kbd->name, strerror ( rc ) ); - goto err_set_protocol; - } - - /* Set idle time */ - if ( ( rc = usbhid_set_idle ( usb, func->interface[0], 0, - USBKBD_IDLE_DURATION ) ) != 0 ) { - DBGC ( kbd, "KBD %s could not set idle time: %s\n", - kbd->name, strerror ( rc ) ); - goto err_set_idle; - } - - /* Open USB human interface device */ - if ( ( rc = usbhid_open ( &kbd->hid ) ) != 0 ) { - DBGC ( kbd, "KBD %s could not open: %s\n", - kbd->name, strerror ( rc ) ); - goto err_open; - } - - /* Add to list of USB keyboards */ - list_add_tail ( &kbd->list, &usb_keyboards ); - - usb_func_set_drvdata ( func, kbd ); - return 0; - - usbhid_close ( &kbd->hid ); - err_open: - err_set_idle: - err_set_protocol: - err_describe: - free ( kbd ); - err_alloc: - return rc; -} - -/** - * Remove device - * - * @v func USB function - */ -static void usbkbd_remove ( struct usb_function *func ) { - struct usb_keyboard *kbd = usb_func_get_drvdata ( func ); - - /* Remove from list of USB keyboards */ - list_del ( &kbd->list ); - - /* Close USB human interface device */ - usbhid_close ( &kbd->hid ); - - /* Free device */ - free ( kbd ); -} - -/** USB keyboard device IDs */ -static struct usb_device_id usbkbd_ids[] = { - { - .name = "kbd", - .vendor = USB_ANY_ID, - .product = USB_ANY_ID, - .class = { - .class = USB_CLASS_HID, - .subclass = USB_SUBCLASS_HID_BOOT, - .protocol = USBKBD_PROTOCOL, - }, - }, -}; - -/** USB keyboard driver */ -struct usb_driver usbkbd_driver __usb_driver = { - .ids = usbkbd_ids, - .id_count = ( sizeof ( usbkbd_ids ) / sizeof ( usbkbd_ids[0] ) ), - .probe = usbkbd_probe, - .remove = usbkbd_remove, -}; - -/****************************************************************************** - * - * Console interface - * - ****************************************************************************** - */ - -/** - * Read a character from the console - * - * @ret character Character read - */ -static int usbkbd_getchar ( void ) { - struct usb_keyboard *kbd; - - /* Consume first available key */ - list_for_each_entry ( kbd, &usb_keyboards, list ) { - if ( usbkbd_fill ( kbd ) ) - return usbkbd_consume ( kbd ); - } - - return 0; -} - -/** - * Check for available input - * - * @ret is_available Input is available - */ -static int usbkbd_iskey ( void ) { - struct usb_keyboard *kbd; - unsigned int fill; - - /* Poll all USB keyboards and refill endpoints */ - list_for_each_entry ( kbd, &usb_keyboards, list ) { - usb_poll ( kbd->bus ); - usb_refill ( &kbd->hid.in ); - } - - /* Check for a non-empty keyboard buffer */ - list_for_each_entry ( kbd, &usb_keyboards, list ) { - fill = usbkbd_fill ( kbd ); - if ( fill ) - return fill; - } - - return 0; -} - -/** USB keyboard console */ -struct console_driver usbkbd_console __console_driver = { - .getchar = usbkbd_getchar, - .iskey = usbkbd_iskey, -}; |