/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "HidDefs.h" #include "HidParser.h" #include "HidLog.h" #include <iostream> #include <iomanip> namespace HidUtil { void HidParser::reset() { mGlobalStack = HidGlobalStack(); mLocal = HidLocal(); mTree = std::make_shared<HidTreeNode>(); mCurrent = mTree; } bool HidParser::parse(const std::vector<HidItem> &token) { // Clean up internal states of the parser for a new stream of descriptor token reset(); bool ret = true; using namespace HidDef::TagType; for (auto &i : token) { switch (i.type) { case MAIN: ret = processMainTag(i); break; case GLOBAL: ret = mGlobalStack.append(i); break; case LOCAL: ret = mLocal.append(i); break; default: LOG_E << "HidParser found illegal HidItem: " << i << LOG_ENDL; ret = false; } // in case a parse failure, quit prematurely if (!ret) { break; } } return ret; } bool HidParser::processMainTag(const HidItem &i) { using namespace HidDef::MainTag; using namespace HidDef::ReportFlag; bool ret = true; switch (i.tag) { case COLLECTION: { unsigned int collectionType; if (!i.dataAsUnsigned(&collectionType)) { LOG_E << "Cannot get collection type at offset " << i.offset << LOG_ENDL; ret = false; break; } unsigned int fullUsage = mGlobalStack.top().usagePage.get(0) << 16 | mLocal.getUsage(0); mCurrent = mCurrent->addChild( std::make_shared<HidTreeNode>(mCurrent, collectionType, fullUsage)); break; } case END_COLLECTION: mCurrent = mCurrent->getParent(); if (!mCurrent) { // trigger parse failure so that mCurrent will not be accessed LOG_E << "unmatched END_COLLECTION at " << i.offset << LOG_ENDL; ret = false; } break; case INPUT: case OUTPUT: case FEATURE: { unsigned int reportType = i.tag; unsigned int flag; if (!i.dataAsUnsigned(&flag)) { LOG_E << "Cannot get report flag at offset " << i.offset << LOG_ENDL; ret = false; break; } const HidGlobal &top = mGlobalStack.top(); // usage page, local min/max, report size and count have to be defined at report // definition. if (!(top.usagePage.isSet() && top.logicalMin.isSet() && top.logicalMax.isSet() && top.reportSize.isSet() && top.reportCount.isSet())) { LOG_E << "Report defined at " << i.offset << " does not have all mandatory fields set" << LOG_ENDL; ret = false; break; } if (top.reportSize.get(0) > 32) { LOG_E << "Report defined at " << i.offset << " has unsupported report size(> 32 bit)" << LOG_ENDL; ret = false; break; } HidReport report(reportType, flag, top, mLocal); mReport.push_back(report); std::shared_ptr<HidTreeNode> node(new HidReportNode(mCurrent, report)); mCurrent->addChild(node); break; } default: LOG_E << "unknown main tag, " << i << LOG_ENDL; ret = false; } // locals is cleared after any main tag according to HID spec mLocal.clear(); return ret; } bool HidParser::parse(const unsigned char *begin, size_t size) { std::vector<HidItem> hidItemVector = HidItem::tokenize(begin, size); return parse(hidItemVector); } void HidParser::filterTree() { if (mTree != nullptr) { filterTree(mTree); } } void HidParser::filterTree(std::shared_ptr<HidTreeNode> &node) { if (node->isReportCollection()) { std::shared_ptr<HidReportNode> reportNode = std::static_pointer_cast<HidReportNode>(node->getChildren().front()); if (reportNode != nullptr) { reportNode->collapse(node->getFullUsage()); node = reportNode; } } else { for (auto &i : node->getChildren()) { filterTree(i); } } } HidParser::DigestVector HidParser::generateDigest( const std::unordered_set<unsigned int> &interestedUsage) { DigestVector digestVector; digest(&digestVector, mTree, interestedUsage); return digestVector; } void HidParser::digest(HidParser::DigestVector *digestVector, const std::shared_ptr<HidTreeNode> &node, const std::unordered_set<unsigned int> &interestedUsage) { if (digestVector == nullptr) { return; } if (node->isUsageCollection() && interestedUsage.find(node->getFullUsage()) != interestedUsage.end()) { // this collection contains the usage interested ReportSetGroup reportSetGroup; // one layer deep search for (auto &i : node->getChildren()) { // skip all nodes that is not a report node if (i->getNodeType() != HidTreeNode::TYPE_REPORT) { continue; } const HidReport &report = std::static_pointer_cast<HidReportNode>(i)->getReport(); unsigned int id = report.getReportId();; if (reportSetGroup.find(id) == reportSetGroup.end()) { // create an id group if it is not created reportSetGroup.emplace(id, ReportSet()); } ReportSet &reportGroup = reportSetGroup[id]; switch(report.getType()) { using namespace HidDef::MainTag; case FEATURE: reportGroup[REPORT_TYPE_FEATURE].push_back(report); break; case INPUT: reportGroup[REPORT_TYPE_INPUT].push_back(report); break; case OUTPUT: reportGroup[REPORT_TYPE_OUTPUT].push_back(report); break; } } ReportDigest digest = { .fullUsage = node->getFullUsage(), .packets = convertGroupToPacket(reportSetGroup) }; digestVector->emplace_back(digest); } else { for (const auto &child : node->getChildren()) { if (child->getNodeType() == HidTreeNode::TYPE_NORMAL) { // only follow into collection nodes digest(digestVector, child, interestedUsage); } } } } std::vector<HidParser::ReportPacket> HidParser::convertGroupToPacket( const HidParser::ReportSetGroup &group) { std::vector<ReportPacket> packets; const std::vector<int> types = {REPORT_TYPE_FEATURE, REPORT_TYPE_INPUT, REPORT_TYPE_OUTPUT}; for (const auto &setPair : group) { unsigned int id = setPair.first; for (auto type : types) { const auto &reports = setPair.second[type]; // feature // template ReportPacket packet = { .type = type, .id = id, .bitSize = 0 }; for (const auto &r : reports) { auto logical = r.getLogicalRange(); auto physical = r.getPhysicalRange(); int64_t offset = physical.first - logical.first; double scale = static_cast<double>((physical.second - physical.first)) / (logical.second - logical.first); scale *= r.getExponentValue(); ReportItem digest = { .usage = r.getFullUsage(), .id = id, .minRaw = logical.first, .maxRaw = logical.second, .a = scale, .b = offset, .bitOffset = packet.bitSize, .bitSize = r.getSize(), .count = r.getCount(), .unit = r.getUnit(), }; packet.reports.push_back(digest); packet.bitSize += digest.bitSize * digest.count; } if (!packet.reports.empty()) { packets.push_back(std::move(packet)); } } } return packets; } static std::string reportTypeToString(int reportType) { switch (reportType) { case HidParser::REPORT_TYPE_INPUT: return "INPUT"; case HidParser::REPORT_TYPE_OUTPUT: return "OUTPUT"; case HidParser::REPORT_TYPE_FEATURE: return "FEATURE"; default: return "INVALID REPORT"; } } std::ostream& operator<<(std::ostream &os, const HidParser::DigestVector &digests) { for (const auto &i : digests) { os << "Usage: 0x" << std::hex << i.fullUsage << std::dec << ", " << i.packets.size() << " report packet:" << LOG_ENDL; for (const auto &packet : i.packets) { os << reportTypeToString(packet.type) << " id: " << packet.id << " size: " << packet.bitSize << "b(" << packet.getByteSize() << "B), " << packet.reports.size() << " entries" << LOG_ENDL; for (const auto &report : packet.reports) { double min, max; report.decode(report.mask(report.minRaw), &min); report.decode(report.mask(report.maxRaw), &max); os << " " << report.bitOffset << " size: " << report.bitSize << ", count: " << report.count << ", usage: " << std::hex << std::setfill('0') << std::setw(8) << report.usage << std::dec << ", min: " << report.minRaw << ", max: " << report.maxRaw << ", minDecoded: " << min << ", maxDecoded: " << max << ", a: " << report.a << ", b: " << report.b << std::hex << ", minRawHex: 0x" << report.mask(report.minRaw) << ", maxRawHex: 0x" << report.mask(report.maxRaw) << ", rawMasked: 0x" << report.rawMask() << std::dec << LOG_ENDL; } } os << LOG_ENDL; } os << LOG_ENDL; return os; } } // namespace HidUtil