summaryrefslogtreecommitdiff
path: root/keyboard/hhkb_rn42/rn42
diff options
context:
space:
mode:
Diffstat (limited to 'keyboard/hhkb_rn42/rn42')
-rw-r--r--keyboard/hhkb_rn42/rn42/main.c102
-rw-r--r--keyboard/hhkb_rn42/rn42/rn42.c192
-rw-r--r--keyboard/hhkb_rn42/rn42/rn42.h18
-rw-r--r--keyboard/hhkb_rn42/rn42/rn42_task.c255
-rw-r--r--keyboard/hhkb_rn42/rn42/rn42_task.h10
-rw-r--r--keyboard/hhkb_rn42/rn42/suart.S156
-rw-r--r--keyboard/hhkb_rn42/rn42/suart.h8
7 files changed, 741 insertions, 0 deletions
diff --git a/keyboard/hhkb_rn42/rn42/main.c b/keyboard/hhkb_rn42/rn42/main.c
new file mode 100644
index 0000000000..a3a37c0747
--- /dev/null
+++ b/keyboard/hhkb_rn42/rn42/main.c
@@ -0,0 +1,102 @@
+#include <avr/io.h>
+#include <avr/power.h>
+#include <avr/wdt.h>
+#include "lufa.h"
+#include "print.h"
+#include "sendchar.h"
+#include "rn42.h"
+#include "rn42_task.h"
+#include "serial.h"
+#include "keyboard.h"
+#include "keycode.h"
+#include "action.h"
+#include "action_util.h"
+#include "wait.h"
+#include "suart.h"
+#include "suspend.h"
+
+static int8_t sendchar_func(uint8_t c)
+{
+ sendchar(c); // LUFA
+ xmit(c); // SUART
+ return 0;
+}
+
+static void SetupHardware(void)
+{
+ /* Disable watchdog if enabled by bootloader/fuses */
+ MCUSR &= ~(1 << WDRF);
+ wdt_disable();
+
+ /* Disable clock division */
+ clock_prescale_set(clock_div_1);
+
+ // Leonardo needs. Without this USB device is not recognized.
+ USB_Disable();
+
+ USB_Init();
+
+ // for Console_Task
+ USB_Device_EnableSOFEvents();
+ print_set_sendchar(sendchar_func);
+
+ // SUART PD0:output, PD1:input
+ DDRD |= (1<<0);
+ PORTD |= (1<<0);
+ DDRD &= ~(1<<1);
+ PORTD |= (1<<1);
+}
+
+int main(void) __attribute__ ((weak));
+int main(void)
+{
+ SetupHardware();
+ sei();
+
+ /* 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
+ USB_USBTask();
+#endif
+ }
+ print("USB configured.\n");
+
+ rn42_init();
+ rn42_task_init();
+ print("RN-42 init\n");
+
+ /* init modules */
+ keyboard_init();
+
+ if (!rn42_rts()) {
+ 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
+
+ rn42_task();
+ }
+}
diff --git a/keyboard/hhkb_rn42/rn42/rn42.c b/keyboard/hhkb_rn42/rn42/rn42.c
new file mode 100644
index 0000000000..3fcd64ad6a
--- /dev/null
+++ b/keyboard/hhkb_rn42/rn42/rn42.c
@@ -0,0 +1,192 @@
+#include <avr/io.h>
+#include "host.h"
+#include "host_driver.h"
+#include "serial.h"
+#include "rn42.h"
+#include "print.h"
+#include "wait.h"
+
+
+/* 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);
+
+host_driver_t rn42_driver = {
+ keyboard_leds,
+ send_keyboard,
+ send_mouse,
+ send_system,
+ send_consumer
+};
+
+
+void rn42_init(void)
+{
+ // 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();
+
+ // PF1: RTS(low: allowed to send, high: not allowed)
+ DDRF &= ~(1<<1);
+ PORTF &= ~(1<<1);
+
+ // PD5: CTS(low: allow to send, high:not allow)
+ DDRD |= (1<<5);
+ PORTD &= ~(1<<5);
+
+ serial_init();
+}
+
+void rn42_putc(uint8_t c)
+{
+ serial_send(c);
+}
+
+bool rn42_autoconnecting(void)
+{
+ // GPIO6 for control connection(high: auto connect, low: disconnect)
+ // Note that this needs config: SM,4(Auto-Connect DTR Mode)
+ return (PORTF & (1<<7) ? true : false);
+}
+
+void rn42_autoconnect(void)
+{
+ // hi to auto connect
+ DDRF |= (1<<7);
+ PORTF |= (1<<7);
+}
+
+void rn42_disconnect(void)
+{
+ // low to disconnect
+ DDRF |= (1<<7);
+ PORTF &= ~(1<<7);
+}
+
+bool rn42_rts(void)
+{
+ // low when RN-42 is powered and ready to receive
+ return PINF&(1<<1);
+}
+
+void rn42_cts_hi(void)
+{
+ // not allow to send
+ PORTD |= (1<<5);
+}
+
+void rn42_cts_lo(void)
+{
+ // allow to send
+ PORTD &= ~(1<<5);
+}
+
+
+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&0xFF);
+ serial_send((bits>>8)&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) {}
diff --git a/keyboard/hhkb_rn42/rn42/rn42.h b/keyboard/hhkb_rn42/rn42/rn42.h
new file mode 100644
index 0000000000..4189733b49
--- /dev/null
+++ b/keyboard/hhkb_rn42/rn42/rn42.h
@@ -0,0 +1,18 @@
+#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);
+bool rn42_autoconnecting(void);
+void rn42_autoconnect(void);
+void rn42_disconnect(void);
+bool rn42_rts(void);
+void rn42_cts_hi(void);
+void rn42_cts_lo(void);
+
+#endif
diff --git a/keyboard/hhkb_rn42/rn42/rn42_task.c b/keyboard/hhkb_rn42/rn42/rn42_task.c
new file mode 100644
index 0000000000..7ec4c1b540
--- /dev/null
+++ b/keyboard/hhkb_rn42/rn42/rn42_task.c
@@ -0,0 +1,255 @@
+#include <stdint.h>
+#include "keycode.h"
+#include "serial.h"
+#include "host.h"
+#include "action.h"
+#include "action_util.h"
+#include "lufa.h"
+#include "rn42_task.h"
+#include "print.h"
+#include "timer.h"
+#include "command.h"
+
+static bool config_mode = false;
+static bool force_usb = false;
+
+static void battery_adc_init(void)
+{
+ ADMUX = (1<<REFS1) | (1<<REFS0); // Ref:2.56V band-gap, Input:ADC0(PF0)
+ ADCSRA = (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // Prescale:128 16MHz/128=125KHz
+ ADCSRA |= (1<<ADEN); // enable ADC
+}
+
+static uint16_t battery_adc(void)
+{
+ volatile uint16_t bat;
+ ADCSRA |= (1<<ADEN);
+
+ // discard first result
+ ADCSRA |= (1<<ADSC);
+ while (ADCSRA & (1<<ADSC)) ;
+ bat = ADC;
+
+ // discard second result
+ ADCSRA |= (1<<ADSC);
+ while (ADCSRA & (1<<ADSC)) ;
+ bat = ADC;
+
+ ADCSRA |= (1<<ADSC);
+ while (ADCSRA & (1<<ADSC)) ;
+ bat = ADC;
+
+ ADCSRA &= ~(1<<ADEN);
+ return bat;
+}
+
+
+void rn42_task_init(void)
+{
+ battery_adc_init();
+}
+
+void rn42_task(void)
+{
+ int16_t c;
+ if (config_mode) {
+ // Config mode: print output from RN-42
+ while ((c = serial_recv2()) != -1) {
+ // without flow control it'll fail to receive data when flooded
+ rn42_cts_hi();
+ xprintf("%c", c);
+ rn42_cts_lo();
+ }
+ } else {
+ // Raw mode: interpret output report of LED state
+ 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("%02X\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: %02X\n", c);
+ state = LED_INIT;
+ break;
+ default:
+ state = LED_INIT;
+ }
+ }
+ }
+
+ /* Bluetooth mode when ready */
+ if (!config_mode && !force_usb) {
+ if (!rn42_rts() && host_get_driver() != &rn42_driver) {
+ clear_keyboard();
+ host_set_driver(&rn42_driver);
+ } else if (rn42_rts() && host_get_driver() != &lufa_driver) {
+ clear_keyboard();
+ host_set_driver(&lufa_driver);
+ }
+ }
+}
+
+
+
+/******************************************************************************
+ * Command
+ ******************************************************************************/
+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("Del: enter/exit config mode(auto_connect/disconnect)\n");
+ print("i: RN-42 info\n");
+ print("b: battery voltage\n");
+
+ if (config_mode) {
+ return true;
+ } else {
+ print("u: Force USB mode\n");
+ return false; // to display default command help
+ }
+ case KC_DELETE:
+ if (rn42_autoconnecting()) {
+ prev_driver = host_get_driver();
+ clear_keyboard();
+ _delay_ms(500);
+ host_set_driver(&rn42_config_driver); // null driver; not to send a key to host
+ rn42_disconnect();
+ print("\nRN-42: disconnect\n");
+ print("Enter config mode\n");
+ print("type $$$ to start and + for local echo\n");
+ command_state = CONSOLE;
+ config_mode = true;
+ } else {
+ rn42_autoconnect();
+ print("\nRN-42: auto_connect\n");
+ print("Exit config mode\n");
+ command_state = ONESHOT;
+ config_mode = false;
+ //clear_keyboard();
+ host_set_driver(prev_driver);
+ }
+ 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_I:
+ print("\n----- RN-42 info -----\n");
+ xprintf("protocol: %s\n", (host_get_driver() == &rn42_driver) ? "RN-42" : "LUFA");
+ xprintf("force_usb: %X\n", force_usb);
+ xprintf("rn42_autoconnecting(): %X\n", rn42_autoconnecting());
+ xprintf("rn42_rts(): %X\n", rn42_rts());
+ xprintf("config_mode: %X\n", config_mode);
+ return true;
+ case KC_B:
+ // battery monitor
+ xprintf("BAT: %04X(%08lX)\n", battery_adc(), timer_read32());
+ 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));
+ 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 ' ';
+ }
+}
diff --git a/keyboard/hhkb_rn42/rn42/rn42_task.h b/keyboard/hhkb_rn42/rn42/rn42_task.h
new file mode 100644
index 0000000000..8f6c3ab64d
--- /dev/null
+++ b/keyboard/hhkb_rn42/rn42/rn42_task.h
@@ -0,0 +1,10 @@
+#ifndef RN42_TASK_H
+#define RN42_TASK_H
+
+#include <stdbool.h>
+#include "rn42.h"
+
+void rn42_task_init(void);
+void rn42_task(void);
+
+#endif
diff --git a/keyboard/hhkb_rn42/rn42/suart.S b/keyboard/hhkb_rn42/rn42/suart.S
new file mode 100644
index 0000000000..9fa5452928
--- /dev/null
+++ b/keyboard/hhkb_rn42/rn42/suart.S
@@ -0,0 +1,156 @@
+;---------------------------------------------------------------------------;
+; Software implemented UART module ;
+; (C)ChaN, 2005 (http://elm-chan.org/) ;
+;---------------------------------------------------------------------------;
+; Bit rate settings:
+;
+; 1MHz 2MHz 4MHz 6MHz 8MHz 10MHz 12MHz 16MHz 20MHz
+; 2.4kbps 138 - - - - - - - -
+; 4.8kbps 68 138 - - - - - - -
+; 9.6kbps 33 68 138 208 - - - - -
+; 19.2kbps - 33 68 102 138 173 208 - -
+; 38.4kbps - - 33 50 68 85 102 138 172
+; 57.6kbps - - 21 33 44 56 68 91 114
+; 115.2kbps - - - - 21 27 33 44 56
+
+.nolist
+#include <avr/io.h>
+.list
+
+#define BPS 44 /* Bit delay. (see above table) */
+#define BIDIR 0 /* 0:Separated Tx/Rx, 1:Shared Tx/Rx */
+
+#define OUT_1 sbi _SFR_IO_ADDR(SUART_OUT_PORT), SUART_OUT_BIT /* Output 1 */
+#define OUT_0 cbi _SFR_IO_ADDR(SUART_OUT_PORT), SUART_OUT_BIT /* Output 0 */
+#define SKIP_IN_1 sbis _SFR_IO_ADDR(SUART_IN_PIN), SUART_IN_BIT /* Skip if 1 */
+#define SKIP_IN_0 sbic _SFR_IO_ADDR(SUART_IN_PIN), SUART_IN_BIT /* Skip if 0 */
+
+
+
+#ifdef SPM_PAGESIZE
+.macro _LPMI reg
+ lpm \reg, Z+
+.endm
+.macro _MOVW dh,dl, sh,sl
+ movw \dl, \sl
+.endm
+#else
+.macro _LPMI reg
+ lpm
+ mov \reg, r0
+ adiw ZL, 1
+.endm
+.macro _MOVW dh,dl, sh,sl
+ mov \dl, \sl
+ mov \dh, \sh
+.endm
+#endif
+
+
+
+;---------------------------------------------------------------------------;
+; Transmit a byte in serial format of N81
+;
+;Prototype: void xmit (uint8_t data);
+;Size: 16 words
+
+.global xmit
+.func xmit
+xmit:
+#if BIDIR
+ ldi r23, BPS-1 ;Pre-idle time for bidirectional data line
+5: dec r23 ;
+ brne 5b ;/
+#endif
+ in r0, _SFR_IO_ADDR(SREG) ;Save flags
+
+ com r24 ;C = start bit
+ ldi r25, 10 ;Bit counter
+ cli ;Start critical section
+
+1: ldi r23, BPS-1 ;----- Bit transferring loop
+2: dec r23 ;Wait for a bit time
+ brne 2b ;/
+ brcs 3f ;MISO = bit to be sent
+ OUT_1 ;
+3: brcc 4f ;
+ OUT_0 ;/
+4: lsr r24 ;Get next bit into C
+ dec r25 ;All bits sent?
+ brne 1b ; no, coutinue
+
+ out _SFR_IO_ADDR(SREG), r0 ;End of critical section
+ ret
+.endfunc
+
+
+
+;---------------------------------------------------------------------------;
+; Receive a byte
+;
+;Prototype: uint8_t rcvr (void);
+;Size: 19 words
+
+.global rcvr
+.func rcvr
+rcvr:
+ in r0, _SFR_IO_ADDR(SREG) ;Save flags
+
+ ldi r24, 0x80 ;Receiving shift reg
+ cli ;Start critical section
+
+1: SKIP_IN_1 ;Wait for idle
+ rjmp 1b
+2: SKIP_IN_0 ;Wait for start bit
+ rjmp 2b
+ ldi r25, BPS/2 ;Wait for half bit time
+3: dec r25
+ brne 3b
+
+4: ldi r25, BPS ;----- Bit receiving loop
+5: dec r25 ;Wait for a bit time
+ brne 5b ;/
+ lsr r24 ;Next bit
+ SKIP_IN_0 ;Get a data bit into r24.7
+ ori r24, 0x80
+ brcc 4b ;All bits received? no, continue
+
+ out _SFR_IO_ADDR(SREG), r0 ;End of critical section
+ ret
+.endfunc
+
+
+; Not wait for start bit. This should be called after detecting start bit.
+.global recv
+.func recv
+recv:
+ in r0, _SFR_IO_ADDR(SREG) ;Save flags
+
+ ldi r24, 0x80 ;Receiving shift reg
+ cli ;Start critical section
+
+;1: SKIP_IN_1 ;Wait for idle
+; rjmp 1b
+;2: SKIP_IN_0 ;Wait for start bit
+; rjmp 2b
+ ldi r25, BPS/2 ;Wait for half bit time
+3: dec r25
+ brne 3b
+
+4: ldi r25, BPS ;----- Bit receiving loop
+5: dec r25 ;Wait for a bit time
+ brne 5b ;/
+ lsr r24 ;Next bit
+ SKIP_IN_0 ;Get a data bit into r24.7
+ ori r24, 0x80
+ brcc 4b ;All bits received? no, continue
+
+ ldi r25, BPS/2 ;Wait for half bit time
+6: dec r25
+ brne 6b
+7: SKIP_IN_1 ;Wait for stop bit
+ rjmp 7b
+
+ out _SFR_IO_ADDR(SREG), r0 ;End of critical section
+ ret
+.endfunc
diff --git a/keyboard/hhkb_rn42/rn42/suart.h b/keyboard/hhkb_rn42/rn42/suart.h
new file mode 100644
index 0000000000..72725b998f
--- /dev/null
+++ b/keyboard/hhkb_rn42/rn42/suart.h
@@ -0,0 +1,8 @@
+#ifndef SUART
+#define SUART
+
+void xmit(uint8_t);
+uint8_t rcvr(void);
+uint8_t recv(void);
+
+#endif /* SUART */