|  | @@ -0,0 +1,509 @@
 | 
		
	
		
			
			|  | 1 | +/*
 | 
		
	
		
			
			|  | 2 | + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
 | 
		
	
		
			
			|  | 3 | + *
 | 
		
	
		
			
			|  | 4 | + * This program is free software; you can redistribute it and/or
 | 
		
	
		
			
			|  | 5 | + * modify it under the terms of the GNU General Public License as
 | 
		
	
		
			
			|  | 6 | + * published by the Free Software Foundation; either version 2 of the
 | 
		
	
		
			
			|  | 7 | + * License, or (at your option) any later version.
 | 
		
	
		
			
			|  | 8 | + *
 | 
		
	
		
			
			|  | 9 | + * This program is distributed in the hope that it will be useful, but
 | 
		
	
		
			
			|  | 10 | + * WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
		
	
		
			
			|  | 11 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
		
	
		
			
			|  | 12 | + * General Public License for more details.
 | 
		
	
		
			
			|  | 13 | + *
 | 
		
	
		
			
			|  | 14 | + * You should have received a copy of the GNU General Public License
 | 
		
	
		
			
			|  | 15 | + * along with this program; if not, write to the Free Software
 | 
		
	
		
			
			|  | 16 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
		
	
		
			
			|  | 17 | + * 02110-1301, USA.
 | 
		
	
		
			
			|  | 18 | + *
 | 
		
	
		
			
			|  | 19 | + * You can also choose to distribute this program under the terms of
 | 
		
	
		
			
			|  | 20 | + * the Unmodified Binary Distribution Licence (as given in the file
 | 
		
	
		
			
			|  | 21 | + * COPYING.UBDL), provided that you have satisfied its requirements.
 | 
		
	
		
			
			|  | 22 | + */
 | 
		
	
		
			
			|  | 23 | +
 | 
		
	
		
			
			|  | 24 | +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 | 
		
	
		
			
			|  | 25 | +
 | 
		
	
		
			
			|  | 26 | +#include <stdlib.h>
 | 
		
	
		
			
			|  | 27 | +#include <string.h>
 | 
		
	
		
			
			|  | 28 | +#include <errno.h>
 | 
		
	
		
			
			|  | 29 | +#include <assert.h>
 | 
		
	
		
			
			|  | 30 | +#include <ipxe/console.h>
 | 
		
	
		
			
			|  | 31 | +#include <ipxe/keys.h>
 | 
		
	
		
			
			|  | 32 | +#include <ipxe/usb.h>
 | 
		
	
		
			
			|  | 33 | +#include "usbkbd.h"
 | 
		
	
		
			
			|  | 34 | +
 | 
		
	
		
			
			|  | 35 | +/** @file
 | 
		
	
		
			
			|  | 36 | + *
 | 
		
	
		
			
			|  | 37 | + * USB keyboard driver
 | 
		
	
		
			
			|  | 38 | + *
 | 
		
	
		
			
			|  | 39 | + */
 | 
		
	
		
			
			|  | 40 | +
 | 
		
	
		
			
			|  | 41 | +/** List of USB keyboards */
 | 
		
	
		
			
			|  | 42 | +static LIST_HEAD ( usb_keyboards );
 | 
		
	
		
			
			|  | 43 | +
 | 
		
	
		
			
			|  | 44 | +/******************************************************************************
 | 
		
	
		
			
			|  | 45 | + *
 | 
		
	
		
			
			|  | 46 | + * Keyboard map
 | 
		
	
		
			
			|  | 47 | + *
 | 
		
	
		
			
			|  | 48 | + ******************************************************************************
 | 
		
	
		
			
			|  | 49 | + */
 | 
		
	
		
			
			|  | 50 | +
 | 
		
	
		
			
			|  | 51 | +/**
 | 
		
	
		
			
			|  | 52 | + * Map USB keycode to iPXE key
 | 
		
	
		
			
			|  | 53 | + *
 | 
		
	
		
			
			|  | 54 | + * @v keycode		Keycode
 | 
		
	
		
			
			|  | 55 | + * @v modifiers		Modifiers
 | 
		
	
		
			
			|  | 56 | + * @ret key		iPXE key
 | 
		
	
		
			
			|  | 57 | + *
 | 
		
	
		
			
			|  | 58 | + * Key codes are defined in the USB HID Usage Tables Keyboard/Keypad
 | 
		
	
		
			
			|  | 59 | + * page.
 | 
		
	
		
			
			|  | 60 | + */
 | 
		
	
		
			
			|  | 61 | +static unsigned int usbkbd_map ( unsigned int keycode,
 | 
		
	
		
			
			|  | 62 | +				 unsigned int modifiers ) {
 | 
		
	
		
			
			|  | 63 | +	unsigned int key;
 | 
		
	
		
			
			|  | 64 | +
 | 
		
	
		
			
			|  | 65 | +	if ( keycode < USBKBD_KEY_A ) {
 | 
		
	
		
			
			|  | 66 | +		/* Not keys */
 | 
		
	
		
			
			|  | 67 | +		key = 0;
 | 
		
	
		
			
			|  | 68 | +	} else if ( keycode <= USBKBD_KEY_Z ) {
 | 
		
	
		
			
			|  | 69 | +		/* Alphabetic keys */
 | 
		
	
		
			
			|  | 70 | +		key = ( keycode - USBKBD_KEY_A + 'a' );
 | 
		
	
		
			
			|  | 71 | +		if ( modifiers & USBKBD_CTRL ) {
 | 
		
	
		
			
			|  | 72 | +			key -= ( 'a' - CTRL_A );
 | 
		
	
		
			
			|  | 73 | +		} else if ( modifiers & USBKBD_SHIFT ) {
 | 
		
	
		
			
			|  | 74 | +			key -= ( 'a' - 'A' );
 | 
		
	
		
			
			|  | 75 | +		}
 | 
		
	
		
			
			|  | 76 | +	} else if ( keycode <= USBKBD_KEY_0 ) {
 | 
		
	
		
			
			|  | 77 | +		/* Numeric key row */
 | 
		
	
		
			
			|  | 78 | +		if ( modifiers & USBKBD_SHIFT ) {
 | 
		
	
		
			
			|  | 79 | +			key = "!@#$%^&*()" [ keycode - USBKBD_KEY_1 ];
 | 
		
	
		
			
			|  | 80 | +		} else {
 | 
		
	
		
			
			|  | 81 | +			key = ( ( ( keycode - USBKBD_KEY_1 + 1 ) % 10 ) + '0' );
 | 
		
	
		
			
			|  | 82 | +		}
 | 
		
	
		
			
			|  | 83 | +	} else if ( keycode <= USBKBD_KEY_SPACE ) {
 | 
		
	
		
			
			|  | 84 | +		/* Unmodifiable keys */
 | 
		
	
		
			
			|  | 85 | +		static const uint8_t unmodifable[] =
 | 
		
	
		
			
			|  | 86 | +			{ LF, ESC, BACKSPACE, TAB, ' ' };
 | 
		
	
		
			
			|  | 87 | +		key = unmodifable[ keycode - USBKBD_KEY_ENTER ];
 | 
		
	
		
			
			|  | 88 | +	} else if ( keycode <= USBKBD_KEY_SLASH ) {
 | 
		
	
		
			
			|  | 89 | +		/* Punctuation keys */
 | 
		
	
		
			
			|  | 90 | +		if ( modifiers & USBKBD_SHIFT ) {
 | 
		
	
		
			
			|  | 91 | +			key = "_+{}|~:\"~<>?" [ keycode - USBKBD_KEY_MINUS ];
 | 
		
	
		
			
			|  | 92 | +		} else {
 | 
		
	
		
			
			|  | 93 | +			key = "-=[]\\#;'`,./" [ keycode - USBKBD_KEY_MINUS ];
 | 
		
	
		
			
			|  | 94 | +		}
 | 
		
	
		
			
			|  | 95 | +	} else if ( keycode <= USBKBD_KEY_UP ) {
 | 
		
	
		
			
			|  | 96 | +		/* Special keys */
 | 
		
	
		
			
			|  | 97 | +		static const uint16_t special[] = {
 | 
		
	
		
			
			|  | 98 | +			0, 0, 0, 0, 0, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9,
 | 
		
	
		
			
			|  | 99 | +			KEY_F10, KEY_F11, KEY_F12, 0, 0, 0, KEY_IC, KEY_HOME,
 | 
		
	
		
			
			|  | 100 | +			KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
 | 
		
	
		
			
			|  | 101 | +			KEY_LEFT, KEY_DOWN, KEY_UP
 | 
		
	
		
			
			|  | 102 | +		};
 | 
		
	
		
			
			|  | 103 | +		key = special[ keycode - USBKBD_KEY_CAPSLOCK ];
 | 
		
	
		
			
			|  | 104 | +	} else {
 | 
		
	
		
			
			|  | 105 | +		key = 0;
 | 
		
	
		
			
			|  | 106 | +	}
 | 
		
	
		
			
			|  | 107 | +
 | 
		
	
		
			
			|  | 108 | +	return key;
 | 
		
	
		
			
			|  | 109 | +}
 | 
		
	
		
			
			|  | 110 | +
 | 
		
	
		
			
			|  | 111 | +/******************************************************************************
 | 
		
	
		
			
			|  | 112 | + *
 | 
		
	
		
			
			|  | 113 | + * Keyboard buffer
 | 
		
	
		
			
			|  | 114 | + *
 | 
		
	
		
			
			|  | 115 | + ******************************************************************************
 | 
		
	
		
			
			|  | 116 | + */
 | 
		
	
		
			
			|  | 117 | +
 | 
		
	
		
			
			|  | 118 | +/**
 | 
		
	
		
			
			|  | 119 | + * Insert keypress into keyboard buffer
 | 
		
	
		
			
			|  | 120 | + *
 | 
		
	
		
			
			|  | 121 | + * @v kbd		USB keyboard
 | 
		
	
		
			
			|  | 122 | + * @v keycode		Keycode
 | 
		
	
		
			
			|  | 123 | + * @v modifiers		Modifiers
 | 
		
	
		
			
			|  | 124 | + */
 | 
		
	
		
			
			|  | 125 | +static void usbkbd_produce ( struct usb_keyboard *kbd, unsigned int keycode,
 | 
		
	
		
			
			|  | 126 | +			     unsigned int modifiers ) {
 | 
		
	
		
			
			|  | 127 | +	unsigned int key;
 | 
		
	
		
			
			|  | 128 | +
 | 
		
	
		
			
			|  | 129 | +	/* Map to iPXE key */
 | 
		
	
		
			
			|  | 130 | +	key = usbkbd_map ( keycode, modifiers );
 | 
		
	
		
			
			|  | 131 | +
 | 
		
	
		
			
			|  | 132 | +	/* Do nothing if this keycode has no corresponding iPXE key */
 | 
		
	
		
			
			|  | 133 | +	if ( ! key ) {
 | 
		
	
		
			
			|  | 134 | +		DBGC ( kbd, "KBD %s has no key for keycode %#02x:%#02x\n",
 | 
		
	
		
			
			|  | 135 | +		       kbd->name, modifiers, keycode );
 | 
		
	
		
			
			|  | 136 | +		return;
 | 
		
	
		
			
			|  | 137 | +	}
 | 
		
	
		
			
			|  | 138 | +
 | 
		
	
		
			
			|  | 139 | +	/* Check for buffer overrun */
 | 
		
	
		
			
			|  | 140 | +	if ( usbkbd_fill ( kbd ) >= USBKBD_BUFSIZE ) {
 | 
		
	
		
			
			|  | 141 | +		DBGC ( kbd, "KBD %s buffer overrun (key %#02x)\n",
 | 
		
	
		
			
			|  | 142 | +		       kbd->name, key );
 | 
		
	
		
			
			|  | 143 | +		return;
 | 
		
	
		
			
			|  | 144 | +	}
 | 
		
	
		
			
			|  | 145 | +
 | 
		
	
		
			
			|  | 146 | +	/* Insert into buffer */
 | 
		
	
		
			
			|  | 147 | +	kbd->key[ ( kbd->prod++ ) % USBKBD_BUFSIZE ] = key;
 | 
		
	
		
			
			|  | 148 | +	DBGC2 ( kbd, "KBD %s key %#02x produced\n", kbd->name, key );
 | 
		
	
		
			
			|  | 149 | +}
 | 
		
	
		
			
			|  | 150 | +
 | 
		
	
		
			
			|  | 151 | +/**
 | 
		
	
		
			
			|  | 152 | + * Consume character from keyboard buffer
 | 
		
	
		
			
			|  | 153 | + *
 | 
		
	
		
			
			|  | 154 | + * @v kbd		USB keyboard
 | 
		
	
		
			
			|  | 155 | + * @ret character	Character
 | 
		
	
		
			
			|  | 156 | + */
 | 
		
	
		
			
			|  | 157 | +static unsigned int usbkbd_consume ( struct usb_keyboard *kbd ) {
 | 
		
	
		
			
			|  | 158 | +	static char buf[] = "\x1b[xx~";
 | 
		
	
		
			
			|  | 159 | +	char *tmp = &buf[2];
 | 
		
	
		
			
			|  | 160 | +	unsigned int key;
 | 
		
	
		
			
			|  | 161 | +	unsigned int character;
 | 
		
	
		
			
			|  | 162 | +	unsigned int ansi_n;
 | 
		
	
		
			
			|  | 163 | +	unsigned int len;
 | 
		
	
		
			
			|  | 164 | +
 | 
		
	
		
			
			|  | 165 | +	/* Sanity check */
 | 
		
	
		
			
			|  | 166 | +	assert ( usbkbd_fill ( kbd ) > 0 );
 | 
		
	
		
			
			|  | 167 | +
 | 
		
	
		
			
			|  | 168 | +	/* Get current keypress */
 | 
		
	
		
			
			|  | 169 | +	key = kbd->key[ kbd->cons % USBKBD_BUFSIZE ];
 | 
		
	
		
			
			|  | 170 | +
 | 
		
	
		
			
			|  | 171 | +	/* If this is a straightforward key, just consume and return it */
 | 
		
	
		
			
			|  | 172 | +	if ( key < KEY_MIN ) {
 | 
		
	
		
			
			|  | 173 | +		kbd->cons++;
 | 
		
	
		
			
			|  | 174 | +		DBGC2 ( kbd, "KBD %s key %#02x consumed\n", kbd->name, key );
 | 
		
	
		
			
			|  | 175 | +		return key;
 | 
		
	
		
			
			|  | 176 | +	}
 | 
		
	
		
			
			|  | 177 | +
 | 
		
	
		
			
			|  | 178 | +	/* Construct ANSI sequence */
 | 
		
	
		
			
			|  | 179 | +	ansi_n = KEY_ANSI_N ( key );
 | 
		
	
		
			
			|  | 180 | +	if ( ansi_n )
 | 
		
	
		
			
			|  | 181 | +		tmp += sprintf ( tmp, "%d", ansi_n );
 | 
		
	
		
			
			|  | 182 | +	*(tmp++) = KEY_ANSI_TERMINATOR ( key );
 | 
		
	
		
			
			|  | 183 | +	*tmp = '\0';
 | 
		
	
		
			
			|  | 184 | +	len = ( tmp - buf );
 | 
		
	
		
			
			|  | 185 | +	assert ( len < sizeof ( buf ) );
 | 
		
	
		
			
			|  | 186 | +	if ( kbd->subcons == 0 ) {
 | 
		
	
		
			
			|  | 187 | +		DBGC2 ( kbd, "KBD %s key %#02x consumed as ^[%s\n",
 | 
		
	
		
			
			|  | 188 | +			kbd->name, key, &buf[1] );
 | 
		
	
		
			
			|  | 189 | +	}
 | 
		
	
		
			
			|  | 190 | +
 | 
		
	
		
			
			|  | 191 | +	/* Extract character from ANSI sequence */
 | 
		
	
		
			
			|  | 192 | +	assert ( kbd->subcons < len );
 | 
		
	
		
			
			|  | 193 | +	character = buf[ kbd->subcons++ ];
 | 
		
	
		
			
			|  | 194 | +
 | 
		
	
		
			
			|  | 195 | +	/* Consume key if applicable */
 | 
		
	
		
			
			|  | 196 | +	if ( kbd->subcons == len ) {
 | 
		
	
		
			
			|  | 197 | +		kbd->cons++;
 | 
		
	
		
			
			|  | 198 | +		kbd->subcons = 0;
 | 
		
	
		
			
			|  | 199 | +	}
 | 
		
	
		
			
			|  | 200 | +
 | 
		
	
		
			
			|  | 201 | +	return character;
 | 
		
	
		
			
			|  | 202 | +}
 | 
		
	
		
			
			|  | 203 | +
 | 
		
	
		
			
			|  | 204 | +/******************************************************************************
 | 
		
	
		
			
			|  | 205 | + *
 | 
		
	
		
			
			|  | 206 | + * Keyboard report
 | 
		
	
		
			
			|  | 207 | + *
 | 
		
	
		
			
			|  | 208 | + ******************************************************************************
 | 
		
	
		
			
			|  | 209 | + */
 | 
		
	
		
			
			|  | 210 | +
 | 
		
	
		
			
			|  | 211 | +/**
 | 
		
	
		
			
			|  | 212 | + * Check for presence of keycode in report
 | 
		
	
		
			
			|  | 213 | + *
 | 
		
	
		
			
			|  | 214 | + * @v report		Keyboard report
 | 
		
	
		
			
			|  | 215 | + * @v keycode		Keycode (must be non-zero)
 | 
		
	
		
			
			|  | 216 | + * @ret has_keycode	Keycode is present in report
 | 
		
	
		
			
			|  | 217 | + */
 | 
		
	
		
			
			|  | 218 | +static int usbkbd_has_keycode ( struct usb_keyboard_report *report,
 | 
		
	
		
			
			|  | 219 | +				unsigned int keycode ) {
 | 
		
	
		
			
			|  | 220 | +	unsigned int i;
 | 
		
	
		
			
			|  | 221 | +
 | 
		
	
		
			
			|  | 222 | +	/* Check for keycode */
 | 
		
	
		
			
			|  | 223 | +	for ( i = 0 ; i < ( sizeof ( report->keycode ) /
 | 
		
	
		
			
			|  | 224 | +			    sizeof ( report->keycode[0] ) ) ; i++ ) {
 | 
		
	
		
			
			|  | 225 | +		if ( report->keycode[i] == keycode )
 | 
		
	
		
			
			|  | 226 | +			return keycode;
 | 
		
	
		
			
			|  | 227 | +	}
 | 
		
	
		
			
			|  | 228 | +
 | 
		
	
		
			
			|  | 229 | +	return 0;
 | 
		
	
		
			
			|  | 230 | +}
 | 
		
	
		
			
			|  | 231 | +
 | 
		
	
		
			
			|  | 232 | +/**
 | 
		
	
		
			
			|  | 233 | + * Handle keyboard report
 | 
		
	
		
			
			|  | 234 | + *
 | 
		
	
		
			
			|  | 235 | + * @v kbd		USB keyboard
 | 
		
	
		
			
			|  | 236 | + * @v new		New keyboard report
 | 
		
	
		
			
			|  | 237 | + */
 | 
		
	
		
			
			|  | 238 | +static void usbkbd_report ( struct usb_keyboard *kbd,
 | 
		
	
		
			
			|  | 239 | +			    struct usb_keyboard_report *new ) {
 | 
		
	
		
			
			|  | 240 | +	struct usb_keyboard_report *old = &kbd->report;
 | 
		
	
		
			
			|  | 241 | +	unsigned int keycode;
 | 
		
	
		
			
			|  | 242 | +	unsigned int i;
 | 
		
	
		
			
			|  | 243 | +
 | 
		
	
		
			
			|  | 244 | +	/* Check if current key has been released */
 | 
		
	
		
			
			|  | 245 | +	if ( kbd->keycode && ! usbkbd_has_keycode ( new, kbd->keycode ) ) {
 | 
		
	
		
			
			|  | 246 | +		DBGC2 ( kbd, "KBD %s keycode %#02x released\n",
 | 
		
	
		
			
			|  | 247 | +			kbd->name, kbd->keycode );
 | 
		
	
		
			
			|  | 248 | +		kbd->keycode = 0;
 | 
		
	
		
			
			|  | 249 | +	}
 | 
		
	
		
			
			|  | 250 | +
 | 
		
	
		
			
			|  | 251 | +	/* Decrement auto-repeat hold-off timer, if applicable */
 | 
		
	
		
			
			|  | 252 | +	if ( kbd->holdoff )
 | 
		
	
		
			
			|  | 253 | +		kbd->holdoff--;
 | 
		
	
		
			
			|  | 254 | +
 | 
		
	
		
			
			|  | 255 | +	/* Check if a new key has been pressed */
 | 
		
	
		
			
			|  | 256 | +	for ( i = 0 ; i < ( sizeof ( new->keycode ) /
 | 
		
	
		
			
			|  | 257 | +			    sizeof ( new->keycode[0] ) ) ; i++ ) {
 | 
		
	
		
			
			|  | 258 | +
 | 
		
	
		
			
			|  | 259 | +		/* Ignore keys present in the previous report */
 | 
		
	
		
			
			|  | 260 | +		keycode = new->keycode[i];
 | 
		
	
		
			
			|  | 261 | +		if ( ( keycode == 0 ) || usbkbd_has_keycode ( old, keycode ) )
 | 
		
	
		
			
			|  | 262 | +			continue;
 | 
		
	
		
			
			|  | 263 | +		DBGC2 ( kbd, "KBD %s keycode %#02x pressed\n",
 | 
		
	
		
			
			|  | 264 | +			kbd->name, keycode );
 | 
		
	
		
			
			|  | 265 | +
 | 
		
	
		
			
			|  | 266 | +		/* Insert keypress into keyboard buffer */
 | 
		
	
		
			
			|  | 267 | +		usbkbd_produce ( kbd, keycode, new->modifiers );
 | 
		
	
		
			
			|  | 268 | +
 | 
		
	
		
			
			|  | 269 | +		/* Record as most recent keycode */
 | 
		
	
		
			
			|  | 270 | +		kbd->keycode = keycode;
 | 
		
	
		
			
			|  | 271 | +
 | 
		
	
		
			
			|  | 272 | +		/* Start auto-repeat hold-off timer */
 | 
		
	
		
			
			|  | 273 | +		kbd->holdoff = USBKBD_HOLDOFF;
 | 
		
	
		
			
			|  | 274 | +	}
 | 
		
	
		
			
			|  | 275 | +
 | 
		
	
		
			
			|  | 276 | +	/* Insert auto-repeated keypress into keyboard buffer, if applicable */
 | 
		
	
		
			
			|  | 277 | +	if ( kbd->keycode && ! kbd->holdoff )
 | 
		
	
		
			
			|  | 278 | +		usbkbd_produce ( kbd, kbd->keycode, new->modifiers );
 | 
		
	
		
			
			|  | 279 | +
 | 
		
	
		
			
			|  | 280 | +	/* Record report */
 | 
		
	
		
			
			|  | 281 | +	memcpy ( old, new, sizeof ( *old ) );
 | 
		
	
		
			
			|  | 282 | +}
 | 
		
	
		
			
			|  | 283 | +
 | 
		
	
		
			
			|  | 284 | +/******************************************************************************
 | 
		
	
		
			
			|  | 285 | + *
 | 
		
	
		
			
			|  | 286 | + * Interrupt endpoint
 | 
		
	
		
			
			|  | 287 | + *
 | 
		
	
		
			
			|  | 288 | + ******************************************************************************
 | 
		
	
		
			
			|  | 289 | + */
 | 
		
	
		
			
			|  | 290 | +
 | 
		
	
		
			
			|  | 291 | +/**
 | 
		
	
		
			
			|  | 292 | + * Complete interrupt transfer
 | 
		
	
		
			
			|  | 293 | + *
 | 
		
	
		
			
			|  | 294 | + * @v ep		USB endpoint
 | 
		
	
		
			
			|  | 295 | + * @v iobuf		I/O buffer
 | 
		
	
		
			
			|  | 296 | + * @v rc		Completion status code
 | 
		
	
		
			
			|  | 297 | + */
 | 
		
	
		
			
			|  | 298 | +static void usbkbd_complete ( struct usb_endpoint *ep,
 | 
		
	
		
			
			|  | 299 | +			      struct io_buffer *iobuf, int rc ) {
 | 
		
	
		
			
			|  | 300 | +	struct usb_keyboard *kbd = container_of ( ep, struct usb_keyboard,
 | 
		
	
		
			
			|  | 301 | +						  hid.in );
 | 
		
	
		
			
			|  | 302 | +	struct usb_keyboard_report *report;
 | 
		
	
		
			
			|  | 303 | +
 | 
		
	
		
			
			|  | 304 | +	/* Ignore packets cancelled when the endpoint closes */
 | 
		
	
		
			
			|  | 305 | +	if ( ! ep->open )
 | 
		
	
		
			
			|  | 306 | +		goto drop;
 | 
		
	
		
			
			|  | 307 | +
 | 
		
	
		
			
			|  | 308 | +	/* Ignore packets with errors */
 | 
		
	
		
			
			|  | 309 | +	if ( rc != 0 ) {
 | 
		
	
		
			
			|  | 310 | +		DBGC ( kbd, "KBD %s interrupt IN failed: %s\n",
 | 
		
	
		
			
			|  | 311 | +		       kbd->name, strerror ( rc ) );
 | 
		
	
		
			
			|  | 312 | +		goto drop;
 | 
		
	
		
			
			|  | 313 | +	}
 | 
		
	
		
			
			|  | 314 | +
 | 
		
	
		
			
			|  | 315 | +	/* Ignore underlength packets */
 | 
		
	
		
			
			|  | 316 | +	if ( iob_len ( iobuf ) < sizeof ( *report ) ) {
 | 
		
	
		
			
			|  | 317 | +		DBGC ( kbd, "KBD %s underlength report:\n", kbd->name );
 | 
		
	
		
			
			|  | 318 | +		DBGC_HDA ( kbd, 0, iobuf->data, iob_len ( iobuf ) );
 | 
		
	
		
			
			|  | 319 | +		goto drop;
 | 
		
	
		
			
			|  | 320 | +	}
 | 
		
	
		
			
			|  | 321 | +	report = iobuf->data;
 | 
		
	
		
			
			|  | 322 | +
 | 
		
	
		
			
			|  | 323 | +	/* Handle keyboard report */
 | 
		
	
		
			
			|  | 324 | +	usbkbd_report ( kbd, report );
 | 
		
	
		
			
			|  | 325 | +
 | 
		
	
		
			
			|  | 326 | + drop:
 | 
		
	
		
			
			|  | 327 | +	/* Recycle I/O buffer */
 | 
		
	
		
			
			|  | 328 | +	usb_recycle ( &kbd->hid.in, iobuf );
 | 
		
	
		
			
			|  | 329 | +}
 | 
		
	
		
			
			|  | 330 | +
 | 
		
	
		
			
			|  | 331 | +/** Interrupt endpoint operations */
 | 
		
	
		
			
			|  | 332 | +static struct usb_endpoint_driver_operations usbkbd_operations = {
 | 
		
	
		
			
			|  | 333 | +	.complete = usbkbd_complete,
 | 
		
	
		
			
			|  | 334 | +};
 | 
		
	
		
			
			|  | 335 | +
 | 
		
	
		
			
			|  | 336 | +/******************************************************************************
 | 
		
	
		
			
			|  | 337 | + *
 | 
		
	
		
			
			|  | 338 | + * USB interface
 | 
		
	
		
			
			|  | 339 | + *
 | 
		
	
		
			
			|  | 340 | + ******************************************************************************
 | 
		
	
		
			
			|  | 341 | + */
 | 
		
	
		
			
			|  | 342 | +
 | 
		
	
		
			
			|  | 343 | +/**
 | 
		
	
		
			
			|  | 344 | + * Probe device
 | 
		
	
		
			
			|  | 345 | + *
 | 
		
	
		
			
			|  | 346 | + * @v func		USB function
 | 
		
	
		
			
			|  | 347 | + * @v config		Configuration descriptor
 | 
		
	
		
			
			|  | 348 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 349 | + */
 | 
		
	
		
			
			|  | 350 | +static int usbkbd_probe ( struct usb_function *func,
 | 
		
	
		
			
			|  | 351 | +			  struct usb_configuration_descriptor *config ) {
 | 
		
	
		
			
			|  | 352 | +	struct usb_device *usb = func->usb;
 | 
		
	
		
			
			|  | 353 | +	struct usb_keyboard *kbd;
 | 
		
	
		
			
			|  | 354 | +	int rc;
 | 
		
	
		
			
			|  | 355 | +
 | 
		
	
		
			
			|  | 356 | +	/* Allocate and initialise structure */
 | 
		
	
		
			
			|  | 357 | +	kbd = zalloc ( sizeof ( *kbd ) );
 | 
		
	
		
			
			|  | 358 | +	if ( ! kbd ) {
 | 
		
	
		
			
			|  | 359 | +		rc = -ENOMEM;
 | 
		
	
		
			
			|  | 360 | +		goto err_alloc;
 | 
		
	
		
			
			|  | 361 | +	}
 | 
		
	
		
			
			|  | 362 | +	kbd->name = func->name;
 | 
		
	
		
			
			|  | 363 | +	kbd->bus = usb->port->hub->bus;
 | 
		
	
		
			
			|  | 364 | +	usbhid_init ( &kbd->hid, func, &usbkbd_operations, NULL );
 | 
		
	
		
			
			|  | 365 | +	usb_refill_init ( &kbd->hid.in, sizeof ( kbd->report ),
 | 
		
	
		
			
			|  | 366 | +			  USBKBD_INTR_MAX_FILL );
 | 
		
	
		
			
			|  | 367 | +
 | 
		
	
		
			
			|  | 368 | +	/* Describe USB human interface device */
 | 
		
	
		
			
			|  | 369 | +	if ( ( rc = usbhid_describe ( &kbd->hid, config ) ) != 0 ) {
 | 
		
	
		
			
			|  | 370 | +		DBGC ( kbd, "KBD %s could not describe: %s\n",
 | 
		
	
		
			
			|  | 371 | +		       kbd->name, strerror ( rc ) );
 | 
		
	
		
			
			|  | 372 | +		goto err_describe;
 | 
		
	
		
			
			|  | 373 | +	}
 | 
		
	
		
			
			|  | 374 | +	DBGC ( kbd, "KBD %s using %s (len %zd)\n",
 | 
		
	
		
			
			|  | 375 | +	       kbd->name, usb_endpoint_name ( &kbd->hid.in ), kbd->hid.in.mtu );
 | 
		
	
		
			
			|  | 376 | +
 | 
		
	
		
			
			|  | 377 | +	/* Set boot protocol */
 | 
		
	
		
			
			|  | 378 | +	if ( ( rc = usbhid_set_protocol ( usb, func->interface[0],
 | 
		
	
		
			
			|  | 379 | +					  USBHID_PROTOCOL_BOOT ) ) != 0 ) {
 | 
		
	
		
			
			|  | 380 | +		DBGC ( kbd, "KBD %s could not set boot protocol: %s\n",
 | 
		
	
		
			
			|  | 381 | +		       kbd->name, strerror ( rc ) );
 | 
		
	
		
			
			|  | 382 | +		goto err_set_protocol;
 | 
		
	
		
			
			|  | 383 | +	}
 | 
		
	
		
			
			|  | 384 | +
 | 
		
	
		
			
			|  | 385 | +	/* Set idle time */
 | 
		
	
		
			
			|  | 386 | +	if ( ( rc = usbhid_set_idle ( usb, func->interface[0], 0,
 | 
		
	
		
			
			|  | 387 | +				      USBKBD_IDLE_DURATION ) ) != 0 ) {
 | 
		
	
		
			
			|  | 388 | +		DBGC ( kbd, "KBD %s could not set idle time: %s\n",
 | 
		
	
		
			
			|  | 389 | +		       kbd->name, strerror ( rc ) );
 | 
		
	
		
			
			|  | 390 | +		goto err_set_idle;
 | 
		
	
		
			
			|  | 391 | +	}
 | 
		
	
		
			
			|  | 392 | +
 | 
		
	
		
			
			|  | 393 | +	/* Open USB human interface device */
 | 
		
	
		
			
			|  | 394 | +	if ( ( rc = usbhid_open ( &kbd->hid ) ) != 0 ) {
 | 
		
	
		
			
			|  | 395 | +		DBGC ( kbd, "KBD %s could not open: %s\n",
 | 
		
	
		
			
			|  | 396 | +		       kbd->name, strerror ( rc ) );
 | 
		
	
		
			
			|  | 397 | +		goto err_open;
 | 
		
	
		
			
			|  | 398 | +	}
 | 
		
	
		
			
			|  | 399 | +
 | 
		
	
		
			
			|  | 400 | +	/* Add to list of USB keyboards */
 | 
		
	
		
			
			|  | 401 | +	list_add_tail ( &kbd->list, &usb_keyboards );
 | 
		
	
		
			
			|  | 402 | +
 | 
		
	
		
			
			|  | 403 | +	usb_func_set_drvdata ( func, kbd );
 | 
		
	
		
			
			|  | 404 | +	return 0;
 | 
		
	
		
			
			|  | 405 | +
 | 
		
	
		
			
			|  | 406 | +	usbhid_close ( &kbd->hid );
 | 
		
	
		
			
			|  | 407 | + err_open:
 | 
		
	
		
			
			|  | 408 | + err_set_idle:
 | 
		
	
		
			
			|  | 409 | + err_set_protocol:
 | 
		
	
		
			
			|  | 410 | + err_describe:
 | 
		
	
		
			
			|  | 411 | +	free ( kbd );
 | 
		
	
		
			
			|  | 412 | + err_alloc:
 | 
		
	
		
			
			|  | 413 | +	return rc;
 | 
		
	
		
			
			|  | 414 | +}
 | 
		
	
		
			
			|  | 415 | +
 | 
		
	
		
			
			|  | 416 | +/**
 | 
		
	
		
			
			|  | 417 | + * Remove device
 | 
		
	
		
			
			|  | 418 | + *
 | 
		
	
		
			
			|  | 419 | + * @v func		USB function
 | 
		
	
		
			
			|  | 420 | + */
 | 
		
	
		
			
			|  | 421 | +static void usbkbd_remove ( struct usb_function *func ) {
 | 
		
	
		
			
			|  | 422 | +	struct usb_keyboard *kbd = usb_func_get_drvdata ( func );
 | 
		
	
		
			
			|  | 423 | +
 | 
		
	
		
			
			|  | 424 | +	/* Remove from list of USB keyboards */
 | 
		
	
		
			
			|  | 425 | +	list_del ( &kbd->list );
 | 
		
	
		
			
			|  | 426 | +
 | 
		
	
		
			
			|  | 427 | +	/* Close USB human interface device */
 | 
		
	
		
			
			|  | 428 | +	usbhid_close ( &kbd->hid );
 | 
		
	
		
			
			|  | 429 | +
 | 
		
	
		
			
			|  | 430 | +	/* Free device */
 | 
		
	
		
			
			|  | 431 | +	free ( kbd );
 | 
		
	
		
			
			|  | 432 | +}
 | 
		
	
		
			
			|  | 433 | +
 | 
		
	
		
			
			|  | 434 | +/** USB keyboard device IDs */
 | 
		
	
		
			
			|  | 435 | +static struct usb_device_id usbkbd_ids[] = {
 | 
		
	
		
			
			|  | 436 | +	{
 | 
		
	
		
			
			|  | 437 | +		.name = "kbd",
 | 
		
	
		
			
			|  | 438 | +		.vendor = USB_ANY_ID,
 | 
		
	
		
			
			|  | 439 | +		.product = USB_ANY_ID,
 | 
		
	
		
			
			|  | 440 | +		.class = {
 | 
		
	
		
			
			|  | 441 | +			.class = USB_CLASS_HID,
 | 
		
	
		
			
			|  | 442 | +			.subclass = USB_SUBCLASS_HID_BOOT,
 | 
		
	
		
			
			|  | 443 | +			.protocol = USBKBD_PROTOCOL,
 | 
		
	
		
			
			|  | 444 | +		},
 | 
		
	
		
			
			|  | 445 | +	},
 | 
		
	
		
			
			|  | 446 | +};
 | 
		
	
		
			
			|  | 447 | +
 | 
		
	
		
			
			|  | 448 | +/** USB keyboard driver */
 | 
		
	
		
			
			|  | 449 | +struct usb_driver usbkbd_driver __usb_driver = {
 | 
		
	
		
			
			|  | 450 | +	.ids = usbkbd_ids,
 | 
		
	
		
			
			|  | 451 | +	.id_count = ( sizeof ( usbkbd_ids ) / sizeof ( usbkbd_ids[0] ) ),
 | 
		
	
		
			
			|  | 452 | +	.probe = usbkbd_probe,
 | 
		
	
		
			
			|  | 453 | +	.remove = usbkbd_remove,
 | 
		
	
		
			
			|  | 454 | +};
 | 
		
	
		
			
			|  | 455 | +
 | 
		
	
		
			
			|  | 456 | +/******************************************************************************
 | 
		
	
		
			
			|  | 457 | + *
 | 
		
	
		
			
			|  | 458 | + * Console interface
 | 
		
	
		
			
			|  | 459 | + *
 | 
		
	
		
			
			|  | 460 | + ******************************************************************************
 | 
		
	
		
			
			|  | 461 | + */
 | 
		
	
		
			
			|  | 462 | +
 | 
		
	
		
			
			|  | 463 | +/**
 | 
		
	
		
			
			|  | 464 | + * Read a character from the console
 | 
		
	
		
			
			|  | 465 | + *
 | 
		
	
		
			
			|  | 466 | + * @ret character	Character read
 | 
		
	
		
			
			|  | 467 | + */
 | 
		
	
		
			
			|  | 468 | +static int usbkbd_getchar ( void ) {
 | 
		
	
		
			
			|  | 469 | +	struct usb_keyboard *kbd;
 | 
		
	
		
			
			|  | 470 | +
 | 
		
	
		
			
			|  | 471 | +	/* Consume first available key */
 | 
		
	
		
			
			|  | 472 | +	list_for_each_entry ( kbd, &usb_keyboards, list ) {
 | 
		
	
		
			
			|  | 473 | +		if ( usbkbd_fill ( kbd ) )
 | 
		
	
		
			
			|  | 474 | +			return usbkbd_consume ( kbd );
 | 
		
	
		
			
			|  | 475 | +	}
 | 
		
	
		
			
			|  | 476 | +
 | 
		
	
		
			
			|  | 477 | +	return 0;
 | 
		
	
		
			
			|  | 478 | +}
 | 
		
	
		
			
			|  | 479 | +
 | 
		
	
		
			
			|  | 480 | +/**
 | 
		
	
		
			
			|  | 481 | + * Check for available input
 | 
		
	
		
			
			|  | 482 | + *
 | 
		
	
		
			
			|  | 483 | + * @ret is_available	Input is available
 | 
		
	
		
			
			|  | 484 | + */
 | 
		
	
		
			
			|  | 485 | +static int usbkbd_iskey ( void ) {
 | 
		
	
		
			
			|  | 486 | +	struct usb_keyboard *kbd;
 | 
		
	
		
			
			|  | 487 | +	unsigned int fill;
 | 
		
	
		
			
			|  | 488 | +
 | 
		
	
		
			
			|  | 489 | +	/* Poll all USB keyboards and refill endpoints */
 | 
		
	
		
			
			|  | 490 | +	list_for_each_entry ( kbd, &usb_keyboards, list ) {
 | 
		
	
		
			
			|  | 491 | +		usb_poll ( kbd->bus );
 | 
		
	
		
			
			|  | 492 | +		usb_refill ( &kbd->hid.in );
 | 
		
	
		
			
			|  | 493 | +	}
 | 
		
	
		
			
			|  | 494 | +
 | 
		
	
		
			
			|  | 495 | +	/* Check for a non-empty keyboard buffer */
 | 
		
	
		
			
			|  | 496 | +	list_for_each_entry ( kbd, &usb_keyboards, list ) {
 | 
		
	
		
			
			|  | 497 | +		fill = usbkbd_fill ( kbd );
 | 
		
	
		
			
			|  | 498 | +		if ( fill )
 | 
		
	
		
			
			|  | 499 | +			return fill;
 | 
		
	
		
			
			|  | 500 | +	}
 | 
		
	
		
			
			|  | 501 | +
 | 
		
	
		
			
			|  | 502 | +	return 0;
 | 
		
	
		
			
			|  | 503 | +}
 | 
		
	
		
			
			|  | 504 | +
 | 
		
	
		
			
			|  | 505 | +/** USB keyboard console */
 | 
		
	
		
			
			|  | 506 | +struct console_driver usbkbd_console __console_driver = {
 | 
		
	
		
			
			|  | 507 | +	.getchar = usbkbd_getchar,
 | 
		
	
		
			
			|  | 508 | +	.iskey = usbkbd_iskey,
 | 
		
	
		
			
			|  | 509 | +};
 |