Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88a84fe6f2 | ||
|
|
91906866a8 | ||
|
|
22cabd06dc | ||
|
|
bf52296e60 | ||
|
|
9bdc0dbb33 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,5 +3,4 @@ build
|
||||
release
|
||||
generated
|
||||
tools
|
||||
.ignore
|
||||
src/usbh/tusb_hid/experiment
|
||||
.ignore
|
||||
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
[submodule "lib/Pico-PIO-USB"]
|
||||
path = lib/Pico-PIO-USB
|
||||
url = https://github.com/sekigon-gonnoc/Pico-PIO-USB.git
|
||||
[submodule "lib/tinyusb"]
|
||||
path = lib/tinyusb
|
||||
url = https://github.com/hathach/tinyusb.git
|
||||
@@ -40,6 +40,7 @@ file(GLOB_RECURSE SOURCES
|
||||
"src/usbd/shared/*"
|
||||
"src/usbd/switch/*"
|
||||
"src/usbd/xboxog/*"
|
||||
"src/usbd/dinput/*"
|
||||
"src/usbd/xinput/*")
|
||||
|
||||
# Firmware
|
||||
@@ -52,25 +53,15 @@ target_include_directories(${NAME} PUBLIC
|
||||
|
||||
include_directories(
|
||||
lib/)
|
||||
# src/usbh
|
||||
# src/usbd)
|
||||
|
||||
#------- Comment out the following line if compiling for the normal Pi Pico -------#
|
||||
|
||||
# add_compile_definitions(FEATHER_RP2040)
|
||||
add_compile_definitions(FEATHER_RP2040)
|
||||
|
||||
#------- USB host data +/- will be GPIO 0/1 ---------------------------------------#
|
||||
|
||||
|
||||
#------ These are your options for platforms -----#
|
||||
#------ Uncomment only one at a time -------------#
|
||||
|
||||
add_compile_definitions(HOST_ORIGINAL_XBOX)
|
||||
# add_compile_definitions(HOST_XINPUT)
|
||||
# add_compile_definitions(HOST_NINTENDO_SWITCH)
|
||||
# add_compile_definitions(HOST_PLAYSTATION_3)
|
||||
# add_compile_definitions(HOST_PLAYSTATION_CLASSIC)
|
||||
# add_compile_definitions(HOST_DEBUG) # CDC device, include log.h and use log() as you would printf()
|
||||
#------- CDC MODE --------#
|
||||
# add_compile_definitions(HOST_DEBUG) # makes RP2040 a CDC device, include "utilities/log.h" and use log() as you would printf()
|
||||
|
||||
|
||||
target_link_libraries(${NAME}
|
||||
@@ -90,6 +81,7 @@ target_link_libraries(${NAME}
|
||||
tinyusb_host
|
||||
tinyusb_pico_pio_usb
|
||||
CRC32
|
||||
hid_parser
|
||||
cmsis_core
|
||||
)
|
||||
|
||||
|
||||
48
README.md
48
README.md
@@ -1,43 +1,59 @@
|
||||
# OGX-Mini
|
||||

|
||||
|
||||
Firmware for the RP2040, setup for the [Adafruit Feather USB Host board](https://www.adafruit.com/product/5723) (an be used with the Pi Pico), capable of emulating gamepads for
|
||||
Firmware for the RP2040, setup for the [Adafruit Feather USB Host board](https://www.adafruit.com/product/5723) (can be used with the Pi Pico as well), capable of emulating gamepads for
|
||||
- Original Xbox
|
||||
- XInput (not Xbox 360)
|
||||
- Nintendo Switch (docked)
|
||||
- Playstation Classic
|
||||
- Playstation 3
|
||||
- Nintendo Switch (docked)
|
||||
- XInput (not Xbox 360)
|
||||
- Playstation Classic
|
||||
|
||||
Currently there's no way to switch what device is being emulated on the fly, so the firmware must be compiled specifically for whichever platform you'd like to play on. As long as that's the case, I'll provide compiled .uf2 files for each platform in [Releases](https://github.com/wiredopposite/OGX-Mini/releases).
|
||||
|
||||
# Supported devices
|
||||
## Wired controllers
|
||||
## Supported devices
|
||||
### Wired controllers
|
||||
- Original Xbox Duke and S
|
||||
- Xbox 360, One, Series, and Elite
|
||||
- Dualshock 4 (PS4)
|
||||
- Dualsense (PS5, Dualsense Edge should work but it's untested)
|
||||
- Nintendo Switch Pro
|
||||
- Nintnedo Switch wired (tested with PowerA brand, no rumble)
|
||||
- Nintendo Switch wired (tested with PowerA brand)
|
||||
- Nintendo 64 USB (experimental, tested with RetroLink brand)
|
||||
- Playstation Classic
|
||||
|
||||
## Wireless adapters
|
||||
### Wireless adapters
|
||||
- Xbox 360 PC adapter (Microsoft or clones, syncs 1 controller)
|
||||
- 8Bitdo v1 and v2 Bluetooth adapters
|
||||
- Most wireless adapters that present themselves as Switch/XInput/Playstation controllers should work
|
||||
- Most wireless adapters that present themselves as Switch/XInput/PlayStation controllers should work
|
||||
|
||||
Note: There are some third party controllers that use incorrect VID and PIDs, these will not work correctly.
|
||||
Note: There are some third party controllers that can change their VID/PID, these might not work correctly.
|
||||
|
||||
# Adding supported controllers
|
||||
## Changing input mode
|
||||
By default the input mode is set to OG Xbox, you must hold a button combo for 3 seconds to change which platform you want to play on. Your chosen input mode will persist after powering off the device.
|
||||
|
||||
Start = Plus (Switch) = Options (Dualsense/DS4)
|
||||
|
||||
### XInput
|
||||
Start + Dpad Up
|
||||
### Original Xbox
|
||||
Start + Dpad Right
|
||||
### Switch
|
||||
Start + Dpad Down
|
||||
### PlayStation 3
|
||||
Start + Dpad Left
|
||||
### PlayStation Classic
|
||||
Start + A (Cross for PlayStation and B for Switch gamepads)
|
||||
|
||||
After a new mode is stored, the RP2040 will reset itself so you don't need to unplug it.
|
||||
|
||||
## Adding supported controllers
|
||||
If your third party controller isn't working, but the original version is listed above, send me the device's VID and PID and I'll add it so it's recognized properly.
|
||||
|
||||
# Compiling
|
||||
## Compiling
|
||||
You can compile this for the Pi Pico by commenting out this line in CMakeLists.txt
|
||||
`add_compile_definitions(FEATHER_RP2040)`
|
||||
That will set the D+ and D- host pins to GPIO 0 and 1. Below that you can uncomment whichever platform (OG Xbox, Xinput, etc.) you'd like to use.
|
||||
That will set the D+ and D- host pins to GPIO 0 and 1.
|
||||
|
||||
Here's a diagram of how you'd use the Pico:
|
||||

|
||||
|
||||
# Special thanks
|
||||
## Special thanks
|
||||
Thank you to Ryzee119 and the OpenStickCommunity, without their work this project would not exist.
|
||||
@@ -1 +1,2 @@
|
||||
add_subdirectory(CRC32)
|
||||
add_subdirectory(CRC32)
|
||||
add_subdirectory(hid_parser)
|
||||
8
lib/hid_parser/CMakeLists.txt
Normal file
8
lib/hid_parser/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
add_library(hid_parser
|
||||
src/hid_parser.c
|
||||
src/hid_parser.h
|
||||
)
|
||||
|
||||
target_include_directories(hid_parser INTERFACE
|
||||
src
|
||||
)
|
||||
444
lib/hid_parser/src/hid_parser.c
Normal file
444
lib/hid_parser/src/hid_parser.c
Normal file
@@ -0,0 +1,444 @@
|
||||
|
||||
/****************************************************************************
|
||||
* Adapted from the LUFA Library:
|
||||
*
|
||||
* Copyright 2011 Dean Camera (dean [at] fourwalledcubicle [dot] com)
|
||||
* dean [at] fourwalledcubicle [dot] com, www.lufa-lib.org
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this
|
||||
* software and its documentation for any purpose is hereby granted
|
||||
* without fee, provided that the above copyright notice appear in
|
||||
* all copies and that both that the copyright notice and this
|
||||
* permission notice and warranty disclaimer appear in supporting
|
||||
* documentation, and that the name of the author not be used in
|
||||
* advertising or publicity pertaining to distribution of the
|
||||
* software without specific, written prior permission.
|
||||
*
|
||||
* The author disclaim all warranties with regard to this
|
||||
* software, including all implied warranties of merchantability
|
||||
* and fitness. In no event shall the author be liable for any
|
||||
* special, indirect or consequential damages or any damages
|
||||
* whatsoever resulting from loss of use, data or profits, whether
|
||||
* in an action of contract, negligence or other tortious action,
|
||||
* arising out of or in connection with the use or performance of
|
||||
* this software.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include "hid_parser.h"
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifdef DYNAMIC
|
||||
#include <malloc.h>
|
||||
#define ACQUIRE_AND_RELEASE(structure, size) \
|
||||
__attribute__ ((always_inline)) static inline structure##_t* acquire_##structure() { return malloc(sizeof(structure##_t)); } \
|
||||
__attribute__ ((always_inline)) static inline void release_##structure(structure##_t* pointer) { free(pointer); }
|
||||
#else
|
||||
#define ACQUIRE_AND_RELEASE(structure, size) \
|
||||
enum { MAX_##structure = size }; \
|
||||
static structure##_t structure##s[MAX_##structure]; \
|
||||
static bool structure##sAcquired[MAX_##structure]; \
|
||||
static structure##_t* acquire_##structure() \
|
||||
{\
|
||||
for(uint8_t i=0; i < MAX_##structure; i++) \
|
||||
{\
|
||||
if(!structure##sAcquired[i])\
|
||||
{\
|
||||
structure##sAcquired[i] = true;\
|
||||
return &structure##s[i];\
|
||||
}\
|
||||
}\
|
||||
assert(false);\
|
||||
return NULL;\
|
||||
}\
|
||||
static void release_##structure(structure##_t* pointer)\
|
||||
{\
|
||||
structure##sAcquired[pointer - structure##s] = false;\
|
||||
}
|
||||
#endif
|
||||
|
||||
ACQUIRE_AND_RELEASE(HID_ReportSizeInfo, 100);
|
||||
ACQUIRE_AND_RELEASE(HID_CollectionPath, 25);
|
||||
ACQUIRE_AND_RELEASE(HID_ReportInfo, 1);
|
||||
ACQUIRE_AND_RELEASE(HID_ReportItem, 50);
|
||||
|
||||
void USB_FreeReportInfo(HID_ReportInfo_t *ReportInfo)
|
||||
{
|
||||
if (ReportInfo)
|
||||
{
|
||||
HID_ReportItem_t *item = ReportInfo->FirstReportItem;
|
||||
while (item)
|
||||
{
|
||||
HID_ReportItem_t *current = item;
|
||||
item = item->Next;
|
||||
release_HID_ReportItem(current);
|
||||
}
|
||||
release_HID_ReportInfo(ReportInfo);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t USB_ProcessHIDReport(const uint8_t *ReportData,
|
||||
uint16_t ReportSize,
|
||||
HID_ReportInfo_t **ParserDataOut)
|
||||
{
|
||||
HID_ReportSizeInfo_t *FirstReportIDSize = acquire_HID_ReportSizeInfo();
|
||||
HID_CollectionPath_t *FirstCollectionPath = acquire_HID_CollectionPath();
|
||||
memset(FirstCollectionPath, 0, sizeof(HID_CollectionPath_t));
|
||||
HID_ReportInfo_t *ParserData = acquire_HID_ReportInfo();
|
||||
HID_StateTable_t StateTable[HID_STATETABLE_STACK_DEPTH];
|
||||
HID_StateTable_t *CurrStateTable = &StateTable[0];
|
||||
HID_CollectionPath_t *CurrCollectionPath = NULL;
|
||||
HID_ReportSizeInfo_t *CurrReportIDInfo = FirstReportIDSize;
|
||||
uint16_t UsageList[HID_USAGE_STACK_DEPTH];
|
||||
uint8_t UsageListSize = 0;
|
||||
HID_MinMax_t UsageMinMax = {0, 0};
|
||||
|
||||
memset(ParserData, 0x00, sizeof(HID_ReportInfo_t));
|
||||
memset(CurrStateTable, 0x00, sizeof(HID_StateTable_t));
|
||||
memset(CurrReportIDInfo, 0x00, sizeof(HID_ReportSizeInfo_t));
|
||||
|
||||
ParserData->TotalDeviceReports = 1;
|
||||
uint8_t Result = HID_PARSE_Successful;
|
||||
|
||||
while (ReportSize)
|
||||
{
|
||||
uint8_t HIDReportItem = *ReportData;
|
||||
uint32_t ReportItemData;
|
||||
|
||||
ReportData++;
|
||||
ReportSize--;
|
||||
|
||||
switch (HIDReportItem & HID_RI_DATA_SIZE_MASK)
|
||||
{
|
||||
case HID_RI_DATA_BITS_32:
|
||||
ReportItemData = (((uint32_t)ReportData[3] << 24) | ((uint32_t)ReportData[2] << 16) |
|
||||
((uint16_t)ReportData[1] << 8) | ReportData[0]);
|
||||
ReportSize -= 4;
|
||||
ReportData += 4;
|
||||
break;
|
||||
|
||||
case HID_RI_DATA_BITS_16:
|
||||
ReportItemData = (((uint16_t)ReportData[1] << 8) | (ReportData[0]));
|
||||
ReportSize -= 2;
|
||||
ReportData += 2;
|
||||
break;
|
||||
|
||||
case HID_RI_DATA_BITS_8:
|
||||
ReportItemData = ReportData[0];
|
||||
ReportSize -= 1;
|
||||
ReportData += 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
ReportItemData = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (HIDReportItem & (HID_RI_TYPE_MASK | HID_RI_TAG_MASK))
|
||||
{
|
||||
case HID_RI_PUSH(0):
|
||||
if (CurrStateTable == &StateTable[HID_STATETABLE_STACK_DEPTH - 1])
|
||||
{
|
||||
Result = HID_PARSE_HIDStackOverflow;
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy((CurrStateTable + 1),
|
||||
CurrStateTable,
|
||||
sizeof(HID_StateTable_t));
|
||||
|
||||
CurrStateTable++;
|
||||
break;
|
||||
|
||||
case HID_RI_POP(0):
|
||||
if (CurrStateTable == &StateTable[0])
|
||||
{
|
||||
Result = HID_PARSE_HIDStackUnderflow;
|
||||
break;
|
||||
}
|
||||
|
||||
CurrStateTable--;
|
||||
break;
|
||||
|
||||
case HID_RI_USAGE_PAGE(0):
|
||||
CurrStateTable->Attributes.Usage.Page = ReportItemData;
|
||||
break;
|
||||
|
||||
case HID_RI_LOGICAL_MINIMUM(0):
|
||||
CurrStateTable->Attributes.Logical.Minimum = ReportItemData;
|
||||
break;
|
||||
|
||||
case HID_RI_LOGICAL_MAXIMUM(0):
|
||||
CurrStateTable->Attributes.Logical.Maximum = ReportItemData;
|
||||
break;
|
||||
|
||||
case HID_RI_PHYSICAL_MINIMUM(0):
|
||||
CurrStateTable->Attributes.Physical.Minimum = ReportItemData;
|
||||
break;
|
||||
|
||||
case HID_RI_PHYSICAL_MAXIMUM(0):
|
||||
CurrStateTable->Attributes.Physical.Maximum = ReportItemData;
|
||||
break;
|
||||
|
||||
case HID_RI_UNIT_EXPONENT(0):
|
||||
CurrStateTable->Attributes.Unit.Exponent = ReportItemData;
|
||||
break;
|
||||
|
||||
case HID_RI_UNIT(0):
|
||||
CurrStateTable->Attributes.Unit.Type = ReportItemData;
|
||||
break;
|
||||
|
||||
case HID_RI_REPORT_SIZE(0):
|
||||
CurrStateTable->Attributes.BitSize = ReportItemData;
|
||||
break;
|
||||
|
||||
case HID_RI_REPORT_COUNT(0):
|
||||
CurrStateTable->ReportCount = ReportItemData;
|
||||
break;
|
||||
|
||||
case HID_RI_REPORT_ID(0):
|
||||
CurrStateTable->ReportID = ReportItemData;
|
||||
|
||||
if (ParserData->UsingReportIDs)
|
||||
{
|
||||
CurrReportIDInfo = NULL;
|
||||
HID_ReportSizeInfo_t *iterator = FirstReportIDSize;
|
||||
while (true)
|
||||
{
|
||||
if (iterator->ReportID == CurrStateTable->ReportID)
|
||||
{
|
||||
CurrReportIDInfo = iterator;
|
||||
break;
|
||||
}
|
||||
if (!iterator->Next)
|
||||
break;
|
||||
iterator = iterator->Next;
|
||||
}
|
||||
|
||||
if (CurrReportIDInfo == NULL)
|
||||
{
|
||||
ParserData->TotalDeviceReports++;
|
||||
iterator->Next = CurrReportIDInfo = acquire_HID_ReportSizeInfo();
|
||||
memset(CurrReportIDInfo, 0x00, sizeof(HID_ReportSizeInfo_t));
|
||||
}
|
||||
}
|
||||
|
||||
ParserData->UsingReportIDs = true;
|
||||
|
||||
CurrReportIDInfo->ReportID = CurrStateTable->ReportID;
|
||||
break;
|
||||
|
||||
case HID_RI_USAGE(0):
|
||||
if (UsageListSize == HID_USAGE_STACK_DEPTH)
|
||||
{
|
||||
Result = HID_PARSE_UsageListOverflow;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((HIDReportItem & HID_RI_DATA_SIZE_MASK) == HID_RI_DATA_BITS_32)
|
||||
CurrStateTable->Attributes.Usage.Page = (ReportItemData >> 16);
|
||||
|
||||
UsageList[UsageListSize++] = ReportItemData;
|
||||
break;
|
||||
|
||||
case HID_RI_USAGE_MINIMUM(0):
|
||||
UsageMinMax.Minimum = ReportItemData;
|
||||
break;
|
||||
|
||||
case HID_RI_USAGE_MAXIMUM(0):
|
||||
UsageMinMax.Maximum = ReportItemData;
|
||||
break;
|
||||
|
||||
case HID_RI_COLLECTION(0):
|
||||
if (CurrCollectionPath == NULL)
|
||||
{
|
||||
CurrCollectionPath = FirstCollectionPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
HID_CollectionPath_t *ParentCollectionPath = CurrCollectionPath;
|
||||
|
||||
CurrCollectionPath = FirstCollectionPath;
|
||||
while (CurrCollectionPath->Next)
|
||||
{
|
||||
CurrCollectionPath = CurrCollectionPath->Next;
|
||||
}
|
||||
HID_CollectionPath_t *NewCollectionPath = acquire_HID_CollectionPath();
|
||||
CurrCollectionPath->Next = NewCollectionPath;
|
||||
CurrCollectionPath = NewCollectionPath;
|
||||
memset(CurrCollectionPath, 0, sizeof(HID_CollectionPath_t));
|
||||
CurrCollectionPath->Parent = ParentCollectionPath;
|
||||
}
|
||||
|
||||
CurrCollectionPath->Type = ReportItemData;
|
||||
CurrCollectionPath->Usage.Page = CurrStateTable->Attributes.Usage.Page;
|
||||
|
||||
if (UsageListSize)
|
||||
{
|
||||
CurrCollectionPath->Usage.Usage = UsageList[0];
|
||||
|
||||
for (uint8_t i = 1; i < UsageListSize; i++)
|
||||
UsageList[i - 1] = UsageList[i];
|
||||
|
||||
UsageListSize--;
|
||||
}
|
||||
else if (UsageMinMax.Minimum <= UsageMinMax.Maximum)
|
||||
{
|
||||
CurrCollectionPath->Usage.Usage = UsageMinMax.Minimum++;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case HID_RI_END_COLLECTION(0):
|
||||
if (CurrCollectionPath == NULL)
|
||||
{
|
||||
Result = HID_PARSE_UnexpectedEndCollection;
|
||||
break;
|
||||
}
|
||||
CurrCollectionPath = CurrCollectionPath->Parent;
|
||||
if (CurrCollectionPath)
|
||||
{
|
||||
release_HID_CollectionPath(CurrCollectionPath->Next);
|
||||
CurrCollectionPath->Next = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case HID_RI_INPUT(0):
|
||||
case HID_RI_OUTPUT(0):
|
||||
case HID_RI_FEATURE(0):
|
||||
for (uint8_t ReportItemNum = 0; ReportItemNum < CurrStateTable->ReportCount; ReportItemNum++)
|
||||
{
|
||||
HID_ReportItem_t NewReportItem;
|
||||
|
||||
memcpy(&NewReportItem.Attributes,
|
||||
&CurrStateTable->Attributes,
|
||||
sizeof(HID_ReportItem_Attributes_t));
|
||||
|
||||
NewReportItem.ItemFlags = ReportItemData;
|
||||
NewReportItem.ReportID = CurrStateTable->ReportID;
|
||||
if (UsageListSize)
|
||||
{
|
||||
NewReportItem.Attributes.Usage.Usage = UsageList[0];
|
||||
|
||||
for (uint8_t i = 1; i < UsageListSize; i++)
|
||||
UsageList[i - 1] = UsageList[i];
|
||||
|
||||
UsageListSize--;
|
||||
}
|
||||
else if (UsageMinMax.Minimum <= UsageMinMax.Maximum)
|
||||
{
|
||||
NewReportItem.Attributes.Usage.Usage = UsageMinMax.Minimum++;
|
||||
}
|
||||
|
||||
uint8_t ItemTypeTag = (HIDReportItem & (HID_RI_TYPE_MASK | HID_RI_TAG_MASK));
|
||||
|
||||
if (ItemTypeTag == HID_RI_INPUT(0))
|
||||
NewReportItem.ItemType = HID_REPORT_ITEM_In;
|
||||
else if (ItemTypeTag == HID_RI_OUTPUT(0))
|
||||
NewReportItem.ItemType = HID_REPORT_ITEM_Out;
|
||||
else
|
||||
NewReportItem.ItemType = HID_REPORT_ITEM_Feature;
|
||||
|
||||
NewReportItem.BitOffset = CurrReportIDInfo->ReportSizeBits[NewReportItem.ItemType];
|
||||
|
||||
CurrReportIDInfo->ReportSizeBits[NewReportItem.ItemType] += CurrStateTable->Attributes.BitSize;
|
||||
|
||||
ParserData->LargestReportSizeBits = MAX(ParserData->LargestReportSizeBits, CurrReportIDInfo->ReportSizeBits[NewReportItem.ItemType]);
|
||||
|
||||
if (!(ReportItemData & HID_IOF_CONSTANT) && CALLBACK_HIDParser_FilterHIDReportItem(&NewReportItem))
|
||||
{
|
||||
if (!ParserData->FirstReportItem)
|
||||
{
|
||||
ParserData->FirstReportItem = acquire_HID_ReportItem();
|
||||
ParserData->LastReportItem = ParserData->FirstReportItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
ParserData->LastReportItem->Next = acquire_HID_ReportItem();
|
||||
ParserData->LastReportItem = ParserData->LastReportItem->Next;
|
||||
}
|
||||
memcpy(ParserData->LastReportItem, &NewReportItem, sizeof(HID_ReportItem_t));
|
||||
ParserData->LastReportItem->Next = NULL;
|
||||
ParserData->TotalReportItems++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (Result != HID_PARSE_Successful)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if ((HIDReportItem & HID_RI_TYPE_MASK) == HID_RI_TYPE_MAIN)
|
||||
{
|
||||
UsageMinMax.Minimum = 0;
|
||||
UsageMinMax.Maximum = 0;
|
||||
UsageListSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(ParserData->TotalReportItems))
|
||||
Result = HID_PARSE_NoUnfilteredReportItems;
|
||||
if (Result != HID_PARSE_Successful)
|
||||
{
|
||||
USB_FreeReportInfo(ParserData);
|
||||
}
|
||||
else
|
||||
{
|
||||
*ParserDataOut = ParserData;
|
||||
}
|
||||
|
||||
HID_ReportSizeInfo_t *iteratorReportIDSizes = FirstReportIDSize;
|
||||
while (iteratorReportIDSizes)
|
||||
{
|
||||
HID_ReportSizeInfo_t *temp = iteratorReportIDSizes->Next;
|
||||
release_HID_ReportSizeInfo(iteratorReportIDSizes);
|
||||
iteratorReportIDSizes = temp;
|
||||
}
|
||||
|
||||
HID_CollectionPath_t *iteratorCollectionPaths = FirstCollectionPath;
|
||||
while (iteratorCollectionPaths)
|
||||
{
|
||||
HID_CollectionPath_t *temp = iteratorCollectionPaths->Next;
|
||||
release_HID_CollectionPath(iteratorCollectionPaths);
|
||||
iteratorCollectionPaths = temp;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool USB_GetHIDReportItemInfo(uint16_t report_id, const uint8_t *ReportData,
|
||||
HID_ReportItem_t *const ReportItem)
|
||||
{
|
||||
if (ReportItem == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReportItem->ReportID != report_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t DataBitsRem = ReportItem->Attributes.BitSize;
|
||||
uint16_t CurrentBit = ReportItem->BitOffset;
|
||||
uint32_t BitMask = (1 << 0);
|
||||
|
||||
ReportItem->PreviousValue = ReportItem->Value;
|
||||
ReportItem->Value = 0;
|
||||
|
||||
while (DataBitsRem--)
|
||||
{
|
||||
if (ReportData[CurrentBit / 8] & (1 << (CurrentBit % 8)))
|
||||
ReportItem->Value |= BitMask;
|
||||
|
||||
CurrentBit++;
|
||||
BitMask <<= 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
424
lib/hid_parser/src/hid_parser.h
Normal file
424
lib/hid_parser/src/hid_parser.h
Normal file
@@ -0,0 +1,424 @@
|
||||
/****************************************************************************
|
||||
* Adapted from the LUFA Library:
|
||||
*
|
||||
* Copyright 2011 Dean Camera (dean [at] fourwalledcubicle [dot] com)
|
||||
* dean [at] fourwalledcubicle [dot] com, www.lufa-lib.org
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this
|
||||
* software and its documentation for any purpose is hereby granted
|
||||
* without fee, provided that the above copyright notice appear in
|
||||
* all copies and that both that the copyright notice and this
|
||||
* permission notice and warranty disclaimer appear in supporting
|
||||
* documentation, and that the name of the author not be used in
|
||||
* advertising or publicity pertaining to distribution of the
|
||||
* software without specific, written prior permission.
|
||||
*
|
||||
* The author disclaim all warranties with regard to this
|
||||
* software, including all implied warranties of merchantability
|
||||
* and fitness. In no event shall the author be liable for any
|
||||
* special, indirect or consequential damages or any damages
|
||||
* whatsoever resulting from loss of use, data or profits, whether
|
||||
* in an action of contract, negligence or other tortious action,
|
||||
* arising out of or in connection with the use or performance of
|
||||
* this software.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/** \file
|
||||
* \brief USB Human Interface Device (HID) Class report descriptor parser.
|
||||
*
|
||||
* This file allows for the easy parsing of complex HID report descriptors, which describes the data that
|
||||
* a HID device transmits to the host. It also provides an easy API for extracting and processing the data
|
||||
* elements inside a HID report sent from an attached HID device.
|
||||
*/
|
||||
|
||||
/** \ingroup Group_USB
|
||||
* \defgroup Group_HIDParser HID Report Parser
|
||||
* \brief USB Human Interface Device (HID) Class report descriptor parser.
|
||||
*
|
||||
* \section Sec_HIDParser_Dependencies Module Source Dependencies
|
||||
* The following files must be built with any user project that uses this module:
|
||||
* - LUFA/Drivers/USB/Class/Host/HIDParser.c <i>(Makefile source module name: LUFA_SRC_USB)</i>
|
||||
*
|
||||
* \section Sec_HIDParser_ModDescription Module Description
|
||||
* Human Interface Device (HID) class report descriptor parser. This module implements a parser than is
|
||||
* capable of processing a complete HID report descriptor, and outputting a flat structure containing the
|
||||
* contents of the report in an a more friendly format. The parsed data may then be further processed and used
|
||||
* within an application to process sent and received HID reports to and from an attached HID device.
|
||||
*
|
||||
* A HID report descriptor consists of a set of HID report items, which describe the function and layout
|
||||
* of data exchanged between a HID device and a host, including both the physical encoding of each item
|
||||
* (such as a button, key press or joystick axis) in the sent and received data packets - known as "reports" -
|
||||
* as well as other information about each item such as the usages, data range, physical location and other
|
||||
* characteristics. In this way a HID device can retain a high degree of flexibility in its capabilities, as it
|
||||
* is not forced to comply with a given report layout or feature-set.
|
||||
*
|
||||
* This module also contains routines for the processing of data in an actual HID report, using the parsed report
|
||||
* descriptor data as a guide for the encoding.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* Includes: */
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Enable C linkage for C++ Compilers: */
|
||||
#if defined(__cplusplus)
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
/* Macros: */
|
||||
#if !defined(HID_STATETABLE_STACK_DEPTH) || defined(__DOXYGEN__)
|
||||
/** Constant indicating the maximum stack depth of the state table. A larger state table
|
||||
* allows for more PUSH/POP report items to be nested, but consumes more memory. By default
|
||||
* this is set to 2 levels (allowing non-nested PUSH items) but this can be overridden by
|
||||
* defining \c HID_STATETABLE_STACK_DEPTH to another value in the user project makefile, passing the
|
||||
* define to the compiler using the -D compiler switch.
|
||||
*/
|
||||
#define HID_STATETABLE_STACK_DEPTH 2
|
||||
#endif
|
||||
|
||||
#if !defined(HID_USAGE_STACK_DEPTH) || defined(__DOXYGEN__)
|
||||
/** Constant indicating the maximum stack depth of the usage table. A larger usage table
|
||||
* allows for more USAGE items to be indicated sequentially for REPORT COUNT entries of more than
|
||||
* one, but requires more stack space. By default this is set to 8 levels (allowing for a report
|
||||
* item with a count of 8) but this can be overridden by defining \c HID_USAGE_STACK_DEPTH to another
|
||||
* value in the user project makefile, passing the define to the compiler using the -D compiler
|
||||
* switch.
|
||||
*/
|
||||
#define HID_USAGE_STACK_DEPTH 8
|
||||
#endif
|
||||
|
||||
/** Returns the value a given HID report item (once its value has been fetched via \ref USB_GetHIDReportItemInfo())
|
||||
* left-aligned to the given data type. This allows for signed data to be interpreted correctly, by shifting the data
|
||||
* leftwards until the data's sign bit is in the correct position.
|
||||
*
|
||||
* \param[in] ReportItem HID Report Item whose retrieved value is to be aligned.
|
||||
* \param[in] Type Data type to align the HID report item's value to.
|
||||
*
|
||||
* \return Left-aligned data of the given report item's pre-retrieved value for the given datatype.
|
||||
*/
|
||||
#define HID_ALIGN_DATA(ReportItem, Type) ((Type)(ReportItem->Value << ((8 * sizeof(Type)) - ReportItem->Attributes.BitSize)))
|
||||
|
||||
/** Convenience macro to determine the larger of two values.
|
||||
*
|
||||
* \attention This macro should only be used with operands that do not have side effects from being evaluated
|
||||
* multiple times.
|
||||
*
|
||||
* \param[in] x First value to compare
|
||||
* \param[in] y First value to compare
|
||||
*
|
||||
* \return The larger of the two input parameters
|
||||
*/
|
||||
#if !defined(MAX) || defined(__DOXYGEN__)
|
||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
/** Convenience macro to determine the smaller of two values.
|
||||
*
|
||||
* \attention This macro should only be used with operands that do not have side effects from being evaluated
|
||||
* multiple times.
|
||||
*
|
||||
* \param[in] x First value to compare.
|
||||
* \param[in] y First value to compare.
|
||||
*
|
||||
* \return The smaller of the two input parameters
|
||||
*/
|
||||
#if !defined(MIN) || defined(__DOXYGEN__)
|
||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
#if !defined(CONCAT) || defined(__DOXYGEN__)
|
||||
/** Concatenates the given input into a single token, via the C Preprocessor.
|
||||
*
|
||||
* \param[in] x First item to concatenate.
|
||||
* \param[in] y Second item to concatenate.
|
||||
*
|
||||
* \return Concatenated version of the input.
|
||||
*/
|
||||
#define CONCAT(x, y) x##y
|
||||
|
||||
/** CConcatenates the given input into a single token after macro expansion, via the C Preprocessor.
|
||||
*
|
||||
* \param[in] x First item to concatenate.
|
||||
* \param[in] y Second item to concatenate.
|
||||
*
|
||||
* \return Concatenated version of the expanded input.
|
||||
*/
|
||||
#define CONCAT_EXPANDED(x, y) CONCAT(x, y)
|
||||
#endif
|
||||
|
||||
/* Public Interface - May be used in end-application: */
|
||||
/* Enums: */
|
||||
/** Enum for the possible error codes in the return value of the \ref USB_ProcessHIDReport() function. */
|
||||
enum HID_Parse_ErrorCodes_t
|
||||
{
|
||||
HID_PARSE_Successful = 0, /**< Successful parse of the HID report descriptor, no error. */
|
||||
HID_PARSE_HIDStackOverflow = 1, /**< More than \ref HID_STATETABLE_STACK_DEPTH nested PUSHes in the report. */
|
||||
HID_PARSE_HIDStackUnderflow = 2, /**< A POP was found when the state table stack was empty. */
|
||||
HID_PARSE_UnexpectedEndCollection = 3, /**< An END COLLECTION item found without matching COLLECTION item. */
|
||||
HID_PARSE_UsageListOverflow = 4, /**< More than \ref HID_USAGE_STACK_DEPTH usages listed in a row. */
|
||||
HID_PARSE_NoUnfilteredReportItems = 5, /**< All report items from the device were filtered by the filtering callback routine. */
|
||||
};
|
||||
|
||||
/* Private Interface - For use in library only: */
|
||||
#if !defined(__DOXYGEN__)
|
||||
/* Macros: */
|
||||
#define HID_RI_DATA_SIZE_MASK 0x03
|
||||
#define HID_RI_TYPE_MASK 0x0C
|
||||
#define HID_RI_TAG_MASK 0xF0
|
||||
|
||||
#define HID_RI_TYPE_MAIN 0x00
|
||||
#define HID_RI_TYPE_GLOBAL 0x04
|
||||
#define HID_RI_TYPE_LOCAL 0x08
|
||||
|
||||
#define HID_RI_DATA_BITS_0 0x00
|
||||
#define HID_RI_DATA_BITS_8 0x01
|
||||
#define HID_RI_DATA_BITS_16 0x02
|
||||
#define HID_RI_DATA_BITS_32 0x03
|
||||
#define HID_RI_DATA_BITS(DataBits) CONCAT_EXPANDED(HID_RI_DATA_BITS_, DataBits)
|
||||
|
||||
#define _HID_RI_ENCODE_0(Data)
|
||||
#define _HID_RI_ENCODE_8(Data) , (Data & 0xFF)
|
||||
#define _HID_RI_ENCODE_16(Data) \
|
||||
_HID_RI_ENCODE_8(Data) \
|
||||
_HID_RI_ENCODE_8(Data >> 8)
|
||||
#define _HID_RI_ENCODE_32(Data) \
|
||||
_HID_RI_ENCODE_16(Data) \
|
||||
_HID_RI_ENCODE_16(Data >> 16)
|
||||
#define _HID_RI_ENCODE(DataBits, ...) CONCAT_EXPANDED(_HID_RI_ENCODE_, DataBits(__VA_ARGS__))
|
||||
|
||||
#define _HID_RI_ENTRY(Type, Tag, DataBits, ...) (Type | Tag | HID_RI_DATA_BITS(DataBits)) _HID_RI_ENCODE(DataBits, (__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
/* Public Interface - May be used in end-application: */
|
||||
/* Macros: */
|
||||
/** \name HID Input, Output and Feature Report Descriptor Item Flags */
|
||||
/**@{*/
|
||||
#define HID_IOF_CONSTANT (1 << 0)
|
||||
#define HID_IOF_DATA (0 << 0)
|
||||
#define HID_IOF_VARIABLE (1 << 1)
|
||||
#define HID_IOF_ARRAY (0 << 1)
|
||||
#define HID_IOF_RELATIVE (1 << 2)
|
||||
#define HID_IOF_ABSOLUTE (0 << 2)
|
||||
#define HID_IOF_WRAP (1 << 3)
|
||||
#define HID_IOF_NO_WRAP (0 << 3)
|
||||
#define HID_IOF_NON_LINEAR (1 << 4)
|
||||
#define HID_IOF_LINEAR (0 << 4)
|
||||
#define HID_IOF_NO_PREFERRED_STATE (1 << 5)
|
||||
#define HID_IOF_PREFERRED_STATE (0 << 5)
|
||||
#define HID_IOF_NULLSTATE (1 << 6)
|
||||
#define HID_IOF_NO_NULL_POSITION (0 << 6)
|
||||
#define HID_IOF_VOLATILE (1 << 7)
|
||||
#define HID_IOF_NON_VOLATILE (0 << 7)
|
||||
#define HID_IOF_BUFFERED_BYTES (1 << 8)
|
||||
#define HID_IOF_BITFIELD (0 << 8)
|
||||
/**@}*/
|
||||
|
||||
/** \name HID Report Descriptor Item Macros */
|
||||
/**@{*/
|
||||
#define HID_RI_INPUT(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_MAIN, 0x80, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_OUTPUT(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_MAIN, 0x90, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_COLLECTION(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_MAIN, 0xA0, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_FEATURE(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_MAIN, 0xB0, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_END_COLLECTION(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_MAIN, 0xC0, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_USAGE_PAGE(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x00, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_LOGICAL_MINIMUM(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x10, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_LOGICAL_MAXIMUM(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x20, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_PHYSICAL_MINIMUM(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x30, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_PHYSICAL_MAXIMUM(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x40, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_UNIT_EXPONENT(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x50, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_UNIT(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x60, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_REPORT_SIZE(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x70, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_REPORT_ID(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x80, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_REPORT_COUNT(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0x90, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_PUSH(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0xA0, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_POP(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_GLOBAL, 0xB0, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_USAGE(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_LOCAL, 0x00, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_USAGE_MINIMUM(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_LOCAL, 0x10, DataBits, __VA_ARGS__)
|
||||
#define HID_RI_USAGE_MAXIMUM(DataBits, ...) _HID_RI_ENTRY(HID_RI_TYPE_LOCAL, 0x20, DataBits, __VA_ARGS__)
|
||||
/**@}*/
|
||||
|
||||
/* Type Defines: */
|
||||
/** \brief HID Parser Report Item Min/Max Structure.
|
||||
*
|
||||
* Type define for an attribute with both minimum and maximum values (e.g. Logical Min/Max).
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint32_t Minimum; /**< Minimum value for the attribute. */
|
||||
uint32_t Maximum; /**< Maximum value for the attribute. */
|
||||
} HID_MinMax_t;
|
||||
|
||||
/** \brief HID Parser Report Item Unit Structure.
|
||||
*
|
||||
* Type define for the Unit attributes of a report item.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint32_t Type; /**< Unit type (refer to HID specifications for details). */
|
||||
uint8_t Exponent; /**< Unit exponent (refer to HID specifications for details). */
|
||||
} HID_Unit_t;
|
||||
|
||||
/** \brief HID Parser Report Item Usage Structure.
|
||||
*
|
||||
* Type define for the Usage attributes of a report item.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint16_t Page; /**< Usage page of the report item. */
|
||||
uint16_t Usage; /**< Usage of the report item. */
|
||||
} HID_Usage_t;
|
||||
|
||||
/** \brief HID Parser Report Item Collection Path Structure.
|
||||
*
|
||||
* Type define for a COLLECTION object. Contains the collection attributes and a reference to the
|
||||
* parent collection if any.
|
||||
*/
|
||||
typedef struct HID_CollectionPath
|
||||
{
|
||||
uint8_t Type; /**< Collection type (e.g. "Generic Desktop"). */
|
||||
HID_Usage_t Usage; /**< Collection usage. */
|
||||
struct HID_CollectionPath *Parent; /**< Reference to parent collection, or \c NULL if root collection. */
|
||||
struct HID_CollectionPath *Next; /**< Reference to parent collection, or \c NULL if root collection. */
|
||||
} HID_CollectionPath_t;
|
||||
|
||||
/** \brief HID Parser Report Item Attributes Structure.
|
||||
*
|
||||
* Type define for all the data attributes of a report item, except flags.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint8_t BitSize; /**< Size in bits of the report item's data. */
|
||||
HID_Usage_t Usage; /**< Usage of the report item. */
|
||||
HID_Unit_t Unit; /**< Unit type and exponent of the report item. */
|
||||
HID_MinMax_t Logical; /**< Logical minimum and maximum of the report item. */
|
||||
HID_MinMax_t Physical; /**< Physical minimum and maximum of the report item. */
|
||||
} HID_ReportItem_Attributes_t;
|
||||
|
||||
/** \brief HID Parser Report Item Details Structure.
|
||||
*
|
||||
* Type define for a report item (IN, OUT or FEATURE) layout attributes and other details.
|
||||
*/
|
||||
typedef struct HID_ReportItem_s
|
||||
{
|
||||
uint16_t BitOffset; /**< Bit offset in the IN, OUT or FEATURE report of the item. */
|
||||
uint8_t ItemType; /**< Report item type, a value in \ref HID_ReportItemTypes_t. */
|
||||
uint16_t ItemFlags; /**< Item data flags, a mask of \c HID_IOF_* constants. */
|
||||
uint8_t ReportID; /**< Report ID this item belongs to, or 0x00 if device has only one report */
|
||||
HID_ReportItem_Attributes_t Attributes; /**< Report item attributes. */
|
||||
uint32_t Value; /**< Current value of the report item - use \ref HID_ALIGN_DATA() when processing
|
||||
* a retrieved value so that it is aligned to a specific type.
|
||||
*/
|
||||
uint32_t PreviousValue; /**< Previous value of the report item. */
|
||||
struct HID_ReportItem_s* Next;
|
||||
} HID_ReportItem_t;
|
||||
|
||||
/** \brief HID Parser Report Size Structure.
|
||||
*
|
||||
* Type define for a report item size information structure, to retain the size of a device's reports by ID.
|
||||
*/
|
||||
typedef struct HID_ReportSizeInfo_s
|
||||
{
|
||||
uint8_t ReportID; /**< Report ID of the report within the HID interface. */
|
||||
uint16_t ReportSizeBits[3]; /**< Total number of bits in each report type for the given Report ID,
|
||||
* indexed by the \ref HID_ReportItemTypes_t enum.
|
||||
*/
|
||||
struct HID_ReportSizeInfo_s* Next;
|
||||
} HID_ReportSizeInfo_t;
|
||||
|
||||
/** \brief HID Parser State Structure.
|
||||
*
|
||||
* Type define for a complete processed HID report, including all report item data and collections.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t TotalReportItems; /**< Total number of report items stored in the \c ReportItems array. */
|
||||
HID_ReportItem_t* FirstReportItem; /**< Report items array, including all IN, OUT
|
||||
* and FEATURE items.
|
||||
*/
|
||||
uint8_t TotalDeviceReports; /**< Number of reports within the HID interface */
|
||||
uint16_t LargestReportSizeBits; /**< Largest report that the attached device will generate, in bits */
|
||||
bool UsingReportIDs; /**< Indicates if the device has at least one REPORT ID
|
||||
* element in its HID report descriptor.
|
||||
*/
|
||||
HID_ReportItem_t* LastReportItem;
|
||||
} HID_ReportInfo_t;
|
||||
|
||||
/* Function Prototypes: */
|
||||
/** Function to process a given HID report returned from an attached device, and store it into a given
|
||||
* \ref HID_ReportInfo_t structure.
|
||||
*
|
||||
* \param[in] ReportData Buffer containing the device's HID report table.
|
||||
* \param[in] ReportSize Size in bytes of the HID report table.
|
||||
* \param[out] ParserData Pointer to a \ref HID_ReportInfo_t instance for the parser output.
|
||||
*
|
||||
* \return A value in the \ref HID_Parse_ErrorCodes_t enum.
|
||||
*/
|
||||
uint8_t USB_ProcessHIDReport(const uint8_t *ReportData,
|
||||
uint16_t ReportSize,
|
||||
HID_ReportInfo_t **ParserData);
|
||||
|
||||
void USB_FreeReportInfo(HID_ReportInfo_t *ReportInfo);
|
||||
|
||||
/** Extracts the given report item's value out of the given HID report and places it into the Value
|
||||
* member of the report item's \ref HID_ReportItem_t structure.
|
||||
*
|
||||
* When called on a report with an item that exists in that report, this copies the report item's \c Value
|
||||
* to its \c PreviousValue element for easy checking to see if an item's value has changed before processing
|
||||
* a report. If the given item does not exist in the report, the function does not modify the report item's
|
||||
* data.
|
||||
*
|
||||
* \param[in] ReportData Buffer containing an IN or FEATURE report from an attached device.
|
||||
* \param[in,out] ReportItem Pointer to the report item of interest in a \ref HID_ReportInfo_t ReportItem array.
|
||||
*
|
||||
* \returns Boolean \c true if the item to retrieve was located in the given report, \c false otherwise.
|
||||
*/
|
||||
bool USB_GetHIDReportItemInfo(uint16_t report_id, const uint8_t *ReportData,
|
||||
HID_ReportItem_t *const ReportItem);
|
||||
|
||||
/** Callback routine for the HID Report Parser. This callback <b>must</b> be implemented by the user code when
|
||||
* the parser is used, to determine what report IN, OUT and FEATURE item's information is stored into the user
|
||||
* \ref HID_ReportInfo_t structure. This can be used to filter only those items the application will be using, so that
|
||||
* no RAM is wasted storing the attributes for report items which will never be referenced by the application.
|
||||
*
|
||||
* Report item pointers passed to this callback function may be cached by the user application for later use
|
||||
* when processing report items. This provides faster report processing in the user application than would
|
||||
* a search of the entire parsed report item table for each received or sent report.
|
||||
*
|
||||
* \param[in] CurrentItem Pointer to the current report item for user checking.
|
||||
*
|
||||
* \return Boolean \c true if the item should be stored into the \ref HID_ReportInfo_t structure, \c false if
|
||||
* it should be ignored.
|
||||
*/
|
||||
bool CALLBACK_HIDParser_FilterHIDReportItem(HID_ReportItem_t *const CurrentItem);
|
||||
|
||||
/** Enum for the different types of HID reports. */
|
||||
enum HID_ReportItemTypes_t
|
||||
{
|
||||
HID_REPORT_ITEM_In = 0, /**< Indicates that the item is an IN report type. */
|
||||
HID_REPORT_ITEM_Out = 1, /**< Indicates that the item is an OUT report type. */
|
||||
HID_REPORT_ITEM_Feature = 2, /**< Indicates that the item is a FEATURE report type. */
|
||||
};
|
||||
|
||||
/* Private Interface - For use in library only: */
|
||||
#if !defined(__DOXYGEN__)
|
||||
/* Type Defines: */
|
||||
typedef struct
|
||||
{
|
||||
HID_ReportItem_Attributes_t Attributes;
|
||||
uint8_t ReportCount;
|
||||
uint8_t ReportID;
|
||||
} HID_StateTable_t;
|
||||
#endif
|
||||
|
||||
/* Disable C linkage for C++ Compilers: */
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
@@ -9,9 +9,9 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "usbh/tusb_hid/ps3.h"
|
||||
#include "usbh/tusb_hid/ps4.h"
|
||||
#include "usbh/tusb_hid/ps5.h"
|
||||
// #include "usbh/tusb_hid/ps3.h"
|
||||
// #include "usbh/tusb_hid/ps4.h"
|
||||
// #include "usbh/tusb_hid/ps5.h"
|
||||
#include "usbh/tusb_xinput/xinput_host.h"
|
||||
|
||||
struct GamepadState{
|
||||
@@ -48,7 +48,6 @@ class Gamepad {
|
||||
public:
|
||||
GamepadState state;
|
||||
|
||||
// void update_gamepad_state_from_dualshock4(const sony_ds4_report_t* ds4_data);
|
||||
void update_gamepad_state_from_xinput(const xinput_gamepad_t* xinput_data);
|
||||
void reset_state();
|
||||
};
|
||||
|
||||
298
src/descriptors/DInputDescriptors.h
Normal file
298
src/descriptors/DInputDescriptors.h
Normal file
@@ -0,0 +1,298 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define HID_ENDPOINT_SIZE 64
|
||||
|
||||
// Mac OS-X and Linux automatically load the correct drivers. On
|
||||
// Windows, even though the driver is supplied by Microsoft, an
|
||||
// INF file is needed to load the driver. These numbers need to
|
||||
// match the INF file.
|
||||
// #define VENDOR_ID 0x10C4
|
||||
// #define PRODUCT_ID 0x82C0
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Endpoint Buffer Configuration
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
#define ENDPOINT0_SIZE 64
|
||||
|
||||
#define GAMEPAD_INTERFACE 0
|
||||
#define GAMEPAD_ENDPOINT 1
|
||||
#define GAMEPAD_SIZE 64
|
||||
|
||||
#define LSB(n) (n & 255)
|
||||
#define MSB(n) ((n >> 8) & 255)
|
||||
|
||||
#define DINPUT_HAT_UP 0x00
|
||||
#define DINPUT_HAT_UPRIGHT 0x01
|
||||
#define DINPUT_HAT_RIGHT 0x02
|
||||
#define DINPUT_HAT_DOWNRIGHT 0x03
|
||||
#define DINPUT_HAT_DOWN 0x04
|
||||
#define DINPUT_HAT_DOWNLEFT 0x05
|
||||
#define DINPUT_HAT_LEFT 0x06
|
||||
#define DINPUT_HAT_UPLEFT 0x07
|
||||
#define DINPUT_HAT_NOTHING 0x08
|
||||
|
||||
#define DINPUT_MASK_SQUARE (1U << 0)
|
||||
#define DINPUT_MASK_CROSS (1U << 1)
|
||||
#define DINPUT_MASK_CIRCLE (1U << 2)
|
||||
#define DINPUT_MASK_TRIANGLE (1U << 3)
|
||||
#define DINPUT_MASK_L1 (1U << 4)
|
||||
#define DINPUT_MASK_R1 (1U << 5)
|
||||
#define DINPUT_MASK_L2 (1U << 6)
|
||||
#define DINPUT_MASK_R2 (1U << 7)
|
||||
#define DINPUT_MASK_SELECT (1U << 8)
|
||||
#define DINPUT_MASK_START (1U << 9)
|
||||
#define DINPUT_MASK_L3 (1U << 10)
|
||||
#define DINPUT_MASK_R3 (1U << 11)
|
||||
#define DINPUT_MASK_PS (1U << 12)
|
||||
#define DINPUT_MASK_TP (1U << 13)
|
||||
|
||||
#define DINPUT_JOYSTICK_MIN 0x00
|
||||
#define DINPUT_JOYSTICK_MID 0x80
|
||||
#define DINPUT_JOYSTICK_MAX 0xFF
|
||||
|
||||
typedef struct __attribute((packed, aligned(1)))
|
||||
{
|
||||
// digital buttons, 0 = off, 1 = on
|
||||
// uint8_t report_id;
|
||||
// uint8_t padding;
|
||||
|
||||
uint8_t square_btn : 1;
|
||||
uint8_t cross_btn : 1;
|
||||
uint8_t circle_btn : 1;
|
||||
uint8_t triangle_btn : 1;
|
||||
|
||||
uint8_t l1_btn : 1;
|
||||
uint8_t r1_btn : 1;
|
||||
uint8_t l2_btn : 1;
|
||||
uint8_t r2_btn : 1;
|
||||
|
||||
uint8_t select_btn : 1;
|
||||
uint8_t start_btn : 1;
|
||||
uint8_t l3_btn : 1;
|
||||
uint8_t r3_btn : 1;
|
||||
|
||||
uint8_t ps_btn : 1;
|
||||
uint8_t tp_btn : 1;
|
||||
// uint8_t l2_btn_alt : 1;
|
||||
|
||||
// uint8_t r2_btn_alt : 1;
|
||||
uint8_t : 2;
|
||||
|
||||
// digital direction, use the dir_* constants(enum)
|
||||
// 8 = center, 0 = up, 1 = up/right, 2 = right, 3 = right/down
|
||||
// 4 = down, 5 = down/left, 6 = left, 7 = left/up
|
||||
|
||||
// uint8_t direction;
|
||||
uint8_t direction : 4;
|
||||
uint8_t : 4;
|
||||
|
||||
// left and right analog sticks, 0x00 left/up, 0x80 middle, 0xff right/down
|
||||
|
||||
uint8_t l_x_axis;
|
||||
uint8_t l_y_axis;
|
||||
uint8_t r_x_axis;
|
||||
uint8_t r_y_axis;
|
||||
|
||||
// Gonna assume these are button analog values for the d-pad.
|
||||
// NOTE: NOT EVEN SURE THIS IS RIGHT, OR IN THE CORRECT ORDER
|
||||
uint8_t right_axis;
|
||||
uint8_t left_axis;
|
||||
uint8_t up_axis;
|
||||
uint8_t down_axis;
|
||||
|
||||
// button axis, 0x00 = unpressed, 0xff = fully pressed
|
||||
|
||||
uint8_t triangle_axis;
|
||||
uint8_t circle_axis;
|
||||
uint8_t cross_axis;
|
||||
uint8_t square_axis;
|
||||
|
||||
uint8_t l1_axis;
|
||||
uint8_t r1_axis;
|
||||
uint8_t l2_axis;
|
||||
uint8_t r2_axis;
|
||||
} DInputReport;
|
||||
|
||||
struct DInputLed {
|
||||
uint8_t time_enabled; /* the total time the led is active (0xff means forever) */
|
||||
uint8_t duty_length; /* how long a cycle is in deciseconds (0 means "really fast") */
|
||||
uint8_t enabled;
|
||||
uint8_t duty_off; /* % of duty_length the led is off (0xff means 100%) */
|
||||
uint8_t duty_on; /* % of duty_length the led is on (0xff mean 100%) */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct DInputRumble {
|
||||
uint8_t padding;
|
||||
uint8_t right_duration; /* Right motor duration (0xff means forever) */
|
||||
uint8_t right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
|
||||
uint8_t left_duration; /* Left motor duration (0xff means forever) */
|
||||
uint8_t left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct DInputOutReport {
|
||||
struct DInputRumble rumble;
|
||||
uint8_t padding[4];
|
||||
uint8_t leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
|
||||
struct DInputLed led[4]; /* LEDx at (4 - x) */
|
||||
struct DInputLed _reserved; /* LED5, not actually soldered */
|
||||
} __attribute__((packed));
|
||||
|
||||
static const uint8_t dinput_string_language[] = { 0x09, 0x04 };
|
||||
static const uint8_t dinput_string_manufacturer[] = "SHANWAN";
|
||||
static const uint8_t dinput_string_product[] = "2In1 USB Joystick";
|
||||
static const uint8_t dinput_string_version[] = "1.0";
|
||||
|
||||
static const uint8_t *dinput_string_descriptors[] __attribute__((unused)) =
|
||||
{
|
||||
dinput_string_language,
|
||||
dinput_string_manufacturer,
|
||||
dinput_string_product,
|
||||
dinput_string_version
|
||||
};
|
||||
|
||||
static const uint8_t dinput_device_descriptor[] =
|
||||
{
|
||||
0x12, // bLength
|
||||
0x01, // bDescriptorType (Device)
|
||||
0x10, 0x01, // bcdUSB 1.10
|
||||
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
0x40, // bMaxPacketSize0 64
|
||||
0x63, 0x25, // idVendor 0x2563
|
||||
0x75, 0x05, // idProduct 0x0575
|
||||
0x00, 0x02, // bcdDevice 4.00
|
||||
0x01, // iManufacturer (String Index)
|
||||
0x02, // iProduct (String Index)
|
||||
0x00, // iSerialNumber (String Index)
|
||||
0x01, // bNumConfigurations 1
|
||||
};
|
||||
|
||||
static const uint8_t dinput_report_descriptor[] =
|
||||
{
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x05, // Usage (Game Pad)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x35, 0x00, // Physical Minimum (0)
|
||||
0x45, 0x01, // Physical Maximum (1)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x0D, // Report Count (13)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x0D, // Usage Maximum (0x0D)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x03, // Report Count (3)
|
||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x25, 0x07, // Logical Maximum (7)
|
||||
0x46, 0x3B, 0x01, // Physical Maximum (315)
|
||||
0x75, 0x04, // Report Size (4)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x65, 0x14, // Unit (System: English Rotation, Length: Centimeter)
|
||||
0x09, 0x39, // Usage (Hat switch)
|
||||
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
|
||||
0x65, 0x00, // Unit (None)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x46, 0xFF, 0x00, // Physical Maximum (255)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
0x09, 0x32, // Usage (Z)
|
||||
0x09, 0x35, // Usage (Rz)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
|
||||
0x09, 0x20, // Usage (0x20)
|
||||
0x09, 0x21, // Usage (0x21)
|
||||
0x09, 0x22, // Usage (0x22)
|
||||
0x09, 0x23, // Usage (0x23)
|
||||
0x09, 0x24, // Usage (0x24)
|
||||
0x09, 0x25, // Usage (0x25)
|
||||
0x09, 0x26, // Usage (0x26)
|
||||
0x09, 0x27, // Usage (0x27)
|
||||
0x09, 0x28, // Usage (0x28)
|
||||
0x09, 0x29, // Usage (0x29)
|
||||
0x09, 0x2A, // Usage (0x2A)
|
||||
0x09, 0x2B, // Usage (0x2B)
|
||||
0x95, 0x0C, // Report Count (12)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x0A, 0x21, 0x26, // Usage (0x2621)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x0A, 0x21, 0x26, // Usage (0x2621)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x26, 0xFF, 0x03, // Logical Maximum (1023)
|
||||
0x46, 0xFF, 0x03, // Physical Maximum (1023)
|
||||
0x09, 0x2C, // Usage (0x2C)
|
||||
0x09, 0x2D, // Usage (0x2D)
|
||||
0x09, 0x2E, // Usage (0x2E)
|
||||
0x09, 0x2F, // Usage (0x2F)
|
||||
0x75, 0x10, // Report Size (16)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
};
|
||||
|
||||
static const uint8_t dinput_hid_descriptor[] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x10, 0x01, // bcdHID 1.10
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
0x89, 0x00, // wDescriptorLength[0] 137
|
||||
};
|
||||
|
||||
static const uint8_t dinput_configuration_descriptor[] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x02, // bDescriptorType (Configuration)
|
||||
0x29, 0x00, // wTotalLength 41
|
||||
0x01, // bNumInterfaces 1
|
||||
0x01, // bConfigurationValue
|
||||
0x00, // iConfiguration (String Index)
|
||||
0x80, // bmAttributes
|
||||
0xFA, // bMaxPower 500mA
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x00, // bInterfaceNumber 0
|
||||
0x00, // bAlternateSetting
|
||||
0x02, // bNumEndpoints 2
|
||||
0x03, // bInterfaceClass
|
||||
0x00, // bInterfaceSubClass
|
||||
0x00, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x10, 0x01, // bcdHID 1.10
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
0x89, 0x00, // wDescriptorLength[0] 137
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x02, // bEndpointAddress (OUT/H2D)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x0A, // bInterval 10 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x81, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x20, 0x00, // wMaxPacketSize 32
|
||||
0x0A, // bInterval 10 (unit depends on device speed)
|
||||
};
|
||||
@@ -213,13 +213,13 @@ static const uint8_t hid_report_descriptor[] =
|
||||
|
||||
static const uint8_t hid_hid_descriptor[] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x11, 0x01, // bcdHID 1.11
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
sizeof(hid_report_descriptor), 0x00, // wDescriptorLength[0] 90
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x11, 0x01, // bcdHID 1.11
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
sizeof(hid_report_descriptor), 0x00, // wDescriptorLength[0] 90
|
||||
};
|
||||
|
||||
#define CONFIG1_DESC_SIZE (9+9+9+7)
|
||||
|
||||
298
src/descriptors/PS3Descriptors.h
Normal file
298
src/descriptors/PS3Descriptors.h
Normal file
@@ -0,0 +1,298 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define HID_ENDPOINT_SIZE 64
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
* Endpoint Buffer Configuration
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
#define ENDPOINT0_SIZE 64
|
||||
|
||||
#define GAMEPAD_INTERFACE 0
|
||||
#define GAMEPAD_ENDPOINT 1
|
||||
#define GAMEPAD_SIZE 64
|
||||
|
||||
#define LSB(n) (n & 255)
|
||||
#define MSB(n) ((n >> 8) & 255)
|
||||
|
||||
#define PS3_HAT_UP 0x00
|
||||
#define PS3_HAT_UPRIGHT 0x01
|
||||
#define PS3_HAT_RIGHT 0x02
|
||||
#define PS3_HAT_DOWNRIGHT 0x03
|
||||
#define PS3_HAT_DOWN 0x04
|
||||
#define PS3_HAT_DOWNLEFT 0x05
|
||||
#define PS3_HAT_LEFT 0x06
|
||||
#define PS3_HAT_UPLEFT 0x07
|
||||
#define PS3_HAT_NOTHING 0x08
|
||||
|
||||
#define PS3_MASK_SQUARE (1U << 0)
|
||||
#define PS3_MASK_CROSS (1U << 1)
|
||||
#define PS3_MASK_CIRCLE (1U << 2)
|
||||
#define PS3_MASK_TRIANGLE (1U << 3)
|
||||
#define PS3_MASK_L1 (1U << 4)
|
||||
#define PS3_MASK_R1 (1U << 5)
|
||||
#define PS3_MASK_L2 (1U << 6)
|
||||
#define PS3_MASK_R2 (1U << 7)
|
||||
#define PS3_MASK_SELECT (1U << 8)
|
||||
#define PS3_MASK_START (1U << 9)
|
||||
#define PS3_MASK_L3 (1U << 10)
|
||||
#define PS3_MASK_R3 (1U << 11)
|
||||
#define PS3_MASK_PS (1U << 12)
|
||||
#define PS3_MASK_TP (1U << 13)
|
||||
|
||||
#define PS3_JOYSTICK_MIN 0x00
|
||||
#define PS3_JOYSTICK_MID 0x80
|
||||
#define PS3_JOYSTICK_MAX 0xFF
|
||||
|
||||
typedef struct __attribute((packed, aligned(1)))
|
||||
{
|
||||
uint8_t report_id;
|
||||
uint8_t unk0;
|
||||
|
||||
uint8_t select : 1;
|
||||
uint8_t l3 : 1;
|
||||
uint8_t r3 : 1;
|
||||
uint8_t start : 1;
|
||||
uint8_t up : 1;
|
||||
uint8_t right : 1;
|
||||
uint8_t down : 1;
|
||||
uint8_t left : 1;
|
||||
|
||||
uint8_t l2 : 1;
|
||||
uint8_t r2 : 1;
|
||||
uint8_t l1 : 1;
|
||||
uint8_t r1 : 1;
|
||||
uint8_t triangle : 1;
|
||||
uint8_t circle : 1;
|
||||
uint8_t cross : 1;
|
||||
uint8_t square : 1;
|
||||
|
||||
uint8_t ps : 1;
|
||||
uint8_t : 0;
|
||||
|
||||
uint8_t unknown1;
|
||||
|
||||
uint8_t left_x;
|
||||
uint8_t left_y;
|
||||
uint8_t right_x;
|
||||
uint8_t right_y;
|
||||
|
||||
uint8_t unknown2[4];
|
||||
|
||||
uint8_t up_axis;
|
||||
uint8_t right_axis;
|
||||
uint8_t down_axis;
|
||||
uint8_t left_axis;
|
||||
|
||||
uint8_t l2_axis;
|
||||
uint8_t r2_axis;
|
||||
uint8_t l1_axis;
|
||||
uint8_t r1_axis;
|
||||
|
||||
uint8_t triangle_axis;
|
||||
uint8_t circle_axis;
|
||||
uint8_t cross_axis;
|
||||
uint8_t square_axis;
|
||||
|
||||
uint8_t unknown3[15];
|
||||
|
||||
int16_t acceler_x;
|
||||
int16_t acceler_y;
|
||||
int16_t acceler_z;
|
||||
|
||||
int16_t velocity_z;
|
||||
} DualShock3Report;
|
||||
|
||||
struct sixaxis_led {
|
||||
uint8_t time_enabled; /* the total time the led is active (0xff means forever) */
|
||||
uint8_t duty_length; /* how long a cycle is in deciseconds (0 means "really fast") */
|
||||
uint8_t enabled;
|
||||
uint8_t duty_off; /* % of duty_length the led is off (0xff means 100%) */
|
||||
uint8_t duty_on; /* % of duty_length the led is on (0xff mean 100%) */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sixaxis_rumble {
|
||||
uint8_t padding;
|
||||
uint8_t right_duration; /* Right motor duration (0xff means forever) */
|
||||
uint8_t right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
|
||||
uint8_t left_duration; /* Left motor duration (0xff means forever) */
|
||||
uint8_t left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sixaxis_output_report {
|
||||
struct sixaxis_rumble rumble;
|
||||
uint8_t padding[4];
|
||||
uint8_t leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
|
||||
struct sixaxis_led led[4]; /* LEDx at (4 - x) */
|
||||
struct sixaxis_led _reserved; /* LED5, not actually soldered */
|
||||
} __attribute__((packed));
|
||||
|
||||
static const uint8_t ps3_string_language[] = { 0x09, 0x04 };
|
||||
static const uint8_t ps3_string_manufacturer[] = "Sony";
|
||||
static const uint8_t ps3_string_product[] = "PLAYSTATION(R)3 Controller";
|
||||
static const uint8_t ps3_string_version[] = "1.0";
|
||||
|
||||
static const uint8_t *ps3_string_descriptors[] __attribute__((unused)) =
|
||||
{
|
||||
ps3_string_language,
|
||||
ps3_string_manufacturer,
|
||||
ps3_string_product,
|
||||
ps3_string_version
|
||||
};
|
||||
|
||||
static const uint8_t ps3_device_descriptor[] =
|
||||
{
|
||||
0x12, // bLength
|
||||
0x01, // bDescriptorType (Device)
|
||||
0x00, 0x02, // bcdUSB 2.00
|
||||
0x00, // bDeviceClass (Use class information in the Interface Descriptors)
|
||||
0x00, // bDeviceSubClass
|
||||
0x00, // bDeviceProtocol
|
||||
0x40, // bMaxPacketSize0 64
|
||||
0x4C, 0x05, // idVendor 0x054C
|
||||
0x68, 0x02, // idProduct 0x0268
|
||||
0x00, 0x01, // bcdDevice 2.00
|
||||
0x01, // iManufacturer (String Index)
|
||||
0x02, // iProduct (String Index)
|
||||
0x00, // iSerialNumber (String Index)
|
||||
0x01, // bNumConfigurations 1
|
||||
};
|
||||
|
||||
static const uint8_t ps3_report_descriptor[] =
|
||||
{
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x04, // Usage (Joystick)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0xA1, 0x02, // Collection (Logical)
|
||||
0x85, 0x01, // Report ID (1)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x13, // Report Count (19)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x35, 0x00, // Physical Minimum (0)
|
||||
0x45, 0x01, // Physical Maximum (1)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x13, // Usage Maximum (0x13)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x0D, // Report Count (13)
|
||||
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
|
||||
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0xA1, 0x00, // Collection (Physical)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x35, 0x00, // Physical Minimum (0)
|
||||
0x46, 0xFF, 0x00, // Physical Maximum (255)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
0x09, 0x32, // Usage (Z)
|
||||
0x09, 0x35, // Usage (Rz)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x27, // Report Count (39)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x30, // Report Count (48)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x30, // Report Count (48)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
0xA1, 0x02, // Collection (Logical)
|
||||
0x85, 0x02, // Report ID (2)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x30, // Report Count (48)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
0xA1, 0x02, // Collection (Logical)
|
||||
0x85, 0xEE, // Report ID (-18)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x30, // Report Count (48)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
0xA1, 0x02, // Collection (Logical)
|
||||
0x85, 0xEF, // Report ID (-17)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x30, // Report Count (48)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
0xC0, // End Collection
|
||||
};
|
||||
|
||||
static const uint8_t ps3_hid_descriptor[] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x11, 0x01, // bcdHID 1.11
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
0x94, 0x00, // wDescriptorLength[0] 148
|
||||
};
|
||||
|
||||
// #define CONFIG1_DESC_SIZE (9+9+9+7)
|
||||
static const uint8_t ps3_configuration_descriptor[] =
|
||||
{
|
||||
0x09, // bLength
|
||||
0x02, // bDescriptorType (Configuration)
|
||||
0x29, 0x00, // wTotalLength 41
|
||||
0x01, // bNumInterfaces 1
|
||||
0x01, // bConfigurationValue
|
||||
0x00, // iConfiguration (String Index)
|
||||
0x80, // bmAttributes
|
||||
0xFA, // bMaxPower 500mA
|
||||
|
||||
0x09, // bLength
|
||||
0x04, // bDescriptorType (Interface)
|
||||
0x00, // bInterfaceNumber 0
|
||||
0x00, // bAlternateSetting
|
||||
0x02, // bNumEndpoints 2
|
||||
0x03, // bInterfaceClass
|
||||
0x00, // bInterfaceSubClass
|
||||
0x00, // bInterfaceProtocol
|
||||
0x00, // iInterface (String Index)
|
||||
|
||||
0x09, // bLength
|
||||
0x21, // bDescriptorType (HID)
|
||||
0x10, 0x01, // bcdHID 1.10
|
||||
0x00, // bCountryCode
|
||||
0x01, // bNumDescriptors
|
||||
0x22, // bDescriptorType[0] (HID)
|
||||
0x94, 0x00, // wDescriptorLength[0] 148
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x81, // bEndpointAddress (IN/D2H)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x40, 0x00, // wMaxPacketSize 64
|
||||
0x0A, // bInterval 10 (unit depends on device speed)
|
||||
|
||||
0x07, // bLength
|
||||
0x05, // bDescriptorType (Endpoint)
|
||||
0x02, // bEndpointAddress (OUT/H2D)
|
||||
0x03, // bmAttributes (Interrupt)
|
||||
0x40, 0x00, // wMaxPacketSize 64
|
||||
0x0A, // bInterval 10 (unit depends on device speed)
|
||||
};
|
||||
@@ -5,12 +5,6 @@
|
||||
#include "tusb.h"
|
||||
#include "bsp/board_api.h"
|
||||
|
||||
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
|
||||
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
|
||||
*
|
||||
* Auto ProductID layout's Bitmap:
|
||||
* [MSB] HID | MSC | CDC [LSB]
|
||||
*/
|
||||
#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) )
|
||||
#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
|
||||
_PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
|
||||
@@ -24,8 +18,6 @@ tusb_desc_device_t const usbserial_device_descriptor =
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = USB_BCD,
|
||||
|
||||
// Use Interface Association Descriptor (IAD) for CDC
|
||||
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
@@ -72,7 +64,6 @@ enum {
|
||||
STRID_SERIAL,
|
||||
};
|
||||
|
||||
// array of pointer to string descriptors
|
||||
char const *string_desc_arr[] =
|
||||
{
|
||||
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
|
||||
|
||||
@@ -2,21 +2,94 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hardware/flash.h"
|
||||
#include "hardware/sync.h"
|
||||
|
||||
#include "input_mode.h"
|
||||
|
||||
#define AIRCR_Register (*((volatile uint32_t*)(PPB_BASE + 0x0ED0C)))
|
||||
|
||||
#define EEPROM_SIZE 1
|
||||
#define EEPROM_FLASH_TARGET_OFFSET (2 * 1024 * 1024 - EEPROM_SIZE)
|
||||
|
||||
void store_input_mode(enum InputMode mode)
|
||||
{
|
||||
uint8_t data = (uint8_t)mode;
|
||||
|
||||
uint32_t flash_offset = EEPROM_FLASH_TARGET_OFFSET % FLASH_SECTOR_SIZE;
|
||||
uint32_t flash_sector_base = EEPROM_FLASH_TARGET_OFFSET - flash_offset;
|
||||
|
||||
uint8_t new_sector_content[FLASH_SECTOR_SIZE];
|
||||
|
||||
memcpy(new_sector_content, (const void *)(XIP_BASE + flash_sector_base), FLASH_SECTOR_SIZE);
|
||||
|
||||
new_sector_content[flash_offset] = data;
|
||||
|
||||
flash_range_erase(flash_sector_base, FLASH_SECTOR_SIZE);
|
||||
flash_range_program(flash_sector_base, new_sector_content, FLASH_SECTOR_SIZE);
|
||||
}
|
||||
|
||||
void change_input_mode(Gamepad previous_gamepad)
|
||||
{
|
||||
if (!previous_gamepad.state.start)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InputMode new_mode;
|
||||
|
||||
if (previous_gamepad.state.up)
|
||||
{
|
||||
new_mode = INPUT_MODE_XINPUT;
|
||||
}
|
||||
else if (previous_gamepad.state.left)
|
||||
{
|
||||
new_mode = INPUT_MODE_HID;
|
||||
}
|
||||
else if (previous_gamepad.state.right)
|
||||
{
|
||||
new_mode = INPUT_MODE_XBOXORIGINAL;
|
||||
}
|
||||
else if (previous_gamepad.state.down)
|
||||
{
|
||||
new_mode = INPUT_MODE_SWITCH;
|
||||
}
|
||||
else if (previous_gamepad.state.a)
|
||||
{
|
||||
new_mode = INPUT_MODE_PSCLASSIC;
|
||||
}
|
||||
// else if (previous_gamepad.state.b)
|
||||
// {
|
||||
// new_mode = INPUT_MODE_USBSERIAL;
|
||||
// }
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
store_input_mode(new_mode);
|
||||
sleep_ms(200);
|
||||
|
||||
// restart the rp2040
|
||||
AIRCR_Register = 0x5FA0004;
|
||||
sleep_ms(200);
|
||||
}
|
||||
|
||||
enum InputMode get_input_mode()
|
||||
{
|
||||
#ifdef HOST_ORIGINAL_XBOX
|
||||
return INPUT_MODE_XBOXORIGINAL;
|
||||
#elif HOST_NINTENDO_SWITCH
|
||||
return INPUT_MODE_SWITCH;
|
||||
#elif HOST_PLAYSTATION_CLASSIC
|
||||
return INPUT_MODE_PSCLASSIC;
|
||||
#elif HOST_PLAYSTATION_3
|
||||
return INPUT_MODE_HID;
|
||||
#elif HOST_DEBUG
|
||||
#ifdef HOST_DEBUG
|
||||
return INPUT_MODE_USBSERIAL;
|
||||
#else
|
||||
return INPUT_MODE_XINPUT;
|
||||
#endif
|
||||
|
||||
const uint8_t* flash_addr = (const uint8_t*)(XIP_BASE + EEPROM_FLASH_TARGET_OFFSET);
|
||||
uint8_t stored_value = *flash_addr;
|
||||
|
||||
if (stored_value >= INPUT_MODE_XINPUT && stored_value <= INPUT_MODE_XBOXORIGINAL)
|
||||
{
|
||||
return(enum InputMode)stored_value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return INPUT_MODE_XBOXORIGINAL;
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "Gamepad.h"
|
||||
|
||||
enum InputMode
|
||||
{
|
||||
INPUT_MODE_XINPUT,
|
||||
@@ -18,6 +20,7 @@ enum InputMode
|
||||
};
|
||||
|
||||
enum InputMode get_input_mode();
|
||||
void change_input_mode(Gamepad previous_gamepad_state);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
23
src/main.cpp
23
src/main.cpp
@@ -13,7 +13,6 @@
|
||||
#include "usbd/drivermanager.h"
|
||||
#include "usbd/gpdriver.h"
|
||||
|
||||
#include "Gamepad.h"
|
||||
#include "input_mode.h"
|
||||
|
||||
Gamepad gamepad;
|
||||
@@ -34,12 +33,34 @@ int main(void)
|
||||
multicore_reset_core1();
|
||||
multicore_launch_core1(usbh_main);
|
||||
|
||||
Gamepad previous_gamepad = gamepad;
|
||||
absolute_time_t last_time_gamepad_changed = get_absolute_time();
|
||||
absolute_time_t last_time_gamepad_checked = get_absolute_time();
|
||||
|
||||
while (1)
|
||||
{
|
||||
uint8_t outBuffer[64];
|
||||
GPDriver* driver = driverManager.getDriver();
|
||||
driver->process(&gamepad, outBuffer);
|
||||
|
||||
if (absolute_time_diff_us(last_time_gamepad_checked, get_absolute_time()) >= 200000)
|
||||
{
|
||||
// check if digital buttons have changed (first 16 bytes of gamepad.state)
|
||||
if (memcmp(&gamepad.state, &previous_gamepad.state, 16) != 0)
|
||||
{
|
||||
memcpy(&previous_gamepad.state, &gamepad.state, sizeof(gamepad.state));
|
||||
last_time_gamepad_changed = get_absolute_time();
|
||||
}
|
||||
// haven't changed for 3 seconds
|
||||
else if (absolute_time_diff_us(last_time_gamepad_changed, get_absolute_time()) >= 3000000)
|
||||
{
|
||||
change_input_mode(previous_gamepad);
|
||||
last_time_gamepad_changed = get_absolute_time();
|
||||
}
|
||||
|
||||
last_time_gamepad_checked = get_absolute_time();
|
||||
}
|
||||
|
||||
sleep_ms(1);
|
||||
tud_task();
|
||||
}
|
||||
|
||||
197
src/usbd/dinput/DInputDriver.cpp
Normal file
197
src/usbd/dinput/DInputDriver.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info)
|
||||
*/
|
||||
#include "utilities/scaling.h"
|
||||
|
||||
#include "usbd/dinput/DInputDriver.h"
|
||||
#include "descriptors/DInputDescriptors.h"
|
||||
|
||||
#include "usbd/shared/driverhelper.h"
|
||||
|
||||
// Magic byte sequence to enable PS button on PS3
|
||||
static const uint8_t ps3_magic_init_bytes[8] = {0x21, 0x26, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
static bool hid_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
|
||||
{
|
||||
if ( request->bmRequestType == 0xA1 &&
|
||||
request->bRequest == HID_REQ_CONTROL_GET_REPORT &&
|
||||
request->wValue == 0x0300 )
|
||||
{
|
||||
return tud_control_xfer(rhport, request, (void *) ps3_magic_init_bytes, sizeof(ps3_magic_init_bytes));
|
||||
}
|
||||
else
|
||||
{
|
||||
return hidd_control_xfer_cb(rhport, stage, request);
|
||||
}
|
||||
}
|
||||
|
||||
void DInputDriver::initialize()
|
||||
{
|
||||
dinput_report = {
|
||||
// .report_id = 0,
|
||||
.square_btn = 0, .cross_btn = 0, .circle_btn = 0, .triangle_btn = 0,
|
||||
.l1_btn = 0, .r1_btn = 0, .l2_btn = 0, .r2_btn = 0,
|
||||
.select_btn = 0, .start_btn = 0, .l3_btn = 0, .r3_btn = 0, .ps_btn = 0, .tp_btn = 0,
|
||||
.direction = 0x08,
|
||||
.l_x_axis = DINPUT_JOYSTICK_MID,
|
||||
.l_y_axis = DINPUT_JOYSTICK_MID,
|
||||
.r_x_axis = DINPUT_JOYSTICK_MID,
|
||||
.r_y_axis = DINPUT_JOYSTICK_MID,
|
||||
.right_axis = 0x00, .left_axis = 0x00, .up_axis = 0x00, .down_axis = 0x00,
|
||||
.triangle_axis = 0x00, .circle_axis = 0x00, .cross_axis = 0x00, .square_axis = 0x00,
|
||||
.l1_axis = 0x00, .r1_axis = 0x00, .l2_axis = 0x00, .r2_axis = 0x00
|
||||
};
|
||||
|
||||
class_driver = {
|
||||
#if CFG_TUSB_DEBUG >= 2
|
||||
.name = "HID",
|
||||
#endif
|
||||
.init = hidd_init,
|
||||
.reset = hidd_reset,
|
||||
.open = hidd_open,
|
||||
.control_xfer_cb = hid_control_xfer_cb,
|
||||
.xfer_cb = hidd_xfer_cb,
|
||||
.sof = NULL
|
||||
};
|
||||
}
|
||||
|
||||
// Generate HID report from gamepad and send to TUSB Device
|
||||
void DInputDriver::process(Gamepad * gamepad, uint8_t * outBuffer)
|
||||
{
|
||||
if (gamepad->state.up) {
|
||||
if (gamepad->state.right) {
|
||||
dinput_report.direction = DINPUT_HAT_UPRIGHT;
|
||||
} else if (gamepad->state.left) {
|
||||
dinput_report.direction = DINPUT_HAT_UPLEFT;
|
||||
} else {
|
||||
dinput_report.direction = DINPUT_HAT_UP;
|
||||
}
|
||||
} else if (gamepad->state.down) {
|
||||
if (gamepad->state.right) {
|
||||
dinput_report.direction = DINPUT_HAT_DOWNRIGHT;
|
||||
} else if (gamepad->state.left) {
|
||||
dinput_report.direction = DINPUT_HAT_DOWNLEFT;
|
||||
} else {
|
||||
dinput_report.direction = DINPUT_HAT_DOWN;
|
||||
}
|
||||
} else if (gamepad->state.left) {
|
||||
dinput_report.direction = DINPUT_HAT_LEFT;
|
||||
} else if (gamepad->state.right) {
|
||||
dinput_report.direction = DINPUT_HAT_RIGHT;
|
||||
} else {
|
||||
dinput_report.direction = DINPUT_HAT_NOTHING;
|
||||
}
|
||||
|
||||
dinput_report.cross_btn = gamepad->state.a ? 1 : 0;
|
||||
dinput_report.circle_btn = gamepad->state.b ? 1 : 0;
|
||||
dinput_report.square_btn = gamepad->state.x ? 1 : 0;
|
||||
dinput_report.triangle_btn = gamepad->state.y ? 1 : 0;
|
||||
dinput_report.l1_btn = gamepad->state.lb ? 1 : 0;
|
||||
dinput_report.r1_btn = gamepad->state.rb ? 1 : 0;
|
||||
dinput_report.l2_btn = gamepad->state.lt > 0 ? 1 : 0;
|
||||
dinput_report.r2_btn = gamepad->state.rt > 0 ? 1 : 0;
|
||||
dinput_report.select_btn = gamepad->state.back ? 1 : 0;
|
||||
dinput_report.start_btn = gamepad->state.start ? 1 : 0;
|
||||
dinput_report.l3_btn = gamepad->state.l3 ? 1 : 0;
|
||||
dinput_report.r3_btn = gamepad->state.r3 ? 1 : 0;
|
||||
dinput_report.ps_btn = gamepad->state.sys ? 1 : 0;
|
||||
dinput_report.tp_btn = gamepad->state.misc ? 1 : 0;
|
||||
|
||||
dinput_report.cross_axis = gamepad->state.a ? 0xFF : 0x00;
|
||||
dinput_report.circle_axis = gamepad->state.b ? 0xFF : 0x00;
|
||||
dinput_report.square_axis = gamepad->state.x ? 0xFF : 0x00;
|
||||
dinput_report.triangle_axis = gamepad->state.y ? 0xFF : 0x00;
|
||||
dinput_report.l1_axis = gamepad->state.lb ? 0xFF : 0x00;
|
||||
dinput_report.r1_axis = gamepad->state.rb ? 0xFF : 0x00;
|
||||
|
||||
dinput_report.l2_axis = gamepad->state.lt;
|
||||
dinput_report.r2_axis = gamepad->state.rt;
|
||||
|
||||
dinput_report.l_x_axis = scale_int16_to_uint8(gamepad->state.lx, false);
|
||||
dinput_report.l_y_axis = scale_int16_to_uint8(gamepad->state.ly, true);
|
||||
dinput_report.r_x_axis = scale_int16_to_uint8(gamepad->state.rx, false);
|
||||
dinput_report.r_y_axis = scale_int16_to_uint8(gamepad->state.ry, true);
|
||||
|
||||
// Wake up TinyUSB device
|
||||
if (tud_suspended())
|
||||
tud_remote_wakeup();
|
||||
|
||||
void * report = &dinput_report;
|
||||
uint16_t report_size = sizeof(dinput_report);
|
||||
// if (memcmp(last_report, report, report_size) != 0)
|
||||
// {
|
||||
// HID ready + report sent, copy previous report
|
||||
if (tud_hid_ready() && tud_hid_report(0, report, report_size) == true ) {
|
||||
memcpy(last_report, report, report_size);
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
// tud_hid_get_report_cb
|
||||
uint16_t DInputDriver::get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
memcpy(buffer, &dinput_report, sizeof(DInputReport));
|
||||
return sizeof(DInputReport);
|
||||
}
|
||||
|
||||
// Only PS4 does anything with set report
|
||||
void DInputDriver::set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize)
|
||||
{
|
||||
// testing things, I'll have a usb sniffer soon so this will get figured out then
|
||||
|
||||
// DInputOutReport dinput_out_report = {0};
|
||||
// memcpy(&dinput_out_report, buffer, sizeof(dinput_out_report));
|
||||
|
||||
// // gamepadOut.out_state.rrumble = dinput_out_report.rumble.right_motor_on ? 0xFF : 0x00;
|
||||
// // gamepadOut.out_state.lrumble = gamepadOut.out_state.rrumble = dinput_out_report.rumble.left_motor_force;
|
||||
|
||||
// // if (dinput_out_report.rumble.left_motor_force > 0)
|
||||
// if (dinput_out_report.led->time_enabled > 0)
|
||||
// gamepadOut.out_state.lrumble = gamepadOut.out_state.rrumble = 0xFF;
|
||||
// if (dinput_out_report.led->duty_length > 0)
|
||||
// gamepadOut.out_state.lrumble = gamepadOut.out_state.rrumble = 0xFF;
|
||||
// if (dinput_out_report.led->enabled > 0)
|
||||
// gamepadOut.out_state.lrumble = gamepadOut.out_state.rrumble = 0xFF;
|
||||
// if (dinput_out_report.led->duty_off > 0)
|
||||
// gamepadOut.out_state.lrumble = gamepadOut.out_state.rrumble = 0xFF;
|
||||
// if (dinput_out_report.led->duty_on > 0)
|
||||
// gamepadOut.out_state.lrumble = gamepadOut.out_state.rrumble = 0xFF;
|
||||
}
|
||||
|
||||
// Only XboxOG and Xbox One use vendor control xfer cb
|
||||
bool DInputDriver::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint16_t * DInputDriver::get_descriptor_string_cb(uint8_t index, uint16_t langid)
|
||||
{
|
||||
const char *value = (const char *)dinput_string_descriptors[index];
|
||||
return getStringDescriptor(value, index); // getStringDescriptor returns a static array
|
||||
}
|
||||
|
||||
const uint8_t * DInputDriver::get_descriptor_device_cb()
|
||||
{
|
||||
return dinput_device_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * DInputDriver::get_hid_descriptor_report_cb(uint8_t itf)
|
||||
{
|
||||
return dinput_report_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * DInputDriver::get_descriptor_configuration_cb(uint8_t index)
|
||||
{
|
||||
return dinput_configuration_descriptor;
|
||||
}
|
||||
|
||||
const uint8_t * DInputDriver::get_descriptor_device_qualifier_cb()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint16_t DInputDriver::GetJoystickMidValue()
|
||||
{
|
||||
return DINPUT_JOYSTICK_MID << 8;
|
||||
}
|
||||
32
src/usbd/dinput/DInputDriver.h
Normal file
32
src/usbd/dinput/DInputDriver.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2024 OpenStickCommunity (gp2040-ce.info)
|
||||
*/
|
||||
|
||||
#ifndef _HID_DRIVER_H_
|
||||
#define _HID_DRIVER_H_
|
||||
|
||||
#include "usbd/gpdriver.h"
|
||||
#include "descriptors/DInputDescriptors.h"
|
||||
#include "descriptors/PS3Descriptors.h"
|
||||
|
||||
class DInputDriver : public GPDriver {
|
||||
public:
|
||||
virtual void initialize();
|
||||
virtual void process(Gamepad * gamepad, uint8_t * outBuffer);
|
||||
virtual uint16_t get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen);
|
||||
virtual void set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize);
|
||||
virtual bool vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request);
|
||||
virtual const uint16_t * get_descriptor_string_cb(uint8_t index, uint16_t langid);
|
||||
virtual const uint8_t * get_descriptor_device_cb();
|
||||
virtual const uint8_t * get_hid_descriptor_report_cb(uint8_t itf) ;
|
||||
virtual const uint8_t * get_descriptor_configuration_cb(uint8_t index);
|
||||
virtual const uint8_t * get_descriptor_device_qualifier_cb();
|
||||
virtual uint16_t GetJoystickMidValue();
|
||||
private:
|
||||
uint8_t last_report[CFG_TUD_ENDPOINT0_SIZE] = { };
|
||||
|
||||
DInputReport dinput_report;
|
||||
};
|
||||
|
||||
#endif // _HID_DRIVER_H_
|
||||
@@ -3,6 +3,7 @@
|
||||
// #include "net/NetDriver.h"
|
||||
// #include "keyboard/KeyboardDriver.h"
|
||||
|
||||
// #include "usbd/dinput/DInputDriver.h"
|
||||
#include "usbd/hid/HIDDriver.h"
|
||||
#include "usbd/psclassic/PSClassicDriver.h"
|
||||
#include "usbd/switch/SwitchDriver.h"
|
||||
@@ -23,6 +24,9 @@ void DriverManager::setup(InputMode mode)
|
||||
case INPUT_MODE_HID:
|
||||
driver = new HIDDriver();
|
||||
break;
|
||||
// case INPUT_MODE_HID:
|
||||
// driver = new DInputDriver();
|
||||
// break;
|
||||
case INPUT_MODE_PSCLASSIC:
|
||||
driver = new PSClassicDriver();
|
||||
break;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "usbd/hid/HIDDriver.h"
|
||||
#include "descriptors/HIDDescriptors.h"
|
||||
#include "descriptors/PS3Descriptors.h"
|
||||
#include "usbd/shared/driverhelper.h"
|
||||
|
||||
// Magic byte sequence to enable PS button on PS3
|
||||
@@ -127,11 +128,13 @@ void HIDDriver::process(Gamepad * gamepad, uint8_t * outBuffer)
|
||||
uint16_t HIDDriver::get_report(uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
memcpy(buffer, &hidReport, sizeof(HIDReport));
|
||||
return sizeof(HIDReport);
|
||||
return sizeof(HIDReport);
|
||||
}
|
||||
|
||||
// Only PS4 does anything with set report
|
||||
void HIDDriver::set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize) {}
|
||||
void HIDDriver::set_report(uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize)
|
||||
{
|
||||
}
|
||||
|
||||
// Only XboxOG and Xbox One use vendor control xfer cb
|
||||
bool HIDDriver::vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
|
||||
#include "usbh/tusb_host_manager.h" // global enum host_mode
|
||||
|
||||
// TODO: make a host driver class for all this
|
||||
// TODO: make a class for all this
|
||||
|
||||
Mouse* mouse = nullptr;
|
||||
N64USB* n64usb = nullptr;
|
||||
PSClassic* psclassic = nullptr;
|
||||
Dualshock3* dualshock3 = nullptr;
|
||||
@@ -81,6 +82,11 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re
|
||||
n64usb = new N64USB();
|
||||
n64usb->init(dev_addr, instance);
|
||||
}
|
||||
else if (host_mode == HOST_MODE_HID_MOUSE && !mouse)
|
||||
{
|
||||
mouse = new Mouse();
|
||||
mouse->init(dev_addr, instance);
|
||||
}
|
||||
|
||||
if (!tuh_hid_receive_report(dev_addr, instance))
|
||||
{
|
||||
@@ -129,6 +135,11 @@ void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
|
||||
delete dualsense;
|
||||
dualsense = nullptr;
|
||||
}
|
||||
if (mouse)
|
||||
{
|
||||
delete mouse;
|
||||
mouse = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +169,9 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t cons
|
||||
case HOST_MODE_HID_N64USB:
|
||||
n64usb->process_report(report, len);
|
||||
break;
|
||||
case HOST_MODE_HID_MOUSE:
|
||||
mouse->process_report(report, len);
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !tuh_hid_receive_report(dev_addr, instance) )
|
||||
@@ -201,6 +215,9 @@ bool send_fb_data_to_hid_gamepad()
|
||||
case HOST_MODE_HID_N64USB:
|
||||
rumble_sent = n64usb->send_fb_data();
|
||||
break;
|
||||
case HOST_MODE_HID_MOUSE:
|
||||
rumble_sent = mouse->send_fb_data();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef _HID_HOST_APP_H_
|
||||
#define _HID_HOST_APP_H_
|
||||
|
||||
#include "usbh/tusb_hid/mouse.h"
|
||||
#include "usbh/tusb_hid/n64usb.h"
|
||||
#include "usbh/tusb_hid/psclassic.h"
|
||||
#include "usbh/tusb_hid/ps3.h"
|
||||
|
||||
76
src/usbh/tusb_hid/mouse.cpp
Normal file
76
src/usbh/tusb_hid/mouse.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include <stdint.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "tusb.h"
|
||||
|
||||
#include "usbh/tusb_hid/mouse.h"
|
||||
|
||||
#include "utilities/scaling.h"
|
||||
|
||||
#include "Gamepad.h"
|
||||
|
||||
void Mouse::init(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
mouse.dev_addr = dev_addr;
|
||||
mouse.instance = instance;
|
||||
}
|
||||
|
||||
int16_t Mouse::scale_and_clamp_axes(int32_t value)
|
||||
{
|
||||
// minimum % of int16 +/- joystick value allowed
|
||||
float minimum_percentage = 0.10; // 5%
|
||||
int32_t scaled_value = 0;
|
||||
|
||||
if (value > 0)
|
||||
{
|
||||
scaled_value = (INT16_MAX * minimum_percentage) + ((1 - minimum_percentage) * value);
|
||||
}
|
||||
else if (value < 0)
|
||||
{
|
||||
scaled_value = (INT16_MIN * minimum_percentage) + ((1 - minimum_percentage) * value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (scaled_value >= INT16_MAX) return INT16_MAX;
|
||||
else if (scaled_value <= INT16_MIN) return INT16_MIN;
|
||||
|
||||
return (int16_t)scaled_value;
|
||||
}
|
||||
|
||||
void Mouse::update_gamepad(const hid_mouse_report_t* mouse_report)
|
||||
{
|
||||
// gamepad.reset_state();
|
||||
|
||||
gamepad.state.rt = (mouse_report->buttons & MOUSE_BUTTON_LEFT ) ? 0xFF : 0x00;
|
||||
gamepad.state.lt = (mouse_report->buttons & MOUSE_BUTTON_RIGHT) ? 0xFF : 0x00;
|
||||
|
||||
// for testing
|
||||
gamepad.state.x = (mouse_report->buttons & MOUSE_BUTTON_BACKWARD);
|
||||
gamepad.state.a = (mouse_report->buttons & MOUSE_BUTTON_FORWARD );
|
||||
|
||||
gamepad.state.y = (mouse_report->wheel != 0);
|
||||
|
||||
int32_t scaled_y = scale_int8_to_int16(mouse_report->y, true) * 2; // * 2 to make more responsive
|
||||
int32_t scaled_x = scale_int8_to_int16(mouse_report->x, false) * 2;
|
||||
|
||||
gamepad.state.ry = scale_and_clamp_axes(scaled_y);
|
||||
gamepad.state.rx = scale_and_clamp_axes(scaled_x);
|
||||
}
|
||||
|
||||
void Mouse::process_report(uint8_t const* report, uint16_t len)
|
||||
{
|
||||
static hid_mouse_report_t prev_report = { 0 };
|
||||
|
||||
hid_mouse_report_t mouse_report;
|
||||
memcpy(&mouse_report, report, sizeof(mouse_report));
|
||||
|
||||
if (memcmp(&mouse_report, &prev_report, sizeof(mouse_report)) != 0)
|
||||
update_gamepad(&mouse_report);
|
||||
}
|
||||
|
||||
bool Mouse::send_fb_data()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
31
src/usbh/tusb_hid/mouse.h
Normal file
31
src/usbh/tusb_hid/mouse.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _MOUSE_H_
|
||||
#define _MOUSE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "class/hid/hid.h"
|
||||
#include "usbh/tusb_hid/shared.h"
|
||||
|
||||
struct MouseState
|
||||
{
|
||||
uint8_t dev_addr = {0};
|
||||
uint8_t instance = {0};
|
||||
};
|
||||
|
||||
class Mouse
|
||||
{
|
||||
public:
|
||||
void init(uint8_t dev_addr, uint8_t instance);
|
||||
void process_report(uint8_t const* report, uint16_t len);
|
||||
bool send_fb_data();
|
||||
|
||||
private:
|
||||
MouseState mouse;
|
||||
|
||||
int16_t scale_and_clamp_axes(int32_t value);
|
||||
void update_gamepad(const hid_mouse_report_t* mouse_report);
|
||||
};
|
||||
|
||||
#endif // _MOUSE_H_
|
||||
@@ -6,9 +6,7 @@
|
||||
#include "class/hid/hid_host.h"
|
||||
|
||||
#include "utilities/scaling.h"
|
||||
|
||||
#include "usbh/tusb_hid/ps3.h"
|
||||
|
||||
#include "Gamepad.h"
|
||||
|
||||
#include "utilities/log.h"
|
||||
@@ -21,6 +19,10 @@ void Dualshock3::init(uint8_t dev_addr, uint8_t instance)
|
||||
{
|
||||
dualshock3.dev_addr = dev_addr;
|
||||
dualshock3.instance = instance;
|
||||
|
||||
uint16_t vid, pid;
|
||||
tuh_vid_pid_get(dualshock3.dev_addr, &vid, &pid);
|
||||
if (vid == 0x054C && pid == 0x0268) dualshock3.sixaxis = true;
|
||||
|
||||
// tuh_hid_set_protocol(dualshock3.dev_addr, dualshock3.instance, HID_PROTOCOL_REPORT);
|
||||
// sleep_ms(200);
|
||||
@@ -73,6 +75,66 @@ bool Dualshock3::enable_reports()
|
||||
// dualshock3.instance = 0;
|
||||
// }
|
||||
|
||||
void Dualshock3::update_gamepad_dinput(const DInputReport* dinput_report)
|
||||
{
|
||||
gamepad.reset_state();
|
||||
|
||||
switch (dinput_report->direction)
|
||||
{
|
||||
case DINPUT_HAT_UP:
|
||||
gamepad.state.up = true;
|
||||
break;
|
||||
case DINPUT_HAT_UPRIGHT:
|
||||
gamepad.state.up = true;
|
||||
gamepad.state.right = true;
|
||||
break;
|
||||
case DINPUT_HAT_RIGHT:
|
||||
gamepad.state.right = true;
|
||||
break;
|
||||
case DINPUT_HAT_DOWNRIGHT:
|
||||
gamepad.state.right = true;
|
||||
gamepad.state.down = true;
|
||||
break;
|
||||
case DINPUT_HAT_DOWN:
|
||||
gamepad.state.down = true;
|
||||
break;
|
||||
case DINPUT_HAT_DOWNLEFT:
|
||||
gamepad.state.down = true;
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
case DINPUT_HAT_LEFT:
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
case DINPUT_HAT_UPLEFT:
|
||||
gamepad.state.up = true;
|
||||
gamepad.state.left = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dinput_report->square_btn) gamepad.state.x = true;
|
||||
if (dinput_report->triangle_btn) gamepad.state.y = true;
|
||||
if (dinput_report->cross_btn) gamepad.state.a = true;
|
||||
if (dinput_report->circle_btn) gamepad.state.b = true;
|
||||
|
||||
if (dinput_report->select_btn) gamepad.state.back = true;
|
||||
if (dinput_report->start_btn) gamepad.state.start = true;
|
||||
if (dinput_report->ps_btn) gamepad.state.sys = true;
|
||||
|
||||
if (dinput_report->l3_btn) gamepad.state.l3 = true;
|
||||
if (dinput_report->r3_btn) gamepad.state.r3 = true;
|
||||
|
||||
if (dinput_report->l1_btn) gamepad.state.lb = true;
|
||||
if (dinput_report->r1_btn) gamepad.state.rb = true;
|
||||
|
||||
if (dinput_report->l2_btn) gamepad.state.lt = 0xFF;
|
||||
if (dinput_report->r2_btn) gamepad.state.rt = 0xFF;
|
||||
|
||||
gamepad.state.lx = scale_uint8_to_int16(dinput_report->l_x_axis, false);
|
||||
gamepad.state.ly = scale_uint8_to_int16(dinput_report->l_y_axis, true);
|
||||
gamepad.state.rx = scale_uint8_to_int16(dinput_report->r_x_axis, false);
|
||||
gamepad.state.ry = scale_uint8_to_int16(dinput_report->r_y_axis, true);
|
||||
}
|
||||
|
||||
void Dualshock3::update_gamepad(const DualShock3Report* ds3_data)
|
||||
{
|
||||
gamepad.reset_state();
|
||||
@@ -100,10 +162,10 @@ void Dualshock3::update_gamepad(const DualShock3Report* ds3_data)
|
||||
if (ds3_data->l2) gamepad.state.lt = 0xFF;
|
||||
if (ds3_data->r2) gamepad.state.rt = 0xFF;
|
||||
|
||||
gamepad.state.lx = scale_uint8_to_int16(ds3_data->leftX, false);
|
||||
gamepad.state.ly = scale_uint8_to_int16(ds3_data->leftY, true);
|
||||
gamepad.state.rx = scale_uint8_to_int16(ds3_data->rightX, false);
|
||||
gamepad.state.ry = scale_uint8_to_int16(ds3_data->rightY, true);
|
||||
gamepad.state.lx = scale_uint8_to_int16(ds3_data->left_x, false);
|
||||
gamepad.state.ly = scale_uint8_to_int16(ds3_data->left_y, true);
|
||||
gamepad.state.rx = scale_uint8_to_int16(ds3_data->right_x, false);
|
||||
gamepad.state.ry = scale_uint8_to_int16(ds3_data->right_y, true);
|
||||
}
|
||||
|
||||
void Dualshock3::process_report(uint8_t const* report, uint16_t len)
|
||||
@@ -118,16 +180,22 @@ void Dualshock3::process_report(uint8_t const* report, uint16_t len)
|
||||
}
|
||||
log(hex_buffer);
|
||||
|
||||
static DualShock3Report prev_report = { 0 };
|
||||
|
||||
DualShock3Report ds3_report;
|
||||
memcpy(&ds3_report, report, sizeof(ds3_report));
|
||||
|
||||
// if (memcmp(&ds3_report, &prev_report, sizeof(ds3_report)) != 0)
|
||||
// {
|
||||
if (!dualshock3.sixaxis)
|
||||
{
|
||||
static DInputReport prev_report = {0};
|
||||
DInputReport dinput_report;
|
||||
memcpy(&dinput_report, report, sizeof(dinput_report));
|
||||
update_gamepad_dinput(&dinput_report);
|
||||
prev_report = dinput_report;
|
||||
}
|
||||
else
|
||||
{
|
||||
static DualShock3Report prev_report = { 0 };
|
||||
DualShock3Report ds3_report;
|
||||
memcpy(&ds3_report, report, sizeof(ds3_report));
|
||||
update_gamepad(&ds3_report);
|
||||
prev_report = ds3_report;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
bool Dualshock3::send_fb_data()
|
||||
@@ -142,13 +210,22 @@ bool Dualshock3::send_fb_data()
|
||||
0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
struct sixaxis_output_report default_output_report;
|
||||
struct sixaxis_output_report output_report;
|
||||
|
||||
memcpy(&default_output_report, default_report, sizeof(default_output_report));
|
||||
memcpy(&output_report, default_report, sizeof(output_report));
|
||||
|
||||
default_output_report.leds_bitmap |= 0x1 << (dualshock3.instance+1);
|
||||
// default_output_report.leds_bitmap |= 0x1 << (dualshock3.instance+1);
|
||||
output_report.leds_bitmap = 0x02;
|
||||
// output_report.led->time_enabled = 0xFF;
|
||||
// output_report.led->duty_on = 0xFF;
|
||||
|
||||
bool rumble_sent = tuh_hid_send_report(dualshock3.dev_addr, dualshock3.instance, 0x1, &default_output_report, sizeof(default_output_report));
|
||||
output_report.rumble.right_duration = UINT8_MAX / 2;
|
||||
if (gamepadOut.out_state.rrumble > 0) output_report.rumble.right_motor_on = 1;
|
||||
|
||||
output_report.rumble.left_duration = UINT8_MAX / 2;
|
||||
output_report.rumble.left_motor_force = gamepadOut.out_state.lrumble;
|
||||
|
||||
bool rumble_sent = tuh_hid_send_report(dualshock3.dev_addr, dualshock3.instance, 1, &output_report, sizeof(output_report));
|
||||
|
||||
return rumble_sent;
|
||||
// return true;
|
||||
|
||||
@@ -6,12 +6,15 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "usbh/tusb_hid/shared.h"
|
||||
#include "descriptors/DInputDescriptors.h"
|
||||
#include "descriptors/PS3Descriptors.h"
|
||||
|
||||
#define PS3_REPORT_BUFFER_SIZE 48
|
||||
|
||||
const usb_vid_pid_t ps3_devices[] =
|
||||
{
|
||||
{0x054C, 0x0268}, // Sony Batoh (Dualshock 3)
|
||||
{0x2563, 0x0575}, // Nuplay Armor 3
|
||||
// {0x045E, 0x028E}, // Voyee generic "P3", won't work, IDs are for a 360 controller (dumb)
|
||||
{0x044F, 0xB324}, // ThrustMaster Dual Trigger (PS3 mode)
|
||||
{0x0738, 0x8818}, // MadCatz Street Fighter IV Arcade FightStick
|
||||
@@ -19,76 +22,10 @@ const usb_vid_pid_t ps3_devices[] =
|
||||
{0x146B, 0x0902} // BigBen Interactive Wired Mini PS3 Game Controller
|
||||
};
|
||||
|
||||
struct DualShock3Report
|
||||
{
|
||||
uint8_t reportId;
|
||||
uint8_t unk0;
|
||||
|
||||
uint8_t select : 1;
|
||||
uint8_t l3 : 1;
|
||||
uint8_t r3 : 1;
|
||||
uint8_t start : 1;
|
||||
uint8_t up : 1;
|
||||
uint8_t right : 1;
|
||||
uint8_t down : 1;
|
||||
uint8_t left : 1;
|
||||
|
||||
uint8_t l2 : 1;
|
||||
uint8_t r2 : 1;
|
||||
uint8_t l1 : 1;
|
||||
uint8_t r1 : 1;
|
||||
uint8_t triangle : 1;
|
||||
uint8_t circle : 1;
|
||||
uint8_t cross : 1;
|
||||
uint8_t square : 1;
|
||||
|
||||
uint8_t ps : 1;
|
||||
uint8_t : 0;
|
||||
|
||||
uint8_t unk1;
|
||||
|
||||
uint8_t leftX;
|
||||
uint8_t leftY;
|
||||
uint8_t rightX;
|
||||
uint8_t rightY;
|
||||
|
||||
uint8_t unk2[31];
|
||||
|
||||
int16_t accelerX;
|
||||
int16_t accelerY;
|
||||
int16_t accelerZ;
|
||||
|
||||
int16_t velocityZ;
|
||||
}
|
||||
__attribute__((packed));
|
||||
|
||||
struct sixaxis_led {
|
||||
uint8_t time_enabled; /* the total time the led is active (0xff means forever) */
|
||||
uint8_t duty_length; /* how long a cycle is in deciseconds (0 means "really fast") */
|
||||
uint8_t enabled;
|
||||
uint8_t duty_off; /* % of duty_length the led is off (0xff means 100%) */
|
||||
uint8_t duty_on; /* % of duty_length the led is on (0xff mean 100%) */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sixaxis_rumble {
|
||||
uint8_t padding;
|
||||
uint8_t right_duration; /* Right motor duration (0xff means forever) */
|
||||
uint8_t right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
|
||||
uint8_t left_duration; /* Left motor duration (0xff means forever) */
|
||||
uint8_t left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sixaxis_output_report {
|
||||
struct sixaxis_rumble rumble;
|
||||
uint8_t padding[4];
|
||||
uint8_t leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
|
||||
struct sixaxis_led led[4]; /* LEDx at (4 - x) */
|
||||
struct sixaxis_led _reserved; /* LED5, not actually soldered */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct Dualshock3State
|
||||
{
|
||||
bool report_enabled {false};
|
||||
bool sixaxis {false};
|
||||
uint8_t dev_addr = {0};
|
||||
uint8_t instance = {0};
|
||||
};
|
||||
@@ -104,8 +41,9 @@ class Dualshock3
|
||||
Dualshock3State dualshock3;
|
||||
|
||||
bool enable_reports();
|
||||
void reset_state();
|
||||
// void reset_state();
|
||||
void update_gamepad(const DualShock3Report* ds3_data);
|
||||
void update_gamepad_dinput(const DInputReport* dinput_report);
|
||||
};
|
||||
|
||||
#endif // _PS3_H_
|
||||
@@ -14,6 +14,7 @@ const usb_vid_pid_t ps4_devices[] =
|
||||
{0x054C, 0x05C4}, // DS4
|
||||
{0x054C, 0x09CC}, // DS4
|
||||
{0x054C, 0x0BA0}, // DS4 wireless adapter
|
||||
{0x2563, 0x0357}, // MPOW Wired Gamepad (ShenZhen ShanWan)
|
||||
{0x0F0D, 0x005E}, // Hori FC4
|
||||
{0x0F0D, 0x00EE}, // Hori PS4 Mini (PS4-099U)
|
||||
{0x1F4F, 0x1002} // ASW GG Xrd controller
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
#include "tusb_host.h"
|
||||
#include "tusb_host_manager.h"
|
||||
|
||||
// TODO figure out something to do with the leds
|
||||
// tuh_mounted is always true after unplugging a gamepad, idk what's up with that
|
||||
|
||||
#ifdef FEATHER_RP2040
|
||||
#define PIO_USB_DP_PIN 16 // DM = 17
|
||||
#define PIN_5V_EN 18
|
||||
|
||||
@@ -14,6 +14,7 @@ HostMode host_mode;
|
||||
|
||||
bool device_mounted = false;
|
||||
uint8_t device_daddr = 0;
|
||||
tusb_desc_interface_t config_descriptor;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -26,7 +27,7 @@ const DeviceTypeInfo device_types[] =
|
||||
{
|
||||
{ n64_devices, sizeof(n64_devices) / sizeof(usb_vid_pid_t), HOST_MODE_HID_N64USB },
|
||||
{ psc_devices, sizeof(psc_devices) / sizeof(usb_vid_pid_t), HOST_MODE_HID_PSCLASSIC },
|
||||
{ ps3_devices, sizeof(ps3_devices) / sizeof(usb_vid_pid_t), HOST_MODE_HID_PS3 },
|
||||
// { ps3_devices, sizeof(ps3_devices) / sizeof(usb_vid_pid_t), HOST_MODE_HID_PS3 },
|
||||
{ ps4_devices, sizeof(ps4_devices) / sizeof(usb_vid_pid_t), HOST_MODE_HID_PS4 },
|
||||
{ ps5_devices, sizeof(ps5_devices) / sizeof(usb_vid_pid_t), HOST_MODE_HID_PS5 },
|
||||
{ switch_wired_devices, sizeof(switch_wired_devices) / sizeof(usb_vid_pid_t), HOST_MODE_HID_SWITCH_WIRED },
|
||||
@@ -46,6 +47,20 @@ bool check_vid_pid(const usb_vid_pid_t* devices, size_t num_devices, HostMode ch
|
||||
return false;
|
||||
}
|
||||
|
||||
void config_descriptor_complete_cb(tuh_xfer_t* xfer)
|
||||
{
|
||||
if (xfer->result == XFER_RESULT_SUCCESS)
|
||||
{
|
||||
if ((config_descriptor.bInterfaceSubClass == 0x5D && config_descriptor.bInterfaceProtocol == 0x81) ||
|
||||
(config_descriptor.bInterfaceSubClass == 0x5D && config_descriptor.bInterfaceProtocol == 0x01) ||
|
||||
(config_descriptor.bInterfaceSubClass == 0x47 && config_descriptor.bInterfaceProtocol == 0xD0) ||
|
||||
(config_descriptor.bInterfaceSubClass == 0x42 && config_descriptor.bInterfaceClass == 0x58))
|
||||
{
|
||||
host_mode = HOST_MODE_XINPUT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tuh_mount_cb(uint8_t daddr)
|
||||
{
|
||||
if (!device_mounted)
|
||||
@@ -57,8 +72,6 @@ void tuh_mount_cb(uint8_t daddr)
|
||||
uint16_t vid, pid;
|
||||
tuh_vid_pid_get(daddr, &vid, &pid);
|
||||
|
||||
host_mode = HOST_MODE_XINPUT;
|
||||
|
||||
// set host mode depending on VID/PID match
|
||||
const size_t num_device_types = sizeof(device_types) / sizeof(DeviceTypeInfo);
|
||||
|
||||
@@ -71,7 +84,10 @@ void tuh_mount_cb(uint8_t daddr)
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
// if (!tuh_descriptor_get_configuration(daddr, 0, &config_descriptor, sizeof(config_descriptor), config_descriptor_complete_cb, 0))
|
||||
// {
|
||||
host_mode = HOST_MODE_HID_MOUSE; // idk
|
||||
// }
|
||||
}
|
||||
|
||||
void tuh_umount_cb(uint8_t daddr)
|
||||
@@ -106,17 +122,7 @@ void send_fb_data_to_gamepad()
|
||||
else
|
||||
{
|
||||
if (send_fb_data_to_hid_gamepad())
|
||||
{
|
||||
// needed so rumble doesn't get stuck on
|
||||
gamepadOut.rumble_hid_reset();
|
||||
}
|
||||
gamepadOut.rumble_hid_reset(); // needed so rumble doesn't get stuck on
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not sure why this and tuh_mounted are always true
|
||||
// unused atm, come back to it
|
||||
// bool tuh_device_mounted()
|
||||
// {
|
||||
// return device_mounted;
|
||||
// }
|
||||
}
|
||||
@@ -10,7 +10,8 @@ typedef enum
|
||||
HOST_MODE_HID_PS3,
|
||||
HOST_MODE_HID_PS4,
|
||||
HOST_MODE_HID_PS5,
|
||||
HOST_MODE_HID_N64USB
|
||||
HOST_MODE_HID_N64USB,
|
||||
HOST_MODE_HID_MOUSE
|
||||
} HostMode;
|
||||
|
||||
extern HostMode host_mode;
|
||||
|
||||
@@ -44,5 +44,28 @@ int16_t scale_uint8_to_int16(uint8_t value, bool invert)
|
||||
scaled_value = INT16_MAX;
|
||||
}
|
||||
|
||||
return (int16_t)scaled_value;
|
||||
}
|
||||
|
||||
int16_t scale_int8_to_int16(int8_t value, bool invert)
|
||||
{
|
||||
const uint16_t multiplier = 257;
|
||||
|
||||
int32_t scaled_value = (int32_t)value * multiplier;
|
||||
|
||||
if (invert)
|
||||
{
|
||||
scaled_value = -scaled_value;
|
||||
}
|
||||
|
||||
if (scaled_value < INT16_MIN)
|
||||
{
|
||||
scaled_value = INT16_MIN;
|
||||
}
|
||||
else if (scaled_value > INT16_MAX)
|
||||
{
|
||||
scaled_value = INT16_MAX;
|
||||
}
|
||||
|
||||
return (int16_t)scaled_value;
|
||||
}
|
||||
@@ -7,5 +7,6 @@
|
||||
|
||||
uint8_t scale_int16_to_uint8(int16_t value, bool invert);
|
||||
int16_t scale_uint8_to_int16(uint8_t value, bool invert);
|
||||
int16_t scale_int8_to_int16(int8_t value, bool invert);
|
||||
|
||||
#endif // _SCALING_H_
|
||||
Reference in New Issue
Block a user