Files
refleks-lite/arcadestick_software/USB_DEVICE/App/usbd_dinput.c

391 lines
11 KiB
C

#include "usbd_dinput.h"
#include "usbd_ctlreq.h"
#include <string.h>
#define HID_DESCRIPTOR_TYPE 0x21U
#define HID_REPORT_DESC 0x22U
#define HID_REQ_GET_REPORT 0x01U
#define HID_REQ_GET_IDLE 0x02U
#define HID_REQ_GET_PROTOCOL 0x03U
#define HID_REQ_SET_REPORT 0x09U
#define HID_REQ_SET_IDLE 0x0AU
#define HID_REQ_SET_PROTOCOL 0x0BU
typedef struct
{
uint8_t altSetting[3];
uint8_t protocol;
uint8_t idle;
uint8_t busy;
} USBD_DINPUT_HandleTypeDef;
static uint8_t DINPUT_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
static uint8_t DINPUT_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx);
static uint8_t DINPUT_Setup(USBD_HandleTypeDef *pdev,
USBD_SetupReqTypedef *req);
static uint8_t DINPUT_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum);
static uint8_t DINPUT_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum);
static uint8_t *DINPUT_GetCfgDesc(uint16_t *length);
static uint8_t *DINPUT_GetDeviceQualifierDesc(uint16_t *length);
#if (USBD_SUPPORT_USER_STRING_DESC == 1U)
static uint8_t *DINPUT_GetUserString(USBD_HandleTypeDef *pdev,
uint8_t index,
uint16_t *length);
#endif
static uint8_t dinputInBuffer[DINPUT_REPORT_SIZE];
static uint8_t dinputSetupOutBuffer[DINPUT_EP_SIZE];
#if (USBD_SUPPORT_USER_STRING_DESC == 1U)
static uint8_t dinputStringBuffer[32];
#endif
USBD_ClassTypeDef USBD_DINPUT =
{
DINPUT_Init,
DINPUT_DeInit,
DINPUT_Setup,
NULL,
NULL,
DINPUT_DataIn,
DINPUT_DataOut,
NULL,
NULL,
NULL,
DINPUT_GetCfgDesc,
DINPUT_GetCfgDesc,
DINPUT_GetCfgDesc,
DINPUT_GetDeviceQualifierDesc,
#if (USBD_SUPPORT_USER_STRING_DESC == 1U)
DINPUT_GetUserString,
#endif
};
/* Descriptor order and report sizes match the original three-interface unit.
* IF0 is the only functional interface. IF1/IF2 are inert compatibility
* shells and their data endpoints are halted in DINPUT_Init(). */
__ALIGN_BEGIN static uint8_t dinputConfigDescriptor[USB_DINPUT_CONFIG_DESC_SIZ] __ALIGN_END =
{
0x09, USB_DESC_TYPE_CONFIGURATION,
LOBYTE(USB_DINPUT_CONFIG_DESC_SIZ), HIBYTE(USB_DINPUT_CONFIG_DESC_SIZ),
0x03, 0x01, 0x00, 0x80, 0x32,
0x09, USB_DESC_TYPE_INTERFACE,
0x00, 0x00, 0x01,
0x03, 0x00, 0x00, 0x05,
0x09, HID_DESCRIPTOR_TYPE,
0x11, 0x01, 0x00, 0x01, HID_REPORT_DESC,
0x51, 0x00,
0x07, USB_DESC_TYPE_ENDPOINT,
DINPUT_GAMEPAD_EPIN_ADDR, 0x03,
LOBYTE(DINPUT_EP_SIZE), HIBYTE(DINPUT_EP_SIZE), 0x01,
0x09, USB_DESC_TYPE_INTERFACE,
0x01, 0x00, 0x02,
0x03, 0x00, 0x00, 0x06,
0x09, HID_DESCRIPTOR_TYPE,
0x11, 0x01, 0x00, 0x01, HID_REPORT_DESC,
0x22, 0x00,
0x07, USB_DESC_TYPE_ENDPOINT,
DINPUT_SETUP_EPIN_ADDR, 0x03,
LOBYTE(DINPUT_EP_SIZE), HIBYTE(DINPUT_EP_SIZE), 0x01,
0x07, USB_DESC_TYPE_ENDPOINT,
DINPUT_SETUP_EPOUT_ADDR, 0x03,
LOBYTE(DINPUT_EP_SIZE), HIBYTE(DINPUT_EP_SIZE), 0x01,
0x09, USB_DESC_TYPE_INTERFACE,
0x02, 0x00, 0x01,
0x03, 0x00, 0x00, 0x07,
0x09, HID_DESCRIPTOR_TYPE,
0x11, 0x01, 0x00, 0x01, HID_REPORT_DESC,
0x15, 0x00,
0x07, USB_DESC_TYPE_ENDPOINT,
DINPUT_RAW_EPIN_ADDR, 0x03,
LOBYTE(DINPUT_EP_SIZE), HIBYTE(DINPUT_EP_SIZE), 0x01,
};
/* 11 buttons, hat and six unsigned 8-bit axes: 9 bytes total. */
__ALIGN_BEGIN static uint8_t gamepadReportDescriptor[] __ALIGN_END =
{
0x05, 0x01, 0x09, 0x05, 0xA1, 0x01,
0x05, 0x09, 0x19, 0x01, 0x29, 0x0B,
0x15, 0x00, 0x25, 0x01, 0x75, 0x01,
0x95, 0x0B, 0x81, 0x02,
0x75, 0x01, 0x95, 0x05, 0x81, 0x03,
0x05, 0x01, 0x09, 0x39, 0x15, 0x00,
0x25, 0x07, 0x35, 0x00, 0x46, 0x3B, 0x01,
0x65, 0x14, 0x75, 0x04, 0x95, 0x01,
0x81, 0x42, 0x75, 0x04, 0x95, 0x01, 0x81, 0x03,
0x05, 0x01,
0x09, 0x30, 0x09, 0x31, 0x09, 0x32,
0x09, 0x33, 0x09, 0x34, 0x09, 0x35,
0x15, 0x00, 0x26, 0xFF, 0x00,
0x75, 0x08, 0x95, 0x06, 0x81, 0x02,
0xC0
};
__ALIGN_BEGIN static uint8_t setupReportDescriptor[] __ALIGN_END =
{
0x06, 0x00, 0xFF, 0x09, 0x01, 0xA1, 0x01,
0x09, 0x10, 0x15, 0x00, 0x26, 0xFF, 0x00,
0x75, 0x08, 0x95, 0x1A, 0x81, 0x02,
0x09, 0x11, 0x15, 0x00, 0x26, 0xFF, 0x00,
0x75, 0x08, 0x95, 0x1B, 0x91, 0x02,
0xC0
};
__ALIGN_BEGIN static uint8_t rawReportDescriptor[] __ALIGN_END =
{
0x06, 0x00, 0xFF, 0x09, 0x02, 0xA1, 0x01,
0x09, 0x20, 0x15, 0x00, 0x26, 0xFF, 0x00,
0x75, 0x08, 0x95, 0x18, 0x81, 0x02,
0xC0
};
__ALIGN_BEGIN static uint8_t dinputDeviceQualifierDescriptor[USB_LEN_DEV_QUALIFIER_DESC] __ALIGN_END =
{
USB_LEN_DEV_QUALIFIER_DESC, USB_DESC_TYPE_DEVICE_QUALIFIER,
0x00, 0x02, 0x00, 0x00, 0x00, USB_MAX_EP0_SIZE, 0x01, 0x00
};
_Static_assert(sizeof(gamepadReportDescriptor) == 81U,
"DInput gamepad report descriptor length changed");
_Static_assert(sizeof(setupReportDescriptor) == 34U,
"Setup report descriptor length changed");
_Static_assert(sizeof(rawReportDescriptor) == 21U,
"Raw report descriptor length changed");
static uint8_t DINPUT_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
USBD_DINPUT_HandleTypeDef *handle;
UNUSED(cfgidx);
pdev->pClassData = USBD_malloc(sizeof(USBD_DINPUT_HandleTypeDef));
if (pdev->pClassData == NULL)
{
return USBD_FAIL;
}
handle = (USBD_DINPUT_HandleTypeDef *)pdev->pClassData;
memset(handle, 0, sizeof(*handle));
USBD_LL_OpenEP(pdev, DINPUT_GAMEPAD_EPIN_ADDR,
USBD_EP_TYPE_INTR, DINPUT_EP_SIZE);
pdev->ep_in[DINPUT_GAMEPAD_EPIN_ADDR & 0x0FU].is_used = 1U;
USBD_LL_OpenEP(pdev, DINPUT_SETUP_EPIN_ADDR,
USBD_EP_TYPE_INTR, DINPUT_EP_SIZE);
pdev->ep_in[DINPUT_SETUP_EPIN_ADDR & 0x0FU].is_used = 1U;
USBD_LL_OpenEP(pdev, DINPUT_SETUP_EPOUT_ADDR,
USBD_EP_TYPE_INTR, DINPUT_EP_SIZE);
pdev->ep_out[DINPUT_SETUP_EPOUT_ADDR & 0x0FU].is_used = 1U;
USBD_LL_OpenEP(pdev, DINPUT_RAW_EPIN_ADDR,
USBD_EP_TYPE_INTR, DINPUT_EP_SIZE);
pdev->ep_in[DINPUT_RAW_EPIN_ADDR & 0x0FU].is_used = 1U;
/* Stub HID data paths: unused IN endpoints stay at NAK. Setup OUT packets
* are acknowledged and discarded so the interface can start cleanly. */
USBD_LL_PrepareReceive(pdev, DINPUT_SETUP_EPOUT_ADDR,
dinputSetupOutBuffer, DINPUT_EP_SIZE);
return USBD_OK;
}
static uint8_t DINPUT_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx)
{
UNUSED(cfgidx);
USBD_LL_CloseEP(pdev, DINPUT_GAMEPAD_EPIN_ADDR);
USBD_LL_CloseEP(pdev, DINPUT_SETUP_EPIN_ADDR);
USBD_LL_CloseEP(pdev, DINPUT_SETUP_EPOUT_ADDR);
USBD_LL_CloseEP(pdev, DINPUT_RAW_EPIN_ADDR);
pdev->ep_in[DINPUT_GAMEPAD_EPIN_ADDR & 0x0FU].is_used = 0U;
pdev->ep_in[DINPUT_SETUP_EPIN_ADDR & 0x0FU].is_used = 0U;
pdev->ep_out[DINPUT_SETUP_EPOUT_ADDR & 0x0FU].is_used = 0U;
pdev->ep_in[DINPUT_RAW_EPIN_ADDR & 0x0FU].is_used = 0U;
if (pdev->pClassData != NULL)
{
USBD_free(pdev->pClassData);
pdev->pClassData = NULL;
}
return USBD_OK;
}
static uint8_t DINPUT_Setup(USBD_HandleTypeDef *pdev,
USBD_SetupReqTypedef *req)
{
USBD_DINPUT_HandleTypeDef *handle =
(USBD_DINPUT_HandleTypeDef *)pdev->pClassData;
uint8_t interface = (uint8_t)req->wIndex;
uint16_t status = 0U;
uint8_t *descriptor = NULL;
uint16_t length = 0U;
if ((handle == NULL) || (interface > 2U))
{
USBD_CtlError(pdev, req);
return USBD_FAIL;
}
if ((req->bmRequest & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_CLASS)
{
/* The original Setup/Raw protocols are intentionally not implemented. */
if (interface != 0U)
{
USBD_CtlError(pdev, req);
return USBD_FAIL;
}
switch (req->bRequest)
{
case HID_REQ_SET_PROTOCOL:
handle->protocol = (uint8_t)req->wValue;
return USBD_OK;
case HID_REQ_GET_PROTOCOL:
USBD_CtlSendData(pdev, &handle->protocol, 1U);
return USBD_OK;
case HID_REQ_SET_IDLE:
handle->idle = (uint8_t)(req->wValue >> 8);
return USBD_OK;
case HID_REQ_GET_IDLE:
USBD_CtlSendData(pdev, &handle->idle, 1U);
return USBD_OK;
case HID_REQ_GET_REPORT:
case HID_REQ_SET_REPORT:
default:
USBD_CtlError(pdev, req);
return USBD_FAIL;
}
}
if ((req->bmRequest & USB_REQ_TYPE_MASK) == USB_REQ_TYPE_STANDARD)
{
switch (req->bRequest)
{
case USB_REQ_GET_STATUS:
USBD_CtlSendData(pdev, (uint8_t *)&status, sizeof(status));
return USBD_OK;
case USB_REQ_GET_INTERFACE:
USBD_CtlSendData(pdev, &handle->altSetting[interface], 1U);
return USBD_OK;
case USB_REQ_SET_INTERFACE:
if (req->wValue == 0U)
{
handle->altSetting[interface] = 0U;
return USBD_OK;
}
break;
case USB_REQ_GET_DESCRIPTOR:
if ((req->wValue >> 8) == HID_REPORT_DESC)
{
if (interface == 0U)
{
descriptor = gamepadReportDescriptor;
length = sizeof(gamepadReportDescriptor);
}
else if (interface == 1U)
{
descriptor = setupReportDescriptor;
length = sizeof(setupReportDescriptor);
}
else
{
descriptor = rawReportDescriptor;
length = sizeof(rawReportDescriptor);
}
}
else if ((req->wValue >> 8) == HID_DESCRIPTOR_TYPE)
{
static const uint8_t hidOffsets[3] = {18U, 43U, 75U};
descriptor = &dinputConfigDescriptor[hidOffsets[interface]];
length = 9U;
}
if (descriptor != NULL)
{
USBD_CtlSendData(pdev, descriptor, MIN(length, req->wLength));
return USBD_OK;
}
break;
default:
break;
}
}
USBD_CtlError(pdev, req);
return USBD_FAIL;
}
uint8_t USBD_DINPUT_SendReport(USBD_HandleTypeDef *pdev,
uint8_t *report,
uint16_t len)
{
USBD_DINPUT_HandleTypeDef *handle =
(USBD_DINPUT_HandleTypeDef *)pdev->pClassData;
if ((pdev->dev_state == USBD_STATE_CONFIGURED) &&
(handle != NULL) && (handle->busy == 0U) &&
(len == DINPUT_REPORT_SIZE))
{
handle->busy = 1U;
memcpy(dinputInBuffer, report, DINPUT_REPORT_SIZE);
return USBD_LL_Transmit(pdev, DINPUT_GAMEPAD_EPIN_ADDR,
dinputInBuffer, len);
}
return USBD_OK;
}
static uint8_t DINPUT_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
if ((epnum == (DINPUT_GAMEPAD_EPIN_ADDR & 0x0FU)) &&
(pdev->pClassData != NULL))
{
((USBD_DINPUT_HandleTypeDef *)pdev->pClassData)->busy = 0U;
return USBD_OK;
}
return USBD_FAIL;
}
static uint8_t DINPUT_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
if (epnum == (DINPUT_SETUP_EPOUT_ADDR & 0x0FU))
{
USBD_LL_PrepareReceive(pdev, DINPUT_SETUP_EPOUT_ADDR,
dinputSetupOutBuffer, DINPUT_EP_SIZE);
return USBD_OK;
}
return USBD_FAIL;
}
static uint8_t *DINPUT_GetCfgDesc(uint16_t *length)
{
*length = sizeof(dinputConfigDescriptor);
return dinputConfigDescriptor;
}
static uint8_t *DINPUT_GetDeviceQualifierDesc(uint16_t *length)
{
*length = sizeof(dinputDeviceQualifierDescriptor);
return dinputDeviceQualifierDescriptor;
}
#if (USBD_SUPPORT_USER_STRING_DESC == 1U)
static uint8_t *DINPUT_GetUserString(USBD_HandleTypeDef *pdev,
uint8_t index,
uint16_t *length)
{
const char *text;
UNUSED(pdev);
if (index == 0x06U)
{
text = "Setup";
}
else if (index == 0x07U)
{
text = "Raw";
}
else
{
*length = 0U;
return NULL;
}
USBD_GetString((uint8_t *)text, dinputStringBuffer, length);
return dinputStringBuffer;
}
#endif