Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 30 Nov 2018 08:42:14 +0000 (UTC)
From:      Toomas Soome <tsoome@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r341329 - head/stand/efi/libefi
Message-ID:  <201811300842.wAU8gEph036935@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: tsoome
Date: Fri Nov 30 08:42:14 2018
New Revision: 341329
URL: https://svnweb.freebsd.org/changeset/base/341329

Log:
  loader.efi: fix EFI getchar() for multiple consoles
  
  This fix is ported from illumos (issue #9970), the analysis and initial
  implementation was done by John Levon.
  
  See also: https://www.illumos.org/issues/9970
  
  Currently, efi_cons_getchar() will wait for a key. While this seems to make
  sense, the implementation of getchar() in common/console.c will loop across
  getchar() for all consoles without doing ischar() first.
  
  This means that if we've configured multiple consoles, we can't input into
  the serial, as getchar() will be sat waiting for input only from efi_console.c
  
  This patch does implement a bit more generic key buffer to support
  translation of input keys, and we use generic efi_readkey() to reduce
  duplication from calls from getchar() and poll().

Modified:
  head/stand/efi/libefi/efi_console.c

Modified: head/stand/efi/libefi/efi_console.c
==============================================================================
--- head/stand/efi/libefi/efi_console.c	Fri Nov 30 08:01:11 2018	(r341328)
+++ head/stand/efi/libefi/efi_console.c	Fri Nov 30 08:42:14 2018	(r341329)
@@ -51,7 +51,8 @@ void HO(void);
 void end_term(void);
 #endif
 
-static EFI_INPUT_KEY key_cur;
+#define	KEYBUFSZ 10
+static unsigned keybuf[KEYBUFSZ];	/* keybuf for extended codes */
 static int key_pending;
 
 static void efi_cons_probe(struct console *);
@@ -438,55 +439,120 @@ efi_cons_putchar(int c)
 #endif
 }
 
-int
-efi_cons_getchar()
+static int
+keybuf_getchar(void)
 {
-	EFI_INPUT_KEY key;
-	EFI_STATUS status;
-	UINTN junk;
+	int i, c = 0;
 
-	if (key_pending) {
-		key = key_cur;
-		key_pending = 0;
-	} else {
-		/* Try to read a key stroke. We wait for one if none is pending. */
-		status = conin->ReadKeyStroke(conin, &key);
-		while (status == EFI_NOT_READY) {
-			/* Some EFI implementation (u-boot for example) do not support WaitForKey */
-			if (conin->WaitForKey != NULL)
-				BS->WaitForEvent(1, &conin->WaitForKey, &junk);
-			status = conin->ReadKeyStroke(conin, &key);
+	for (i = 0; i < KEYBUFSZ; i++) {
+		if (keybuf[i] != 0) {
+			c = keybuf[i];
+			keybuf[i] = 0;
+			break;
 		}
 	}
 
-	switch (key.ScanCode) {
-	case 0x17: /* ESC */
-		return (0x1b);  /* esc */
+	return (c);
+}
+
+static bool
+keybuf_ischar(void)
+{
+	int i;
+
+	for (i = 0; i < KEYBUFSZ; i++) {
+		if (keybuf[i] != 0)
+			return (true);
 	}
+	return (false);
+}
 
-	/* this can return  */
-	return (key.UnicodeChar);
+/*
+ * We are not reading input before keybuf is empty, so we are safe
+ * just to fill keybuf from the beginning.
+ */
+static void
+keybuf_inschar(EFI_INPUT_KEY *key)
+{
+
+	switch (key->ScanCode) {
+	case 0x1: /* UP */
+		keybuf[0] = 0x1b;	/* esc */
+		keybuf[1] = '[';
+		keybuf[2] = 'A';
+		break;
+	case 0x2: /* DOWN */
+		keybuf[0] = 0x1b;	/* esc */
+		keybuf[1] = '[';
+		keybuf[2] = 'B';
+		break;
+	case 0x3: /* RIGHT */
+		keybuf[0] = 0x1b;	/* esc */
+		keybuf[1] = '[';
+		keybuf[2] = 'C';
+		break;
+	case 0x4: /* LEFT */
+		keybuf[0] = 0x1b;	/* esc */
+		keybuf[1] = '[';
+		keybuf[2] = 'D';
+		break;
+	case 0x17:
+		keybuf[0] = 0x1b;	/* esc */
+		break;
+	default:
+		keybuf[0] = key->UnicodeChar;
+		break;
+	}
 }
 
-int
-efi_cons_poll()
+static bool
+efi_readkey(void)
 {
-	EFI_INPUT_KEY key;
 	EFI_STATUS status;
+	EFI_INPUT_KEY key;
 
-	if (conin->WaitForKey == NULL) {
-		if (key_pending)
-			return (1);
-		status = conin->ReadKeyStroke(conin, &key);
-		if (status == EFI_SUCCESS) {
-			key_cur = key;
-			key_pending = 1;
-		}
-		return (key_pending);
+	status = conin->ReadKeyStroke(conin, &key);
+	if (status == EFI_SUCCESS) {
+		keybuf_inschar(&key);
+		return (true);
 	}
+	return (false);
+}
 
-	/* This can clear the signaled state. */
-	return (BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS);
+int
+efi_cons_getchar(void)
+{
+	int c;
+
+	if ((c = keybuf_getchar()) != 0)
+		return (c);
+
+	key_pending = 0;
+
+	if (efi_readkey())
+		return (keybuf_getchar());
+
+	return (-1);
+}
+
+int
+efi_cons_poll(void)
+{
+
+	if (keybuf_ischar() || key_pending)
+		return (1);
+
+	/*
+	 * Some EFI implementation (u-boot for example) do not support
+	 * WaitForKey().
+	 * CheckEvent() can clear the signaled state.
+	 */
+	if (conin->WaitForKey == NULL)
+		key_pending = efi_readkey();
+	else
+		key_pending = BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS;
+
+	return (key_pending);
 }
 
 /* Plain direct access to EFI OutputString(). */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201811300842.wAU8gEph036935>