Skip to content

Commit

Permalink
Add VeryLong report type and support for modern HID++ collections
Browse files Browse the repository at this point in the history
Dispatcher stores in ReportInfo which reports are present on the device.
  • Loading branch information
cvuchener committed Aug 9, 2021
1 parent d843ed4 commit 34f3041
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 183 deletions.
5 changes: 4 additions & 1 deletion src/libhidpp/hidpp/Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <misc/Log.h>

#include <algorithm>
#include <cassert>

using namespace HIDPP;

Expand Down Expand Up @@ -76,7 +77,9 @@ Device::Device (Dispatcher *dispatcher, DeviceIndex device_index):

// Check protocol version
static constexpr unsigned int software_id = 1;
Report request (Report::Short, _device_index, HIDPP20::IRoot::index, HIDPP20::IRoot::Ping, software_id);
auto type = _dispatcher->reportInfo ().findReport ();
assert (type);
Report request (*type, _device_index, HIDPP20::IRoot::index, HIDPP20::IRoot::Ping, software_id);
auto response = _dispatcher->sendCommand (std::move (request));
try {
auto report = response->get (is_wireless ? 2000 : 500); // use longer timeout for wireless devices that can be sleeping.
Expand Down
95 changes: 95 additions & 0 deletions src/libhidpp/hidpp/Dispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

#include "Dispatcher.h"

#include <misc/Log.h>

using namespace HIDPP;

const char *Dispatcher::NoHIDPPReportException::what () const noexcept
Expand Down Expand Up @@ -58,3 +60,96 @@ void Dispatcher::processEvent (const Report &report)
it = _listeners.erase (it);
}
}

static bool hasReport(const HID::ReportCollection &collection, HID::ReportID::Type type, uint8_t id, HID::Usage usage, unsigned int count)
{
using namespace HID;
auto it = collection.reports.find (ReportID {type, id});
if (it == collection.reports.end ())
return false;
const auto &fields = it->second;
if (fields.size () != 1)
return false;
const auto &field = fields.front ();
if (auto usages = std::get_if<std::vector<Usage>> (&field.usages)) {
return field.flags.Data () && field.flags.Array () &&
field.count == count && field.size == 8 &&
usages->size () == 1 && usages->front () == usage;
}
else
return false;
}

void Dispatcher::checkReportDescriptor (const HID::ReportDescriptor &rdesc)
{
enum {
Unknown,
Legacy,
Modern,
} scheme = Unknown;
_report_info.flags = 0;
uint8_t expected_reports = 0;
for (const auto &collection: rdesc.collections) {
auto collection_usage_msb = static_cast<uint8_t> (collection.usage.usage >> 8);
auto collection_usage_lsb = static_cast<uint8_t> (collection.usage.usage);
if (collection.usage.usage_page == 0xFF43) { // Modern scheme usage page
if (scheme == Unknown) {
scheme = Modern;
expected_reports = collection_usage_msb;
}
else if (scheme != Modern) {
Log::warning () << "Ignoring HID++ collection with unexpected usage page." << std::endl;
continue;
}
else if (expected_reports != collection_usage_msb) {
Log::warning () << "Ignoring HID++ collection with mismatched usage report flags." << std::endl;
continue;
}
}
else if (collection.usage.usage_page == 0xFF00) { // Legacy scheme usage page
if (scheme == Unknown)
scheme = Legacy;
else if (scheme != Legacy) {
Log::warning () << "Ignoring HID++ collection with unexpected usage page." << std::endl;
continue;
}
else if (collection_usage_msb != 0) {
Log::warning () << "Ignoring HID++ collection with invalid usage." << std::endl;
continue;
}
}
else // Not a HID++ collection
continue;
for (auto [type, flag]: {
std::make_tuple (Report::Short, ReportInfo::HasShortReport),
std::make_tuple (Report::Long, ReportInfo::HasLongReport),
std::make_tuple (Report::VeryLong, ReportInfo::HasVeryLongReport) }) {
if (collection_usage_lsb == flag) {
auto usage = HID::Usage (collection.usage.usage_page, flag);
bool has_input = hasReport (
collection,
HID::ReportID::Type::Input,
static_cast<uint8_t> (type),
usage,
Report::reportLength (type)-1);
if (!has_input)
Log::warning () << "Missing input report for report " << type << std::endl;
bool has_output = hasReport (
collection,
HID::ReportID::Type::Output,
static_cast<uint8_t> (type),
usage,
Report::reportLength(type)-1);
if (!has_output)
Log::warning () << "Missing output report for report " << type << std::endl;
if (has_input && has_output)
_report_info.flags |= flag;
}
}
}
if (scheme == Modern && expected_reports != _report_info.flags)
Log::warning () << "Expected HID++ reports were not found." << std::endl;
if (_report_info.flags == 0)
throw Dispatcher::NoHIDPPReportException ();
}

29 changes: 29 additions & 0 deletions src/libhidpp/hidpp/Dispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <memory>
#include <map>
#include <functional>
#include <optional>

namespace HIDPP
{
Expand Down Expand Up @@ -119,11 +120,39 @@ class Dispatcher
*/
virtual void unregisterEventHandler (listener_iterator it);

struct ReportInfo {
enum Flags { // flags are also the usage for collections and reports
HasShortReport = 1<<0,
HasLongReport = 1<<1,
HasVeryLongReport = 1<<2,
};
int flags;

bool hasReport (Report::Type type) const noexcept {
switch (type) {
case Report::Short: return flags & HasShortReport;
case Report::Long: return flags & HasLongReport;
case Report::VeryLong: return flags & HasVeryLongReport;
default: return false;
}
}

std::optional<Report::Type> findReport (std::size_t minimum_parameter_length = 0) const noexcept {
for (auto type: { Report::Short, Report::Long, Report::VeryLong })
if (hasReport (type) && minimum_parameter_length <= Report::parameterLength (type))
return type;
return std::nullopt;
}
};
ReportInfo reportInfo () const noexcept { return _report_info; }

protected:
void processEvent (const Report &);
void checkReportDescriptor (const HID::ReportDescriptor &report_desc);

private:
listener_container _listeners;
ReportInfo _report_info;
};

}
Expand Down
6 changes: 2 additions & 4 deletions src/libhidpp/hidpp/DispatcherThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,7 @@ DispatcherThread::DispatcherThread (const char *path):
_dev (path),
_stopped (false)
{
const HID::ReportDescriptor &rdesc = _dev.getReportDescriptor ();
if (!checkReportDescriptor (rdesc))
throw Dispatcher::NoHIDPPReportException ();
checkReportDescriptor (_dev.getReportDescriptor ());
}

DispatcherThread::~DispatcherThread ()
Expand Down Expand Up @@ -151,7 +149,7 @@ void DispatcherThread::run ()
{
while (!_stopped) {
try {
std::vector<uint8_t> raw_report (Report::MaxDataLength+1);
std::vector<uint8_t> raw_report (MaxReportLength);
if (0 != _dev.readReport (raw_report))
processReport (std::move (raw_report));
}
Expand Down
Loading

0 comments on commit 34f3041

Please sign in to comment.