david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[usb] Add support for numeric keypad on USB keyboards

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown 2016-01-06 16:30:01 +00:00
parent 173c48a57e
commit 2f861d736f
3 changed files with 134 additions and 7 deletions

View File

@ -53,13 +53,14 @@ static LIST_HEAD ( usb_keyboards );
*
* @v keycode Keycode
* @v modifiers Modifiers
* @v leds LED state
* @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 ) {
static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers,
unsigned int leds ) {
unsigned int key;
if ( keycode < USBKBD_KEY_A ) {
@ -70,7 +71,8 @@ static unsigned int usbkbd_map ( unsigned int keycode,
key = ( keycode - USBKBD_KEY_A + 'a' );
if ( modifiers & USBKBD_CTRL ) {
key -= ( 'a' - CTRL_A );
} else if ( modifiers & USBKBD_SHIFT ) {
} else if ( ( modifiers & USBKBD_SHIFT ) ||
( leds & USBKBD_LED_CAPS_LOCK ) ) {
key -= ( 'a' - 'A' );
}
} else if ( keycode <= USBKBD_KEY_0 ) {
@ -100,7 +102,22 @@ static unsigned int usbkbd_map ( unsigned int keycode,
KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
KEY_LEFT, KEY_DOWN, KEY_UP
};
key = special[ keycode - USBKBD_KEY_CAPSLOCK ];
key = special[ keycode - USBKBD_KEY_CAPS_LOCK ];
} else if ( keycode <= USBKBD_KEY_PAD_ENTER ) {
/* Keypad (unaffected by Num Lock) */
key = "\0/*-+\n" [ keycode - USBKBD_KEY_NUM_LOCK ];
} else if ( keycode <= USBKBD_KEY_PAD_DOT ) {
/* Keypad (affected by Num Lock) */
if ( leds & USBKBD_LED_NUM_LOCK ) {
key = "1234567890." [ keycode - USBKBD_KEY_PAD_1 ];
} else {
static const uint16_t keypad[] = {
KEY_END, KEY_DOWN, KEY_NPAGE, KEY_LEFT, 0,
KEY_RIGHT, KEY_HOME, KEY_UP, KEY_PPAGE,
KEY_IC, KEY_DC
};
key = keypad[ keycode - USBKBD_KEY_PAD_1 ];
};
} else {
key = 0;
}
@ -124,10 +141,25 @@ static unsigned int usbkbd_map ( unsigned int keycode,
*/
static void usbkbd_produce ( struct usb_keyboard *kbd, unsigned int keycode,
unsigned int modifiers ) {
unsigned int leds = 0;
unsigned int key;
/* Check for LED-modifying keys */
if ( keycode == USBKBD_KEY_CAPS_LOCK ) {
leds = USBKBD_LED_CAPS_LOCK;
} else if ( keycode == USBKBD_KEY_NUM_LOCK ) {
leds = USBKBD_LED_NUM_LOCK;
}
/* Handle LED-modifying keys */
if ( leds ) {
kbd->leds ^= leds;
kbd->leds_changed = 1;
return;
}
/* Map to iPXE key */
key = usbkbd_map ( keycode, modifiers );
key = usbkbd_map ( keycode, modifiers, kbd->leds );
/* Do nothing if this keycode has no corresponding iPXE key */
if ( ! key ) {
@ -333,6 +365,37 @@ static struct usb_endpoint_driver_operations usbkbd_operations = {
.complete = usbkbd_complete,
};
/******************************************************************************
*
* Keyboard LEDs
*
******************************************************************************
*/
/**
* Set keyboard LEDs
*
* @v kbd USB keyboard
* @ret rc Return status code
*/
static int usbkbd_set_leds ( struct usb_keyboard *kbd ) {
struct usb_function *func = kbd->hid.func;
int rc;
DBGC2 ( kbd, "KBD %s setting LEDs to %#02x\n", kbd->name, kbd->leds );
/* Set keyboard LEDs */
if ( ( rc = usbhid_set_report ( func->usb, func->interface[0],
USBHID_REPORT_OUTPUT, 0, &kbd->leds,
sizeof ( kbd->leds ) ) ) != 0 ) {
DBGC ( kbd, "KBD %s could not set LEDs to %#02x: %s\n",
kbd->name, kbd->leds, strerror ( rc ) );
return rc;
}
return 0;
}
/******************************************************************************
*
* USB interface
@ -400,6 +463,9 @@ static int usbkbd_probe ( struct usb_function *func,
/* Add to list of USB keyboards */
list_add_tail ( &kbd->list, &usb_keyboards );
/* Set initial LED state */
usbkbd_set_leds ( kbd );
usb_func_set_drvdata ( func, kbd );
return 0;
@ -484,10 +550,20 @@ static int usbkbd_iskey ( void ) {
struct usb_keyboard *kbd;
unsigned int fill;
/* Poll all USB keyboards and refill endpoints */
/* Poll USB keyboards, refill endpoints, and set LEDs if applicable */
list_for_each_entry ( kbd, &usb_keyboards, list ) {
/* Poll keyboard */
usb_poll ( kbd->bus );
/* Refill endpoints */
usb_refill ( &kbd->hid.in );
/* Update keyboard LEDs, if applicable */
if ( kbd->leds_changed ) {
usbkbd_set_leds ( kbd );
kbd->leds_changed = 0;
}
}
/* Check for a non-empty keyboard buffer */

View File

@ -68,8 +68,20 @@ enum usb_keycode {
USBKBD_KEY_SPACE = 0x2c,
USBKBD_KEY_MINUS = 0x2d,
USBKBD_KEY_SLASH = 0x38,
USBKBD_KEY_CAPSLOCK = 0x39,
USBKBD_KEY_CAPS_LOCK = 0x39,
USBKBD_KEY_F1 = 0x3a,
USBKBD_KEY_UP = 0x52,
USBKBD_KEY_NUM_LOCK = 0x53,
USBKBD_KEY_PAD_ENTER = 0x58,
USBKBD_KEY_PAD_1 = 0x59,
USBKBD_KEY_PAD_DOT = 0x63,
};
/** USB keyboard LEDs */
enum usb_keyboard_led {
USBKBD_LED_NUM_LOCK = 0x01,
USBKBD_LED_CAPS_LOCK = 0x02,
USBKBD_LED_SCROLL_LOCK = 0x04,
};
/** Keyboard idle duration (in 4ms units)
@ -120,6 +132,11 @@ struct usb_keyboard {
/** Autorepeat hold-off time (in number of completions reported) */
unsigned int holdoff;
/** Keyboard LED state */
uint8_t leds;
/** Keyboard LEDs changed */
uint8_t leds_changed;
/** Keyboard buffer
*
* This stores iPXE key values.

View File

@ -33,6 +33,20 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \
USB_REQUEST_TYPE ( 0x0a ) )
/** Set report */
#define USBHID_SET_REPORT \
( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \
USB_REQUEST_TYPE ( 0x09 ) )
/** Input report type */
#define USBHID_REPORT_INPUT 0x01
/** Output report type */
#define USBHID_REPORT_OUTPUT 0x02
/** Feature report type */
#define USBHID_REPORT_FEATURE 0x03
/** A USB human interface device */
struct usb_hid {
/** USB function */
@ -97,6 +111,26 @@ usbhid_set_idle ( struct usb_device *usb, unsigned int interface,
interface, NULL, 0 );
}
/**
* Set report
*
* @v usb USB device
* @v interface Interface number
* @v type Report type
* @v report Report ID
* @v data Report data
* @v len Length of report data
* @ret rc Return status code
*/
static inline __attribute__ (( always_inline )) int
usbhid_set_report ( struct usb_device *usb, unsigned int interface,
unsigned int type, unsigned int report, void *data,
size_t len ) {
return usb_control ( usb, USBHID_SET_REPORT, ( ( type << 8 ) | report ),
interface, data, len );
}
extern int usbhid_open ( struct usb_hid *hid );
extern void usbhid_close ( struct usb_hid *hid );
extern int usbhid_refill ( struct usb_hid *hid );