aboutsummaryrefslogtreecommitdiffstats
path: root/arduino/libraries/Bluefruit52Lib/src/services/BLEHidGeneric.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'arduino/libraries/Bluefruit52Lib/src/services/BLEHidGeneric.cpp')
-rwxr-xr-xarduino/libraries/Bluefruit52Lib/src/services/BLEHidGeneric.cpp516
1 files changed, 516 insertions, 0 deletions
diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEHidGeneric.cpp b/arduino/libraries/Bluefruit52Lib/src/services/BLEHidGeneric.cpp
new file mode 100755
index 0000000..a95ddc2
--- /dev/null
+++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEHidGeneric.cpp
@@ -0,0 +1,516 @@
+/**************************************************************************/
+/*!
+ @file BLEHidGeneric.cpp
+ @author hathach (tinyusb.org)
+
+ @section LICENSE
+
+ Software License Agreement (BSD License)
+
+ Copyright (c) 2018, Adafruit Industries (adafruit.com)
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. 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.
+ 3. Neither the name of the copyright holders nor the
+ names of its 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 ''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 HOLDER 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.
+*/
+/**************************************************************************/
+
+#include "bluefruit.h"
+
+enum {
+ REPORT_TYPE_INPUT = 1,
+ REPORT_TYPE_OUTPUT,
+ REPORT_TYPE_FEATURE
+};
+
+BLEHidGeneric::BLEHidGeneric(uint8_t num_input, uint8_t num_output, uint8_t num_feature)
+ : BLEService(UUID16_SVC_HUMAN_INTERFACE_DEVICE), _chr_control(UUID16_CHR_HID_CONTROL_POINT)
+{
+ _has_keyboard = _has_mouse = false;
+ _protocol_mode = HID_PROTOCOL_MODE_REPORT;
+
+ _report_map = NULL;
+ _report_map_len = 0;
+
+ _input_len = _output_len = _feature_len = NULL;
+
+ _num_input = num_input;
+ _num_output = num_output;
+ _num_feature = num_feature;
+
+ // HID Information
+ // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.hid_information.xml
+ // bcd 1.1, country = 0, flag = normal connect
+ _hid_info[0] = 0x01;
+ _hid_info[1] = 0x01;
+ _hid_info[2] = 0x00;
+ _hid_info[3] = bit(1);
+
+ _chr_protocol = NULL;
+ _chr_inputs = _chr_outputs = _chr_features = NULL;
+ _chr_boot_keyboard_input = _chr_boot_keyboard_output = _chr_boot_mouse_input = NULL;
+
+ if ( _num_input )
+ {
+ _chr_inputs = new BLECharacteristic[_num_input];
+ }
+
+ if ( _num_output )
+ {
+ _chr_outputs = new BLECharacteristic[_num_output];
+ }
+
+ if ( _num_feature )
+ {
+ _chr_features = new BLECharacteristic[_num_feature];
+ }
+}
+
+/*------------------------------------------------------------------*/
+/* CONFIG
+ *------------------------------------------------------------------*/
+void BLEHidGeneric::enableKeyboard(bool enable)
+{
+ _has_keyboard = enable;
+}
+
+void BLEHidGeneric::enableMouse(bool enable)
+{
+ _has_mouse = enable;
+}
+
+void BLEHidGeneric::setHidInfo(uint16_t bcd, uint8_t country, uint8_t flags)
+{
+ memcpy(_hid_info, &bcd, 2);
+ _hid_info[2] = country;
+ _hid_info[3] = flags;
+}
+
+void BLEHidGeneric::setReportMap(const uint8_t* report_map, size_t len)
+{
+ _report_map = report_map;
+ _report_map_len = len;
+}
+
+void BLEHidGeneric::setReportLen(uint16_t input_len[], uint16_t output_len[], uint16_t feature_len[])
+{
+ _input_len = input_len;
+ _output_len = output_len;
+ _feature_len = feature_len;
+}
+
+void BLEHidGeneric::setOutputReportCallback(uint8_t reportID, BLECharacteristic::write_cb_t fp)
+{
+ // index is ID-1
+ uint8_t const idx = ( reportID ? (reportID-1) : 0 );
+
+ // report mode
+ if ( idx < _num_output ) _chr_outputs[idx].setWriteCallback(fp);
+}
+
+/*------------------------------------------------------------------*/
+/* Callbacks
+ *------------------------------------------------------------------*/
+void blehid_generic_protocol_mode_cb(BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset)
+{
+ BLEHidGeneric& svc = (BLEHidGeneric&) chr.parentService();
+ svc._protocol_mode = *data;
+
+ LOG_LV2("HID", "Protocol Mode : %d (0 Boot, 1 Report)", *data);
+}
+
+/*------------------------------------------------------------------*/
+/* Begin
+ *------------------------------------------------------------------*/
+err_t BLEHidGeneric::begin(void)
+{
+ VERIFY ( (_report_map != NULL) && _report_map_len, NRF_ERROR_INVALID_PARAM);
+
+ // Invoke base class begin()
+ VERIFY_STATUS( BLEService::begin() );
+
+ // Protocol Mode
+ if ( _has_keyboard || _has_mouse )
+ {
+ _chr_protocol = new BLECharacteristic(UUID16_CHR_PROTOCOL_MODE);
+ VERIFY(_chr_protocol, NRF_ERROR_NO_MEM);
+
+ _chr_protocol->setProperties(CHR_PROPS_READ | CHR_PROPS_WRITE_WO_RESP);
+ _chr_protocol->setFixedLen(1);
+ _chr_protocol->setWriteCallback(blehid_generic_protocol_mode_cb);
+ VERIFY_STATUS( _chr_protocol->begin() );
+ _chr_protocol->write8(_protocol_mode);
+ }
+
+ // Input reports
+ for(uint8_t i=0; i<_num_input; i++)
+ {
+ _chr_inputs[i].setUuid(UUID16_CHR_REPORT);
+ _chr_inputs[i].setProperties(CHR_PROPS_READ | CHR_PROPS_NOTIFY);
+ _chr_inputs[i].setPermission(SECMODE_ENC_NO_MITM, SECMODE_NO_ACCESS);
+ _chr_inputs[i].setReportRefDescriptor(i+1, REPORT_TYPE_INPUT);
+
+ // Input report len is configured, else variable len up to 255
+ if ( _input_len ) _chr_inputs[i].setFixedLen( _input_len[i] );
+
+ VERIFY_STATUS( _chr_inputs[i].begin() );
+ }
+
+ // Output reports
+ for(uint8_t i=0; i<_num_output; i++)
+ {
+ _chr_outputs[i].setUuid(UUID16_CHR_REPORT);
+ _chr_outputs[i].setProperties(CHR_PROPS_READ | CHR_PROPS_WRITE | CHR_PROPS_WRITE_WO_RESP);
+ _chr_outputs[i].setPermission(SECMODE_ENC_NO_MITM, SECMODE_ENC_NO_MITM);
+ _chr_outputs[i].setReportRefDescriptor(i+1, REPORT_TYPE_OUTPUT);
+
+ // Input report len is configured, else variable len up to 255
+ if ( _output_len ) _chr_outputs[i].setFixedLen( _output_len[i] );
+
+ VERIFY_STATUS( _chr_outputs[i].begin() );
+
+ _chr_outputs[i].write8(0);
+ }
+
+ // Report Map (HID Report Descriptor)
+ BLECharacteristic report_map(UUID16_CHR_REPORT_MAP);
+ report_map.setTempMemory();
+ report_map.setProperties(CHR_PROPS_READ);
+ report_map.setPermission(SECMODE_ENC_NO_MITM, SECMODE_NO_ACCESS);
+ report_map.setFixedLen(_report_map_len);
+ VERIFY_STATUS( report_map.begin() );
+ report_map.write(_report_map, _report_map_len);
+
+ // Boot Keyboard Input & Output Report
+ if ( _has_keyboard )
+ {
+ _chr_boot_keyboard_input = new BLECharacteristic(UUID16_CHR_BOOT_KEYBOARD_INPUT_REPORT);
+ _chr_boot_keyboard_input->setProperties(CHR_PROPS_READ | CHR_PROPS_NOTIFY);
+ _chr_boot_keyboard_input->setFixedLen(8); // boot keyboard is 8 bytes
+ _chr_boot_keyboard_input->setPermission(SECMODE_ENC_NO_MITM, SECMODE_NO_ACCESS);
+ VERIFY_STATUS(_chr_boot_keyboard_input->begin());
+
+ _chr_boot_keyboard_output = new BLECharacteristic(UUID16_CHR_BOOT_KEYBOARD_OUTPUT_REPORT);
+ _chr_boot_keyboard_output->setProperties(CHR_PROPS_READ | CHR_PROPS_WRITE | CHR_PROPS_WRITE_WO_RESP);
+ _chr_boot_keyboard_output->setFixedLen(1); // boot keyboard is 1 byte
+ _chr_boot_keyboard_output->setPermission(SECMODE_ENC_NO_MITM, SECMODE_ENC_NO_MITM);
+ VERIFY_STATUS(_chr_boot_keyboard_output->begin());
+ _chr_boot_keyboard_output->write8(0);
+ }
+
+ // Boot Mouse Input Report
+ if ( _has_mouse )
+ {
+ _chr_boot_mouse_input = new BLECharacteristic(UUID16_CHR_BOOT_MOUSE_INPUT_REPORT);
+ _chr_boot_mouse_input->setProperties(CHR_PROPS_READ | CHR_PROPS_NOTIFY);
+ _chr_boot_mouse_input->setFixedLen(sizeof(hid_mouse_report_t));
+ _chr_boot_mouse_input->setPermission(SECMODE_ENC_NO_MITM, SECMODE_NO_ACCESS);
+ VERIFY_STATUS(_chr_boot_mouse_input->begin());
+ }
+
+ // HID Info
+ BLECharacteristic hid_info(UUID16_CHR_HID_INFORMATION);
+ hid_info.setTempMemory();
+ hid_info.setProperties(CHR_PROPS_READ);
+ hid_info.setPermission(SECMODE_ENC_NO_MITM, SECMODE_NO_ACCESS);
+ hid_info.setFixedLen(sizeof(_hid_info));
+ VERIFY_STATUS( hid_info.begin() );
+ hid_info.write(_hid_info, sizeof(_hid_info));
+
+ // HID Control Point
+ _chr_control.setProperties(CHR_PROPS_WRITE_WO_RESP);
+ _chr_control.setPermission(SECMODE_NO_ACCESS, SECMODE_ENC_NO_MITM);
+ _chr_control.setFixedLen(1);
+ VERIFY_STATUS( _chr_control.begin() );
+ _chr_control.write8(0);
+
+ return ERROR_NONE;
+}
+
+/*------------------------------------------------------------------*/
+/* Input Report
+ *------------------------------------------------------------------*/
+bool BLEHidGeneric::inputReport(uint8_t reportID, void const* data, int len)
+{
+ // index is ID-1
+ uint8_t const idx = ( reportID ? (reportID-1) : 0 );
+
+ return _chr_inputs[idx].notify( (uint8_t const*) data, len);
+}
+
+bool BLEHidGeneric::bootKeyboardReport(void const* data, int len)
+{
+ return _chr_boot_keyboard_input->notify(data, len);
+}
+
+bool BLEHidGeneric::bootMouseReport(void const* data, int len)
+{
+ return _chr_boot_mouse_input->notify(data, len);
+}
+
+/*------------------------------------------------------------------*/
+/* Ascii to Keycode
+ *------------------------------------------------------------------*/
+const hid_ascii_to_keycode_entry_t HID_ASCII_TO_KEYCODE[128] =
+{
+ {0, 0 }, // 0x00 Null
+ {0, 0 }, // 0x01
+ {0, 0 }, // 0x02
+ {0, 0 }, // 0x03
+ {0, 0 }, // 0x04
+ {0, 0 }, // 0x05
+ {0, 0 }, // 0x06
+ {0, 0 }, // 0x07
+ {0, HID_KEY_BACKSPACE }, // 0x08 Backspace
+ {0, HID_KEY_TAB }, // 0x09 Horizontal Tab
+ {0, HID_KEY_RETURN }, // 0x0A Line Feed
+ {0, 0 }, // 0x0B
+ {0, 0 }, // 0x0C
+ {0, HID_KEY_RETURN }, // 0x0D Carriage return
+ {0, 0 }, // 0x0E
+ {0, 0 }, // 0x0F
+ {0, 0 }, // 0x10
+ {0, 0 }, // 0x11
+ {0, 0 }, // 0x12
+ {0, 0 }, // 0x13
+ {0, 0 }, // 0x14
+ {0, 0 }, // 0x15
+ {0, 0 }, // 0x16
+ {0, 0 }, // 0x17
+ {0, 0 }, // 0x18
+ {0, 0 }, // 0x19
+ {0, 0 }, // 0x1A
+ {0, HID_KEY_ESCAPE }, // 0x1B Escape
+ {0, 0 }, // 0x1C
+ {0, 0 }, // 0x1D
+ {0, 0 }, // 0x1E
+ {0, 0 }, // 0x1F
+
+ {0, HID_KEY_SPACE }, // 0x20
+ {1, HID_KEY_1 }, // 0x21 !
+ {1, HID_KEY_APOSTROPHE }, // 0x22 "
+ {1, HID_KEY_3 }, // 0x23 #
+ {1, HID_KEY_4 }, // 0x24 $
+ {1, HID_KEY_5 }, // 0x25 %
+ {1, HID_KEY_7 }, // 0x26 &
+ {0, HID_KEY_APOSTROPHE }, // 0x27 '
+ {1, HID_KEY_9 }, // 0x28 (
+ {1, HID_KEY_0 }, // 0x29 )
+ {1, HID_KEY_8 }, // 0x2A *
+ {1, HID_KEY_EQUAL }, // 0x2B +
+ {0, HID_KEY_COMMA }, // 0x2C ,
+ {0, HID_KEY_MINUS }, // 0x2D -
+ {0, HID_KEY_PERIOD }, // 0x2E .
+ {0, HID_KEY_SLASH }, // 0x2F /
+ {0, HID_KEY_0 }, // 0x30 0
+ {0, HID_KEY_1 }, // 0x31 1
+ {0, HID_KEY_2 }, // 0x32 2
+ {0, HID_KEY_3 }, // 0x33 3
+ {0, HID_KEY_4 }, // 0x34 4
+ {0, HID_KEY_5 }, // 0x35 5
+ {0, HID_KEY_6 }, // 0x36 6
+ {0, HID_KEY_7 }, // 0x37 7
+ {0, HID_KEY_8 }, // 0x38 8
+ {0, HID_KEY_9 }, // 0x39 9
+ {1, HID_KEY_SEMICOLON }, // 0x3A :
+ {0, HID_KEY_SEMICOLON }, // 0x3B ;
+ {1, HID_KEY_COMMA }, // 0x3C <
+ {0, HID_KEY_EQUAL }, // 0x3D =
+ {1, HID_KEY_PERIOD }, // 0x3E >
+ {1, HID_KEY_SLASH }, // 0x3F ?
+
+ {1, HID_KEY_2 }, // 0x40 @
+ {1, HID_KEY_A }, // 0x41 A
+ {1, HID_KEY_B }, // 0x42 B
+ {1, HID_KEY_C }, // 0x43 C
+ {1, HID_KEY_D }, // 0x44 D
+ {1, HID_KEY_E }, // 0x45 E
+ {1, HID_KEY_F }, // 0x46 F
+ {1, HID_KEY_G }, // 0x47 G
+ {1, HID_KEY_H }, // 0x48 H
+ {1, HID_KEY_I }, // 0x49 I
+ {1, HID_KEY_J }, // 0x4A J
+ {1, HID_KEY_K }, // 0x4B K
+ {1, HID_KEY_L }, // 0x4C L
+ {1, HID_KEY_M }, // 0x4D M
+ {1, HID_KEY_N }, // 0x4E N
+ {1, HID_KEY_O }, // 0x4F O
+ {1, HID_KEY_P }, // 0x50 P
+ {1, HID_KEY_Q }, // 0x51 Q
+ {1, HID_KEY_R }, // 0x52 R
+ {1, HID_KEY_S }, // 0x53 S
+ {1, HID_KEY_T }, // 0x55 T
+ {1, HID_KEY_U }, // 0x55 U
+ {1, HID_KEY_V }, // 0x56 V
+ {1, HID_KEY_W }, // 0x57 W
+ {1, HID_KEY_X }, // 0x58 X
+ {1, HID_KEY_Y }, // 0x59 Y
+ {1, HID_KEY_Z }, // 0x5A Z
+ {0, HID_KEY_BRACKET_LEFT }, // 0x5B [
+ {0, HID_KEY_BACKSLASH }, // 0x5C '\'
+ {0, HID_KEY_BRACKET_RIGHT }, // 0x5D ]
+ {1, HID_KEY_6 }, // 0x5E ^
+ {1, HID_KEY_MINUS }, // 0x5F _
+
+ {0, HID_KEY_GRAVE }, // 0x60 `
+ {0, HID_KEY_A }, // 0x61 a
+ {0, HID_KEY_B }, // 0x62 b
+ {0, HID_KEY_C }, // 0x63 c
+ {0, HID_KEY_D }, // 0x66 d
+ {0, HID_KEY_E }, // 0x65 e
+ {0, HID_KEY_F }, // 0x66 f
+ {0, HID_KEY_G }, // 0x67 g
+ {0, HID_KEY_H }, // 0x68 h
+ {0, HID_KEY_I }, // 0x69 i
+ {0, HID_KEY_J }, // 0x6A j
+ {0, HID_KEY_K }, // 0x6B k
+ {0, HID_KEY_L }, // 0x6C l
+ {0, HID_KEY_M }, // 0x6D m
+ {0, HID_KEY_N }, // 0x6E n
+ {0, HID_KEY_O }, // 0x6F o
+ {0, HID_KEY_P }, // 0x70 p
+ {0, HID_KEY_Q }, // 0x71 q
+ {0, HID_KEY_R }, // 0x72 r
+ {0, HID_KEY_S }, // 0x73 s
+ {0, HID_KEY_T }, // 0x75 t
+ {0, HID_KEY_U }, // 0x75 u
+ {0, HID_KEY_V }, // 0x76 v
+ {0, HID_KEY_W }, // 0x77 w
+ {0, HID_KEY_X }, // 0x78 x
+ {0, HID_KEY_Y }, // 0x79 y
+ {0, HID_KEY_Z }, // 0x7A z
+ {1, HID_KEY_BRACKET_LEFT }, // 0x7B {
+ {1, HID_KEY_BACKSLASH }, // 0x7C |
+ {1, HID_KEY_BRACKET_RIGHT }, // 0x7D }
+ {1, HID_KEY_GRAVE }, // 0x7E ~
+ {0, HID_KEY_DELETE } // 0x7F Delete
+};
+
+/*------------------------------------------------------------------*/
+/* Keycode to Ascii
+ *------------------------------------------------------------------*/
+const hid_keycode_to_ascii_t HID_KEYCODE_TO_ASCII[128] =
+{
+ {0 , 0 }, // 0x00
+ {0 , 0 }, // 0x01
+ {0 , 0 }, // 0x02
+ {0 , 0 }, // 0x03
+ {'a' , 'A' }, // 0x04
+ {'b' , 'B' }, // 0x05
+ {'c' , 'C' }, // 0x06
+ {'d' , 'D' }, // 0x07
+ {'e' , 'E' }, // 0x08
+ {'f' , 'F' }, // 0x09
+ {'g' , 'G' }, // 0x0a
+ {'h' , 'H' }, // 0x0b
+ {'i' , 'I' }, // 0x0c
+ {'j' , 'J' }, // 0x0d
+ {'k' , 'K' }, // 0x0e
+ {'l' , 'L' }, // 0x0f
+ {'m' , 'M' }, // 0x10
+ {'n' , 'N' }, // 0x11
+ {'o' , 'O' }, // 0x12
+ {'p' , 'P' }, // 0x13
+ {'q' , 'Q' }, // 0x14
+ {'r' , 'R' }, // 0x15
+ {'s' , 'S' }, // 0x16
+ {'t' , 'T' }, // 0x17
+ {'u' , 'U' }, // 0x18
+ {'v' , 'V' }, // 0x19
+ {'w' , 'W' }, // 0x1a
+ {'x' , 'X' }, // 0x1b
+ {'y' , 'Y' }, // 0x1c
+ {'z' , 'Z' }, // 0x1d
+ {'1' , '!' }, // 0x1e
+ {'2' , '@' }, // 0x1f
+ {'3' , '#' }, // 0x20
+ {'4' , '$' }, // 0x21
+ {'5' , '%' }, // 0x22
+ {'6' , '^' }, // 0x23
+ {'7' , '&' }, // 0x24
+ {'8' , '*' }, // 0x25
+ {'9' , '(' }, // 0x26
+ {'0' , ')' }, // 0x27
+ {'\r' , '\r' }, // 0x28
+ {'\x1b', '\x1b' }, // 0x29
+ {'\b' , '\b' }, // 0x2a
+ {'\t' , '\t' }, // 0x2b
+ {' ' , ' ' }, // 0x2c
+ {'-' , '_' }, // 0x2d
+ {'=' , '+' }, // 0x2e
+ {'[' , '{' }, // 0x2f
+ {']' , '}' }, // 0x30
+ {'\\' , '|' }, // 0x31
+ {'#' , '~' }, // 0x32
+ {';' , ':' }, // 0x33
+ {'\'' , '\"' }, // 0x34
+ {0 , 0 }, // 0x35
+ {',' , '<' }, // 0x36
+ {'.' , '>' }, // 0x37
+ {'/' , '?' }, // 0x38
+
+ {0 , 0 }, // 0x39
+ {0 , 0 }, // 0x3a
+ {0 , 0 }, // 0x3b
+ {0 , 0 }, // 0x3c
+ {0 , 0 }, // 0x3d
+ {0 , 0 }, // 0x3e
+ {0 , 0 }, // 0x3f
+ {0 , 0 }, // 0x40
+ {0 , 0 }, // 0x41
+ {0 , 0 }, // 0x42
+ {0 , 0 }, // 0x43
+ {0 , 0 }, // 0x44
+ {0 , 0 }, // 0x45
+ {0 , 0 }, // 0x46
+ {0 , 0 }, // 0x47
+ {0 , 0 }, // 0x48
+ {0 , 0 }, // 0x49
+ {0 , 0 }, // 0x4a
+ {0 , 0 }, // 0x4b
+ {0 , 0 }, // 0x4c
+ {0 , 0 }, // 0x4d
+ {0 , 0 }, // 0x4e
+ {0 , 0 }, // 0x4f
+ {0 , 0 }, // 0x50
+ {0 , 0 }, // 0x51
+ {0 , 0 }, // 0x52
+ {0 , 0 }, // 0x53
+
+ {'/' , '/' }, // 0x54
+ {'*' , '*' }, // 0x55
+ {'-' , '-' }, // 0x56
+ {'+' , '+' }, // 0x57
+ {'\r' , '\r' }, // 0x58
+ {'1' , 0 }, // 0x59 /* numpad1 & end */ \
+ {'2' , 0 }, // 0x5a
+ {'3' , 0 }, // 0x5b
+ {'4' , 0 }, // 0x5c
+ {'5' , '5' }, // 0x5d
+ {'6' , 0 }, // 0x5e
+ {'7' , 0 }, // 0x5f
+ {'8' , 0 }, // 0x60
+ {'9' , 0 }, // 0x61
+ {'0' , 0 }, // 0x62
+ {'0' , 0 }, // 0x63
+ {'=' , '=' }, // 0x67
+};