Skip to content
Snippets Groups Projects
keyboard.c 13.7 KiB
Newer Older
/*
  MNT Reform 2.0 Keyboard Firmware
  Copyright 2019-2021  Lukas F. Hartmann / MNT Research GmbH, Berlin (lukas@mntre.com)
  And Contributors:
  Chartreuse
  Robey Pointer (robey@lag.net)

  Based on code from LUFA Library (lufa-lib.org):
  Copyright 2018 Dean Camera (dean [at] fourwalledcubicle [dot] com)

  SPDX-License-Identifier: MIT
*/

#include <stdlib.h>
#include <avr/io.h>
#include "backlight.h"
// Important: version and variant info moved here
#include "constants.h"
#include "hid_report.h"
#include "keyboard.h"
#include "menu.h"
#include "oled.h"
#include "powersave.h"
#include "remote.h"
#include "scancodes.h"

#ifdef KBD_VARIANT_V
#include "matrix_v.h"
#endif

/** Buffer to hold the previously generated Keyboard HID report, for comparison purposes inside the HID class driver. */
static uint8_t PrevKeyboardHIDReportBuffer[sizeof(USB_KeyboardReport_Data_t)];
static uint8_t PrevMediaControlHIDReportBuffer[sizeof(USB_MediaReport_Data_t)];

/** LUFA HID Class driver interface configuration and state information. This structure is
 *  passed to all HID Class driver functions, so that multiple instances of the same class
 *  within a device can be differentiated from one another.
 */
USB_ClassInfo_HID_Device_t Keyboard_HID_Interface =
  {
    .Config =
      {
        .InterfaceNumber              = INTERFACE_ID_Keyboard,
        .ReportINEndpoint             =
          {
            .Address              = KEYBOARD_EPADDR,
            .Banks                = 1,
          },
        .PrevReportINBuffer           = PrevKeyboardHIDReportBuffer,
        .PrevReportINBufferSize       = sizeof(PrevKeyboardHIDReportBuffer),
      },
  };

/** LUFA HID Class driver interface configuration and state information for the Media Controller */
USB_ClassInfo_HID_Device_t MediaControl_HID_Interface =
  {
    .Config =
      {
        .InterfaceNumber              = INTERFACE_ID_MediaControl,
        .ReportINEndpoint             =
          {
            .Address              = MEDIACONTROL_EPADDR,
            .Size                 = HID_EPSIZE,
            .Banks                = 1,
          },
        .PrevReportINBuffer           = PrevMediaControlHIDReportBuffer,
        .PrevReportINBufferSize       = sizeof(PrevMediaControlHIDReportBuffer),
      },
  };

uint8_t matrix_debounce[KBD_COLS*KBD_ROWS];
uint8_t matrix_state[KBD_COLS*KBD_ROWS];

int active_meta_mode = 0;
uint8_t last_meta_key = 0;

uint8_t* active_matrix = matrix;
bool media_toggle = 0;
bool fn_key = 0; // Am I holding FN?
bool circle = 0; // Am I holding circle?

// enter the menu
void enter_meta_mode(void) {
  active_meta_mode = 1;
  reset_and_render_menu();
}

void reset_keyboard_state(void) {
  for (int i = 0; i < KBD_COLS*KBD_ROWS; i++) {
    matrix_debounce[i] = 0;
    matrix_state[i] = 0;
  }
  last_meta_key = 0;
inline bool is_media_key(uint8_t keycode) {
  return (keycode>=HID_KEYBOARD_SC_MEDIA_PLAY);
}

bool get_media_keys(uint8_t keycode, USB_MediaReport_Data_t* mcr) {
  bool media_key = false;
  if (keycode == HID_KEYBOARD_SC_MEDIA_MUTE) {
    if (mcr) mcr->Mute = 1;
    media_key = true;
  } else if (keycode == HID_KEYBOARD_SC_MEDIA_VOLUME_UP) {
    if (mcr) mcr->VolumeUp = 1;
    media_key = true;
  } else if (keycode == HID_KEYBOARD_SC_MEDIA_VOLUME_DOWN) {
    if (mcr) mcr->VolumeDown = 1;
    media_key = true;
  } else if (keycode == HID_KEYBOARD_SC_MEDIA_BACKWARD) {
    if (mcr) mcr->PreviousTrack = 1;
    media_key = true;
  } else if (keycode == HID_KEYBOARD_SC_MEDIA_FORWARD) {
    if (mcr) mcr->NextTrack = 1;
    media_key = true;
  } else if (keycode == HID_KEYBOARD_SC_MEDIA_PLAY) {
    if (mcr) mcr->PlayPause = 1;
    media_key = true;
  }
  return media_key;
}

#define MAX_SCANCODES 6
static uint8_t pressed_scancodes[MAX_SCANCODES] = {0,0,0,0,0,0};

// usb_report_mode: if you pass 0, you can leave KeyboardReport NULL
int process_keyboard(uint8_t* resulting_scancodes) {
  // how many keys are pressed this round
  uint8_t total_pressed = 0;
  uint8_t used_key_codes = 0;

  // pull ROWs low one after the other
  for (int y = 0; y < KBD_ROWS; y++) {
    switch (y) {
    case 0: output_low(PORTB, 6); break;
    case 1: output_low(PORTB, 5); break;
    case 2: output_low(PORTB, 4); break;
    case 3: output_low(PORTD, 7); break;
    case 4: output_low(PORTD, 6); break;
    case 5: output_low(PORTD, 4); break;
    }

    // wait for signal to stabilize
    // TODO maybe not necessary

    // check input COLs
    for (int x=0; x<14; x++) {
      uint16_t keycode;
      uint16_t loc = y*KBD_COLS+x;
      keycode = active_matrix[loc];
      uint8_t  pressed = 0;
      uint8_t  debounced_pressed = 0;

      // column pins are all over the place
      switch (x) {
      case 0:  pressed = !(PIND&(1<<5)); break;
      case 1:  pressed = !(PINF&(1<<7)); break;
      case 2:  pressed = !(PINE&(1<<6)); break;
      case 3:  pressed = !(PINC&(1<<7)); break;
      case 4:  pressed = !(PINB&(1<<3)); break;
      case 5:  pressed = !(PINB&(1<<2)); break;
      case 6:  pressed = !(PINB&(1<<1)); break;
      case 7:  pressed = !(PINB&(1<<0)); break;
      case 8:  pressed = !(PINF&(1<<0)); break;
      case 9:  pressed = !(PINF&(1<<1)); break;
      case 10: pressed = !(PINF&(1<<4)); break;
      case 11: pressed = !(PINF&(1<<5)); break;
      case 12: pressed = !(PINF&(1<<6)); break;
      case 13: pressed = !(PINC&(1<<6)); break;
      }

      // shift new state as bit into debounce "register"
      matrix_debounce[loc] = (matrix_debounce[loc]<<1)|pressed;

      // if unclear state, we need to keep the last state of the key
      if (matrix_debounce[loc] == 0x00) {
        matrix_state[loc] = 0;
      } else if (matrix_debounce[loc] == 0x01) {
        matrix_state[loc] = 1;
      }
      debounced_pressed = matrix_state[loc];

      if (debounced_pressed) {
        total_pressed++;

        // Is circle key pressed?
        if (keycode == KEY_CIRCLE) {
          if (fn_key) {
            circle = 1;
          } else {
            if (!active_meta_mode && !last_meta_key) {
              enter_meta_mode();
            }
          }
        } else if (keycode == HID_KEYBOARD_SC_EXECUTE) {
          fn_key = 1;
          active_matrix = matrix_fn;
        } else {
          if (active_meta_mode) {
            // not holding the same key?
            if (last_meta_key != keycode) {
              // hyper/circle/menu functions
              int stay_meta = execute_meta_function(keycode);
              // don't repeat action while key is held down
              last_meta_key = keycode;

              // exit meta mode
              if (!stay_meta) {
                active_meta_mode = 0;
              }

              // after wake-up from sleep mode, skip further keymap processing
              if (stay_meta == 2) {
                reset_keyboard_state();
                enter_meta_mode();
              }
            }
          } else if (!last_meta_key) {
            // not meta mode, regular key: report keypress via USB
            // 6 keys is the limit in the HID descriptor
            if (used_key_codes < MAX_SCANCODES && resulting_scancodes) {
              resulting_scancodes[used_key_codes++] = keycode;
            }
          }
        }
      } else {
        // key not pressed
        if (keycode == HID_KEYBOARD_SC_EXECUTE) {
          fn_key = 0;
          if (media_toggle) {
            active_matrix = matrix_fn_toggled;
          } else {
            active_matrix = matrix;
          }
        } else if (keycode == KEY_CIRCLE) {
          if (fn_key && circle) {
            if (!media_toggle) {
              media_toggle = 1;
              active_matrix = matrix_fn_toggled;
      }
    }

    switch (y) {
    case 0: output_high(PORTB, 6); break;
    case 1: output_high(PORTB, 5); break;
    case 2: output_high(PORTB, 4); break;
    case 3: output_high(PORTD, 7); break;
    case 4: output_high(PORTD, 6); break;
    case 5: output_high(PORTD, 4); break;
    }
  }

  // if no more keys are held down, allow a new meta command
  if (total_pressed<1) last_meta_key = 0;
}

int main(void)
{
#ifdef KBD_VARIANT_QWERTY_US
  matrix[KBD_COLS*4+1]=KEY_DELETE;
#endif
#ifdef KBD_VARIANT_NEO2
  matrix[KBD_COLS*3+0]=HID_KEYBOARD_SC_CAPS_LOCK; // M3
  matrix[KBD_COLS*2+13]=KEY_ENTER;
  matrix[KBD_COLS*3+13]=KEY_BACKSLASH_AND_PIPE; // M3
#endif

  setup_hardware();
  GlobalInterruptEnable();
  anim_hello();

  int counter = 0;

  for (;;)
  {
    HID_Device_USBTask(&Keyboard_HID_Interface);
    HID_Device_USBTask(&MediaControl_HID_Interface);
    USB_USBTask();
    counter++;
#ifndef KBD_VARIANT_STANDALONE
      if (counter>=100000) {
        remote_check_for_low_battery();
        counter = 0;
      }
      if (counter%750 == 0) {
        remote_process_alerts();
      }
#endif
  }
}

void setup_hardware(void)
{
  // Disable watchdog if enabled by bootloader/fuses
  MCUSR &= ~(1 << WDRF);
  wdt_disable();

  // Disable clock division
  clock_prescale_set(clock_div_1);

  // declare port pins as inputs (0) and outputs (1)
  DDRB  = 0b11110000;
  DDRC  = 0b00000000;
  DDRD  = 0b11011001;
  DDRE  = 0b00000000;
  DDRF  = 0b00000000;

  // initial pin states
  PORTB = 0b10001111;
  PORTC = 0b11000000;
  PORTD = 0b00100000;
  PORTE = 0b01000000;
  PORTF = 0b11111111;

  // disable JTAG
  MCUCR |=(1<<JTD);
  MCUCR |=(1<<JTD);

  kbd_brightness_init();
  gfx_init(false);
  remote_init();
  USB_Init();
}

ISR(WDT_vect)
{
  // WDT interrupt enable and flag cleared on entry
}

/** Event handler for the library USB Connection event. */
void EVENT_USB_Device_Connect(void)
{
}

/** Event handler for the library USB Disconnection event. */
void EVENT_USB_Device_Disconnect(void)
{
}

/** Event handler for the library USB Configuration Changed event. */
void EVENT_USB_Device_ConfigurationChanged(void)
{
  bool ConfigSuccess = true;

  ConfigSuccess &= HID_Device_ConfigureEndpoints(&Keyboard_HID_Interface);
	ConfigSuccess &= HID_Device_ConfigureEndpoints(&MediaControl_HID_Interface);

  USB_Device_EnableSOFEvents();
}

/** Event handler for the library USB Control Request reception event. */
void EVENT_USB_Device_ControlRequest(void)
{
  HID_Device_ProcessControlRequest(&Keyboard_HID_Interface);
  HID_Device_ProcessControlRequest(&MediaControl_HID_Interface);
}

/** Event handler for the USB device Start Of Frame event. */
void EVENT_USB_Device_StartOfFrame(void)
{
  HID_Device_MillisecondElapsed(&Keyboard_HID_Interface);
  HID_Device_MillisecondElapsed(&MediaControl_HID_Interface);
}

/** HID class driver callback function for the creation of HID reports to the host.
 *
 *  \param[in]     HIDInterfaceInfo  Pointer to the HID class interface configuration structure being referenced
 *  \param[in,out] ReportID    Report ID requested by the host if non-zero, otherwise callback should set to the generated report ID
 *  \param[in]     ReportType  Type of the report to create, either HID_REPORT_ITEM_In or HID_REPORT_ITEM_Feature
 *  \param[out]    ReportData  Pointer to a buffer where the created report should be stored
 *  \param[out]    ReportSize  Number of bytes written in the report (or zero if no report is to be sent)
 *
 *  \return Boolean \c true to force the sending of the report, \c false to let the library determine if it needs to be sent
 */

bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
                                         uint8_t* const ReportID,
                                         const uint8_t ReportType,
                                         void* ReportData,
                                         uint16_t* const ReportSize)
{
  int num_keys = process_keyboard(pressed_scancodes);
  if (num_keys > MAX_SCANCODES) num_keys = MAX_SCANCODES;

  if (HIDInterfaceInfo == &Keyboard_HID_Interface) {
    // host asks for a keyboard report
    USB_KeyboardReport_Data_t* KeyboardReport = (USB_KeyboardReport_Data_t*)ReportData;
    *ReportSize = sizeof(USB_KeyboardReport_Data_t);
    for (int i=0; i<num_keys; i++) {
      uint8_t sc = pressed_scancodes[i];
      if (!is_media_key(sc)) {
        KeyboardReport->KeyCode[i] = sc;
      }
    }
  } else if (HIDInterfaceInfo == &MediaControl_HID_Interface) {
    // host asks for a media control report
    USB_MediaReport_Data_t* MediaControlReport = (USB_MediaReport_Data_t*)ReportData;
    *ReportSize = sizeof(USB_MediaReport_Data_t);
    for (int i=0; i<num_keys; i++) {
      uint8_t sc = pressed_scancodes[i];
      if (is_media_key(sc)) {
        get_media_keys(sc, MediaControlReport);
      }
    }
  }
  return false;
}

/** HID class driver callback function for the processing of HID reports from the host.
 *
 *  \param[in] HIDInterfaceInfo  Pointer to the HID class interface configuration structure being referenced
 *  \param[in] ReportID    Report ID of the received report from the host
 *  \param[in] ReportType  The type of report that the host has sent, either HID_REPORT_ITEM_Out or HID_REPORT_ITEM_Feature
 *  \param[in] ReportData  Pointer to a buffer where the received report has been stored
 *  \param[in] ReportSize  Size in bytes of the received HID report
 */
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
                                          const uint8_t ReportID,
                                          const uint8_t ReportType,
                                          const void* ReportData,
                                          const uint16_t ReportSize)
{
  uint8_t* data = (uint8_t*)ReportData;
  if (ReportSize<4) return;

  hid_report_cmd(data);
}