From 6b1009b7a876db1f0cc5674cfa64739139ea4098 Mon Sep 17 00:00:00 2001
From: Nikolaus Wittenstein <nikolaus.wittenstein@gmail.com>
Date: Tue, 22 Jan 2019 18:17:41 -0500
Subject: [PATCH] [Keyboard] Add DataHand keyboard support (#4847)

---
 keyboards/handwired/datahand/config.h         |  87 +++++
 keyboards/handwired/datahand/datahand.h       | 133 ++++++++
 .../datahand/keymaps/default/keymap.c         | 313 ++++++++++++++++++
 keyboards/handwired/datahand/matrix.c         | 125 +++++++
 keyboards/handwired/datahand/readme.md        |  15 +
 keyboards/handwired/datahand/rules.mk         |  59 ++++
 keyboards/readme.md                           |   1 +
 7 files changed, 733 insertions(+)
 create mode 100644 keyboards/handwired/datahand/config.h
 create mode 100644 keyboards/handwired/datahand/datahand.h
 create mode 100644 keyboards/handwired/datahand/keymaps/default/keymap.c
 create mode 100644 keyboards/handwired/datahand/matrix.c
 create mode 100644 keyboards/handwired/datahand/readme.md
 create mode 100644 keyboards/handwired/datahand/rules.mk

diff --git a/keyboards/handwired/datahand/config.h b/keyboards/handwired/datahand/config.h
new file mode 100644
index 0000000000..1ba8479f0a
--- /dev/null
+++ b/keyboards/handwired/datahand/config.h
@@ -0,0 +1,87 @@
+/* Copyright 2017-2019 Nikolaus Wittenstein <nikolaus.wittenstein@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+
+#include "config_common.h"
+
+/* USB Device descriptor parameter */
+/* Taken from the DataHand PS/2-USB adaptor. */
+#define VENDOR_ID       0x13BA
+#define PRODUCT_ID      0x0017
+#define DEVICE_VER      0x0001
+#define MANUFACTURER    DataHand
+#define PRODUCT         DataHand
+#define DESCRIPTION     DataHand Teensy++ 2.0 conversion
+
+/* key matrix size */
+#define MATRIX_ROWS 13
+#define MATRIX_COLS 4
+
+#define DIODE_DIRECTION CUSTOM_MATRIX
+
+/* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */
+#define DEBOUNCING_DELAY 0
+
+/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */
+#define LOCKING_SUPPORT_ENABLE
+/* Locking resynchronize hack */
+#define LOCKING_RESYNC_ENABLE
+
+/*
+ * Force NKRO
+ *
+ * Force NKRO (nKey Rollover) to be enabled by default, regardless of the saved
+ * state in the bootmagic EEPROM settings. (Note that NKRO must be enabled in the
+ * makefile for this to work.)
+ *
+ * If forced on, NKRO can be disabled via magic key (default = LShift+RShift+N)
+ * until the next keyboard reset.
+ *
+ * NKRO may prevent your keystrokes from being detected in the BIOS, but it is
+ * fully operational during normal computer usage.
+ *
+ * For a less heavy-handed approach, enable NKRO via magic key (LShift+RShift+N)
+ * or via bootmagic (hold SPACE+N while plugging in the keyboard). Once set by
+ * bootmagic, NKRO mode will always be enabled until it is toggled again during a
+ * power-up.
+ */
+#define FORCE_NKRO
+
+/*
+ * Magic Key Options
+ *
+ * Magic keys are hotkey commands that allow control over firmware functions of
+ * the keyboard. They are best used in combination with the HID Listen program,
+ * found here: https://www.pjrc.com/teensy/hid_listen.html
+ *
+ * The options below allow the magic key functionality to be changed. This is
+ * useful if your keyboard/keypad is missing keys and you want magic key support.
+ */
+
+/* key combination for magic key command */
+#define IS_COMMAND() ( \
+    keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) \
+)
+
+/*
+ * Command/Windows key option
+ *
+ * If you define this, the thumb enter key becomes the Command/Windows key. There's still an enter key on the right
+ * ring finger, so this key is much better utilized as the otherwise nonexistent Command key. I think some newer
+ * DataHands let you remap right ring east as Command, but having it on the thumb is nicer. Comment out this define
+ * to use the original layout.
+ */
+#define DATAHAND_THUMB_RETURN_COMMAND
diff --git a/keyboards/handwired/datahand/datahand.h b/keyboards/handwired/datahand/datahand.h
new file mode 100644
index 0000000000..cb9a4d3e3b
--- /dev/null
+++ b/keyboards/handwired/datahand/datahand.h
@@ -0,0 +1,133 @@
+/* Copyright 2017-2019 Nikolaus Wittenstein <nikolaus.wittenstein@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#pragma once
+
+#include "quantum.h"
+
+/* This a shortcut to help you visually see your layout.
+ * The first section contains all of the arguements; the second converts the arguments into a two-dimensional array.
+ */
+
+/* Each code is three letters
+ * l or r - left or right hand
+ * p, r, m, i, t - pinky, ring finger, middle finger, index finger, thumb
+ * fingers: n, s, e, w, c - north, south, east, west, and center (manual calls this "well" but we already have "west")
+ * thumb:   p, n, c, l, u, k - pad, nail, center, lock (harder center), up, knuckle
+ */
+#define LAYOUT( \
+     lpn,             lrn,             lmn,             lin,                rin,             rmn,             rrn,             rpn,      \
+lpw, lpc, lpe,   lrw, lrc, lre,   lmw, lmc, lme,   liw, lic, lie,      riw, ric, rie,   rmw, rmc, rme,   rrw, rrc, rre,   rpw, rpc, rpe, \
+     lps,             lrs,             lms,             lis,                ris,             rms,             rrs,             rps,      \
+                                                       ltp,    ltn,  rtn,    rtp, \
+                                                           ltc,          rtc, \
+                                                           ltl,          rtl, \
+                                                       ltu,    ltk,  rtk,    rtu) \
+{ \
+  {riw, rin, lpw, lpn},\
+  {ric, rie, lpc, lpe},\
+  {ris, rms, lps, lrs},\
+  {rmw, rmn, lrw, lrn},\
+  {rmc, rme, lrc, lre},\
+  {rrw, rrn, lmw, lmn},\
+  {rrc, rre, lmc, lme},\
+  {rrs, rps, lms, lis},\
+  {rpw, rpn, liw, lin},\
+  {rpc, rpe, lic, lie},\
+  {rtk, rtn, ltk, ltn},\
+  {rtc, rtl, ltc, ltl},\
+  {rtp, rtu, ltp, ltu},\
+}
+
+/* Mode LEDs are active-low on Port B on the Teensy. */
+#define LED_MODE_PORT PORTB
+#define LED_TENKEY    (1<<3)
+#define LED_FN        (1<<4)
+#define LED_NORMAL    (1<<5)
+#define LED_NAS       (1<<6)
+
+/* Lock LEDs are active-low on Port F on the Teensy. */
+#define LED_LOCK_PORT   PORTF
+#define LED_CAPS_LOCK   (1<<4)
+#define LED_MOUSE_LOCK  (1<<5)
+#define LED_NUM_LOCK    (1<<6)
+#define LED_SCROLL_LOCK (1<<7)
+
+
+/* Appendix:
+ * Table based on https://geekhack.org/index.php?topic=12212.msg2059319#msg2059319
+ * Some pin assignments (e.g. for PS/2 I/O) have been fixed.
+ *
+ * Teensy    Datahand     8051   pin     pin      8051    Datahand             Teensy
+ * ------    --------     ----   ---     ---      ----    --------             ------
+ * GND       Mtrx send A  P1.0     1      40      VCC     VCC                  VCC
+ * PB7       Mtrx send B  P1.1     2      39      P0.0    LED RH NAS           PB6
+ * PD0       Mtrx send C  P1.2     3      38      P0.1    LED RH NORM          PB5
+ * PD1       Mtrx send D  P1.3     4      37      P0.2    LED RH FCTN          PB4
+ * PD2       RH rcv 0     P1.4     5      36      P0.3    LED RH 10K           PB3
+ * PD3       RH rcv 1     P1.5     6      35      P0.4    LED RH unused        PB2
+ * PD4       LH rcv 0     P1.6     7      34      P0.5    LED RH unused        PE1
+ * PD5       LH rcv 1     P1.7     8      33      P0.6    LED RH unused        PE0
+ * PD6       Reset button RST      9      32      P0.7    ?                    PE7
+ * PD7       ?            P3.0    10      31      VPP     -                    PE6
+ * PE0       ?            P3.1    11      30      ALE     -                    GND
+ * PE1       kbd data     P3.2    12      29      PSEN    -                    AREF
+ * PC0       ?            P3.3    13      28      P2.7    ?                    PF0
+ * PC1       kbd clk      P3.4    14      27      P2.6    ?                    PF1
+ * PC2       ?            P3.5    15      26      P2.5    ?                    PF2
+ * PC3       RAM          P3.6    16      25      P2.4    ?                    PF3
+ * PC4       RAM          P3.7    17      24      P2.3    LED D15 LH (CAPLK)   PF4
+ * PC5       XTAL2        XTAL2   18      23      P2.2    LED D13 LH (MSELK)   PF5
+ * PC6       XTAL1        XTAL1   19      22      P2.1    LED D6  LH (NUMLK)   PF6
+ * PC7       GND          GND     20      21      P2.0    LED D14 LH (SCRLK)   PF7
+ *
+ * JP3 Pinout
+ * 2 - keyboard data
+ * 3 - keyboard clock
+ *
+ * In order to get the Teensy to work, we need to move pin 1 to a different pin. This is
+ * because on the Teensy pin 1 is ground, but we need to write to pin 1 in order to read
+ * the keyboard matrix. An ideal pin to move it to is VPP (pin 31), because this pin tells
+ * the 8051 whether it should read from external or internal memory. The Teensy doesn't
+ * care about that.
+ *
+ * The easiest way to reassign the pin is to use standoffs. You can check out this thread:
+ * https://geekhack.org/index.php?topic=12212.msg235382#msg235382 for a picture of what
+ * this looks like. Note that in the picture the pin has been reassigned to pin 12. We
+ * don't want to do that because we're going to use that pin to send data over PS/2.
+ *
+ * We could if we wanted also reassign the PS/2 pins to Teensy hardware UART pins, but
+ * that's more work. Instead we'll just bit-bang PS/2 because it's an old, slow protocol
+ * (and because there's already a bit-banged PS/2 host implementation in QMK - we just
+ * need to add the device side).
+ *
+ * So overall, we want the following inputs and outputs:
+ * Outputs:
+ *   Matrix:
+ *     PB7
+ *     PD0
+ *     PD1
+ *     PE6 (moved from pin1, GND)
+ *   LEDs:
+ *     PB3-6
+ *     PF4-7
+ * Inputs:
+ *   Matrix:
+ *     PD2-5
+ * I/Os (start up as inputs):
+ *   PS/2:
+ *     PC1
+ *     PE1
+ */
diff --git a/keyboards/handwired/datahand/keymaps/default/keymap.c b/keyboards/handwired/datahand/keymaps/default/keymap.c
new file mode 100644
index 0000000000..8f44535594
--- /dev/null
+++ b/keyboards/handwired/datahand/keymaps/default/keymap.c
@@ -0,0 +1,313 @@
+/* Copyright 2017-2019 Nikolaus Wittenstein <nikolaus.wittenstein@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include QMK_KEYBOARD_H
+
+#include <stdbool.h>
+
+/* Datahand features not supported:
+ * * All online reprogramming (user settings using the reset button).
+ *   * Program Selection features.
+ *   * Macros.
+ *   * Direct substitutions.
+ * * L/R Modf.
+ * * Mouse Click Lock (Function Direct Access + Mouse Button key).
+ * * Different mouse movement speeds with the two pointer fingers, and using both pointer fingers to move even faster.
+ *
+ * As far as I know, everything else works.
+ */
+
+enum layer {
+  NORMAL,
+#ifdef DATAHAND_THUMB_RETURN_COMMAND
+  NORMAL_THUMB_RETURN_COMMAND,
+#endif
+  FUNCTION_MOUSE,
+  FUNCTION_ARROWS,
+  NAS,
+  NAS_NUMLOCK,
+  NAS_TENKEY,
+  NAS_TENKEY_NUMLOCK,
+
+  NUM_LAYERS
+};
+
+enum custom_keycodes {
+  N = SAFE_RANGE,   /* Normal */
+  NS,               /* NAS */
+  NSL,              /* NAS Lock */
+  NLK,              /* Numlock */
+  FN,               /* Function mode - needs to be able to switch to mouse or arrow layer */
+  TK0,              /* Ten-key off button */
+  TK1,              /* Ten-key on button */
+  AR,               /* FN arrow mode */
+  MS,               /* FN mouse mode */
+  DZ,               /* Double zero button */
+};
+
+static bool mouse_enabled = true;
+static bool tenkey_enabled = false;
+static bool numlock_enabled = false;
+static bool nas_locked = false;
+
+/* Declared weak so that it can easily be overridden. */
+__attribute__((weak)) const uint16_t PROGMEM keymaps[NUM_LAYERS][MATRIX_ROWS][MATRIX_COLS] = {
+[NORMAL] = LAYOUT(
+        KC_Q,                     KC_W,                 KC_E,                 KC_R,                       KC_U,                   KC_I,                     KC_O,                    KC_P,
+KC_DEL, KC_A, KC_LBRC,    KC_ESC, KC_S, KC_B,   KC_GRV, KC_D, KC_T,   KC_DQT, KC_F, KC_G,           KC_H, KC_J, KC_QUOT,   KC_Y,  KC_K,   KC_COLN,   KC_N,  KC_L, KC_ENT,   KC_RBRC, KC_SCLN, KC_BSLS,
+        KC_Z,                     KC_X,                 KC_C,                 KC_V,                       KC_M,                   KC_COMM,                  KC_DOT,                  KC_SLSH,
+                                                                          KC_ENT,    KC_TAB,   KC_BSPC,  KC_SPC,
+                                                                              KC_LSFT,              NS,
+                                                                              KC_CAPS,              NSL,
+                                                                          N,         KC_LCTL,  KC_LALT,  FN),
+
+#ifdef DATAHAND_THUMB_RETURN_COMMAND
+[NORMAL_THUMB_RETURN_COMMAND] = LAYOUT(
+         _______,                    _______,                    _______,                    _______,                       _______,                    _______,                    _______,                    _______,
+_______, _______, _______,  _______, _______, _______,  _______, _______, _______,  _______, _______, _______,     _______, _______, _______,  _______, _______, _______,  _______, _______, _______,  _______, _______, _______,
+         _______,                    _______,                    _______,                    _______,                       _______,                    _______,                    _______,                    _______,
+                                                                                            KC_LCMD,   _______,   _______,   _______,
+                                                                                                 _______,              _______,
+                                                                                                 _______,              _______,
+                                                                                             _______,   _______,   _______,   _______),
+#endif
+
+[FUNCTION_MOUSE] = LAYOUT(
+         KC_F2,                     KC_F4,                  KC_F6,                   KC_MS_U,                      KC_MS_U,                    KC_F8,                   KC_F10,                  KC_PGUP,
+_______, KC_NO, KC_SLCK,   _______, KC_BTN3, NLK,  KC_BTN1, MS, KC_BTN2,    KC_MS_L, KC_BTN1, KC_MS_R,    KC_MS_L, KC_BTN2, KC_MS_R,   KC_END, AR,  KC_LSFT,    KC_INS, KC_9,  KC_ENT,   KC_F11, KC_0,   KC_F12,
+         KC_F1,                     KC_F3,                  KC_F5,                   KC_MS_D,                      KC_MS_D,                    KC_F7,                   KC_F9,                   KC_PGDN,
+                                                                                             _______,   _______,   _______,   _______,
+                                                                                                  _______,              _______,
+                                                                                                  _______,              _______,
+                                                                                             _______,   _______,   _______,   _______),
+[FUNCTION_ARROWS] = LAYOUT(
+         _______,                     _______,                     _______,                     KC_UP,                              KC_UP,                       _______,                     _______,                     _______,
+_______, _______, _______,   _______, KC_LCTL, _______,   _______, _______, _______,   KC_LEFT, KC_HOME, KC_RGHT,          KC_LEFT, KC_HOME, KC_RGHT,   _______, _______, _______,   _______, _______, _______,   _______, _______, _______,
+         _______,                     _______,                     _______,                     KC_DOWN,                            KC_DOWN,                     _______,                     _______,                     _______,
+                                                                                             _______,   _______,   _______,   _______,
+                                                                                                  _______,              _______,
+                                                                                                  _______,              _______,
+                                                                                             _______,   _______,   _______,   _______),
+[NAS] = LAYOUT(
+         KC_EXLM,                    KC_AT,                KC_HASH,                    KC_DLR,                      KC_AMPR,                    KC_ASTR,                    KC_LPRN,                KC_RPRN,
+_______, KC_1,   KC_TILD,   _______, KC_2, NLK,   KC_LABK, KC_3,   KC_RABK,   KC_SLSH, KC_4,   KC_5,          KC_6, KC_7,   KC_UNDS,   KC_CIRC, KC_8,   KC_ENT,   KC_SCLN,  KC_9,   KC_BSLS,   TK0, KC_0,   TK1,
+         KC_EQL,                     KC_X,                 KC_PERC,                    KC_MINS,                     KC_PLUS,                    KC_DOT,                     KC_SLSH,                KC_QUES,
+                                                                                             _______,   _______,   _______,   _______,
+                                                                                                  _______,              _______,
+                                                                                                  _______,              _______,
+                                                                                             _______,   _______,   _______,   _______),
+[NAS_NUMLOCK] = LAYOUT(
+         _______,                     _______,                     _______,                     _______,                            _______,                     KC_PAST,                     _______,                     _______,
+_______, KC_KP_1, _______,   _______, KC_KP_2, _______,   _______, KC_KP_3, _______,   KC_PSLS, KC_KP_4, KC_KP_5,          KC_KP_6, KC_KP_7, _______,   _______, KC_KP_8, _______,   _______, KC_KP_9, KC_PENT,   _______, KC_KP_0, _______,
+         KC_PEQL,                     _______,                     _______,                     KC_PMNS,                            KC_PPLS,                     _______,                     KC_PDOT,                     _______,
+                                                                                             _______,   _______,   _______,   _______,
+                                                                                                  _______,              _______,
+                                                                                                  _______,              _______,
+                                                                                             _______,   _______,   _______,   _______),
+[NAS_TENKEY] = LAYOUT(
+         _______,                     _______,                   _______,                    KC_UP,                       KC_7,               KC_8,                   KC_9,                   KC_ASTR,
+_______, KC_QUOT, _______,   _______, KC_DLR, _______,  _______, KC_AMPR, _______,  KC_LEFT, KC_HOME, KC_RGHT,      KC_0, KC_4, DZ,  KC_PLUS, KC_5, KC_MINS,  KC_EQL, KC_6, KC_ENT,  _______, KC_DOT, _______,
+         KC_LPRN,                     KC_RPRN,                   _______,                    KC_DOWN,                     KC_1,               KC_2,                   KC_3,                   KC_SLSH,
+                                                                                             _______,   _______,   _______,   _______,
+                                                                                                  _______,              _______,
+                                                                                                  _______,              _______,
+                                                                                             _______,   _______,   _______,   _______),
+[NAS_TENKEY_NUMLOCK] = LAYOUT(
+         _______,                    _______,                    _______,                    _______,                       KC_KP_7,                    KC_KP_8,                    KC_KP_9,                    KC_PAST,
+_______, _______, _______,  _______, _______, _______,  _______, _______, _______,  _______, _______, _______,     KC_KP_0, KC_KP_4, _______,  KC_PPLS, KC_KP_5, KC_PMNS,  KC_PEQL, KC_KP_6, KC_PENT,  _______, KC_PDOT, _______,
+         _______,                    _______,                    _______,                    _______,                       KC_KP_1,                    KC_KP_2,                    KC_KP_3,                    KC_PSLS,
+                                                                                             _______,   _______,   _______,   _______,
+                                                                                                  _______,              _______,
+                                                                                                  _______,              _______,
+                                                                                             _______,   _______,   _______,   _______),
+};
+
+static void lock_led_set(bool on, uint8_t led) {
+  if (on) {
+    LED_LOCK_PORT &= ~led;
+  } else {
+    LED_LOCK_PORT |= led;
+  }
+}
+
+static void mode_led_set(uint8_t led) {
+  static const uint8_t ALL_MODE_LEDS = LED_FN | LED_NORMAL | LED_NAS | LED_TENKEY;
+  LED_MODE_PORT |= ALL_MODE_LEDS;
+  LED_MODE_PORT &= ~led;
+}
+
+static void layer_set(bool on, uint8_t layer) {
+  if (on) {
+    layer_on(layer);
+  } else {
+    layer_off(layer);
+  }
+
+  if (layer_state_is(NAS) || layer_state_is(NAS_NUMLOCK) || layer_state_is(NAS_TENKEY) || layer_state_is(NAS_TENKEY_NUMLOCK)) {
+    if (tenkey_enabled) {
+      mode_led_set(LED_NAS | LED_TENKEY);
+    } else {
+      mode_led_set(LED_NAS);
+    }
+  } else if (layer_state_is(FUNCTION_MOUSE) || layer_state_is(FUNCTION_ARROWS)) {
+    mode_led_set(LED_FN);
+  } else if (layer_state_is(NORMAL)) {
+    mode_led_set(LED_NORMAL);
+  }
+}
+
+static void set_normal(void) {
+  layer_move(NORMAL);
+
+#ifdef DATAHAND_THUMB_RETURN_COMMAND
+  layer_set(true, NORMAL_THUMB_RETURN_COMMAND);
+#endif
+
+  /* Then call layer_set to update LEDs. */
+  layer_set(true, NORMAL);
+}
+
+static void set_nas(bool on) {
+  /* Always turn on the base NAS layer so other layers can fall through. */
+  layer_set(on, NAS);
+
+  layer_set(on && numlock_enabled, NAS_NUMLOCK);
+  layer_set(on && tenkey_enabled, NAS_TENKEY);
+  layer_set(on && tenkey_enabled && numlock_enabled, NAS_TENKEY_NUMLOCK);
+}
+
+static void set_tenkey(bool on) {
+  tenkey_enabled = on;
+  
+  /* We have to be on the NAS layer in order to be able to toggle TK.
+   * Re-toggle it on so that we move to the right layer (and set the right LED).
+   */
+  set_nas(true);
+}
+
+static void toggle_numlock(void) {
+  numlock_enabled = !numlock_enabled;
+  lock_led_set(numlock_enabled, LED_NUM_LOCK);
+  
+  if (layer_state_is(NAS)) {
+    /* If we're already in NAS, re-set it so that we activate the numlock layer. */
+    set_nas(true);
+  }
+}
+
+static void set_function(void) {
+  /* Make sure to turn off NAS if we're entering function */
+  set_nas(false);
+
+  /* Always turn on the mouse layer so the arrow layer can fall through. */
+  layer_set(true, FUNCTION_MOUSE);
+  layer_set(!mouse_enabled, FUNCTION_ARROWS);
+}
+
+static void set_mouse_enabled(bool on) {
+  mouse_enabled = on;
+
+  /* Re-run set_function to set our layers correctly. */
+  set_function();
+}
+
+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+  bool pressed = record->event.pressed;
+  
+  switch(keycode) {
+    case N:
+    if (pressed) {
+      set_normal();
+    }
+    break;
+
+    case NS:
+    if (pressed) {
+      nas_locked = false;
+    }
+    set_nas(pressed);
+    break;
+    
+    case NSL:
+    if (pressed) {
+      nas_locked = true;
+      set_nas(true);
+    }
+    break;
+    
+    case NLK:
+    if (pressed) {
+      toggle_numlock();
+      SEND_STRING(SS_DOWN(X_NUMLOCK));
+    } else {
+      SEND_STRING(SS_UP(X_NUMLOCK));
+    }
+    break;
+    
+    case FN:
+    if (pressed) {
+      set_function();
+    }
+    break;
+    
+    case TK0:
+    if (pressed) {
+      set_tenkey(false);
+    }
+    break;
+    
+    case TK1:
+    if (pressed) {
+      set_tenkey(true);
+    }
+    break;
+
+    case MS:
+    if (pressed) {
+      set_mouse_enabled(true);
+    }
+    break;
+
+    case AR:
+    if (pressed) {
+      set_mouse_enabled(false);
+    }
+    break;
+    
+    case DZ:
+    if (pressed) {
+      SEND_STRING(SS_TAP(X_KP_0) SS_TAP(X_KP_0));
+    }
+    break;
+  }
+
+  return true;
+};
+
+void matrix_init_user(void) {
+#ifdef DATAHAND_THUMB_RETURN_COMMAND
+  set_normal();
+#endif
+}
+
+void matrix_scan_user(void) {
+
+}
+
+void led_set_user(uint8_t usb_led) {
+  lock_led_set(usb_led & (1<<USB_LED_NUM_LOCK), LED_NUM_LOCK);
+  lock_led_set(usb_led & (1<<USB_LED_CAPS_LOCK), LED_CAPS_LOCK);
+  lock_led_set(usb_led & (1<<USB_LED_SCROLL_LOCK), LED_SCROLL_LOCK);
+}
diff --git a/keyboards/handwired/datahand/matrix.c b/keyboards/handwired/datahand/matrix.c
new file mode 100644
index 0000000000..a08450d779
--- /dev/null
+++ b/keyboards/handwired/datahand/matrix.c
@@ -0,0 +1,125 @@
+/* Copyright 2017-2019 Nikolaus Wittenstein <nikolaus.wittenstein@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "datahand.h"
+
+#include "matrix.h"
+#include "action.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <avr/io.h>
+
+static matrix_row_t matrix[MATRIX_ROWS];
+
+static matrix_row_t read_cols(void);
+static void select_row(uint8_t row);
+
+void matrix_init(void) {
+  /* See datahand.h for more detail on pins. */
+
+  /* 7 - matrix scan; 6-3 - mode LEDs */
+  DDRB = 0b11111000;
+
+  /* 1-0 - matrix scan */
+  DDRD = 0b00000011;
+
+  /* 6 - matrix scan */
+  DDRE = 0b01000000;
+
+  /* 7-4 - lock LEDs */
+  DDRF = 0b11110000;
+
+  /* Turn off the non-Normal LEDs (they're active low). */
+  PORTB |= LED_TENKEY | LED_FN | LED_NAS;
+
+  /* Turn off the lock LEDs. */
+  PORTF |= LED_CAPS_LOCK | LED_NUM_LOCK | LED_SCROLL_LOCK | LED_MOUSE_LOCK;
+
+  matrix_init_user();
+}
+
+uint8_t matrix_scan(void) {
+  for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
+    select_row(row);
+    /* The default hardware works down to at least 100us, but I have a replacement
+     * photodiode that responds a little more slowly. Cranking it up to 1000us fixes
+     * shadowing issues.
+     */
+    _delay_us(1000);
+    matrix[row] = read_cols();
+  }
+
+  matrix_scan_user();
+
+  return 1;
+}
+
+matrix_row_t matrix_get_row(uint8_t row) {
+  return matrix[row];
+}
+
+void matrix_print(void) {
+  print("\nr/c 01234567\n");
+
+  for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
+    phex(row);
+    print(": ");
+    print_bin_reverse8(matrix_get_row(row));
+    print("\n");
+  }
+}
+
+bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
+  return process_record_user(keycode, record);
+}
+
+static void select_row(uint8_t row) {
+  /* Original 8051: P1 bits 0-3 (pins 1-4)
+   * Teensy++: PE0, PB7, PD0, PD1
+   */
+
+  if (row & (1<<0)) {
+    PORTE |= (1<<6);
+  } else {
+    PORTE &= ~(1<<6);
+  }
+
+  if (row & (1<<1)) {
+    PORTB |= (1<<7);
+  } else {
+    PORTB &= ~(1<<7);
+  }
+
+  if (row & (1<<2)) {
+    PORTD |= (1<<0);
+  } else {
+    PORTD &= ~(1<<0);
+  }
+
+  if (row & (1<<3)) {
+    PORTD |= (1<<1);
+  } else {
+    PORTD &= ~(1<<1);
+  }
+}
+
+static matrix_row_t read_cols(void) {
+  /* Original 8051: P1 bits 4-7 (pins 5-8)
+   * Teensy++: PD bits 2-5
+   */
+
+  return (PIND & 0b00111100) >> 2;
+}
diff --git a/keyboards/handwired/datahand/readme.md b/keyboards/handwired/datahand/readme.md
new file mode 100644
index 0000000000..923cebb00e
--- /dev/null
+++ b/keyboards/handwired/datahand/readme.md
@@ -0,0 +1,15 @@
+# DataHand
+
+A keyboard designed to prevent RSI. See [Wikipedia](https://en.wikipedia.org/wiki/DataHand) and [this website](http://octopup.org/computer/datahand) for more info.
+
+To use this firmware, you have to replace the stock microcontroller with a Teensy++ 2.0. This is relatively easy and also reversible. See the notes at the bottom of datahand.h for more info.
+
+Keyboard Maintainer: [Nikolaus Wittenstein](https://github.com/adzenith)  
+Hardware Supported: DataHand Personal or Pro II  
+Hardware Availability: No longer in production
+
+Make example for this keyboard (after setting up your build environment):
+
+    make handwired/datahand:default
+
+See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
diff --git a/keyboards/handwired/datahand/rules.mk b/keyboards/handwired/datahand/rules.mk
new file mode 100644
index 0000000000..59f14e17a4
--- /dev/null
+++ b/keyboards/handwired/datahand/rules.mk
@@ -0,0 +1,59 @@
+# Project-specific includes
+SRC = matrix.c
+
+# MCU name
+MCU = at90usb1286
+
+# Processor frequency.
+#     This will define a symbol, F_CPU, in all source code files equal to the
+#     processor frequency in Hz. You can then use this symbol in your source code to
+#     calculate timings. Do NOT tack on a 'UL' at the end, this will be done
+#     automatically to create a 32-bit value in your source code.
+#
+#     This will be an integer division of F_USB below, as it is sourced by
+#     F_USB after it has run through any CPU prescalers. Note that this value
+#     does not *change* the processor frequency - it should merely be updated to
+#     reflect the processor speed set externally so that the code can use accurate
+#     software delays.
+F_CPU = 16000000
+
+#
+# LUFA specific
+#
+# Target architecture (see library "Board Types" documentation).
+ARCH = AVR8
+
+# Input clock frequency.
+#     This will define a symbol, F_USB, in all source code files equal to the
+#     input clock frequency (before any prescaling is performed) in Hz. This value may
+#     differ from F_CPU if prescaling is used on the latter, and is required as the
+#     raw input clock is fed directly to the PLL sections of the AVR for high speed
+#     clock generation for the USB and other AVR subsections. Do NOT tack on a 'UL'
+#     at the end, this will be done automatically to create a 32-bit value in your
+#     source code.
+#
+#     If no clock division is performed on the input clock inside the AVR (via the
+#     CPU clock adjust registers or the clock division fuses), this will be equal to F_CPU.
+F_USB = $(F_CPU)
+
+# Interrupt driven control endpoint task(+60)
+OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT
+
+BOOTLOADER = halfkay
+
+# Build Options
+BOOTMAGIC_ENABLE  = no   # Virtual DIP switch configuration(+1000)
+MOUSEKEY_ENABLE   = yes  # Mouse keys(+4700)
+EXTRAKEY_ENABLE   = yes  # Audio control and System control(+450)
+CONSOLE_ENABLE    = yes  # Console for debug(+400)
+COMMAND_ENABLE    = yes  # Commands for debug and configuration
+NKRO_ENABLE       = yes  # USB Nkey Rollover
+BACKLIGHT_ENABLE  = no   # Enable keyboard backlight functionality on B7 by default
+MIDI_ENABLE       = no   # MIDI controls
+UNICODE_ENABLE    = no   # Unicode
+BLUETOOTH_ENABLE  = no   # Enable Bluetooth with the Adafruit EZ-Key HID
+AUDIO_ENABLE      = no   # Audio output on port C6
+CUSTOM_MATRIX     = yes  # We definitely have a nonstandard matrix
+
+# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
+SLEEP_LED_ENABLE = no       # Breathing sleep LED during USB suspend
diff --git a/keyboards/readme.md b/keyboards/readme.md
index 5ba98c6c45..8b0c10ab48 100644
--- a/keyboards/readme.md
+++ b/keyboards/readme.md
@@ -38,6 +38,7 @@ These keyboards are part of the QMK repository, but their manufacturers are not
 * [Arrow Pad](/keyboards/arrow_pad) &mdash; A custom creation by IBNobody.
 * [Atreus](/keyboards/atreus) &mdash; Made by Technomancy.
 * [Bantam44](/keyboards/bantam44) &mdash; It is a 44-key 40% staggered keyboard.
+* [DataHand](/keyboards/handwired/datahand) &mdash; DataHand keyboard converted to use a Teensy board.
 * [Ergodox Infinity](/keyboards/ergodox_infinity) - Ergonomic Split Keyboard by Input Club.
 * [GH60](/keyboards/gh60) &mdash; A 60% Geekhack community-driven project.
 * [GON NerD](/keyboards/gonnerd) &mdash; Korean custom 60% PCB