diff --git a/converter/ibmpc_usb/Makefile b/converter/ibmpc_usb/Makefile index 30ce9248..242e7ce1 100644 --- a/converter/ibmpc_usb/Makefile +++ b/converter/ibmpc_usb/Makefile @@ -69,7 +69,7 @@ OPT_DEFS += -DSUSPEND_MODE_STANDBY # comment out to disable the options. # BOOTMAGIC_ENABLE ?= no # Virtual DIP switch configuration(+1000) -MOUSEKEY_ENABLE ?= no # Mouse keys(+4700) +MOUSEKEY_ENABLE ?= yes # Mouse keys(+4700) EXTRAKEY_ENABLE ?= yes # Audio control and System control(+450) CONSOLE_ENABLE ?= yes # Console for debug(+400) COMMAND_ENABLE ?= yes # Commands for debug and configuration @@ -78,6 +78,10 @@ NKRO_ENABLE ?= yes # USB Nkey Rollover KEYMAP_SECTION_ENABLE ?= yes UNIMAP_ENABLE ?= yes +# IBMPC Options +IBMPC_SECONDARY ?= no # enable secondary interface +IBMPC_MOUSE_ENABLE ?= no # enable mouse support + # Optimize size but this may cause error "relocation truncated to fit" #EXTRALDFLAGS = -Wl,--relax @@ -102,6 +106,16 @@ else endif +ifeq (yes,$(strip $(IBMPC_SECONDARY))) + OPT_DEFS += -DIBMPC_SECONDARY +endif + +ifeq (yes,$(strip $(IBMPC_MOUSE_ENABLE))) + OPT_DEFS += -DIBMPC_MOUSE_ENABLE + OPT_DEFS += -DMOUSE_ENABLE +endif + + # Search Path VPATH += $(TARGET_DIR) VPATH += $(TMK_DIR) diff --git a/converter/ibmpc_usb/Makefile.x2 b/converter/ibmpc_usb/Makefile.x2 new file mode 100644 index 00000000..5c9bc325 --- /dev/null +++ b/converter/ibmpc_usb/Makefile.x2 @@ -0,0 +1,12 @@ +# With two interfaces on PS/2 connector +TARGET ?= ibmpc_usb_x2 +MCU = atmega32u2 +SRC ?= protocol/ibmpc.cpp \ + ibmpc_usb.cpp + +COMMAND_ENABLE ?= no # Commands for debug and configuration + +IBMPC_SECONDARY ?= yes # enable secondary interface +IBMPC_MOUSE_ENABLE ?= yes # enable mouse support + +include Makefile diff --git a/converter/ibmpc_usb/README.md b/converter/ibmpc_usb/README.md index c4e1141d..21f8c73f 100644 --- a/converter/ibmpc_usb/README.md +++ b/converter/ibmpc_usb/README.md @@ -1,22 +1,23 @@ IBM PC Keyboard Converter ========================= -The converter translates IBM PC keyboard protocols to use classic keyboards with modern computer with USB ports. -It supports both IBM XT and AT protocols, and all of scan code set 1, 2 and 3 with one firmware. - -This is not finished project and still work in progress as of 2020-03-02. Test in the field and feedback from users are needed to improve firmware code. - -You can discuss about this project here. - -https://geekhack.org/index.php?topic=103648.0 +The converter translates IBM PC keyboard protocols into USB HID to use classic keyboards on modern computer with USB ports. +This can supports IBM XT and AT protocols and recognize scan code set 1, 2 and 3 with just one firmware. -This project is intended to integrated existent TMK XT, PS/2 and Terminal converters. +This project is intended to integrated existent TMK XT, PS/2 and Terminal keyboard converters. - IBM XT converter: https://geekhack.org/index.php?topic=94649.0 - PS/2 converter: https://geekhack.org/index.php?topic=14618.0 - IBM Terminal converter: http://geekhack.org/index.php?topic=27272.0 +Test in the field and feedback from users are needed to improve firmware code. + +Discuss in this thread or on github. https://geekhack.org/index.php?topic=103648.0 + +You can buy prebuilt TMK converter to support this project here. https://geekhack.org/index.php?topic=72052.0 + + Keyboard supported ------------------ @@ -27,87 +28,46 @@ Keyboard supported - PC Terminal keyboard of IBM 5271(3270 PC) - 122-key: 6110344 6110345 1397000 - 102-key: 1390680 1395764 1392595 -- PS/2 keyboards(AT+CodeSet2) -- Clones of above models + - IBM 5576-001 +- PS/2 keyboards +- Other IBM PC compatible keyboards + +Optionally PS/2 mouses can be supported. +- PS/2 mouse + - Default + - Microsoft IntelliMouse + - Microsoft ExplorerMouse + - Logitech PS/2++ -Hardware +Firmware -------- -Firmware supports ATMega32u4 and ATMega32u2 by default, Teensy2 or ProMicro can be used. -Wire controller pins below to keyboard signals, besides VCC and GND. This is compatible for Soarer's converter. - -- Data PD0 -- Clock PD1 -- Reset PB6 or PB7 (For some of XT keyboards. Not needed for AT, PS/2 and Terminal) - -Pull up resistors of 1-4.7K Ohm on both Data and Clock line are recommended, without them it won't work in some cases. - -### Reset -Old Type-1 IBM XT keyboard and some of XT clones need this to reset its controller on startup. Many of IBM XT keyboards available are Type-2 and don't need the reset pin. - -See this for Type-1 vs Type-2: -https://vintagecomputer.ca/ibm-pc-model-f-keyboard-type-1-vs-type-2/ - -As for clones Zenith Z-150 XT and Leading Edge DC-2014 are known to need this. - -### Connector pinouts -#### XT -- http://www.kbdbabel.org/conn/kbd_connector_ibmpc.png -- https://allpinouts.org/pinouts/connectors/input_device/keyboard-xt-5-pin/ - -#### AT -- http://www.kbdbabel.org/conn/kbd_connector_ps2.png -- https://old.pinouts.ru/InputCables/KeyboardPC5_pinout.shtml - -#### PS/2 -- https://pinouts.ru/InputCables/KeyboardPC6_pinout.shtml - -#### Terminal -- http://www.kbdbabel.org/conn/kbd_connector_ibmterm.png -- http://www.kbdbabel.org/conn/kbd_connector_ibm3179_318x_319x.png - - - -Source Code ------------ -https://github.com/tmk/tmk_keyboard/tree/master/converter/ibmpc_usb - - - -Build Firmware --------------- +### Build Firmware $ cd converter/ibmpc_usb - $ make clean - $ make + $ make -f Makefile. clean + $ make -f Makefile. + + +### Build Options +In Makefiile: + + # IBMPC Options + IBMPC_SECONDARY ?= no + IBMPC_MOUSE_ENABLE ?= no + +These options bloat firmware size and you may need to disable other options. + +- `IBMPC_SECONDARY` - enables secondary interface for converter with PS/2 Mini-DIN-6 connector +- `IBMPC_MOUSE_ENABLE` - enables PS/2 mouse support -Keyboard discrimination ------------------------ -This section explains how the converter determines proper protocol and scan code set for keyboard. The converter need to do that before starting to receive and translate scan codes from keyboard. +### Keymap Editor +You can edit keymap on web browser and download prebuilt firmware here. -### Keyboard ID -After startup the converter sends 0xF2 command to get keyboard ID and sees how the keyboard responds to the command. - -Response from keyboard: - -- XT keyboard doesn't support any command and returns no response. -- AT keyboard should respond with 0xFA to the command but returns no keyboard ID. -- PS/2 keyboard should respond with 0xFA to the command, followd by keyboard ID, such as 0xAB86. -- Terminal keyboard should respond with 0xFA to the command, followed by keyboard ID, such as 0xBFBF. - -Now we can dscriminate the keyboard and determine suitable protocol and scan code set as described below. - -### Protocol -- Signals from XT keyboard are recognized by XT protocol. -- Signals from AT, PS/2 and Terminal keyboard are recognized by AT protocol. - -### Scan code Set -- Scan codes from XT keyboard are handled as CodeSet1. -- Scan codes from AT and PS/2 keyboard are handled as CodeSet2. -- Scan codes from Terminal keyhboard are handled as CodeSet3. +http://www.tmk-kbd.com/tmk_keyboard/editor/#ibmpc_usb @@ -115,62 +75,74 @@ Debug ----- Use hid_listen to see debug outputs from the converter. -https://www.pjrc.com/teensy/hid_listen.html +https://github.com/tmk/tmk_keyboard/wiki#debug + + + +Converter Hardware +------------------ +This firmware supports ATMega32u4, ATMega32u2 and AT90USB1286 by default, Teensy2/2++ or ProMicro can be used. +Wire microcontroller pins below to keyboard signals, besides VCC and GND. This pin configuration is compatible for Soarer's converter. + +Pull up resistors of 1-4.7K Ohm on both Data and Clock line are recommended, without them it won't work in some cases. + +- Data PD0 +- Clock PD1 +- Reset PB6 or PB7 (For some of XT keyboards. Not needed for AT, PS/2 and Terminal) + +For optional secondary interface use these pins. +- Data PD2 +- Clock PD3 + + +### Reset +Old Type-1 IBM XT keyboard and some of XT clones need Reset pin to starup its controller. +Connect Reset pin to pin3 of DIN-5(180-degree) connector. This should not harm keyboards which don't require reset, +it is safe and recommended to have Reset pin on AT/XT converter. + +Zenith Z-150 XT and Leading Edge DC-2014 are also known to need this. + +See this for IBM XT Type-1 vs Type-2: https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol#type-1-vs-type-2 + + +### Connector pinouts +- DIN: https://en.wikipedia.org/wiki/DIN_connector#Circular_connectors +- Mini-DIN: https://en.wikipedia.org/wiki/Mini-DIN_connector + +#### XT - DIN-5(180-degree) +- http://www.kbdbabel.org/conn/kbd_connector_ibmpc.png +- https://allpinouts.org/pinouts/connectors/input_device/keyboard-xt-5-pin/ + +#### AT - DIN-5(180-degree) +- http://www.kbdbabel.org/conn/kbd_connector_ps2.png +- https://old.pinouts.ru/InputCables/KeyboardPC5_pinout.shtml + +#### Terminal - DIN-5(240-degree) or DIN-6 +- http://www.kbdbabel.org/conn/kbd_connector_ibmterm.png +- http://www.kbdbabel.org/conn/kbd_connector_ibm3179_318x_319x.png + +#### PS/2 - Mini-DIN-6 +- https://pinouts.ru/InputCables/KeyboardPC6_pinout.shtml + +For secondary interface use pin2 and pin6 for data and clock respectively. +You can use PS/2 Y-splitter cable to access secondary interface. + + + +Scan Code Mapping +----------------- +This is how the converter map keyboard scan code to USB key. + +https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-Keyboard-Converter#default-mapping Resources --------- -IBM PC Keyboard Protocol Resources: - -[a] [Microsoft USB HID to PS/2 Translation Table - Scan Code Set 1 and 2]( -http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf) - -[b] [Microsoft Keyboard Scan Code Specification - Special rules of Scan Code Set 1 and 2]( -http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc) - -[1] [PS/2 Reference Manuals - Collection of IBM Personal System/2 documents]( -http://www.mcamafia.de/pdf/pdfref.htm) - -[2] [Keyboard and Auxiliary Device Controller - Signal Timing and Format]( -http://www.mcamafia.de/pdf/ibm_hitrc07.pdf) - -[3] [Keyboards(101- and 102-key) - Keyboard Layout, Scan Code Set, POR, and Commands]( -http://www.mcamafia.de/pdf/ibm_hitrc11.pdf) - -[4] [IBM PC XT Keyboard Protocol]( -https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol) - -[5] [IBM Keyboard Scan Code by John Elliott - 83-key, 84-key, 102-key and 122-key]( -https://www.seasip.info/VintagePC/index.html) - -[6] [IBM 1391406 Keyboard - Scan Code Set 2 of 102-key PS/2 keyboard]( -https://www.seasip.info/VintagePC/ibm_1391406.html) - -[7] [The IBM 6110344 Keyboard - Scan Code Set 3 of 122-key terminal keyboard]( -https://www.seasip.info/VintagePC/ibm_6110344.html) - -[8] [IBM PC AT Technical Reference 1986]( -(http://bitsavers.org/pdf/ibm/pc/at/6183355_PC_AT_Technical_Reference_Mar86.pdf) - -[y] [TrackPoint Engineering Specifications for version 3E]( -https://web.archive.org/web/20100526161812/http://wwwcssrv.almaden.ibm.com/trackpoint/download.html) - -[z] [Soarer's XT/AT/PS2/Terminal to USB converter]( -https://geekhack.org/index.php?topic=17458.0) - - - -TODO ----- -### Reset method for rescue -For converter without accesible reset button when magickey combo doesn't work. - -Check pin status at powerup: - -- if Data and/or Clock are pull down to GND - -### Force protocol and scan code set -Keyboard discrimination may fail and you have to configure them by hand. - -### Add AT90usb1286 support for Teensy2++ +- Source Code: https://github.com/tmk/tmk_keyboard/tree/master/converter/ibmpc_usb +- Discussion: https://geekhack.org/index.php?topic=103648.0 +- Conveter impl: https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-Keyboard-Converter +- XT Protocol: https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol +- AT Protocol: https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol +- Scan Code: https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#scan-codes +- PS/2 Mouse: https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-Mouse diff --git a/converter/ibmpc_usb/config.h b/converter/ibmpc_usb/config.h index cf734bd9..1b36c58a 100644 --- a/converter/ibmpc_usb/config.h +++ b/converter/ibmpc_usb/config.h @@ -29,8 +29,8 @@ along with this program. If not, see . /* matrix size */ -#define MATRIX_ROWS 16 // keycode bit: 6-3 -#define MATRIX_COLS 8 // keycode bit: 2-0 +#define MATRIX_ROWS 8 +#define MATRIX_COLS 16 /* key combination for command */ @@ -53,56 +53,77 @@ along with this program. If not, see . * Pin and interrupt configuration */ #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega32U2__) || defined(__AVR_AT90USB1286__) -/* clock line */ +// clock requires External Interrupt pin(INT*) #define IBMPC_CLOCK_PORT PORTD #define IBMPC_CLOCK_PIN PIND #define IBMPC_CLOCK_DDR DDRD -#define IBMPC_CLOCK_BIT 1 -/* data line */ #define IBMPC_DATA_PORT PORTD #define IBMPC_DATA_PIN PIND #define IBMPC_DATA_DDR DDRD + +// primary interface +#define IBMPC_CLOCK_BIT 1 #define IBMPC_DATA_BIT 0 -/* reset line */ -#define IBMPC_RST_PORT PORTB -#define IBMPC_RST_PIN PINB -#define IBMPC_RST_DDR DDRB -#define IBMPC_RST_BIT1 6 -#define IBMPC_RST_BIT2 7 -/* reset for XT Type-1 keyboard: low pulse for 500ms */ -#define IBMPC_RST_HIZ() do { \ - IBMPC_RST_PORT &= ~(1<. #include #include +#include #include "print.h" #include "util.h" #include "debug.h" @@ -26,6 +27,8 @@ along with this program. If not, see . #include "matrix.h" #include "timer.h" #include "action.h" +#include "unimap.h" +#include "unimap_trans.h" #include "ibmpc_usb.h" #include "ibmpc.h" @@ -38,9 +41,9 @@ static int8_t process_cs2(uint8_t code); static int8_t process_cs3(uint8_t code); -static uint8_t matrix[MATRIX_ROWS]; -#define ROW(code) ((code>>3)&0x0F) -#define COL(code) (code&0x07) +static matrix_row_t matrix[MATRIX_ROWS]; +#define ROW(code) ((code>>4)&0x07) +#define COL(code) (code&0x0F) static int16_t read_wait(uint16_t wait_ms) { @@ -131,6 +134,7 @@ uint8_t matrix_scan(void) READ_ID, SETUP, LOOP, + ERROR, } state = INIT; static uint16_t init_time; @@ -152,7 +156,7 @@ uint8_t matrix_scan(void) // keyboard init again if (state == LOOP) { xprintf("[RST] "); - state = INIT; + state = ERROR; } } @@ -172,7 +176,7 @@ uint8_t matrix_scan(void) ((current_protocol&IBMPC_PROTOCOL_AT) && (ibmpc_protocol&IBMPC_PROTOCOL_XT))) { if (state == LOOP) { xprintf("[CHG] "); - state = INIT; + state = ERROR; } } @@ -188,7 +192,6 @@ uint8_t matrix_scan(void) current_protocol = 0; matrix_clear(); - clear_keyboard(); init_time = timer_read(); state = WAIT_SETTLE; @@ -473,13 +476,13 @@ MOUSE_DONE: switch (keyboard_kind) { case PC_XT: - if (process_cs1(code) == -1) state = INIT; + if (process_cs1(code) == -1) state = ERROR; break; case PC_AT: - if (process_cs2(code) == -1) state = INIT; + if (process_cs2(code) == -1) state = ERROR; break; case PC_TERMINAL: - if (process_cs3(code) == -1) state = INIT; + if (process_cs3(code) == -1) state = ERROR; break; #ifdef IBMPC_MOUSE_ENABLE case PC_MOUSE: { @@ -493,9 +496,9 @@ MOUSE_DONE: b0 = code; b1 = ibmpc_host_recv_response(); - if (b1 == -1) break; + if (b1 == -1) { state = ERROR; break; } b2 = ibmpc_host_recv_response(); - if (b2 == -1) break; + if (b2 == -1) { state = ERROR; break; } switch (mouse_id) { case MOUSE_DEFAULT: @@ -593,6 +596,11 @@ MOUSE_DONE: } } break; + case ERROR: + // something goes wrong + clear_keyboard(); + state = INIT; + break; default: break; } @@ -606,7 +614,7 @@ bool matrix_is_on(uint8_t row, uint8_t col) } inline -uint8_t matrix_get_row(uint8_t row) +matrix_row_t matrix_get_row(uint8_t row) { return matrix[row]; } @@ -620,20 +628,44 @@ uint8_t matrix_key_count(void) return count; } +extern const action_t actionmaps[][UNIMAP_ROWS][UNIMAP_COLS]; +action_t action_for_key(uint8_t layer, keypos_t key) +{ + return (action_t){ .code = pgm_read_word(&actionmaps[(layer)][key.row & 0x07][key.col & 0x0F]) }; +} + +static uint8_t to_unimap(uint8_t code) { + uint8_t row = ROW(code); + uint8_t col = COL(code); + switch (keyboard_kind) { + case PC_XT: + return pgm_read_byte(&unimap_cs1[row][col]); + case PC_AT: + return pgm_read_byte(&unimap_cs2[row][col]); + case PC_TERMINAL: + return pgm_read_byte(&unimap_cs3[row][col]); + default: + return UNIMAP_NO; + } +} inline static void matrix_make(uint8_t code) { - if (!matrix_is_on(ROW(code), COL(code))) { - matrix[ROW(code)] |= 1< 0x7F) return; + if (!matrix_is_on(ROW(u), COL(u))) { + matrix[ROW(u)] |= 1< 0x7F) return; + if (matrix_is_on(ROW(u), COL(u))) { + matrix[ROW(u)] &= ~(1< + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include +#include +#include +#include "print.h" +#include "util.h" +#include "debug.h" +#include "host.h" +#include "led.h" +#include "matrix.h" +#include "timer.h" +#include "action.h" +#include "hook.h" +#include "ibmpc.hpp" +#include "ibmpc_usb.hpp" + + +// Converter +IBMPCConverter converter0 = IBMPCConverter(IBMPC::interface0); +#if defined(IBMPC_CLOCK_BIT1) && defined(IBMPC_DATA_BIT1) +IBMPCConverter converter1 = IBMPCConverter(IBMPC::interface1); +#endif + + +void hook_early_init(void) +{ + converter0.init(); +#if defined(IBMPC_CLOCK_BIT1) && defined(IBMPC_DATA_BIT1) + converter1.init(); +#endif +} + +void matrix_init(void) +{ + debug_enable = true; + + matrix_clear(); +} + +matrix_row_t matrix_get_row(uint8_t row) +{ + return IBMPCConverter::matrix_get_row(row); +} + +void matrix_clear(void) +{ + IBMPCConverter::matrix_clear(); +} + +uint8_t matrix_scan(void) +{ + converter0.process_interface(); +#if defined(IBMPC_CLOCK_BIT1) && defined(IBMPC_DATA_BIT1) + converter1.process_interface(); +#endif +} + +void led_set(uint8_t usb_led) +{ + converter0.set_led(usb_led); +#if defined(IBMPC_CLOCK_BIT1) && defined(IBMPC_DATA_BIT1) + converter1.set_led(usb_led); +#endif +} + +extern const action_t actionmaps[][UNIMAP_ROWS][UNIMAP_COLS]; +action_t action_for_key(uint8_t layer, keypos_t key) +{ + return (action_t){ .code = pgm_read_word(&actionmaps[(layer)][key.row & 0x07][key.col & 0x0F]) }; +} + + +void IBMPCConverter::set_led(uint8_t usb_led) +{ + // Sending before keyboard recognition may be harmful for XT keyboard + if (keyboard_kind == NONE) return; + + // XT keyobard doesn't support any command and it is harmful perhaps + // https://github.com/tmk/tmk_keyboard/issues/635#issuecomment-626993437 + if (keyboard_kind == PC_XT) return; + if (keyboard_kind == PC_MOUSE) return; + + // It should be safe to send the command to keyboards with AT protocol + // - IBM Terminal doesn't support the command and response with 0xFE but it is not harmful. + // - Some other Terminals like G80-2551 supports the command. + // https://geekhack.org/index.php?topic=103648.msg2894921#msg2894921 + + // TODO: PC_TERMINAL_IBM_RT support + uint8_t ibmpc_led = 0; + if (usb_led & (1< 3000) { + state = AT_RESET; + } + break; + case AT_RESET: + xprintf("A%u ", timer_read()); + + // SKIDATA-2-DE(and some other keyboards?) stores 'Code Set' setting in nonvolatile memory + // and keeps it until receiving reset. Sending reset here may be useful to clear it, perhaps. + // https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#select-alternate-scan-codesf0 + + // reset command + if (0xFA == ibmpc.host_send(0xFF)) { + state = WAIT_AA; + } else { + state = XT_RESET; + } + break; + case XT_RESET: + // Reset XT-initialize keyboard + // XT: hard reset 500ms for IBM XT Type-1 keyboard and clones + // XT: soft reset 20ms min + // https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol#keyboard-soft-reset + ibmpc.host_disable(); // soft reset: Clock Lo/Data Hi + IBMPC_RST_LO(); // hard reset: Reset pin Lo + + init_time = timer_read(); + state = XT_RESET_WAIT; + break; + case XT_RESET_WAIT: + if (timer_elapsed(init_time) > 500) { + state = XT_RESET_DONE; + } + break; + case XT_RESET_DONE: + IBMPC_RST_HIZ(); // hard reset: Reset pin HiZ + ibmpc.host_isr_clear(); + ibmpc.host_enable(); // soft reset: idle(Clock Hi/Data Hi) + + xprintf("X%u ", timer_read()); + init_time = timer_read(); + state = WAIT_AA; + break; + case WAIT_AA: + // 1) Read BAT code and ID on keybaord power-up + // For example, XT/AT sends 'AA' and Terminal sends 'AA BF BF' after BAT + // AT 84-key: POR and BAT can take 900-9900ms according to AT TechRef [8] 4-7 + // AT 101/102-key: POR and BAT can take 450-2500ms according to AT TechRef [8] 4-39 + // 2) Read key typed by user or anything after error on protocol or scan code + // This can happen in case of keyboard hotswap, unstable hardware, signal integrity problem or bug + + /* wait until keyboard sends any code without 10000ms timeout + if (timer_elapsed(init_time) > 10000) { + state = READ_ID; + } + */ + if (ibmpc.host_recv() != -1) { // wait for AA + xprintf("W%u ", timer_read()); + init_time = timer_read(); + state = WAIT_AABF; + } + break; + case WAIT_AABF: + // NOTE: we can omit to wait BF BF + // ID takes 500ms max? TechRef [8] 4-41, though 1ms is enough for 122-key Terminal 6110345 + if (timer_elapsed(init_time) > 500) { + state = READ_ID; + } + if (ibmpc.host_recv() != -1) { // wait for BF + xprintf("W%u ", timer_read()); + init_time = timer_read(); + state = WAIT_AABFBF; + } + break; + case WAIT_AABFBF: + if (timer_elapsed(init_time) > 500) { + state = READ_ID; + } + if (ibmpc.host_recv() != -1) { // wait for BF + xprintf("W%u ", timer_read()); + state = READ_ID; + } + break; + case READ_ID: + keyboard_id = read_keyboard_id(); + xprintf("R%u ", timer_read()); + + if (0x0000 == keyboard_id) { // CodeSet2 AT(IBM PC AT 84-key) + keyboard_kind = PC_AT; + } else if (0xFFFF == keyboard_id) { // CodeSet1 XT + keyboard_kind = PC_XT; + } else if (0xFFFE == keyboard_id) { // CodeSet2 PS/2 fails to response? + keyboard_kind = PC_AT; + } else if (0xFFFD == keyboard_id) { // Zenith Z-150 AT + keyboard_kind = PC_AT; + } else if (0x00FF == keyboard_id) { // Mouse is not supported + keyboard_kind = PC_MOUSE; + } else if (0xAB85 == keyboard_id || // IBM 122-key Model M, NCD N-97 + 0xAB86 == keyboard_id || // Cherry G80-2551, IBM 1397000 + 0xAB92 == keyboard_id) { // IBM 5576-001 + // https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#ab85 + // https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#ab86 + // https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#ab92 + + if ((0xFA == ibmpc.host_send(0xF0)) && + (0xFA == ibmpc.host_send(0x03))) { + // switch to code set 3 + keyboard_kind = PC_TERMINAL; + } else { + keyboard_kind = PC_AT; + } + } else if (0xAB90 == keyboard_id || // IBM 5576-002 + 0xAB91 == keyboard_id) { // IBM 5576-003 + // https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#ab90 + // https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#ab91 + + xprintf("\n5576_CS82h:"); + if ((0xFA == ibmpc.host_send(0xF0)) && + (0xFA == ibmpc.host_send(0x82))) { + // switch to code set 82h + // https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#ibm-5576-scan-codes-set + xprintf("OK "); + } else { + xprintf("NG "); + } + keyboard_kind = PC_AT; + } else if (0xBFB0 == keyboard_id) { // IBM RT Keyboard + // https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#bfb0 + // TODO: LED indicator fix + //keyboard_kind = PC_TERMINAL_IBM_RT; + keyboard_kind = PC_TERMINAL; + } else if (0xAB00 == (keyboard_id & 0xFF00)) { // CodeSet2 PS/2 + keyboard_kind = PC_AT; + } else if (0xBF00 == (keyboard_id & 0xFF00)) { // CodeSet3 Terminal + keyboard_kind = PC_TERMINAL; + } else if (0x7F00 == (keyboard_id & 0xFF00)) { // CodeSet3 Terminal 1394204 + keyboard_kind = PC_TERMINAL; + } else { + xprintf("\nUnknown ID: Report to TMK "); + if ((0xFA == ibmpc.host_send(0xF0)) && + (0xFA == ibmpc.host_send(0x02))) { + // switch to code set 2 + keyboard_kind = PC_AT; + } else if ((0xFA == ibmpc.host_send(0xF0)) && + (0xFA == ibmpc.host_send(0x03))) { + // switch to code set 3 + keyboard_kind = PC_TERMINAL; + } else { + keyboard_kind = PC_AT; + } + } + + xprintf("\nID:%04X(%s%s) ", keyboard_id, KEYBOARD_KIND_STR(keyboard_kind), ID_STR(keyboard_id)); + + state = SETUP; + break; + case SETUP: + xprintf("S%u ", timer_read()); + switch (keyboard_kind) { + case PC_XT: + break; + case PC_AT: + led_set(host_keyboard_leds()); + break; + case PC_TERMINAL: + // Set all keys to make/break type + ibmpc.host_send(0xF8); + // This should not be harmful + led_set(host_keyboard_leds()); + break; +#ifdef IBMPC_MOUSE_ENABLE + case PC_MOUSE: { + uint8_t s[3]; + ibmpc.host_send(0xF5); // Disable + ibmpc.host_send(0xEA); // Set Stream Mode + mouse_read_status(s); + + // Logitech Magic Status + // https://github.com/torvalds/linux/blob/master/drivers/input/mouse/logips2pp.c#L352 + xprintf("\nLMS: "); + ibmpc.host_send(0xE8); ibmpc.host_send(0x00); + ibmpc.host_send(0xE6); ibmpc.host_send(0xE6); ibmpc.host_send(0xE6); + mouse_read_status(s); + if (s[0] == 0 || s[1] == 0) { + // Not Logitech + goto MOUSE_INTELLI; + } + + // Logitech Magic Knock + // https://www.win.tue.nl/~aeb/linux/kbd/scancodes-13.html + // https://web.archive.org/web/20030714000535/www.dqcs.com/logitech/ps2ppspec.htm + // https://github.com/torvalds/linux/blob/5bfc75d92efd494db37f5c4c173d3639d4772966/drivers/input/serio/libps2.c#L347 + xprintf("\nLOG: "); + // sliced magic byte: 0x39 + ibmpc.host_send(0xE6); + ibmpc.host_send(0xE8); ibmpc.host_send(0x00); + ibmpc.host_send(0xE8); ibmpc.host_send(0x03); + ibmpc.host_send(0xE8); ibmpc.host_send(0x02); + ibmpc.host_send(0xE8); ibmpc.host_send(0x01); + // sliced magic byte: 0xDB + ibmpc.host_send(0xE6); + ibmpc.host_send(0xE8); ibmpc.host_send(0x03); + ibmpc.host_send(0xE8); ibmpc.host_send(0x01); + ibmpc.host_send(0xE8); ibmpc.host_send(0x02); + ibmpc.host_send(0xE8); ibmpc.host_send(0x03); + mouse_id = MOUSE_LOGITECH; // 9 + goto MOUSE_DONE; + +MOUSE_INTELLI: + // Intellimouse protocol: 3 + xprintf("\nINT: "); + ibmpc.host_send(0xF3); ibmpc.host_send(0xC8); + ibmpc.host_send(0xF3); ibmpc.host_send(0x64); + ibmpc.host_send(0xF3); ibmpc.host_send(0x50); + mouse_id = ((read_keyboard_id() >> 8) == MOUSE_INTELLI ? MOUSE_INTELLI : MOUSE_DEFAULT); + + // Intellimouse Explorer protocol: 4 + xprintf("\nEXP: "); + ibmpc.host_send(0xF3); ibmpc.host_send(0xC8); + ibmpc.host_send(0xF3); ibmpc.host_send(0xC8); + ibmpc.host_send(0xF3); ibmpc.host_send(0x50); + mouse_id = ((read_keyboard_id() >> 8) == MOUSE_EXPLORER ? MOUSE_EXPLORER : MOUSE_DEFAULT); + + // Not Intellimouse + if (mouse_id == 0) { + xprintf("\nDEF: "); + ibmpc.host_send(0xF6); // Set Default + } + +MOUSE_DONE: + //ibmpc.host_send(0xEA); // Set Stream Mode + ibmpc.host_send(0xF4); // Enable + mouse_read_status(s); + xprintf("\nMouse: %s\n", ((mouse_id == MOUSE_LOGITECH) ? "LOGITECH" : + ((mouse_id == MOUSE_INTELLI) ? "INTELLI" : + ((mouse_id == MOUSE_EXPLORER) ? "EXPLORER" : + ((mouse_id == MOUSE_DEFAULT) ? "DEFAULT" : "???"))))); + break; } +#endif + default: + break; + } + state = LOOP; + xprintf("L%u ", timer_read()); + case LOOP: + { + uint16_t code = ibmpc.host_recv(); + if (code == -1) { + // no code + break; + } + + // Keyboard Error/Overrun([3]p.26) or Buffer full + // Scan Code Set 1: 0xFF + // Scan Code Set 2 and 3: 0x00 + // Buffer full(IBMPC_ERR_FULL): 0xFF + if (keyboard_kind != PC_MOUSE && (code == 0x00 || code == 0xFF)) { + // clear stuck keys + matrix_clear(); + clear_keyboard(); + + xprintf("\n[CLR] "); + break; + } + + switch (keyboard_kind) { + case PC_XT: + if (process_cs1(code) == -1) state = ERROR; + break; + case PC_AT: + if (process_cs2(code) == -1) state = ERROR; + break; + case PC_TERMINAL: + if (process_cs3(code) == -1) state = ERROR; + break; +#ifdef IBMPC_MOUSE_ENABLE + case PC_MOUSE: { + // Logitec Mouse Data: + // https://github.com/torvalds/linux/blob/d2912cb15bdda8ba4a5dd73396ad62641af2f520/drivers/input/mouse/logips2pp.c#L41 + // Intellimouse Data: + // https://www.win.tue.nl/~aeb/linux/kbd/scancodes-13.html + int16_t b0, b1, b2, b3; + int16_t x = 0, y = 0; + int8_t v = 0, h = 0; + + b0 = code; + b1 = ibmpc.host_recv_response(); + if (b1 == -1) { state = ERROR; break; } + b2 = ibmpc.host_recv_response(); + if (b2 == -1) { state = ERROR; break; } + + switch (mouse_id) { + case MOUSE_DEFAULT: + case MOUSE_INTELLI: + case MOUSE_EXPLORER: + mouse_btn = (mouse_btn & 0xF8) | (b0 & 0x07); + x = (b0 & 0x10) ? (b1 | 0xFF00) : b1; + y = (b0 & 0x20) ? (b2 | 0xFF00) : b2; + break; + case MOUSE_LOGITECH: + if ((b0 & 0x48) == 0x48 && (b1 & 0x02) == 0x02) { + switch (((b0 & 0x30) >> 2) | ((b1 & 0x30) >> 4)) { + case 1: // C8 Dx xx + // Ignored while Scroll-Up/Down is pressed + if (!(b2 & 0x40)) { + if (b2 & 0x80) + h = ((b2 & 0x08) ? 0xF0 : 0x00) | (b2 & 0x0F); + else + v = ((b2 & 0x08) ? 0xF0 : 0x00) | (b2 & 0x0F); + } + // Back + if (b2 & 0x10) mouse_btn |= (1 << 3); else mouse_btn &= ~(1 << 3); + // Forward + if (b2 & 0x20) mouse_btn |= (1 << 4); else mouse_btn &= ~(1 << 4); + break; + case 2: // C8 Ex xx + if (b2 & 0x01) mouse_btn |= (1 << 6); else mouse_btn &= ~(1 << 6); + if (b2 & 0x02) mouse_btn |= (1 << 7); else mouse_btn &= ~(1 << 7); + // Task + if (b2 & 0x04) mouse_btn |= (1 << 5); else mouse_btn &= ~(1 << 5); + // Scroll-Up + if (b2 & 0x08) mouse_btn |= (1 << 6); else mouse_btn &= ~(1 << 6); + // Scroll-Down + if (b2 & 0x10) mouse_btn |= (1 << 7); else mouse_btn &= ~(1 << 7); + break; + case 3: // TouchPad? + if (b2 & 0x80) + h = ((b2 & 0x80) ? 0xF0 : 0x00) | ((b2 >> 4) & 0x0F); + else + v = ((b2 & 0x80) ? 0xF0 : 0x00) | ((b2 >> 4) & 0x0F); + + mouse_btn = (mouse_btn & 0xF8) | (b2 & 0x07); + break; + } + } else { + mouse_btn = (mouse_btn & 0xF8) | (b0 & 0x07); + x = (b0 & 0x10) ? (b1 | 0xFF00) : b1; + y = (b0 & 0x20) ? (b2 | 0xFF00) : b2; + } + break; + } + + // Extra byte + switch (mouse_id) { + case MOUSE_INTELLI: + b3 = ibmpc.host_recv_response(); + if (b3 == -1) break; + v = b3 & 0xFF; + break; + case MOUSE_EXPLORER: + b3 = ibmpc.host_recv_response(); + if (b3 == -1) break; + // sign extension + v = ((b3 & 0x08) ? 0xF0 : 0x00) | (b3 & 0x0F); + + // Back/Forward + if (b3 & 0x10) mouse_btn |= (1 << 3); else mouse_btn &= ~(1 << 3); + if (b3 & 0x20) mouse_btn |= (1 << 4); else mouse_btn &= ~(1 << 4); + break; + default: + break; + } + + + // chop to 8-bit + #define CHOP8(a) (((a) > 127) ? 127 : (((a) < -127) ? -127 : (a))) + report_mouse_t mouse_report = {}; + mouse_report.buttons = mouse_btn; + #ifdef MOUSE_EXT_REPORT + mouse_report.x = x; + mouse_report.y = -y; + #else + mouse_report.x = CHOP8(x); + mouse_report.y = -CHOP8(y); + #endif + mouse_report.v = -CHOP8(v); + mouse_report.h = CHOP8(h); + host_mouse_send(&mouse_report); + xprintf("M[x:%d y:%d v:%d h:%d b:%02X]\n", mouse_report.x, mouse_report.y, + mouse_report.v, mouse_report.h, mouse_report.buttons); + break; } +#endif + default: + break; + } + } + break; + case ERROR: + // something goes wrong + clear_keyboard(); + state = INIT; + break; + default: + break; + } + return 1; +} + + +/******************************************************************************* + * XT: Scan Code Set 1 + * + * See [3], [a] + * + * E0-escaped scan codes are translated into unused range of the matrix.(54-7F) + * + * 01-53: Normal codes used in original XT keyboard + * 54-7F: Not used in original XT keyboard + * + * 0 1 2 3 4 5 6 7 8 9 A B C D E F + * 50 - - - - * * x x x x * * * * * * + * 60 * * * * x x x x x x x x x x x * + * 70 x * * x * * x * * x * x * x x * + * + * -: codes existed in original XT keyboard + * *: E0-escaped codes translated + * x: Non-espcaped codes(Some are not used in real keyboards probably) + * + * Codes assigned in range 54-7F: + * + * 50 - 60 Up* 70 KANAx + * 51 - 61 Left* 71 Insert* + * 52 - 62 Down* 72 Delete* + * 53 - 63 Right* 73 ROx + * 54 PrintScr* 64 F13x 74 Home* + * 55 Pause* 65 F14x 75 End* + * 56 Euro2x 66 F15x 76 F24x + * 57 F11x 67 F16x 77 PageUp* + * 58 F12x 68 F17x 78 PageDown* + * 59 Keypad=x 69 F18x 79 HENKANx + * 5A LGUI* 6A F19x 7A RCTL* + * 5B RGUI* 6B F20x 7B MUHENKANx + * 5C APP* 6C F21x 7C RALT* + * 5D Mute* 6D F22x 7D JPYx + * 5E Volume Down* 6E F23x 7E Keypad,x + * 5F Volume Up* 6F Keypad Enter* 7F Keypad/ * + */ +uint8_t IBMPCConverter::cs1_e0code(uint8_t code) { + switch(code) { + // Original IBM XT keyboard doesn't use E0-codes probably + // Some XT compatilble keyobards need these keys? + case 0x37: return 0x54; // Print Screen + case 0x46: return 0x55; // Ctrl + Pause + case 0x5B: return 0x5A; // Left GUI + case 0x5C: return 0x5B; // Right GUI + case 0x5D: return 0x5C; // Application + case 0x20: return 0x5D; // Mute + case 0x2E: return 0x5E; // Volume Down + case 0x30: return 0x5F; // Volume Up + case 0x48: return 0x60; // Up + case 0x4B: return 0x61; // Left + case 0x50: return 0x62; // Down + case 0x4D: return 0x63; // Right + case 0x1C: return 0x6F; // Keypad Enter + case 0x52: return 0x71; // Insert + case 0x53: return 0x72; // Delete + case 0x47: return 0x74; // Home + case 0x4F: return 0x75; // End + case 0x49: return 0x77; // Page Up + case 0x51: return 0x78; // Page Down + case 0x1D: return 0x7A; // Right Ctrl + case 0x38: return 0x7C; // Right Alt + case 0x35: return 0x7F; // Keypad / + + // Shared matrix cell with other keys + case 0x5E: return 0x70; // Power (KANA) + case 0x5F: return 0x79; // Sleep (HENKAN) + case 0x63: return 0x7B; // Wake (MUHENKAN) + + default: + xprintf("!CS1_E0_%02X!\n", code); + return code; + } + return 0x00; +} + +int8_t IBMPCConverter::process_cs1(uint8_t code) +{ + switch (state_cs1) { + case CS1_INIT: + switch (code) { + case 0xE0: + state_cs1 = CS1_E0; + break; + case 0xE1: + state_cs1 = CS1_E1; + break; + default: + if (code < 0x80) + matrix_make(code); + else + matrix_break(code & 0x7F); + break; + } + break; + case CS1_E0: + switch (code) { + case 0x2A: + case 0xAA: + case 0x36: + case 0xB6: + //ignore fake shift + state_cs1 = CS1_INIT; + break; + default: + if (code < 0x80) + matrix_make(cs1_e0code(code)); + else + matrix_break(cs1_e0code(code & 0x7F)); + state_cs1 = CS1_INIT; + break; + } + break; + case CS1_E1: + switch (code) { + case 0x1D: + state_cs1 = CS1_E1_1D; + break; + case 0x9D: + state_cs1 = CS1_E1_9D; + break; + default: + state_cs1 = CS1_INIT; + break; + } + break; + case CS1_E1_1D: + switch (code) { + case 0x45: + matrix_make(0x55); // Pause + state_cs1 = CS1_INIT; + break; + default: + state_cs1 = CS1_INIT; + break; + } + break; + case CS1_E1_9D: + switch (code) { + case 0xC5: + matrix_break(0x55); // Pause + state_cs1 = CS1_INIT; + break; + default: + state_cs1 = CS1_INIT; + break; + } + break; + default: + state_cs1 = CS1_INIT; + } + return 0; +} + + +/******************************************************************************* + * AT, PS/2: Scan Code Set 2 + * + * Exceptional Handling + * -------------------- + * Some keys should be handled exceptionally. See [b]. + * + * Scan codes are varied or prefix/postfix'd depending on modifier key state. + * + * 1) Insert, Delete, Home, End, PageUp, PageDown, Up, Down, Right, Left + * a) when Num Lock is off + * modifiers | make | break + * ----------+---------------------------+---------------------- + * Ohter | | + * LShift | E0 F0 12 | E0 12 + * RShift | E0 F0 59 | E0 59 + * L+RShift | E0 F0 12 E0 F0 59 | E0 59 E0 12 + * + * b) when Num Lock is on + * modifiers | make | break + * ----------+---------------------------+---------------------- + * Other | E0 12 | E0 F0 12 + * Shift'd | | + * + * Handling: These prefix/postfix codes are ignored. + * + * + * 2) Keypad / + * modifiers | make | break + * ----------+---------------------------+---------------------- + * Ohter | | + * LShift | E0 F0 12 | E0 12 + * RShift | E0 F0 59 | E0 59 + * L+RShift | E0 F0 12 E0 F0 59 | E0 59 E0 12 + * + * Handling: These prefix/postfix codes are ignored. + * + * + * 3) PrintScreen + * modifiers | make | break + * ----------+--------------+----------------------------------- + * Other | E0 12 E0 7C | E0 F0 7C E0 F0 12 + * Shift'd | E0 7C | E0 F0 7C + * Control'd | E0 7C | E0 F0 7C + * Alt'd | 84 | F0 84 + * + * Handling: These prefix/postfix codes are ignored, and both scan codes + * 'E0 7C' and 84 are seen as PrintScreen. + * + * 4) Pause + * modifiers | make(no break code) + * ----------+-------------------------------------------------- + * Other | E1 14 77 E1 F0 14 F0 77 + * Control'd | E0 7E E0 F0 7E + * + * Handling: Both code sequences are treated as a whole. + * And we need a ad hoc 'pseudo break code' hack to get the key off + * because it has no break code. + * + * Notes: + * 'Hanguel/English'(F1) and 'Hanja'(F2) have no break code. See [a]. + * These two Korean keys need exceptional handling and are not supported for now. + * + */ +uint8_t IBMPCConverter::cs2_e0code(uint8_t code) { + switch(code) { + // E0 prefixed codes translation See [a]. + case 0x11: return 0x0F; // right alt + case 0x14: return 0x17; // right control + case 0x1F: return 0x19; // left GUI + case 0x27: return 0x1F; // right GUI + case 0x2F: return 0x5C; // apps + case 0x4A: return 0x60; // keypad / + case 0x5A: return 0x62; // keypad enter + case 0x69: return 0x27; // end + case 0x6B: return 0x53; // cursor left + case 0x6C: return 0x2F; // home + case 0x70: return 0x39; // insert + case 0x71: return 0x37; // delete + case 0x72: return 0x3F; // cursor down + case 0x74: return 0x47; // cursor right + case 0x75: return 0x4F; // cursor up + case 0x7A: return 0x56; // page down + case 0x7D: return 0x5E; // page up + case 0x7C: return 0x7F; // Print Screen + case 0x7E: return 0x00; // Control'd Pause + + case 0x21: return 0x65; // volume down + case 0x32: return 0x6E; // volume up + case 0x23: return 0x6F; // mute + case 0x10: return 0x08; // (WWW search) -> F13 + case 0x18: return 0x10; // (WWW favourites) -> F14 + case 0x20: return 0x18; // (WWW refresh) -> F15 + case 0x28: return 0x20; // (WWW stop) -> F16 + case 0x30: return 0x28; // (WWW forward) -> F17 + case 0x38: return 0x30; // (WWW back) -> F18 + case 0x3A: return 0x38; // (WWW home) -> F19 + case 0x40: return 0x40; // (my computer) -> F20 + case 0x48: return 0x48; // (email) -> F21 + case 0x2B: return 0x50; // (calculator) -> F22 + case 0x34: return 0x08; // (play/pause) -> F13 + case 0x3B: return 0x10; // (stop) -> F14 + case 0x15: return 0x18; // (previous track) -> F15 + case 0x4D: return 0x20; // (next track) -> F16 + case 0x50: return 0x28; // (media select) -> F17 + case 0x5E: return 0x50; // (ACPI wake) -> F22 + case 0x3F: return 0x57; // (ACPI sleep) -> F23 + case 0x37: return 0x5F; // (ACPI power) -> F24 + + // https://github.com/tmk/tmk_keyboard/pull/636 + case 0x03: return 0x18; // Help DEC LK411 -> F15 + case 0x04: return 0x08; // F13 DEC LK411 + case 0x0B: return 0x20; // Do DEC LK411 -> F16 + case 0x0C: return 0x10; // F14 DEC LK411 + case 0x0D: return 0x19; // LCompose DEC LK411 -> LGUI + case 0x79: return 0x6D; // KP- DEC LK411 -> PCMM + case 0x83: return 0x28; // F17 DEC LK411 + default: return (code & 0x7F); + } +} + +// IBM 5576-002/003 Scan code translation +// https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#ibm-5576-code-set-82h +uint8_t IBMPCConverter::translate_5576_cs2(uint8_t code) { + switch (code) { + case 0x11: return 0x0F; // Zenmen -> RALT + case 0x13: return 0x11; // Kanji -> LALT + case 0x0E: return 0x54; // @ + case 0x54: return 0x5B; // [ + case 0x5B: return 0x5D; // ] + case 0x5C: return 0x6A; // JYEN + case 0x5D: return 0x6A; // JYEN + case 0x62: return 0x0E; // Han/Zen -> `~ + case 0x7C: return 0x77; // Keypad * + } + return code; +} +uint8_t IBMPCConverter::translate_5576_cs2_e0(uint8_t code) { + switch (code) { + case 0x11: return 0x13; // Hiragana -> KANA + case 0x41: return 0x7C; // Keypad ' + } + return code; +} + +int8_t IBMPCConverter::process_cs2(uint8_t code) +{ + switch (state_cs2) { + case CS2_INIT: + if (0xAB90 == keyboard_id || 0xAB91 == keyboard_id) { + code = translate_5576_cs2(code); + } + switch (code) { + case 0xE0: + state_cs2 = CS2_E0; + break; + case 0xF0: + state_cs2 = CS2_F0; + break; + case 0xE1: + state_cs2 = CS2_E1; + break; + case 0x83: // F7 + matrix_make(0x02); + state_cs2 = CS2_INIT; + break; + case 0x84: // Alt'd PrintScreen + matrix_make(0x7F); + state_cs2 = CS2_INIT; + break; + case 0xAA: // Self-test passed + case 0xFC: // Self-test failed + // replug or unstable connection probably + default: // normal key make + state_cs2 = CS2_INIT; + if (code < 0x80) { + matrix_make(code); + } else { + matrix_clear(); + xprintf("!CS2_INIT!\n"); + return -1; + } + } + break; + case CS2_E0: // E0-Prefixed + if (0xAB90 == keyboard_id || 0xAB91 == keyboard_id) { + code = translate_5576_cs2_e0(code); + } + switch (code) { + case 0x12: // to be ignored + case 0x59: // to be ignored + state_cs2 = CS2_INIT; + break; + case 0xF0: + state_cs2 = CS2_E0_F0; + break; + default: + state_cs2 = CS2_INIT; + if (code < 0x80) { + matrix_make(cs2_e0code(code)); + } else { + matrix_clear(); + xprintf("!CS2_E0!\n"); + return -1; + } + } + break; + case CS2_F0: // Break code + if (0xAB90 == keyboard_id || 0xAB91 == keyboard_id) { + code = translate_5576_cs2(code); + } + switch (code) { + case 0x83: // F7 + matrix_break(0x02); + state_cs2 = CS2_INIT; + break; + case 0x84: // Alt'd PrintScreen + matrix_break(0x7F); + state_cs2 = CS2_INIT; + break; + default: + state_cs2 = CS2_INIT; + if (code < 0x80) { + matrix_break(code); + } else { + matrix_clear(); + xprintf("!CS2_F0! %02X\n", code); + return -1; + } + } + break; + case CS2_E0_F0: // Break code of E0-prefixed + if (0xAB90 == keyboard_id || 0xAB91 == keyboard_id) { + code = translate_5576_cs2_e0(code); + } + switch (code) { + case 0x12: // to be ignored + case 0x59: // to be ignored + state_cs2 = CS2_INIT; + break; + default: + state_cs2 = CS2_INIT; + if (code < 0x80) { + matrix_break(cs2_e0code(code)); + } else { + matrix_clear(); + xprintf("!CS2_E0_F0!\n"); + return -1; + } + } + break; + // Pause make: E1 14 77 + case CS2_E1: + switch (code) { + case 0x14: + state_cs2 = CS2_E1_14; + break; + case 0xF0: + state_cs2 = CS2_E1_F0; + break; + default: + state_cs2 = CS2_INIT; + } + break; + case CS2_E1_14: + switch (code) { + case 0x77: + matrix_make(0x00); + state_cs2 = CS2_INIT; + break; + default: + state_cs2 = CS2_INIT; + } + break; + // Pause break: E1 F0 14 F0 77 + case CS2_E1_F0: + switch (code) { + case 0x14: + state_cs2 = CS2_E1_F0_14; + break; + default: + state_cs2 = CS2_INIT; + } + break; + case CS2_E1_F0_14: + switch (code) { + case 0xF0: + state_cs2 = CS2_E1_F0_14_F0; + break; + default: + state_cs2 = CS2_INIT; + } + break; + case CS2_E1_F0_14_F0: + switch (code) { + case 0x77: + matrix_break(0x00); + state_cs2 = CS2_INIT; + break; + default: + state_cs2 = CS2_INIT; + } + break; + default: + state_cs2 = CS2_INIT; + } + return 0; +} + + +/* + * Terminal: Scan Code Set 3 + * + * See [3], [7] and + * https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#scan-code-set-3 + */ +// IBM 5576-001 Scan code translation +// https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#ibm-5576-code-set-3 +uint8_t IBMPCConverter::translate_5576_cs3(uint8_t code) { + switch (code) { + // Fix positon of keys to fit 122-key layout + case 0x13: return 0x5D; // JYEN + case 0x5C: return 0x51; // RO + case 0x76: return 0x7E; // Keypad ' + case 0x7E: return 0x76; // Keypad Dup + } + return code; +} + +int8_t IBMPCConverter::process_cs3(uint8_t code) +{ + switch (code) { + case 0xAA: // BAT code + case 0xFC: // BAT code + case 0xBF: // Part of keyboard ID + case 0xAB: // Part keyboard ID + state_cs3 = CS3_READY; + xprintf("!CS3_RESET!\n"); + return -1; + } + + switch (state_cs3) { + case CS3_READY: + if (0xAB92 == keyboard_id) { + code = translate_5576_cs3(code); + } + switch (code) { + case 0xF0: + state_cs3 = CS3_F0; + break; + case 0x83: // PrintScreen + matrix_make(0x02); + break; + case 0x84: // Keypad * + matrix_make(0x7F); + break; + case 0x85: // Muhenkan + matrix_make(0x68); + break; + case 0x86: // Henkan + matrix_make(0x78); + break; + case 0x87: // Hiragana + matrix_make(0x00); + break; + case 0x8B: // Left GUI + matrix_make(0x01); + break; + case 0x8C: // Right GUI + matrix_make(0x09); + break; + case 0x8D: // Application + matrix_make(0x0A); + break; +#ifdef G80_2551_SUPPORT + case 0x80: // G80-2551 four extra keys around cursor keys + state_cs3 = CS3_G80; + break; +#endif + default: // normal key make + if (code < 0x80) { + matrix_make(code); + } else { + xprintf("!CS3_READY!\n"); + } + } + break; + case CS3_F0: // Break code + state_cs3 = CS3_READY; + if (0xAB92 == keyboard_id) { + code = translate_5576_cs3(code); + } + switch (code) { + case 0x83: // PrintScreen + matrix_break(0x02); + break; + case 0x84: // Keypad * + matrix_break(0x7F); + break; + case 0x85: // Muhenkan + matrix_break(0x68); + break; + case 0x86: // Henkan + matrix_break(0x78); + break; + case 0x87: // Hiragana + matrix_break(0x00); + break; + case 0x8B: // Left GUI + matrix_break(0x01); + break; + case 0x8C: // Right GUI + matrix_break(0x09); + break; + case 0x8D: // Application + matrix_break(0x0A); + break; + default: + if (code < 0x80) { + matrix_break(code); + } else { + xprintf("!CS3_F0!\n"); + } + } + break; +#ifdef G80_2551_SUPPORT + /* + * G80-2551 terminal keyboard support + * https://deskthority.net/wiki/Cherry_G80-2551 + * https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#g80-2551-in-code-set-3 + */ + case CS3_G80: // G80-2551 four extra keys around cursor keys + switch (code) { + case (0x26): // TD= -> JYEN + matrix_make(0x5D); + break; + case (0x25): // page with edge -> NUHS + matrix_make(0x53); + break; + case (0x16): // two pages -> RO + matrix_make(0x51); + break; + case (0x1E): // calc -> KANA + matrix_make(0x00); + break; + case (0xF0): + state_cs3 = CS3_G80_F0; + return 0; + default: + // Not supported + matrix_clear(); + break; + } + state_cs3 = CS3_READY; + break; + case CS3_G80_F0: + switch (code) { + case (0x26): // TD= -> JYEN + matrix_break(0x5D); + break; + case (0x25): // page with edge -> NUHS + matrix_break(0x53); + break; + case (0x16): // two pages -> RO + matrix_break(0x51); + break; + case (0x1E): // calc -> KANA + matrix_break(0x00); + break; + default: + // Not supported + matrix_clear(); + break; + } + state_cs3 = CS3_READY; + break; +#endif + } + return 0; +} + +/* + * IBM PC Keyboard Protocol Resources: + * + * [a] Microsoft USB HID to PS/2 Translation Table - Scan Code Set 1 and 2 + * http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/translate.pdf + * + * [b] Microsoft Keyboard Scan Code Specification - Special rules of Scan Code Set 1 and 2 + * http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc + * + * [1] PS/2 Reference Manuals - Collection of IBM Personal System/2 documents. + * http://www.mcamafia.de/pdf/pdfref.htm + * + * [2] Keyboard and Auxiliary Device Controller - Signal Timing and Format + * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf + * + * [3] Keyboards(101- and 102-key) - Keyboard Layout, Scan Code Set, POR, and Commands. + * http://www.mcamafia.de/pdf/ibm_hitrc11.pdf + * + * [4] IBM PC XT Keyboard Protocol + * https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol + * + * [5] IBM Keyboard Scan Code by John Elliott - 83-key, 84-key, 102-key and 122-key + * https://www.seasip.info/VintagePC/index.html + * + * [6] IBM 1391406 Keyboard - Scan Code Set 2 of 102-key PS/2 keyboard + * https://www.seasip.info/VintagePC/ibm_1391406.html + * + * [7] The IBM 6110344 Keyboard - Scan Code Set 3 of 122-key terminal keyboard + * https://www.seasip.info/VintagePC/ibm_6110344.html + * + * [8] IBM PC AT Technical Reference 1986 + * http://bitsavers.org/pdf/ibm/pc/at/6183355_PC_AT_Technical_Reference_Mar86.pdf + * + * [y] TrackPoint Engineering Specifications for version 3E + * https://web.archive.org/web/20100526161812/http://wwwcssrv.almaden.ibm.com/trackpoint/download.html + * + * [z] [Soarer's XT/AT/PS2/Terminal to USB converter] + * https://geekhack.org/index.php?topic=17458.0 + * + */ diff --git a/converter/ibmpc_usb/ibmpc_usb.h b/converter/ibmpc_usb/ibmpc_usb.h index f805e5d8..9f9bf35e 100644 --- a/converter/ibmpc_usb/ibmpc_usb.h +++ b/converter/ibmpc_usb/ibmpc_usb.h @@ -9,8 +9,4 @@ typedef enum { NONE, PC_XT, PC_AT, PC_TERMINAL, PC_MOUSE } keyboard_kind_t; kind == PC_MOUSE ? "MOUSE" : \ "NONE") - -extern uint16_t keyboard_id; -extern keyboard_kind_t keyboard_kind; - #endif diff --git a/converter/ibmpc_usb/ibmpc_usb.hpp b/converter/ibmpc_usb/ibmpc_usb.hpp new file mode 100644 index 00000000..26ad2984 --- /dev/null +++ b/converter/ibmpc_usb/ibmpc_usb.hpp @@ -0,0 +1,164 @@ +#ifndef IBMPC_USB_HPP +#define IBMPC_USB_HPP + +#include +#include "matrix.h" +#include "unimap_trans.h" +#include "ibmpc_usb.h" + + + +#define ID_STR(id) (id == 0xFFFE ? "_????" : \ + (id == 0xFFFD ? "_Z150" : \ + (id == 0x0000 ? "_AT84" : \ + ""))) + +#define ROW(code) ((code>>4)&0x07) +#define COL(code) (code&0x0F) + + +class IBMPCConverter { + public: + static matrix_row_t matrix[MATRIX_ROWS]; + + IBMPC &ibmpc; + + IBMPCConverter(IBMPC &_ibmpc) : ibmpc(_ibmpc), keyboard_id(0), keyboard_kind(NONE), current_protocol(0) { + matrix_clear(); + } + + void init(void) { + ibmpc.host_init(); + } + + uint8_t process_interface(void); + + void set_led(uint8_t usb_led); + + static inline void matrix_clear(void) { + for (uint8_t i=0; i < MATRIX_ROWS; i++) matrix[i] = 0x00; + } + + static inline matrix_row_t matrix_get_row(uint8_t row) { + return matrix[row]; + } + + + private: + uint16_t keyboard_id = 0x0000; + keyboard_kind_t keyboard_kind = NONE; + uint8_t current_protocol = 0; + uint16_t init_time; + + enum Converter_state { + INIT, + WAIT_SETTLE, + AT_RESET, + XT_RESET, + XT_RESET_WAIT, + XT_RESET_DONE, + WAIT_AA, + WAIT_AABF, + WAIT_AABFBF, + READ_ID, + SETUP, + LOOP, + ERROR, + } state = INIT; + + enum CS1_state { + CS1_INIT, + CS1_E0, + // Pause: E1 1D 45, E1 9D C5 [a] + CS1_E1, + CS1_E1_1D, + CS1_E1_9D, + } state_cs1 = CS1_INIT; + + enum CS2_state { + CS2_INIT, + CS2_F0, + CS2_E0, + CS2_E0_F0, + // Pause + CS2_E1, + CS2_E1_14, + CS2_E1_F0, + CS2_E1_F0_14, + CS2_E1_F0_14_F0, + } state_cs2 = CS2_INIT; + + enum CS3_state { + CS3_READY, + CS3_F0, +#ifdef G80_2551_SUPPORT + // G80-2551 four extra keys around cursor keys + CS3_G80, + CS3_G80_F0, +#endif + } state_cs3 = CS3_READY; + + int8_t process_cs1(uint8_t code); + int8_t process_cs2(uint8_t code); + int8_t process_cs3(uint8_t code); + uint8_t cs1_e0code(uint8_t code); + uint8_t cs2_e0code(uint8_t code); + uint8_t translate_5576_cs2(uint8_t code); + uint8_t translate_5576_cs2_e0(uint8_t code); + uint8_t translate_5576_cs3(uint8_t code); + + int16_t read_wait(uint16_t wait_ms); + uint16_t read_keyboard_id(void); + + // translate to Unimap before storing in matrix + inline void matrix_make(uint8_t code) { + uint8_t u = to_unimap(code); + if (u > 0x7F) return; + if (!matrix_is_on(ROW(u), COL(u))) { + matrix[ROW(u)] |= 1< 0x7F) return; + if (matrix_is_on(ROW(u), COL(u))) { + matrix[ROW(u)] &= ~(1<. */ -#include "unimap_trans.h" +#include "unimap.h" #define AC_FN0 ACTION_LAYER_TAP_KEY(1, KC_APPLICATION) diff --git a/converter/ibmpc_usb/unimap_trans.h b/converter/ibmpc_usb/unimap_trans.h index 97472b7b..4b3520d2 100644 --- a/converter/ibmpc_usb/unimap_trans.h +++ b/converter/ibmpc_usb/unimap_trans.h @@ -17,12 +17,8 @@ along with this program. If not, see . #ifndef UNIMAP_TRANS_H #define UNIMAP_TRANS_H -#include #include #include "unimap.h" -#include "action.h" -#include "ibmpc_usb.h" - /* @@ -91,22 +87,22 @@ along with this program. If not, see . * Unsupported codes or error -> 00. UNIMAP_NUBS is unused. */ const uint8_t PROGMEM unimap_cs1[MATRIX_ROWS][MATRIX_COLS] = { - { UNIMAP_NO, UNIMAP_ESC, UNIMAP_1, UNIMAP_2, UNIMAP_3, UNIMAP_4, UNIMAP_5, UNIMAP_6 }, /* 00-07 */ - { UNIMAP_7, UNIMAP_8, UNIMAP_9, UNIMAP_0, UNIMAP_MINUS, UNIMAP_EQUAL, UNIMAP_BSPACE,UNIMAP_TAB }, /* 08-0F */ - { UNIMAP_Q, UNIMAP_W, UNIMAP_E, UNIMAP_R, UNIMAP_T, UNIMAP_Y, UNIMAP_U, UNIMAP_I }, /* 10-17 */ - { UNIMAP_O, UNIMAP_P, UNIMAP_LBRC, UNIMAP_RBRC, UNIMAP_ENTER, UNIMAP_LCTL, UNIMAP_A, UNIMAP_S, }, /* 18-1F */ - { UNIMAP_D, UNIMAP_F, UNIMAP_G, UNIMAP_H, UNIMAP_J, UNIMAP_K, UNIMAP_L, UNIMAP_SCLN }, /* 20-27 */ - { UNIMAP_QUOTE, UNIMAP_GRAVE, UNIMAP_LSHIFT,UNIMAP_BSLASH,UNIMAP_Z, UNIMAP_X, UNIMAP_C, UNIMAP_V, }, /* 28-2F */ - { UNIMAP_B, UNIMAP_N, UNIMAP_M, UNIMAP_COMMA, UNIMAP_DOT, UNIMAP_SLASH, UNIMAP_RSHIFT,UNIMAP_PAST }, /* 30-37 */ - { UNIMAP_LALT, UNIMAP_SPACE, UNIMAP_CAPS, UNIMAP_F1, UNIMAP_F2, UNIMAP_F3, UNIMAP_F4, UNIMAP_F5 }, /* 38-3F */ - { UNIMAP_F6, UNIMAP_F7, UNIMAP_F8, UNIMAP_F9, UNIMAP_F10, UNIMAP_NLCK, UNIMAP_SLCK, UNIMAP_P7 }, /* 40-47 */ - { UNIMAP_P8, UNIMAP_P9, UNIMAP_PMNS, UNIMAP_P4, UNIMAP_P5, UNIMAP_P6, UNIMAP_PPLS, UNIMAP_P1 }, /* 48-4F */ - { UNIMAP_P2, UNIMAP_P3, UNIMAP_P0, UNIMAP_PDOT, UNIMAP_PSCR, UNIMAP_PAUSE, UNIMAP_NUHS, UNIMAP_F11 }, /* 50-57 */ - { UNIMAP_F12, UNIMAP_PEQL, UNIMAP_LGUI, UNIMAP_RGUI, UNIMAP_APP, UNIMAP_MUTE, UNIMAP_VOLD, UNIMAP_VOLU }, /* 58-5F */ - { UNIMAP_UP, UNIMAP_LEFT, UNIMAP_DOWN, UNIMAP_RIGHT, UNIMAP_F13, UNIMAP_F14, UNIMAP_F15, UNIMAP_F16 }, /* 60-67 */ - { UNIMAP_F17, UNIMAP_F18, UNIMAP_F19, UNIMAP_F20, UNIMAP_F21, UNIMAP_F22, UNIMAP_F23, UNIMAP_PENT }, /* 68-6F */ - { UNIMAP_KANA, UNIMAP_INSERT,UNIMAP_DELETE,UNIMAP_RO, UNIMAP_HOME, UNIMAP_END, UNIMAP_F24, UNIMAP_PGUP }, /* 70-77 */ - { UNIMAP_PGDN, UNIMAP_HENK, UNIMAP_RCTL, UNIMAP_MHEN, UNIMAP_RALT, UNIMAP_JYEN, UNIMAP_PCMM, UNIMAP_PSLS }, /* 78-7F */ + { UNIMAP_NO, UNIMAP_ESC, UNIMAP_1, UNIMAP_2, UNIMAP_3, UNIMAP_4, UNIMAP_5, UNIMAP_6, /* 00-07 */ + UNIMAP_7, UNIMAP_8, UNIMAP_9, UNIMAP_0, UNIMAP_MINUS, UNIMAP_EQUAL, UNIMAP_BSPACE,UNIMAP_TAB }, /* 08-0F */ + { UNIMAP_Q, UNIMAP_W, UNIMAP_E, UNIMAP_R, UNIMAP_T, UNIMAP_Y, UNIMAP_U, UNIMAP_I, /* 10-17 */ + UNIMAP_O, UNIMAP_P, UNIMAP_LBRC, UNIMAP_RBRC, UNIMAP_ENTER, UNIMAP_LCTL, UNIMAP_A, UNIMAP_S, }, /* 18-1F */ + { UNIMAP_D, UNIMAP_F, UNIMAP_G, UNIMAP_H, UNIMAP_J, UNIMAP_K, UNIMAP_L, UNIMAP_SCLN, /* 20-27 */ + UNIMAP_QUOTE, UNIMAP_GRAVE, UNIMAP_LSHIFT,UNIMAP_BSLASH,UNIMAP_Z, UNIMAP_X, UNIMAP_C, UNIMAP_V, }, /* 28-2F */ + { UNIMAP_B, UNIMAP_N, UNIMAP_M, UNIMAP_COMMA, UNIMAP_DOT, UNIMAP_SLASH, UNIMAP_RSHIFT,UNIMAP_PAST, /* 30-37 */ + UNIMAP_LALT, UNIMAP_SPACE, UNIMAP_CAPS, UNIMAP_F1, UNIMAP_F2, UNIMAP_F3, UNIMAP_F4, UNIMAP_F5 }, /* 38-3F */ + { UNIMAP_F6, UNIMAP_F7, UNIMAP_F8, UNIMAP_F9, UNIMAP_F10, UNIMAP_NLCK, UNIMAP_SLCK, UNIMAP_P7, /* 40-47 */ + UNIMAP_P8, UNIMAP_P9, UNIMAP_PMNS, UNIMAP_P4, UNIMAP_P5, UNIMAP_P6, UNIMAP_PPLS, UNIMAP_P1 }, /* 48-4F */ + { UNIMAP_P2, UNIMAP_P3, UNIMAP_P0, UNIMAP_PDOT, UNIMAP_PSCR, UNIMAP_PAUSE, UNIMAP_NUHS, UNIMAP_F11, /* 50-57 */ + UNIMAP_F12, UNIMAP_PEQL, UNIMAP_LGUI, UNIMAP_RGUI, UNIMAP_APP, UNIMAP_MUTE, UNIMAP_VOLD, UNIMAP_VOLU }, /* 58-5F */ + { UNIMAP_UP, UNIMAP_LEFT, UNIMAP_DOWN, UNIMAP_RIGHT, UNIMAP_F13, UNIMAP_F14, UNIMAP_F15, UNIMAP_F16, /* 60-67 */ + UNIMAP_F17, UNIMAP_F18, UNIMAP_F19, UNIMAP_F20, UNIMAP_F21, UNIMAP_F22, UNIMAP_F23, UNIMAP_PENT }, /* 68-6F */ + { UNIMAP_KANA, UNIMAP_INSERT,UNIMAP_DELETE,UNIMAP_RO, UNIMAP_HOME, UNIMAP_END, UNIMAP_F24, UNIMAP_PGUP, /* 70-77 */ + UNIMAP_PGDN, UNIMAP_HENK, UNIMAP_RCTL, UNIMAP_MHEN, UNIMAP_RALT, UNIMAP_JYEN, UNIMAP_PCMM, UNIMAP_PSLS }, /* 78-7F */ }; @@ -151,22 +147,22 @@ const uint8_t PROGMEM unimap_cs1[MATRIX_ROWS][MATRIX_COLS] = { * 51, 63, 68, 6A, 6D: Hidden keys in IBM model M [6] */ const uint8_t PROGMEM unimap_cs2[MATRIX_ROWS][MATRIX_COLS] = { - { UNIMAP_PAUS, UNIMAP_F9, UNIMAP_F7, UNIMAP_F5, UNIMAP_F3, UNIMAP_F1, UNIMAP_F2, UNIMAP_F12 }, /* 00-07 */ - { UNIMAP_F13, UNIMAP_F10, UNIMAP_F8, UNIMAP_F6, UNIMAP_F4, UNIMAP_TAB, UNIMAP_GRV, UNIMAP_RALT }, /* 08-0F */ - { UNIMAP_F14, UNIMAP_LALT, UNIMAP_LSHIFT,UNIMAP_KANA, UNIMAP_LCTL, UNIMAP_Q, UNIMAP_1, UNIMAP_RCTL }, /* 10-17 */ - { UNIMAP_F15, UNIMAP_LGUI, UNIMAP_Z, UNIMAP_S, UNIMAP_A, UNIMAP_W, UNIMAP_2, UNIMAP_RGUI }, /* 18-1F */ - { UNIMAP_F16, UNIMAP_C, UNIMAP_X, UNIMAP_D, UNIMAP_E, UNIMAP_4, UNIMAP_3, UNIMAP_END }, /* 20-27 */ - { UNIMAP_F17, UNIMAP_SPACE, UNIMAP_V, UNIMAP_F, UNIMAP_T, UNIMAP_R, UNIMAP_5, UNIMAP_HOME }, /* 28-2F */ - { UNIMAP_F18, UNIMAP_N, UNIMAP_B, UNIMAP_H, UNIMAP_G, UNIMAP_Y, UNIMAP_6, UNIMAP_DEL }, /* 30-37 */ - { UNIMAP_F19, UNIMAP_INS, UNIMAP_M, UNIMAP_J, UNIMAP_U, UNIMAP_7, UNIMAP_8, UNIMAP_DOWN }, /* 38-3F */ - { UNIMAP_F20, UNIMAP_COMMA, UNIMAP_K, UNIMAP_I, UNIMAP_O, UNIMAP_0, UNIMAP_9, UNIMAP_RIGHT }, /* 40-47 */ - { UNIMAP_F21, UNIMAP_DOT, UNIMAP_SLASH, UNIMAP_L, UNIMAP_SCOLON,UNIMAP_P, UNIMAP_MINUS, UNIMAP_UP }, /* 48-4F */ - { UNIMAP_F22, UNIMAP_RO, UNIMAP_QUOTE, UNIMAP_LEFT, UNIMAP_LBRC, UNIMAP_EQUAL, UNIMAP_PGDN, UNIMAP_F23 }, /* 50-57 */ - { UNIMAP_CAPS, UNIMAP_RSHIFT,UNIMAP_ENTER, UNIMAP_RBRC, UNIMAP_APP, UNIMAP_BSLASH,UNIMAP_PGUP, UNIMAP_F24 }, /* 58-5F */ - { UNIMAP_PSLS, UNIMAP_NUBS, UNIMAP_PENT, UNIMAP_PEQL, UNIMAP_HENK, UNIMAP_VOLD, UNIMAP_BSPACE,UNIMAP_MHEN }, /* 60-67 */ - { UNIMAP_NUHS, UNIMAP_P1, UNIMAP_JYEN, UNIMAP_P4, UNIMAP_P7, UNIMAP_PCMM, UNIMAP_VOLU, UNIMAP_MUTE }, /* 68-6F */ - { UNIMAP_P0, UNIMAP_PDOT, UNIMAP_P2, UNIMAP_P5, UNIMAP_P6, UNIMAP_P8, UNIMAP_ESC, UNIMAP_NLCK }, /* 70-77 */ - { UNIMAP_F11, UNIMAP_PPLS, UNIMAP_P3, UNIMAP_PMNS, UNIMAP_PAST, UNIMAP_P9, UNIMAP_SLCK, UNIMAP_PSCR }, /* 78-7F */ + { UNIMAP_PAUS, UNIMAP_F9, UNIMAP_F7, UNIMAP_F5, UNIMAP_F3, UNIMAP_F1, UNIMAP_F2, UNIMAP_F12, /* 00-07 */ + UNIMAP_F13, UNIMAP_F10, UNIMAP_F8, UNIMAP_F6, UNIMAP_F4, UNIMAP_TAB, UNIMAP_GRV, UNIMAP_RALT }, /* 08-0F */ + { UNIMAP_F14, UNIMAP_LALT, UNIMAP_LSHIFT,UNIMAP_KANA, UNIMAP_LCTL, UNIMAP_Q, UNIMAP_1, UNIMAP_RCTL, /* 10-17 */ + UNIMAP_F15, UNIMAP_LGUI, UNIMAP_Z, UNIMAP_S, UNIMAP_A, UNIMAP_W, UNIMAP_2, UNIMAP_RGUI }, /* 18-1F */ + { UNIMAP_F16, UNIMAP_C, UNIMAP_X, UNIMAP_D, UNIMAP_E, UNIMAP_4, UNIMAP_3, UNIMAP_END, /* 20-27 */ + UNIMAP_F17, UNIMAP_SPACE, UNIMAP_V, UNIMAP_F, UNIMAP_T, UNIMAP_R, UNIMAP_5, UNIMAP_HOME }, /* 28-2F */ + { UNIMAP_F18, UNIMAP_N, UNIMAP_B, UNIMAP_H, UNIMAP_G, UNIMAP_Y, UNIMAP_6, UNIMAP_DEL, /* 30-37 */ + UNIMAP_F19, UNIMAP_INS, UNIMAP_M, UNIMAP_J, UNIMAP_U, UNIMAP_7, UNIMAP_8, UNIMAP_DOWN }, /* 38-3F */ + { UNIMAP_F20, UNIMAP_COMMA, UNIMAP_K, UNIMAP_I, UNIMAP_O, UNIMAP_0, UNIMAP_9, UNIMAP_RIGHT, /* 40-47 */ + UNIMAP_F21, UNIMAP_DOT, UNIMAP_SLASH, UNIMAP_L, UNIMAP_SCOLON,UNIMAP_P, UNIMAP_MINUS, UNIMAP_UP }, /* 48-4F */ + { UNIMAP_F22, UNIMAP_RO, UNIMAP_QUOTE, UNIMAP_LEFT, UNIMAP_LBRC, UNIMAP_EQUAL, UNIMAP_PGDN, UNIMAP_F23, /* 50-57 */ + UNIMAP_CAPS, UNIMAP_RSHIFT,UNIMAP_ENTER, UNIMAP_RBRC, UNIMAP_APP, UNIMAP_BSLASH,UNIMAP_PGUP, UNIMAP_F24 }, /* 58-5F */ + { UNIMAP_PSLS, UNIMAP_NUBS, UNIMAP_PENT, UNIMAP_PEQL, UNIMAP_HENK, UNIMAP_VOLD, UNIMAP_BSPACE,UNIMAP_MHEN, /* 60-67 */ + UNIMAP_NUHS, UNIMAP_P1, UNIMAP_JYEN, UNIMAP_P4, UNIMAP_P7, UNIMAP_PCMM, UNIMAP_VOLU, UNIMAP_MUTE }, /* 68-6F */ + { UNIMAP_P0, UNIMAP_PDOT, UNIMAP_P2, UNIMAP_P5, UNIMAP_P6, UNIMAP_P8, UNIMAP_ESC, UNIMAP_NLCK, /* 70-77 */ + UNIMAP_F11, UNIMAP_PPLS, UNIMAP_P3, UNIMAP_PMNS, UNIMAP_PAST, UNIMAP_P9, UNIMAP_SLCK, UNIMAP_PSCR }, /* 78-7F */ }; @@ -210,45 +206,22 @@ const uint8_t PROGMEM unimap_cs2[MATRIX_ROWS][MATRIX_COLS] = { * 51, 5C, 5D, 68, 78: Hidden keys in IBM 122-key terminal keyboard [7] */ const uint8_t PROGMEM unimap_cs3[MATRIX_ROWS][MATRIX_COLS] = { - { UNIMAP_KANA, UNIMAP_LGUI, UNIMAP_PSCR, UNIMAP_VOLD, UNIMAP_VOLU, UNIMAP_MUTE, UNIMAP_HENK, UNIMAP_F1 }, /* 00-07 */ - { UNIMAP_F13, UNIMAP_RGUI, UNIMAP_APP, UNIMAP_MHEN, UNIMAP_PAUS, UNIMAP_TAB, UNIMAP_GRV, UNIMAP_F2 }, /* 08-0F */ - { UNIMAP_F14, UNIMAP_LCTL, UNIMAP_LSHIFT,UNIMAP_NUBS, UNIMAP_CAPS, UNIMAP_Q, UNIMAP_1, UNIMAP_F3 }, /* 10-17 */ - { UNIMAP_F15, UNIMAP_LALT, UNIMAP_Z, UNIMAP_S, UNIMAP_A, UNIMAP_W, UNIMAP_2, UNIMAP_F4 }, /* 18-1F */ - { UNIMAP_F16, UNIMAP_C, UNIMAP_X, UNIMAP_D, UNIMAP_E, UNIMAP_4, UNIMAP_3, UNIMAP_F5 }, /* 20-27 */ - { UNIMAP_F17, UNIMAP_SPACE, UNIMAP_V, UNIMAP_F, UNIMAP_T, UNIMAP_R, UNIMAP_5, UNIMAP_F6 }, /* 28-2F */ - { UNIMAP_F18, UNIMAP_N, UNIMAP_B, UNIMAP_H, UNIMAP_G, UNIMAP_Y, UNIMAP_6, UNIMAP_F7 }, /* 30-37 */ - { UNIMAP_F19, UNIMAP_RALT, UNIMAP_M, UNIMAP_J, UNIMAP_U, UNIMAP_7, UNIMAP_8, UNIMAP_F8 }, /* 38-3F */ - { UNIMAP_F20, UNIMAP_COMMA, UNIMAP_K, UNIMAP_I, UNIMAP_O, UNIMAP_0, UNIMAP_9, UNIMAP_F9 }, /* 40-47 */ - { UNIMAP_F21, UNIMAP_DOT, UNIMAP_SLASH, UNIMAP_L, UNIMAP_SCOLON,UNIMAP_P, UNIMAP_MINUS, UNIMAP_F10 }, /* 48-4F */ - { UNIMAP_F22, UNIMAP_RO, UNIMAP_QUOTE, UNIMAP_NUHS, UNIMAP_LBRC, UNIMAP_EQUAL, UNIMAP_F11, UNIMAP_F23 }, /* 50-57 */ - { UNIMAP_RCTL, UNIMAP_RSHIFT,UNIMAP_ENTER, UNIMAP_RBRC, UNIMAP_BSLASH,UNIMAP_JYEN, UNIMAP_F12, UNIMAP_F24 }, /* 58-5F */ - { UNIMAP_DOWN, UNIMAP_LEFT, UNIMAP_HOME, UNIMAP_UP, UNIMAP_END, UNIMAP_INS, UNIMAP_BSPACE,UNIMAP_PSLS }, /* 60-67 */ - { UNIMAP_PCMM, UNIMAP_P1, UNIMAP_RIGHT, UNIMAP_P4, UNIMAP_P7, UNIMAP_DEL, UNIMAP_PGUP, UNIMAP_PGDN }, /* 68-6F */ - { UNIMAP_P0, UNIMAP_PDOT, UNIMAP_P2, UNIMAP_P5, UNIMAP_P6, UNIMAP_P8, UNIMAP_ESC, UNIMAP_NLCK }, /* 70-77 */ - { UNIMAP_PEQL, UNIMAP_PENT, UNIMAP_P3, UNIMAP_PMNS, UNIMAP_PPLS, UNIMAP_P9, UNIMAP_SLCK, UNIMAP_PAST }, /* 78-7F */ + { UNIMAP_KANA, UNIMAP_LGUI, UNIMAP_PSCR, UNIMAP_VOLD, UNIMAP_VOLU, UNIMAP_MUTE, UNIMAP_HENK, UNIMAP_F1, /* 00-07 */ + UNIMAP_F13, UNIMAP_RGUI, UNIMAP_APP, UNIMAP_MHEN, UNIMAP_PAUS, UNIMAP_TAB, UNIMAP_GRV, UNIMAP_F2 }, /* 08-0F */ + { UNIMAP_F14, UNIMAP_LCTL, UNIMAP_LSHIFT,UNIMAP_NUBS, UNIMAP_CAPS, UNIMAP_Q, UNIMAP_1, UNIMAP_F3, /* 10-17 */ + UNIMAP_F15, UNIMAP_LALT, UNIMAP_Z, UNIMAP_S, UNIMAP_A, UNIMAP_W, UNIMAP_2, UNIMAP_F4 }, /* 18-1F */ + { UNIMAP_F16, UNIMAP_C, UNIMAP_X, UNIMAP_D, UNIMAP_E, UNIMAP_4, UNIMAP_3, UNIMAP_F5, /* 20-27 */ + UNIMAP_F17, UNIMAP_SPACE, UNIMAP_V, UNIMAP_F, UNIMAP_T, UNIMAP_R, UNIMAP_5, UNIMAP_F6 }, /* 28-2F */ + { UNIMAP_F18, UNIMAP_N, UNIMAP_B, UNIMAP_H, UNIMAP_G, UNIMAP_Y, UNIMAP_6, UNIMAP_F7, /* 30-37 */ + UNIMAP_F19, UNIMAP_RALT, UNIMAP_M, UNIMAP_J, UNIMAP_U, UNIMAP_7, UNIMAP_8, UNIMAP_F8 }, /* 38-3F */ + { UNIMAP_F20, UNIMAP_COMMA, UNIMAP_K, UNIMAP_I, UNIMAP_O, UNIMAP_0, UNIMAP_9, UNIMAP_F9, /* 40-47 */ + UNIMAP_F21, UNIMAP_DOT, UNIMAP_SLASH, UNIMAP_L, UNIMAP_SCOLON,UNIMAP_P, UNIMAP_MINUS, UNIMAP_F10 }, /* 48-4F */ + { UNIMAP_F22, UNIMAP_RO, UNIMAP_QUOTE, UNIMAP_NUHS, UNIMAP_LBRC, UNIMAP_EQUAL, UNIMAP_F11, UNIMAP_F23, /* 50-57 */ + UNIMAP_RCTL, UNIMAP_RSHIFT,UNIMAP_ENTER, UNIMAP_RBRC, UNIMAP_BSLASH,UNIMAP_JYEN, UNIMAP_F12, UNIMAP_F24 }, /* 58-5F */ + { UNIMAP_DOWN, UNIMAP_LEFT, UNIMAP_HOME, UNIMAP_UP, UNIMAP_END, UNIMAP_INS, UNIMAP_BSPACE,UNIMAP_PSLS, /* 60-67 */ + UNIMAP_PCMM, UNIMAP_P1, UNIMAP_RIGHT, UNIMAP_P4, UNIMAP_P7, UNIMAP_DEL, UNIMAP_PGUP, UNIMAP_PGDN }, /* 68-6F */ + { UNIMAP_P0, UNIMAP_PDOT, UNIMAP_P2, UNIMAP_P5, UNIMAP_P6, UNIMAP_P8, UNIMAP_ESC, UNIMAP_NLCK, /* 70-77 */ + UNIMAP_PEQL, UNIMAP_PENT, UNIMAP_P3, UNIMAP_PMNS, UNIMAP_PPLS, UNIMAP_P9, UNIMAP_SLCK, UNIMAP_PAST }, /* 78-7F */ }; - -extern const action_t actionmaps[][UNIMAP_ROWS][UNIMAP_COLS]; -action_t action_for_key(uint8_t layer, keypos_t key) -{ - uint8_t unimap_pos; - switch (keyboard_kind) { - case PC_XT: - unimap_pos = pgm_read_byte(&unimap_cs1[key.row][key.col]); - break; - case PC_AT: - unimap_pos = pgm_read_byte(&unimap_cs2[key.row][key.col]); - break; - case PC_TERMINAL: - unimap_pos = pgm_read_byte(&unimap_cs3[key.row][key.col]); - break; - default: - return (action_t)ACTION_NO; - } - - if (unimap_pos == UNIMAP_NO) return (action_t)ACTION_NO; - - return (action_t)pgm_read_word(&actionmaps[(layer)][(unimap_pos & 0x70) >> 4][(unimap_pos & 0x0f)]); -} #endif diff --git a/converter/usb_desc_dump/Makefile b/converter/usb_desc_dump/Makefile new file mode 100644 index 00000000..1db71359 --- /dev/null +++ b/converter/usb_desc_dump/Makefile @@ -0,0 +1,88 @@ +TARGET ?= usb_desc_dump +TMK_DIR ?= ../../tmk_core +TARGET_DIR ?= . + +SRC ?= ino.cpp +CONFIG_H ?= config.h + +# MCU name +MCU ?= atmega32u4 +# Processor frequency. +F_CPU ?= 16000000 + +# +# LUFA specific +# +ARCH ?= AVR8 +F_USB ?= $(F_CPU) +OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT + +# +# Build Options +# +MOUSEKEY_ENABLE ?= no # Mouse keys +EXTRAKEY_ENABLE ?= no # Media control and System control +CONSOLE_ENABLE ?= yes # Console for debug +COMMAND_ENABLE ?= no # Commands for debug and configuration +NKRO_ENABLE ?= no # USB Nkey Rollover +NO_KEYBOARD ?= yes # No keyboard interface + +# Boot Section Size in bytes +OPT_DEFS += -DBOOTLOADER_SIZE=4096 +OPT_DEFS += -DNO_DEBUG + +EXTRACPPFLAGS = -fpermissive + +# program Leonardo +PROGRAM_CMD = avrdude -p$(MCU) -cavr109 -b57600 -Uflash:w:$(TARGET).hex -P$(DEV) + +# Search Path +VPATH += $(TARGET_DIR) +VPATH += $(TMK_DIR) + + + +# +# USB_desc_dump build setting +# +include $(TMK_DIR)/protocol/usb_hid.mk + +USB_DESC_DUMP_DIR = $(USB_HOST_SHIELD_DIR)/examples/USB_desc_dump +SRC += $(USB_DESC_DUMP_DIR)/USB_desc_dump.cpp +SRC += $(USB_HOST_SHIELD_DIR)/hidescriptorparser.cpp + +# Print Standard descriptor +OPT_DEFS += -DPRINT_DESC +#OPT_DEFS += -DNO_PRINT_DESC + +# Print High-speed Hub descriptor +#OPT_DEFS += -DPRINT_DESC_HSHUB +OPT_DEFS += -DNO_PRINT_DESC_HSHUB + +# Print HID Report descriptor +#OPT_DEFS += -DPRINT_DESC_REPORT +OPT_DEFS += -DNO_PRINT_DESC_REPORT + + + +include $(TMK_DIR)/protocol/lufa.mk +include $(TMK_DIR)/common.mk +include $(TMK_DIR)/rules.mk + + + +$(OBJDIR)/$(USB_DESC_DUMP_DIR)/USB_desc_dump.cpp.o : $(OBJDIR)/$(USB_DESC_DUMP_DIR)/USB_desc_dump.cpp $(OBJDIR)/$(USB_DESC_DUMP_DIR)/desc.h + @echo + mkdir -p $(@D) + @echo $(MSG_COMPILING_CPP) $< + $(CC) -c $(ALL_CPPFLAGS) $< -o $@ + +$(OBJDIR)/$(USB_DESC_DUMP_DIR)/USB_desc_dump.cpp : $(TMK_DIR)/$(USB_DESC_DUMP_DIR)/USB_desc_dump.ino + @echo + mkdir -p $(@D) + $(COPY) $< $@ + +$(OBJDIR)/$(USB_DESC_DUMP_DIR)/desc.h : $(TMK_DIR)/$(USB_DESC_DUMP_DIR)/desc.h + @echo + mkdir -p $(@D) + $(COPY) $< $@ diff --git a/converter/usb_desc_dump/README.md b/converter/usb_desc_dump/README.md new file mode 100644 index 00000000..6cd3b284 --- /dev/null +++ b/converter/usb_desc_dump/README.md @@ -0,0 +1,163 @@ +USB Descriptor Dumper +===================== +2021-07-20 + +`Usb_desc_dump` gets USB descriptors and shows in HEX dump and human readable form. +This works on TMK USB-USB converter and USB Host Shield with Arduino Leonardo. + + +Following descriptors are supported. + +- Device Descriptor +- Configuration Descriptor + - Interface Descriptor + - Endpoint Descriptor + - HID Descriptor +- HID Report descriptor +- String Descriptor +- Device Qualifier* +- Other Speed* +- Audio/MIDI Class* +*: partly supported + + +See source code for the detail. + +- https://github.com/tmk/USB_Host_Shield_2.0/tree/master/examples/USB_desc_dump + + + +Example optput: + +``` +usb_state: 90 + +////////////////////////////////////////////////////////////////////// +// USB_desc_dump +// Address: 01 +// Lowspeed: 00 + +// Devicer dump: +12 01 00 02 00 00 00 08 6A 04 11 00 00 01 00 00 +00 01 + +// Device: +bLength: 12 +bDescriptorType: 01 +bcdUSB: 0200 +bDeviceClass: 00 +bDeviceSubClass: 00 +bDeviceProtocol: 00 +bMaxPacketSize0: 08 +idVendor: 046A +idProduct: 0011 +bcdDevice: 0100 +iManufacturer: 00 +iProduct: 00 +iSerialNumber: 00 +bNumConfigurations: 01 + +// Config0 dump: len: 0022 +09 02 22 00 01 01 00 A0 32 09 04 00 00 01 03 01 +01 00 09 21 11 01 00 01 22 40 00 07 05 81 03 08 +00 0A + +// Config: +bLength: 09 +bDescriptorType: 02 +wTotalLength: 0022 +bNumInterfaces: 01 +bConfigurationValue: 01 +iConfiguration: 00 +bmAttributes: A0 +bMaxPower: 32 + +// Interface0.0: +bLength: 09 +bDescriptorType: 04 +bInterfaceNumber: 00 +bAlternateSetting: 00 +bNumEndpoints: 01 +bInterfaceClass: 03 +bInterfaceSubClass: 01 +bInterfaceProtocol: 01 +iInterface: 00 + +// HID: +bLength: 09 +bDescriptorType: 21 +bcdHID: 0111 +bCountryCode: 00 +bNumDescriptors: 01 +bDescrType: 22 +wDescriptorLength: 0040 + +// Report0 dump: len: 0040 +05 01 09 06 A1 01 05 07 19 E0 29 E7 15 00 25 01 +75 01 95 08 81 02 95 01 75 08 81 01 95 03 75 01 +05 08 19 01 29 03 91 02 95 05 75 01 91 01 95 06 +75 08 15 00 26 DD 00 05 07 19 00 29 DD 81 00 C0 + +// Endpoint: +bLength: 07 +bDescriptorType: 05 +bEndpointAddress: 81 +bmAttributes: 03 +wMaxPacketSize: 0008 +bInterval: 0A + +// Parse data here: http://eleccelerator.com/usbdescreqparser/ +``` + + + +To inspect descriptor content closely use 'USB Descriptor and Request Parser' on line. + +- https://eleccelerator.com/usbdescreqparser/ + + +Also you can use command line tool `hidrd-convert` like below. + +- https://github.com/DIGImend/hidrd + +``` +$ cat | hidrd-convert -i hex -o spec +05 01 09 06 A1 01 05 07 19 E0 29 E7 15 00 25 01 +75 01 95 08 81 02 95 01 75 08 81 01 95 03 75 01 +05 08 19 01 29 03 91 02 95 05 75 01 91 01 95 06 +75 08 15 00 26 DD 00 05 07 19 00 29 DD 81 00 C0 +^D + +Usage Page (Desktop), ; Generic desktop controls (01h) +Usage (Keyboard), ; Keyboard (06h, application collection) +Collection (Application), + Usage Page (Keyboard), ; Keyboard/keypad (07h) + Usage Minimum (KB Leftcontrol), ; Keyboard left control (E0h, dynamic value) + Usage Maximum (KB Right GUI), ; Keyboard right GUI (E7h, dynamic value) + Logical Minimum (0), + Logical Maximum (1), + Report Size (1), + Report Count (8), + Input (Variable), + Report Count (1), + Report Size (8), + Input (Constant), + Report Count (3), + Report Size (1), + Usage Page (LED), ; LEDs (08h) + Usage Minimum (01h), + Usage Maximum (03h), + Output (Variable), + Report Count (5), + Report Size (1), + Output (Constant), + Report Count (6), + Report Size (8), + Logical Minimum (0), + Logical Maximum (221), + Usage Page (Keyboard), ; Keyboard/keypad (07h) + Usage Minimum (None), ; No event (00h, selector) + Usage Maximum (KP Hexadecimal), ; Keypad Hexadecimal (DDh, selector) + Input, +End Collection +``` diff --git a/converter/usb_desc_dump/binary/usb_desc_dump.hex b/converter/usb_desc_dump/binary/usb_desc_dump.hex new file mode 100644 index 00000000..8e3e9994 --- /dev/null +++ b/converter/usb_desc_dump/binary/usb_desc_dump.hex @@ -0,0 +1,1191 @@ +:100000000C9489040C94DC040C94DC040C94DC0443 +:100010000C94DC040C94DC040C94DC040C94DC04E0 +:100020000C94DC040C94DC040C9410220C94E32259 +:100030000C9456230C94DC040C94DC040C94DC0427 +:100040000C94DC040C94DC040C94DC040C94DC04B0 +:100050000C94DC040C9445060C94DC040C94DC0435 +:100060000C94DC040C94DC040C94DC040C94DC0490 +:100070000C94DC040C94DC040C94DC040C94DC0480 +:100080000C94DC040C94DC040C94DC040C94DC0470 +:100090000C94DC040C94DC040C94DC040C94DC0460 +:1000A0000C94DC040C94DC040C94DC047573625F27 +:1000B00073746174653A20004F534320646964206F +:1000C0006E6F742073746172742E00537461727455 +:1000D000000D0A2F2F2050617273652064617461D6 +:1000E00020686572653A20687474703A2F2F656CC9 +:1000F000656363656C657261746F722E636F6D2FDB +:100100007573626465736372657170617273657231 +:100110002F000D0A2F2F206953657269616C4E758F +:100120006D6265723A20000D0A2F2F206950726FA0 +:10013000647563743A20000D0A2F2F20694D616E9B +:100140007566616374757265723A20002C20002F09 +:100150002F204C616E6749643A20006C656E3A202E +:10016000000D0A2F2F20537472696E67205A657232 +:100170006F3A20000D0A2F2F204465766963653A97 +:10018000000D0A2F2F20446576696365722064751F +:100190006D703A002F2F204C6F7773706565643A4D +:1001A00020002F2F20416464726573733A20000D84 +:1001B0000A2F2F205553425F646573635F64756D2A +:1001C00070000D0A2F2F204875623A20546F20646A +:1001D000756D702064657363726970746F72206FDF +:1001E000662068756220736565204E4F544520690E +:1001F0006E20636F6465000D0A2F2F2069436F6EB8 +:1002000066696775726174696F6E3A20004275663F +:10021000666572206973206E6F7420656E6F7567F6 +:100220006821006C656E3A20002064756D703A0993 +:10023000000D0A2F2F20436F6E666967000D0A2F8D +:100240002F20556E6B6E6F776E20484944204465B1 +:1002500073633A20747970653A3A20002064756DB2 +:10026000703A096C656E3A20000D0A2F2F205265F6 +:10027000706F7274000D0A2F2F204849443A2000F5 +:100280000D0A2F2F2069496E746572666163653AA5 +:1002900020003A002E000D0A2F2F20496E7465723F +:1002A00066616365000D0A2F2F20456E64706F69CB +:1002B0006E743A000D0A2F2F20436F6E6669673AFD +:1002C000002F2F20006C656E3A2000293A2000286C +:1002D00000537472696E670042614173736F634AC1 +:1002E00061636B49443A090900624E756D456D6260 +:1002F0004D4944494A61636B3A090062446573633E +:10030000726970746F72537562547970653A09003E +:100310006244657363726970746F72547970653A80 +:100320000900624C656E6774683A09090020474508 +:100330004E4552414C000D0A2F2F204D4944492073 +:1003400053747265616D696E6720456E64706F6984 +:100350006E743A00694A61636B3A090909004261A7 +:10036000536F7572636550696E3A090900426153B3 +:100370006F7572636549443A090900624E72496EAD +:1003800070757450696E733A0909004A61636B496C +:10039000443A09090900624A61636B547970653A0D +:1003A0000909006244657363726970746F725375F2 +:1003B00062547970653A09006244657363726970CA +:1003C000746F72547970653A0900624C656E677497 +:1003D000683A090900204F55545F4A41434B006970 +:1003E0004A61636B3A090909004A61636B49443AFF +:1003F00009090900624A61636B547970653A090919 +:10040000006244657363726970746F7253756254ED +:100410007970653A09006244657363726970746F3C +:1004200072547970653A0900624C656E6774683A77 +:1004300009090020494E5F4A41434B0077546F74CD +:10044000616C4C656E6774683A0909006263644DBB +:1004500053433A0909090062446573637269707411 +:100460006F72537562547970653A0900624465731E +:1004700063726970746F72547970653A0900624CE6 +:10048000656E6774683A0909002048454144455241 +:10049000000D0A2F2F204D49444920537472656185 +:1004A0006D696E6720496E7465666163653A0062C6 +:1004B0006D436F6E74726F6C733A09090077546FF5 +:1004C00074616C4C656E6774683A09090062436137 +:1004D0007465676F72793A09090062636441444345 +:1004E0003A090909006244657363726970746F7236 +:1004F000537562547970653A09006244657363729A +:100500006970746F72547970653A0900624C656E57 +:100510006774683A09090020484541444552000D76 +:100520000A2F2F20417564696F20436F6E74726FBC +:100530006C20496E7465666163653A007744657343 +:1005400063726970746F724C656E6774683A090003 +:10055000624465736372547970653A090900624EAA +:10056000756D44657363726970746F72733A0900D4 +:1005700062436F756E747279436F64653A0909005E +:100580006263644849443A09090900624465736337 +:10059000726970746F72547970653A0900624C65C3 +:1005A0006E6774683A09090069496E74657266611C +:1005B00063653A09090062496E7465726661636534 +:1005C00050726F746F636F6C3A090062496E7465A4 +:1005D0007266616365537562436C6173733A0900B7 +:1005E00062496E74657266616365436C6173733AE8 +:1005F0000900624E756D456E64706F696E74733A72 +:1006000009090062416C7465726E617465536574AA +:1006100074696E673A090062496E74657266616357 +:10062000654E756D6265723A090062446573637266 +:100630006970746F72547970653A0900624C656E26 +:100640006774683A09090062496E74657276616C74 +:100650003A090900774D61785061636B657453699D +:100660007A653A090900626D417474726962757441 +:1006700065733A09090062456E64706F696E744172 +:100680006464726573733A090062446573637269E6 +:1006900070746F72547970653A0900624C656E67C8 +:1006A00074683A090900624D6178506F7765723A53 +:1006B000090900626D417474726962757465733AF8 +:1006C00009090069436F6E66696775726174696FC5 +:1006D0006E3A09090062436F6E66696775726174EC +:1006E000696F6E56616C75653A0900624E756D49A9 +:1006F0006E74657266616365733A09090077546FB9 +:1007000074616C4C656E6774683A090900624465EF +:100710007363726970746F72547970653A0900621C +:100720004C656E6774683A090900624E756D436FD7 +:100730006E66696775726174696F6E733A090069F4 +:1007400053657269616C4E756D6265723A09090094 +:100750006950726F647563743A090900694D616E7E +:100760007566616374757265723A09090062636443 +:100770004465766963653A090900696450726F647B +:100780007563743A090900696456656E646F723A5C +:10079000090900624D61785061636B657453697A31 +:1007A00065303A09006244657669636550726F741A +:1007B0006F636F6C3A0900624465766963655375CF +:1007C00062436C6173733A090062446576696365DC +:1007D000436C6173733A0909006263645553423A8A +:1007E000090909006244657363726970746F725419 +:1007F0007970653A0900624C656E6774683A090958 +:1008000000206C656E3A20000D0A2F2F20556E6B6C +:100810006E6F776E20446573633A20747970653A21 +:1008200020002F2F20004552524F523A2000300016 +:100830000A4C6F6F702073746172742E0A000A552F +:10084000534220636F6E666967757265642E0A0095 +:100850000A544D4B3A3762323832372F4C554641A5 +:100860003A6436613764662F554853323A39363127 +:100870003132640A002C035500530042002000442A +:100880000065007300630072006900700074006FFF +:100890000072002000440075006D00700065007259 +:1008A000000000080354004D004B00000004030941 +:1008B0000409022900010100A0320904000002031A +:1008C0000000000921110100012222000705810317 +:1008D00020000107050103200001120110010000A2 +:1008E0000008EDFE5B001408010200010631FF095B +:1008F00074A1010975150026FF0095207508810275 +:100900000976150026FF00952075089102C0BE05E6 +:10091000441811241FBECFEFDAE0DEBFCDBF04B60E +:1009200003FE28C08091650590916605A09167053A +:10093000B09168058730904BA740B04BD9F4109226 +:10094000650510926605109267051092680514BE41 +:100950000FB6F894A895809160008861809360003C +:10096000109260000FBE1092010810920008E0E0A3 +:10097000F8E3099511E0A0E0B1E0E2E0FAE402C09A +:1009800005900D92A035B107D9F725E0A0E5B1E0BB +:1009900001C01D92A536B207E1F714E0C9E8D4E022 +:1009A00004C02197FE010E94F924C738D107C9F776 +:1009B0000E94091E0C94FF240C940000FF93EF93F7 +:1009C000E0915001F0915101309709F00995EF91B4 +:1009D000FF910895FC018591803011F0EFDFFBCF8E +:1009E0000895FF27E0E230E247FF0CC0419597FFF2 +:1009F00009C0EDE26095709580959095611D711D1F +:100A0000811D911D27FF02C0219530E350E2AA27E6 +:100A1000660F771F881F991FAA1FA41710F0A41B29 +:100A200063955A95A9F7AA3008F0A95FA05DAF9326 +:100A3000F395611571058105910541F7ED3211F4CA +:100A4000EF93F395F21718F43F93F395FBCF8F9143 +:100A5000B5DFFA95E1F70895E991F9918591803034 +:100A600021F0853219F0AADFF9CF089520E0E8944B +:100A700055915532C1F3503311F4689455915A335E +:100A800048F4505390F3220F022E220F220F200D14 +:100A9000250FF4CF0EF4219589919991533619F3CE +:100AA000533729F1533559F1BC0188279927E89428 +:100AB0005C3621F48991999168945591543639F4B2 +:100AC00046EF7EF077FF0DC08FEF9FEF0AC05537DE +:100AD0004AE039F0583540E121F0523642E009F061 +:100AE0000895FF93EF937DDFEF91FF91B7CFFF93D1 +:100AF000EF93FC0181918030B9F360DFFBCFFF936E +:100B0000EF9368DFF1CFDF93CF93CDB7DEB72596B4 +:100B1000A3DFCF91DF9108950C941E1808950C94D3 +:100B20005A1C089590E080E008958FEF9FEF08959C +:100B300081E090E00895862F0E94F21C81E090E011 +:100B40000895EF92FF920F931F93CF93DF938A0143 +:100B5000EB017B01E40EF51ECE15DF0521F0899136 +:100B60000E94F21CF9CFC801DF91CF911F910F9124 +:100B7000FF90EF900895089581E00895E2E5F1E097 +:100B80001382128288EE93E0A0E0B0E08483958324 +:100B9000A683B78386E191E0918380838FEF9FEFF7 +:100BA0009587848708950C9439068F929F92AF920F +:100BB000BF92CF92DF92EF92FF920E94B3234B013C +:100BC0005C0120E030E04AE755E40E946A246B01B2 +:100BD0007C0120E030E040E85FE30E947F2387FD56 +:100BE00030C020E03FEF4FE757E4C701B6010E9455 +:100BF00065241816F4F420E030E040E251E4C50129 +:100C0000B4010E946A240E94842320E931E0611526 +:100C1000710549F4FF90EF90DF90CF90BF90AF90B7 +:100C20009F908F900895C9010197F1F76150710964 +:100C3000EECFC701B6010E948423CB010197F1F7E3 +:100C4000E9CF61E070E0F9CF82E084BD93E095BD2B +:100C50009AEF97BD80936E0008952FB7F894809116 +:100C6000A2019091A301A091A401B091A5012FBF71 +:100C700008952FB7F8946091A2017091A30180911B +:100C8000A4019091A5012FBF089578941F920F920F +:100C90000FB60F9211248F939F93AF93BF938091C0 +:100CA000A2019091A301A091A401B091A501019688 +:100CB000A11DB11D8093A2019093A301A093A40153 +:100CC000B093A501BF91AF919F918F910F900FBEEF +:100CD0000F901F901895CF93DF93FC0167FD12C012 +:100CE000283080F4442319F08581873059F0EF01D2 +:100CF000A1E0B0E08D85882349F111962596A031B9 +:100D0000B105C1F780E018C085E08A9FE0018B9FA4 +:100D1000D00D1124CE0FDF1F442389F081E4888792 +:100D200085818F5F858325E02A9FC0012B9F900DD1 +:100D30001124E80FF91F8085DF91CF91089581E09C +:100D40008887F1CF2770687B622B0FC06623E1F2A2 +:100D50006770660F660F660F4423A1F36064858198 +:100D60008F5F85838770687F682B25E02A9FC0018D +:100D70002B9F900D1124E80FF91F6087862FDCCF81 +:100D8000DF92EF92FF920F931F93CF93DF93EC01CB +:100D900085E0E62EF12CFE01869FE00DF11D112469 +:100DA000908596FF24C0192F177001E085E0D82E9A +:100DB0008FEF800F8F3008F001E0602F70E0062F7A +:100DC000D69EF001D79EF00D1124EC0FFD1F8085FB +:100DD0008695869586958770181701F16F5F7F4F0E +:100DE000603168F38D81811302C011501D8385E04D +:100DF0008E9DF0018F9DF00D1124EC0FFD1F1086CC +:100E000081E0818712862296D783C683DF91CF91B6 +:100E10001F910F91FF90EF90DF900895CE010E94F7 +:100E2000C006C6CFFC0121E0611102C006960895FC +:100E30003585631739F02F5F35962031C9F790E07B +:100E400080E00895069635E0239F800D911D1124C2 +:100E50000895FC0121E0613499F49C01255B3F4F2A +:100E600061E0AC014E5F5F4F158666871786548739 +:100E7000438735962E173F07B9F7FC011582089571 +:100E80003585631729F02F5F35962031C9F708950E +:100E9000622F0C94C006FC0188E291E0918380836C +:100EA000108681E081871286CF0102969783868320 +:100EB000128228E023832CE324831582DF019F0123 +:100EC000255B3F4F41E01D961C921D971E964C934B +:100ED0001E971F961C921F971C969C938E931B9790 +:100EE0001596A217B30779F7EA58FF4F108281E1F0 +:100EF0008093AB0108958091AB010895CF93C42FE7 +:100F00000E941207009789F0DC01ED91FC91119786 +:100F1000309769F013969C9180E0981731F020810A +:100F20002C1729F033968F5FF8CFF0E0E0E0CF0187 +:100F3000CF9108950F931F93CF93DF932115310520 +:100F400099F0E901042F162F0E941207FC0186ED8B +:100F5000309729F01283D183C083038380E0DF912F +:100F6000CF911F910F91089588EDF9CFCF92DF9225 +:100F7000FF920F931F93CF93DF93662381F1F62E99 +:100F80008C016C0186E5C80ED11CD0E0C0E0D60112 +:100F90008D919D916D010097F1F0DC01ED91FC9137 +:100FA0000084F185E02D09958F1115C0AB96CC0F0B +:100FB000DD1FC00FD11F88819981DC01ED91FC916B +:100FC0000480F581E02DDF91CF911F910F91FF906B +:100FD000DF90CF9009942196C031D105C1F680E011 +:100FE000DF91CF911F910F91FF90DF90CF900895E7 +:100FF0002E9808952E9A0895CF93C82F0E94F8072F +:10100000CEBD0DB407FEFDCF1EBC0DB407FEFDCF57 +:101010000E94FA078EB5CF910895CF93DF93C82F22 +:10102000D62F0E94F807C260CEBD0DB407FEFDCFDB +:10103000DEBD0DB407FEFDCFDF91CF910C94FA0712 +:10104000AF92BF92CF92DF92EF92FF920F931F93D6 +:10105000CF93DF93EC01D62EC42E59010E941207C4 +:101060007C0186EDE114F104E1F1F70180819181C9 +:10107000892B09F441C04C2D6D2DCE010E947E07B5 +:10108000F501918380830097C9F1FC0122812695A7 +:101090002695203108F02FE081E090E0A0E0B0E05C +:1010A00004C0880F991FAA1FBB1F2A95D2F701976A +:1010B000F801918380836D2D80EE0E940D0888EDEC +:1010C0000E94FC07F7019481682F697F992329F01A +:1010D000CA58DF4F68816260682B88ED0E940D0856 +:1010E00080E0DF91CF911F910F91FF90EF90DF9003 +:1010F000CF90BF90AF90089587EDF3CF8BEDF1CFF8 +:101100008F929F92AF92BF92CF92DF92EF92FF9217 +:101110000F931F93CF93DF93862E942E59010E9435 +:10112000D3056B017C0128E8C20E23E1D21EE11C2D +:10113000F11CC0E010E000E0D0E098280E94D30548 +:101140006C197D098E099F0997FF1EC0692D80EFDC +:101150000E940D080E94D3056C197D098E099F0914 +:1011600097FF09C088EC0E94FC0787FFF3CF60E877 +:1011700088EC0E940D0888EF0E94FC07C82FCF70F2 +:10118000C43081F0CE30B9F08C2FDF91CF911F9118 +:101190000F91FF90EF90DF90CF90BF90AF909F9016 +:1011A0008F9008950F5F1F4FA114B10439F20A15F3 +:1011B0001B0521F6E9CFDF5FD33009F0BFCFE4CFC5 +:1011C0009F92AF92BF92CF92DF92EF92FF920F93D6 +:1011D0001F93CF93DF936C01C42FD22F9924939444 +:1011E000E62EF12CEE0CFF1CE80EF91E86E5E80E4B +:1011F000F11CA42EB12CABE2AA0EB11CAA0CBB1C94 +:10120000AC0CBD1CF70180819181DC01ED91FC915A +:101210000280F381E02D202F4D2F6C2F0995182F80 +:10122000803EC9F5C1112BC061E088EE0E940D0817 +:1012300066E670E080E090E00E94D505F7018081CD +:101240009181DC01ED91FC910190F081E02D202F46 +:101250004D2F6C2F0995182F8D30D1F58FEF890DFB +:101260008330F8F0C11137C061E088EE0E940D08AC +:1012700066E670E080E090E00E94D5051CC0F501B4 +:1012800080819181DC01ED91FC910284F385E02D58 +:101290006D2F0995D3CF8D3061F48FEF890D833099 +:1012A00050F464E670E080E090E00E94D5059394ED +:1012B000A9CF882319F2812FDF91CF911F910F9130 +:1012C000FF90EF90DF90CF90BF90AF909F900895E8 +:1012D000882389F3C7CF8C2F90E08B96880F991FB6 +:1012E0008C0D9D1DFC0180819181DC01ED91FC91B3 +:1012F0000284F385E02D6D2F0995DDCF0F931F93A9 +:10130000CF93DF93D82FC62F8A010E94F807D260AF +:10131000DEBDF8018C2F882339F00DB407FEFDCF18 +:1013200091919EBD8150F7CFC00FD12FD11D0DB42B +:1013300007FEFDCF0E94FA07CE01DF91CF911F91EA +:101340000F9108952F923F924F925F926F927F92EA +:101350008F929F92AF92BF92CF92DF92EF92FF92C5 +:101360000F931F93CF93DF9300D000D01F92CDB780 +:10137000DEB77D836C835A016901FB01F1808FEF39 +:101380008F0D803408F0B5C00E94D3052B013C01BD +:10139000F8E84F0EF3E15F1E611C711CEC81FD81CA +:1013A000828160E480FD60E888EE0E940D08312CA7 +:1013B0008F2D90E09A838983C114D10409F475C0FC +:1013C000E980FA80CE14DF0408F47601EB82A801EC +:1013D0006E2D80E10E947E096E2D88E30E940D082B +:1013E000EC81FD816081606280EF0E940D0888ECD5 +:1013F0000E94FC0787FFFBCF60E888EC0E940D0885 +:1014000088EF0E94FC078F70382E912C812C212CA4 +:101410003320F9F00E94D3056419750986099709EC +:1014200097FF17C0F6E03F16E9F02EE03216B1F054 +:1014300084E0381209C09FEF891A990AA114B104F7 +:1014400089F08A149B0471F460E088E30E940D081F +:101450002EC0CE18DF080E0D1F1DAECF2394E3E083 +:101460002E1691F360E088E30E940D08F801608178 +:1014700080E10E940D086B8188E30E940D08EC81D9 +:10148000FD816081606280EF0E940D0888EC0E94FF +:10149000FC0787FFFBCF60E888EC0E940D0888EF0F +:1014A0000E94FC078F70382EB3CF3110CDCF88EF5C +:1014B0000E94FC07EC81FD81928185FB90F992836B +:1014C000832D0F900F900F900F900F90DF91CF9181 +:1014D0001F910F91FF90EF90DF90CF90BF90AF9052 +:1014E0009F908F907F906F905F904F903F902F9044 +:1014F00008958AED382EE4CF2F923F924F925F925B +:101500006F927F928F929F92AF92BF92CF92DF9213 +:10151000EF92FF920F931F93CF93DF93CDB7DEB778 +:1015200029970FB6F894DEBF0FBECDBF9C838B8387 +:101530006B015E834D835901E982F90180809180BE +:10154000FB01F181FA83F90111821082FB01828192 +:1015500060E181FD60E288EE0E940D088F82F981D2 +:101560004F2E512C712C612C2D813E81F601408132 +:1015700060E08B819C810E948008382EF6E08F17F6 +:1015800099F3811155C088EC0E94FC0782FF73C05B +:1015900080E30E94FC07282E8815190411F008F03A +:1015A0002F80F501808191819401281B390B3987A7 +:1015B000288737FF02C019861886E22CF12C88850F +:1015C0009985E816F90614F4F986E88638840E94AD +:1015D000F80798E09EBD0DB407FEFDCFF80133205B +:1015E00041F01EBC3A940DB407FEFDCF8EB5819339 +:1015F000F6CFE885F9850E0F1F1F0E94FA0764E0F9 +:1016000088EC0E940D08F50180819181E80EF91E99 +:10161000F182E082FA812F1618F0E814F90410F133 +:1016200088EF0E94FC07F601928184FB91F9928376 +:10163000832D29960FB6F894DEBF0FBECDBFDF9184 +:10164000CF911F910F91FF90EF90DF90CF90BF90BF +:10165000AF909F908F907F906F905F904F903F9052 +:101660002F900895F981FF2309F47ECFC301B201C1 +:101670000E94D50579CF80EF382EDACF2F923F9296 +:101680004F925F926F927F928F929F92AF92BF9292 +:10169000CF92DF92EF92FF920F931F93CF93DF933E +:1016A000CDB7DEB761970FB6F894DEBF0FBECDBFE2 +:1016B0003C01F42E322E202EC98A65014EA05FA077 +:1016C000A8A4B9A4188A1F861E861D868E01035FF2 +:1016D0001F4F9E01215F3F4F0E942008182F81114C +:1016E00077C039822A82EB8289898C83DE82CD821F +:1016F00098868F82AE014F5F5F4F68E080E20E9464 +:101700007E092D853E854F2D60E1C3010E94800832 +:10171000182F81115DC0A114B10409F444C0EF85F4 +:10172000F889828137FE45C0826082836401C114DA +:10173000D10409F46EC09C2D8D2D4C145D0410F461 +:10174000942D852D9B878C874D855E856F85788947 +:10175000E12C85019E01255F3F4FC3010E947C0A59 +:10176000182F863021F3811133C0AAA5BBA510978D +:1017700099F06B857C85ED91FC9111970190F0813A +:10178000E02DC4018C199D099A8789879E01275FE6 +:101790003F4FA501CD0109958B859C85C81AD90AB3 +:1017A0008415950520F62D853E8537FE2BC060EA11 +:1017B0002AC0816082834D855E8585019201BF01CB +:1017C000C3010E94A209182F2D853E858823D1F0E0 +:1017D000812F61960FB6F894DEBF0FBECDBFDF91AB +:1017E000CF911F910F91FF90EF90DF90CF90BF901E +:1017F000AF909F908F907F906F905F904F903F90B1 +:101800002F90089560E84F2DC3010E948008182F83 +:10181000DFCF1111DDCF2D853E85C9CF8F929F92ED +:10182000AF92BF92CF92EF920F931F931F921F928E +:101830001F930F933F932F934901B12CA12CCC24DC +:10184000C394E12C06E020E80E943E0B0F900F901D +:101850000F900F900F900F901F910F91EF90CF90DE +:10186000BF90AF909F908F9008958F929F92AF926C +:10187000BF92CF92EF92FF920F931F921F92FF920F +:10188000EF923F932F934901B12CA12C22E0C22E5D +:10189000E02E06E020E80E943E0B0F900F900F9084 +:1018A0000F900F900F900F91FF90EF90CF90BF90FF +:1018B000AF909F908F9008958F929F92AF92BF921A +:1018C000CF92DF92EF92FF920F931F921F92DF92BF +:1018D000CF923F932F934901570123E0C22EE02E70 +:1018E00006E020E80E943E0B0F900F900F900F90A3 +:1018F0000F900F900F91FF90EF90DF90CF90BF90DF +:10190000AF909F908F9008958F929F92AF92BF92C9 +:10191000CF92EF920F93CF931F921F921F921F921D +:101920001F921F92912C812CB12CA12CC12CE22E44 +:1019300005E020E00E943E0BC82F6CE271E080E0E1 +:1019400090E00E94D5050F900F900F900F900F9090 +:101950000F908C2FCF910F91EF90CF90BF90AF90C1 +:101960009F908F9008950F931F93CF93DF938A3F9B +:101970003FEF930739F416ED812FDF91CF911F913F +:101980000F910895DC011696ED91FC9117EDEF2B68 +:1019900099F3122F242FEC011A8740E00E946B0666 +:1019A000082F8823B1F0682FCE010E9412070097FC +:1019B00011F3FC011483202F40E060E0CE010E946F +:1019C000840C182F8823C1F2602FCE010E942907B2 +:1019D000D3CF14EDD1CF2F923F924F925F926F925F +:1019E0007F928F929F92AF92BF92CF92DF92EF92AF +:1019F000FF920F931F93CF93DF93CDB7DEB7689716 +:101A00000FB6F894DEBF0FBECDBF1B8A38E03C8B0B +:101A10003CE33D8B8A3F3FEF930709F4E2C0622E1F +:101A2000342E262E7C01DC011696CD90DC90179783 +:101A3000CE01439617969C938E9316971A962C93E5 +:101A40008E010F5F1F4F22E130E040E060E0C701F0 +:101A50000E940E0C782EF701D782C68281115AC0DF +:101A600049845A848B849C84FD81FF8B3E81388F0E +:101A7000670186E5C80ED11C56011E8AD5018D91DD +:101A80009D915D010097B9F4BE89BF5FBE8BB031F7 +:101A9000A9F7F601819191916F01009709F061C05A +:101AA0007394F0E17F12F5CF262D432D622DC701EF +:101AB0000E94B30C2FC0DC01ED91FC910084F185F4 +:101AC000E02D09958111E0CFF501929182918F016E +:101AD000DC01ED91FC910088F189E02D688D09957C +:101AE000882391F2F80180819181DC01ED91FC91D4 +:101AF0000484F585E02DA401B2010995882319F12C +:101B0000062D232D422D6E89C7010E94E008813DDC +:101B100009F4BACF68960FB6F894DEBF0FBECDBFFA +:101B2000DF91CF911F910F91FF90EF90DF90CF90B9 +:101B3000BF90AF909F908F907F906F905F904F90ED +:101B40003F902F900895F80180819181DC01ED9103 +:101B5000FC910684F785E02D6F8909958111D0CF1E +:101B600093CFDC01ED91FC910084F185E02D099586 +:101B7000811196CFF601929182918F01DC01ED9156 +:101B8000FC910088F189E02D688D099581110DC0C7 +:101B9000062D232D422D672DC7010E94E008982FA6 +:101BA000977F913D09F0B6CF7BCFF801808191817D +:101BB000DC01ED91FC910484F585E02DA401B201D6 +:101BC000099581116DCFF80180819181DC01ED9142 +:101BD000FC910684F785E02D6F8909958823C1F271 +:101BE0005FCF86ED97CF8F929F92AF92BF92CF92A9 +:101BF000EF920F931F921F921F921F921F921F929C +:101C0000912C812CB12CA12CC12CE22E09E020E0DA +:101C10000E943E0B0F900F900F900F900F900F901F +:101C20000F91EF90CF90BF90AF909F908F900895BD +:101C30008F929F92AF92BF92CF92DF92EF920F93CB +:101C40001F93CF93DF9300D000D0CDB7DEB76C01E8 +:101C5000590148011C821B821A8219828E010F5F72 +:101C60001F4F9E012D5F3F4F0E942008811109C028 +:101C700049815A816B817C8184019501C6010E9452 +:101C80007C0A0F900F900F900F90DF91CF911F91D2 +:101C90000F91EF90DF90CF90BF90AF909F908F907B +:101CA000089588EF0E94FC07807C8034D1F018F4FE +:101CB000882341F10895803819F0803C09F1089596 +:101CC00088ED0E94FC079091A60181FD11C0923021 +:101CD000F9F061EC88ED0E940D0882E08093A60186 +:101CE000089588ED0E94FC079091A60181FDEFCF39 +:101CF000933071F063EC88ED0E940D0883E0EECF25 +:101D000081E0ECCF61ED88ED0E940D081092A601F4 +:101D10000895AF92BF92CF92DF92EF92FF920F930E +:101D20001F93CF937C0183B1C82FC07285FD0DC076 +:101D300088EC0E94FC0785FF04C0C7010E94510E79 +:101D4000C0E26C2F88EC0E940D088091A6018130C2 +:101D500041F058F0823009F46BC0833069F0C0E084 +:101D60002AC083E18093AB01FACF8091AB01807FE1 +:101D70008031A9F381E1F6CFC1E08091AB01807F92 +:101D80008031C9F40E94D30568537F4F8F4F9F4F16 +:101D90006093A7017093A8018093A9019093AA0171 +:101DA00080E28093AB0180ED0E94FC07682F6F7D7D +:101DB00080ED0E940D08670186E5C80ED11C570111 +:101DC000A6E7AA0EB11C8601F801819191918F01BD +:101DD000009739F0DC01ED91FC910680F781E02D50 +:101DE00009950A151B0581F78091AB01803409F430 +:101DF00067C000F5803209F445C0803309F452C051 +:101E00008131B1F5F8011082D6018D919D916D015E +:101E1000009739F0DC01ED91FC910480F581E02D13 +:101E200009950C151D0581F782E18093AB0120C057 +:101E3000C0E0A3CF813509F456C0803809F466C0EC +:101E40008035B1F488EC0E94FC0786FF11C081E563 +:101E50008093AB010E94D3056C5E7F4F8F4F9F4FE5 +:101E60006093A7017093A8018093A9019093AA01A0 +:101E7000CF911F910F91FF90EF90DF90CF90BF9087 +:101E8000AF9008950E94D3050091A7011091A80179 +:101E90002091A9013091AA01601B710B820B930B59 +:101EA00097FDE6CF80ED0E94FC07682F606280ED11 +:101EB0000E940D0861E088EE0E940D0880E4B5CF15 +:101EC00088EE0E94FC0780FDD3CF60E488EC0E947E +:101ED0000D0888ED0E94FC07682F686088ED0E945D +:101EE0000D0880E5A2CF0E94D3050091A7011091B3 +:101EF000A8012091A9013091AA01601B710B820BEE +:101F0000930B97FDB5CF80E88093AB012C2F40E079 +:101F100060E0C7010E94EB0C882329F0823D09F4A0 +:101F2000A7CF80EA82CF80E980CF80E0089580E06B +:101F3000089581E00895FC018285089581E069306B +:101F400009F080E00895CF93DF93EC018A819B81B3 +:101F5000DC01ED91FC910480F581E02D6A85099505 +:101F60008A85813439F4EA81FB81EA58FF4F808108 +:101F70008B7F80831A861B861C861D861E861F8685 +:101F8000188A80E0DF91CF9108958F929F92AF924F +:101F9000BF92CF92EF920F93B22EA42E1F921F9258 +:101FA0001F921F921F921F92912C812CC12CE62E02 +:101FB00001E023E240E0FC016285828193810E947E +:101FC0003E0B0F900F900F900F900F900F900F916E +:101FD000EF90CF90BF90AF909F908F9008958F9289 +:101FE0009F92AF92BF92CF92EF920F93A62EB12CF9 +:101FF0001F921F923F932F935F934F934A01C12CDF +:10200000E12C00E023EA40E0FC01628582819381BB +:102010000E943E0B0F900F900F900F900F900F901B +:102020000F91EF90CF90BF90AF909F908F900895B9 +:102030008F929F92AF92BF92CF92EF920F93B22E58 +:10204000A42E1F921F921F921F921F921F92912CDB +:10205000812CC12CE62E03E023E240E0FC016285E6 +:10206000828193810E943E0B0F900F900F900F90F2 +:102070000F900F900F91EF90CF90BF90AF909F90E7 +:102080008F9008952F923F924F925F926F927F921E +:102090008F929F92AF92BF92CF92DF92EF92FF9278 +:1020A0000F931F93CF93DF93CDB7DEB7E8970FB6AB +:1020B000F894DEBF0FBECDBFDC011A963C9119ED3E +:1020C000311169C0122F242E362E3C01FC01C28032 +:1020D000D380D601ED91FC910190F081E02D60E07C +:1020E000C60109957C01009709F40BC1FC01A08091 +:1020F000B180A114B10409F406C14301F4E08F0ECC +:10210000911CDC0111969C928E9214961C93FE01F8 +:1021100031962F018F0128E030E040E060E0D301EC +:1021200012968D919C910E940E0C182FF70114822B +:102130008111EBC08D8111ED893069F50981D601DE +:10214000ED91FC910280F381E02D222D41E0632D81 +:10215000C6010995F301828714ED8823E1F0988583 +:102160009583282F40E060E0828193810E94840C57 +:10217000182FD701AD92BC92882339F1D601ED9189 +:10218000FC910480F581E02DD3011A966C91C60173 +:102190000995F3011286812FE8960FB6F894DEBFF9 +:1021A0000FBECDBFDF91CF911F910F91FF90EF90A8 +:1021B000DF90CF90BF90AF909F908F907F906F9067 +:1021C0005F904F903F902F900895011179C0940136 +:1021D00042E0F3016285828193810E949A07182F61 +:1021E0008111D9CF1F921F925F924F921F9288E068 +:1021F0008F9388E0882E912CB12CA12C99E2C92EC6 +:10220000E12C06E020EA40E0F301628582819381BF +:102210000E943E0B182F0F900F900F900F900F9071 +:102220000F908111B8CF8B81D3011B968C931B9794 +:10223000720100E028E030E040E01A966C911A97B5 +:1022400012968D919C910E94350C182F8111A3CF6D +:102250002B813C8140E0F3016285828193810E9461 +:10226000350C182F811197CF298130E0CE0181964E +:102270007C0140E0D3011A966C911A9712968D91C9 +:102280009C910E94350C182F811185CF2E8140E042 +:10229000F3016285828193810E94F30D182F8111D1 +:1022A0007ACF01E0D3011B968C911B978017E0F049 +:1022B00020E0402F68E0C3010E9418100F5FF2CFAA +:1022C000013208F000E2202F30E0820140E0D3012B +:1022D0001A966C911A9712968D919C910E940E0CF1 +:1022E000182F811158CF73CF1296ED91FC9113974F +:1022F000EA58FF4F80818460808381E050968C9300 +:102300004ACF16ED48CF17ED46CFB182A08243CF1A +:10231000CF92DF92FF920F931F93CF93DF9300D062 +:1023200000D0CDB7DEB78C01F62E19821A821B823F +:102330001C8220E0462F61E10E94C50F20E04F2D56 +:1023400060E1C8010E94C50F20E04F2D64E0C80184 +:102350000E94181083E0C82ED12C9E012F5F3F4FA2 +:1023600044E050E06F2DC8010E94EF0F811114C0AE +:1023700089819A81AB81BC819D7F83309140A0414E +:10238000B10551F064E670E080E090E00E94D50570 +:1023900081E0C81AD10809F720E04F2D64E1C80197 +:1023A0000E94C50F20E04F2D60E1C8010E94C50FBB +:1023B00064E170E080E090E00E94D5050F900F90FE +:1023C0000F900F90DF91CF911F910F91FF90DF90B1 +:1023D000CF900895FC0182E391E091838083738321 +:1023E0006283108A148288E085839CE3968391E05F +:1023F0009783808784E081871286138614861586EA +:102400001686178661157105B1F0DB01AA5ABF4F18 +:1024100090E080E02D913D91232B49F4DC019B96C7 +:10242000AA0FBB1FA60FB71FED93FC93089501964B +:102430008031910579F70895EF92FF921F93CF9322 +:10244000DF93EC01162F7A01FA0180819181A2813C +:10245000B3818130F3E09F07F1E0AF07B105D9F117 +:1024600070F48115E1E09E07AE07B10509F453C091 +:1024700081309140A140B10571F180E046C08330C8 +:1024800021E0920720E1A207B10529F083309340B3 +:10249000A041B10591F720E0412F64E1CE010E94F7 +:1024A000C50F20E0412F60E1CE010E94C50F64E11D +:1024B00070E080E090E00E94D5056A85F7012181F7 +:1024C000269521706770412F8A819B810E94EB0CB9 +:1024D0001092AC01D2CF8091AC018111CECF20E01F +:1024E000412F61E1CE010E94C50F20E0412F60E144 +:1024F000CE010E94C50F20E0412F64E0CE010E9472 +:10250000181081E08093AC0181EBDF91CF911F9196 +:10251000FF90EF90089520E0462F61E1CE010E94E8 +:10252000C50F20E0412F60E1CE010E94C50F10923F +:10253000AC016A8567701770660F660F660F612BB6 +:102540008A819B810E94B60798CFCF92DF92EF924B +:10255000FF920F931F93CF93DF93CDB7DEB72E97E4 +:102560000FB6F894DEBF0FBECDBF6C0181E090E0E6 +:102570009E878D87E12C8E010F5F1F4F9E01235F89 +:102580003F4F41E0F6016285828193810E94180EDF +:10259000082F811123C082E0F82E11E089818F215C +:1025A00069F119861A861B861C869E01275F3F4F9C +:1025B00044E050E0612FC6010E94EF0FE82E811128 +:1025C0001DC0AE01475F5F4F612FC6010E941C1204 +:1025D000813B19F0882391F0E82E0E2D802F2E9646 +:1025E0000FB6F894DEBF0FBECDBFDF91CF911F9124 +:1025F0000F91FF90EF90DF90CF900895FF0C1F5F39 +:10260000183061F611E0F6018385811738F31986D9 +:102610001A861B861C869E01275F3F4F44E050E0D0 +:10262000612FC6010E94EF0FF82E811117C0898516 +:102630009A85877091708130914081F48B859C855B +:1026400081609C878B87AE01475F5F4F612FC6011A +:102650000E941C12813B29F0811102C01F5FD3CF61 +:10266000F82E0F2DBBCF0F931F93CF93DF93FC0159 +:102670002089211107C010E0812FDF91CF911F9198 +:102680000F910895EC010E94D3050C851D852E85C0 +:102690003F85601B710B820B930B97FDECCFCE0136 +:1026A0000E94A512182F0E94D3056C597F4F8F4F9F +:1026B0009F4F6C877D878E879F87DECFFB010190C0 +:1026C0000020E9F73197AF01461B570BDC01ED9174 +:1026D000FC910280F381E02D0994EF92FF920F9319 +:1026E0001F93CF93DF938C017B01D0E0C0E0F70113 +:1026F000EC0FFD1F6491662361F0D801ED91FC9110 +:102700000190F081E02DC8010995892B11F02196E7 +:10271000EECFCE01DF91CF911F910F91FF90EF90FF +:1027200008956115710511F00C945E1390E080E03E +:102730000895DC01ED91FC910190F081E02D099468 +:1027400064E471E00C945E130F931F93CF93DF93B7 +:10275000EC010E946D138C01CE010E94A013800F2A +:10276000911FDF91CF911F910F9108958F929F92AA +:10277000AF92BF92EF92FF920F931F93CF93DF938D +:10278000CDB7DEB7A1970FB6F894DEBF0FBECDBFB1 +:102790007C01FA01CB0119A2223008F42AE08E0153 +:1027A0000F5D1F4F822E912CB12CA12CBF01A501D2 +:1027B00094010E94D724F901CA016A3008F5605DCE +:1027C000D8016E938D01232B242B252B79F790E0D4 +:1027D00080E0109721F0BD01C7010E945E13A19611 +:1027E0000FB6F894DEBF0FBECDBFDF91CF911F9122 +:1027F0000F91FF90EF90BF90AF909F908F900895B2 +:10280000695CDECFCF92DF92EF92FF920F931F931E +:10281000CF93DF932115310581F4DC01ED91FC911B +:102820000190F081E02D642FDF91CF911F910F91E6 +:10283000FF90EF90DF90CF9009942A30310501F599 +:102840002AE077FF1DC06A017B01EC016DE20E9466 +:1028500099138C0144275527BA014C195D096E095B +:102860007F092AE0CE010E94B613800F911FDF91ED +:10287000CF911F910F91FF90EF90DF90CF9008952F +:10288000DF91CF911F910F91FF90EF90DF90CF904C +:102890000C94B6139A01AB01770F660B770B0C946F +:1028A00002142115310541F4DC01ED91FC910190F8 +:1028B000F081E02D642F09940C94B6139A01462FF1 +:1028C00050E070E060E00C945114CF93C82F803139 +:1028D00030F46EE278E082E591E00E946D1340E111 +:1028E00050E06C2F82E591E0CF910C945E14CF9272 +:1028F000DF92EF92FF920F931F93CF93DF936B01C1 +:1029000079018A01D0E0C0E0CC15DD05F1F0F701D6 +:10291000808191818C0F9D1F9C012F703327232B69 +:1029200069F067E471E082E591E00E949113F8019B +:1029300081918F010E9465142196E6CF892BB9F30E +:1029400082E591E00E94A013F2CFDF91CF911F9119 +:102950000F91FF90EF90DF90CF900895CF93C82F05 +:10296000892F0E9465148C2FCF910C946514CF93FE +:10297000C62FBC0182E591E00E946D138C2F0E944E +:10298000651482E591E0CF910C94A013CF93DF936F +:10299000EB01BC0182E591E00E946D13CE010E9423 +:1029A000AE1482E591E0DF91CF910C94A013682FD3 +:1029B00086E298E00C94B714DF92EF92FF920F93A7 +:1029C0001F93CF93DF937C01D42E8B01D0E0C0E026 +:1029D000CE15DF050CF5CE018F709927892B69F094 +:1029E00067E471E082E591E00E949113F801819122 +:1029F0008F010E9465142196EBCF209721F082E58C +:102A000091E00E94A013DD2089F362E278E082E584 +:102A100091E00E946D13EACF82E591E0DF91CF91C2 +:102A20001F910F91FF90EF90DF900C94A013CF9324 +:102A3000DF93EC0168E078E082E591E00E946D139D +:102A400089810E946514688181E098E00E94B71432 +:102A5000888140E0BE0190E0DF91CF910C94DC14BE +:102A6000CF93DF93EC01688186EF97E00E94B71463 +:102A7000698184EE97E00E94B7146A817B8189EDB9 +:102A800097E00E94C6146C8189EC97E00E94B7140D +:102A90006D8187EB97E00E94B7146E8185EA97E01D +:102AA0000E94B7146F8183E997E00E94B71468858C +:102AB000798587E897E00E94C6146A857B858AE756 +:102AC00097E00E94C6146C857D858DE697E00E9494 +:102AD000C6146E858CE597E00E94B7146F8580E57B +:102AE00097E00E94B71468898FE397E00E94B714BB +:102AF00069898AE297E0DF91CF910C94B714CF9364 +:102B0000DF93EC0168818FE197E00E94B71469813F +:102B10008DE097E00E94B7146A817B818DEF96E08B +:102B20000E94C6146C818BEE96E00E94B7146D81F2 +:102B300085ED96E00E94B7146E8183EC96E00E94CA +:102B4000B7146F8183EB96E00E94B714688586EA1C +:102B500096E0DF91CF910C94B714CF93DF93EC0103 +:102B600068818BE996E00E94B714698189E896E054 +:102B70000E94B7146A8186E796E00E94B7146B81C1 +:102B800086E696E00E94B7146C817D8184E596E02C +:102B90000E94C6146E8187E496E0DF91CF910C9479 +:102BA000B714CF93DF93EC0168818CE396E00E9429 +:102BB000B71469818AE296E00E94B7146A8187E1BE +:102BC00096E00E94B7146B8183E096E00E94B714F0 +:102BD0006C8182EF95E00E94B7146D8180EE95E0E4 +:102BE0000E94B7146E818BEC95E00E94B7146F8140 +:102BF00086EB95E00E94B714688588EA95E0DF913E +:102C0000CF910C94B7140F931F93CF93DF93EC01E4 +:102C100068818DE995E00E94B71469818BE895E0A1 +:102C20000E94B7146A817B8180E895E00E94C614F7 +:102C30006C8180E795E00E94B7146D818EE595E088 +:102C40000E94B7148E010A5F1F4F2881C8018C1B98 +:102C50009D0B2817190689F084F0F801608180E542 +:102C600095E00E94B714F801618170E08CE395E073 +:102C70000E94C6140D5F1F4FE8CFDF91CF911F91C7 +:102C80000F910895FF920F931F93CF93DF93EC0161 +:102C900008811981FA806FE175E082E591E00E9478 +:102CA0006D1381E0F81230C067E175E082E591E0D4 +:102CB0000E94A413602F8CE095E00E94B714612F4E +:102CC0008AEF94E00E94B71461E085EE94E00E94E0 +:102CD000B7146B817C818AED94E00E94C6146D81EB +:102CE0008DEC94E00E94B7146E817F818DEB94E0AF +:102CF0000E94C61468858FEA94E0DF91CF911F91FE +:102D00000F91FF900C94B714CE01DF91CF911F91DA +:102D10000F91FF900C941715FF920F931F93CF9371 +:102D2000DF93EC0108811981FA8061E974E082E5A2 +:102D300091E00E946D1382E0F81669F183E0F816C5 +:102D400009F452C081E0F81281C069E874E082E5BC +:102D500091E00E94A413602F8EE794E00E94B714C4 +:102D6000612F8CE694E00E94B71461E087E594E05F +:102D70000E94B7146B817C818CE494E00E94C6149D +:102D80006D817E818CE394E0DF91CF911F910F9153 +:102D9000FF900C94C61463E374E082E591E00E9416 +:102DA000A413602F88E294E00E94B714612F86E19B +:102DB00094E00E94B71462E081E094E00E94B714AE +:102DC0006B8184EF93E00E94B7146C8189EE93E0ED +:102DD0000E94B7146D818FED93E0DF91CF911F9129 +:102DE0000F91FF900C94B71465ED73E082E591E0CC +:102DF0000E94A413602F8AEC93E00E94B714612F05 +:102E000088EB93E00E94B71463E083EA93E00E94AA +:102E1000B7146B8186E993E00E94B7146C818BE84C +:102E200093E00E94B7146D818BE793E00E94B71482 +:102E30006E818DE693E00E94B7146F818EE593E07A +:102E40000E94B714688584E593E0C7CFCE01DF9177 +:102E5000CF911F910F91FF900C941715FF920F9334 +:102E60001F93CF93DF93EC0108811981FA8066E309 +:102E700073E082E591E00E946D1381E0F81224C0B6 +:102E80006DE273E082E591E00E94A413602F82E27C +:102E900093E00E94B714612F80E193E00E94B71481 +:102EA00061E08BEF92E00E94B7146B8189EE92E0B3 +:102EB0000E94B7146C8188ED92E0DF91CF911F9151 +:102EC0000F91FF900C94B714CE01DF91CF911F9119 +:102ED0000F91FF900C9417158F929F92AF92BF9213 +:102EE000CF92EF920F931F93CF93DF93CDB7DEB7BF +:102EF000C054D1090FB6F894DEBF0FBECDBF1F93EB +:102F00000F93FE013196FF93EF931F9250E45F936E +:102F100049015B0122E2C22EE42E06E021E840E0F6 +:102F2000FC0162818BED93E00E943E0B182F82E53D +:102F300091E00E94A0130F900F900F900F900F90B0 +:102F40000F90112319F0812F0E94D714812FC05C9C +:102F5000DF4F0FB6F894DEBF0FBECDBFDF91CF912C +:102F60001F910F91EF90CF90BF90AF909F908F9057 +:102F700008952E988EBD0DB407FEFDCF1EBC0DB476 +:102F800007FEFDCF0E94FA078EB508952E98826045 +:102F90008EBD0DB407FEFDCF6EBD0DB407FEFDCF97 +:102FA0000C94FA070F931F93CF93DF938C01269A0B +:102FB0000E94FA07219A229A2398209A80E58CBDD4 +:102FC00081E08DBD259868E188E80E94C61760E21F +:102FD00088E70E94C61760E088E70E94C617D0E025 +:102FE000C0E088E60E94B917219680FF22C061ECFC +:102FF00088ED0E94C61760E280ED0E94C61764E06B +:1030000088EE0E94C61788EE0E94B91782FFFBCF98 +:10301000C8010E94510E60E288EC0E94C61761E070 +:1030200080E80E94C61780E0DF91CF911F910F9139 +:103030000895CF3FDC07A9F68FEFF6CF40E052ECC2 +:1030400061E070E082E591E00E94BB0582E591E0DD +:103050000E94BC058823D1F36BEC70E082E591E01F +:103060000E94A4138BED93E00E94D2178F3F31F49E +:1030700068EB70E082E591E00E94A41368EC70E0D8 +:1030800080E090E00C94D5058BED93E00E944B0717 +:103090006BED73E08AEC93E00E94EA116BED73E054 +:1030A00089EB93E00C94EA113F924F925F926F92FA +:1030B0007F928F929F92AF92BF92CF92DF92EF92C8 +:1030C000FF920F931F93CF93DF93CDB7DEB7DA95BF +:1030D0000FB6F894DEBF0FBECDBF662311F1162FD9 +:1030E0004C0180EBA82E81E0B82E96EB492E91E0A2 +:1030F000592EF501E080F180E114F10491F09E0178 +:103100002F5F3F4F6901012F21E030E040E0F401E3 +:1031100062818BED93E00E945C0C8823D1F00E94C9 +:10312000D714D3950FB6F894DEBF0FBECDBFDF9195 +:10313000CF911F910F91FF90EF90DF90CF90BF90B4 +:10314000AF909F908F907F906F905F904F903F9047 +:1031500008953980F501E080F180632C712C9E0187 +:103160002F5F3F4F6901930140E0F40162818BEDD5 +:1031700093E00E945C0C8111D2CF61ED72E082E598 +:1031800091E00E946D134AE050E0612F82E591E0EA +:103190000E945E146FEC72E082E591E00E946D1374 +:1031A000F501808191810E94AE146BEC72E082E5A2 +:1031B00091E00E946D13632D85EC92E00E94B7149C +:1031C000898141E0BE016F5F7F4F90E00E94DC1477 +:1031D00061EC72E082E591E00E946D1302E0802FC5 +:1031E00090E09C012F5F3F4F261537057CF541E0AD +:1031F00050E04C0F5D1F240F351FF9012081222361 +:1032000059F06EE282E591E00E949B056EE282E554 +:1032100091E00E949B0516C021E030E02C0F3D1F7D +:10322000820F931FFC01608180EE860F8F3560F462 +:1032300082E591E00E949B05F501808191818930B2 +:10324000944021F70E5FCBCF6EE2F2CF82E591E0A2 +:103250000E94A01322E0A20EB11C4A145B0409F0E4 +:1032600048CF5FCF2F923F924F925F926F927F92A3 +:103270008F929F92AF92BF92CF92DF92EF92FF9286 +:103280000F931F93CF93DF9300D000D0CDB7DEB75D +:103290004C015C834B83312C512C412C212C9B0104 +:1032A00027543E4F3A8329838B819C81E981FA819F +:1032B0008E179F0708F0E0C0FC018181853009F47A +:1032C00065C058F4823009F44FC0843009F469C0F5 +:1032D0008B819C810E94171551C0843209F4A8C0CB +:1032E000853209F4BCC0813299F765E772E082E566 +:1032F00091E00E94A4138B819C810E940316CB80D5 +:10330000DC80F12CE12C622C712CEB81FC8185811D +:103310008E151F0499F194F1F6018681823209F02D +:103320007FC0A780B12C69E672E082E591E00E943F +:103330006D134AE050E0B70182E591E00E944A1423 +:10334000B5018CE592E00E94C61400E011E0950101 +:103350004E2DB301C4010E946C17FFEFEF1AFF0A54 +:1033600023E0C20ED11CD1CF64EB72E082E591E084 +:103370000E94A4138B819C810E947F15EB81FC81AC +:103380008081E80FF11DFC83EB838ECF65EA72E04C +:1033900082E591E00E94A4138B819C810E94AD156F +:1033A000EDCFEB81FC81228045805680378066E935 +:1033B00072E082E591E00E946D134AE050E0622DD8 +:1033C00082E591E00E945E1464E972E082E591E09A +:1033D0000E946D134AE050E0EB81FC81638182E53D +:1033E00091E00E945E1462E972E082E591E00E9441 +:1033F000A4138B819C810E94D115EB81FC81808577 +:10340000882309F4BBCF60E872E082E591E00E9476 +:103410006D13EB81FC816085C4010E945418AECF0E +:10342000EB81FC8166818DE392E00E94B71495CF19 +:1034300031E043124DCF531208C0311049CF8B8178 +:103440009C810E944216452C99CF83E0581240CFB0 +:1034500031103ECF8B819C810E948C168FCF91E0E2 +:10346000491236CFE3E05E1233CF311031CF8B817A +:103470009C810E942E1782CF0F900F900F900F907B +:10348000DF91CF911F910F91FF90EF90DF90CF9040 +:10349000BF90AF909F908F907F906F905F904F9074 +:1034A0003F902F900895AF92BF92CF92DF92EF920C +:1034B000FF920F93CF93DF936C01D62FC0E0CD170F +:1034C00009F478C061E372E082E591E00E946D1337 +:1034D0004AE050E06C2F82E591E00E945E1469E2C0 +:1034E00072E082E591E00E946D1329EBE22E21E06B +:1034F000F22E0C2F29E030E040E0F60162818BEDE6 +:1035000093E00E94350C811156C06091BB0170910F +:10351000BC0183E292E00E94C614A090BB01B0906F +:10352000BC01F1E0AF16F2E0BF0618F0A12C92E06A +:10353000B92E89EBE82E81E0F82E0C2F950140E0A2 +:10354000F60162818BED93E00E94350C811133C04E +:1035500040E0B701C5010E94DC148091BB0190914D +:10356000BC018130924030F06DE072E082E591E084 +:103570000E94A4138091BF01882359F067EF71E086 +:1035800082E591E00E946D136091BF01C6010E9427 +:1035900054182091BE0140E0F60162818BED93E06A +:1035A0000E94F30D49EB51E0B501C6010E943219AA +:1035B000CF5F85CF80E0DF91CF910F91FF90EF90AB +:1035C000DF90CF90BF90AF9008957F928F929F929F +:1035D000AF92BF92CF92DF92EF92FF920F931F9321 +:1035E000CF93DF93CDB7DEB728970FB6F894DEBF41 +:1035F0000FBECDBF5C011092B1011092B0011092CC +:10360000B3011092B2011092B5011092B401DC0125 +:1036100012968C9162EC71E086FD9BC082E591E090 +:103620000E94A01306E410E06FE282E591E00E94A0 +:103630009B0501501109C1F76FEA71E082E591E045 +:103640000E94A413F501628182EA91E00E94B714FE +:10365000D50114966C9184E991E00E94B71409EBAE +:1036600011E022E130E040E0F50162818BED93E072 +:103670000E940E0C882319F00E94D7145BC0709032 +:10368000CA011092B6018091C0018093B7018CE30A +:103690008093B801E091DB03F091DC030190F081AD +:1036A000E02DD50112966C918BED93E0099526EBF8 +:1036B00031E0FC013183208361E871E082E591E033 +:1036C0000E94A41340E069EB71E082E190E00E9467 +:1036D000DC1464E771E082E591E00E94A41389EBB9 +:1036E00091E00E9430158091C701811109C080913D +:1036F000C801811105C08091C901882309F4ABC0BC +:10370000CE0101966C01F12CE12C00E024E030E0C8 +:1037100040E0D50112966C918BED93E00E945C0C19 +:10372000882379F10E94D714672DC5010E94531A8E +:103730008111A2CFE091DB03F091DC030480F581DD +:10374000E02DD50112966C918BED93E0099561ED1A +:1037500070E082E591E00E94A41328960FB6F894D9 +:10376000DEBF0FBECDBFDF91CF911F910F91FF90B4 +:10377000EF90DF90CF90BF90AF909F908F907F9011 +:1037800008951981193008F018E0812E912CFE015E +:1037900031966F01F12CE12C00E0940140E0D5015D +:1037A00012966C918BED93E00E945C0C8111BACF64 +:1037B00061E671E082E591E00E946D13612F8BE577 +:1037C00091E00E94B714898141E0BE016F5F7F4F95 +:1037D00090E00E94DC146FE471E082E591E00E94C9 +:1037E0006D137401B2E0EB1AF108F7FE04C0740126 +:1037F000E1E0EE1AF108F594E7948E010F5F1F4F98 +:1038000090EB892E91E0992ED12CC12CCE14DF049F +:10381000F4F4C114D10431F06CE471E082E591E07C +:103820000E946D13FFEFCF1ADF0AD80113968C9117 +:10383000139712969C91892798278927F4018193E1 +:1038400091934F010E94AE140E5F1F4FDFCF82E5B0 +:1038500091E00E94A0138091C701882359F067E38B +:1038600071E082E591E00E946D136091C701C5018E +:103870000E9454188091C801882359F067E271E0D2 +:1038800082E591E00E946D136091C801C5010E941C +:1038900054188091C901882309F446CF62E171E090 +:1038A00082E591E00E946D136091C901C5010E94FB +:1038B00054183ACFAF92BF92CF92DF92EF92FF921D +:1038C0000F931F93CF93DF938BED93E00E94890EAC +:1038D0008BED93E00E947B079091AF01891779F0FF +:1038E0008BED93E00E947B078093AF018BED93E01B +:1038F0000E947B07682F8CEA90E00E94B7148BED42 +:1039000093E00E947B07803909F04CC0E091DB0313 +:10391000F091DC030190F081E02D60E08BED93E00D +:1039200009956C0110E000E0EE24E394F12CC1E075 +:10393000D0E0F6018281811102C0D0E0C0E05E01DA +:10394000002E02C0AA0CBB1C0A94E2F7E701002E6D +:1039500002C0CC0FDD1F0A94E2F78091AD01909177 +:10396000AE018C239D238A159B05A1F0A114B104FF +:1039700019F0C6010E94E51AC095D0958091AD015D +:103980009091AE01C823D923CA29DB29D093AE0177 +:10399000C093AD010F5F1F4FF5E0CF0ED11C00317A +:1039A000110529F6DF91CF911F910F91FF90EF90B4 +:1039B000DF90CF90BF90AF90089508950895CF9372 +:1039C000C0915404C1110BC00E943906643C794077 +:1039D0008105910520F081E080935404C1E08C2F93 +:1039E000CF910895FF920F931F93CF93DF93D82F1A +:1039F0000FB607FE46C080915C05843079F0809157 +:103A0000050190E001962091070130E08223932385 +:103A1000209106012817190609F0D0C00E94DF1C6A +:103A200081110FC08091050190E00196209107015E +:103A300030E082239323209106012817190609F00C +:103A4000BDC00E942D068C01C091E900CF7080910D +:103A5000EC0080FD37C080E0C82B81E08093E90056 +:103A600085E0F82E8091EB0085FD08C08091EB0089 +:103A700080FF04C08091EE0087FD44C0CF70C093EA +:103A8000E900E09105018E2F90E001962091070159 +:103A900030E082239323209106012817190609F4A8 +:103AA00086C08091030190910401E80FF92FF11D68 +:103AB000D083809105018F5F9091070189238093C6 +:103AC000050175C080E8C8CFE0910301F0910401C1 +:103AD000E80FF11D90818F5F209107018223809371 +:103AE00006019093F1008091E80085FD0BC0809164 +:103AF000E80080FF07C08091E8008E778093E8009F +:103B0000F09202018091060190910501981759F1F8 +:103B10009091E80095FDD8CF80E02CC01092F10084 +:103B200035C08091050190E001962091070130E0B9 +:103B300082239323209106012817190609F09ECFAE +:103B400080910201882309F499CF0E942D069C01DF +:103B50008017910709F486CF9091020191509093BC +:103B6000020189017FCF8091E80085FFD5CFD093F6 +:103B7000F10081E02091F3009091F200322F292F83 +:103B8000232B79F09091E80090FF0BC09091E80012 +:103B900095FDC4CF9091E8009E779093E800F09255 +:103BA0000201882309F4BDCFCF70C093E90080E003 +:103BB000DF91CF911F910F91FF900895E091050142 +:103BC00070CF089580915B05811104C00E949521FA +:103BD0000C94F221089510925B050895089542E235 +:103BE00061EC81E00C94281F089508950C94F51D54 +:103BF0000E942A2380915A05882329F00E9453238A +:103C000081110C94121F08950C9455230C94041EDA +:103C10000895CF9384B7877F84BF0FB6F894A89593 +:103C200080916000886180936000109260000FBEF8 +:103C300080E890E00FB6F894809361009093610063 +:103C40000FBE82EF9CE10E94252388E091E00E9454 +:103C5000202380E598E00E94EA040E948C050E94DF +:103C600095210E94F22178940E94240680915C059F +:103C7000843009F043C08EE398E00E94EA040E9479 +:103C80008E0580E398E00E94EA04C1E080915C0523 +:103C90008530B9F10E948F052091E4003091E50054 +:103CA00080915204909153042817390779F3809139 +:103CB000E4009091E50090935304809352040E9495 +:103CC000DF1C882319F380915C058430F9F690910C +:103CD000E9009F708091EC0080FD16C080E0892B88 +:103CE000C093E9009091EB0090FF04C09091EE002A +:103CF00097FD28C08F708093E900C8CF0E94081EEE +:103D0000B5CF0E94F81DC2CF80E8E9CFE091030152 +:103D1000F0910401E90FF11D20819F5F30910701AF +:103D20009323909306012093F1009091E80095FD74 +:103D300009C09091E80090FF05C09091E8009E773F +:103D40009093E8009091060120910501291721F038 +:103D50002091E80025FDDACF2091F3009091F20048 +:103D6000322F292F232B31F29091E80090FFC2CF00 +:103D70009091E80095FD06C09091E8009E779093A1 +:103D8000E800B8CF1092F100F3CFE92FFF27E3301E +:103D9000F10521F150F4E130F10591F03297C9F0CD +:103DA000F0E0E0E090E080E00FC0E132F10591F159 +:103DB000E232F105A9F78CEE98E0672B89F704C091 +:103DC000E2E1F0E08AED98E0DA018D939C93CF0177 +:103DD0000895E9E2F0E081EB98E0F6CF9927813091 +:103DE000910589F048F00297D9F6E5E7F8E0E4910B +:103DF000F0E085E798E0E8CFEDEAF8E0E491F0E064 +:103E00008DEA98E0E1CFE3EAF8E0E491F0E083EABC +:103E100098E0DACFE9E0F0E083EC98E0672B09F076 +:103E2000BFCFD2CF80E189BD82E189BD09B400FE58 +:103E3000FDCF8091D8008F7D8093D8008091E000E5 +:103E400082608093E0008091E00081FDFCCF0895C6 +:103E5000982F973028F08F708093E90081E00895C3 +:103E60009093E900242F762F50E0981731F070914D +:103E7000EC002091ED005091F00021FD02C09F5F09 +:103E8000E8CF3091EB003E7F3093EB003091ED00B6 +:103E90003D7F3093ED003091EB0031603093EB00CB +:103EA0007093EC002093ED005093F0002091EE0011 +:103EB00027FDE5CF80E0089580915D0587FF13C061 +:103EC0008091E80082FF06C08091E8008B778093A4 +:103ED000E80004C080915C058111F2CF08958091C3 +:103EE0005C058823D9F38091E80080FFF8CF8091AA +:103EF000E8008E77ECCFFC0180916305909164051A +:103F000086179707A0F06115710529F49091E800D4 +:103F10009E779093E80090E06115710551F491113E +:103F200008C08091E80082FF35C080E00895BC01A0 +:103F3000F2CF80915C058823C9F18530C9F1809169 +:103F4000E80083FD31C08091E80082FDEACF8091D6 +:103F5000E80080FFE1CF2091F3008091F200322F42 +:103F6000282F6115710519F02830310558F091E0BE +:103F70002830310509F090E02091E8002E77209359 +:103F8000E800CACF81918093F100615071092F5FE1 +:103F90003F4FE7CF80915C05882341F0853041F0A9 +:103FA0008091E80083FFBDCF81E0089582E008950D +:103FB00083E00895FC018091630590916405861764 +:103FC0009707A0F06115710529F48091E8008E77BC +:103FD0008093E80090E06115710551F4911108C0DB +:103FE0008091E80082FF36C080E00895BC01F2CFE6 +:103FF00080915C058823D1F18530D1F18091E80072 +:1040000083FD32C08091E80082FDEACF8091E80014 +:1040100080FFE1CF2091F3008091F200322F282F12 +:104020006115710519F02830310558F091E02830FC +:10403000310509F090E08091E8008E778093E800E8 +:10404000CACF84918093F1003196615071092F5F3E +:104050003F4FE6CF80915C05882341F0853041F0E9 +:104060008091E80083FFBCCF81E0089582E008954D +:1040700083E008951F93CF93DF93CDB7DEB7AA9760 +:104080000FB6F894DEBF0FBECDBFEDE5F5E0809131 +:10409000F100819325E0E536F207C9F70E94F41D8F +:1040A0008091E80083FF44C080915D0590915E059A +:1040B0009A3008F03DC0E92FF0E0EF59FF4D0C9425 +:1040C000F9246B209120982091209820EF20112135 +:1040D000982068217521803889F0823849F58091CF +:1040E00061058F70873020F58093E9008091EB00A7 +:1040F00085FB882780F91092E90006C08091590558 +:1041000090915A05911182609091E800977F909369 +:10411000E8008093F1001092F1008091E8008E7722 +:104120002DC0282F2D7F21F48823D1F0823059F122 +:104130008091E80083FF0AC08091E800877F809328 +:10414000E8008091EB0080628093EB00AA960FB6A6 +:10415000F894DEBF0FBECDBFDF91CF911F910895C0 +:1041600080915F05813021F7933009F080E08093E2 +:104170005A051092E9008091E800877F8093E8005B +:104180000E945C1FD5CF80915F058111F2CF809195 +:1041900061058F702FEF280F263050F68093E900CD +:1041A0002091EB0020FFE5CF933031F48091EB00BC +:1041B00080628093EB00DDCF9091EB009061909353 +:1041C000EB0021E030E001C0220F8A95EAF720934E +:1041D000EA001092EA008091EB008860EACF81113A +:1041E000A7CF10915F051F778091E3008078812B26 +:1041F0008093E3008091E800877F8093E8000E942D +:104200005C1F8091E80080FFFCCF8091E300806814 +:104210008093E30083E0111101C082E080935C058C +:1042200087CF8058823008F083CF80915F059091CE +:1042300060058C3D23E09207A1F583E08A838AE242 +:1042400089834FB7F894DE01139620E03EE051E2F7 +:10425000E32FF0E050935700E49120FF03C0E29574 +:10426000EF703F5FEF708E2F90E0EA30C0F0C7969E +:104270008D939D932F5F243159F74FBF8091E800B4 +:10428000877F8093E8006AE270E0CE0101960E9489 +:104290007B1F8091E8008B778093E80049CFC09620 +:1042A000E7CF6091610570916205AE014F5F5F4F8E +:1042B0000E94C51EBC01892B09F43ACF9091E800F9 +:1042C000977F9093E80089819A810E94DA1FE1CF5D +:1042D000803809F02DCF8091E800877F8093E80037 +:1042E000809158058093F10018CF811121CF9091D2 +:1042F0005F05923008F01CCF8091E800877F8093A3 +:10430000E800909358050E945C1F8091580588230F +:1043100031F084E080935C050E94EF1D09CF80910D +:10432000E30087FDF6CF81E0F5CF0E9403220E94D3 +:104330000B22E0EEF0E0808181608083E8EDF0E028 +:1043400080818F77808319BCA7EDB0E08C918E7F40 +:104350008C9380818F7E808310925B0508950F93EC +:104360001F93CF93DF930E9403220E940B22C8ED7C +:10437000D0E088818F77888388818068888388816E +:104380008F7D888319BC10925C051092580510929D +:104390005A051092590500EE10E0F80180818B7FDC +:1043A000808388818160888342E060E080E00E94B1 +:1043B000281FE1EEF0E080818E7F8083E2EEF0E066 +:1043C000808181608083808188608083F801808122 +:1043D0008E7F8083888180618883DF91CF911F9158 +:1043E0000F910895E8EDF0E080818F7E8083E7ED06 +:1043F000F0E080818160808384E082BF81E08093EF +:104400005B050C94AF21E8EDF0E080818E7F808326 +:104410001092E20008951092DA001092E1000895DF +:104420001F920F920FB60F9211242F933F934F9329 +:104430005F936F937F938F939F93AF93BF93EF930C +:10444000FF938091E10082FF0BC08091E20082FF28 +:1044500007C08091E1008B7F8093E1000E941F23C1 +:104460008091DA0080FF19C08091D80080FF15C0CC +:104470008091DA008E7F8093DA008091D90080FFEE +:1044800094C080E189BD82E189BD09B400FEFDCF01 +:1044900081E080935C050E94E21D8091E10080FF35 +:1044A00019C08091E20080FF15C08091E2008E7FEC +:1044B0008093E2008091E20080618093E20080912D +:1044C000D80080628093D80019BC85E080935C0599 +:1044D0000E94F61D8091E10084FF29C08091E200D6 +:1044E00084FF25C080E189BD82E189BD09B400FE59 +:1044F000FDCF8091D8008F7D8093D8008091E1001E +:104500008F7E8093E1008091E2008F7E8093E200B5 +:104510008091E20081608093E200809158058823B9 +:1045200009F449C084E080935C050E94061E8091D6 +:10453000E10083FF29C08091E20083FF25C08091C4 +:10454000E100877F8093E10082E080935C05109218 +:1045500058058091E1008E7F8093E1008091E20018 +:104560008E7F8093E2008091E20080618093E20080 +:1045700042E060E080E00E94281F8091F0008860A7 +:104580008093F0000E94EE1DFF91EF91BF91AF91DB +:104590009F918F917F916F915F914F913F912F915B +:1045A0000F900FBE0F901F90189519BC10925C05CC +:1045B0000E94EB1D72CF8091E30087FD02C081E075 +:1045C000B2CF83E0B0CF1F920F920FB60F9211249B +:1045D0002F933F934F935F936F937F938F939F930B +:1045E000AF93BF93CF93EF93FF93C091E900CF7048 +:1045F0008091EC001092E9008091F000877F809319 +:10460000F00078940E943A201092E9008091F00026 +:1046100088608093F000C093E900FF91EF91CF9103 +:10462000BF91AF919F918F917F916F915F914F91CA +:104630003F912F910F900FBE0F901F9018950895E6 +:10464000909356058093550508959093510180935A +:104650005001089580915C05843021F11092570536 +:1046600088E190E020E40FB6F894A895809360006C +:104670000FBE2093600083B7817F846083BF83B7C0 +:10468000816083BF7894889583B78E7F83BF0FB690 +:10469000F894A895809160008861809360001092E2 +:1046A00060000FBE089580E0089508951F920F9254 +:1046B0000FB60F9211248F939F93AF93BF93809166 +:1046C0005705811113C08091A2019091A301A0917F +:1046D000A401B091A5014196A11DB11D8093A20135 +:1046E0009093A301A093A401B093A501BF91AF91B2 +:1046F0009F918F910F900FBE0F901F9018950E9461 +:10470000F02308F481E008950E94442488F09F5724 +:1047100098F0B92F9927B751B0F0E1F0660F771FE5 +:10472000881F991F1AF0BA95C9F714C0B13091F0DB +:104730000E945E24B1E008950C945E24672F782FC8 +:104740008827B85F39F0B93FCCF3869577956795A0 +:10475000B395D9F73EF490958095709561957F4F0C +:104760008F4F9F4F0895E89409C097FB3EF49095B2 +:104770008095709561957F4F8F4F9F4F9923A9F03A +:10478000F92F96E9BB279395F695879577956795C9 +:10479000B795F111F8CFFAF4BB0F11F460FF1BC00D +:1047A0006F5F7F4F8F4F9F4F16C0882311F096E9A0 +:1047B00011C0772321F09EE8872F762F05C066234E +:1047C00071F096E8862F70E060E02AF09A95660F07 +:1047D000771F881FDAF7880F9695879597F90895C0 +:1047E000990F0008550FAA0BE0E8FEEF1616170602 +:1047F000E807F907C0F012161306E407F50798F06A +:10480000621B730B840B950B39F40A2661F0232B82 +:10481000242B252B21F408950A2609F4A140A695FE +:104820008FEF811D811D089597F99F6780E870E0E3 +:1048300060E008959FEF80EC089500240A94161616 +:10484000170618060906089500240A94121613067E +:10485000140605060895092E0394000C11F488230C +:1048600052F0BB0F40F4BF2B11F460FF04C06F5F28 +:104870007F4F8F4F9F4F089557FD9058440F551FFE +:1048800059F05F3F71F04795880F97FB991F61F0D2 +:104890009F3F79F087950895121613061406551F49 +:1048A000F2CF4695F1DF08C0161617061806991FB5 +:1048B000F1CF86957105610508940895E894BB27AA +:1048C00066277727CB0197F908950E94F02308F413 +:1048D0008FEF08950E947D240C942B240E941D24A8 +:1048E00038F00E94242420F0952311F00C94142415 +:1048F0000C941A2411240C945F240E943C2470F31D +:10490000959FC1F3950F50E0551F629FF001729F74 +:10491000BB27F00DB11D639FAA27F00DB11DAA1F83 +:10492000649F6627B00DA11D661F829F2227B00DD0 +:10493000A11D621F739FB00DA11D621F839FA00D5B +:10494000611D221F749F3327A00D611D231F849FAB +:10495000600D211D822F762F6A2F11249F57504002 +:104960009AF0F1F088234AF0EE0FFF1FBB1F661F7D +:10497000771F881F91505040A9F79E3F510580F046 +:104980000C9414240C945F245F3FE4F3983ED4F31A +:10499000869577956795B795F795E7959F5FC1F7EA +:1049A000FE2B880F911D9695879597F90895A1E2A2 +:1049B0001A2EAA1BBB1BFD010DC0AA1FBB1FEE1F99 +:1049C000FF1FA217B307E407F50720F0A21BB30BE4 +:1049D000E40BF50B661F771F881F991F1A9469F760 +:1049E00060957095809590959B01AC01BD01CF01BC +:1049F0000895EE0FFF1F0590F491E02D0994F894AF +:024A0000FFCFE6 +:104A02004D010555040000FF00000000DD1CDE1C06 +:104A1200E11D000000009B05A10598059105920586 +:104A2200950595050000000012076B062907000096 +:104A320000004210950FA30F33139B0F8811970F9D +:104A42009E0F990F0D0A002000000000007714004D +:00000001FF diff --git a/converter/usb_desc_dump/config.h b/converter/usb_desc_dump/config.h new file mode 100644 index 00000000..80de3a28 --- /dev/null +++ b/converter/usb_desc_dump/config.h @@ -0,0 +1,52 @@ +/* +Copyright 2012 Jun Wako + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#ifndef CONFIG_H +#define CONFIG_H + + +#define VENDOR_ID 0xFEED +#define PRODUCT_ID 0x005B +#define DEVICE_VER 0x0814 +#define MANUFACTURER TMK +#define PRODUCT USB Descriptor Dumper + + +#define DESCRIPTION Product from TMK keyboard firmware project + + +/* matrix size */ +#define MATRIX_ROWS 0 +#define MATRIX_COLS 0 + +/* key combination for command */ +#define IS_COMMAND() (keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT))) + + +// Disable power saving in USB suspend loop but remote wakeup is still valid. +// This allows keep USB::Task() going during suspend without power down time delay. +//#define NO_SUSPEND_POWER_DOWN + + +// Disable USB startup wait, which can delays starting UHS2 Task() for 350-600ms. +//#define NO_USB_STARTUP_WAIT_LOOP + +// Disable USB suspend loop, which blocks UHS2 Task() while power saving. +// Note that this also disables power saving and remote wakeup from keyboard completely. +//#define NO_USB_SUSPEND_LOOP + +#endif diff --git a/converter/usb_desc_dump/ino.cpp b/converter/usb_desc_dump/ino.cpp new file mode 100644 index 00000000..b290c068 --- /dev/null +++ b/converter/usb_desc_dump/ino.cpp @@ -0,0 +1,37 @@ +/* +Copyright 2021 Jun Wako + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#include "Arduino.h" +#include "hook.h" + + +void hook_early_init(void) { + // arduino + setup(); +} + +void hook_late_init(void) { +} + +void hook_main_loop(void) { + // arduino + loop(); +} diff --git a/tmk_core/common.mk b/tmk_core/common.mk index 9c3425b4..6805fa62 100644 --- a/tmk_core/common.mk +++ b/tmk_core/common.mk @@ -17,7 +17,10 @@ SRC += $(COMMON_DIR)/host.c \ $(COMMON_DIR)/avr/bootloader.c -# Option modules +ifeq (yes,$(strip $(NO_KEYBOARD))) + OPT_DEFS += -DNO_KEYBOARD +endif + ifeq (yes,$(strip $(UNIMAP_ENABLE))) SRC += $(COMMON_DIR)/unimap.c OPT_DEFS += -DUNIMAP_ENABLE @@ -75,6 +78,10 @@ ifeq (yes,$(strip $(NKRO_ENABLE))) OPT_DEFS += -DNKRO_ENABLE endif +ifeq (yes,$(strip $(NKRO_6KRO_ENABLE))) + OPT_DEFS += -DNKRO_6KRO_ENABLE +endif + ifeq (yes,$(strip $(USB_6KRO_ENABLE))) OPT_DEFS += -DUSB_6KRO_ENABLE endif @@ -109,7 +116,7 @@ ifeq (yes,$(strip $(KEYMAP_SECTION_ENABLE))) endif # Version string -TMK_VERSION := $(shell (git describe --always --dirty=+ || echo 'unknown') 2> /dev/null) +TMK_VERSION := $(shell (git rev-parse --short=6 HEAD || echo 'unknown') 2> /dev/null) OPT_DEFS += -DTMK_VERSION=$(TMK_VERSION) diff --git a/tmk_core/common/action_util.c b/tmk_core/common/action_util.c index f81877dd..7324bb27 100644 --- a/tmk_core/common/action_util.c +++ b/tmk_core/common/action_util.c @@ -22,7 +22,7 @@ along with this program. If not, see . static inline void add_key_byte(uint8_t code); static inline void del_key_byte(uint8_t code); -#ifdef NKRO_ENABLE +#if defined(NKRO_ENABLE) || defined(NKRO_6KRO_ENABLE) static inline void add_key_bit(uint8_t code); static inline void del_key_bit(uint8_t code); #endif @@ -75,7 +75,7 @@ void send_keyboard_report(void) { /* key */ void add_key(uint8_t key) { -#ifdef NKRO_ENABLE +#if defined(NKRO_ENABLE) || defined(NKRO_6KRO_ENABLE) if (keyboard_protocol && keyboard_nkro) { add_key_bit(key); return; @@ -86,7 +86,7 @@ void add_key(uint8_t key) void del_key(uint8_t key) { -#ifdef NKRO_ENABLE +#if defined(NKRO_ENABLE) || defined(NKRO_6KRO_ENABLE) if (keyboard_protocol && keyboard_nkro) { del_key_bit(key); return; @@ -159,7 +159,7 @@ uint8_t has_anymod(void) uint8_t get_first_key(void) { -#ifdef NKRO_ENABLE +#if defined(NKRO_ENABLE) || defined(NKRO_6KRO_ENABLE) if (keyboard_protocol && keyboard_nkro) { uint8_t i = 0; for (; i < KEYBOARD_REPORT_BITS && !keyboard_report->nkro.bits[i]; i++) @@ -286,7 +286,7 @@ static inline void del_key_byte(uint8_t code) #endif } -#ifdef NKRO_ENABLE +#if defined(NKRO_ENABLE) || defined(NKRO_6KRO_ENABLE) static inline void add_key_bit(uint8_t code) { if ((code>>3) < KEYBOARD_REPORT_BITS) { diff --git a/tmk_core/common/avr/suspend.c b/tmk_core/common/avr/suspend.c index ea8099d0..7a17edbf 100644 --- a/tmk_core/common/avr/suspend.c +++ b/tmk_core/common/avr/suspend.c @@ -115,21 +115,25 @@ void suspend_power_down(void) bool suspend_wakeup_condition(void) { +#ifndef NO_KEYBOARD matrix_power_up(); matrix_scan(); matrix_power_down(); for (uint8_t r = 0; r < MATRIX_ROWS; r++) { if (matrix_get_row(r)) return true; } +#endif return false; } // run immediately after wakeup void suspend_wakeup_init(void) { +#ifndef NO_KEYBOARD // clear keyboard state matrix_clear(); clear_keyboard(); +#endif #ifdef BACKLIGHT_ENABLE backlight_init(); #endif diff --git a/tmk_core/common/bootmagic.c b/tmk_core/common/bootmagic.c index eb06d787..220eb14f 100644 --- a/tmk_core/common/bootmagic.c +++ b/tmk_core/common/bootmagic.c @@ -88,7 +88,7 @@ void bootmagic(void) } eeconfig_write_keymap(keymap_config.raw); -#ifdef NKRO_ENABLE +#if defined(NKRO_ENABLE) || defined(NKRO_6KRO_ENABLE) keyboard_nkro = keymap_config.nkro; #endif diff --git a/tmk_core/common/command.c b/tmk_core/common/command.c index 54050bd9..74255736 100644 --- a/tmk_core/common/command.c +++ b/tmk_core/common/command.c @@ -134,7 +134,7 @@ static void command_common_help(void) "e: eeprom\n" #endif -#ifdef NKRO_ENABLE +#if defined(NKRO_ENABLE) || defined(NKRO_6KRO_ENABLE) "n: NKRO\n" #endif @@ -314,7 +314,7 @@ static bool command_common(uint8_t code) #ifdef COMMAND_ENABLE " COMMAND" #endif -#ifdef NKRO_ENABLE +#if defined(NKRO_ENABLE) || defined(NKRO_6KRO_ENABLE) " NKRO" #endif #ifdef KEYMAP_SECTION_ENABLE @@ -336,7 +336,7 @@ static bool command_common(uint8_t code) print_val_hex8(host_keyboard_leds()); print_val_hex8(keyboard_protocol); print_val_hex8(keyboard_idle); -#ifdef NKRO_ENABLE +#if defined(NKRO_ENABLE) || defined(NKRO_6KRO_ENABLE) print_val_hex8(keyboard_nkro); #endif print_val_hex32(timer_read32()); @@ -355,7 +355,7 @@ static bool command_common(uint8_t code) # endif #endif break; -#ifdef NKRO_ENABLE +#if defined(NKRO_ENABLE) || defined(NKRO_6KRO_ENABLE) case KC_N: clear_keyboard(); //Prevents stuck keys. keyboard_nkro = !keyboard_nkro; diff --git a/tmk_core/common/hook.c b/tmk_core/common/hook.c index 22be9a30..6cf9977d 100644 --- a/tmk_core/common/hook.c +++ b/tmk_core/common/hook.c @@ -22,6 +22,9 @@ along with this program. If not, see . * Definitions of default hooks * ------------------------------------------------- */ +__attribute__((weak)) +void hook_main_loop(void) {} + __attribute__((weak)) void hook_keyboard_loop(void) {} diff --git a/tmk_core/common/hook.h b/tmk_core/common/hook.h index ddd05c6f..77f10845 100644 --- a/tmk_core/common/hook.h +++ b/tmk_core/common/hook.h @@ -56,6 +56,10 @@ void hook_usb_wakeup(void); /* Default behaviour: do nothing. */ void hook_usb_startup_wait_loop(void); +/* Called periodically from main loop */ +/* Default behaviour: do nothing. */ +void hook_main_loop(void); + /* ------------------------------------- * Keyboard hooks diff --git a/tmk_core/common/host.c b/tmk_core/common/host.c index 27aa44a2..1f40aa49 100644 --- a/tmk_core/common/host.c +++ b/tmk_core/common/host.c @@ -23,7 +23,7 @@ along with this program. If not, see . #include "debug.h" -#ifdef NKRO_ENABLE +#if defined(NKRO_ENABLE) || defined(NKRO_6KRO_ENABLE) bool keyboard_nkro = true; #endif diff --git a/tmk_core/common/host.h b/tmk_core/common/host.h index fefb8141..e1359782 100644 --- a/tmk_core/common/host.h +++ b/tmk_core/common/host.h @@ -28,7 +28,7 @@ along with this program. If not, see . extern "C" { #endif -#ifdef NKRO_ENABLE +#if defined(NKRO_ENABLE) || defined(NKRO_6KRO_ENABLE) extern bool keyboard_nkro; #endif diff --git a/tmk_core/common/keymap.c b/tmk_core/common/keymap.c index 617a1f70..f9b8ca12 100644 --- a/tmk_core/common/keymap.c +++ b/tmk_core/common/keymap.c @@ -171,10 +171,6 @@ static action_t keycode_to_action(uint8_t keycode) * Legacy keymap support * Consider using new keymap API instead. */ -extern const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; -extern const uint8_t fn_layer[]; -extern const uint8_t fn_keycode[]; - __attribute__ ((weak)) uint8_t keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t col) { @@ -223,10 +219,6 @@ action_t keymap_fn_to_action(uint8_t keycode) #else -/* user keymaps should be defined somewhere */ -extern const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; -extern const action_t fn_actions[]; - __attribute__ ((weak)) uint8_t keymap_key_to_keycode(uint8_t layer, keypos_t key) { diff --git a/tmk_core/common/keymap.h b/tmk_core/common/keymap.h index 659ea357..0a9c5d48 100644 --- a/tmk_core/common/keymap.h +++ b/tmk_core/common/keymap.h @@ -23,6 +23,10 @@ along with this program. If not, see . #include "action.h" +#ifdef __cplusplus +extern "C" { +#endif + #ifdef BOOTMAGIC_ENABLE /* NOTE: Not portable. Bit field order depends on implementation */ typedef union { @@ -40,6 +44,8 @@ typedef union { } keymap_config_t; #endif +extern const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; +extern const action_t fn_actions[]; /* translates key to keycode */ uint8_t keymap_key_to_keycode(uint8_t layer, keypos_t key); @@ -54,6 +60,10 @@ action_t keymap_fn_to_action(uint8_t keycode); * Legacy keymap * Consider using new keymap API above instead. */ +extern const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; +extern const uint8_t fn_layer[]; +extern const uint8_t fn_keycode[]; + /* keycode of key */ __attribute__ ((deprecated)) uint8_t keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t col); @@ -65,6 +75,16 @@ uint8_t keymap_fn_layer(uint8_t fn_bits); /* keycode to send when release Fn key without using */ __attribute__ ((deprecated)) uint8_t keymap_fn_keycode(uint8_t fn_bits); + +#else + +/* user keymaps should be defined somewhere */ +extern const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; +extern const action_t fn_actions[]; +#endif + +#ifdef __cplusplus +} #endif #endif diff --git a/tmk_core/common/print.h b/tmk_core/common/print.h index 9eec9191..4f72f865 100644 --- a/tmk_core/common/print.h +++ b/tmk_core/common/print.h @@ -100,6 +100,12 @@ void print_set_sendchar(int8_t (*print_sendchar_func)(uint8_t)); #else /* NO_PRINT */ #define xprintf(s,...) ((void)0) +#define xsprintf(s,...) ((void)0) +#define xfprintf(s,...) ((void)0) +#define xputs(s) ((void)0) +#define xputc(c) ((void)0) +#define xitoa(v, r, w) ((void)0) +#define xatoi(s, r) ((void)0) #define print(s) ((void)0) #define println(s) ((void)0) #define print_set_sendchar(func) ((void)0) diff --git a/tmk_core/common/report.h b/tmk_core/common/report.h index 5f91550a..fcafbd5f 100644 --- a/tmk_core/common/report.h +++ b/tmk_core/common/report.h @@ -94,7 +94,7 @@ along with this program. If not, see . # define KEYBOARD_REPORT_KEYS (KBD2_SIZE - 2) # define KEYBOARD_REPORT_BITS (KBD2_SIZE - 1) -#elif defined(PROTOCOL_LUFA) && defined(NKRO_ENABLE) +#elif defined(PROTOCOL_LUFA) && (defined(NKRO_ENABLE) || defined(NKRO_6KRO_ENABLE)) # include "protocol/lufa/descriptor.h" # define KEYBOARD_REPORT_SIZE NKRO_EPSIZE # define KEYBOARD_REPORT_KEYS (NKRO_EPSIZE - 2) @@ -142,7 +142,7 @@ typedef union { uint8_t reserved; uint8_t keys[KEYBOARD_REPORT_KEYS]; }; -#ifdef NKRO_ENABLE +#if defined(NKRO_ENABLE) || defined(NKRO_6KRO_ENABLE) struct { uint8_t mods; uint8_t bits[KEYBOARD_REPORT_BITS]; diff --git a/tmk_core/common/ringbuf.h b/tmk_core/common/ringbuf.h index 23bb41e0..d9eec238 100644 --- a/tmk_core/common/ringbuf.h +++ b/tmk_core/common/ringbuf.h @@ -19,6 +19,7 @@ static inline void ringbuf_write(ringbuf_t *buf, uint8_t data); static inline bool ringbuf_is_empty(ringbuf_t *buf); static inline bool ringbuf_is_full(ringbuf_t *buf); static inline void ringbuf_reset(ringbuf_t *buf); +static inline void ringbuf_push(ringbuf_t *buf, uint8_t data); static inline void ringbuf_init(ringbuf_t *buf, uint8_t *array, uint8_t size) { @@ -70,4 +71,10 @@ static inline void ringbuf_reset(ringbuf_t *buf) buf->head = 0; buf->tail = 0; } +static inline void ringbuf_push(ringbuf_t *buf, uint8_t data) +{ + buf->buffer[buf->head] = data; + buf->head++; + buf->head &= buf->size_mask; +} #endif diff --git a/tmk_core/protocol/ibmpc.c b/tmk_core/protocol/ibmpc.c index ff4f54ca..4fd0f508 100644 --- a/tmk_core/protocol/ibmpc.c +++ b/tmk_core/protocol/ibmpc.c @@ -214,11 +214,10 @@ void ibmpc_host_isr_clear(void) ringbuf_reset(&rb); } -#define LO8(w) (*((uint8_t *)&(w))) -#define HI8(w) (*(((uint8_t *)&(w))+1)) -// NOTE: With this ISR data line can be read within 2us after clock falling edge. -// To read data line early as possible: -// write naked ISR with asembly code to read the line and call C func to do other job? + +// NOTE: With this ISR data line should be read within 5us after clock falling edge. +// Confirmed that ATmega32u4 can read data line in 2.5us from interrupt after +// ISR prologue pushs r18, r19, r20, r21, r24, r25 r30 and r31 with GCC 5.4.0 ISR(IBMPC_INT_VECT) { uint8_t dbit; @@ -366,16 +365,19 @@ ISR(IBMPC_INT_VECT) DONE: // store data - if (!ringbuf_put(&rb, isr_state & 0xFF)) { - // buffer overflow - ibmpc_error = IBMPC_ERR_FULL; - + ringbuf_push(&rb, isr_state & 0xFF); + if (ringbuf_is_full(&rb)) { + // just became full // Disable ISR if buffer is full IBMPC_INT_OFF(); // inhibit: clock_lo IBMPC_CLOCK_PORT &= ~(1< + +This software is licensed with a Modified BSD License. +All of this is supposed to be Free Software, Open Source, DFSG-free, +GPL-compatible, and OK to use in both free and proprietary applications. +Additions and corrections to this file are welcome. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +* Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + * IBM PC keyboard protocol + */ + +#include +#include +#include +#include "debug.h" +#include "timer.h" +#include "wait.h" +#include "ibmpc.hpp" + + +#define WAIT(stat, us, err) do { \ + if (!wait_##stat(us)) { \ + error = err; \ + goto ERROR; \ + } \ +} while (0) + + +IBMPC IBMPC::interface0 = IBMPC(IBMPC_CLOCK_BIT, IBMPC_DATA_BIT); +#if defined(IBMPC_CLOCK_BIT1) && defined(IBMPC_DATA_BIT1) +IBMPC IBMPC::interface1 = IBMPC(IBMPC_CLOCK_BIT1, IBMPC_DATA_BIT1); +#endif + + +void IBMPC::host_init(void) +{ + // initialize reset pin to HiZ + IBMPC_RST_HIZ(); + inhibit(); + int_init(); + int_off(); + host_isr_clear(); +} + +void IBMPC::host_enable(void) +{ + int_on(); + idle(); +} + +void IBMPC::host_disable(void) +{ + int_off(); + inhibit(); +} + +int16_t IBMPC::host_send(uint8_t data) +{ + bool parity = true; + error = IBMPC_ERR_NONE; + uint8_t retry = 0; + + dprintf("w%02X ", data); + + // Not receiving data + if (isr_state != 0x8000) dprintf("isr:%04X ", isr_state); + while (isr_state != 0x8000) ; + + // Not clock Lo + if (!clock_in()) dprintf("c:%u ", wait_clock_hi(1000)); + + // Not data Lo + if (!data_in()) dprintf("d:%u ", wait_data_hi(1000)); + + int_off(); + +RETRY: + /* terminate a transmission if we have */ + inhibit(); + wait_us(200); // [5]p.54 + + /* 'Request to Send' and Start bit */ + data_lo(); + wait_us(200); + clock_hi(); // [5]p.54 [clock low]>100us [5]p.50 + WAIT(clock_lo, 10000, 1); // [5]p.53, -10ms [5]p.50 + + /* Data bit[2-9] */ + for (uint8_t i = 0; i < 8; i++) { + wait_us(15); + if (data&(1<= 3) { + isr_debug = isr_state; + error = IBMPC_ERR_TIMEOUT; + goto ERROR; + + // timeout error recovery - start receiving new data + // it seems to work somehow but may not under unstable situation + //timer_start = t; + //isr_state = 0x8000; + } + } + + isr_state = isr_state>>1; + if (dbit) isr_state |= 0x8000; + + // isr_state: state of receiving data from keyboard + // + // This should be initialized with 0x8000 before receiving data and + // the MSB '*1' works as marker to discrimitate between protocols. + // It stores sampled bit at MSB after right shift on each clock falling edge. + // + // XT protocol has two variants of signaling; XT_IBM and XT_Clone. + // XT_IBM uses two start bits 0 and 1 while XT_Clone uses just start bit 1. + // https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol + // + // 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + // ----------------------------------------------------- + // *1 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 Initial state(0x8000) + // + // x x x x x x x x | 0 0 0 0 0 0 0 0 midway(0-7 bits received) + // x x x x x x x x | *1 0 0 0 0 0 0 0 midway(8 bits received) + // b6 b5 b4 b3 b2 b1 b0 1 | 0 *1 0 0 0 0 0 0 XT_IBM-midway ^1 + // b7 b6 b5 b4 b3 b2 b1 b0 | 0 *1 0 0 0 0 0 0 AT-midway ^1 + // b7 b6 b5 b4 b3 b2 b1 b0 | 1 *1 0 0 0 0 0 0 XT_Clone-done ^3 + // b6 b5 b4 b3 b2 b1 b0 1 | 1 *1 0 0 0 0 0 0 XT_IBM-error ^3 + // pr b7 b6 b5 b4 b3 b2 b1 | 0 0 *1 0 0 0 0 0 AT-midway[b0=0] + // b7 b6 b5 b4 b3 b2 b1 b0 | 1 0 *1 0 0 0 0 0 XT_IBM-done ^2 + // pr b7 b6 b5 b4 b3 b2 b1 | 1 0 *1 0 0 0 0 0 AT-midway[b0=1] ^2 + // b7 b6 b5 b4 b3 b2 b1 b0 | 1 1 *1 0 0 0 0 0 XT_IBM-error-done + // x x x x x x x x | 0 1 *1 0 0 0 0 0 illegal + // st pr b7 b6 b5 b4 b3 b2 | b1 b0 0 *1 0 0 0 0 AT-done + // x x x x x x x x | x x 1 *1 0 0 0 0 illegal + // all other states than above illegal + // + // ^1: AT and XT_IBM takes same state. + // ^2: AT and XT_IBM takes same state in case that AT b0 is 1, + // we have to check AT stop bit to discriminate between the two protocol. + switch (isr_state & 0xFF) { + case 0b00000000: + case 0b10000000: + case 0b01000000: // ^1 + case 0b00100000: + // midway + goto NEXT; + break; + case 0b11000000: // ^3 + { + uint8_t us = 100; + // wait for rising and falling edge of b7 of XT_IBM + if (!protocol) { + while (!(IBMPC_CLOCK_PIN & clock_mask) && us) { wait_us(1); us--; } + while ( (IBMPC_CLOCK_PIN & clock_mask) && us) { wait_us(1); us--; } + } else if (protocol == IBMPC_PROTOCOL_XT_CLONE) { + us = 0; + } + + if (us) { + // XT_IBM-error: read start(0) as 1 + goto NEXT; + } else { + // XT_Clone-done + isr_debug = isr_state; + isr_state = isr_state>>8; + protocol = IBMPC_PROTOCOL_XT_CLONE; + goto DONE; + } + } + break; + case 0b11100000: + // XT_IBM-error-done + isr_debug = isr_state; + isr_state = isr_state>>8; + protocol = IBMPC_PROTOCOL_XT_ERROR; + goto DONE; + break; + case 0b10100000: // ^2 + { + uint8_t us = 100; + // wait for rising and falling edge of AT stop bit to discriminate between XT and AT + if (!protocol) { + while (!(IBMPC_CLOCK_PIN & clock_mask) && us) { wait_us(1); us--; } + while ( (IBMPC_CLOCK_PIN & clock_mask) && us) { wait_us(1); us--; } + } else if (protocol == IBMPC_PROTOCOL_XT_IBM) { + us = 0; + } + + if (us) { + // found stop bit: AT-midway - process the stop bit in next ISR + goto NEXT; + } else { + // no stop bit: XT_IBM-done + isr_debug = isr_state; + isr_state = isr_state>>8; + protocol = IBMPC_PROTOCOL_XT_IBM; + goto DONE; + } + } + break; + case 0b00010000: + case 0b10010000: + case 0b01010000: + case 0b11010000: + // AT-done + // TODO: parity check? + isr_debug = isr_state; + // stop bit check + if (isr_state & 0x8000) { + protocol = IBMPC_PROTOCOL_AT; + } else { + // Zenith Z-150 AT(beige/white lable) asserts stop bit as low + // https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-AT-Keyboard-Protocol#zenith-z-150-beige + protocol = IBMPC_PROTOCOL_AT_Z150; + } + isr_state = isr_state>>6; + goto DONE; + break; + case 0b01100000: + case 0b00110000: + case 0b10110000: + case 0b01110000: + case 0b11110000: + default: // xxxx_oooo(any 1 in low nibble) + // Illegal + protocol = 0; + isr_debug = isr_state; + error = IBMPC_ERR_ILLEGAL; + goto ERROR; + break; + } + +DONE: + // store data + ringbuf_put(isr_state & 0xFF); + if (ringbuf_is_full()) { + // Disable ISR if buffer is full + int_off(); + // inhibit: clock_lo() instead of inhibit() for ISR optimization + clock_lo(); + } + if (ringbuf_is_empty()) { + // buffer overflow + error = IBMPC_ERR_FULL; + } +ERROR: + // clear for next data + isr_state = 0x8000; +NEXT: + return; +} + +/* send LED state to keyboard */ +void IBMPC::host_set_led(uint8_t led) +{ + if (0xFA == host_send(0xED)) { + host_send(led); + } +} + + +// NOTE: With this ISR data line should be read within 5us after clock falling edge. +// Confirmed that ATmega32u4 can read data line in 2.5us from interrupt after +// ISR prologue pushs r18, r19, r20, r21, r24, r25 r30 and r31 with GCC 5.4.0 +ISR(IBMPC_INT_VECT) +{ + IBMPC::interface0.isr(); +} + +#if defined(IBMPC_CLOCK_BIT1) && defined(IBMPC_DATA_BIT1) && defined(IBMPC_INT_VECT1) +ISR(IBMPC_INT_VECT1) +{ + IBMPC::interface1.isr(); +} +#endif diff --git a/tmk_core/protocol/ibmpc.hpp b/tmk_core/protocol/ibmpc.hpp new file mode 100644 index 00000000..e13f3ea0 --- /dev/null +++ b/tmk_core/protocol/ibmpc.hpp @@ -0,0 +1,276 @@ +/* +Copyright 2010,2011,2012,2013,2019 Jun WAKO + +This software is licensed with a Modified BSD License. +All of this is supposed to be Free Software, Open Source, DFSG-free, +GPL-compatible, and OK to use in both free and proprietary applications. +Additions and corrections to this file are welcome. + + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +* Neither the name of the copyright holders nor the names of + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef IBMPC_HPP +#define IBMPC_HPP + +#include +#include "wait.h" + +/* + * IBM PC keyboard protocol + * + * PS/2 Resources + * -------------- + * [1] The PS/2 Mouse/Keyboard Protocol + * http://www.computer-engineering.org/ps2protocol/ + * Concise and thorough primer of PS/2 protocol. + * + * [2] Keyboard and Auxiliary Device Controller + * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf + * Signal Timing and Format + * + * [3] Keyboards(101- and 102-key) + * http://www.mcamafia.de/pdf/ibm_hitrc11.pdf + * Keyboard Layout, Scan Code Set, POR, and Commands. + * + * [4] PS/2 Reference Manuals + * http://www.mcamafia.de/pdf/ibm_hitrc07.pdf + * Collection of IBM Personal System/2 documents. + * + * [5] TrackPoint Engineering Specifications for version 3E + * https://web.archive.org/web/20100526161812/http://wwwcssrv.almaden.ibm.com/trackpoint/download.html + */ +#define IBMPC_ACK 0xFA +#define IBMPC_RESEND 0xFE +#define IBMPC_SET_LED 0xED + +#define IBMPC_PROTOCOL_NO 0 +#define IBMPC_PROTOCOL_AT 0x10 +#define IBMPC_PROTOCOL_AT_Z150 0x11 +#define IBMPC_PROTOCOL_XT 0x20 +#define IBMPC_PROTOCOL_XT_IBM 0x21 +#define IBMPC_PROTOCOL_XT_CLONE 0x22 +#define IBMPC_PROTOCOL_XT_ERROR 0x23 + +// Error numbers +#define IBMPC_ERR_NONE 0 +#define IBMPC_ERR_RECV 0x00 +#define IBMPC_ERR_SEND 0x10 +#define IBMPC_ERR_TIMEOUT 0x20 +#define IBMPC_ERR_FULL 0x40 +#define IBMPC_ERR_ILLEGAL 0x80 +#define IBMPC_ERR_FF 0xF0 + +#define IBMPC_LED_SCROLL_LOCK 0 +#define IBMPC_LED_NUM_LOCK 1 +#define IBMPC_LED_CAPS_LOCK 2 + + +class IBMPC +{ + public: + static IBMPC interface0; +#if defined(IBMPC_CLOCK_BIT1) && defined(IBMPC_DATA_BIT1) + static IBMPC interface1; +#endif + + volatile uint16_t isr_debug; + volatile uint8_t protocol; + volatile uint8_t error; + + void host_init(void); + void host_enable(void); + void host_disable(void); + int16_t host_send(uint8_t data); + int16_t host_recv_response(void); + int16_t host_recv(void); + void host_isr_clear(void); + void host_set_led(uint8_t led); + + IBMPC(uint8_t clock, uint8_t data) : + isr_debug(IBMPC_ERR_NONE), protocol(IBMPC_PROTOCOL_NO), error(IBMPC_ERR_NONE), + isr_state(0x8000), timer_start(0), clock_bit(clock), data_bit(data), + clock_mask(1 << clock), data_mask(1 << data) { + }; + + inline void isr(void) __attribute__((__always_inline__)); + + + private: + volatile uint16_t isr_state; + uint8_t timer_start; + + /* ring buffer */ + // Size should be power of 2 + #define RINGBUF_SIZE 16 + uint8_t rb_head; + uint8_t rb_tail; + uint8_t rb_buffer[RINGBUF_SIZE]; + + const uint8_t clock_bit, data_bit; + const uint8_t clock_mask, data_mask; + + + inline void clock_lo(void) __attribute__((__always_inline__)) // needed for ISR optimization + { + IBMPC_CLOCK_PORT &= ~clock_mask; + IBMPC_CLOCK_DDR |= clock_mask; + } + + inline void clock_hi(void) + { + /* input with pull up */ + IBMPC_CLOCK_DDR &= ~clock_mask; + IBMPC_CLOCK_PORT |= clock_mask; + } + + inline bool clock_in(void) + { + IBMPC_CLOCK_DDR &= ~clock_mask; + IBMPC_CLOCK_PORT |= clock_mask; + wait_us(1); + return IBMPC_CLOCK_PIN & clock_mask; + } + + inline void data_lo(void) + { + IBMPC_DATA_PORT &= ~data_mask; + IBMPC_DATA_DDR |= data_mask; + } + + inline void data_hi(void) + { + /* input with pull up */ + IBMPC_DATA_DDR &= ~data_mask; + IBMPC_DATA_PORT |= data_mask; + } + + inline bool data_in(void) + { + IBMPC_DATA_DDR &= ~data_mask; + IBMPC_DATA_PORT |= data_mask; + wait_us(1); + return IBMPC_DATA_PIN & data_mask; + } + + inline uint16_t wait_clock_lo(uint16_t us) + { + while (clock_in() && us) { asm(""); wait_us(1); us--; } + return us; + } + inline uint16_t wait_clock_hi(uint16_t us) + { + while (!clock_in() && us) { asm(""); wait_us(1); us--; } + return us; + } + inline uint16_t wait_data_lo(uint16_t us) + { + while (data_in() && us) { asm(""); wait_us(1); us--; } + return us; + } + inline uint16_t wait_data_hi(uint16_t us) + { + while (!data_in() && us) { asm(""); wait_us(1); us--; } + return us; + } + + /* idle state that device can send */ + inline void idle(void) + { + clock_hi(); + data_hi(); + } + + /* inhibit device to send(AT), soft reset(XT) */ + inline void inhibit(void) + { + clock_lo(); + data_hi(); + } + + /* inhibit device to send(XT) */ + inline void inhibit_xt(void) + { + clock_hi(); + data_lo(); + } + + void int_init(void) + { + // interrupt at falling edge + if (clock_bit < 4) { + EICRA |= (0x2 << ((clock_bit&0x3)*2)); + } else { + EICRB |= (0x2 << ((clock_bit&0x3)*2)); + } + } + + void int_on(void) + { + EIFR |= clock_mask; + EIMSK |= clock_mask; + } + + inline void int_off(void) __attribute__((__always_inline__)) // needed for ISR optimization + { + EIMSK &= ~clock_mask; + } + + /* + * ring buffer + */ + inline int16_t ringbuf_get(void) __attribute__((__always_inline__)) + { + if (ringbuf_is_empty()) return -1; + uint8_t data = rb_buffer[rb_tail]; + rb_tail++; + rb_tail &= (RINGBUF_SIZE - 1); + return data; + } + inline void ringbuf_put(uint8_t data) __attribute__((__always_inline__)) + { + rb_buffer[rb_head] = data; + rb_head++; + rb_head &= (RINGBUF_SIZE - 1); + } + inline bool ringbuf_is_empty(void) __attribute__((__always_inline__)) + { + return (rb_head == rb_tail); + } + inline bool ringbuf_is_full(void) __attribute__((__always_inline__)) + { + return (((rb_head + 1) & (RINGBUF_SIZE - 1)) == rb_tail); + } + inline void ringbuf_reset(void) __attribute__((__always_inline__)) + { + rb_head = 0; + rb_tail = 0; + } +}; + +#endif diff --git a/tmk_core/protocol/lufa.mk b/tmk_core/protocol/lufa.mk index 7afedd73..82ccc23b 100644 --- a/tmk_core/protocol/lufa.mk +++ b/tmk_core/protocol/lufa.mk @@ -4,6 +4,10 @@ TMK_LUFA_DIR = protocol/lufa TMK_LUFA_PATH ?= $(TMK_LUFA_DIR)/lufa-abcminiuser +# Version string +TMK_LUFA_VERSION := $(shell (cd $(TMK_DIR)/$(TMK_LUFA_PATH); git rev-parse --short=6 HEAD || echo 'unknown') 2> /dev/null) +OPT_DEFS += -DTMK_LUFA_VERSION=$(TMK_LUFA_VERSION) + # Create the LUFA source path variables by including the LUFA makefile ifneq (, $(wildcard $(TMK_DIR)/$(TMK_LUFA_PATH)/LUFA/Build/LUFA/lufa-sources.mk)) LUFA_PATH = $(TMK_LUFA_PATH)/LUFA diff --git a/tmk_core/protocol/lufa/descriptor.c b/tmk_core/protocol/lufa/descriptor.c index 77d86e55..f365222d 100644 --- a/tmk_core/protocol/lufa/descriptor.c +++ b/tmk_core/protocol/lufa/descriptor.c @@ -44,8 +44,11 @@ /******************************************************************************* * HID Report Descriptors ******************************************************************************/ +#ifndef NO_KEYBOARD const USB_Descriptor_HIDReport_Datatype_t PROGMEM KeyboardReport[] = { +#ifndef NKRO_ENABLE + /* 6KRO - Boot protocol */ HID_RI_USAGE_PAGE(8, 0x01), /* Generic Desktop */ HID_RI_USAGE(8, 0x06), /* Keyboard */ HID_RI_COLLECTION(8, 0x01), /* Application */ @@ -81,17 +84,55 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM KeyboardReport[] = HID_RI_REPORT_SIZE(8, 0x08), HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), HID_RI_END_COLLECTION(0), -}; +#else + /* NKRO - Report protocol */ + HID_RI_USAGE_PAGE(8, 0x01), /* Generic Desktop */ + HID_RI_USAGE(8, 0x06), /* Keyboard */ + HID_RI_COLLECTION(8, 0x01), /* Application */ + HID_RI_USAGE_PAGE(8, 0x07), /* Key Codes */ + HID_RI_USAGE_MINIMUM(8, 0xE0), /* Keyboard Left Control */ + HID_RI_USAGE_MAXIMUM(8, 0xE7), /* Keyboard Right GUI */ + HID_RI_LOGICAL_MINIMUM(8, 0x00), + HID_RI_LOGICAL_MAXIMUM(8, 0x01), + HID_RI_REPORT_COUNT(8, 0x08), + HID_RI_REPORT_SIZE(8, 0x01), + HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), -#ifdef MOUSE_ENABLE + HID_RI_USAGE_PAGE(8, 0x08), /* LEDs */ + HID_RI_USAGE_MINIMUM(8, 0x01), /* Num Lock */ + HID_RI_USAGE_MAXIMUM(8, 0x05), /* Kana */ + HID_RI_REPORT_COUNT(8, 0x05), + HID_RI_REPORT_SIZE(8, 0x01), + HID_RI_OUTPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE | HID_IOF_NON_VOLATILE), + HID_RI_REPORT_COUNT(8, 0x01), + HID_RI_REPORT_SIZE(8, 0x03), + HID_RI_OUTPUT(8, HID_IOF_CONSTANT), + + HID_RI_USAGE_PAGE(8, 0x07), /* Key Codes */ + HID_RI_USAGE_MINIMUM(8, 0x00), /* Keyboard 0 */ + HID_RI_USAGE_MAXIMUM(8, (NKRO_EPSIZE-1)*8-1), /* Keyboard Right GUI */ + HID_RI_LOGICAL_MINIMUM(8, 0x00), + HID_RI_LOGICAL_MAXIMUM(8, 0x01), + HID_RI_REPORT_COUNT(8, (NKRO_EPSIZE-1)*8), + HID_RI_REPORT_SIZE(8, 0x01), + HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + HID_RI_END_COLLECTION(0), +#endif +}; +#endif + +#if defined(MOUSE_ENABLE) || defined(EXTRAKEY_ENABLE) const USB_Descriptor_HIDReport_Datatype_t PROGMEM MouseReport[] = { +#ifdef MOUSE_ENABLE HID_RI_USAGE_PAGE(8, 0x01), /* Generic Desktop */ HID_RI_USAGE(8, 0x02), /* Mouse */ HID_RI_COLLECTION(8, 0x01), /* Application */ HID_RI_USAGE(8, 0x01), /* Pointer */ HID_RI_COLLECTION(8, 0x00), /* Physical */ + HID_RI_REPORT_ID(8, REPORT_ID_MOUSE), + HID_RI_USAGE_PAGE(8, 0x09), /* Button */ HID_RI_USAGE_MINIMUM(8, 0x01), /* Button 1 */ HID_RI_USAGE_MAXIMUM(8, 0x08), /* Button 8 */ @@ -147,12 +188,9 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM MouseReport[] = HID_RI_END_COLLECTION(0), HID_RI_END_COLLECTION(0), -}; #endif #ifdef EXTRAKEY_ENABLE -const USB_Descriptor_HIDReport_Datatype_t PROGMEM ExtrakeyReport[] = -{ HID_RI_USAGE_PAGE(8, 0x01), /* Generic Desktop */ HID_RI_USAGE(8, 0x80), /* System Control */ HID_RI_COLLECTION(8, 0x01), /* Application */ @@ -178,6 +216,7 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM ExtrakeyReport[] = HID_RI_REPORT_COUNT(8, 1), HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), HID_RI_END_COLLECTION(0), +#endif }; #endif @@ -203,7 +242,7 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM ConsoleReport[] = }; #endif -#ifdef NKRO_ENABLE +#if !defined(NO_KEYBOARD) && defined(NKRO_6KRO_ENABLE) const USB_Descriptor_HIDReport_Datatype_t PROGMEM NKROReport[] = { HID_RI_USAGE_PAGE(8, 0x01), /* Generic Desktop */ @@ -289,6 +328,7 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = /* * Keyboard */ +#ifndef NO_KEYBOARD .Keyboard_Interface = { .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, @@ -322,14 +362,20 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = .EndpointAddress = (ENDPOINT_DIR_IN | KEYBOARD_IN_EPNUM), .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), +#ifdef NKRO_ENABLE + .EndpointSize = NKRO_EPSIZE, + .PollingIntervalMS = 0x01 +#else .EndpointSize = KEYBOARD_EPSIZE, .PollingIntervalMS = 0x0A +#endif }, +#endif /* * Mouse */ -#ifdef MOUSE_ENABLE +#if defined(MOUSE_ENABLE) || defined(EXTRAKEY_ENABLE) .Mouse_Interface = { .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, @@ -340,8 +386,13 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = .TotalEndpoints = 1, .Class = HID_CSCP_HIDClass, +#if defined(MOUSE_ENABLE) .SubClass = HID_CSCP_BootSubclass, .Protocol = HID_CSCP_MouseBootProtocol, +#else + .SubClass = HID_CSCP_NonBootSubclass, + .Protocol = HID_CSCP_NonBootProtocol, +#endif .InterfaceStrIndex = NO_DESCRIPTOR }, @@ -364,49 +415,7 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = .EndpointAddress = (ENDPOINT_DIR_IN | MOUSE_IN_EPNUM), .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), .EndpointSize = MOUSE_EPSIZE, - .PollingIntervalMS = 0x0A - }, -#endif - - /* - * Extra - */ -#ifdef EXTRAKEY_ENABLE - .Extrakey_Interface = - { - .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, - - .InterfaceNumber = EXTRAKEY_INTERFACE, - .AlternateSetting = 0x00, - - .TotalEndpoints = 1, - - .Class = HID_CSCP_HIDClass, - .SubClass = HID_CSCP_NonBootSubclass, - .Protocol = HID_CSCP_NonBootProtocol, - - .InterfaceStrIndex = NO_DESCRIPTOR - }, - - .Extrakey_HID = - { - .Header = {.Size = sizeof(USB_HID_Descriptor_HID_t), .Type = HID_DTYPE_HID}, - - .HIDSpec = VERSION_BCD(1,1,1), - .CountryCode = 0x00, - .TotalReportDescriptors = 1, - .HIDReportType = HID_DTYPE_Report, - .HIDReportLength = sizeof(ExtrakeyReport) - }, - - .Extrakey_INEndpoint = - { - .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, - - .EndpointAddress = (ENDPOINT_DIR_IN | EXTRAKEY_IN_EPNUM), - .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), - .EndpointSize = EXTRAKEY_EPSIZE, - .PollingIntervalMS = 0x0A + .PollingIntervalMS = 0x01 }, #endif @@ -465,7 +474,7 @@ const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = /* * NKRO */ -#ifdef NKRO_ENABLE +#if !defined(NO_KEYBOARD) && defined(NKRO_6KRO_ENABLE) .NKRO_Interface = { .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, @@ -578,29 +587,25 @@ uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, break; case HID_DTYPE_HID: switch (wIndex) { +#ifndef NO_KEYBOARD case KEYBOARD_INTERFACE: Address = &ConfigurationDescriptor.Keyboard_HID; Size = sizeof(USB_HID_Descriptor_HID_t); break; -#ifdef MOUSE_ENABLE +#endif +#if defined(MOUSE_ENABLE) || defined(EXTRAKEY_ENABLE) case MOUSE_INTERFACE: Address = &ConfigurationDescriptor.Mouse_HID; Size = sizeof(USB_HID_Descriptor_HID_t); break; #endif -#ifdef EXTRAKEY_ENABLE - case EXTRAKEY_INTERFACE: - Address = &ConfigurationDescriptor.Extrakey_HID; - Size = sizeof(USB_HID_Descriptor_HID_t); - break; -#endif #ifdef CONSOLE_ENABLE case CONSOLE_INTERFACE: Address = &ConfigurationDescriptor.Console_HID; Size = sizeof(USB_HID_Descriptor_HID_t); break; #endif -#ifdef NKRO_ENABLE +#if !defined(NO_KEYBOARD) && defined(NKRO_6KRO_ENABLE) case NKRO_INTERFACE: Address = &ConfigurationDescriptor.NKRO_HID; Size = sizeof(USB_HID_Descriptor_HID_t); @@ -610,29 +615,25 @@ uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, break; case HID_DTYPE_Report: switch (wIndex) { +#ifndef NO_KEYBOARD case KEYBOARD_INTERFACE: Address = &KeyboardReport; Size = sizeof(KeyboardReport); break; -#ifdef MOUSE_ENABLE +#endif +#if defined(MOUSE_ENABLE) || defined(EXTRAKEY_ENABLE) case MOUSE_INTERFACE: Address = &MouseReport; Size = sizeof(MouseReport); break; #endif -#ifdef EXTRAKEY_ENABLE - case EXTRAKEY_INTERFACE: - Address = &ExtrakeyReport; - Size = sizeof(ExtrakeyReport); - break; -#endif #ifdef CONSOLE_ENABLE case CONSOLE_INTERFACE: Address = &ConsoleReport; Size = sizeof(ConsoleReport); break; #endif -#ifdef NKRO_ENABLE +#if !defined(NO_KEYBOARD) && defined(NKRO_6KRO_ENABLE) case NKRO_INTERFACE: Address = &NKROReport; Size = sizeof(NKROReport); diff --git a/tmk_core/protocol/lufa/descriptor.h b/tmk_core/protocol/lufa/descriptor.h index ed58f21e..4bba0cc0 100644 --- a/tmk_core/protocol/lufa/descriptor.h +++ b/tmk_core/protocol/lufa/descriptor.h @@ -52,25 +52,20 @@ typedef struct { USB_Descriptor_Configuration_Header_t Config; +#ifndef NO_KEYBOARD // Keyboard HID Interface USB_Descriptor_Interface_t Keyboard_Interface; USB_HID_Descriptor_HID_t Keyboard_HID; USB_Descriptor_Endpoint_t Keyboard_INEndpoint; +#endif -#ifdef MOUSE_ENABLE +#if defined(MOUSE_ENABLE) || defined(EXTRAKEY_ENABLE) // Mouse HID Interface USB_Descriptor_Interface_t Mouse_Interface; USB_HID_Descriptor_HID_t Mouse_HID; USB_Descriptor_Endpoint_t Mouse_INEndpoint; #endif -#ifdef EXTRAKEY_ENABLE - // Extrakey HID Interface - USB_Descriptor_Interface_t Extrakey_Interface; - USB_HID_Descriptor_HID_t Extrakey_HID; - USB_Descriptor_Endpoint_t Extrakey_INEndpoint; -#endif - #ifdef CONSOLE_ENABLE // Console HID Interface USB_Descriptor_Interface_t Console_Interface; @@ -79,7 +74,7 @@ typedef struct USB_Descriptor_Endpoint_t Console_OUTEndpoint; #endif -#ifdef NKRO_ENABLE +#if !defined(NO_KEYBOARD) && defined(NKRO_6KRO_ENABLE) // NKRO HID Interface USB_Descriptor_Interface_t NKRO_Interface; USB_HID_Descriptor_HID_t NKRO_HID; @@ -89,27 +84,25 @@ typedef struct /* index of interface */ -#define KEYBOARD_INTERFACE 0 +#ifndef NO_KEYBOARD +# define KEYBOARD_INTERFACE 0 +#else +# define KEYBOARD_INTERFACE -1 +#endif -#ifdef MOUSE_ENABLE +#if defined(MOUSE_ENABLE) || defined(EXTRAKEY_ENABLE) # define MOUSE_INTERFACE (KEYBOARD_INTERFACE + 1) #else # define MOUSE_INTERFACE KEYBOARD_INTERFACE #endif -#ifdef EXTRAKEY_ENABLE -# define EXTRAKEY_INTERFACE (MOUSE_INTERFACE + 1) -#else -# define EXTRAKEY_INTERFACE MOUSE_INTERFACE -#endif - #ifdef CONSOLE_ENABLE -# define CONSOLE_INTERFACE (EXTRAKEY_INTERFACE + 1) +# define CONSOLE_INTERFACE (MOUSE_INTERFACE + 1) #else -# define CONSOLE_INTERFACE EXTRAKEY_INTERFACE +# define CONSOLE_INTERFACE MOUSE_INTERFACE #endif -#ifdef NKRO_ENABLE +#if !defined(NO_KEYBOARD) && defined(NKRO_6KRO_ENABLE) # define NKRO_INTERFACE (CONSOLE_INTERFACE + 1) #else # define NKRO_INTERFACE CONSOLE_INTERFACE @@ -121,28 +114,26 @@ typedef struct // Endopoint number and size -#define KEYBOARD_IN_EPNUM 1 +#ifndef NO_KEYBOARD +# define KEYBOARD_IN_EPNUM 1 +#else +# define KEYBOARD_IN_EPNUM 0 +#endif -#ifdef MOUSE_ENABLE +#if defined(MOUSE_ENABLE) || defined(EXTRAKEY_ENABLE) # define MOUSE_IN_EPNUM (KEYBOARD_IN_EPNUM + 1) #else # define MOUSE_IN_EPNUM KEYBOARD_IN_EPNUM #endif -#ifdef EXTRAKEY_ENABLE -# define EXTRAKEY_IN_EPNUM (MOUSE_IN_EPNUM + 1) -#else -# define EXTRAKEY_IN_EPNUM MOUSE_IN_EPNUM -#endif - #ifdef CONSOLE_ENABLE -# define CONSOLE_IN_EPNUM (EXTRAKEY_IN_EPNUM + 1) -# define CONSOLE_OUT_EPNUM (EXTRAKEY_IN_EPNUM + 1) +# define CONSOLE_IN_EPNUM (MOUSE_IN_EPNUM + 1) +# define CONSOLE_OUT_EPNUM (MOUSE_IN_EPNUM + 1) #else -# define CONSOLE_OUT_EPNUM EXTRAKEY_IN_EPNUM +# define CONSOLE_OUT_EPNUM MOUSE_IN_EPNUM #endif -#ifdef NKRO_ENABLE +#if !defined(NO_KEYBOARD) && defined(NKRO_6KRO_ENABLE) # define NKRO_IN_EPNUM (CONSOLE_OUT_EPNUM + 1) #else # define NKRO_IN_EPNUM CONSOLE_OUT_EPNUM @@ -150,13 +141,18 @@ typedef struct /* Check number of endpoints. ATmega32u2 has only four except for control endpoint. */ #if defined(__AVR_ATmega32U2__) && NKRO_IN_EPNUM > 4 -# error "Endpoints are not available enough to support all functions. Disable some of build options in Makefile.(MOUSEKEY, EXTRAKEY, CONSOLE, NKRO)" +# error "Endpoints are not available enough to support all functions. Disable some of build options in Makefile.(MOUSEKEY, CONSOLE, NKRO)" #endif #define KEYBOARD_EPSIZE 8 + +#if defined(MOUSE_EXT_REPORT) +#define MOUSE_EPSIZE 10 +#else #define MOUSE_EPSIZE 8 -#define EXTRAKEY_EPSIZE 8 +#endif + #define CONSOLE_EPSIZE 32 #define NKRO_EPSIZE 32 diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c index 54221591..8653c46e 100644 --- a/tmk_core/protocol/lufa/lufa.c +++ b/tmk_core/protocol/lufa/lufa.c @@ -68,26 +68,35 @@ //#define TMK_LUFA_DEBUG +#ifndef NO_KEYBOARD uint8_t keyboard_idle = 0; /* 0: Boot Protocol, 1: Report Protocol(default) */ uint8_t keyboard_protocol = 1; static uint8_t keyboard_led_stats = 0; - static report_keyboard_t keyboard_report_sent; +#endif + +#ifdef MOUSE_ENABLE +uint8_t mouse_protocol = 1; +#endif /* Host driver */ +#ifndef NO_KEYBOARD static uint8_t keyboard_leds(void); static void send_keyboard(report_keyboard_t *report); +#endif static void send_mouse(report_mouse_t *report); static void send_system(uint16_t data); static void send_consumer(uint16_t data); host_driver_t lufa_driver = { - keyboard_leds, - send_keyboard, - send_mouse, - send_system, - send_consumer +#ifndef NO_KEYBOARD + .keyboard_leds = keyboard_leds, + .send_keyboard = send_keyboard, +#endif + .send_mouse = send_mouse, + .send_system = send_system, + .send_consumer = send_consumer }; @@ -327,22 +336,23 @@ void EVENT_USB_Device_ConfigurationChanged(void) #endif bool ConfigSuccess = true; +#ifndef NO_KEYBOARD /* Setup Keyboard HID Report Endpoints */ ConfigSuccess &= ENDPOINT_CONFIG(KEYBOARD_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN, - KEYBOARD_EPSIZE, ENDPOINT_BANK_SINGLE); +#ifdef NKRO_ENABLE + NKRO_EPSIZE, +#else + KEYBOARD_EPSIZE, +#endif + ENDPOINT_BANK_SINGLE); +#endif -#ifdef MOUSE_ENABLE +#if defined(MOUSE_ENABLE) || defined(EXTRAKEY_ENABLE) /* Setup Mouse HID Report Endpoint */ ConfigSuccess &= ENDPOINT_CONFIG(MOUSE_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN, MOUSE_EPSIZE, ENDPOINT_BANK_SINGLE); #endif -#ifdef EXTRAKEY_ENABLE - /* Setup Extra HID Report Endpoint */ - ConfigSuccess &= ENDPOINT_CONFIG(EXTRAKEY_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN, - EXTRAKEY_EPSIZE, ENDPOINT_BANK_SINGLE); -#endif - #ifdef CONSOLE_ENABLE /* Setup Console HID Report Endpoints */ ConfigSuccess &= ENDPOINT_CONFIG(CONSOLE_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN, @@ -353,7 +363,7 @@ void EVENT_USB_Device_ConfigurationChanged(void) #endif #endif -#ifdef NKRO_ENABLE +#ifdef NKRO_6KRO_ENABLE /* Setup NKRO HID Report Endpoints */ ConfigSuccess &= ENDPOINT_CONFIG(NKRO_IN_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN, NKRO_EPSIZE, ENDPOINT_BANK_SINGLE); @@ -378,15 +388,16 @@ Other Device Required Optional Optional Optional Optional Opti */ void EVENT_USB_Device_ControlRequest(void) { - uint8_t* ReportData = NULL; - uint8_t ReportSize = 0; - /* Handle HID Class specific requests */ switch (USB_ControlRequest.bRequest) { case HID_REQ_GetReport: +#ifndef NO_KEYBOARD if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE)) { + uint8_t* ReportData = NULL; + uint8_t ReportSize = 0; + Endpoint_ClearSETUP(); // Interface @@ -405,6 +416,7 @@ void EVENT_USB_Device_ControlRequest(void) xprintf("[r%d]", USB_ControlRequest.wIndex); #endif } +#endif break; case HID_REQ_SetReport: @@ -413,8 +425,9 @@ void EVENT_USB_Device_ControlRequest(void) // Interface switch (USB_ControlRequest.wIndex) { +#ifndef NO_KEYBOARD case KEYBOARD_INTERFACE: -#ifdef NKRO_ENABLE +#ifdef NKRO_6KRO_ENABLE case NKRO_INTERFACE: #endif Endpoint_ClearSETUP(); @@ -431,6 +444,7 @@ void EVENT_USB_Device_ControlRequest(void) xprintf("[L%d]", USB_ControlRequest.wIndex); #endif break; +#endif } } @@ -440,6 +454,7 @@ void EVENT_USB_Device_ControlRequest(void) case HID_REQ_GetProtocol: if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE)) { +#ifndef NO_KEYBOARD if (USB_ControlRequest.wIndex == KEYBOARD_INTERFACE) { Endpoint_ClearSETUP(); while (!(Endpoint_IsINReady())); @@ -450,12 +465,23 @@ void EVENT_USB_Device_ControlRequest(void) print("[p]"); #endif } +#endif +#if defined(MOUSE_ENABLE) + if (USB_ControlRequest.wIndex == MOUSE_INTERFACE) { + Endpoint_ClearSETUP(); + while (!(Endpoint_IsINReady())); + Endpoint_Write_8(mouse_protocol); + Endpoint_ClearIN(); + Endpoint_ClearStatusStage(); + } +#endif } break; case HID_REQ_SetProtocol: if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE)) { +#ifndef NO_KEYBOARD if (USB_ControlRequest.wIndex == KEYBOARD_INTERFACE) { Endpoint_ClearSETUP(); Endpoint_ClearStatusStage(); @@ -466,18 +492,30 @@ void EVENT_USB_Device_ControlRequest(void) print("[P]"); #endif } +#endif +#if defined(MOUSE_ENABLE) + if (USB_ControlRequest.wIndex == MOUSE_INTERFACE) { + Endpoint_ClearSETUP(); + Endpoint_ClearStatusStage(); + + mouse_protocol = (USB_ControlRequest.wValue & 0xFF); + clear_keyboard(); + } +#endif } break; case HID_REQ_SetIdle: if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE)) { +#ifndef NO_KEYBOARD Endpoint_ClearSETUP(); Endpoint_ClearStatusStage(); keyboard_idle = ((USB_ControlRequest.wValue & 0xFF00) >> 8); #ifdef TMK_LUFA_DEBUG xprintf("[I%d]%d", USB_ControlRequest.wIndex, (USB_ControlRequest.wValue & 0xFF00) >> 8); +#endif #endif } @@ -485,6 +523,7 @@ void EVENT_USB_Device_ControlRequest(void) case HID_REQ_GetIdle: if (USB_ControlRequest.bmRequestType == (REQDIR_DEVICETOHOST | REQTYPE_CLASS | REQREC_INTERFACE)) { +#ifndef NO_KEYBOARD Endpoint_ClearSETUP(); while (!(Endpoint_IsINReady())); Endpoint_Write_8(keyboard_idle); @@ -492,6 +531,7 @@ void EVENT_USB_Device_ControlRequest(void) Endpoint_ClearStatusStage(); #ifdef TMK_LUFA_DEBUG print("[i]"); +#endif #endif } @@ -502,6 +542,7 @@ void EVENT_USB_Device_ControlRequest(void) /******************************************************************************* * Host driver ******************************************************************************/ +#ifndef NO_KEYBOARD static uint8_t keyboard_leds(void) { return keyboard_led_stats; @@ -515,10 +556,14 @@ static void send_keyboard(report_keyboard_t *report) return; /* Select the Keyboard Report Endpoint */ -#ifdef NKRO_ENABLE +#if defined(NKRO_ENABLE) || defined(NKRO_6KRO_ENABLE) if (keyboard_protocol && keyboard_nkro) { /* Report protocol - NKRO */ + #if defined(NKRO_6KRO_ENABLE) Endpoint_SelectEndpoint(NKRO_IN_EPNUM); + #else + Endpoint_SelectEndpoint(KEYBOARD_IN_EPNUM); + #endif /* Check if write ready for a polling interval around 1ms */ while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(8); @@ -546,6 +591,7 @@ static void send_keyboard(report_keyboard_t *report) keyboard_report_sent = *report; } +#endif static void send_mouse(report_mouse_t *report) { @@ -563,7 +609,14 @@ static void send_mouse(report_mouse_t *report) if (!Endpoint_IsReadWriteAllowed()) return; /* Write Mouse Report Data */ - Endpoint_Write_Stream_LE(report, sizeof(report_mouse_t), NULL); + if (mouse_protocol) { + // Report + Endpoint_Write_8(REPORT_ID_MOUSE); + Endpoint_Write_Stream_LE(report, sizeof(report_mouse_t), NULL); + } else { + // Boot + Endpoint_Write_Stream_LE(report, 3, NULL); + } /* Finalize the stream transfer to send the last packet */ Endpoint_ClearIN(); @@ -578,11 +631,13 @@ static void send_system(uint16_t data) if (USB_DeviceState != DEVICE_STATE_Configured) return; - report_extra_t r = { - .report_id = REPORT_ID_SYSTEM, - .usage = data - SYSTEM_POWER_DOWN + 1 - }; - Endpoint_SelectEndpoint(EXTRAKEY_IN_EPNUM); + report_extra_t r = { .report_id = REPORT_ID_SYSTEM }; + if (data < SYSTEM_POWER_DOWN) { + r.usage = 0; + } else { + r.usage = data - SYSTEM_POWER_DOWN + 1; + } + Endpoint_SelectEndpoint(MOUSE_IN_EPNUM); /* Check if write ready for a polling interval around 10ms */ while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40); @@ -605,7 +660,7 @@ static void send_consumer(uint16_t data) .report_id = REPORT_ID_CONSUMER, .usage = data }; - Endpoint_SelectEndpoint(EXTRAKEY_IN_EPNUM); + Endpoint_SelectEndpoint(MOUSE_IN_EPNUM); /* Check if write ready for a polling interval around 10ms */ while (timeout-- && !Endpoint_IsReadWriteAllowed()) _delay_us(40); @@ -677,17 +732,32 @@ int main(void) print_set_sendchar(sendchar); host_set_driver(&lufa_driver); - print("\n\nTMK:" STR(TMK_VERSION) "/LUFA\n\n"); + print("\nTMK:" STR(TMK_VERSION) "/LUFA:" STR(TMK_LUFA_VERSION) +#ifdef TMK_USB_HOST_SHIELD_VERSION + "/UHS2:" STR(TMK_USB_HOST_SHIELD_VERSION) +#endif + "\n"); + hook_early_init(); + +#ifndef NO_KEYBOARD keyboard_setup(); +#endif + setup_usb(); + #ifdef SLEEP_LED_ENABLE sleep_led_init(); #endif sei(); +#ifndef NO_KEYBOARD keyboard_init(); +#else + // TODO: keyboard_init() should be used only for things related to keyboard + timer_init(); +#endif #ifndef NO_USB_STARTUP_WAIT_LOOP /* wait for USB startup */ @@ -704,7 +774,7 @@ int main(void) hook_late_init(); - print("\nKeyboard start.\n"); + print("\nLoop start.\n"); while (1) { #ifndef NO_USB_SUSPEND_LOOP while (USB_DeviceState == DEVICE_STATE_Suspended) { @@ -712,7 +782,11 @@ int main(void) } #endif + hook_main_loop(); + +#ifndef NO_KEYBOARD keyboard_task(); +#endif #ifdef CONSOLE_ENABLE console_task(); @@ -732,10 +806,13 @@ void hook_early_init(void) {} __attribute__((weak)) void hook_late_init(void) {} +#ifndef NO_KEYBOARD static uint8_t _led_stats = 0; +#endif __attribute__((weak)) void hook_usb_suspend_entry(void) { +#ifndef NO_KEYBOARD // Turn off LED to save power and keep its status to resotre it later. // LED status will be updated by keyboard_task() in main loop hopefully. _led_stats = keyboard_led_stats; @@ -745,6 +822,7 @@ void hook_usb_suspend_entry(void) matrix_clear(); clear_keyboard(); +#endif #ifdef SLEEP_LED_ENABLE sleep_led_enable(); #endif @@ -770,8 +848,10 @@ void hook_usb_wakeup(void) sleep_led_disable(); #endif +#ifndef NO_KEYBOARD // Restore LED status and update at keyboard_task() in main loop keyboard_led_stats = _led_stats; +#endif // Calling long task here can prevent USB state transition } diff --git a/tmk_core/protocol/usb_hid.mk b/tmk_core/protocol/usb_hid.mk index 6881ebc5..e6f957e8 100644 --- a/tmk_core/protocol/usb_hid.mk +++ b/tmk_core/protocol/usb_hid.mk @@ -12,6 +12,10 @@ USB_HOST_SHIELD_SRC = \ $(USB_HOST_SHIELD_DIR)/parsetools.cpp \ $(USB_HOST_SHIELD_DIR)/message.cpp +# Version string +TMK_USB_HOST_SHIELD_VERSION := $(shell (cd $(TMK_DIR)/$(USB_HOST_SHIELD_DIR); git rev-parse --short=6 HEAD || echo 'unknown') 2> /dev/null) +OPT_DEFS += -DTMK_USB_HOST_SHIELD_VERSION=$(TMK_USB_HOST_SHIELD_VERSION) + # diff --git a/tmk_core/protocol/usb_hid/USB_Host_Shield_2.0-tmk b/tmk_core/protocol/usb_hid/USB_Host_Shield_2.0-tmk index b6128f25..96112d43 160000 --- a/tmk_core/protocol/usb_hid/USB_Host_Shield_2.0-tmk +++ b/tmk_core/protocol/usb_hid/USB_Host_Shield_2.0-tmk @@ -1 +1 @@ -Subproject commit b6128f252700a8dd2d12400c55680ef67cdcac86 +Subproject commit 96112d43858b5c5327565c36bd8ea2192ec77d1b diff --git a/tmk_core/protocol/usb_hid/override_Serial.cpp b/tmk_core/protocol/usb_hid/override_Serial.cpp index 00bc018c..7cea44a9 100644 --- a/tmk_core/protocol/usb_hid/override_Serial.cpp +++ b/tmk_core/protocol/usb_hid/override_Serial.cpp @@ -51,8 +51,10 @@ size_t Serial_::write(uint8_t c) size_t Serial_::write(const uint8_t *buffer, size_t size) { - sendchar(*buffer); - return 1; + for (int i = 0; i < size; i++) { + sendchar(buffer[i]); + } + return size; } Serial_::operator bool() { diff --git a/tmk_core/rules.mk b/tmk_core/rules.mk index 3c386b1f..b559e57b 100644 --- a/tmk_core/rules.mk +++ b/tmk_core/rules.mk @@ -334,10 +334,10 @@ MSG_CREATING_LIBRARY = Creating library: # Define all object files. -OBJ = $(patsubst %.c,$(OBJDIR)/%.o,$(patsubst %.cpp,$(OBJDIR)/%.o,$(patsubst %.S,$(OBJDIR)/%.o,$(SRC)))) +OBJ = $(patsubst %.c,$(OBJDIR)/%.o,$(patsubst %.cpp,$(OBJDIR)/%.cpp.o,$(patsubst %.S,$(OBJDIR)/%.o,$(SRC)))) # Define all listing files. -LST = $(patsubst %.c,$(OBJDIR)/%.lst,$(patsubst %.cpp,$(OBJDIR)/%.lst,$(patsubst %.S,$(OBJDIR)/%.lst,$(SRC)))) +LST = $(patsubst %.c,$(OBJDIR)/%.lst,$(patsubst %.cpp,$(OBJDIR)/%.cpp.lst,$(patsubst %.S,$(OBJDIR)/%.lst,$(SRC)))) # Compiler flags to generate dependency files. @@ -348,9 +348,9 @@ GENDEPFLAGS = -MMD -MP -MF .dep/$(subst /,_,$@).d # Combine all necessary flags and optional flags. # Add target processor to flags. # You can give extra flags at 'make' command line like: make EXTRAFLAGS=-DFOO=bar -ALL_CFLAGS = -mmcu=$(MCU) $(CFLAGS) $(GENDEPFLAGS) $(EXTRAFLAGS) -ALL_CPPFLAGS = -mmcu=$(MCU) -x c++ $(CPPFLAGS) $(GENDEPFLAGS) $(EXTRAFLAGS) -ALL_ASFLAGS = -mmcu=$(MCU) -x assembler-with-cpp $(ASFLAGS) $(EXTRAFLAGS) +ALL_CFLAGS = -mmcu=$(MCU) $(CFLAGS) $(GENDEPFLAGS) $(EXTRAFLAGS) $(EXTRACFLAGS) +ALL_CPPFLAGS = -mmcu=$(MCU) -x c++ $(CPPFLAGS) $(GENDEPFLAGS) $(EXTRAFLAGS) $(EXTRACPPFLAGS) +ALL_ASFLAGS = -mmcu=$(MCU) -x assembler-with-cpp $(ASFLAGS) $(EXTRAFLAGS) $(EXTRAASFLAGS) @@ -561,7 +561,7 @@ $(OBJDIR)/%.o : %.c # Compile: create object files from C++ source files. -$(OBJDIR)/%.o : %.cpp +$(OBJDIR)/%.cpp.o : %.cpp @echo mkdir -p $(@D) @echo $(MSG_COMPILING_CPP) $<