summaryrefslogtreecommitdiff
path: root/modules/sensors/dynamic_sensor/HidUtils/HidParser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/sensors/dynamic_sensor/HidUtils/HidParser.cpp')
-rw-r--r--modules/sensors/dynamic_sensor/HidUtils/HidParser.cpp319
1 files changed, 319 insertions, 0 deletions
diff --git a/modules/sensors/dynamic_sensor/HidUtils/HidParser.cpp b/modules/sensors/dynamic_sensor/HidUtils/HidParser.cpp
new file mode 100644
index 00000000..5efaf65a
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/HidUtils/HidParser.cpp
@@ -0,0 +1,319 @@
+/*
+ * 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