Compare commits
96 Commits
Author | SHA1 | Date |
---|---|---|
Raphaël Assenat | c681f648a5 | |
Matthew C | 62711a3236 | |
Matthew C | 0ec5f22f0f | |
Matthew C | 1514d08f44 | |
Matthew C | ff893e2152 | |
Raphael Assenat | 52eea713be | |
Raphael Assenat | a698f8cd06 | |
Raphael Assenat | 5d06a6d6a3 | |
Raphael Assenat | 786088e4b1 | |
Raphael Assenat | 07c4cc7a4a | |
Raphael Assenat | e5f6c6ee02 | |
Raphael Assenat | ee3adafd26 | |
Raphael Assenat | 90779c014b | |
Raphaël Assénat | 59c2627f40 | |
Daan Sprenkels | 7e90c06cf9 | |
Raphael Assenat | 2dd64af033 | |
Raphael Assenat | 74cf60d839 | |
Raphael Assenat | 4999f07c14 | |
Raphael Assenat | 9872399739 | |
Raphael Assenat | c7992e5334 | |
Raphael Assenat | f5e268fb19 | |
Raphael Assenat | 9c99048c6f | |
Raphael Assenat | 2d3795c29a | |
Raphael Assenat | b01df9f239 | |
Raphael Assenat | 55fe6d0312 | |
Raphael Assenat | 1dc71a835b | |
Raphael Assenat | 03ebfde089 | |
Raphael Assenat | f42a29b888 | |
Raphael Assenat | 2a2b326001 | |
Raphael Assenat | 9d5ca629a7 | |
Raphael Assenat | 708fb22072 | |
Raphael Assenat | 0e0c381fbd | |
Raphael Assenat | 95d6b2ec15 | |
Raphael Assenat | 5328827234 | |
Raphael Assenat | 66a1a45f22 | |
Raphael Assenat | 9f665068ae | |
Raphael Assenat | 7b01a454d4 | |
Raphael Assenat | be2894b68c | |
Raphael Assenat | e7426e684a | |
Raphael Assenat | 05b47f9967 | |
Raphael Assenat | c123fd6b52 | |
Raphael Assenat | d72815e9e6 | |
Raphael Assenat | a041e8eaf8 | |
Raphael Assenat | a9d5df2b8f | |
Raphael Assenat | 324bb81b1e | |
Raphael Assenat | eeb2ecbae4 | |
Raphael Assenat | 158d9bffda | |
Raphael Assenat | c7e8dc7ad4 | |
Raphael Assenat | b22985712f | |
Raphael Assenat | 2eaafb7786 | |
Raphael Assenat | 412c1a42eb | |
Raphael Assenat | 083c915c34 | |
Raphael Assenat | 6d8d2d27bf | |
Raphael Assenat | 5eb8a587c4 | |
Raphael Assenat | 64115ad9ef | |
Raphael Assenat | e944017c36 | |
Raphael Assenat | e7bca6ce08 | |
Raphael Assenat | 1623a46179 | |
Raphael Assenat | d573ff55c3 | |
Raphael Assenat | 6bb9ce18a9 | |
Raphael Assenat | c2fd3a10ba | |
Raphael Assenat | 434aec0d4b | |
Raphael Assenat | bbefc8203c | |
Raphael Assenat | 1659d2d106 | |
Raphael Assenat | 1c05ad1326 | |
Raphael Assenat | 21ebef2ed3 | |
Raphael Assenat | d7bdf59860 | |
Raphael Assenat | bb9e6dce17 | |
Raphael Assenat | 835a4935c5 | |
Raphael Assenat | 81662cef2a | |
Raphael Assenat | 07ffccc787 | |
Raphael Assenat | 8da31387f6 | |
Raphael Assenat | 96bd132b66 | |
Raphael Assenat | af12f0d242 | |
Raphael Assenat | f6e4281321 | |
Raphael Assenat | 4ecc3f571e | |
Raphael Assenat | 90aae55470 | |
Raphael Assenat | fe3814f7d1 | |
Raphael Assenat | fb43524d34 | |
Raphael Assenat | 0c8dc9e7de | |
Raphael Assenat | 1fcfe8b594 | |
Raphael Assenat | 0c66478dc5 | |
Raphael Assenat | 7d8e8af290 | |
Raphael Assenat | b6386b8948 | |
Raphael Assenat | 158fc4fdca | |
Raphael Assenat | 553598ab64 | |
Raphael Assenat | 671c2e403f | |
Raphael Assenat | b9a6f23607 | |
Raphael Assenat | 4fbba42896 | |
Raphael Assenat | 6f6d77956b | |
Raphael Assenat | 4fac12c760 | |
Raphael Assenat | cc34233b76 | |
Raphael Assenat | 3ec1c0b711 | |
Raphael Assenat | a333980812 | |
Raphael Assenat | fb1be3eee5 | |
Raphael Assenat | b8be4c754b |
24
Makefile
24
Makefile
|
@ -7,7 +7,7 @@ include Makefile.inc
|
||||||
PROGNAME=gcn64usb
|
PROGNAME=gcn64usb
|
||||||
OBJDIR=objs-$(PROGNAME)
|
OBJDIR=objs-$(PROGNAME)
|
||||||
CPU=atmega32u2
|
CPU=atmega32u2
|
||||||
CFLAGS=-Wall -mmcu=$(CPU) -DF_CPU=16000000L -Os -DUART1_STDOUT -DVERSIONSTR=$(VERSIONSTR)
|
CFLAGS=-Wall -mmcu=$(CPU) -DF_CPU=16000000L -Os -DUART1_STDOUT -DVERSIONSTR=$(VERSIONSTR) -DVERSIONSTR_SHORT=$(VERSIONSTR_SHORT) -DVERSIONBCD=$(VERSIONBCD) -std=gnu99
|
||||||
LDFLAGS=-mmcu=$(CPU) -Wl,-Map=$(PROGNAME).map
|
LDFLAGS=-mmcu=$(CPU) -Wl,-Map=$(PROGNAME).map
|
||||||
HEXFILE=$(PROGNAME).hex
|
HEXFILE=$(PROGNAME).hex
|
||||||
|
|
||||||
|
@ -16,6 +16,18 @@ all: $(HEXFILE)
|
||||||
clean:
|
clean:
|
||||||
rm -f $(PROGNAME).elf $(PROGNAME).hex $(PROGNAME).map $(OBJS)
|
rm -f $(PROGNAME).elf $(PROGNAME).hex $(PROGNAME).map $(OBJS)
|
||||||
|
|
||||||
|
gcn64txrx0.o: gcn64txrx.S
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@ -DSUFFIX=0 -DGCN64_DATA_BIT=0
|
||||||
|
|
||||||
|
gcn64txrx1.o: gcn64txrx.S
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@ -DSUFFIX=1 -DGCN64_DATA_BIT=2
|
||||||
|
|
||||||
|
gcn64txrx2.o: gcn64txrx.S
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@ -DSUFFIX=2 -DGCN64_DATA_BIT=1
|
||||||
|
|
||||||
|
gcn64txrx3.o: gcn64txrx.S
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@ -DSUFFIX=3 -DGCN64_DATA_BIT=3
|
||||||
|
|
||||||
%.o: %.S
|
%.o: %.S
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
@ -30,7 +42,7 @@ $(PROGNAME).elf: $(OBJS)
|
||||||
|
|
||||||
$(PROGNAME).hex: $(PROGNAME).elf
|
$(PROGNAME).hex: $(PROGNAME).elf
|
||||||
avr-objcopy -j .data -j .text -O ihex $(PROGNAME).elf $(PROGNAME).hex
|
avr-objcopy -j .data -j .text -O ihex $(PROGNAME).elf $(PROGNAME).hex
|
||||||
avr-size $(PROGNAME).elf
|
avr-size $(PROGNAME).elf -C --mcu=$(CPU)
|
||||||
|
|
||||||
fuse:
|
fuse:
|
||||||
|
|
||||||
|
@ -42,7 +54,11 @@ justflash: $(HEXFILE)
|
||||||
./scripts/wait_then_flash.sh $(CPU) $(HEXFILE)
|
./scripts/wait_then_flash.sh $(CPU) $(HEXFILE)
|
||||||
|
|
||||||
chip_erase:
|
chip_erase:
|
||||||
dfu-programmer atmega32u2 erase
|
dfu-programmer $(CPU) erase
|
||||||
|
|
||||||
reset:
|
reset:
|
||||||
dfu-programmer atmega32u2 reset
|
dfu-programmer $(CPU) reset
|
||||||
|
|
||||||
|
restart:
|
||||||
|
- ./scripts/enter_bootloader.sh
|
||||||
|
./scripts/start.sh $(CPU)
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
CC=avr-gcc
|
||||||
|
AS=$(CC)
|
||||||
|
LD=$(CC)
|
||||||
|
|
||||||
|
include Makefile.inc
|
||||||
|
|
||||||
|
PROGNAME=gcn64usb
|
||||||
|
OBJDIR=objs-$(PROGNAME)
|
||||||
|
CPU=atmega32u4
|
||||||
|
CFLAGS=-Wall -mmcu=$(CPU) -DF_CPU=16000000L -Os -DUART1_STDOUT -DVERSIONSTR=$(VERSIONSTR) -DVERSIONSTR_SHORT=$(VERSIONSTR_SHORT) -DVERSIONBCD=$(VERSIONBCD) -std=gnu99
|
||||||
|
LDFLAGS=-mmcu=$(CPU) -Wl,-Map=$(PROGNAME).map
|
||||||
|
HEXFILE=$(PROGNAME).hex
|
||||||
|
|
||||||
|
all: $(HEXFILE)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(PROGNAME).elf $(PROGNAME).hex $(PROGNAME).map $(OBJS)
|
||||||
|
|
||||||
|
gcn64txrx0.o: gcn64txrx.S
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@ -DSUFFIX=0 -DGCN64_DATA_BIT=0
|
||||||
|
|
||||||
|
gcn64txrx1.o: gcn64txrx.S
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@ -DSUFFIX=1 -DGCN64_DATA_BIT=2
|
||||||
|
|
||||||
|
gcn64txrx2.o: gcn64txrx.S
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@ -DSUFFIX=2 -DGCN64_DATA_BIT=1
|
||||||
|
|
||||||
|
gcn64txrx3.o: gcn64txrx.S
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@ -DSUFFIX=3 -DGCN64_DATA_BIT=3
|
||||||
|
|
||||||
|
%.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 -C --mcu=$(CPU)
|
||||||
|
|
||||||
|
fuse:
|
||||||
|
|
||||||
|
flash: $(HEXFILE)
|
||||||
|
- ./scripts/enter_bootloader.sh
|
||||||
|
./scripts/wait_then_flash.sh $(CPU) $(HEXFILE)
|
||||||
|
|
||||||
|
justflash: $(HEXFILE)
|
||||||
|
./scripts/wait_then_flash.sh $(CPU) $(HEXFILE)
|
||||||
|
|
||||||
|
chip_erase:
|
||||||
|
dfu-programmer $(CPU) erase
|
||||||
|
|
||||||
|
reset:
|
||||||
|
dfu-programmer $(CPU) reset
|
||||||
|
|
||||||
|
restart:
|
||||||
|
- ./scripts/enter_bootloader.sh
|
||||||
|
./scripts/start.sh $(CPU)
|
|
@ -1,2 +1,4 @@
|
||||||
OBJS=main.o usb.o usbpad.o mappings.o gcn64_protocol.o n64.o gamecube.o usart1.o bootloader.o eeprom.o config.o hiddata.o usbstrings.o intervaltimer.o version.o gcn64txrx.o gamepads.o
|
OBJS=main.o usb.o usbpad.o mappings.o gcn64_protocol.o n64.o gamecube.o usart1.o bootloader.o eeprom.o config.o hiddata.o usbstrings.o intervaltimer.o intervaltimer2.o version.o gcn64txrx0.o gcn64txrx1.o gcn64txrx2.o gcn64txrx3.o gamepads.o stkchk.o gc_kb.o
|
||||||
VERSIONSTR=\"3.1.0\"
|
VERSIONSTR=\"3.6.1\"
|
||||||
|
VERSIONSTR_SHORT=\"3.6\"
|
||||||
|
VERSIONBCD=0x0361
|
||||||
|
|
|
@ -7,7 +7,7 @@ include Makefile.inc
|
||||||
PROGNAME=gcn64usb-stk500
|
PROGNAME=gcn64usb-stk500
|
||||||
OBJDIR=objs-$(PROGNAME)
|
OBJDIR=objs-$(PROGNAME)
|
||||||
CPU=at90usb1287
|
CPU=at90usb1287
|
||||||
CFLAGS=-Wall -mmcu=$(CPU) -DF_CPU=16000000L -Os -DUART1_STDOUT -DSTK525 -DVERSIONSTR=$(VERSIONSTR)
|
CFLAGS=-Wall -mmcu=$(CPU) -DF_CPU=16000000L -Os -DUART1_STDOUT -DSTK525 -DVERSIONSTR=$(VERSIONSTR) -DVERSIONSTR_SHORT=$(VERSIONSTR_SHORT) -DVERSIONBCD=$(VERSIONBCD) -std=gnu99
|
||||||
LDFLAGS=-mmcu=$(CPU) -Wl,-Map=$(PROGNAME).map
|
LDFLAGS=-mmcu=$(CPU) -Wl,-Map=$(PROGNAME).map
|
||||||
HEXFILE=$(PROGNAME).hex
|
HEXFILE=$(PROGNAME).hex
|
||||||
|
|
||||||
|
@ -17,6 +17,19 @@ all: $(HEXFILE)
|
||||||
main.o: main.c reportdesc.c dataHidReport.c
|
main.o: main.c reportdesc.c dataHidReport.c
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
gcn64txrx0.o: gcn64txrx.S
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@ -DSUFFIX=0 -DGCN64_DATA_BIT=0
|
||||||
|
|
||||||
|
gcn64txrx1.o: gcn64txrx.S
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@ -DSUFFIX=1 -DGCN64_DATA_BIT=2
|
||||||
|
|
||||||
|
gcn64txrx2.o: gcn64txrx.S
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@ -DSUFFIX=2 -DGCN64_DATA_BIT=1
|
||||||
|
|
||||||
|
gcn64txrx3.o: gcn64txrx.S
|
||||||
|
$(CC) $(CFLAGS) -c $< -o $@ -DSUFFIX=3 -DGCN64_DATA_BIT=3
|
||||||
|
|
||||||
|
|
||||||
%.o: %.S
|
%.o: %.S
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
@ -46,7 +59,7 @@ justflash: $(HEXFILE)
|
||||||
./scripts/wait_then_flash.sh $(CPU) $(HEXFILE)
|
./scripts/wait_then_flash.sh $(CPU) $(HEXFILE)
|
||||||
|
|
||||||
chip_erase:
|
chip_erase:
|
||||||
dfu-programmer atmega32u2 erase
|
dfu-programmer at90usb1287 erase
|
||||||
|
|
||||||
reset:
|
reset:
|
||||||
dfu-programmer atmega32u2 reset
|
dfu-programmer at90usb1287 reset
|
||||||
|
|
48
README.md
48
README.md
|
@ -1,27 +1,24 @@
|
||||||
# Gamecube/N64 to USB adapter
|
# Gamecube/N64 to USB adapter firmware (3rd generation)
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
This is the source code for a Gamecube/N64 controller to USB adapter firmware
|
This is the source code for a Gamecube/N64 controller to USB adapter firmware
|
||||||
meant to run on [raphnet.net Multiuse PCB-X](http://www.raphnet.net/electronique/multiuse_pcbX/index_en.php).
|
meant to run on [raphnet.net Multiuse PCB-X](http://www.raphnet.net/electronique/multiuse_pcbX/index_en.php).
|
||||||
|
|
||||||
The project homepage is located at: http://www.raphnet.net/electronique/multiuse_pcbX/index.php
|
## Homepage
|
||||||
|
|
||||||
|
* English: [Gamecube/N64 controller to USB adapter (Third generation)](http://www.raphnet.net/electronique/gcn64_usb_adapter_gen3/index_en.php)
|
||||||
|
* French: [Adaptateur manette Gamecube/N64 à USB (Troisième génération)](http://www.raphnet.net/electronique/gcn64_usb_adapter_gen3/index.php)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
The project is released under the General Public License version 3.
|
The project is released under the General Public License version 3.
|
||||||
|
|
||||||
## Directories
|
|
||||||
|
|
||||||
* / : The firmware source code
|
|
||||||
* tool/ : This directory contains utilities to configure and update an adapter, manipulate mempak
|
|
||||||
image files, etc.
|
|
||||||
* scripts/ : Scripts and/or useful files
|
|
||||||
|
|
||||||
## Compiling the firmware
|
## Compiling the firmware
|
||||||
|
|
||||||
You will need a working avr-gcc toolchain with avr-libc and standard utilities such as make. Just
|
You will need a working avr-gcc toolchain with avr-libc and standard utilities such as make. Just
|
||||||
type 'make' and it should build just fine. Under Linux at least.
|
type 'make' and it should build just fine. Under Linux at least.
|
||||||
|
If you are compiling for a custom board or Arduino running on an ATmega32u4, then run 'make -f Makefile.32u4' instead.
|
||||||
|
|
||||||
## Programming the firmware
|
## Programming the firmware
|
||||||
|
|
||||||
|
@ -29,36 +26,3 @@ The makefile has a convenient 'flash' target which sends a command to the firmwa
|
||||||
the bootloader and then executes dfu-programmer (it must of course be installed) with the
|
the bootloader and then executes dfu-programmer (it must of course be installed) with the
|
||||||
correct arguments.
|
correct arguments.
|
||||||
|
|
||||||
Note that the tool must be compiled first and your permissions must also be set so that your
|
|
||||||
user may access the device. See 'Using the tools' below for more information.
|
|
||||||
|
|
||||||
## Compiling the tools
|
|
||||||
|
|
||||||
In the tool/ directory, just type make.
|
|
||||||
|
|
||||||
There are a few dependencies:
|
|
||||||
- libhidapi-dev
|
|
||||||
- libhidapi-hidraw0
|
|
||||||
- pkg-config
|
|
||||||
- gtk3 (for the gui only)
|
|
||||||
|
|
||||||
Provided you have all the dependencies installed, under Linux at least, it should
|
|
||||||
compile without errors. For other environments such has MinGW, there are provisions
|
|
||||||
in the makefile to auto-detect and tweak the build accordingly, but it if fails, be
|
|
||||||
prepared to hack the makefile.
|
|
||||||
|
|
||||||
## Using the tools
|
|
||||||
|
|
||||||
Under Linux, you should configure udev to give the proper permissions to your user,
|
|
||||||
otherwise communicating with the device won't be possible. The same requirement
|
|
||||||
also applies to dfu-programmer.
|
|
||||||
|
|
||||||
An easy way to do this is to copy the two files below to /etc/udev/rules.d, restart
|
|
||||||
udev and reconnect the devices.
|
|
||||||
|
|
||||||
scripts/99-atmel-dfu.rules
|
|
||||||
scripts/99-raphnet.rules
|
|
||||||
|
|
||||||
For information on how to actually /use/ the tools, try --help. Ex:
|
|
||||||
|
|
||||||
$ ./gcn64ctl --help
|
|
||||||
|
|
32
bootloader.c
32
bootloader.c
|
@ -1,5 +1,5 @@
|
||||||
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
||||||
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
|
Copyright (C) 2007-2021 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -15,18 +15,44 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#include <avr/io.h>
|
#include <avr/io.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <avr/wdt.h>
|
||||||
|
#include <util/delay.h>
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
|
|
||||||
void enterBootLoader(void)
|
void enterBootLoader(void)
|
||||||
{
|
{
|
||||||
|
cli();
|
||||||
usb_shutdown();
|
usb_shutdown();
|
||||||
|
_delay_ms(10);
|
||||||
|
|
||||||
|
#if defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega32U4__)
|
||||||
|
// ATmega32u2 : 0x3800
|
||||||
|
asm volatile(
|
||||||
|
"cli \n"
|
||||||
|
"ldi r30, 0x00 \n" // ZL
|
||||||
|
"ldi r31, 0x38 \n" // ZH
|
||||||
|
"ijmp");
|
||||||
|
#elif defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__)
|
||||||
// AT90USB1287/1286 : 0xF000 (word address)
|
// AT90USB1287/1286 : 0xF000 (word address)
|
||||||
// ATmega32u2 : ???
|
|
||||||
asm volatile(
|
asm volatile(
|
||||||
"cli \n"
|
"cli \n"
|
||||||
"ldi r30, 0x00 \n" // ZL
|
"ldi r30, 0x00 \n" // ZL
|
||||||
"ldi r31, 0xF0 \n" // ZH
|
"ldi r31, 0xF0 \n" // ZH
|
||||||
"ijmp");
|
"ijmp");
|
||||||
|
#else
|
||||||
|
#error Bootloader address unknown for this CPU
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetFirmware(void)
|
||||||
|
{
|
||||||
|
usb_shutdown();
|
||||||
|
|
||||||
|
// jump to the application reset vector
|
||||||
|
asm volatile(
|
||||||
|
"cli \n"
|
||||||
|
"ldi r30, 0x00 \n" // ZL
|
||||||
|
"ldi r31, 0x00 \n" // ZH
|
||||||
|
"ijmp");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,7 @@
|
||||||
|
#ifndef _bootloader_h__
|
||||||
|
#define _bootloader_h__
|
||||||
|
|
||||||
void enterBootLoader(void);
|
void enterBootLoader(void);
|
||||||
|
void resetFirmware(void);
|
||||||
|
|
||||||
|
#endif // _bootloader_h__
|
||||||
|
|
|
@ -1,3 +1,65 @@
|
||||||
|
- Future ideas / TODO
|
||||||
|
- Add very basic N64 mouse support (detect and treat it like a controller)
|
||||||
|
|
||||||
|
- March 5, 2021 : Version 3.6.1
|
||||||
|
- Alter timing so the brawler64 wireless gamepad will work. Those will now
|
||||||
|
work with a poll interval >= 2ms on single port adapters, and >= 4ms on dual
|
||||||
|
port adapters.
|
||||||
|
- Add a feature to swap the main analog stick and the D-Pad
|
||||||
|
- Save memory in usb.c (no user visible effects)
|
||||||
|
- Correct bootloader entry address (*maybe* less chances of failing)
|
||||||
|
|
||||||
|
- November 6, 2018 : Version 3.6.0
|
||||||
|
- Add gamecube keyboard support
|
||||||
|
|
||||||
|
- September 18, 2018 : Version 3.5.2
|
||||||
|
- Improve PID (force feedback) implementation
|
||||||
|
- Implement reset firmware command
|
||||||
|
- Add echo feature (USB comm test)
|
||||||
|
|
||||||
|
- April 10, 2018 : Version 3.5.1
|
||||||
|
- Fix never-stopping vibration issues (Dolphin)
|
||||||
|
|
||||||
|
- Novembre 25, 2017 : Version 3.5.0
|
||||||
|
- Add a "triggers as buttons" mode for Gamecube controllers
|
||||||
|
- Add a "disable analog triggers" mode for Gamecube controllers
|
||||||
|
- Internal changes to workaround a presumed Windows bug (Joystick ID
|
||||||
|
confusion where the second controller stops working or gives an
|
||||||
|
error in the Game controller test dialog)
|
||||||
|
- Implement a feature to let the adapter manager query the feature
|
||||||
|
set of the current firmware.
|
||||||
|
|
||||||
|
- January 8, 2017 : Version 3.4.0
|
||||||
|
- New IO request for even lower latency when using the raphnetraw plugins
|
||||||
|
- Reduced memory footprint
|
||||||
|
|
||||||
|
- November 27, 2016 : Version 3.3.2
|
||||||
|
- Fix the get controller type command
|
||||||
|
|
||||||
|
- November 2, 2016 : Version 3.3.1
|
||||||
|
- Fix freeze when only one joystick was used in dual controller mode.
|
||||||
|
|
||||||
|
- October 25, 2016 : Version 3.3.0
|
||||||
|
- Implement multi player support (maximum two on Atmega32u2 due to endpoint
|
||||||
|
limit). New personalities, each with unique USB PID and product name:
|
||||||
|
- Dual GC/N64 to USB mode
|
||||||
|
- Dual N64-only to USB mode
|
||||||
|
- Dual GC-only to USB mode
|
||||||
|
- Core communication code updated to support up to four channels.
|
||||||
|
|
||||||
|
- August 22, 2016 : Version 3.2.1
|
||||||
|
- Implement N64-only and GC-only personalities (Different product ID and
|
||||||
|
device name)
|
||||||
|
|
||||||
|
- May 22, 2016 : Version 3.2.0
|
||||||
|
- Fix reconnecting loop in MacOS X
|
||||||
|
- Change gamecube trigger HID usage (Slider became Z). Now it works fine in openEMU.
|
||||||
|
- Version and product string updated.
|
||||||
|
- USB product ID changed.
|
||||||
|
|
||||||
|
- February 29, 2016 : Version 3.1.1
|
||||||
|
- Fix vibration code for x360ce (Was always on)
|
||||||
|
|
||||||
- February 24, 2016 : Version 3.1.0
|
- February 24, 2016 : Version 3.1.0
|
||||||
- Add a test rumble command (for GUI tool, or for simple rumble control)
|
- Add a test rumble command (for GUI tool, or for simple rumble control)
|
||||||
- Adjust Gamecube/N64 wire protocol timing (Fixes Mad Catz controller)
|
- Adjust Gamecube/N64 wire protocol timing (Fixes Mad Catz controller)
|
||||||
|
|
83
config.c
83
config.c
|
@ -1,5 +1,5 @@
|
||||||
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
||||||
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
|
Copyright (C) 2007-2016 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -42,8 +42,41 @@ static void config_set_serial(char serial[SERIAL_NUM_LEN])
|
||||||
eeprom_commit();
|
eeprom_commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct paramAndFlag {
|
||||||
|
uint8_t param; // CFG_PARAM_* (requests.h)
|
||||||
|
uint32_t flag; // FLAG_* (config.h)
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct paramAndFlag paramsAndFlags[] = {
|
||||||
|
{ CFG_PARAM_INVERT_TRIG, FLAG_GC_INVERT_TRIGS },
|
||||||
|
{ CFG_PARAM_FULL_SLIDERS, FLAG_GC_FULL_SLIDERS },
|
||||||
|
{ CFG_PARAM_TRIGGERS_AS_BUTTONS, FLAG_GC_SLIDERS_AS_BUTTONS },
|
||||||
|
{ CFG_PARAM_DISABLE_ANALOG_TRIGGERS, FLAG_DISABLE_ANALOG_TRIGGERS },
|
||||||
|
{ CFG_PARAM_SWAP_STICK_AND_DPAD, FLAG_SWAP_STICK_AND_DPAD },
|
||||||
|
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t config_getSupportedParams(uint8_t *dst)
|
||||||
|
{
|
||||||
|
uint8_t n = 0, i;
|
||||||
|
|
||||||
|
dst[n++] = CFG_PARAM_MODE;
|
||||||
|
dst[n++] = CFG_PARAM_SERIAL;
|
||||||
|
for (i=0; i<NUM_CHANNELS; i++) {
|
||||||
|
dst[n++] = CFG_PARAM_POLL_INTERVAL0 + i;
|
||||||
|
}
|
||||||
|
for (i=0; paramsAndFlags[i].flag; i++) {
|
||||||
|
dst[n++] = paramsAndFlags[i].param;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned char config_getParam(unsigned char param, unsigned char *value, unsigned char max_len)
|
unsigned char config_getParam(unsigned char param, unsigned char *value, unsigned char max_len)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
switch (param)
|
switch (param)
|
||||||
{
|
{
|
||||||
case CFG_PARAM_MODE:
|
case CFG_PARAM_MODE:
|
||||||
|
@ -70,12 +103,14 @@ unsigned char config_getParam(unsigned char param, unsigned char *value, unsigne
|
||||||
*value = g_eeprom_data.cfg.poll_interval[3];
|
*value = g_eeprom_data.cfg.poll_interval[3];
|
||||||
return 1;
|
return 1;
|
||||||
#endif
|
#endif
|
||||||
case CFG_PARAM_INVERT_TRIG:
|
|
||||||
*value = (g_eeprom_data.cfg.flags & FLAG_GC_INVERT_TRIGS) ? 1 : 0;
|
default:
|
||||||
return 1;
|
for (i=0; paramsAndFlags[i].flag; i++) {
|
||||||
case CFG_PARAM_FULL_SLIDERS:
|
if (param == paramsAndFlags[i].param) {
|
||||||
*value = (g_eeprom_data.cfg.flags & FLAG_GC_FULL_SLIDERS) ? 1 : 0;
|
*value = (g_eeprom_data.cfg.flags & paramsAndFlags[i].flag) ? 1 : 0;
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -83,6 +118,8 @@ unsigned char config_getParam(unsigned char param, unsigned char *value, unsigne
|
||||||
|
|
||||||
unsigned char config_setParam(unsigned char param, const unsigned char *value)
|
unsigned char config_setParam(unsigned char param, const unsigned char *value)
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
if (!value)
|
if (!value)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -112,22 +149,24 @@ unsigned char config_setParam(unsigned char param, const unsigned char *value)
|
||||||
g_eeprom_data.cfg.poll_interval[3] = value[0];
|
g_eeprom_data.cfg.poll_interval[3] = value[0];
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case CFG_PARAM_FULL_SLIDERS:
|
|
||||||
if (value[0]) {
|
|
||||||
g_eeprom_data.cfg.flags |= FLAG_GC_FULL_SLIDERS;
|
|
||||||
} else {
|
|
||||||
g_eeprom_data.cfg.flags &= ~FLAG_GC_FULL_SLIDERS;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CFG_PARAM_INVERT_TRIG:
|
|
||||||
if (value[0]) {
|
|
||||||
g_eeprom_data.cfg.flags |= FLAG_GC_INVERT_TRIGS;
|
|
||||||
} else {
|
|
||||||
g_eeprom_data.cfg.flags &= ~FLAG_GC_INVERT_TRIGS;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
return 0;
|
for (i=0; paramsAndFlags[i].flag; i++) {
|
||||||
|
if (param == paramsAndFlags[i].param) {
|
||||||
|
if (value[0]) {
|
||||||
|
g_eeprom_data.cfg.flags |= paramsAndFlags[i].flag;
|
||||||
|
} else {
|
||||||
|
g_eeprom_data.cfg.flags &= ~paramsAndFlags[i].flag;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we made it through the list without finding
|
||||||
|
// a matching parameter, do nothing.
|
||||||
|
if (!paramsAndFlags[i].flag) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eeprom_commit();
|
eeprom_commit();
|
||||||
|
|
9
config.h
9
config.h
|
@ -10,8 +10,11 @@ struct eeprom_cfg {
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define FLAG_GC_FULL_SLIDERS 1
|
#define FLAG_GC_FULL_SLIDERS 0x01
|
||||||
#define FLAG_GC_INVERT_TRIGS 2
|
#define FLAG_GC_INVERT_TRIGS 0x02
|
||||||
|
#define FLAG_GC_SLIDERS_AS_BUTTONS 0x04
|
||||||
|
#define FLAG_DISABLE_ANALOG_TRIGGERS 0x08
|
||||||
|
#define FLAG_SWAP_STICK_AND_DPAD 0x10
|
||||||
|
|
||||||
void eeprom_app_write_defaults(void);
|
void eeprom_app_write_defaults(void);
|
||||||
void eeprom_app_ready(void);
|
void eeprom_app_ready(void);
|
||||||
|
@ -19,4 +22,6 @@ void eeprom_app_ready(void);
|
||||||
unsigned char config_setParam(unsigned char param, const unsigned char *value);
|
unsigned char config_setParam(unsigned char param, const unsigned char *value);
|
||||||
unsigned char config_getParam(unsigned char param, unsigned char *value, unsigned char max_len);
|
unsigned char config_getParam(unsigned char param, unsigned char *value, unsigned char max_len);
|
||||||
|
|
||||||
|
uint8_t config_getSupportedParams(uint8_t *dst);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
||||||
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
|
Copyright (C) 2007-2016 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -24,7 +24,7 @@ const uint8_t dataHidReport[] PROGMEM = {
|
||||||
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||||
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
|
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
|
||||||
0x75, 0x08, // REPORT_SIZE (8)
|
0x75, 0x08, // REPORT_SIZE (8)
|
||||||
0x95, 40, // REPORT_COUNT (40)
|
0x95, 63, // REPORT_COUNT (63)
|
||||||
0x09, 0x01, // USAGE (Vendor defined)
|
0x09, 0x01, // USAGE (Vendor defined)
|
||||||
0xB1, 0x00, // FEATURE (Data,Ary,Abs)
|
0xB1, 0x00, // FEATURE (Data,Ary,Abs)
|
||||||
0xc0 // END_COLLECTION
|
0xc0 // END_COLLECTION
|
||||||
|
|
2
eeprom.c
2
eeprom.c
|
@ -1,5 +1,5 @@
|
||||||
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
||||||
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
|
Copyright (C) 2007-2016 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
177
gamecube.c
177
gamecube.c
|
@ -1,5 +1,5 @@
|
||||||
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
||||||
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
|
Copyright (C) 2007-2016 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -23,20 +23,30 @@
|
||||||
#include "gcn64_protocol.h"
|
#include "gcn64_protocol.h"
|
||||||
|
|
||||||
/*********** prototypes *************/
|
/*********** prototypes *************/
|
||||||
static void gamecubeInit(void);
|
static void gamecubeInit(unsigned char chn);
|
||||||
static char gamecubeUpdate(void);
|
static void gamecubeInitKB(unsigned char chn);
|
||||||
static char gamecubeChanged(void);
|
static char gamecubeUpdate(unsigned char chn);
|
||||||
|
static char gamecubeUpdateKB(unsigned char chn);
|
||||||
|
static char gamecubeChanged(unsigned char chn);
|
||||||
|
|
||||||
static char gc_rumbling = 0;
|
static char gc_rumbling[GAMEPAD_MAX_CHANNELS] = { };
|
||||||
static char origins_set = 0;
|
static char origins_set[GAMEPAD_MAX_CHANNELS] = { };
|
||||||
static unsigned char orig_x, orig_y, orig_cx, orig_cy;
|
static unsigned char orig_x[GAMEPAD_MAX_CHANNELS];
|
||||||
|
static unsigned char orig_y[GAMEPAD_MAX_CHANNELS];
|
||||||
|
static unsigned char orig_cx[GAMEPAD_MAX_CHANNELS];
|
||||||
|
static unsigned char orig_cy[GAMEPAD_MAX_CHANNELS];
|
||||||
|
|
||||||
static void gamecubeInit(void)
|
static void gamecubeInit(unsigned char chn)
|
||||||
{
|
{
|
||||||
gamecubeUpdate();
|
gamecubeUpdate(chn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gc_decodeAnswer(unsigned char data[8])
|
static void gamecubeInitKB(unsigned char chn)
|
||||||
|
{
|
||||||
|
gamecubeUpdateKB(chn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc_decodeAnswer(unsigned char chn, unsigned char data[8])
|
||||||
{
|
{
|
||||||
unsigned char x,y,cx,cy;
|
unsigned char x,y,cx,cy;
|
||||||
|
|
||||||
|
@ -81,107 +91,120 @@ void gc_decodeAnswer(unsigned char data[8])
|
||||||
56-63 Right Btn Val
|
56-63 Right Btn Val
|
||||||
*/
|
*/
|
||||||
|
|
||||||
last_built_report.pad_type = PAD_TYPE_GAMECUBE;
|
last_built_report[chn].pad_type = PAD_TYPE_GAMECUBE;
|
||||||
last_built_report.gc.buttons = data[0] | data[1] << 8;
|
last_built_report[chn].gc.buttons = data[0] | data[1] << 8;
|
||||||
x = data[2];
|
x = data[2];
|
||||||
y = data[3];
|
y = data[3];
|
||||||
cx = data[4];
|
cx = data[4];
|
||||||
cy = data[5];
|
cy = data[5];
|
||||||
last_built_report.gc.lt = data[6];
|
last_built_report[chn].gc.lt = data[6];
|
||||||
last_built_report.gc.rt = data[7];
|
last_built_report[chn].gc.rt = data[7];
|
||||||
memcpy(last_built_report.gc.raw_data, data, 8);
|
|
||||||
|
|
||||||
if (origins_set) {
|
#ifdef PAD_DATA_HAS_RAW
|
||||||
last_built_report.gc.x = ((int)x-(int)orig_x);
|
memcpy(last_built_report[chn].gc.raw_data, data, 8);
|
||||||
last_built_report.gc.y = ((int)y-(int)orig_y);
|
#endif
|
||||||
last_built_report.gc.cx = ((int)cx-(int)orig_cx);
|
|
||||||
last_built_report.gc.cy = ((int)cy-(int)orig_cy);
|
if (origins_set[chn]) {
|
||||||
|
last_built_report[chn].gc.x = ((int)x-(int)orig_x[chn]);
|
||||||
|
last_built_report[chn].gc.y = ((int)y-(int)orig_y[chn]);
|
||||||
|
last_built_report[chn].gc.cx = ((int)cx-(int)orig_cx[chn]);
|
||||||
|
last_built_report[chn].gc.cy = ((int)cy-(int)orig_cy[chn]);
|
||||||
} else {
|
} else {
|
||||||
orig_x = x;
|
orig_x[chn] = x;
|
||||||
orig_y = y;
|
orig_y[chn] = y;
|
||||||
orig_cx = cx;
|
orig_cx[chn] = cx;
|
||||||
orig_cy = cy;
|
orig_cy[chn] = cy;
|
||||||
last_built_report.gc.x = 0;
|
last_built_report[chn].gc.x = 0;
|
||||||
last_built_report.gc.y = 0;
|
last_built_report[chn].gc.y = 0;
|
||||||
last_built_report.gc.cx = 0;
|
last_built_report[chn].gc.cx = 0;
|
||||||
last_built_report.gc.cy = 0;
|
last_built_report[chn].gc.cy = 0;
|
||||||
origins_set = 1;
|
origins_set[chn] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char gamecubeUpdateKB(unsigned char chn)
|
||||||
static char gamecubeUpdate()
|
|
||||||
{
|
{
|
||||||
unsigned char tmpdata[GC_GETSTATUS_REPLY_LENGTH];
|
unsigned char tmpdata[GC_GETSTATUS_REPLY_LENGTH];
|
||||||
unsigned char count;
|
unsigned char count;
|
||||||
|
unsigned char i, lrc;
|
||||||
|
|
||||||
#if 0
|
tmpdata[0] = GC_POLL_KB1;
|
||||||
/* The GetID command. This is required for the Nintendo Wavebird to work... */
|
tmpdata[1] = GC_POLL_KB2;
|
||||||
tmp = GC_GETID;
|
tmpdata[2] = GC_POLL_KB3;
|
||||||
count = gcn64_transaction(&tmp, 1);
|
|
||||||
if (count != GC_GETID_REPLY_LENGTH) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
count = gcn64_transaction(chn, tmpdata, 3, tmpdata, GC_GETSTATUS_REPLY_LENGTH);
|
||||||
* The wavebird needs time. It does not answer the
|
|
||||||
* folowwing get status command if we don't wait here.
|
|
||||||
*
|
|
||||||
* A good 2:1 safety margin has been chosen.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
// 10 : does not work
|
|
||||||
// 20 : does not work
|
|
||||||
// 25 : works
|
|
||||||
// 30 : works
|
|
||||||
_delay_us(50);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
tmpdata[0] = GC_GETSTATUS1;
|
|
||||||
tmpdata[1] = GC_GETSTATUS2;
|
|
||||||
tmpdata[2] = GC_GETSTATUS3(gc_rumbling);
|
|
||||||
|
|
||||||
count = gcn64_transaction(tmpdata, 3, tmpdata, GC_GETSTATUS_REPLY_LENGTH);
|
|
||||||
if (count != GC_GETSTATUS_REPLY_LENGTH) {
|
if (count != GC_GETSTATUS_REPLY_LENGTH) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
gc_decodeAnswer(tmpdata);
|
// Compute LRC
|
||||||
|
for (i=0, lrc=0; i<6; i++) {
|
||||||
|
lrc ^= tmpdata[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmpdata[7] != lrc) {
|
||||||
|
return 1; // LRC error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ok, fill the report
|
||||||
|
last_built_report[chn].pad_type = PAD_TYPE_GC_KB;
|
||||||
|
for (i=0; i<3; i++) {
|
||||||
|
last_built_report[chn].gckb.keys[i] = tmpdata[4+i];
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gamecubeHotplug(void)
|
static char gamecubeUpdate(unsigned char chn)
|
||||||
{
|
{
|
||||||
// Make sure next read becomes the refence center values
|
unsigned char tmpdata[GC_GETSTATUS_REPLY_LENGTH];
|
||||||
origins_set = 0;
|
unsigned char count;
|
||||||
|
|
||||||
|
tmpdata[0] = GC_GETSTATUS1;
|
||||||
|
tmpdata[1] = GC_GETSTATUS2;
|
||||||
|
tmpdata[2] = GC_GETSTATUS3(gc_rumbling[chn]);
|
||||||
|
|
||||||
|
count = gcn64_transaction(chn, tmpdata, 3, tmpdata, GC_GETSTATUS_REPLY_LENGTH);
|
||||||
|
if (count != GC_GETSTATUS_REPLY_LENGTH) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
gc_decodeAnswer(chn, tmpdata);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char gamecubeProbe(void)
|
static void gamecubeHotplug(unsigned char chn)
|
||||||
{
|
{
|
||||||
origins_set = 0;
|
// Make sure next read becomes the refence center values
|
||||||
|
origins_set[chn] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (gamecubeUpdate()) {
|
static char gamecubeProbe(unsigned char chn)
|
||||||
|
{
|
||||||
|
origins_set[chn] = 0;
|
||||||
|
|
||||||
|
if (gamecubeUpdate(chn)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char gamecubeChanged(void)
|
static char gamecubeChanged(unsigned char chn)
|
||||||
{
|
{
|
||||||
return memcmp(&last_built_report, &last_sent_report, sizeof(gamepad_data));
|
return memcmp(&last_built_report[chn], &last_sent_report[chn], sizeof(gamepad_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gamecubeGetReport(gamepad_data *dst)
|
static void gamecubeGetReport(unsigned char chn, gamepad_data *dst)
|
||||||
{
|
{
|
||||||
if (dst)
|
if (dst)
|
||||||
memcpy(dst, &last_built_report, sizeof(gamepad_data));
|
memcpy(dst, &last_built_report[chn], sizeof(gamepad_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gamecubeVibration(char enable)
|
static void gamecubeVibration(unsigned char chn, char enable)
|
||||||
{
|
{
|
||||||
gc_rumbling = enable;
|
gc_rumbling[chn] = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
Gamepad GamecubeGamepad = {
|
Gamepad GamecubeGamepad = {
|
||||||
|
@ -198,3 +221,17 @@ Gamepad *gamecubeGetGamepad(void)
|
||||||
{
|
{
|
||||||
return &GamecubeGamepad;
|
return &GamecubeGamepad;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Gamepad GamecubeKeyboard = {
|
||||||
|
.init = gamecubeInitKB,
|
||||||
|
.update = gamecubeUpdateKB,
|
||||||
|
.changed = gamecubeChanged,
|
||||||
|
.getReport = gamecubeGetReport,
|
||||||
|
.probe = gamecubeProbe,
|
||||||
|
};
|
||||||
|
|
||||||
|
Gamepad *gamecubeGetKeyboard(void)
|
||||||
|
{
|
||||||
|
return &GamecubeKeyboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include "gamepads.h"
|
#include "gamepads.h"
|
||||||
|
|
||||||
#define GAMECUBE_UPDATE_NORMAL 0
|
|
||||||
#define GAMECUBE_UPDATE_ORIGIN 1
|
|
||||||
|
|
||||||
Gamepad *gamecubeGetGamepad(void);
|
Gamepad *gamecubeGetGamepad(void);
|
||||||
|
Gamepad *gamecubeGetKeyboard(void);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
||||||
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
|
Copyright (C) 2007-2016 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -16,7 +16,8 @@
|
||||||
*/
|
*/
|
||||||
#include "gamepads.h"
|
#include "gamepads.h"
|
||||||
|
|
||||||
/* Shared between N64 and GC (only one is used at a time). Saves memory. */
|
|
||||||
|
|
||||||
gamepad_data last_sent_report;
|
/* Shared between N64 and GC (only one is used at a time). Saves memory. */
|
||||||
gamepad_data last_built_report;
|
gamepad_data last_sent_report[GAMEPAD_MAX_CHANNELS];
|
||||||
|
gamepad_data last_built_report[GAMEPAD_MAX_CHANNELS];
|
||||||
|
|
||||||
|
|
32
gamepads.h
32
gamepads.h
|
@ -1,9 +1,12 @@
|
||||||
#ifndef _gamepads_h__
|
#ifndef _gamepads_h__
|
||||||
#define _gamepads_h__
|
#define _gamepads_h__
|
||||||
|
|
||||||
|
#undef PAD_DATA_HAS_RAW
|
||||||
|
|
||||||
#define PAD_TYPE_NONE 0
|
#define PAD_TYPE_NONE 0
|
||||||
#define PAD_TYPE_N64 4
|
#define PAD_TYPE_N64 4
|
||||||
#define PAD_TYPE_GAMECUBE 5
|
#define PAD_TYPE_GAMECUBE 5
|
||||||
|
#define PAD_TYPE_GC_KB 6
|
||||||
|
|
||||||
#define N64_RAW_SIZE 4
|
#define N64_RAW_SIZE 4
|
||||||
#define GC_RAW_SIZE 8
|
#define GC_RAW_SIZE 8
|
||||||
|
@ -12,7 +15,9 @@ typedef struct _n64_pad_data {
|
||||||
unsigned char pad_type; // PAD_TYPE_N64
|
unsigned char pad_type; // PAD_TYPE_N64
|
||||||
char x,y;
|
char x,y;
|
||||||
unsigned short buttons;
|
unsigned short buttons;
|
||||||
|
#ifdef PAD_DATA_HAS_RAW
|
||||||
unsigned char raw_data[N64_RAW_SIZE];
|
unsigned char raw_data[N64_RAW_SIZE];
|
||||||
|
#endif
|
||||||
} n64_pad_data;
|
} n64_pad_data;
|
||||||
|
|
||||||
#define N64_BTN_A 0x8000
|
#define N64_BTN_A 0x8000
|
||||||
|
@ -38,7 +43,9 @@ typedef struct _gc_pad_data {
|
||||||
char x,y,cx,cy;
|
char x,y,cx,cy;
|
||||||
unsigned char lt,rt;
|
unsigned char lt,rt;
|
||||||
unsigned short buttons;
|
unsigned short buttons;
|
||||||
|
#ifdef PAD_DATA_HAS_RAW
|
||||||
unsigned char raw_data[GC_RAW_SIZE];
|
unsigned char raw_data[GC_RAW_SIZE];
|
||||||
|
#endif
|
||||||
} gc_pad_data;
|
} gc_pad_data;
|
||||||
|
|
||||||
#define GC_BTN_A 0x0001
|
#define GC_BTN_A 0x0001
|
||||||
|
@ -63,29 +70,36 @@ typedef struct _gc_pad_data {
|
||||||
|
|
||||||
#define GC_ALL_BUTTONS (GC_BTN_START|GC_BTN_Y|GC_BTN_X|GC_BTN_B|GC_BTN_A|GC_BTN_L|GC_BTN_R|GC_BTN_Z|GC_BTN_DPAD_UP|GC_BTN_DPAD_DOWN|GC_BTN_DPAD_RIGHT|GC_BTN_DPAD_LEFT)
|
#define GC_ALL_BUTTONS (GC_BTN_START|GC_BTN_Y|GC_BTN_X|GC_BTN_B|GC_BTN_A|GC_BTN_L|GC_BTN_R|GC_BTN_Z|GC_BTN_DPAD_UP|GC_BTN_DPAD_DOWN|GC_BTN_DPAD_RIGHT|GC_BTN_DPAD_LEFT)
|
||||||
|
|
||||||
|
typedef struct _gc_keyboard_data {
|
||||||
|
unsigned char pad_type;
|
||||||
|
unsigned char keys[3];
|
||||||
|
} gc_keyboard_data;
|
||||||
|
|
||||||
typedef struct _gamepad_data {
|
typedef struct _gamepad_data {
|
||||||
union {
|
union {
|
||||||
unsigned char pad_type; // PAD_TYPE_*
|
unsigned char pad_type; // PAD_TYPE_*
|
||||||
n64_pad_data n64;
|
n64_pad_data n64;
|
||||||
gc_pad_data gc;
|
gc_pad_data gc;
|
||||||
|
gc_keyboard_data gckb;
|
||||||
};
|
};
|
||||||
} gamepad_data;
|
} gamepad_data;
|
||||||
|
|
||||||
|
#define GAMEPAD_MAX_CHANNELS 2
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void (*init)(void);
|
void (*init)(unsigned char chn);
|
||||||
char (*update)(void);
|
char (*update)(unsigned char chn);
|
||||||
char (*changed)(void);
|
char (*changed)(unsigned char chn);
|
||||||
void (*hotplug)(void);
|
void (*hotplug)(unsigned char chn);
|
||||||
void (*getReport)(gamepad_data *dst);
|
void (*getReport)(unsigned char chn, gamepad_data *dst);
|
||||||
void (*setVibration)(char enable);
|
void (*setVibration)(unsigned char chn, char enable);
|
||||||
char (*probe)(void); /* return true if found */
|
char (*probe)(unsigned char chn); /* return true if found */
|
||||||
} Gamepad;
|
} Gamepad;
|
||||||
|
|
||||||
/* What was most recently read from the controller */
|
/* What was most recently read from the controller */
|
||||||
extern gamepad_data last_built_report;
|
extern gamepad_data last_built_report[GAMEPAD_MAX_CHANNELS];
|
||||||
|
|
||||||
/* What was most recently sent to the host */
|
/* What was most recently sent to the host */
|
||||||
extern gamepad_data last_sent_report;
|
extern gamepad_data last_sent_report[GAMEPAD_MAX_CHANNELS];
|
||||||
|
|
||||||
#endif // _gamepads_h__
|
#endif // _gamepads_h__
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
/* 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/>.
|
||||||
|
*/
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
#include "gc_kb.h"
|
||||||
|
#include "hid_keycodes.h"
|
||||||
|
#include "gcn64_protocol.h"
|
||||||
|
|
||||||
|
|
||||||
|
// http://www2d.biglobe.ne.jp/~msyk/keyboard/layout/usbkeycode.html
|
||||||
|
|
||||||
|
static const unsigned char gc_to_hid_table[] PROGMEM = {
|
||||||
|
GC_KEY_RESERVED, HID_KB_NOEVENT,
|
||||||
|
GC_KEY_HOME, HID_KB_HOME,
|
||||||
|
GC_KEY_END, HID_KB_END,
|
||||||
|
GC_KEY_PGUP, HID_KB_PGUP,
|
||||||
|
GC_KEY_PGDN, HID_KB_PGDN,
|
||||||
|
GC_KEY_SCROLL_LOCK, HID_KB_SCROLL_LOCK,
|
||||||
|
GC_KEY_DASH_UNDERSCORE, HID_KB_DASH_UNDERSCORE,
|
||||||
|
GC_KEY_PLUS_EQUAL, HID_KB_EQUAL_PLUS,
|
||||||
|
GC_KEY_YEN, HID_KB_INTERNATIONAL3,
|
||||||
|
GC_KEY_OPEN_BRKT_BRACE, HID_KB_OPEN_BRKT_BRACE,
|
||||||
|
GC_KEY_SEMI_COLON_COLON,HID_KB_SEMI_COLON_COLON,
|
||||||
|
GC_KEY_QUOTES, HID_KB_QUOTES,
|
||||||
|
GC_KEY_CLOSE_BRKT_BRACE,HID_KB_CLOSE_BRKT_BRACE,
|
||||||
|
GC_KEY_BRACKET_MU, HID_KB_NONUS_HASH_TILDE,
|
||||||
|
GC_KEY_COMMA_ST, HID_KB_COMMA_SMALLER_THAN,
|
||||||
|
GC_KEY_PERIOD_GT, HID_KB_PERIOD_GREATER_THAN,
|
||||||
|
GC_KEY_SLASH_QUESTION, HID_KB_SLASH_QUESTION,
|
||||||
|
GC_KEY_INTERNATIONAL1, HID_KB_INTERNATIONAL1,
|
||||||
|
GC_KEY_ESC, HID_KB_ESCAPE,
|
||||||
|
GC_KEY_INSERT, HID_KB_INSERT,
|
||||||
|
GC_KEY_DELETE, HID_KB_DELETE_FORWARD,
|
||||||
|
GC_KEY_HANKAKU, HID_KB_GRAVE_ACCENT_AND_TILDE,
|
||||||
|
GC_KEY_BACKSPACE, HID_KB_BACKSPACE,
|
||||||
|
GC_KEY_TAB, HID_KB_TAB,
|
||||||
|
GC_KEY_CAPS_LOCK, HID_KB_CAPS_LOCK,
|
||||||
|
GC_KEY_MUHENKAN, HID_KB_INTERNATIONAL5,
|
||||||
|
GC_KEY_SPACE, HID_KB_SPACE,
|
||||||
|
GC_KEY_HENKAN, HID_KB_INTERNATIONAL4,
|
||||||
|
GC_KEY_KANA, HID_KB_INTERNATIONAL2,
|
||||||
|
GC_KEY_LEFT, HID_KB_LEFT_ARROW,
|
||||||
|
GC_KEY_DOWN, HID_KB_DOWN_ARROW,
|
||||||
|
GC_KEY_UP, HID_KB_UP_ARROW,
|
||||||
|
GC_KEY_RIGHT, HID_KB_RIGHT_ARROW,
|
||||||
|
GC_KEY_ENTER, HID_KB_ENTER,
|
||||||
|
|
||||||
|
/* "shift" keys */
|
||||||
|
GC_KEY_LEFT_SHIFT, HID_KB_LEFT_SHIFT,
|
||||||
|
GC_KEY_RIGHT_SHIFT, HID_KB_RIGHT_SHIFT,
|
||||||
|
GC_KEY_LEFT_CTRL, HID_KB_LEFT_CONTROL,
|
||||||
|
|
||||||
|
/* This keyboard only has a left alt key. But as right alt is required to access some
|
||||||
|
* functions on japanese keyboards, I map the key to right alt.
|
||||||
|
*
|
||||||
|
* eg: RO-MAJI on the hiragana/katakana key */
|
||||||
|
GC_KEY_LEFT_ALT, HID_KB_RIGHT_ALT,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned char gcKeycodeToHID(unsigned char gc_code)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (gc_code >= GC_KEY_A && gc_code <= GC_KEY_0) {
|
||||||
|
// Note: This works since A-Z, 1-9, 0 have consecutive keycode values.
|
||||||
|
return (gc_code - GC_KEY_A) + HID_KB_A;
|
||||||
|
}
|
||||||
|
if (gc_code >= GC_KEY_F1 && gc_code <= GC_KEY_F12) {
|
||||||
|
return (gc_code - GC_KEY_F1) + HID_KB_F1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i<sizeof(gc_to_hid_table); i++) {
|
||||||
|
if (pgm_read_byte(gc_to_hid_table + i*2) == gc_code) {
|
||||||
|
return pgm_read_byte(gc_to_hid_table + i*2 + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0x38; // HID /? key for unknown keys
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
||||||
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
|
Copyright (C) 2007-2016 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -31,13 +31,19 @@
|
||||||
#define GCN64_DATA_PORT PORTD
|
#define GCN64_DATA_PORT PORTD
|
||||||
#define GCN64_DATA_DDR DDRD
|
#define GCN64_DATA_DDR DDRD
|
||||||
#define GCN64_DATA_PIN PIND
|
#define GCN64_DATA_PIN PIND
|
||||||
#define GCN64_DATA_BIT (1<<0)
|
#define GCN64_DATA_BIT0 (1<<0)
|
||||||
|
#define GCN64_DATA_BIT1 (1<<1)
|
||||||
|
#define GCN64_DATA_BIT2 (1<<2)
|
||||||
|
#define GCN64_DATA_BIT3 (1<<3)
|
||||||
#define GCN64_BIT_NUM_S "0" // for asm
|
#define GCN64_BIT_NUM_S "0" // for asm
|
||||||
#else
|
#else
|
||||||
#define GCN64_DATA_PORT PORTA
|
#define GCN64_DATA_PORT PORTA
|
||||||
#define GCN64_DATA_DDR DDRA
|
#define GCN64_DATA_DDR DDRA
|
||||||
#define GCN64_DATA_PIN PINA
|
#define GCN64_DATA_PIN PINA
|
||||||
#define GCN64_DATA_BIT (1<<0)
|
#define GCN64_DATA_BIT0 (1<<0)
|
||||||
|
#define GCN64_DATA_BIT1 (1<<2) // This is not an error
|
||||||
|
#define GCN64_DATA_BIT2 (1<<1) // This is not an error
|
||||||
|
#define GCN64_DATA_BIT3 (1<<3)
|
||||||
#define GCN64_BIT_NUM_S "0" // for asm
|
#define GCN64_BIT_NUM_S "0" // for asm
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -46,11 +52,11 @@
|
||||||
void gcn64protocol_hwinit(void)
|
void gcn64protocol_hwinit(void)
|
||||||
{
|
{
|
||||||
// data as input
|
// data as input
|
||||||
GCN64_DATA_DDR &= ~(GCN64_DATA_BIT);
|
GCN64_DATA_DDR &= ~(GCN64_DATA_BIT0 | GCN64_DATA_BIT1 | GCN64_DATA_BIT2 | GCN64_DATA_BIT3);
|
||||||
|
|
||||||
// keep data low. By toggling the direction, we make the
|
// keep data low. By toggling the direction, we make the
|
||||||
// pin act as an open-drain output.
|
// pin act as an open-drain output.
|
||||||
GCN64_DATA_PORT &= ~GCN64_DATA_BIT;
|
GCN64_DATA_PORT &= ~(GCN64_DATA_BIT0 | GCN64_DATA_BIT1 | GCN64_DATA_BIT2 | GCN64_DATA_BIT3);
|
||||||
|
|
||||||
/* debug bit PORTB4 (MISO) */
|
/* debug bit PORTB4 (MISO) */
|
||||||
DDRB |= 0x10;
|
DDRB |= 0x10;
|
||||||
|
@ -59,15 +65,31 @@ void gcn64protocol_hwinit(void)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Send n data bytes + stop bit, wait for answer.
|
* \brief Send n data bytes + stop bit, wait for answer.
|
||||||
* \return The number of bytes received, 0 on timeout/error.
|
|
||||||
*
|
*
|
||||||
* The result is in gcn64_workbuf.
|
* \param chn Channel (0 to 3)
|
||||||
|
* \param tx Data to be transmitted
|
||||||
|
* \param tx_len Transmission length
|
||||||
|
* \param rx Reception buffer
|
||||||
|
* \param rx_max Buffer size
|
||||||
|
* \return The number of bytes received, 0 on timeout/error.
|
||||||
*/
|
*/
|
||||||
unsigned char gcn64_transaction(const unsigned char *tx, int tx_len, unsigned char *rx, unsigned char rx_max)
|
unsigned char gcn64_transaction(unsigned char chn, const unsigned char *tx, int tx_len, unsigned char *rx, unsigned char rx_max)
|
||||||
{
|
{
|
||||||
int count;
|
int count;
|
||||||
unsigned char sreg = SREG;
|
unsigned char sreg = SREG;
|
||||||
|
|
||||||
|
void (*sendBytes)(const unsigned char *data, unsigned char n_bytes);
|
||||||
|
unsigned char (*receiveBytes)(unsigned char *dstbuf, unsigned char max_bytes);
|
||||||
|
|
||||||
|
switch(chn)
|
||||||
|
{
|
||||||
|
case 0: sendBytes = gcn64_sendBytes0; receiveBytes = gcn64_receiveBytes0; break;
|
||||||
|
case 1: sendBytes = gcn64_sendBytes1; receiveBytes = gcn64_receiveBytes1; break;
|
||||||
|
case 2: sendBytes = gcn64_sendBytes2; receiveBytes = gcn64_receiveBytes2; break;
|
||||||
|
case 3: sendBytes = gcn64_sendBytes3; receiveBytes = gcn64_receiveBytes3; break;
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef TRACE_GCN64
|
#ifdef TRACE_GCN64
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -81,8 +103,8 @@ unsigned char gcn64_transaction(const unsigned char *tx, int tx_len, unsigned ch
|
||||||
#ifdef DISABLE_INTS_DURING_COMM
|
#ifdef DISABLE_INTS_DURING_COMM
|
||||||
cli();
|
cli();
|
||||||
#endif
|
#endif
|
||||||
gcn64_sendBytes(tx, tx_len);
|
sendBytes(tx, tx_len);
|
||||||
count = gcn64_receiveBytes(rx, rx_max);
|
count = receiveBytes(rx, rx_max);
|
||||||
SREG = sreg;
|
SREG = sreg;
|
||||||
|
|
||||||
if (count == 0xff) {
|
if (count == 0xff) {
|
||||||
|
@ -119,14 +141,14 @@ unsigned char gcn64_transaction(const unsigned char *tx, int tx_len, unsigned ch
|
||||||
#if (GC_GETID != N64_GET_CAPABILITIES)
|
#if (GC_GETID != N64_GET_CAPABILITIES)
|
||||||
#error N64 vs GC detection commnad broken
|
#error N64 vs GC detection commnad broken
|
||||||
#endif
|
#endif
|
||||||
int gcn64_detectController(void)
|
int gcn64_detectController(unsigned char chn)
|
||||||
{
|
{
|
||||||
unsigned char tmp = GC_GETID;
|
unsigned char tmp = GC_GETID;
|
||||||
unsigned char count;
|
unsigned char count;
|
||||||
unsigned short id;
|
unsigned short id;
|
||||||
unsigned char data[4];
|
unsigned char data[4];
|
||||||
|
|
||||||
count = gcn64_transaction(&tmp, 1, data, sizeof(data));
|
count = gcn64_transaction(chn, &tmp, 1, data, sizeof(data));
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
return CONTROLLER_IS_ABSENT;
|
return CONTROLLER_IS_ABSENT;
|
||||||
}
|
}
|
||||||
|
@ -159,6 +181,9 @@ int gcn64_detectController(void)
|
||||||
* 0000 0101 0000 0000 0000 0001 : 0x050001 With expansion pack
|
* 0000 0101 0000 0000 0000 0001 : 0x050001 With expansion pack
|
||||||
* 0000 0101 0000 0000 0000 0010 : 0x050002 Expansion pack removed
|
* 0000 0101 0000 0000 0000 0010 : 0x050002 Expansion pack removed
|
||||||
*
|
*
|
||||||
|
* -- N64 mouse --
|
||||||
|
* 0000 0010 0000 0000 0000 0000 : 0x020000
|
||||||
|
*
|
||||||
* -- Ascii keyboard (keyboard connector)
|
* -- Ascii keyboard (keyboard connector)
|
||||||
* 0000 1000 0010 0000 0000 0000 : 0x082000
|
* 0000 1000 0010 0000 0000 0000 : 0x082000
|
||||||
*
|
*
|
||||||
|
@ -193,6 +218,9 @@ int gcn64_detectController(void)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
switch ((id >> 8) & 0x0F) {
|
switch ((id >> 8) & 0x0F) {
|
||||||
|
case 0x02:
|
||||||
|
return CONTROLLER_IS_N64_MOUSE;
|
||||||
|
|
||||||
case 0x05:
|
case 0x05:
|
||||||
return CONTROLLER_IS_N64;
|
return CONTROLLER_IS_N64;
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#define CONTROLLER_IS_GC 2
|
#define CONTROLLER_IS_GC 2
|
||||||
#define CONTROLLER_IS_GC_KEYBOARD 3
|
#define CONTROLLER_IS_GC_KEYBOARD 3
|
||||||
#define CONTROLLER_IS_UNKNOWN 4
|
#define CONTROLLER_IS_UNKNOWN 4
|
||||||
|
#define CONTROLLER_IS_N64_MOUSE 5
|
||||||
|
|
||||||
|
|
||||||
/* Return many unknown bits, but two are about the expansion port. */
|
/* Return many unknown bits, but two are about the expansion port. */
|
||||||
|
@ -160,7 +161,12 @@
|
||||||
#define GC_KEY_ENTER 0x61
|
#define GC_KEY_ENTER 0x61
|
||||||
|
|
||||||
void gcn64protocol_hwinit(void);
|
void gcn64protocol_hwinit(void);
|
||||||
int gcn64_detectController(void);
|
|
||||||
unsigned char gcn64_transaction(const unsigned char *tx, int tx_len, unsigned char *rx, unsigned char rx_max);
|
#define GCN64_CHANNEL_0 0
|
||||||
|
#define GCN64_CHANNEL_1 1
|
||||||
|
#define GCN64_CHANNEL_2 2
|
||||||
|
#define GCN64_CHANNEL_3 3
|
||||||
|
int gcn64_detectController(unsigned char chn);
|
||||||
|
unsigned char gcn64_transaction(unsigned char chn, const unsigned char *tx, int tx_len, unsigned char *rx, unsigned char rx_max);
|
||||||
|
|
||||||
#endif // _gcn64_protocol_h__
|
#endif // _gcn64_protocol_h__
|
||||||
|
|
22
gcn64txrx.S
22
gcn64txrx.S
|
@ -1,8 +1,20 @@
|
||||||
#include <avr/io.h>
|
#include <avr/io.h>
|
||||||
|
|
||||||
|
; When compiling, you must define the following.
|
||||||
|
;
|
||||||
|
; SUFFIX : The suffix for exported function (eg: Set to 0 to generate gcn64_sendBytes0)
|
||||||
|
; GCN64_DATA_BIT : The bit number in the port.
|
||||||
|
;
|
||||||
|
; Port and Pin IOs are defined below.
|
||||||
|
;
|
||||||
|
|
||||||
|
#define CONCAT(a,b) a##b
|
||||||
|
#define EXPORT_SYMBOL(a, b) .global CONCAT(a,b)
|
||||||
|
#define FUNCTION(a, b) CONCAT(a,b)
|
||||||
|
|
||||||
.text
|
.text
|
||||||
.global gcn64_sendBytes
|
EXPORT_SYMBOL(gcn64_sendBytes, SUFFIX)
|
||||||
.global gcn64_receiveBytes
|
EXPORT_SYMBOL(gcn64_receiveBytes, SUFFIX)
|
||||||
|
|
||||||
#define xl r26
|
#define xl r26
|
||||||
#define xh r27
|
#define xh r27
|
||||||
|
@ -24,7 +36,7 @@
|
||||||
#define GCN64_DATA_DDR _SFR_IO_ADDR(DDRD)
|
#define GCN64_DATA_DDR _SFR_IO_ADDR(DDRD)
|
||||||
#define GCN64_DATA_PIN _SFR_IO_ADDR(PIND)
|
#define GCN64_DATA_PIN _SFR_IO_ADDR(PIND)
|
||||||
#endif
|
#endif
|
||||||
#define GCN64_DATA_BIT 0
|
;#define GCN64_DATA_BIT 0
|
||||||
|
|
||||||
#if F_CPU != 16000000L
|
#if F_CPU != 16000000L
|
||||||
#error Only 16MHz clock supported
|
#error Only 16MHz clock supported
|
||||||
|
@ -50,7 +62,7 @@
|
||||||
; r24,r25 : dstbuf
|
; r24,r25 : dstbuf
|
||||||
; r22 : max bytes (for fututre use)
|
; r22 : max bytes (for fututre use)
|
||||||
; return: count in r24,r25 (0xff: Error, 0xfe: Overflow [max_bytes too low])
|
; return: count in r24,r25 (0xff: Error, 0xfe: Overflow [max_bytes too low])
|
||||||
gcn64_receiveBytes:
|
FUNCTION(gcn64_receiveBytes, SUFFIX):
|
||||||
clr xl
|
clr xl
|
||||||
clr xh
|
clr xh
|
||||||
mov zl, r24
|
mov zl, r24
|
||||||
|
@ -149,7 +161,7 @@ rxdone:
|
||||||
* A stop bit is added at thy end of the packet.
|
* A stop bit is added at thy end of the packet.
|
||||||
*
|
*
|
||||||
************************************************/
|
************************************************/
|
||||||
gcn64_sendBytes:
|
FUNCTION(gcn64_sendBytes, SUFFIX):
|
||||||
; Move r23,r24 pointer to z
|
; Move r23,r24 pointer to z
|
||||||
mov zl, r24
|
mov zl, r24
|
||||||
mov zh, r25
|
mov zh, r25
|
||||||
|
|
10
gcn64txrx.h
10
gcn64txrx.h
|
@ -1,7 +1,10 @@
|
||||||
#ifndef _gcn64txrx_h__
|
#ifndef _gcn64txrx_h__
|
||||||
#define _gcn64txrx_h__
|
#define _gcn64txrx_h__
|
||||||
|
|
||||||
void gcn64_sendBytes(const unsigned char *data, unsigned char n_bytes);
|
void gcn64_sendBytes0(const unsigned char *data, unsigned char n_bytes);
|
||||||
|
void gcn64_sendBytes1(const unsigned char *data, unsigned char n_bytes);
|
||||||
|
void gcn64_sendBytes2(const unsigned char *data, unsigned char n_bytes);
|
||||||
|
void gcn64_sendBytes3(const unsigned char *data, unsigned char n_bytes);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Receive up to \max_bytes bytes
|
* \brief Receive up to \max_bytes bytes
|
||||||
|
@ -9,6 +12,9 @@ void gcn64_sendBytes(const unsigned char *data, unsigned char n_bytes);
|
||||||
* \param max_bytes The maximum number of bytes
|
* \param max_bytes The maximum number of bytes
|
||||||
* \return The number of received bytes. 0xFF in case of error, 0xFE in case of overflow (max_bytes too small)
|
* \return The number of received bytes. 0xFF in case of error, 0xFE in case of overflow (max_bytes too small)
|
||||||
*/
|
*/
|
||||||
unsigned char gcn64_receiveBytes(unsigned char *dstbuf, unsigned char max_bytes);
|
unsigned char gcn64_receiveBytes0(unsigned char *dstbuf, unsigned char max_bytes);
|
||||||
|
unsigned char gcn64_receiveBytes1(unsigned char *dstbuf, unsigned char max_bytes);
|
||||||
|
unsigned char gcn64_receiveBytes2(unsigned char *dstbuf, unsigned char max_bytes);
|
||||||
|
unsigned char gcn64_receiveBytes3(unsigned char *dstbuf, unsigned char max_bytes);
|
||||||
|
|
||||||
#endif // _gcn64txrx_h__
|
#endif // _gcn64txrx_h__
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
#ifndef _hid_keycodes_h__
|
||||||
|
#define _hid_keycodes_h__
|
||||||
|
|
||||||
|
/* From Hut1_12.pdf : Universal Serial Bus HID Usage Tables,
|
||||||
|
* section 10 Keyboard/Keypad Page (0x07) */
|
||||||
|
|
||||||
|
#define HID_KB_NOEVENT 0x00
|
||||||
|
|
||||||
|
#define HID_KB_A 0x04
|
||||||
|
#define HID_KB_B 0x05
|
||||||
|
#define HID_KB_C 0x06
|
||||||
|
#define HID_KB_D 0x07
|
||||||
|
#define HID_KB_E 0x08
|
||||||
|
#define HID_KB_F 0x09
|
||||||
|
#define HID_KB_G 0x0a
|
||||||
|
#define HID_KB_H 0x0b
|
||||||
|
#define HID_KB_I 0x0c
|
||||||
|
#define HID_KB_J 0x0d
|
||||||
|
#define HID_KB_K 0x0e
|
||||||
|
#define HID_KB_L 0x0f
|
||||||
|
#define HID_KB_M 0x10
|
||||||
|
#define HID_KB_N 0x11
|
||||||
|
#define HID_KB_O 0x12
|
||||||
|
#define HID_KB_P 0x13
|
||||||
|
#define HID_KB_Q 0x14
|
||||||
|
#define HID_KB_R 0x15
|
||||||
|
#define HID_KB_S 0x16
|
||||||
|
#define HID_KB_T 0x17
|
||||||
|
#define HID_KB_U 0x18
|
||||||
|
#define HID_KB_V 0x19
|
||||||
|
#define HID_KB_W 0x1a
|
||||||
|
#define HID_KB_X 0x1b
|
||||||
|
#define HID_KB_Y 0x1c
|
||||||
|
#define HID_KB_Z 0x1d
|
||||||
|
|
||||||
|
#define HID_KB_1 0x1e
|
||||||
|
#define HID_KB_2 0x1f
|
||||||
|
#define HID_KB_3 0x20
|
||||||
|
#define HID_KB_4 0x21
|
||||||
|
#define HID_KB_5 0x22
|
||||||
|
#define HID_KB_6 0x23
|
||||||
|
#define HID_KB_7 0x24
|
||||||
|
#define HID_KB_8 0x25
|
||||||
|
#define HID_KB_9 0x26
|
||||||
|
#define HID_KB_0 0x27
|
||||||
|
|
||||||
|
#define HID_KB_ESCAPE 0x29
|
||||||
|
#define HID_KB_BACKSPACE 0x2a
|
||||||
|
#define HID_KB_TAB 0x2b
|
||||||
|
|
||||||
|
#define HID_KB_SPACE 0x2c
|
||||||
|
#define HID_KB_DASH_UNDERSCORE 0x2d
|
||||||
|
#define HID_KB_EQUAL_PLUS 0x2e
|
||||||
|
#define HID_KB_OPEN_BRKT_BRACE 0x2f
|
||||||
|
|
||||||
|
#define HID_KB_CLOSE_BRKT_BRACE 0x30
|
||||||
|
#define HID_KB_NONUS_HASH_TILDE 0x32
|
||||||
|
#define HID_KB_SEMI_COLON_COLON 0x33
|
||||||
|
#define HID_KB_QUOTES 0x34
|
||||||
|
#define HID_KB_GRAVE_ACCENT_AND_TILDE 0x35
|
||||||
|
#define HID_KB_COMMA_SMALLER_THAN 0x36
|
||||||
|
#define HID_KB_PERIOD_GREATER_THAN 0x37
|
||||||
|
#define HID_KB_SLASH_QUESTION 0x38
|
||||||
|
#define HID_KB_CAPS_LOCK 0x39
|
||||||
|
|
||||||
|
#define HID_KB_F1 0x3a
|
||||||
|
#define HID_KB_F2 0x3b
|
||||||
|
#define HID_KB_F3 0x3c
|
||||||
|
#define HID_KB_F4 0x3d
|
||||||
|
#define HID_KB_F5 0x3e
|
||||||
|
#define HID_KB_F6 0x3f
|
||||||
|
#define HID_KB_F7 0x40
|
||||||
|
#define HID_KB_F8 0x41
|
||||||
|
#define HID_KB_F9 0x42
|
||||||
|
#define HID_KB_F10 0x43
|
||||||
|
#define HID_KB_F11 0x44
|
||||||
|
#define HID_KB_F12 0x45
|
||||||
|
|
||||||
|
#define HID_KB_SCROLL_LOCK 0x47
|
||||||
|
#define HID_KB_INSERT 0x49
|
||||||
|
#define HID_KB_HOME 0x4a
|
||||||
|
#define HID_KB_PGUP 0x4b
|
||||||
|
#define HID_KB_DELETE_FORWARD 0x4c
|
||||||
|
#define HID_KB_END 0x4d
|
||||||
|
#define HID_KB_PGDN 0x4e
|
||||||
|
|
||||||
|
#define HID_KB_RIGHT_ARROW 0x4f
|
||||||
|
#define HID_KB_LEFT_ARROW 0x50
|
||||||
|
#define HID_KB_DOWN_ARROW 0x51
|
||||||
|
#define HID_KB_UP_ARROW 0x52
|
||||||
|
|
||||||
|
#define HID_KB_ENTER 0x28
|
||||||
|
|
||||||
|
#define HID_KB_INTERNATIONAL1 0x87
|
||||||
|
#define HID_KB_INTERNATIONAL2 0x88
|
||||||
|
#define HID_KB_INTERNATIONAL3 0x89
|
||||||
|
#define HID_KB_INTERNATIONAL4 0x8a
|
||||||
|
#define HID_KB_INTERNATIONAL5 0x8b
|
||||||
|
|
||||||
|
#define HID_KB_LEFT_CONTROL 0xe0
|
||||||
|
#define HID_KB_LEFT_SHIFT 0xe1
|
||||||
|
#define HID_KB_LEFT_ALT 0xe2
|
||||||
|
#define HID_KB_LEFT_GUI 0xe3
|
||||||
|
#define HID_KB_RIGHT_CONTROL 0xe4
|
||||||
|
#define HID_KB_RIGHT_SHIFT 0xe5
|
||||||
|
#define HID_KB_RIGHT_ALT 0xe6
|
||||||
|
#define HID_KB_RIGHT_GUI 0xe7
|
||||||
|
|
||||||
|
#endif // _hid_keycodes_h__
|
||||||
|
|
118
hiddata.c
118
hiddata.c
|
@ -1,5 +1,5 @@
|
||||||
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
||||||
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
|
Copyright (C) 2007-2016 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -25,8 +25,8 @@
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
|
||||||
// dataHidReport is 40 bytes.
|
// dataHidReport is 63 bytes. Endpoint is 64 bytes.
|
||||||
#define CMDBUF_SIZE 41
|
#define CMDBUF_SIZE 64
|
||||||
|
|
||||||
#define STATE_IDLE 0
|
#define STATE_IDLE 0
|
||||||
#define STATE_NEW_COMMAND 1 // New command in buffer
|
#define STATE_NEW_COMMAND 1 // New command in buffer
|
||||||
|
@ -41,7 +41,7 @@ static unsigned char cmdbuf[CMDBUF_SIZE];
|
||||||
static volatile unsigned char cmdbuf_len = 0;
|
static volatile unsigned char cmdbuf_len = 0;
|
||||||
|
|
||||||
/*** Get/Set report called from interrupt context! */
|
/*** Get/Set report called from interrupt context! */
|
||||||
uint16_t hiddata_get_report(struct usb_request *rq, const uint8_t **dat)
|
uint16_t hiddata_get_report(void *ctx, struct usb_request *rq, const uint8_t **dat)
|
||||||
{
|
{
|
||||||
// printf("Get data\n");
|
// printf("Get data\n");
|
||||||
if (state == STATE_COMMAND_DONE) {
|
if (state == STATE_COMMAND_DONE) {
|
||||||
|
@ -56,7 +56,7 @@ uint16_t hiddata_get_report(struct usb_request *rq, const uint8_t **dat)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** Get/Set report called from interrupt context! */
|
/*** Get/Set report called from interrupt context! */
|
||||||
uint8_t hiddata_set_report(const struct usb_request *rq, const uint8_t *dat, uint16_t len)
|
uint8_t hiddata_set_report(void *ctx, const struct usb_request *rq, const uint8_t *dat, uint16_t len)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
int i;
|
int i;
|
||||||
|
@ -74,9 +74,54 @@ uint8_t hiddata_set_report(const struct usb_request *rq, const uint8_t *dat, uin
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hiddata_processCommandBuffer(void)
|
static uint8_t processBlockIO(void)
|
||||||
{
|
{
|
||||||
//unsigned char channel;
|
uint8_t requestCopy[CMDBUF_SIZE];
|
||||||
|
int i, rx_offset = 0, rx;
|
||||||
|
uint8_t chn, n_tx, n_rx;
|
||||||
|
|
||||||
|
memcpy(requestCopy, cmdbuf, CMDBUF_SIZE);
|
||||||
|
memset(cmdbuf + 1, 0xff, CMDBUF_SIZE-1);
|
||||||
|
|
||||||
|
for (rx_offset = 1, i=1; i<CMDBUF_SIZE; ) {
|
||||||
|
if (i + 3 >= CMDBUF_SIZE)
|
||||||
|
break;
|
||||||
|
|
||||||
|
chn = requestCopy[i];
|
||||||
|
if (chn == 0xff)
|
||||||
|
break;
|
||||||
|
i++;
|
||||||
|
n_tx = requestCopy[i];
|
||||||
|
i++;
|
||||||
|
n_rx = requestCopy[i];
|
||||||
|
i++;
|
||||||
|
if (n_tx == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (rx_offset + 1 + n_rx >= CMDBUF_SIZE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
rx = gcn64_transaction(chn, requestCopy + i, n_tx, cmdbuf + rx_offset + 1, n_rx);
|
||||||
|
cmdbuf[rx_offset] = n_rx;
|
||||||
|
if (rx <= 0) {
|
||||||
|
// timeout
|
||||||
|
cmdbuf[rx_offset] |= 0x80;
|
||||||
|
} else if (rx < n_rx) {
|
||||||
|
// less than expected
|
||||||
|
cmdbuf[rx_offset] |= 0x40;
|
||||||
|
}
|
||||||
|
rx_offset += n_rx + 1;
|
||||||
|
|
||||||
|
i += n_tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 63;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hiddata_processCommandBuffer(struct hiddata_ops *ops)
|
||||||
|
{
|
||||||
|
unsigned char channel;
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
int i;
|
int i;
|
||||||
#endif
|
#endif
|
||||||
|
@ -89,14 +134,23 @@ static void hiddata_processCommandBuffer(void)
|
||||||
// printf("Process cmd 0x%02x\r\n", cmdbuf[0]);
|
// printf("Process cmd 0x%02x\r\n", cmdbuf[0]);
|
||||||
switch(cmdbuf[0])
|
switch(cmdbuf[0])
|
||||||
{
|
{
|
||||||
|
case RQ_GCN64_ECHO:
|
||||||
|
// Cmd : RQ, data[]
|
||||||
|
// Answer: RQ, data[]
|
||||||
|
break;
|
||||||
case RQ_GCN64_JUMP_TO_BOOTLOADER:
|
case RQ_GCN64_JUMP_TO_BOOTLOADER:
|
||||||
enterBootLoader();
|
enterBootLoader();
|
||||||
break;
|
break;
|
||||||
|
case RQ_RNT_RESET_FIRMWARE:
|
||||||
|
resetFirmware();
|
||||||
|
break;
|
||||||
case RQ_GCN64_RAW_SI_COMMAND:
|
case RQ_GCN64_RAW_SI_COMMAND:
|
||||||
// TODO : Range checking
|
// TODO : Range checking
|
||||||
// cmdbuf[] : RQ, CHN, LEN, data[]
|
// cmdbuf[] : RQ, CHN, LEN, data[]
|
||||||
//channel = cmdbuf[1];
|
channel = cmdbuf[1];
|
||||||
cmdbuf_len = gcn64_transaction(cmdbuf+3, cmdbuf[2], cmdbuf + 3, CMDBUF_SIZE-3);
|
if (channel >= NUM_CHANNELS)
|
||||||
|
break;
|
||||||
|
cmdbuf_len = gcn64_transaction(channel, cmdbuf+3, cmdbuf[2], cmdbuf + 3, CMDBUF_SIZE-3);
|
||||||
cmdbuf[2] = cmdbuf_len;
|
cmdbuf[2] = cmdbuf_len;
|
||||||
cmdbuf_len += 3; // Answer: RQ, CHN, LEN, data[]
|
cmdbuf_len += 3; // Answer: RQ, CHN, LEN, data[]
|
||||||
break;
|
break;
|
||||||
|
@ -114,7 +168,9 @@ static void hiddata_processCommandBuffer(void)
|
||||||
break;
|
break;
|
||||||
case RQ_GCN64_SUSPEND_POLLING:
|
case RQ_GCN64_SUSPEND_POLLING:
|
||||||
// CMD: RQ, PARAM
|
// CMD: RQ, PARAM
|
||||||
g_polling_suspended = cmdbuf[1];
|
if (ops && ops->suspendPolling) {
|
||||||
|
ops->suspendPolling(cmdbuf[1]);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RQ_GCN64_GET_VERSION:
|
case RQ_GCN64_GET_VERSION:
|
||||||
// CMD: RQ
|
// CMD: RQ
|
||||||
|
@ -129,15 +185,49 @@ static void hiddata_processCommandBuffer(void)
|
||||||
case RQ_GCN64_GET_CONTROLLER_TYPE:
|
case RQ_GCN64_GET_CONTROLLER_TYPE:
|
||||||
// CMD : RQ, CHN
|
// CMD : RQ, CHN
|
||||||
// Answer: RQ, CHN, TYPE
|
// Answer: RQ, CHN, TYPE
|
||||||
cmdbuf[2] = current_pad_type;
|
channel = cmdbuf[1];
|
||||||
|
if (channel >= NUM_CHANNELS)
|
||||||
|
break;
|
||||||
|
cmdbuf[2] = current_pad_type[channel];
|
||||||
cmdbuf_len = 3;
|
cmdbuf_len = 3;
|
||||||
break;
|
break;
|
||||||
case RQ_GCN64_SET_VIBRATION:
|
case RQ_GCN64_SET_VIBRATION:
|
||||||
// CMD : RQ, CHN, Vibrate
|
// CMD : RQ, CHN, Vibrate
|
||||||
// Answer: RQ, CHN, Vibrate
|
// Answer: RQ, CHN, Vibrate
|
||||||
usbpad_forceVibrate(cmdbuf[2]);
|
if (ops && ops->forceVibration) {
|
||||||
|
ops->forceVibration(cmdbuf[1], cmdbuf[2]);
|
||||||
|
}
|
||||||
cmdbuf_len = 3;
|
cmdbuf_len = 3;
|
||||||
break;
|
break;
|
||||||
|
case RQ_GCN64_BLOCK_IO:
|
||||||
|
cmdbuf_len = processBlockIO();
|
||||||
|
break;
|
||||||
|
case RQ_RNT_GET_SUPPORTED_REQUESTS:
|
||||||
|
cmdbuf[1] = RQ_GCN64_JUMP_TO_BOOTLOADER;
|
||||||
|
cmdbuf[2] = RQ_GCN64_RAW_SI_COMMAND;
|
||||||
|
cmdbuf[3] = RQ_GCN64_GET_CONFIG_PARAM;
|
||||||
|
cmdbuf[4] = RQ_GCN64_SET_CONFIG_PARAM;
|
||||||
|
cmdbuf[5] = RQ_GCN64_SUSPEND_POLLING;
|
||||||
|
cmdbuf[6] = RQ_GCN64_GET_VERSION;
|
||||||
|
cmdbuf[7] = RQ_GCN64_GET_SIGNATURE;
|
||||||
|
cmdbuf[8] = RQ_GCN64_GET_CONTROLLER_TYPE;
|
||||||
|
cmdbuf[9] = RQ_GCN64_SET_VIBRATION;
|
||||||
|
cmdbuf[10] = RQ_GCN64_BLOCK_IO;
|
||||||
|
cmdbuf[11] = RQ_RNT_GET_SUPPORTED_CFG_PARAMS;
|
||||||
|
cmdbuf[12] = RQ_RNT_GET_SUPPORTED_MODES;
|
||||||
|
cmdbuf[13] = RQ_RNT_GET_SUPPORTED_REQUESTS;
|
||||||
|
cmdbuf_len = 14;
|
||||||
|
break;
|
||||||
|
case RQ_RNT_GET_SUPPORTED_CFG_PARAMS:
|
||||||
|
cmdbuf_len = 1 + config_getSupportedParams(cmdbuf + 1);
|
||||||
|
break;
|
||||||
|
case RQ_RNT_GET_SUPPORTED_MODES:
|
||||||
|
cmdbuf_len = 1;
|
||||||
|
if (ops && ops->getSupportedModes) {
|
||||||
|
cmdbuf_len += ops->getSupportedModes(cmdbuf + 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
@ -151,7 +241,7 @@ static void hiddata_processCommandBuffer(void)
|
||||||
state = STATE_COMMAND_DONE;
|
state = STATE_COMMAND_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hiddata_doTask(void)
|
void hiddata_doTask(struct hiddata_ops *ops)
|
||||||
{
|
{
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
|
@ -161,7 +251,7 @@ void hiddata_doTask(void)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_NEW_COMMAND:
|
case STATE_NEW_COMMAND:
|
||||||
hiddata_processCommandBuffer();
|
hiddata_processCommandBuffer(ops);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_COMMAND_DONE:
|
case STATE_COMMAND_DONE:
|
||||||
|
|
12
hiddata.h
12
hiddata.h
|
@ -4,9 +4,15 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
|
|
||||||
uint16_t hiddata_get_report(struct usb_request *rq, const uint8_t **dat);
|
struct hiddata_ops {
|
||||||
uint8_t hiddata_set_report(const struct usb_request *rq, const uint8_t *dat, uint16_t len);
|
void (*suspendPolling)(uint8_t suspend);
|
||||||
|
void (*forceVibration)(uint8_t channel, uint8_t force);
|
||||||
|
uint8_t (*getSupportedModes)(uint8_t *dst);
|
||||||
|
};
|
||||||
|
|
||||||
void hiddata_doTask(void);
|
uint16_t hiddata_get_report(void *ctx, struct usb_request *rq, const uint8_t **dat);
|
||||||
|
uint8_t hiddata_set_report(void *ctx, const struct usb_request *rq, const uint8_t *dat, uint16_t len);
|
||||||
|
|
||||||
|
void hiddata_doTask(struct hiddata_ops *ops);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
||||||
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
|
Copyright (C) 2007-2016 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
||||||
|
Copyright (C) 2007-2016 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include "intervaltimer2.h"
|
||||||
|
|
||||||
|
void intervaltimer2_init(void)
|
||||||
|
{
|
||||||
|
TCCR0A = (1<<WGM01);
|
||||||
|
TCCR0B = (1<<CS02) | (1<<CS00); // CTC, /1024 prescaler
|
||||||
|
}
|
||||||
|
|
||||||
|
void intervaltimer2_set16ms(void)
|
||||||
|
{
|
||||||
|
int interval_ms = 16;
|
||||||
|
|
||||||
|
// Maximum 16ms. 17ms overflows the 8-bit OCR0A register (value of 265)
|
||||||
|
// 16ms: 250
|
||||||
|
|
||||||
|
TCNT0 = 0;
|
||||||
|
OCR0A = interval_ms * (F_CPU/1024) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
char intervaltimer2_get(void)
|
||||||
|
{
|
||||||
|
if (TIFR0 & (1<<OCF0A)) {
|
||||||
|
TIFR0 = 1<<OCF0A;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef _interval_timer2_h__
|
||||||
|
#define _interval_timer2_h__
|
||||||
|
|
||||||
|
void intervaltimer2_init(void);
|
||||||
|
void intervaltimer2_set16ms(void);
|
||||||
|
char intervaltimer2_get(void);
|
||||||
|
|
||||||
|
#endif // _interval_timer_h__
|
||||||
|
|
814
main.c
814
main.c
|
@ -1,5 +1,5 @@
|
||||||
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
||||||
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
|
Copyright (C) 2007-2016 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -36,6 +36,25 @@
|
||||||
#include "hiddata.h"
|
#include "hiddata.h"
|
||||||
#include "usbstrings.h"
|
#include "usbstrings.h"
|
||||||
#include "intervaltimer.h"
|
#include "intervaltimer.h"
|
||||||
|
#include "intervaltimer2.h"
|
||||||
|
#include "requests.h"
|
||||||
|
#include "stkchk.h"
|
||||||
|
|
||||||
|
#define MAX_PLAYERS 2
|
||||||
|
|
||||||
|
#define GCN64_USB_PID 0x0060
|
||||||
|
#define N64_USB_PID 0x0061
|
||||||
|
#define GC_USB_PID 0x0062
|
||||||
|
|
||||||
|
#define DUAL_GCN64_USB_PID 0x0063
|
||||||
|
#define DUAL_N64_USB_PID 0x0064
|
||||||
|
#define DUAL_GC_USB_PID 0x0065
|
||||||
|
|
||||||
|
#define KEYBOARD_PID 0x0066
|
||||||
|
#define KEYBOARD_PID2 0x0067
|
||||||
|
#define KEYBOARD_JS_PID 0x0068
|
||||||
|
|
||||||
|
int keyboard_main(void);
|
||||||
|
|
||||||
/* Those .c files are included rather than linked for we
|
/* Those .c files are included rather than linked for we
|
||||||
* want the sizeof() operator to work on the arrays */
|
* want the sizeof() operator to work on the arrays */
|
||||||
|
@ -43,6 +62,7 @@
|
||||||
#include "dataHidReport.c"
|
#include "dataHidReport.c"
|
||||||
|
|
||||||
#define MAX_READ_ERRORS 30
|
#define MAX_READ_ERRORS 30
|
||||||
|
static uint8_t error_count[MAX_PLAYERS] = { };
|
||||||
|
|
||||||
struct cfg0 {
|
struct cfg0 {
|
||||||
struct usb_configuration_descriptor configdesc;
|
struct usb_configuration_descriptor configdesc;
|
||||||
|
@ -60,13 +80,13 @@ static const struct cfg0 cfg0 PROGMEM = {
|
||||||
.bLength = sizeof(struct usb_configuration_descriptor),
|
.bLength = sizeof(struct usb_configuration_descriptor),
|
||||||
.bDescriptorType = CONFIGURATION_DESCRIPTOR,
|
.bDescriptorType = CONFIGURATION_DESCRIPTOR,
|
||||||
.wTotalLength = sizeof(cfg0), // includes all descriptors returned together
|
.wTotalLength = sizeof(cfg0), // includes all descriptors returned together
|
||||||
.bNumInterfaces = 2,
|
.bNumInterfaces = 1 + 1, // one interface per player + one management interface
|
||||||
.bConfigurationValue = 1,
|
.bConfigurationValue = 1,
|
||||||
.bmAttributes = CFG_DESC_ATTR_RESERVED, // set Self-powred and remote-wakeup here if needed.
|
.bmAttributes = CFG_DESC_ATTR_RESERVED, // set Self-powred and remote-wakeup here if needed.
|
||||||
.bMaxPower = 25, // for 50mA
|
.bMaxPower = 25, // for 50mA
|
||||||
},
|
},
|
||||||
|
|
||||||
// Main interface, HID
|
// Main interface, HID (player 1)
|
||||||
.interface = {
|
.interface = {
|
||||||
.bLength = sizeof(struct usb_interface_descriptor),
|
.bLength = sizeof(struct usb_interface_descriptor),
|
||||||
.bDescriptorType = INTERFACE_DESCRIPTOR,
|
.bDescriptorType = INTERFACE_DESCRIPTOR,
|
||||||
|
@ -91,7 +111,7 @@ static const struct cfg0 cfg0 PROGMEM = {
|
||||||
.bDescriptorType = ENDPOINT_DESCRIPTOR,
|
.bDescriptorType = ENDPOINT_DESCRIPTOR,
|
||||||
.bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 1, // 0x81
|
.bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 1, // 0x81
|
||||||
.bmAttributes = TRANSFER_TYPE_INT,
|
.bmAttributes = TRANSFER_TYPE_INT,
|
||||||
.wMaxPacketsize = 64,
|
.wMaxPacketsize = 16,
|
||||||
.bInterval = LS_FS_INTERVAL_MS(1),
|
.bInterval = LS_FS_INTERVAL_MS(1),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -123,20 +143,305 @@ static const struct cfg0 cfg0 PROGMEM = {
|
||||||
.wMaxPacketsize = 64,
|
.wMaxPacketsize = 64,
|
||||||
.bInterval = LS_FS_INTERVAL_MS(1),
|
.bInterval = LS_FS_INTERVAL_MS(1),
|
||||||
},
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct usb_device_descriptor device_descriptor PROGMEM = {
|
static const struct cfg0 cfg0_kb PROGMEM = {
|
||||||
|
.configdesc = {
|
||||||
|
.bLength = sizeof(struct usb_configuration_descriptor),
|
||||||
|
.bDescriptorType = CONFIGURATION_DESCRIPTOR,
|
||||||
|
.wTotalLength = sizeof(cfg0), // includes all descriptors returned together
|
||||||
|
.bNumInterfaces = 1 + 1, // one interface per player + one management interface
|
||||||
|
.bConfigurationValue = 1,
|
||||||
|
.bmAttributes = CFG_DESC_ATTR_RESERVED, // set Self-powred and remote-wakeup here if needed.
|
||||||
|
.bMaxPower = 25, // for 50mA
|
||||||
|
},
|
||||||
|
|
||||||
|
// Main interface, HID Keyboard
|
||||||
|
.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(gcKeyboardReport),
|
||||||
|
},
|
||||||
|
.ep1_in = {
|
||||||
|
.bLength = sizeof(struct usb_endpoint_descriptor),
|
||||||
|
.bDescriptorType = ENDPOINT_DESCRIPTOR,
|
||||||
|
.bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 1, // 0x81
|
||||||
|
.bmAttributes = TRANSFER_TYPE_INT,
|
||||||
|
.wMaxPacketsize = 16,
|
||||||
|
.bInterval = LS_FS_INTERVAL_MS(1),
|
||||||
|
},
|
||||||
|
|
||||||
|
// Second HID interface for config and update
|
||||||
|
.interface_admin = {
|
||||||
|
.bLength = sizeof(struct usb_interface_descriptor),
|
||||||
|
.bDescriptorType = INTERFACE_DESCRIPTOR,
|
||||||
|
.bInterfaceNumber = 1,
|
||||||
|
.bAlternateSetting = 0,
|
||||||
|
.bNumEndpoints = 1,
|
||||||
|
.bInterfaceClass = USB_DEVICE_CLASS_HID,
|
||||||
|
.bInterfaceSubClass = HID_SUBCLASS_NONE,
|
||||||
|
.bInterfaceProtocol = HID_PROTOCOL_NONE,
|
||||||
|
},
|
||||||
|
.hid_data = {
|
||||||
|
.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(dataHidReport),
|
||||||
|
},
|
||||||
|
.ep2_in = {
|
||||||
|
.bLength = sizeof(struct usb_endpoint_descriptor),
|
||||||
|
.bDescriptorType = ENDPOINT_DESCRIPTOR,
|
||||||
|
.bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 2, // 0x82
|
||||||
|
.bmAttributes = TRANSFER_TYPE_INT,
|
||||||
|
.wMaxPacketsize = 64,
|
||||||
|
.bInterval = LS_FS_INTERVAL_MS(1),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct cfg0_2p {
|
||||||
|
struct usb_configuration_descriptor configdesc;
|
||||||
|
struct usb_interface_descriptor interface;
|
||||||
|
struct usb_hid_descriptor hid;
|
||||||
|
struct usb_endpoint_descriptor ep1_in;
|
||||||
|
|
||||||
|
struct usb_interface_descriptor interface_p2;
|
||||||
|
struct usb_hid_descriptor hid_p2;
|
||||||
|
struct usb_endpoint_descriptor ep2_in;
|
||||||
|
|
||||||
|
struct usb_interface_descriptor interface_admin;
|
||||||
|
struct usb_hid_descriptor hid_data;
|
||||||
|
struct usb_endpoint_descriptor ep3_in;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct cfg0_2p cfg0_2p PROGMEM = {
|
||||||
|
.configdesc = {
|
||||||
|
.bLength = sizeof(struct usb_configuration_descriptor),
|
||||||
|
.bDescriptorType = CONFIGURATION_DESCRIPTOR,
|
||||||
|
.wTotalLength = sizeof(cfg0_2p), // includes all descriptors returned together
|
||||||
|
.bNumInterfaces = 2 + 1, // one interface per player + one management interface
|
||||||
|
.bConfigurationValue = 1,
|
||||||
|
.bmAttributes = CFG_DESC_ATTR_RESERVED, // set Self-powred and remote-wakeup here if needed.
|
||||||
|
.bMaxPower = 25, // for 50mA
|
||||||
|
},
|
||||||
|
|
||||||
|
// Main interface, HID (player 1)
|
||||||
|
.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 = 16,
|
||||||
|
.bInterval = LS_FS_INTERVAL_MS(1),
|
||||||
|
},
|
||||||
|
|
||||||
|
// Main interface, HID (player 2)
|
||||||
|
.interface_p2 = {
|
||||||
|
.bLength = sizeof(struct usb_interface_descriptor),
|
||||||
|
.bDescriptorType = INTERFACE_DESCRIPTOR,
|
||||||
|
.bInterfaceNumber = 1,
|
||||||
|
.bAlternateSetting = 0,
|
||||||
|
.bNumEndpoints = 1,
|
||||||
|
.bInterfaceClass = USB_DEVICE_CLASS_HID,
|
||||||
|
.bInterfaceSubClass = HID_SUBCLASS_NONE,
|
||||||
|
.bInterfaceProtocol = HID_PROTOCOL_NONE,
|
||||||
|
},
|
||||||
|
.hid_p2 = {
|
||||||
|
.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),
|
||||||
|
},
|
||||||
|
.ep2_in = {
|
||||||
|
.bLength = sizeof(struct usb_endpoint_descriptor),
|
||||||
|
.bDescriptorType = ENDPOINT_DESCRIPTOR,
|
||||||
|
.bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 2, // 0x82
|
||||||
|
.bmAttributes = TRANSFER_TYPE_INT,
|
||||||
|
.wMaxPacketsize = 16,
|
||||||
|
.bInterval = LS_FS_INTERVAL_MS(1),
|
||||||
|
},
|
||||||
|
|
||||||
|
// Second HID interface for config and update
|
||||||
|
.interface_admin = {
|
||||||
|
.bLength = sizeof(struct usb_interface_descriptor),
|
||||||
|
.bDescriptorType = INTERFACE_DESCRIPTOR,
|
||||||
|
.bInterfaceNumber = 2,
|
||||||
|
.bAlternateSetting = 0,
|
||||||
|
.bNumEndpoints = 1,
|
||||||
|
.bInterfaceClass = USB_DEVICE_CLASS_HID,
|
||||||
|
.bInterfaceSubClass = HID_SUBCLASS_NONE,
|
||||||
|
.bInterfaceProtocol = HID_PROTOCOL_NONE,
|
||||||
|
},
|
||||||
|
.hid_data = {
|
||||||
|
.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(dataHidReport),
|
||||||
|
},
|
||||||
|
.ep3_in = {
|
||||||
|
.bLength = sizeof(struct usb_endpoint_descriptor),
|
||||||
|
.bDescriptorType = ENDPOINT_DESCRIPTOR,
|
||||||
|
.bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 3, // 0x83
|
||||||
|
.bmAttributes = TRANSFER_TYPE_INT,
|
||||||
|
.wMaxPacketsize = 64,
|
||||||
|
.bInterval = LS_FS_INTERVAL_MS(1),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct cfg0_2p cfg0_2p_keyboard PROGMEM = {
|
||||||
|
.configdesc = {
|
||||||
|
.bLength = sizeof(struct usb_configuration_descriptor),
|
||||||
|
.bDescriptorType = CONFIGURATION_DESCRIPTOR,
|
||||||
|
.wTotalLength = sizeof(cfg0_2p), // includes all descriptors returned together
|
||||||
|
.bNumInterfaces = 2 + 1, // one interface per player + one management interface
|
||||||
|
.bConfigurationValue = 1,
|
||||||
|
.bmAttributes = CFG_DESC_ATTR_RESERVED, // set Self-powred and remote-wakeup here if needed.
|
||||||
|
.bMaxPower = 25, // for 50mA
|
||||||
|
},
|
||||||
|
|
||||||
|
// Joystick interface
|
||||||
|
.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 = 16,
|
||||||
|
.bInterval = LS_FS_INTERVAL_MS(1),
|
||||||
|
},
|
||||||
|
|
||||||
|
// HID Keyboard interface
|
||||||
|
.interface_p2 = {
|
||||||
|
.bLength = sizeof(struct usb_interface_descriptor),
|
||||||
|
.bDescriptorType = INTERFACE_DESCRIPTOR,
|
||||||
|
.bInterfaceNumber = 1,
|
||||||
|
.bAlternateSetting = 0,
|
||||||
|
.bNumEndpoints = 1,
|
||||||
|
.bInterfaceClass = USB_DEVICE_CLASS_HID,
|
||||||
|
.bInterfaceSubClass = HID_SUBCLASS_NONE,
|
||||||
|
.bInterfaceProtocol = HID_PROTOCOL_NONE,
|
||||||
|
},
|
||||||
|
.hid_p2 = {
|
||||||
|
.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(gcKeyboardReport),
|
||||||
|
},
|
||||||
|
.ep2_in = {
|
||||||
|
.bLength = sizeof(struct usb_endpoint_descriptor),
|
||||||
|
.bDescriptorType = ENDPOINT_DESCRIPTOR,
|
||||||
|
.bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 2, // 0x82
|
||||||
|
.bmAttributes = TRANSFER_TYPE_INT,
|
||||||
|
.wMaxPacketsize = 16,
|
||||||
|
.bInterval = LS_FS_INTERVAL_MS(1),
|
||||||
|
},
|
||||||
|
|
||||||
|
// Second HID interface for config and update
|
||||||
|
.interface_admin = {
|
||||||
|
.bLength = sizeof(struct usb_interface_descriptor),
|
||||||
|
.bDescriptorType = INTERFACE_DESCRIPTOR,
|
||||||
|
.bInterfaceNumber = 2,
|
||||||
|
.bAlternateSetting = 0,
|
||||||
|
.bNumEndpoints = 1,
|
||||||
|
.bInterfaceClass = USB_DEVICE_CLASS_HID,
|
||||||
|
.bInterfaceSubClass = HID_SUBCLASS_NONE,
|
||||||
|
.bInterfaceProtocol = HID_PROTOCOL_NONE,
|
||||||
|
},
|
||||||
|
.hid_data = {
|
||||||
|
.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(dataHidReport),
|
||||||
|
},
|
||||||
|
.ep3_in = {
|
||||||
|
.bLength = sizeof(struct usb_endpoint_descriptor),
|
||||||
|
.bDescriptorType = ENDPOINT_DESCRIPTOR,
|
||||||
|
.bEndpointAddress = USB_RQT_DEVICE_TO_HOST | 3, // 0x83
|
||||||
|
.bmAttributes = TRANSFER_TYPE_INT,
|
||||||
|
.wMaxPacketsize = 64,
|
||||||
|
.bInterval = LS_FS_INTERVAL_MS(1),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct usb_device_descriptor device_descriptor = {
|
||||||
.bLength = sizeof(struct usb_device_descriptor),
|
.bLength = sizeof(struct usb_device_descriptor),
|
||||||
.bDescriptorType = DEVICE_DESCRIPTOR,
|
.bDescriptorType = DEVICE_DESCRIPTOR,
|
||||||
.bcdUSB = 0x0101,
|
.bcdUSB = 0x0110,
|
||||||
.bDeviceClass = 0, // set at interface
|
.bDeviceClass = 0, // set at interface
|
||||||
.bDeviceSubClass = 0, // set at interface
|
.bDeviceSubClass = 0, // set at interface
|
||||||
.bDeviceProtocol = 0,
|
.bDeviceProtocol = 0,
|
||||||
.bMaxPacketSize = 64,
|
.bMaxPacketSize = 64,
|
||||||
.idVendor = 0x289B,
|
.idVendor = 0x289B,
|
||||||
.idProduct = 0x0017,
|
.idProduct = GCN64_USB_PID,
|
||||||
.bcdDevice = 0x0300, // 1.0.0
|
.bcdDevice = VERSIONBCD,
|
||||||
.bNumConfigurations = 1,
|
.bNumConfigurations = 1,
|
||||||
.iManufacturer = 1,
|
.iManufacturer = 1,
|
||||||
.iProduct = 2,
|
.iProduct = 2,
|
||||||
|
@ -145,29 +450,40 @@ const struct usb_device_descriptor device_descriptor PROGMEM = {
|
||||||
|
|
||||||
/** **/
|
/** **/
|
||||||
|
|
||||||
|
static uint16_t _usbpad_hid_get_report(void *ctx, struct usb_request *rq, const uint8_t **dat)
|
||||||
|
{
|
||||||
|
return usbpad_hid_get_report((struct usbpad*)ctx, rq, dat);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t _usbpad_hid_set_report(void *ctx, const struct usb_request *rq, const uint8_t *dat, uint16_t len)
|
||||||
|
{
|
||||||
|
return usbpad_hid_set_report((struct usbpad*)ctx, rq, dat, len);
|
||||||
|
}
|
||||||
|
|
||||||
static struct usb_parameters usb_params = {
|
static struct usb_parameters usb_params = {
|
||||||
.flags = USB_PARAM_FLAG_CONFDESC_PROGMEM |
|
.flags = USB_PARAM_FLAG_CONFDESC_PROGMEM |
|
||||||
USB_PARAM_FLAG_DEVDESC_PROGMEM |
|
|
||||||
USB_PARAM_FLAG_REPORTDESC_PROGMEM,
|
USB_PARAM_FLAG_REPORTDESC_PROGMEM,
|
||||||
.devdesc = (PGM_VOID_P)&device_descriptor,
|
.devdesc = (PGM_VOID_P)&device_descriptor,
|
||||||
.configdesc = (PGM_VOID_P)&cfg0,
|
.configdesc = (PGM_VOID_P)&cfg0, // Patched in main() for two players
|
||||||
.configdesc_ttllen = sizeof(cfg0),
|
.configdesc_ttllen = sizeof(cfg0), // Patched in main() for two players
|
||||||
.num_strings = NUM_USB_STRINGS,
|
.num_strings = NUM_USB_STRINGS,
|
||||||
.strings = g_usb_strings,
|
.strings = g_usb_strings,
|
||||||
|
|
||||||
.n_hid_interfaces = 2,
|
.n_hid_interfaces = 1 + 1, // One per player + one management interface (patched in main() for two players)
|
||||||
.hid_params = {
|
.hid_params = {
|
||||||
[0] = {
|
[0] = {
|
||||||
.reportdesc = gcn64_usbHidReportDescriptor,
|
.reportdesc = gcn64_usbHidReportDescriptor,
|
||||||
.reportdesc_len = sizeof(gcn64_usbHidReportDescriptor),
|
.reportdesc_len = sizeof(gcn64_usbHidReportDescriptor),
|
||||||
.getReport = usbpad_hid_get_report,
|
.getReport = _usbpad_hid_get_report,
|
||||||
.setReport = usbpad_hid_set_report,
|
.setReport = _usbpad_hid_set_report,
|
||||||
|
.endpoint_size = 16,
|
||||||
},
|
},
|
||||||
[1] = {
|
[1] = {
|
||||||
.reportdesc = dataHidReport,
|
.reportdesc = dataHidReport,
|
||||||
.reportdesc_len = sizeof(dataHidReport),
|
.reportdesc_len = sizeof(dataHidReport),
|
||||||
.getReport = hiddata_get_report,
|
.getReport = hiddata_get_report,
|
||||||
.setReport = hiddata_set_report,
|
.setReport = hiddata_set_report,
|
||||||
|
.endpoint_size = 64,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -225,28 +541,28 @@ void hwinit(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define NUM_PAD_TYPES 2
|
uint8_t num_players = 1;
|
||||||
|
unsigned char current_pad_type[NUM_CHANNELS] = { };
|
||||||
|
|
||||||
unsigned char current_pad_type = CONTROLLER_IS_ABSENT;
|
Gamepad *detectPad(unsigned char chn)
|
||||||
|
|
||||||
Gamepad *detectPad(void)
|
|
||||||
{
|
{
|
||||||
current_pad_type = gcn64_detectController();
|
current_pad_type[chn] = gcn64_detectController(chn);
|
||||||
|
|
||||||
switch (current_pad_type)
|
switch (current_pad_type[chn])
|
||||||
{
|
{
|
||||||
case CONTROLLER_IS_ABSENT:
|
case CONTROLLER_IS_ABSENT:
|
||||||
case CONTROLLER_IS_UNKNOWN:
|
case CONTROLLER_IS_UNKNOWN:
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
case CONTROLLER_IS_N64_MOUSE:
|
||||||
case CONTROLLER_IS_N64:
|
case CONTROLLER_IS_N64:
|
||||||
printf("Detected N64 controller\n");
|
|
||||||
return n64GetGamepad();
|
return n64GetGamepad();
|
||||||
break;
|
|
||||||
|
|
||||||
case CONTROLLER_IS_GC:
|
case CONTROLLER_IS_GC:
|
||||||
printf("Detected GC controller\n");
|
|
||||||
return gamecubeGetGamepad();
|
return gamecubeGetGamepad();
|
||||||
|
|
||||||
|
case CONTROLLER_IS_GC_KEYBOARD:
|
||||||
|
return gamecubeGetKeyboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -265,46 +581,176 @@ void eeprom_app_ready(void)
|
||||||
g_usb_strings[USB_STRING_SERIAL_IDX] = serial_from_eeprom;
|
g_usb_strings[USB_STRING_SERIAL_IDX] = serial_from_eeprom;
|
||||||
}
|
}
|
||||||
|
|
||||||
char g_polling_suspended = 0;
|
static struct usbpad usbpads[MAX_PLAYERS];
|
||||||
|
static char g_polling_suspended = 0;
|
||||||
|
|
||||||
void pollDelay(void)
|
static void setSuspendPolling(uint8_t suspend)
|
||||||
{
|
{
|
||||||
int i;
|
g_polling_suspended = suspend;
|
||||||
for (i=0; i<g_eeprom_data.cfg.poll_interval[0]; i++) {
|
}
|
||||||
_delay_ms(1);
|
|
||||||
|
static void forceVibration(uint8_t channel, uint8_t force)
|
||||||
|
{
|
||||||
|
if (channel < MAX_PLAYERS) {
|
||||||
|
usbpad_forceVibrate(&usbpads[channel], force);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t getSupportedModes(uint8_t *dst)
|
||||||
|
{
|
||||||
|
uint8_t idx = 0;
|
||||||
|
|
||||||
|
switch (g_eeprom_data.cfg.mode)
|
||||||
|
{
|
||||||
|
// Allow toggling between keyboard and joystick modes on
|
||||||
|
// single-port gamecube adapter
|
||||||
|
case CFG_MODE_GC_ONLY:
|
||||||
|
case CFG_MODE_KEYBOARD:
|
||||||
|
dst[idx++] = CFG_MODE_GC_ONLY;
|
||||||
|
dst[idx++] = CFG_MODE_KEYBOARD;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Allow toggling between two joysticks and joystick + keyboard modes
|
||||||
|
// on dual-port gamecube adapter
|
||||||
|
case CFG_MODE_2P_GC_ONLY:
|
||||||
|
case CFG_MODE_KB_AND_JS:
|
||||||
|
dst[idx++] = CFG_MODE_2P_GC_ONLY;
|
||||||
|
dst[idx++] = CFG_MODE_KB_AND_JS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// On N64/GC adapters, there is a GC port so we should support
|
||||||
|
// keyboards there. Use KEYBOARD_2 config here to avoid mixup
|
||||||
|
// with the GC-only adapter variation.
|
||||||
|
case CFG_MODE_STANDARD:
|
||||||
|
case CFG_MODE_KEYBOARD_2:
|
||||||
|
dst[idx++] = CFG_MODE_STANDARD;
|
||||||
|
dst[idx++] = CFG_MODE_KEYBOARD_2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dst[idx++] = CFG_MODE_STANDARD;
|
||||||
|
dst[idx++] = CFG_MODE_N64_ONLY;
|
||||||
|
dst[idx++] = CFG_MODE_GC_ONLY;
|
||||||
|
dst[idx++] = CFG_MODE_2P_STANDARD;
|
||||||
|
dst[idx++] = CFG_MODE_2P_N64_ONLY;
|
||||||
|
dst[idx++] = CFG_MODE_2P_GC_ONLY;
|
||||||
|
dst[idx++] = CFG_MODE_KEYBOARD;
|
||||||
|
dst[idx++] = CFG_MODE_KB_AND_JS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct hiddata_ops hiddata_ops = {
|
||||||
|
.suspendPolling = setSuspendPolling,
|
||||||
|
.forceVibration = forceVibration,
|
||||||
|
.getSupportedModes = getSupportedModes,
|
||||||
|
};
|
||||||
|
|
||||||
#define STATE_WAIT_POLLTIME 0
|
#define STATE_WAIT_POLLTIME 0
|
||||||
#define STATE_POLL_PAD 1
|
#define STATE_POLL_PAD 1
|
||||||
#define STATE_WAIT_INTERRUPT_READY 2
|
#define STATE_WAIT_INTERRUPT_READY 2
|
||||||
#define STATE_TRANSMIT 3
|
#define STATE_TRANSMIT 3
|
||||||
|
#define STATE_WAIT_INTERRUPT_READY_P2 4
|
||||||
|
#define STATE_TRANSMIT_P2 5
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
Gamepad *pad = NULL;
|
Gamepad *pads[MAX_PLAYERS] = { };
|
||||||
gamepad_data pad_data;
|
gamepad_data pad_data;
|
||||||
unsigned char gamepad_vibrate = 0;
|
uint8_t gamepad_vibrate = 0;
|
||||||
unsigned char state = STATE_WAIT_POLLTIME;
|
uint8_t state = STATE_WAIT_POLLTIME;
|
||||||
int error_count=0;
|
uint8_t channel;
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
hwinit();
|
hwinit();
|
||||||
usart1_init();
|
usart1_init();
|
||||||
eeprom_init();
|
eeprom_init();
|
||||||
intervaltimer_init();
|
intervaltimer_init();
|
||||||
|
intervaltimer2_init();
|
||||||
|
stkchk_init();
|
||||||
|
|
||||||
/* Init the buffer with idle data */
|
switch (g_eeprom_data.cfg.mode)
|
||||||
usbpad_update(NULL);
|
{
|
||||||
|
default:
|
||||||
|
case CFG_MODE_STANDARD:
|
||||||
|
usbstrings_changeProductString_P(PSTR("GC/N64 to USB v"VERSIONSTR_SHORT));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CFG_MODE_N64_ONLY:
|
||||||
|
usbstrings_changeProductString_P(PSTR("N64 to USB v"VERSIONSTR_SHORT));
|
||||||
|
device_descriptor.idProduct = N64_USB_PID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CFG_MODE_GC_ONLY:
|
||||||
|
usbstrings_changeProductString_P(PSTR("Gamecube to USB v"VERSIONSTR_SHORT));
|
||||||
|
device_descriptor.idProduct = GC_USB_PID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CFG_MODE_2P_STANDARD:
|
||||||
|
usbstrings_changeProductString_P(PSTR("Dual GC/N64 to USB v"VERSIONSTR_SHORT));
|
||||||
|
device_descriptor.idProduct = DUAL_GCN64_USB_PID;
|
||||||
|
num_players = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CFG_MODE_2P_N64_ONLY:
|
||||||
|
usbstrings_changeProductString_P(PSTR("Dual N64 to USB v"VERSIONSTR_SHORT));
|
||||||
|
device_descriptor.idProduct = DUAL_N64_USB_PID;
|
||||||
|
num_players = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CFG_MODE_2P_GC_ONLY:
|
||||||
|
usbstrings_changeProductString_P(PSTR("Dual Gamecube to USB v"VERSIONSTR_SHORT));
|
||||||
|
device_descriptor.idProduct = DUAL_GC_USB_PID;
|
||||||
|
num_players = 2;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CFG_MODE_KB_AND_JS:
|
||||||
|
case CFG_MODE_KEYBOARD:
|
||||||
|
case CFG_MODE_KEYBOARD_2:
|
||||||
|
keyboard_main();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2-players common
|
||||||
|
if (num_players == 2) {
|
||||||
|
usb_params.configdesc = (PGM_VOID_P)&cfg0_2p;
|
||||||
|
usb_params.configdesc_ttllen = sizeof(cfg0_2p);
|
||||||
|
usb_params.n_hid_interfaces = 3;
|
||||||
|
// Move the management interface is the last position
|
||||||
|
memcpy(usb_params.hid_params + 2, usb_params.hid_params + 1, sizeof(struct usb_hid_parameters));
|
||||||
|
// Add a second player interface between them
|
||||||
|
memcpy(usb_params.hid_params + 1, usb_params.hid_params + 0, sizeof(struct usb_hid_parameters));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i<num_players; i++) {
|
||||||
|
usbpad_init(&usbpads[i]);
|
||||||
|
usb_params.hid_params[i].ctx = &usbpads[i];
|
||||||
|
}
|
||||||
|
|
||||||
sei();
|
sei();
|
||||||
usb_init(&usb_params);
|
usb_init(&usb_params);
|
||||||
|
|
||||||
|
// Timebase for force feedback 'loop count'
|
||||||
|
intervaltimer2_set16ms();
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
static char last_v = 0;
|
static char last_v[MAX_PLAYERS] = { };
|
||||||
|
|
||||||
|
if (stkchk_verify()) {
|
||||||
|
enterBootLoader();
|
||||||
|
}
|
||||||
|
|
||||||
usb_doTasks();
|
usb_doTasks();
|
||||||
hiddata_doTask();
|
hiddata_doTask(&hiddata_ops);
|
||||||
|
// Run vibration tasks
|
||||||
|
if (intervaltimer2_get()) {
|
||||||
|
for (channel=0; channel < num_players; channel++) {
|
||||||
|
usbpad_vibrationTask(&usbpads[channel]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch(state)
|
switch(state)
|
||||||
{
|
{
|
||||||
|
@ -318,64 +764,282 @@ int main(void)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_POLL_PAD:
|
case STATE_POLL_PAD:
|
||||||
/* Try to auto-detect controller if none*/
|
for (channel=0; channel<num_players; channel++)
|
||||||
if (!pad) {
|
{
|
||||||
pad = detectPad();
|
/* Try to auto-detect controller if none*/
|
||||||
if (pad && (pad->hotplug)) {
|
if (!pads[channel]) {
|
||||||
// For gamecube, this make sure the next
|
pads[channel] = detectPad(channel);
|
||||||
// analog values we read become the center
|
if (pads[channel] && (pads[channel]->hotplug)) {
|
||||||
// reference.
|
// For gamecube, this make sure the next
|
||||||
pad->hotplug();
|
// analog values we read become the center
|
||||||
|
// reference.
|
||||||
|
pads[channel]->hotplug(channel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (pad) {
|
/* Read from the pad by calling update */
|
||||||
if (pad->update()) {
|
if (pads[channel]) {
|
||||||
error_count++;
|
if (pads[channel]->update(channel)) {
|
||||||
if (error_count > MAX_READ_ERRORS) {
|
error_count[channel]++;
|
||||||
pad = NULL;
|
if (error_count[channel] > MAX_READ_ERRORS) {
|
||||||
state = STATE_WAIT_POLLTIME;
|
pads[channel] = NULL;
|
||||||
error_count = 0;
|
error_count[channel] = 0;
|
||||||
break;
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error_count[channel]=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pads[channel]->changed(channel))
|
||||||
|
{
|
||||||
|
pads[channel]->getReport(channel, &pad_data);
|
||||||
|
usbpad_update(&usbpads[channel], &pad_data);
|
||||||
|
state = STATE_WAIT_INTERRUPT_READY;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error_count=0;
|
/* Just make sure the gamepad state holds valid data
|
||||||
|
* to appear inactive (no buttons and axes in neutral) */
|
||||||
|
usbpad_update(&usbpads[channel], NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pad->changed()) {
|
|
||||||
|
|
||||||
pad->getReport(&pad_data);
|
|
||||||
usbpad_update(&pad_data);
|
|
||||||
state = STATE_WAIT_INTERRUPT_READY;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Just make sure the gamepad state holds valid data
|
|
||||||
* to appear inactive (no buttons and axes in neutral) */
|
|
||||||
usbpad_update(NULL);
|
|
||||||
}
|
}
|
||||||
state = STATE_WAIT_POLLTIME;
|
/* If there were change on any of the gamepads, state will
|
||||||
|
* be set to STATE_WAIT_INTERRUPT_READY. Otherwise, go back
|
||||||
|
* to WAIT_POLLTIME. */
|
||||||
|
if (state == STATE_POLL_PAD) {
|
||||||
|
state = STATE_WAIT_POLLTIME;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_WAIT_INTERRUPT_READY:
|
case STATE_WAIT_INTERRUPT_READY:
|
||||||
if (usb_interruptReady()) {
|
/* Wait until one of the interrupt endpoint is ready */
|
||||||
|
if (usb_interruptReady_ep1() || (num_players>1 && usb_interruptReady_ep2())) {
|
||||||
state = STATE_TRANSMIT;
|
state = STATE_TRANSMIT;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_TRANSMIT:
|
case STATE_TRANSMIT:
|
||||||
usb_interruptSend(usbpad_getReportBuffer(), usbpad_getReportSize());
|
if (usb_interruptReady_ep1()) {
|
||||||
|
usb_interruptSend_ep1(usbpad_getReportBuffer(&usbpads[0]), usbpad_getReportSize());
|
||||||
|
}
|
||||||
|
if (num_players>1 && usb_interruptReady_ep2()) {
|
||||||
|
usb_interruptSend_ep2(usbpad_getReportBuffer(&usbpads[1]), usbpad_getReportSize());
|
||||||
|
}
|
||||||
state = STATE_WAIT_POLLTIME;
|
state = STATE_WAIT_POLLTIME;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gamepad_vibrate = usbpad_mustVibrate();
|
for (channel=0; channel < num_players; channel++) {
|
||||||
if (last_v != gamepad_vibrate) {
|
gamepad_vibrate = usbpad_mustVibrate(&usbpads[channel]);
|
||||||
if (pad && pad->setVibration) {
|
if (last_v[channel] != gamepad_vibrate) {
|
||||||
pad->setVibration(gamepad_vibrate);
|
if (pads[channel] && pads[channel]->setVibration) {
|
||||||
|
pads[channel]->setVibration(channel, gamepad_vibrate);
|
||||||
|
}
|
||||||
|
last_v[channel] = gamepad_vibrate;
|
||||||
}
|
}
|
||||||
last_v = gamepad_vibrate;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int keyboard_main(void)
|
||||||
|
{
|
||||||
|
Gamepad *pads[MAX_PLAYERS] = { };
|
||||||
|
gamepad_data pad_data;
|
||||||
|
uint8_t gamepad_vibrate = 0;
|
||||||
|
uint8_t state = STATE_WAIT_POLLTIME;
|
||||||
|
uint8_t channel;
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
hwinit();
|
||||||
|
usart1_init();
|
||||||
|
eeprom_init();
|
||||||
|
intervaltimer_init();
|
||||||
|
intervaltimer2_init();
|
||||||
|
stkchk_init();
|
||||||
|
|
||||||
|
switch (g_eeprom_data.cfg.mode)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case CFG_MODE_KEYBOARD_2:
|
||||||
|
usbstrings_changeProductString_P(PSTR("KB to USB v"VERSIONSTR_SHORT));
|
||||||
|
device_descriptor.idProduct = KEYBOARD_PID2;
|
||||||
|
|
||||||
|
usb_params.configdesc = (PGM_VOID_P)&cfg0_kb;
|
||||||
|
usb_params.configdesc_ttllen = sizeof(cfg0_kb);
|
||||||
|
|
||||||
|
// replace Joystick report descriptor by keyboard
|
||||||
|
usb_params.hid_params[0].reportdesc = gcKeyboardReport;
|
||||||
|
usb_params.hid_params[0].reportdesc_len = sizeof(gcKeyboardReport);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CFG_MODE_KEYBOARD:
|
||||||
|
usbstrings_changeProductString_P(PSTR("GC KB to USB v"VERSIONSTR_SHORT));
|
||||||
|
device_descriptor.idProduct = KEYBOARD_PID;
|
||||||
|
|
||||||
|
usb_params.configdesc = (PGM_VOID_P)&cfg0_kb;
|
||||||
|
usb_params.configdesc_ttllen = sizeof(cfg0_kb);
|
||||||
|
|
||||||
|
// replace Joystick report descriptor by keyboard
|
||||||
|
usb_params.hid_params[0].reportdesc = gcKeyboardReport;
|
||||||
|
usb_params.hid_params[0].reportdesc_len = sizeof(gcKeyboardReport);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CFG_MODE_KB_AND_JS:
|
||||||
|
usbstrings_changeProductString_P(PSTR("GC KB+JS to USB v"VERSIONSTR_SHORT));
|
||||||
|
device_descriptor.idProduct = KEYBOARD_JS_PID;
|
||||||
|
|
||||||
|
usb_params.configdesc = (PGM_VOID_P)&cfg0_2p_keyboard;
|
||||||
|
usb_params.configdesc_ttllen = sizeof(cfg0_2p_keyboard);
|
||||||
|
|
||||||
|
// Move the management interface to the last position
|
||||||
|
memcpy(usb_params.hid_params + 2, usb_params.hid_params + 1, sizeof(struct usb_hid_parameters));
|
||||||
|
// Add a second player interface between them (still a joystick)
|
||||||
|
memcpy(usb_params.hid_params + 1, usb_params.hid_params + 0, sizeof(struct usb_hid_parameters));
|
||||||
|
// Convert second Joystick report descriptor to a keyboard
|
||||||
|
usb_params.hid_params[1].reportdesc = gcKeyboardReport;
|
||||||
|
usb_params.hid_params[1].reportdesc_len = sizeof(gcKeyboardReport);
|
||||||
|
|
||||||
|
usb_params.n_hid_interfaces = 3;
|
||||||
|
num_players = 2;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0; i<num_players; i++) {
|
||||||
|
usbpad_init(&usbpads[i]);
|
||||||
|
usb_params.hid_params[i].ctx = &usbpads[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
sei();
|
||||||
|
usb_init(&usb_params);
|
||||||
|
|
||||||
|
// Timebase for force feedback 'loop count'
|
||||||
|
intervaltimer2_set16ms();
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
static char last_v[MAX_PLAYERS] = { };
|
||||||
|
|
||||||
|
if (stkchk_verify()) {
|
||||||
|
enterBootLoader();
|
||||||
|
}
|
||||||
|
|
||||||
|
usb_doTasks();
|
||||||
|
hiddata_doTask(&hiddata_ops);
|
||||||
|
// Run vibration tasks
|
||||||
|
if (intervaltimer2_get()) {
|
||||||
|
for (channel=0; channel < num_players; channel++) {
|
||||||
|
usbpad_vibrationTask(&usbpads[channel]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(state)
|
||||||
|
{
|
||||||
|
case STATE_WAIT_POLLTIME:
|
||||||
|
if (!g_polling_suspended) {
|
||||||
|
intervaltimer_set(g_eeprom_data.cfg.poll_interval[0]);
|
||||||
|
if (intervaltimer_get()) {
|
||||||
|
state = STATE_POLL_PAD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STATE_POLL_PAD:
|
||||||
|
for (channel=0; channel<num_players; channel++)
|
||||||
|
{
|
||||||
|
/* Try to auto-detect controller if none*/
|
||||||
|
if (!pads[channel]) {
|
||||||
|
pads[channel] = detectPad(channel);
|
||||||
|
if (pads[channel] && (pads[channel]->hotplug)) {
|
||||||
|
// For gamecube, this make sure the next
|
||||||
|
// analog values we read become the center
|
||||||
|
// reference.
|
||||||
|
pads[channel]->hotplug(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read from the pad by calling update */
|
||||||
|
if (pads[channel]) {
|
||||||
|
if (pads[channel]->update(channel)) {
|
||||||
|
error_count[channel]++;
|
||||||
|
if (error_count[channel] > MAX_READ_ERRORS) {
|
||||||
|
pads[channel] = NULL;
|
||||||
|
error_count[channel] = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error_count[channel]=0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pads[channel]->changed(channel))
|
||||||
|
{
|
||||||
|
pads[channel]->getReport(channel, &pad_data);
|
||||||
|
|
||||||
|
if ((num_players == 1) && (channel == 0)) {
|
||||||
|
// single-port adapter in keyboard mode (kb in port 1)
|
||||||
|
usbpad_update_kb(&usbpads[channel], &pad_data);
|
||||||
|
} else if ((num_players == 2) && (channel == 1)) {
|
||||||
|
// dual-port adapter in keyboard mode (kb in port 2)
|
||||||
|
usbpad_update_kb(&usbpads[channel], &pad_data);
|
||||||
|
} else {
|
||||||
|
usbpad_update(&usbpads[channel], &pad_data);
|
||||||
|
}
|
||||||
|
state = STATE_WAIT_INTERRUPT_READY;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Just make sure the gamepad state holds valid data
|
||||||
|
* to appear inactive (no buttons and axes in neutral) */
|
||||||
|
usbpad_update(&usbpads[channel], NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* If there were change on any of the gamepads, state will
|
||||||
|
* be set to STATE_WAIT_INTERRUPT_READY. Otherwise, go back
|
||||||
|
* to WAIT_POLLTIME. */
|
||||||
|
if (state == STATE_POLL_PAD) {
|
||||||
|
state = STATE_WAIT_POLLTIME;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STATE_WAIT_INTERRUPT_READY:
|
||||||
|
/* Wait until one of the interrupt endpoint is ready */
|
||||||
|
if (usb_interruptReady_ep1() || (num_players>1 && usb_interruptReady_ep2())) {
|
||||||
|
state = STATE_TRANSMIT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STATE_TRANSMIT:
|
||||||
|
if (usb_interruptReady_ep1()) {
|
||||||
|
if (num_players == 1) {
|
||||||
|
// Single-port adapters have the keyboard in port 1
|
||||||
|
usb_interruptSend_ep1(usbpad_getReportBuffer(&usbpads[0]), usbpad_getReportSizeKB());
|
||||||
|
} else {
|
||||||
|
usb_interruptSend_ep1(usbpad_getReportBuffer(&usbpads[0]), usbpad_getReportSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Keyboard is always in second port on dual port adapters
|
||||||
|
if (num_players>1 && usb_interruptReady_ep2()) {
|
||||||
|
usb_interruptSend_ep2(usbpad_getReportBuffer(&usbpads[1]), usbpad_getReportSizeKB());
|
||||||
|
}
|
||||||
|
state = STATE_WAIT_POLLTIME;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for (channel=0; channel < num_players; channel++) {
|
||||||
|
gamepad_vibrate = usbpad_mustVibrate(&usbpads[channel]);
|
||||||
|
if (last_v[channel] != gamepad_vibrate) {
|
||||||
|
if (pads[channel] && pads[channel]->setVibration) {
|
||||||
|
pads[channel]->setVibration(channel, gamepad_vibrate);
|
||||||
|
}
|
||||||
|
last_v[channel] = gamepad_vibrate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
7
main.h
7
main.h
|
@ -1,6 +1,11 @@
|
||||||
#ifndef _main_h__
|
#ifndef _main_h__
|
||||||
#define _main_h__
|
#define _main_h__
|
||||||
|
|
||||||
extern unsigned char current_pad_type;
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
extern unsigned char current_pad_type[NUM_CHANNELS];
|
||||||
|
extern uint8_t num_players;
|
||||||
|
|
||||||
#endif // _main_h__
|
#endif // _main_h__
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
||||||
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
|
Copyright (C) 2007-2016 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
181
n64.c
181
n64.c
|
@ -1,5 +1,5 @@
|
||||||
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
||||||
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
|
Copyright (C) 2007-2021 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -21,36 +21,39 @@
|
||||||
#include "gamepads.h"
|
#include "gamepads.h"
|
||||||
#include "n64.h"
|
#include "n64.h"
|
||||||
#include "gcn64_protocol.h"
|
#include "gcn64_protocol.h"
|
||||||
|
#include "eeprom.h"
|
||||||
|
#include "main.h" // for num_players
|
||||||
|
|
||||||
#undef BUTTON_A_RUMBLE_TEST
|
#undef BUTTON_A_RUMBLE_TEST
|
||||||
|
|
||||||
/*********** prototypes *************/
|
/*********** prototypes *************/
|
||||||
static void n64Init(void);
|
static void n64Init(unsigned char chn);
|
||||||
static char n64Update(void);
|
static char n64Update(unsigned char chn);
|
||||||
static char n64Changed(void);
|
static char n64Changed(unsigned char chn);
|
||||||
static void n64GetReport(gamepad_data *dst);
|
static void n64GetReport(unsigned char chn, gamepad_data *dst);
|
||||||
static void n64SetVibration(char enable);
|
static void n64SetVibration(unsigned char chn, char enable);
|
||||||
|
|
||||||
static char must_rumble = 0;
|
static char must_rumble[GAMEPAD_MAX_CHANNELS] = { };
|
||||||
#ifdef BUTTON_A_RUMBLE_TEST
|
#ifdef BUTTON_A_RUMBLE_TEST
|
||||||
static char force_rumble = 0;
|
static char force_rumble[GAMEPAD_MAX_CHANNELS] = { };
|
||||||
#endif
|
#endif
|
||||||
|
static unsigned char n64_rumble_state[GAMEPAD_MAX_CHANNELS] = { };
|
||||||
|
|
||||||
static void n64Init(void)
|
unsigned char tmpdata[40]; // Shared between channels
|
||||||
{
|
|
||||||
n64Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
#define RSTATE_INIT 0
|
#define RSTATE_UNAVAILABLE 0
|
||||||
#define RSTATE_OFF 1
|
#define RSTATE_OFF 1
|
||||||
#define RSTATE_TURNON 2
|
#define RSTATE_TURNON 2
|
||||||
#define RSTATE_ON 3
|
#define RSTATE_ON 3
|
||||||
#define RSTATE_TURNOFF 4
|
#define RSTATE_TURNOFF 4
|
||||||
#define RSTATE_UNAVAILABLE 5
|
#define RSTATE_INIT 5
|
||||||
static unsigned char n64_rumble_state = RSTATE_UNAVAILABLE;
|
|
||||||
unsigned char tmpdata[40];
|
|
||||||
|
|
||||||
static char initRumble(void)
|
static void n64Init(unsigned char chn)
|
||||||
|
{
|
||||||
|
n64Update(chn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char initRumble(unsigned char chn)
|
||||||
{
|
{
|
||||||
int count;
|
int count;
|
||||||
unsigned char data[4];
|
unsigned char data[4];
|
||||||
|
@ -60,14 +63,14 @@ static char initRumble(void)
|
||||||
tmpdata[2] = 0x01;
|
tmpdata[2] = 0x01;
|
||||||
memset(tmpdata+3, 0x80, 32);
|
memset(tmpdata+3, 0x80, 32);
|
||||||
|
|
||||||
count = gcn64_transaction(tmpdata, 35, data, sizeof(data));
|
count = gcn64_transaction(chn, tmpdata, 35, data, sizeof(data));
|
||||||
if (count == 1)
|
if (count == 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char controlRumble(char enable)
|
static char controlRumble(unsigned char chn, char enable)
|
||||||
{
|
{
|
||||||
int count;
|
int count;
|
||||||
unsigned char data[4];
|
unsigned char data[4];
|
||||||
|
@ -76,14 +79,14 @@ static char controlRumble(char enable)
|
||||||
tmpdata[1] = 0xc0;
|
tmpdata[1] = 0xc0;
|
||||||
tmpdata[2] = 0x1b;
|
tmpdata[2] = 0x1b;
|
||||||
memset(tmpdata+3, enable ? 0x01 : 0x00, 32);
|
memset(tmpdata+3, enable ? 0x01 : 0x00, 32);
|
||||||
count = gcn64_transaction(tmpdata, 35, data, sizeof(data));
|
count = gcn64_transaction(chn, tmpdata, 35, data, sizeof(data));
|
||||||
if (count == 1)
|
if (count == 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char n64Update(void)
|
static char n64Update(unsigned char chn)
|
||||||
{
|
{
|
||||||
unsigned char count;
|
unsigned char count;
|
||||||
unsigned char x,y;
|
unsigned char x,y;
|
||||||
|
@ -101,78 +104,112 @@ static char n64Update(void)
|
||||||
* Bit 1 tells is if there was something connected that has been removed.
|
* Bit 1 tells is if there was something connected that has been removed.
|
||||||
*/
|
*/
|
||||||
tmpdata[0] = N64_GET_CAPABILITIES;
|
tmpdata[0] = N64_GET_CAPABILITIES;
|
||||||
count = gcn64_transaction(tmpdata, 1, caps, sizeof(caps));
|
count = gcn64_transaction(chn, tmpdata, 1, caps, sizeof(caps));
|
||||||
if (count != N64_CAPS_REPLY_LENGTH) {
|
if (count != N64_CAPS_REPLY_LENGTH) {
|
||||||
// a failed read could mean the pack or controller was gone. Init
|
// a failed read could mean the pack or controller was gone. Init
|
||||||
// will be necessary next time we detect a pack is present.
|
// will be necessary next time we detect a pack is present.
|
||||||
n64_rumble_state = RSTATE_INIT;
|
n64_rumble_state[chn] = RSTATE_INIT;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The brawler 64 wireless gamepad does not like when the get caps command is followed
|
||||||
|
* too closely by the get status command. Without a long pause between the two commands,
|
||||||
|
* it just returns all zeros. */
|
||||||
|
if (num_players > 1) {
|
||||||
|
// n64Update is called two times on two-player adapters. This
|
||||||
|
// means time lost waiting here doubles. Without this, the controllers
|
||||||
|
// are no longer being polled at the rate set in the gui, which is bad.
|
||||||
|
// (I want this setting to be reliable)
|
||||||
|
//
|
||||||
|
// So on dual port adapters, brawler 64 wireless controllers will be
|
||||||
|
// usable only at intervals >= 4ms.
|
||||||
|
//
|
||||||
|
// TODO: Interleave access like this to allow a higher polling
|
||||||
|
// frequency: Read caps port 1, Read caps port 2, delay, Read status port 1,
|
||||||
|
// read status port 2. (Maybe this could make 3ms intervals work)
|
||||||
|
//
|
||||||
|
if (g_eeprom_data.cfg.poll_interval[0] >= 8) {
|
||||||
|
_delay_ms(2.5);
|
||||||
|
} else if (g_eeprom_data.cfg.poll_interval[0] >= 6) {
|
||||||
|
_delay_ms(1.5);
|
||||||
|
} else if (g_eeprom_data.cfg.poll_interval[0] >= 4) {
|
||||||
|
_delay_ms(1.25);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (g_eeprom_data.cfg.poll_interval[0] >= 4) {
|
||||||
|
_delay_ms(2.5);
|
||||||
|
} else if (g_eeprom_data.cfg.poll_interval[0] >= 3) {
|
||||||
|
_delay_ms(1.5);
|
||||||
|
} else if (g_eeprom_data.cfg.poll_interval[0] >= 2) {
|
||||||
|
_delay_ms(1.25); // does not work at 1ms
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Detect when a pack becomes present and schedule initialisation when it happens. */
|
/* Detect when a pack becomes present and schedule initialisation when it happens. */
|
||||||
if ((caps[2] & 0x01) && (n64_rumble_state == RSTATE_UNAVAILABLE)) {
|
if ((caps[2] & 0x01) && (n64_rumble_state[chn] == RSTATE_UNAVAILABLE)) {
|
||||||
n64_rumble_state = RSTATE_INIT;
|
n64_rumble_state[chn] = RSTATE_INIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Detect when a pack is removed. */
|
/* Detect when a pack is removed. */
|
||||||
if (!(caps[2] & 0x01) || (caps[2] & 0x02) ) {
|
if (!(caps[2] & 0x01) || (caps[2] & 0x02) ) {
|
||||||
n64_rumble_state = RSTATE_UNAVAILABLE;
|
n64_rumble_state[chn] = RSTATE_UNAVAILABLE;
|
||||||
}
|
}
|
||||||
#ifdef BUTTON_A_RUMBLE_TEST
|
#ifdef BUTTON_A_RUMBLE_TEST
|
||||||
must_rumble = force_rumble;
|
must_rumble[chn] = force_rumble[chn];
|
||||||
//printf("Caps: %02x %02x %02x\r\n", caps[0], caps[1], caps[2]);
|
//printf("Caps: %02x %02x %02x\r\n", caps[0], caps[1], caps[2]);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
switch (n64_rumble_state)
|
switch (n64_rumble_state[chn])
|
||||||
{
|
{
|
||||||
case RSTATE_INIT:
|
case RSTATE_INIT:
|
||||||
/* Retry until the controller answers with a full byte. */
|
/* Retry until the controller answers with a full byte. */
|
||||||
if (initRumble() != 0) {
|
if (initRumble(chn) != 0) {
|
||||||
if (initRumble() != 0) {
|
if (initRumble(chn) != 0) {
|
||||||
n64_rumble_state = RSTATE_UNAVAILABLE;
|
n64_rumble_state[chn] = RSTATE_UNAVAILABLE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (must_rumble) {
|
if (must_rumble[chn]) {
|
||||||
controlRumble(1);
|
controlRumble(chn, 1);
|
||||||
n64_rumble_state = RSTATE_ON;
|
n64_rumble_state[chn] = RSTATE_ON;
|
||||||
} else {
|
} else {
|
||||||
controlRumble(0);
|
controlRumble(chn, 0);
|
||||||
n64_rumble_state = RSTATE_OFF;
|
n64_rumble_state[chn] = RSTATE_OFF;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RSTATE_TURNON:
|
case RSTATE_TURNON:
|
||||||
if (0 == controlRumble(1)) {
|
if (0 == controlRumble(chn, 1)) {
|
||||||
n64_rumble_state = RSTATE_ON;
|
n64_rumble_state[chn] = RSTATE_ON;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RSTATE_TURNOFF:
|
case RSTATE_TURNOFF:
|
||||||
if (0 == controlRumble(0)) {
|
if (0 == controlRumble(chn, 0)) {
|
||||||
n64_rumble_state = RSTATE_OFF;
|
n64_rumble_state[chn] = RSTATE_OFF;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RSTATE_ON:
|
case RSTATE_ON:
|
||||||
if (!must_rumble) {
|
if (!must_rumble[chn]) {
|
||||||
controlRumble(0);
|
controlRumble(chn, 0);
|
||||||
n64_rumble_state = RSTATE_OFF;
|
n64_rumble_state[chn] = RSTATE_OFF;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RSTATE_OFF:
|
case RSTATE_OFF:
|
||||||
if (must_rumble) {
|
if (must_rumble[chn]) {
|
||||||
controlRumble(1);
|
controlRumble(chn, 1);
|
||||||
n64_rumble_state = RSTATE_ON;
|
n64_rumble_state[chn] = RSTATE_ON;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpdata[0] = N64_GET_STATUS;
|
tmpdata[0] = N64_GET_STATUS;
|
||||||
count = gcn64_transaction(tmpdata, 1, status, sizeof(status));
|
count = gcn64_transaction(chn, tmpdata, 1, status, sizeof(status));
|
||||||
if (count != N64_GET_STATUS_REPLY_LENGTH) {
|
if (count != N64_GET_STATUS_REPLY_LENGTH) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -206,22 +243,24 @@ static char n64Update(void)
|
||||||
|
|
||||||
#ifdef BUTTON_A_RUMBLE_TEST
|
#ifdef BUTTON_A_RUMBLE_TEST
|
||||||
if (btns1 & 0x80) {
|
if (btns1 & 0x80) {
|
||||||
force_rumble = 1;
|
force_rumble[chn] = 1;
|
||||||
} else {
|
} else {
|
||||||
force_rumble = 0;
|
force_rumble[chn] = 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
last_built_report.pad_type = PAD_TYPE_N64;
|
last_built_report[chn].pad_type = PAD_TYPE_N64;
|
||||||
last_built_report.n64.buttons = (btns1 << 8) | btns2;
|
last_built_report[chn].n64.buttons = (btns1 << 8) | btns2;
|
||||||
last_built_report.n64.x = x;
|
last_built_report[chn].n64.x = x;
|
||||||
last_built_report.n64.y = y;
|
last_built_report[chn].n64.y = y;
|
||||||
|
|
||||||
|
#ifdef PAD_DATA_HAS_RAW
|
||||||
/* Copy all the data as-is for the raw field */
|
/* Copy all the data as-is for the raw field */
|
||||||
last_built_report.n64.raw_data[0] = btns1;
|
last_built_report[chn].n64.raw_data[0] = btns1;
|
||||||
last_built_report.n64.raw_data[1] = btns2;
|
last_built_report[chn].n64.raw_data[1] = btns2;
|
||||||
last_built_report.n64.raw_data[2] = x;
|
last_built_report[chn].n64.raw_data[2] = x;
|
||||||
last_built_report.n64.raw_data[3] = y;
|
last_built_report[chn].n64.raw_data[3] = y;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Some cheap non-official controllers
|
/* Some cheap non-official controllers
|
||||||
* use the full 8 bit range instead of the
|
* use the full 8 bit range instead of the
|
||||||
|
@ -242,15 +281,15 @@ static char n64Update(void)
|
||||||
* on a N64, or maybe a little better. This should
|
* on a N64, or maybe a little better. This should
|
||||||
* help people realise they got what the paid for
|
* help people realise they got what the paid for
|
||||||
* instead of suspecting the adapter. */
|
* instead of suspecting the adapter. */
|
||||||
if (last_built_report.n64.x == -128)
|
if (last_built_report[chn].n64.x == -128)
|
||||||
last_built_report.n64.x = -127;
|
last_built_report[chn].n64.x = -127;
|
||||||
if (last_built_report.n64.y == -128)
|
if (last_built_report[chn].n64.y == -128)
|
||||||
last_built_report.n64.y = -127;
|
last_built_report[chn].n64.y = -127;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char n64Probe(void)
|
static char n64Probe(unsigned char chn)
|
||||||
{
|
{
|
||||||
int count;
|
int count;
|
||||||
char i;
|
char i;
|
||||||
|
@ -267,14 +306,14 @@ static char n64Probe(void)
|
||||||
* Bit 1 tells is if there was something connected that has been removed.
|
* Bit 1 tells is if there was something connected that has been removed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
n64_rumble_state = RSTATE_UNAVAILABLE;
|
n64_rumble_state[chn] = RSTATE_UNAVAILABLE;
|
||||||
|
|
||||||
for (i=0; i<15; i++)
|
for (i=0; i<15; i++)
|
||||||
{
|
{
|
||||||
_delay_ms(30);
|
_delay_ms(30);
|
||||||
|
|
||||||
tmp = N64_GET_CAPABILITIES;
|
tmp = N64_GET_CAPABILITIES;
|
||||||
count = gcn64_transaction(&tmp, 1, data, sizeof(data));
|
count = gcn64_transaction(chn, &tmp, 1, data, sizeof(data));
|
||||||
|
|
||||||
if (count == N64_CAPS_REPLY_LENGTH) {
|
if (count == N64_CAPS_REPLY_LENGTH) {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -283,22 +322,22 @@ static char n64Probe(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char n64Changed(void)
|
static char n64Changed(unsigned char chn)
|
||||||
{
|
{
|
||||||
return memcmp(&last_built_report, &last_sent_report, sizeof(gamepad_data));
|
return memcmp(&last_built_report[chn], &last_sent_report[chn], sizeof(gamepad_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void n64GetReport(gamepad_data *dst)
|
static void n64GetReport(unsigned char chn, gamepad_data *dst)
|
||||||
{
|
{
|
||||||
if (dst)
|
if (dst)
|
||||||
memcpy(dst, &last_built_report, sizeof(gamepad_data));
|
memcpy(dst, &last_built_report[chn], sizeof(gamepad_data));
|
||||||
|
|
||||||
memcpy(&last_sent_report, &last_built_report, sizeof(gamepad_data));
|
memcpy(&last_sent_report[chn], &last_built_report[chn], sizeof(gamepad_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void n64SetVibration(char enable)
|
static void n64SetVibration(unsigned char chn, char enable)
|
||||||
{
|
{
|
||||||
must_rumble = enable;
|
must_rumble[chn] = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Gamepad N64Gamepad = {
|
static Gamepad N64Gamepad = {
|
||||||
|
|
|
@ -32,7 +32,7 @@ if [ -f $RELEASEDIR/$FILENAME ]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
git tag $TAG -f
|
git tag $TAG -f -a
|
||||||
git archive --format=tar --prefix=$DIRNAME/ HEAD | gzip > $RELEASEDIR/$FILENAME
|
git archive --format=tar --prefix=$DIRNAME/ HEAD | gzip > $RELEASEDIR/$FILENAME
|
||||||
|
|
||||||
cd $RELEASEDIR
|
cd $RELEASEDIR
|
||||||
|
|
28
reportdesc.c
28
reportdesc.c
|
@ -1,5 +1,5 @@
|
||||||
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
/* gc_n64_usb : Gamecube or N64 controller to USB adapter firmware
|
||||||
Copyright (C) 2007-2015 Raphael Assenat <raph@raphnet.net>
|
Copyright (C) 2007-2016 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -41,12 +41,18 @@ const uint8_t gcn64_usbHidReportDescriptor[] PROGMEM = {
|
||||||
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||||
0x26, 0x00, 0x7D, // LOGICAL_MAXIMUM (32000)
|
0x26, 0x00, 0x7D, // LOGICAL_MAXIMUM (32000)
|
||||||
|
|
||||||
|
// Main analog stick
|
||||||
0x09, 0x30, // USAGE (X)
|
0x09, 0x30, // USAGE (X)
|
||||||
0x09, 0x31, // USAGE (Y)
|
0x09, 0x31, // USAGE (Y)
|
||||||
|
|
||||||
|
// Gamecube C-stick
|
||||||
0x09, 0x33, // USAGE (Rx)
|
0x09, 0x33, // USAGE (Rx)
|
||||||
0x09, 0x34, // USAGE (Ry)
|
0x09, 0x34, // USAGE (Ry)
|
||||||
|
|
||||||
|
// Triggers
|
||||||
0x09, 0x35, // USAGE (Rz)
|
0x09, 0x35, // USAGE (Rz)
|
||||||
0x09, 0x36, // USAGE (Slider)
|
0x09, 0x32, // USAGE (Z)
|
||||||
|
|
||||||
0x81, 0x02, // INPUT
|
0x81, 0x02, // INPUT
|
||||||
0xc0, // END COLLECTION
|
0xc0, // END COLLECTION
|
||||||
|
|
||||||
|
@ -639,3 +645,21 @@ const uint8_t gcn64_usbHidReportDescriptor[] PROGMEM = {
|
||||||
0xC0, // End Collection
|
0xC0, // End Collection
|
||||||
0xC0, // End Collection
|
0xC0, // End Collection
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const unsigned char gcKeyboardReport[] PROGMEM = {
|
||||||
|
0x05, 0x01, // Usage page : Generic Desktop
|
||||||
|
0x09, 0x06, // Usage (Keyboard)
|
||||||
|
0xA1, 0x01, // Collection (Application)
|
||||||
|
0x05, 0x07, // Usage Page (Key Codes)
|
||||||
|
|
||||||
|
0x95, 0x03, // Report Count(3)
|
||||||
|
0x75, 0x08, // Report Size(8)
|
||||||
|
0x15, 0x00, // Logical Minimum (0)
|
||||||
|
0x25, 0xE7, // Logical maximum (231)
|
||||||
|
|
||||||
|
0x19, 0x00, // Usage Minimum(0)
|
||||||
|
0x29, 0xE7, // Usage Maximum(231)
|
||||||
|
0x81, 0x00, // Input (Data, Array)
|
||||||
|
|
||||||
|
0xc0, // END_COLLECTION
|
||||||
|
};
|
||||||
|
|
21
requests.h
21
requests.h
|
@ -2,6 +2,7 @@
|
||||||
#define _gcn64_requests_h__
|
#define _gcn64_requests_h__
|
||||||
|
|
||||||
/* Commands */
|
/* Commands */
|
||||||
|
#define RQ_GCN64_ECHO 0x00
|
||||||
#define RQ_GCN64_SET_CONFIG_PARAM 0x01
|
#define RQ_GCN64_SET_CONFIG_PARAM 0x01
|
||||||
#define RQ_GCN64_GET_CONFIG_PARAM 0x02
|
#define RQ_GCN64_GET_CONFIG_PARAM 0x02
|
||||||
#define RQ_GCN64_SUSPEND_POLLING 0x03
|
#define RQ_GCN64_SUSPEND_POLLING 0x03
|
||||||
|
@ -10,11 +11,27 @@
|
||||||
#define RQ_GCN64_GET_CONTROLLER_TYPE 0x06
|
#define RQ_GCN64_GET_CONTROLLER_TYPE 0x06
|
||||||
#define RQ_GCN64_SET_VIBRATION 0x07
|
#define RQ_GCN64_SET_VIBRATION 0x07
|
||||||
#define RQ_GCN64_RAW_SI_COMMAND 0x80
|
#define RQ_GCN64_RAW_SI_COMMAND 0x80
|
||||||
|
#define RQ_GCN64_BLOCK_IO 0x81
|
||||||
|
#define RQ_RNT_GET_SUPPORTED_REQUESTS 0xF0
|
||||||
|
#define RQ_RNT_GET_SUPPORTED_MODES 0xF1
|
||||||
|
#define RQ_RNT_GET_SUPPORTED_CFG_PARAMS 0xF2
|
||||||
|
#define RQ_RNT_RESET_FIRMWARE 0xFE
|
||||||
#define RQ_GCN64_JUMP_TO_BOOTLOADER 0xFF
|
#define RQ_GCN64_JUMP_TO_BOOTLOADER 0xFF
|
||||||
|
|
||||||
/* Configuration parameters and constants */
|
/* Configuration parameters and constants */
|
||||||
#define CFG_PARAM_MODE 0x00
|
#define CFG_PARAM_MODE 0x00
|
||||||
|
|
||||||
|
/* Values for mode */
|
||||||
#define CFG_MODE_STANDARD 0x00
|
#define CFG_MODE_STANDARD 0x00
|
||||||
|
#define CFG_MODE_N64_ONLY 0x01
|
||||||
|
#define CFG_MODE_GC_ONLY 0x02
|
||||||
|
|
||||||
|
#define CFG_MODE_2P_STANDARD 0x10
|
||||||
|
#define CFG_MODE_2P_N64_ONLY 0x11
|
||||||
|
#define CFG_MODE_2P_GC_ONLY 0x12
|
||||||
|
#define CFG_MODE_KEYBOARD 0x13
|
||||||
|
#define CFG_MODE_KB_AND_JS 0x14
|
||||||
|
#define CFG_MODE_KEYBOARD_2 0x15
|
||||||
|
|
||||||
#define CFG_PARAM_SERIAL 0x01
|
#define CFG_PARAM_SERIAL 0x01
|
||||||
|
|
||||||
|
@ -28,6 +45,10 @@
|
||||||
#define CFG_PARAM_GC_CSTICK_SQUARE 0x22 // Not implemented
|
#define CFG_PARAM_GC_CSTICK_SQUARE 0x22 // Not implemented
|
||||||
#define CFG_PARAM_FULL_SLIDERS 0x23
|
#define CFG_PARAM_FULL_SLIDERS 0x23
|
||||||
#define CFG_PARAM_INVERT_TRIG 0x24
|
#define CFG_PARAM_INVERT_TRIG 0x24
|
||||||
|
#define CFG_PARAM_TRIGGERS_AS_BUTTONS 0x25
|
||||||
|
|
||||||
|
#define CFG_PARAM_DPAD_AS_AXES 0x31
|
||||||
|
#define CFG_PARAM_DISABLE_ANALOG_TRIGGERS 0x32
|
||||||
|
#define CFG_PARAM_SWAP_STICK_AND_DPAD 0x34
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1 +1,21 @@
|
||||||
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="0017", MODE="0664", GROUP="plugdev"
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="0017", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="001d", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="0020", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="0021", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="0022", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="0030", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="0031", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="0032", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="0033", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="0034", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="0035", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="0036", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="0037", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="0038", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="0039", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="003A", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="003B", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="003C", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="003D", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="003E", MODE="0664", GROUP="plugdev"
|
||||||
|
SUBSYSTEM=="hidraw", ATTRS{idVendor}=="289b", ATTRS{idProduct}=="003F", MODE="0664", GROUP="plugdev"
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
echo "Syntax: ./start.sh CPU"
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
CPU=$1
|
||||||
|
|
||||||
|
echo "Polling for chip..."
|
||||||
|
while true; do
|
||||||
|
dfu-programmer $1 start
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "Chip found. Started."
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <util/atomic.h>
|
||||||
|
#include "stkchk.h"
|
||||||
|
|
||||||
|
extern uint16_t __stack;
|
||||||
|
extern uint16_t _end;
|
||||||
|
|
||||||
|
/** Write a canary at the end of the stack. */
|
||||||
|
void stkchk_init(void)
|
||||||
|
{
|
||||||
|
*((&_end)-1) = 0xDEAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check if the canary is still alive.
|
||||||
|
*
|
||||||
|
* Call this perdiocally to check if the
|
||||||
|
* stack grew too large.
|
||||||
|
**/
|
||||||
|
char stkchk_verify(void)
|
||||||
|
{
|
||||||
|
if (*((&_end)-1) != 0xDEAD) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* In order to get an approximate idea of how
|
||||||
|
* much stack you are using, this can be called
|
||||||
|
* at strategic places where you know the
|
||||||
|
* call stack is deep or where large automatic
|
||||||
|
* buffers are used.
|
||||||
|
*/
|
||||||
|
#ifdef STKCHK_WITH_STATUS_CHECK
|
||||||
|
void stkchk(const char *fname)
|
||||||
|
{
|
||||||
|
static int max_usage = 0;
|
||||||
|
uint16_t end = ((uint16_t)&_end);
|
||||||
|
uint16_t s_top = ((uint16_t)&__stack);
|
||||||
|
uint16_t s_cur = SPL | SPH<<8;
|
||||||
|
uint16_t used, s_size;
|
||||||
|
uint8_t grew = 0;
|
||||||
|
|
||||||
|
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
|
||||||
|
{
|
||||||
|
used = s_top - s_cur;
|
||||||
|
s_size = s_top - end;
|
||||||
|
if (used > max_usage) {
|
||||||
|
max_usage = used;
|
||||||
|
grew = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grew) {
|
||||||
|
printf("[stkchk] %s: %d/%d\r\n", fname, used, s_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -0,0 +1,16 @@
|
||||||
|
#ifndef _stkchk_h__
|
||||||
|
#define _stkchk_h__
|
||||||
|
|
||||||
|
#undef STKCHK_WITH_STATUS_CHECK
|
||||||
|
|
||||||
|
#ifdef STKCHK_WITH_STATUS_CHECK
|
||||||
|
#define stkcheck() stkchk(__FUNCTION__);
|
||||||
|
void stkchk(const char *label);
|
||||||
|
#else
|
||||||
|
#define stkcheck()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void stkchk_init(void);
|
||||||
|
char stkchk_verify(void);
|
||||||
|
|
||||||
|
#endif // _stkchk_h__
|
|
@ -0,0 +1 @@
|
||||||
|
gcn64ctl
|
2
usart1.c
2
usart1.c
|
@ -1,5 +1,5 @@
|
||||||
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
||||||
Copyright (C) 2007-2013 Raphael Assenat <raph@raphnet.net>
|
Copyright (C) 2007-2016 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
332
usb.c
332
usb.c
|
@ -1,5 +1,5 @@
|
||||||
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
||||||
Copyright (C) 2007-2013 Raphael Assenat <raph@raphnet.net>
|
Copyright (C) 2007-2021 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -24,17 +24,23 @@
|
||||||
|
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
|
|
||||||
|
#undef VERBOSE
|
||||||
|
|
||||||
#define STATE_POWERED 0
|
#define STATE_POWERED 0
|
||||||
#define STATE_DEFAULT 1
|
#define STATE_DEFAULT 1
|
||||||
#define STATE_ADDRESS 2
|
#define STATE_ADDRESS 2
|
||||||
#define STATE_CONFIGURED 3
|
#define STATE_CONFIGURED 3
|
||||||
|
|
||||||
static volatile uint8_t g_usb_suspend;
|
static volatile uint8_t g_usb_suspend;
|
||||||
static uint8_t g_ep0_buf[64];
|
//static uint8_t g_ep0_buf[64];
|
||||||
static uint8_t g_device_state = STATE_DEFAULT;
|
static uint8_t g_device_state = STATE_DEFAULT;
|
||||||
static uint8_t g_current_config;
|
static uint8_t g_current_config;
|
||||||
static void *interrupt_data;
|
static void *interrupt_data;
|
||||||
static volatile int interrupt_data_len = -1;
|
static volatile int interrupt_data_len = -1;
|
||||||
|
static void *interrupt_data2;
|
||||||
|
static volatile int interrupt_data_len2 = -1;
|
||||||
|
static void *interrupt_data3;
|
||||||
|
static volatile int interrupt_data_len3 = -1;
|
||||||
|
|
||||||
#define CONTROL_WRITE_BUFSIZE 64
|
#define CONTROL_WRITE_BUFSIZE 64
|
||||||
static struct usb_request control_write_rq;
|
static struct usb_request control_write_rq;
|
||||||
|
@ -62,8 +68,28 @@ static int wcslen(const wchar_t *str)
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setupEndpoints(void)
|
/** Return the values for the UECFG1X register
|
||||||
|
*
|
||||||
|
* \return The EPSIZE bits if supported, 0xFF if invalid.
|
||||||
|
**/
|
||||||
|
static uint8_t getEPsizebits(int epsize)
|
||||||
{
|
{
|
||||||
|
switch(epsize)
|
||||||
|
{
|
||||||
|
case 64: return (1<<EPSIZE0)|(1<<EPSIZE1);
|
||||||
|
case 32: return (1<<EPSIZE1);
|
||||||
|
case 16: return (1<<EPSIZE0);
|
||||||
|
case 8: return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setupEndpoints()
|
||||||
|
{
|
||||||
|
uint8_t epsize;
|
||||||
|
int i;
|
||||||
|
|
||||||
/*** EP0 ***/
|
/*** EP0 ***/
|
||||||
|
|
||||||
// Order from figure 23-2
|
// Order from figure 23-2
|
||||||
|
@ -72,7 +98,8 @@ static void setupEndpoints(void)
|
||||||
UECONX = 1<<EPEN; // activate endpoint
|
UECONX = 1<<EPEN; // activate endpoint
|
||||||
UECFG0X = 0; // Control OUT
|
UECFG0X = 0; // Control OUT
|
||||||
UEIENX = (1<<RXSTPE) | (1<<RXOUTE) | (1<<NAKINE); /* | (1<<STALLEDE) | (1<<NAKOUTE) | (1<<TXINE) | (1<<RXOUTE) */;
|
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
|
epsize = getEPsizebits(64);
|
||||||
|
UECFG1X |= epsize|(1<<ALLOC); // 64 bytes, one bank, and allocate
|
||||||
UEINTX = 0;
|
UEINTX = 0;
|
||||||
|
|
||||||
if (!(UESTA0X & (1<<CFGOK))) {
|
if (!(UESTA0X & (1<<CFGOK))) {
|
||||||
|
@ -81,21 +108,57 @@ static void setupEndpoints(void)
|
||||||
}
|
}
|
||||||
// printf_P("ok\r\n");
|
// printf_P("ok\r\n");
|
||||||
|
|
||||||
/*** EP1 ***/
|
for (i=0; i<g_params->n_hid_interfaces; i++) {
|
||||||
UENUM = 0x01; // select endpoint
|
UENUM = 0x01 + i; // select endpoint
|
||||||
|
|
||||||
UECONX = 1<<EPEN; // activate endpoint
|
UECONX = 1<<EPEN; // activate endpoint
|
||||||
UECFG0X = (3<<6) | (1<<EPDIR); // Interrupt IN
|
UECFG0X = (3<<6) | (1<<EPDIR); // Interrupt IN
|
||||||
UEIENX = (1<<TXINE);
|
UEIENX = (1<<TXINE);
|
||||||
UECFG1X |= (1<<EPSIZE0)|(1<<EPSIZE1)|(1<<ALLOC); // 64 bytes, one bank, and allocate
|
epsize = getEPsizebits(g_params->hid_params[i].endpoint_size);
|
||||||
UEINTX = 0;
|
if (epsize == 0xff) {
|
||||||
|
printf_P(PSTR("Invalid ep size\r\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UECFG1X = epsize|(1<<ALLOC); // one bank, and allocate
|
||||||
|
UEINTX = 0;
|
||||||
|
|
||||||
if (!(UESTA0X & (1<<CFGOK))) {
|
if (!(UESTA0X & (1<<CFGOK))) {
|
||||||
printf_P(PSTR("CFG EP1 fail\r\n"));
|
printf_P(PSTR("CFG EP fail\r\n"));
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Requires UENUM already set
|
||||||
|
static uint16_t getEPlen(void)
|
||||||
|
{
|
||||||
|
#ifdef UEBCHX
|
||||||
|
return UEBCLX | (UEBCHX << 8);
|
||||||
|
#else
|
||||||
|
return UEBCLX;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requires UENUM already set
|
||||||
|
// writes up to n bytes
|
||||||
|
static uint16_t readEP2buf_n(void *dstbuf, int n)
|
||||||
|
{
|
||||||
|
uint16_t len;
|
||||||
|
int i;
|
||||||
|
uint8_t *dst = dstbuf;
|
||||||
|
#ifdef UEBCHX
|
||||||
|
len = UEBCLX | (UEBCHX << 8);
|
||||||
|
#else
|
||||||
|
len = UEBCLX;
|
||||||
|
#endif
|
||||||
|
for (i=0; i<len && i<n; i++) {
|
||||||
|
*dst = UEDATX;
|
||||||
|
dst++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
// Requires UENUM already set
|
// Requires UENUM already set
|
||||||
static uint16_t readEP2buf(uint8_t *dst)
|
static uint16_t readEP2buf(uint8_t *dst)
|
||||||
{
|
{
|
||||||
|
@ -141,11 +204,40 @@ static void buf2EP(uint8_t epnum, const void *src, uint16_t len, uint16_t max_le
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
static void longDescriptorHelper(const uint8_t *data, uint16_t len, uint16_t rq_len, uint8_t progmem)
|
||||||
|
{
|
||||||
|
uint16_t todo = rq_len > len ? len : rq_len;
|
||||||
|
uint16_t pos = 0;
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
if (todo > 64) {
|
||||||
|
buf2EP(0, data+pos, 64, 64, progmem);
|
||||||
|
UEINTX &= ~(1<<TXINI);
|
||||||
|
pos += 64;
|
||||||
|
todo -= 64;
|
||||||
|
while (!(UEINTX & (1<<TXINI)));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buf2EP(0, data+pos, todo,
|
||||||
|
todo,
|
||||||
|
progmem);
|
||||||
|
UEINTX &= ~(1<<TXINI);
|
||||||
|
while (!(UEINTX & (1<<TXINI)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void handleSetupPacket(struct usb_request *rq)
|
static void handleSetupPacket(struct usb_request *rq)
|
||||||
{
|
{
|
||||||
char unhandled = 0;
|
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);
|
#ifdef VERBOSE
|
||||||
|
printf_P(PSTR("t: %02x, rq: 0x%02x, val: %04x, l: %d\r\n"), rq->bmRequestType, rq->bRequest, rq->wValue, rq->wLength);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (USB_RQT_IS_HOST_TO_DEVICE(rq->bmRequestType))
|
if (USB_RQT_IS_HOST_TO_DEVICE(rq->bmRequestType))
|
||||||
{
|
{
|
||||||
|
@ -160,7 +252,9 @@ static void handleSetupPacket(struct usb_request *rq)
|
||||||
UEINTX &= ~(1<<TXINI);
|
UEINTX &= ~(1<<TXINI);
|
||||||
while (!(UEINTX & (1<<TXINI)));
|
while (!(UEINTX & (1<<TXINI)));
|
||||||
UDADDR |= (1<<ADDEN);
|
UDADDR |= (1<<ADDEN);
|
||||||
|
#ifdef VERBOSE
|
||||||
printf_P(PSTR("Addr: %d\r\n"), rq->wValue);
|
printf_P(PSTR("Addr: %d\r\n"), rq->wValue);
|
||||||
|
#endif
|
||||||
if (!rq->wValue) {
|
if (!rq->wValue) {
|
||||||
g_device_state = STATE_DEFAULT;
|
g_device_state = STATE_DEFAULT;
|
||||||
} else {
|
} else {
|
||||||
|
@ -177,7 +271,9 @@ static void handleSetupPacket(struct usb_request *rq)
|
||||||
}
|
}
|
||||||
while (!(UEINTX & (1<<TXINI)));
|
while (!(UEINTX & (1<<TXINI)));
|
||||||
UEINTX &= ~(1<<TXINI);
|
UEINTX &= ~(1<<TXINI);
|
||||||
|
#ifdef VERBOSE
|
||||||
printf_P(PSTR("Configured: %d\r\n"), g_current_config);
|
printf_P(PSTR("Configured: %d\r\n"), g_current_config);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -191,10 +287,10 @@ static void handleSetupPacket(struct usb_request *rq)
|
||||||
case USB_RQT_CLASS:
|
case USB_RQT_CLASS:
|
||||||
switch(rq->bRequest)
|
switch(rq->bRequest)
|
||||||
{
|
{
|
||||||
case HID_CLSRQ_SET_IDLE:
|
// case HID_CLSRQ_SET_IDLE:
|
||||||
while (!(UEINTX & (1<<TXINI)));
|
// while (!(UEINTX & (1<<TXINI)));
|
||||||
UEINTX &= ~(1<<TXINI);
|
// UEINTX &= ~(1<<TXINI);
|
||||||
break;
|
// break;
|
||||||
case HID_CLSRQ_SET_REPORT:
|
case HID_CLSRQ_SET_REPORT:
|
||||||
while (!(UEINTX & (1<<TXINI)));
|
while (!(UEINTX & (1<<TXINI)));
|
||||||
UEINTX &= ~(1<<TXINI);
|
UEINTX &= ~(1<<TXINI);
|
||||||
|
@ -257,8 +353,8 @@ static void handleSetupPacket(struct usb_request *rq)
|
||||||
g_params->flags & USB_PARAM_FLAG_DEVDESC_PROGMEM);
|
g_params->flags & USB_PARAM_FLAG_DEVDESC_PROGMEM);
|
||||||
break;
|
break;
|
||||||
case CONFIGURATION_DESCRIPTOR:
|
case CONFIGURATION_DESCRIPTOR:
|
||||||
// Check index if more than 1 config
|
// Would need to check index if more than 1 configs...
|
||||||
buf2EP(0, (unsigned char*)g_params->configdesc, g_params->configdesc_ttllen,
|
longDescriptorHelper(g_params->configdesc, g_params->configdesc_ttllen,
|
||||||
rq->wLength, g_params->flags & USB_PARAM_FLAG_CONFDESC_PROGMEM);
|
rq->wLength, g_params->flags & USB_PARAM_FLAG_CONFDESC_PROGMEM);
|
||||||
break;
|
break;
|
||||||
case STRING_DESCRIPTOR:
|
case STRING_DESCRIPTOR:
|
||||||
|
@ -330,51 +426,17 @@ static void handleSetupPacket(struct usb_request *rq)
|
||||||
{
|
{
|
||||||
case REPORT_DESCRIPTOR:
|
case REPORT_DESCRIPTOR:
|
||||||
{
|
{
|
||||||
uint16_t rqlen = rq->wLength;
|
|
||||||
uint16_t todo = rqlen;
|
|
||||||
uint16_t pos = 0;
|
|
||||||
unsigned char *reportdesc;
|
|
||||||
|
|
||||||
// HID 1.1 : 7.1.1 Get_Descriptor request. wIndex is the interface number.
|
// HID 1.1 : 7.1.1 Get_Descriptor request. wIndex is the interface number.
|
||||||
//
|
//
|
||||||
if (rq->wIndex > g_params->n_hid_interfaces)
|
if (rq->wIndex > g_params->n_hid_interfaces) {
|
||||||
|
unhandled = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
reportdesc = (unsigned char*)g_params->hid_params[rq->wIndex].reportdesc;
|
|
||||||
if (rqlen > g_params->hid_params[rq->wIndex].reportdesc_len) {
|
|
||||||
// rqlen = g_params->hid_params[rq->wIndex].reportdesc_len;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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_P(PSTR("pos %d todo %d\r\n"), pos, todo);
|
|
||||||
if (todo > 64) {
|
|
||||||
buf2EP(0, reportdesc+pos, 64,
|
|
||||||
64,
|
|
||||||
g_params->flags & USB_PARAM_FLAG_REPORTDESC_PROGMEM);
|
|
||||||
UEINTX &= ~(1<<TXINI);
|
|
||||||
pos += 64;
|
|
||||||
todo -= 64;
|
|
||||||
while (!(UEINTX & (1<<TXINI)));
|
|
||||||
} else {
|
|
||||||
buf2EP(0, reportdesc+pos, todo,
|
|
||||||
todo,
|
|
||||||
g_params->flags & USB_PARAM_FLAG_REPORTDESC_PROGMEM);
|
|
||||||
UEINTX &= ~(1<<TXINI);
|
|
||||||
while (!(UEINTX & (1<<TXINI)));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
if (UEINTX & (1<<RXOUTI)) {
|
|
||||||
UEINTX &= ~(1<<RXOUTI); // ACK
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
longDescriptorHelper(g_params->hid_params[rq->wIndex].reportdesc,
|
||||||
|
g_params->hid_params[rq->wIndex].reportdesc_len,
|
||||||
|
rq->wLength,
|
||||||
|
g_params->flags & USB_PARAM_FLAG_REPORTDESC_PROGMEM);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -400,7 +462,9 @@ static void handleSetupPacket(struct usb_request *rq)
|
||||||
if (g_params->hid_params[rq->wIndex].getReport) {
|
if (g_params->hid_params[rq->wIndex].getReport) {
|
||||||
const unsigned char *data;
|
const unsigned char *data;
|
||||||
uint16_t len;
|
uint16_t len;
|
||||||
len = g_params->hid_params[rq->wIndex].getReport(rq, &data);
|
len = g_params->hid_params[rq->wIndex].getReport(
|
||||||
|
g_params->hid_params[rq->wIndex].ctx,
|
||||||
|
rq, &data);
|
||||||
if (len) {
|
if (len) {
|
||||||
buf2EP(0, data, len, rq->wLength, 0);
|
buf2EP(0, data, len, rq->wLength, 0);
|
||||||
}
|
}
|
||||||
|
@ -460,7 +524,7 @@ static void handleSetupPacket(struct usb_request *rq)
|
||||||
} // IS DEVICE-TO-HOST
|
} // IS DEVICE-TO-HOST
|
||||||
|
|
||||||
if (unhandled) {
|
if (unhandled) {
|
||||||
// printf_P(PSTR("t: %02x, rq: 0x%02x, val: %04x\r\n"), rq->bmRequestType, rq->bRequest, rq->wValue);
|
printf_P(PSTR("t: %02x, rq: 0x%02x, val: %04x\r\n"), rq->bmRequestType, rq->bRequest, rq->wValue);
|
||||||
UECONX |= (1<<STALLRQ);
|
UECONX |= (1<<STALLRQ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -479,7 +543,9 @@ static void handleDataPacket(const struct usb_request *rq, uint8_t *dat, uint16_
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (g_params->hid_params[rq->wIndex].setReport) {
|
if (g_params->hid_params[rq->wIndex].setReport) {
|
||||||
if (g_params->hid_params[rq->wIndex].setReport(rq, dat, len)) {
|
if (g_params->hid_params[rq->wIndex].setReport(
|
||||||
|
g_params->hid_params[rq->wIndex].ctx,
|
||||||
|
rq, dat, len)) {
|
||||||
UECONX |= (1<<STALLRQ);
|
UECONX |= (1<<STALLRQ);
|
||||||
} else {
|
} else {
|
||||||
// xmit status
|
// xmit status
|
||||||
|
@ -506,7 +572,9 @@ ISR(USB_GEN_vect)
|
||||||
UDINT &= ~(1<<SUSPI);
|
UDINT &= ~(1<<SUSPI);
|
||||||
g_usb_suspend = 1;
|
g_usb_suspend = 1;
|
||||||
UDIEN |= (1<<WAKEUPE);
|
UDIEN |= (1<<WAKEUPE);
|
||||||
// printf_P(PSTR("SUSPI\r\n"));
|
#ifdef VERBOSE
|
||||||
|
printf_P(PSTR("SUSPI\r\n"));
|
||||||
|
#endif
|
||||||
// CPU could now be put in low power mode. Later,
|
// CPU could now be put in low power mode. Later,
|
||||||
// WAKEUPI would wake it up.
|
// WAKEUPI would wake it up.
|
||||||
}
|
}
|
||||||
|
@ -516,13 +584,17 @@ ISR(USB_GEN_vect)
|
||||||
UDINT &= ~(1<<WAKEUPE);
|
UDINT &= ~(1<<WAKEUPE);
|
||||||
if (g_usb_suspend) {
|
if (g_usb_suspend) {
|
||||||
g_usb_suspend = 0;
|
g_usb_suspend = 0;
|
||||||
// printf_P(PSTR("WAKEUPI\r\n"));
|
#ifdef VERBOSE
|
||||||
|
printf_P(PSTR("WAKEUPI\r\n"));
|
||||||
|
#endif
|
||||||
UDIEN &= ~(1<<WAKEUPE); // woke up. Not needed anymore.
|
UDIEN &= ~(1<<WAKEUPE); // woke up. Not needed anymore.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i & (1<<EORSTI)) {
|
if (i & (1<<EORSTI)) {
|
||||||
// printf_P(PSTR("EORSTI\r\n"));
|
#ifdef VERBOSE
|
||||||
|
printf_P(PSTR("EORSTI\r\n"));
|
||||||
|
#endif
|
||||||
g_usb_suspend = 0;
|
g_usb_suspend = 0;
|
||||||
setupEndpoints();
|
setupEndpoints();
|
||||||
UDINT &= ~(1<<EORSTI);
|
UDINT &= ~(1<<EORSTI);
|
||||||
|
@ -530,17 +602,45 @@ ISR(USB_GEN_vect)
|
||||||
|
|
||||||
if (i & (1<<SOFI)) {
|
if (i & (1<<SOFI)) {
|
||||||
UDINT &= ~(1<<SOFI);
|
UDINT &= ~(1<<SOFI);
|
||||||
// printf_P(PSTR("SOFI\r\n"));
|
#ifdef VERBOSE
|
||||||
|
printf_P(PSTR("SOFI\r\n"));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i & (1<<EORSMI)) {
|
if (i & (1<<EORSMI)) {
|
||||||
UDINT &= ~(1<<EORSMI);
|
UDINT &= ~(1<<EORSMI);
|
||||||
// printf_P(PSTR("EORSMI\r\n"));
|
#ifdef VERBOSE
|
||||||
|
printf_P(PSTR("EORSMI\r\n"));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i & (1<<UPRSMI)) {
|
if (i & (1<<UPRSMI)) {
|
||||||
UDINT &= ~(1<<UPRSMI);
|
UDINT &= ~(1<<UPRSMI);
|
||||||
|
#ifdef VERBOSE
|
||||||
printf_P(PSTR("UPRSMI\r\n"));
|
printf_P(PSTR("UPRSMI\r\n"));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_interrupt_xmit(uint8_t ep, void **interrupt_data, volatile int *interrupt_data_len)
|
||||||
|
{
|
||||||
|
uint8_t i;
|
||||||
|
|
||||||
|
UENUM = ep;
|
||||||
|
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(ep, (void*)*interrupt_data, *interrupt_data_len, *interrupt_data_len, 0);
|
||||||
|
*interrupt_data = NULL;
|
||||||
|
*interrupt_data_len = -1;
|
||||||
|
UEINTX &= ~(1<<FIFOCON);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,23 +657,26 @@ ISR(USB_COM_vect)
|
||||||
i = UEINTX;
|
i = UEINTX;
|
||||||
|
|
||||||
if (i & (1<<RXSTPI)) {
|
if (i & (1<<RXSTPI)) {
|
||||||
// printf_P(PSTR("RXSTPI\r\n"));
|
struct usb_request rq;
|
||||||
readEP2buf(g_ep0_buf);
|
// readEP2buf(g_ep0_buf);
|
||||||
|
readEP2buf_n(&rq, sizeof(struct usb_request));
|
||||||
UEINTX &= ~(1<<RXSTPI);
|
UEINTX &= ~(1<<RXSTPI);
|
||||||
handleSetupPacket((struct usb_request *)g_ep0_buf);
|
handleSetupPacket(&rq);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i & (1<<RXOUTI)) {
|
if (i & (1<<RXOUTI)) {
|
||||||
uint16_t len;
|
uint16_t len;
|
||||||
len = readEP2buf(g_ep0_buf);
|
|
||||||
UEINTX &= ~(1<<RXOUTI);
|
len = getEPlen();
|
||||||
|
|
||||||
if (control_write_in_progress) {
|
if (control_write_in_progress) {
|
||||||
// printf_P(PSTR("chunk: %d\r\n"), len);
|
|
||||||
if (control_write_len + len < CONTROL_WRITE_BUFSIZE) {
|
if (control_write_len + len < CONTROL_WRITE_BUFSIZE) {
|
||||||
memcpy(control_write_buf + control_write_len, g_ep0_buf, len);
|
readEP2buf(control_write_buf + control_write_len);
|
||||||
control_write_len += len;
|
control_write_len += len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UEINTX &= ~(1<<RXOUTI);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i & (1<<NAKINI)) {
|
if (i & (1<<NAKINI)) {
|
||||||
|
@ -587,22 +690,15 @@ ISR(USB_COM_vect)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ueint & (1<<EPINT1)) {
|
if (ueint & (1<<EPINT1)) {
|
||||||
UENUM = 1;
|
handle_interrupt_xmit(1, &interrupt_data, &interrupt_data_len);
|
||||||
i = UEINTX;
|
}
|
||||||
|
|
||||||
if (i & (1<<TXINI)) {
|
if (ueint & (1<<EPINT2)) {
|
||||||
if (interrupt_data_len < 0) {
|
handle_interrupt_xmit(2, &interrupt_data2, &interrupt_data_len2);
|
||||||
// If there's not already data waiting to be
|
}
|
||||||
// sent, disable the interrupt.
|
|
||||||
UEIENX &= ~(1<<TXINE);
|
if (ueint & (1<<EPINT3)) {
|
||||||
} else {
|
handle_interrupt_xmit(3, &interrupt_data3, &interrupt_data_len3);
|
||||||
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 0
|
||||||
|
@ -613,12 +709,56 @@ ISR(USB_COM_vect)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
char usb_interruptReady(void)
|
char usb_interruptReady_ep3(void)
|
||||||
|
{
|
||||||
|
return interrupt_data_len3 == -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void usb_interruptSend_ep3(void *data, int len)
|
||||||
|
{
|
||||||
|
uint8_t sreg = SREG;
|
||||||
|
|
||||||
|
while (interrupt_data_len3 != -1) { }
|
||||||
|
|
||||||
|
cli();
|
||||||
|
|
||||||
|
interrupt_data3 = data;
|
||||||
|
interrupt_data_len3 = len;
|
||||||
|
|
||||||
|
UENUM = 3;
|
||||||
|
UEIENX |= (1<<TXINE);
|
||||||
|
|
||||||
|
SREG = sreg;
|
||||||
|
}
|
||||||
|
|
||||||
|
char usb_interruptReady_ep2(void)
|
||||||
|
{
|
||||||
|
return interrupt_data_len2 == -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void usb_interruptSend_ep2(void *data, int len)
|
||||||
|
{
|
||||||
|
uint8_t sreg = SREG;
|
||||||
|
|
||||||
|
while (interrupt_data_len2 != -1) { }
|
||||||
|
|
||||||
|
cli();
|
||||||
|
|
||||||
|
interrupt_data2 = data;
|
||||||
|
interrupt_data_len2 = len;
|
||||||
|
|
||||||
|
UENUM = 2;
|
||||||
|
UEIENX |= (1<<TXINE);
|
||||||
|
|
||||||
|
SREG = sreg;
|
||||||
|
}
|
||||||
|
|
||||||
|
char usb_interruptReady_ep1(void)
|
||||||
{
|
{
|
||||||
return interrupt_data_len == -1;
|
return interrupt_data_len == -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void usb_interruptSend(void *data, int len)
|
void usb_interruptSend_ep1(void *data, int len)
|
||||||
{
|
{
|
||||||
uint8_t sreg = SREG;
|
uint8_t sreg = SREG;
|
||||||
|
|
||||||
|
@ -664,7 +804,9 @@ void usb_doTasks(void)
|
||||||
#ifdef USBSTA
|
#ifdef USBSTA
|
||||||
if (USBSTA & (1<<VBUS)) {
|
if (USBSTA & (1<<VBUS)) {
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef VERBOSE
|
||||||
printf_P(PSTR("ATTACH\r\n"));
|
printf_P(PSTR("ATTACH\r\n"));
|
||||||
|
#endif
|
||||||
UDCON &= ~(1<<DETACH); // clear DETACH bit
|
UDCON &= ~(1<<DETACH); // clear DETACH bit
|
||||||
usb_state = STATE_ATTACHED;
|
usb_state = STATE_ATTACHED;
|
||||||
#ifdef USBSTA
|
#ifdef USBSTA
|
||||||
|
@ -676,7 +818,7 @@ void usb_doTasks(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__AVR_ATmega32U2__)
|
#if defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega32U4__)
|
||||||
|
|
||||||
/* Atmega32u2 datasheet 8.11.6, PLLCSR.
|
/* Atmega32u2 datasheet 8.11.6, PLLCSR.
|
||||||
* But register summary says PLLP0... */
|
* But register summary says PLLP0... */
|
||||||
|
@ -742,8 +884,10 @@ void usb_init(const struct usb_parameters *params)
|
||||||
USBCON |= (1<<FRZCLK); // initial value
|
USBCON |= (1<<FRZCLK); // initial value
|
||||||
#ifdef UHWCON
|
#ifdef UHWCON
|
||||||
UHWCON |= (1<<UVREGE); // Enable USB pad regulator
|
UHWCON |= (1<<UVREGE); // Enable USB pad regulator
|
||||||
|
#if defined(UIDE) && defined(UIMOD)
|
||||||
UHWCON &= ~(1<<UIDE);
|
UHWCON &= ~(1<<UIDE);
|
||||||
UHWCON |= (1<UIMOD);
|
UHWCON |= (1<UIMOD);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef UPOE
|
#ifdef UPOE
|
||||||
|
|
18
usb.h
18
usb.h
|
@ -169,15 +169,18 @@ struct usb_hid_descriptor {
|
||||||
#define USB_PARAM_FLAG_CONFDESC_PROGMEM 2
|
#define USB_PARAM_FLAG_CONFDESC_PROGMEM 2
|
||||||
#define USB_PARAM_FLAG_REPORTDESC_PROGMEM 4
|
#define USB_PARAM_FLAG_REPORTDESC_PROGMEM 4
|
||||||
|
|
||||||
#define MAX_HID_INTERFACES 2
|
#define MAX_HID_INTERFACES 5
|
||||||
|
|
||||||
struct usb_hid_parameters {
|
struct usb_hid_parameters {
|
||||||
uint16_t reportdesc_len;
|
uint16_t reportdesc_len;
|
||||||
const unsigned char *reportdesc;
|
const unsigned char *reportdesc;
|
||||||
|
|
||||||
|
int endpoint_size;
|
||||||
|
|
||||||
// Warning: Called from interrupt handler. Implement accordingly.
|
// Warning: Called from interrupt handler. Implement accordingly.
|
||||||
uint16_t (*getReport)(struct usb_request *rq, const uint8_t **dat);
|
void *ctx;
|
||||||
uint8_t (*setReport)(const struct usb_request *rq, const uint8_t *dat, uint16_t len);
|
uint16_t (*getReport)(void *ctx, struct usb_request *rq, const uint8_t **dat);
|
||||||
|
uint8_t (*setReport)(void *ctx, const struct usb_request *rq, const uint8_t *dat, uint16_t len);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct usb_parameters {
|
struct usb_parameters {
|
||||||
|
@ -198,8 +201,13 @@ struct usb_parameters {
|
||||||
struct usb_hid_parameters hid_params[MAX_HID_INTERFACES];
|
struct usb_hid_parameters hid_params[MAX_HID_INTERFACES];
|
||||||
};
|
};
|
||||||
|
|
||||||
char usb_interruptReady(void);
|
char usb_interruptReady_ep1(void);
|
||||||
void usb_interruptSend(void *data, int len); // EP1
|
void usb_interruptSend_ep1(void *data, int len);
|
||||||
|
char usb_interruptReady_ep2(void);
|
||||||
|
void usb_interruptSend_ep2(void *data, int len);
|
||||||
|
char usb_interruptReady_ep3(void);
|
||||||
|
void usb_interruptSend_ep3(void *data, int len);
|
||||||
|
|
||||||
void usb_init(const struct usb_parameters *params);
|
void usb_init(const struct usb_parameters *params);
|
||||||
void usb_doTasks(void);
|
void usb_doTasks(void);
|
||||||
void usb_shutdown(void);
|
void usb_shutdown(void);
|
||||||
|
|
331
usbpad.c
331
usbpad.c
|
@ -16,15 +16,20 @@
|
||||||
*/
|
*/
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
#include "gamepads.h"
|
#include "gamepads.h"
|
||||||
#include "usbpad.h"
|
#include "usbpad.h"
|
||||||
#include "mappings.h"
|
#include "mappings.h"
|
||||||
#include "eeprom.h"
|
#include "eeprom.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "hid_keycodes.h"
|
||||||
|
#include "gc_kb.h"
|
||||||
|
|
||||||
|
#define STICK_TO_BTN_THRESHOLD 40
|
||||||
|
|
||||||
#define REPORT_ID 1
|
#define REPORT_ID 1
|
||||||
#define REPORT_SIZE 15
|
|
||||||
|
|
||||||
// Output Report IDs for various functions
|
// Output Report IDs for various functions
|
||||||
#define REPORT_SET_EFFECT 0x01
|
#define REPORT_SET_EFFECT 0x01
|
||||||
|
@ -48,25 +53,34 @@
|
||||||
#define PID_SIMULTANEOUS_MAX 3
|
#define PID_SIMULTANEOUS_MAX 3
|
||||||
#define PID_BLOCK_LOAD_REPORT 2
|
#define PID_BLOCK_LOAD_REPORT 2
|
||||||
|
|
||||||
static volatile unsigned char gamepad_vibrate = 0; // output
|
#undef DEBUG
|
||||||
static unsigned char vibration_on = 0, force_vibrate = 0;
|
|
||||||
static unsigned char constant_force = 0;
|
|
||||||
static unsigned char periodic_magnitude = 0;
|
|
||||||
|
|
||||||
static unsigned char _FFB_effect_index;
|
#ifdef DEBUG
|
||||||
#define LOOP_MAX 0xFFFF
|
static void hexdump(const unsigned char *ptr, int len)
|
||||||
static unsigned int _loop_count;
|
|
||||||
|
|
||||||
static unsigned char gamepad_report0[REPORT_SIZE];
|
|
||||||
static unsigned char hid_report_data[8]; // Used for force feedback
|
|
||||||
|
|
||||||
void usbpad_init()
|
|
||||||
{
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i=0; i<len; i++) {
|
||||||
|
printf_P(PSTR("%02x "), ptr[i]);
|
||||||
|
}
|
||||||
|
printf_P(PSTR("\n"));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define printf_P(...)
|
||||||
|
#define hexdump(...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void buildIdleReport(unsigned char dstbuf[USBPAD_REPORT_SIZE]);
|
||||||
|
|
||||||
|
void usbpad_init(struct usbpad *pad)
|
||||||
|
{
|
||||||
|
memset(pad, 0, sizeof(struct usbpad));
|
||||||
|
buildIdleReport(pad->gamepad_report0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int usbpad_getReportSize(void)
|
int usbpad_getReportSize(void)
|
||||||
{
|
{
|
||||||
return REPORT_SIZE;
|
return USBPAD_REPORT_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int16_t minmax(int16_t input, int16_t min, int16_t max)
|
static int16_t minmax(int16_t input, int16_t min, int16_t max)
|
||||||
|
@ -85,7 +99,7 @@ static void btnsToReport(unsigned short buttons, unsigned char dstbuf[2])
|
||||||
dstbuf[1] = buttons >> 8;
|
dstbuf[1] = buttons >> 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void buildIdleReport(unsigned char dstbuf[REPORT_SIZE])
|
static void buildIdleReport(unsigned char dstbuf[USBPAD_REPORT_SIZE])
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
@ -102,10 +116,23 @@ static void buildIdleReport(unsigned char dstbuf[REPORT_SIZE])
|
||||||
dstbuf[14] = 0;
|
dstbuf[14] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void buildReportFromGC(const gc_pad_data *gc_data, unsigned char dstbuf[REPORT_SIZE])
|
int usbpad_getReportSizeKB(void)
|
||||||
|
{
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void buildIdleReportKB(unsigned char dstbuf[USBPAD_REPORT_SIZE])
|
||||||
|
{
|
||||||
|
dstbuf[0] = HID_KB_NOEVENT;
|
||||||
|
dstbuf[1] = HID_KB_NOEVENT;
|
||||||
|
dstbuf[2] = HID_KB_NOEVENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void buildReportFromGC(const gc_pad_data *gc_data, unsigned char dstbuf[USBPAD_REPORT_SIZE])
|
||||||
{
|
{
|
||||||
int16_t xval,yval,cxval,cyval,ltrig,rtrig;
|
int16_t xval,yval,cxval,cyval,ltrig,rtrig;
|
||||||
uint16_t buttons;
|
uint16_t buttons;
|
||||||
|
uint16_t gcbuttons = gc_data->buttons;
|
||||||
|
|
||||||
/* Force official range */
|
/* Force official range */
|
||||||
xval = minmax(gc_data->x, -100, 100);
|
xval = minmax(gc_data->x, -100, 100);
|
||||||
|
@ -115,6 +142,24 @@ static void buildReportFromGC(const gc_pad_data *gc_data, unsigned char dstbuf[R
|
||||||
ltrig = gc_data->lt;
|
ltrig = gc_data->lt;
|
||||||
rtrig = gc_data->rt;
|
rtrig = gc_data->rt;
|
||||||
|
|
||||||
|
if (g_eeprom_data.cfg.flags & FLAG_SWAP_STICK_AND_DPAD) {
|
||||||
|
|
||||||
|
// Generate new D-Pad button status based on stick
|
||||||
|
gcbuttons &= ~(GC_BTN_DPAD_UP|GC_BTN_DPAD_DOWN|GC_BTN_DPAD_LEFT|GC_BTN_DPAD_RIGHT);
|
||||||
|
if (xval <= -STICK_TO_BTN_THRESHOLD) { gcbuttons |= GC_BTN_DPAD_LEFT; }
|
||||||
|
if (xval >= STICK_TO_BTN_THRESHOLD) { gcbuttons |= GC_BTN_DPAD_RIGHT; }
|
||||||
|
if (yval <= -STICK_TO_BTN_THRESHOLD) { gcbuttons |= GC_BTN_DPAD_DOWN; }
|
||||||
|
if (yval >= STICK_TO_BTN_THRESHOLD) { gcbuttons |= GC_BTN_DPAD_UP; }
|
||||||
|
|
||||||
|
// Generate new stick values based on button (use gc_data here)
|
||||||
|
xval = 0; yval = 0;
|
||||||
|
if (gc_data->buttons & GC_BTN_DPAD_UP) { yval = 100; }
|
||||||
|
if (gc_data->buttons & GC_BTN_DPAD_DOWN) { yval = -100; }
|
||||||
|
if (gc_data->buttons & GC_BTN_DPAD_LEFT) { xval = -100; }
|
||||||
|
if (gc_data->buttons & GC_BTN_DPAD_RIGHT) { xval = 100; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Scale -100 ... + 1000 to -16000 ... +16000 */
|
/* Scale -100 ... + 1000 to -16000 ... +16000 */
|
||||||
xval *= 160;
|
xval *= 160;
|
||||||
yval *= -160;
|
yval *= -160;
|
||||||
|
@ -122,25 +167,41 @@ static void buildReportFromGC(const gc_pad_data *gc_data, unsigned char dstbuf[R
|
||||||
cxval *= 160;
|
cxval *= 160;
|
||||||
cyval *= -160;
|
cyval *= -160;
|
||||||
|
|
||||||
if (g_eeprom_data.cfg.flags & FLAG_GC_FULL_SLIDERS) {
|
if (g_eeprom_data.cfg.flags & FLAG_GC_SLIDERS_AS_BUTTONS) {
|
||||||
int16_t lts = (int16_t)ltrig - 127;
|
/* In this mode, the sliders control buttons */
|
||||||
int16_t rts = (int16_t)rtrig - 127;
|
if (ltrig > 64)
|
||||||
lts *= 126;
|
gcbuttons |= GC_BTN_L;
|
||||||
ltrig = lts;
|
if (rtrig > 64)
|
||||||
rts *= 126;
|
gcbuttons |= GC_BTN_R;
|
||||||
rtrig = rts;
|
|
||||||
|
|
||||||
} else {
|
/* And the sliders analog values are fixed. */
|
||||||
/* Scale 0...255 to 0...16000 */
|
ltrig = rtrig = 0;
|
||||||
ltrig *= 63;
|
}
|
||||||
if (ltrig > 16000) ltrig=16000;
|
else {
|
||||||
rtrig *= 63;
|
if (g_eeprom_data.cfg.flags & FLAG_GC_FULL_SLIDERS) {
|
||||||
if (rtrig > 16000) rtrig=16000;
|
int16_t lts = (int16_t)ltrig - 127;
|
||||||
|
int16_t rts = (int16_t)rtrig - 127;
|
||||||
|
lts *= 126;
|
||||||
|
ltrig = lts;
|
||||||
|
rts *= 126;
|
||||||
|
rtrig = rts;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/* Scale 0...255 to 0...16000 */
|
||||||
|
ltrig *= 63;
|
||||||
|
if (ltrig > 16000) ltrig=16000;
|
||||||
|
rtrig *= 63;
|
||||||
|
if (rtrig > 16000) rtrig=16000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_eeprom_data.cfg.flags & FLAG_GC_INVERT_TRIGS) {
|
||||||
|
ltrig = -ltrig;
|
||||||
|
rtrig = -rtrig;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_eeprom_data.cfg.flags & FLAG_GC_INVERT_TRIGS) {
|
if (g_eeprom_data.cfg.flags & FLAG_DISABLE_ANALOG_TRIGGERS) {
|
||||||
ltrig = -ltrig;
|
ltrig = rtrig = 0;
|
||||||
rtrig = -rtrig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unsign for HID report */
|
/* Unsign for HID report */
|
||||||
|
@ -167,19 +228,37 @@ static void buildReportFromGC(const gc_pad_data *gc_data, unsigned char dstbuf[R
|
||||||
dstbuf[11] = ((uint8_t*)&rtrig)[0];
|
dstbuf[11] = ((uint8_t*)&rtrig)[0];
|
||||||
dstbuf[12] = ((uint8_t*)&rtrig)[1];
|
dstbuf[12] = ((uint8_t*)&rtrig)[1];
|
||||||
|
|
||||||
buttons = mappings_do(MAPPING_GAMECUBE_DEFAULT, gc_data->buttons);
|
buttons = mappings_do(MAPPING_GAMECUBE_DEFAULT, gcbuttons);
|
||||||
btnsToReport(buttons, dstbuf+13);
|
btnsToReport(buttons, dstbuf+13);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void buildReportFromN64(const n64_pad_data *n64_data, unsigned char dstbuf[REPORT_SIZE])
|
|
||||||
|
static void buildReportFromN64(const n64_pad_data *n64_data, unsigned char dstbuf[USBPAD_REPORT_SIZE])
|
||||||
{
|
{
|
||||||
int16_t xval, yval;
|
int16_t xval, yval;
|
||||||
uint16_t buttons;
|
uint16_t usb_buttons, n64_buttons = n64_data->buttons;
|
||||||
|
|
||||||
/* Force official range */
|
/* Force official range */
|
||||||
xval = minmax(n64_data->x, -80, 80);
|
xval = minmax(n64_data->x, -80, 80);
|
||||||
yval = minmax(n64_data->y, -80, 80);
|
yval = minmax(n64_data->y, -80, 80);
|
||||||
|
|
||||||
|
if (g_eeprom_data.cfg.flags & FLAG_SWAP_STICK_AND_DPAD) {
|
||||||
|
|
||||||
|
// Generate new D-Pad button status based on stick
|
||||||
|
n64_buttons &= ~(N64_BTN_DPAD_UP|N64_BTN_DPAD_DOWN|N64_BTN_DPAD_LEFT|N64_BTN_DPAD_RIGHT);
|
||||||
|
if (xval <= -STICK_TO_BTN_THRESHOLD) { n64_buttons |= N64_BTN_DPAD_LEFT; }
|
||||||
|
if (xval >= STICK_TO_BTN_THRESHOLD) { n64_buttons |= N64_BTN_DPAD_RIGHT; }
|
||||||
|
if (yval <= -STICK_TO_BTN_THRESHOLD) { n64_buttons |= N64_BTN_DPAD_DOWN; }
|
||||||
|
if (yval >= STICK_TO_BTN_THRESHOLD) { n64_buttons |= N64_BTN_DPAD_UP; }
|
||||||
|
|
||||||
|
// Generate new stick values based on button (use n64_data here)
|
||||||
|
xval = 0; yval = 0;
|
||||||
|
if (n64_data->buttons & N64_BTN_DPAD_UP) { yval = 80; }
|
||||||
|
if (n64_data->buttons & N64_BTN_DPAD_DOWN) { yval = -80; }
|
||||||
|
if (n64_data->buttons & N64_BTN_DPAD_LEFT) { xval = -80; }
|
||||||
|
if (n64_data->buttons & N64_BTN_DPAD_RIGHT) { xval = 80; }
|
||||||
|
}
|
||||||
|
|
||||||
/* Scale -80 ... +80 to -16000 ... +16000 */
|
/* Scale -80 ... +80 to -16000 ... +16000 */
|
||||||
xval *= 200;
|
xval *= 200;
|
||||||
yval *= 200;
|
yval *= 200;
|
||||||
|
@ -194,26 +273,26 @@ static void buildReportFromN64(const n64_pad_data *n64_data, unsigned char dstbu
|
||||||
dstbuf[3] = ((uint8_t*)&yval)[0];
|
dstbuf[3] = ((uint8_t*)&yval)[0];
|
||||||
dstbuf[4] = ((uint8_t*)&yval)[1];
|
dstbuf[4] = ((uint8_t*)&yval)[1];
|
||||||
|
|
||||||
buttons = mappings_do(MAPPING_N64_DEFAULT, n64_data->buttons);
|
usb_buttons = mappings_do(MAPPING_N64_DEFAULT, n64_buttons);
|
||||||
btnsToReport(buttons, dstbuf+13);
|
btnsToReport(usb_buttons, dstbuf+13);
|
||||||
}
|
}
|
||||||
|
|
||||||
void usbpad_update(const gamepad_data *pad_data)
|
void usbpad_update(struct usbpad *pad, const gamepad_data *pad_data)
|
||||||
{
|
{
|
||||||
/* Always start with an idle report. Specific report builders can just
|
/* Always start with an idle report. Specific report builders can just
|
||||||
* simply ignore unused parts */
|
* simply ignore unused parts */
|
||||||
buildIdleReport(gamepad_report0);
|
buildIdleReport(pad->gamepad_report0);
|
||||||
|
|
||||||
if (pad_data)
|
if (pad_data)
|
||||||
{
|
{
|
||||||
switch (pad_data->pad_type)
|
switch (pad_data->pad_type)
|
||||||
{
|
{
|
||||||
case PAD_TYPE_N64:
|
case PAD_TYPE_N64:
|
||||||
buildReportFromN64(&pad_data->n64, gamepad_report0);
|
buildReportFromN64(&pad_data->n64, pad->gamepad_report0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PAD_TYPE_GAMECUBE:
|
case PAD_TYPE_GAMECUBE:
|
||||||
buildReportFromGC(&pad_data->gc, gamepad_report0);
|
buildReportFromGC(&pad_data->gc, pad->gamepad_report0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -222,38 +301,71 @@ void usbpad_update(const gamepad_data *pad_data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void usbpad_forceVibrate(char force)
|
void usbpad_update_kb(struct usbpad *pad, const gamepad_data *pad_data)
|
||||||
{
|
{
|
||||||
force_vibrate = force;
|
unsigned char i;
|
||||||
|
|
||||||
|
/* Always start with an idle report. Specific report builders can just
|
||||||
|
* simply ignore unused parts */
|
||||||
|
buildIdleReportKB(pad->gamepad_report0);
|
||||||
|
|
||||||
|
if (pad_data->pad_type == PAD_TYPE_GC_KB) {
|
||||||
|
for (i=0; i<3; i++) {
|
||||||
|
pad->gamepad_report0[i] = gcKeycodeToHID(pad_data->gckb.keys[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char usbpad_mustVibrate(void)
|
|
||||||
|
void usbpad_forceVibrate(struct usbpad *pad, char force)
|
||||||
{
|
{
|
||||||
if (force_vibrate) {
|
pad->force_vibrate = force;
|
||||||
|
}
|
||||||
|
|
||||||
|
void usbpad_vibrationTask(struct usbpad *pad)
|
||||||
|
{
|
||||||
|
uint8_t sreg;
|
||||||
|
|
||||||
|
sreg = SREG;
|
||||||
|
cli();
|
||||||
|
if (pad->_loop_count) {
|
||||||
|
pad->_loop_count--;
|
||||||
|
}
|
||||||
|
SREG = sreg;
|
||||||
|
}
|
||||||
|
|
||||||
|
char usbpad_mustVibrate(struct usbpad *pad)
|
||||||
|
{
|
||||||
|
if (pad->force_vibrate) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vibration_on) {
|
if (!pad->vibration_on) {
|
||||||
gamepad_vibrate = 0;
|
pad->gamepad_vibrate = 0;
|
||||||
} else {
|
} else {
|
||||||
if (constant_force > 0x7f) {
|
if (pad->_loop_count > 0) {
|
||||||
gamepad_vibrate = 1;
|
if (pad->constant_force > 0x7f) {
|
||||||
} else if (periodic_magnitude > 0x7f) {
|
pad->gamepad_vibrate = 1;
|
||||||
gamepad_vibrate = 1;
|
} else if (pad->periodic_magnitude > 0x7f) {
|
||||||
|
pad->gamepad_vibrate = 1;
|
||||||
|
} else {
|
||||||
|
pad->gamepad_vibrate = 0;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
gamepad_vibrate = 0;
|
// Loop count = 0 -> Stop
|
||||||
|
pad->gamepad_vibrate = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return gamepad_vibrate;
|
return pad->gamepad_vibrate;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char *usbpad_getReportBuffer(void)
|
unsigned char *usbpad_getReportBuffer(struct usbpad *pad)
|
||||||
{
|
{
|
||||||
return gamepad_report0;
|
return pad->gamepad_report0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t usbpad_hid_get_report(struct usb_request *rq, const uint8_t **dat)
|
uint16_t usbpad_hid_get_report(struct usbpad *pad, struct usb_request *rq, const uint8_t **dat)
|
||||||
{
|
{
|
||||||
uint8_t report_id = (rq->wValue & 0xff);
|
uint8_t report_id = (rq->wValue & 0xff);
|
||||||
|
|
||||||
|
@ -268,15 +380,16 @@ uint16_t usbpad_hid_get_report(struct usb_request *rq, const uint8_t **dat)
|
||||||
if (report_id == 1) { // Joystick
|
if (report_id == 1) { // Joystick
|
||||||
// report_id = rq->wValue & 0xff
|
// report_id = rq->wValue & 0xff
|
||||||
// interface = rq->wIndex
|
// interface = rq->wIndex
|
||||||
*dat = gamepad_report0;
|
*dat = pad->gamepad_report0;
|
||||||
printf_P(PSTR("Get joy report\r\n"));
|
printf_P(PSTR("Get joy report\r\n"));
|
||||||
return 9;
|
return USBPAD_REPORT_SIZE
|
||||||
|
;
|
||||||
} else if (report_id == 2) { // 2 : ES playing
|
} else if (report_id == 2) { // 2 : ES playing
|
||||||
hid_report_data[0] = report_id;
|
pad->hid_report_data[0] = report_id;
|
||||||
hid_report_data[1] = 0;
|
pad->hid_report_data[1] = 0;
|
||||||
hid_report_data[2] = _FFB_effect_index;
|
pad->hid_report_data[2] = pad->_FFB_effect_index;
|
||||||
printf_P(PSTR("ES playing\r\n"));
|
printf_P(PSTR("ES playing\r\n"));
|
||||||
*dat = hid_report_data;
|
*dat = pad->hid_report_data;
|
||||||
return 3;
|
return 3;
|
||||||
} else {
|
} else {
|
||||||
printf_P(PSTR("Get input report %d ??\r\n"), rq->wValue & 0xff);
|
printf_P(PSTR("Get input report %d ??\r\n"), rq->wValue & 0xff);
|
||||||
|
@ -286,32 +399,32 @@ uint16_t usbpad_hid_get_report(struct usb_request *rq, const uint8_t **dat)
|
||||||
|
|
||||||
case HID_REPORT_TYPE_FEATURE:
|
case HID_REPORT_TYPE_FEATURE:
|
||||||
if (report_id == PID_BLOCK_LOAD_REPORT) {
|
if (report_id == PID_BLOCK_LOAD_REPORT) {
|
||||||
hid_report_data[0] = report_id;
|
pad->hid_report_data[0] = report_id;
|
||||||
hid_report_data[1] = 0x1; // Effect block index
|
pad->hid_report_data[1] = 0x1; // Effect block index
|
||||||
hid_report_data[2] = 0x1; // (1: success, 2: oom, 3: load error)
|
pad->hid_report_data[2] = 0x1; // (1: success, 2: oom, 3: load error)
|
||||||
hid_report_data[3] = 10;
|
pad->hid_report_data[3] = 10;
|
||||||
hid_report_data[4] = 10;
|
pad->hid_report_data[4] = 10;
|
||||||
printf_P(PSTR("block load\r\n"));
|
printf_P(PSTR("block load\r\n"));
|
||||||
*dat = hid_report_data;
|
*dat = pad->hid_report_data;
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
else if (report_id == PID_SIMULTANEOUS_MAX) {
|
else if (report_id == PID_SIMULTANEOUS_MAX) {
|
||||||
hid_report_data[0] = report_id;
|
pad->hid_report_data[0] = report_id;
|
||||||
// ROM Effect Block count
|
// ROM Effect Block count
|
||||||
hid_report_data[1] = 0x1;
|
pad->hid_report_data[1] = 0x1;
|
||||||
hid_report_data[2] = 0x1;
|
pad->hid_report_data[2] = 0x1;
|
||||||
// PID pool move report?
|
// PID pool move report?
|
||||||
hid_report_data[3] = 0xff;
|
pad->hid_report_data[3] = 0xff;
|
||||||
hid_report_data[4] = 1;
|
pad->hid_report_data[4] = 1;
|
||||||
printf_P(PSTR("simultaneous max\r\n"));
|
printf_P(PSTR("simultaneous max\r\n"));
|
||||||
*dat = hid_report_data;
|
*dat = pad->hid_report_data;
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
else if (report_id == REPORT_CREATE_EFFECT) {
|
else if (report_id == REPORT_CREATE_EFFECT) {
|
||||||
hid_report_data[0] = report_id;
|
pad->hid_report_data[0] = report_id;
|
||||||
hid_report_data[1] = 1;
|
pad->hid_report_data[1] = 1;
|
||||||
printf_P(PSTR("create effect\r\n"));
|
printf_P(PSTR("create effect\r\n"));
|
||||||
*dat = hid_report_data;
|
*dat = pad->hid_report_data;
|
||||||
return 2;
|
return 2;
|
||||||
} else {
|
} else {
|
||||||
printf_P(PSTR("Unknown feature %d\r\n"), rq->wValue & 0xff);
|
printf_P(PSTR("Unknown feature %d\r\n"), rq->wValue & 0xff);
|
||||||
|
@ -323,7 +436,7 @@ uint16_t usbpad_hid_get_report(struct usb_request *rq, const uint8_t **dat)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t usbpad_hid_set_report(const struct usb_request *rq, const uint8_t *data, uint16_t len)
|
uint8_t usbpad_hid_set_report(struct usbpad *pad, const struct usb_request *rq, const uint8_t *data, uint16_t len)
|
||||||
{
|
{
|
||||||
if (len < 1) {
|
if (len < 1) {
|
||||||
printf_P(PSTR("shrt\n"));
|
printf_P(PSTR("shrt\n"));
|
||||||
|
@ -342,37 +455,58 @@ uint8_t usbpad_hid_set_report(const struct usb_request *rq, const uint8_t *data,
|
||||||
break;
|
break;
|
||||||
case REPORT_DISABLE_ACTUATORS:
|
case REPORT_DISABLE_ACTUATORS:
|
||||||
printf_P(PSTR("disable actuators\r\n"));
|
printf_P(PSTR("disable actuators\r\n"));
|
||||||
periodic_magnitude = 0;
|
pad->periodic_magnitude = 0;
|
||||||
constant_force = 0;
|
pad->constant_force = 0;
|
||||||
vibration_on = 0;
|
pad->vibration_on = 0;
|
||||||
break;
|
break;
|
||||||
case REPORT_PID_POOL:
|
case REPORT_PID_POOL:
|
||||||
printf_P(PSTR("pid pool\r\n"));
|
printf_P(PSTR("pid pool\r\n"));
|
||||||
break;
|
break;
|
||||||
case REPORT_SET_EFFECT:
|
case REPORT_SET_EFFECT:
|
||||||
_FFB_effect_index = data[1];
|
pad->_FFB_effect_index = data[1];
|
||||||
printf_P(PSTR("set effect %d\r\n"), data[1]);
|
pad->_FFB_effect_duration = data[3] | (data[4]<<8);
|
||||||
|
printf_P(PSTR("set effect %d. duration: %u\r\n"), data[1], pad->_FFB_effect_duration);
|
||||||
|
hexdump(data, len);
|
||||||
break;
|
break;
|
||||||
case REPORT_SET_PERIODIC:
|
case REPORT_SET_PERIODIC:
|
||||||
periodic_magnitude = data[2];
|
pad->periodic_magnitude = data[2];
|
||||||
printf_P(PSTR("periodic mag: %d"), data[2]);
|
printf_P(PSTR("Set periodic - mag: %d, period: %u\r\n"), data[2], data[5] | (data[6]<<8));
|
||||||
|
hexdump(data, len);
|
||||||
break;
|
break;
|
||||||
case REPORT_SET_CONSTANT_FORCE:
|
case REPORT_SET_CONSTANT_FORCE:
|
||||||
if (data[1] == 1) {
|
if (data[1] == 1) {
|
||||||
constant_force = data[2];
|
pad->constant_force = data[2];
|
||||||
printf_P(PSTR("Constant force %d\r\n"), data[2]);
|
printf_P(PSTR("Constant force %d\r\n"), data[2]);
|
||||||
}
|
}
|
||||||
|
hexdump(data, len);
|
||||||
break;
|
break;
|
||||||
case REPORT_EFFECT_OPERATION:
|
case REPORT_EFFECT_OPERATION:
|
||||||
if (len != 4)
|
if (len != 4) {
|
||||||
|
printf_P(PSTR("Hey!\r\n"));
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
/* Byte 0 : report ID
|
/* Byte 0 : report ID
|
||||||
* Byte 1 : bit 7=rom flag, bits 6-0=effect block index
|
* Byte 1 : bit 7=rom flag, bits 6-0=effect block index
|
||||||
* Byte 2 : Effect operation
|
* Byte 2 : Effect operation
|
||||||
* Byte 3 : Loop count */
|
* Byte 3 : Loop count */
|
||||||
_loop_count = data[3]<<3;
|
|
||||||
|
|
||||||
printf_P(PSTR("EFFECT OP: rom=%s, idx=0x%02x"), data[1] & 0x80 ? "Yes":"No", data[1] & 0x7F);
|
|
||||||
|
printf_P(PSTR("EFFECT OP: rom=%s, idx=0x%02x : "), data[1] & 0x80 ? "Yes":"No", data[1] & 0x7F);
|
||||||
|
|
||||||
|
// With dolphin, an "infinite" duration is set. The effect is started, then never
|
||||||
|
// stopped. Maybe I misunderstood something? In any case, the following works
|
||||||
|
// and feels about right.
|
||||||
|
if (pad->_FFB_effect_duration == 0xffff) {
|
||||||
|
if (data[3]) {
|
||||||
|
pad->_loop_count = data[3] + 1; // +1 for a bit more strength
|
||||||
|
} else {
|
||||||
|
pad->_loop_count = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// main.c uses a 16ms interval timer for vibration "loops"
|
||||||
|
pad->_loop_count = (pad->_FFB_effect_duration / 16) * data[3];
|
||||||
|
printf_P(PSTR("%d loops for %d ms\r\n"), data[3], pad->_loop_count * 16);
|
||||||
|
}
|
||||||
|
|
||||||
switch(data[1] & 0x7F) // Effect block index
|
switch(data[1] & 0x7F) // Effect block index
|
||||||
{
|
{
|
||||||
|
@ -382,24 +516,27 @@ uint8_t usbpad_hid_set_report(const struct usb_request *rq, const uint8_t *data,
|
||||||
switch (data[2]) // effect operation
|
switch (data[2]) // effect operation
|
||||||
{
|
{
|
||||||
case EFFECT_OP_START:
|
case EFFECT_OP_START:
|
||||||
printf_P(PSTR("Start\r\n"));
|
printf_P(PSTR("Start (lp=%d)\r\n"), pad->_loop_count);
|
||||||
vibration_on = 1;
|
pad->vibration_on = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EFFECT_OP_START_SOLO:
|
case EFFECT_OP_START_SOLO:
|
||||||
printf_P(PSTR("Start solo\r\n"));
|
printf_P(PSTR("Start solo (lp=%d)\r\n"), pad->_loop_count);
|
||||||
vibration_on = 1;
|
pad->vibration_on = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EFFECT_OP_STOP:
|
case EFFECT_OP_STOP:
|
||||||
printf_P(PSTR("Stop\r\n"));
|
printf_P(PSTR("Stop (lp=%d)\r\n"), pad->_loop_count);
|
||||||
vibration_on = 0;
|
pad->vibration_on = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf_P(PSTR("OP?? %02x (lp=%d)\r\n"), data[2], pad->_loop_count);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// TODO : should probably drop these from the descriptor since they are
|
// TODO : should probably drop these from the descriptor since they are
|
||||||
|
default:
|
||||||
case 2: // ramp
|
case 2: // ramp
|
||||||
case 5: // triangle
|
case 5: // triangle
|
||||||
case 6: // sawtooth up
|
case 6: // sawtooth up
|
||||||
|
@ -421,7 +558,7 @@ uint8_t usbpad_hid_set_report(const struct usb_request *rq, const uint8_t *data,
|
||||||
switch(data[0])
|
switch(data[0])
|
||||||
{
|
{
|
||||||
case REPORT_CREATE_EFFECT:
|
case REPORT_CREATE_EFFECT:
|
||||||
_FFB_effect_index = data[1];
|
pad->_FFB_effect_index = data[1];
|
||||||
printf_P(PSTR("create effect %d\n"), data[1]);
|
printf_P(PSTR("create effect %d\n"), data[1]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
35
usbpad.h
35
usbpad.h
|
@ -4,18 +4,39 @@
|
||||||
#include "gamepads.h"
|
#include "gamepads.h"
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
|
|
||||||
void usbpad_init(void);
|
#define USBPAD_REPORT_SIZE 15
|
||||||
|
|
||||||
|
struct usbpad {
|
||||||
|
volatile unsigned char gamepad_vibrate; // output
|
||||||
|
unsigned char vibration_on, force_vibrate;
|
||||||
|
unsigned char constant_force;
|
||||||
|
unsigned char periodic_magnitude;
|
||||||
|
|
||||||
|
unsigned short _FFB_effect_duration; // in milliseconds
|
||||||
|
unsigned char _FFB_effect_index;
|
||||||
|
#define LOOP_MAX 0xFFFF
|
||||||
|
unsigned int _loop_count;
|
||||||
|
|
||||||
|
unsigned char gamepad_report0[USBPAD_REPORT_SIZE];
|
||||||
|
unsigned char hid_report_data[8]; // Used for force feedback
|
||||||
|
};
|
||||||
|
|
||||||
|
void usbpad_init(struct usbpad *pad);
|
||||||
int usbpad_getReportSize(void);
|
int usbpad_getReportSize(void);
|
||||||
unsigned char *usbpad_getReportBuffer(void);
|
unsigned char *usbpad_getReportBuffer(struct usbpad *pad);
|
||||||
|
|
||||||
void usbpad_update(const gamepad_data *pad_data);
|
void usbpad_update(struct usbpad *pad, const gamepad_data *pad_data);
|
||||||
char usbpad_mustVibrate(void);
|
void usbpad_vibrationTask(struct usbpad *pad);
|
||||||
void usbpad_forceVibrate(char force);
|
char usbpad_mustVibrate(struct usbpad *pad);
|
||||||
|
void usbpad_forceVibrate(struct usbpad *pad, char force);
|
||||||
|
|
||||||
uint8_t usbpad_hid_set_report(const struct usb_request *rq, const uint8_t *data, uint16_t len);
|
uint8_t usbpad_hid_set_report(struct usbpad *pad, const struct usb_request *rq, const uint8_t *data, uint16_t len);
|
||||||
uint16_t usbpad_hid_get_report(struct usb_request *rq, const uint8_t **dat);
|
uint16_t usbpad_hid_get_report(struct usbpad *pad, struct usb_request *rq, const uint8_t **dat);
|
||||||
|
|
||||||
// For mappings. ID starts at 0.
|
// For mappings. ID starts at 0.
|
||||||
#define USB_BTN(id) (0x0001 << (id))
|
#define USB_BTN(id) (0x0001 << (id))
|
||||||
|
|
||||||
|
int usbpad_getReportSizeKB(void);
|
||||||
|
void usbpad_update_kb(struct usbpad *pad, const gamepad_data *pad_data);
|
||||||
|
|
||||||
#endif // USBPAD_H__
|
#endif // USBPAD_H__
|
||||||
|
|
27
usbstrings.c
27
usbstrings.c
|
@ -1,5 +1,5 @@
|
||||||
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
||||||
Copyright (C) 2007-2013 Raphael Assenat <raph@raphnet.net>
|
Copyright (C) 2007-2016 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
@ -17,9 +17,32 @@
|
||||||
#include <stdlib.h> // for wchar_t
|
#include <stdlib.h> // for wchar_t
|
||||||
#include "usbstrings.h"
|
#include "usbstrings.h"
|
||||||
|
|
||||||
|
static wchar_t product_string[PRODUCT_STRING_MAXCHARS]; // = L"GC/N64 to USB v"VERSIONSTR_SHORT;
|
||||||
|
|
||||||
const wchar_t *g_usb_strings[] = {
|
const wchar_t *g_usb_strings[] = {
|
||||||
[0] = L"raphnet technologies", // 1 : Vendor
|
[0] = L"raphnet technologies", // 1 : Vendor
|
||||||
[1] = L"GC/N64 to USB v3.0", // 2: Product
|
[1] = product_string, // 2: Product
|
||||||
[2] = L"123456", // 3 : Serial
|
[2] = L"123456", // 3 : Serial
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void usbstrings_changeProductString_P(const char *str)
|
||||||
|
{
|
||||||
|
const char *s = str;
|
||||||
|
wchar_t *d = product_string;
|
||||||
|
uint8_t c;
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
/* Make sure target is always NUL terminated. */
|
||||||
|
n++;
|
||||||
|
if (n == PRODUCT_STRING_MAXCHARS) {
|
||||||
|
*d = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
c = pgm_read_byte(s);
|
||||||
|
|
||||||
|
*d = c;
|
||||||
|
s++; d++;
|
||||||
|
} while (c);
|
||||||
|
}
|
||||||
|
|
10
usbstrings.h
10
usbstrings.h
|
@ -1,11 +1,21 @@
|
||||||
#ifndef _usbstrings_h__
|
#ifndef _usbstrings_h__
|
||||||
#define _usbstrings_h__
|
#define _usbstrings_h__
|
||||||
|
|
||||||
|
#include <avr/pgmspace.h>
|
||||||
|
|
||||||
extern const wchar_t *g_usb_strings[];
|
extern const wchar_t *g_usb_strings[];
|
||||||
|
|
||||||
|
/* Sample: "Dual Gamecube to USB v3.4" (25) */
|
||||||
|
#define PRODUCT_STRING_MAXCHARS 32
|
||||||
|
|
||||||
#define NUM_USB_STRINGS 3
|
#define NUM_USB_STRINGS 3
|
||||||
|
|
||||||
/* Array indexes (i.e. zero-based0 */
|
/* Array indexes (i.e. zero-based0 */
|
||||||
#define USB_STRING_SERIAL_IDX 2
|
#define USB_STRING_SERIAL_IDX 2
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \param str Must be in PROGMEM
|
||||||
|
*/
|
||||||
|
void usbstrings_changeProductString_P(const char *str);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
/* gc_n64_usb : Gamecube or N64 controller to USB firmware
|
||||||
Copyright (C) 2007-2013 Raphael Assenat <raph@raphnet.net>
|
Copyright (C) 2007-2016 Raphael Assenat <raph@raphnet.net>
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|
Loading…
Reference in New Issue