]> git.donarmstrong.com Git - qmk_firmware.git/blobdiff - tmk_core/protocol/iwrap/iwrap.c
Merge commit 'a074364c3731d66b56d988c8a6c960a83ea0e0a1' as 'tmk_core'
[qmk_firmware.git] / tmk_core / protocol / iwrap / iwrap.c
diff --git a/tmk_core/protocol/iwrap/iwrap.c b/tmk_core/protocol/iwrap/iwrap.c
new file mode 100644 (file)
index 0000000..6a40411
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+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)
+    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
+}