mirror of
https://github.com/ipxe/ipxe
synced 2025-12-15 00:12:19 +03:00
[usb] Add support for numeric keypad on USB keyboards
Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
@@ -53,13 +53,14 @@ static LIST_HEAD ( usb_keyboards );
|
|||||||
*
|
*
|
||||||
* @v keycode Keycode
|
* @v keycode Keycode
|
||||||
* @v modifiers Modifiers
|
* @v modifiers Modifiers
|
||||||
|
* @v leds LED state
|
||||||
* @ret key iPXE key
|
* @ret key iPXE key
|
||||||
*
|
*
|
||||||
* Key codes are defined in the USB HID Usage Tables Keyboard/Keypad
|
* Key codes are defined in the USB HID Usage Tables Keyboard/Keypad
|
||||||
* page.
|
* page.
|
||||||
*/
|
*/
|
||||||
static unsigned int usbkbd_map ( unsigned int keycode,
|
static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers,
|
||||||
unsigned int modifiers ) {
|
unsigned int leds ) {
|
||||||
unsigned int key;
|
unsigned int key;
|
||||||
|
|
||||||
if ( keycode < USBKBD_KEY_A ) {
|
if ( keycode < USBKBD_KEY_A ) {
|
||||||
@@ -70,7 +71,8 @@ static unsigned int usbkbd_map ( unsigned int keycode,
|
|||||||
key = ( keycode - USBKBD_KEY_A + 'a' );
|
key = ( keycode - USBKBD_KEY_A + 'a' );
|
||||||
if ( modifiers & USBKBD_CTRL ) {
|
if ( modifiers & USBKBD_CTRL ) {
|
||||||
key -= ( 'a' - CTRL_A );
|
key -= ( 'a' - CTRL_A );
|
||||||
} else if ( modifiers & USBKBD_SHIFT ) {
|
} else if ( ( modifiers & USBKBD_SHIFT ) ||
|
||||||
|
( leds & USBKBD_LED_CAPS_LOCK ) ) {
|
||||||
key -= ( 'a' - 'A' );
|
key -= ( 'a' - 'A' );
|
||||||
}
|
}
|
||||||
} else if ( keycode <= USBKBD_KEY_0 ) {
|
} 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_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT,
|
||||||
KEY_LEFT, KEY_DOWN, KEY_UP
|
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 {
|
} else {
|
||||||
key = 0;
|
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,
|
static void usbkbd_produce ( struct usb_keyboard *kbd, unsigned int keycode,
|
||||||
unsigned int modifiers ) {
|
unsigned int modifiers ) {
|
||||||
|
unsigned int leds = 0;
|
||||||
unsigned int key;
|
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 */
|
/* 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 */
|
/* Do nothing if this keycode has no corresponding iPXE key */
|
||||||
if ( ! key ) {
|
if ( ! key ) {
|
||||||
@@ -333,6 +365,37 @@ static struct usb_endpoint_driver_operations usbkbd_operations = {
|
|||||||
.complete = usbkbd_complete,
|
.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
|
* USB interface
|
||||||
@@ -400,6 +463,9 @@ static int usbkbd_probe ( struct usb_function *func,
|
|||||||
/* Add to list of USB keyboards */
|
/* Add to list of USB keyboards */
|
||||||
list_add_tail ( &kbd->list, &usb_keyboards );
|
list_add_tail ( &kbd->list, &usb_keyboards );
|
||||||
|
|
||||||
|
/* Set initial LED state */
|
||||||
|
usbkbd_set_leds ( kbd );
|
||||||
|
|
||||||
usb_func_set_drvdata ( func, kbd );
|
usb_func_set_drvdata ( func, kbd );
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -484,10 +550,20 @@ static int usbkbd_iskey ( void ) {
|
|||||||
struct usb_keyboard *kbd;
|
struct usb_keyboard *kbd;
|
||||||
unsigned int fill;
|
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 ) {
|
list_for_each_entry ( kbd, &usb_keyboards, list ) {
|
||||||
|
|
||||||
|
/* Poll keyboard */
|
||||||
usb_poll ( kbd->bus );
|
usb_poll ( kbd->bus );
|
||||||
|
|
||||||
|
/* Refill endpoints */
|
||||||
usb_refill ( &kbd->hid.in );
|
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 */
|
/* Check for a non-empty keyboard buffer */
|
||||||
|
|||||||
@@ -68,8 +68,20 @@ enum usb_keycode {
|
|||||||
USBKBD_KEY_SPACE = 0x2c,
|
USBKBD_KEY_SPACE = 0x2c,
|
||||||
USBKBD_KEY_MINUS = 0x2d,
|
USBKBD_KEY_MINUS = 0x2d,
|
||||||
USBKBD_KEY_SLASH = 0x38,
|
USBKBD_KEY_SLASH = 0x38,
|
||||||
USBKBD_KEY_CAPSLOCK = 0x39,
|
USBKBD_KEY_CAPS_LOCK = 0x39,
|
||||||
|
USBKBD_KEY_F1 = 0x3a,
|
||||||
USBKBD_KEY_UP = 0x52,
|
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)
|
/** Keyboard idle duration (in 4ms units)
|
||||||
@@ -120,6 +132,11 @@ struct usb_keyboard {
|
|||||||
/** Autorepeat hold-off time (in number of completions reported) */
|
/** Autorepeat hold-off time (in number of completions reported) */
|
||||||
unsigned int holdoff;
|
unsigned int holdoff;
|
||||||
|
|
||||||
|
/** Keyboard LED state */
|
||||||
|
uint8_t leds;
|
||||||
|
/** Keyboard LEDs changed */
|
||||||
|
uint8_t leds_changed;
|
||||||
|
|
||||||
/** Keyboard buffer
|
/** Keyboard buffer
|
||||||
*
|
*
|
||||||
* This stores iPXE key values.
|
* 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_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \
|
||||||
USB_REQUEST_TYPE ( 0x0a ) )
|
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 */
|
/** A USB human interface device */
|
||||||
struct usb_hid {
|
struct usb_hid {
|
||||||
/** USB function */
|
/** USB function */
|
||||||
@@ -97,6 +111,26 @@ usbhid_set_idle ( struct usb_device *usb, unsigned int interface,
|
|||||||
interface, NULL, 0 );
|
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 int usbhid_open ( struct usb_hid *hid );
|
||||||
extern void usbhid_close ( struct usb_hid *hid );
|
extern void usbhid_close ( struct usb_hid *hid );
|
||||||
extern int usbhid_refill ( struct usb_hid *hid );
|
extern int usbhid_refill ( struct usb_hid *hid );
|
||||||
|
|||||||
Reference in New Issue
Block a user