mirror of
https://github.com/raphnet/gc_n64_usb-v3
synced 2024-12-21 06:48:52 -05:00
WIP
This commit is contained in:
commit
c59242383f
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
*.o
|
||||
*.elf
|
||||
*.hex
|
||||
*.map
|
||||
*.swp
|
44
Makefile
Normal file
44
Makefile
Normal file
@ -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
|
25
gamepad.h
Normal file
25
gamepad.h
Normal file
@ -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__
|
||||
|
||||
|
492
gcn64_protocol.c
Normal file
492
gcn64_protocol.c
Normal file
@ -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;
|
||||
}
|
||||
|
||||
|
168
gcn64_protocol.h
Normal file
168
gcn64_protocol.h
Normal file
@ -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__
|
436
main.c
Normal file
436
main.c
Normal file
@ -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;
|
||||
}
|
332
n64.c
Normal file
332
n64.c
Normal file
@ -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;
|
||||
}
|
||||
|
||||
static void n64SetVibration(int value)
|
||||
{
|
||||
must_rumble = value;
|
||||
}
|
||||
|
||||
static Gamepad N64Gamepad = {
|
||||
.init = n64Init,
|
||||
.update = n64Update,
|
||||
.changed = n64Changed,
|
||||
.buildReport = n64BuildReport,
|
||||
.probe = n64Probe,
|
||||
.num_reports = 1,
|
||||
.setVibration = n64SetVibration,
|
||||
};
|
||||
|
||||
Gamepad *n64GetGamepad(void)
|
||||
{
|
||||
return &N64Gamepad;
|
||||
}
|
652
reportdesc.c
Normal file
652
reportdesc.c
Normal file
@ -0,0 +1,652 @@
|
||||
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
||||
Copyright (C) 2007-2013 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/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The original file gamepad.c contained this descriptor which was
|
||||
* adapted from my original reportdesc.c by Sean Green for force
|
||||
* feedback support. Big thanks to him from sharing back!
|
||||
*
|
||||
* The descriptor is intact, except for minor changes such as
|
||||
* axis types and button quantity.
|
||||
*/
|
||||
|
||||
const uint8_t gcn64_usbHidReportDescriptor[] PROGMEM = {
|
||||
///// gampad
|
||||
0x05,0x01, // Usage Page Generic Desktop
|
||||
0x09,0x05, // Usage Joystick
|
||||
0xA1,0x01, // Collection Application
|
||||
//axis
|
||||
0x85,0x01, // Report ID 1
|
||||
// not sure how this is to work?
|
||||
// 0x05, 0x02, //USAGE_PAGE (Simulation Controls)
|
||||
//0x09, 0xBB, //USAGE (Throttle)
|
||||
0x09, 0x01, // usage pointer
|
||||
0xA1, 0x00, // COLLECTION (phys)
|
||||
0x05, 0x01, // USAGE_PAGE (Generic desktop)
|
||||
0x75, 0x08, // REPORT_SIZE (8)
|
||||
0x95, 0x06, // REPORT_COUNT (6)
|
||||
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||
0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM (255)
|
||||
0x35, 0x00, // Physical Minimum (0)
|
||||
0x46, 0xFF, 0x00, // Physical Minimum (255)
|
||||
0x09, 0x30, // USAGE (X)
|
||||
0x09, 0x31, // USAGE (Y)
|
||||
0x09, 0x33, // USAGE (Rx)
|
||||
0x09, 0x34, // USAGE (Ry)
|
||||
0x09, 0x35, // USAGE (Rz)
|
||||
0x09, 0x36, // USAGE (Slider)
|
||||
0x81, 0x02, // INPUT
|
||||
|
||||
#define NUM_BUTTONS 16
|
||||
|
||||
//buttons
|
||||
0x05, 0x09, // USAGE_PAGE (Button)
|
||||
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||
0x25, 0x01, // LOGICAL_MAXIMUM (1)
|
||||
0x75, 0x01, // REPORT_SIZE (1)
|
||||
0x95, NUM_BUTTONS, // REPORT_COUNT (14)
|
||||
0x19, 0x01, // USAGE_MINIMUM (Button 1)
|
||||
0x29, NUM_BUTTONS, // USAGE_MAXIMUM (Button 14)
|
||||
0x75, 0x01, // REPORT_SIZE (1)
|
||||
0x95, NUM_BUTTONS, // REPORT_COUNT (16)
|
||||
0x81, 0x02, // INPUT
|
||||
|
||||
0xc0, // END COLLECTION
|
||||
#if 0
|
||||
//???
|
||||
0x06,0x01,0xFF, // Usage Page Generic Desktop
|
||||
0x09,0x49, // Usage Undefined
|
||||
0x75,0x01, // Report Size 1
|
||||
0x95,0x01, // Report Count 1
|
||||
0x81,0x02, // Input (Variable)
|
||||
0x75,0x07, // Report Size 7
|
||||
0x81,0x03, // Input (Constant, Variable)
|
||||
#endif
|
||||
|
||||
|
||||
0x05,0x0F, // Usage Page Physical Interface
|
||||
0x09,0x92, // Usage ES Playing
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x85,0x02, // Report ID 2
|
||||
0x09,0x9F, // Usage DS Device is Reset
|
||||
0x09,0xA0, // Usage DS Device is Pause
|
||||
0x09,0xA4, // Usage Actuator Power
|
||||
0x09,0xA5, // Usage Undefined
|
||||
0x09,0xA6, // Usage Undefined
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x25,0x01, // Logical Maximum 1
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x45,0x01, // Physical Maximum 1
|
||||
0x75,0x01, // Report Size 1
|
||||
0x95,0x05, // Report Count 5
|
||||
0x81,0x02, // Input (Variable)
|
||||
0x95,0x03, // Report Count 3
|
||||
0x75,0x01, // Report Size 1
|
||||
0x81,0x03, // Input (Constant, Variable)
|
||||
0x09,0x94, // Usage PID Device Control
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x25,0x01, // Logical Maximum 1
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x45,0x01, // Physical Maximum 1
|
||||
0x75,0x01, // Report Size 1
|
||||
0x95,0x01, // Report Count 1
|
||||
0x81,0x02, // Input (Variable)
|
||||
0x09,0x22, // Usage Effect Block Index
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x25,0x28, // Logical Maximum 28h (40d)
|
||||
0x35,0x01, // Physical Minimum 1
|
||||
0x45,0x28, // Physical Maximum 28h (40d)
|
||||
0x75,0x07, // Report Size 7
|
||||
0x95,0x01, // Report Count 1
|
||||
0x81,0x02, // Input (Variable)
|
||||
0xC0 , // End Collection
|
||||
|
||||
|
||||
0x09,0x21, // Usage Set Effect Report
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x85,0x01, // Report ID 1
|
||||
0x09,0x22, // Usage Effect Block Index
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x25,0x28, // Logical Maximum 28h (40d)
|
||||
0x35,0x01, // Physical Minimum 1
|
||||
0x45,0x28, // Physical Maximum 28h (40d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x25, // Usage Effect Type
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x09,0x26, // Usage ET Constant Force
|
||||
0x09,0x27, // Usage ET Ramp
|
||||
0x09,0x30, // Usage ET Square
|
||||
0x09,0x31, // Usage ET Sine
|
||||
0x09,0x32, // Usage ET Triangle
|
||||
0x09,0x33, // Usage ET Sawtooth Up
|
||||
0x09,0x34, // Usage ET Sawtooth Down
|
||||
0x09,0x40, // Usage ET Spring
|
||||
0x09,0x41, // Usage ET Damper
|
||||
0x09,0x42, // Usage ET Inertia
|
||||
0x09,0x43, // Usage ET Friction
|
||||
0x09,0x28, // Usage ET Custom Force Data
|
||||
0x25,0x0C, // Logical Maximum Ch (12d)
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x35,0x01, // Physical Minimum 1
|
||||
0x45,0x0C, // Physical Maximum Ch (12d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x00, // Output
|
||||
0xC0 , // End Collection
|
||||
|
||||
0x09,0x50, // Usage Duration
|
||||
0x09,0x54, // Usage Trigger Repeat Interval
|
||||
0x09,0x51, // Usage Sample Period
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d)
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d)
|
||||
0x66,0x03,0x10, // Unit 1003h (4099d)
|
||||
0x55,0xFD, // Unit Exponent FDh (253d)
|
||||
0x75,0x10, // Report Size 10h (16d)
|
||||
0x95,0x03, // Report Count 3
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x55,0x00, // Unit Exponent 0
|
||||
0x66,0x00,0x00, // Unit 0
|
||||
0x09,0x52, // Usage Gain
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x26,0xFF,0x00, // Logical Maximum FFh (255d)
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x46,0x10,0x27, // Physical Maximum 2710h (10000d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x53, // Usage Trigger Button
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x25,0x08, // Logical Maximum 8
|
||||
0x35,0x01, // Physical Minimum 1
|
||||
0x45,0x08, // Physical Maximum 8
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x55, // Usage Axes Enable
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x05,0x01, // Usage Page Generic Desktop
|
||||
0x09,0x30, // Usage X
|
||||
0x09,0x31, // Usage Y
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x25,0x01, // Logical Maximum 1
|
||||
0x75,0x01, // Report Size 1
|
||||
0x95,0x02, // Report Count 2
|
||||
0x91,0x02, // Output (Variable)
|
||||
0xC0 , // End Collection
|
||||
0x05,0x0F, // Usage Page Physical Interface
|
||||
0x09,0x56, // Usage Direction Enable
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x95,0x05, // Report Count 5
|
||||
0x91,0x03, // Output (Constant, Variable)
|
||||
0x09,0x57, // Usage Direction
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x0B,0x01,0x00,0x0A,0x00, // Usage Ordinals: Instance 1
|
||||
0x0B,0x02,0x00,0x0A,0x00, // Usage Ordinals: Instance 2
|
||||
0x66,0x14,0x00, // Unit 14h (20d)
|
||||
0x55,0xFE, // Unit Exponent FEh (254d)
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x26,0xFF,0x00, // Logical Maximum FFh (255d)
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x47,0xA0,0x8C,0x00,0x00, // Physical Maximum 8CA0h (36000d)
|
||||
0x66,0x00,0x00, // Unit 0
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x02, // Report Count 2
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x55,0x00, // Unit Exponent 0
|
||||
0x66,0x00,0x00, // Unit 0
|
||||
0xC0 , // End Collection
|
||||
0x05,0x0F, // Usage Page Physical Interface
|
||||
0x09,0xA7, // Usage Undefined
|
||||
0x66,0x03,0x10, // Unit 1003h (4099d)
|
||||
0x55,0xFD, // Unit Exponent FDh (253d)
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d)
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d)
|
||||
0x75,0x10, // Report Size 10h (16d)
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x66,0x00,0x00, // Unit 0
|
||||
0x55,0x00, // Unit Exponent 0
|
||||
0xC0 , // End Collection
|
||||
0x05,0x0F, // Usage Page Physical Interface
|
||||
0x09,0x5A, // Usage Set Envelope Report
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x85,0x02, // Report ID 2
|
||||
0x09,0x22, // Usage Effect Block Index
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x25,0x28, // Logical Maximum 28h (40d)
|
||||
0x35,0x01, // Physical Minimum 1
|
||||
0x45,0x28, // Physical Maximum 28h (40d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x5B, // Usage Attack Level
|
||||
0x09,0x5D, // Usage Fade Level
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x26,0xFF,0x00, // Logical Maximum FFh (255d)
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x46,0x10,0x27, // Physical Maximum 2710h (10000d)
|
||||
0x95,0x02, // Report Count 2
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x5C, // Usage Attack Time
|
||||
0x09,0x5E, // Usage Fade Time
|
||||
0x66,0x03,0x10, // Unit 1003h (4099d)
|
||||
0x55,0xFD, // Unit Exponent FDh (253d)
|
||||
0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d)
|
||||
0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d)
|
||||
0x75,0x10, // Report Size 10h (16d)
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x45,0x00, // Physical Maximum 0
|
||||
0x66,0x00,0x00, // Unit 0
|
||||
0x55,0x00, // Unit Exponent 0
|
||||
0xC0 , // End Collection
|
||||
0x09,0x5F, // Usage Set Condition Report
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x85,0x03, // Report ID 3
|
||||
0x09,0x22, // Usage Effect Block Index
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x25,0x28, // Logical Maximum 28h (40d)
|
||||
0x35,0x01, // Physical Minimum 1
|
||||
0x45,0x28, // Physical Maximum 28h (40d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x23, // Usage Parameter Block Offset
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x25,0x01, // Logical Maximum 1
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x45,0x01, // Physical Maximum 1
|
||||
0x75,0x04, // Report Size 4
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x58, // Usage Type Specific Block Off...
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x0B,0x01,0x00,0x0A,0x00, // Usage Ordinals: Instance 1
|
||||
0x0B,0x02,0x00,0x0A,0x00, // Usage Ordinals: Instance 2
|
||||
0x75,0x02, // Report Size 2
|
||||
0x95,0x02, // Report Count 2
|
||||
0x91,0x02, // Output (Variable)
|
||||
0xC0 , // End Collection
|
||||
0x15,0x80, // Logical Minimum 80h (-128d)
|
||||
0x25,0x7F, // Logical Maximum 7Fh (127d)
|
||||
0x36,0xF0,0xD8, // Physical Minimum D8F0h (-10000d)
|
||||
0x46,0x10,0x27, // Physical Maximum 2710h (10000d)
|
||||
0x09,0x60, // Usage CP Offset
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x36,0xF0,0xD8, // Physical Minimum D8F0h (-10000d)
|
||||
0x46,0x10,0x27, // Physical Maximum 2710h (10000d)
|
||||
0x09,0x61, // Usage Positive Coefficient
|
||||
0x09,0x62, // Usage Negative Coefficient
|
||||
0x95,0x02, // Report Count 2
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x26,0xFF,0x00, // Logical Maximum FFh (255d)
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x46,0x10,0x27, // Physical Maximum 2710h (10000d)
|
||||
0x09,0x63, // Usage Positive Saturation
|
||||
0x09,0x64, // Usage Negative Saturation
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x02, // Report Count 2
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x65, // Usage Dead Band
|
||||
0x46,0x10,0x27, // Physical Maximum 2710h (10000d)
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0xC0 , // End Collection
|
||||
0x09,0x6E, // Usage Set Periodic Report
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x85,0x04, // Report ID 4
|
||||
0x09,0x22, // Usage Effect Block Index
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x25,0x28, // Logical Maximum 28h (40d)
|
||||
0x35,0x01, // Physical Minimum 1
|
||||
0x45,0x28, // Physical Maximum 28h (40d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x70, // Usage Magnitude
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x26,0xFF,0x00, // Logical Maximum FFh (255d)
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x46,0x10,0x27, // Physical Maximum 2710h (10000d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x6F, // Usage Offset
|
||||
0x15,0x80, // Logical Minimum 80h (-128d)
|
||||
0x25,0x7F, // Logical Maximum 7Fh (127d)
|
||||
0x36,0xF0,0xD8, // Physical Minimum D8F0h (-10000d)
|
||||
0x46,0x10,0x27, // Physical Maximum 2710h (10000d)
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x71, // Usage Phase
|
||||
0x66,0x14,0x00, // Unit 14h (20d)
|
||||
0x55,0xFE, // Unit Exponent FEh (254d)
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x26,0xFF,0x00, // Logical Maximum FFh (255d)
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x47,0xA0,0x8C,0x00,0x00, // Physical Maximum 8CA0h (36000d)
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x72, // Usage Period
|
||||
0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d)
|
||||
0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d)
|
||||
0x66,0x03,0x10, // Unit 1003h (4099d)
|
||||
0x55,0xFD, // Unit Exponent FDh (253d)
|
||||
0x75,0x10, // Report Size 10h (16d)
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x66,0x00,0x00, // Unit 0
|
||||
0x55,0x00, // Unit Exponent 0
|
||||
0xC0 , // End Collection
|
||||
0x09,0x73, // Usage Set Constant Force Rep...
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x85,0x05, // Report ID 5
|
||||
0x09,0x22, // Usage Effect Block Index
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x25,0x28, // Logical Maximum 28h (40d)
|
||||
0x35,0x01, // Physical Minimum 1
|
||||
0x45,0x28, // Physical Maximum 28h (40d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x70, // Usage Magnitude
|
||||
0x16,0x01,0xFF, // Logical Minimum FF01h (-255d)
|
||||
0x26,0xFF,0x00, // Logical Maximum FFh (255d)
|
||||
0x36,0xF0,0xD8, // Physical Minimum D8F0h (-10000d)
|
||||
0x46,0x10,0x27, // Physical Maximum 2710h (10000d)
|
||||
0x75,0x10, // Report Size 10h (16d)
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0xC0 , // End Collection
|
||||
0x09,0x74, // Usage Set Ramp Force Report
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x85,0x06, // Report ID 6
|
||||
0x09,0x22, // Usage Effect Block Index
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x25,0x28, // Logical Maximum 28h (40d)
|
||||
0x35,0x01, // Physical Minimum 1
|
||||
0x45,0x28, // Physical Maximum 28h (40d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x75, // Usage Ramp Start
|
||||
0x09,0x76, // Usage Ramp End
|
||||
0x15,0x80, // Logical Minimum 80h (-128d)
|
||||
0x25,0x7F, // Logical Maximum 7Fh (127d)
|
||||
0x36,0xF0,0xD8, // Physical Minimum D8F0h (-10000d)
|
||||
0x46,0x10,0x27, // Physical Maximum 2710h (10000d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x02, // Report Count 2
|
||||
0x91,0x02, // Output (Variable)
|
||||
0xC0 , // End Collection
|
||||
0x09,0x68, // Usage Custom Force Data Rep...
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x85,0x07, // Report ID 7
|
||||
0x09,0x22, // Usage Effect Block Index
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x25,0x28, // Logical Maximum 28h (40d)
|
||||
0x35,0x01, // Physical Minimum 1
|
||||
0x45,0x28, // Physical Maximum 28h (40d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x6C, // Usage Custom Force Data Offset
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x26,0x10,0x27, // Logical Maximum 2710h (10000d)
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x46,0x10,0x27, // Physical Maximum 2710h (10000d)
|
||||
0x75,0x10, // Report Size 10h (16d)
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x69, // Usage Custom Force Data
|
||||
0x15,0x81, // Logical Minimum 81h (-127d)
|
||||
0x25,0x7F, // Logical Maximum 7Fh (127d)
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x46,0xFF,0x00, // Physical Maximum FFh (255d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x0C, // Report Count Ch (12d)
|
||||
0x92,0x02,0x01, // Output (Variable, Buffered)
|
||||
0xC0 , // End Collection
|
||||
0x09,0x66, // Usage Download Force Sample
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x85,0x08, // Report ID 8
|
||||
0x05,0x01, // Usage Page Generic Desktop
|
||||
0x09,0x30, // Usage X
|
||||
0x09,0x31, // Usage Y
|
||||
0x15,0x81, // Logical Minimum 81h (-127d)
|
||||
0x25,0x7F, // Logical Maximum 7Fh (127d)
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x46,0xFF,0x00, // Physical Maximum FFh (255d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x02, // Report Count 2
|
||||
0x91,0x02, // Output (Variable)
|
||||
0xC0 , // End Collection
|
||||
0x05,0x0F, // Usage Page Physical Interface
|
||||
0x09,0x77, // Usage Effect Operation Report
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x85,0x0A, // Report ID Ah (10d)
|
||||
0x09,0x22, // Usage Effect Block Index
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x25,0x28, // Logical Maximum 28h (40d)
|
||||
0x35,0x01, // Physical Minimum 1
|
||||
0x45,0x28, // Physical Maximum 28h (40d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x78, // Usage Operation
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x09,0x79, // Usage Op Effect Start
|
||||
0x09,0x7A, // Usage Op Effect Start Solo
|
||||
0x09,0x7B, // Usage Op Effect Stop
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x25,0x03, // Logical Maximum 3
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x00, // Output
|
||||
0xC0 , // End Collection
|
||||
0x09,0x7C, // Usage Loop Count
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x26,0xFF,0x00, // Logical Maximum FFh (255d)
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x46,0xFF,0x00, // Physical Maximum FFh (255d)
|
||||
0x91,0x02, // Output (Variable)
|
||||
0xC0 , // End Collection
|
||||
0x09,0x90, // Usage PID State Report
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x85,0x0B, // Report ID Bh (11d)
|
||||
0x09,0x22, // Usage Effect Block Index
|
||||
0x25,0x28, // Logical Maximum 28h (40d)
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x35,0x01, // Physical Minimum 1
|
||||
0x45,0x28, // Physical Maximum 28h (40d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0xC0 , // End Collection
|
||||
0x09,0x96, // Usage DC Disable Actuators
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x85,0x0C, // Report ID Ch (12d)
|
||||
0x09,0x97, // Usage DC Stop All Effects
|
||||
0x09,0x98, // Usage DC Device Reset
|
||||
0x09,0x99, // Usage DC Device Pause
|
||||
0x09,0x9A, // Usage DC Device Continue
|
||||
0x09,0x9B, // Usage PID Device State
|
||||
0x09,0x9C, // Usage DS Actuators Enabled
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x25,0x06, // Logical Maximum 6
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x00, // Output
|
||||
0xC0 , // End Collection
|
||||
0x09,0x7D, // Usage PID Pool Report
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x85,0x0D, // Report ID Dh (13d)
|
||||
0x09,0x7E, // Usage RAM Pool Size
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x26,0xFF,0x00, // Logical Maximum FFh (255d)
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x46,0x10,0x27, // Physical Maximum 2710h (10000d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0xC0 , // End Collection
|
||||
0x09,0x6B, // Usage Set Custom Force Report
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x85,0x0E, // Report ID Eh (14d)
|
||||
0x09,0x22, // Usage Effect Block Index
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x25,0x28, // Logical Maximum 28h (40d)
|
||||
0x35,0x01, // Physical Minimum 1
|
||||
0x45,0x28, // Physical Maximum 28h (40d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x6D, // Usage Sample Count
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x26,0xFF,0x00, // Logical Maximum FFh (255d)
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x46,0xFF,0x00, // Physical Maximum FFh (255d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x09,0x51, // Usage Sample Period
|
||||
0x66,0x03,0x10, // Unit 1003h (4099d)
|
||||
0x55,0xFD, // Unit Exponent FDh (253d)
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x26,0xFF,0x7F, // Logical Maximum 7FFFh (32767d)
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x46,0xFF,0x7F, // Physical Maximum 7FFFh (32767d)
|
||||
0x75,0x10, // Report Size 10h (16d)
|
||||
0x95,0x01, // Report Count 1
|
||||
0x91,0x02, // Output (Variable)
|
||||
0x55,0x00, // Unit Exponent 0
|
||||
0x66,0x00,0x00, // Unit 0
|
||||
0xC0 , // End Collection
|
||||
0x09,0xAB, // Usage Undefined << Create New Effect Report
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x85,0x09, // Report ID 9
|
||||
0x09,0x25, // Usage Effect Type
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x09,0x26, // Usage ET Constant Force
|
||||
0x09,0x27, // Usage ET Ramp
|
||||
0x09,0x30, // Usage ET Square
|
||||
0x09,0x31, // Usage ET Sine
|
||||
0x09,0x32, // Usage ET Triangle
|
||||
0x09,0x33, // Usage ET Sawtooth Up
|
||||
0x09,0x34, // Usage ET Sawtooth Down
|
||||
0x09,0x40, // Usage ET Spring
|
||||
0x09,0x41, // Usage ET Damper
|
||||
0x09,0x42, // Usage ET Inertia
|
||||
0x09,0x43, // Usage ET Friction
|
||||
0x09,0x28, // Usage ET Custom Force Data
|
||||
0x25,0x0C, // Logical Maximum Ch (12d)
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x35,0x01, // Physical Minimum 1
|
||||
0x45,0x0C, // Physical Maximum Ch (12d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0xB1,0x00, // Feature
|
||||
0xC0 , // End Collection
|
||||
0x05,0x01, // Usage Page Generic Desktop
|
||||
0x09,0x3B, // Usage Byte Count
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x26,0xFF,0x01, // Logical Maximum 1FFh (511d)
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x46,0xFF,0x01, // Physical Maximum 1FFh (511d)
|
||||
0x75,0x0A, // Report Size Ah (10d)
|
||||
0x95,0x01, // Report Count 1
|
||||
0xB1,0x02, // Feature (Variable)
|
||||
0x75,0x06, // Report Size 6
|
||||
0xB1,0x01, // Feature (Constant)
|
||||
0xC0 , // End Collection
|
||||
0x05,0x0F, // Usage Page Physical Interface
|
||||
0x09,0x89, // Usage Block Load Status
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x85,0x02, // Report ID 2
|
||||
0x09,0x22, // Usage Effect Block Index
|
||||
0x25,0x28, // Logical Maximum 28h (40d)
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x35,0x01, // Physical Minimum 1
|
||||
0x45,0x28, // Physical Maximum 28h (40d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0xB1,0x02, // Feature (Variable)
|
||||
0x09,0x8B, // Usage Block Load Full
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x09,0x8C, // Usage Block Load Error
|
||||
0x09,0x8D, // Usage Block Handle
|
||||
0x09,0x8E, // Usage PID Block Free Report
|
||||
0x25,0x03, // Logical Maximum 3
|
||||
0x15,0x01, // Logical Minimum 1
|
||||
0x35,0x01, // Physical Minimum 1
|
||||
0x45,0x03, // Physical Maximum 3
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0xB1,0x00, // Feature
|
||||
0xC0 , // End Collection
|
||||
0x09,0xAC, // Usage Undefined
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x27,0xFF,0xFF,0x00,0x00, // Logical Maximum FFFFh (65535d)
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x47,0xFF,0xFF,0x00,0x00, // Physical Maximum FFFFh (65535d)
|
||||
0x75,0x10, // Report Size 10h (16d)
|
||||
0x95,0x01, // Report Count 1
|
||||
0xB1,0x00, // Feature
|
||||
0xC0 , // End Collection
|
||||
0x09,0x7F, // Usage ROM Pool Size
|
||||
0xA1,0x02, // Collection Datalink
|
||||
0x85,0x03, // Report ID 3
|
||||
0x09,0x80, // Usage ROM Effect Block Count
|
||||
0x75,0x10, // Report Size 10h (16d)
|
||||
0x95,0x01, // Report Count 1
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x27,0xFF,0xFF,0x00,0x00, // Logical Maximum FFFFh (65535d)
|
||||
0x47,0xFF,0xFF,0x00,0x00, // Physical Maximum FFFFh (65535d)
|
||||
0xB1,0x02, // Feature (Variable)
|
||||
0x09,0x83, // Usage PID Pool Move Report
|
||||
0x26,0xFF,0x00, // Logical Maximum FFh (255d)
|
||||
0x46,0xFF,0x00, // Physical Maximum FFh (255d)
|
||||
0x75,0x08, // Report Size 8
|
||||
0x95,0x01, // Report Count 1
|
||||
0xB1,0x02, // Feature (Variable)
|
||||
0x09,0xA9, // Usage Undefined
|
||||
0x09,0xAA, // Usage Undefined
|
||||
0x75,0x01, // Report Size 1
|
||||
0x95,0x02, // Report Count 2
|
||||
0x15,0x00, // Logical Minimum 0
|
||||
0x25,0x01, // Logical Maximum 1
|
||||
0x35,0x00, // Physical Minimum 0
|
||||
0x45,0x01, // Physical Maximum 1
|
||||
0xB1,0x02, // Feature (Variable)
|
||||
0x75,0x06, // Report Size 6
|
||||
0x95,0x01, // Report Count 1
|
||||
0xB1,0x03, // Feature (Constant, Variable)
|
||||
0xC0, // End Collection
|
||||
0xC0, // End Collection
|
||||
|
||||
|
||||
};
|
||||
|
39
usart1.c
Normal file
39
usart1.c
Normal file
@ -0,0 +1,39 @@
|
||||
#include <stdio.h>
|
||||
#include <avr/io.h>
|
||||
#include "usart1.h"
|
||||
|
||||
#ifdef UART1_STDOUT
|
||||
static int uart1_putchar(char c, FILE *stream)
|
||||
{
|
||||
usart1_send(&c, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static FILE mystdout = FDEV_SETUP_STREAM(uart1_putchar, NULL,
|
||||
_FDEV_SETUP_WRITE);
|
||||
#endif
|
||||
|
||||
void usart1_send(void *data, int len)
|
||||
{
|
||||
while (!(UCSR1A & (1<<UDRE1)));
|
||||
UDR1 = *((unsigned char*)data);
|
||||
data++;
|
||||
}
|
||||
|
||||
void usart1_init(void)
|
||||
{
|
||||
UCSR1B = (1<<TXEN1);
|
||||
UCSR1C = (1<<UCSZ11) | (1<<UCSZ10);
|
||||
UBRR1H = 0;
|
||||
#if F_CPU==8000000L
|
||||
UBRR1L = 8; // 57600 at 3.5% error at 8MHz
|
||||
#elif F_CPU==16000000L
|
||||
UBRR1L = 16; // 57600 at 2.1% error at 16MHz
|
||||
#else
|
||||
#error Unsupported clock
|
||||
#endif
|
||||
|
||||
#ifdef UART1_STDOUT
|
||||
stdout = &mystdout;
|
||||
#endif
|
||||
}
|
7
usart1.h
Normal file
7
usart1.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef _usart1_h__
|
||||
#define _usart1_h__
|
||||
|
||||
void usart1_send(void *data, int len);
|
||||
void usart1_init(void);
|
||||
|
||||
#endif // _usart1_h__
|
725
usb.c
Normal file
725
usb.c
Normal file
@ -0,0 +1,725 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
#define STATE_POWERED 0
|
||||
#define STATE_DEFAULT 1
|
||||
#define STATE_ADDRESS 2
|
||||
#define STATE_CONFIGURED 3
|
||||
|
||||
static volatile uint8_t g_usb_suspend;
|
||||
static uint8_t g_ep0_buf[64];
|
||||
static uint8_t g_device_state = STATE_DEFAULT;
|
||||
static uint8_t g_current_config;
|
||||
static void *interrupt_data;
|
||||
static volatile int interrupt_data_len = -1;
|
||||
|
||||
#define CONTROL_WRITE_BUFSIZE 64
|
||||
static struct usb_request control_write_rq;
|
||||
static volatile uint16_t control_write_len;
|
||||
static volatile uint8_t control_write_in_progress;
|
||||
static uint8_t control_write_buf[CONTROL_WRITE_BUFSIZE];
|
||||
|
||||
static const struct usb_parameters *g_params;
|
||||
|
||||
static void initControlWrite(const struct usb_request *rq)
|
||||
{
|
||||
memcpy(&control_write_rq, rq, sizeof(struct usb_request));
|
||||
control_write_len = 0;
|
||||
control_write_in_progress = 1;
|
||||
// printf("Init cw\r\n");
|
||||
}
|
||||
|
||||
static int wcslen(const wchar_t *str)
|
||||
{
|
||||
int i=0;
|
||||
while (*str) {
|
||||
str++;
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static void setupEndpoints(void)
|
||||
{
|
||||
/*** EP0 ***/
|
||||
|
||||
// Order from figure 23-2
|
||||
UENUM = 0x00; // select endpoint
|
||||
// UERST |= 0x01; // reset endpoint
|
||||
UECONX = 1<<EPEN; // activate endpoint
|
||||
UECFG0X = 0; // Control OUT
|
||||
UEIENX = (1<<RXSTPE) | (1<<RXOUTE) | (1<<NAKINE); /* | (1<<STALLEDE) | (1<<NAKOUTE) | (1<<TXINE) | (1<<RXOUTE) */;
|
||||
UECFG1X |= (1<<EPSIZE0)|(1<<EPSIZE1)|(1<<ALLOC); // 64 bytes, one bank, and allocate
|
||||
UEINTX = 0;
|
||||
|
||||
if (!(UESTA0X & (1<<CFGOK))) {
|
||||
// printf_P("CFG EP0 fail\r\n");
|
||||
return;
|
||||
}
|
||||
// printf_P("ok\r\n");
|
||||
|
||||
/*** EP1 ***/
|
||||
UENUM = 0x01; // select endpoint
|
||||
|
||||
UECONX = 1<<EPEN; // activate endpoint
|
||||
UECFG0X = (3<<6) | (1<<EPDIR); // Interrupt IN
|
||||
UEIENX = (1<<TXINE);
|
||||
UECFG1X |= (1<<EPSIZE0)|(1<<EPSIZE1)|(1<<ALLOC); // 64 bytes, one bank, and allocate
|
||||
UEINTX = 0;
|
||||
|
||||
if (!(UESTA0X & (1<<CFGOK))) {
|
||||
printf_P(PSTR("CFG EP1 fail\r\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Requires UENUM already set
|
||||
static uint16_t readEP2buf(uint8_t *dst)
|
||||
{
|
||||
uint16_t len;
|
||||
int i;
|
||||
#ifdef UEBCHX
|
||||
len = UEBCLX | (UEBCHX << 8);
|
||||
#else
|
||||
len = UEBCLX;
|
||||
#endif
|
||||
|
||||
for (i=0; i<len; i++) {
|
||||
*dst = UEDATX;
|
||||
dst++;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static void buf2EP(uint8_t epnum, const void *src, uint16_t len, uint16_t max_len, uint8_t progmem)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
||||
UENUM = epnum; // select endpoint
|
||||
|
||||
if (len > max_len) {
|
||||
len = max_len;
|
||||
}
|
||||
|
||||
if (progmem) {
|
||||
const unsigned char *s = src;
|
||||
for (i=0; i<len; i++) {
|
||||
UEDATX = pgm_read_byte(s);
|
||||
s++;
|
||||
}
|
||||
} else {
|
||||
const unsigned char *s = src;
|
||||
for (i=0; i<len; i++) {
|
||||
UEDATX = *s;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void handleSetupPacket(struct usb_request *rq)
|
||||
{
|
||||
char unhandled = 0;
|
||||
|
||||
// printf_P(PSTR("t: %02x, rq: 0x%02x, val: %04x, l: %d\r\n"), rq->bmRequestType, rq->bRequest, rq->wValue, rq->wLength);
|
||||
|
||||
if (USB_RQT_IS_HOST_TO_DEVICE(rq->bmRequestType))
|
||||
{
|
||||
switch (rq->bmRequestType & USB_RQT_RECIPIENT_MASK)
|
||||
{
|
||||
case USB_RQT_RECIPIENT_DEVICE:
|
||||
switch (rq->bRequest)
|
||||
{
|
||||
case USB_RQ_SET_ADDRESS:
|
||||
UDADDR = rq->wValue;
|
||||
while (!(UEINTX & (1<<TXINI)));
|
||||
UEINTX &= ~(1<<TXINI);
|
||||
while (!(UEINTX & (1<<TXINI)));
|
||||
UDADDR |= (1<<ADDEN);
|
||||
printf_P(PSTR("Addr: %d\r\n"), rq->wValue);
|
||||
if (!rq->wValue) {
|
||||
g_device_state = STATE_DEFAULT;
|
||||
} else {
|
||||
g_device_state = STATE_ADDRESS;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_RQ_SET_CONFIGURATION:
|
||||
g_current_config = rq->wValue;
|
||||
if (!g_current_config) {
|
||||
g_device_state = STATE_ADDRESS;
|
||||
} else {
|
||||
g_device_state = STATE_CONFIGURED;
|
||||
}
|
||||
while (!(UEINTX & (1<<TXINI)));
|
||||
UEINTX &= ~(1<<TXINI);
|
||||
printf_P(PSTR("Configured: %d\r\n"), g_current_config);
|
||||
break;
|
||||
|
||||
default:
|
||||
unhandled = 1;
|
||||
}
|
||||
break; // USB_RQT_RECIPIENT_DEVICE
|
||||
|
||||
case USB_RQT_RECIPIENT_INTERFACE:
|
||||
switch(rq->bmRequestType & (USB_RQT_TYPE_MASK))
|
||||
{
|
||||
case USB_RQT_CLASS:
|
||||
switch(rq->bRequest)
|
||||
{
|
||||
case HID_CLSRQ_SET_IDLE:
|
||||
while (!(UEINTX & (1<<TXINI)));
|
||||
UEINTX &= ~(1<<TXINI);
|
||||
break;
|
||||
case HID_CLSRQ_SET_REPORT:
|
||||
while (!(UEINTX & (1<<TXINI)));
|
||||
UEINTX &= ~(1<<TXINI);
|
||||
initControlWrite(rq);
|
||||
break;
|
||||
default:
|
||||
printf_P(PSTR("Unhandled class bRequest 0x%02x\n"), rq->bRequest);
|
||||
unhandled = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
unhandled = 1;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case USB_RQT_RECIPIENT_ENDPOINT:
|
||||
case USB_RQT_RECIPIENT_OTHER:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Request where we send data to the host. Handlers
|
||||
// simply load the endpoint buffer and transmission
|
||||
// is handled automatically.
|
||||
if (USB_RQT_IS_DEVICE_TO_HOST(rq->bmRequestType))
|
||||
{
|
||||
switch (rq->bmRequestType & USB_RQT_RECIPIENT_MASK)
|
||||
{
|
||||
case USB_RQT_RECIPIENT_DEVICE:
|
||||
switch (rq->bRequest)
|
||||
{
|
||||
case USB_RQ_GET_STATUS:
|
||||
{
|
||||
unsigned char status[2] = { 0x00, 0x00 };
|
||||
// status[0] & 0x01 : Self powered
|
||||
// status[1] & 0x02 : Remote wakeup
|
||||
buf2EP(0, status, 2, rq->wLength, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_RQ_GET_CONFIGURATION:
|
||||
{
|
||||
if (g_device_state != STATE_CONFIGURED) {
|
||||
unsigned char zero = 0;
|
||||
buf2EP(0, &zero, 1, rq->wLength, 0);
|
||||
} else {
|
||||
buf2EP(0, &g_current_config, 1, rq->wLength, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_RQ_GET_DESCRIPTOR:
|
||||
switch (rq->wValue >> 8)
|
||||
{
|
||||
case DEVICE_DESCRIPTOR:
|
||||
buf2EP(0, (unsigned char*)g_params->devdesc,
|
||||
sizeof(struct usb_device_descriptor), rq->wLength,
|
||||
g_params->flags & USB_PARAM_FLAG_DEVDESC_PROGMEM);
|
||||
break;
|
||||
case CONFIGURATION_DESCRIPTOR:
|
||||
// Check index if more than 1 config
|
||||
buf2EP(0, (unsigned char*)g_params->configdesc, g_params->configdesc_ttllen,
|
||||
rq->wLength, g_params->flags & USB_PARAM_FLAG_CONFDESC_PROGMEM);
|
||||
break;
|
||||
case STRING_DESCRIPTOR:
|
||||
{
|
||||
int id, len, slen;
|
||||
struct usb_string_descriptor_header hdr;
|
||||
|
||||
id = (rq->wValue & 0xff);
|
||||
if (id > 0 && id <= g_params->num_strings)
|
||||
{
|
||||
id -= 1; // Our string table is zero-based
|
||||
|
||||
len = rq->wLength;
|
||||
slen = wcslen(g_params->strings[id]) << 1;
|
||||
|
||||
hdr.bLength = sizeof(hdr) + slen;
|
||||
hdr.bDescriptorType = STRING_DESCRIPTOR;
|
||||
|
||||
buf2EP(0, (unsigned char*)&hdr, 2, len, 0);
|
||||
len -= 2;
|
||||
buf2EP(0, (unsigned char*)g_params->strings[id], slen, len, 0);
|
||||
}
|
||||
else if (id == 0) // Table of supported languages (string id 0)
|
||||
{
|
||||
unsigned char languages[4] = {
|
||||
4, STRING_DESCRIPTOR, 0x09, 0x10 // English (Canadian)
|
||||
};
|
||||
buf2EP(0, languages, 4, rq->wLength, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf_P(PSTR("Unknown string id\r\n"));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DEVICE_QUALIFIER_DESCRIPTOR:
|
||||
// Full speed devices must respond with a request error.
|
||||
unhandled = 1;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
// printf("Unhandled descriptor 0x%02x\n", rq->wValue>>8);
|
||||
unhandled = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
unhandled = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_RQT_RECIPIENT_INTERFACE:
|
||||
switch(rq->bmRequestType & (USB_RQT_TYPE_MASK))
|
||||
{
|
||||
case USB_RQT_STANDARD:
|
||||
switch (rq->bRequest)
|
||||
{
|
||||
case USB_RQ_GET_STATUS:
|
||||
{ // 9.4.5 Get Status, Figure 9-5. Reserved (0)
|
||||
unsigned char status[2] = { 0x00, 0x00 };
|
||||
buf2EP(0, status, 2, rq->wLength, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_RQ_GET_DESCRIPTOR:
|
||||
switch (rq->wValue >> 8)
|
||||
{
|
||||
case REPORT_DESCRIPTOR:
|
||||
{
|
||||
uint16_t rqlen = rq->wLength;
|
||||
if (rqlen > g_params->reportdesc_len) {
|
||||
rqlen = g_params->reportdesc_len;
|
||||
};
|
||||
uint16_t todo = rqlen;
|
||||
uint16_t pos = 0;
|
||||
printf_P(PSTR("t: %02x, rq: 0x%02x, val: %04x, l: %d\r\n"), rq->bmRequestType, rq->bRequest, rq->wValue, rq->wLength);
|
||||
|
||||
while(1)
|
||||
{
|
||||
printf("pos %d todo %d\r\n", pos, todo);
|
||||
if (todo > 64) {
|
||||
buf2EP(0, ((unsigned char*)g_params->reportdesc)+pos, 64,
|
||||
64,
|
||||
g_params->flags & USB_PARAM_FLAG_REPORTDESC_PROGMEM);
|
||||
UEINTX &= ~(1<<TXINI);
|
||||
pos += 64;
|
||||
todo -= 64;
|
||||
} else {
|
||||
buf2EP(0, ((unsigned char*)g_params->reportdesc)+pos, todo,
|
||||
todo,
|
||||
g_params->flags & USB_PARAM_FLAG_REPORTDESC_PROGMEM);
|
||||
UEINTX &= ~(1<<TXINI);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
if (UEINTX & (1<<RXOUTI)) {
|
||||
UEINTX &= ~(1<<RXOUTI); // ACK
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
unhandled = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
unhandled = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_RQT_CLASS:
|
||||
switch (rq->bRequest)
|
||||
{
|
||||
case HID_CLSRQ_GET_REPORT:
|
||||
{
|
||||
if (g_params->getReport) {
|
||||
const unsigned char *data;
|
||||
uint16_t len;
|
||||
len = g_params->getReport(rq, &data);
|
||||
if (len) {
|
||||
buf2EP(0, data, len, rq->wLength, 0);
|
||||
}
|
||||
} else {
|
||||
// Treat as not-supported (i.e. STALL endpoint)
|
||||
unhandled = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
unhandled = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
unhandled = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_RQT_RECIPIENT_ENDPOINT:
|
||||
switch (rq->bRequest)
|
||||
{
|
||||
case USB_RQ_GET_STATUS:
|
||||
{ // 9.4.5 Get Status, Figure 0-6
|
||||
unsigned char status[2] = { 0x00, 0x00 };
|
||||
// status[0] & 0x01 : Halt
|
||||
buf2EP(0, status, 2, rq->wLength, 0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
unhandled = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case USB_RQT_RECIPIENT_OTHER:
|
||||
default:
|
||||
unhandled = 1;
|
||||
}
|
||||
|
||||
if (!unhandled)
|
||||
{
|
||||
// Handle transmission now
|
||||
UEINTX &= ~(1<<TXINI);
|
||||
while (1)
|
||||
{
|
||||
if (UEINTX & (1<<TXINI)) {
|
||||
UEINTX &= ~(1<<TXINI);
|
||||
}
|
||||
|
||||
if (UEINTX & (1<<RXOUTI)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UEINTX &= ~(1<<RXOUTI); // ACK
|
||||
}
|
||||
} // IS DEVICE-TO-HOST
|
||||
|
||||
if (unhandled) {
|
||||
// printf_P(PSTR("t: %02x, rq: 0x%02x, val: %04x\r\n"), rq->bmRequestType, rq->bRequest, rq->wValue);
|
||||
UECONX |= (1<<STALLRQ);
|
||||
}
|
||||
}
|
||||
|
||||
static void handleDataPacket(const struct usb_request *rq, uint8_t *dat, uint16_t len)
|
||||
{
|
||||
uint16_t i;
|
||||
|
||||
if ((rq->bmRequestType & (USB_RQT_TYPE_MASK)) == USB_RQT_CLASS) {
|
||||
if (g_params->setReport) {
|
||||
if (g_params->setReport(rq, dat, len)) {
|
||||
UECONX |= (1<<STALLRQ);
|
||||
} else {
|
||||
// xmit status
|
||||
UEINTX &= ~(1<<TXINI);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
printf_P(PSTR("Unhandled control write [%d] : "), len);
|
||||
for (i=0; i<len; i++) {
|
||||
printf("%02X ", dat[i]);
|
||||
}
|
||||
printf("\r\n");
|
||||
}
|
||||
|
||||
// Device interrupt
|
||||
ISR(USB_GEN_vect)
|
||||
{
|
||||
uint8_t i;
|
||||
i = UDINT;
|
||||
|
||||
if (i & (1<<SUSPI)) {
|
||||
UDINT &= ~(1<<SUSPI);
|
||||
g_usb_suspend = 1;
|
||||
UDIEN |= (1<<WAKEUPE);
|
||||
printf_P(PSTR("SUSPI\r\n"));
|
||||
// CPU could now be put in low power mode. Later,
|
||||
// WAKEUPI would wake it up.
|
||||
}
|
||||
|
||||
// this interrupt is to wakeup the cpu from sleep mode.
|
||||
if (i & (1<<WAKEUPI)) {
|
||||
UDINT &= ~(1<<WAKEUPE);
|
||||
if (g_usb_suspend) {
|
||||
g_usb_suspend = 0;
|
||||
printf_P(PSTR("WAKEUPI\r\n"));
|
||||
UDIEN &= ~(1<<WAKEUPE); // woke up. Not needed anymore.
|
||||
}
|
||||
}
|
||||
|
||||
if (i & (1<<EORSTI)) {
|
||||
printf_P(PSTR("EORSTI\r\n"));
|
||||
g_usb_suspend = 0;
|
||||
setupEndpoints();
|
||||
UDINT &= ~(1<<EORSTI);
|
||||
}
|
||||
|
||||
if (i & (1<<SOFI)) {
|
||||
UDINT &= ~(1<<SOFI);
|
||||
// printf_P(PSTR("SOFI\r\n"));
|
||||
}
|
||||
|
||||
if (i & (1<<EORSMI)) {
|
||||
UDINT &= ~(1<<EORSMI);
|
||||
printf_P(PSTR("EORSMI\r\n"));
|
||||
}
|
||||
|
||||
if (i & (1<<UPRSMI)) {
|
||||
UDINT &= ~(1<<UPRSMI);
|
||||
printf_P(PSTR("UPRSMI\r\n"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Endpoint interrupt
|
||||
ISR(USB_COM_vect)
|
||||
{
|
||||
uint8_t ueint;
|
||||
uint8_t i;
|
||||
|
||||
ueint = UEINT;
|
||||
|
||||
if (ueint & (1<<EPINT0)) {
|
||||
UENUM = 0;
|
||||
i = UEINTX;
|
||||
|
||||
if (i & (1<<RXSTPI)) {
|
||||
// printf_P(PSTR("RXSTPI\r\n"));
|
||||
readEP2buf(g_ep0_buf);
|
||||
UEINTX &= ~(1<<RXSTPI);
|
||||
handleSetupPacket((struct usb_request *)g_ep0_buf);
|
||||
}
|
||||
|
||||
if (i & (1<<RXOUTI)) {
|
||||
uint16_t len;
|
||||
len = readEP2buf(g_ep0_buf);
|
||||
UEINTX &= ~(1<<RXOUTI);
|
||||
if (control_write_in_progress) {
|
||||
// printf("chunk: %d\r\n", len);
|
||||
if (control_write_len + len < CONTROL_WRITE_BUFSIZE) {
|
||||
memcpy(control_write_buf + control_write_len, g_ep0_buf, len);
|
||||
control_write_len += len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i & (1<<NAKINI)) {
|
||||
UEINTX &= ~(1<<NAKINI);
|
||||
if (control_write_in_progress) {
|
||||
// printf("end. total: %d\n", control_write_len);
|
||||
handleDataPacket(&control_write_rq, control_write_buf, control_write_len);
|
||||
control_write_in_progress = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ueint & (1<<EPINT1)) {
|
||||
UENUM = 1;
|
||||
i = UEINTX;
|
||||
|
||||
if (i & (1<<TXINI)) {
|
||||
if (interrupt_data_len < 0) {
|
||||
// If there's not already data waiting to be
|
||||
// sent, disable the interrupt.
|
||||
UEIENX &= ~(1<<TXINE);
|
||||
} else {
|
||||
UEINTX &= ~(1<<TXINI);
|
||||
buf2EP(1, interrupt_data, interrupt_data_len, interrupt_data_len, 0);
|
||||
interrupt_data = NULL;
|
||||
interrupt_data_len = -1;
|
||||
UEINTX &= ~(1<<FIFOCON);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (i & (1<<RXOUTI)) {
|
||||
UEINTX &= ~(1<<RXOUTI);
|
||||
printf_P(PSTR("RXOUTI\r\n"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void usb_interruptSend(void *data, int len)
|
||||
{
|
||||
uint8_t sreg = SREG;
|
||||
|
||||
while (interrupt_data_len != -1) { }
|
||||
|
||||
cli();
|
||||
|
||||
interrupt_data = data;
|
||||
interrupt_data_len = len;
|
||||
|
||||
UENUM = 1;
|
||||
UEIENX |= (1<<TXINE);
|
||||
|
||||
SREG = sreg;
|
||||
}
|
||||
|
||||
void usb_shutdown(void)
|
||||
{
|
||||
UDCON |= (1<<DETACH);
|
||||
|
||||
// Disable interrupts
|
||||
UDIEN = 0;
|
||||
|
||||
|
||||
USBCON &= (1<<USBE);
|
||||
USBCON |= (1<<FRZCLK); // initial value
|
||||
#ifdef UHWCON
|
||||
UHWCON &= ~(1<<UVREGE); // Disable USB pad regulator
|
||||
#endif
|
||||
}
|
||||
|
||||
#define STATE_WAIT_VBUS 0
|
||||
#define STATE_ATTACHED 1
|
||||
static unsigned char usb_state;
|
||||
|
||||
void usb_doTasks(void)
|
||||
{
|
||||
switch (usb_state)
|
||||
{
|
||||
default:
|
||||
usb_state = STATE_WAIT_VBUS;
|
||||
case STATE_WAIT_VBUS:
|
||||
#ifdef USBSTA
|
||||
if (USBSTA & (1<<VBUS)) {
|
||||
#endif
|
||||
printf_P(PSTR("ATTACH\r\n"));
|
||||
UDCON &= ~(1<<DETACH); // clear DETACH bit
|
||||
usb_state = STATE_ATTACHED;
|
||||
#ifdef USBSTA
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case STATE_ATTACHED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__AVR_ATmega32U2__)
|
||||
|
||||
/* Atmega32u2 datasheet 8.11.6, PLLCSR.
|
||||
* But register summary says PLLP0... */
|
||||
#ifndef PINDIV
|
||||
#define PINDIV 2
|
||||
#endif
|
||||
static void pll_init(void)
|
||||
{
|
||||
#if F_CPU==8000000L
|
||||
PLLCSR = 0;
|
||||
#elif F_CPU==16000000L
|
||||
PLLCSR = (1<<PINDIV);
|
||||
#else
|
||||
#error Unsupported clock frequency
|
||||
#endif
|
||||
PLLCSR |= (1<<PLLE);
|
||||
while (!(PLLCSR&(1<<PLOCK))) {
|
||||
// wait for PLL lock
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void pll_init(void)
|
||||
{
|
||||
#if F_CPU==8000000L
|
||||
// The PLL generates a clock that is 24x a nominal 2MHz input.
|
||||
// Hence, we need to divide by 4 the external 8MHz crystal
|
||||
// frequency.
|
||||
PLLCSR = (1<<PLLP1)|(1<<PLLP0);
|
||||
#elif F_CPU==16000000L
|
||||
// The PLL generates a clock that is 24x a nominal 2MHz input.
|
||||
// Hence, we need to divide by 8 the external 16MHz crystal
|
||||
// frequency.
|
||||
PLLCSR = (1<<PLLP2)|(1<<PLLP0);
|
||||
#else
|
||||
#error Unsupported clock frequency
|
||||
#endif
|
||||
|
||||
PLLCSR |= (1<<PLLE);
|
||||
while (!(PLLCSR&(1<<PLOCK))) {
|
||||
// wait for PLL lock
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void usb_init(const struct usb_parameters *params)
|
||||
{
|
||||
g_params = params;
|
||||
|
||||
// Set some initial values
|
||||
USBCON &= (1<<USBE);
|
||||
USBCON |= (1<<FRZCLK); // initial value
|
||||
#ifdef UHWCON
|
||||
UHWCON |= (1<<UVREGE); // Enable USB pad regulator
|
||||
UHWCON &= ~(1<<UIDE);
|
||||
UHWCON |= (1<UIMOD);
|
||||
#endif
|
||||
|
||||
#ifdef UPOE
|
||||
UPOE = 0; // Disable direct drive of USB pins
|
||||
#endif
|
||||
#ifdef REGCR
|
||||
REGCR = 0; // Enable the regulator
|
||||
#endif
|
||||
|
||||
pll_init();
|
||||
|
||||
USBCON |= (1<<USBE);
|
||||
USBCON &= ~(1<<FRZCLK); // Unfreeze clock
|
||||
#ifdef OTGPADE
|
||||
USBCON |= (1<<OTGPADE);
|
||||
#endif
|
||||
|
||||
#ifdef LSM
|
||||
// Select full speed mode
|
||||
UDCON &= (1<<LSM);
|
||||
#endif
|
||||
|
||||
setupEndpoints();
|
||||
|
||||
UDINT &= ~(1<<SUSPI);
|
||||
UDIEN = (1<<SUSPE) | (1<<EORSTE) |/* (1<<SOFE) |*/ (1<<WAKEUPE) | (1<<EORSME) | (1<<UPRSME);
|
||||
}
|
||||
|
||||
|
200
usb.h
Normal file
200
usb.h
Normal file
@ -0,0 +1,200 @@
|
||||
#ifndef _usb_h__
|
||||
#define _usb_h__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
/**** Standard USB defines ****/
|
||||
|
||||
#define DEVICE_DESCRIPTOR 0x01
|
||||
#define CONFIGURATION_DESCRIPTOR 0x02
|
||||
#define STRING_DESCRIPTOR 0x03
|
||||
#define INTERFACE_DESCRIPTOR 0x04
|
||||
#define ENDPOINT_DESCRIPTOR 0x05
|
||||
#define DEVICE_QUALIFIER_DESCRIPTOR 0x06
|
||||
|
||||
// USB HID 1.11 section 7.1.1
|
||||
#define HID_DESCRIPTOR 0x21
|
||||
#define REPORT_DESCRIPTOR 0x22
|
||||
#define PHYSICAL_DESCRIPTOR 0x23
|
||||
|
||||
#define USB_RQT_IS_HOST_TO_DEVICE(r) (((r) & 0x80) == 0)
|
||||
#define USB_RQT_IS_DEVICE_TO_HOST(r) (((r) & 0x80) == 0x80)
|
||||
#define USB_RQT_HOST_TO_DEVICE 0x00
|
||||
#define USB_RQT_DEVICE_TO_HOST 0x80
|
||||
#define USB_RQT_STANDARD (0x00 << 5)
|
||||
#define USB_RQT_CLASS (0x01 << 5)
|
||||
#define USB_RQT_VENDOR (0x02 << 5)
|
||||
#define USB_RQT_TYPE_MASK (0x03 << 5)
|
||||
#define USB_RQT_RECIPIENT_DEVICE 0x00
|
||||
#define USB_RQT_RECIPIENT_INTERFACE 0x01
|
||||
#define USB_RQT_RECIPIENT_ENDPOINT 0x02
|
||||
#define USB_RQT_RECIPIENT_OTHER 0x03
|
||||
#define USB_RQT_RECIPIENT_MASK 0x1F
|
||||
|
||||
#define USB_RQ_GET_STATUS 0x00
|
||||
#define USB_RQ_CLEAR_FEATURE 0x01
|
||||
#define USB_RQ_SET_FEATURE 0x03
|
||||
#define USB_RQ_SET_ADDRESS 0x05
|
||||
#define USB_RQ_GET_DESCRIPTOR 0x06
|
||||
#define USB_RQ_SET_DESCRIPTOR 0x07
|
||||
#define USB_RQ_GET_CONFIGURATION 0x08
|
||||
#define USB_RQ_SET_CONFIGURATION 0x09
|
||||
#define USB_RQ_GET_INTERFACE 0x0A
|
||||
#define USB_RQ_SET_INTERFACE 0x0B
|
||||
|
||||
/* If the one you need is missing, add it. For values,
|
||||
* see: http://www.usb.org/developers/defined_class
|
||||
*/
|
||||
#define USB_DEVICE_CLASS_AUDIO 0x01
|
||||
#define USB_DEVICE_CLASS_CDC 0x02
|
||||
#define USB_DEVICE_CLASS_HID 0x03
|
||||
#define USB_DEVICE_CLASS_MASS_STORAGE 0x04
|
||||
#define USB_DEVICE_CLASS_HUB 0x05
|
||||
#define USB_DEVICE_CLASS_VENDOR 0xFF
|
||||
|
||||
struct usb_request {
|
||||
uint8_t bmRequestType;
|
||||
uint8_t bRequest;
|
||||
uint16_t wValue;
|
||||
uint16_t wIndex;
|
||||
uint16_t wLength;
|
||||
};
|
||||
|
||||
struct usb_device_descriptor {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType; // DEVICE_DESCRIPTOR
|
||||
uint16_t bcdUSB;
|
||||
uint8_t bDeviceClass; // class code
|
||||
uint8_t bDeviceSubClass;
|
||||
uint8_t bDeviceProtocol;
|
||||
uint8_t bMaxPacketSize; // Max packet size for endpoint zero
|
||||
uint16_t idVendor;
|
||||
uint16_t idProduct;
|
||||
uint16_t bcdDevice;
|
||||
uint8_t iManufacturer;
|
||||
uint8_t iProduct;
|
||||
uint8_t iSerialNumber;
|
||||
uint8_t bNumConfigurations;
|
||||
};
|
||||
|
||||
#define CFG_DESC_ATTR_RESERVED 0x80
|
||||
#define CFG_DESC_ATTR_SELF_POWERED 0x40
|
||||
#define CFG_DESC_ATTR_REMOTE_WAKEUP 0x20
|
||||
struct usb_configuration_descriptor {
|
||||
uint8_t bLength; // sizeof(struct usb_configuration_descriptor)
|
||||
uint8_t bDescriptorType; // CONFIGURATION_DESCRIPTOR
|
||||
uint16_t wTotalLength; // for all descriptors in this configuration. (Cfg, interface, endpoint, class)
|
||||
uint8_t bNumInterfaces;
|
||||
uint8_t bConfigurationValue; // for SET_CONFIGURATION argument(must be >= 1)
|
||||
uint8_t iConfiguration; // String descriptor index
|
||||
uint8_t bmAttributes; // D7 (set to one), D6 Selft-powred, D5 remote wakeup
|
||||
uint8_t bMaxPower; // in 2mA units
|
||||
};
|
||||
|
||||
struct usb_interface_descriptor {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType; // INTERFACE_DESCRIPTOR
|
||||
uint8_t bInterfaceNumber;
|
||||
uint8_t bAlternateSetting;
|
||||
uint8_t bNumEndpoints;
|
||||
uint8_t bInterfaceClass;
|
||||
uint8_t bInterfaceSubClass;
|
||||
uint8_t bInterfaceProtocol;
|
||||
uint8_t iInterface; // String descriptor index
|
||||
};
|
||||
|
||||
#define TRANSFER_TYPE_CONTROL 0x0
|
||||
#define TRANSFER_TYPE_ISOCHRONOUS 0x1
|
||||
#define TRANSFER_TYPE_BULK 0x2
|
||||
#define TRANSFER_TYPE_INT 0x3
|
||||
#define LS_FS_INTERVAL_MS(v) ((v)) // At low/full speed, 1 ms units
|
||||
#define HS_INTERVAL_US(v) ((v)/125) // At high speed, .125us units
|
||||
struct usb_endpoint_descriptor {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint8_t bEndpointAddress;
|
||||
uint8_t bmAttributes;
|
||||
uint16_t wMaxPacketsize;
|
||||
uint8_t bInterval;
|
||||
};
|
||||
|
||||
struct usb_string_descriptor_header {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
};
|
||||
|
||||
/* HID 1.11 section 4.2 */
|
||||
#define HID_SUBCLASS_NONE 0
|
||||
#define HUD_SUBCLASS_BOOT 1
|
||||
|
||||
/* HID 1.11 section 4.3 */
|
||||
#define HID_PROTOCOL_NONE 0
|
||||
#define HID_PROTOCOL_KEYBOARD 1
|
||||
#define HID_PROTOCOL_MOUSE 2
|
||||
|
||||
/* Other values defined at HID 1.11, 6.2.1 */
|
||||
#define HID_COUNTRY_NOT_SUPPORTED 0x00
|
||||
|
||||
struct usb_hid_descriptor {
|
||||
uint8_t bLength;
|
||||
uint8_t bDescriptorType;
|
||||
uint16_t bcdHid;
|
||||
uint8_t bCountryCode;
|
||||
uint8_t bNumDescriptors;
|
||||
|
||||
uint8_t bClassDescriptorType;
|
||||
uint16_t wClassDescriptorLength;
|
||||
// Note: At least one descriptor (REPORT) is
|
||||
// required. Additional descriptors
|
||||
// are declared as above (type, length) and
|
||||
// appended to the HID descriptor.
|
||||
};
|
||||
|
||||
#define HID_CLSRQ_GET_REPORT 0x01
|
||||
#define HID_CLSRQ_GET_IDLE 0x02
|
||||
#define HID_CLSRQ_GET_PROTOCOL 0x03
|
||||
#define HID_CLSRQ_SET_REPORT 0x09
|
||||
#define HID_CLSRQ_SET_IDLE 0x0A
|
||||
#define HID_CLSRQ_SET_PROTOCOL 0x0B
|
||||
|
||||
#define HID_REPORT_TYPE_INPUT 0x01
|
||||
#define HID_REPORT_TYPE_OUTPUT 0x02
|
||||
#define HID_REPORT_TYPE_FEATURE 0x03
|
||||
|
||||
/**** API ****/
|
||||
|
||||
#define USB_PARAM_FLAG_DEVDESC_PROGMEM 1
|
||||
#define USB_PARAM_FLAG_CONFDESC_PROGMEM 2
|
||||
#define USB_PARAM_FLAG_REPORTDESC_PROGMEM 4
|
||||
|
||||
struct usb_parameters {
|
||||
uint8_t flags;
|
||||
|
||||
const struct usb_device_descriptor *devdesc;
|
||||
|
||||
const void *configdesc;
|
||||
// length including all descriptors returned together
|
||||
// for getdescriptor(config). (i.e. Config descriptor +
|
||||
// interface descritor + hid and endpoints)
|
||||
uint16_t configdesc_ttllen;
|
||||
|
||||
uint8_t num_strings;
|
||||
const wchar_t *const *strings;
|
||||
|
||||
uint16_t reportdesc_len;
|
||||
const unsigned char *reportdesc;
|
||||
|
||||
// Warning: Called from interrupt handler. Implement accordingly.
|
||||
uint16_t (*getReport)(struct usb_request *rq, const uint8_t **dat);
|
||||
uint8_t (*setReport)(const struct usb_request *rq, const uint8_t *dat, uint16_t len);
|
||||
};
|
||||
|
||||
void usb_interruptSend(void *data, int len); // EP1
|
||||
void usb_init(const struct usb_parameters *params);
|
||||
void usb_doTasks(void);
|
||||
void usb_shutdown(void);
|
||||
|
||||
|
||||
#endif // _usb_h__
|
27
wait_then_flash.sh
Executable file
27
wait_then_flash.sh
Executable file
@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Syntax: ./wait_then_flash.sh CPU HEXFILE"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
CPU=$1
|
||||
HEXFILE=$2
|
||||
|
||||
echo "Will program $HEXFILE on $CPU target"
|
||||
echo "Polling for chip..."
|
||||
while true; do
|
||||
dfu-programmer $1 erase
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Chip found. Erased."
|
||||
break;
|
||||
fi
|
||||
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "Writing flash..."
|
||||
dfu-programmer $CPU flash $HEXFILE
|
||||
|
||||
echo "Starting program..."
|
||||
dfu-programmer $CPU start
|
Loading…
Reference in New Issue
Block a user