]> git.donarmstrong.com Git - qmk_firmware.git/blob - protocol/next_kbd.c
Squashed 'tmk_core/' changes from dc0e46e..57d27a8
[qmk_firmware.git] / protocol / next_kbd.c
1 /*
2
3 NeXT non-ADB Keyboard Protocol
4  
5 Copyright 2013, Benjamin Gould (bgould@github.com)
6
7 Based on:
8 TMK firmware code Copyright 2011,2012 Jun WAKO <wakojun@gmail.com>
9 Arduino code by "Ladyada" Limor Fried (http://ladyada.net/, http://adafruit.com/), released under BSD license
10
11 Timing reference thanks to http://m0115.web.fc2.com/ (dead link), http://cfile7.uf.tistory.com/image/14448E464F410BF22380BB
12 Pinouts thanks to http://www.68k.org/~degs/nextkeyboard.html
13 Keycodes from http://ftp.netbsd.org/pub/NetBSD/NetBSD-release-6/src/sys/arch/next68k/dev/
14
15 This software is licensed with a Modified BSD License.
16 All of this is supposed to be Free Software, Open Source, DFSG-free,
17 GPL-compatible, and OK to use in both free and proprietary applications.
18 Additions and corrections to this file are welcome.
19
20 Redistribution and use in source and binary forms, with or without
21 modification, are permitted provided that the following conditions are met:
22
23 * Redistributions of source code must retain the above copyright
24   notice, this list of conditions and the following disclaimer.
25
26 * Redistributions in binary form must reproduce the above copyright
27   notice, this list of conditions and the following disclaimer in
28   the documentation and/or other materials provided with the
29   distribution.
30
31 * Neither the name of the copyright holders nor the names of
32   contributors may be used to endorse or promote products derived
33   from this software without specific prior written permission.
34
35 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
39 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
40 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
41 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
42 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
43 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
44 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
45 POSSIBILITY OF SUCH DAMAGE.
46
47 */
48
49 #include <stdint.h>
50 #include <stdbool.h>
51 #include <util/atomic.h>
52 #include <util/delay.h>
53 #include "next_kbd.h"
54 #include "debug.h"
55
56 static inline void out_lo(void);
57 static inline void out_hi(void);
58 static inline void query(void);
59 static inline void reset(void);
60 static inline uint32_t response(void);
61
62 /* The keyboard sends signal with 50us pulse width on OUT line
63  * while it seems to miss the 50us pulse on In line.
64  * next_kbd_set_leds() often fails to sync LED status with 50us
65  * but it works well with 51us(+1us) on TMK converter(ATMeaga32u2) at least.
66  * TODO: test on Teensy and Pro Micro configuration
67  */
68 #define out_hi_delay(intervals)  do { out_hi(); _delay_us((NEXT_KBD_TIMING+1) * intervals); } while (0);
69 #define out_lo_delay(intervals)  do { out_lo(); _delay_us((NEXT_KBD_TIMING+1) * intervals); } while (0);
70 #define query_delay(intervals)   do { query();  _delay_us((NEXT_KBD_TIMING+1) * intervals); } while (0);
71 #define reset_delay(intervals)   do { reset();  _delay_us((NEXT_KBD_TIMING+1) * intervals); } while (0);
72
73 void next_kbd_init(void)
74 {
75     out_hi();
76     NEXT_KBD_IN_DDR   &= ~(1<<NEXT_KBD_IN_BIT);   // KBD_IN  to input
77     NEXT_KBD_IN_PORT  |=  (1<<NEXT_KBD_IN_BIT);   // KBD_IN  pull up
78     
79     query_delay(5);
80     reset_delay(8);
81     
82     query_delay(5);
83     reset_delay(8);
84 }
85
86 void next_kbd_set_leds(bool left, bool right)
87 {
88     cli();
89     out_lo_delay(9);
90     
91     out_hi_delay(3);
92     out_lo_delay(1);
93     
94     if (left) {
95         out_hi_delay(1);
96     } else {
97         out_lo_delay(1);
98     }
99     
100     if (right) {
101         out_hi_delay(1);
102     } else {
103         out_lo_delay(1);
104     }
105     
106     out_lo_delay(7);
107     out_hi();
108     sei();
109 }
110
111 #define NEXT_KBD_READ (NEXT_KBD_IN_PIN&(1<<NEXT_KBD_IN_BIT))
112 uint32_t next_kbd_recv(void)
113 {
114     
115     // First check to make sure that the keyboard is actually connected;
116     // if not, just return
117     // TODO: reflect the status of the keyboard in a return code
118     if (!NEXT_KBD_READ) {
119         sei();
120         return 0;
121     }
122
123     query();
124     uint32_t resp = response();
125     
126     return resp;
127 }
128
129 static inline uint32_t response(void)
130 {
131     cli();
132     
133     // try a 5ms read; this should be called after the query method has
134     // been run so if a key is pressed we should get a response within
135     // 5ms; if not then send a reset and exit
136     uint8_t     i  = 0;
137     uint32_t data  = 0;
138     uint16_t reset_timeout = 50000;
139     while (NEXT_KBD_READ && reset_timeout)  {
140         asm(""); _delay_us(1); reset_timeout--;
141     }
142     if (!reset_timeout) {
143         reset();
144         sei();
145         return 0;
146     }
147     _delay_us(NEXT_KBD_TIMING / 2);
148     for (; i < 22; i++)
149     {
150         if (NEXT_KBD_READ)
151         {
152             data |= ((uint32_t) 1 << i);
153             /* Note:
154              * My testing with the ATmega32u4 showed that there might
155              * something wrong with the timing here; by the end of the
156              * second data byte some of the modifiers can get bumped out
157              * to the next bit over if we just cycle through the data
158              * based on the expected interval.  There is a bit (i = 10) 
159              * in the middle of the data that is always on followed by 
160              * one that is always off - so we'll use that to reset our 
161              * timing in case we've gotten ahead of the keyboard;
162              */
163             if (i == 10)
164             {
165                 i++;
166                 while (NEXT_KBD_READ) ;
167                 _delay_us(NEXT_KBD_TIMING / 2);
168             }
169         } else {
170             /* redundant - but I don't want to remove if it might screw
171              * up the timing
172              */
173             data |= ((uint32_t) 0 << i);
174         }
175         _delay_us(NEXT_KBD_TIMING);
176     }
177     
178     sei();
179     
180     return data;
181 }
182
183 static inline void out_lo(void)
184 {
185     NEXT_KBD_OUT_PORT &= ~(1<<NEXT_KBD_OUT_BIT);
186     NEXT_KBD_OUT_DDR  |=  (1<<NEXT_KBD_OUT_BIT);
187 }
188
189 static inline void out_hi(void)
190 {
191     /* input with pull up */
192     NEXT_KBD_OUT_DDR  &= ~(1<<NEXT_KBD_OUT_BIT);
193     NEXT_KBD_OUT_PORT |=  (1<<NEXT_KBD_OUT_BIT);
194 }
195
196 static inline void query(void)
197 {
198     out_lo_delay(5);
199     out_hi_delay(1);
200     out_lo_delay(3);
201     out_hi();
202 }
203
204 static inline void reset(void)
205 {
206     out_lo_delay(1);
207     out_hi_delay(4);
208     out_lo_delay(1);
209     out_hi_delay(6);
210     out_lo_delay(10);
211     out_hi();
212 }