mirror of
https://github.com/raphnet/gc_n64_usb-v3
synced 2024-12-21 23:08:53 -05:00
Add support for Gamecube keyboards
This commit is contained in:
parent
9872399739
commit
4999f07c14
@ -1,4 +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 intervaltimer2.o version.o gcn64txrx0.o gcn64txrx1.o gcn64txrx2.o gcn64txrx3.o gamepads.o stkchk.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.5.2\"
|
VERSIONSTR=\"3.6.0\"
|
||||||
VERSIONSTR_SHORT=\"3.5\"
|
VERSIONSTR_SHORT=\"3.6\"
|
||||||
VERSIONBCD=0x0352
|
VERSIONBCD=0x0360
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
- November 6, 2018 : Version 3.6.0
|
||||||
|
- Add gamecube keyboard support
|
||||||
|
|
||||||
- September 18, 2018 : Version 3.5.2
|
- September 18, 2018 : Version 3.5.2
|
||||||
- Improve PID (force feedback) implementation
|
- Improve PID (force feedback) implementation
|
||||||
- Implement reset firmware command
|
- Implement reset firmware command
|
||||||
|
54
gamecube.c
54
gamecube.c
@ -24,7 +24,9 @@
|
|||||||
|
|
||||||
/*********** prototypes *************/
|
/*********** prototypes *************/
|
||||||
static void gamecubeInit(unsigned char chn);
|
static void gamecubeInit(unsigned char chn);
|
||||||
|
static void gamecubeInitKB(unsigned char chn);
|
||||||
static char gamecubeUpdate(unsigned char chn);
|
static char gamecubeUpdate(unsigned char chn);
|
||||||
|
static char gamecubeUpdateKB(unsigned char chn);
|
||||||
static char gamecubeChanged(unsigned char chn);
|
static char gamecubeChanged(unsigned char chn);
|
||||||
|
|
||||||
static char gc_rumbling[GAMEPAD_MAX_CHANNELS] = { };
|
static char gc_rumbling[GAMEPAD_MAX_CHANNELS] = { };
|
||||||
@ -39,6 +41,11 @@ static void gamecubeInit(unsigned char chn)
|
|||||||
gamecubeUpdate(chn);
|
gamecubeUpdate(chn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gamecubeInitKB(unsigned char chn)
|
||||||
|
{
|
||||||
|
gamecubeUpdateKB(chn);
|
||||||
|
}
|
||||||
|
|
||||||
void gc_decodeAnswer(unsigned char chn, unsigned char data[8])
|
void gc_decodeAnswer(unsigned char chn, unsigned char data[8])
|
||||||
{
|
{
|
||||||
unsigned char x,y,cx,cy;
|
unsigned char x,y,cx,cy;
|
||||||
@ -115,6 +122,39 @@ void gc_decodeAnswer(unsigned char chn, unsigned char data[8])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char gamecubeUpdateKB(unsigned char chn)
|
||||||
|
{
|
||||||
|
unsigned char tmpdata[GC_GETSTATUS_REPLY_LENGTH];
|
||||||
|
unsigned char count;
|
||||||
|
unsigned char i, lrc;
|
||||||
|
|
||||||
|
tmpdata[0] = GC_POLL_KB1;
|
||||||
|
tmpdata[1] = GC_POLL_KB2;
|
||||||
|
tmpdata[2] = GC_POLL_KB3;
|
||||||
|
|
||||||
|
count = gcn64_transaction(chn, tmpdata, 3, tmpdata, GC_GETSTATUS_REPLY_LENGTH);
|
||||||
|
if (count != GC_GETSTATUS_REPLY_LENGTH) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
static char gamecubeUpdate(unsigned char chn)
|
static char gamecubeUpdate(unsigned char chn)
|
||||||
{
|
{
|
||||||
unsigned char tmpdata[GC_GETSTATUS_REPLY_LENGTH];
|
unsigned char tmpdata[GC_GETSTATUS_REPLY_LENGTH];
|
||||||
@ -181,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,4 +1,5 @@
|
|||||||
#include "gamepads.h"
|
#include "gamepads.h"
|
||||||
|
|
||||||
Gamepad *gamecubeGetGamepad(void);
|
Gamepad *gamecubeGetGamepad(void);
|
||||||
|
Gamepad *gamecubeGetKeyboard(void);
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#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
|
||||||
@ -69,11 +70,17 @@ 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;
|
||||||
|
|
||||||
|
93
gc_kb.c
Normal file
93
gc_kb.c
Normal file
@ -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
|
||||||
|
}
|
||||||
|
|
110
hid_keycodes.h
Normal file
110
hid_keycodes.h
Normal file
@ -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__
|
||||||
|
|
452
main.c
452
main.c
@ -42,13 +42,19 @@
|
|||||||
|
|
||||||
#define MAX_PLAYERS 2
|
#define MAX_PLAYERS 2
|
||||||
|
|
||||||
#define GCN64_USB_PID 0x0038
|
#define GCN64_USB_PID 0x0060
|
||||||
#define N64_USB_PID 0x0039
|
#define N64_USB_PID 0x0061
|
||||||
#define GC_USB_PID 0x003A
|
#define GC_USB_PID 0x0062
|
||||||
|
|
||||||
#define DUAL_GCN64_USB_PID 0x003B
|
#define DUAL_GCN64_USB_PID 0x0063
|
||||||
#define DUAL_N64_USB_PID 0x003C
|
#define DUAL_N64_USB_PID 0x0064
|
||||||
#define DUAL_GC_USB_PID 0x003D
|
#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 */
|
||||||
@ -139,6 +145,77 @@ static const struct cfg0 cfg0 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 cfg0_2p {
|
||||||
struct usb_configuration_descriptor configdesc;
|
struct usb_configuration_descriptor configdesc;
|
||||||
struct usb_interface_descriptor interface;
|
struct usb_interface_descriptor interface;
|
||||||
@ -253,6 +330,107 @@ static const struct cfg0_2p cfg0_2p PROGMEM = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 = {
|
struct usb_device_descriptor device_descriptor = {
|
||||||
.bLength = sizeof(struct usb_device_descriptor),
|
.bLength = sizeof(struct usb_device_descriptor),
|
||||||
.bDescriptorType = DEVICE_DESCRIPTOR,
|
.bDescriptorType = DEVICE_DESCRIPTOR,
|
||||||
@ -376,13 +554,13 @@ Gamepad *detectPad(unsigned char chn)
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
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;
|
||||||
@ -418,13 +596,48 @@ static void forceVibration(uint8_t channel, uint8_t force)
|
|||||||
|
|
||||||
static uint8_t getSupportedModes(uint8_t *dst)
|
static uint8_t getSupportedModes(uint8_t *dst)
|
||||||
{
|
{
|
||||||
dst[0] = CFG_MODE_STANDARD;
|
uint8_t idx = 0;
|
||||||
dst[1] = CFG_MODE_N64_ONLY;
|
|
||||||
dst[2] = CFG_MODE_GC_ONLY;
|
switch (g_eeprom_data.cfg.mode)
|
||||||
dst[3] = CFG_MODE_2P_STANDARD;
|
{
|
||||||
dst[4] = CFG_MODE_2P_N64_ONLY;
|
// Allow toggling between keyboard and joystick modes on
|
||||||
dst[5] = CFG_MODE_2P_GC_ONLY;
|
// single-port gamecube adapter
|
||||||
return 6;
|
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 = {
|
static struct hiddata_ops hiddata_ops = {
|
||||||
@ -491,6 +704,12 @@ int main(void)
|
|||||||
device_descriptor.idProduct = DUAL_GC_USB_PID;
|
device_descriptor.idProduct = DUAL_GC_USB_PID;
|
||||||
num_players = 2;
|
num_players = 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CFG_MODE_KB_AND_JS:
|
||||||
|
case CFG_MODE_KEYBOARD:
|
||||||
|
case CFG_MODE_KEYBOARD_2:
|
||||||
|
keyboard_main();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2-players common
|
// 2-players common
|
||||||
@ -623,3 +842,204 @@ int main(void)
|
|||||||
|
|
||||||
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 num_players = 1;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
18
reportdesc.c
18
reportdesc.c
@ -645,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
|
||||||
|
};
|
||||||
|
@ -29,6 +29,9 @@
|
|||||||
#define CFG_MODE_2P_STANDARD 0x10
|
#define CFG_MODE_2P_STANDARD 0x10
|
||||||
#define CFG_MODE_2P_N64_ONLY 0x11
|
#define CFG_MODE_2P_N64_ONLY 0x11
|
||||||
#define CFG_MODE_2P_GC_ONLY 0x12
|
#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
|
||||||
|
|
||||||
|
30
usbpad.c
30
usbpad.c
@ -24,6 +24,8 @@
|
|||||||
#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 REPORT_ID 1
|
#define REPORT_ID 1
|
||||||
|
|
||||||
@ -112,6 +114,18 @@ static void buildIdleReport(unsigned char dstbuf[USBPAD_REPORT_SIZE])
|
|||||||
dstbuf[14] = 0;
|
dstbuf[14] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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])
|
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;
|
||||||
@ -249,6 +263,22 @@ void usbpad_update(struct usbpad *pad, const gamepad_data *pad_data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void usbpad_update_kb(struct usbpad *pad, const gamepad_data *pad_data)
|
||||||
|
{
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void usbpad_forceVibrate(struct usbpad *pad, char force)
|
void usbpad_forceVibrate(struct usbpad *pad, char force)
|
||||||
{
|
{
|
||||||
pad->force_vibrate = force;
|
pad->force_vibrate = force;
|
||||||
|
3
usbpad.h
3
usbpad.h
@ -36,4 +36,7 @@ uint16_t usbpad_hid_get_report(struct usbpad *pad, struct usb_request *rq, const
|
|||||||
// 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__
|
||||||
|
Loading…
Reference in New Issue
Block a user