diff --git a/build/gcc/Makefile b/build/gcc/Makefile index 81e1a77a..4844cbc1 100644 --- a/build/gcc/Makefile +++ b/build/gcc/Makefile @@ -137,8 +137,10 @@ SRC = ../../src/main.c \ SRC += ../../src/keybi/keybi.c \ ../../src/keybi/hid_keyboard.c \ ../../src/keybi/hid_mouse.c \ - ../../src/keybi/tests.c \ - ../../src/keybi/drivers/pmw3360.c + ../../src/keybi/keymap.c \ + ../../src/keybi/drivers/delay.c \ + ../../src/keybi/drivers/pmw3360.c \ + ../../src/keybi/drivers/matrix.c # List C source files here which must be compiled in ARM-Mode. diff --git a/build/gcc/stm32.ld b/build/gcc/stm32.ld index 8277247f..ce6cddee 100644 --- a/build/gcc/stm32.ld +++ b/build/gcc/stm32.ld @@ -13,7 +13,7 @@ INCLUDE "./STM32_COMMON.ld" MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K /* also change _estack below */ - FLASH (rx) : ORIGIN = 0x8002000, LENGTH = 110K-8K + FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 110K USER (rx) : ORIGIN = 0x8000000 + 110K, LENGTH = 128K-110K FLASHB1 (rx) : ORIGIN = 0x00000000, LENGTH = 0 EXTMEMB0 (rx) : ORIGIN = 0x00000000, LENGTH = 0 diff --git a/src/inc/platform_config.h b/src/inc/platform_config.h index 50ff0c1a..c8094b2b 100644 --- a/src/inc/platform_config.h +++ b/src/inc/platform_config.h @@ -49,10 +49,12 @@ /* Define the STM32F10x hardware depending on the used evaluation board */ #ifdef USE_STM3210B_EVAL -#define USB_DISCONNECT GPIOA -#define USB_DISCONNECT_PIN GPIO_Pin_15 // Use GPIO_Pin_10 +// FIXME changed for patched keybi board, revert to PA15 like on nkpro +// (routed without USB_EN / DISCONNECT, but had trouble enumerating without it +#define USB_DISCONNECT GPIOC +#define USB_DISCONNECT_PIN GPIO_Pin_8 // Use GPIO_Pin_10 // for older PCB -#define RCC_APB2Periph_GPIO_DISCONNECT RCC_APB2Periph_GPIOA +#define RCC_APB2Periph_GPIO_DISCONNECT RCC_APB2Periph_GPIOC // smartcard power supply diff --git a/src/keybi/drivers/delay.c b/src/keybi/drivers/delay.c new file mode 100644 index 00000000..3307a788 --- /dev/null +++ b/src/keybi/drivers/delay.c @@ -0,0 +1,22 @@ +#include "keybi/drivers/delay.h" +#include "stm32f10x_systick.h" + +void Keybi_DelayUs(uint32_t duration_us) { + uint32_t remaining_ticks = duration_us * 72; + uint32_t prev = SysTick_GetCounter(); + + while (1) { + uint32_t curr = SysTick_GetCounter(); + uint32_t elapsed; + if (curr > prev) { + elapsed = 719999 + prev - curr; + } else { + elapsed = prev - curr; + } + if (elapsed >= remaining_ticks) { + break; + } + remaining_ticks -= elapsed; + prev = curr; + } +} diff --git a/src/keybi/drivers/delay.h b/src/keybi/drivers/delay.h new file mode 100644 index 00000000..ce3759be --- /dev/null +++ b/src/keybi/drivers/delay.h @@ -0,0 +1,5 @@ +#pragma once + +#include "stm32f10x.h" + +void Keybi_DelayUs(uint32_t duration_us); diff --git a/src/keybi/drivers/matrix.c b/src/keybi/drivers/matrix.c new file mode 100644 index 00000000..ab0a6251 --- /dev/null +++ b/src/keybi/drivers/matrix.c @@ -0,0 +1,80 @@ +#include "keybi/drivers/matrix.h" +#include "keybi/drivers/delay.h" + +typedef struct gpio_t { + GPIO_TypeDef * port; + uint16_t pin; +} gpio_t; + +static gpio_t rows[KEYBI_MATRIX_ROWS] = { + {GPIOC, GPIO_Pin_3}, // R01 + {GPIOA, GPIO_Pin_0}, // R02 + {GPIOA, GPIO_Pin_1}, // R03 + {GPIOC, GPIO_Pin_4}, // R04 + {GPIOC, GPIO_Pin_5}}; // R05 + +static gpio_t cols[KEYBI_MATRIX_COLS] = { + {GPIOB, GPIO_Pin_7}, // C01 + {GPIOB, GPIO_Pin_8}, // C02 + {GPIOB, GPIO_Pin_9}, // C03 + {GPIOC, GPIO_Pin_0}, // C04 + {GPIOC, GPIO_Pin_1}, // C05 + {GPIOC, GPIO_Pin_2}, // C06 + {GPIOA, GPIO_Pin_15}, // C07 + {GPIOC, GPIO_Pin_10}, // C08 + {GPIOC, GPIO_Pin_11}, // C09 + {GPIOC, GPIO_Pin_12}, // C10 + {GPIOA, GPIO_Pin_10}, // C11 + {GPIOA, GPIO_Pin_9}, // C12 + {GPIOC, GPIO_Pin_9}, // C13 + {GPIOB, GPIO_Pin_1}}; // C14 + +static uint8_t key_states[KEYBI_MATRIX_COLS][KEYBI_MATRIX_ROWS] = {0}; + +void Keybi_Matrix_Init(void) { + + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); + + for (int r = 0; r < KEYBI_MATRIX_ROWS; ++r) { + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = rows[r].pin; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init(rows[r].port, &GPIO_InitStructure); + } + + for (int c = 0; c < KEYBI_MATRIX_COLS; ++c) { + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = cols[c].pin; + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_Init(cols[c].port, &GPIO_InitStructure); + } +} + +void Keybi_Matrix_Scan(int (*callback)(keybi_keyboard_matrix_event_t)) { + for (uint8_t c = 0; c < KEYBI_MATRIX_COLS; ++c) { + GPIO_SetBits(cols[c].port, cols[c].pin); + for (uint8_t r = 0; r < KEYBI_MATRIX_ROWS; ++r) { + uint8_t state = GPIO_ReadInputDataBit(rows[r].port, rows[r].pin); + if (state != key_states[c][r]) { + keybi_keyboard_matrix_event_t event = { + .col = c, + .row = r, + .pressed = state, + .time = 0 + }; + if (callback(event) == 0) { + // Save state if callback has been able to handle it + key_states[c][r] = state; + } + } + } + GPIO_ResetBits(cols[c].port, cols[c].pin); + // Adjacent columns of the first rows registered on a single key press + // TODO investigate why and find the appropriate delay + Keybi_DelayUs(100); + } +} diff --git a/src/keybi/drivers/matrix.h b/src/keybi/drivers/matrix.h new file mode 100644 index 00000000..d7a396e0 --- /dev/null +++ b/src/keybi/drivers/matrix.h @@ -0,0 +1,16 @@ +#pragma once + +#include "stm32f10x.h" + +#define KEYBI_MATRIX_ROWS (5) +#define KEYBI_MATRIX_COLS (14) + +typedef struct { + uint8_t col; + uint8_t row; + uint8_t pressed; + uint16_t time; +} keybi_keyboard_matrix_event_t; + +void Keybi_Matrix_Init(void); +void Keybi_Matrix_Scan(int (*callback)(keybi_keyboard_matrix_event_t)); diff --git a/src/keybi/drivers/pmw3360.c b/src/keybi/drivers/pmw3360.c index 59e3c700..d02c37a2 100644 --- a/src/keybi/drivers/pmw3360.c +++ b/src/keybi/drivers/pmw3360.c @@ -8,7 +8,7 @@ #include "keybi/drivers/pmw3360.h" #include "stm32f10x_spi.h" -#include "stm32f10x_systick.h" +#include "keybi/drivers/delay.h" // Registers #define Product_ID 0x00 @@ -68,7 +68,6 @@ int init_complete = 0; static const unsigned char firmware_data[4094]; static void performStartup(void); -static void delayMicroseconds(uint32_t delay_us); static byte adns_read_reg(byte reg_addr); static void adns_write_reg(byte reg_addr, byte data); @@ -111,7 +110,7 @@ int Keybi_Pmw3360_Init() { performStartup(); - delayMicroseconds(1000 * 1000); // 1s (TODO was 5s but 1s is still too long I guess) + Keybi_DelayUs(1000 * 1000); // 1s (TODO was 5s but 1s is still too long I guess) uint8_t product_id = adns_read_reg(Product_ID); uint8_t inverse_product_id = adns_read_reg(Inverse_Product_ID); @@ -136,29 +135,6 @@ void Keybi_Pmw3360_Read(keybi_pmw3360_motion_t * motion) { } } -static void delayMicroseconds(uint32_t delay_us) -{ - uint32_t remaining_ticks = delay_us * 72; - - uint32_t prev = SysTick_GetCounter(); - - while (1) - { - uint32_t curr = SysTick_GetCounter(); - uint32_t elapsed; - if (curr > prev) { - elapsed = prev; // FIXME don’t know initial value - } else { - elapsed = prev - curr; - } - if (elapsed > remaining_ticks) { - break; - } - remaining_ticks -= elapsed; - prev = curr; - } -} - static uint8_t Keybi_Pmw3360_SpiTransfer(uint8_t data); static uint8_t Keybi_Pmw3360_SpiTransfer(uint8_t data) { @@ -186,13 +162,13 @@ static byte adns_read_reg(byte reg_addr){ // send adress of the register, with MSBit = 0 to indicate it's a read Keybi_Pmw3360_SpiTransfer(reg_addr & 0x7f ); - delayMicroseconds(100); // tSRAD + Keybi_DelayUs(100); // tSRAD // read data byte data = Keybi_Pmw3360_SpiTransfer(0); - delayMicroseconds(1); // tSCLK-NCS for read operation is 120ns + Keybi_DelayUs(1); // tSCLK-NCS for read operation is 120ns adns_com_end(); - delayMicroseconds(19); // tSRW/tSRR (=20us) minus tSCLK-NCS + Keybi_DelayUs(19); // tSRW/tSRR (=20us) minus tSCLK-NCS return data; } @@ -205,9 +181,9 @@ static void adns_write_reg(byte reg_addr, byte data){ //sent data Keybi_Pmw3360_SpiTransfer(data); - delayMicroseconds(20); // tSCLK-NCS for write operation + Keybi_DelayUs(20); // tSCLK-NCS for write operation adns_com_end(); - delayMicroseconds(100); // tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound + Keybi_DelayUs(100); // tSWW/tSWR (=120us) minus tSCLK-NCS. Could be shortened, but is looks like a safe lower bound } static void adns_upload_firmware(void); @@ -222,7 +198,7 @@ static void adns_upload_firmware(){ adns_write_reg(SROM_Enable, 0x1d); // wait for more than one frame period - delayMicroseconds(10 * 1000); // assume that the frame rate is as low as 100fps... even if it should never be that low + Keybi_DelayUs(10 * 1000); // assume that the frame rate is as low as 100fps... even if it should never be that low // write 0x18 to SROM_enable to start SROM download adns_write_reg(SROM_Enable, 0x18); @@ -230,12 +206,12 @@ static void adns_upload_firmware(){ // write the SROM file (=firmware data) adns_com_begin(); Keybi_Pmw3360_SpiTransfer(SROM_Load_Burst | 0x80); // write burst destination adress - delayMicroseconds(15); + Keybi_DelayUs(15); // send all bytes of the firmware for(int i = 0; i < sizeof(firmware_data); i++){ Keybi_Pmw3360_SpiTransfer(firmware_data[i]); - delayMicroseconds(15); + Keybi_DelayUs(15); } //Read the SROM_ID register to verify the ID before any other register reads or writes. @@ -256,7 +232,7 @@ static void performStartup(void){ adns_com_begin(); // ensure that the serial port is reset adns_com_end(); // ensure that the serial port is reset adns_write_reg(Power_Up_Reset, 0x5a); // force reset - delayMicroseconds(50 * 1000); // wait for it to reboot + Keybi_DelayUs(50 * 1000); // wait for it to reboot // read registers 0x02 to 0x06 (and discard the data) adns_read_reg(Motion); adns_read_reg(Delta_X_L); @@ -265,7 +241,7 @@ static void performStartup(void){ adns_read_reg(Delta_Y_H); // upload the firmware adns_upload_firmware(); - delayMicroseconds(10 * 1000); + Keybi_DelayUs(10 * 1000); } static const unsigned char firmware_data[] = { diff --git a/src/keybi/hid_keyboard.c b/src/keybi/hid_keyboard.c index 7892d434..630d64ec 100644 --- a/src/keybi/hid_keyboard.c +++ b/src/keybi/hid_keyboard.c @@ -1,4 +1,5 @@ #include "keybi/hid_keyboard.h" +#include "keybi/qmk/keycode.h" #include "CCIDHID_usb_conf.h" #include "CCIDHID_usb_desc.h" @@ -100,3 +101,59 @@ uint8_t* Keybi_Keyboard_GetHIDDescriptor(uint16_t Length) { return Standard_GetDescriptorData(Length, &Keybi_Keyboard_Hid_Descriptor); } + +int Keybi_Keyboard_QueueEvents(keybi_keyboard_event_queue_t * queue, keybi_keyboard_event_t * events, uint8_t count) { + if (queue->size + count > queue->capacity) { + return -1; + } + for (int i = 0; i < count; ++i) { + unsigned tail = (queue->head + queue->size) % queue->capacity; + queue->events[tail] = events[i]; + queue->size++; + } + return 0; +} + +int Keybi_Keyboard_QueueEvent(keybi_keyboard_event_queue_t * queue, keybi_keyboard_event_t event) { + return Keybi_Keyboard_QueueEvents(queue, &event, 1); +} + +int Keybi_Keyboard_QueueToReport(keybi_keyboard_event_queue_t * queue, uint8_t * report) { + if (queue->size == 0) { + return 0; + } + keybi_keyboard_event_t * event = &queue->events[queue->head]; + queue->head = (queue->head + 1) % queue->capacity; + queue->size--; + if (IS_KEY(event->keycode)) { + if (event->pressed) { + // Do not add a pressed event if already pressed + for (int i = 2; i < 8; ++i) { + if (report[i] == event->keycode) { + return 1; + } + } + // Add the pressed event to the first available report byte + for (int i = 2; i < 8; ++i) { + if (report[i] == 0) { + report[i] = event->keycode; + return 1; + } + } + } else { + for (int i = 2; i < 8; ++i) { + if (report[i] == event->keycode) { + report[i] = 0; + return 1; + } + } + } + } else if (IS_MOD(event->keycode)) { + if (event->pressed) { + report[0] |= MOD_BIT(event->keycode); + } else { + report[0] &= ~MOD_BIT(event->keycode); + } + } + return 1; +} diff --git a/src/keybi/hid_keyboard.h b/src/keybi/hid_keyboard.h index 10ca523f..12b64443 100644 --- a/src/keybi/hid_keyboard.h +++ b/src/keybi/hid_keyboard.h @@ -16,3 +16,20 @@ uint8_t* Keybi_Keyboard_GetReportDescriptor(uint16_t Length); uint8_t* Keybi_Keyboard_GetHIDDescriptor(uint16_t Length); void Keybi_Keyboard_SendReportCompleted(void); + +typedef struct { + uint8_t keycode; + uint8_t pressed; +} keybi_keyboard_event_t; + +typedef struct { + keybi_keyboard_event_t* events; + uint32_t head; + uint32_t size; + uint32_t capacity; +} keybi_keyboard_event_queue_t; + +int Keybi_Keyboard_QueueEvents(keybi_keyboard_event_queue_t * queue, keybi_keyboard_event_t * event, uint8_t count); +int Keybi_Keyboard_QueueEvent(keybi_keyboard_event_queue_t * queue, keybi_keyboard_event_t event); + +int Keybi_Keyboard_QueueToReport(keybi_keyboard_event_queue_t * queue, uint8_t * report); diff --git a/src/keybi/keybi.c b/src/keybi/keybi.c index ee83c59f..bf189df3 100644 --- a/src/keybi/keybi.c +++ b/src/keybi/keybi.c @@ -1,53 +1,95 @@ #include "keybi/keybi.h" #include "keybi/hid_keyboard.h" #include "keybi/hid_mouse.h" -#include "keybi/tests.h" #include "keybi/drivers/pmw3360.h" +#include "keybi/drivers/matrix.h" +#include "keybi/keymap.h" void Keybi_Init(void) { - Keybi_Tests_Run(); - - // Use the Nucleo user button as the only keyboard switch for now - - GPIO_InitTypeDef GPIO_InitStructure; - - RCC_APB2PeriphClockCmd (NUCLEO_BTN_PERIPH, ENABLE); - - GPIO_InitStructure.GPIO_Pin = NUCLEO_BTN_PIN; - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; - GPIO_Init(NUCLEO_BTN_PIN_PORT, &GPIO_InitStructure); - Keybi_Pmw3360_Init(); + Keybi_Matrix_Init(); +} + +static int8_t SignedByteClamp(int value) { + if (value < -128) { + return -128; + } + if (value > 127) { + return 127; + } + return value; } +int32_t move_x = 0; +int32_t move_y = 0; +int32_t scroll_v = 0; +int32_t scroll_h = 0; + +#define MOVE_X_SCALE (3.f / 4.f) +#define MOVE_Y_SCALE (1.f / 2.f) +#define SCROLL_V_SCALE (-MOVE_Y_SCALE / 30.0) +#define SCROLL_H_SCALE (MOVE_X_SCALE / 30.0) void Keybi_MainLoop(void) { - static uint8_t prev_switch_pressed = 0; static keybi_pmw3360_motion_t trackball_motion; - static uint8_t keyboard_report[8] = {0}; - static uint8_t mouse_report[5] = {0}; + static uint8_t keyboard_hid_report[8] = {0}; + static uint8_t mouse_hid_report[5] = {0}; + + Keybi_Matrix_Scan(&Keybi_Keymap_EventHandler); + while (Keybi_Keyboard_QueueToReport(&keybi_keymap_events, keyboard_hid_report)) { + Keybi_Keyboard_SendReport(keyboard_hid_report); + } + + Keybi_Pmw3360_Read(&trackball_motion); + if (trackball_motion.dx != 0 || trackball_motion.dy != 0) { + if (keybi_keyboard_layer != L_MOUSE) { + keybi_keyboard_layer = L_MOUSE; + keybi_mouse_is_scrolling = 0; + } + if (!keybi_mouse_is_scrolling) { + + move_x += trackball_motion.dy; + move_y += trackball_motion.dx; + + int8_t dx = SignedByteClamp(move_x * MOVE_X_SCALE); + int8_t dy = SignedByteClamp(move_y * MOVE_Y_SCALE); + + if (dx != 0 || dy != 0) { + mouse_hid_report[1] = dx; + mouse_hid_report[2] = dy; + mouse_hid_report[3] = 0; + mouse_hid_report[4] = 0; + Keybi_Mouse_SendReport(mouse_hid_report); + move_x -= dx / MOVE_X_SCALE; + move_y -= dy / MOVE_Y_SCALE; + } + } else { - uint8_t switch_pressed = !GPIO_ReadInputDataBit(NUCLEO_BTN_PIN_PORT, NUCLEO_BTN_PIN); + scroll_h += trackball_motion.dy; + scroll_v += trackball_motion.dx; + int8_t dh = SignedByteClamp(scroll_h * SCROLL_H_SCALE); + int8_t dv = SignedByteClamp(scroll_v * SCROLL_V_SCALE); - if (switch_pressed && !prev_switch_pressed) { - // press and release key - keyboard_report[2] = 'a' - 93; - Keybi_Keyboard_SendReport(keyboard_report); - keyboard_report[2] = 0; - Keybi_Keyboard_SendReport(keyboard_report); + if (dh != 0 || dv != 0) { + mouse_hid_report[1] = 0; + mouse_hid_report[2] = 0; + mouse_hid_report[3] = dv; + mouse_hid_report[4] = dh; + Keybi_Mouse_SendReport(mouse_hid_report); + scroll_h -= dh / SCROLL_H_SCALE; + scroll_v -= dv / SCROLL_V_SCALE; + } + } } - prev_switch_pressed = switch_pressed; - - if (switch_pressed) { - Keybi_Pmw3360_Read(&trackball_motion); - if (trackball_motion.dx != 0 || trackball_motion.dy != 0) { - mouse_report[1] = trackball_motion.dx; - mouse_report[2] = trackball_motion.dy; - Keybi_Mouse_SendReport(mouse_report); - } + if (mouse_hid_report[0] != keybi_mouse_buttons) { + mouse_hid_report[0] = keybi_mouse_buttons; + mouse_hid_report[1] = 0; + mouse_hid_report[2] = 0; + mouse_hid_report[3] = 0; + mouse_hid_report[4] = 0; + Keybi_Mouse_SendReport(mouse_hid_report); } } diff --git a/src/keybi/keymap.c b/src/keybi/keymap.c new file mode 100644 index 00000000..438cbfc8 --- /dev/null +++ b/src/keybi/keymap.c @@ -0,0 +1,256 @@ +#include "keybi/keymap.h" +#include "keybi/drivers/matrix.h" + +// Quick and dirty way to get a keymap behaving like a previous experiment +// using QMK. Needs to be cleaned up. + +// TODO progmem? + +#define SAFE_RANGE (0x100) +#define ____ KC_TRNS +#define XXXX KC_NO + +#define OOOO CL_MOUSE_OUT + +#define LAYOUT( \ + e00, e01, e02, e03, e04, e05, e99, e06, e07, e08, e09, e10, e11, e12, e13, \ + d00, d01, d02, d03, d04, d05, d06, d07, d08, d09, d10, d11, d12, d13, \ + c00, c01, c02, c03, c04, c05, c07, c08, c09, c10, c11, c12, c13, \ + b00, b01, b02, b03, b04, b05, b06, b07, b08, b09, b10, b11, b12, b13, \ + a00, a02, a03, a04, a05, a06, a07, a08, a09, a10, a13 \ +) \ +{ \ + { e00, e01, e02, e03, e04, e05, e99, e07, e08, e09, e10, e11, e12, e13}, \ + { d00, d01, d02, d03, d04, d05, e06, d07, d08, d09, d10, d11, d12, d13}, \ + { c00, c01, c02, c03, c04, c05, d06, c07, c08, c09, c10, c11, c12, c13}, \ + { b00, b01, b02, b03, b04, b05, b06, b07, b08, b09, b10, b11, b12, b13}, \ + { a00, KC_NO, a02, a03, a04, a05, a06, a07, a08, a09, a10, KC_NO, KC_NO, a13} \ +} + +const uint16_t keymaps[][KEYBI_MATRIX_ROWS][KEYBI_MATRIX_COLS] = { + + /* Layer 0: Default Layer + * ,---------------------------. ,-----------------------------------. + * | ` | 1| 2| 3| 4| 5| | | 6| 7| 8| 9| 0| -| =| Bksp| + * |---------------------------' `-----------------------------------| + * |Tab | Q| W| E| R| T| | Y| U| I| O| P| [| ]| | + * |--------------------------. `---------------------------------| + * |Esc | A| S| D| F| G| | H| J| K| L| ;| '| Enter| + * |----------------------------. ,---. ,`--------------------------------| + * |Shift | Z| X| C| V| B| | ↑ | | | N| M| ,| .| /| Shift| + * |----------------------------------------------------------------------| + * |Ctrl | |Alt |Cmd |Spc | ← | ↓ | → | Fn| Alt| Cmd| | Ctrl| + * `-----' `-----------------------------------------' `-----' + */ + [L_BASE] = LAYOUT( /* Base */ + KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, XXXX, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, \ + KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_NUHS, \ + CL_CODE_SWITCH, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, \ + KC_LSHIFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_UP, KC_NUBS, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSHIFT, \ + KC_LCTL, KC_LALT, CL_CMD_CTRL, KC_SPC, KC_LEFT, KC_DOWN, KC_RIGHT, CL_FN_SWITCH, KC_RALT, KC_RGUI, KC_RCTL \ + ), + [L_FN] = LAYOUT( /* Fn, mostly used for navigation and window management */ + KC_PWR, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_NO, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_DELETE, \ + ____, ____, ____, ____, ____, ____, KC_HOME, KC_PGDN, KC_PGUP, KC_END, ____, ____, ____, ____, \ + ____, ____, ____, ____, ____, ____, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT, ____, ____, ____, \ + XXXX, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, XXXX, \ + XXXX, XXXX, CL_TOGGLE_CMD_CTRL, XXXX, ____, ____, ____, ____, XXXX, XXXX, XXXX \ + ), + [L_CODE] = LAYOUT( /* Mostly used for code */ + ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, \ + ____, ____, ____, ____, ____, ____, ____, CL_SQUOTS, CL_DQUOTS, ____, ____, ____, ____, ____, \ + ____, ____, ____, ____, ____, ____, ____, CL_PARENS, CL_BRCKTS, CL_BRACES, ____, ____, ____, \ + XXXX, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, ____, XXXX, \ + XXXX, XXXX, XXXX, ____, ____, ____, ____, ____, XXXX, XXXX, XXXX \ + ), + [L_MOUSE] = LAYOUT( /* Mouse mode */ + OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, \ + OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, \ + OOOO, OOOO, KC_BTN2, KC_BTN3, KC_BTN1, OOOO, OOOO, KC_BTN1, KC_BTN3, KC_BTN2, OOOO, OOOO, OOOO, \ + OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, OOOO, \ + OOOO, OOOO, OOOO, CL_MOUSE_TOGGLE_SCROLL, OOOO, OOOO, OOOO, CL_MOUSE_TOGGLE_SCROLL, OOOO, OOOO, OOOO \ + ), +}; + +static keybi_keyboard_event_t events_data[16]; + +keybi_keyboard_event_queue_t keybi_keymap_events = { + .events = events_data, + .head = 0, + .size = 0, + .capacity = 16 +}; + +int keybi_keyboard_layer = L_BASE; +static bool is_cmd_ctrl = 1; +static int key_pressed_on_code_layer = 0; + +int keybi_mouse_is_scrolling = 0; +uint8_t keybi_mouse_buttons = 0; + +uint16_t registered_events[KEYBI_MATRIX_ROWS][KEYBI_MATRIX_COLS] = {KC_NO}; + +int Keybi_Keymap_EventHandler(keybi_keyboard_matrix_event_t matrix_event) { + + // Here keycode is 16 bit to hold special values + uint16_t keycode = keymaps[keybi_keyboard_layer][matrix_event.row][matrix_event.col]; + + if (keybi_keyboard_layer == L_CODE && matrix_event.pressed) { + key_pressed_on_code_layer++; + } + + while (1) { + if (keycode == CL_FN_SWITCH) { + if (matrix_event.pressed) { + keybi_keyboard_layer = L_FN; + } else { + keybi_keyboard_layer = L_BASE; + } + return 0; + } + if (keycode == CL_CODE_SWITCH) { + if (matrix_event.pressed) { + keybi_keyboard_layer = L_CODE; + key_pressed_on_code_layer = 0; + } else { + keybi_keyboard_layer = L_BASE; + // if no event other than layer + if (!key_pressed_on_code_layer) { + keybi_keyboard_event_t events[2] = {{KC_ESC, 1}, {KC_ESC, 0}}; + return Keybi_Keyboard_QueueEvents(&keybi_keymap_events, events, 2); + } + } + return 0; + } else if (keycode == OOOO) { + if (matrix_event.pressed) { + keybi_keyboard_layer = L_BASE; + } + return 0; + } + else if (keycode == CL_CMD_CTRL) { + if (is_cmd_ctrl) { + keycode = KC_LCTL; + } else { + keycode = KC_LGUI; + } + } else if (keycode == CL_TOGGLE_CMD_CTRL) { + if (matrix_event.pressed) { + is_cmd_ctrl = !is_cmd_ctrl; + } + return 0; + } else if (keycode == ____) { + keycode = keymaps[L_BASE][matrix_event.row][matrix_event.col]; + if (keybi_keyboard_layer == L_FN && IS_KEY(keycode)) { + if (matrix_event.pressed) { + // TODO only handle taps for now + keybi_keyboard_event_t events[10] = { + {KC_LCTRL, 1}, + {KC_LSHIFT, 1}, + {KC_LALT, 1}, + {KC_LGUI, 1}, + {keycode, 1}, + {keycode, 0}, + {KC_LCTRL, 0}, + {KC_LSHIFT, 0}, + {KC_LALT, 0}, + {KC_LGUI, 0} + }; + return Keybi_Keyboard_QueueEvents(&keybi_keymap_events, events, 10); + } else { + return 0; + } + } + continue; // handle this keycode if special + } else if (keycode == CL_PARENS && matrix_event.pressed) { + keybi_keyboard_event_t events[6] = { + {KC_5, 1}, {KC_5, 0}, + {KC_6, 1}, {KC_6, 0}, + {KC_LEFT, 1}, {KC_LEFT, 0}, + }; + return Keybi_Keyboard_QueueEvents(&keybi_keymap_events, events, 6); + } else if (keycode == CL_BRCKTS && matrix_event.pressed) { + keybi_keyboard_event_t events[8] = { + {KC_RALT, 1}, + {KC_5, 1}, {KC_5, 0}, + {KC_6, 1}, {KC_6, 0}, + {KC_RALT, 0}, + {KC_LEFT, 1}, {KC_LEFT, 0}, + }; + return Keybi_Keyboard_QueueEvents(&keybi_keymap_events, events, 8); + } else if (keycode == CL_BRACES && matrix_event.pressed) { + keybi_keyboard_event_t events[8] = { + {KC_RALT, 1}, + {KC_T, 1}, {KC_T, 0}, + {KC_Y, 1}, {KC_Y, 0}, + {KC_RALT, 0}, + {KC_LEFT, 1}, {KC_LEFT, 0}, + }; + return Keybi_Keyboard_QueueEvents(&keybi_keymap_events, events, 8); + } else if (keycode == CL_SQUOTS && matrix_event.pressed) { + keybi_keyboard_event_t events[6] = { + {KC_MINS, 1}, {KC_MINS, 0}, + {KC_MINS, 1}, {KC_MINS, 0}, + {KC_LEFT, 1}, {KC_LEFT, 0}, + }; + return Keybi_Keyboard_QueueEvents(&keybi_keymap_events, events, 6); + } else if (keycode == CL_DQUOTS && matrix_event.pressed) { + keybi_keyboard_event_t events[8] = { + {KC_LSHIFT, 1}, + {KC_MINS, 1}, {KC_MINS, 0}, + {KC_MINS, 1}, {KC_MINS, 0}, + {KC_LSHIFT, 0}, + {KC_LEFT, 1}, {KC_LEFT, 0} + }; + return Keybi_Keyboard_QueueEvents(&keybi_keymap_events, events, 8); + } else if (keycode == CL_MOUSE_TOGGLE_SCROLL) { + if (matrix_event.pressed) { + keybi_mouse_is_scrolling = !keybi_mouse_is_scrolling; + } + return 0; + } else if (keycode == KC_BTN1) { + if (matrix_event.pressed) { + keybi_mouse_buttons |= (1 << 0); + } else { + keybi_mouse_buttons &= ~(1 << 0); + } + return 0; + } else if (keycode == KC_BTN2) { + if (matrix_event.pressed) { + keybi_mouse_buttons |= (1 << 1); + } else { + keybi_mouse_buttons &= ~(1 << 1); + } + return 0; + } else if (keycode == KC_BTN3) { + if (matrix_event.pressed) { + keybi_mouse_buttons |= (1 << 2); + } else { + keybi_mouse_buttons &= ~(1 << 2); + } + return 0; + } + // no need to handle this keycode further + break; + }; + + // TODO uggly fix + if (matrix_event.pressed) { + // indicates this row/col key has been registered as keycode + registered_events[matrix_event.row][matrix_event.col] = keycode; + } else { + if (registered_events[matrix_event.row][matrix_event.col] != KC_NO) { + // replace the keycode by what it was when pressed + // handles keys pressed when one layer was active and released + // when it was no longer active. + keycode = registered_events[matrix_event.row][matrix_event.col]; + } + registered_events[matrix_event.row][matrix_event.col] = KC_NO; + } + + // TODO check if keycode is not special here + keybi_keyboard_event_t key_event = { + .keycode = keycode, + .pressed = matrix_event.pressed + }; + return Keybi_Keyboard_QueueEvent(&keybi_keymap_events, key_event); +} diff --git a/src/keybi/keymap.h b/src/keybi/keymap.h new file mode 100644 index 00000000..73677f7c --- /dev/null +++ b/src/keybi/keymap.h @@ -0,0 +1,37 @@ +#pragma once + +#include "stm32f10x.h" +#include "keybi/drivers/matrix.h" +#include "keybi/hid_keyboard.h" +#include "keybi/qmk/keycode.h" + +typedef enum { + L_BASE = 0, + L_FN, + L_CODE, + L_MOUSE +} layer_id_t; + +enum custom_keycodes { + CL_FN_SWITCH = 0x100, // TODO safe range above uint8_t? + CL_CODE_SWITCH, + CL_TOGGLE_DIGITS, + CL_SQUOTS, + CL_DQUOTS, + CL_PARENS, + CL_BRCKTS, + CL_BRACES, + CL_CMD_CTRL, + CL_TOGGLE_CMD_CTRL, + CL_MOUSE_OUT, + CL_MOUSE_TOGGLE_SCROLL, + CL_SAFE_RANGE +}; + +int Keybi_Keymap_EventHandler(keybi_keyboard_matrix_event_t event); + +// TODO used from outside for experiments, to refactor +extern keybi_keyboard_event_queue_t keybi_keymap_events; +extern int keybi_keyboard_layer; +extern int keybi_mouse_is_scrolling; +extern uint8_t keybi_mouse_buttons; diff --git a/src/keybi/qmk/keycode.h b/src/keybi/qmk/keycode.h new file mode 100644 index 00000000..e1059fad --- /dev/null +++ b/src/keybi/qmk/keycode.h @@ -0,0 +1,543 @@ +/* +Copyright 2011,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 . +*/ + +/* + * Keycodes based on HID Keyboard/Keypad Usage Page (0x07) plus media keys from Generic Desktop Page (0x01) and Consumer Page (0x0C) + * + * See https://web.archive.org/web/20060218214400/http://www.usb.org/developers/devclass_docs/Hut1_12.pdf + * or http://www.usb.org/developers/hidpage/Hut1_12v2.pdf (older) + */ +#ifndef KEYCODE_H +#define KEYCODE_H + +/* FIXME: Add doxygen comments here */ + +#define IS_ERROR(code) (KC_ROLL_OVER <= (code) && (code) <= KC_UNDEFINED) +#define IS_ANY(code) (KC_A <= (code) && (code) <= 0xFF) +#define IS_KEY(code) (KC_A <= (code) && (code) <= KC_EXSEL) +#define IS_MOD(code) (KC_LCTRL <= (code) && (code) <= KC_RGUI) + +#define IS_SPECIAL(code) ((0xA5 <= (code) && (code) <= 0xDF) || (0xE8 <= (code) && (code) <= 0xFF)) +#define IS_SYSTEM(code) (KC_PWR <= (code) && (code) <= KC_WAKE) +#define IS_CONSUMER(code) (KC_MUTE <= (code) && (code) <= KC_BRID) + +#define IS_FN(code) (KC_FN0 <= (code) && (code) <= KC_FN31) + +#define IS_MOUSEKEY(code) (KC_MS_UP <= (code) && (code) <= KC_MS_ACCEL2) +#define IS_MOUSEKEY_MOVE(code) (KC_MS_UP <= (code) && (code) <= KC_MS_RIGHT) +#define IS_MOUSEKEY_BUTTON(code) (KC_MS_BTN1 <= (code) && (code) <= KC_MS_BTN5) +#define IS_MOUSEKEY_WHEEL(code) (KC_MS_WH_UP <= (code) && (code) <= KC_MS_WH_RIGHT) +#define IS_MOUSEKEY_ACCEL(code) (KC_MS_ACCEL0 <= (code) && (code) <= KC_MS_ACCEL2) + +#define MOD_BIT(code) (1 << MOD_INDEX(code)) +#define MOD_INDEX(code) ((code)&0x07) + +#define MOD_MASK_CTRL (MOD_BIT(KC_LCTRL) | MOD_BIT(KC_RCTRL)) +#define MOD_MASK_SHIFT (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) +#define MOD_MASK_ALT (MOD_BIT(KC_LALT) | MOD_BIT(KC_RALT)) +#define MOD_MASK_GUI (MOD_BIT(KC_LGUI) | MOD_BIT(KC_RGUI)) +#define MOD_MASK_CS (MOD_MASK_CTRL | MOD_MASK_SHIFT) +#define MOD_MASK_CA (MOD_MASK_CTRL | MOD_MASK_ALT) +#define MOD_MASK_CG (MOD_MASK_CTRL | MOD_MASK_GUI) +#define MOD_MASK_SA (MOD_MASK_SHIFT | MOD_MASK_ALT) +#define MOD_MASK_SG (MOD_MASK_SHIFT | MOD_MASK_GUI) +#define MOD_MASK_AG (MOD_MASK_ALT | MOD_MASK_GUI) +#define MOD_MASK_CSA (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT) +#define MOD_MASK_CSG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_GUI) +#define MOD_MASK_CAG (MOD_MASK_CTRL | MOD_MASK_ALT | MOD_MASK_GUI) +#define MOD_MASK_SAG (MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI) +#define MOD_MASK_CSAG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI) + +#define FN_BIT(code) (1 << FN_INDEX(code)) +#define FN_INDEX(code) ((code)-KC_FN0) +#define FN_MIN KC_FN0 +#define FN_MAX KC_FN31 + +/* + * Short names for ease of definition of keymap + */ +/* Transparent */ +#define KC_TRANSPARENT 0x01 +#define KC_TRNS KC_TRANSPARENT + +/* Punctuation */ +#define KC_ENT KC_ENTER +#define KC_ESC KC_ESCAPE +#define KC_BSPC KC_BSPACE +#define KC_SPC KC_SPACE +#define KC_MINS KC_MINUS +#define KC_EQL KC_EQUAL +#define KC_LBRC KC_LBRACKET +#define KC_RBRC KC_RBRACKET +#define KC_BSLS KC_BSLASH +#define KC_NUHS KC_NONUS_HASH +#define KC_SCLN KC_SCOLON +#define KC_QUOT KC_QUOTE +#define KC_GRV KC_GRAVE +#define KC_COMM KC_COMMA +#define KC_SLSH KC_SLASH +#define KC_NUBS KC_NONUS_BSLASH + +/* Lock Keys */ +#define KC_CLCK KC_CAPSLOCK +#define KC_CAPS KC_CAPSLOCK +#define KC_SLCK KC_SCROLLLOCK +#define KC_NLCK KC_NUMLOCK +#define KC_LCAP KC_LOCKING_CAPS +#define KC_LNUM KC_LOCKING_NUM +#define KC_LSCR KC_LOCKING_SCROLL + +/* Commands */ +#define KC_PSCR KC_PSCREEN +#define KC_PAUS KC_PAUSE +#define KC_BRK KC_PAUSE +#define KC_INS KC_INSERT +#define KC_DEL KC_DELETE +#define KC_PGDN KC_PGDOWN +#define KC_RGHT KC_RIGHT +#define KC_APP KC_APPLICATION +#define KC_EXEC KC_EXECUTE +#define KC_SLCT KC_SELECT +#define KC_AGIN KC_AGAIN +#define KC_PSTE KC_PASTE +#define KC_ERAS KC_ALT_ERASE +#define KC_CLR KC_CLEAR + +/* Keypad */ +#define KC_PSLS KC_KP_SLASH +#define KC_PAST KC_KP_ASTERISK +#define KC_PMNS KC_KP_MINUS +#define KC_PPLS KC_KP_PLUS +#define KC_PENT KC_KP_ENTER +#define KC_P1 KC_KP_1 +#define KC_P2 KC_KP_2 +#define KC_P3 KC_KP_3 +#define KC_P4 KC_KP_4 +#define KC_P5 KC_KP_5 +#define KC_P6 KC_KP_6 +#define KC_P7 KC_KP_7 +#define KC_P8 KC_KP_8 +#define KC_P9 KC_KP_9 +#define KC_P0 KC_KP_0 +#define KC_PDOT KC_KP_DOT +#define KC_PEQL KC_KP_EQUAL +#define KC_PCMM KC_KP_COMMA + +/* Japanese specific */ +#define KC_ZKHK KC_GRAVE +#define KC_RO KC_INT1 +#define KC_KANA KC_INT2 +#define KC_JYEN KC_INT3 +#define KC_HENK KC_INT4 +#define KC_MHEN KC_INT5 + +/* Korean specific */ +#define KC_HAEN KC_LANG1 +#define KC_HANJ KC_LANG2 + +/* Modifiers */ +#define KC_LCTL KC_LCTRL +#define KC_LSFT KC_LSHIFT +#define KC_LCMD KC_LGUI +#define KC_LWIN KC_LGUI +#define KC_RCTL KC_RCTRL +#define KC_RSFT KC_RSHIFT +#define KC_ALGR KC_RALT +#define KC_RCMD KC_RGUI +#define KC_RWIN KC_RGUI + +/* Generic Desktop Page (0x01) */ +#define KC_PWR KC_SYSTEM_POWER +#define KC_SLEP KC_SYSTEM_SLEEP +#define KC_WAKE KC_SYSTEM_WAKE + +/* Consumer Page (0x0C) */ +#define KC_MUTE KC_AUDIO_MUTE +#define KC_VOLU KC_AUDIO_VOL_UP +#define KC_VOLD KC_AUDIO_VOL_DOWN +#define KC_MNXT KC_MEDIA_NEXT_TRACK +#define KC_MPRV KC_MEDIA_PREV_TRACK +#define KC_MSTP KC_MEDIA_STOP +#define KC_MPLY KC_MEDIA_PLAY_PAUSE +#define KC_MSEL KC_MEDIA_SELECT +#define KC_EJCT KC_MEDIA_EJECT +#define KC_CALC KC_CALCULATOR +#define KC_MYCM KC_MY_COMPUTER +#define KC_WSCH KC_WWW_SEARCH +#define KC_WHOM KC_WWW_HOME +#define KC_WBAK KC_WWW_BACK +#define KC_WFWD KC_WWW_FORWARD +#define KC_WSTP KC_WWW_STOP +#define KC_WREF KC_WWW_REFRESH +#define KC_WFAV KC_WWW_FAVORITES +#define KC_MFFD KC_MEDIA_FAST_FORWARD +#define KC_MRWD KC_MEDIA_REWIND +#define KC_BRIU KC_BRIGHTNESS_UP +#define KC_BRID KC_BRIGHTNESS_DOWN + +/* System Specific */ +#define KC_BRMU KC_PAUSE +#define KC_BRMD KC_SCROLLLOCK + +/* Mouse Keys */ +#define KC_MS_U KC_MS_UP +#define KC_MS_D KC_MS_DOWN +#define KC_MS_L KC_MS_LEFT +#define KC_MS_R KC_MS_RIGHT +#define KC_BTN1 KC_MS_BTN1 +#define KC_BTN2 KC_MS_BTN2 +#define KC_BTN3 KC_MS_BTN3 +#define KC_BTN4 KC_MS_BTN4 +#define KC_BTN5 KC_MS_BTN5 +#define KC_WH_U KC_MS_WH_UP +#define KC_WH_D KC_MS_WH_DOWN +#define KC_WH_L KC_MS_WH_LEFT +#define KC_WH_R KC_MS_WH_RIGHT +#define KC_ACL0 KC_MS_ACCEL0 +#define KC_ACL1 KC_MS_ACCEL1 +#define KC_ACL2 KC_MS_ACCEL2 + +/* Keyboard/Keypad Page (0x07) */ +enum hid_keyboard_keypad_usage { + KC_NO = 0x00, + KC_ROLL_OVER, + KC_POST_FAIL, + KC_UNDEFINED, + KC_A, + KC_B, + KC_C, + KC_D, + KC_E, + KC_F, + KC_G, + KC_H, + KC_I, + KC_J, + KC_K, + KC_L, + KC_M, // 0x10 + KC_N, + KC_O, + KC_P, + KC_Q, + KC_R, + KC_S, + KC_T, + KC_U, + KC_V, + KC_W, + KC_X, + KC_Y, + KC_Z, + KC_1, + KC_2, + KC_3, // 0x20 + KC_4, + KC_5, + KC_6, + KC_7, + KC_8, + KC_9, + KC_0, + KC_ENTER, + KC_ESCAPE, + KC_BSPACE, + KC_TAB, + KC_SPACE, + KC_MINUS, + KC_EQUAL, + KC_LBRACKET, + KC_RBRACKET, // 0x30 + KC_BSLASH, + KC_NONUS_HASH, + KC_SCOLON, + KC_QUOTE, + KC_GRAVE, + KC_COMMA, + KC_DOT, + KC_SLASH, + KC_CAPSLOCK, + KC_F1, + KC_F2, + KC_F3, + KC_F4, + KC_F5, + KC_F6, + KC_F7, // 0x40 + KC_F8, + KC_F9, + KC_F10, + KC_F11, + KC_F12, + KC_PSCREEN, + KC_SCROLLLOCK, + KC_PAUSE, + KC_INSERT, + KC_HOME, + KC_PGUP, + KC_DELETE, + KC_END, + KC_PGDOWN, + KC_RIGHT, + KC_LEFT, // 0x50 + KC_DOWN, + KC_UP, + KC_NUMLOCK, + KC_KP_SLASH, + KC_KP_ASTERISK, + KC_KP_MINUS, + KC_KP_PLUS, + KC_KP_ENTER, + KC_KP_1, + KC_KP_2, + KC_KP_3, + KC_KP_4, + KC_KP_5, + KC_KP_6, + KC_KP_7, + KC_KP_8, // 0x60 + KC_KP_9, + KC_KP_0, + KC_KP_DOT, + KC_NONUS_BSLASH, + KC_APPLICATION, + KC_POWER, + KC_KP_EQUAL, + KC_F13, + KC_F14, + KC_F15, + KC_F16, + KC_F17, + KC_F18, + KC_F19, + KC_F20, + KC_F21, // 0x70 + KC_F22, + KC_F23, + KC_F24, + KC_EXECUTE, + KC_HELP, + KC_MENU, + KC_SELECT, + KC_STOP, + KC_AGAIN, + KC_UNDO, + KC_CUT, + KC_COPY, + KC_PASTE, + KC_FIND, + KC__MUTE, + KC__VOLUP, // 0x80 + KC__VOLDOWN, + KC_LOCKING_CAPS, + KC_LOCKING_NUM, + KC_LOCKING_SCROLL, + KC_KP_COMMA, + KC_KP_EQUAL_AS400, + KC_INT1, + KC_INT2, + KC_INT3, + KC_INT4, + KC_INT5, + KC_INT6, + KC_INT7, + KC_INT8, + KC_INT9, + KC_LANG1, // 0x90 + KC_LANG2, + KC_LANG3, + KC_LANG4, + KC_LANG5, + KC_LANG6, + KC_LANG7, + KC_LANG8, + KC_LANG9, + KC_ALT_ERASE, + KC_SYSREQ, + KC_CANCEL, + KC_CLEAR, + KC_PRIOR, + KC_RETURN, + KC_SEPARATOR, + KC_OUT, // 0xA0 + KC_OPER, + KC_CLEAR_AGAIN, + KC_CRSEL, + KC_EXSEL, + +#if 0 + // *************************************************************** + // These keycodes are present in the HID spec, but are * + // nonfunctional on modern OSes. QMK uses this range (0xA5-0xDF) * + // for the media and function keys instead - see below. * + // *************************************************************** + + KC_KP_00 = 0xB0, + KC_KP_000, + KC_THOUSANDS_SEPARATOR, + KC_DECIMAL_SEPARATOR, + KC_CURRENCY_UNIT, + KC_CURRENCY_SUB_UNIT, + KC_KP_LPAREN, + KC_KP_RPAREN, + KC_KP_LCBRACKET, + KC_KP_RCBRACKET, + KC_KP_TAB, + KC_KP_BSPACE, + KC_KP_A, + KC_KP_B, + KC_KP_C, + KC_KP_D, + KC_KP_E, //0xC0 + KC_KP_F, + KC_KP_XOR, + KC_KP_HAT, + KC_KP_PERC, + KC_KP_LT, + KC_KP_GT, + KC_KP_AND, + KC_KP_LAZYAND, + KC_KP_OR, + KC_KP_LAZYOR, + KC_KP_COLON, + KC_KP_HASH, + KC_KP_SPACE, + KC_KP_ATMARK, + KC_KP_EXCLAMATION, + KC_KP_MEM_STORE, //0xD0 + KC_KP_MEM_RECALL, + KC_KP_MEM_CLEAR, + KC_KP_MEM_ADD, + KC_KP_MEM_SUB, + KC_KP_MEM_MUL, + KC_KP_MEM_DIV, + KC_KP_PLUS_MINUS, + KC_KP_CLEAR, + KC_KP_CLEAR_ENTRY, + KC_KP_BINARY, + KC_KP_OCTAL, + KC_KP_DECIMAL, + KC_KP_HEXADECIMAL, +#endif + + /* Modifiers */ + KC_LCTRL = 0xE0, + KC_LSHIFT, + KC_LALT, + KC_LGUI, + KC_RCTRL, + KC_RSHIFT, + KC_RALT, + KC_RGUI + + // ********************************************** + // * 0xF0-0xFF are unallocated in the HID spec. * + // * QMK uses these for Mouse Keys - see below. * + // ********************************************** +}; + +/* Media and Function keys */ +enum internal_special_keycodes { + /* Generic Desktop Page (0x01) */ + KC_SYSTEM_POWER = 0xA5, + KC_SYSTEM_SLEEP, + KC_SYSTEM_WAKE, + + /* Consumer Page (0x0C) */ + KC_AUDIO_MUTE, + KC_AUDIO_VOL_UP, + KC_AUDIO_VOL_DOWN, + KC_MEDIA_NEXT_TRACK, + KC_MEDIA_PREV_TRACK, + KC_MEDIA_STOP, + KC_MEDIA_PLAY_PAUSE, + KC_MEDIA_SELECT, + KC_MEDIA_EJECT, // 0xB0 + KC_MAIL, + KC_CALCULATOR, + KC_MY_COMPUTER, + KC_WWW_SEARCH, + KC_WWW_HOME, + KC_WWW_BACK, + KC_WWW_FORWARD, + KC_WWW_STOP, + KC_WWW_REFRESH, + KC_WWW_FAVORITES, + KC_MEDIA_FAST_FORWARD, + KC_MEDIA_REWIND, + KC_BRIGHTNESS_UP, + KC_BRIGHTNESS_DOWN, + + /* Fn keys */ + KC_FN0 = 0xC0, + KC_FN1, + KC_FN2, + KC_FN3, + KC_FN4, + KC_FN5, + KC_FN6, + KC_FN7, + KC_FN8, + KC_FN9, + KC_FN10, + KC_FN11, + KC_FN12, + KC_FN13, + KC_FN14, + KC_FN15, + KC_FN16, // 0xD0 + KC_FN17, + KC_FN18, + KC_FN19, + KC_FN20, + KC_FN21, + KC_FN22, + KC_FN23, + KC_FN24, + KC_FN25, + KC_FN26, + KC_FN27, + KC_FN28, + KC_FN29, + KC_FN30, + KC_FN31 +}; + +enum mouse_keys { + /* Mouse Buttons */ + KC_MS_UP = 0xF0, + KC_MS_DOWN, + KC_MS_LEFT, + KC_MS_RIGHT, + KC_MS_BTN1, + KC_MS_BTN2, + KC_MS_BTN3, + KC_MS_BTN4, + KC_MS_BTN5, + + /* Mouse Wheel */ + KC_MS_WH_UP, + KC_MS_WH_DOWN, + KC_MS_WH_LEFT, + KC_MS_WH_RIGHT, + + /* Acceleration */ + KC_MS_ACCEL0, + KC_MS_ACCEL1, + KC_MS_ACCEL2 +}; +#endif diff --git a/src/keybi/tests.c b/src/keybi/tests.c deleted file mode 100644 index 94fe014b..00000000 --- a/src/keybi/tests.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "keybi/tests.h" -#include "CCIDHID_usb_desc.h" - -#pragma GCC push_options -#pragma GCC optimize ("O0") - -void Keybi_Test_Failed(char *file, int line, const char * expr) { - while (1); -} - -void Keybi_Test_ConfigDescriptor(void); - -void Keybi_Tests_Run() { - Keybi_Test_ConfigDescriptor(); -} - -void Keybi_Test_ConfigDescriptor() { - KEYBI_ASSERT(sizeof(CCID_ConfigDescriptor) == CCID_SIZ_CONFIG_DESC); - KEYBI_ASSERT(sizeof(Keybi_Keyboard_ReportDescriptor) == KEYBI_KEYBOARD_SIZ_REPORT_DESC); - KEYBI_ASSERT(sizeof(Keybi_Mouse_ReportDescriptor) == KEYBI_MOUSE_SIZ_REPORT_DESC); - - KEYBI_ASSERT(CCID_ConfigDescriptor[KEYBOARD_OFF_HID_DESC] == KEYBOARD_SIZ_HID_DESC); - KEYBI_ASSERT(CCID_ConfigDescriptor[KEYBI_KEYBOARD_OFF_HID_DESC] == KEYBI_KEYBOARD_SIZ_HID_DESC); - KEYBI_ASSERT(CCID_ConfigDescriptor[KEYBI_MOUSE_OFF_HID_DESC] == KEYBI_MOUSE_SIZ_HID_DESC); -} - -#pragma GCC pop_options diff --git a/src/keybi/tests.h b/src/keybi/tests.h deleted file mode 100644 index 890c6c3e..00000000 --- a/src/keybi/tests.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -void Keybi_Tests_Run(void); - -#define KEYBI_ASSERT(expr) \ - if (!(expr)) \ - Keybi_Test_Failed(__FILE__, __LINE__, #expr) - -void Keybi_Test_Failed(char *file, int line, const char * expr); diff --git a/src/keybi/tests/Makefile b/src/keybi/tests/Makefile new file mode 100644 index 00000000..4a3570e3 --- /dev/null +++ b/src/keybi/tests/Makefile @@ -0,0 +1,47 @@ +GTEST = /usr/src/gtest +FIRMWARE_SRC = ../.. + +INCDIRS = . \ + $(GTEST) \ + $(FIRMWARE_SRC) \ + $(FIRMWARE_SRC)/inc \ + $(FIRMWARE_SRC)/stm/Libraries/STM32_USB-FS-Device_Driver/inc + +CFLAGS = -g -Wall -Wextra -lpthread -pthread + +CFLAGS += $(patsubst %,-I%,$(INCDIRS)) + +.PHONY: all +all: tests + +.PHONY: clean +clean: + rm -f *.o tests + +.PHONY: run_tests +run_tests: tests + ./tests + +%.o : $(GTEST)/src/%.cc + g++ $(CFLAGS) -c $< -o $@ + +%.o: %.cpp + g++ $(CFLAGS) -c $< -o $@ + +USB_DESCRIPTORS_TEST = usb_descriptors_test.o CCIDHID_usb_desc.o + +CCIDHID_usb_desc.o: $(FIRMWARE_SRC)/ccid/CCIDHID_USB/CCIDHID_usb_desc.c + gcc $(CFLAGS) -c $< -o $@ + +KEYBOARD_EVENT_QUEUE_TEST = keyboard_event_queue.o hid_keyboard.o + +hid_keyboard.o: $(FIRMWARE_SRC)/keybi/hid_keyboard.c + gcc $(CFLAGS) -c $< -o $@ + +KEYMAP_TEST = keymap_test.o keymap.o + +keymap.o: $(FIRMWARE_SRC)/keybi/keymap.c + gcc $(CFLAGS) -c $< -o $@ + +tests: gtest-all.o gtest_main.o $(USB_DESCRIPTORS_TEST) $(KEYBOARD_EVENT_QUEUE_TEST) $(KEYMAP_TEST) + g++ $(CFLAGS) $^ -o $@ \ No newline at end of file diff --git a/src/keybi/tests/keyboard_event_queue.cpp b/src/keybi/tests/keyboard_event_queue.cpp new file mode 100644 index 00000000..51e721a4 --- /dev/null +++ b/src/keybi/tests/keyboard_event_queue.cpp @@ -0,0 +1,147 @@ +#include +#include + +extern "C" { + #include "keybi/hid_keyboard.h" + #include "keybi/qmk/keycode.h" + #include "usb_core.h" + + // Stubs to avoid undefined references + __IO uint32_t bDeviceState; + void UserToPMABufferCopy (uint8_t *, uint16_t, uint16_t) {} + void SetEPTxCount (uint8_t, uint16_t) {} + void SetEPTxStatus (uint8_t, uint16_t) {} + uint8_t* Standard_GetDescriptorData (uint16_t, ONE_DESCRIPTOR *) { return NULL; } +} + +class KeyboardEventQueue : public ::testing::Test { + + protected: + + uint8_t report[8] = {0}; + keybi_keyboard_event_t queue_data[16]; + keybi_keyboard_event_queue_t queue; + + KeyboardEventQueue() : + report {0}, + queue_data {0,0}, + queue({ + .events = queue_data, + .head = 0, + .size = 0, + .capacity = 16 + }) + {} +}; + +bool ReportsEq(uint8_t expected[8], uint8_t actual[8]) { + return 0 == std::memcmp(expected, actual, 8); +} + +std::ostream &operator<<(std::ostream &os, const uint8_t (& report) [8]) { + os << "{"; + for (int i = 0; i < 8; ++i) { + os << " " << (int) report[i]; + } + os << " }"; + return os; +} + +TEST_F(KeyboardEventQueue, HandlesASingleKeypress) { + keybi_keyboard_event_t events[] = { + {KC_A, 1}, + {KC_A, 0} + }; + Keybi_Keyboard_QueueEvents(&queue, events, 2); + + uint8_t expected_reports[2][8] = { + {0, 0, KC_A, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0} + }; + + for (int i = 0; i < 2; ++i) { + ASSERT_TRUE(Keybi_Keyboard_QueueToReport(&queue, report)); + ASSERT_TRUE(ReportsEq(expected_reports[i], report)) << i << ": " << report; + } + ASSERT_FALSE(Keybi_Keyboard_QueueToReport(&queue, report)); +} + +TEST_F(KeyboardEventQueue, TellsWhenFull) { + for (unsigned i = 0; i < queue.capacity; ++i) { + ASSERT_EQ(0, Keybi_Keyboard_QueueEvent(&queue, {KC_A, 1})); + } + ASSERT_NE(0, Keybi_Keyboard_QueueEvent(&queue, {KC_B, 1})); + // B has not been added + for (unsigned i = 0; i < queue.capacity; ++i) { + ASSERT_EQ(KC_A, queue.events[i].keycode); + } +} + +TEST_F(KeyboardEventQueue, HandlesMultipleKeypresses) { + keybi_keyboard_event_t events[] = { + {KC_A, 1}, + {KC_B, 1}, + {KC_C, 1}, + {KC_B, 0} + }; + Keybi_Keyboard_QueueEvents(&queue, events, 3); + + uint8_t expected_reports[4][8] = { + {0, 0, KC_A, 0, 0, 0, 0, 0}, + {0, 0, KC_A, KC_B, 0, 0, 0, 0}, + {0, 0, KC_A, KC_B, KC_C, 0, 0, 0}, + {0, 0, KC_A, 0, KC_C, 0, 0, 0} + }; + + for (int i = 0; i < 3; ++i) { + ASSERT_TRUE(Keybi_Keyboard_QueueToReport(&queue, report)); + ASSERT_TRUE(ReportsEq(expected_reports[i], report)) << i << ": " << report; + } + ASSERT_FALSE(Keybi_Keyboard_QueueToReport(&queue, report)); +} + +TEST_F(KeyboardEventQueue, HandlesModpresses) { + + keybi_keyboard_event_t events[] = { + {KC_LCTL, 1}, + {KC_LCTL, 0} + }; + Keybi_Keyboard_QueueEvents(&queue, events, 2); + + uint8_t expected_reports[][8] = { + {1, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0} + }; + + for (int i = 0; i < 2; ++i) { + ASSERT_TRUE(Keybi_Keyboard_QueueToReport(&queue, report)); + ASSERT_TRUE(ReportsEq(expected_reports[i], report)) << i << ": " << report; + } + ASSERT_FALSE(Keybi_Keyboard_QueueToReport(&queue, report)); +} + +TEST_F(KeyboardEventQueue, DoNotRegisterTheSameKeycodeMultipleTimes) { + // On keymaps, more than one key may be mapped to the same keycode + // so the same keycode might be pressed two times in the queue + + keybi_keyboard_event_t events[] = { + {KC_UP, 1}, + {KC_UP, 1}, + {KC_UP, 0}, + {KC_UP, 0} + }; + Keybi_Keyboard_QueueEvents(&queue, events, 4); + + uint8_t expected_reports[4][8] = { + {0, 0, KC_UP, 0, 0, 0, 0, 0}, + {0, 0, KC_UP, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0} + }; + + for (int i = 0; i < 4; ++i) { + EXPECT_TRUE(Keybi_Keyboard_QueueToReport(&queue, report)); + EXPECT_TRUE(ReportsEq(expected_reports[i], report)) << i << ": " << report; + } + EXPECT_FALSE(Keybi_Keyboard_QueueToReport(&queue, report)); +} \ No newline at end of file diff --git a/src/keybi/tests/keymap_test.cpp b/src/keybi/tests/keymap_test.cpp new file mode 100644 index 00000000..b951304f --- /dev/null +++ b/src/keybi/tests/keymap_test.cpp @@ -0,0 +1,138 @@ +#include +#include + +extern "C" { + #include "keybi/keymap.h" + #include "keybi/qmk/keycode.h" +} + +class Keymap : public ::testing::Test { + + protected: + + Keymap() { + keybi_keymap_events.size = 0; + keybi_keyboard_layer = L_BASE; + } +}; + +struct event_to_make_t { + uint16_t keycode; + uint8_t pressed; +}; + +keybi_keyboard_matrix_event_t MakeMatrixEvent(event_to_make_t event) { + switch (event.keycode) { + case KC_GRV: return {0, 0, event.pressed, 0}; + case KC_A: return {1, 2, event.pressed, 0}; + case CL_FN_SWITCH: return {8, 4, event.pressed, 0}; + } + throw std::invalid_argument("keycode not handled"); +} + +bool operator==(keybi_keyboard_event_t const& a, keybi_keyboard_event_t const& b) { + return a.keycode == b.keycode && a.pressed == b.pressed; +} + +keybi_keyboard_event_t GetQueuedElement(uint32_t i) { + if (i >= keybi_keymap_events.size) { + throw std::out_of_range("out of range"); + } + i = (i + keybi_keymap_events.head) % keybi_keymap_events.capacity; + return keybi_keymap_events.events[i]; +} + +std::ostream &operator<<(std::ostream &os, keybi_keyboard_event_t const &event) { + return os << "{" << (int) event.keycode << ", " << (int) event.pressed << " }"; +} + +TEST_F(Keymap, HandlesBaseLayerKeys) { + event_to_make_t events[] = { + {KC_A, 1}, + {KC_A, 0} + }; + for (int i = 0; i < 2; ++i) { + auto matrix_event = MakeMatrixEvent(events[i]); + ASSERT_EQ(0, Keybi_Keymap_EventHandler(matrix_event)); + } + keybi_keyboard_event_t expected_events[] = { + {KC_A, 1}, + {KC_A, 0} + }; + for (unsigned i = 0; i < 2; ++i) { + keybi_keyboard_event_t event = GetQueuedElement(i); + ASSERT_TRUE(expected_events[i] == event) << i << ": " << event; + } +} + +TEST_F(Keymap, HandlesFnLayerKeys) { + event_to_make_t events[] = { + {CL_FN_SWITCH, 1}, + {KC_GRV, 1}, + {KC_GRV, 0}, + {CL_FN_SWITCH, 0} + }; + for (int i = 0; i < 4; ++i) { + auto matrix_event = MakeMatrixEvent(events[i]); + ASSERT_EQ(0, Keybi_Keymap_EventHandler(matrix_event)); + } + keybi_keyboard_event_t expected_events[] = { + {KC_PWR, 1}, + {KC_PWR, 0} + }; + for (unsigned i = 0; i < 2; ++i) { + keybi_keyboard_event_t event = GetQueuedElement(i); + ASSERT_TRUE(expected_events[i] == event) << i << ": " << event; + } +} + +TEST_F(Keymap, HandlesRollingFnLayerKeys) { + event_to_make_t events[] = { + {CL_FN_SWITCH, 1}, + {KC_GRV, 1}, + {CL_FN_SWITCH, 0}, + {KC_GRV, 0} + }; + for (int i = 0; i < 4; ++i) { + auto matrix_event = MakeMatrixEvent(events[i]); + ASSERT_EQ(0, Keybi_Keymap_EventHandler(matrix_event)); + } + keybi_keyboard_event_t expected_events[] = { + {KC_PWR, 1}, + {KC_PWR, 0} + }; + for (unsigned i = 0; i < 2; ++i) { + keybi_keyboard_event_t event = GetQueuedElement(i); + ASSERT_TRUE(expected_events[i] == event) << i << ": " << event; + } +} + +TEST_F(Keymap, HandlesTransparentFnLayerKeysAsHypr) { + event_to_make_t events[] = { + {CL_FN_SWITCH, 1}, + {KC_A, 1}, + {KC_A, 0}, + {CL_FN_SWITCH, 0} + }; + for (int i = 0; i < 4; ++i) { + auto matrix_event = MakeMatrixEvent(events[i]); + ASSERT_EQ(0, Keybi_Keymap_EventHandler(matrix_event)); + } + keybi_keyboard_event_t expected_events[] = { + {KC_LCTRL, 1}, + {KC_LSHIFT, 1}, + {KC_LALT, 1}, + {KC_LGUI, 1}, + {KC_A, 1}, + {KC_A, 0}, + {KC_LCTRL, 0}, + {KC_LSHIFT, 0}, + {KC_LALT, 0}, + {KC_LGUI, 0}, + }; + for (unsigned i = 0; i < 10; ++i) { + keybi_keyboard_event_t event = GetQueuedElement(i); + ASSERT_TRUE(expected_events[i] == event) << i << ": " << event; + } + ASSERT_EQ(L_BASE, keybi_keyboard_layer); +} diff --git a/src/keybi/tests/stm32f10x.h b/src/keybi/tests/stm32f10x.h new file mode 100644 index 00000000..39af88b4 --- /dev/null +++ b/src/keybi/tests/stm32f10x.h @@ -0,0 +1,22 @@ +#ifndef __STM32F10x_H +#define __STM32F10x_H + +// Stub header replacing the original for unit tests + +#include + +// Normally passed through commandline when compiling +#define GLOBAL_VID 0x20a0 +#define GLOBAL_PID 0x4108 + +#define __IO volatile + +#ifndef __cplusplus +typedef enum +{ FALSE = 0, TRUE = !FALSE } bool; +#endif + +// typedef enum +// { DISABLE = 0, ENABLE = !DISABLE } FunctionalState; + +#endif \ No newline at end of file diff --git a/src/keybi/tests/usb_descriptors_test.cpp b/src/keybi/tests/usb_descriptors_test.cpp new file mode 100644 index 00000000..6184a24a --- /dev/null +++ b/src/keybi/tests/usb_descriptors_test.cpp @@ -0,0 +1,15 @@ +#include + +extern "C" { + #include "CCIDHID_usb_desc.h" +} + +TEST(UsbDescriptors, DefinesConsistentSizes) { + ASSERT_EQ(CCID_SIZ_CONFIG_DESC, sizeof(CCID_ConfigDescriptor)); + ASSERT_EQ(KEYBI_KEYBOARD_SIZ_REPORT_DESC, sizeof(Keybi_Keyboard_ReportDescriptor)); + ASSERT_EQ(KEYBI_MOUSE_SIZ_REPORT_DESC, sizeof(Keybi_Mouse_ReportDescriptor)); + + ASSERT_EQ(KEYBOARD_SIZ_HID_DESC, CCID_ConfigDescriptor[KEYBOARD_OFF_HID_DESC]); + ASSERT_EQ(KEYBI_KEYBOARD_SIZ_HID_DESC, CCID_ConfigDescriptor[KEYBI_KEYBOARD_OFF_HID_DESC]); + ASSERT_EQ(KEYBI_MOUSE_SIZ_HID_DESC, CCID_ConfigDescriptor[KEYBI_MOUSE_OFF_HID_DESC]); +} diff --git a/src/stm/Libraries/STM32F10x_StdPeriph_Driver/inc/misc.h b/src/stm/Libraries/STM32F10x_StdPeriph_Driver/inc/misc.h index e1702869..35e128e1 100644 --- a/src/stm/Libraries/STM32F10x_StdPeriph_Driver/inc/misc.h +++ b/src/stm/Libraries/STM32F10x_StdPeriph_Driver/inc/misc.h @@ -62,7 +62,7 @@ typedef struct */ #define NVIC_VectTab_RAM ((uint32_t)0x20000000) -#define NVIC_VectTab_FLASH ((uint32_t)0x08002000) +#define NVIC_VectTab_FLASH ((uint32_t)0x08000000) #define IS_NVIC_VECTTAB(VECTTAB) (((VECTTAB) == NVIC_VectTab_RAM) || \ ((VECTTAB) == NVIC_VectTab_FLASH)) /**