qmk_firmware/tmk_core/protocol/iwrap/iwrap.c

420 lines
10 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/>.
*/
/* host driver for Bulegiga iWRAP */
/* Bluegiga BT12
* Connections
* Hardware UART Software UART BlueTooth
* PC=====UART=======AVR=====SUART====iWRAP(BT12)-----------PC
*
* - Hardware UART for Debug Console to communicate iWRAP
* - Software UART for iWRAP control to send keyboard/mouse data
*/
#include <stdint.h>
#include <string.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "keycode.h"
#include "suart.h"
#include "uart.h"
#include "report.h"
#include "host_driver.h"
#include "iwrap.h"
#include "print.h"
/* iWRAP MUX mode utils. 3.10 HID raw mode(iWRAP_HID_Application_Note.pdf) */
#define MUX_HEADER(LINK, LENGTH) \
do { \
xmit(0xbf); /* SOF */ \
xmit(LINK); /* Link */ \
xmit(0x00); /* Flags */ \
xmit(LENGTH); /* Length */ \
} while (0)
#define MUX_FOOTER(LINK) xmit(LINK ^ 0xff)
static uint8_t connected = 0;
// static uint8_t channel = 1;
/* iWRAP buffer */
#define MUX_BUF_SIZE 64
static char buf[MUX_BUF_SIZE];
static uint8_t snd_pos = 0;
#define MUX_RCV_BUF_SIZE 256
static char rcv_buf[MUX_RCV_BUF_SIZE];
static uint8_t rcv_head = 0;
static uint8_t rcv_tail = 0;
/* receive buffer */
static void rcv_enq(char c) {
uint8_t next = (rcv_head + 1) % MUX_RCV_BUF_SIZE;
if (next != rcv_tail) {
rcv_buf[rcv_head] = c;
rcv_head = next;
}
}
static char rcv_deq(void) {
char c = 0;
if (rcv_head != rcv_tail) {
c = rcv_buf[rcv_tail++];
rcv_tail %= MUX_RCV_BUF_SIZE;
}
return c;
}
/*
static char rcv_peek(void)
{
if (rcv_head == rcv_tail)
return 0;
return rcv_buf[rcv_tail];
}
*/
static void rcv_clear(void) { rcv_tail = rcv_head = 0; }
/* iWRAP response */
ISR(PCINT1_vect, ISR_BLOCK) // recv() runs away in case of ISR_NOBLOCK
{
if ((SUART_IN_PIN & (1 << SUART_IN_BIT))) return;
static volatile uint8_t mux_state = 0xff;
static volatile uint8_t mux_link = 0xff;
uint8_t c = recv();
switch (mux_state) {
case 0xff: // SOF
if (c == 0xbf) mux_state--;
break;
case 0xfe: // Link
mux_state--;
mux_link = c;
break;
case 0xfd: // Flags
mux_state--;
break;
case 0xfc: // Length
mux_state = c;
break;
case 0x00:
mux_state = 0xff;
mux_link = 0xff;
break;
default:
if (mux_state--) {
uart_putchar(c);
rcv_enq(c);
}
}
}
/*------------------------------------------------------------------*
* iWRAP communication
*------------------------------------------------------------------*/
void iwrap_init(void) {
// reset iWRAP if in already MUX mode after AVR software-reset
iwrap_send("RESET");
iwrap_mux_send("RESET");
_delay_ms(3000);
iwrap_send("\r\nSET CONTROL MUX 1\r\n");
_delay_ms(500);
iwrap_check_connection();
}
void iwrap_mux_send(const char *s) {
rcv_clear();
MUX_HEADER(0xff, strlen((char *)s));
iwrap_send(s);
MUX_FOOTER(0xff);
}
void iwrap_send(const char *s) {
while (*s) xmit(*s++);
}
/* send buffer */
void iwrap_buf_add(uint8_t c) {
// need space for '\0'
if (snd_pos < MUX_BUF_SIZE - 1) buf[snd_pos++] = c;
}
void iwrap_buf_del(void) {
if (snd_pos) snd_pos--;
}
void iwrap_buf_send(void) {
buf[snd_pos] = '\0';
snd_pos = 0;
iwrap_mux_send(buf);
}
void iwrap_call(void) {
char *p;
iwrap_mux_send("SET BT PAIR");
_delay_ms(500);
p = rcv_buf + rcv_tail;
while (!strncmp(p, "SET BT PAIR", 11)) {
p += 7;
strncpy(p, "CALL", 4);
strncpy(p + 22, " 11 HID\n\0", 9);
print_S(p);
iwrap_mux_send(p);
// TODO: skip to next line
p += 57;
DEBUG_LED_CONFIG;
DEBUG_LED_ON;
_delay_ms(500);
DEBUG_LED_OFF;
_delay_ms(500);
DEBUG_LED_ON;
_delay_ms(500);
DEBUG_LED_OFF;
_delay_ms(500);
DEBUG_LED_ON;
_delay_ms(500);
DEBUG_LED_OFF;
_delay_ms(500);
DEBUG_LED_ON;
_delay_ms(500);
DEBUG_LED_OFF;
_delay_ms(500);
DEBUG_LED_ON;
_delay_ms(500);
DEBUG_LED_OFF;
_delay_ms(500);
}
iwrap_check_connection();
}
void iwrap_kill(void) {
char c;
iwrap_mux_send("LIST");
_delay_ms(500);
while ((c = rcv_deq()) && c != '\n')
;
if (strncmp(rcv_buf + rcv_tail, "LIST ", 5)) {
print("no connection to kill.\n");
return;
}
// skip 10 'space' chars
for (uint8_t i = 10; i; i--)
while ((c = rcv_deq()) && c != ' ')
;
char *p = rcv_buf + rcv_tail - 5;
strncpy(p, "KILL ", 5);
strncpy(p + 22, "\n\0", 2);
print_S(p);
iwrap_mux_send(p);
_delay_ms(500);
iwrap_check_connection();
}
void iwrap_unpair(void) {
iwrap_mux_send("SET BT PAIR");
_delay_ms(500);
char *p = rcv_buf + rcv_tail;
if (!strncmp(p, "SET BT PAIR", 11)) {
strncpy(p + 29, "\n\0", 2);
print_S(p);
iwrap_mux_send(p);
}
}
void iwrap_sleep(void) { iwrap_mux_send("SLEEP"); }
void iwrap_sniff(void) {}
void iwrap_subrate(void) {}
bool iwrap_failed(void) {
if (strncmp(rcv_buf, "SYNTAX ERROR", 12))
return true;
else
return false;
}
uint8_t iwrap_connected(void) { return connected; }
uint8_t iwrap_check_connection(void) {
iwrap_mux_send("LIST");
_delay_ms(100);
if (strncmp(rcv_buf, "LIST ", 5) || !strncmp(rcv_buf, "LIST 0", 6))
connected = 0;
else
connected = 1;
return connected;
}
/*------------------------------------------------------------------*
* Host driver
*------------------------------------------------------------------*/
static uint8_t keyboard_leds(void);
static void send_keyboard(report_keyboard_t *report);
static void send_mouse(report_mouse_t *report);
static void send_system(uint16_t data);
static void send_consumer(uint16_t data);
static host_driver_t driver = {keyboard_leds, send_keyboard, send_mouse, send_system, send_consumer};
host_driver_t *iwrap_driver(void) { return &driver; }
static uint8_t keyboard_leds(void) { return 0; }
static void send_keyboard(report_keyboard_t *report) {
if (!iwrap_connected() && !iwrap_check_connection()) return;
MUX_HEADER(0x01, 0x0c);
// HID raw mode header
xmit(0x9f);
xmit(0x0a); // Length
xmit(0xa1); // DATA(Input)
xmit(0x01); // Report ID
xmit(report->mods);
xmit(0x00); // reserved byte(always 0)
xmit(report->keys[0]);
xmit(report->keys[1]);
xmit(report->keys[2]);
xmit(report->keys[3]);
xmit(report->keys[4]);
xmit(report->keys[5]);
MUX_FOOTER(0x01);
}
static void send_mouse(report_mouse_t *report) {
#if defined(MOUSEKEY_ENABLE) || defined(PS2_MOUSE_ENABLE) || defined(POINTING_DEVICE_ENABLE)
if (!iwrap_connected() && !iwrap_check_connection()) return;
MUX_HEADER(0x01, 0x09);
// HID raw mode header
xmit(0x9f);
xmit(0x07); // Length
xmit(0xa1); // DATA(Input)
xmit(0x02); // Report ID
xmit(report->buttons);
xmit(report->x);
xmit(report->y);
xmit(report->v);
xmit(report->h);
MUX_FOOTER(0x01);
#endif
}
static void send_system(uint16_t data) { /* not supported */ }
static void send_consumer(uint16_t data) {
#ifdef EXTRAKEY_ENABLE
static uint16_t last_data = 0;
uint8_t bits1 = 0;
uint8_t bits2 = 0;
uint8_t bits3 = 0;
if (!iwrap_connected() && !iwrap_check_connection()) return;
if (data == last_data) return;
last_data = data;
// 3.10 HID raw mode(iWRAP_HID_Application_Note.pdf)
switch (data) {
case AUDIO_VOL_UP:
bits1 = 0x01;
break;
case AUDIO_VOL_DOWN:
bits1 = 0x02;
break;
case AUDIO_MUTE:
bits1 = 0x04;
break;
case TRANSPORT_PLAY_PAUSE:
bits1 = 0x08;
break;
case TRANSPORT_NEXT_TRACK:
bits1 = 0x10;
break;
case TRANSPORT_PREV_TRACK:
bits1 = 0x20;
break;
case TRANSPORT_STOP:
bits1 = 0x40;
break;
case TRANSPORT_EJECT:
bits1 = 0x80;
break;
case AL_EMAIL:
bits2 = 0x01;
break;
case AC_SEARCH:
bits2 = 0x02;
break;
case AC_BOOKMARKS:
bits2 = 0x04;
break;
case AC_HOME:
bits2 = 0x08;
break;
case AC_BACK:
bits2 = 0x10;
break;
case AC_FORWARD:
bits2 = 0x20;
break;
case AC_STOP:
bits2 = 0x40;
break;
case AC_REFRESH:
bits2 = 0x80;
break;
case AL_CC_CONFIG:
bits3 = 0x01;
break;
case AL_CALCULATOR:
bits3 = 0x04;
break;
case AL_LOCK:
bits3 = 0x08;
break;
case AL_LOCAL_BROWSER:
bits3 = 0x10;
break;
case AC_MINIMIZE:
bits3 = 0x20;
break;
case TRANSPORT_RECORD:
bits3 = 0x40;
break;
case TRANSPORT_REWIND:
bits3 = 0x80;
break;
}
MUX_HEADER(0x01, 0x07);
xmit(0x9f);
xmit(0x05); // Length
xmit(0xa1); // DATA(Input)
xmit(0x03); // Report ID
xmit(bits1);
xmit(bits2);
xmit(bits3);
MUX_FOOTER(0x01);
#endif
}