~ruther/qmk_firmware

51f17f02317700e64b3c1113fe230d78bac7fecd — tmk 14 years ago 66ece29
add build option: NKRO_ENABLE(remove: USB_12KRO)
12 files changed, 414 insertions(+), 260 deletions(-)

M Makefile.common
M README
A USB_NKRO.txt
M hhkb/Makefile
M hhkb/keymap.c
M key_process.c
M tmk.c
M usb.c -rw-r--r-- => -rwxr-xr-x
M usb_keyboard.c
M usb_keyboard.h
M usb_mouse.c
M util.c
M Makefile.common => Makefile.common +2 -2
@@ 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



M README => README +17 -92
@@ 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. 
Use PJRC's hid_listen.exe to see debug messages.

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

A USB_NKRO.txt => USB_NKRO.txt +99 -0
@@ 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

M hhkb/Makefile => hhkb/Makefile +3 -2
@@ 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

M hhkb/keymap.c => hhkb/keymap.c +3 -3
@@ 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)
     * ,-----------------------------------------------------------.

M key_process.c => key_process.c +45 -26
@@ 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();

M tmk.c => tmk.c +8 -2
@@ 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

M usb.c => usb.c +78 -35
@@ 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();

M usb_keyboard.c => usb_keyboard.c +129 -81
@@ 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;

	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;
    int8_t result = 0;

#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);
    }
}


M usb_keyboard.h => usb_keyboard.h +24 -15
@@ 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

M usb_mouse.c => usb_mouse.c +4 -2
@@ 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;

M util.c => util.c +2 -0
@@ 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;