RP20240_Hote_USB/hid_app.c

584 lines
19 KiB
C

/*
* 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_api.h"
#include "hid_app.h"
#include "tusb.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
// If your host terminal support ansi escape code such as TeraTerm
// it can be use to simulate mouse cursor movement within terminal
#define USE_ANSI_ESCAPE 0
#define MAX_REPORT 4
static uint8_t const keycode2ascii[128][2] = { HID_KEYCODE_TO_ASCII };
// Each HID instance can has multiple reports
static struct
{
uint8_t report_count;
tuh_hid_report_info_t report_info[MAX_REPORT];
uint16_t vid, pid;
}hid_info[CFG_TUH_HID];
static void process_kbd_report(hid_keyboard_report_t const *report);
static void process_mouse_report(hid_mouse_report_t const * report);
static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
struct gamepad_data_t process_8bitpro_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
struct gamepad_data_t process_SonyDualShock4_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
struct gamepad_data_t process_BDA_NSW_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len);
void affiche_gamepad_data(struct gamepad_data_t _gamepad_report);
void refresh_gamepad_data(struct gamepad_data_t * gamepad_data);
// HID report from 8bitPro
typedef struct TU_ATTR_PACKED
{
uint8_t report_id;
uint b1:1; // Bouton A
uint b2:1; // Bouton B
uint b3:1; // 4 : ???
uint b4:1; // Bouton X
uint b5:1; // Bouton Y
uint b6:1; // 32 : ???
uint b7:1; // Bouton L
uint b8:1; // Bouton R
uint b9:1; // Bouton L2
uint b10:1; // Bouton R2
uint b11:1; // Start
uint b12:1; // Select
uint b13:1;
uint b14:1; // Click joystick gauche
uint b15:1; // Click joystick droit
uint b16:1; // 16 bits
uint8_t hat_switch; // OK
uint8_t x, y; // joystick Xg, Yg
uint8_t z, rz; // joystick Xd, Yd
uint8_t gachette_gauche; // ok
uint8_t gachette_droite; // ok
} _8bitpro_report_t ;
typedef struct TU_ATTR_PACKED
{
uint8_t report_id;
uint8_t x, y, z, rz; // joystick
uint hat_switch:4;
uint b1:1;
uint b2:1;
uint b3:1;
uint b4:1;
// 8 bits
uint b5:1;
uint b6:1;
uint b7:1;
uint b8:1;
uint b9:1;
uint b10:1;
uint b11:1;
uint b12:1; // 16 bits
uint data_1:8;
uint gachette_gauche:8;
uint gachette_droite:8;
} SonyDualShock4_report_t ;
typedef struct TU_ATTR_PACKED
{
uint b1:1; // Bouton Y
uint b2:1; // Bouton B
uint b3:1; // Bouton A
uint b4:1; // Bouton X
uint b5:1; // Bouton L
uint b6:1; // Bouton R
uint b7:1; // Bouton L2
uint b8:1; // Bouton R2
uint b9:1; // Bouton -
uint b10:1; // Bouton +
uint b11:1; // Bouton joystick droit
uint b12:1; // Bouton joystick gauche
uint b13:1; // Bouton Start
uint b14:1; // Bouton Select
uint b15:1; // --
uint b16:1; // --
uint8_t hat_switch; // OK
uint8_t x, y; // joystick Xg, Yg
uint8_t z, rz; // joystick Xd, Yd
} BDA_NSW_report_t ;
struct gamepad_data_t gamepad_data;
void hid_app_task(void)
{
// nothing to do
}
//--------------------------------------------------------------------+
// 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)
{
printf("HID device address = %d, instance = %d is mounted\r\n", dev_addr, instance);
// Interface protocol (hid_interface_protocol_enum_t)
const char* protocol_str[] = { "None", "Keyboard", "Mouse" };
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
printf("HID Interface Protocol = %s\r\n", protocol_str[itf_protocol]);
// By default host stack will use activate boot protocol on supported interface.
// Therefore for this simple example, we only need to parse generic report descriptor (with built-in parser)
if ( itf_protocol == HID_ITF_PROTOCOL_NONE )
{
uint16_t vid, pid;
tuh_vid_pid_get(dev_addr, &vid, &pid);
printf("VID = %04x, PID = %04x\r\n", vid, pid);
hid_info[instance].vid = vid;
hid_info[instance].pid = pid;
hid_info[instance].report_count = tuh_hid_parse_report_descriptor(hid_info[instance].report_info, MAX_REPORT, desc_report, desc_len);
printf("HID has %u reports \r\n", hid_info[instance].report_count);
for(int i=0; i< hid_info[instance].report_count; i++){
if(hid_info[instance].report_info->usage == 5 &&
hid_info[instance].report_info->usage_page == 1){
printf("Found Gamepad descriptor\n");
//create_gamepad_configuration(dev_addr, instance, desc_report, desc_len);
}
}
}
// 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);
}
// 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)
{
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
switch (itf_protocol)
{
case HID_ITF_PROTOCOL_KEYBOARD:
TU_LOG2("HID receive boot keyboard report\r\n");
process_kbd_report( (hid_keyboard_report_t const*) report );
break;
case HID_ITF_PROTOCOL_MOUSE:
TU_LOG2("HID receive boot mouse report\r\n");
process_mouse_report( (hid_mouse_report_t const*) report );
break;
default:
// Generic report requires matching ReportID and contents with previous parsed report info
// Report - 2dc8:6002 8BitDo 8BitDo SN30 Pro+
if(hid_info[instance].vid == 0x2dc8 && hid_info[instance].pid == 0x6002){
gamepad_data = process_8bitpro_report(dev_addr, instance, report, len);
}else if(hid_info[instance].vid == 0x054c && hid_info[instance].pid == 0x05c4){
gamepad_data = process_SonyDualShock4_report(dev_addr, instance, report, len);
}else if(hid_info[instance].vid == 0x20d6 && hid_info[instance].pid == 0xa713){
gamepad_data = process_BDA_NSW_report(dev_addr, instance, report, len);
}else{
process_generic_report(dev_addr, instance, report, len);
}
break;
}
// continue to request to receive report
if ( !tuh_hid_receive_report(dev_addr, instance) )
{
printf("Error: cannot request to receive report\r\n");
}
}
//--------------------------------------------------------------------+
// Keyboard
//--------------------------------------------------------------------+
// look up new key in previous keys
static inline bool find_key_in_report(hid_keyboard_report_t const *report, uint8_t keycode)
{
for(uint8_t i=0; i<6; i++)
{
if (report->keycode[i] == keycode) return true;
}
return false;
}
static void process_kbd_report(hid_keyboard_report_t const *report)
{
static hid_keyboard_report_t prev_report = { 0, 0, {0} }; // previous report to check key released
//------------- example code ignore control (non-printable) key affects -------------//
for(uint8_t i=0; i<6; i++)
{
if ( report->keycode[i] )
{
if ( find_key_in_report(&prev_report, report->keycode[i]) )
{
// exist in previous report means the current key is holding
}else
{
// not existed in previous report means the current key is pressed
bool const is_shift = report->modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT);
uint8_t ch = keycode2ascii[report->keycode[i]][is_shift ? 1 : 0];
putchar(ch);
if ( ch == '\r' ) putchar('\n'); // added new line for enter key
#ifndef __ICCARM__ // TODO IAR doesn't support stream control ?
fflush(stdout); // flush right away, else nanolib will wait for newline
#endif
}
}
// TODO example skips key released
}
prev_report = *report;
}
//--------------------------------------------------------------------+
// Mouse
//--------------------------------------------------------------------+
void cursor_movement(int8_t x, int8_t y, int8_t wheel)
{
#if USE_ANSI_ESCAPE
// Move X using ansi escape
if ( x < 0)
{
printf(ANSI_CURSOR_BACKWARD(%d), (-x)); // move left
}else if ( x > 0)
{
printf(ANSI_CURSOR_FORWARD(%d), x); // move right
}
// Move Y using ansi escape
if ( y < 0)
{
printf(ANSI_CURSOR_UP(%d), (-y)); // move up
}else if ( y > 0)
{
printf(ANSI_CURSOR_DOWN(%d), y); // move down
}
// Scroll using ansi escape
if (wheel < 0)
{
printf(ANSI_SCROLL_UP(%d), (-wheel)); // scroll up
}else if (wheel > 0)
{
printf(ANSI_SCROLL_DOWN(%d), wheel); // scroll down
}
printf("\r\n");
#else
printf("(%d %d %d)\r\n", x, y, wheel);
#endif
}
static void process_mouse_report(hid_mouse_report_t const * report)
{
static hid_mouse_report_t prev_report = { 0 };
//------------- button state -------------//
uint8_t button_changed_mask = report->buttons ^ prev_report.buttons;
if ( button_changed_mask & report->buttons)
{
printf(" %c%c%c ",
report->buttons & MOUSE_BUTTON_LEFT ? 'L' : '-',
report->buttons & MOUSE_BUTTON_MIDDLE ? 'M' : '-',
report->buttons & MOUSE_BUTTON_RIGHT ? 'R' : '-');
}
//------------- cursor movement -------------//
cursor_movement(report->x, report->y, report->wheel);
}
//--------------------------------------------------------------------+
// Report - 2dc8:6002 8BitDo 8BitDo SN30 Pro+
//--------------------------------------------------------------------+
struct gamepad_data_t process_8bitpro_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len){
struct gamepad_data_t gamepad_data;
_8bitpro_report_t gamepad_report;
memcpy(&gamepad_report, report, sizeof(gamepad_report));
refresh_gamepad_data(&gamepad_data);
// Copie des données du rapport vers la structure standard
gamepad_data.joystick_x_gauche = gamepad_report.x;
gamepad_data.joystick_y_gauche = gamepad_report.y;
gamepad_data.joystick_x_droit = gamepad_report.z;
gamepad_data.joystick_y_droit = gamepad_report.rz;
gamepad_data.bouton_A = gamepad_report.b1;
gamepad_data.bouton_B = gamepad_report.b2;
gamepad_data.bouton_X = gamepad_report.b4;
gamepad_data.bouton_Y = gamepad_report.b5;
gamepad_data.bouton_L = gamepad_report.b7;
gamepad_data.bouton_R = gamepad_report.b8;
gamepad_data.bouton_L2 = gamepad_report.b9;
gamepad_data.bouton_R2 = gamepad_report.b10;
gamepad_data.bouton_Start = gamepad_report.b11;
gamepad_data.bouton_Select = gamepad_report.b12;
gamepad_data.bouton_joystick_gauche = gamepad_report.b15;
gamepad_data.bouton_joystick_droit = gamepad_report.b14;
gamepad_data.dpad = gamepad_report.hat_switch;
gamepad_data.gachette_droite = gamepad_report.gachette_droite;
gamepad_data.gachette_gauche = gamepad_report.gachette_gauche;
affiche_gamepad_data(gamepad_data);
return gamepad_data;
}
//--------------------------------------------------------------------+
// 054c:05c4 Sony Corp. DualShock 4 [CUH-ZCT1x]
//--------------------------------------------------------------------+
struct gamepad_data_t process_SonyDualShock4_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len){
struct gamepad_data_t gamepad_data;
SonyDualShock4_report_t gamepad_report;
memcpy(&gamepad_report, report, sizeof(gamepad_report));
refresh_gamepad_data(&gamepad_data);
// Copie des données du rapport vers la structure standard
gamepad_data.joystick_x_gauche = gamepad_report.x;
gamepad_data.joystick_y_gauche = gamepad_report.y;
gamepad_data.joystick_x_droit = gamepad_report.z;
gamepad_data.joystick_y_droit = gamepad_report.rz;
gamepad_data.bouton_Y = gamepad_report.b4;
gamepad_data.bouton_A = gamepad_report.b3;
gamepad_data.bouton_B = gamepad_report.b2;
gamepad_data.bouton_X = gamepad_report.b1;
gamepad_data.bouton_joystick_droit = gamepad_report.b11;
gamepad_data.bouton_joystick_gauche = gamepad_report.b12;
gamepad_data.bouton_Select = gamepad_report.b10;
gamepad_data.bouton_Start = gamepad_report.b9;
gamepad_data.bouton_R2 = gamepad_report.b8;
gamepad_data.bouton_L2 = gamepad_report.b7;
gamepad_data.bouton_R = gamepad_report.b6;
gamepad_data.bouton_L = gamepad_report.b5;
gamepad_data.dpad = gamepad_report.hat_switch;
gamepad_data.gachette_droite = gamepad_report.gachette_droite;
gamepad_data.gachette_gauche = gamepad_report.gachette_gauche;
affiche_gamepad_data(gamepad_data);
return gamepad_data;
}
//--------------------------------------------------------------------+
// Report - 20d6:a713 BDA NSW wired controller
//--------------------------------------------------------------------+
struct gamepad_data_t process_BDA_NSW_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len){
struct gamepad_data_t gamepad_data;
BDA_NSW_report_t gamepad_report;
memcpy(&gamepad_report, report, sizeof(gamepad_report));
refresh_gamepad_data(&gamepad_data);
// Copie des données du rapport vers la structure standard
gamepad_data.joystick_x_gauche = gamepad_report.x;
gamepad_data.joystick_y_gauche = gamepad_report.y;
gamepad_data.joystick_x_droit = gamepad_report.z;
gamepad_data.joystick_y_droit = gamepad_report.rz;
gamepad_data.bouton_Y = gamepad_report.b1;
gamepad_data.bouton_B = gamepad_report.b2;
gamepad_data.bouton_A = gamepad_report.b3;
gamepad_data.bouton_X = gamepad_report.b4;
gamepad_data.bouton_L = gamepad_report.b5;
gamepad_data.bouton_R = gamepad_report.b6;
gamepad_data.bouton_L2 = gamepad_report.b7;
gamepad_data.bouton_R2 = gamepad_report.b8;
gamepad_data.bouton_joystick_gauche = gamepad_report.b12;
gamepad_data.bouton_joystick_droit = gamepad_report.b11;
gamepad_data.bouton_Start = gamepad_report.b13;
gamepad_data.bouton_Select = gamepad_report.b14;
gamepad_data.dpad = gamepad_report.hat_switch;
affiche_gamepad_data(gamepad_data);
return gamepad_data;
}
//--------------------------------------------------------------------+
// Generic Report
//--------------------------------------------------------------------+
static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
{
(void) dev_addr;
uint8_t const rpt_count = hid_info[instance].report_count;
tuh_hid_report_info_t* rpt_info_arr = hid_info[instance].report_info;
tuh_hid_report_info_t* rpt_info = NULL;
//printf("HID Report brut\n");
printf(">a:1\n");
for(int i=0; i< len; i++){
printf("0x%X ", report[i]);
}
printf("\n");
if ( rpt_count == 1 && rpt_info_arr[0].report_id == 0)
{
// Simple report without report ID as 1st byte
rpt_info = &rpt_info_arr[0];
}else
{
// Composite report, 1st byte is report ID, data starts from 2nd byte
uint8_t const rpt_id = report[0];
// Find report id in the array
for(uint8_t i=0; i<rpt_count; i++)
{
if (rpt_id == rpt_info_arr[i].report_id )
{
rpt_info = &rpt_info_arr[i];
break;
}
}
report++;
len--;
}
if (!rpt_info)
{
printf("Couldn't find report info !\r\n");
return;
}
// For complete list of Usage Page & Usage checkout src/class/hid/hid.h. For examples:
// - Keyboard : Desktop, Keyboard
// - Mouse : Desktop, Mouse
// - Gamepad : Desktop, Gamepad
// - Consumer Control (Media Key) : Consumer, Consumer Control
// - System Control (Power key) : Desktop, System Control
// - Generic (vendor) : 0xFFxx, xx
if ( rpt_info->usage_page == HID_USAGE_PAGE_DESKTOP )
{
switch (rpt_info->usage)
{
case HID_USAGE_DESKTOP_KEYBOARD:
TU_LOG1("HID receive keyboard report\r\n");
// Assume keyboard follow boot report layout
process_kbd_report( (hid_keyboard_report_t const*) report );
break;
case HID_USAGE_DESKTOP_MOUSE:
TU_LOG1("HID receive mouse report\r\n");
// Assume mouse follow boot report layout
process_mouse_report( (hid_mouse_report_t const*) report );
break;
case HID_USAGE_DESKTOP_GAMEPAD:
break;
default: break;
}
}
}
void process_gamepad_report(){
}
void affiche_gamepad_data(struct gamepad_data_t _gamepad_report){
printf(">jxd:%d\n", _gamepad_report.joystick_x_gauche);
printf(">jyd:%d\n", _gamepad_report.joystick_y_gauche);
printf(">jxg:%d\n", _gamepad_report.joystick_x_droit);
printf(">jyg:%d\n", _gamepad_report.joystick_y_droit);
printf(">hat:%d\n", _gamepad_report.dpad);
printf(">ba:%d\n", _gamepad_report.bouton_A);
printf(">bb:%d\n", _gamepad_report.bouton_B);
printf(">bx:%d\n", _gamepad_report.bouton_X);
printf(">by:%d\n", _gamepad_report.bouton_Y);
printf(">bstart:%d\n", _gamepad_report.bouton_Start);
printf(">bselect:%d\n", _gamepad_report.bouton_Select);
printf(">bl:%d\n", _gamepad_report.bouton_L);
printf(">br:%d\n", _gamepad_report.bouton_R);
printf(">bl2:%d\n", _gamepad_report.bouton_L2);
printf(">br2:%d\n", _gamepad_report.bouton_R2);
printf(">bjg:%d\n", _gamepad_report.bouton_joystick_droit);
printf(">bjd:%d\n", _gamepad_report.bouton_joystick_gauche);
//printf(">data1:%d\n", _gamepad_report.data_1);
printf(">gd:%d\n", _gamepad_report.gachette_droite);
printf(">gg:%d\n", _gamepad_report.gachette_gauche);
}
void refresh_gamepad_data(struct gamepad_data_t * gamepad_data){
gamepad_data->temps_ms = (uint32_t) get_absolute_time() / 1000;
}
bool gamepad_connecte(){
return ((get_absolute_time() / 1000) - gamepad_data.temps_ms < 1000);
}
struct gamepad_data_t get_gamepad_data(){
return gamepad_data;
}