tags/v2.9caca2c0
Add mouse support to ADB5b0835a
Merge commit '1fe4406f374291ab2e86e95a97341fd9c475fcb8'f2f7556
Remove unneeded tap delays #2018124195
Fix URL of HID Usage Tables pdf0bb4679
Fix typo of doc/keycode.txtb24fa1a
Fix handling of consumer usage #1996ae1a3d
Improve anti-ghosting behavior for fast typinga4c9763
Fix dfu-programmer parametersb62b3f2
Move ring_buffer.h file6ec424f
Fix debug print502fb0c
Fix ibm4704 protocol with using interrupt git-subtree-dir: tmk_core git-subtree-split:caca2c0155
case ACT_RMODS_TAP: | case ACT_RMODS_TAP: | ||||
case ACT_LAYER_TAP: | case ACT_LAYER_TAP: | ||||
case ACT_LAYER_TAP_EXT: | case ACT_LAYER_TAP_EXT: | ||||
return true; | |||||
switch (action.layer_tap.code) { | |||||
case 0x00 ... 0xdf: | |||||
case OP_TAP_TOGGLE: | |||||
return true; | |||||
} | |||||
return false; | |||||
case ACT_MACRO: | case ACT_MACRO: | ||||
case ACT_FUNCTION: | case ACT_FUNCTION: | ||||
if (action.func.opt & FUNC_TAP) { return true; } | if (action.func.opt & FUNC_TAP) { return true; } |
* 1001|oopp|BBBB BBBB 8-bit Bitwise Operation??? | * 1001|oopp|BBBB BBBB 8-bit Bitwise Operation??? | ||||
* | * | ||||
* ACT_LAYER_TAP(101x): | * ACT_LAYER_TAP(101x): | ||||
* 101E|LLLL| keycode On/Off with tap key | |||||
* 101E|LLLL|1110 mods On/Off with modifiers(0xE0-EF) | |||||
* 101E|LLLL|1111 0000 Invert with tap toggle(0xF0) | |||||
* 101E|LLLL|1111 0001 On/Off | |||||
* 101E|LLLL|1111 0010 Off/On | |||||
* 101E|LLLL|1111 0011 Set/Clear | |||||
* 101E|LLLL|1111 xxxx Reserved(0xF4-FF) | |||||
* 101E|LLLL| keycode On/Off with tap key (0x00-DF)[TAP] | |||||
* 101E|LLLL|1110 mods On/Off with modifiers (0xE0-EF)[NOT TAP] | |||||
* 101E|LLLL|1111 0000 Invert with tap toggle (0xF0) [TAP] | |||||
* 101E|LLLL|1111 0001 On/Off (0xF1) [NOT TAP] | |||||
* 101E|LLLL|1111 0010 Off/On (0xF2) [NOT TAP] | |||||
* 101E|LLLL|1111 0011 Set/Clear (0xF3) [NOT TAP] | |||||
* 101E|LLLL|1111 xxxx Reserved (0xF4-FF) | |||||
* ELLLL: layer 0-31(E: extra bit for layer 16-31) | * ELLLL: layer 0-31(E: extra bit for layer 16-31) | ||||
* | * | ||||
* | * |
#ifdef SERIAL_MOUSE_ENABLE | #ifdef SERIAL_MOUSE_ENABLE | ||||
#include "serial_mouse.h" | #include "serial_mouse.h" | ||||
#endif | #endif | ||||
#ifdef ADB_MOUSE_ENABLE | |||||
#include "adb.h" | |||||
#endif | |||||
#ifdef MATRIX_HAS_GHOST | #ifdef MATRIX_HAS_GHOST | ||||
#ifdef SERIAL_MOUSE_ENABLE | #ifdef SERIAL_MOUSE_ENABLE | ||||
serial_mouse_init(); | serial_mouse_init(); | ||||
#endif | #endif | ||||
#ifdef ADB_MOUSE_ENABLE | |||||
adb_mouse_init(); | |||||
#endif | |||||
#ifdef BOOTMAGIC_ENABLE | #ifdef BOOTMAGIC_ENABLE | ||||
void keyboard_task(void) | void keyboard_task(void) | ||||
{ | { | ||||
static matrix_row_t matrix_prev[MATRIX_ROWS]; | static matrix_row_t matrix_prev[MATRIX_ROWS]; | ||||
#ifdef MATRIX_HAS_GHOST | |||||
static matrix_row_t matrix_ghost[MATRIX_ROWS]; | |||||
#endif | |||||
static uint8_t led_status = 0; | static uint8_t led_status = 0; | ||||
matrix_row_t matrix_row = 0; | matrix_row_t matrix_row = 0; | ||||
matrix_row_t matrix_change = 0; | matrix_row_t matrix_change = 0; | ||||
matrix_row = matrix_get_row(r); | matrix_row = matrix_get_row(r); | ||||
matrix_change = matrix_row ^ matrix_prev[r]; | matrix_change = matrix_row ^ matrix_prev[r]; | ||||
if (matrix_change) { | if (matrix_change) { | ||||
if (debug_matrix) matrix_print(); | |||||
#ifdef MATRIX_HAS_GHOST | #ifdef MATRIX_HAS_GHOST | ||||
if (has_ghost_in_row(r)) { | if (has_ghost_in_row(r)) { | ||||
matrix_prev[r] = matrix_row; | |||||
/* Keep track of whether ghosted status has changed for | |||||
* debugging. But don't update matrix_prev until un-ghosted, or | |||||
* the last key would be lost. | |||||
*/ | |||||
if (debug_matrix && matrix_ghost[r] != matrix_row) { | |||||
matrix_print(); | |||||
} | |||||
matrix_ghost[r] = matrix_row; | |||||
continue; | continue; | ||||
} | } | ||||
matrix_ghost[r] = matrix_row; | |||||
#endif | #endif | ||||
if (debug_matrix) matrix_print(); | |||||
for (uint8_t c = 0; c < MATRIX_COLS; c++) { | for (uint8_t c = 0; c < MATRIX_COLS; c++) { | ||||
if (matrix_change & ((matrix_row_t)1<<c)) { | if (matrix_change & ((matrix_row_t)1<<c)) { | ||||
action_exec((keyevent_t){ | action_exec((keyevent_t){ | ||||
serial_mouse_task(); | serial_mouse_task(); | ||||
#endif | #endif | ||||
#ifdef ADB_MOUSE_ENABLE | |||||
adb_mouse_task(); | |||||
#endif | |||||
// update LED | // update LED | ||||
if (led_status != host_keyboard_leds()) { | if (led_status != host_keyboard_leds()) { | ||||
led_status = host_keyboard_leds(); | led_status = host_keyboard_leds(); |
case KC_SYSTEM_POWER ... KC_SYSTEM_WAKE: | case KC_SYSTEM_POWER ... KC_SYSTEM_WAKE: | ||||
action.code = ACTION_USAGE_SYSTEM(KEYCODE2SYSTEM(keycode)); | action.code = ACTION_USAGE_SYSTEM(KEYCODE2SYSTEM(keycode)); | ||||
break; | break; | ||||
case KC_AUDIO_MUTE ... KC_WWW_FAVORITES: | |||||
case KC_AUDIO_MUTE ... KC_MEDIA_REWIND: | |||||
action.code = ACTION_USAGE_CONSUMER(KEYCODE2CONSUMER(keycode)); | action.code = ACTION_USAGE_CONSUMER(KEYCODE2CONSUMER(keycode)); | ||||
break; | break; | ||||
case KC_MS_UP ... KC_MS_ACCEL2: | case KC_MS_UP ... KC_MS_ACCEL2: |
KC_CRSEL A3 Keyboard CrSel/Props | KC_CRSEL A3 Keyboard CrSel/Props | ||||
KC_EXSEL A4 Keyboard ExSel | KC_EXSEL A4 Keyboard ExSel | ||||
/* Modifiers */ | /* Modifiers */ | ||||
KC_LCTRL KC_LCTRL E0 Keyboard LeftControl | |||||
KC_LCTRL KC_LCTL E0 Keyboard LeftControl | |||||
KC_LSHIFT KC_LSFT E1 Keyboard LeftShift | KC_LSHIFT KC_LSFT E1 Keyboard LeftShift | ||||
KC_LALT E2 Keyboard LeftAlt | KC_LALT E2 Keyboard LeftAlt | ||||
KC_LGUI E3 Keyboard Left GUI(Windows/Apple/Meta key) | KC_LGUI E3 Keyboard Left GUI(Windows/Apple/Meta key) |
See keycode table in [`doc/keycode.txt`](./keycode.txt) for description of keycodes. | See keycode table in [`doc/keycode.txt`](./keycode.txt) for description of keycodes. | ||||
In regard to implementation side most of keycodes are identical with [HID usage][HID_usage](pdf) sent to host for real and some virtual keycodes are defined to support special actions. | In regard to implementation side most of keycodes are identical with [HID usage][HID_usage](pdf) sent to host for real and some virtual keycodes are defined to support special actions. | ||||
[HID_usage]: http://www.usb.org/developers/devclass_docs/Hut1_11.pdf | |||||
[HID_usage]: http://www.usb.org/developers/hidpage/Hut1_12v2.pdf | |||||
SRC += $(PROTOCOL_DIR)/serial_uart.c | SRC += $(PROTOCOL_DIR)/serial_uart.c | ||||
endif | endif | ||||
ifdef ADB_MOUSE_ENABLE | |||||
OPT_DEFS += -DADB_MOUSE_ENABLE -DMOUSE_ENABLE | |||||
endif | |||||
# Search Path | # Search Path | ||||
VPATH += $(TMK_DIR)/protocol | VPATH += $(TMK_DIR)/protocol |
static inline void send_byte(uint8_t data); | static inline void send_byte(uint8_t data); | ||||
static inline uint16_t wait_data_lo(uint16_t us); | static inline uint16_t wait_data_lo(uint16_t us); | ||||
static inline uint16_t wait_data_hi(uint16_t us); | static inline uint16_t wait_data_hi(uint16_t us); | ||||
static inline uint16_t adb_host_dev_recv(uint8_t device); | |||||
void adb_host_init(void) | void adb_host_init(void) | ||||
// | // | ||||
// [from Apple IIgs Hardware Reference Second Edition] | // [from Apple IIgs Hardware Reference Second Edition] | ||||
enum { | |||||
ADDR_KEYB = 0x20, | |||||
ADDR_MOUSE = 0x30 | |||||
}; | |||||
uint16_t adb_host_kbd_recv(void) | uint16_t adb_host_kbd_recv(void) | ||||
{ | |||||
return adb_host_dev_recv(ADDR_KEYB); | |||||
} | |||||
#ifdef ADB_MOUSE_ENABLE | |||||
void adb_mouse_init(void) { | |||||
return; | |||||
} | |||||
uint16_t adb_host_mouse_recv(void) | |||||
{ | |||||
return adb_host_dev_recv(ADDR_MOUSE); | |||||
} | |||||
#endif | |||||
static inline uint16_t adb_host_dev_recv(uint8_t device) | |||||
{ | { | ||||
uint16_t data = 0; | uint16_t data = 0; | ||||
cli(); | cli(); | ||||
attention(); | attention(); | ||||
send_byte(0x2C); // Addr:Keyboard(0010), Cmd:Talk(11), Register0(00) | |||||
send_byte(device|0x0C); // Addr:Keyboard(0010)/Mouse(0011), Cmd:Talk(11), Register0(00) | |||||
place_bit0(); // Stopbit(0) | place_bit0(); // Stopbit(0) | ||||
if (!wait_data_hi(500)) { // Service Request(310us Adjustable Keyboard): just ignored | if (!wait_data_hi(500)) { // Service Request(310us Adjustable Keyboard): just ignored | ||||
sei(); | sei(); |
void adb_host_init(void); | void adb_host_init(void); | ||||
bool adb_host_psw(void); | bool adb_host_psw(void); | ||||
uint16_t adb_host_kbd_recv(void); | uint16_t adb_host_kbd_recv(void); | ||||
uint16_t adb_host_mouse_recv(void); | |||||
void adb_host_listen(uint8_t cmd, uint8_t data_h, uint8_t data_l); | void adb_host_listen(uint8_t cmd, uint8_t data_h, uint8_t data_l); | ||||
void adb_host_kbd_led(uint8_t led); | void adb_host_kbd_led(uint8_t led); | ||||
void adb_mouse_task(void); | |||||
void adb_mouse_init(void); | |||||
#endif | #endif |
#include <stdbool.h> | #include <stdbool.h> | ||||
#include <util/delay.h> | #include <util/delay.h> | ||||
#include "debug.h" | #include "debug.h" | ||||
#include "ring_buffer.h" | |||||
#include "ibm4704.h" | #include "ibm4704.h" | ||||
void ibm4704_init(void) | void ibm4704_init(void) | ||||
{ | { | ||||
inhibit(); | |||||
IBM4704_INT_INIT(); | |||||
IBM4704_INT_ON(); | |||||
idle(); | |||||
} | } | ||||
/* | /* | ||||
bool parity = true; // odd parity | bool parity = true; // odd parity | ||||
ibm4704_error = 0; | ibm4704_error = 0; | ||||
IBM4704_INT_OFF(); | |||||
/* Request to send */ | /* Request to send */ | ||||
idle(); | idle(); | ||||
clock_lo(); | clock_lo(); | ||||
/* Data bit */ | /* Data bit */ | ||||
for (uint8_t i = 0; i < 8; i++) { | for (uint8_t i = 0; i < 8; i++) { | ||||
WAIT(clock_hi, 100, 0x40+i); | WAIT(clock_hi, 100, 0x40+i); | ||||
//_delay_us(5); | |||||
if (data&(1<<i)) { | if (data&(1<<i)) { | ||||
parity = !parity; | parity = !parity; | ||||
data_hi(); | data_hi(); | ||||
/* End */ | /* End */ | ||||
WAIT(data_lo, 100, 0x36); | WAIT(data_lo, 100, 0x36); | ||||
inhibit(); | |||||
_delay_us(200); // wait to recover clock to hi | |||||
idle(); | |||||
IBM4704_INT_ON(); | |||||
return 0; | return 0; | ||||
ERROR: | ERROR: | ||||
inhibit(); | |||||
if (ibm4704_error >= 0x30) { | |||||
xprintf("x%02X ", ibm4704_error); | |||||
idle(); | |||||
if (ibm4704_error > 0x30) { | |||||
xprintf("S:%02X ", ibm4704_error); | |||||
} | } | ||||
_delay_us(200); // wait to recover clock to hi | |||||
IBM4704_INT_ON(); | |||||
return -1; | return -1; | ||||
} | } | ||||
/* receive data when host want else inhibit communication */ | |||||
/* wait forever to receive data */ | |||||
uint8_t ibm4704_recv_response(void) | 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; | |||||
while (!rbuf_has_data()) { | |||||
_delay_ms(1); | |||||
} | |||||
return rbuf_dequeue(); | |||||
} | } | ||||
/* | /* | ||||
*/ | */ | ||||
uint8_t ibm4704_recv(void) | 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, 0x11); // wait for keyboard to send | |||||
WAIT(data_hi, 100, 0x12); // can be delayed that long | |||||
WAIT(clock_hi, 100, 0x13); // 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); | |||||
if (rbuf_has_data()) { | |||||
return rbuf_dequeue(); | |||||
} else { | |||||
return -1; | |||||
} | } | ||||
} | |||||
/* parity */ | |||||
WAIT(clock_hi, 100, 0x17); | |||||
if (data_in() != parity) { | |||||
ibm4704_error = IBM4704_ERR_PARITY; | |||||
goto ERROR; | |||||
} | |||||
WAIT(clock_lo, 150, 0x18); | |||||
/* stop bit */ | |||||
WAIT(clock_hi, 100, 0x19); | |||||
WAIT(data_lo, 1, 0x19); | |||||
ISR(IBM4704_INT_VECT) | |||||
{ | |||||
static enum { | |||||
INIT, START, BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7, PARITY, | |||||
} state = INIT; | |||||
// LSB first | |||||
static uint8_t data = 0; | |||||
// Odd parity | |||||
static uint8_t parity = false; | |||||
inhibit(); | |||||
_delay_us(200); // wait to recover clock to hi | |||||
return data; | |||||
ERROR: | |||||
if (ibm4704_error > 0x12) { | |||||
xprintf("x%02X ", ibm4704_error); | |||||
ibm4704_error = 0; | |||||
// return unless falling edge | |||||
if (clock_in()) { goto RETURN; } // why this occurs? | |||||
state++; | |||||
switch (state) { | |||||
case START: | |||||
// Data:Low | |||||
WAIT(data_hi, 10, state); | |||||
break; | |||||
case BIT0: | |||||
case BIT1: | |||||
case BIT2: | |||||
case BIT3: | |||||
case BIT4: | |||||
case BIT5: | |||||
case BIT6: | |||||
case BIT7: | |||||
data >>= 1; | |||||
if (data_in()) { | |||||
data |= 0x80; | |||||
parity = !parity; | |||||
} | |||||
break; | |||||
case PARITY: | |||||
if (data_in()) { | |||||
parity = !parity; | |||||
} | |||||
if (!parity) | |||||
goto ERROR; | |||||
rbuf_enqueue(data); | |||||
ibm4704_error = IBM4704_ERR_NONE; | |||||
goto DONE; | |||||
break; | |||||
default: | |||||
goto ERROR; | |||||
} | } | ||||
inhibit(); | |||||
_delay_us(200); // wait to recover clock to hi | |||||
return -1; | |||||
goto RETURN; | |||||
ERROR: | |||||
ibm4704_error = state; | |||||
while (ibm4704_send(0xFE)) _delay_ms(1); // resend | |||||
xprintf("R:%02X%02X\n", state, data); | |||||
DONE: | |||||
state = INIT; | |||||
data = 0; | |||||
parity = false; | |||||
RETURN: | |||||
return; | |||||
} | } |
SRC += $(PJRC_DIR)/usb_mouse.c | SRC += $(PJRC_DIR)/usb_mouse.c | ||||
endif | endif | ||||
ifdef ADB_MOUSE_ENABLE | |||||
SRC += $(PJRC_DIR)/usb_mouse.c | |||||
endif | |||||
ifdef PS2_MOUSE_ENABLE | ifdef PS2_MOUSE_ENABLE | ||||
SRC += $(PJRC_DIR)/usb_mouse.c | SRC += $(PJRC_DIR)/usb_mouse.c | ||||
endif | endif |
#ifndef RING_BUFFER_H | |||||
#define RING_BUFFER_H | |||||
/*-------------------------------------------------------------------- | |||||
* Ring buffer to store scan codes from keyboard | |||||
*------------------------------------------------------------------*/ | |||||
#define RBUF_SIZE 32 | |||||
static uint8_t rbuf[RBUF_SIZE]; | |||||
static uint8_t rbuf_head = 0; | |||||
static uint8_t rbuf_tail = 0; | |||||
static inline void rbuf_enqueue(uint8_t data) | |||||
{ | |||||
uint8_t sreg = SREG; | |||||
cli(); | |||||
uint8_t next = (rbuf_head + 1) % RBUF_SIZE; | |||||
if (next != rbuf_tail) { | |||||
rbuf[rbuf_head] = data; | |||||
rbuf_head = next; | |||||
} else { | |||||
print("rbuf: full\n"); | |||||
} | |||||
SREG = sreg; | |||||
} | |||||
static inline uint8_t rbuf_dequeue(void) | |||||
{ | |||||
uint8_t val = 0; | |||||
uint8_t sreg = SREG; | |||||
cli(); | |||||
if (rbuf_head != rbuf_tail) { | |||||
val = rbuf[rbuf_tail]; | |||||
rbuf_tail = (rbuf_tail + 1) % RBUF_SIZE; | |||||
} | |||||
SREG = sreg; | |||||
return val; | |||||
} | |||||
static inline bool rbuf_has_data(void) | |||||
{ | |||||
uint8_t sreg = SREG; | |||||
cli(); | |||||
bool has_data = (rbuf_head != rbuf_tail); | |||||
SREG = sreg; | |||||
return has_data; | |||||
} | |||||
static inline void rbuf_clear(void) | |||||
{ | |||||
uint8_t sreg = SREG; | |||||
cli(); | |||||
rbuf_head = rbuf_tail = 0; | |||||
SREG = sreg; | |||||
} | |||||
#endif /* RING_BUFFER_H */ |
$(REMOVE) $(TARGET)eep.hex | $(REMOVE) $(TARGET)eep.hex | ||||
dfu-ee: $(TARGET).hex $(TARGET).eep | dfu-ee: $(TARGET).hex $(TARGET).eep | ||||
dfu-programmer $(MCU) eeprom-flash $(TARGET).eep | |||||
ifneq (, $(findstring 0.7, $(shell dfu-programmer --version 2>&1))) | |||||
dfu-programmer $(MCU) flash --eeprom $(TARGET).eep | |||||
else | |||||
dfu-programmer $(MCU) flash-eeprom $(TARGET).eep | |||||
endif | |||||
dfu-programmer $(MCU) reset | dfu-programmer $(MCU) reset | ||||