Conflicts: common.mk common/keyboard.c keyboard/gh60/Makefile keyboard/gh60/keymap_hasu.c keyboard/gh60/matrix.ccore
@@ -25,6 +25,7 @@ endif | |||
ifdef MOUSEKEY_ENABLE | |||
SRC += $(COMMON_DIR)/mousekey.c | |||
OPT_DEFS += -DMOUSEKEY_ENABLE | |||
OPT_DEFS += -DMOUSE_ENABLE | |||
endif | |||
ifdef EXTRAKEY_ENABLE | |||
@@ -47,20 +48,6 @@ ifdef NKRO_ENABLE | |||
OPT_DEFS += -DNKRO_ENABLE | |||
endif | |||
ifdef PS2_MOUSE_ENABLE | |||
SRC += $(COMMON_DIR)/ps2.c \ | |||
$(COMMON_DIR)/ps2_mouse.c | |||
OPT_DEFS += -DPS2_MOUSE_ENABLE | |||
endif | |||
ifdef MOUSEKEY_ENABLE | |||
OPT_DEFS += -DMOUSE_ENABLE | |||
endif | |||
ifdef PS4_MOUSE_ENABLE | |||
OPT_DEFS += -DMOUSE_ENABLE | |||
endif | |||
ifdef SLEEP_LED_ENABLE | |||
SRC += $(COMMON_DIR)/sleep_led.c | |||
OPT_DEFS += -DSLEEP_LED_ENABLE |
@@ -128,6 +128,17 @@ void process_action(keyrecord_t *record) | |||
} | |||
break; | |||
#endif | |||
case MODS_TAP_TOGGLE: | |||
if (event.pressed) { | |||
if (tap_count <= TAPPING_TOGGLE) { | |||
register_mods(mods); | |||
} | |||
} else { | |||
if (tap_count < TAPPING_TOGGLE) { | |||
unregister_mods(mods); | |||
} | |||
} | |||
break; | |||
default: | |||
if (event.pressed) { | |||
if (tap_count > 0) { | |||
@@ -485,12 +496,6 @@ void clear_keyboard_but_mods(void) | |||
#endif | |||
} | |||
bool sending_anykey(void) | |||
{ | |||
return (has_anykey() || host_mouse_in_use() || | |||
host_last_sysytem_report() || host_last_consumer_report()); | |||
} | |||
bool is_tap_key(key_t key) | |||
{ | |||
action_t action = layer_switch_get_action(key); |
@@ -64,7 +64,6 @@ void unregister_mods(uint8_t mods); | |||
//void set_mods(uint8_t mods); | |||
void clear_keyboard(void); | |||
void clear_keyboard_but_mods(void); | |||
bool sending_anykey(void); | |||
void layer_switch(uint8_t new_layer); | |||
bool is_tap_key(key_t key); | |||
@@ -34,6 +34,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
* | |||
* ACT_MODS_TAP(001r): | |||
* 001r|mods|0000 0000 Modifiers with OneShot | |||
* 001r|mods|0000 0001 Modifiers with tap toggle | |||
* 001r|mods|0000 00xx (reserved) | |||
* 001r|mods| keycode Modifiers with Tap Key(Dual role) | |||
* | |||
@@ -205,12 +206,14 @@ enum mods_bit { | |||
}; | |||
enum mods_codes { | |||
MODS_ONESHOT = 0x00, | |||
MODS_TAP_TOGGLE = 0x01, | |||
}; | |||
#define ACTION_KEY(key) ACTION(ACT_MODS, (key)) | |||
#define ACTION_MODS(mods) ACTION(ACT_MODS, (mods&0x1f)<<8 | 0) | |||
#define ACTION_MODS_KEY(mods, key) ACTION(ACT_MODS, (mods&0x1f)<<8 | (key)) | |||
#define ACTION_MODS_TAP_KEY(mods, key) ACTION(ACT_MODS_TAP, (mods&0x1f)<<8 | (key)) | |||
#define ACTION_MODS_ONESHOT(mods) ACTION(ACT_MODS_TAP, (mods&0x1f)<<8 | MODS_ONESHOT) | |||
#define ACTION_MODS(mods) ACTION(ACT_MODS, ((mods)&0x1f)<<8 | 0) | |||
#define ACTION_MODS_KEY(mods, key) ACTION(ACT_MODS, ((mods)&0x1f)<<8 | (key)) | |||
#define ACTION_MODS_TAP_KEY(mods, key) ACTION(ACT_MODS_TAP, ((mods)&0x1f)<<8 | (key)) | |||
#define ACTION_MODS_ONESHOT(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f)<<8 | MODS_ONESHOT) | |||
#define ACTION_MODS_TAP_TOGGLE(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f)<<8 | MODS_TAP_TOGGLE) | |||
/* |
@@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
#define ACTION_UTIL_H | |||
#include <stdint.h> | |||
#include "report.h" | |||
extern report_keyboard_t *keyboard_report; | |||
@@ -27,9 +27,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
bool keyboard_nkro = false; | |||
#endif | |||
report_mouse_t mouse_report = {}; | |||
static host_driver_t *driver; | |||
static uint16_t last_system_report = 0; | |||
static uint16_t last_consumer_report = 0; | |||
@@ -89,11 +86,6 @@ void host_consumer_send(uint16_t report) | |||
(*driver->send_consumer)(report); | |||
} | |||
uint8_t host_mouse_in_use(void) | |||
{ | |||
return (mouse_report.buttons | mouse_report.x | mouse_report.y | mouse_report.v | mouse_report.h); | |||
} | |||
uint16_t host_last_sysytem_report(void) | |||
{ | |||
return last_system_report; |
@@ -32,9 +32,6 @@ extern "C" { | |||
extern bool keyboard_nkro; | |||
#endif | |||
/* report */ | |||
extern report_mouse_t mouse_report; | |||
/* host driver */ | |||
void host_set_driver(host_driver_t *driver); | |||
@@ -47,9 +44,6 @@ void host_mouse_send(report_mouse_t *report); | |||
void host_system_send(uint16_t data); | |||
void host_consumer_send(uint16_t data); | |||
/* mouse report utils */ | |||
uint8_t host_mouse_in_use(void); | |||
uint16_t host_last_sysytem_report(void); | |||
uint16_t host_last_consumer_report(void); | |||
@@ -30,9 +30,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
#include "sendchar.h" | |||
#include "bootmagic.h" | |||
#include "eeconfig.h" | |||
#include "mousekey.h" | |||
#include "backlight.h" | |||
#include "keymap_ex.h" | |||
#ifdef MOUSEKEY_ENABLE | |||
# include "mousekey.h" | |||
#endif | |||
#ifdef PS2_MOUSE_ENABLE | |||
# include "ps2_mouse.h" | |||
#endif | |||
#ifdef MATRIX_HAS_GHOST | |||
@@ -116,10 +121,16 @@ void keyboard_task(void) | |||
action_exec(TICK); | |||
MATRIX_LOOP_END: | |||
#ifdef MOUSEKEY_ENABLE | |||
// mousekey repeat & acceleration | |||
mousekey_task(); | |||
#endif | |||
#ifdef PS2_MOUSE_ENABLE | |||
ps2_mouse_task(); | |||
#endif | |||
// update LED | |||
if (led_status != host_keyboard_leds()) { | |||
led_status = host_keyboard_leds(); |
@@ -26,6 +26,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
static report_mouse_t mouse_report = {}; | |||
static uint8_t mousekey_repeat = 0; | |||
static uint8_t mousekey_accel = 0; | |||
@@ -526,6 +526,12 @@ Say you want to type 'The', you have to push and hold Shift key before type 't' | |||
Oneshot effect is cancel unless following key is pressed down within `ONESHOT_TIMEOUT` of `config.h`. No timeout when it is `0` or not defined. | |||
### 4.4 Tap Toggle Mods | |||
Similar to layer tap toggle, this works as a momentary modifier when holding, but toggles on with several taps. A single tap will 'unstick' the modifier again. | |||
ACTION_MODS_TAP_TOGGLE(MOD_LSFT) | |||
## 5. Legacy Keymap |
@@ -1,2 +1,27 @@ | |||
PROTOCOL_DIR = protocol | |||
ifdef PS2_MOUSE_ENABLE | |||
SRC += $(PROTOCOL_DIR)/ps2_mouse.c | |||
OPT_DEFS += -DPS2_MOUSE_ENABLE | |||
OPT_DEFS += -DMOUSE_ENABLE | |||
endif | |||
ifdef PS2_USE_BUSYWAIT | |||
SRC += protocol/ps2_busywait.c | |||
OPT_DEFS += -DPS2_USE_BUSYWAIT | |||
endif | |||
ifdef PS2_USE_INT | |||
SRC += protocol/ps2_interrupt.c | |||
OPT_DEFS += -DPS2_USE_INT | |||
endif | |||
ifdef PS2_USE_USART | |||
SRC += protocol/ps2_usart.c | |||
OPT_DEFS += -DPS2_USE_USART | |||
endif | |||
# Search Path | |||
VPATH += $(TOP_DIR)/protocol |
@@ -0,0 +1,27 @@ | |||
BLUEFRUIT_DIR = protocol/bluefruit | |||
PJRC_DIR = protocol/pjrc | |||
SRC += $(BLUEFRUIT_DIR)/main.c \ | |||
$(BLUEFRUIT_DIR)/bluefruit.c \ | |||
serial_uart.c \ | |||
$(PJRC_DIR)/pjrc.c \ | |||
$(PJRC_DIR)/usb_keyboard.c \ | |||
$(PJRC_DIR)/usb_debug.c \ | |||
$(PJRC_DIR)/usb.c | |||
# Option modules | |||
ifdef $(or MOUSEKEY_ENABLE, PS2_MOUSE_ENABLE) | |||
SRC += $(PJRC_DIR)/usb_mouse.c | |||
endif | |||
ifdef EXTRAKEY_ENABLE | |||
SRC += $(PJRC_DIR)/usb_extra.c | |||
endif | |||
# Search Path | |||
VPATH += $(TOP_DIR)/$(BLUEFRUIT_DIR) | |||
#VPATH += $(TOP_DIR)/$(BLUEFRUIT_DIR)/usb_debug_only | |||
VPATH += $(TOP_DIR)/$(PJRC_DIR) | |||
OPT_DEFS += -DPROTOCOL_BLUEFRUIT | |||
OPT_DEFS += -DPROTOCOL_PJRC |
@@ -0,0 +1,202 @@ | |||
/* | |||
Bluefruit Protocol for TMK firmware | |||
Author: Benjamin Gould, 2013 | |||
Based on code Copyright 2011 Jun Wako <[email protected]> | |||
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 "host.h" | |||
#include "report.h" | |||
#include "print.h" | |||
#include "debug.h" | |||
#include "host_driver.h" | |||
#include "serial.h" | |||
#include "bluefruit.h" | |||
#define BLUEFRUIT_TRACE_SERIAL 1 | |||
static uint8_t bluefruit_keyboard_leds = 0; | |||
static void bluefruit_serial_send(uint8_t); | |||
void bluefruit_keyboard_print_report(report_keyboard_t *report) | |||
{ | |||
if (!debug_keyboard) return; | |||
dprintf("keys: "); for (int i = 0; i < REPORT_KEYS; i++) { debug_hex8(report->keys[i]); dprintf(" "); } | |||
dprintf(" mods: "); debug_hex8(report->mods); | |||
dprintf(" reserved: "); debug_hex8(report->reserved); | |||
dprintf("\n"); | |||
} | |||
#ifdef BLUEFRUIT_TRACE_SERIAL | |||
static void bluefruit_trace_header() | |||
{ | |||
dprintf("+------------------------------------+\n"); | |||
dprintf("| HID report to Bluefruit via serial |\n"); | |||
dprintf("+------------------------------------+\n|"); | |||
} | |||
static void bluefruit_trace_footer() | |||
{ | |||
dprintf("|\n+------------------------------------+\n\n"); | |||
} | |||
#endif | |||
static void bluefruit_serial_send(uint8_t data) | |||
{ | |||
#ifdef BLUEFRUIT_TRACE_SERIAL | |||
dprintf(" "); | |||
debug_hex8(data); | |||
dprintf(" "); | |||
#endif | |||
serial_send(data); | |||
} | |||
/*------------------------------------------------------------------* | |||
* 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); | |||
static host_driver_t driver = { | |||
keyboard_leds, | |||
send_keyboard, | |||
send_mouse, | |||
send_system, | |||
send_consumer | |||
}; | |||
host_driver_t *bluefruit_driver(void) | |||
{ | |||
return &driver; | |||
} | |||
static uint8_t keyboard_leds(void) { | |||
return bluefruit_keyboard_leds; | |||
} | |||
static void send_keyboard(report_keyboard_t *report) | |||
{ | |||
#ifdef BLUEFRUIT_TRACE_SERIAL | |||
bluefruit_trace_header(); | |||
#endif | |||
bluefruit_serial_send(0xFD); | |||
for (uint8_t i = 0; i < REPORT_SIZE; i++) { | |||
bluefruit_serial_send(report->raw[i]); | |||
} | |||
#ifdef BLUEFRUIT_TRACE_SERIAL | |||
bluefruit_trace_footer(); | |||
#endif | |||
} | |||
static void send_mouse(report_mouse_t *report) | |||
{ | |||
#ifdef BLUEFRUIT_TRACE_SERIAL | |||
bluefruit_trace_header(); | |||
#endif | |||
bluefruit_serial_send(0xFD); | |||
bluefruit_serial_send(0x00); | |||
bluefruit_serial_send(0x03); | |||
bluefruit_serial_send(report->buttons); | |||
bluefruit_serial_send(report->x); | |||
bluefruit_serial_send(report->y); | |||
bluefruit_serial_send(report->v); // should try sending the wheel v here | |||
bluefruit_serial_send(report->h); // should try sending the wheel h here | |||
bluefruit_serial_send(0x00); | |||
#ifdef BLUEFRUIT_TRACE_SERIAL | |||
bluefruit_trace_footer(); | |||
#endif | |||
} | |||
static void send_system(uint16_t data) | |||
{ | |||
} | |||
/* | |||
+-----------------+-------------------+-------+ | |||
| Consumer Key | Bit Map | Hex | | |||
+-----------------+-------------------+-------+ | |||
| Home | 00000001 00000000 | 01 00 | | |||
| KeyboardLayout | 00000010 00000000 | 02 00 | | |||
| Search | 00000100 00000000 | 04 00 | | |||
| Snapshot | 00001000 00000000 | 08 00 | | |||
| VolumeUp | 00010000 00000000 | 10 00 | | |||
| VolumeDown | 00100000 00000000 | 20 00 | | |||
| Play/Pause | 01000000 00000000 | 40 00 | | |||
| Fast Forward | 10000000 00000000 | 80 00 | | |||
| Rewind | 00000000 00000001 | 00 01 | | |||
| Scan Next Track | 00000000 00000010 | 00 02 | | |||
| Scan Prev Track | 00000000 00000100 | 00 04 | | |||
| Random Play | 00000000 00001000 | 00 08 | | |||
| Stop | 00000000 00010000 | 00 10 | | |||
+-------------------------------------+-------+ | |||
*/ | |||
#define CONSUMER2BLUEFRUIT(usage) \ | |||
(usage == AUDIO_MUTE ? 0x0000 : \ | |||
(usage == AUDIO_VOL_UP ? 0x1000 : \ | |||
(usage == AUDIO_VOL_DOWN ? 0x2000 : \ | |||
(usage == TRANSPORT_NEXT_TRACK ? 0x0002 : \ | |||
(usage == TRANSPORT_PREV_TRACK ? 0x0004 : \ | |||
(usage == TRANSPORT_STOP ? 0x0010 : \ | |||
(usage == TRANSPORT_STOP_EJECT ? 0x0000 : \ | |||
(usage == TRANSPORT_PLAY_PAUSE ? 0x4000 : \ | |||
(usage == AL_CC_CONFIG ? 0x0000 : \ | |||
(usage == AL_EMAIL ? 0x0000 : \ | |||
(usage == AL_CALCULATOR ? 0x0000 : \ | |||
(usage == AL_LOCAL_BROWSER ? 0x0000 : \ | |||
(usage == AC_SEARCH ? 0x0400 : \ | |||
(usage == AC_HOME ? 0x0100 : \ | |||
(usage == AC_BACK ? 0x0000 : \ | |||
(usage == AC_FORWARD ? 0x0000 : \ | |||
(usage == AC_STOP ? 0x0000 : \ | |||
(usage == AC_REFRESH ? 0x0000 : \ | |||
(usage == AC_BOOKMARKS ? 0x0000 : 0))))))))))))))))))) | |||
static void send_consumer(uint16_t data) | |||
{ | |||
static uint16_t last_data = 0; | |||
if (data == last_data) return; | |||
last_data = data; | |||
uint16_t bitmap = CONSUMER2BLUEFRUIT(data); | |||
#ifdef BLUEFRUIT_TRACE_SERIAL | |||
dprintf("\nData: "); | |||
debug_hex16(data); | |||
dprintf("; bitmap: "); | |||
debug_hex16(bitmap); | |||
dprintf("\n"); | |||
bluefruit_trace_header(); | |||
#endif | |||
bluefruit_serial_send(0xFD); | |||
bluefruit_serial_send(0x00); | |||
bluefruit_serial_send(0x02); | |||
bluefruit_serial_send((bitmap>>8)&0xFF); | |||
bluefruit_serial_send(bitmap&0xFF); | |||
bluefruit_serial_send(0x00); | |||
bluefruit_serial_send(0x00); | |||
bluefruit_serial_send(0x00); | |||
bluefruit_serial_send(0x00); | |||
#ifdef BLUEFRUIT_TRACE_SERIAL | |||
bluefruit_trace_footer(); | |||
#endif | |||
} | |||
@@ -0,0 +1,28 @@ | |||
/* | |||
Bluefruit Protocol for TMK firmware | |||
Author: Benjamin Gould, 2013 | |||
Based on code Copyright 2011 Jun Wako <[email protected]> | |||
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 VUSB_H | |||
#define VUSB_H | |||
#include "host_driver.h" | |||
host_driver_t *bluefruit_driver(void); | |||
#endif |
@@ -0,0 +1,116 @@ | |||
/* | |||
Bluefruit Protocol for TMK firmware | |||
Author: Benjamin Gould, 2013 | |||
Based on code Copyright 2011 Jun Wako <[email protected]> | |||
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 <avr/interrupt.h> | |||
#include <avr/wdt.h> | |||
#include <avr/sleep.h> | |||
#include <util/delay.h> | |||
#include "serial.h" | |||
#include "keyboard.h" | |||
#include "usb.h" | |||
#include "host.h" | |||
#include "timer.h" | |||
#include "print.h" | |||
#include "debug.h" | |||
#include "sendchar.h" | |||
#include "suspend.h" | |||
#include "bluefruit.h" | |||
#include "pjrc.h" | |||
#define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n)) | |||
#define HOST_DRIVER_NOT_SET 0 | |||
#define BLUEFRUIT_HOST_DRIVER 1 | |||
#define PJRC_HOST_DRIVER 2 | |||
int main(void) | |||
{ | |||
CPU_PRESCALE(0); | |||
DDRD = _BV(PD5); | |||
DDRB = _BV(PB0); | |||
PORTD = _BV(PD5); | |||
PORTB = _BV(PB0); | |||
print_set_sendchar(sendchar); | |||
usb_init(); | |||
_delay_ms(2000); | |||
// while (!usb_configured()) /* wait */ | |||
dprintf("Initializing keyboard...\n"); | |||
keyboard_init(); | |||
// This implementation is pretty simplistic... if the USB connection | |||
// is not configured, choose the Bluefruit, otherwise use USB | |||
// Definitely would prefer to have this driven by an input pin and make | |||
// it switch dynamically - BCG | |||
if (!usb_configured()) { | |||
// Send power to Bluefruit... Adafruit says it takes 27 mA, I think | |||
// the pins should provide 40 mA, but just in case I switch the | |||
// Bluefruit using a transistor - BCG | |||
DDRB = _BV(PB6); | |||
PORTB |= _BV(PB6); | |||
dprintf("Setting host driver to bluefruit...\n"); | |||
host_set_driver(bluefruit_driver()); | |||
dprintf("Initializing serial...\n"); | |||
serial_init(); | |||
// wait an extra second for the PC's operating system | |||
// to load drivers and do whatever it does to actually | |||
// be ready for input | |||
_delay_ms(1000); | |||
PORTD = ~_BV(PD5); | |||
dprintf("Starting main loop"); | |||
while (1) { | |||
keyboard_task(); | |||
} | |||
} else { | |||
// I'm not smart enough to get this done with LUFA - BCG | |||
dprintf("Setting host driver to PJRC...\n"); | |||
host_set_driver(pjrc_driver()); | |||
#ifdef SLEEP_LED_ENABLE | |||
sleep_led_init(); | |||
#endif | |||
// wait an extra second for the PC's operating system | |||
// to load drivers and do whatever it does to actually | |||
// be ready for input | |||
_delay_ms(1000); | |||
PORTB = ~_BV(PB0); | |||
dprintf("Starting main loop"); | |||
while (1) { | |||
while (suspend) { | |||
suspend_power_down(); | |||
if (remote_wakeup && suspend_wakeup_condition()) { | |||
usb_remote_wakeup(); | |||
} | |||
} | |||
keyboard_task(); | |||
} | |||
} | |||
} |
@@ -148,7 +148,6 @@ static void Console_Task(void) | |||
*/ | |||
void EVENT_USB_Device_Connect(void) | |||
{ | |||
led_set(0x1f); // all on | |||
} | |||
void EVENT_USB_Device_Disconnect(void) | |||
@@ -172,8 +171,9 @@ void EVENT_USB_Device_WakeUp() | |||
#ifdef SLEEP_LED_ENABLE | |||
sleep_led_disable(); | |||
#endif | |||
// NOTE: converters may not accept this | |||
led_set(host_keyboard_leds()); | |||
#endif | |||
} | |||
void EVENT_USB_Device_StartOfFrame(void) | |||
@@ -539,11 +539,18 @@ int main(void) | |||
{ | |||
SetupHardware(); | |||
sei(); | |||
/* wait for USB startup & debug output */ | |||
while (USB_DeviceState != DEVICE_STATE_Configured) { | |||
#if defined(INTERRUPT_CONTROL_ENDPOINT) | |||
while (USB_DeviceState != DEVICE_STATE_Configured) ; | |||
; | |||
#else | |||
USB_USBTask(); | |||
#endif | |||
} | |||
print("USB configured.\n"); | |||
/* init modules */ | |||
keyboard_init(); | |||
host_set_driver(&lufa_driver); | |||
#ifdef SLEEP_LED_ENABLE |
@@ -7,7 +7,11 @@ SRC += $(PJRC_DIR)/main.c \ | |||
$(PJRC_DIR)/usb.c | |||
# Option modules | |||
ifdef $(or MOUSEKEY_ENABLE, PS2_MOUSE_ENABLE) | |||
ifdef MOUSEKEY_ENABLE | |||
SRC += $(PJRC_DIR)/usb_mouse.c | |||
endif | |||
ifdef PS2_MOUSE_ENABLE | |||
SRC += $(PJRC_DIR)/usb_mouse.c | |||
endif | |||
@@ -662,8 +662,9 @@ ISR(USB_GEN_vect) | |||
suspend_wakeup_init(); | |||
#ifdef SLEEP_LED_ENABLE | |||
sleep_led_disable(); | |||
#endif | |||
// NOTE: converters may not accept this | |||
led_set(host_keyboard_leds()); | |||
#endif | |||
UDIEN |= (1<<SUSPE); | |||
UDIEN &= ~(1<<WAKEUPE); |
@@ -1,5 +1,5 @@ | |||
/* | |||
Copyright 2010,2011 Jun WAKO <[email protected]> | |||
Copyright 2010,2011,2012,2013 Jun WAKO <[email protected]> | |||
This software is licensed with a Modified BSD License. | |||
All of this is supposed to be Free Software, Open Source, DFSG-free, | |||
@@ -37,32 +37,46 @@ POSSIBILITY OF SUCH DAMAGE. | |||
#ifndef PS2_H | |||
#define PS2_H | |||
#include <stdbool.h> | |||
#include <util/delay.h> | |||
#include <avr/io.h> | |||
/* | |||
* Primitive PS/2 Library for AVR | |||
* | |||
* PS/2 Resources | |||
* -------------- | |||
* [1] The PS/2 Mouse/Keyboard Protocol | |||
* http://www.computer-engineering.org/ps2protocol/ | |||
* Concise and thorough primer of PS/2 protocol. | |||
* | |||
* [2] Keyboard and Auxiliary Device Controller | |||
* http://www.mcamafia.de/pdf/ibm_hitrc07.pdf | |||
* Signal Timing and Format | |||
* | |||
* [3] Keyboards(101- and 102-key) | |||
* http://www.mcamafia.de/pdf/ibm_hitrc11.pdf | |||
* Keyboard Layout, Scan Code Set, POR, and Commands. | |||
* | |||
* [4] PS/2 Reference Manuals | |||
* http://www.mcamafia.de/pdf/ibm_hitrc07.pdf | |||
* Collection of IBM Personal System/2 documents. | |||
* | |||
* [5] TrackPoint Engineering Specifications for version 3E | |||
* https://web.archive.org/web/20100526161812/http://wwwcssrv.almaden.ibm.com/trackpoint/download.html | |||
*/ | |||
/* port settings for clock and data line */ | |||
#if !(defined(PS2_CLOCK_PORT) && \ | |||
defined(PS2_CLOCK_PIN) && \ | |||
defined(PS2_CLOCK_DDR) && \ | |||
defined(PS2_CLOCK_BIT)) | |||
# error "PS/2 clock port setting is required in config.h" | |||
#endif | |||
#if !(defined(PS2_DATA_PORT) && \ | |||
defined(PS2_DATA_PIN) && \ | |||
defined(PS2_DATA_DDR) && \ | |||
defined(PS2_DATA_BIT)) | |||
# error "PS/2 data port setting is required in config.h" | |||
#endif | |||
#define PS2_ACK 0xFA | |||
#define PS2_RESEND 0xFE | |||
#define PS2_SET_LED 0xED | |||
#define PS2_ERR_NONE 0 | |||
#define PS2_ERR_PARITY 0x10 | |||
// TODO: error numbers | |||
#define PS2_ERR_NONE 0 | |||
#define PS2_ERR_STARTBIT1 1 | |||
#define PS2_ERR_STARTBIT2 2 | |||
#define PS2_ERR_STARTBIT3 3 | |||
#define PS2_ERR_PARITY 0x10 | |||
#define PS2_ERR_NODATA 0x20 | |||
#define PS2_LED_SCROLL_LOCK 0 | |||
#define PS2_LED_NUM_LOCK 1 | |||
@@ -71,13 +85,101 @@ POSSIBILITY OF SUCH DAMAGE. | |||
extern uint8_t ps2_error; | |||
/* host role */ | |||
void ps2_host_init(void); | |||
uint8_t ps2_host_send(uint8_t data); | |||
uint8_t ps2_host_recv_response(void); | |||
uint8_t ps2_host_recv(void); | |||
void ps2_host_set_led(uint8_t usb_led); | |||
/* device role */ | |||
/* Check port settings for clock and data line */ | |||
#if !(defined(PS2_CLOCK_PORT) && \ | |||
defined(PS2_CLOCK_PIN) && \ | |||
defined(PS2_CLOCK_DDR) && \ | |||
defined(PS2_CLOCK_BIT)) | |||
# error "PS/2 clock port setting is required in config.h" | |||
#endif | |||
#if !(defined(PS2_DATA_PORT) && \ | |||
defined(PS2_DATA_PIN) && \ | |||
defined(PS2_DATA_DDR) && \ | |||
defined(PS2_DATA_BIT)) | |||
# error "PS/2 data port setting is required in config.h" | |||
#endif | |||
/*-------------------------------------------------------------------- | |||
* static functions | |||
*------------------------------------------------------------------*/ | |||
static inline void clock_lo(void) | |||
{ | |||
PS2_CLOCK_PORT &= ~(1<<PS2_CLOCK_BIT); | |||
PS2_CLOCK_DDR |= (1<<PS2_CLOCK_BIT); | |||
} | |||
static inline void clock_hi(void) | |||
{ | |||
/* input with pull up */ | |||
PS2_CLOCK_DDR &= ~(1<<PS2_CLOCK_BIT); | |||
PS2_CLOCK_PORT |= (1<<PS2_CLOCK_BIT); | |||
} | |||
static inline bool clock_in(void) | |||
{ | |||
PS2_CLOCK_DDR &= ~(1<<PS2_CLOCK_BIT); | |||
PS2_CLOCK_PORT |= (1<<PS2_CLOCK_BIT); | |||
_delay_us(1); | |||
return PS2_CLOCK_PIN&(1<<PS2_CLOCK_BIT); | |||
} | |||
static inline void data_lo(void) | |||
{ | |||
PS2_DATA_PORT &= ~(1<<PS2_DATA_BIT); | |||
PS2_DATA_DDR |= (1<<PS2_DATA_BIT); | |||
} | |||
static inline void data_hi(void) | |||
{ | |||
/* input with pull up */ | |||
PS2_DATA_DDR &= ~(1<<PS2_DATA_BIT); | |||
PS2_DATA_PORT |= (1<<PS2_DATA_BIT); | |||
} | |||
static inline bool data_in(void) | |||
{ | |||
PS2_DATA_DDR &= ~(1<<PS2_DATA_BIT); | |||
PS2_DATA_PORT |= (1<<PS2_DATA_BIT); | |||
_delay_us(1); | |||
return PS2_DATA_PIN&(1<<PS2_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 */ | |||
static inline void inhibit(void) | |||
{ | |||
clock_lo(); | |||
data_hi(); | |||
} | |||
#endif |
@@ -0,0 +1,185 @@ | |||
/* | |||
Copyright 2010,2011,2012,2013 Jun WAKO <[email protected]> | |||
This software is licensed with a Modified BSD License. | |||
All of this is supposed to be Free Software, Open Source, DFSG-free, | |||
GPL-compatible, and OK to use in both free and proprietary applications. | |||
Additions and corrections to this file are welcome. | |||
Redistribution and use in source and binary forms, with or without | |||
modification, are permitted provided that the following conditions are met: | |||
* Redistributions of source code must retain the above copyright | |||
notice, this list of conditions and the following disclaimer. | |||
* Redistributions in binary form must reproduce the above copyright | |||
notice, this list of conditions and the following disclaimer in | |||
the documentation and/or other materials provided with the | |||
distribution. | |||
* Neither the name of the copyright holders nor the names of | |||
contributors may be used to endorse or promote products derived | |||
from this software without specific prior written permission. | |||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |||
POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
/* | |||
* PS/2 protocol busywait version | |||
*/ | |||
#include <stdbool.h> | |||
#include <util/delay.h> | |||
#include "ps2.h" | |||
#include "debug.h" | |||
#define WAIT(stat, us, err) do { \ | |||
if (!wait_##stat(us)) { \ | |||
ps2_error = err; \ | |||
goto ERROR; \ | |||
} \ | |||
} while (0) | |||
uint8_t ps2_error = PS2_ERR_NONE; | |||
void ps2_host_init(void) | |||
{ | |||
// POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20) | |||
_delay_ms(2500); | |||
inhibit(); | |||
} | |||
uint8_t ps2_host_send(uint8_t data) | |||
{ | |||
bool parity = true; | |||
ps2_error = PS2_ERR_NONE; | |||
/* terminate a transmission if we have */ | |||
inhibit(); | |||
_delay_us(100); // 100us [4]p.13, [5]p.50 | |||
/* 'Request to Send' and Start bit */ | |||
data_lo(); | |||
clock_hi(); | |||
WAIT(clock_lo, 10000, 10); // 10ms [5]p.50 | |||
/* Data bit */ | |||
for (uint8_t i = 0; i < 8; i++) { | |||
_delay_us(15); | |||
if (data&(1<<i)) { | |||
parity = !parity; | |||
data_hi(); | |||
} else { | |||
data_lo(); | |||
} | |||
WAIT(clock_hi, 50, 2); | |||
WAIT(clock_lo, 50, 3); | |||
} | |||
/* Parity bit */ | |||
_delay_us(15); | |||
if (parity) { data_hi(); } else { data_lo(); } | |||
WAIT(clock_hi, 50, 4); | |||
WAIT(clock_lo, 50, 5); | |||
/* Stop bit */ | |||
_delay_us(15); | |||
data_hi(); | |||
/* Ack */ | |||
WAIT(data_lo, 50, 6); | |||
WAIT(clock_lo, 50, 7); | |||
/* wait for idle state */ | |||
WAIT(clock_hi, 50, 8); | |||
WAIT(data_hi, 50, 9); | |||
inhibit(); | |||
return ps2_host_recv_response(); | |||
ERROR: | |||
inhibit(); | |||
return 0; | |||
} | |||
/* receive data when host want else inhibit communication */ | |||
uint8_t ps2_host_recv_response(void) | |||
{ | |||
// Command may take 25ms/20ms at most([5]p.46, [3]p.21) | |||
// 250 * 100us(wait for start bit in ps2_host_recv) | |||
uint8_t data = 0; | |||
uint8_t try = 250; | |||
do { | |||
data = ps2_host_recv(); | |||
} while (try-- && ps2_error); | |||
return data; | |||
} | |||
/* called after start bit comes */ | |||
uint8_t ps2_host_recv(void) | |||
{ | |||
uint8_t data = 0; | |||
bool parity = true; | |||
ps2_error = PS2_ERR_NONE; | |||
/* release lines(idle state) */ | |||
idle(); | |||
/* start bit [1] */ | |||
WAIT(clock_lo, 100, 1); // TODO: this is enough? | |||
WAIT(data_lo, 1, 2); | |||
WAIT(clock_hi, 50, 3); | |||
/* data [2-9] */ | |||
for (uint8_t i = 0; i < 8; i++) { | |||
WAIT(clock_lo, 50, 4); | |||
if (data_in()) { | |||
parity = !parity; | |||
data |= (1<<i); | |||
} | |||
WAIT(clock_hi, 50, 5); | |||
} | |||
/* parity [10] */ | |||
WAIT(clock_lo, 50, 6); | |||
if (data_in() != parity) { | |||
ps2_error = PS2_ERR_PARITY; | |||
goto ERROR; | |||
} | |||
WAIT(clock_hi, 50, 7); | |||
/* stop bit [11] */ | |||
WAIT(clock_lo, 50, 8); | |||
WAIT(data_hi, 1, 9); | |||
WAIT(clock_hi, 50, 10); | |||
inhibit(); | |||
return data; | |||
ERROR: | |||
if (ps2_error > PS2_ERR_STARTBIT3) { | |||
xprintf("x%02X\n", ps2_error); | |||
} | |||
inhibit(); | |||
return 0; | |||
} | |||
/* send LED state to keyboard */ | |||
void ps2_host_set_led(uint8_t led) | |||
{ | |||
ps2_host_send(0xED); | |||
ps2_host_send(led); | |||
} |
@@ -1,5 +1,5 @@ | |||
/* | |||
Copyright 2010,2011 Jun WAKO <[email protected]> | |||
Copyright 2010,2011,2012,2013 Jun WAKO <[email protected]> | |||
This software is licensed with a Modified BSD License. | |||
All of this is supposed to be Free Software, Open Source, DFSG-free, | |||
@@ -35,45 +35,15 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |||
POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
/* | |||
* PS/2 protocol Pin interrupt version | |||
*/ | |||
#include <stdbool.h> | |||
#include <avr/io.h> | |||
#include <avr/interrupt.h> | |||
#include <util/delay.h> | |||
#include "ps2.h" | |||
#include "debug.h" | |||
static uint8_t recv_data(void); | |||
static inline void clock_lo(void); | |||
static inline void clock_hi(void); | |||
static inline bool clock_in(void); | |||
static inline void data_lo(void); | |||
static inline void data_hi(void); | |||
static inline bool data_in(void); | |||
static inline uint16_t wait_clock_lo(uint16_t us); | |||
static inline uint16_t wait_clock_hi(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 void idle(void); | |||
static inline void inhibit(void); | |||
/* | |||
Primitive PS/2 Library for AVR | |||
============================== | |||
Host side is only supported now. | |||
I/O control | |||
----------- | |||
High state is asserted by input with pull up. | |||
PS/2 References | |||
--------------- | |||
http://www.computer-engineering.org/ps2protocol/ | |||
http://www.mcamafia.de/pdf/ibm_hitrc07.pdf | |||
*/ | |||
#include "print.h" | |||
#define WAIT(stat, us, err) do { \ | |||
@@ -87,35 +57,38 @@ http://www.mcamafia.de/pdf/ibm_hitrc07.pdf | |||
uint8_t ps2_error = PS2_ERR_NONE; | |||
static inline uint8_t pbuf_dequeue(void); | |||
static inline void pbuf_enqueue(uint8_t data); | |||
static inline bool pbuf_has_data(void); | |||
static inline void pbuf_clear(void); | |||
void ps2_host_init(void) | |||
{ | |||
#ifdef PS2_USE_INT | |||
idle(); | |||
PS2_INT_INIT(); | |||
PS2_INT_ON(); | |||
idle(); | |||
#else | |||
inhibit(); | |||
#endif | |||
// POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20) | |||
//_delay_ms(2500); | |||
} | |||
// TODO: send using interrupt if available | |||
uint8_t ps2_host_send(uint8_t data) | |||
{ | |||
uint8_t res = 0; | |||
bool parity = true; | |||
ps2_error = PS2_ERR_NONE; | |||
#ifdef PS2_USE_INT | |||
PS2_INT_OFF(); | |||
#endif | |||
/* terminate a transmission if we have */ | |||
inhibit(); | |||
_delay_us(100); | |||
_delay_us(100); // 100us [4]p.13, [5]p.50 | |||
/* start bit [1] */ | |||
/* 'Request to Send' and Start bit */ | |||
data_lo(); | |||
clock_hi(); | |||
WAIT(clock_lo, 15000, 1); | |||
/* data [2-9] */ | |||
WAIT(clock_lo, 10000, 10); // 10ms [5]p.50 | |||
/* Data bit[2-9] */ | |||
for (uint8_t i = 0; i < 8; i++) { | |||
_delay_us(15); | |||
if (data&(1<<i)) { | |||
@@ -127,15 +100,18 @@ uint8_t ps2_host_send(uint8_t data) | |||
WAIT(clock_hi, 50, 2); | |||
WAIT(clock_lo, 50, 3); | |||
} | |||
/* parity [10] */ | |||
/* Parity bit */ | |||
_delay_us(15); | |||
if (parity) { data_hi(); } else { data_lo(); } | |||
WAIT(clock_hi, 50, 4); | |||
WAIT(clock_lo, 50, 5); | |||
/* stop bit [11] */ | |||
/* Stop bit */ | |||
_delay_us(15); | |||
data_hi(); | |||
/* ack [12] */ | |||
/* Ack */ | |||
WAIT(data_lo, 50, 6); | |||
WAIT(clock_lo, 50, 7); | |||
@@ -143,99 +119,37 @@ uint8_t ps2_host_send(uint8_t data) | |||
WAIT(clock_hi, 50, 8); | |||
WAIT(data_hi, 50, 9); | |||
res = ps2_host_recv_response(); | |||
ERROR: | |||
#ifdef PS2_USE_INT | |||
idle(); | |||
PS2_INT_ON(); | |||
return ps2_host_recv_response(); | |||
ERROR: | |||
idle(); | |||
#else | |||
inhibit(); | |||
#endif | |||
return res; | |||
PS2_INT_ON(); | |||
return 0; | |||
} | |||
/* receive data when host want else inhibit communication */ | |||
uint8_t ps2_host_recv_response(void) | |||
{ | |||
uint8_t data = 0; | |||
/* terminate a transmission if we have */ | |||
inhibit(); | |||
_delay_us(100); | |||
/* release lines(idle state) */ | |||
idle(); | |||
/* wait start bit */ | |||
wait_clock_lo(2000); | |||
data = recv_data(); | |||
inhibit(); | |||
return data; | |||
} | |||
#ifndef PS2_USE_INT | |||
uint8_t ps2_host_recv(void) | |||
{ | |||
return ps2_host_recv_response(); | |||
} | |||
#else | |||
/* ring buffer to store ps/2 key data */ | |||
#define PBUF_SIZE 32 | |||
static uint8_t pbuf[PBUF_SIZE]; | |||
static uint8_t pbuf_head = 0; | |||
static uint8_t pbuf_tail = 0; | |||
static inline void pbuf_enqueue(uint8_t data) | |||
{ | |||
if (!data) | |||
return; | |||
uint8_t sreg = SREG; | |||
cli(); | |||
uint8_t next = (pbuf_head + 1) % PBUF_SIZE; | |||
if (next != pbuf_tail) { | |||
pbuf[pbuf_head] = data; | |||
pbuf_head = next; | |||
} else { | |||
debug("pbuf: full\n"); | |||
} | |||
SREG = sreg; | |||
} | |||
static inline uint8_t pbuf_dequeue(void) | |||
{ | |||
uint8_t val = 0; | |||
uint8_t sreg = SREG; | |||
cli(); | |||
if (pbuf_head != pbuf_tail) { | |||
val = pbuf[pbuf_tail]; | |||
pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE; | |||
// Command may take 25ms/20ms at most([5]p.46, [3]p.21) | |||
uint8_t retry = 25; | |||
while (retry-- && !pbuf_has_data()) { | |||
_delay_ms(1); | |||
} | |||
SREG = sreg; | |||
return val; | |||
return pbuf_dequeue(); | |||
} | |||
/* get data received by interrupt */ | |||
uint8_t ps2_host_recv(void) | |||
{ | |||
if (ps2_error) { | |||
print("x"); | |||
phex(ps2_error); | |||
ps2_host_send(0xFE); // request to resend | |||
if (pbuf_has_data()) { | |||
ps2_error = PS2_ERR_NONE; | |||
return pbuf_dequeue(); | |||
} else { | |||
ps2_error = PS2_ERR_NODATA; | |||
return 0; | |||
} | |||
idle(); | |||
return pbuf_dequeue(); | |||
} | |||
#if 0 | |||
#define DEBUGP_INIT() do { DDRC = 0xFF; } while (0) | |||
#define DEBUGP(x) do { PORTC = x; } while (0) | |||
#else | |||
#define DEBUGP_INIT() | |||
#define DEBUGP(x) | |||
#endif | |||
ISR(PS2_INT_VECT) | |||
{ | |||
static enum { | |||
@@ -256,7 +170,6 @@ ISR(PS2_INT_VECT) | |||
} | |||
state++; | |||
DEBUGP(state); | |||
switch (state) { | |||
case START: | |||
if (data_in()) | |||
@@ -296,8 +209,6 @@ ISR(PS2_INT_VECT) | |||
} | |||
goto RETURN; | |||
ERROR: | |||
DEBUGP(0x0F); | |||
inhibit(); | |||
ps2_error = state; | |||
DONE: | |||
state = INIT; | |||
@@ -306,13 +217,6 @@ DONE: | |||
RETURN: | |||
return; | |||
} | |||
#endif | |||
static void ps2_reset(void) | |||
{ | |||
ps2_host_send(0xFF); | |||
} | |||
/* send LED state to keyboard */ | |||
void ps2_host_set_led(uint8_t led) | |||
@@ -322,114 +226,53 @@ void ps2_host_set_led(uint8_t led) | |||
} | |||
/* called after start bit comes */ | |||
static uint8_t recv_data(void) | |||
/*-------------------------------------------------------------------- | |||
* Ring buffer to store scan codes from keyboard | |||
*------------------------------------------------------------------*/ | |||
#define PBUF_SIZE 32 | |||
static uint8_t pbuf[PBUF_SIZE]; | |||
static uint8_t pbuf_head = 0; | |||
static uint8_t pbuf_tail = 0; | |||
static inline void pbuf_enqueue(uint8_t data) | |||
{ | |||
uint8_t data = 0; | |||
bool parity = true; | |||
ps2_error = PS2_ERR_NONE; | |||
/* start bit [1] */ | |||
WAIT(clock_lo, 1, 1); | |||
WAIT(data_lo, 1, 2); | |||
WAIT(clock_hi, 50, 3); | |||
/* data [2-9] */ | |||
for (uint8_t i = 0; i < 8; i++) { | |||
WAIT(clock_lo, 50, 4); | |||
if (data_in()) { | |||
parity = !parity; | |||
data |= (1<<i); | |||
} | |||
WAIT(clock_hi, 50, 5); | |||
} | |||
/* parity [10] */ | |||
WAIT(clock_lo, 50, 6); | |||
if (data_in() != parity) { | |||
ps2_error = PS2_ERR_PARITY; | |||
goto ERROR; | |||
uint8_t sreg = SREG; | |||
cli(); | |||
uint8_t next = (pbuf_head + 1) % PBUF_SIZE; | |||
if (next != pbuf_tail) { | |||
pbuf[pbuf_head] = data; | |||
pbuf_head = next; | |||
} else { | |||
print("pbuf: full\n"); | |||
} | |||
WAIT(clock_hi, 50, 7); | |||
/* stop bit [11] */ | |||
WAIT(clock_lo, 50, 8); | |||
WAIT(data_hi, 1, 9); | |||
WAIT(clock_hi, 50, 10); | |||
return data; | |||
ERROR: | |||
return 0; | |||
} | |||
static inline void clock_lo() | |||
{ | |||
PS2_CLOCK_PORT &= ~(1<<PS2_CLOCK_BIT); | |||
PS2_CLOCK_DDR |= (1<<PS2_CLOCK_BIT); | |||
} | |||
static inline void clock_hi() | |||
{ | |||
/* input with pull up */ | |||
PS2_CLOCK_DDR &= ~(1<<PS2_CLOCK_BIT); | |||
PS2_CLOCK_PORT |= (1<<PS2_CLOCK_BIT); | |||
} | |||
static inline bool clock_in() | |||
{ | |||
PS2_CLOCK_DDR &= ~(1<<PS2_CLOCK_BIT); | |||
PS2_CLOCK_PORT |= (1<<PS2_CLOCK_BIT); | |||
_delay_us(1); | |||
return PS2_CLOCK_PIN&(1<<PS2_CLOCK_BIT); | |||
} | |||
static inline void data_lo() | |||
{ | |||
PS2_DATA_PORT &= ~(1<<PS2_DATA_BIT); | |||
PS2_DATA_DDR |= (1<<PS2_DATA_BIT); | |||
} | |||
static inline void data_hi() | |||
{ | |||
/* input with pull up */ | |||
PS2_DATA_DDR &= ~(1<<PS2_DATA_BIT); | |||
PS2_DATA_PORT |= (1<<PS2_DATA_BIT); | |||
SREG = sreg; | |||
} | |||
static inline bool data_in() | |||
static inline uint8_t pbuf_dequeue(void) | |||
{ | |||
PS2_DATA_DDR &= ~(1<<PS2_DATA_BIT); | |||
PS2_DATA_PORT |= (1<<PS2_DATA_BIT); | |||
_delay_us(1); | |||
return PS2_DATA_PIN&(1<<PS2_DATA_BIT); | |||
} | |||
uint8_t val = 0; | |||
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; | |||
uint8_t sreg = SREG; | |||
cli(); | |||
if (pbuf_head != pbuf_tail) { | |||
val = pbuf[pbuf_tail]; | |||
pbuf_tail = (pbuf_tail + 1) % PBUF_SIZE; | |||
} | |||
SREG = sreg; | |||
return val; | |||
} | |||
static inline uint16_t wait_data_hi(uint16_t us) | |||
static inline bool pbuf_has_data(void) | |||
{ | |||
while (!data_in() && us) { asm(""); _delay_us(1); us--; } | |||
return us; | |||
uint8_t sreg = SREG; | |||
cli(); | |||
bool has_data = (pbuf_head != pbuf_tail); | |||
SREG = sreg; | |||
return has_data; | |||
} | |||
/* idle state that device can send */ | |||
static inline void idle(void) | |||
static inline void pbuf_clear(void) | |||
{ | |||
clock_hi(); | |||
data_hi(); | |||
uint8_t sreg = SREG; | |||
cli(); | |||
pbuf_head = pbuf_tail = 0; | |||
SREG = sreg; | |||
} | |||
/* inhibit device to send */ | |||
static inline void inhibit(void) | |||
{ | |||
clock_lo(); | |||
data_hi(); | |||
} |
@@ -1,5 +1,5 @@ | |||
/* | |||
Copyright 2011 Jun Wako <[email protected]> | |||
Copyright 2011,2013 Jun Wako <[email protected]> | |||
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 | |||
@@ -20,199 +20,196 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
#include<util/delay.h> | |||
#include "ps2.h" | |||
#include "ps2_mouse.h" | |||
#include "usb_mouse.h" | |||
#include "report.h" | |||
#include "host.h" | |||
#include "timer.h" | |||
#include "print.h" | |||
#include "debug.h" | |||
#define PS2_MOUSE_DEBUG | |||
#ifdef PS2_MOUSE_DEBUG | |||
# include "print.h" | |||
# include "debug.h" | |||
#else | |||
# define print(s) | |||
# define phex(h) | |||
# define phex16(h) | |||
#endif | |||
// disable when errors occur 255 times. | |||
#define ERROR_RETURN() do { \ | |||
if (ps2_error) { \ | |||
if (ps2_mouse_error_count < 255) { \ | |||
ps2_mouse_error_count++; \ | |||
} else { \ | |||
ps2_mouse_error_count = 0; \ | |||
ps2_mouse_enable = false; \ | |||
} \ | |||
return ps2_error; \ | |||
} \ | |||
} while (0) | |||
static report_mouse_t mouse_report = {}; | |||
/* | |||
TODO | |||
---- | |||
- Stream mode | |||
- Tracpoint command support: needed | |||
- Middle button + move = Wheel traslation | |||
*/ | |||
bool ps2_mouse_enable = true; | |||
uint8_t ps2_mouse_x = 0; | |||
uint8_t ps2_mouse_y = 0; | |||
uint8_t ps2_mouse_btn = 0; | |||
uint8_t ps2_mouse_error_count = 0; | |||
static uint8_t ps2_mouse_btn_prev = 0; | |||
static void print_usb_data(void); | |||
/* supports only 3 button mouse at this time */ | |||
uint8_t ps2_mouse_init(void) { | |||
uint8_t rcv; | |||
if (!ps2_mouse_enable) return 1; | |||
ps2_host_init(); | |||
// Reset | |||
rcv = ps2_host_send(0xFF); | |||
print("ps2_mouse_init: send 0xFF: "); | |||
phex(ps2_error); print("\n"); | |||
ERROR_RETURN(); | |||
_delay_ms(1000); // wait for powering up | |||
// ACK | |||
rcv = ps2_host_recv(); | |||
print("ps2_mouse_init: read ACK: "); | |||
// send Reset | |||
rcv = ps2_host_send(0xFF); | |||
print("ps2_mouse_init: send Reset: "); | |||
phex(rcv); phex(ps2_error); print("\n"); | |||
ERROR_RETURN(); | |||
// BAT takes some time | |||
_delay_ms(100); | |||
rcv = ps2_host_recv(); | |||
// read completion code of BAT | |||
rcv = ps2_host_recv_response(); | |||
print("ps2_mouse_init: read BAT: "); | |||
phex(rcv); phex(ps2_error); print("\n"); | |||
ERROR_RETURN(); | |||
// Device ID | |||
rcv = ps2_host_recv(); | |||
// read Device ID | |||
rcv = ps2_host_recv_response(); | |||
print("ps2_mouse_init: read DevID: "); | |||
phex(rcv); phex(ps2_error); print("\n"); | |||
ERROR_RETURN(); | |||
// Enable data reporting | |||
ps2_host_send(0xF4); | |||
print("ps2_mouse_init: send 0xF4: "); | |||
phex(ps2_error); print("\n"); | |||
ERROR_RETURN(); | |||
// ACK | |||
rcv = ps2_host_recv(); | |||
print("ps2_mouse_init: read ACK: "); | |||
phex(rcv); phex(ps2_error); print("\n"); | |||
ERROR_RETURN(); | |||
// Set Remote mode | |||
ps2_host_send(0xF0); | |||
// send Set Remote mode | |||
rcv = ps2_host_send(0xF0); | |||
print("ps2_mouse_init: send 0xF0: "); | |||
phex(ps2_error); print("\n"); | |||
ERROR_RETURN(); | |||
// ACK | |||
rcv = ps2_host_recv(); | |||
print("ps2_mouse_init: read ACK: "); | |||
phex(rcv); phex(ps2_error); print("\n"); | |||
ERROR_RETURN(); | |||
return 0; | |||
} | |||
/* | |||
Data format: | |||
bit: 7 6 5 4 3 2 1 0 | |||
----------------------------------------------------------------------- | |||
0 btn: Yovflw Xovflw Ysign Xsign 1 Middle Right Left | |||
1 x: X movement(0-255) | |||
2 y: Y movement(0-255) | |||
*/ | |||
uint8_t ps2_mouse_read(void) | |||
#define X_IS_NEG (mouse_report.buttons & (1<<PS2_MOUSE_X_SIGN)) | |||
#define Y_IS_NEG (mouse_report.buttons & (1<<PS2_MOUSE_Y_SIGN)) | |||
#define X_IS_OVF (mouse_report.buttons & (1<<PS2_MOUSE_X_OVFLW)) | |||
#define Y_IS_OVF (mouse_report.buttons & (1<<PS2_MOUSE_Y_OVFLW)) | |||
void ps2_mouse_task(void) | |||
{ | |||
enum { SCROLL_NONE, SCROLL_BTN, SCROLL_SENT }; | |||
static uint8_t scroll_state = SCROLL_NONE; | |||
static uint8_t buttons_prev = 0; | |||
/* receives packet from mouse */ | |||
uint8_t rcv; | |||
rcv = ps2_host_send(PS2_MOUSE_READ_DATA); | |||
if (rcv == PS2_ACK) { | |||
mouse_report.buttons = ps2_host_recv_response(); | |||
mouse_report.x = ps2_host_recv_response(); | |||
mouse_report.y = ps2_host_recv_response(); | |||
} else { | |||
if (!debug_mouse) print("ps2_mouse: fail to get mouse packet\n"); | |||
return; | |||
} | |||
if (!ps2_mouse_enable) return 1; | |||
/* if mouse moves or buttons state changes */ | |||
if (mouse_report.x || mouse_report.y || | |||
((mouse_report.buttons ^ buttons_prev) & PS2_MOUSE_BTN_MASK)) { | |||
ps2_host_send(0xEB); | |||
ERROR_RETURN(); | |||
#ifdef PS2_MOUSE_DEBUG | |||
print("ps2_mouse raw: ["); | |||
phex(mouse_report.buttons); print("|"); | |||
print_hex8((uint8_t)mouse_report.x); print(" "); | |||
print_hex8((uint8_t)mouse_report.y); print("]\n"); | |||
#endif | |||
rcv=ps2_host_recv(); | |||
ERROR_RETURN(); | |||
buttons_prev = mouse_report.buttons; | |||
// PS/2 mouse data is '9-bit integer'(-256 to 255) which is comprised of sign-bit and 8-bit value. | |||
// bit: 8 7 ... 0 | |||
// sign \8-bit/ | |||
// | |||
// Meanwhile USB HID mouse indicates 8bit data(-127 to 127), note that -128 is not used. | |||
// | |||
// This converts PS/2 data into HID value. Use only -127-127 out of PS/2 9-bit. | |||
mouse_report.x = X_IS_NEG ? | |||
((!X_IS_OVF && -127 <= mouse_report.x && mouse_report.x <= -1) ? mouse_report.x : -127) : | |||
((!X_IS_OVF && 0 <= mouse_report.x && mouse_report.x <= 127) ? mouse_report.x : 127); | |||
mouse_report.y = Y_IS_NEG ? | |||
((!Y_IS_OVF && -127 <= mouse_report.y && mouse_report.y <= -1) ? mouse_report.y : -127) : | |||
((!Y_IS_OVF && 0 <= mouse_report.y && mouse_report.y <= 127) ? mouse_report.y : 127); | |||
// remove sign and overflow flags | |||
mouse_report.buttons &= PS2_MOUSE_BTN_MASK; | |||
// invert coordinate of y to conform to USB HID mouse | |||
mouse_report.y = -mouse_report.y; | |||
#if PS2_MOUSE_SCROLL_BTN_MASK | |||
static uint16_t scroll_button_time = 0; | |||
if ((mouse_report.buttons & (PS2_MOUSE_SCROLL_BTN_MASK)) == (PS2_MOUSE_SCROLL_BTN_MASK)) { | |||
if (scroll_state == SCROLL_NONE) { | |||
scroll_button_time = timer_read(); | |||
scroll_state = SCROLL_BTN; | |||
} | |||
if(rcv==0xFA) { | |||
ps2_mouse_btn = ps2_host_recv(); | |||
ERROR_RETURN(); | |||
ps2_mouse_x = ps2_host_recv(); | |||
ERROR_RETURN(); | |||
ps2_mouse_y = ps2_host_recv(); | |||
ERROR_RETURN(); | |||
} | |||
return 0; | |||
} | |||
// doesn't send Scroll Button | |||
//mouse_report.buttons &= ~(PS2_MOUSE_SCROLL_BTN_MASK); | |||
bool ps2_mouse_changed(void) | |||
{ | |||
return (ps2_mouse_x || ps2_mouse_y || (ps2_mouse_btn & PS2_MOUSE_BTN_MASK) != ps2_mouse_btn_prev); | |||
} | |||
if (mouse_report.x || mouse_report.y) { | |||
scroll_state = SCROLL_SENT; | |||
#define PS2_MOUSE_SCROLL_BUTTON 0x04 | |||
void ps2_mouse_usb_send(void) | |||
{ | |||
static bool scrolled = false; | |||
if (!ps2_mouse_enable) return; | |||
if (ps2_mouse_changed()) { | |||
int8_t x, y, v, h; | |||
x = y = v = h = 0; | |||
// convert scale of X, Y: PS/2(-256/255) -> USB(-127/127) | |||
if (ps2_mouse_btn & (1<<PS2_MOUSE_X_SIGN)) | |||
x = ps2_mouse_x > 128 ? (int8_t)ps2_mouse_x : -127; | |||
else | |||
x = ps2_mouse_x < 128 ? (int8_t)ps2_mouse_x : 127; | |||
if (ps2_mouse_btn & (1<<PS2_MOUSE_Y_SIGN)) | |||
y = ps2_mouse_y > 128 ? (int8_t)ps2_mouse_y : -127; | |||
else | |||
y = ps2_mouse_y < 128 ? (int8_t)ps2_mouse_y : 127; | |||
// Y is needed to reverse | |||
y = -y; | |||
if (ps2_mouse_btn & PS2_MOUSE_SCROLL_BUTTON) { | |||
// scroll | |||
if (x > 0 || x < 0) h = (x > 64 ? 64 : (x < -64 ? -64 :x)); | |||
if (y > 0 || y < 0) v = (y > 64 ? 64 : (y < -64 ? -64 :y)); | |||
if (h || v) { | |||
scrolled = true; | |||
usb_mouse_send(0,0, -v/16, h/16, 0); | |||
mouse_report.v = -mouse_report.y/(PS2_MOUSE_SCROLL_DIVISOR_V); | |||
mouse_report.h = mouse_report.x/(PS2_MOUSE_SCROLL_DIVISOR_H); | |||
mouse_report.x = 0; | |||
mouse_report.y = 0; | |||
//host_mouse_send(&mouse_report); | |||
} | |||
} | |||
else if ((mouse_report.buttons & (PS2_MOUSE_SCROLL_BTN_MASK)) == 0) { | |||
#if PS2_MOUSE_SCROLL_BTN_SEND | |||
if (scroll_state == SCROLL_BTN && | |||
TIMER_DIFF_16(timer_read(), scroll_button_time) < PS2_MOUSE_SCROLL_BTN_SEND) { | |||
// send Scroll Button(down and up at once) when not scrolled | |||
mouse_report.buttons |= (PS2_MOUSE_SCROLL_BTN_MASK); | |||
host_mouse_send(&mouse_report); | |||
_delay_ms(100); | |||
mouse_report.buttons &= ~(PS2_MOUSE_SCROLL_BTN_MASK); | |||
} | |||
} else if (!scrolled && (ps2_mouse_btn_prev & PS2_MOUSE_SCROLL_BUTTON)) { | |||
usb_mouse_send(0,0,0,0, PS2_MOUSE_SCROLL_BUTTON); | |||
_delay_ms(100); | |||
usb_mouse_send(0,0,0,0, 0); | |||
} else { | |||
scrolled = false; | |||
usb_mouse_send(x, y, 0, 0, ps2_mouse_btn & PS2_MOUSE_BTN_MASK); | |||
#endif | |||
scroll_state = SCROLL_NONE; | |||
} | |||
// doesn't send Scroll Button | |||
mouse_report.buttons &= ~(PS2_MOUSE_SCROLL_BTN_MASK); | |||
#endif | |||
ps2_mouse_btn_prev = (ps2_mouse_btn & PS2_MOUSE_BTN_MASK); | |||
ps2_mouse_print(); | |||
host_mouse_send(&mouse_report); | |||
print_usb_data(); | |||
} | |||
ps2_mouse_x = 0; | |||
ps2_mouse_y = 0; | |||
ps2_mouse_btn = 0; | |||
// clear report | |||
mouse_report.x = 0; | |||
mouse_report.y = 0; | |||
mouse_report.v = 0; | |||
mouse_report.h = 0; | |||
mouse_report.buttons = 0; | |||
} | |||
void ps2_mouse_print(void) | |||
static void print_usb_data(void) | |||
{ | |||
if (!debug_mouse) return; | |||
print("ps2_mouse[btn|x y]: "); | |||
phex(ps2_mouse_btn); print("|"); | |||
phex(ps2_mouse_x); print(" "); | |||
phex(ps2_mouse_y); print("\n"); | |||
print("ps2_mouse usb: ["); | |||
phex(mouse_report.buttons); print("|"); | |||
print_hex8((uint8_t)mouse_report.x); print(" "); | |||
print_hex8((uint8_t)mouse_report.y); print(" "); | |||
print_hex8((uint8_t)mouse_report.v); print(" "); | |||
print_hex8((uint8_t)mouse_report.h); print("]\n"); | |||
} | |||
/* PS/2 Mouse Synopsis | |||
* http://www.computer-engineering.org/ps2mouse/ | |||
* | |||
* Command: | |||
* 0xFF: Reset | |||
* 0xF6: Set Defaults Sampling; rate=100, resolution=4cnt/mm, scaling=1:1, reporting=disabled | |||
* 0xF5: Disable Data Reporting | |||
* 0xF4: Enable Data Reporting | |||
* 0xF3: Set Sample Rate | |||
* 0xF2: Get Device ID | |||
* 0xF0: Set Remote Mode | |||
* 0xEB: Read Data | |||
* 0xEA: Set Stream Mode | |||
* 0xE9: Status Request | |||
* 0xE8: Set Resolution | |||
* 0xE7: Set Scaling 2:1 | |||
* 0xE6: Set Scaling 1:1 | |||
* | |||
* Mode: | |||
* Stream Mode: devices sends the data when it changs its state | |||
* Remote Mode: host polls the data periodically | |||
* | |||
* This code uses Remote Mode and polls the data with Read Data(0xEB). | |||
* | |||
* Data format: | |||
* byte|7 6 5 4 3 2 1 0 | |||
* ----+-------------------------------------------------------------- | |||
* 0|Yovflw Xovflw Ysign Xsign 1 Middle Right Left | |||
* 1| X movement | |||
* 2| Y movement | |||
*/ |
@@ -20,6 +20,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
#include <stdbool.h> | |||
#define PS2_MOUSE_READ_DATA 0xEB | |||
/* | |||
* Data format: | |||
* byte|7 6 5 4 3 2 1 0 | |||
* ----+-------------------------------------------------------------- | |||
* 0|Yovflw Xovflw Ysign Xsign 1 Middle Right Left | |||
* 1| X movement(0-255) | |||
* 2| Y movement(0-255) | |||
*/ | |||
#define PS2_MOUSE_BTN_MASK 0x07 | |||
#define PS2_MOUSE_BTN_LEFT 0 | |||
#define PS2_MOUSE_BTN_RIGHT 1 | |||
@@ -29,16 +39,28 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
#define PS2_MOUSE_X_OVFLW 6 | |||
#define PS2_MOUSE_Y_OVFLW 7 | |||
bool ps2_mouse_enable; | |||
extern uint8_t ps2_mouse_x; | |||
extern uint8_t ps2_mouse_y; | |||
extern uint8_t ps2_mouse_btn; | |||
extern uint8_t ps2_mouse_error_count; | |||
/* | |||
* Scroll by mouse move with pressing button | |||
*/ | |||
/* mouse button to start scrolling; set 0 to disable scroll */ | |||
#ifndef PS2_MOUSE_SCROLL_BTN_MASK | |||
#define PS2_MOUSE_SCROLL_BTN_MASK (1<<PS2_MOUSE_BTN_MIDDLE) | |||
#endif | |||
/* send button event when button is released within this value(ms); set 0 to disable */ | |||
#ifndef PS2_MOUSE_SCROLL_BTN_SEND | |||
#define PS2_MOUSE_SCROLL_BTN_SEND 300 | |||
#endif | |||
/* divide virtical and horizontal mouse move by this to convert to scroll move */ | |||
#ifndef PS2_MOUSE_SCROLL_DIVISOR_V | |||
#define PS2_MOUSE_SCROLL_DIVISOR_V 2 | |||
#endif | |||
#ifndef PS2_MOUSE_SCROLL_DIVISOR_H | |||
#define PS2_MOUSE_SCROLL_DIVISOR_H 2 | |||
#endif | |||
uint8_t ps2_mouse_init(void); | |||
uint8_t ps2_mouse_read(void); | |||
bool ps2_mouse_changed(void); | |||
void ps2_mouse_usb_send(void); | |||
void ps2_mouse_print(void); | |||
void ps2_mouse_task(void); | |||
#endif |
@@ -1,5 +1,5 @@ | |||
/* | |||
Copyright 2010,2011 Jun WAKO <[email protected]> | |||
Copyright 2010,2011,2012,2013 Jun WAKO <[email protected]> | |||
This software is licensed with a Modified BSD License. | |||
All of this is supposed to be Free Software, Open Source, DFSG-free, | |||
@@ -36,41 +36,15 @@ POSSIBILITY OF SUCH DAMAGE. | |||
*/ | |||
/* | |||
Primitive PS/2 Library for AVR | |||
============================== | |||
Host side is only supported now. | |||
Synchronous USART is used to receive data by hardware process | |||
rather than interrupt. During V-USB interrupt runs, CLOCK interrupt | |||
cannot interpose. In the result it is prone to lost CLOCK edge. | |||
* PS/2 protocol USART version | |||
*/ | |||
I/O control | |||
----------- | |||
High state is asserted by internal pull-up. | |||
If you have a signaling problem, you may need to have | |||
external pull-up resisters on CLOCK and DATA line. | |||
PS/2 References | |||
--------------- | |||
http://www.computer-engineering.org/ps2protocol/ | |||
http://www.mcamafia.de/pdf/ibm_hitrc07.pdf | |||
*/ | |||
#include <stdbool.h> | |||
#include <avr/io.h> | |||
#include <avr/interrupt.h> | |||
#include <util/delay.h> | |||
#include "ps2.h" | |||
#include "debug.h" | |||
#include "print.h" | |||
#if 0 | |||
#define DEBUGP_INIT() do { DDRC = 0xFF; } while (0) | |||
#define DEBUGP(x) do { PORTC = x; } while (0) | |||
#else | |||
#define DEBUGP_INIT() | |||
#define DEBUGP(x) | |||
#endif | |||
#define WAIT(stat, us, err) do { \ | |||
if (!wait_##stat(us)) { \ | |||
@@ -83,49 +57,38 @@ http://www.mcamafia.de/pdf/ibm_hitrc07.pdf | |||
uint8_t ps2_error = PS2_ERR_NONE; | |||
static inline void clock_lo(void); | |||
static inline void clock_hi(void); | |||
static inline bool clock_in(void); | |||
static inline void data_lo(void); | |||
static inline void data_hi(void); | |||
static inline bool data_in(void); | |||
static inline uint16_t wait_clock_lo(uint16_t us); | |||
static inline uint16_t wait_clock_hi(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 void idle(void); | |||
static inline void inhibit(void); | |||
static inline uint8_t pbuf_dequeue(void); | |||
static inline void pbuf_enqueue(uint8_t data); | |||
static inline bool pbuf_has_data(void); | |||
static inline void pbuf_clear(void); | |||
void ps2_host_init(void) | |||
{ | |||
DEBUGP_INIT(); | |||
DEBUGP(0x1); | |||
idle(); | |||
idle(); // without this many USART errors occur when cable is disconnected | |||
PS2_USART_INIT(); | |||
PS2_USART_RX_INT_ON(); | |||
// POR(150-2000ms) plus BAT(300-500ms) may take 2.5sec([3]p.20) | |||
//_delay_ms(2500); | |||
} | |||
uint8_t ps2_host_send(uint8_t data) | |||
{ | |||
uint8_t res = 0; | |||
bool parity = true; | |||
ps2_error = PS2_ERR_NONE; | |||
DEBUGP(0x6); | |||
PS2_USART_OFF(); | |||
/* terminate a transmission if we have */ | |||
inhibit(); | |||
_delay_us(100); | |||
_delay_us(100); // [4]p.13 | |||
/* start bit [1] */ | |||
/* 'Request to Send' and Start bit */ | |||
data_lo(); | |||
clock_hi(); | |||
WAIT(clock_lo, 15000, 1); | |||
/* data [2-9] */ | |||
WAIT(clock_lo, 10000, 10); // 10ms [5]p.50 | |||
/* Data bit[2-9] */ | |||
for (uint8_t i = 0; i < 8; i++) { | |||
_delay_us(15); | |||
if (data&(1<<i)) { | |||
@@ -137,15 +100,18 @@ uint8_t ps2_host_send(uint8_t data) | |||
WAIT(clock_hi, 50, 2); | |||
WAIT(clock_lo, 50, 3); | |||
} | |||
/* parity [10] */ | |||
/* Parity bit */ | |||
_delay_us(15); | |||
if (parity) { data_hi(); } else { data_lo(); } | |||
WAIT(clock_hi, 50, 4); | |||
WAIT(clock_lo, 50, 5); | |||
/* stop bit [11] */ | |||
/* Stop bit */ | |||
_delay_us(15); | |||
data_hi(); | |||
/* ack [12] */ | |||
/* Ack */ | |||
WAIT(data_lo, 50, 6); | |||
WAIT(clock_lo, 50, 7); | |||
@@ -153,134 +119,55 @@ uint8_t ps2_host_send(uint8_t data) | |||
WAIT(clock_hi, 50, 8); | |||
WAIT(data_hi, 50, 9); | |||
res = ps2_host_recv_response(); | |||
idle(); | |||
PS2_USART_INIT(); | |||
PS2_USART_RX_INT_ON(); | |||
return ps2_host_recv_response(); | |||
ERROR: | |||
idle(); | |||
PS2_USART_INIT(); | |||
PS2_USART_RX_INT_ON(); | |||
return res; | |||
return 0; | |||
} | |||
// Do polling data from keyboard to get response to last command. | |||
uint8_t ps2_host_recv_response(void) | |||
{ | |||
uint8_t data = 0; | |||
PS2_USART_INIT(); | |||
PS2_USART_RX_POLL_ON(); | |||
while (!PS2_USART_RX_READY) | |||
; | |||
data = PS2_USART_RX_DATA; | |||
PS2_USART_OFF(); | |||
DEBUGP(0x9); | |||
return data; | |||
// Command may take 25ms/20ms at most([5]p.46, [3]p.21) | |||
uint8_t retry = 25; | |||
while (retry-- && !pbuf_has_data()) { | |||
_delay_ms(1); | |||
} | |||
return pbuf_dequeue(); | |||
} | |||
uint8_t ps2_host_recv(void) | |||
{ | |||
return pbuf_dequeue(); | |||
if (pbuf_has_data()) { | |||
ps2_error = PS2_ERR_NONE; | |||
return pbuf_dequeue(); | |||
} else { | |||
ps2_error = PS2_ERR_NODATA; | |||
return 0; | |||
} | |||
} | |||
ISR(PS2_USART_RX_VECT) | |||
{ | |||
DEBUGP(0x7); | |||
uint8_t error = PS2_USART_ERROR; | |||
// TODO: request RESEND when error occurs? | |||
uint8_t error = PS2_USART_ERROR; // USART error should be read before data | |||
uint8_t data = PS2_USART_RX_DATA; | |||
if (error) { | |||
DEBUGP(error>>2); | |||
} else { | |||
if (!error) { | |||
pbuf_enqueue(data); | |||
} else { | |||
xprintf("PS2 USART error: %02X data: %02X\n", error, data); | |||
} | |||
DEBUGP(0x8); | |||
} | |||
/* send LED state to keyboard */ | |||
void ps2_host_set_led(uint8_t led) | |||
{ | |||
// send 0xED then keyboard keeps waiting for next LED data | |||
// and keyboard does not send any scan codes during waiting. | |||
// If fail to send LED data keyboard looks like being freezed. | |||
uint8_t retry = 3; | |||
while (retry-- && ps2_host_send(PS2_SET_LED) != PS2_ACK) | |||
; | |||
retry = 3; | |||
while (retry-- && ps2_host_send(led) != PS2_ACK) | |||
; | |||
} | |||
/*-------------------------------------------------------------------- | |||
* static functions | |||
*------------------------------------------------------------------*/ | |||
static inline void clock_lo() | |||
{ | |||
PS2_CLOCK_PORT &= ~(1<<PS2_CLOCK_BIT); | |||
PS2_CLOCK_DDR |= (1<<PS2_CLOCK_BIT); | |||
} | |||
static inline void clock_hi() | |||
{ | |||
/* input with pull up */ | |||
PS2_CLOCK_DDR &= ~(1<<PS2_CLOCK_BIT); | |||
PS2_CLOCK_PORT |= (1<<PS2_CLOCK_BIT); | |||
} | |||
static inline bool clock_in() | |||
{ | |||
PS2_CLOCK_DDR &= ~(1<<PS2_CLOCK_BIT); | |||
PS2_CLOCK_PORT |= (1<<PS2_CLOCK_BIT); | |||
_delay_us(1); | |||
return PS2_CLOCK_PIN&(1<<PS2_CLOCK_BIT); | |||
} | |||
static inline void data_lo() | |||
{ | |||
PS2_DATA_PORT &= ~(1<<PS2_DATA_BIT); | |||
PS2_DATA_DDR |= (1<<PS2_DATA_BIT); | |||
} | |||
static inline void data_hi() | |||
{ | |||
/* input with pull up */ | |||
PS2_DATA_DDR &= ~(1<<PS2_DATA_BIT); | |||
PS2_DATA_PORT |= (1<<PS2_DATA_BIT); | |||
} | |||
static inline bool data_in() | |||
{ | |||
PS2_DATA_DDR &= ~(1<<PS2_DATA_BIT); | |||
PS2_DATA_PORT |= (1<<PS2_DATA_BIT); | |||
_delay_us(1); | |||
return PS2_DATA_PIN&(1<<PS2_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 */ | |||
static inline void inhibit(void) | |||
{ | |||
clock_lo(); | |||
data_hi(); | |||
ps2_host_send(0xED); | |||
ps2_host_send(led); | |||
} | |||
@@ -293,9 +180,6 @@ static uint8_t pbuf_head = 0; | |||
static uint8_t pbuf_tail = 0; | |||
static inline void pbuf_enqueue(uint8_t data) | |||
{ | |||
if (!data) | |||
return; | |||
uint8_t sreg = SREG; | |||
cli(); | |||
uint8_t next = (pbuf_head + 1) % PBUF_SIZE; | |||
@@ -303,11 +187,10 @@ static inline void pbuf_enqueue(uint8_t data) | |||
pbuf[pbuf_head] = data; | |||
pbuf_head = next; | |||
} else { | |||
debug("pbuf: full\n"); | |||
print("pbuf: full\n"); | |||
} | |||
SREG = sreg; | |||
} | |||
static inline uint8_t pbuf_dequeue(void) | |||
{ | |||
uint8_t val = 0; | |||
@@ -322,3 +205,18 @@ static inline uint8_t pbuf_dequeue(void) | |||
return val; | |||
} | |||
static inline bool pbuf_has_data(void) | |||
{ | |||
uint8_t sreg = SREG; | |||
cli(); | |||
bool has_data = (pbuf_head != pbuf_tail); | |||
SREG = sreg; | |||
return has_data; | |||
} | |||
static inline void pbuf_clear(void) | |||
{ | |||
uint8_t sreg = SREG; | |||
cli(); | |||
pbuf_head = pbuf_tail = 0; | |||
SREG = sreg; | |||
} |