Merge branch 'ibmpc_update'
This commit is contained in:
commit
4052955535
9 changed files with 4019 additions and 2514 deletions
|
|
@ -145,6 +145,9 @@ https://www.seasip.info/VintagePC/ibm_1391406.html)
|
|||
[7] [The IBM 6110344 Keyboard - Scan Code Set 3 of 122-key terminal keyboard](
|
||||
https://www.seasip.info/VintagePC/ibm_6110344.html)
|
||||
|
||||
[8] [IBM PC AT Technical Reference 1986](
|
||||
(http://bitsavers.org/pdf/ibm/pc/at/6183355_PC_AT_Technical_Reference_Mar86.pdf)
|
||||
|
||||
[y] [TrackPoint Engineering Specifications for version 3E](
|
||||
https://web.archive.org/web/20100526161812/http://wwwcssrv.almaden.ibm.com/trackpoint/download.html)
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
1228
converter/ibmpc_usb/binary/ibmpc_usb_atmega32u2.hex
Normal file
1228
converter/ibmpc_usb/binary/ibmpc_usb_atmega32u2.hex
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -44,38 +44,47 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
* Pin and interrupt configuration
|
||||
*/
|
||||
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega32U2__) || defined(__AVR_AT90USB1286__)
|
||||
/* uses INT1 for clock line */
|
||||
/* clock line */
|
||||
#define IBMPC_CLOCK_PORT PORTD
|
||||
#define IBMPC_CLOCK_PIN PIND
|
||||
#define IBMPC_CLOCK_DDR DDRD
|
||||
#define IBMPC_CLOCK_BIT 1
|
||||
/* data line */
|
||||
#define IBMPC_DATA_PORT PORTD
|
||||
#define IBMPC_DATA_PIN PIND
|
||||
#define IBMPC_DATA_DDR DDRD
|
||||
#define IBMPC_DATA_BIT 0
|
||||
/* reset line */
|
||||
#define IBMPC_RST_PORT PORTB
|
||||
#define IBMPC_RST_PIN PINB
|
||||
#define IBMPC_RST_DDR DDRB
|
||||
#define IBMPC_RST_BIT1 6
|
||||
#define IBMPC_RST_BIT2 7
|
||||
|
||||
/* reset for XT keyboard: low pulse for 500ms and after that HiZ for safety */
|
||||
#define IBMPC_RESET() do { \
|
||||
/* reset for XT Type-1 keyboard: low pulse for 500ms */
|
||||
#define IBMPC_RST_HIZ() do { \
|
||||
IBMPC_RST_PORT &= ~(1<<IBMPC_RST_BIT1); \
|
||||
IBMPC_RST_DDR &= ~(1<<IBMPC_RST_BIT1); \
|
||||
IBMPC_RST_PORT &= ~(1<<IBMPC_RST_BIT2); \
|
||||
IBMPC_RST_DDR &= ~(1<<IBMPC_RST_BIT2); \
|
||||
} while (0)
|
||||
|
||||
#define IBMPC_RST_LO() do { \
|
||||
IBMPC_RST_PORT &= ~(1<<IBMPC_RST_BIT1); \
|
||||
IBMPC_RST_DDR |= (1<<IBMPC_RST_BIT1); \
|
||||
IBMPC_RST_PORT &= ~(1<<IBMPC_RST_BIT2); \
|
||||
IBMPC_RST_DDR |= (1<<IBMPC_RST_BIT2); \
|
||||
_delay_ms(500); \
|
||||
IBMPC_RST_DDR &= ~(1<<IBMPC_RST_BIT1); \
|
||||
IBMPC_RST_DDR &= ~(1<<IBMPC_RST_BIT2); \
|
||||
} while (0)
|
||||
|
||||
/* interrupt for clock line */
|
||||
#define IBMPC_INT_INIT() do { \
|
||||
EICRA |= ((1<<ISC11) | \
|
||||
(0<<ISC10)); \
|
||||
} while (0)
|
||||
|
||||
/* NOTE: clear flag and enabling to ditch unwanted interrupt */
|
||||
#define IBMPC_INT_ON() do { \
|
||||
EIFR |= (1<<INTF1); \
|
||||
EIMSK |= (1<<INT1); \
|
||||
} while (0)
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "timer.h"
|
||||
#include "action.h"
|
||||
#include "ibmpc_usb.h"
|
||||
#include "ibmpc.h"
|
||||
|
||||
|
||||
static void matrix_make(uint8_t code);
|
||||
|
|
@ -55,33 +56,37 @@ static uint16_t read_keyboard_id(void)
|
|||
int16_t code = 0;
|
||||
|
||||
// Disable
|
||||
code = ibmpc_host_send(0xF5);
|
||||
//code = ibmpc_host_send(0xF5);
|
||||
|
||||
// Read ID
|
||||
code = ibmpc_host_send(0xF2);
|
||||
if (code == -1) return 0xFFFF; // XT or No keyboard
|
||||
if (code != 0xFA) return 0xFFFE; // Broken PS/2?
|
||||
if (code == -1) { id = 0xFFFF; goto DONE; } // XT or No keyboard
|
||||
if (code != 0xFA) { id = 0xFFFE; goto DONE; } // Broken PS/2?
|
||||
|
||||
code = read_wait(1000);
|
||||
if (code == -1) return 0x0000; // AT
|
||||
// ID takes 500ms max TechRef [8] 4-41
|
||||
code = read_wait(500);
|
||||
if (code == -1) { id = 0x0000; goto DONE; } // AT
|
||||
id = (code & 0xFF)<<8;
|
||||
|
||||
code = read_wait(1000);
|
||||
code = read_wait(500);
|
||||
id |= code & 0xFF;
|
||||
|
||||
DONE:
|
||||
// Enable
|
||||
code = ibmpc_host_send(0xF4);
|
||||
//code = ibmpc_host_send(0xF4);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void hook_early_init(void)
|
||||
{
|
||||
ibmpc_host_init();
|
||||
ibmpc_host_enable();
|
||||
}
|
||||
|
||||
void matrix_init(void)
|
||||
{
|
||||
debug_enable = true;
|
||||
ibmpc_host_init();
|
||||
|
||||
// hard reset for XT keyboard
|
||||
IBMPC_RESET();
|
||||
|
||||
// initialize matrix state: all keys off
|
||||
for (uint8_t i=0; i < MATRIX_ROWS; i++) matrix[i] = 0x00;
|
||||
|
|
@ -106,17 +111,21 @@ uint8_t matrix_scan(void)
|
|||
// scan code reading states
|
||||
static enum {
|
||||
INIT,
|
||||
WAIT_STARTUP,
|
||||
XT_RESET,
|
||||
XT_RESET_WAIT,
|
||||
XT_RESET_DONE,
|
||||
WAIT_AA,
|
||||
WAIT_AABF,
|
||||
WAIT_AABFBF,
|
||||
READ_ID,
|
||||
LED_SET,
|
||||
SETUP,
|
||||
LOOP,
|
||||
END
|
||||
} state = INIT;
|
||||
static uint16_t last_time;
|
||||
static uint16_t init_time;
|
||||
|
||||
|
||||
if (ibmpc_error) {
|
||||
xprintf("err: %02X\n", ibmpc_error);
|
||||
xprintf("\nERR:%02X\n", ibmpc_error);
|
||||
|
||||
// when recv error, neither send error nor buffer full
|
||||
if (!(ibmpc_error & (IBMPC_ERR_SEND | IBMPC_ERR_FULL))) {
|
||||
|
|
@ -133,82 +142,135 @@ uint8_t matrix_scan(void)
|
|||
|
||||
switch (state) {
|
||||
case INIT:
|
||||
ibmpc_protocol = IBMPC_PROTOCOL_AT;
|
||||
xprintf("I%u ", timer_read());
|
||||
keyboard_kind = NONE;
|
||||
keyboard_id = 0x0000;
|
||||
last_time = timer_read();
|
||||
state = WAIT_STARTUP;
|
||||
|
||||
matrix_clear();
|
||||
clear_keyboard();
|
||||
|
||||
state = XT_RESET;
|
||||
break;
|
||||
case WAIT_STARTUP:
|
||||
// read and ignore BAT code and other codes when power-up
|
||||
ibmpc_host_recv();
|
||||
if (timer_elapsed(last_time) > 1000) {
|
||||
case XT_RESET:
|
||||
// Reset XT-initialize keyboard
|
||||
// XT: hard reset 500ms for IBM XT Type-1 keyboard and clones
|
||||
// XT: soft reset 20ms min(clock Lo)
|
||||
ibmpc_host_disable(); // soft reset: inihibit(clock Lo/Data Hi)
|
||||
IBMPC_RST_LO(); // hard reset: reset pin Lo
|
||||
|
||||
init_time = timer_read();
|
||||
state = XT_RESET_WAIT;
|
||||
break;
|
||||
case XT_RESET_WAIT:
|
||||
if (timer_elapsed(init_time) > 500) {
|
||||
state = XT_RESET_DONE;
|
||||
}
|
||||
break;
|
||||
case XT_RESET_DONE:
|
||||
IBMPC_RST_HIZ(); // hard reset: reset pin HiZ
|
||||
ibmpc_host_isr_clear();
|
||||
ibmpc_host_enable(); // soft reset: idle(clock Hi/Data Hi)
|
||||
|
||||
xprintf("X%u ", timer_read());
|
||||
init_time = timer_read();
|
||||
state = WAIT_AA;
|
||||
break;
|
||||
case WAIT_AA:
|
||||
// 1) Read BAT code and ID on keybaord power-up
|
||||
// For example, XT/AT sends 'AA' and Terminal sends 'AA BF BF' after BAT
|
||||
// AT 84-key: POR and BAT can take 900-9900ms according to AT TechRef [8] 4-7
|
||||
// AT 101/102-key: POR and BAT can take 450-2500ms according to AT TechRef [8] 4-39
|
||||
// 2) Read key typed by user or anything after error on protocol or scan code
|
||||
// This can happen in case of keyboard hotswap, unstable hardware, signal integrity problem or bug
|
||||
|
||||
/* wait until keyboard sends any code without 10000ms timeout
|
||||
if (timer_elapsed(init_time) > 10000) {
|
||||
state = READ_ID;
|
||||
}
|
||||
*/
|
||||
if (ibmpc_host_recv() != -1) { // wait for AA
|
||||
xprintf("W%u ", timer_read());
|
||||
init_time = timer_read();
|
||||
state = WAIT_AABF;
|
||||
}
|
||||
break;
|
||||
case WAIT_AABF:
|
||||
// NOTE: we can omit to wait BF BF
|
||||
// ID takes 500ms max? TechRef [8] 4-41, though 1ms is enough for 122-key Terminal 6110345
|
||||
if (timer_elapsed(init_time) > 500) {
|
||||
state = READ_ID;
|
||||
}
|
||||
if (ibmpc_host_recv() != -1) { // wait for BF
|
||||
xprintf("W%u ", timer_read());
|
||||
init_time = timer_read();
|
||||
state = WAIT_AABFBF;
|
||||
}
|
||||
break;
|
||||
case WAIT_AABFBF:
|
||||
if (timer_elapsed(init_time) > 500) {
|
||||
state = READ_ID;
|
||||
}
|
||||
if (ibmpc_host_recv() != -1) { // wait for BF
|
||||
xprintf("W%u ", timer_read());
|
||||
state = READ_ID;
|
||||
}
|
||||
break;
|
||||
case READ_ID:
|
||||
xprintf("R%u ", timer_read());
|
||||
|
||||
keyboard_id = read_keyboard_id();
|
||||
if (ibmpc_error) {
|
||||
xprintf("err: %02X\n", ibmpc_error);
|
||||
xprintf("\nERR:%02X\n", ibmpc_error);
|
||||
ibmpc_error = IBMPC_ERR_NONE;
|
||||
}
|
||||
xprintf("ID: %04X\n", keyboard_id);
|
||||
if (0xAB00 == (keyboard_id & 0xFF00)) {
|
||||
// CodeSet2 PS/2
|
||||
|
||||
if (0xAB00 == (keyboard_id & 0xFF00)) { // CodeSet2 PS/2
|
||||
keyboard_kind = PC_AT;
|
||||
} else if (0xBF00 == (keyboard_id & 0xFF00)) {
|
||||
// CodeSet3 Terminal
|
||||
} else if (0xBF00 == (keyboard_id & 0xFF00)) { // CodeSet3 Terminal
|
||||
keyboard_kind = PC_TERMINAL;
|
||||
} else if (0x0000 == keyboard_id) {
|
||||
// CodeSet2 AT
|
||||
} else if (0x0000 == keyboard_id) { // CodeSet2 AT
|
||||
keyboard_kind = PC_AT;
|
||||
} else if (0xFFFF == keyboard_id) {
|
||||
// CodeSet1 XT
|
||||
} else if (0xFFFF == keyboard_id) { // CodeSet1 XT
|
||||
keyboard_kind = PC_XT;
|
||||
} else if (0xFFFE == keyboard_id) {
|
||||
// CodeSet2 PS/2 fails to response?
|
||||
} else if (0xFFFE == keyboard_id) { // CodeSet2 PS/2 fails to response?
|
||||
keyboard_kind = PC_AT;
|
||||
} else if (0x00FF == keyboard_id) {
|
||||
// Mouse is not supported
|
||||
} else if (0x00FF == keyboard_id) { // Mouse is not supported
|
||||
xprintf("Mouse: not supported\n");
|
||||
keyboard_kind = NONE;
|
||||
} else {
|
||||
keyboard_kind = PC_AT;
|
||||
}
|
||||
|
||||
// protocol
|
||||
if (keyboard_kind == PC_XT) {
|
||||
xprintf("kbd: XT\n");
|
||||
ibmpc_protocol = IBMPC_PROTOCOL_XT;
|
||||
} else if (keyboard_kind == PC_AT) {
|
||||
xprintf("kbd: AT\n");
|
||||
ibmpc_protocol = IBMPC_PROTOCOL_AT;
|
||||
} else if (keyboard_kind == PC_TERMINAL) {
|
||||
xprintf("kbd: Terminal\n");
|
||||
ibmpc_protocol = IBMPC_PROTOCOL_AT;
|
||||
// Set all keys - make/break [3]p.23
|
||||
ibmpc_host_send(0xF8);
|
||||
} else {
|
||||
xprintf("kbd: Unknown\n");
|
||||
ibmpc_protocol = IBMPC_PROTOCOL_AT;
|
||||
}
|
||||
state = LED_SET;
|
||||
xprintf("ID:%04X(%d)\n", keyboard_id, keyboard_kind);
|
||||
|
||||
state = SETUP;
|
||||
break;
|
||||
case LED_SET:
|
||||
led_set(host_keyboard_leds());
|
||||
case SETUP:
|
||||
xprintf("S%u ", timer_read());
|
||||
switch (keyboard_kind) {
|
||||
case PC_XT:
|
||||
break;
|
||||
case PC_AT:
|
||||
led_set(host_keyboard_leds());
|
||||
break;
|
||||
case PC_TERMINAL:
|
||||
ibmpc_host_send(0xF8);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
state = LOOP;
|
||||
xprintf("L%u ", timer_read());
|
||||
case LOOP:
|
||||
switch (keyboard_kind) {
|
||||
case PC_XT:
|
||||
process_cs1();
|
||||
if (process_cs1() == -1) state = INIT;
|
||||
break;
|
||||
case PC_AT:
|
||||
process_cs2();
|
||||
if (process_cs2() == -1) state = INIT;
|
||||
break;
|
||||
case PC_TERMINAL:
|
||||
process_cs3();
|
||||
if (process_cs3() == -1) state = INIT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -349,7 +411,7 @@ static uint8_t cs1_e0code(uint8_t code) {
|
|||
case 0x63: return 0x7B; // Wake (MUHENKAN)
|
||||
|
||||
default:
|
||||
xprintf("!CS1_?!\n");
|
||||
xprintf("!CS1_E0_%02X!\n", code);
|
||||
return code;
|
||||
}
|
||||
return 0x00;
|
||||
|
|
@ -360,7 +422,7 @@ static int8_t process_cs1(void)
|
|||
static enum {
|
||||
INIT,
|
||||
E0,
|
||||
// Pause: E1 1D 45, E1 9D C5
|
||||
// Pause: E1 1D 45, E1 9D C5 [a] (TODO: test)
|
||||
E1,
|
||||
E1_1D,
|
||||
E1_9D,
|
||||
|
|
@ -371,9 +433,22 @@ static int8_t process_cs1(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Check invalid codes; 0x59-7F won't be used in real XT keyboards probably
|
||||
// 0x62 is used to handle escape code E0 and E1
|
||||
if ((code & 0x7F) >= 0x62) {
|
||||
xprintf("!CS1_INV!\n");
|
||||
state = INIT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (state) {
|
||||
case INIT:
|
||||
switch (code) {
|
||||
case 0x00:
|
||||
case 0xFF: // Error/Overrun [3]p.26
|
||||
xprintf("!CS1_ERR!\n");
|
||||
return -1;
|
||||
break;
|
||||
case 0xE0:
|
||||
state = E0;
|
||||
break;
|
||||
|
|
@ -591,6 +666,17 @@ static int8_t process_cs2(void)
|
|||
switch (state) {
|
||||
case INIT:
|
||||
switch (code) {
|
||||
case 0x00: // Error/Overrun [3]p.26
|
||||
xprintf("!CS2_OVR!\n");
|
||||
matrix_clear();
|
||||
clear_keyboard();
|
||||
break;
|
||||
case 0xFF:
|
||||
matrix_clear();
|
||||
xprintf("!CS2_ERR!\n");
|
||||
state = INIT;
|
||||
return -1;
|
||||
break;
|
||||
case 0xE0:
|
||||
state = E0;
|
||||
break;
|
||||
|
|
@ -608,11 +694,6 @@ static int8_t process_cs2(void)
|
|||
matrix_make(0x6F);
|
||||
state = INIT;
|
||||
break;
|
||||
case 0x00: // Overrun [3]p.26
|
||||
matrix_clear();
|
||||
xprintf("!CS2_OVERRUN!\n");
|
||||
state = INIT;
|
||||
break;
|
||||
case 0xAA: // Self-test passed
|
||||
case 0xFC: // Self-test failed
|
||||
// reset or plugin-in new keyboard
|
||||
|
|
@ -620,13 +701,14 @@ static int8_t process_cs2(void)
|
|||
return -1;
|
||||
break;
|
||||
default: // normal key make
|
||||
state = INIT;
|
||||
if (code < 0x80) {
|
||||
matrix_make(code);
|
||||
} else {
|
||||
matrix_clear();
|
||||
xprintf("!CS2_INIT!\n");
|
||||
return -1;
|
||||
}
|
||||
state = INIT;
|
||||
}
|
||||
break;
|
||||
case E0: // E0-Prefixed
|
||||
|
|
@ -639,13 +721,14 @@ static int8_t process_cs2(void)
|
|||
state = E0_F0;
|
||||
break;
|
||||
default:
|
||||
state = INIT;
|
||||
if (code < 0x80) {
|
||||
matrix_make(cs2_e0code(code));
|
||||
} else {
|
||||
matrix_clear();
|
||||
xprintf("!CS2_E0!\n");
|
||||
return -1;
|
||||
}
|
||||
state = INIT;
|
||||
}
|
||||
break;
|
||||
case F0: // Break code
|
||||
|
|
@ -659,13 +742,14 @@ static int8_t process_cs2(void)
|
|||
state = INIT;
|
||||
break;
|
||||
default:
|
||||
state = INIT;
|
||||
if (code < 0x80) {
|
||||
matrix_break(code);
|
||||
} else {
|
||||
matrix_clear();
|
||||
xprintf("!CS2_F0!\n");
|
||||
return -1;
|
||||
}
|
||||
state = INIT;
|
||||
}
|
||||
break;
|
||||
case E0_F0: // Break code of E0-prefixed
|
||||
|
|
@ -675,13 +759,14 @@ static int8_t process_cs2(void)
|
|||
state = INIT;
|
||||
break;
|
||||
default:
|
||||
state = INIT;
|
||||
if (code < 0x80) {
|
||||
matrix_break(cs2_e0code(code));
|
||||
} else {
|
||||
matrix_clear();
|
||||
xprintf("!CS2_E0_F0!\n");
|
||||
return -1;
|
||||
}
|
||||
state = INIT;
|
||||
}
|
||||
break;
|
||||
// Pause make: E1 14 77
|
||||
|
|
@ -764,9 +849,14 @@ static int8_t process_cs3(void)
|
|||
switch (state) {
|
||||
case READY:
|
||||
switch (code) {
|
||||
case 0x00:
|
||||
case 0xff:
|
||||
xprintf("!CS3_%02X!\n", code);
|
||||
case 0x00: // Error/Overrun [3]p.26
|
||||
xprintf("!CS3_OVR!\n");
|
||||
matrix_clear();
|
||||
clear_keyboard();
|
||||
break;
|
||||
case 0xFF:
|
||||
xprintf("!CS3_ERR!\n");
|
||||
return -1;
|
||||
break;
|
||||
case 0xF0:
|
||||
state = F0;
|
||||
|
|
@ -781,18 +871,24 @@ static int8_t process_cs3(void)
|
|||
if (code < 0x80) {
|
||||
matrix_make(code);
|
||||
} else {
|
||||
xprintf("!CS3_%02X!\n", code);
|
||||
xprintf("!CS3_READY!\n");
|
||||
return -1;
|
||||
}
|
||||
state = READY;
|
||||
}
|
||||
break;
|
||||
case F0: // Break code
|
||||
switch (code) {
|
||||
case 0x00:
|
||||
case 0xff:
|
||||
xprintf("!CS3_F0_%02X!\n", code);
|
||||
xprintf("!CS3_F0_OVR!\n");
|
||||
matrix_clear();
|
||||
clear_keyboard();
|
||||
state = READY;
|
||||
break;
|
||||
case 0xFF:
|
||||
xprintf("!CS3_F0_ERR!\n");
|
||||
state = READY;
|
||||
return -1;
|
||||
break;
|
||||
case 0x83: // F7
|
||||
matrix_break(0x02);
|
||||
state = READY;
|
||||
|
|
@ -802,12 +898,13 @@ static int8_t process_cs3(void)
|
|||
state = READY;
|
||||
break;
|
||||
default:
|
||||
state = READY;
|
||||
if (code < 0x80) {
|
||||
matrix_break(code);
|
||||
} else {
|
||||
xprintf("!CS3_F0_%02X!\n", code);
|
||||
xprintf("!CS3_F0!\n");
|
||||
return -1;
|
||||
}
|
||||
state = READY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "unimap_trans.h"
|
||||
|
||||
|
||||
#define AC_FN0 ACTION_LAYER_MOMENTARY(1)
|
||||
#define AC_FN0 ACTION_LAYER_TAP_KEY(1, KC_APPLICATION)
|
||||
|
||||
#ifdef KEYMAP_SECTION_ENABLE
|
||||
const action_t actionmaps[][UNIMAP_ROWS][UNIMAP_COLS] __attribute__ ((section (".keymap.keymaps"))) = {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#include <stdbool.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include "ringbuf.h"
|
||||
#include <util/atomic.h>
|
||||
#include "ibmpc.h"
|
||||
#include "debug.h"
|
||||
#include "timer.h"
|
||||
|
|
@ -56,27 +56,40 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
} while (0)
|
||||
|
||||
|
||||
#define BUF_SIZE 16
|
||||
static uint8_t buf[BUF_SIZE];
|
||||
static ringbuf_t rb = {
|
||||
.buffer = buf,
|
||||
.head = 0,
|
||||
.tail = 0,
|
||||
.size_mask = BUF_SIZE - 1
|
||||
};
|
||||
|
||||
volatile uint8_t ibmpc_protocol = IBMPC_PROTOCOL_AT;
|
||||
volatile uint8_t ibmpc_protocol = IBMPC_PROTOCOL_NO;
|
||||
volatile uint8_t ibmpc_error = IBMPC_ERR_NONE;
|
||||
|
||||
/* 2-byte buffer for data received from keyhboard
|
||||
* buffer states:
|
||||
* FFFF: empty
|
||||
* FFss: one data
|
||||
* sstt: two data(full)
|
||||
* 0xFF can not be stored as data in buffer because it means empty or no data.
|
||||
*/
|
||||
static volatile uint16_t recv_data = 0xFFFF;
|
||||
/* internal state of receiving data */
|
||||
static volatile uint16_t isr_state = 0x8000;
|
||||
static uint8_t timer_start = 0;
|
||||
|
||||
void ibmpc_host_init(void)
|
||||
{
|
||||
clock_init();
|
||||
data_init();
|
||||
idle();
|
||||
// initialize reset pin to HiZ
|
||||
IBMPC_RST_HIZ();
|
||||
inhibit();
|
||||
IBMPC_INT_INIT();
|
||||
IBMPC_INT_OFF();
|
||||
}
|
||||
|
||||
void ibmpc_host_enable(void)
|
||||
{
|
||||
IBMPC_INT_ON();
|
||||
// POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20)
|
||||
//wait_ms(2500);
|
||||
idle();
|
||||
}
|
||||
|
||||
void ibmpc_host_disable(void)
|
||||
{
|
||||
IBMPC_INT_OFF();
|
||||
inhibit();
|
||||
}
|
||||
|
||||
int16_t ibmpc_host_send(uint8_t data)
|
||||
|
|
@ -84,8 +97,6 @@ int16_t ibmpc_host_send(uint8_t data)
|
|||
bool parity = true;
|
||||
ibmpc_error = IBMPC_ERR_NONE;
|
||||
|
||||
if (ibmpc_protocol == IBMPC_PROTOCOL_XT) return -1;
|
||||
|
||||
dprintf("w%02X ", data);
|
||||
|
||||
IBMPC_INT_OFF();
|
||||
|
|
@ -130,6 +141,10 @@ int16_t ibmpc_host_send(uint8_t data)
|
|||
WAIT(clock_hi, 50, 8);
|
||||
WAIT(data_hi, 50, 9);
|
||||
|
||||
// clear buffer to get response correctly
|
||||
recv_data = 0xFFFF;
|
||||
ibmpc_host_isr_clear();
|
||||
|
||||
idle();
|
||||
IBMPC_INT_ON();
|
||||
return ibmpc_host_recv_response();
|
||||
|
|
@ -140,119 +155,179 @@ ERROR:
|
|||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Receive data from keyboard
|
||||
*/
|
||||
int16_t ibmpc_host_recv(void)
|
||||
{
|
||||
uint16_t data = 0;
|
||||
uint8_t ret = 0xFF;
|
||||
|
||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
||||
data = recv_data;
|
||||
if ((data&0xFF00) != 0xFF00) { // recv_data:sstt -> recv_data:FFtt, ret:ss
|
||||
ret = (data>>8)&0x00FF;
|
||||
recv_data = data | 0xFF00;
|
||||
} else if (data != 0xFFFF) { // recv_data:FFss -> recv_data:FFFF, ret:ss
|
||||
ret = data&0x00FF;
|
||||
recv_data = data | 0x00FF;
|
||||
}
|
||||
}
|
||||
|
||||
if ((data | 0x00FF) != 0xFFFF) dprintf("b%04X ", data);
|
||||
if (ret != 0xFF) dprintf("r%02X ", ret);
|
||||
return ((ret != 0xFF) ? ret : -1);
|
||||
|
||||
}
|
||||
|
||||
int16_t ibmpc_host_recv_response(void)
|
||||
{
|
||||
// Command may take 25ms/20ms at most([5]p.46, [3]p.21)
|
||||
uint8_t retry = 25;
|
||||
while (retry-- && ringbuf_is_empty(&rb)) {
|
||||
int16_t data = -1;
|
||||
while (retry-- && (data = ibmpc_host_recv()) == -1) {
|
||||
wait_ms(1);
|
||||
}
|
||||
int16_t data = ringbuf_get(&rb);
|
||||
if (data != -1) dprintf("r%02X ", data);
|
||||
return data;
|
||||
}
|
||||
|
||||
/* get data received by interrupt */
|
||||
int16_t ibmpc_host_recv(void)
|
||||
void ibmpc_host_isr_clear(void)
|
||||
{
|
||||
int16_t data = ringbuf_get(&rb);
|
||||
if (data != -1) dprintf("r%02X ", data);
|
||||
return data;
|
||||
isr_state = 0x8000;
|
||||
recv_data = 0xFFFF;
|
||||
}
|
||||
|
||||
// NOTE: With this ISR data line can be read within 2us after clock falling edge.
|
||||
// To read data line early as possible:
|
||||
// write naked ISR with asembly code to read the line and call C func to do other job?
|
||||
ISR(IBMPC_INT_VECT)
|
||||
{
|
||||
static uint16_t last_time = 0;
|
||||
static enum {
|
||||
START,
|
||||
BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7,
|
||||
PARITY,
|
||||
STOP,
|
||||
} state = START;
|
||||
static uint8_t data = 0;
|
||||
static uint8_t parity = 1;
|
||||
uint8_t dbit;
|
||||
dbit = IBMPC_DATA_PIN&(1<<IBMPC_DATA_BIT);
|
||||
|
||||
// return unless falling edge
|
||||
if (clock_in()) {
|
||||
return;
|
||||
// Timeout check
|
||||
uint8_t t;
|
||||
// use only the least byte of millisecond timer
|
||||
asm("lds %0, %1" : "=r" (t) : "p" (&timer_count));
|
||||
//t = (uint8_t)timer_count; // compiler uses four registers instead of one
|
||||
if (isr_state == 0x8000) {
|
||||
timer_start = t;
|
||||
} else {
|
||||
// should not take more than 1ms
|
||||
if (timer_start != t && (uint8_t)(timer_start + 1) != t) {
|
||||
ibmpc_error = IBMPC_ERR_TIMEOUT;
|
||||
//goto ERROR;
|
||||
// timeout error recovery by clearing isr_state?
|
||||
timer_start = t;
|
||||
isr_state = 0x8000;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset state when taking more than 1ms
|
||||
if (last_time && timer_elapsed(last_time) > 10) {
|
||||
ibmpc_error = IBMPC_ERR_TIMEOUT | IBMPC_ERR_RECV | state;
|
||||
state = START;
|
||||
data = 0;
|
||||
parity = 1;
|
||||
}
|
||||
last_time = timer_read();
|
||||
isr_state = isr_state>>1;
|
||||
if (dbit) isr_state |= 0x8000;
|
||||
|
||||
switch (state) {
|
||||
case START:
|
||||
if (ibmpc_protocol == IBMPC_PROTOCOL_XT) {
|
||||
// ignore start(0) bit
|
||||
if (!data_in()) return;
|
||||
} else {
|
||||
if (data_in())
|
||||
goto ERROR;
|
||||
}
|
||||
case BIT0:
|
||||
case BIT1:
|
||||
case BIT2:
|
||||
case BIT3:
|
||||
case BIT4:
|
||||
case BIT5:
|
||||
case BIT6:
|
||||
case BIT7:
|
||||
data >>= 1;
|
||||
if (data_in()) {
|
||||
data |= 0x80;
|
||||
parity++;
|
||||
}
|
||||
if (state == BIT7 && ibmpc_protocol == IBMPC_PROTOCOL_XT) {
|
||||
if (!ringbuf_put(&rb, data)) {
|
||||
ibmpc_error = IBMPC_ERR_FULL;
|
||||
goto ERROR;
|
||||
}
|
||||
ibmpc_error = IBMPC_ERR_NONE;
|
||||
goto DONE;
|
||||
}
|
||||
// isr_state: state of receiving data from keyboard
|
||||
//
|
||||
// This should be initialized with 0x8000 before receiving data and
|
||||
// the MSB '*1' works as marker to discrimitate between protocols.
|
||||
// It stores sampled bit at MSB after right shift on each clock falling edge.
|
||||
//
|
||||
// XT protocol has two variants of signaling; XT_IBM and XT_Clone.
|
||||
// XT_IBM uses two start bits 0 and 1 while XT_Clone uses just start bit 1.
|
||||
// https://github.com/tmk/tmk_keyboard/wiki/IBM-PC-XT-Keyboard-Protocol
|
||||
//
|
||||
// 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
||||
// -----------------------------------------------------
|
||||
// *1 0 0 0 0 0 0 0 | 0 0 0 0 0 0 0 0 Initial state(0x8000)
|
||||
//
|
||||
// x x x x x x x x | 0 0 0 0 0 0 0 0 midway(0-7 bits received)
|
||||
// x x x x x x x x | *1 0 0 0 0 0 0 0 midway(8 bits received)
|
||||
// b6 b5 b4 b3 b2 b1 b0 1 | 0 *1 0 0 0 0 0 0 XT_IBM-midway ^1
|
||||
// b7 b6 b5 b4 b3 b2 b1 b0 | 0 *1 0 0 0 0 0 0 AT-midway ^1
|
||||
// b7 b6 b5 b4 b3 b2 b1 b0 | 1 *1 0 0 0 0 0 0 XT_Clone-done
|
||||
// pr b7 b6 b5 b4 b3 b2 b1 | 0 0 *1 0 0 0 0 0 AT-midway[b0=0]
|
||||
// b7 b6 b5 b4 b3 b2 b1 b0 | 1 0 *1 0 0 0 0 0 XT_IBM-done ^2
|
||||
// pr b7 b6 b5 b4 b3 b2 b1 | 1 0 *1 0 0 0 0 0 AT-midway[b0=1] ^2
|
||||
// x x x x x x x x | x 1 1 0 0 0 0 0 illegal
|
||||
// st pr b7 b6 b5 b4 b3 b2 | b1 b0 0 *1 0 0 0 0 AT-done
|
||||
// x x x x x x x x | x x 1 *1 0 0 0 0 illegal
|
||||
// all other states than above illegal
|
||||
//
|
||||
// ^1: AT and XT_IBM takes same state.
|
||||
// ^2: AT and XT_IBM takes same state in case that AT b0 is 1,
|
||||
// we have to check AT stop bit to discriminate between the two protocol.
|
||||
switch (isr_state & 0xFF) {
|
||||
case 0b00000000:
|
||||
case 0b10000000:
|
||||
case 0b01000000: // ^1
|
||||
case 0b00100000:
|
||||
// midway
|
||||
goto NEXT;
|
||||
break;
|
||||
case PARITY:
|
||||
if (data_in()) {
|
||||
if (!(parity & 0x01))
|
||||
goto ERROR;
|
||||
} else {
|
||||
if (parity & 0x01)
|
||||
goto ERROR;
|
||||
}
|
||||
break;
|
||||
case STOP:
|
||||
if (!data_in())
|
||||
goto ERROR;
|
||||
if (!ringbuf_put(&rb, data)) {
|
||||
ibmpc_error = IBMPC_ERR_FULL;
|
||||
goto ERROR;
|
||||
}
|
||||
ibmpc_error = IBMPC_ERR_NONE;
|
||||
case 0b11000000:
|
||||
// XT_Clone-done
|
||||
isr_state = isr_state>>8;
|
||||
ibmpc_protocol = IBMPC_PROTOCOL_XT_CLONE;
|
||||
goto DONE;
|
||||
break;
|
||||
default:
|
||||
case 0b10100000: // ^2
|
||||
{
|
||||
uint8_t us = 100;
|
||||
// wait for rising and falling edge of AT stop bit to discriminate between XT and AT
|
||||
while (!(IBMPC_CLOCK_PIN&(1<<IBMPC_CLOCK_BIT)) && us) { wait_us(1); us--; }
|
||||
while ( IBMPC_CLOCK_PIN&(1<<IBMPC_CLOCK_BIT) && us) { wait_us(1); us--; }
|
||||
|
||||
if (us) {
|
||||
// found stop bit: AT-midway - process the stop bit in next ISR
|
||||
goto NEXT;
|
||||
} else {
|
||||
// no stop bit: XT_IBM-done
|
||||
isr_state = isr_state>>8;
|
||||
ibmpc_protocol = IBMPC_PROTOCOL_XT_IBM;
|
||||
goto DONE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0b00010000:
|
||||
case 0b10010000:
|
||||
case 0b01010000:
|
||||
case 0b11010000:
|
||||
// AT-done
|
||||
// TODO: parity check?
|
||||
isr_state = isr_state>>6;
|
||||
ibmpc_protocol = IBMPC_PROTOCOL_AT;
|
||||
goto DONE;
|
||||
break;
|
||||
case 0b01100000:
|
||||
case 0b11100000:
|
||||
case 0b00110000:
|
||||
case 0b10110000:
|
||||
case 0b01110000:
|
||||
case 0b11110000:
|
||||
default: // xxxx_oooo(any 1 in low nibble)
|
||||
// Illegal
|
||||
ibmpc_error = IBMPC_ERR_ILLEGAL;
|
||||
goto ERROR;
|
||||
break;
|
||||
}
|
||||
goto NEXT;
|
||||
|
||||
ERROR:
|
||||
ibmpc_error |= state;
|
||||
ibmpc_error |= IBMPC_ERR_RECV;
|
||||
ringbuf_reset(&rb);
|
||||
DONE:
|
||||
last_time = 0;
|
||||
state = START;
|
||||
data = 0;
|
||||
parity = 1;
|
||||
isr_state = 0x8000;
|
||||
recv_data = 0xFF00; // clear data and scancode of error 0x00
|
||||
return;
|
||||
DONE:
|
||||
if ((isr_state & 0x00FF) == 0x00FF) {
|
||||
// receive error code 0xFF
|
||||
ibmpc_error = IBMPC_ERR_FF;
|
||||
}
|
||||
if ((recv_data & 0xFF00) != 0xFF00) {
|
||||
// buffer full and overwritten
|
||||
ibmpc_error = IBMPC_ERR_FULL;
|
||||
}
|
||||
recv_data = recv_data<<8;
|
||||
recv_data |= isr_state & 0xFF;
|
||||
isr_state = 0x8000; // clear to next data
|
||||
NEXT:
|
||||
state++;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,15 +70,19 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
#define IBMPC_RESEND 0xFE
|
||||
#define IBMPC_SET_LED 0xED
|
||||
|
||||
#define IBMPC_PROTOCOL_AT 0
|
||||
#define IBMPC_PROTOCOL_XT 1
|
||||
#define IBMPC_PROTOCOL_NO 0
|
||||
#define IBMPC_PROTOCOL_AT 1
|
||||
#define IBMPC_PROTOCOL_XT_IBM 2
|
||||
#define IBMPC_PROTOCOL_XT_CLONE 3
|
||||
|
||||
// TODO: error numbers
|
||||
// Error numbers
|
||||
#define IBMPC_ERR_NONE 0
|
||||
#define IBMPC_ERR_RECV 0x00
|
||||
#define IBMPC_ERR_SEND 0x10
|
||||
#define IBMPC_ERR_TIMEOUT 0x20
|
||||
#define IBMPC_ERR_FULL 0x40
|
||||
#define IBMPC_ERR_ILLEGAL 0x80
|
||||
#define IBMPC_ERR_FF 0xFF
|
||||
|
||||
#define IBMPC_LED_SCROLL_LOCK 0
|
||||
#define IBMPC_LED_NUM_LOCK 1
|
||||
|
|
@ -89,9 +93,12 @@ extern volatile uint8_t ibmpc_protocol;
|
|||
extern volatile uint8_t ibmpc_error;
|
||||
|
||||
void ibmpc_host_init(void);
|
||||
void ibmpc_host_enable(void);
|
||||
void ibmpc_host_disable(void);
|
||||
int16_t ibmpc_host_send(uint8_t data);
|
||||
int16_t ibmpc_host_recv_response(void);
|
||||
int16_t ibmpc_host_recv(void);
|
||||
void ibmpc_host_isr_clear(void);
|
||||
void ibmpc_host_set_led(uint8_t usb_led);
|
||||
|
||||
|
||||
|
|
@ -102,12 +109,6 @@ void ibmpc_host_set_led(uint8_t usb_led);
|
|||
/*
|
||||
* Clock
|
||||
*/
|
||||
static inline void clock_init(void)
|
||||
{
|
||||
IBMPC_CLOCK_PORT &= ~(1<<IBMPC_CLOCK_BIT);
|
||||
IBMPC_CLOCK_DDR |= (1<<IBMPC_CLOCK_BIT);
|
||||
}
|
||||
|
||||
static inline void clock_lo(void)
|
||||
{
|
||||
IBMPC_CLOCK_PORT &= ~(1<<IBMPC_CLOCK_BIT);
|
||||
|
|
@ -132,12 +133,6 @@ static inline bool clock_in(void)
|
|||
/*
|
||||
* Data
|
||||
*/
|
||||
static inline void data_init(void)
|
||||
{
|
||||
IBMPC_DATA_DDR &= ~(1<<IBMPC_DATA_BIT);
|
||||
IBMPC_DATA_PORT |= (1<<IBMPC_DATA_BIT);
|
||||
}
|
||||
|
||||
static inline void data_lo(void)
|
||||
{
|
||||
IBMPC_DATA_PORT &= ~(1<<IBMPC_DATA_BIT);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue