-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathgcn64_protocol.c
244 lines (210 loc) · 6.58 KB
/
gcn64_protocol.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
Copyright (C) 2007-2016 Raphael Assenat <raph@raphnet.net>
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 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <string.h>
#include "gcn64_protocol.h"
#include "gcn64txrx.h"
#undef FORCE_KEYBOARD
#undef TRACE_GCN64
/******** IO port definitions and options **************/
#ifndef STK525
#define GCN64_DATA_PORT PORTD
#define GCN64_DATA_DDR DDRD
#define GCN64_DATA_PIN PIND
#define GCN64_DATA_BIT0 (1<<0)
#define GCN64_DATA_BIT1 (1<<1)
#define GCN64_DATA_BIT2 (1<<2)
#define GCN64_DATA_BIT3 (1<<3)
#define GCN64_BIT_NUM_S "0" // for asm
#else
#define GCN64_DATA_PORT PORTA
#define GCN64_DATA_DDR DDRA
#define GCN64_DATA_PIN PINA
#define GCN64_DATA_BIT0 (1<<0)
#define GCN64_DATA_BIT1 (1<<2) // This is not an error
#define GCN64_DATA_BIT2 (1<<1) // This is not an error
#define GCN64_DATA_BIT3 (1<<3)
#define GCN64_BIT_NUM_S "0" // for asm
#endif
#define DISABLE_INTS_DURING_COMM
void gcn64protocol_hwinit(void)
{
// data as input
GCN64_DATA_DDR &= ~(GCN64_DATA_BIT0 | GCN64_DATA_BIT1 | GCN64_DATA_BIT2 | GCN64_DATA_BIT3);
// keep data low. By toggling the direction, we make the
// pin act as an open-drain output.
GCN64_DATA_PORT &= ~(GCN64_DATA_BIT0 | GCN64_DATA_BIT1 | GCN64_DATA_BIT2 | GCN64_DATA_BIT3);
/* debug bit PORTB4 (MISO) */
DDRB |= 0x10;
PORTB &= ~0x10;
}
/**
* \brief Send n data bytes + stop bit, wait for answer.
*
* \param chn Channel (0 to 3)
* \param tx Data to be transmitted
* \param tx_len Transmission length
* \param rx Reception buffer
* \param rx_max Buffer size
* \return The number of bytes received, 0 on timeout/error.
*/
unsigned char gcn64_transaction(unsigned char chn, const unsigned char *tx, int tx_len, unsigned char *rx, unsigned char rx_max)
{
int count;
unsigned char sreg = SREG;
void (*sendBytes)(const unsigned char *data, unsigned char n_bytes);
unsigned char (*receiveBytes)(unsigned char *dstbuf, unsigned char max_bytes);
switch(chn)
{
case 0: sendBytes = gcn64_sendBytes0; receiveBytes = gcn64_receiveBytes0; break;
case 1: sendBytes = gcn64_sendBytes1; receiveBytes = gcn64_receiveBytes1; break;
case 2: sendBytes = gcn64_sendBytes2; receiveBytes = gcn64_receiveBytes2; break;
case 3: sendBytes = gcn64_sendBytes3; receiveBytes = gcn64_receiveBytes3; break;
default: return 0;
}
#ifdef TRACE_GCN64
int i;
printf("TX[%d] = { ", tx_len);
for (i=0; i<tx_len; i++) {
printf("%02x ", tx[i]);
}
printf("}\r\n");
#endif
#ifdef DISABLE_INTS_DURING_COMM
cli();
#endif
sendBytes(tx, tx_len);
count = receiveBytes(rx, rx_max);
SREG = sreg;
if (count == 0xff) {
#ifdef TRACE_GCN64
printf("rx error\r\n");
#endif
return 0;
}
if (count == 0xfe) {
#ifdef TRACE_GCN64
printf("buffer too small\r\n");
#endif
return 0;
}
#ifdef TRACE_GCN64
printf("RX[%d] = { ", count);
for (i=0; i<count; i++) {
printf("%02x ", rx[i]);
}
printf("}\r\n");
#endif
/* this delay is required on N64 controllers. Otherwise, after sending
* a rumble-on or rumble-off command (probably init too), the following
* get status fails. This starts to work at 30us. 60us should be safe. */
_delay_us(80); // Note that this results in a ~100us delay between packets.
return count;
}
#if (GC_GETID != N64_GET_CAPABILITIES)
#error N64 vs GC detection commnad broken
#endif
int gcn64_detectController(unsigned char chn)
{
unsigned char tmp = GC_GETID;
unsigned char count;
unsigned short id;
unsigned char data[4];
count = gcn64_transaction(chn, &tmp, 1, data, sizeof(data));
if (count == 0) {
return CONTROLLER_IS_ABSENT;
}
if (count != GC_GETID_REPLY_LENGTH) {
return CONTROLLER_IS_UNKNOWN;
}
/*
* -- Standard gamecube controller answer:
* 0000 1001 0000 0000 0010 0011 : 0x090023 or
* 0000 1001 0000 0000 0010 0000 : 0x090020
*
* 0000 1001 0000 0000 0010 0000
*
* -- Wavebird gamecube controller
* 1010 1000 0000 0000 0000 0000 : 0xA80000
* (receiver first power up, controller off)
*
* 1110 1001 1010 0000 0001 0111 : 0xE9A017
* (controller on)
*
* 1010 1000 0000
*
* -- Intec wireless gamecube controller
* 0000 1001 0000 0000 0010 0000 : 0x090020
*
*
* -- Standard N64 controller
* 0000 0101 0000 0000 0000 0000 : 0x050000 (no pack)
* 0000 0101 0000 0000 0000 0001 : 0x050001 With expansion pack
* 0000 0101 0000 0000 0000 0010 : 0x050002 Expansion pack removed
*
* -- N64 mouse --
* 0000 0010 0000 0000 0000 0000 : 0x020000
*
* -- Ascii keyboard (keyboard connector)
* 0000 1000 0010 0000 0000 0000 : 0x082000
*
* Ok, so based on the above, when the second nibble is a 9 or 8, a
* gamecube compatible controller is present. If on the other hand
* we have a 5, then we are communicating with a N64 controller.
*
* This conclusion appears to be corroborated by my old printout of
* the document named "Yet another gamecube documentation (but one
* that's worth printing). The document explains that and ID can
* be read by sending what they call the 'SI command 0x00 to
* which the controller replies with 3 bytes. (Clearly, that's
* what we are doing here). The first 16 bits are the id, and they
* list, among other type of devices, the following:
*
* 0x0500 N64 controller
* 0x0900 GC standard controller
* 0x0900 Dkongas
* 0xe960 Wavebird
* 0xe9a0 Wavebird
* 0xa800 Wavebird
* 0xebb0 Wavebird
*
**/
id = (data[0]<<8) | data[1];
printf("Id: %04x (%d: %02x %02x %02x)\r\n", id, count, data[0], data[1], data[2]);
#ifdef FORCE_KEYBOARD
return CONTROLLER_IS_GC_KEYBOARD;
#endif
switch ((id >> 8) & 0x0F) {
case 0x02:
return CONTROLLER_IS_N64_MOUSE;
case 0x05:
return CONTROLLER_IS_N64;
case 0x09: // normal controllers
case 0x0b: // Never saw this one, but it is mentionned above.
return CONTROLLER_IS_GC;
case 0x08:
if (id == 0x0820) {
// Ascii keyboard
return CONTROLLER_IS_GC_KEYBOARD;
}
// wavebird, controller off.
return CONTROLLER_IS_GC;
default:
return CONTROLLER_IS_UNKNOWN;
}
return 0;
}