~ruther/qmk_firmware

ebe951a445b5d774542731d6165a9c5cd56beb2b — tmk 11 years ago cb3a547
Add Initial files of 4704_usb
A converter/ibm4704_usb/Makefile => converter/ibm4704_usb/Makefile +93 -0
@@ 0,0 1,93 @@
# Target file name (without extension).
TARGET = ibm4704_usb

# Directory common source filess exist
TOP_DIR = ../..

# Directory keyboard dependent files exist
TARGET_DIR = .

# project specific files
SRC =	keymap_common.c \
	matrix.c \
	led.c \
	protocol/ibm4704.c

ifdef KEYMAP
    SRC := keymap_$(KEYMAP).c $(SRC)
else
    SRC := keymap_plain.c $(SRC)
endif

CONFIG_H = config.h


# MCU name
#MCU = at90usb1287
MCU = atmega32u4

# Processor frequency.
#     This will define a symbol, F_CPU, in all source code files equal to the
#     processor frequency in Hz. You can then use this symbol in your source code to
#     calculate timings. Do NOT tack on a 'UL' at the end, this will be done
#     automatically to create a 32-bit value in your source code.
#
#     This will be an integer division of F_USB below, as it is sourced by
#     F_USB after it has run through any CPU prescalers. Note that this value
#     does not *change* the processor frequency - it should merely be updated to
#     reflect the processor speed set externally so that the code can use accurate
#     software delays.
F_CPU = 16000000


#
# LUFA specific
#
# Target architecture (see library "Board Types" documentation).
ARCH = AVR8

# Input clock frequency.
#     This will define a symbol, F_USB, in all source code files equal to the
#     input clock frequency (before any prescaling is performed) in Hz. This value may
#     differ from F_CPU if prescaling is used on the latter, and is required as the
#     raw input clock is fed directly to the PLL sections of the AVR for high speed
#     clock generation for the USB and other AVR subsections. Do NOT tack on a 'UL'
#     at the end, this will be done automatically to create a 32-bit value in your
#     source code.
#
#     If no clock division is performed on the input clock inside the AVR (via the
#     CPU clock adjust registers or the clock division fuses), this will be equal to F_CPU.
F_USB = $(F_CPU)

# Interrupt driven control endpoint task(+60)
OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT


# Boot Section Size in *bytes*
#   Teensy halfKay   512
#   Teensy++ halfKay 1024
#   Atmel DFU loader 4096
#   LUFA bootloader  4096
#   USBaspLoader     2048
OPT_DEFS += -DBOOTLOADER_SIZE=4096


# Build Options
#   comment out to disable the options.
#
#BOOTMAGIC_ENABLE = yes	# Virtual DIP switch configuration(+1000)
#MOUSEKEY_ENABLE = yes	# Mouse keys(+4700)
##EXTRAKEY_ENABLE = yes	# Audio control and System control(+450)
CONSOLE_ENABLE = yes	# Console for debug(+400)
COMMAND_ENABLE = yes    # Commands for debug and configuration
#NKRO_ENABLE = yes	# USB Nkey Rollover - not yet supported in LUFA


# 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

A converter/ibm4704_usb/README.md => converter/ibm4704_usb/README.md +31 -0
@@ 0,0 1,31 @@
IBM 4704 to USB keyboard converter
=======----=======================
This firmware converts IBM 4704 keyboard protocol to USB HID.


Connect Wires
-------------
In case of Teensy2.0(ATMega32U4):

1. Connect Vcc and GND.
2. Connect Clock and Data line. 
    - Clock is on PD1 and Data on PD2.
3. Optionally you need pull-up register. 1KOhm is OK?

To change pin configuration edit config.h.


Build Firmware
--------------
Just run `make`:

    $ make

To select keymap:

    $ make KEYMAP=[plain|...]


Keymap
------
Several version of keymap are available in advance but you are recommended to define your favorite layout yourself. To define your own keymap create file named `keymap_<name>.c` and see keymap document(you can find in top README.md) and existent keymap files.

A converter/ibm4704_usb/config.h => converter/ibm4704_usb/config.h +73 -0
@@ 0,0 1,73 @@
/*
Copyright 2014 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/>.
*/

#ifndef CONFIG_H
#define CONFIG_H

#include <avr/interrupt.h>

#define VENDOR_ID       0xFEED
#define PRODUCT_ID      0x4707
#define DEVICE_VER      0x0001
#define MANUFACTURER    t.m.k.
#define PRODUCT         IBM 4704 keyboard converter
#define DESCRIPTION     convert IBM 4704 keyboard to USB


/* matrix size */
#define MATRIX_ROWS 16  // keycode bit3-6 
#define MATRIX_COLS 8   // keycode bit0-2


/* key combination for command */
#define IS_COMMAND() ( \
    keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) \
)



/*
 * Busywait
 */
#define IBM4704_CLOCK_PORT  PORTD
#define IBM4704_CLOCK_PIN   PIND
#define IBM4704_CLOCK_DDR   DDRD
#define IBM4704_CLOCK_BIT   1
#define IBM4704_DATA_PORT   PORTD
#define IBM4704_DATA_PIN    PIND
#define IBM4704_DATA_DDR    DDRD
#define IBM4704_DATA_BIT    2

/*
 * Pin interrupt
 */
#ifdef IBM4704_USE_INT
#define IBM4704_INT_INIT()  do {    \
    EICRA |= ((1<<ISC11) |      \
              (0<<ISC10));      \
} while (0)
#define IBM4704_INT_ON()  do {      \
    EIMSK |= (1<<INT1);         \
} while (0)
#define IBM4704_INT_OFF() do {      \
    EIMSK &= ~(1<<INT1);        \
} while (0)
#define IBM4704_INT_VECT    INT1_vect
#endif


#endif

A converter/ibm4704_usb/keymap_common.c => converter/ibm4704_usb/keymap_common.c +30 -0
@@ 0,0 1,30 @@
/*
Copyright 2011,2012,2013 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/>.
*/
#include "keymap_common.h"


/* translates key to keycode */
uint8_t keymap_key_to_keycode(uint8_t layer, key_t key)
{
    return pgm_read_byte(&keymaps[(layer)][(key.row)][(key.col)]);
}

/* translates Fn keycode to action */
action_t keymap_fn_to_action(uint8_t keycode)
{
    return (action_t){ .code = pgm_read_word(&fn_actions[FN_INDEX(keycode)]) };
}

A converter/ibm4704_usb/keymap_common.h => converter/ibm4704_usb/keymap_common.h +73 -0
@@ 0,0 1,73 @@
/*
Copyright 2011,2012,2013 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/>.
*/
#ifndef KEYMAP_COMMON_H
#define KEYMAP_COMMON_H

#include <stdint.h>
#include <stdbool.h>
#include <avr/pgmspace.h>
#include "keycode.h"
#include "action.h"
#include "action_macro.h"
#include "report.h"
#include "print.h"
#include "debug.h"
#include "keymap.h"


// 32*8(256) byte array which converts PS/2 code into USB code
extern const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS];
extern const uint16_t fn_actions[];


/* Original keys */
#define KEYMAP( \
    K00,K18,K19,K1A,K10,K11,K12,K08,K09,K0A,K0F,K1F,K0D,K0C,K0E, \
    K04,K05,K06,K13,K14,K15,K16,K17,K01,K02,K03,K1B,K1C,K1D, \
    K20,K21,K22,K23,K24,K25,K26,K27,K28,K29,K2A,K2B,K2C,K2D, \
    K30,K3E,K32,K33,K34,K35,K36,K37,K38,K39,K3A,K3B,K3D, \
    K31,K41,K3F,        K40,                    K42,K2F \
) { \
    { KC_##K00, KC_##K01, KC_##K02, KC_##K03, KC_##K04, KC_##K05, KC_##K06, KC_NO }, \
    { KC_##K08, KC_##K09, KC_##K0A, KC_NO,    KC_##K0C, KC_##K0D, KC_##K0E, KC_##K0F }, \
    { KC_##K10, KC_##K11, KC_##K12, KC_##K13, KC_##K14, KC_##K15, KC_##K16, KC_##K17 }, \
    { KC_##K18, KC_##K19, KC_##K1A, KC_##K1B, KC_##K1C, KC_##K1D, KC_NO,    KC_##K1F }, \
    { KC_##K20, KC_##K21, KC_##K22, KC_##K23, KC_##K24, KC_##K25, KC_##K26, KC_##K27 }, \
    { KC_##K28, KC_##K29, KC_##K2A, KC_##K2B, KC_##K2C, KC_##K2D, KC_NO,    KC_##K2F }, \
    { KC_##K30, KC_##K31, KC_##K32, KC_##K33, KC_##K34, KC_##K35, KC_##K36, KC_##K37 }, \
    { KC_##K38, KC_##K39, KC_##K3A, KC_##K3B, KC_NO,    KC_##K3D, KC_##K3E, KC_##K3F }, \
    { KC_##K40, KC_##K41, KC_##K42, KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO }, \
    { KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO }, \
    { KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO }, \
    { KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO }, \
    { KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO }, \
    { KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO }, \
    { KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO }, \
    { KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO,    KC_NO }, \
}

#endif

/*
    { K48, K49, K4A, K4B, K4C, K4D, K4E, K4F }, \
    { K50, K51, K52, K53, K54, K55, K56, K57 }, \
    { K58, K59, K5A, K5B, K5C, K5D, K5E, K5F }, \
    { K60, K61, K62, K63, K64, K65, K66, K67 }, \
    { K68, K69, K6A, K6B, K6C, K6D, K6E, K6F }, \
    { K70, K71, K72, K73, K74, K75, K76, K77 }, \
    { K78, K79, K7A, K7B, K7C, K7D, K7E, K7F }, \
*/

A converter/ibm4704_usb/keymap_plain.c => converter/ibm4704_usb/keymap_plain.c +28 -0
@@ 0,0 1,28 @@
#include "keymap_common.h"


const uint8_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
    /* 0: default
     * ,-----------------------------------------------------------.
     * |  `|  1|  2|  3|  4|  5|  6|  7|  8|  9|  0|  -|  =|???|BS |
     * |-----------------------------------------------------------|
     * |Tab  |  Q|  W|  E|  R|  T|  Y|  U|  I|  O|  P|  [|  ]|    \|
     * |-----------------------------------------------------------|
     * |Ctrl  |  A|  S|  D|  F|  G|  H|  J|  K|  L|  ;|  '|???|Ret |
     * |-----------------------------------------------------------|
     * |Shif|???|  Z|  X|  C|  V|  B|  N|  M|  ,|  ,|  /|???|Shift |
     * |-----------------------------------------------------------|
     * |Ctrl |Gui|Alt  |         Space             |Alt  |Gui|Ctrl |
     * `-----------------------------------------------------------'
     */
    KEYMAP(
    ESC, 1,   2,   3,   4,   5,   6,   7,   8,   9,   0,   MINS,EQL, BSLS,GRV, \
    TAB, Q,   W,   E,   R,   T,   Y,   U,   I,   O,   P,   LBRC,RBRC,BSPC, \
    LCTL,A,   S,   D,   F,   G,   H,   J,   K,   L,   SCLN,QUOT,NO,  ENT, \
    LSFT,NO,  Z,   X,   C,   V,   B,   N,   M,   COMM,DOT, SLSH,     RSFT, \
    LCTL,LGUI,LALT,          SPC,                               RGUI,RCTL \
    ),
};

const uint16_t PROGMEM fn_actions[] = {
};

A converter/ibm4704_usb/led.c => converter/ibm4704_usb/led.c +24 -0
@@ 0,0 1,24 @@
/*
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/>.
*/

#include "stdint.h"
#include "led.h"


void led_set(uint8_t usb_led)
{
}

A converter/ibm4704_usb/matrix.c => converter/ibm4704_usb/matrix.c +179 -0
@@ 0,0 1,179 @@
/*
Copyright 2014 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/>.
*/

#include <stdint.h>
#include <stdbool.h>
#include <avr/io.h>
#include <util/delay.h>
#include "action.h"
#include "print.h"
#include "debug.h"
#include "util.h"
#include "ibm4704.h"
#include "matrix.h"


static void matrix_make(uint8_t code);
static void matrix_break(uint8_t code);
static void matrix_clear(void);


/*
 * Matrix Array usage:
 * IBM 4704 scan codes are assigned into 128(16x8)-cell matrix.
 *
 *    8bit wide
 *   +---------+
 *  0|         |
 *  :|   XX    | 00-7F
 *  f|         |
 *   +---------+
 *
 * Exceptions:
 */
static uint8_t matrix[MATRIX_ROWS];

// scan code bits  7654 3210
// R:row/C:column  -RRR RCCC
#define ROW(code)      ((code>>3)&0x0f)
#define COL(code)      (code&0x07)


inline
uint8_t matrix_rows(void)
{
    return MATRIX_ROWS;
}

inline
uint8_t matrix_cols(void)
{
    return MATRIX_COLS;
}

static void enable_break(void)
{
    uint8_t ret;
    for (uint8_t code = 0; code < 0x80; code++) {
        while (ibm4704_send(0x80|code) != 0) {
            print("z");
        }
        _delay_ms(1);
        ret = ibm4704_recv(); 
        if (ret!=0xFF) {
            xprintf("%0X: %0X ", code, ret);
        }
    }
    _delay_us(100);
    print("End\n");
    while (ibm4704_send(0xFF) != 0) {
        print("Z");
    } // End
}

void matrix_init(void)
{
    debug_enable = true;

    ibm4704_init();
    matrix_clear();

    xprintf("------\n");
    enable_break();
    //while (ibm4704_send(0x8C) != 0) ;
    //while (ibm4704_send(0xFF) != 0) ;
}

/*
 * IBM 4704 Scan Code
 */
uint8_t matrix_scan(void)
{
    uint8_t code = ibm4704_recv();
    if (code==0xFF) {
        // Not receivd
        return 0;
    } else if ((code&0x78)==0x78) {
        // 0xFF-F8 and 0x7F-78 is not scan code
        dprintf("Error: %0X\n", code);
        matrix_clear();
        return 0;
    } else if (code&0x80) {
        matrix_make(code);
    } else {
        matrix_break(code);
    }
    return 1;
}

inline
bool matrix_has_ghost(void)
{
    return false;
}

inline
bool matrix_is_on(uint8_t row, uint8_t col)
{
    return (matrix[row] & (1<<col));
}

inline
uint8_t matrix_get_row(uint8_t row)
{
    return matrix[row];
}

void matrix_print(void)
{
    print("\nr/c 01234567\n");
    for (uint8_t row = 0; row < matrix_rows(); row++) {
        // TODO: use new function
        phex(row); print(": ");
        pbin_reverse(matrix_get_row(row));
        print("\n");
    }
}

uint8_t matrix_key_count(void)
{
    uint8_t count = 0;
    for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
        count += bitpop(matrix[i]);
    }
    return count;
}



inline
static void matrix_make(uint8_t code)
{
    matrix[ROW(code)] |= 1<<COL(code);
}

inline
static void matrix_break(uint8_t code)
{
    matrix[ROW(code)] &= ~(1<<COL(code));
}

inline
static void matrix_clear(void)
{
    for (uint8_t i=0; i < MATRIX_ROWS; i++) matrix[i] = 0x00;
}

A protocol/ibm4704.c => protocol/ibm4704.c +150 -0
@@ 0,0 1,150 @@
/*
Copyright 2010,2011,2012,2013 Jun WAKO <wakojun@gmail.com>
*/
#include <stdbool.h>
#include <util/delay.h>
#include "debug.h"
#include "ibm4704.h"


#define WAIT(stat, us, err) do { \
    if (!wait_##stat(us)) { \
        ibm4704_error = err; \
        goto ERROR; \
    } \
} while (0)


uint8_t ibm4704_error = 0;


void ibm4704_init(void)
{
    // POR
    //_delay_ms(2500);
    //while ( 0xA3 != ibm4704_recv() ) ;

    inhibit();
    DDRD |= 1<<3;
    PORTD &= ~(1<<3);
}

uint8_t ibm4704_send(uint8_t data)
{
    bool parity = true; // odd parity
    ibm4704_error = 0;

    /* Request to send */
    idle();
    clock_lo();
PIND |= 1<<3;

    /* wait for Start bit(Clock:lo/Data:hi) */
    WAIT(data_hi, 300, 0x30);

    /* Data bit */
    for (uint8_t i = 0; i < 8; i++) {
        WAIT(clock_hi, 100, 0x31);
        //_delay_us(5);
PIND |= 1<<3;
        if (data&(1<<i)) {
            parity = !parity;
            data_hi();
        } else {
            data_lo();
        }
        WAIT(clock_lo, 100, 0x32);
    }

    /* Parity bit */
    WAIT(clock_hi, 100, 4);
PIND |= 1<<3;
    if (parity) { data_hi(); } else { data_lo(); }
    WAIT(clock_lo, 100, 5);

    /* Stop bit */
    WAIT(clock_hi, 100, 4);
    data_hi();

    /* End */
    WAIT(data_lo, 100, 6);

    inhibit();
    _delay_us(200); // wait to recover clock to hi
    return 0;
ERROR:
    inhibit();
xprintf("x%02X ", ibm4704_error);
    _delay_us(200); // wait to recover clock to hi
    return -1;
}

/* receive data when host want else inhibit communication */
uint8_t ibm4704_recv_response(void)
{
    // 250 * 100us(wait start bit in ibm4704_recv)
    uint8_t data = 0;
    uint8_t try = 250;
    do {
        data = ibm4704_recv();
    } while (try-- && ibm4704_error);
    return data;
}

/*
Keyboard to Host:
Clock   ~~~~___~~_~~_~~_~~_~~_~~_~~_~~_~~_~~~~~~~~ H:60us/L:30us

Data    ____~~X==X==X==X==X==X==X==X==X==X________
            |  0  1  2  3  4  5  6  7  P(odd)
            |  LSB                MSB
            Start bit(80us)
*/
uint8_t ibm4704_recv(void)
{
    uint8_t data = 0;
    bool parity = true;    // odd parity
    ibm4704_error = IBM4704_ERR_NONE;

    idle();
    _delay_us(5);   // wait for line settles

    /* start bit */
    WAIT(clock_lo, 100, 1); // wait for keyboard to send
    WAIT(data_hi, 100, 2);  // can be delayed that long

    WAIT(clock_hi, 100, 3); // first rising edge which can take longer
    /* data */
    for (uint8_t i = 0; i < 8; i++) {
        WAIT(clock_hi, 100, 0x20+i);
        //_delay_us(5);
        if (data_in()) {
            parity = !parity;
            data |= (1<<i);
        }
        WAIT(clock_lo, 150, 0x28+i);
    }

    /* parity */
    WAIT(clock_hi, 100, 7);
    if (data_in() != parity) {
        ibm4704_error = IBM4704_ERR_PARITY;
        goto ERROR;
    }
    WAIT(clock_lo, 150, 8);

    /* stop bit */
    WAIT(clock_hi, 100, 9);
    WAIT(data_lo, 1, 9);

    inhibit();
    _delay_us(200); // wait to recover clock to hi
    return data;
ERROR:
    if (ibm4704_error > 2) {
        xprintf("x%02X ", ibm4704_error);
    }
    inhibit();
    _delay_us(200); // wait to recover clock to hi
    return -1;
}

A protocol/ibm4704.h => protocol/ibm4704.h +111 -0
@@ 0,0 1,111 @@
/*
Copyright 2014 Jun WAKO <wakojun@gmail.com>
*/
#ifndef IBM4704_H
#define IBM4704_H

#define IBM4704_ERR_NONE        0
#define IBM4704_ERR_STARTBIT    1
#define IBM4704_ERR_PARITY      0x70


void ibm4704_init(void);
uint8_t ibm4704_send(uint8_t data);
uint8_t ibm4704_recv_response(void);
uint8_t ibm4704_recv(void);


/* Check pin configuration */
#if !(defined(IBM4704_CLOCK_PORT) && \
      defined(IBM4704_CLOCK_PIN) && \
      defined(IBM4704_CLOCK_DDR) && \
      defined(IBM4704_CLOCK_BIT))
#   error "ibm4704 clock pin configuration is required in config.h"
#endif

#if !(defined(IBM4704_DATA_PORT) && \
      defined(IBM4704_DATA_PIN) && \
      defined(IBM4704_DATA_DDR) && \
      defined(IBM4704_DATA_BIT))
#   error "ibm4704 data pin configuration is required in config.h"
#endif


/*--------------------------------------------------------------------
 * static functions
 *------------------------------------------------------------------*/
static inline void clock_lo(void)
{
    IBM4704_CLOCK_PORT &= ~(1<<IBM4704_CLOCK_BIT);
    IBM4704_CLOCK_DDR  |=  (1<<IBM4704_CLOCK_BIT);
}
static inline void clock_hi(void)
{
    /* input with pull up */
    IBM4704_CLOCK_DDR  &= ~(1<<IBM4704_CLOCK_BIT);
    IBM4704_CLOCK_PORT |=  (1<<IBM4704_CLOCK_BIT);
}
static inline bool clock_in(void)
{
    IBM4704_CLOCK_DDR  &= ~(1<<IBM4704_CLOCK_BIT);
    IBM4704_CLOCK_PORT |=  (1<<IBM4704_CLOCK_BIT);
    _delay_us(1);
    return IBM4704_CLOCK_PIN&(1<<IBM4704_CLOCK_BIT);
}
static inline void data_lo(void)
{
    IBM4704_DATA_PORT &= ~(1<<IBM4704_DATA_BIT);
    IBM4704_DATA_DDR  |=  (1<<IBM4704_DATA_BIT);
}
static inline void data_hi(void)
{
    /* input with pull up */
    IBM4704_DATA_DDR  &= ~(1<<IBM4704_DATA_BIT);
    IBM4704_DATA_PORT |=  (1<<IBM4704_DATA_BIT);
}
static inline bool data_in(void)
{
    IBM4704_DATA_DDR  &= ~(1<<IBM4704_DATA_BIT);
    IBM4704_DATA_PORT |=  (1<<IBM4704_DATA_BIT);
    _delay_us(1);
    return IBM4704_DATA_PIN&(1<<IBM4704_DATA_BIT);
}

static inline uint16_t wait_clock_lo(uint16_t us)
{
    while (clock_in()  && us) { asm(""); _delay_us(1); us--; }
    return us;
}
static inline uint16_t wait_clock_hi(uint16_t us)
{
    while (!clock_in() && us) { asm(""); _delay_us(1); us--; }
    return us;
}
static inline uint16_t wait_data_lo(uint16_t us)
{
    while (data_in() && us)  { asm(""); _delay_us(1); us--; }
    return us;
}
static inline uint16_t wait_data_hi(uint16_t us)
{
    while (!data_in() && us)  { asm(""); _delay_us(1); us--; }
    return us;
}

/* idle state that device can send */
static inline void idle(void)
{
    clock_hi();
    data_hi();
}

/* inhibit device to send 
 * keyboard checks Data line on start bit(Data:hi) and it stops sending if Data line is low.
 */
static inline void inhibit(void)
{
    clock_hi();
    data_lo();
}

#endif