421 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			421 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
 | 
						|
}
 |