event: Correct dependencies on the EVENT framework
[platform/kernel/u-boot.git] / common / usb_kbd.c
index ebfd972..4cbc9ac 100644 (file)
 #include <dm.h>
 #include <env.h>
 #include <errno.h>
+#include <log.h>
 #include <malloc.h>
 #include <memalign.h>
 #include <stdio_dev.h>
 #include <watchdog.h>
 #include <asm/byteorder.h>
+#ifdef CONFIG_SANDBOX
+#include <asm/state.h>
+#endif
 
 #include <usb.h>
 
@@ -75,13 +79,12 @@ static const unsigned char usb_kbd_num_keypad[] = {
        '.', 0, 0, 0, '='
 };
 
-/*
- * map arrow keys to ^F/^B ^N/^P, can't really use the proper
- * ANSI sequence for arrow keys because the queuing code breaks
- * when a single keypress expands to 3 queue elements
- */
-static const unsigned char usb_kbd_arrow[] = {
-       0x6, 0x2, 0xe, 0x10
+static const u8 usb_special_keys[] = {
+#ifdef CONFIG_USB_KEYBOARD_FN_KEYS
+       '2', 'H', '5', '3', 'F', '6', 'C', 'D', 'B', 'A'
+#else
+       'C', 'D', 'B', 'A'
+#endif
 };
 
 /*
@@ -96,12 +99,6 @@ static const unsigned char usb_kbd_arrow[] = {
 #define USB_KBD_LEDMASK                \
        (USB_KBD_NUMLOCK | USB_KBD_CAPSLOCK | USB_KBD_SCROLLLOCK)
 
-/*
- * USB Keyboard reports are 8 bytes in boot protocol.
- * Appendix B of HID Device Class Definition 1.11
- */
-#define USB_KBD_BOOT_REPORT_SIZE 8
-
 struct usb_kbd_pdata {
        unsigned long   intpipe;
        int             intpktsize;
@@ -124,10 +121,10 @@ struct usb_kbd_pdata {
 extern int __maybe_unused net_busy_flag;
 
 /* The period of time between two calls of usb_kbd_testc(). */
-static unsigned long __maybe_unused kbd_testc_tms;
+static unsigned long kbd_testc_tms;
 
 /* Puts character in the queue and sets up the in and out pointer. */
-static void usb_kbd_put_queue(struct usb_kbd_pdata *data, char c)
+static void usb_kbd_put_queue(struct usb_kbd_pdata *data, u8 c)
 {
        if (data->usb_in_pointer == USB_KBD_BUFFER_LEN - 1) {
                /* Check for buffer full. */
@@ -146,12 +143,6 @@ static void usb_kbd_put_queue(struct usb_kbd_pdata *data, char c)
        data->usb_kbd_buffer[data->usb_in_pointer] = c;
 }
 
-static void usb_kbd_put_sequence(struct usb_kbd_pdata *data, char *s)
-{
-       for (; *s; s++)
-               usb_kbd_put_queue(data, *s);
-}
-
 /*
  * Set the LEDs. Since this is used in the irq routine, the control job is
  * issued with a timeout of 0. This means, that the job is queued without
@@ -214,10 +205,6 @@ static int usb_kbd_translate(struct usb_kbd_pdata *data, unsigned char scancode,
                        keycode = usb_kbd_numkey[scancode - 0x1e];
        }
 
-       /* Arrow keys */
-       if ((scancode >= 0x4f) && (scancode <= 0x52))
-               keycode = usb_kbd_arrow[scancode - 0x4f];
-
        /* Numeric keypad */
        if ((scancode >= 0x54) && (scancode <= 0x67))
                keycode = usb_kbd_num_keypad[scancode - 0x54];
@@ -242,28 +229,58 @@ static int usb_kbd_translate(struct usb_kbd_pdata *data, unsigned char scancode,
        }
 
        /* Report keycode if any */
-       if (keycode)
+       if (keycode) {
                debug("%c", keycode);
-
-       switch (keycode) {
-       case 0x0e:                                      /* Down arrow key */
-               usb_kbd_put_sequence(data, "\e[B");
-               break;
-       case 0x10:                                      /* Up arrow key */
-               usb_kbd_put_sequence(data, "\e[A");
-               break;
-       case 0x06:                                      /* Right arrow key */
-               usb_kbd_put_sequence(data, "\e[C");
-               break;
-       case 0x02:                                      /* Left arrow key */
-               usb_kbd_put_sequence(data, "\e[D");
-               break;
-       default:
                usb_kbd_put_queue(data, keycode);
-               break;
+               return 0;
        }
 
+#ifdef CONFIG_USB_KEYBOARD_FN_KEYS
+       if (scancode < 0x3a || scancode > 0x52 ||
+           scancode == 0x46 || scancode == 0x47)
+               return 1;
+
+       usb_kbd_put_queue(data, 0x1b);
+       if (scancode < 0x3e) {
+               /* F1 - F4 */
+               usb_kbd_put_queue(data, 0x4f);
+               usb_kbd_put_queue(data, scancode - 0x3a + 'P');
+               return 0;
+       }
+       usb_kbd_put_queue(data, '[');
+       if (scancode < 0x42) {
+               /* F5 - F8 */
+               usb_kbd_put_queue(data, '1');
+               if (scancode == 0x3e)
+                       --scancode;
+               keycode = scancode - 0x3f + '7';
+       } else if (scancode < 0x49) {
+               /* F9 - F12 */
+               usb_kbd_put_queue(data, '2');
+               if (scancode > 0x43)
+                       ++scancode;
+               keycode = scancode - 0x42 + '0';
+       } else {
+               /*
+                * INSERT, HOME, PAGE UP, DELETE, END, PAGE DOWN,
+                * RIGHT, LEFT, DOWN, UP
+                */
+               keycode = usb_special_keys[scancode - 0x49];
+       }
+       usb_kbd_put_queue(data, keycode);
+       if (scancode < 0x4f && scancode != 0x4a && scancode != 0x4d)
+               usb_kbd_put_queue(data, '~');
        return 0;
+#else
+       /* Left, Right, Up, Down */
+       if (scancode > 0x4e && scancode < 0x53) {
+               usb_kbd_put_queue(data, 0x1b);
+               usb_kbd_put_queue(data, '[');
+               usb_kbd_put_queue(data, usb_special_keys[scancode - 0x4f]);
+               return 0;
+       }
+       return 1;
+#endif /* CONFIG_USB_KEYBOARD_FN_KEYS */
 }
 
 static uint32_t usb_kbd_service_key(struct usb_device *dev, int i, int up)
@@ -339,9 +356,9 @@ static inline void usb_kbd_poll_for_event(struct usb_device *dev)
 #if defined(CONFIG_SYS_USB_EVENT_POLL)
        struct usb_kbd_pdata *data = dev->privptr;
 
-       /* Submit a interrupt transfer request */
+       /* Submit an interrupt transfer request */
        if (usb_int_msg(dev, data->intpipe, &data->new[0],
-                       data->intpktsize, data->intinterval) >= 0)
+                       data->intpktsize, data->intinterval, true) >= 0)
                usb_kbd_irq_worker(dev);
 #elif defined(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP) || \
       defined(CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE)
@@ -380,21 +397,39 @@ static int usb_kbd_testc(struct stdio_dev *sdev)
        struct usb_device *usb_kbd_dev;
        struct usb_kbd_pdata *data;
 
+       /*
+        * Polling the keyboard for an event can take dozens of milliseconds.
+        * Add a delay between polls to avoid blocking activity which polls
+        * rapidly, like the UEFI console timer.
+        */
+       unsigned long poll_delay = CONFIG_SYS_HZ / 50;
+
 #ifdef CONFIG_CMD_NET
        /*
         * If net_busy_flag is 1, NET transfer is running,
         * then we check key-pressed every second (first check may be
         * less than 1 second) to improve TFTP booting performance.
         */
-       if (net_busy_flag && (get_timer(kbd_testc_tms) < CONFIG_SYS_HZ))
-               return 0;
-       kbd_testc_tms = get_timer(0);
+       if (net_busy_flag)
+               poll_delay = CONFIG_SYS_HZ;
+#endif
+
+#ifdef CONFIG_SANDBOX
+       /*
+        * Skip delaying polls if a test requests it.
+        */
+       if (state_get_skip_delays())
+               poll_delay = 0;
 #endif
+
        dev = stdio_get_by_name(sdev->name);
        usb_kbd_dev = (struct usb_device *)dev->priv;
        data = usb_kbd_dev->privptr;
 
-       usb_kbd_poll_for_event(usb_kbd_dev);
+       if (get_timer(kbd_testc_tms) >= poll_delay) {
+               usb_kbd_poll_for_event(usb_kbd_dev);
+               kbd_testc_tms = get_timer(0);
+       }
 
        return !(data->usb_in_pointer == data->usb_out_pointer);
 }
@@ -411,7 +446,7 @@ static int usb_kbd_getc(struct stdio_dev *sdev)
        data = usb_kbd_dev->privptr;
 
        while (data->usb_in_pointer == data->usb_out_pointer) {
-               WATCHDOG_RESET();
+               schedule();
                usb_kbd_poll_for_event(usb_kbd_dev);
        }
 
@@ -429,6 +464,7 @@ static int usb_kbd_probe_dev(struct usb_device *dev, unsigned int ifnum)
        struct usb_interface *iface;
        struct usb_endpoint_descriptor *ep;
        struct usb_kbd_pdata *data;
+       int epNum;
 
        if (dev->descriptor.bNumConfigurations != 1)
                return 0;
@@ -444,19 +480,21 @@ static int usb_kbd_probe_dev(struct usb_device *dev, unsigned int ifnum)
        if (iface->desc.bInterfaceProtocol != USB_PROT_HID_KEYBOARD)
                return 0;
 
-       if (iface->desc.bNumEndpoints != 1)
-               return 0;
+       for (epNum = 0; epNum < iface->desc.bNumEndpoints; epNum++) {
+               ep = &iface->ep_desc[epNum];
 
-       ep = &iface->ep_desc[0];
+               /* Check if endpoint is interrupt IN endpoint */
+               if ((ep->bmAttributes & 3) != 3)
+                       continue;
 
-       /* Check if endpoint 1 is interrupt endpoint */
-       if (!(ep->bEndpointAddress & 0x80))
-               return 0;
+               if (ep->bEndpointAddress & 0x80)
+                       break;
+       }
 
-       if ((ep->bmAttributes & 3) != 3)
+       if (epNum == iface->desc.bNumEndpoints)
                return 0;
 
-       debug("USB KBD: found set protocol...\n");
+       debug("USB KBD: found interrupt EP: 0x%x\n", ep->bEndpointAddress);
 
        data = malloc(sizeof(struct usb_kbd_pdata));
        if (!data) {
@@ -484,13 +522,15 @@ static int usb_kbd_probe_dev(struct usb_device *dev, unsigned int ifnum)
        data->last_report = -1;
 
        /* We found a USB Keyboard, install it. */
+       debug("USB KBD: set boot protocol\n");
        usb_set_protocol(dev, iface->desc.bInterfaceNumber, 0);
 
-       debug("USB KBD: found set idle...\n");
 #if !defined(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP) && \
     !defined(CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE)
+       debug("USB KBD: set idle interval...\n");
        usb_set_idle(dev, iface->desc.bInterfaceNumber, REPEAT_RATE / 4, 0);
 #else
+       debug("USB KBD: set idle interval=0...\n");
        usb_set_idle(dev, iface->desc.bInterfaceNumber, 0, 0);
 #endif
 
@@ -505,7 +545,7 @@ static int usb_kbd_probe_dev(struct usb_device *dev, unsigned int ifnum)
                           1, 0, data->new, USB_KBD_BOOT_REPORT_SIZE) < 0) {
 #else
        if (usb_int_msg(dev, data->intpipe, data->new, data->intpktsize,
-                       data->intinterval) < 0) {
+                       data->intinterval, false) < 0) {
 #endif
                printf("Failed to get keyboard state from device %04x:%04x\n",
                       dev->descriptor.idVendor, dev->descriptor.idProduct);
@@ -541,21 +581,22 @@ static int probe_usb_keyboard(struct usb_device *dev)
 
        stdinname = env_get("stdin");
 #if CONFIG_IS_ENABLED(CONSOLE_MUX)
-       error = iomux_doenv(stdin, stdinname);
-       if (error)
-               return error;
+       if (strstr(stdinname, DEVNAME) != NULL) {
+               error = iomux_doenv(stdin, stdinname);
+               if (error)
+                       return error;
+       }
 #else
        /* Check if this is the standard input device. */
-       if (strcmp(stdinname, DEVNAME))
-               return 1;
-
-       /* Reassign the console */
-       if (overwrite_console())
-               return 1;
+       if (!strcmp(stdinname, DEVNAME)) {
+               /* Reassign the console */
+               if (overwrite_console())
+                       return 1;
 
-       error = console_assign(stdin, DEVNAME);
-       if (error)
-               return error;
+               error = console_assign(stdin, DEVNAME);
+               if (error)
+                       return error;
+       }
 #endif
 
        return 0;
@@ -603,12 +644,12 @@ int usb_kbd_deregister(int force)
        if (dev) {
                usb_kbd_dev = (struct usb_device *)dev->priv;
                data = usb_kbd_dev->privptr;
-               if (stdio_deregister_dev(dev, force) != 0)
-                       return 1;
 #if CONFIG_IS_ENABLED(CONSOLE_MUX)
-               if (iomux_doenv(stdin, env_get("stdin")) != 0)
+               if (iomux_replace_device(stdin, DEVNAME, force ? "nulldev" : ""))
                        return 1;
 #endif
+               if (stdio_deregister_dev(dev, force) != 0)
+                       return 1;
 #ifdef CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE
                destroy_int_queue(usb_kbd_dev, data->intq);
 #endif
@@ -646,16 +687,16 @@ static int usb_kbd_remove(struct udevice *dev)
                goto err;
        }
        data = udev->privptr;
-       if (stdio_deregister_dev(sdev, true)) {
-               ret = -EPERM;
-               goto err;
-       }
 #if CONFIG_IS_ENABLED(CONSOLE_MUX)
-       if (iomux_doenv(stdin, env_get("stdin"))) {
+       if (iomux_replace_device(stdin, DEVNAME, "nulldev")) {
                ret = -ENOLINK;
                goto err;
        }
 #endif
+       if (stdio_deregister_dev(sdev, true)) {
+               ret = -EPERM;
+               goto err;
+       }
 #ifdef CONFIG_SYS_USB_EVENT_POLL_VIA_INT_QUEUE
        destroy_int_queue(udev, data->intq);
 #endif