Files
edgetx-gx12/radio/src/usb_joystick.cpp
T
661657feca feat: New advanced joystick mode on radio, improvements to Companion and Simulator joystick support (#3111)
* Allow switches on the connected radio to control switches in the simulator via the joystick interface.

* Merge configurable radio joystick interface from banyaszg.

* Add two more 'axis' channels to companion.

* Fix 6POS from USB joystick to simulator.

* Reduce data size.

* Fix build error on B&W radios.

* Add 'Companion' button mode to work with the button logic in the Companion app.

* Build Companion joystick setup layout at runtime.
Change #define names for simulator joystick.
Allow 15 axes and 32 buttons.
Initial support for trim sliders.

* Remove custom joystick button type for companion - use 3 buttons per switch in companion to control simulator switch state.

* Fix joystick constructor calls parameters. Connect some radio joystick trims to simulator widgets.

* Restore 'companion' button type to reduce number of buttons needed. Make trims work via buttons in USB joystick.

* Connect joystick trim buttons to simulator widgets.

* Use companion source names for trims.

* Add encode / decode of usb joystick custom settings to Companion.

* Convert to lvgl layouts.
Update language files.

* Fix generated YAML storage files.

* Fix initialisation error when editing button channel. Cleanup code.

* Fix rebase.

* Fix refresh of joystick channel list view after editing an entry.
Companion joystick config window uses two columns.

* Fix vertical joystick inversion bug.
Change stick axis labels.

---------

Co-authored-by: Phil Mitchell <phil.a.mitchell@gmail.com>
2023-03-22 10:31:29 +10:00

759 lines
24 KiB
C++

/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "opentx.h"
#include "usb_joystick.h"
#define MAX_HID_REPORTDESC 160
#define MAX_HID_REPORT 80
uint8_t _hidReportDesc[MAX_HID_REPORTDESC] = { };
uint8_t _hidReportDescSize = 0;
uint8_t _hidReport[MAX_HID_REPORT] = { };
uint8_t _hidReportSize = 0;
static uint8_t _buttonState[USBJ_BUTTON_SIZE >> 3] = { };
static uint16_t _usbLastChannelOutput[USBJ_MAX_JOYSTICK_CHANNELS] = { };
static uint8_t _usbChannelTimerActive[USBJ_MAX_JOYSTICK_CHANNELS] = { };
static uint8_t _usbChannelTimer[USBJ_MAX_JOYSTICK_CHANNELS] = { };
static uint8_t _usbJoystickIfMode = 0;
static uint8_t _usbJoystickCircularCut = 0;
static USBJoystickChData _usbJoystickCh[USBJ_MAX_JOYSTICK_CHANNELS];
#define USBJ_CC_XYZrX 1
#define USBJ_CC_XYrXrZ 2
static uint8_t _usbJoystickButtonChannels[USBJ_MAX_JOYSTICK_CHANNELS] = { };
static uint8_t _usbJoystickButtonCount = 0;
static uint8_t _usbJoystickAxisChannels[USBJ_MAX_JOYSTICK_CHANNELS] = { };
static uint8_t _usbJoystickAxisCount = 0;
static uint8_t _usbJoystickAxisPairs[3][2] = { };
static const uint8_t _usbJoystickUniqueAxis[USBJOYS_AXIS_LAST + 1] =
{ 1, 1, 1, 1, 1, 1, 0, 0, 0 };
static const uint8_t _usbJoystickUniqueSims[USBJOYS_SIM_LAST + 1] =
{ 1, 1, 1, 1 };
/*
This USB HID endpoint report description defines a device with:
* 24 digital buttons
* 8 analog axes with 8bit resolution
Repot packet described as C struct is:
struct {
uint8_t buttons1; //bit 0 - button 1, bit 1 - button 2, ..., mapped to channels 9-16, on if channel > 0
uint8_t buttons2; // mapped to channels 17-24, on if channel > 0
uint8_t buttons3; // mapped to channels 25-32, on if channel > 0
uint8_t X; //analog value, mapped to channel 1
uint8_t Y; //analog value, mapped to channel 2
uint8_t Z; //analog value, mapped to channel 3
uint8_t Rx; //analog value, mapped to channel 4
uint8_t Ry //analog value, mapped to channel 5
uint8_t Rz; //analog value, mapped to channel 6
uint8_t S1; //analog value, mapped to channel 7
uint8_t S2; //analog value, mapped to channel 8
}
*/
static const uint8_t HID_JOYSTICK_ReportDesc[] =
{
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x05, // USAGE (Game Pad)
0xa1, 0x01, // COLLECTION (Application)
0xa1, 0x00, // COLLECTION (Physical)
0x05, 0x09, // USAGE_PAGE (Button)
0x19, 0x01, // USAGE_MINIMUM (Button 1)
0x29, 0x18, // USAGE_MAXIMUM (Button 24)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x95, 0x18, // REPORT_COUNT (24)
0x75, 0x01, // REPORT_SIZE (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x30, // USAGE (X)
0x09, 0x31, // USAGE (Y)
0x09, 0x32, // USAGE (Z)
0x09, 0x33, // USAGE (Rx)
0x09, 0x34, // USAGE (Ry)
0x09, 0x35, // USAGE (Rz)
0x09, 0x36, // USAGE (Slider)
0x09, 0x36, // USAGE (Slider)
0x16, 0x00, 0x00, // LOGICAL_MINIMUM (0)
0x26, 0xFF, 0x07, // LOGICAL_MAXIMUM (2047)
0x75, 0x10, // REPORT_SIZE (16)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0xc0 // END_COLLECTION
};
int usbJoystickExtMode()
{
return g_model.usbJoystickExtMode;
}
int usbJoystickActive()
{
return (usbPlugged() && getSelectedUsbMode() == USB_JOYSTICK_MODE);
}
int usbJoystickSettingsChanged()
{
if (!usbJoystickActive()) return false;
if((_usbJoystickIfMode != g_model.usbJoystickIfMode)
|| (_usbJoystickCircularCut != g_model.usbJoystickCircularCut)) return true;
if(memcmp(_usbJoystickCh, g_model.usbJoystickCh, sizeof(g_model.usbJoystickCh)) != 0) return true;
return false;
}
int setupUSBJoystick()
{
static const uint8_t axisTypeCodes[USBJOYS_AXIS_LAST + 1] =
{ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 };
static const uint8_t simTypeCodes[USBJOYS_SIM_LAST + 1] =
{ 0xb0, 0xb8, 0xba, 0xbb };
static uint8_t oldHIDReportDesc[MAX_HID_REPORTDESC] = { };
static uint8_t oldHIDReportDescSize = 0;
// copy old to compare
memcpy(oldHIDReportDesc, _hidReportDesc, MAX_HID_REPORTDESC);
oldHIDReportDescSize = _hidReportDescSize;
// copy the model snapshot
_usbJoystickIfMode = g_model.usbJoystickIfMode;
_usbJoystickCircularCut = g_model.usbJoystickCircularCut;
memcpy(_usbJoystickCh, g_model.usbJoystickCh, sizeof(g_model.usbJoystickCh));
// Init data
memset(_hidReportDesc, 0, MAX_HID_REPORTDESC);
_hidReportDescSize = 0;
memset(_hidReport, 0, MAX_HID_REPORT);
_hidReportSize = 0;
memset(_buttonState, 0, (USBJ_BUTTON_SIZE >> 3));
memset(_usbLastChannelOutput, 0xff, sizeof(_usbLastChannelOutput));
memset(_usbChannelTimer, 0, sizeof(_usbChannelTimer));
memset(_usbChannelTimerActive, 0, sizeof(_usbChannelTimerActive));
memset(_usbJoystickButtonChannels, 0, USBJ_MAX_JOYSTICK_CHANNELS);
_usbJoystickButtonCount = 0;
memset(_usbJoystickAxisChannels, 0, USBJ_MAX_JOYSTICK_CHANNELS);
_usbJoystickAxisCount = 0;
memset(_usbJoystickAxisPairs, 0xff, sizeof(_usbJoystickAxisPairs));
if (!usbJoystickExtMode()) {
// Classic USB Joystick report description
memcpy(_hidReportDesc, HID_JOYSTICK_ReportDesc, sizeof(HID_JOYSTICK_ReportDesc));
_hidReportDescSize = sizeof(HID_JOYSTICK_ReportDesc);
_hidReportSize = 19;
} else {
uint8_t genAxisCount = 0;
uint8_t simAxisCount = 0;
// sort channels by type
uint8_t typeCount[USBJOYS_CH_LAST + 1] = { };
uint8_t axisCount[USBJOYS_AXIS_LAST + 1] = { };
uint8_t simCount[USBJOYS_SIM_LAST + 1] = { };
uint8_t axisIx[USBJOYS_AXIS_LAST + 1] = { };
uint8_t simIx[USBJOYS_SIM_LAST + 1] = { };
uint8_t mode = 0;
uint8_t submode = 0;
for (uint8_t i = 0; i < USBJ_MAX_JOYSTICK_CHANNELS; i++) {
mode = _usbJoystickCh[i].mode;
if (_usbJoystickCh[i].mode == USBJOYS_CH_BUTTON) {
typeCount[mode]++;
}
else if (_usbJoystickCh[i].mode == USBJOYS_CH_AXIS) {
submode = _usbJoystickCh[i].param;
if (submode <= USBJOYS_AXIS_LAST) {
if ((_usbJoystickUniqueAxis[submode] == 0) || (axisCount[submode] == 0)) {
axisCount[submode]++;
typeCount[mode]++;
}
}
}
else if (_usbJoystickCh[i].mode == USBJOYS_CH_SIM) {
submode = _usbJoystickCh[i].param;
if (submode <= USBJOYS_SIM_LAST) {
if ((_usbJoystickUniqueSims[submode] == 0) || (simCount[submode] == 0)) {
simCount[submode]++;
typeCount[mode]++;
}
}
}
}
uint8_t ixcount = 0;
for (uint8_t i = 0; i <= USBJOYS_AXIS_LAST; i++) {
axisIx[i] = ixcount;
ixcount += axisCount[i];
}
ixcount = 0;
for (uint8_t i = 0; i <= USBJOYS_SIM_LAST; i++) {
simIx[i] = ixcount;
ixcount += simCount[i];
}
ixcount = 0;
for (uint8_t i = 0; i < USBJ_MAX_JOYSTICK_CHANNELS; i++) {
mode = _usbJoystickCh[i].mode;
if (_usbJoystickCh[i].mode == USBJOYS_CH_BUTTON) {
_usbJoystickButtonChannels[ixcount] = i;
ixcount++;
}
else if (_usbJoystickCh[i].mode == USBJOYS_CH_AXIS) {
submode = _usbJoystickCh[i].param;
if (submode <= USBJOYS_AXIS_LAST) {
_usbJoystickAxisChannels[axisIx[submode]] = i;
if (_usbJoystickUniqueAxis[submode] == 0) {
axisIx[submode]++;
}
// save axis pairs
if(submode == USBJOYS_AXIS_X) {
_usbJoystickAxisPairs[0][0] = i;
}
else if(submode == USBJOYS_AXIS_Y) {
_usbJoystickAxisPairs[0][1] = i;
}
else if(submode == USBJOYS_AXIS_Z) {
_usbJoystickAxisPairs[1][0] = i;
}
else if(submode == USBJOYS_AXIS_RX) {
_usbJoystickAxisPairs[1][1] = i;
_usbJoystickAxisPairs[2][0] = i;
}
else if(submode == USBJOYS_AXIS_RY) {
_usbJoystickAxisPairs[2][1] = i;
}
}
}
else if (_usbJoystickCh[i].mode == USBJOYS_CH_SIM) {
submode = _usbJoystickCh[i].param;
if (submode <= USBJOYS_SIM_LAST) {
_usbJoystickAxisChannels[simIx[submode]] = i;
if (_usbJoystickUniqueSims[submode] == 0) {
simIx[submode]++;
}
}
}
}
// store counts
_usbJoystickButtonCount = typeCount[USBJOYS_CH_BUTTON];
genAxisCount = typeCount[USBJOYS_CH_AXIS];
simAxisCount = typeCount[USBJOYS_CH_SIM];
_usbJoystickAxisCount = typeCount[USBJOYS_CH_AXIS] + typeCount[USBJOYS_CH_SIM];
// generate report desc
// USAGE_PAGE (Generic Desktop)
_hidReportDesc[_hidReportDescSize++] = 0x05;
_hidReportDesc[_hidReportDescSize++] = 0x01;
// USAGE (Joystick=0x04, Gamepad=0x05, Multi-axis Controller=0x08)
uint8_t joystickType = 0x04;
if (_usbJoystickIfMode == USBJOYS_GAMEPAD) joystickType = 0x05;
else if (_usbJoystickIfMode == USBJOYS_MULTIAXIS) joystickType = 0x08;
_hidReportDesc[_hidReportDescSize++] = 0x09;
_hidReportDesc[_hidReportDescSize++] = joystickType;
// COLLECTION (Application)
_hidReportDesc[_hidReportDescSize++] = 0xa1;
_hidReportDesc[_hidReportDescSize++] = 0x01;
// COLLECTION (Physical)
_hidReportDesc[_hidReportDescSize++] = 0xa1;
_hidReportDesc[_hidReportDescSize++] = 0x00;
// REPORT_ID (1)
// _hidReportDesc[_hidReportDescSize++] = 0x85;
// _hidReportDesc[_hidReportDescSize++] = 0x01;
// button types
// USAGE_PAGE (Button)
_hidReportDesc[_hidReportDescSize++] = 0x05;
_hidReportDesc[_hidReportDescSize++] = 0x09;
// USAGE_MINIMUM (Button 1)
_hidReportDesc[_hidReportDescSize++] = 0x19;
_hidReportDesc[_hidReportDescSize++] = 0x01;
// USAGE_MAXIMUM (Button USBJ_BUTTON_SIZE)
_hidReportDesc[_hidReportDescSize++] = 0x29;
_hidReportDesc[_hidReportDescSize++] = USBJ_BUTTON_SIZE;
// LOGICAL_MINIMUM (0)
_hidReportDesc[_hidReportDescSize++] = 0x15;
_hidReportDesc[_hidReportDescSize++] = 0x00;
// LOGICAL_MAXIMUM (1)
_hidReportDesc[_hidReportDescSize++] = 0x25;
_hidReportDesc[_hidReportDescSize++] = 0x01;
// REPORT_COUNT (#)
_hidReportDesc[_hidReportDescSize++] = 0x95;
_hidReportDesc[_hidReportDescSize++] = USBJ_BUTTON_SIZE;
// REPORT_SIZE (1)
_hidReportDesc[_hidReportDescSize++] = 0x75;
_hidReportDesc[_hidReportDescSize++] = 0x01;
// INPUT (Data,Var,Abs,Preferred)
_hidReportDesc[_hidReportDescSize++] = 0x81;
_hidReportDesc[_hidReportDescSize++] = 0x02;
// generic axis types
if (genAxisCount > 0) {
// USAGE_PAGE (Generic Desktop)
_hidReportDesc[_hidReportDescSize++] = 0x05;
_hidReportDesc[_hidReportDescSize++] = 0x01;
for (uint8_t i = 0; i <= USBJOYS_AXIS_LAST; i++) {
for (uint8_t cnt = 0; cnt < axisCount[i]; cnt++) {
// USAGE (#)
_hidReportDesc[_hidReportDescSize++] = 0x09;
_hidReportDesc[_hidReportDescSize++] = axisTypeCodes[i];
}
}
// LOGICAL_MINIMUM (0)
_hidReportDesc[_hidReportDescSize++] = 0x16;
_hidReportDesc[_hidReportDescSize++] = 0x00;
_hidReportDesc[_hidReportDescSize++] = 0x00;
// LOGICAL_MAXIMUM (2047)
_hidReportDesc[_hidReportDescSize++] = 0x26;
_hidReportDesc[_hidReportDescSize++] = 0xFF;
_hidReportDesc[_hidReportDescSize++] = 0x07;
// REPORT_SIZE (16)
_hidReportDesc[_hidReportDescSize++] = 0x75;
_hidReportDesc[_hidReportDescSize++] = 0x10;
// REPORT_COUNT (#)
_hidReportDesc[_hidReportDescSize++] = 0x95;
_hidReportDesc[_hidReportDescSize++] = genAxisCount;
// INPUT (Data,Var,Abs)
_hidReportDesc[_hidReportDescSize++] = 0x81;
_hidReportDesc[_hidReportDescSize++] = 0x02;
}
// sim axis types
if (simAxisCount > 0) {
// USAGE_PAGE (Simulation Controls)
_hidReportDesc[_hidReportDescSize++] = 0x05;
_hidReportDesc[_hidReportDescSize++] = 0x02;
for (uint8_t i = 0; i <= USBJOYS_SIM_LAST; i++) {
for (uint8_t cnt = 0; cnt < simCount[i]; cnt++) {
// USAGE (#)
_hidReportDesc[_hidReportDescSize++] = 0x09;
_hidReportDesc[_hidReportDescSize++] = simTypeCodes[i];
}
}
// LOGICAL_MINIMUM (0)
_hidReportDesc[_hidReportDescSize++] = 0x16;
_hidReportDesc[_hidReportDescSize++] = 0x00;
_hidReportDesc[_hidReportDescSize++] = 0x00;
// LOGICAL_MAXIMUM (2047)
_hidReportDesc[_hidReportDescSize++] = 0x26;
_hidReportDesc[_hidReportDescSize++] = 0xFF;
_hidReportDesc[_hidReportDescSize++] = 0x07;
// REPORT_SIZE (16)
_hidReportDesc[_hidReportDescSize++] = 0x75;
_hidReportDesc[_hidReportDescSize++] = 0x10;
// REPORT_COUNT (#)
_hidReportDesc[_hidReportDescSize++] = 0x95;
_hidReportDesc[_hidReportDescSize++] = simAxisCount;
// INPUT (Data,Var,Abs)
_hidReportDesc[_hidReportDescSize++] = 0x81;
_hidReportDesc[_hidReportDescSize++] = 0x02;
}
// END_COLLECTION
_hidReportDesc[_hidReportDescSize++] = 0xc0;
// END_COLLECTION
_hidReportDesc[_hidReportDescSize++] = 0xc0;
// end of report desc
_hidReportSize = (USBJ_BUTTON_SIZE / 8) + (_usbJoystickAxisCount * 2);
}
//compare with the old description
if (_hidReportDescSize != oldHIDReportDescSize) return true;
if (memcmp(_hidReportDesc, oldHIDReportDesc, _hidReportDescSize) != 0) return true;
return false;
}
extern "C" struct usbReport_t usbReportDesc()
{
usbReport_t res = { _hidReportDesc, _hidReportDescSize };
return res;
}
void usbClassicStateUpdate()
{
memset(_hidReport, 0, MAX_HID_REPORT);
//buttons
_hidReport[0] = 0;
_hidReport[1] = 0;
_hidReport[2] = 0;
for (int i = 0; i < 8; ++i) {
if ( channelOutputs[i+8] > 0 ) {
_hidReport[0] |= (1 << i);
}
if ( channelOutputs[i+16] > 0 ) {
_hidReport[1] |= (1 << i);
}
if ( channelOutputs[i+24] > 0 ) {
_hidReport[2] |= (1 << i);
}
}
//analog values
//uint8_t * p = HID_Buffer + 1;
for (int i = 0; i < 8; ++i) {
int16_t value = channelOutputs[i] + 1024;
if ( value > 2047 ) value = 2047;
else if ( value < 0 ) value = 0;
_hidReport[i*2 +3] = static_cast<uint8_t>(value & 0xFF);
_hidReport[i*2 +4] = static_cast<uint8_t>((value >> 8) & 0x07);
}
}
static void setBtnBits(uint8_t ix, uint8_t value, uint8_t size)
{
for(uint8_t i = 0; i < size; i++) {
uint8_t bitix = ix + i;
if(bitix >= USBJ_BUTTON_SIZE) break;
if(value & (1 << i)) {
_buttonState[(bitix / 8)] |= 1 << (bitix % 8);
}
else {
_buttonState[(bitix / 8)] &= ~(1 << (bitix % 8));
}
}
}
static void toggleBtnBit(uint8_t bitix)
{
if(bitix >= USBJ_BUTTON_SIZE) return;
_buttonState[(bitix / 8)] ^= 1 << (bitix % 8);
}
#define g_usbTmr10ms (*(uint8_t*)&g_tmr10ms)
#define BTNPUSH_TIME 20 /* x10ms */
static int16_t circularCutoutValue(uint8_t chix)
{
int32_t value = channelOutputs[chix];
uint8_t chpair = 0xff;
const int32_t limit = 1048576; /* 1024^2 */
if(_usbJoystickCircularCut == USBJ_CC_XYZrX) {
if(chix == _usbJoystickAxisPairs[0][0]) chpair = _usbJoystickAxisPairs[0][1];
else if(chix == _usbJoystickAxisPairs[0][1]) chpair = _usbJoystickAxisPairs[0][0];
else if(chix == _usbJoystickAxisPairs[1][0]) chpair = _usbJoystickAxisPairs[1][1];
else if(chix == _usbJoystickAxisPairs[1][1]) chpair = _usbJoystickAxisPairs[1][0];
}
else if(_usbJoystickCircularCut == USBJ_CC_XYrXrZ) {
if(chix == _usbJoystickAxisPairs[0][0]) chpair = _usbJoystickAxisPairs[0][1];
else if(chix == _usbJoystickAxisPairs[0][1]) chpair = _usbJoystickAxisPairs[0][0];
else if(chix == _usbJoystickAxisPairs[2][0]) chpair = _usbJoystickAxisPairs[2][1];
else if(chix == _usbJoystickAxisPairs[2][1]) chpair = _usbJoystickAxisPairs[2][0];
}
if(chpair != 0xff) {
int32_t pval = channelOutputs[chpair];
int32_t sum = ((value*value) + (pval*pval));
if(sum > limit) {
double ratio = sqrt((double)limit / (double)sum);
value *= ratio;
}
}
return value;
}
void usbStateUpdate()
{
const uint8_t button_ix = 0;
const uint8_t axis_ix = button_ix + (USBJ_BUTTON_SIZE >> 3);
memset(_hidReport, 0, MAX_HID_REPORT);
uint8_t chix = 0;
int16_t value = 0;
uint8_t swpos = 0;
uint8_t btnval = 0;
for (uint8_t i = 0; i < _usbJoystickButtonCount; i++) {
chix = _usbJoystickButtonChannels[i];
value = channelOutputs[chix] + 1024;
if ( value > 2047 ) value = 2047;
else if ( value < 0 ) value = 0;
if (_usbJoystickCh[chix].inversion) value = 2047 - value;
swpos = _usbJoystickCh[chix].switch_npos + 1;
// Quantization of the values
if ((_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_NORMAL)
|| (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_ON_PULSE)) {
if (swpos == 1) {
btnval = (value > 1024);
} else {
btnval = static_cast<uint16_t>(value) / (2048 / swpos);
if (btnval >= swpos) btnval = swpos - 1;
}
}
else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_SW_EMU) {
btnval = (value > 1024);
}
else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_DELTA) {
btnval = (value >> 6);
}
else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_COMPANION) {
if (swpos == 1 || swpos == 2) {
btnval = (value > 1024);
} else if (swpos == 3) {
btnval = (value < 1024) ? 1 : (value > 1024) ? 2 : 0;
} else {
btnval = static_cast<uint16_t>(value) / (2048 / swpos);
if (btnval >= swpos) btnval = swpos - 1;
}
}
// Channel Output value changed
if ((_usbLastChannelOutput[chix] == 0xffff) || (_usbLastChannelOutput[chix] != btnval)) {
if ((_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_NORMAL)
|| (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_ON_PULSE)) {
if (swpos == 1) {
setBtnBits(_usbJoystickCh[chix].btn_num, btnval, 1);
} else {
setBtnBits(_usbJoystickCh[chix].btn_num, 1 << btnval, swpos);
}
// Timer
if(_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_ON_PULSE) {
_usbChannelTimerActive[chix] = 1;
_usbChannelTimer[chix] = g_usbTmr10ms;
}
}
else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_SW_EMU) {
if ((_usbLastChannelOutput[chix] != 0xffff) && (_usbLastChannelOutput[chix] < btnval)) {
toggleBtnBit(_usbJoystickCh[chix].btn_num);
}
}
else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_DELTA) {
if (_usbLastChannelOutput[chix] != 0xffff) {
if (_usbLastChannelOutput[chix] < btnval) {
setBtnBits(_usbJoystickCh[chix].btn_num, 2, 2);
}
else if (_usbLastChannelOutput[chix] > btnval) {
setBtnBits(_usbJoystickCh[chix].btn_num, 1, 2);
}
_usbChannelTimerActive[chix] = 1;
_usbChannelTimer[chix] = g_usbTmr10ms;
}
}
else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_COMPANION) {
if (swpos == 1 || swpos == 2) {
setBtnBits(_usbJoystickCh[chix].btn_num, btnval, 1);
} else if (swpos == 3) {
setBtnBits(_usbJoystickCh[chix].btn_num, btnval, 2);
} else {
setBtnBits(_usbJoystickCh[chix].btn_num, 1 << btnval, swpos);
}
}
_usbLastChannelOutput[chix] = btnval;
}
// Timer check
if((_usbChannelTimerActive[chix]) && (((uint8_t)(g_usbTmr10ms - _usbChannelTimer[chix]) >= BTNPUSH_TIME))) {
_usbChannelTimerActive[chix] = 0;
if(_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_ON_PULSE) {
setBtnBits(_usbJoystickCh[chix].btn_num, 0, swpos);
}
else if (_usbJoystickCh[chix].param == USBJOYS_BTN_MODE_DELTA) {
setBtnBits(_usbJoystickCh[chix].btn_num, 0, 2);
}
}
}
memcpy(_hidReport + button_ix, _buttonState, (USBJ_BUTTON_SIZE >> 3));
for (uint8_t i = 0; i < _usbJoystickAxisCount; i++) {
chix = _usbJoystickAxisChannels[i];
value = circularCutoutValue(chix) + 1024;
if ( value > 2047 ) value = 2047;
else if ( value < 0 ) value = 0;
if (_usbJoystickCh[chix].inversion) value = 2047 - value;
_hidReport[i*2 + axis_ix] = static_cast<uint8_t>(value & 0xFF);
_hidReport[i*2 + axis_ix+1] = static_cast<uint8_t>((value >> 8) & 0x07);
}
}
uint8_t usbReportSize()
{
return _hidReportSize;
}
struct usbReport_t usbReport()
{
if (!usbJoystickExtMode()) {
// Classic USB Joystick report
usbClassicStateUpdate();
} else {
usbStateUpdate();
}
usbReport_t res = { _hidReport, _hidReportSize };
return res;
}
void onUSBJoystickModelChanged()
{
if (!usbJoystickActive()) return;
if (setupUSBJoystick()) {
#if defined(STM32) && !defined(SIMU)
usbJoystickRestart();
#endif
}
}
int isUSBAxisCollision(uint8_t chIdx)
{
USBJoystickChData * cch = usbJChAddress(chIdx);
// non axis
if (cch->mode != USBJOYS_CH_AXIS) return false;
// non-unique axis check
if (_usbJoystickUniqueAxis[cch->param] == 0) {
return false;
}
// collision check
for (int i = 0; i < USBJ_MAX_JOYSTICK_CHANNELS; i++) {
if (i == chIdx) continue;
USBJoystickChData * och = usbJChAddress(i);
if (cch->mode != och->mode) continue;
if (cch->param == och->param) {
return true;
}
}
return false;
}
int isUSBSimCollision(uint8_t chIdx)
{
USBJoystickChData * cch = usbJChAddress(chIdx);
// non axis
if (cch->mode != USBJOYS_CH_SIM) return false;
// non-unique axis check
if (_usbJoystickUniqueSims[cch->param] == 0) {
return false;
}
// collision check
for (int i = 0; i < USBJ_MAX_JOYSTICK_CHANNELS; i++) {
if (i == chIdx) continue;
USBJoystickChData * och = usbJChAddress(i);
if (cch->mode != och->mode) continue;
if (cch->param == och->param) {
return true;
}
}
return false;
}
int isUSBBtnNumCollision(uint8_t chIdx)
{
USBJoystickChData * cch = usbJChAddress(chIdx);
// non button
if (cch->mode != USBJOYS_CH_BUTTON) return false;
// collision check
uint8_t cfirst = cch->btn_num;
uint8_t clast = cch->lastBtnNum();
for (int i = 0; i < USBJ_MAX_JOYSTICK_CHANNELS; i++) {
if (i == chIdx) continue;
USBJoystickChData * och = usbJChAddress(i);
if (och->mode != USBJOYS_CH_BUTTON) continue;
uint8_t ofirst = och->btn_num;
uint8_t olast = och->lastBtnNum();
// overlap
if ((cfirst <= olast) && (ofirst <= clast)) return true;
}
return false;
}