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"
#include "matrix.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,
.Size = HID_EPSIZE,
.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;
minute
committed
reset_menu();
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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
//_delay_us(10);
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// 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();
return 0;
}
}
} 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;
} else {
active_matrix = matrix_fn;
}
}
circle = 0;
}
}
}
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;
return used_key_codes;
}
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 (;;)
{
process_keyboard(NULL);
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;
}
minute
committed
if (counter%2048 == 0) {
refresh_menu_page();
}
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
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);
}