keychron_qmk_firmware/protocol/iwrap/main.c
2012-10-27 03:07:37 +09:00

376 lines
9.2 KiB
C

/*
Copyright 2011 Jun Wako <wakojun@gmail.com>
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 <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
//#include <avr/wdt.h>
#include "wd.h" // in order to use watchdog in interrupt mode
#include <avr/sleep.h>
#include <util/delay.h>
#include <avr/power.h>
#include "keyboard.h"
#include "matrix.h"
#include "host.h"
#include "iwrap.h"
#ifdef HOST_VUSB
# include "vusb.h"
# include "usbdrv.h"
#endif
#include "uart.h"
#include "suart.h"
#include "timer.h"
#include "debug.h"
#include "keycode.h"
#include "command.h"
static void sleep(uint8_t term);
static bool console(void);
static uint8_t console_command(uint8_t c);
static uint8_t key2asc(uint8_t key);
/*
static void set_prr(void)
{
power_adc_disable();
power_spi_disable();
power_twi_disable();
#ifndef TIMER_H
//power_timer0_disable(); // used in timer.c
#endif
power_timer1_disable();
power_timer2_disable();
}
*/
/*
static void pullup_pins(void)
{
// DDRs are set to 0(input) by default.
#ifdef PORTA
PORTA = 0xFF;
#endif
PORTB = 0xFF;
PORTC = 0xFF;
PORTD = 0xFF;
#ifdef PORTE
PORTE = 0xFF;
#endif
#ifdef PORTE
PORTF = 0xFF;
#endif
}
*/
#ifdef HOST_VUSB
static void disable_vusb(void)
{
// disable interrupt & disconnect to prevent host from enumerating
USB_INTR_ENABLE &= ~(1 << USB_INTR_ENABLE_BIT);
usbDeviceDisconnect();
}
static void enable_vusb(void)
{
USB_INTR_ENABLE |= (1 << USB_INTR_ENABLE_BIT);
usbDeviceConnect();
}
static void init_vusb(void)
{
uint8_t i = 0;
usbInit();
disable_vusb();
/* fake USB disconnect for > 250 ms */
while(--i){
_delay_ms(1);
}
enable_vusb();
}
#endif
void change_driver(host_driver_t *driver)
{
host_clear_keyboard_report();
host_swap_keyboard_report();
host_clear_keyboard_report();
host_send_keyboard_report();
_delay_ms(1000);
host_set_driver(driver);
}
static bool sleeping = false;
static bool insomniac = false; // TODO: should be false for power saving
static uint16_t last_timer = 0;
int main(void)
{
MCUSR = 0;
clock_prescale_set(clock_div_1);
WD_SET(WD_OFF);
// power saving: the result is worse than nothing... why?
//pullup_pins();
//set_prr();
#ifdef HOST_VUSB
disable_vusb();
#endif
uart_init(115200);
keyboard_init();
print("\nSend BREAK for UART Console Commands.\n");
// TODO: move to iWRAP/suart file
print("suart init\n");
// suart init
// PC4: Tx Output IDLE(Hi)
PORTC |= (1<<4);
DDRC |= (1<<4);
// PC5: Rx Input(pull-up)
PORTC |= (1<<5);
DDRC &= ~(1<<5);
// suart receive interrut(PC5/PCINT13)
PCMSK1 = 0b00100000;
PCICR = 0b00000010;
host_set_driver(iwrap_driver());
print("iwrap_init()\n");
iwrap_init();
iwrap_call();
last_timer = timer_read();
while (true) {
#ifdef HOST_VUSB
if (host_get_driver() == vusb_driver())
usbPoll();
#endif
keyboard_task();
#ifdef HOST_VUSB
if (host_get_driver() == vusb_driver())
vusb_transfer_keyboard();
#endif
if (matrix_is_modified() || console()) {
last_timer = timer_read();
sleeping = false;
} else if (!sleeping && timer_elapsed(last_timer) > 4000) {
sleeping = true;
iwrap_check_connection();
}
if (host_get_driver() == iwrap_driver()) {
if (sleeping && !insomniac) {
_delay_ms(1); // wait for UART to send
iwrap_sleep();
sleep(WDTO_60MS);
}
}
}
}
static void sleep(uint8_t term)
{
WD_SET(WD_IRQ, term);
cli();
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_bod_disable();
sei();
sleep_cpu();
sleep_disable();
WD_SET(WD_OFF);
}
ISR(WDT_vect)
{
// wake up
}
static bool console(void)
{
// Send to Bluetoot module WT12
static bool breaked = false;
if (!uart_available())
return false;
else {
uint8_t c;
c = uart_getchar();
uart_putchar(c);
switch (c) {
case 0x00: // BREAK signal
if (!breaked) {
print("break(? for help): ");
breaked = true;
}
break;
case '\r':
uart_putchar('\n');
iwrap_buf_send();
break;
case '\b':
iwrap_buf_del();
break;
default:
if (breaked) {
print("\n");
console_command(c);
breaked = false;
} else {
iwrap_buf_add(c);
}
break;
}
return true;
}
}
uint8_t command_extra()
{
return console_command(key2asc(host_get_first_key()));
}
static uint8_t console_command(uint8_t c)
{
switch (c) {
case 'h':
case '?':
print("\nCommands for Bluetooth(WT12/iWRAP):\n");
print("r: reset. software reset by watchdog\n");
print("i: insomniac. prevent KB from sleeping\n");
print("c: iwrap_call. CALL for BT connection.\n");
#ifdef HOST_VUSB
print("u: USB mode. switch to USB.\n");
print("w: BT mode. switch to Bluetooth.\n");
#endif
print("k: kill first connection.\n");
print("Del: unpair first pairing.\n");
print("\n");
return 0;
case 'r':
print("reset\n");
WD_AVR_RESET();
return 1;
case 'i':
insomniac = !insomniac;
if (insomniac)
print("insomniac\n");
else
print("not insomniac\n");
return 1;
case 'c':
print("iwrap_call()\n");
iwrap_call();
return 1;
#ifdef HOST_VUSB
case 'u':
print("USB mode\n");
init_vusb();
change_driver(vusb_driver());
//iwrap_kill();
//iwrap_sleep();
// disable suart receive interrut(PC5/PCINT13)
PCMSK1 &= ~(0b00100000);
PCICR &= ~(0b00000010);
return 1;
case 'w':
print("iWRAP mode\n");
change_driver(iwrap_driver());
disable_vusb();
// enable suart receive interrut(PC5/PCINT13)
PCMSK1 |= 0b00100000;
PCICR |= 0b00000010;
return 1;
#endif
case 'k':
print("kill\n");
iwrap_kill();
return 1;
case 0x7F: // DELETE
print("unpair\n");
iwrap_unpair();
return 1;
}
return 0;
}
// convert keycode into ascii charactor
static uint8_t key2asc(uint8_t key)
{
switch (key) {
case KC_A: return 'a';
case KC_B: return 'b';
case KC_C: return 'c';
case KC_D: return 'd';
case KC_E: return 'e';
case KC_F: return 'f';
case KC_G: return 'g';
case KC_H: return 'h';
case KC_I: return 'i';
case KC_J: return 'j';
case KC_K: return 'k';
case KC_L: return 'l';
case KC_M: return 'm';
case KC_N: return 'n';
case KC_O: return 'o';
case KC_P: return 'p';
case KC_Q: return 'q';
case KC_R: return 'r';
case KC_S: return 's';
case KC_T: return 't';
case KC_U: return 'u';
case KC_V: return 'v';
case KC_W: return 'w';
case KC_X: return 'x';
case KC_Y: return 'y';
case KC_Z: return 'z';
case KC_1: return '1';
case KC_2: return '2';
case KC_3: return '3';
case KC_4: return '4';
case KC_5: return '5';
case KC_6: return '6';
case KC_7: return '7';
case KC_8: return '8';
case KC_9: return '9';
case KC_0: return '0';
case KC_ENTER: return '\n';
case KC_ESCAPE: return 0x1B;
case KC_BSPACE: return '\b';
case KC_TAB: return '\t';
case KC_SPACE: return ' ';
case KC_MINUS: return '-';
case KC_EQUAL: return '=';
case KC_LBRACKET: return '[';
case KC_RBRACKET: return ']';
case KC_BSLASH: return '\\';
case KC_NONUS_HASH: return '\\';
case KC_SCOLON: return ';';
case KC_QUOTE: return '\'';
case KC_GRAVE: return '`';
case KC_COMMA: return ',';
case KC_DOT: return '.';
case KC_SLASH: return '/';
default: return 0x00;
}
}