From 51f17f02317700e64b3c1113fe230d78bac7fecd Mon Sep 17 00:00:00 2001
From: tmk <nobody@nowhere>
Date: Wed, 8 Dec 2010 01:47:57 +0900
Subject: [PATCH] add build option: NKRO_ENABLE(remove: USB_12KRO)

---
 Makefile.common |   4 +-
 README          | 109 ++++---------------------
 USB_NKRO.txt    |  99 +++++++++++++++++++++++
 hhkb/Makefile   |   5 +-
 hhkb/keymap.c   |   6 +-
 key_process.c   |  71 +++++++++++------
 tmk.c           |  10 ++-
 usb.c           | 113 ++++++++++++++++++--------
 usb_keyboard.c  | 208 +++++++++++++++++++++++++++++-------------------
 usb_keyboard.h  |  39 +++++----
 usb_mouse.c     |   6 +-
 util.c          |   2 +
 12 files changed, 413 insertions(+), 259 deletions(-)
 create mode 100644 USB_NKRO.txt
 mode change 100644 => 100755 usb.c

diff --git a/Makefile.common b/Makefile.common
index 7f3f9ae50a..d212424360 100644
--- a/Makefile.common
+++ b/Makefile.common
@@ -129,8 +129,8 @@ CDEFS += -DPRODUCT=$(PRODUCT)
 ifdef MOUSE_DELAY_TIME
 CDEFS += -DMOUSE_DELAY_TIME=$(MOUSE_DELAY_TIME)
 endif
-ifdef USB_12KRO
-CDEFS += -DUSB_12KRO
+ifdef NKRO_ENABLE
+CDEFS += -DNKRO_ENABLE
 endif
 
 
diff --git a/README b/README
index 4c19ddba3b..57bce575c7 100644
--- a/README
+++ b/README
@@ -2,42 +2,23 @@ t.m.k. Keyboard Firmware
 ========================
 http://github.com/tmk/tmk_keyboard
 
-This is keyboard firmware for PFU HHKB style keyboard and Teensy/Teensy++ 2.0.
-OS see this as composite device which has keyboard and mouse.
+This is keyboard firmware for AVR USB MCUs or Teensy/Teensy++ 2.0.
 
 The project is heavily based on PJRC USB Keyboard/Mouse Example and
 owes a debt to preceding keyboard firmware projects.
 
+http://www.pjrc.com/teensy
 
-Version
--------
-0.1     2010/08/23
-        It works as normal keyboard.
-        It is for modified Macway keyboard(TP-999KB-E).
 
-1.0     2010/10/02
-        keyboard has mouse key now.
-        keyboard with layers.(see keymap.c)
-            FN_1(right cmd):
-                vi style layer
-            FN_2(next to right shift):
-                HHKB style layer
-            FN_3(left bottom):
-                h j k l:   mouse move
-                a s d spc: mouse buttons
-                m ,:       mouse wheel
 
-1.1     2010/10/08
-        Matrix wiring changed for casing.
-        (and my Teensy PD3 seems to be latchuped and unusable. :<)
-
-1.2     2010/10/13
-        HHKB support
-        horizontal mouse wheel support
-        change keymaps
-
-2.0     2010/10/27
-        HHKB/Macway support merged
+Functions
+---------
+Mouse key
+System Control Key
+    Power Down, Sleep, Wake Up & USB Remote Wake up
+Media Control Key
+    Volume Down/Up, Mute
+USB NKRO
 
 
 Build
@@ -47,17 +28,16 @@ Compiling sources need AVR GCC, AVR Libc and GNU make.(You can use WinAVR on Win
 $ cd <target> (hhkb or macway)
 $ make
 
+http://winavr.sourceforge.net/
 
-Debuging
---------
-Debug print is on if 4 keys are pressed during booting. 
+
+Debuging & Rescue
+-----------------
 Use PJRC's hid_listen.exe to see debug messages.
+Press right Control + Shift + Alt + GUI + H to debug menu. 
 
-
-AVR Target board
-----------------
-Teensy/Teensy++
-http://www.pjrc.com/teensy
+Pressing any 3 keys when connected enables debug output.
+Pressing any 4 keys when connected makes bootloader comes up.
 
 
 Projects related
@@ -83,59 +63,4 @@ ps2avr
     http://sourceforge.net/projects/ps2avr/
 
 
-TODO & ideas
-------------
-licensing notes(GPL)
-    I think GPL is not infringement of PJRC license.
-souce code cleaning
-sleep&wakeup
-debouncing logic
-    will be coded when bouncing occurs.
-    bouncing doesnt occur on my ALPS switch so far.
-    scan rate is too slow?(to be measure)
-
-Trackpoint(PS/2)
-    receive PS/2 signal from TrackPoint
-    send USB HID report
-Thinkpad keyboard support
-    turn keyboard to USB keyboard/mouse composite device
-setting menu(configure without changing firmware)
-    console for display
-    keymap/layer setting
-    mouse speed/acceleration
-    matrix display
-PS/2 keyboard mode
-    with USB to PS/2 dumb adapter(possible?)
-AT90USBKEY support
-    and other AVR USB boards
-
-DONE:
-support for HHKB pro matrix signal
-    exchange controller board with teensy
-    2010/10/11
-keymap
-    Matias half keyboard style
-    2010/10/23
-souce code cleaning
-    2010/10/23
-debug on/off
-    debug off by default
-    pressing keys during booting
-    2010/10/23
-mouse horizontal wheel
-    http://www.microchip.com/forums/tm.aspx?high=&m=391435&mpage=1#391521
-    http://www.keil.com/forum/15671/
-    http://www.microsoft.com/whdc/device/input/wheel.mspx
-    2010/10/13
-debug on/off
-    Fn key conbination during normal operation
-    matrix print on/off
-    key print on/off
-    mouse print on/off
-    2010/10/26
-layer switching
-    time before switching
-    timeout when not used during specific time
-    2010/10/30
-
 EOF
diff --git a/USB_NKRO.txt b/USB_NKRO.txt
new file mode 100644
index 0000000000..9bd8f704c3
--- /dev/null
+++ b/USB_NKRO.txt
@@ -0,0 +1,99 @@
+USB NKRO MEMO
+=============
+2010/12/07
+
+
+References
+----------
+USB - boot mode, NKRO, compatibility, etc...
+    http://geekhack.org/showthread.php?t=13162
+NKey Rollover - Overview, Testing Methodology, and Results
+    http://geekhack.org/showwiki.php?title=NKey+Rollover+-+Overview+Testing+Methodology+and+Results
+
+
+Terminogy
+---------
+NKRO
+ghost
+matrix
+mechanical with diodes
+membrane
+
+
+OS Support Status
+-----------------
+NKRO is possible at least relatively new OS.
+Following OS supports both Extended and Bitmarp report.
+    Windows7 64bit
+    Windows2000 SP4
+    Ubuntu 10.4(Linux 2.6)
+
+
+USB NKRO methods
+----------------
+1. Virtual keyboards
+    Keyboard can increase its KRO by using virtual keyboards with Standard or Extended report.
+    If the keyboard has 2 virtul keyboard with Standard report(6KRO), it gets 12KRO.
+    Using this method means the keyboard is a composite device.
+
+2. Exteded report
+    It needs large report size for this method to achive NKRO.
+    If a keyboard has 101keys, it needs 103byte report. It seems to be inefficient.
+
+3. Bitmap report
+    If the keyboard has less than 128keys, 16byte report will be enough for NKRO.
+    The 16byte report seems to be reasonable cost to get NKRO.
+
+
+Report Format
+-------------
+Other report formats than followings are possible, though these format are typical one.
+
+1. Standard             8bytes
+    modifiers(bitmap)       1byte
+    reserved                1byte(not used)
+    keys(array)             1byte*6
+Standard report can send 6keys plus 8modifiers simultaneously.
+Standard report is used by most keyboards in the marketplace.
+Standard report is identical to boot protocol report.
+Standard report is hard to suffer from compatibility problems.
+
+2. Extended standard    16,32,64bytes
+    modifiers(bitmap)       1byte
+    reserved                1byte(not used)
+    keys(array)             1byte*(14,32,62)
+Extended report can send N-keys by using N+2bytes.
+Extended report is expected to be compatible with boot protocol.
+
+3. Bitmap               16,32,64bytes
+    keys(bitmap)            (16,32)bytes
+Bitmap report can send at most 128keys by 16bytes and 256keys by 32bytes.
+Bitmap report can achieve USB NKRO efficiently in terms of report size.
+Bitmap report needs a deliberation for boot protocol implementation.
+
+
+Compatibility Problem
+---------------------
+Some BIOS doesn't send SET_PROTCOL request, a keyboard can't switch to boot protocol mode.
+This may cuase a problem on a keyboard which uses other report than Standard.
+
+
+Windows Problem
+---------------
+1. Windows accepts only 6keys  in case of Standard report.
+        It should be able to send 6keys plus 8modifiers.
+
+2. Windows accepts only 10keys in case of 16bytes Extended report.
+        It should be able to send 14keys plus 8modifiers.
+
+3. Windows accepts only 18keys in case of 32bytes Extended report.
+        It should be able to send 30keys plus 8modifiers.
+
+If keys are pressed in excess of the number, wrong keys are registered on Windows.
+
+
+This problem will be reportedly fixed soon.(2010/12/05)
+    http://forums.anandtech.com/showpost.php?p=30873364&postcount=17
+
+
+EOF
diff --git a/hhkb/Makefile b/hhkb/Makefile
index c248a67588..bf5d75ee9f 100644
--- a/hhkb/Makefile
+++ b/hhkb/Makefile
@@ -39,14 +39,15 @@
 # To rebuild project do "make clean" then "make all".
 #----------------------------------------------------------------------------
 
+# TODO: use config.h for build options?
 VENDOR_ID = 0xFEED
 PRODUCT_ID = 0xCAFE
 MANUFACTURER = 't.m.k.'
-PRODUCT = 't.m.k. HHKB pro'
+PRODUCT = 'HHKB Mod'
 DESCRIPTION = 't.m.k. firmware for HHKB pro'
 
 MOUSE_DELAY_TIME = 127
-USB_12KRO = yes
+NKRO_ENABLE = true
 
 # Target file name (without extension).
 TARGET = tmk_hhkb
diff --git a/hhkb/keymap.c b/hhkb/keymap.c
index 42a830c527..fd9bcce8a2 100644
--- a/hhkb/keymap.c
+++ b/hhkb/keymap.c
@@ -42,8 +42,8 @@ static const uint8_t PROGMEM fn_keycode[] = {
     KB_NO,          // FN_0 [NOT USED]
     KB_NO,          // FN_1 layer 1
     KB_SLSH,        // FN_2 layer 2
-    KB_SCOLON,      // FN_3 layer 3
-    KB_SPACE,       // FN_4 layer 4
+    KB_SCLN,        // FN_3 layer 3
+    KB_SPC,         // FN_4 layer 4
     KB_NO,          // FN_5 [NOT USED]
     KB_NO,          // FN_6 [NOT USED]
     KB_NO           // FN_7 layer 1
@@ -67,7 +67,7 @@ static const uint8_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
            KB_TAB, KB_Q,   KB_W,   KB_E,   KB_R,   KB_T,   KB_Y,   KB_U,   KB_I,   KB_O,   KB_P,   KB_LBRC,KB_RBRC,KB_BSPC, \
            KB_LCTL,KB_A,   KB_S,   KB_D,   KB_F,   KB_G,   KB_H,   KB_J,   KB_K,   KB_L,   FN_3,   KB_QUOT,KB_ENT, \
            KB_LSFT,KB_Z,   KB_X,   KB_C,   KB_V,   KB_B,   KB_N,   KB_M,   KB_COMM,KB_DOT, FN_2,   KB_RSFT,FN_1, \
-           KB_LGUI,KB_LALT,FN_4,   KB_RALT,FN_7),
+           KB_LGUI,KB_LALT,FN_4,   KB_RALT,KB_RGUI),
 
     /* Layer 1: HHKB mode (HHKB Fn)
      * ,-----------------------------------------------------------.
diff --git a/key_process.c b/key_process.c
index c23d17277b..bb1bca02d7 100644
--- a/key_process.c
+++ b/key_process.c
@@ -1,5 +1,6 @@
 #include <stdbool.h>
 #include <avr/io.h>
+#include <avr/interrupt.h>
 #include <util/delay.h>
 #include "print.h"
 #include "debug.h"
@@ -68,7 +69,7 @@ void proc_matrix(void) {
             if (code == KB_NO) {
                 // do nothing
             } else if (IS_MOD(code)) {
-                usb_keyboard_mods |= MOD_BIT(code);
+                usb_keyboard_add_mod(code);
             } else if (IS_FN(code)) {
                 fn_bits |= FN_BIT(code);
             } else if (IS_MOUSE(code)) {
@@ -111,22 +112,7 @@ void proc_matrix(void) {
 
             // normal keys
             else {
-                // TODO: fix ugly code
-                int8_t i = 0;
-                int8_t empty = -1;
-                for (; i < KEYBOARD_REPORT_MAX; i++) {
-                    if (usb_keyboard_keys_prev[i] == code) {
-                        usb_keyboard_keys[i] = code;
-                        break;
-                    } else if (empty == -1 && usb_keyboard_keys_prev[i] == 0 && usb_keyboard_keys[i] == 0) {
-                        empty = i;
-                    }
-                }
-                if (i == KEYBOARD_REPORT_MAX) {
-                    if (empty != -1) {
-                        usb_keyboard_keys[empty] = code;
-                    }
-                }
+                usb_keyboard_add_key(code);
             }
         }
     }
@@ -142,20 +128,24 @@ void proc_matrix(void) {
     layer_switching(fn_bits);
 
     // TODO: clean code
-    // when 4 left modifier keys down
+    // special mode for control, develop and debug
     if (keymap_is_special_mode(fn_bits)) {
-        switch (usb_keyboard_keys[0]) {
+        switch (usb_keyboard_get_key()) {
             case KB_H: // help
                 print_enable = true;
                 print("b: jump to bootloader\n");
-                print("d: debug print toggle\n");
-                print("x: matrix debug toggle\n");
-                print("k: keyboard debug toggle\n");
-                print("m: mouse debug toggle\n");
-                print("p: print enable toggle\n");
+                print("d: toggle debug enable\n");
+                print("x: toggle matrix debug\n");
+                print("k: toggle keyboard debug\n");
+                print("m: toggle mouse debug\n");
+                print("p: toggle print enable\n");
                 print("v: print version\n");
                 print("t: print timer count\n");
-                print("r: print registers\n");
+                print("s: print status\n");
+                print("`: toggle protcol(boot/report)\n");
+#ifdef NKRO_ENABLE
+                print("n: toggle NKRO\n");
+#endif
                 print("ESC: power down/wake up\n");
                 _delay_ms(500);
                 print_enable = false;
@@ -243,13 +233,42 @@ void proc_matrix(void) {
                 }
                 _delay_ms(1000);
                 break;
-            case KB_R:
+            case KB_S:
                 usb_keyboard_clear_report();
                 usb_keyboard_send();
+                print("UDCON: "); phex(UDCON); print("\n");
                 print("UDIEN: "); phex(UDIEN); print("\n");
                 print("UDINT: "); phex(UDINT); print("\n");
+                print("usb_keyboard_leds:"); phex(usb_keyboard_leds); print("\n");
+                print("usb_keyboard_protocol:"); phex(usb_keyboard_protocol); print("\n");
+                print("usb_keyboard_idle_config:"); phex(usb_keyboard_idle_config); print("\n");
+                print("usb_keyboard_idle_count:"); phex(usb_keyboard_idle_count); print("\n");
+                print("mouse_protocol:"); phex(mouse_protocol); print("\n");
+                if (usb_keyboard_nkro) print("NKRO: enabled\n"); else print("NKRO: disabled\n");
+                _delay_ms(500);
+                break;
+            case KB_GRV:
+                usb_keyboard_clear_report();
+                usb_keyboard_send();
+                usb_keyboard_protocol = !usb_keyboard_protocol;
+                mouse_protocol = !mouse_protocol;
+                print("keyboard protcol: ");
+                if (usb_keyboard_protocol) print("report"); else print("boot");
+                print("\n");
+                print("mouse protcol: ");
+                if (mouse_protocol) print("report"); else print("boot");
+                print("\n");
                 _delay_ms(1000);
                 break;
+#ifdef NKRO_ENABLE
+            case KB_N:
+                usb_keyboard_clear_report();
+                usb_keyboard_send();
+                usb_keyboard_nkro = !usb_keyboard_nkro;
+                if (usb_keyboard_nkro) print("NKRO: enabled\n"); else print("NKRO: disabled\n");
+                _delay_ms(1000);
+                break;
+#endif
             case KB_ESC:
                 usb_keyboard_clear_report();
                 usb_keyboard_send();
diff --git a/tmk.c b/tmk.c
index f7042bd6fb..b4f76f5d4a 100644
--- a/tmk.c
+++ b/tmk.c
@@ -66,8 +66,7 @@ int main(void)
 
     matrix_init();
     matrix_scan();
-    // bootloader comes up when any 4 or more keys are pressed at startup
-    if (matrix_key_count() >= 4) {
+    if (matrix_key_count() >= 3) {
 #ifdef DEBUG_LED
         for (int i = 0; i < 6; i++) {
             DEBUG_LED_CONFIG;
@@ -80,6 +79,13 @@ int main(void)
         _delay_ms(5000);
 #endif
         print_enable = true;
+        debug_enable = true;
+        debug_matrix = true;
+        debug_keyboard = true;
+        debug_mouse = true;
+        print("debug enabled.\n");
+    }
+    if (matrix_key_count() >= 4) {
         print("jump to bootloader...\n");
         _delay_ms(1000);
         jump_bootloader(); // not return
diff --git a/usb.c b/usb.c
old mode 100644
new mode 100755
index e633b96383..215f05de18
--- a/usb.c
+++ b/usb.c
@@ -90,12 +90,12 @@ bool suspend = false;
 // 0:control endpoint is enabled automatically by controller.
 static const uint8_t PROGMEM endpoint_config_table[] = {
 	// enable, UECFG0X(type, direction), UECFG1X(size, bank, allocation)
-	1, EP_TYPE_INTERRUPT_IN,  EP_SIZE(KEYBOARD_SIZE) | KEYBOARD_BUFFER, // 1
+	1, EP_TYPE_INTERRUPT_IN,  EP_SIZE(KBD_SIZE)      | KBD_BUFFER,      // 1
 	1, EP_TYPE_INTERRUPT_IN,  EP_SIZE(MOUSE_SIZE)    | MOUSE_BUFFER,    // 2
 	1, EP_TYPE_INTERRUPT_IN,  EP_SIZE(DEBUG_TX_SIZE) | DEBUG_TX_BUFFER, // 3
 	1, EP_TYPE_INTERRUPT_IN,  EP_SIZE(EXTRA_SIZE)    | EXTRA_BUFFER,    // 4
-#ifdef USB_12KRO
-	1, EP_TYPE_INTERRUPT_IN,  EP_SIZE(KEYBOARD_SIZE) | KEYBOARD_BUFFER, // 5
+#ifdef NKRO_ENABLE
+	1, EP_TYPE_INTERRUPT_IN,  EP_SIZE(KBD2_SIZE)      | KBD2_BUFFER,      // 5
 #else
         0, // 5
 #endif
@@ -158,16 +158,52 @@ static uint8_t PROGMEM keyboard_hid_report_desc[] = {
         0x95, 0x01,          //   Report Count (1),
         0x75, 0x03,          //   Report Size (3),
         0x91, 0x03,          //   Output (Constant),                 ;LED report padding
-        0x95, 0x06,          //   Report Count (6),
+        0x95, KBD_REPORT_KEYS,    //   Report Count (),
         0x75, 0x08,          //   Report Size (8),
         0x15, 0x00,          //   Logical Minimum (0),
-        0x25, 0x68,          //   Logical Maximum(104),
+        0x25, 0xFF,          //   Logical Maximum(255),
         0x05, 0x07,          //   Usage Page (Key Codes),
         0x19, 0x00,          //   Usage Minimum (0),
-        0x29, 0x68,          //   Usage Maximum (104),
+        0x29, 0xFF,          //   Usage Maximum (255),
         0x81, 0x00,          //   Input (Data, Array),
         0xc0                 // End Collection
 };
+#ifdef NKRO_ENABLE
+static uint8_t PROGMEM keyboard2_hid_report_desc[] = {
+        0x05, 0x01,          // Usage Page (Generic Desktop),
+        0x09, 0x06,          // Usage (Keyboard),
+        0xA1, 0x01,          // Collection (Application),
+        0x75, 0x01,          //   Report Size (1),
+        0x95, 0x08,          //   Report Count (8),
+        0x05, 0x07,          //   Usage Page (Key Codes),
+        0x19, 0xE0,          //   Usage Minimum (224),
+        0x29, 0xE7,          //   Usage Maximum (231),
+        0x15, 0x00,          //   Logical Minimum (0),
+        0x25, 0x01,          //   Logical Maximum (1),
+        0x81, 0x02,          //   Input (Data, Variable, Absolute), ;Modifier byte
+        0x95, 0x01,          //   Report Count (1),
+        0x75, 0x08,          //   Report Size (8),
+        0x81, 0x03,          //   Input (Constant),                 ;Reserved byte
+        0x95, 0x05,          //   Report Count (5),
+        0x75, 0x01,          //   Report Size (1),
+        0x05, 0x08,          //   Usage Page (LEDs),
+        0x19, 0x01,          //   Usage Minimum (1),
+        0x29, 0x05,          //   Usage Maximum (5),
+        0x91, 0x02,          //   Output (Data, Variable, Absolute), ;LED report
+        0x95, 0x01,          //   Report Count (1),
+        0x75, 0x03,          //   Report Size (3),
+        0x91, 0x03,          //   Output (Constant),                 ;LED report padding
+        0x95, KBD2_REPORT_KEYS*8,	//   Report Count (),
+        0x75, 0x01,          //   Report Size (1),
+        0x15, 0x00,          //   Logical Minimum (0),
+        0x25, 0x01,          //   Logical Maximum(1),
+        0x05, 0x07,          //   Usage Page (Key Codes),
+        0x19, 0x00,          //   Usage Minimum (0),
+        0x29, KBD2_REPORT_KEYS*8-1,	//   Usage Maximum (),
+        0x81, 0x02,          //   Input (Data, Variable, Absolute),
+        0xc0                 // End Collection
+};
+#endif
 
 // Mouse Protocol 1, HID 1.11 spec, Appendix B, page 59-60, with wheel extension
 // http://www.microchip.com/forums/tm.aspx?high=&m=391435&mpage=1#391521
@@ -296,17 +332,17 @@ static uint8_t PROGMEM extra_hid_report_desc[] = {
     0xc0                           // END_COLLECTION
 };
 
-#define KEYBOARD_HID_DESC_OFFSET	(9+(9+9+7)*0+9)
-#define MOUSE_HID_DESC_OFFSET		(9+(9+9+7)*1+9)
-#define DEBUG_HID_DESC_OFFSET		(9+(9+9+7)*2+9)
-#define EXTRA_HID_DESC_OFFSET		(9+(9+9+7)*3+9)
-#ifdef USB_12KRO
-#   define NUM_INTERFACES		5
-#   define KEYBOARD2_HID_DESC_OFFSET	(9+(9+9+7)*4+9)
+#define KBD_HID_DESC_OFFSET	(9+(9+9+7)*0+9)
+#define MOUSE_HID_DESC_OFFSET	(9+(9+9+7)*1+9)
+#define DEBUG_HID_DESC_OFFSET	(9+(9+9+7)*2+9)
+#define EXTRA_HID_DESC_OFFSET	(9+(9+9+7)*3+9)
+#ifdef NKRO_ENABLE
+#   define NUM_INTERFACES	5
+#   define KBD2_HID_DESC_OFFSET	(9+(9+9+7)*4+9)
 #else
-#   define NUM_INTERFACES		4
+#   define NUM_INTERFACES	4
 #endif
-#define CONFIG1_DESC_SIZE		(9+(9+9+7)*NUM_INTERFACES)
+#define CONFIG1_DESC_SIZE	(9+(9+9+7)*NUM_INTERFACES)
 static uint8_t PROGMEM config1_descriptor[CONFIG1_DESC_SIZE] = {
 	// configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10
 	9, 					// bLength;
@@ -322,7 +358,7 @@ static uint8_t PROGMEM config1_descriptor[CONFIG1_DESC_SIZE] = {
 	// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
 	9,					// bLength
 	4,					// bDescriptorType
-	KEYBOARD_INTERFACE,			// bInterfaceNumber
+	KBD_INTERFACE,				// bInterfaceNumber
 	0,					// bAlternateSetting
 	1,					// bNumEndpoints
 	0x03,					// bInterfaceClass (0x03 = HID)
@@ -341,10 +377,10 @@ static uint8_t PROGMEM config1_descriptor[CONFIG1_DESC_SIZE] = {
 	// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
 	7,					// bLength
 	5,					// bDescriptorType
-	KEYBOARD_ENDPOINT | 0x80,		// bEndpointAddress
+	KBD_ENDPOINT | 0x80,			// bEndpointAddress
 	0x03,					// bmAttributes (0x03=intr)
-	KEYBOARD_SIZE, 0,			// wMaxPacketSize
-	1,					// bInterval
+	KBD_SIZE, 0,				// wMaxPacketSize
+	10,					// bInterval
 
 	// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
 	9,					// bLength
@@ -353,8 +389,13 @@ static uint8_t PROGMEM config1_descriptor[CONFIG1_DESC_SIZE] = {
 	0,					// bAlternateSetting
 	1,					// bNumEndpoints
 	0x03,					// bInterfaceClass (0x03 = HID)
+        // ThinkPad T23 BIOS doesn't work with boot mouse.
+	0x00,					// bInterfaceSubClass (0x01 = Boot)
+	0x00,					// bInterfaceProtocol (0x02 = Mouse)
+/*
 	0x01,					// bInterfaceSubClass (0x01 = Boot)
 	0x02,					// bInterfaceProtocol (0x02 = Mouse)
+*/
 	0,					// iInterface
 	// HID descriptor, HID 1.11 spec, section 6.2.1
 	9,					// bLength
@@ -427,11 +468,11 @@ static uint8_t PROGMEM config1_descriptor[CONFIG1_DESC_SIZE] = {
 	EXTRA_SIZE, 0,				// wMaxPacketSize
 	10,					// bInterval
 
-#ifdef USB_12KRO
+#ifdef NKRO_ENABLE
 	// interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
 	9,					// bLength
 	4,					// bDescriptorType
-	KEYBOARD_INTERFACE2,			// bInterfaceNumber
+	KBD2_INTERFACE,				// bInterfaceNumber
 	0,					// bAlternateSetting
 	1,					// bNumEndpoints
 	0x03,					// bInterfaceClass (0x03 = HID)
@@ -445,14 +486,14 @@ static uint8_t PROGMEM config1_descriptor[CONFIG1_DESC_SIZE] = {
 	0,					// bCountryCode
 	1,					// bNumDescriptors
 	0x22,					// bDescriptorType
-	sizeof(keyboard_hid_report_desc),     	// wDescriptorLength
+	sizeof(keyboard2_hid_report_desc),     	// wDescriptorLength
 	0,
 	// endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
 	7,					// bLength
 	5,					// bDescriptorType
-	KEYBOARD_ENDPOINT2 | 0x80,		// bEndpointAddress
+	KBD2_ENDPOINT | 0x80,			// bEndpointAddress
 	0x03,					// bmAttributes (0x03=intr)
-	KEYBOARD_SIZE, 0,			// wMaxPacketSize
+	KBD2_SIZE, 0,				// wMaxPacketSize
 	1,					// bInterval
 #endif
 };
@@ -494,17 +535,17 @@ static struct descriptor_list_struct {
         // CONFIGURATION descriptor
 	{0x0200, 0x0000, config1_descriptor, sizeof(config1_descriptor)},
         // HID/REPORT descriptors
-	{0x2100, KEYBOARD_INTERFACE, config1_descriptor+KEYBOARD_HID_DESC_OFFSET, 9},
-	{0x2200, KEYBOARD_INTERFACE, keyboard_hid_report_desc, sizeof(keyboard_hid_report_desc)},
+	{0x2100, KBD_INTERFACE, config1_descriptor+KBD_HID_DESC_OFFSET, 9},
+	{0x2200, KBD_INTERFACE, keyboard_hid_report_desc, sizeof(keyboard_hid_report_desc)},
 	{0x2100, MOUSE_INTERFACE, config1_descriptor+MOUSE_HID_DESC_OFFSET, 9},
 	{0x2200, MOUSE_INTERFACE, mouse_hid_report_desc, sizeof(mouse_hid_report_desc)},
 	{0x2100, DEBUG_INTERFACE, config1_descriptor+DEBUG_HID_DESC_OFFSET, 9},
 	{0x2200, DEBUG_INTERFACE, debug_hid_report_desc, sizeof(debug_hid_report_desc)},
 	{0x2100, EXTRA_INTERFACE, config1_descriptor+EXTRA_HID_DESC_OFFSET, 9},
 	{0x2200, EXTRA_INTERFACE, extra_hid_report_desc, sizeof(extra_hid_report_desc)},
-#ifdef USB_12KRO
-	{0x2100, KEYBOARD_INTERFACE2, config1_descriptor+KEYBOARD2_HID_DESC_OFFSET, 9},
-	{0x2200, KEYBOARD_INTERFACE2, keyboard_hid_report_desc, sizeof(keyboard_hid_report_desc)},
+#ifdef NKRO_ENABLE
+	{0x2100, KBD2_INTERFACE, config1_descriptor+KBD2_HID_DESC_OFFSET, 9},
+	{0x2200, KBD2_INTERFACE, keyboard2_hid_report_desc, sizeof(keyboard2_hid_report_desc)},
 #endif
         // STRING descriptors
 	{0x0300, 0x0000, (const uint8_t *)&string0, 4},
@@ -603,7 +644,7 @@ ISR(USB_GEN_vect)
 			}
 		}
 		if (usb_keyboard_idle_config && (++div4 & 3) == 0) {
-			UENUM = KEYBOARD_ENDPOINT;
+			UENUM = KBD_ENDPOINT;
 			if (UEINTX & (1<<RWAL)) {
 				usb_keyboard_idle_count++;
 				if (usb_keyboard_idle_count == usb_keyboard_idle_config) {
@@ -728,10 +769,12 @@ ISR(USB_COM_vect)
 			for (i=1; i<=6; i++) {
 				UENUM = i;
 				en = pgm_read_byte(cfg++);
-				UECONX = en;
-				if (en) {
-					UECFG0X = pgm_read_byte(cfg++);
-					UECFG1X = pgm_read_byte(cfg++);
+                                if (en) {
+                                    UECONX = (1<<EPEN);
+                                    UECFG0X = pgm_read_byte(cfg++);
+                                    UECFG1X = pgm_read_byte(cfg++);
+                                } else {
+                                    UECONX = 0;
 				}
 			}
         		UERST = 0x7E;
@@ -788,7 +831,7 @@ ISR(USB_COM_vect)
                         return;
                     }
 		}
-		if (wIndex == KEYBOARD_INTERFACE) {
+		if (wIndex == KBD_INTERFACE) {
 			if (bmRequestType == 0xA1) {
 				if (bRequest == HID_GET_REPORT) {
 					usb_wait_in_ready();
diff --git a/usb_keyboard.c b/usb_keyboard.c
index 3e13ae4e36..7055a653fb 100644
--- a/usb_keyboard.c
+++ b/usb_keyboard.c
@@ -4,11 +4,12 @@
 #include "usb_keyboard.h"
 #include "print.h"
 #include "debug.h"
+#include "util.h"
 
 
 // keyboard report.
-static usb_keyboard_report_t _report0 = { {0}, 0 };
-static usb_keyboard_report_t _report1 = { {0}, 0 };
+static usb_keyboard_report_t _report0 = { {0}, 0, false };
+static usb_keyboard_report_t _report1 = { {0}, 0, false };
 usb_keyboard_report_t *usb_keyboard_report = &_report0;
 usb_keyboard_report_t *usb_keyboard_report_prev = &_report1;
 
@@ -27,75 +28,37 @@ uint8_t usb_keyboard_idle_count=0;
 // 1=num lock, 2=caps lock, 4=scroll lock, 8=compose, 16=kana
 volatile uint8_t usb_keyboard_leds=0;
 
+// enable NKRO
+bool usb_keyboard_nkro = false;
+
 
 int8_t usb_keyboard_send(void)
 {
     return usb_keyboard_send_report(usb_keyboard_report);
 }
 
-
+static inline int8_t _send_report(usb_keyboard_report_t *report, uint8_t endpoint, uint8_t keys_start, uint8_t keys_end);
 int8_t usb_keyboard_send_report(usb_keyboard_report_t *report)
 {
-	uint8_t i, intr_state, timeout;
+    int8_t result = 0;
 
-	if (!usb_configured()) return -1;
-	intr_state = SREG;
-	cli();
-	UENUM = KEYBOARD_ENDPOINT;
-	timeout = UDFNUML + 50;
-	while (1) {
-		// are we ready to transmit?
-		if (UEINTX & (1<<RWAL)) break;
-		SREG = intr_state;
-		// has the USB gone offline?
-		if (!usb_configured()) return -1;
-		// have we waited too long?
-		if (UDFNUML == timeout) return -1;
-		// get ready to try checking again
-		intr_state = SREG;
-		cli();
-		UENUM = KEYBOARD_ENDPOINT;
-	}
-	UEDATX = report->mods;
-	UEDATX = 0;
-	for (i = 0; i < 6; i++) {
-		UEDATX = report->keys[i];
-	}
-	UEINTX = 0x3A;
-	SREG = intr_state;
-
-#ifdef USB_12KRO
-	if (!usb_configured()) return -1;
-	intr_state = SREG;
-	cli();
-	UENUM = KEYBOARD_ENDPOINT2;
-	timeout = UDFNUML + 50;
-	while (1) {
-		// are we ready to transmit?
-		if (UEINTX & (1<<RWAL)) break;
-		SREG = intr_state;
-		// has the USB gone offline?
-		if (!usb_configured()) return -1;
-		// have we waited too long?
-		if (UDFNUML == timeout) return -1;
-		// get ready to try checking again
-		intr_state = SREG;
-		cli();
-		UENUM = KEYBOARD_ENDPOINT2;
-	}
-	UEDATX = report->mods;
-	UEDATX = 0;
-	for (i = 6; i < 12; i++) {
-		UEDATX = report->keys[i];
-	}
-	UEINTX = 0x3A;
-	SREG = intr_state;
+#ifdef NKRO_ENABLE
+    if (usb_keyboard_nkro)
+        result = _send_report(report, KBD2_ENDPOINT, 0, KBD2_REPORT_KEYS);
+    else
 #endif
+    {
+        if (usb_keyboard_protocol)
+            result = _send_report(report, KBD_ENDPOINT, 0, KBD_REPORT_KEYS);
+        else
+            result = _send_report(report, KBD_ENDPOINT, 0, 6);
+    }
 
-	usb_keyboard_idle_count = 0;
-	report->is_sent =true;
-	usb_keyboard_print_report(report);
-	return 0;
+    if (result) return result;
+    usb_keyboard_idle_count = 0;
+    report->is_sent =true;
+    usb_keyboard_print_report(report);
+    return 0;
 }
 
 void usb_keyboard_swap_report(void) {
@@ -111,7 +74,7 @@ void usb_keyboard_clear_report(void) {
 }
 
 void usb_keyboard_clear_keys(void) {
-    for (int i = 0; i < KEYBOARD_REPORT_MAX; i++) usb_keyboard_report->keys[i] = 0;
+    for (int i = 0; i < KEYS_MAX; i++) usb_keyboard_report->keys[i] = 0;
 }
 
 void usb_keyboard_clear_mods(void)
@@ -119,6 +82,17 @@ void usb_keyboard_clear_mods(void)
     usb_keyboard_report->mods = 0;
 }
 
+void usb_keyboard_set_keys(uint8_t *keys)
+{
+    for (int i = 0; i < KEYS_MAX; i++)
+        usb_keyboard_report->keys[i] = keys[i];
+}
+
+void usb_keyboard_set_mods(uint8_t mods)
+{
+    usb_keyboard_report->mods = mods;
+}
+
 void usb_keyboard_add_code(uint8_t code)
 {
     if (IS_MOD(code)) {
@@ -128,25 +102,17 @@ void usb_keyboard_add_code(uint8_t code)
     }
 }
 
+static inline void _add_key_byte(uint8_t code);
+static inline void _add_key_bit(uint8_t code);
 void usb_keyboard_add_key(uint8_t code)
 {
-    for (int i = 0; i < KEYBOARD_REPORT_MAX; i++) {
-        if (!usb_keyboard_report->keys[i]) {
-            usb_keyboard_report->keys[i] = code;
-            return;
-        }
+#ifdef NKRO_ENABLE
+    if (usb_keyboard_nkro) {
+        _add_key_bit(code);
+        return;
     }
-}
-
-void usb_keyboard_set_keys(uint8_t *keys)
-{
-    for (int i = 0; i < KEYBOARD_REPORT_MAX; i++)
-        usb_keyboard_report->keys[i] = keys[i];
-}
-
-void usb_keyboard_set_mods(uint8_t mods)
-{
-    usb_keyboard_report->mods = mods;
+#endif
+    _add_key_byte(code);
 }
 
 void usb_keyboard_add_mod(uint8_t code)
@@ -165,12 +131,18 @@ void usb_keyboard_del_code(uint8_t code)
 
 void usb_keyboard_del_key(uint8_t code)
 {
-    for (int i = 0; i < KEYBOARD_REPORT_MAX; i++) {
+#ifdef NKRO_ENABLE
+    if ((code>>3) < KEYS_MAX) {
+        usb_keyboard_keys[code>>3] &= ~(1<<(code&7));
+    }
+#else
+    for (int i = 0; i < KEYS_MAX; i++) {
         if (usb_keyboard_report->keys[i] == code) {
             usb_keyboard_report->keys[i] = KB_NO;
             return;
         }
     }
+#endif
 }
 
 void usb_keyboard_del_mod(uint8_t code)
@@ -186,7 +158,7 @@ bool usb_keyboard_is_sent(void)
 bool usb_keyboard_has_key(void)
 {
     uint8_t keys = 0;    
-    for (int i = 0; i < KEYBOARD_REPORT_MAX; i++) keys |= usb_keyboard_report->keys[i];
+    for (int i = 0; i < KEYS_MAX; i++) keys |= usb_keyboard_report->keys[i];
     return keys ? true : false;
 }
 
@@ -195,10 +167,86 @@ bool usb_keyboard_has_mod(void)
     return usb_keyboard_report->mods ? true : false;
 }
 
+uint8_t usb_keyboard_get_key(void)
+{
+#ifdef NKRO_ENABLE
+    if (usb_keyboard_nkro) {
+        uint8_t i = 0;
+        for (; i < KEYS_MAX && !usb_keyboard_keys[i]; i++);
+        return i<<3 | biton(usb_keyboard_keys[i]);
+    }
+#endif
+    return usb_keyboard_keys[0];
+}
+
 void usb_keyboard_print_report(usb_keyboard_report_t *report)
 {
     if (!debug_keyboard) return;
     print("keys: ");
-    for (int i = 0; i < KEYBOARD_REPORT_MAX; i++) { phex(report->keys[i]); print(" "); }
+    for (int i = 0; i < KEYS_MAX; i++) { phex(report->keys[i]); print(" "); }
     print(" mods: "); phex(report->mods); print("\n");
 }
+
+
+static inline int8_t _send_report(usb_keyboard_report_t *report, uint8_t endpoint, uint8_t keys_start, uint8_t keys_end)
+{
+    uint8_t intr_state, timeout;
+
+    if (!usb_configured()) return -1;
+    intr_state = SREG;
+    cli();
+    UENUM = endpoint;
+    timeout = UDFNUML + 50;
+    while (1) {
+            // are we ready to transmit?
+            if (UEINTX & (1<<RWAL)) break;
+            SREG = intr_state;
+            // has the USB gone offline?
+            if (!usb_configured()) return -1;
+            // have we waited too long?
+            if (UDFNUML == timeout) return -1;
+            // get ready to try checking again
+            intr_state = SREG;
+            cli();
+            UENUM = endpoint;
+    }
+    UEDATX = report->mods;
+    UEDATX = 0;
+    for (uint8_t i = keys_start; i < keys_end; i++) {
+            UEDATX = report->keys[i];
+    }
+    UEINTX = 0x3A;
+    SREG = intr_state;
+    return 0;
+}
+
+static inline void _add_key_byte(uint8_t code)
+{
+    // TODO: fix ugly code
+    int8_t i = 0;
+    int8_t empty = -1;
+    for (; i < KEYS_MAX; i++) {
+        if (usb_keyboard_keys_prev[i] == code) {
+            usb_keyboard_keys[i] = code;
+            break;
+        }
+        if (empty == -1 &&
+                usb_keyboard_keys_prev[i] == 0 &&
+                usb_keyboard_keys[i] == 0) {
+            empty = i;
+        }
+    }
+    if (i == KEYS_MAX) {
+        if (empty != -1) {
+            usb_keyboard_keys[empty] = code;
+        }
+    }
+}
+
+static inline void _add_key_bit(uint8_t code)
+{
+    if ((code>>3) < KEYS_MAX) {
+        usb_keyboard_keys[code>>3] |= 1<<(code&7);
+    }
+}
+
diff --git a/usb_keyboard.h b/usb_keyboard.h
index 872e2afc72..88a641f767 100644
--- a/usb_keyboard.h
+++ b/usb_keyboard.h
@@ -6,14 +6,26 @@
 #include "usb.h"
 
 
-#define KEYBOARD_INTERFACE	0
-#define KEYBOARD_ENDPOINT	1
-#ifdef USB_12KRO
-#define KEYBOARD_INTERFACE2	4
-#define KEYBOARD_ENDPOINT2	5
+#define KBD_INTERFACE		0
+#define KBD_ENDPOINT		1
+#define KBD_SIZE		8
+#define KBD_BUFFER		EP_DOUBLE_BUFFER
+#define KBD_REPORT_KEYS		(KBD_SIZE - 2)
+
+// secondary keyboard
+#ifdef NKRO_ENABLE
+#define KBD2_INTERFACE		4
+#define KBD2_ENDPOINT		5
+#define KBD2_SIZE		16
+#define KBD2_BUFFER		EP_DOUBLE_BUFFER
+#define KBD2_REPORT_KEYS	(KBD2_SIZE - 2)
+#endif
+
+#if defined(KBD2_REPORT_KEYS) && KBD2_REPORT_KEYS > KBD_REPORT_KEYS
+#define KEYS_MAX KBD2_REPORT_KEYS
+#else
+#define KEYS_MAX KBD_REPORT_KEYS
 #endif
-#define KEYBOARD_SIZE		8
-#define KEYBOARD_BUFFER		EP_DOUBLE_BUFFER
 
 #define BIT_LCTRL   (1<<0)
 #define BIT_LSHIFT  (1<<1)
@@ -28,13 +40,8 @@
 #define BIT_LSFT BIT_LSHIFT
 #define BIT_RSFT BIT_RSHIFT
 
-#ifdef USB_12KRO
-#   define KEYBOARD_REPORT_MAX	12
-#else
-#   define KEYBOARD_REPORT_MAX	6
-#endif
 typedef struct report {
-    uint8_t keys[KEYBOARD_REPORT_MAX];
+    uint8_t keys[KEYS_MAX];
     uint8_t mods;
     bool is_sent;
 } usb_keyboard_report_t;
@@ -52,9 +59,9 @@ extern uint8_t usb_keyboard_protocol;
 extern uint8_t usb_keyboard_idle_config;
 extern uint8_t usb_keyboard_idle_count;
 extern volatile uint8_t usb_keyboard_leds;
+extern bool usb_keyboard_nkro;
 
 
-int8_t usb_keyboard_press(uint8_t key, uint8_t modifier);
 int8_t usb_keyboard_send(void);
 int8_t usb_keyboard_send_report(usb_keyboard_report_t *report);
 
@@ -64,7 +71,7 @@ void usb_keyboard_clear_report(void);
 void usb_keyboard_clear_keys(void);
 void usb_keyboard_clear_mods(void);
 
-void usb_keyboard_set_keys(uint8_t keys[6]);
+void usb_keyboard_set_keys(uint8_t *keys);
 void usb_keyboard_set_mods(uint8_t mods);
 
 void usb_keyboard_add_code(uint8_t code);
@@ -79,6 +86,8 @@ bool usb_keyboard_is_sent(void);
 bool usb_keyboard_has_key(void);
 bool usb_keyboard_has_mod(void);
 
+uint8_t usb_keyboard_get_key(void);
+
 void usb_keyboard_print_report(usb_keyboard_report_t *report);
 
 #endif
diff --git a/usb_mouse.c b/usb_mouse.c
index dd5d0b0ac3..98292bdd84 100644
--- a/usb_mouse.c
+++ b/usb_mouse.c
@@ -59,8 +59,10 @@ int8_t usb_mouse_move(int8_t x, int8_t y, int8_t wheel, int8_t hwheel)
 	UEDATX = mouse_buttons;
 	UEDATX = x;
 	UEDATX = y;
-	UEDATX = wheel;
-	UEDATX = hwheel;
+        if (mouse_protocol) {
+            UEDATX = wheel;
+            UEDATX = hwheel;
+        }
         
 	UEINTX = 0x3A;
 	SREG = intr_state;
diff --git a/util.c b/util.c
index b02d077cc3..efc7c14818 100644
--- a/util.c
+++ b/util.c
@@ -1,5 +1,6 @@
 #include "util.h"
 
+// bit population
 int bitpop(uint8_t bits)
 {
     int c;
@@ -8,6 +9,7 @@ int bitpop(uint8_t bits)
     return c;
 }
 
+// most significant on-bit
 int biton(uint8_t bits)
 {
     int n = 0;