
commit
c59242383f
14 changed files with 3156 additions and 0 deletions
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
CC=avr-gcc
|
||||
AS=$(CC)
|
||||
LD=$(CC)
|
||||
|
||||
PROGNAME=n64
|
||||
OBJDIR=objs-$(PROGNAME)
|
||||
CPU=atmega32u2
|
||||
CFLAGS=-Wall -mmcu=$(CPU) -DF_CPU=16000000L -Os -DUART1_STDOUT
|
||||
LDFLAGS=-mmcu=$(CPU) -Wl,-Map=$(PROGNAME).map
|
||||
HEXFILE=$(PROGNAME).hex
|
||||
|
||||
OBJS=main.o n64.o gcn64_protocol.o usart1.o usb.o
|
||||
|
||||
all: $(HEXFILE) |
||||
|
||||
clean: |
||||
rm -f $(PROGNAME).elf $(PROGNAME).hex $(PROGNAME).map $(OBJS)
|
||||
|
||||
%.o: %.S |
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
%.o: %.c |
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
%.o: %.c %.h |
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
$(PROGNAME).elf: $(OBJS) |
||||
$(LD) $(OBJS) $(LDFLAGS) -o $(PROGNAME).elf
|
||||
|
||||
$(PROGNAME).hex: $(PROGNAME).elf |
||||
avr-objcopy -j .data -j .text -O ihex $(PROGNAME).elf $(PROGNAME).hex
|
||||
avr-size $(PROGNAME).elf
|
||||
|
||||
fuse: |
||||
|
||||
flash: $(HEXFILE) |
||||
./wait_then_flash.sh $(CPU) $(HEXFILE)
|
||||
|
||||
chip_erase: |
||||
dfu-programmer atmega32u2 erase
|
||||
|
||||
reset: |
||||
dfu-programmer atmega32u2 reset
|
@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
|
||||
#ifndef _gamepad_h__ |
||||
#define _gamepad_h__ |
||||
|
||||
typedef struct { |
||||
int num_reports; |
||||
|
||||
int reportDescriptorSize; |
||||
void *reportDescriptor; // must be in flash
|
||||
|
||||
int deviceDescriptorSize; // if 0, use default
|
||||
void *deviceDescriptor; // must be in flash
|
||||
|
||||
void (*init)(void); |
||||
char (*update)(void); |
||||
char (*changed)(int id); |
||||
int (*buildReport)(unsigned char *buf, int id); |
||||
void (*setVibration)(int value); |
||||
|
||||
/* Check for the controller */ |
||||
char (*probe)(void); /* return true if found */ |
||||
} Gamepad; |
||||
|
||||
#endif // _gamepad_h__
|
||||
|
||||
|
@ -0,0 +1,492 @@
@@ -0,0 +1,492 @@
|
||||
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
||||
Copyright (C) 2007-2015 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 <avr/io.h> |
||||
#include <avr/interrupt.h> |
||||
#include <util/delay.h> |
||||
|
||||
#include "gcn64_protocol.h" |
||||
|
||||
#undef FORCE_KEYBOARD |
||||
|
||||
#define GCN64_BUF_SIZE 300 |
||||
static volatile unsigned char gcn64_workbuf[GCN64_BUF_SIZE]; |
||||
|
||||
/******** IO port definitions and options **************/ |
||||
#define GCN64_DATA_PORT PORTD |
||||
#define GCN64_DATA_DDR DDRD |
||||
#define GCN64_DATA_PIN PIND |
||||
#define GCN64_DATA_BIT (1<<0) |
||||
#define GCN64_BIT_NUM_S "0" // for asm
|
||||
#define FREQ_IS_16MHZ |
||||
#define DISABLE_INTS_DURING_COMM |
||||
|
||||
/*
|
||||
* \brief Explode bytes to bits |
||||
* \param bytes The input byte array |
||||
* \param num_bytes The number of input bytes |
||||
* \param workbuf_bit_offset The offset to start writing at |
||||
* \return number of bits (i.e. written output bytes) |
||||
* |
||||
* 1 input byte = 8 output bytes, where each output byte is zero or non-zero depending |
||||
* on the input byte bits, starting with the most significant one. |
||||
*/ |
||||
static int bitsToWorkbufBytes(unsigned char *bytes, int num_bytes, int workbuf_bit_offset) |
||||
{ |
||||
int i, bit; |
||||
unsigned char p; |
||||
|
||||
if (num_bytes * 8 > GCN64_BUF_SIZE) |
||||
return 0; |
||||
|
||||
for (i=0,bit=0; i<num_bytes; i++) { |
||||
for (p=0x80; p; p>>=1) { |
||||
gcn64_workbuf[bit+workbuf_bit_offset] = bytes[i] & p; |
||||
bit++; |
||||
} |
||||
} |
||||
|
||||
return bit; |
||||
} |
||||
|
||||
/* Read a byte from the buffer (where 1 byte is 1 bit).
|
||||
* MSb first. |
||||
*/ |
||||
unsigned char gcn64_protocol_getByte(int offset) |
||||
{ |
||||
unsigned char val, b; |
||||
unsigned char volatile *addr = gcn64_workbuf + offset; |
||||
|
||||
for (b=0x80, val=0; b; b>>=1) |
||||
{ |
||||
if (*addr) |
||||
val |= b; |
||||
addr++; |
||||
} |
||||
return val; |
||||
} |
||||
|
||||
void gcn64_protocol_getBytes(int offset, int n_bytes, unsigned char *dstbuf) |
||||
{ |
||||
int i; |
||||
|
||||
for (i=0; i<n_bytes; i++) { |
||||
*dstbuf = gcn64_protocol_getByte(offset + (i*8)); |
||||
dstbuf++; |
||||
} |
||||
} |
||||
|
||||
// The bit timeout is a counter to 127. This is the
|
||||
// start value. Counting from 0 takes hundreads of
|
||||
// microseconds. Because of this, the reception function
|
||||
// "hangs in there" much longer than necessary..
|
||||
#ifdef FREQ_IS_16MHZ |
||||
#define TIMING_OFFSET 75 |
||||
#else |
||||
#define TIMING_OFFSET 100 // gives about 12uS. Twice the expected maximum bit period.
|
||||
#endif |
||||
|
||||
static unsigned char gcn64_receive() |
||||
{ |
||||
register unsigned char count=0; |
||||
|
||||
#define SET_DBG " nop\n" |
||||
#define CLR_DBG " nop\n" |
||||
//#define SET_DBG " sbi %3, 4 \n"
|
||||
//#define CLR_DBG " cbi %3, 4 \n"
|
||||
|
||||
// The data line has been released.
|
||||
// The receive part below expects it to be still high
|
||||
// and will wait for it to become low before beginning
|
||||
// the counting.
|
||||
asm volatile( |
||||
" push r30 \n" // save Z
|
||||
" push r31 \n" // save Z
|
||||
|
||||
" clr %0 \n" |
||||
" clr r16 \n" |
||||
"initial_wait_low:\n" |
||||
" inc r16 \n" |
||||
" breq timeout \n" // overflow to 0
|
||||
" sbic %2, "GCN64_BIT_NUM_S" \n" |
||||
" rjmp initial_wait_low \n" |
||||
|
||||
// the next transition is to a high bit
|
||||
" rjmp waithigh \n" |
||||
|
||||
"waitlow:\n" |
||||
" ldi r16, %4 \n" |
||||
"waitlow_lp:\n" |
||||
" inc r16 \n" |
||||
" brmi timeout \n" // > 127 (approx 50uS timeout)
|
||||
" sbic %2, "GCN64_BIT_NUM_S" \n" |
||||
" rjmp waitlow_lp \n" |
||||
|
||||
" inc %0 \n" // count this timed low level
|
||||
" breq overflow \n" // > 255
|
||||
" st z+,r16 \n" |
||||
|
||||
"waithigh:\n" |
||||
" ldi r16, %4 \n" |
||||
"waithigh_lp:\n" |
||||
" inc r16 \n" |
||||
" brmi timeout \n" // > 127
|
||||
" sbis %2, "GCN64_BIT_NUM_S" \n" |
||||
" rjmp waithigh_lp \n" |
||||
|
||||
" inc %0 \n" // count this timed high level
|
||||
" breq overflow \n" // > 255
|
||||
" st z+,r16 \n" |
||||
|
||||
" rjmp waitlow \n" |
||||
|
||||
"overflow: \n" |
||||
"timeout: \n" |
||||
" pop r31 \n" // restore z
|
||||
" pop r30 \n" // restore z
|
||||
|
||||
: "=&r" (count) // %0
|
||||
: "z" ((unsigned char volatile *)gcn64_workbuf), // %1
|
||||
"I" (_SFR_IO_ADDR(GCN64_DATA_PIN)), // %2
|
||||
"I" (_SFR_IO_ADDR(PORTB)), // %3
|
||||
"M" (TIMING_OFFSET) // %4
|
||||
: "r16" |
||||
); |
||||
|
||||
return count; |
||||
} |
||||
|
||||
static void gcn64_sendBytes(unsigned char *data, unsigned char n_bytes) |
||||
{ |
||||
unsigned int bits; |
||||
|
||||
if (n_bytes == 0) |
||||
return; |
||||
|
||||
// Explode the data to one byte per bit for very easy transmission in assembly.
|
||||
// This trades memory for ease of implementation.
|
||||
bits = bitsToWorkbufBytes(data, n_bytes, 0); |
||||
|
||||
// the value of the gpio is pre-configured to low. We simulate
|
||||
// an open drain output by toggling the direction.
|
||||
#define PULL_DATA " sbi %0, "GCN64_BIT_NUM_S"\n" |
||||
#define RELEASE_DATA " cbi %0, "GCN64_BIT_NUM_S"\n" |
||||
|
||||
#ifdef FREQ_IS_16MHZ |
||||
// busy looping delays based on busy loop and nop tuning.
|
||||
// valid for 16Mhz clock. (Tuned to 1us/3us using a scope)
|
||||
#define DLY_SHORT_1ST "ldi r17, 2\n nop\nrcall sb_dly%=\n " |
||||
#define DLY_LARGE_1ST "ldi r17, 13\n rcall sb_dly%=\n" |
||||
#define DLY_SHORT_2ND "nop\nnop\nnop\nnop\n" |
||||
#define DLY_LARGE_2ND "ldi r17, 9\n rcall sb_dly%=\nnop\nnop\n" |
||||
|
||||
#else |
||||
// busy looping delays based on busy loop and nop tuning.
|
||||
// valid for 12Mhz clock.
|
||||
#define DLY_SHORT_1ST "ldi r17, 1\n rcall sb_dly%=\n " |
||||
#define DLY_LARGE_1ST "ldi r17, 9\n rcall sb_dly%=\n" |
||||
#define DLY_SHORT_2ND "\n" |
||||
#define DLY_LARGE_2ND "ldi r17, 5\n rcall sb_dly%=\n nop\nnop\n" |
||||
#endif |
||||
|
||||
asm volatile( |
||||
// Save the modified input operands
|
||||
" push r28 \n" // y
|
||||
" push r29 \n" |
||||
" push r30 \n" // z
|
||||
" push r31 \n" |
||||
|
||||
"sb_loop%=: \n" |
||||
" ld r16, z+ \n" |
||||
" tst r16 \n" |
||||
" breq sb_send0%= \n" |
||||
" brne sb_send1%= \n" |
||||
|
||||
" rjmp sb_end%= \n" // not reached
|
||||
|
||||
|
||||
"sb_send0%=: \n" |
||||
" nop \n" |
||||
PULL_DATA |
||||
DLY_LARGE_1ST |
||||
RELEASE_DATA |
||||
DLY_SHORT_2ND |
||||
" sbiw %1, 1 \n" |
||||
" brne sb_loop%= \n" |
||||
" rjmp sb_end%= \n" |
||||
|
||||
"sb_send1%=: \n" |
||||
PULL_DATA |
||||
DLY_SHORT_1ST |
||||
RELEASE_DATA |
||||
DLY_LARGE_2ND |
||||
" sbiw %1, 1 \n" |
||||
" brne sb_loop%= \n" |
||||
" rjmp sb_end%= \n" |
||||
|
||||
// delay sub (arg r17)
|
||||
"sb_dly%=: \n" |
||||
" dec r17 \n" |
||||
" brne sb_dly%= \n" |
||||
" ret \n" |
||||
|
||||
|
||||
"sb_end%=:\n" |
||||
// going here is fast so we need to extend the last
|
||||
// delay by 500nS
|
||||
" nop\n " |
||||
#ifdef FREQ_IS_16MHZ |
||||
" nop\n" |
||||
#endif |
||||
" pop r31 \n" |
||||
" pop r30 \n" |
||||
PULL_DATA |
||||
" pop r29 \n" |
||||
" pop r28 \n" |
||||
//DLY_SHORT_1ST
|
||||
"nop\nnop\nnop\nnop\n" |
||||
RELEASE_DATA |
||||
|
||||
// Now, we need to loop until the wire is high to
|
||||
// prevent the reception code from thinking this is
|
||||
// the beginning of the first reply bit.
|
||||
|
||||
" ldi r16, 0xff \n" // setup a timeout
|
||||
"sb_waitHigh%=: \n" |
||||
" dec r16 \n" // decrement timeout
|
||||
" breq sb_wait_high_done%= \n" // handle timeout condition
|
||||
" sbis %3, "GCN64_BIT_NUM_S" \n" // Read the port
|
||||
" rjmp sb_waitHigh%= \n" |
||||
"sb_wait_high_done%=:\n" |
||||
: |
||||
: "I" (_SFR_IO_ADDR(GCN64_DATA_DDR)), // %0
|
||||
"w" (bits), // %1
|
||||
"z" ((unsigned char volatile *)gcn64_workbuf), // %2
|
||||
"I" (_SFR_IO_ADDR(GCN64_DATA_PIN)) // %3
|
||||
: "r16", "r17"); |
||||
} |
||||
|
||||
|
||||
/* \brief Decode the received length of low/high states to byte-per-bit format
|
||||
* |
||||
* The result is in workbuf. |
||||
* |
||||
**/ |
||||
static void gcn64_decodeWorkbuf(unsigned char count) |
||||
{ |
||||
unsigned char i; |
||||
volatile unsigned char *output = gcn64_workbuf; |
||||
volatile unsigned char *input = gcn64_workbuf; |
||||
unsigned char t; |
||||
|
||||
//
|
||||
// ________
|
||||
// ________/
|
||||
//
|
||||
// [i*2] [i*2+1]
|
||||
//
|
||||
// ________________
|
||||
// 0 : ____/
|
||||
// ____
|
||||
// 1 : ________________/
|
||||
//
|
||||
// The timings on a real N64 are
|
||||
//
|
||||
// 0 : 1 us low, 3 us high
|
||||
// 1 : 3 us low, 1 us high
|
||||
//
|
||||
// However, HORI pads use something similar to
|
||||
//
|
||||
// 0 : 1.5 us low, 4.5 us high
|
||||
// 1 : 4.5 us low, 1.5 us high
|
||||
//
|
||||
//
|
||||
// No64 us = microseconds
|
||||
|
||||
// This operation takes approximately 100uS on 64bit gamecube messages
|
||||
for (i=0; i<count; i++) { |
||||
t = *input;
|
||||
input++; |
||||
|
||||
*output = t < *input; |
||||
|
||||
input++; |
||||
output++; |
||||
} |
||||
} |
||||
|
||||
void gcn64protocol_hwinit(void) |
||||
{ |
||||
// data as input
|
||||
GCN64_DATA_DDR &= ~(GCN64_DATA_BIT); |
||||
|
||||
// keep data low. By toggling the direction, we make the
|
||||
// pin act as an open-drain output.
|
||||
GCN64_DATA_PORT &= ~GCN64_DATA_BIT; |
||||
|
||||
/* debug bit PORTB4 (MISO) */ |
||||
DDRB |= 0x10; |
||||
PORTB &= ~0x10; |
||||
} |
||||
|
||||
|
||||
|
||||
/**
|
||||
* \brief Send n data bytes + stop bit, wait for answer. |
||||
* \return The number of bits received, 0 on timeout/error. |
||||
* |
||||
* The result is in gcn64_workbuf, where each byte represents |
||||
* a bit. |
||||
*/ |
||||
int gcn64_transaction(unsigned char *data_out, int data_out_len) |
||||
{ |
||||
int count; |
||||
unsigned char sreg = SREG; |
||||
|
||||
#ifdef DISABLE_INTS_DURING_COMM |
||||
cli(); |
||||
#endif |
||||
gcn64_sendBytes(data_out, data_out_len); |
||||
count = gcn64_receive(); |
||||
SREG = sreg; |
||||
|
||||
if (!count) |
||||
return 0; |
||||
|
||||
if (!(count & 0x01)) { |
||||
// If we don't get an odd number of level lengths from gcn64_receive
|
||||
// something is wrong.
|
||||
//
|
||||
// The stop bit is a short (~1us) low state followed by an "infinite"
|
||||
// high state, which timeouts and lets the function return. This
|
||||
// is why we should receive and odd number of lengths.
|
||||
return 0; |
||||
} |
||||
|
||||
gcn64_decodeWorkbuf(count); |
||||
|
||||
/* 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 2us. 5 should be safe. */ |
||||
_delay_us(5); |
||||
|
||||
/* return the number of full bits received. */ |
||||
return (count-1) / 2; |
||||
} |
||||
|
||||
|
||||
#if (GC_GETID != N64_GET_CAPABILITIES) |
||||
#error N64 vs GC detection commnad broken |
||||
#endif |
||||
int gcn64_detectController(void) |
||||
{ |
||||
unsigned char tmp = GC_GETID; |
||||
int count; |
||||
unsigned short id; |
||||
|
||||
count = gcn64_transaction(&tmp, 1); |
||||
if (count == 0) { |
||||
return CONTROLLER_IS_ABSENT; |
||||
} |
||||
if (count != 24) { |
||||
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 |
||||
* |
||||
* -- 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 |
||||
* |
||||
* This last entry worries me. I never observed it, but who knows |
||||
* what the user will connect? Better be safe and consider 0xb as |
||||
* a gamecube controller too. |
||||
* |
||||
* */ |
||||
|
||||
id = gcn64_protocol_getByte(0)<<8; |
||||
id |= gcn64_protocol_getByte(8); |
||||
|
||||
#ifdef FORCE_KEYBOARD |
||||
return CONTROLLER_IS_GC_KEYBOARD; |
||||
#endif |
||||
|
||||
switch (id >> 8) { |
||||
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; |
||||
} |
||||
|
||||
|
@ -0,0 +1,168 @@
@@ -0,0 +1,168 @@
|
||||
#ifndef _gcn64_protocol_h__ |
||||
#define _gcn64_protocol_h__ |
||||
|
||||
#define CONTROLLER_IS_ABSENT 0 |
||||
#define CONTROLLER_IS_N64 1 |
||||
#define CONTROLLER_IS_GC 2 |
||||
#define CONTROLLER_IS_GC_KEYBOARD 3 |
||||
#define CONTROLLER_IS_UNKNOWN 4 |
||||
|
||||
|
||||
/* Return many unknown bits, but two are about the expansion port. */ |
||||
#define N64_GET_CAPABILITIES 0x00 |
||||
#define N64_CAPS_REPLY_LENGTH 24 |
||||
|
||||
#define OFFSET_EXT_REMOVED 22 |
||||
#define OFFSET_EXT_PRESENT 23 |
||||
|
||||
/* Returns button states and axis values */ |
||||
#define N64_GET_STATUS 0x01 |
||||
#define N64_GET_STATUS_REPLY_LENGTH 32 |
||||
|
||||
/* Read from the expansion bus. */ |
||||
#define N64_EXPANSION_READ 0x02 |
||||
|
||||
/* Write to the expansion bus. */ |
||||
#define N64_EXPANSION_WRITE 0x03 |
||||
|
||||
/* Return information about controller. */ |
||||
#define GC_GETID 0x00 |
||||
#define GC_GETID_REPLY_LENGTH 24 |
||||
|
||||
/* 3-byte get status command. Returns axis and buttons. Also
|
||||
* controls motor. */ |
||||
#define GC_GETSTATUS1 0x40 |
||||
#define GC_GETSTATUS2 0x03 |
||||
#define GC_GETSTATUS3(rumbling) ((rumbling) ? 0x01 : 0x00) |
||||
#define GC_GETSTATUS_REPLY_LENGTH 64 |
||||
|
||||
/* 3-byte poll keyboard command.
|
||||
* Source: http://hitmen.c02.at/files/yagcd/yagcd/chap9.html#sec9.3.3
|
||||
* */ |
||||
#define GC_POLL_KB1 0x54 |
||||
#define GC_POLL_KB2 0x00 |
||||
#define GC_POLL_KB3 0x00 |
||||
|
||||
/* Gamecube keycodes are from table 9.3.2:
|
||||
* http://hitmen.c02.at/files/yagcd/yagcd/chap9.html#sec9.3.2
|
||||
* |
||||
* But the table appears to have been built using a PC keyboard |
||||
* converted (Tototek). |
||||
* |
||||
* I was working with an ASCII keyboard so I made a few adjustments |
||||
* to reflect the /real/ key functions. For instance: |
||||
* |
||||
* LWIN, RWIN and MENU are in fact the Henkan, muhenkan and katakana/hiragana keys. |
||||
* They are equivalemnt to the HID keys International 4, International 5 and International 2. |
||||
* The - key (code 0x3F) is also HID international 1. |
||||
* The PrntScrn key (code 0x36) is in fact the Yen key (International 3). |
||||
* The bracket/brace [{ and }] keys are at different places on this Japanese keyboard, |
||||
* but following standard PC practice, some keycodes are named after the US usage. Hence, |
||||
* the aforementioned keys are the 2 keys right of P, even if they are not labeled. |
||||
*/ |
||||
#define GC_KEY_RESERVED 0x00 // No key
|
||||
|
||||
#define GC_KEY_HOME 0x06 |
||||
#define GC_KEY_END 0x07 |
||||
#define GC_KEY_PGUP 0x08 |
||||
#define GC_KEY_PGDN 0x09 |
||||
#define GC_KEY_SCROLL_LOCK 0x0A |
||||
|
||||
#define GC_KEY_A 0x10 |
||||
#define GC_KEY_B 0x11 |
||||
#define GC_KEY_C 0x12 |
||||
#define GC_KEY_D 0x13 |
||||
#define GC_KEY_E 0x14 |
||||
#define GC_KEY_F 0x15 |
||||
#define GC_KEY_G 0x16 |
||||
#define GC_KEY_H 0x17 |
||||
#define GC_KEY_I 0x18 |
||||
#define GC_KEY_J 0x19 |
||||
#define GC_KEY_K 0x1A |
||||
#define GC_KEY_L 0x1B |
||||
#define GC_KEY_M 0x1C |
||||
#define GC_KEY_N 0x1D |
||||
#define GC_KEY_O 0x1E |
||||
#define GC_KEY_P 0x1F |
||||
#define GC_KEY_Q 0x20 |
||||
#define GC_KEY_R 0x21 |
||||
#define GC_KEY_S 0x22 |
||||
#define GC_KEY_T 0x23 |
||||
#define GC_KEY_U 0x24 |
||||
#define GC_KEY_V 0x25 |
||||
#define GC_KEY_W 0x26 |
||||
#define GC_KEY_X 0x27 |
||||
#define GC_KEY_Y 0x28 |
||||
#define GC_KEY_Z 0x29 |
||||
#define GC_KEY_1 0x2A |
||||
#define GC_KEY_2 0x2B |
||||
#define GC_KEY_3 0x2C |
||||
#define GC_KEY_4 0x2D |
||||
#define GC_KEY_5 0x2E |
||||
#define GC_KEY_6 0x2F |
||||
#define GC_KEY_7 0x30 |
||||
#define GC_KEY_8 0x31 |
||||
#define GC_KEY_9 0x32 |
||||
#define GC_KEY_0 0x33 |
||||
#define GC_KEY_DASH_UNDERSCORE 0x34 // Key next to 0
|
||||
#define GC_KEY_PLUS_EQUAL 0x35 |
||||
#define GC_KEY_YEN 0x36 // Yen on ascii keyboard. HID International 3.
|
||||
#define GC_KEY_OPEN_BRKT_BRACE 0x37 // 1st key right of P
|
||||
#define GC_KEY_CLOSE_BRKT_BRACE 0x38 // 2nd key right of P
|
||||
#define GC_KEY_SEMI_COLON_COLON 0x39 // ;:
|
||||
#define GC_KEY_QUOTES 0x3A // '"
|
||||
|
||||
// The 3rd key after 'L'. HID Keyboard non-us # and ~
|
||||
#define GC_KEY_BRACKET_MU 0x3B |
||||
#define GC_KEY_COMMA_ST 0x3C // ,<
|
||||
#define GC_KEY_PERIOD_GT 0x3D // .>
|
||||
#define GC_KEY_SLASH_QUESTION 0x3E // /?
|
||||
|
||||
// (The extra key before right-shift on japanese keyboards.
|
||||
// HID code International 1 [HID usage tables Footnote 15-20]).
|
||||
#define GC_KEY_INTERNATIONAL1 0x3F |
||||
#define GC_KEY_F1 0x40 |
||||
#define GC_KEY_F2 0x41 |
||||
#define GC_KEY_F3 0x42 |
||||
#define GC_KEY_F4 0x43 |
||||
#define GC_KEY_F5 0x44 |
||||
#define GC_KEY_F6 0x45 |
||||
#define GC_KEY_F7 0x46 |
||||
#define GC_KEY_F8 0x47 |
||||
#define GC_KEY_F9 0x48 |
||||
#define GC_KEY_F10 0x49 |
||||
#define GC_KEY_F11 0x4A |
||||
#define GC_KEY_F12 0x4B |
||||
#define GC_KEY_ESC 0x4C |
||||
#define GC_KEY_INSERT 0x4D |
||||
#define GC_KEY_DELETE 0x4E |
||||
|
||||
// (Hankaku/zenkaku/kanji button). Also known as `~
|
||||
#define GC_KEY_HANKAKU 0x4F |
||||
#define GC_KEY_BACKSPACE 0x50 |
||||
#define GC_KEY_TAB 0x51 |
||||
|
||||
#define GC_KEY_CAPS_LOCK 0x53 |
||||
#define GC_KEY_LEFT_SHIFT 0x54 |
||||
#define GC_KEY_RIGHT_SHIFT 0x55 |
||||
#define GC_KEY_LEFT_CTRL 0x56 |
||||
#define GC_KEY_LEFT_ALT 0x57 |
||||
#define GC_KEY_MUHENKAN 0x58 // HID international 5
|
||||
#define GC_KEY_SPACE 0x59 |
||||
#define GC_KEY_HENKAN 0x5A // HID international 4
|
||||
#define GC_KEY_KANA 0x5B // HID international 2
|
||||
#define GC_KEY_LEFT 0x5C |
||||
#define GC_KEY_DOWN 0x5D |
||||
#define GC_KEY_UP 0x5E |
||||
#define GC_KEY_RIGHT 0x5F |
||||
|
||||
#define GC_KEY_ENTER 0x61 |
||||
|
||||
void gcn64protocol_hwinit(void); |
||||
int gcn64_detectController(void); |
||||
int gcn64_transaction(unsigned char *data_out, int data_out_len); |
||||
|
||||
unsigned char gcn64_protocol_getByte(int offset); |
||||
void gcn64_protocol_getBytes(int offset, int n_bytes, unsigned char *dstbuf); |
||||
|
||||
#endif // _gcn64_protocol_h__
|
@ -0,0 +1,436 @@
@@ -0,0 +1,436 @@
|
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
#include <avr/io.h> |
||||
#include <avr/interrupt.h> |
||||
|
||||
#include <util/delay.h> |
||||
|
||||
#include "usart1.h" |
||||
#include "usb.h" |
||||
#include "gamepad.h" |
||||
#include "gcn64_protocol.h" |
||||
#include "n64.h" |
||||
|
||||
#ifndef ARRAY_SIZE |
||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) |
||||
#endif |
||||
|
||||
#ifndef LPSTR |
||||
#define LPSTR(s) ((const PROGMEM wchar_t*)(s)) |
||||
#endif |
||||
|
||||
uint16_t hid_get_report(struct usb_request *rq, const uint8_t **dat); |
||||
uint8_t hid_set_report(const struct usb_request *rq, const uint8_t *dat, uint16_t len); |
||||
|
||||
#include "reportdesc.c" |
||||
|
||||
const wchar_t *const g_usb_strings[] = { |
||||
[0] = L"raphnet technologies", // 1 : Vendor
|
||||
[1] = L"GC/N64 to USB v3.0", // 2: Product
|
||||
[2] = L"123456", // 3 : Serial
|
||||
}; |
||||
|
||||
struct cfg0 { |
||||
struct usb_configuration_descriptor configdesc; |
||||
struct usb_interface_descriptor interface; |
||||
struct usb_hid_descriptor hid; |
||||
struct usb_endpoint_descriptor ep1_in; |
||||
}; |
||||
|
||||
static const struct cfg0 cfg0 PROGMEM = { |
||||
.configdesc = { |
||||
.bLength = sizeof(struct usb_configuration_descriptor), |
||||
.bDescriptorType = CONFIGURATION_DESCRIPTOR, |
||||
.wTotalLength = sizeof(cfg0), // includes all descriptors returned together
|
||||
.bNumInterfaces = 1, |
||||
.bConfigurationValue = 1, |
||||
.bmAttributes = CFG_DESC_ATTR_RESERVED, // set Self-powred and remote-wakeup here if needed.
|
||||
.bMaxPower = 25, // for 50mA
|
||||
}, |
||||
.interface = { |
||||
.bLength = sizeof(struct usb_interface_descriptor), |
||||
.bDescriptorType = INTERFACE_DESCRIPTOR, |
||||
.bInterfaceNumber = 0, |
||||
.bAlternateSetting = 0, |
||||
.bNumEndpoints = 1, |
||||
.bInterfaceClass = USB_DEVICE_CLASS_HID, |
||||
.bInterfaceSubClass = HID_SUBCLASS_NONE, |
||||
.bInterfaceProtocol = HID_PROTOCOL_NONE, |
||||
}, |
||||
.hid = { |
||||
.bLength = sizeof(struct usb_hid_descriptor), |
||||
.bDescriptorType = HID_DESCRIPTOR, |
||||
.bcdHid = 0x0101, |
||||
.bCountryCode = HID_COUNTRY_NOT_SUPPORTED, |
||||
.bNumDescriptors = 1, // Only a report descriptor
|
||||
.bClassDescriptorType = REPORT_DESCRIPTOR, |
||||
.wClassDescriptorLength = sizeof(gcn64_usbHidReportDescriptor), |
||||
}, |
||||
.ep1_in = { |
||||
.bLength = sizeof(struct usb_endpoint_descriptor), |
||||
.bDescriptorType = ENDPOINT_DESCRIPTOR, |
||||
.bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 1, // 0x81
|
||||
.bmAttributes = TRANSFER_TYPE_INT, |
||||
.wMaxPacketsize = 64, |
||||
.bInterval = LS_FS_INTERVAL_MS(1), |
||||
}, |
||||
}; |
||||
|
||||
const struct usb_device_descriptor device_descriptor PROGMEM = { |
||||
.bLength = sizeof(struct usb_device_descriptor), |
||||
.bDescriptorType = DEVICE_DESCRIPTOR, |
||||
.bcdUSB = 0x0101, |
||||
.bDeviceClass = 0, // set at interface
|
||||
.bDeviceSubClass = 0, // set at interface
|
||||
.bDeviceProtocol = 0, |
||||
.bMaxPacketSize = 64, |
||||
.idVendor = 0x289B, |
||||
.idProduct = 0x0017, |
||||
.bcdDevice = 0x0300, // 1.0.0
|
||||
.bNumConfigurations = 1, |
||||
.iManufacturer = 1, |
||||
.iProduct = 2, |
||||
.iSerialNumber = 3, |
||||
}; |
||||
|
||||
static struct usb_parameters usb_params = { |
||||
.flags = USB_PARAM_FLAG_CONFDESC_PROGMEM | |
||||
USB_PARAM_FLAG_DEVDESC_PROGMEM | |
||||
USB_PARAM_FLAG_REPORTDESC_PROGMEM, |
||||
.devdesc = (PGM_VOID_P)&device_descriptor, |
||||
.configdesc = (PGM_VOID_P)&cfg0, |
||||
.configdesc_ttllen = sizeof(cfg0), |
||||
.num_strings = ARRAY_SIZE(g_usb_strings), |
||||
.strings = g_usb_strings, |
||||
.reportdesc = gcn64_usbHidReportDescriptor, |
||||
.reportdesc_len = sizeof(gcn64_usbHidReportDescriptor), |
||||
.getReport = hid_get_report, |
||||
.setReport = hid_set_report, |
||||
}; |
||||
|
||||
void hwinit(void) |
||||
{ |
||||
/* PORTB
|
||||
* |
||||
* 7: NC Output low |
||||
* 6: NC Output low |
||||
* 5: NC Output low |
||||
* 4: NC Output low |
||||
* 3: MISO Output low |
||||
* 2: MOSI Output low |
||||
* 1: SCK Output low |
||||
* 0: NC Output low |
||||
*/ |
||||
PORTB = 0x00; |
||||
DDRB = 0xFF; |
||||
|
||||
/* PORTC
|
||||
* |
||||
* 7: NC Output low |
||||
* 6: NC Output low |
||||
* 5: NC Output low |
||||
* 4: NC Output low |
||||
* 3: (no such pin) |
||||
* 2: NC Output low |
||||
* 1: RESET (N/A: Reset input per fuses) |
||||
* 0: XTAL2 (N/A: Crystal oscillator) |
||||
*/ |
||||
PORTB = 0x00; |
||||
DDRB = 0xff; |
||||
|
||||
/* PORTD
|
||||
* |
||||
* 7: HWB Input (external pull-up) |
||||
* 6: NC Output low |
||||
* 5: NC Output low |
||||
* 4: NC Output low |
||||
* 3: IO3_MCU Input |
||||
* 2: IO2_MCU Input |
||||
* 1: IO1_MCU Input |
||||
* 0: IO0_MCU Input |
||||
*/ |
||||
PORTD = 0x00; |
||||
DDRD = 0x70; |
||||
|
||||
// System clock. External crystal is 16 Mhz and we want
|
||||
// to run at max. speed.
|
||||
CLKPR = 0x80; |
||||
CLKPR = 0x0; // Division factor of 1
|
||||
PRR0 = 0; |
||||
PRR1 = 0; |
||||
} |
||||
|
||||
static unsigned char hid_report_data[32]; |
||||
static unsigned char gamepad_report0[32]; |
||||
|
||||
// Output Report IDs for various functions
|
||||
#define REPORT_SET_EFFECT 0x01 |
||||
#define REPORT_SET_PERIODIC 0x04 |
||||
#define REPORT_SET_CONSTANT_FORCE 0x05 |
||||
#define REPORT_EFFECT_OPERATION 0x0A |
||||
#define REPORT_DISABLE_ACTUATORS 0x0C |
||||
#define REPORT_PID_POOL 0x0D |
||||
|
||||
// Feature reports
|
||||
#define REPORT_CREATE_EFFECT 0x09 |
||||
|
||||
// For the 'Usage Effect Operation' report
|
||||
#define EFFECT_OP_START 1 |
||||
#define EFFECT_OP_START_SOLO 2 |
||||
#define EFFECT_OP_STOP 3 |
||||
|
||||
// Feature report
|
||||
#define PID_SIMULTANEOUS_MAX 3 |
||||
#define PID_BLOCK_LOAD_REPORT 2 |
||||
|
||||
static volatile unsigned char gamepad_vibrate = 0; // output
|
||||
|
||||
static unsigned char vibration_on = 0; |
||||
static unsigned char constant_force = 0; |
||||
static unsigned char magnitude = 0; |
||||
|
||||
static unsigned char _FFB_effect_index; |
||||
#define LOOP_MAX 0xFFFF |
||||
static unsigned int _loop_count; |
||||
|
||||
static void effect_loop() |
||||
{ |
||||
if (_loop_count) { |
||||
if (_loop_count != LOOP_MAX) { |
||||
_loop_count--; |
||||
} |
||||
} |
||||
} |
||||
|
||||
static void decideVibration(void) |
||||
{ |
||||
if (!_loop_count) |
||||
vibration_on = 0; |
||||
|
||||
if (!vibration_on) { |
||||
gamepad_vibrate = 0; |
||||
} else { |
||||
if (constant_force > 0x7f) { |
||||
gamepad_vibrate = 1; |
||||
} |
||||
if (magnitude > 0x7f) { |
||||
gamepad_vibrate = 1; |
||||
} |
||||
} |
||||
} |
||||
|
||||
uint16_t hid_get_report(struct usb_request *rq, const uint8_t **dat) |
||||
{ |
||||
uint8_t report_id = (rq->wValue & 0xff); |
||||
|
||||
// USB HID 1.11 section 7.2.1 Get_Report
|
||||
// wValue high byte : report type
|
||||
// wValue low byte : report id
|
||||
switch (rq->wValue >> 8) |
||||
{ |
||||
case HID_REPORT_TYPE_INPUT: |
||||
{ |
||||
if (report_id == 1) { // Joystick
|
||||
// report_id = rq->wValue & 0xff
|
||||
// interface = rq->wIndex
|
||||
*dat = gamepad_report0; |
||||
printf("Get joy report\r\n"); |
||||
return 9; |
||||
} else if (report_id == 2) { // 2 : ES playing
|
||||
hid_report_data[0] = report_id; |
||||
hid_report_data[1] = 0; |
||||
hid_report_data[2] = _FFB_effect_index; |
||||
printf("ES playing\r\n"); |
||||
*dat = hid_report_data; |
||||
return 3; |
||||
} else { |
||||
printf("Get input report %d ??\r\n", rq->wValue & 0xff); |
||||
} |
||||
} |
||||
break; |
||||
|
||||
case HID_REPORT_TYPE_FEATURE: |
||||
if (report_id == PID_BLOCK_LOAD_REPORT) { |
||||
hid_report_data[0] = report_id; |
||||
hid_report_data[1] = 0x1; |
||||
hid_report_data[2] = 0x1; |
||||
hid_report_data[3] = 10; |
||||
hid_report_data[4] = 10; |
||||
printf("block load\r\n"); |
||||
*dat = hid_report_data; |
||||
return 5; |
||||
} |
||||
else if (report_id == PID_SIMULTANEOUS_MAX) { |
||||
hid_report_data[0] = report_id; |
||||
// ROM Effect Block count
|
||||
hid_report_data[1] = 0x1; |
||||
hid_report_data[2] = 0x1; |
||||
// PID pool move report?
|
||||
hid_report_data[3] = 0xff; |
||||
hid_report_data[4] = 1; |
||||
printf("simultaneous max\r\n"); |
||||
*dat = hid_report_data; |
||||
return 5; |
||||
} |
||||
else if (report_id == REPORT_CREATE_EFFECT) { |
||||
hid_report_data[0] = report_id; |
||||
hid_report_data[1] = 1; |
||||
printf("create effect\r\n"); |
||||
*dat = hid_report_data; |
||||
return 2; |
||||
} else { |
||||
printf("Unknown feature %d\r\n", rq->wValue & 0xff); |
||||
} |
||||
break; |
||||
} |
||||
|
||||
printf("Unhandled hid get report type=0x%02x, rq=0x%02x, wVal=0x%04x, wLen=0x%04x\r\n", rq->bmRequestType, rq->bRequest, rq->wValue, rq->wLength); |
||||
return 0; |
||||
} |
||||
|
||||
uint8_t hid_set_report(const struct usb_request *rq, const uint8_t *data, uint16_t len) |
||||
{ |
||||
if (len < 1) { |
||||
printf("shrt\n"); |
||||
return -1; |
||||
} |
||||
|
||||
if ((rq->wValue >> 8) == HID_REPORT_TYPE_OUTPUT) { |
||||
|
||||
switch(data[0]) |
||||
{ |
||||
case REPORT_DISABLE_ACTUATORS: |
||||
printf("disable actuators\r\n"); |
||||
break; |
||||
case REPORT_PID_POOL: |
||||
printf("pid pool\r\n"); |
||||
break; |
||||
case REPORT_SET_EFFECT: |
||||
_FFB_effect_index = data[1]; |
||||
printf("set effect %d\n", data[1]); |
||||
break; |
||||
case REPORT_SET_PERIODIC: |
||||
magnitude = data[2]; |
||||
decideVibration(); |
||||
printf("periodic mag: %d", data[2]); |
||||
break; |
||||
case REPORT_SET_CONSTANT_FORCE: |
||||
if (data[1] == 1) { |
||||
constant_force = data[2]; |
||||
decideVibration(); |
||||
printf("Constant force"); |
||||
} |
||||
break; |
||||
case REPORT_EFFECT_OPERATION: |
||||
if (len != 4) |
||||
return -1; |
||||
printf("EFFECT OP\n"); |
||||
/* Byte 0 : report ID
|
||||
* Byte 1 : bit 7=rom flag, bits 6-0=effect block index |
||||
* Byte 2 : Effect operation |
||||
* Byte 3 : Loop count */ |
||||
_loop_count = data[3]<<3; |
||||
|
||||
switch(data[1] & 0x7F) // Effect block index
|
||||
{ |
||||
case 1: // constant force
|
||||
case 3: // square
|
||||
case 4: // sine
|
||||
switch (data[2]) // effect operation
|
||||
{ |
||||
case EFFECT_OP_START: |
||||
vibration_on = 1; |
||||
decideVibration(); |
||||
break; |
||||
|
||||
case EFFECT_OP_START_SOLO: |
||||
vibration_on = 1; |
||||
decideVibration(); |
||||
break; |
||||
|
||||
case EFFECT_OP_STOP: |
||||
vibration_on = 0; |
||||
decideVibration(); |
||||
break; |
||||
} |
||||
break; |
||||
|
||||
// TODO : should probably drop these from the descriptor since they are
|
||||
|
||||
case 2: // ramp
|
||||
case 5: // triangle
|
||||
case 6: // sawtooth up
|
||||
case 7: // sawtooth down
|
||||
case 8: // spring
|
||||
case 9: // damper
|
||||
case 10: // inertia
|
||||
case 11: // friction
|
||||
case 12: // custom force data
|
||||
printf("Ununsed effect %d\n", data[1] & 0x7F); |
||||
break; |
||||
} |
||||
break; |
||||
default: |
||||
printf("Set output report 0x%02x\r\n", data[0]); |
||||
} |
||||
} |
||||
else if ((rq->wValue >> 8) == HID_REPORT_TYPE_FEATURE) { |
||||
switch(data[0]) |
||||
{ |
||||
case REPORT_CREATE_EFFECT: |
||||
_FFB_effect_index = data[1]; |
||||
printf("create effect %d\n", data[1]); |
||||
break; |
||||
|
||||
default: |
||||
printf("What?\n"); |
||||
} |
||||
} |
||||
else { |
||||
printf("impossible\n"); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
int main(void) |
||||
{ |
||||
Gamepad *pad = NULL; |
||||
|
||||
hwinit(); |
||||
usart1_init(); |
||||
gcn64protocol_hwinit(); |
||||
|
||||
pad = n64GetGamepad(); |
||||
|
||||
sei(); |
||||
usb_init(&usb_params); |
||||
|
||||
while (1) |
||||
{ |
||||
static char last_v = 0; |
||||
|
||||
usb_doTasks(); |
||||
_delay_ms(5); |
||||
effect_loop(); |
||||
decideVibration(); |
||||
|
||||
if (last_v != gamepad_vibrate) { |
||||
if (pad->setVibration) { |
||||
pad->setVibration(gamepad_vibrate); |
||||
} |
||||
last_v = gamepad_vibrate; |
||||
} |
||||
|
||||
pad->update(); |
||||
if (pad->changed(1)) { |
||||
int report_size; |
||||
|
||||
report_size = pad->buildReport(gamepad_report0, 1); |
||||
usb_interruptSend(gamepad_report0, report_size); |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,332 @@
@@ -0,0 +1,332 @@
|
||||
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
||||
Copyright (C) 2007-2014 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 <avr/io.h> |
||||
#include <avr/interrupt.h> |
||||
#include <util/delay.h> |
||||
#include <string.h> |
||||
#include "gamepad.h" |
||||
#include "n64.h" |
||||
#include "gcn64_protocol.h" |
||||
|
||||
#define GCN64_REPORT_SIZE 9 |
||||
|
||||
#undef BUTTON_A_RUMBLE_TEST |
||||
|
||||
/*********** prototypes *************/ |
||||
static void n64Init(void); |
||||
static char n64Update(void); |
||||
static char n64Changed(int id); |
||||
static int n64BuildReport(unsigned char *reportBuffer, int id); |
||||
static void n64SetVibration(int value); |
||||
|
||||
static char must_rumble = 0; |
||||
#ifdef BUTTON_A_RUMBLE_TEST |
||||
static char force_rumble = 0; |
||||
#endif |
||||
|
||||
/* What was most recently read from the controller */ |
||||
static unsigned char last_built_report[GCN64_REPORT_SIZE]; |
||||
|
||||
/* What was most recently sent to the host */ |
||||
static unsigned char last_sent_report[GCN64_REPORT_SIZE]; |
||||
|
||||
static void n64Init(void) |
||||
{ |
||||
n64Update(); |
||||
} |
||||
|
||||
#define RSTATE_INIT 0 |
||||
#define RSTATE_OFF 1 |
||||
#define RSTATE_TURNON 2 |
||||
#define RSTATE_ON 3 |
||||
#define RSTATE_TURNOFF 4 |
||||
#define RSTATE_UNAVAILABLE 5 |
||||
static unsigned char n64_rumble_state = RSTATE_UNAVAILABLE; |
||||
unsigned char tmpdata[40]; |
||||
|
||||
static char initRumble(void) |
||||
{ |
||||
int count; |
||||
|
||||
tmpdata[0] = N64_EXPANSION_WRITE; |
||||
tmpdata[1] = 0x80; |
||||
tmpdata[2] = 0x01; |
||||
memset(tmpdata+3, 0x80, 32); |
||||
|
||||
/* Note: The old test (count > 0) was not reliable. */ |
||||
count = gcn64_transaction(tmpdata, 35); |
||||
if (count == 8) |
||||
return 0; |
||||
|
||||
return -1; |
||||
} |
||||
|
||||
static char controlRumble(char enable) |
||||
{ |
||||
int count; |
||||
|
||||
tmpdata[0] = N64_EXPANSION_WRITE; |
||||
tmpdata[1] = 0xc0; |
||||
tmpdata[2] = 0x1b; |
||||
memset(tmpdata+3, enable ? 0x01 : 0x00, 32); |
||||
count = gcn64_transaction(tmpdata, 35); |
||||
if (count == 8) |
||||
return 0; |
||||
|
||||
return -1; |
||||
} |
||||
|
||||
static char n64Update(void) |
||||
{ |
||||
int i; |
||||
unsigned char count; |
||||
unsigned char x,y; |
||||
unsigned char btns1, btns2; |
||||
unsigned char rb1, rb2; |
||||
unsigned char caps[3]; |
||||
|
||||
/* Pad answer to N64_GET_CAPABILITIES
|
||||
* |
||||
* 0x050000 : 0000 0101 0000 0000 0000 0000 : No expansion pack |
||||
* 0x050001 : 0000 0101 0000 0000 0000 0001 : With expansion pack |
||||
* 0x050002 : 0000 0101 0000 0000 0000 0010 : Expansion pack removed |
||||
* |
||||
* Bit 0 tells us if there is something connected to the expansion port. |
||||
* Bit 1 tells is if there was something connected that has been removed. |
||||
*/ |
||||
tmpdata[0] = N64_GET_CAPABILITIES; |
||||
count = gcn64_transaction(tmpdata, 1); |
||||
if (count != N64_CAPS_REPLY_LENGTH) { |
||||
// a failed read could mean the pack or controller was gone. Init
|
||||
// will be necessary next time we detect a pack is present.
|
||||
n64_rumble_state = RSTATE_INIT; |
||||
return -1; |
||||
} |
||||
|
||||
caps[0] = gcn64_protocol_getByte(0); |
||||
caps[1] = gcn64_protocol_getByte(8); |
||||
caps[2] = gcn64_protocol_getByte(16); |
||||
|
||||
/* Detect when a pack becomes present and schedule initialisation when it happens. */ |
||||
if ((caps[2] & 0x01) && (n64_rumble_state == RSTATE_UNAVAILABLE)) { |
||||
n64_rumble_state = RSTATE_INIT; |
||||
} |
||||
|
||||
/* Detect when a pack is removed. */ |
||||
if (!(caps[2] & 0x01) || (caps[2] & 0x02) ) { |
||||
n64_rumble_state = RSTATE_UNAVAILABLE; |
||||
} |
||||
#ifdef BUTTON_A_RUMBLE_TEST |
||||
must_rumble = force_rumble; |
||||
#endif |
||||
|
||||
switch (n64_rumble_state) |
||||
{ |
||||
case RSTATE_INIT: |
||||
/* Retry until the controller answers with a full byte. */ |
||||
if (initRumble() != 0) { |
||||
if (initRumble() != 0) { |
||||
n64_rumble_state = RSTATE_UNAVAILABLE; |
||||
} |
||||
break; |
||||
} |
||||
|
||||
if (must_rumble) { |
||||
controlRumble(1); |
||||
n64_rumble_state = RSTATE_ON; |
||||
} else { |
||||
controlRumble(0); |
||||
n64_rumble_state = RSTATE_OFF; |
||||
} |
||||
break; |
||||
|
||||
case RSTATE_TURNON: |
||||
if (0 == controlRumble(1)) { |
||||
n64_rumble_state = RSTATE_ON; |
||||
} |
||||
break; |
||||
|
||||
case RSTATE_TURNOFF: |
||||
if (0 == controlRumble(0)) { |
||||
n64_rumble_state = RSTATE_OFF; |
||||
} |
||||
break; |
||||
|
||||
case RSTATE_ON: |
||||
if (!must_rumble) { |
||||
controlRumble(0); |
||||
n64_rumble_state = RSTATE_OFF; |
||||
} |
||||
break; |
||||
|
||||
case RSTATE_OFF: |
||||
if (must_rumble) { |
||||
controlRumble(1); |
||||
n64_rumble_state = RSTATE_ON; |
||||
} |
||||
break; |
||||
} |
||||
|
||||
tmpdata[0] = N64_GET_STATUS; |
||||
count = gcn64_transaction(tmpdata, 1); |
||||
if (count != N64_GET_STATUS_REPLY_LENGTH) { |
||||
return -1; |
||||
} |
||||
|
||||
/*
|
||||
Bit Function |
||||
0 A |
||||
1 B |
||||
2 Z |
||||
3 Start |
||||
4 Directional Up |
||||
5 Directional Down |
||||
6 Directional Left |
||||
7 Directional Right |
||||
8 unknown (always 0) |
||||
9 unknown (always 0) |
||||
10 L |
||||
11 R |
||||
12 C Up |
||||
13 C Down |
||||
14 C Left |
||||
15 C Right |
||||
16-23: analog X axis |
||||
24-31: analog Y axis |
||||
*/ |
||||
|
||||
btns1 = gcn64_protocol_getByte(0); |
||||
btns2 = gcn64_protocol_getByte(8); |
||||
x = gcn64_protocol_getByte(16); // X axis
|
||||
y = gcn64_protocol_getByte(24); // Y axis
|
||||
|
||||
#ifdef BUTTON_A_RUMBLE_TEST |
||||
if (btns1 & 0x80) { |
||||
force_rumble = 1; |
||||
} else { |
||||
force_rumble = 0; |
||||
} |
||||
#endif |
||||
|
||||
// Remap buttons as they always were by this
|
||||
// adapter. Might change in v3 when a N64
|
||||
// specific report descriptor will be used.
|
||||
//
|
||||
rb1 = rb2 = 0; |
||||
for (i=0; i<4; i++) // A B Z START
|
||||
rb1 |= (btns1 & (0x80 >> i)) ? (0x01<<i) : 0; |
||||
for (i=0; i<4; i++) // C-UP C-DOWN C-LEFT C-RIGHT
|
||||
rb1 |= btns2 & (0x08 >> i) ? (0x10<<i) : 0; |
||||
for (i=0; i<2; i++) // L R
|
||||
rb2 |= btns2 & (0x20 >> i) ? (0x01<<i) : 0; |
||||
for (i=0; i<4; i++) // Up down left right
|
||||
rb2 |= btns1 & (0x08 >> i) ? (0x04<<i) : 0; |
||||
|
||||
x = (x ^ 0x80) - 1; |
||||
y = ((y ^ 0x80) ) ^ 0xFF; |
||||
|
||||
// The following helps a cheap TTX controller
|
||||
// which uses the full 8 bit range instead
|
||||
// of +/- 80. The specific test here prevents
|
||||
// receiving a value of 128 (instead of -127).
|
||||
//
|
||||
// This will have no effect on "normal" controllers.
|
||||
if (x == 0xFF) |
||||
x = 0; |
||||
|
||||
// analog joystick
|
||||
last_built_report[0] = 1; // ID
|
||||
last_built_report[1] = x; |
||||
last_built_report[2] = y; |
||||
|
||||
last_built_report[3] = 0x7f; |
||||
last_built_report[4] = 0x7f; |
||||
last_built_report[5] = 0x7f; |
||||
last_built_report[6] = 0x7f; |
||||
|
||||
// buttons
|
||||
last_built_report[7] = rb1; |
||||
last_built_report[8] = rb2; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static char n64Probe(void) |
||||
{ |
||||
int count; |
||||
char i; |
||||
unsigned char tmp; |
||||
|
||||
/* Pad answer to N64_GET_CAPABILITIES
|
||||
* |
||||
* 0x050000 : 0000 0101 0000 0000 0000 0000 : No expansion pack |
||||
* 0x050001 : 0000 0101 0000 0000 0000 0001 : With expansion pack |
||||
* 0x050002 : 0000 0101 0000 0000 0000 0010 : Expansion pack removed |
||||
* |
||||
* Bit 0 tells us if there is something connected to the expansion port. |
||||
* Bit 1 tells is if there was something connected that has been removed. |
||||
*/ |
||||
|
||||
n64_rumble_state = RSTATE_UNAVAILABLE; |
||||
|
||||
for (i=0; i<15; i++) |
||||
{ |
||||
_delay_ms(30); |
||||
|
||||
tmp = N64_GET_CAPABILITIES; |
||||
count = gcn64_transaction(&tmp, 1); |
||||
|
||||
if (count == N64_CAPS_REPLY_LENGTH) { |
||||
return 1; |
||||
} |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
static char n64Changed(int id) |
||||
{ |
||||
return memcmp(last_built_report, last_sent_report, GCN64_REPORT_SIZE); |
||||
} |
||||
|
||||
static int n64BuildReport(unsigned char *reportBuffer, int id) |
||||
{ |
||||
if (reportBuffer) |
||||
memcpy(reportBuffer, last_built_report, GCN64_REPORT_SIZE); |
||||
|
||||
memcpy( last_sent_report, last_built_report, GCN64_REPORT_SIZE); |
||||
return GCN64_REPORT_SIZE; |
||||
} |
||||