iPXE
Functions | Variables
usbkbd.c File Reference

USB keyboard driver. More...

#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"

Go to the source code of this file.

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static LIST_HEAD (usb_keyboards)
 List of USB keyboards.
static unsigned int usbkbd_map (unsigned int keycode, unsigned int modifiers, unsigned int leds)
 Map USB keycode to iPXE key.
static void usbkbd_produce (struct usb_keyboard *kbd, unsigned int keycode, unsigned int modifiers)
 Insert keypress into keyboard buffer.
static unsigned int usbkbd_consume (struct usb_keyboard *kbd)
 Consume character from keyboard buffer.
static int usbkbd_has_keycode (struct usb_keyboard_report *report, unsigned int keycode)
 Check for presence of keycode in report.
static void usbkbd_report (struct usb_keyboard *kbd, struct usb_keyboard_report *new)
 Handle keyboard report.
static void usbkbd_complete (struct usb_endpoint *ep, struct io_buffer *iobuf, int rc)
 Complete interrupt transfer.
static int usbkbd_set_leds (struct usb_keyboard *kbd)
 Set keyboard LEDs.
static int usbkbd_probe (struct usb_function *func, struct usb_configuration_descriptor *config)
 Probe device.
static void usbkbd_remove (struct usb_function *func)
 Remove device.
static int usbkbd_getchar (void)
 Read a character from the console.
static int usbkbd_iskey (void)
 Check for available input.

Variables

static struct
usb_endpoint_driver_operations 
usbkbd_operations
 Interrupt endpoint operations.
static struct usb_device_id usbkbd_ids []
 USB keyboard device IDs.
struct usb_driver usbkbd_driver __usb_driver
 USB keyboard driver.
struct console_driver
usbkbd_console 
__console_driver
 USB keyboard console.

Detailed Description

USB keyboard driver.

Definition in file usbkbd.c.


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static LIST_HEAD ( usb_keyboards  ) [static]

List of USB keyboards.

static unsigned int usbkbd_map ( unsigned int  keycode,
unsigned int  modifiers,
unsigned int  leds 
) [static]

Map USB keycode to iPXE key.

Parameters:
keycodeKeycode
modifiersModifiers
ledsLED state
Return values:
keyiPXE key

Key codes are defined in the USB HID Usage Tables Keyboard/Keypad page.

Definition at line 62 of file usbkbd.c.

References BACKSPACE, CTRL_A, ESC, key, KEY_DC, KEY_DOWN, KEY_END, KEY_F10, KEY_F11, KEY_F12, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_HOME, KEY_IC, KEY_LEFT, KEY_NPAGE, KEY_PPAGE, KEY_RIGHT, KEY_UP, keypad(), LF, TAB, USBKBD_CTRL, USBKBD_KEY_0, USBKBD_KEY_1, USBKBD_KEY_A, USBKBD_KEY_CAPS_LOCK, USBKBD_KEY_ENTER, USBKBD_KEY_MINUS, USBKBD_KEY_NUM_LOCK, USBKBD_KEY_PAD_1, USBKBD_KEY_PAD_DOT, USBKBD_KEY_PAD_ENTER, USBKBD_KEY_SLASH, USBKBD_KEY_SPACE, USBKBD_KEY_UP, USBKBD_KEY_Z, USBKBD_LED_CAPS_LOCK, USBKBD_LED_NUM_LOCK, and USBKBD_SHIFT.

Referenced by usbkbd_produce().

                                                     {
        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 ) ||
                            ( leds & USBKBD_LED_CAPS_LOCK ) ) {
                        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_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;
        }

        return key;
}
static void usbkbd_produce ( struct usb_keyboard kbd,
unsigned int  keycode,
unsigned int  modifiers 
) [static]

Insert keypress into keyboard buffer.

Parameters:
kbdUSB keyboard
keycodeKeycode
modifiersModifiers

Definition at line 142 of file usbkbd.c.

References DBGC, DBGC2, usb_keyboard::key, key, usb_keyboard::leds, usb_keyboard::leds_changed, usb_keyboard::name, usb_keyboard::prod, USBKBD_BUFSIZE, usbkbd_fill(), USBKBD_KEY_CAPS_LOCK, USBKBD_KEY_NUM_LOCK, USBKBD_LED_CAPS_LOCK, USBKBD_LED_NUM_LOCK, and usbkbd_map().

Referenced by usbkbd_report().

                                                      {
        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, kbd->leds );

        /* 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 );
}
static unsigned int usbkbd_consume ( struct usb_keyboard kbd) [static]

Consume character from keyboard buffer.

Parameters:
kbdUSB keyboard
Return values:
characterCharacter

Definition at line 189 of file usbkbd.c.

References assert, usb_keyboard::cons, DBGC2, usb_keyboard::key, key, KEY_ANSI_N, KEY_ANSI_TERMINATOR, KEY_MIN, len, usb_keyboard::name, sprintf, usb_keyboard::subcons, USBKBD_BUFSIZE, and usbkbd_fill().

Referenced by usbkbd_getchar().

                                                                {
        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;
}
static int usbkbd_has_keycode ( struct usb_keyboard_report report,
unsigned int  keycode 
) [static]

Check for presence of keycode in report.

Parameters:
reportKeyboard report
keycodeKeycode (must be non-zero)
Return values:
has_keycodeKeycode is present in report

Definition at line 250 of file usbkbd.c.

References usb_keyboard_report::keycode.

Referenced by usbkbd_report().

                                                       {
        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;
}
static void usbkbd_report ( struct usb_keyboard kbd,
struct usb_keyboard_report new 
) [static]

Handle keyboard report.

Parameters:
kbdUSB keyboard
newNew keyboard report

Definition at line 270 of file usbkbd.c.

References DBGC2, usb_keyboard::holdoff, usb_keyboard_report::keycode, usb_keyboard::keycode, memcpy(), usb_keyboard::name, usb_keyboard::report, usbkbd_has_keycode(), USBKBD_HOLDOFF, and usbkbd_produce().

Referenced by usbkbd_complete().

                                                              {
        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 ) );
}
static void usbkbd_complete ( struct usb_endpoint ep,
struct io_buffer iobuf,
int  rc 
) [static]

Complete interrupt transfer.

Parameters:
epUSB endpoint
iobufI/O buffer
rcCompletion status code

Definition at line 330 of file usbkbd.c.

References container_of, io_buffer::data, DBGC, DBGC_HDA, usb_keyboard::hid, usb_hid::in, iob_len(), usb_keyboard::name, usb_endpoint::open, strerror(), usb_recycle(), and usbkbd_report().

                                                                {
        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 );
}
static int usbkbd_set_leds ( struct usb_keyboard kbd) [static]

Set keyboard LEDs.

Parameters:
kbdUSB keyboard
Return values:
rcReturn status code

Definition at line 381 of file usbkbd.c.

References DBGC, DBGC2, usb_hid::func, usb_keyboard::hid, usb_function::interface, usb_keyboard::leds, usb_keyboard::name, rc, strerror(), usb_function::usb, USBHID_REPORT_OUTPUT, and usbhid_set_report().

Referenced by usbkbd_iskey(), and usbkbd_probe().

                                                        {
        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;
}
static int usbkbd_probe ( struct usb_function func,
struct usb_configuration_descriptor config 
) [static]

Probe device.

Parameters:
funcUSB function
configConfiguration descriptor
Return values:
rcReturn status code

Definition at line 413 of file usbkbd.c.

References usb_keyboard::bus, usb_hub::bus, DBGC, ENOMEM, free, usb_keyboard::hid, usb_port::hub, usb_hid::in, usb_function::interface, usb_keyboard::list, list_add_tail, usb_endpoint::mtu, usb_keyboard::name, usb_function::name, NULL, usb_device::port, rc, usb_keyboard::report, strerror(), usb_function::usb, usb_endpoint_name(), usb_func_set_drvdata(), usb_refill_init(), usbhid_close(), usbhid_describe(), usbhid_init(), usbhid_open(), USBHID_PROTOCOL_BOOT, usbhid_set_idle(), usbhid_set_protocol(), USBKBD_IDLE_DURATION, USBKBD_INTR_MAX_FILL, usbkbd_set_leds(), and zalloc().

                                                                        {
        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, 0, 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 );

        /* Set initial LED state */
        usbkbd_set_leds ( kbd );

        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;
}
static void usbkbd_remove ( struct usb_function func) [static]

Remove device.

Parameters:
funcUSB function

Definition at line 487 of file usbkbd.c.

References free, usb_keyboard::hid, usb_keyboard::list, list_del, usb_func_get_drvdata(), and usbhid_close().

                                                        {
        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 );
}
static int usbkbd_getchar ( void  ) [static]

Read a character from the console.

Return values:
characterCharacter read

Definition at line 532 of file usbkbd.c.

References usb_keyboard::list, list_for_each_entry, usbkbd_consume(), and usbkbd_fill().

                                   {
        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;
}
static int usbkbd_iskey ( void  ) [static]

Check for available input.

Return values:
is_availableInput is available

Definition at line 549 of file usbkbd.c.

References usb_keyboard::bus, fill, usb_keyboard::hid, usb_hid::in, usb_keyboard::leds_changed, usb_keyboard::list, list_for_each_entry, usb_poll(), usb_refill(), usbkbd_fill(), and usbkbd_set_leds().

                                 {
        struct usb_keyboard *kbd;
        unsigned int fill;

        /* 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 */
        list_for_each_entry ( kbd, &usb_keyboards, list ) {
                fill = usbkbd_fill ( kbd );
                if ( fill )
                        return fill;
        }

        return 0;
}

Variable Documentation

Initial value:
 {
        .complete = usbkbd_complete,
}

Interrupt endpoint operations.

Definition at line 364 of file usbkbd.c.

struct usb_device_id usbkbd_ids[] [static]
Initial value:
 {
        {
                .name = "kbd",
                .vendor = USB_ANY_ID,
                .product = USB_ANY_ID,
        },
}

USB keyboard device IDs.

Definition at line 501 of file usbkbd.c.

struct usb_driver usbkbd_driver __usb_driver
Initial value:
 {
        .ids = usbkbd_ids,
        .id_count = ( sizeof ( usbkbd_ids ) / sizeof ( usbkbd_ids[0] ) ),
        .class = USB_CLASS_ID ( USB_CLASS_HID, USB_SUBCLASS_HID_BOOT,
                                USBKBD_PROTOCOL ),
        .score = USB_SCORE_NORMAL,
        .probe = usbkbd_probe,
        .remove = usbkbd_remove,
}

USB keyboard driver.

Definition at line 510 of file usbkbd.c.

struct console_driver usbkbd_console __console_driver
Initial value:
 {
        .getchar = usbkbd_getchar,
        .iskey = usbkbd_iskey,
}

USB keyboard console.

Definition at line 580 of file usbkbd.c.