]> git.donarmstrong.com Git - tmk_firmware.git/commitdiff
Add RN-42 Bluetooth module support
authortmk <nobody@nowhere>
Mon, 30 Jun 2014 19:29:45 +0000 (04:29 +0900)
committertmk <nobody@nowhere>
Wed, 30 Jul 2014 05:38:26 +0000 (14:38 +0900)
keyboard/hhkb_rn42/MEMO.txt [new file with mode: 0644]
keyboard/hhkb_rn42/Makefile
keyboard/hhkb_rn42/config.h
keyboard/hhkb_rn42/main.c
keyboard/hhkb_rn42/rn42.c
keyboard/hhkb_rn42/rn42.h

diff --git a/keyboard/hhkb_rn42/MEMO.txt b/keyboard/hhkb_rn42/MEMO.txt
new file mode 100644 (file)
index 0000000..cc56834
--- /dev/null
@@ -0,0 +1,294 @@
+Roving RN-42
+============
+
+
+TODO
+----
+- USB connection check
+- BT on/off check: whether RX line is pulled up?
+- CTS/RTS lines are needed? just connect in loop back if no flow control is needed.
+- CTS is needed for waking up from deep sleep?
+- USB/BT switching  BT is on -> BT, BT is off -> USB
+
+- Low voltage lock out LVLO of DC converter for protection of Lipo
+- Lipo charger configuration: fast charge time
+- Low voltage alarm: LED indcates voltage of Lipo
+
+- Power saving: HHKB scan, BT radio control
+
+- Serial API callback  when data are available/received(and when send buffer is available)
+- Serial API instance  several serial ports can be used
+
+
+
+Configuration
+-------------
+Ver 6.15 04/26/2013
+(c) Roving Networks
+***Settings***
+BTA=0006664B3AE3
+BTName=tmkBT-3AE3
+Baudrt(SW4)=115K
+Mode  =Pair
+Authen=2
+Bonded=1
+Rem=001BDC06415B
+***ADVANCED Settings***
+SrvName= keyboard/mouse
+SrvClass=0000
+DevClass=05C0
+InqWindw=0100
+PagWindw=0100
+CfgTimer=255
+StatuStr=NULL
+HidFlags=3f
+DTRtimer=8
+KeySwapr=0
+***OTHER Settings***
+Profile= HID
+CfgChar= $
+SniffEna=0
+LowPower=0
+TX Power=4
+IOPorts= 0
+IOValues=0
+Sleeptmr=0
+DebugMod=0
+RoleSwch=0
+
+Ver 6.15 04/26/2013
+(c) Roving Networks
+***Settings***
+BTA=0006664B3AE3
+BTName=tmkBT-3AE3
+Baudrt(SW4)=115K
+Mode  =DTR
+Authen=2
+Bonded=0
+Rem=BCF5AC9BCB7E
+***ADVANCED Settings***
+SrvName= keyboard/mouse
+SrvClass=0000
+DevClass=0000
+InqWindw=0100
+PagWindw=0100
+CfgTimer=255
+StatuStr=NULL
+HidFlags=3f
+DTRtimer=8
+KeySwapr=0
+***OTHER Settings***
+Profile= HID
+CfgChar= $
+SniffEna=0
+LowPower=0
+TX Power=ffe8
+IOPorts= 0
+IOValues=0
+Sleeptmr=0
+DebugMod=0
+RoleSwch=0
+
+
+command mode
+------------
+To enter command mode disconnect the module from host and type '$$$'.(you will see 'CMD')
+To exit type '---'.(you will see 'END')
+
+Serial line:    115200bps, 8bit, 1-stopbit, non-parity, no flow control
+SSP:            115200bps, 8bit, 1-stopbit, non-parity, no flow control(via Bluetooth)
+
+
+S-,tmkBT        // Device name
+SH,0038         // HID register
+SC,0000         // COD: 000005C0    (see HID spec/Bluegiga doc)
+SD,05C0         //     bit 12-8         7           6           5-0
+                //         00101        1           1           0
+                //         peripheral   pointing    keybaord    joystick, gamepad, ...
+S~,6            // HID profile
+SS,keyboard/mouse   // service name
+SM,6                // Pairing mode: auto connect
+SM,4                // Master mode: Connection can be controled with GPIO6
+
+SY,FEE8             // lower power -20dbM
+
+
+
+HID profile
+-----------
+S~,6    HID profile
+S~,0    SPP profile
+R,1     reboot
+
+
+Apple iOS
+---------
+Cannot supported without apple iAP authentication chip.
+
+
+HID flag register
+-----------------
+SH,0200
+GH
+
+10 0000 0000(0200)  default
+00 0011 1000(0038)  Combo
+|| |  | |\_\____ number of paired devices to which the module can reconnect
+|| |  | \_______ send out reports over UART (0xFF <len> <data>)
+|| \__\_________ descriptor type
+|\______________ toggle virtual keyboard on iOS when first connected
+\_______________ Force HID mode if GPIO11 is high on power-up
+
+    Descriptor type:
+    0000:   keybaord
+    0001:   Game Pad
+    0010:   Mouse
+    0011:   Combo
+    0100:   Joystick
+    1xxx:   reserved
+
+Out report - Indicator
+----------------------
+0xFE 0x02 0x01 <LED_state>
+
+
+LED Status
+----------
+Configuring                     10 times per sec
+Startup/configuration timer     2 times per sec
+Discoverable/Inquiring/Idle     once per sec
+Connected                       solid on
+
+
+Pairing
+-------
+First, host initiates pairing process and once it is done, auto connect will work thereafter.
+SM,3        Master mode
+SM,4        Auto Connect DTR Mode uses GPIO6 to make and break connection(Mode =DTR)
+                confirm: auto connect works and control connection with GPIO6
+SM,5        Auto Connect ANY Mode (Mode =ANY)
+                each time GPIO is set, make inquiry and connect to the first found device
+SM,6        automatically reconnect(Mode =Pair)
+                confirm: auto connect works well but difficult to enter command mode.
+
+
+Fast data mode
+--------------
+The module enters fast data mode after 'remote configuration timer' window is passed from power on.
+In this mode the module does not accept '$$$' to enter command mode.
+
+Power Management
+----------------
+Inquiry and Page window     Idle or Active  (3.1.1)
+    Downside: delay in discovery or connection time
+    SI,         // set inquiry scan window(discovery) on/off duty?
+    SJ,         // set page scan window(connection)
+    This reduces averaege power >20mA to 5mA(3mA in Sniff mode)
+
+Sniff mode                  Transmit
+    Sniff mode is disabled by default and radio is active continuously when connected.(25-30mA)
+    In Sniff mode the radio wakes up intermittently and sleeps in very low power mode.(2mA)
+    SW,<val>    // set interval timer(*0.625ms) 0000-7FFF
+
+Deep sleep                  Idle            (3.1.2)
+    In this mode the module shuts down completly and only draws about 300uA. To enable this set the most signifant bit(0x8000) of Sniff interaval timer.
+    SW,8320     // deep sleep enable(interval=0x320*0.625ms)
+    In normal sleep the firmware is still running in idle mode, and wakes up about 20 times per second to check ports, update LEDs, etc. During deep sleep, the firmware actually stops runnig some tasks and the LEDs only update about once per second.
+    To wake from deep sleep there are three ways: (in worst case wake up takes 5ms)
+        *send a charactor to the UART(first charactor will be lost)
+        *toggle CTS low to high and wait 5ms
+        *wake automatically every slot time(<val>*0.625ms)
+    Once the radio is awake it stay active for exactly 1 second of inactivity and then sleeps again.
+    Downside: latency and data loss
+
+Disable Output driver       Idle or Active  (3.1.3)
+    S%,1000     // set all GPIO pins(0-11) to inputs.
+
+Lower Transmit Power        Idle or Active  (3.1.4)
+    SY,<hex>    // transmit power setting(takes effect after a power cycle and reboot)
+    Downside: reducing effective range
+
+
+Optimizig for Latency
+---------------------
+By default the firmware is optimized for throughput.
+SQ,16           // set latency bit
+SQ,0            // unset latency bit
+
+
+Configuration timer settings
+----------------------------
+Remote configuration is used for the module to be configured with various commands over Bluetooth(SPP profile only?).
+
+The module has remote configuration timer to allow remote configuration over Bluetooth after power up in Slave mode. In Master modes the remote configuration timer is set to 0(no remote configuration). (In Trigger Master mode the timer is used as an idle timer to break the connection after time expires with no charactors receive.)
+    ST,0        // no remote, no local when connected
+    ST,<1-252>  // local and remote with timeout in seconds from power up
+    ST,253      // local only       without timeout
+    ST,254      // remote only      without timeout
+    ST,255      // local and remote without timeout
+
+
+Android
+-------
+3.7.1.5 Note: To connect with Android phone the modules must wake up 11ms every 2.5seconds.
+
+
+Commands
+--------
+SC,
+SM,<val>
+SD,         
+SP,<string>             Pin code(alpahnumeric)
+SQ,<mask>               Special configuration(GPIO, discovery mode, low latency, reboot, UART)
+SR,<hex>                Store remote address
+SR,Z                    Erase all address
+SS,<string>             Set service name(1-20)**
+ST,<val>                Remote configuration timer(Master:0, Slave:0-255, Triger:as idle timer)
+SU,<val>                UART baud rate
+SW,<val>                low-power sniff mode** deep sleep and wake up every 625us * <val>
+SX,<0|1>                bonding enable  only acceps device that matches the stored address
+SY,<hex>                power setting** 
+SZ,<val>                non-standard raw baud rate  <val>=baud*0.004096
+S~,<val>                Profile     0:SPP, 5:APL, 6:HID
+S-,<string>             Device name     -15 alphanumeric charactors
+S?,<0|1>                role switch enable
+S$,<char>               command mode char
+$|,<hex>                low-power connect mode  deep sleep/active(discoverable and connectable) cycle
+D                       display basic setting
+E                       display extended setting
+GB                      display the device's Bluetooth address
+GF                      display Bluetooth address of connected device
+GK                      show connection status
+GR                      show remote address for reconnecting
+G&                      show GPIO pin
+G<char>                 show stored setting
++                       toggle local echo on/off
+&                       show GPIO 3,4,6,7(DIP switch)
+C                       connect to stored remote address
+C,<address>             connect last address
+CFI                     connect and go into fast data mode
+CFR                     connect and go into fast data mode
+CT,<address>,<val>      connect to the address and disconnect after val?
+F,1                     fast data mod:
+H                       display help
+I,<time>,<cod>          inquiry scan with <cod>
+IN
+IQ                      scan
+IS                      inquiry scan with 001F00
+J                       hide pin code
+K,                      kill    disconnects current connection
+L                       link quality
+M                       show modem signlal status
+O                       display other settings
+P,<car>                 pass through?
+Q                       quiet mode  make the module not discoverable
+Q,0                     discoverable and connectable
+Q,1                     not discoverable and not connectable
+Q,2                     not discoverable and connectable
+Q,?                     display current quiet mode
+R,1                     reboot
+T,<0|1>                 pass received data while in command mode
+U,<baud>,<parity>       change UART setting tentatively
+V                       display firmware version
+W                       wake from quiet mode    enable discovery and connection
+Z                       deep sleep mode(<2mA)
index 7b3b993b7047de8ac967f6db7b55c12f0aa13230..8cfe3f11841e465ad56be15f2755a61f2fc8bf71 100644 (file)
@@ -52,12 +52,14 @@ TARGET_DIR = .
 SRC += keymap_common.c \
        matrix.c \
        led.c \
+       serial_uart.c \
+       rn42.c \
        main.c
 
 ifdef KEYMAP
     SRC := keymap_$(KEYMAP).c $(SRC)
 else
-    SRC := keymap_hhkb.c $(SRC)
+    SRC := keymap_hasu.c $(SRC)
 endif
 
 CONFIG_H = config.h
@@ -123,14 +125,15 @@ MOUSEKEY_ENABLE = yes             # Mouse keys
 EXTRAKEY_ENABLE = yes          # Audio control and System control
 CONSOLE_ENABLE = yes           # Console for debug
 COMMAND_ENABLE = yes           # Commands for debug and configuration
-NKRO_ENABLE = yes              # USB Nkey Rollover
-KEYMAP_SECTION_ENABLE = yes    # fixed address keymap for keymap editor
+#NKRO_ENABLE = yes             # USB Nkey Rollover
+#KEYMAP_SECTION_ENABLE = yes   # fixed address keymap for keymap editor
 
 
 # Search Path
 VPATH += $(TARGET_DIR)
 VPATH += $(TOP_DIR)
 
+include $(TOP_DIR)/protocol.mk
 include $(TOP_DIR)/protocol/lufa.mk
 include $(TOP_DIR)/common.mk
 include $(TOP_DIR)/rules.mk
index a8f76ae6b07066802787833718ef1e217e273693..a0ab878eb80fc3db06073f4b6682f47bc0f11c12 100644 (file)
@@ -47,6 +47,35 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #define BOOTMAGIC_KEY_SALT      KC_FN6
 
 
+/*
+ * Hardware Serial(UART)
+ *     Baud rate are calculated with round off(+0.5).
+ */
+#ifdef __AVR_ATmega32U4__
+    /* iom32u4.h has no definition of UCSR1D. copy from iom32u2.h */
+    #define UCSR1D _SFR_MEM8(0xCB)
+    #define RTSEN 0
+    #define CTSEN 1
+
+    #define SERIAL_UART_BAUD       115200
+    #define SERIAL_UART_DATA       UDR1
+    #define SERIAL_UART_UBRR       ((F_CPU/(16.0*SERIAL_UART_BAUD)-1+0.5))
+    #define SERIAL_UART_RXD_VECT   USART1_RX_vect
+    #define SERIAL_UART_TXD_READY  (UCSR1A&(1<<UDRE1))
+    #define SERIAL_UART_INIT()     do { \
+        UBRR1L = (uint8_t) SERIAL_UART_UBRR;       /* baud rate */ \
+        UBRR1H = ((uint16_t)SERIAL_UART_UBRR>>8);  /* baud rate */ \
+        UCSR1B |= (1<<RXCIE1) | (1<<RXEN1); /* RX interrupt, RX: enable */ \
+        UCSR1B |= (0<<TXCIE1) | (1<<TXEN1); /* TX interrupt, TX: enable */ \
+        UCSR1C |= (0<<UPM11) | (0<<UPM10);  /* parity: none(00), even(01), odd(11) */ \
+        UCSR1D |= (0<<RTSEN) | (0<<CTSEN);  /* RTS, CTS */ \
+        sei(); \
+    } while(0)
+#else
+    #error "USART configuration is needed."
+#endif
+
+
 /*
  * Feature disable options
  *  These options are also useful to firmware size reduction.
index b6a8456f7fa1ce9fc0169981c6092b3ca27abc8f..b0097dfe0d7f27fedcdfc9f1d3911395277c8675 100644 (file)
@@ -4,8 +4,18 @@
 #include "lufa.h"
 #include "print.h"
 #include "sendchar.h"
+#include "rn42.h"
+#include "serial.h"
+#include "keyboard.h"
+#include "command.h"
+#include "keycode.h"
+#include "action.h"
+#include "action_util.h"
+#include "wait.h"
 
 
+bool config_mode = false;
+
 static void SetupHardware(void)
 {
     /* Disable watchdog if enabled by bootloader/fuses */
@@ -25,14 +35,17 @@ static void SetupHardware(void)
     print_set_sendchar(sendchar);
 }
 
+static bool force_usb = false;
 int main(void)  __attribute__ ((weak));
 int main(void)
 {
     SetupHardware();
     sei();
 
-    /* wait for USB startup & debug output */
-    while (USB_DeviceState != DEVICE_STATE_Configured) {
+    /* wait for USB startup to get ready for debug output */
+    uint8_t timeout = 255;  // timeout when USB is not available(Bluetooth)
+    while (timeout-- && USB_DeviceState != DEVICE_STATE_Configured) {
+        wait_ms(4);
 #if defined(INTERRUPT_CONTROL_ENDPOINT)
         ;
 #else
@@ -41,27 +54,240 @@ int main(void)
     }
     print("USB configured.\n");
 
+    rn42_init();
+    print("RN-42 init\n");
+
     /* init modules */
     keyboard_init();
-    host_set_driver(&lufa_driver);
+
+    if (rn42_ready()) {
+        host_set_driver(&rn42_driver);
+    } else {
+        host_set_driver(&lufa_driver);
+    }
+
 #ifdef SLEEP_LED_ENABLE
     sleep_led_init();
 #endif
 
     print("Keyboard start.\n");
     while (1) {
+/*
         while (USB_DeviceState == DEVICE_STATE_Suspended) {
             suspend_power_down();
             if (USB_Device_RemoteWakeupEnabled && suspend_wakeup_condition()) {
                     USB_Device_SendRemoteWakeup();
             }
         }
+*/
 
         keyboard_task();
 
 #if !defined(INTERRUPT_CONTROL_ENDPOINT)
         USB_USBTask();
 #endif
+
+        int16_t c;
+        if (config_mode) {
+            while ((c = serial_recv2()) != -1) xprintf("%c", c);
+        } else {
+            while ((c = serial_recv2()) != -1) {
+                // LED Out report: 0xFE, 0x02, 0x01, <leds>
+                // To get the report over UART set bit3 with SH, command.
+                static enum {LED_INIT, LED_FE, LED_02, LED_01} state = LED_INIT;
+                xprintf("%X\n", c);
+                switch (state) {
+                    case LED_INIT:
+                        if (c == 0xFE) state = LED_FE;
+                        else           state = LED_INIT;
+                        break;
+                    case LED_FE:
+                        if (c == 0x02) state = LED_02;
+                        else           state = LED_INIT;
+                        break;
+                    case LED_02:
+                        if (c == 0x01) state = LED_01;
+                        else           state = LED_INIT;
+                        break;
+                    case LED_01:
+                        // TODO: move to rn42.c and make accessible with keyboard_leds()
+                        xprintf("LED status: %X\n", c);
+                        state = LED_INIT;
+                        break;
+                    default:
+                        state = LED_INIT;
+                }
+            }
+        }
+
+        /* Bluetooth mode when ready */
+        if (!force_usb) {
+            if (rn42_ready() && host_get_driver() != &rn42_driver) {
+                clear_keyboard();
+                host_set_driver(&rn42_driver);
+            } else if (!rn42_ready() && host_get_driver() != &lufa_driver) {
+                clear_keyboard();
+                host_set_driver(&lufa_driver);
+            }
+        }
     }
 }
 
+static bool local_echo = false;
+bool command_extra(uint8_t code)
+{
+    static host_driver_t *prev_driver = &rn42_driver;
+    switch (code) {
+        case KC_H:
+        case KC_SLASH: /* ? */
+            print("\n\n----- Bluetooth RN-42 Help -----\n");
+            print("w:   toggle RN-42 config mode(enter/exit)\n");
+            print("l:   toggle print module output(local echo)\n");
+            print("a:   Bluetooth auto connect\n");
+            print("del: Bluetooth disconnect\n");
+            print("i:   info\n");
+
+            if (config_mode) {
+                return true;
+            } else {
+                print("u:   force USB mode\n");
+                return false;   // to display default command help
+            }
+        case KC_W:
+            if (!config_mode) {
+                print("\nEnter RN-42 config mode\n");
+                print("type $$$ to enter RN-42 command mode\n");
+                print("type Delete to disconnect Bluetooth connection\n");
+                command_state = CONSOLE;
+                config_mode = true;
+                prev_driver = host_get_driver();
+                clear_keyboard();
+                host_set_driver(&rn42_config_driver);
+            } else {
+                print("\nExit RN-42 config mode\n");
+                command_state = ONESHOT;
+                config_mode = false;
+                clear_keyboard();
+                host_set_driver(prev_driver);
+            }
+            return true;
+        case KC_L:
+            if (local_echo) {
+                print("local echo off\n");
+                local_echo = false;
+            } else {
+                print("local echo on\n");
+                local_echo = true;
+            }
+            return true;
+        case KC_U:
+            if (config_mode) return false;
+            if (force_usb) {
+                print("Auto mode\n");
+                force_usb = false;
+            } else {
+                print("USB mode\n");
+                force_usb = true;
+                clear_keyboard();
+                host_set_driver(&lufa_driver);
+            }
+            return true;
+        case KC_A:
+            print("auto connect\n");
+            rn42_autoconnect();
+            return true;
+        case KC_DELETE:
+            print("disconnect\n");
+            rn42_disconnect();
+            //rn42_putc('\0');    // see 5.3.4.4 DISCONNECT KEY of User's Guide
+            return true;
+        case KC_I:
+            print("\nRN-42 info\n");
+            xprintf("protocol: %s\n", (host_get_driver() == &rn42_driver) ? "RN-42" : "LUFA");
+            xprintf("force_usb: %X\n", force_usb);
+            xprintf("rn42_ready(): %X\n", rn42_ready());
+            xprintf("config_mode: %X\n", config_mode);
+            return true;
+        default:
+            if (config_mode)
+                return true;
+            else
+                return false;   // exec default command
+    }
+    return true;
+}
+
+static uint8_t code2asc(uint8_t code);
+bool command_console_extra(uint8_t code)
+{
+    switch (code) {
+        default:
+            rn42_putc(code2asc(code));
+            if (local_echo) xprintf("%c", code2asc(code));
+            return true;
+    }
+    return false;
+}
+
+// convert keycode into ascii charactor
+static uint8_t code2asc(uint8_t code)
+{
+    bool shifted = (get_mods() & (MOD_BIT(KC_LSHIFT)|MOD_BIT(KC_RSHIFT))) ? true : false;
+    switch (code) {
+        case KC_A: return (shifted ? 'A' : 'a');
+        case KC_B: return (shifted ? 'B' : 'b');
+        case KC_C: return (shifted ? 'C' : 'c');
+        case KC_D: return (shifted ? 'D' : 'd');
+        case KC_E: return (shifted ? 'E' : 'e');
+        case KC_F: return (shifted ? 'F' : 'f');
+        case KC_G: return (shifted ? 'G' : 'g');
+        case KC_H: return (shifted ? 'H' : 'h');
+        case KC_I: return (shifted ? 'I' : 'i');
+        case KC_J: return (shifted ? 'J' : 'j');
+        case KC_K: return (shifted ? 'K' : 'k');
+        case KC_L: return (shifted ? 'L' : 'l');
+        case KC_M: return (shifted ? 'M' : 'm');
+        case KC_N: return (shifted ? 'N' : 'n');
+        case KC_O: return (shifted ? 'O' : 'o');
+        case KC_P: return (shifted ? 'P' : 'p');
+        case KC_Q: return (shifted ? 'Q' : 'q');
+        case KC_R: return (shifted ? 'R' : 'r');
+        case KC_S: return (shifted ? 'S' : 's');
+        case KC_T: return (shifted ? 'T' : 't');
+        case KC_U: return (shifted ? 'U' : 'u');
+        case KC_V: return (shifted ? 'V' : 'v');
+        case KC_W: return (shifted ? 'W' : 'w');
+        case KC_X: return (shifted ? 'X' : 'x');
+        case KC_Y: return (shifted ? 'Y' : 'y');
+        case KC_Z: return (shifted ? 'Z' : 'z');
+        case KC_1: return (shifted ? '!' : '1');
+        case KC_2: return (shifted ? '@' : '2');
+        case KC_3: return (shifted ? '#' : '3');
+        case KC_4: return (shifted ? '$' : '4');
+        case KC_5: return (shifted ? '%' : '5');
+        case KC_6: return (shifted ? '^' : '6');
+        case KC_7: return (shifted ? '&' : '7');
+        case KC_8: return (shifted ? '*' : '8');
+        case KC_9: return (shifted ? '(' : '9');
+        case KC_0: return (shifted ? ')' : '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 (shifted ? '_' : '-');
+        case KC_EQUAL: return (shifted ? '+' : '=');
+        case KC_LBRACKET: return (shifted ? '{' : '[');
+        case KC_RBRACKET: return (shifted ? '}' : ']');
+        case KC_BSLASH: return (shifted ? '|' : '\\');
+        case KC_NONUS_HASH: return (shifted ? '|' : '\\');
+        case KC_SCOLON: return (shifted ? ':' : ';');
+        case KC_QUOTE: return (shifted ? '"' : '\'');
+        case KC_GRAVE: return (shifted ? '~' : '`');
+        case KC_COMMA: return (shifted ? '<' : ',');
+        case KC_DOT: return (shifted ? '>' : '.');
+        case KC_SLASH: return (shifted ? '?' : '/');
+        case KC_DELETE: return '\0';    // Delete to disconnect
+        default: return ' ';
+    }
+}
index bbf05a392a75baf5e039df5a6517d932fb615797..dceea72ca056888579fc13eca5e2cfc30c050bce 100644 (file)
@@ -1,6 +1,9 @@
 #include "host.h"
 #include "host_driver.h"
+#include "serial.h"
 #include "rn42.h"
+#include "print.h"
+#include "wait.h"
 
 
 /* Host driver */
@@ -18,20 +21,146 @@ host_driver_t rn42_driver = {
     send_consumer
 };
 
+
+void rn42_init(void)
+{
+    // PF1: check RTS(active low)
+    DDRF &= ~(1<<1);
+    PORTF &= ~(1<<1);
+
+    // PF7: BT connection control(HiZ: connect, low: disconnect)
+    // JTAG disable for PORT F. write JTD bit twice within four cycles.
+    MCUCR |= (1<<JTD);
+    MCUCR |= (1<<JTD);
+    rn42_autoconnect();
+
+    // PD5: CTS    (low: allow to send, high:not allowed)
+    DDRD |= (1<<5);
+    PORTD &= ~(1<<5);
+
+    serial_init();
+}
+
+void rn42_putc(uint8_t c)
+{
+    serial_send(c);
+}
+
+void rn42_autoconnect(void)
+{
+    DDRF &= ~(1<<7);
+    PORTF &= ~(1<<7);
+}
+
+void rn42_disconnect(void)
+{
+    DDRF |= (1<<7);
+    PORTF &= ~(1<<7);
+}
+
+bool rn42_ready(void)
+{
+    // RTS low
+    return PINF&(1<<1) ? false : true;
+}
+
+
 static uint8_t keyboard_leds(void) { return 0; }
 
 static void send_keyboard(report_keyboard_t *report)
 {
+    // wake from deep sleep
+    PORTD |= (1<<5);    // high
+    wait_ms(5);
+    PORTD &= ~(1<<5);   // low
+
+    serial_send(0xFD);  // Raw report mode
+    serial_send(9);     // length
+    serial_send(1);     // descriptor type
+    serial_send(report->mods);
+    serial_send(0x00);
+    serial_send(report->keys[0]);
+    serial_send(report->keys[1]);
+    serial_send(report->keys[2]);
+    serial_send(report->keys[3]);
+    serial_send(report->keys[4]);
+    serial_send(report->keys[5]);
 }
 
 static void send_mouse(report_mouse_t *report)
 {
+    // wake from deep sleep
+    PORTD |= (1<<5);    // high
+    wait_ms(5);
+    PORTD &= ~(1<<5);   // low
+
+    serial_send(0xFD);  // Raw report mode
+    serial_send(5);     // length
+    serial_send(2);     // descriptor type
+    serial_send(report->buttons);
+    serial_send(report->x);
+    serial_send(report->y);
+    serial_send(report->v);
 }
 
 static void send_system(uint16_t data)
 {
+    // Table 5-6 of RN-BT-DATA-UB
+    // 81,82,83 scan codes can be used?
+}
+
+
+static uint16_t usage2bits(uint16_t usage)
+{
+    switch (usage) {
+        case AC_HOME:               return 0x01;
+        case AL_EMAIL:              return 0x02;
+        case AC_SEARCH:             return 0x04;
+        //case AL_KBD_LAYOUT:         return 0x08;  // Apple virtual keybaord toggle
+        case AUDIO_VOL_UP:          return 0x10;
+        case AUDIO_VOL_DOWN:        return 0x20;
+        case AUDIO_MUTE:            return 0x40;
+        case TRANSPORT_PLAY_PAUSE:  return 0x80;
+        case TRANSPORT_NEXT_TRACK:  return 0x100;
+        case TRANSPORT_PREV_TRACK:  return 0x200;
+        case TRANSPORT_STOP:        return 0x400;
+        case TRANSPORT_STOP_EJECT:  return 0x800;
+        //case return 0x1000;   // Fast forward
+        //case return 0x2000;   // Rewind
+        //case return 0x4000;   // Stop/eject
+        //case return 0x8000;   // Internet browser
+    };
+    return 0;
 }
 
 static void send_consumer(uint16_t data)
 {
+    uint16_t bits = usage2bits(data);
+    serial_send(0xFD);  // Raw report mode
+    serial_send(3);     // length
+    serial_send(3);     // descriptor type
+    serial_send((bits>>8)&0xFF);
+    serial_send(bits&0xFF);
 }
+
+
+/* Null driver for config_mode */
+static uint8_t config_keyboard_leds(void);
+static void config_send_keyboard(report_keyboard_t *report);
+static void config_send_mouse(report_mouse_t *report);
+static void config_send_system(uint16_t data);
+static void config_send_consumer(uint16_t data);
+
+host_driver_t rn42_config_driver = {
+    config_keyboard_leds,
+    config_send_keyboard,
+    config_send_mouse,
+    config_send_system,
+    config_send_consumer
+};
+
+static uint8_t config_keyboard_leds(void) { return 0; }
+static void config_send_keyboard(report_keyboard_t *report) {}
+static void config_send_mouse(report_mouse_t *report) {}
+static void config_send_system(uint16_t data) {}
+static void config_send_consumer(uint16_t data) {}
index 57ae17ba172ca3669b06ae1498addeb7becad8f9..3e4c2fbe7d3e18db9509d340d1ca986bb121f4a7 100644 (file)
@@ -1,6 +1,15 @@
 #ifndef RN42_H
 #define RN42_H
 
+#include <stdbool.h>
+
 host_driver_t rn42_driver;
+host_driver_t rn42_config_driver;
+
+void rn42_init(void);
+void rn42_putc(uint8_t c);
+void rn42_autoconnect(void);
+void rn42_disconnect(void);
+bool rn42_ready(void);
 
 #endif