Files
CherryUSB/demo/gamepad_template.c

250 lines
6.7 KiB
C
Raw Normal View History

/*
* Copyright (c) 2026, sakumisu
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usbd_core.h"
#include "usbd_gamepad.h"
#define GAMEPAD_IN_EP 0x81
#define GAMEPAD_OUT_EP 0x02
#define USBD_MAX_POWER 500
static const uint8_t xinput_device_descriptor[] = {
USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0x00, 0x00, 0x00, XINPUT_VID, XINPUT_PID, XINPUT_BCD_DEVICE, 0x01)
};
static const uint8_t switch_device_descriptor[] = {
USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0x00, 0x00, 0x00, SWITCH_VID, SWITCH_PID, SWITCH_BCD_DEVICE, 0x01)
};
static const uint8_t xinput_config_descriptor[] = {
USB_CONFIG_DESCRIPTOR_INIT((9 + XINPUT_DESCRIPTOR_LEN), 0x01, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
XINPUT_DESCRIPTOR_INIT(0x00, GAMEPAD_OUT_EP, GAMEPAD_IN_EP)
};
static const uint8_t switch_config_descriptor[] = {
USB_CONFIG_DESCRIPTOR_INIT((9 + SWITCH_DESCRIPTOR_LEN), 0x01, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
SWITCH_DESCRIPTOR_INIT(0x00, GAMEPAD_OUT_EP, GAMEPAD_IN_EP)
};
static const char *xinput_string_descriptors[] = {
(const char[]){ 0x09, 0x04 }, /* Langid */
"Microsoft", /* Manufacturer */
"XInput STANDARD GAMEPAD", /* Product */
"1.0", /* Serial Number */
};
static const char *switch_string_descriptors[] = {
(const char[]){ 0x09, 0x04 }, /* Langid */
"HORI", /* Manufacturer */
"Switch Pro Controller", /* Product */
"1.0", /* Serial Number */
};
uint8_t gamepad_mode = USBD_GAMEPAD_MODE_XINPUT;
bool gamepad_init_flag = false;
static const uint8_t *device_descriptor_callback(uint8_t speed)
{
switch (gamepad_mode) {
case USBD_GAMEPAD_MODE_XINPUT:
return xinput_device_descriptor;
case USBD_GAMEPAD_MODE_SWITCH:
return switch_device_descriptor;
case USBD_GAMEPAD_MODE_XBOXONE:
break;
case USBD_GAMEPAD_MODE_PS4:
break;
default:
break;
}
return NULL;
}
static const uint8_t *config_descriptor_callback(uint8_t speed)
{
switch (gamepad_mode) {
case USBD_GAMEPAD_MODE_XINPUT:
return xinput_config_descriptor;
case USBD_GAMEPAD_MODE_SWITCH:
return switch_config_descriptor;
case USBD_GAMEPAD_MODE_XBOXONE:
break;
case USBD_GAMEPAD_MODE_PS4:
break;
default:
break;
}
return NULL;
}
static const uint8_t *device_quality_descriptor_callback(uint8_t speed)
{
return NULL;
}
static const char *string_descriptor_callback(uint8_t speed, uint8_t index)
{
if (index > 3) {
return NULL;
}
switch (gamepad_mode) {
case USBD_GAMEPAD_MODE_XINPUT:
return xinput_string_descriptors[index];
case USBD_GAMEPAD_MODE_SWITCH:
return switch_string_descriptors[index];
case USBD_GAMEPAD_MODE_XBOXONE:
break;
case USBD_GAMEPAD_MODE_PS4:
break;
default:
break;
}
return NULL;
}
const struct usb_descriptor gamepad_descriptor = {
.device_descriptor_callback = device_descriptor_callback,
.config_descriptor_callback = config_descriptor_callback,
.device_quality_descriptor_callback = device_quality_descriptor_callback,
.string_descriptor_callback = string_descriptor_callback
};
USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t gamepad_read_buffer[64];
struct usb_gamepad_report gamepad_report;
#define GAMEPAD_STATE_IDLE 0
#define GAMEPAD_STATE_BUSY 1
volatile uint8_t gamepad_state = GAMEPAD_STATE_IDLE;
static void usbd_event_handler(uint8_t busid, uint8_t event)
{
switch (event) {
case USBD_EVENT_RESET:
break;
case USBD_EVENT_CONNECTED:
break;
case USBD_EVENT_DISCONNECTED:
break;
case USBD_EVENT_RESUME:
break;
case USBD_EVENT_SUSPEND:
break;
case USBD_EVENT_CONFIGURED:
usbd_ep_start_read(busid, GAMEPAD_OUT_EP, gamepad_read_buffer, usbd_get_ep_mps(busid, GAMEPAD_OUT_EP));
break;
case USBD_EVENT_SET_REMOTE_WAKEUP:
break;
case USBD_EVENT_CLR_REMOTE_WAKEUP:
break;
default:
break;
}
}
static void usbd_gamepad_int_in_callback(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
gamepad_state = GAMEPAD_STATE_IDLE;
}
void usbd_gamepad_int_out_callback(uint8_t busid, uint8_t ep, uint32_t nbytes)
{
usbd_ep_start_read(busid, GAMEPAD_OUT_EP, gamepad_read_buffer, usbd_get_ep_mps(busid, GAMEPAD_OUT_EP));
}
/*!< endpoint call back */
static struct usbd_endpoint gamepad_in_ep = {
.ep_cb = usbd_gamepad_int_in_callback,
.ep_addr = GAMEPAD_IN_EP
};
static struct usbd_endpoint gamepad_out_ep = {
.ep_cb = usbd_gamepad_int_out_callback,
.ep_addr = GAMEPAD_OUT_EP
};
static struct usbd_interface intf0;
void gamepad_init(uint8_t busid, uintptr_t reg_base)
{
if (gamepad_init_flag) {
return;
}
gamepad_init_flag = true;
usbd_desc_register(busid, &gamepad_descriptor);
switch (gamepad_mode) {
case USBD_GAMEPAD_MODE_XINPUT:
usbd_add_interface(busid, usbd_gamepad_xinput_init_intf(&intf0));
break;
case USBD_GAMEPAD_MODE_SWITCH:
usbd_add_interface(busid, usbd_gamepad_switch_init_intf(&intf0));
break;
case USBD_GAMEPAD_MODE_XBOXONE:
break;
case USBD_GAMEPAD_MODE_PS4:
break;
default:
break;
}
usbd_add_endpoint(busid, &gamepad_in_ep);
usbd_add_endpoint(busid, &gamepad_out_ep);
usbd_initialize(busid, reg_base, usbd_event_handler);
}
void gamepad_change_mode(uint8_t mode, uintptr_t reg_base)
{
gamepad_mode = mode;
if (gamepad_init_flag) {
usbd_deinitialize(0);
}
gamepad_init_flag = false;
gamepad_init(0, reg_base);
}
void gamepad_test(uint8_t busid)
{
static uint32_t test_counter = 0;
if (usb_device_is_configured(busid) == false) {
return;
}
gamepad_state = GAMEPAD_STATE_BUSY;
memset(&gamepad_report, 0, sizeof(gamepad_report));
gamepad_report.buttons = (1 << (test_counter % 18));
switch (gamepad_mode) {
case USBD_GAMEPAD_MODE_XINPUT:
usbd_gamepad_xinput_send_report(GAMEPAD_IN_EP, &gamepad_report);
break;
case USBD_GAMEPAD_MODE_SWITCH:
usbd_gamepad_switch_send_report(GAMEPAD_IN_EP, &gamepad_report);
break;
case USBD_GAMEPAD_MODE_XBOXONE:
break;
case USBD_GAMEPAD_MODE_PS4:
break;
default:
break;
}
test_counter++;
while (gamepad_state == GAMEPAD_STATE_BUSY) {
}
}