#ifndef _USBKBD_H
#define _USBKBD_H

/** @file
 *
 * USB keyboard driver
 *
 */

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );

#include <assert.h>
#include <ipxe/usb.h>
#include <ipxe/usbhid.h>

/** Keyboard protocol */
#define USBKBD_PROTOCOL 1

/** A USB keyboard report */
struct usb_keyboard_report {
	/** Modifier keys */
	uint8_t modifiers;
	/** Reserved */
	uint8_t reserved;
	/** Keycodes */
	uint8_t keycode[6];
} __attribute__ (( packed ));

/** USB modifier keys */
enum usb_keyboard_modifier {
	/** Left Ctrl key */
	USBKBD_CTRL_LEFT = 0x01,
	/** Left Shift key */
	USBKBD_SHIFT_LEFT = 0x02,
	/** Left Alt key */
	USBKBD_ALT_LEFT = 0x04,
	/** Left GUI key */
	USBKBD_GUI_LEFT = 0x08,
	/** Right Ctrl key */
	USBKBD_CTRL_RIGHT = 0x10,
	/** Right Shift key */
	USBKBD_SHIFT_RIGHT = 0x20,
	/** Right Alt key */
	USBKBD_ALT_RIGHT = 0x40,
	/** Right GUI key */
	USBKBD_GUI_RIGHT = 0x80,
};

/** Either Ctrl key */
#define USBKBD_CTRL ( USBKBD_CTRL_LEFT | USBKBD_CTRL_RIGHT )

/** Either Shift key */
#define USBKBD_SHIFT ( USBKBD_SHIFT_LEFT | USBKBD_SHIFT_RIGHT )

/** Either Alt key */
#define USBKBD_ALT ( USBKBD_ALT_LEFT | USBKBD_ALT_RIGHT )

/** Either GUI key */
#define USBKBD_GUI ( USBKBD_GUI_LEFT | USBKBD_GUI_RIGHT )

/** USB keycodes */
enum usb_keycode {
	USBKBD_KEY_A = 0x04,
	USBKBD_KEY_Z = 0x1d,
	USBKBD_KEY_1 = 0x1e,
	USBKBD_KEY_0 = 0x27,
	USBKBD_KEY_ENTER = 0x28,
	USBKBD_KEY_SPACE = 0x2c,
	USBKBD_KEY_MINUS = 0x2d,
	USBKBD_KEY_SLASH = 0x38,
	USBKBD_KEY_CAPS_LOCK = 0x39,
	USBKBD_KEY_F1 = 0x3a,
	USBKBD_KEY_UP = 0x52,
	USBKBD_KEY_NUM_LOCK = 0x53,
	USBKBD_KEY_PAD_ENTER = 0x58,
	USBKBD_KEY_PAD_1 = 0x59,
	USBKBD_KEY_PAD_DOT = 0x63,
	USBKBD_KEY_NON_US = 0x64,
};

/** 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)
 *
 * This is a policy decision.  We choose to use an autorepeat rate of
 * approximately 40ms.
 */
#define USBKBD_IDLE_DURATION 10 /* 10 x 4ms = 40ms */

/** Keyboard auto-repeat hold-off (in units of USBKBD_IDLE_DURATION)
 *
 * This is a policy decision.  We choose to use an autorepeat delay of
 * approximately 500ms.
 */
#define USBKBD_HOLDOFF 12 /* 12 x 40ms = 480ms */

/** Interrupt endpoint maximum fill level
 *
 * When idling, we are likely to poll the USB endpoint at only the
 * 18.2Hz system timer tick rate.  With a typical observed bInterval
 * of 10ms (which will be rounded down to 8ms by the HCI drivers),
 * this gives approximately 7 completions per poll.
 */
#define USBKBD_INTR_MAX_FILL 8

/** Keyboard buffer size
 *
 * Must be a power of two.
 */
#define USBKBD_BUFSIZE 8

/** A USB keyboard device */
struct usb_keyboard {
	/** Name */
	const char *name;
	/** List of all USB keyboards */
	struct list_head list;

	/** USB bus */
	struct usb_bus *bus;
	/** USB human interface device */
	struct usb_hid hid;

	/** Most recent keyboard report */
	struct usb_keyboard_report report;
	/** Most recently pressed non-modifier key (if any) */
	unsigned int keycode;
	/** Autorepeat hold-off time (in number of completions reported) */
	unsigned int holdoff;

	/** Keyboard LED state */
	uint8_t leds;
	/** Keyboard LEDs changed */
	uint8_t leds_changed;

	/** Keyboard buffer
	 *
	 * This stores iPXE key values.
	 */
	unsigned int key[USBKBD_BUFSIZE];
	/** Keyboard buffer producer counter */
	unsigned int prod;
	/** Keyboard buffer consumer counter */
	unsigned int cons;
	/** Keyboard buffer sub-consumer counter
	 *
	 * This represents the index within the ANSI escape sequence
	 * corresponding to an iPXE key value.
	 */
	unsigned int subcons;
};

/**
 * Calculate keyboard buffer fill level
 *
 * @v kbd		USB keyboard
 * @ret fill		Keyboard buffer fill level
 */
static inline __attribute__ (( always_inline )) unsigned int
usbkbd_fill ( struct usb_keyboard *kbd ) {
	unsigned int fill = ( kbd->prod - kbd->cons );

	assert ( fill <= USBKBD_BUFSIZE );
	return fill;
}

#endif /* _USBKBD_H */
