summaryrefslogtreecommitdiffstats
path: root/src/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/usb')
-rw-r--r--src/drivers/usb/usbkbd.c27
-rw-r--r--src/drivers/usb/usbkbd.h1
-rw-r--r--src/drivers/usb/xhci.c73
-rw-r--r--src/drivers/usb/xhci.h2
4 files changed, 97 insertions, 6 deletions
diff --git a/src/drivers/usb/usbkbd.c b/src/drivers/usb/usbkbd.c
index a8ab6ab76..b284e584f 100644
--- a/src/drivers/usb/usbkbd.c
+++ b/src/drivers/usb/usbkbd.c
@@ -25,10 +25,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdlib.h>
#include <string.h>
+#include <ctype.h>
#include <errno.h>
#include <assert.h>
#include <ipxe/console.h>
#include <ipxe/keys.h>
+#include <ipxe/keymap.h>
#include <ipxe/usb.h>
#include "usbkbd.h"
@@ -69,10 +71,7 @@ static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers,
} 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 ) ) {
+ if ( modifiers & USBKBD_SHIFT ) {
key -= ( 'a' - 'A' );
}
} else if ( keycode <= USBKBD_KEY_0 ) {
@@ -118,10 +117,30 @@ static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers,
};
key = keypad[ keycode - USBKBD_KEY_PAD_1 ];
};
+ } else if ( keycode == USBKBD_KEY_NON_US ) {
+ /* Non-US \ and | */
+ key = ( ( modifiers & USBKBD_SHIFT ) ?
+ ( KEYMAP_PSEUDO | '|' ) : ( KEYMAP_PSEUDO | '\\' ) );
} else {
key = 0;
}
+ /* Remap key if applicable */
+ if ( ( keycode < USBKBD_KEY_CAPS_LOCK ) ||
+ ( keycode == USBKBD_KEY_NON_US ) ) {
+
+ /* Apply modifiers */
+ if ( modifiers & USBKBD_CTRL )
+ key |= KEYMAP_CTRL;
+ if ( modifiers & USBKBD_ALT_RIGHT )
+ key |= KEYMAP_ALTGR;
+ if ( leds & USBKBD_LED_CAPS_LOCK )
+ key |= KEYMAP_CAPSLOCK;
+
+ /* Remap key */
+ key = key_remap ( key );
+ }
+
return key;
}
diff --git a/src/drivers/usb/usbkbd.h b/src/drivers/usb/usbkbd.h
index cedebfe71..1a3fea1ba 100644
--- a/src/drivers/usb/usbkbd.h
+++ b/src/drivers/usb/usbkbd.h
@@ -75,6 +75,7 @@ enum usb_keycode {
USBKBD_KEY_PAD_ENTER = 0x58,
USBKBD_KEY_PAD_1 = 0x59,
USBKBD_KEY_PAD_DOT = 0x63,
+ USBKBD_KEY_NON_US = 0x64,
};
/** USB keyboard LEDs */
diff --git a/src/drivers/usb/xhci.c b/src/drivers/usb/xhci.c
index cc48af033..3247ee69c 100644
--- a/src/drivers/usb/xhci.c
+++ b/src/drivers/usb/xhci.c
@@ -1165,6 +1165,31 @@ static int xhci_reset ( struct xhci_device *xhci ) {
return -ETIMEDOUT;
}
+/**
+ * Mark xHCI device as permanently failed
+ *
+ * @v xhci xHCI device
+ * @ret rc Return status code
+ */
+static int xhci_fail ( struct xhci_device *xhci ) {
+ size_t len;
+ int rc;
+
+ /* Mark command mechanism as permanently failed */
+ xhci->failed = 1;
+
+ /* Reset device */
+ if ( ( rc = xhci_reset ( xhci ) ) != 0 )
+ return rc;
+
+ /* Discard DCBAA entries since DCBAAP has been cleared */
+ assert ( xhci->dcbaa.context != NULL );
+ len = ( ( xhci->slots + 1 ) * sizeof ( xhci->dcbaa.context[0] ) );
+ memset ( xhci->dcbaa.context, 0, len );
+
+ return 0;
+}
+
/******************************************************************************
*
* Transfer request blocks
@@ -1720,6 +1745,10 @@ static void xhci_event_poll ( struct xhci_device *xhci ) {
unsigned int consumed;
unsigned int type;
+ /* Do nothing if device has permanently failed */
+ if ( xhci->failed )
+ return;
+
/* Poll for events */
profile_start ( &xhci_event_profiler );
for ( consumed = 0 ; ; consumed++ ) {
@@ -1778,6 +1807,7 @@ static void xhci_event_poll ( struct xhci_device *xhci ) {
*/
static void xhci_abort ( struct xhci_device *xhci ) {
physaddr_t crp;
+ uint32_t crcr;
/* Abort the command */
DBGC2 ( xhci, "XHCI %s aborting command\n", xhci->name );
@@ -1786,8 +1816,18 @@ static void xhci_abort ( struct xhci_device *xhci ) {
/* Allow time for command to abort */
mdelay ( XHCI_COMMAND_ABORT_DELAY_MS );
- /* Sanity check */
- assert ( ( readl ( xhci->op + XHCI_OP_CRCR ) & XHCI_CRCR_CRR ) == 0 );
+ /* Check for failure to abort */
+ crcr = readl ( xhci->op + XHCI_OP_CRCR );
+ if ( crcr & XHCI_CRCR_CRR ) {
+
+ /* Device has failed to abort a command and is almost
+ * certainly beyond repair. Reset device, abandoning
+ * all state, and mark device as failed to avoid
+ * delays on any future command attempts.
+ */
+ DBGC ( xhci, "XHCI %s failed to abort command\n", xhci->name );
+ xhci_fail ( xhci );
+ }
/* Consume (and ignore) any final command status */
xhci_event_poll ( xhci );
@@ -1813,6 +1853,12 @@ static int xhci_command ( struct xhci_device *xhci, union xhci_trb *trb ) {
unsigned int i;
int rc;
+ /* Immediately fail all commands if command mechanism has failed */
+ if ( xhci->failed ) {
+ rc = -EPIPE;
+ goto err_failed;
+ }
+
/* Sanity check */
if ( xhci->pending ) {
DBGC ( xhci, "XHCI %s command ring busy\n", xhci->name );
@@ -1863,6 +1909,7 @@ static int xhci_command ( struct xhci_device *xhci, union xhci_trb *trb ) {
err_enqueue:
xhci->pending = NULL;
err_pending:
+ err_failed:
return rc;
}
@@ -3412,14 +3459,36 @@ static int xhci_probe ( struct pci_device *pci ) {
static void xhci_remove ( struct pci_device *pci ) {
struct xhci_device *xhci = pci_get_drvdata ( pci );
struct usb_bus *bus = xhci->bus;
+ uint16_t command;
+
+ /* Some systems are observed to disable bus mastering on
+ * Thunderbolt controllers before we get a chance to shut
+ * down. Detect this and avoid attempting any DMA operations,
+ * which are guaranteed to fail and may end up spuriously
+ * completing after the operating system kernel starts up.
+ */
+ pci_read_config_word ( pci, PCI_COMMAND, &command );
+ if ( ! ( command & PCI_COMMAND_MASTER ) ) {
+ DBGC ( xhci, "XHCI %s DMA was disabled\n", xhci->name );
+ xhci_fail ( xhci );
+ }
+ /* Unregister and free USB bus */
unregister_usb_bus ( bus );
free_usb_bus ( bus );
+
+ /* Reset device and undo any PCH-specific fixes */
xhci_reset ( xhci );
if ( xhci->quirks & XHCI_PCH )
xhci_pch_undo ( xhci, pci );
+
+ /* Release ownership back to BIOS */
xhci_legacy_release ( xhci );
+
+ /* Unmap registers */
iounmap ( xhci->regs );
+
+ /* Free device */
free ( xhci );
}
diff --git a/src/drivers/usb/xhci.h b/src/drivers/usb/xhci.h
index 6e02d70e0..a3c8888af 100644
--- a/src/drivers/usb/xhci.h
+++ b/src/drivers/usb/xhci.h
@@ -1115,6 +1115,8 @@ struct xhci_device {
struct xhci_event_ring event;
/** Current command (if any) */
union xhci_trb *pending;
+ /** Command mechanism has permanently failed */
+ int failed;
/** Device slots, indexed by slot ID */
struct xhci_slot **slot;