]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | Copyright 2011 Jun WAKO <wakojun@gmail.com> | |
3 | Copyright 2013 Shay Green <gblargg@gmail.com> | |
4 | ||
5 | This software is licensed with a Modified BSD License. | |
6 | All of this is supposed to be Free Software, Open Source, DFSG-free, | |
7 | GPL-compatible, and OK to use in both free and proprietary applications. | |
8 | Additions and corrections to this file are welcome. | |
9 | ||
10 | ||
11 | Redistribution and use in source and binary forms, with or without | |
12 | modification, are permitted provided that the following conditions are met: | |
13 | ||
14 | * Redistributions of source code must retain the above copyright | |
15 | notice, this list of conditions and the following disclaimer. | |
16 | ||
17 | * Redistributions in binary form must reproduce the above copyright | |
18 | notice, this list of conditions and the following disclaimer in | |
19 | the documentation and/or other materials provided with the | |
20 | distribution. | |
21 | ||
22 | * Neither the name of the copyright holders nor the names of | |
23 | contributors may be used to endorse or promote products derived | |
24 | from this software without specific prior written permission. | |
25 | ||
26 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
27 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
28 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
29 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
30 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
31 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
32 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
33 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
34 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
35 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
36 | POSSIBILITY OF SUCH DAMAGE. | |
37 | */ | |
38 | ||
39 | #include <stdbool.h> | |
40 | #include <util/delay.h> | |
41 | #include <avr/io.h> | |
42 | #include <avr/interrupt.h> | |
43 | #include "adb.h" | |
44 | ||
45 | ||
46 | // GCC doesn't inline functions normally | |
47 | #define data_lo() (ADB_DDR |= (1<<ADB_DATA_BIT)) | |
48 | #define data_hi() (ADB_DDR &= ~(1<<ADB_DATA_BIT)) | |
49 | #define data_in() (ADB_PIN & (1<<ADB_DATA_BIT)) | |
50 | ||
51 | #ifdef ADB_PSW_BIT | |
52 | static inline void psw_lo(void); | |
53 | static inline void psw_hi(void); | |
54 | static inline bool psw_in(void); | |
55 | #endif | |
56 | ||
57 | static inline void attention(void); | |
58 | static inline void place_bit0(void); | |
59 | static inline void place_bit1(void); | |
60 | static inline void send_byte(uint8_t data); | |
61 | static inline uint16_t wait_data_lo(uint16_t us); | |
62 | static inline uint16_t wait_data_hi(uint16_t us); | |
63 | ||
64 | ||
65 | void adb_host_init(void) | |
66 | { | |
67 | ADB_PORT &= ~(1<<ADB_DATA_BIT); | |
68 | data_hi(); | |
69 | #ifdef ADB_PSW_BIT | |
70 | psw_hi(); | |
71 | #endif | |
72 | } | |
73 | ||
74 | #ifdef ADB_PSW_BIT | |
75 | bool adb_host_psw(void) | |
76 | { | |
77 | return psw_in(); | |
78 | } | |
79 | #endif | |
80 | ||
81 | /* | |
82 | * Don't call this in a row without the delay, otherwise it makes some of poor controllers | |
83 | * overloaded and misses strokes. Recommended interval is 12ms. | |
84 | * | |
85 | * Thanks a lot, blargg! | |
86 | * <http://geekhack.org/index.php?topic=14290.msg1068919#msg1068919> | |
87 | * <http://geekhack.org/index.php?topic=14290.msg1070139#msg1070139> | |
88 | */ | |
89 | ||
90 | // ADB Bit Cells | |
91 | // | |
92 | // bit cell time: 70-130us | |
93 | // low part of bit0: 60-70% of bit cell | |
94 | // low part of bit1: 30-40% of bit cell | |
95 | // | |
96 | // bit cell time 70us 130us | |
97 | // -------------------------------------------- | |
98 | // low part of bit0 42-49 78-91 | |
99 | // high part of bit0 21-28 39-52 | |
100 | // low part of bit1 21-28 39-52 | |
101 | // high part of bit1 42-49 78-91 | |
102 | // | |
103 | // | |
104 | // bit0: | |
105 | // 70us bit cell: | |
106 | // ____________~~~~~~ | |
107 | // 42-49 21-28 | |
108 | // | |
109 | // 130us bit cell: | |
110 | // ____________~~~~~~ | |
111 | // 78-91 39-52 | |
112 | // | |
113 | // bit1: | |
114 | // 70us bit cell: | |
115 | // ______~~~~~~~~~~~~ | |
116 | // 21-28 42-49 | |
117 | // | |
118 | // 130us bit cell: | |
119 | // ______~~~~~~~~~~~~ | |
120 | // 39-52 78-91 | |
121 | // | |
122 | // [from Apple IIgs Hardware Reference Second Edition] | |
123 | ||
124 | uint16_t adb_host_kbd_recv(void) | |
125 | { | |
126 | uint16_t data = 0; | |
127 | cli(); | |
128 | attention(); | |
129 | send_byte(0x2C); // Addr:Keyboard(0010), Cmd:Talk(11), Register0(00) | |
130 | place_bit0(); // Stopbit(0) | |
131 | if (!wait_data_hi(500)) { // Service Request(310us Adjustable Keyboard): just ignored | |
132 | sei(); | |
133 | return -30; // something wrong | |
134 | } | |
135 | if (!wait_data_lo(500)) { // Tlt/Stop to Start(140-260us) | |
136 | sei(); | |
137 | return 0; // No data to send | |
138 | } | |
139 | ||
140 | uint8_t n = 17; // start bit + 16 data bits | |
141 | do { | |
142 | uint8_t lo = (uint8_t) wait_data_hi(130); | |
143 | if (!lo) | |
144 | goto error; | |
145 | ||
146 | uint8_t hi = (uint8_t) wait_data_lo(lo); | |
147 | if (!hi) | |
148 | goto error; | |
149 | ||
150 | hi = lo - hi; | |
151 | lo = 130 - lo; | |
152 | ||
153 | data <<= 1; | |
154 | if (lo < hi) { | |
155 | data |= 1; | |
156 | } | |
157 | else if (n == 17) { | |
158 | sei(); | |
159 | return -20; | |
160 | } | |
161 | } | |
162 | while ( --n ); | |
163 | ||
164 | // Stop bit can't be checked normally since it could have service request lenghtening | |
165 | // and its high state never goes low. | |
166 | if (!wait_data_hi(351) || wait_data_lo(91)) { | |
167 | sei(); | |
168 | return -21; | |
169 | } | |
170 | sei(); | |
171 | return data; | |
172 | ||
173 | error: | |
174 | sei(); | |
175 | return -n; | |
176 | } | |
177 | ||
178 | void adb_host_listen(uint8_t cmd, uint8_t data_h, uint8_t data_l) | |
179 | { | |
180 | cli(); | |
181 | attention(); | |
182 | send_byte(cmd); | |
183 | place_bit0(); // Stopbit(0) | |
184 | _delay_us(200); // Tlt/Stop to Start | |
185 | place_bit1(); // Startbit(1) | |
186 | send_byte(data_h); | |
187 | send_byte(data_l); | |
188 | place_bit0(); // Stopbit(0); | |
189 | sei(); | |
190 | } | |
191 | ||
192 | // send state of LEDs | |
193 | void adb_host_kbd_led(uint8_t led) | |
194 | { | |
195 | // Addr:Keyboard(0010), Cmd:Listen(10), Register2(10) | |
196 | // send upper byte (not used) | |
197 | // send lower byte (bit2: ScrollLock, bit1: CapsLock, bit0: | |
198 | adb_host_listen(0x2A,0,led&0x07); | |
199 | } | |
200 | ||
201 | ||
202 | #ifdef ADB_PSW_BIT | |
203 | static inline void psw_lo() | |
204 | { | |
205 | ADB_DDR |= (1<<ADB_PSW_BIT); | |
206 | ADB_PORT &= ~(1<<ADB_PSW_BIT); | |
207 | } | |
208 | static inline void psw_hi() | |
209 | { | |
210 | ADB_PORT |= (1<<ADB_PSW_BIT); | |
211 | ADB_DDR &= ~(1<<ADB_PSW_BIT); | |
212 | } | |
213 | static inline bool psw_in() | |
214 | { | |
215 | ADB_PORT |= (1<<ADB_PSW_BIT); | |
216 | ADB_DDR &= ~(1<<ADB_PSW_BIT); | |
217 | return ADB_PIN&(1<<ADB_PSW_BIT); | |
218 | } | |
219 | #endif | |
220 | ||
221 | static inline void attention(void) | |
222 | { | |
223 | data_lo(); | |
224 | _delay_us(800-35); // bit1 holds lo for 35 more | |
225 | place_bit1(); | |
226 | } | |
227 | ||
228 | static inline void place_bit0(void) | |
229 | { | |
230 | data_lo(); | |
231 | _delay_us(65); | |
232 | data_hi(); | |
233 | _delay_us(35); | |
234 | } | |
235 | ||
236 | static inline void place_bit1(void) | |
237 | { | |
238 | data_lo(); | |
239 | _delay_us(35); | |
240 | data_hi(); | |
241 | _delay_us(65); | |
242 | } | |
243 | ||
244 | static inline void send_byte(uint8_t data) | |
245 | { | |
246 | for (int i = 0; i < 8; i++) { | |
247 | if (data&(0x80>>i)) | |
248 | place_bit1(); | |
249 | else | |
250 | place_bit0(); | |
251 | } | |
252 | } | |
253 | ||
254 | // These are carefully coded to take 6 cycles of overhead. | |
255 | // inline asm approach became too convoluted | |
256 | static inline uint16_t wait_data_lo(uint16_t us) | |
257 | { | |
258 | do { | |
259 | if ( !data_in() ) | |
260 | break; | |
261 | _delay_us(1 - (6 * 1000000.0 / F_CPU)); | |
262 | } | |
263 | while ( --us ); | |
264 | return us; | |
265 | } | |
266 | ||
267 | static inline uint16_t wait_data_hi(uint16_t us) | |
268 | { | |
269 | do { | |
270 | if ( data_in() ) | |
271 | break; | |
272 | _delay_us(1 - (6 * 1000000.0 / F_CPU)); | |
273 | } | |
274 | while ( --us ); | |
275 | return us; | |
276 | } | |
277 | ||
278 | ||
279 | /* | |
280 | ADB Protocol | |
281 | ============ | |
282 | ||
283 | Resources | |
284 | --------- | |
285 | ADB - The Untold Story: Space Aliens Ate My Mouse | |
286 | http://developer.apple.com/legacy/mac/library/#technotes/hw/hw_01.html | |
287 | ADB Manager | |
288 | http://developer.apple.com/legacy/mac/library/documentation/mac/pdf/Devices/ADB_Manager.pdf | |
289 | Service request(5-17) | |
290 | Apple IIgs Hardware Reference Second Edition [Chapter6 p121] | |
291 | ftp://ftp.apple.asimov.net/pub/apple_II/documentation/Apple%20IIgs%20Hardware%20Reference.pdf | |
292 | ADB Keycode | |
293 | http://72.0.193.250/Documentation/macppc/adbkeycodes/ | |
294 | http://m0115.web.fc2.com/m0115.jpg | |
295 | [Inside Macintosh volume V, pages 191-192] | |
296 | http://www.opensource.apple.com/source/IOHIDFamily/IOHIDFamily-421.18.3/IOHIDFamily/Cosmo_USB2ADB.c | |
297 | ADB Signaling | |
298 | http://kbdbabel.sourceforge.net/doc/kbd_signaling_pcxt_ps2_adb.pdf | |
299 | ADB Overview & History | |
300 | http://en.wikipedia.org/wiki/Apple_Desktop_Bus | |
301 | Microchip Application Note: ADB device(with code for PIC16C) | |
302 | http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1824&appnote=en011062 | |
303 | AVR ATtiny2131 ADB to PS/2 converter(Japanese) | |
304 | http://hp.vector.co.jp/authors/VA000177/html/KeyBoardA5DEA5CBA5A2II.html | |
305 | ||
306 | ||
307 | Pinouts | |
308 | ------- | |
309 | ADB female socket from the front: | |
310 | __________ | |
311 | | | <--- top | |
312 | | 4o o3 | | |
313 | |2o o1| | |
314 | | == | | |
315 | |________| <--- bottom | |
316 | | | <--- 4pins | |
317 | ||
318 | ||
319 | ADB female socket from bottom: | |
320 | ||
321 | ========== <--- front | |
322 | | | | |
323 | | | | |
324 | |2o o1| | |
325 | |4o o3| | |
326 | ---------- <--- back | |
327 | ||
328 | 1: Data | |
329 | 2: Power SW(low when press Power key) | |
330 | 3: Vcc(5V) | |
331 | 4: GND | |
332 | ||
333 | ||
334 | Commands | |
335 | -------- | |
336 | ADB command is 1byte and consists of 4bit-address, 2bit-command | |
337 | type and 2bit-register. The commands are always sent by Host. | |
338 | ||
339 | Command format: | |
340 | 7 6 5 4 3 2 1 0 | |
341 | | | | |------------ address | |
342 | | |-------- command type | |
343 | | |---- register | |
344 | ||
345 | bits commands | |
346 | ------------------------------------------------------ | |
347 | - - - - 0 0 0 0 Send Request(reset all devices) | |
348 | A A A A 0 0 0 1 Flush(reset a device) | |
349 | - - - - 0 0 1 0 Reserved | |
350 | - - - - 0 0 1 1 Reserved | |
351 | - - - - 0 1 - - Reserved | |
352 | A A A A 1 0 R R Listen(write to a device) | |
353 | A A A A 1 1 R R Talk(read from a device) | |
354 | ||
355 | The command to read keycodes from keyboard is 0x2C which | |
356 | consist of keyboard address 2 and Talk against register 0. | |
357 | ||
358 | Address: | |
359 | 2: keyboard | |
360 | 3: mice | |
361 | ||
362 | Registers: | |
363 | 0: application(keyboard uses this to store its data.) | |
364 | 1: application | |
365 | 2: application(keyboard uses this for LEDs and state of modifiers) | |
366 | 3: status and command | |
367 | ||
368 | ||
369 | Communication | |
370 | ------------- | |
371 | This is a minimum information for keyboard communication. | |
372 | See "Resources" for detail. | |
373 | ||
374 | Signaling: | |
375 | ||
376 | ~~~~____________~~||||||||||||__~~~~~_~~|||||||||||||||__~~~~ | |
377 | ||
378 | |800us | |7 Command 0| | | |15-64 Data 0|Stopbit(0) | |
379 | +Attention | | | +Startbit(1) | |
380 | +Startbit(1) | +Tlt(140-260us) | |
381 | +stopbit(0) | |
382 | ||
383 | Bit cells: | |
384 | ||
385 | bit0: ______~~~ | |
386 | 65 :35us | |
387 | ||
388 | bit1: ___~~~~~~ | |
389 | 35 :65us | |
390 | ||
391 | bit0 low time: 60-70% of bit cell(42-91us) | |
392 | bit1 low time: 30-40% of bit cell(21-52us) | |
393 | bit cell time: 70-130us | |
394 | [from Apple IIgs Hardware Reference Second Edition] | |
395 | ||
396 | Criterion for bit0/1: | |
397 | After 55us if line is low/high then bit is 0/1. | |
398 | ||
399 | Attention & start bit: | |
400 | Host asserts low in 560-1040us then places start bit(1). | |
401 | ||
402 | Tlt(Stop to Start): | |
403 | Bus stays high in 140-260us then device places start bit(1). | |
404 | ||
405 | Global reset: | |
406 | Host asserts low in 2.8-5.2ms. All devices are forced to reset. | |
407 | ||
408 | Service request from device(Srq): | |
409 | Device can request to send at commad(Global only?) stop bit. | |
410 | Requesting device keeps low for 140-260us at stop bit of command. | |
411 | ||
412 | ||
413 | Keyboard Data(Register0) | |
414 | This 16bit data can contains two keycodes and two released flags. | |
415 | First keycode is palced in upper byte. When one keyocode is sent, | |
416 | lower byte is 0xFF. | |
417 | Release flag is 1 when key is released. | |
418 | ||
419 | 1514 . . . . . 8 7 6 . . . . . 0 | |
420 | | | | | | | | | | +-+-+-+-+-+-+- Keycode2 | |
421 | | | | | | | | | +--------------- Released2(1 when the key is released) | |
422 | | +-+-+-+-+-+-+----------------- Keycode1 | |
423 | +------------------------------- Released1(1 when the key is released) | |
424 | ||
425 | Keycodes: | |
426 | Scancode consists of 7bit keycode and 1bit release flag. | |
427 | Device can send two keycodes at once. If just one keycode is sent | |
428 | keycode1 contains it and keyocode2 is 0xFF. | |
429 | ||
430 | Power switch: | |
431 | You can read the state from PSW line(active low) however | |
432 | the switch has a special scancode 0x7F7F, so you can | |
433 | also read from Data line. It uses 0xFFFF for release scancode. | |
434 | ||
435 | Keyboard LEDs & state of keys(Register2) | |
436 | This register hold current state of three LEDs and nine keys. | |
437 | The state of LEDs can be changed by sending Listen command. | |
438 | ||
439 | 1514 . . . . . . 7 6 5 . 3 2 1 0 | |
440 | | | | | | | | | | | | | | | | +- LED1(NumLock) | |
441 | | | | | | | | | | | | | | | +--- LED2(CapsLock) | |
442 | | | | | | | | | | | | | | +----- LED3(ScrollLock) | |
443 | | | | | | | | | | | +-+-+------- Reserved | |
444 | | | | | | | | | | +------------- ScrollLock | |
445 | | | | | | | | | +--------------- NumLock | |
446 | | | | | | | | +----------------- Apple/Command | |
447 | | | | | | | +------------------- Option | |
448 | | | | | | +--------------------- Shift | |
449 | | | | | +----------------------- Control | |
450 | | | | +------------------------- Reset/Power | |
451 | | | +--------------------------- CapsLock | |
452 | | +----------------------------- Delete | |
453 | +------------------------------- Reserved | |
454 | ||
455 | END_OF_ADB | |
456 | */ |