31#include <hidapi/hidapi.h>
32#include <hidapi/hidparse.h>
34#include <simgear/structure/exception.hxx>
35#include <simgear/sg_inlines.h>
36#include <simgear/misc/strutils.hxx>
37#include <simgear/io/lowlevel.hxx>
404 const auto enumUsage =
static_cast<UsagePage>(usagePage);
406 std::stringstream os;
407 os <<
"undefined-" <<
usage;
425 case GD_Rx:
return "x-rotate";
426 case GD_Ry:
return "y-rotate";
427 case GD_Rz:
return "z-rotate";
428 case GD_X:
return "x-translate";
429 case GD_Y:
return "y-translate";
430 case GD_Z:
return "z-translate";
439 case GD_Vx:
return "x-vector";
440 case GD_Vy:
return "y-vector";
441 case GD_Vz:
return "z-vector";
442 case GD_Vbrx:
return "relative-x-vector";
443 case GD_Vbry:
return "relative-y-vector";
444 case GD_Vbrz:
return "relative-z-vector";
445 case GD_Vno:
return "non-oriented-vector";
446 case GD_DpadUp:
return "direction-pad-up";
461 case GD_Qx:
return "qx";
462 case GD_Qy:
return "qy";
463 case GD_Qz:
return "qz";
464 case GD_Qw:
return "qw";
508 case GD_RPM:
return "rpm";
560 SG_LOG(SG_INPUT, SG_WARN,
"Unhandled HID generic desktop usage:" <<
usage);
616 SG_LOG(SG_INPUT, SG_WARN,
"Unhandled HID simulation usage:" <<
usage);
621 SG_LOG(SG_INPUT, SG_WARN,
"Unhandled HID consumer usage:" <<
usage);
633 SG_LOG(SG_INPUT, SG_WARN,
"Unhandled HID alphanumeric usage:" <<
usage);
638 case VR_Belt:
return "belt-vr";
646 case VR_Vest:
return "vest-vr";
651 SG_LOG(SG_INPUT, SG_WARN,
"Unhandled HID VR usage:" <<
usage);
675 case LED_CAV:
return "cav-led";
676 case LED_CLV:
return "clv-led";
734 SG_LOG(SG_INPUT, SG_WARN,
"Unhandled HID LED usage:" <<
usage);
738 std::stringstream os;
739 os <<
"button-" <<
usage;
744 SG_LOG(SG_INPUT, SG_WARN,
"Unhandled HID usage page:" << std::hex << usagePage
745 <<
" with usage " << std::hex <<
usage);
753 const auto enumUsage =
static_cast<UsagePage>(usagePage);
798 FGHIDDevice(hid_device_info* devInfo,
801 virtual ~FGHIDDevice();
803 bool Open()
override;
804 void Close()
override;
805 void Configure(SGPropertyNode_ptr node)
override;
807 void update(
double dt)
override;
808 const char *TranslateEventName(
FGEventData &eventData)
override;
809 void Send(
const char * eventName,
double value )
override;
810 void SendFeatureReport(
unsigned int reportId,
const std::string& data)
override;
815 Item(
const std::string& n, uint32_t offset, uint8_t size) :
822 uint32_t bitOffset = 0;
824 bool isRelative =
false;
825 bool doSignExtend =
false;
840 std::vector<Item*> items;
842 uint32_t currentBitSize()
const
845 for (
auto i : items) {
852 bool parseUSBHIDDescriptor();
853 void parseCollection(hid_item* collection);
854 void parseItem(hid_item* item);
856 Report* getReport(
HID::ReportType ty, uint8_t number,
bool doCreate =
false);
858 void sendReport(Report*
report)
const;
860 uint8_t countWithName(
const std::string&
name)
const;
861 std::pair<Report*, Item*> itemWithName(
const std::string&
name)
const;
863 void processInputReport(Report*
report,
unsigned char* data,
size_t length,
864 double dt,
int keyModifiers);
866 int maybeSignExtend(Item* item,
int inValue);
868 void defineReport(SGPropertyNode_ptr reportNode);
870 std::vector<Report*> _reports;
871 std::string _hidPath;
872 hid_device* _device =
nullptr;
873 bool _haveNumberedReports =
false;
874 bool _debugRaw =
false;
879 bool _haveLocalDescriptor =
false;
882 std::vector<uint8_t>_rawXMLDescriptor;
885 std::set<Report*> _dirtyReports;
892 HIDEventData(FGHIDDevice::Item* it,
int value,
double dt,
int keyMods) :
893 FGEventData(value, dt, keyMods),
899 FGHIDDevice::Item* item =
nullptr;
904 class_id =
"FGHIDDevice";
905 _hidPath = devInfo->path;
907 std::wstring manufacturerName, productName;
908 productName = devInfo->product_string ? std::wstring(devInfo->product_string)
909 : L
"unknown HID device";
911 if (devInfo->manufacturer_string) {
912 manufacturerName = std::wstring(devInfo->manufacturer_string);
913 SetName(simgear::strutils::convertWStringToUtf8(manufacturerName) +
" " +
914 simgear::strutils::convertWStringToUtf8(productName));
916 SetName(simgear::strutils::convertWStringToUtf8(productName));
919 const auto serial = devInfo->serial_number;
920 std::string path(devInfo->path);
922 if ((serial !=
nullptr) && std::wcslen(serial) > 0) {
923 SetSerialNumber(simgear::strutils::convertWStringToUtf8(serial));
926 SG_LOG(SG_INPUT, SG_DEBUG,
"HID device:" << GetName() <<
" at path " << _hidPath);
929FGHIDDevice::~FGHIDDevice()
936void FGHIDDevice::Configure(SGPropertyNode_ptr node)
941 if (node->hasChild(
"hid-descriptor")) {
942 _haveLocalDescriptor =
true;
944 SG_LOG(SG_INPUT, SG_INFO, GetUniqueName() <<
" will configure using local HID descriptor");
947 for (
auto report : node->getChild(
"hid-descriptor")->getChildren(
"report")) {
952 if (node->hasChild(
"hid-raw-descriptor")) {
953 _rawXMLDescriptor = simgear::strutils::decodeHex(node->getStringValue(
"hid-raw-descriptor"));
955 SG_LOG(SG_INPUT, SG_INFO, GetUniqueName() <<
" will configure using XML-defined raw HID descriptor");
959 if (node->getBoolValue(
"hid-debug-raw")) {
964bool FGHIDDevice::Open()
966 SG_LOG(SG_INPUT, SG_INFO,
"HID open " << GetUniqueName());
967 _device = hid_open_path(_hidPath.c_str());
968 if (_device ==
nullptr) {
969 SG_LOG(SG_INPUT, SG_WARN, GetUniqueName() <<
": HID: Failed to open:" << _hidPath);
970 SG_LOG(SG_INPUT, SG_WARN,
"\tnote on Linux you may need to adjust permissions of the device using UDev rules.");
974#if !defined(SG_WINDOWS)
975 if (_rawXMLDescriptor.empty()) {
976 _rawXMLDescriptor.resize(2048);
977 int descriptorSize = hid_get_descriptor(_device, _rawXMLDescriptor.data(), _rawXMLDescriptor.size());
978 if (descriptorSize <= 0) {
979 SG_LOG(SG_INPUT, SG_WARN,
"HID: " << GetUniqueName() <<
" failed to read HID descriptor");
983 _rawXMLDescriptor.resize(descriptorSize);
987 if (!_haveLocalDescriptor) {
988 bool ok = parseUSBHIDDescriptor();
993 for (
auto& v : handledEvents) {
994 auto reportItem = itemWithName(v.first);
995 if (!reportItem.second) {
996 SG_LOG(SG_INPUT, SG_WARN,
"HID device:" << GetUniqueName() <<
" has no element for event:" << v.first);
1002 SG_LOG(SG_INPUT, SG_INFO,
"\tfound item for event:" << v.first);
1005 reportItem.second->event = event;
1011bool FGHIDDevice::parseUSBHIDDescriptor()
1013#if defined(SG_WINDOWS)
1014 if (_rawXMLDescriptor.empty()) {
1015 SG_LOG(SG_INPUT, SG_ALERT, GetUniqueName() <<
": on Windows, there is no way to extract the UDB-HID report descriptor. "
1016 <<
"\nPlease supply the report descriptor in the device XML configuration.");
1017 SG_LOG(SG_INPUT, SG_ALERT,
"See this page:<> for information on extracting the report descriptor on Windows");
1023 SG_LOG(SG_INPUT, SG_INFO,
"\nHID: descriptor for:" << GetUniqueName());
1025 std::ostringstream byteString;
1027 for (
size_t i = 0;
i < _rawXMLDescriptor.size(); ++
i) {
1028 byteString << hexTable[_rawXMLDescriptor[i] >> 4];
1029 byteString <<
hexTable[_rawXMLDescriptor[
i] & 0x0f];
1032 SG_LOG(SG_INPUT, SG_INFO,
"\tbytes: " << byteString.str());
1036 hid_item* rootItem =
nullptr;
1037 hid_parse_reportdesc(_rawXMLDescriptor.data(), _rawXMLDescriptor.size(), &rootItem);
1039 SG_LOG(SG_INPUT, SG_INFO,
"\nHID: scan for:" << GetUniqueName());
1042 parseCollection(rootItem);
1044 hid_free_reportdesc(rootItem);
1048void FGHIDDevice::parseCollection(hid_item* c)
1050 for (hid_item* child = c->collection; child !=
nullptr; child = child->next) {
1051 if (child->collection) {
1052 parseCollection(child);
1060auto FGHIDDevice::getReport(
HID::ReportType ty, uint8_t number,
bool doCreate) -> Report*
1063 _haveNumberedReports =
true;
1066 for (
auto report : _reports) {
1067 if ((
report->type == ty) && (
report->number == number)) {
1073 auto r =
new Report{ty, number};
1074 _reports.push_back(r);
1081auto FGHIDDevice::itemWithName(
const std::string&
name)
const -> std::pair<Report*, Item*>
1083 for (
auto report : _reports) {
1084 for (
auto item :
report->items) {
1085 if (item->name ==
name) {
1086 return std::make_pair(
report, item);
1091 return std::make_pair(
static_cast<Report*
>(
nullptr),
static_cast<Item*
>(
nullptr));
1094uint8_t FGHIDDevice::countWithName(
const std::string&
name)
const
1097 size_t nameLength =
name.length();
1099 for (
auto report : _reports) {
1100 for (
auto item :
report->items) {
1101 if (strncmp(
name.c_str(), item->name.c_str(), nameLength) == 0) {
1110void FGHIDDevice::parseItem(hid_item* item)
1113 if (hid_parse_is_relative(item)) {
1120 auto existingItem = itemWithName(
name);
1121 if (existingItem.second) {
1124 if (existingItemType != ty) {
1131 existingItem.second->name =
"feature-" +
name;
1138 int existingCount = countWithName(
name);
1139 if (existingCount > 0) {
1140 if (existingCount == 1) {
1142 auto existingItem = itemWithName(
name);
1143 existingItem.second->name +=
"-0";
1147 std::stringstream os;
1148 os <<
name <<
"-" << existingCount;
1152 auto report = getReport(ty, item->report_id,
true );
1153 uint32_t bitOffset =
report->currentBitSize();
1156 SG_LOG(SG_INPUT, SG_INFO, GetUniqueName() <<
": add:" <<
name <<
", bits: " << bitOffset <<
":" << (
int) item->report_size
1157 <<
", report=" << (
int) item->report_id);
1160 Item* itemObject =
new Item{
name, bitOffset, item->report_size};
1161 itemObject->isRelative = hid_parse_is_relative(item);
1162 itemObject->doSignExtend = (item->logical_min < 0) || (item->logical_max < 0);
1163 report->items.push_back(itemObject);
1166void FGHIDDevice::Close()
1174void FGHIDDevice::update(
double dt)
1180 uint8_t reportBuf[65];
1183 readCount = hid_read_timeout(_device, reportBuf,
sizeof(reportBuf), 0);
1185 if (readCount <= 0) {
1190 const uint8_t reportNumber = _haveNumberedReports ? reportBuf[0] : 0;
1193 SG_LOG(SG_INPUT, SG_WARN, GetName() <<
": FGHIDDevice: Unknown input report number:" <<
1194 static_cast<int>(reportNumber));
1196 uint8_t* reportBytes = _haveNumberedReports ? reportBuf + 1 : reportBuf;
1197 size_t reportSize = _haveNumberedReports ? readCount - 1 : readCount;
1198 processInputReport(inputReport, reportBytes, reportSize, dt, modifiers);
1204 for (
auto rep : _dirtyReports) {
1208 _dirtyReports.clear();
1211void FGHIDDevice::sendReport(Report*
report)
const
1217 uint8_t reportBytes[65];
1218 size_t reportLength = 0;
1219 memset(reportBytes, 0,
sizeof(reportBytes));
1220 reportBytes[0] =
report->number;
1223 for (
auto item :
report->items) {
1224 reportLength += item->bitSize;
1225 if (item->lastValue == 0) {
1229 writeBits(reportBytes + 1, item->bitOffset, item->bitSize, item->lastValue);
1235 std::ostringstream byteString;
1236 for (
size_t i=0;
i<reportLength; ++
i) {
1237 byteString << hexTable[reportBytes[i] >> 4];
1238 byteString <<
hexTable[reportBytes[
i] & 0x0f];
1241 SG_LOG(SG_INPUT, SG_INFO,
"sending bytes: " << byteString.str());
1247 hid_send_feature_report(_device, reportBytes, reportLength + 1);
1250 hid_write(_device, reportBytes, reportLength + 1);
1254int FGHIDDevice::maybeSignExtend(Item* item,
int inValue)
1256 return item->doSignExtend ?
signExtend(inValue, item->bitSize) : inValue;
1259void FGHIDDevice::processInputReport(Report*
report,
unsigned char* data,
1261 double dt,
int keyModifiers)
1264 SG_LOG(SG_INPUT, SG_INFO, GetName() <<
" FGHIDDeivce received input report:" << (
int)
report->number <<
", len=" << length);
1266 std::ostringstream byteString;
1267 for (
size_t i=0;
i<length; ++
i) {
1268 byteString << hexTable[data[i] >> 4];
1272 SG_LOG(SG_INPUT, SG_INFO,
"\tbytes: " << byteString.str());
1276 for (
auto item :
report->items) {
1277 int value =
extractBits(data, length, item->bitOffset, item->bitSize);
1279 value = maybeSignExtend(item, value);
1282 if (item->isRelative) {
1289 if (value == item->lastValue) {
1294 item->lastValue = value;
1299 SG_LOG(SG_INPUT, SG_INFO,
"\titem:" << item->name <<
" = " << value);
1302 HIDEventData
event{item, value, dt, keyModifiers};
1307void FGHIDDevice::SendFeatureReport(
unsigned int reportId,
const std::string& data)
1314 SG_LOG(SG_INPUT, SG_INFO, GetName() <<
": FGHIDDevice: Sending feature report:" << (
int) reportId <<
", len=" << data.size());
1316 std::ostringstream byteString;
1318 for (
unsigned int i=0;
i<data.size(); ++
i) {
1319 byteString << hexTable[data[i] >> 4];
1323 SG_LOG(SG_INPUT, SG_INFO,
"\tbytes: " << byteString.str());
1328 size_t len = std::min(data.length() + 1,
sizeof(buf));
1330 memcpy(buf + 1, data.data(), len - 1);
1331 size_t r = hid_send_feature_report(_device, buf, len);
1333 SG_LOG(SG_INPUT, SG_WARN, GetName() <<
": FGHIDDevice: Sending feature report failed, error-string is:\n"
1334 << simgear::strutils::error_string(errno));
1338const char *FGHIDDevice::TranslateEventName(FGEventData &eventData)
1340 HIDEventData& hidEvent =
static_cast<HIDEventData&
>(eventData);
1341 return hidEvent.item->name.c_str();
1344void FGHIDDevice::Send(
const char *eventName,
double value)
1346 auto item = itemWithName(eventName);
1347 if (item.second ==
nullptr) {
1348 SG_LOG(SG_INPUT, SG_WARN, GetName() <<
": FGHIDDevice:unknown item name:" << eventName);
1352 int intValue =
static_cast<int>(value);
1353 if (item.second->lastValue == intValue) {
1357 lastEventName->setStringValue(eventName);
1358 lastEventValue->setDoubleValue(value);
1361 item.second->lastValue = intValue;
1362 _dirtyReports.insert(item.first);
1365void FGHIDDevice::defineReport(SGPropertyNode_ptr reportNode)
1367 const int nChildren = reportNode->nChildren();
1368 uint32_t bitCount = 0;
1371 SG_LOG(SG_INPUT, SG_WARN, GetName() <<
": FGHIDDevice: invalid report type:" <<
1372 reportNode->getStringValue(
"type"));
1376 const auto id = reportNode->getIntValue(
"id");
1378 _haveNumberedReports =
true;
1381 auto report =
new Report(rty,
id);
1382 _reports.push_back(
report);
1384 for (
int c=0; c < nChildren; ++c) {
1385 const auto nd = reportNode->getChild(c);
1386 const int size = nd->getIntValue(
"size", 1);
1387 if (nd->getNameString() ==
"unused-bits") {
1392 if (nd->getNameString() ==
"type" || nd->getNameString() ==
"id") {
1397 uint8_t count = nd->getIntValue(
"count", 1);
1398 std::string
name = nd->getNameString();
1399 const auto lastHypen =
name.rfind(
"-");
1400 std::string baseName =
name.substr(0, lastHypen + 1);
1401 int baseIndex = std::stoi(
name.substr(lastHypen + 1));
1403 const bool isRelative = (
name.find(
"rel-") == 0);
1404 const bool isSigned = nd->getBoolValue(
"is-signed",
false);
1406 for (uint8_t
i=0;
i < count; ++
i) {
1407 std::ostringstream oss;
1408 oss << baseName << (baseIndex +
i);
1409 Item* itemObject =
new Item{oss.str(), bitCount,
static_cast<uint8_t
>(size)};
1410 itemObject->isRelative = isRelative;
1411 itemObject->doSignExtend = isSigned;
1412 report->items.push_back(itemObject);
1422int extractBits(uint8_t* bytes,
size_t lengthInBytes,
size_t bitOffset,
size_t bitSize)
1424 const size_t wholeBytesToSkip = bitOffset >> 3;
1425 const size_t offsetInByte = bitOffset & 0x7;
1428 const size_t bytesToCopy = std::min(
sizeof(uint32_t), (offsetInByte + bitSize + 7) / 8);
1431 memcpy((
void*) &v, bytes + wholeBytesToSkip, bytesToCopy);
1434 v = v >> offsetInByte;
1437 const uint32_t mask = ~(0xffffffff << bitSize);
1445 const int m = 1U << (bitSize - 1);
1446 return (inValue ^ m) - m;
1449void writeBits(uint8_t* bytes,
size_t bitOffset,
size_t bitSize,
int value)
1451 size_t wholeBytesToSkip = bitOffset >> 3;
1452 uint8_t* dataByte = bytes + wholeBytesToSkip;
1453 size_t offsetInByte = bitOffset & 0x7;
1454 size_t bitsInByte = std::min(bitSize, 8 - offsetInByte);
1455 uint8_t mask = 0xff >> (8 - bitsInByte);
1457 *dataByte |= ((value & mask) << offsetInByte);
1459 if (bitsInByte < bitSize) {
1461 writeBits(bytes, bitOffset + bitsInByte, bitSize - bitsInByte, value >> bitsInByte);
1477 SG_LOG(SG_INPUT, SG_INFO,
"Re-Initializing HID input bindings");
1485 SG_LOG(SG_INPUT, SG_INFO,
"HID event input starting up");
1489 hid_device_info* devices = hid_enumerate(0 , 0 );
1491 for (hid_device_info* curDev = devices; curDev !=
nullptr; curDev = curDev->next) {
1492 d->evaluateDevice(curDev);
1495 hid_free_enumeration(devices);
1500 SG_LOG(SG_INPUT, SG_INFO,
"HID event input shutting down");
1524 p->AddDevice(
new FGHIDDevice(deviceInfo,
p));
@ AD_DisplayControlReport
@ GD_DockableDeviceObjectType
@ GD_ComputerChassisDevice
@ GD_WirelessRadioControls
@ GD_SystemDismissNotification
@ GD_DockableDeviceDockingState
@ GD_ResolutionMultiplier
@ GD_DockableDeviceVendorID
@ GD_SystemMicrophoneMute
@ GD_SystemDisplayRotationLockSliderSwitch
@ GD_CallStateManagementControl
@ GD_SystemDisplayInternal
@ GD_PortableDeviceControl
@ GD_SystemDisplayExternal
@ GD_SystemDisplayRotationLockButton
@ GD_SystemDisplaySwapPrimarySecondary
@ GD_DockableDevicePrimaryUsageID
@ GD_DockableDeviceUniqueID
@ GD_SystemFunctionShiftLockIndicator
@ GD_DockableDevicePrimaryUsagePage
@ GD_CoolantCriticalLevel
@ GD_WirelessRadioSliderSwitch
@ GD_ApplicationDebuggerBreak
@ GD_SystemMultiAxisController
@ GD_SystemFunctionShiftLock
@ GD_DockableDeviceDisplayOcclusion
@ GD_SystemDisplayToggleLCDAutoscale
@ GD_SystemDisplayToggleIntExtMode
@ SC_FlightCommunications
@ SC_FlightSimulationDevice
@ SC_ElectronicCountermeasures
@ SC_SubmarineSimulationDevice
@ SC_SpaceShipSimulationDevice
@ SC_SportsSimulationDevice
@ SC_AirplaneSimulationDevice
@ SC_TankSimulationDevice
@ SC_AutomobileSimulationDevice
@ SC_BycicleSimulationDevice
@ SC_MagicCarpetSimulationDevice
@ SC_MotorcycleSimulationDevice
@ SC_HelicopterSimulationDevice
@ SC_SailingSimulationDevice
std::string nameForUsage(uint32_t usagePage, uint32_t usage)
@ MagneticStripeReadingDevice
bool shouldPrefixWithAbs(uint32_t usagePage, uint32_t usage)
@ LED_UsageMultiModeIndicator
@ LED_UsageIndicatorColor
@ LED_UsageInUseIndicator
@ LED_ExternalPowerConnected
@ LED_UsageSelectedIndicator
@ LED_RecordingFormatDetect
ReportType reportTypeFromString(const std::string &s)