2021-08-24 19:10:23 +07:00
|
|
|
/*
|
|
|
|
* The MIT License (MIT)
|
|
|
|
*
|
|
|
|
* Copyright (c) 2021, Ha Thach (tinyusb.org)
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "bsp/board.h"
|
|
|
|
#include "tusb.h"
|
|
|
|
|
|
|
|
/* From https://www.kernel.org/doc/html/latest/input/gamepad.html
|
|
|
|
____________________________ __
|
|
|
|
/ [__ZL__] [__ZR__] \ |
|
|
|
|
/ [__ TL __] [__ TR __] \ | Front Triggers
|
|
|
|
__/________________________________\__ __|
|
|
|
|
/ _ \ |
|
|
|
|
/ /\ __ (N) \ |
|
|
|
|
/ || __ |MO| __ _ _ \ | Main Pad
|
|
|
|
| <===DP===> |SE| |ST| (W) -|- (E) | |
|
|
|
|
\ || ___ ___ _ / |
|
|
|
|
/\ \/ / \ / \ (S) /\ __|
|
|
|
|
/ \________ | LS | ____ | RS | ________/ \ |
|
|
|
|
| / \ \___/ / \ \___/ / \ | | Control Sticks
|
|
|
|
| / \_____/ \_____/ \ | __|
|
|
|
|
| / \ |
|
|
|
|
\_____/ \_____/
|
|
|
|
|
|
|
|
|________|______| |______|___________|
|
|
|
|
D-Pad Left Right Action Pad
|
|
|
|
Stick Stick
|
|
|
|
|
|
|
|
|_____________|
|
|
|
|
Menu Pad
|
|
|
|
|
|
|
|
Most gamepads have the following features:
|
|
|
|
- Action-Pad 4 buttons in diamonds-shape (on the right side) NORTH, SOUTH, WEST and EAST.
|
|
|
|
- D-Pad (Direction-pad) 4 buttons (on the left side) that point up, down, left and right.
|
|
|
|
- Menu-Pad Different constellations, but most-times 2 buttons: SELECT - START.
|
|
|
|
- Analog-Sticks provide freely moveable sticks to control directions, Analog-sticks may also
|
|
|
|
provide a digital button if you press them.
|
|
|
|
- Triggers are located on the upper-side of the pad in vertical direction. The upper buttons
|
|
|
|
are normally named Left- and Right-Triggers, the lower buttons Z-Left and Z-Right.
|
|
|
|
- Rumble Many devices provide force-feedback features. But are mostly just simple rumble motors.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Sony DS4 report layout detail https://www.psdevwiki.com/ps4/DS4-USB
|
|
|
|
typedef struct TU_ATTR_PACKED
|
|
|
|
{
|
2021-08-24 21:34:51 +07:00
|
|
|
uint8_t x, y, z, rz; // joystick
|
2021-08-24 19:10:23 +07:00
|
|
|
|
|
|
|
struct {
|
|
|
|
uint8_t dpad : 4; // (hat format, 0x08 is released, 0=N, 1=NE, 2=E, 3=SE, 4=S, 5=SW, 6=W, 7=NW)
|
|
|
|
uint8_t square : 1; // west
|
|
|
|
uint8_t cross : 1; // south
|
|
|
|
uint8_t circle : 1; // east
|
|
|
|
uint8_t triangle : 1; // north
|
|
|
|
};
|
|
|
|
|
|
|
|
struct {
|
|
|
|
uint8_t l1 : 1;
|
|
|
|
uint8_t r1 : 1;
|
|
|
|
uint8_t l2 : 1;
|
|
|
|
uint8_t r2 : 1;
|
|
|
|
uint8_t share : 1;
|
|
|
|
uint8_t option : 1;
|
|
|
|
uint8_t l3 : 1;
|
|
|
|
uint8_t r3 : 1;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct {
|
|
|
|
uint8_t ps : 1; // playstation button
|
|
|
|
uint8_t tpad : 1; // track pad click
|
|
|
|
uint8_t counter : 6; // +1 each report
|
|
|
|
};
|
|
|
|
|
2023-02-09 19:42:36 -07:00
|
|
|
uint8_t l2_trigger; // 0 released, 0xff fully pressed
|
|
|
|
uint8_t r2_trigger; // as above
|
2021-08-24 19:10:23 +07:00
|
|
|
|
|
|
|
// uint16_t timestamp;
|
|
|
|
// uint8_t battery;
|
|
|
|
//
|
|
|
|
// int16_t gyro[3]; // x, y, z;
|
|
|
|
// int16_t accel[3]; // x, y, z
|
|
|
|
|
|
|
|
// there is still lots more info
|
|
|
|
|
|
|
|
} sony_ds4_report_t;
|
|
|
|
|
2023-02-09 19:42:36 -07:00
|
|
|
typedef struct TU_ATTR_PACKED {
|
|
|
|
// First 16 bits set what data is pertinent in this structure (1 = set; 0 = not set)
|
2023-02-09 19:52:42 -07:00
|
|
|
uint8_t set_rumble : 1;
|
2023-02-09 19:42:36 -07:00
|
|
|
uint8_t set_led : 1;
|
|
|
|
uint8_t set_led_blink : 1;
|
|
|
|
uint8_t set_ext_write : 1;
|
|
|
|
uint8_t set_left_volume : 1;
|
|
|
|
uint8_t set_right_volume : 1;
|
|
|
|
uint8_t set_mic_volume : 1;
|
|
|
|
uint8_t set_speaker_volume : 1;
|
|
|
|
uint8_t set_flags2;
|
|
|
|
|
2023-02-09 19:52:42 -07:00
|
|
|
uint8_t reserved;
|
2023-02-09 19:42:36 -07:00
|
|
|
|
2023-02-09 19:52:42 -07:00
|
|
|
uint8_t motor_right;
|
|
|
|
uint8_t motor_left;
|
2023-02-09 19:42:36 -07:00
|
|
|
|
2023-02-09 19:52:42 -07:00
|
|
|
uint8_t lightbar_red;
|
|
|
|
uint8_t lightbar_green;
|
|
|
|
uint8_t lightbar_blue;
|
|
|
|
uint8_t lightbar_blink_on;
|
|
|
|
uint8_t lightbar_blink_off;
|
2023-02-09 19:42:36 -07:00
|
|
|
|
|
|
|
uint8_t ext_data[8];
|
|
|
|
|
|
|
|
uint8_t volume_left;
|
|
|
|
uint8_t volume_right;
|
|
|
|
uint8_t volume_mic;
|
|
|
|
uint8_t volume_speaker;
|
|
|
|
|
|
|
|
uint8_t other[9];
|
|
|
|
} sony_ds4_output_report_t;
|
|
|
|
|
|
|
|
static bool ds4_mounted = false;
|
|
|
|
static uint8_t ds4_dev_addr = 0;
|
|
|
|
static uint8_t ds4_instance = 0;
|
|
|
|
static uint8_t motor_left = 0;
|
|
|
|
static uint8_t motor_right = 0;
|
|
|
|
|
2021-08-24 19:10:23 +07:00
|
|
|
// check if device is Sony DualShock 4
|
|
|
|
static inline bool is_sony_ds4(uint8_t dev_addr)
|
|
|
|
{
|
|
|
|
uint16_t vid, pid;
|
|
|
|
tuh_vid_pid_get(dev_addr, &vid, &pid);
|
|
|
|
|
2023-02-09 19:42:36 -07:00
|
|
|
return ( (vid == 0x054c && (pid == 0x09cc || pid == 0x05c4)) // Sony DualShock4
|
|
|
|
|| (vid == 0x0f0d && pid == 0x005e) // Hori FC4
|
|
|
|
|| (vid == 0x0f0d && pid == 0x00ee) // Hori PS4 Mini (PS4-099U)
|
2021-09-06 19:41:09 +02:00
|
|
|
|| (vid == 0x1f4f && pid == 0x1002) // ASW GG xrd controller
|
2021-09-06 19:40:14 +02:00
|
|
|
);
|
2021-08-24 19:10:23 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------+
|
|
|
|
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
|
|
|
|
//--------------------------------------------------------------------+
|
|
|
|
|
|
|
|
void hid_app_task(void)
|
|
|
|
{
|
2023-02-09 19:42:36 -07:00
|
|
|
if (ds4_mounted)
|
|
|
|
{
|
|
|
|
const uint32_t interval_ms = 200;
|
|
|
|
static uint32_t start_ms = 0;
|
|
|
|
|
|
|
|
if ( board_millis() - start_ms >= interval_ms)
|
|
|
|
{
|
|
|
|
start_ms += interval_ms;
|
|
|
|
|
|
|
|
sony_ds4_output_report_t output_report = {0};
|
|
|
|
output_report.set_rumble = 1;
|
|
|
|
output_report.motor_left = motor_left;
|
|
|
|
output_report.motor_right = motor_right;
|
|
|
|
tuh_hid_send_report(ds4_dev_addr, ds4_instance, 5, &output_report, sizeof(output_report));
|
|
|
|
}
|
|
|
|
}
|
2021-08-24 19:10:23 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------+
|
|
|
|
// TinyUSB Callbacks
|
|
|
|
//--------------------------------------------------------------------+
|
|
|
|
|
|
|
|
// Invoked when device with hid interface is mounted
|
|
|
|
// Report descriptor is also available for use. tuh_hid_parse_report_descriptor()
|
|
|
|
// can be used to parse common/simple enough descriptor.
|
|
|
|
// Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped
|
|
|
|
// therefore report_desc = NULL, desc_len = 0
|
|
|
|
void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
|
|
|
|
{
|
2021-11-29 00:45:57 +09:00
|
|
|
(void)desc_report;
|
|
|
|
(void)desc_len;
|
2021-08-24 19:10:23 +07:00
|
|
|
uint16_t vid, pid;
|
|
|
|
tuh_vid_pid_get(dev_addr, &vid, &pid);
|
|
|
|
|
|
|
|
printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
|
|
|
|
printf("VID = %04x, PID = %04x\r\n", vid, pid);
|
|
|
|
|
|
|
|
// Sony DualShock 4 [CUH-ZCT2x]
|
|
|
|
if ( is_sony_ds4(dev_addr) )
|
|
|
|
{
|
2023-02-09 19:42:36 -07:00
|
|
|
if (!ds4_mounted)
|
|
|
|
{
|
|
|
|
ds4_dev_addr = dev_addr;
|
|
|
|
ds4_instance = instance;
|
|
|
|
motor_left = 0;
|
|
|
|
motor_right = 0;
|
|
|
|
ds4_mounted = true;
|
|
|
|
}
|
2021-08-24 19:10:23 +07:00
|
|
|
// request to receive report
|
|
|
|
// tuh_hid_report_received_cb() will be invoked when report is available
|
|
|
|
if ( !tuh_hid_receive_report(dev_addr, instance) )
|
|
|
|
{
|
|
|
|
printf("Error: cannot request to receive report\r\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Invoked when device with hid interface is un-mounted
|
|
|
|
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
|
|
|
|
{
|
|
|
|
printf("HID device address = %d, instance = %d is unmounted\r\n", dev_addr, instance);
|
2023-02-09 19:42:36 -07:00
|
|
|
if (ds4_mounted && ds4_dev_addr == dev_addr && ds4_instance == instance)
|
|
|
|
{
|
|
|
|
ds4_mounted = false;
|
|
|
|
}
|
2021-08-24 19:10:23 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
// check if different than 2
|
2021-08-24 21:34:51 +07:00
|
|
|
bool diff_than_2(uint8_t x, uint8_t y)
|
2021-08-24 19:10:23 +07:00
|
|
|
{
|
|
|
|
return (x - y > 2) || (y - x > 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if 2 reports are different enough
|
|
|
|
bool diff_report(sony_ds4_report_t const* rpt1, sony_ds4_report_t const* rpt2)
|
|
|
|
{
|
|
|
|
bool result;
|
|
|
|
|
|
|
|
// x, y, z, rz must different than 2 to be counted
|
|
|
|
result = diff_than_2(rpt1->x, rpt2->x) || diff_than_2(rpt1->y , rpt2->y ) ||
|
|
|
|
diff_than_2(rpt1->z, rpt2->z) || diff_than_2(rpt1->rz, rpt2->rz);
|
|
|
|
|
2023-02-09 19:42:36 -07:00
|
|
|
// check the rest with mem compare
|
|
|
|
result |= memcmp(&rpt1->rz + 1, &rpt2->rz + 1, sizeof(sony_ds4_report_t)-6);
|
2021-08-24 19:10:23 +07:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void process_sony_ds4(uint8_t const* report, uint16_t len)
|
|
|
|
{
|
|
|
|
const char* dpad_str[] = { "N", "NE", "E", "SE", "S", "SW", "W", "NW", "none" };
|
|
|
|
|
|
|
|
// previous report used to compare for changes
|
|
|
|
static sony_ds4_report_t prev_report = { 0 };
|
|
|
|
|
|
|
|
uint8_t const report_id = report[0];
|
|
|
|
report++;
|
|
|
|
len--;
|
|
|
|
|
|
|
|
// all buttons state is stored in ID 1
|
|
|
|
if (report_id == 1)
|
|
|
|
{
|
|
|
|
sony_ds4_report_t ds4_report;
|
|
|
|
memcpy(&ds4_report, report, sizeof(ds4_report));
|
|
|
|
|
|
|
|
// counter is +1, assign to make it easier to compare 2 report
|
|
|
|
prev_report.counter = ds4_report.counter;
|
|
|
|
|
|
|
|
// only print if changes since it is polled ~ 5ms
|
|
|
|
// Since count+1 after each report and x, y, z, rz fluctuate within 1 or 2
|
|
|
|
// We need more than memcmp to check if report is different enough
|
|
|
|
if ( diff_report(&prev_report, &ds4_report) )
|
|
|
|
{
|
2021-08-24 21:34:51 +07:00
|
|
|
printf("(x, y, z, rz) = (%u, %u, %u, %u)\r\n", ds4_report.x, ds4_report.y, ds4_report.z, ds4_report.rz);
|
2021-08-24 19:10:23 +07:00
|
|
|
printf("DPad = %s ", dpad_str[ds4_report.dpad]);
|
|
|
|
|
|
|
|
if (ds4_report.square ) printf("Square ");
|
|
|
|
if (ds4_report.cross ) printf("Cross ");
|
|
|
|
if (ds4_report.circle ) printf("Circle ");
|
|
|
|
if (ds4_report.triangle ) printf("Triangle ");
|
|
|
|
|
2021-08-24 21:34:51 +07:00
|
|
|
if (ds4_report.l1 ) printf("L1 ");
|
|
|
|
if (ds4_report.r1 ) printf("R1 ");
|
|
|
|
if (ds4_report.l2 ) printf("L2 ");
|
|
|
|
if (ds4_report.r2 ) printf("R2 ");
|
2021-08-24 19:10:23 +07:00
|
|
|
|
2021-08-24 21:34:51 +07:00
|
|
|
if (ds4_report.share ) printf("Share ");
|
|
|
|
if (ds4_report.option ) printf("Option ");
|
|
|
|
if (ds4_report.l3 ) printf("L3 ");
|
|
|
|
if (ds4_report.r3 ) printf("R3 ");
|
2021-08-24 19:10:23 +07:00
|
|
|
|
|
|
|
if (ds4_report.ps ) printf("PS ");
|
|
|
|
if (ds4_report.tpad ) printf("TPad ");
|
|
|
|
|
|
|
|
printf("\r\n");
|
|
|
|
}
|
|
|
|
|
2023-02-09 19:42:36 -07:00
|
|
|
// The left and right triggers control the intensity of the left and right rumble motors
|
|
|
|
motor_left = ds4_report.l2_trigger;
|
|
|
|
motor_right = ds4_report.r2_trigger;
|
|
|
|
|
2021-08-24 19:10:23 +07:00
|
|
|
prev_report = ds4_report;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Invoked when received report from device via interrupt endpoint
|
|
|
|
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
|
|
|
|
{
|
|
|
|
if ( is_sony_ds4(dev_addr) )
|
|
|
|
{
|
|
|
|
process_sony_ds4(report, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
// continue to request to receive report
|
|
|
|
if ( !tuh_hid_receive_report(dev_addr, instance) )
|
|
|
|
{
|
|
|
|
printf("Error: cannot request to receive report\r\n");
|
|
|
|
}
|
|
|
|
}
|