[usb] Add support for numeric keypad on USB keyboards
Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
parent
173c48a57e
commit
2f861d736f
|
@ -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 */
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 );
|
||||
|
|
Reference in New Issue