diff --git a/common.mk b/common.mk index 350558f8..c8809c0c 100644 --- a/common.mk +++ b/common.mk @@ -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 diff --git a/common/action.c b/common/action.c index f7ae85b9..e6938f5a 100644 --- a/common/action.c +++ b/common/action.c @@ -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); diff --git a/common/action.h b/common/action.h index d57f4a86..077711c2 100644 --- a/common/action.h +++ b/common/action.h @@ -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); diff --git a/common/action_code.h b/common/action_code.h index c153838f..8df86b11 100644 --- a/common/action_code.h +++ b/common/action_code.h @@ -34,6 +34,7 @@ along with this program. If not, see . * * 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) /* diff --git a/common/action_util.h b/common/action_util.h index 939bc2b6..f9d3161a 100644 --- a/common/action_util.h +++ b/common/action_util.h @@ -18,6 +18,7 @@ along with this program. If not, see . #define ACTION_UTIL_H #include +#include "report.h" extern report_keyboard_t *keyboard_report; diff --git a/common/host.c b/common/host.c index 0703dba0..1eafef75 100644 --- a/common/host.c +++ b/common/host.c @@ -27,9 +27,6 @@ along with this program. If not, see . 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; diff --git a/common/host.h b/common/host.h index c1a0fbac..8ff26298 100644 --- a/common/host.h +++ b/common/host.h @@ -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); diff --git a/common/keyboard.c b/common/keyboard.c index 4ad92eb6..48a67e4a 100644 --- a/common/keyboard.c +++ b/common/keyboard.c @@ -30,9 +30,14 @@ along with this program. If not, see . #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(); diff --git a/common/mousekey.c b/common/mousekey.c index 3068fc5e..017be941 100644 --- a/common/mousekey.c +++ b/common/mousekey.c @@ -26,6 +26,7 @@ along with this program. If not, see . +static report_mouse_t mouse_report = {}; static uint8_t mousekey_repeat = 0; static uint8_t mousekey_accel = 0; diff --git a/doc/keymap.md b/doc/keymap.md index 11e80a9c..3a549de6 100644 --- a/doc/keymap.md +++ b/doc/keymap.md @@ -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 diff --git a/protocol.mk b/protocol.mk index 1442c520..7f561e62 100644 --- a/protocol.mk +++ b/protocol.mk @@ -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 diff --git a/protocol/bluefruit.mk b/protocol/bluefruit.mk new file mode 100644 index 00000000..7e6328f6 --- /dev/null +++ b/protocol/bluefruit.mk @@ -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 diff --git a/protocol/bluefruit/bluefruit.c b/protocol/bluefruit/bluefruit.c new file mode 100644 index 00000000..f991e4d0 --- /dev/null +++ b/protocol/bluefruit/bluefruit.c @@ -0,0 +1,202 @@ +/* +Bluefruit Protocol for TMK firmware +Author: Benjamin Gould, 2013 +Based on code Copyright 2011 Jun Wako + +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 . + +*/ + +#include +#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 +} + diff --git a/protocol/bluefruit/bluefruit.h b/protocol/bluefruit/bluefruit.h new file mode 100644 index 00000000..4f9b5883 --- /dev/null +++ b/protocol/bluefruit/bluefruit.h @@ -0,0 +1,28 @@ +/* +Bluefruit Protocol for TMK firmware +Author: Benjamin Gould, 2013 +Based on code Copyright 2011 Jun Wako + +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 . +*/ + +#ifndef VUSB_H +#define VUSB_H + +#include "host_driver.h" + + +host_driver_t *bluefruit_driver(void); + +#endif diff --git a/protocol/bluefruit/main.c b/protocol/bluefruit/main.c new file mode 100644 index 00000000..871062ab --- /dev/null +++ b/protocol/bluefruit/main.c @@ -0,0 +1,116 @@ +/* +Bluefruit Protocol for TMK firmware +Author: Benjamin Gould, 2013 +Based on code Copyright 2011 Jun Wako + +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 . +*/ + +#include +#include +#include +#include +#include +#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(); + } + } + +} diff --git a/protocol/lufa/lufa.c b/protocol/lufa/lufa.c index a230d5ba..eca51c87 100644 --- a/protocol/lufa/lufa.c +++ b/protocol/lufa/lufa.c @@ -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 diff --git a/protocol/pjrc.mk b/protocol/pjrc.mk index 27f908b1..5a446138 100644 --- a/protocol/pjrc.mk +++ b/protocol/pjrc.mk @@ -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 diff --git a/protocol/pjrc/usb.c b/protocol/pjrc/usb.c index 84c99972..393b36f7 100644 --- a/protocol/pjrc/usb.c +++ b/protocol/pjrc/usb.c @@ -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< +Copyright 2010,2011,2012,2013 Jun WAKO This software is licensed with a Modified BSD License. All of this is supposed to be Free Software, Open Source, DFSG-free, @@ -37,12 +37,62 @@ POSSIBILITY OF SUCH DAMAGE. #ifndef PS2_H #define PS2_H + +#include +#include +#include + /* * 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 */ +#define PS2_ACK 0xFA +#define PS2_RESEND 0xFE +#define PS2_SET_LED 0xED + +// 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 +#define PS2_LED_CAPS_LOCK 2 -/* port settings for clock and data line */ +extern uint8_t ps2_error; + +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); + + +/* Check port settings for clock and data line */ #if !(defined(PS2_CLOCK_PORT) && \ defined(PS2_CLOCK_PIN) && \ defined(PS2_CLOCK_DDR) && \ @@ -57,27 +107,79 @@ POSSIBILITY OF SUCH DAMAGE. # 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 +/*-------------------------------------------------------------------- + * static functions + *------------------------------------------------------------------*/ +static inline void clock_lo(void) +{ + PS2_CLOCK_PORT &= ~(1< + +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 +#include +#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< 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); +} diff --git a/protocol/ps2.c b/protocol/ps2_interrupt.c similarity index 53% rename from protocol/ps2.c rename to protocol/ps2_interrupt.c index ed456091..259d2540 100644 --- a/protocol/ps2.c +++ b/protocol/ps2_interrupt.c @@ -1,5 +1,5 @@ /* -Copyright 2010,2011 Jun WAKO +Copyright 2010,2011,2012,2013 Jun WAKO 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 -#include #include #include #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< +Copyright 2011,2013 Jun Wako 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 . #include #include "ps2.h" #include "ps2_mouse.h" -#include "usb_mouse.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) +#include "report.h" +#include "host.h" +#include "timer.h" +#include "print.h" +#include "debug.h" -/* -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 report_mouse_t mouse_report = {}; +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 + _delay_ms(1000); // wait for powering up + + // send Reset rcv = ps2_host_send(0xFF); - print("ps2_mouse_init: send 0xFF: "); - phex(ps2_error); print("\n"); - ERROR_RETURN(); - - // ACK - rcv = ps2_host_recv(); - print("ps2_mouse_init: read ACK: "); + 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< USB(-127/127) - if (ps2_mouse_btn & (1< 128 ? (int8_t)ps2_mouse_x : -127; - else - x = ps2_mouse_x < 128 ? (int8_t)ps2_mouse_x : 127; + // remove sign and overflow flags + mouse_report.buttons &= PS2_MOUSE_BTN_MASK; - if (ps2_mouse_btn & (1< 128 ? (int8_t)ps2_mouse_y : -127; - else - y = ps2_mouse_y < 128 ? (int8_t)ps2_mouse_y : 127; + // invert coordinate of y to conform to USB HID mouse + mouse_report.y = -mouse_report.y; - // 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); - _delay_ms(100); +#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; } - } 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); - } - ps2_mouse_btn_prev = (ps2_mouse_btn & PS2_MOUSE_BTN_MASK); - ps2_mouse_print(); + // doesn't send Scroll Button + //mouse_report.buttons &= ~(PS2_MOUSE_SCROLL_BTN_MASK); + + if (mouse_report.x || mouse_report.y) { + scroll_state = SCROLL_SENT; + + 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); + } +#endif + scroll_state = SCROLL_NONE; + } + // doesn't send Scroll Button + mouse_report.buttons &= ~(PS2_MOUSE_SCROLL_BTN_MASK); +#endif + + + 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 + */ diff --git a/protocol/ps2_mouse.h b/protocol/ps2_mouse.h index 4529ce11..27d9790d 100644 --- a/protocol/ps2_mouse.h +++ b/protocol/ps2_mouse.h @@ -20,6 +20,16 @@ along with this program. If not, see . #include +#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 . #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< +Copyright 2010,2011,2012,2013 Jun WAKO This software is licensed with a Modified BSD License. All of this is supposed to be Free Software, Open Source, DFSG-free, @@ -36,42 +36,16 @@ 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 -#include #include #include #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)) { \ ps2_error = err; \ @@ -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<>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<