| // Copyright 2024 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef LIBTOUCHRAW_PARSER_H_ | |
| #define LIBTOUCHRAW_PARSER_H_ | |
| #include <memory> | |
| #include <optional> | |
| #include <span> | |
| #include <utility> | |
| #include <vector> | |
| #include <linux/hidraw.h> | |
| #include <base/task/sequenced_task_runner.h> | |
| #include <brillo/udev/udev_device.h> | |
| #include <gtest/gtest_prod.h> | |
| #include "libtouchraw/consumer_interface.h" | |
| #include "libtouchraw/touchraw.h" | |
| #include "libtouchraw/touchraw_export.h" | |
| namespace touchraw { | |
| // Please refer to | |
| // https://www.usb.org/document-library/device-class-definition-hid-111 for HID | |
| // report item format. | |
| struct Item { | |
| std::span<const uint8_t> data; | |
| uint8_t data_size; // Specify size of data. Based on the device class | |
| // definition doc section 6.2.2.3, long items may contain | |
| // up to 256 bytes of data. So 8 bits is enough here. | |
| uint16_t size; // Specify total size of an item. | |
| uint8_t prefix; // All items have a one-byte prefix that contains the item | |
| // tag, item type, and item size. | |
| }; | |
| // Describe a main item of report descriptor. | |
| struct MainItem { | |
| uint16_t usage_page; | |
| uint16_t usage; | |
| uint8_t report_id; | |
| uint32_t data_size; // Size of the data field in bytes. | |
| }; | |
| // Necessary information to create a main item. | |
| struct MainItemInfo { | |
| uint16_t usage_page; | |
| uint16_t usage; | |
| uint8_t report_id; | |
| uint32_t report_size; | |
| uint32_t report_count; | |
| }; | |
| class ReportDescriptor { | |
| public: | |
| explicit ReportDescriptor(const hidraw_report_descriptor* rpt_desc); | |
| ReportDescriptor(const ReportDescriptor&) = delete; | |
| ReportDescriptor& operator=(const ReportDescriptor&) = delete; | |
| // True if the internal index has not reached the end of the report | |
| // descriptor; False otherwise. | |
| bool HasNextItem(); | |
| // Return the next item. | |
| Item GetNextItem(); | |
| // Reset the internal index. | |
| void Reset(); | |
| private: | |
| const hidraw_report_descriptor* rpt_desc_; | |
| uint16_t next_item_idx_; // Starting index of the next item to be processed. | |
| }; | |
| class LIBTOUCHRAW_EXPORT Parser : public HIDDataConsumerInterface { | |
| public: | |
| /** | |
| * Factory method: creates and returns a Parser. | |
| * May return null on failure cases that it fails to get a report descriptor | |
| * or the report descriptor does not support heat map. | |
| * | |
| * @param fd File descriptor. | |
| * @param q HeatmapChunk consumer queue for tasks to be posted. | |
| * @return Unique pointer of Parser if create succeeds, null pointer | |
| * otherwise. | |
| */ | |
| static std::unique_ptr<Parser> Create( | |
| const int fd, std::unique_ptr<HeatmapChunkConsumerInterface> q); | |
| static std::unique_ptr<Parser> CreateForTesting( | |
| std::unique_ptr<HeatmapChunkConsumerInterface> q); | |
| Parser(const Parser&) = delete; | |
| Parser& operator=(const Parser&) = delete; | |
| void Push(std::unique_ptr<const HIDData> data) override { | |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( | |
| FROM_HERE, base::BindOnce(&Parser::ParseHIDData, base::Unretained(this), | |
| std::move(data))); | |
| } | |
| protected: | |
| /** | |
| * Parse HID data read from the file descriptor. | |
| * | |
| * @param hid_data | |
| */ | |
| void ParseHIDData(std::unique_ptr<const HIDData> hid_data); | |
| private: | |
| FRIEND_TEST(ParserTest, ValidReportDescriptorWithHeatmap); | |
| FRIEND_TEST(ParserTest, ValidReportDescriptorWithoutHeatmap); | |
| FRIEND_TEST(ParserTest, UnknownHidType); | |
| /** | |
| * Parser constructor. | |
| * This class retrieves the input device report descriptor and parses HID data | |
| * into heatmap chunks. | |
| * | |
| * @param q HeatmapChunk consumer queue for tasks to be posted. | |
| */ | |
| explicit Parser(std::unique_ptr<HeatmapChunkConsumerInterface> q); | |
| /** | |
| * Get report descriptor from ioctl. | |
| * | |
| * @return Usually, on success zero is returned. On error, -1 is returned, and | |
| * errno is set to indicate the error. | |
| */ | |
| int GetReportDescriptorIoctl(const int fd, | |
| hidraw_report_descriptor* rpt_desc); | |
| /** | |
| * Helper function to create a udev device. | |
| * | |
| * @return Unique pointer of UdevDevice if create succeeds, null pointer | |
| * otherwise. | |
| */ | |
| std::unique_ptr<brillo::UdevDevice> CreateUdevDevice(const int fd); | |
| /** | |
| * Get report descriptor from sysfs. | |
| * | |
| * @return Usually, on success zero is returned. On error, -1 is returned, and | |
| * errno is set to indicate the error. | |
| */ | |
| int GetReportDescriptorSysfs(const int fd, | |
| hidraw_report_descriptor* rpt_desc); | |
| // TODO: b/317990775 - Extract descriptor parsing into a sub-library of | |
| // libtouchraw. | |
| bool ParseHeatmapReportsFromDescriptor( | |
| const hidraw_report_descriptor* rpt_desc); | |
| /** | |
| * Helper function to get data field value. | |
| * | |
| * @param index Data field index. | |
| * @param size Data field size. | |
| * @return Data field value. | |
| */ | |
| uint32_t GetDataField(int index, int size, std::span<const uint8_t> payload); | |
| // Helper function to process items and save report data fields into the | |
| // usages table. | |
| void ProcessItem(Item& item); | |
| // Helper function to get the value of the data field of a report. | |
| uint32_t GetPropValue(std::span<const uint8_t> data, int data_size); | |
| // Usages table that contains each usage item from the report descriptor. | |
| std::vector<MainItem> usages_; | |
| // Stores the information of each usage item. | |
| MainItemInfo info_; | |
| // Offset index of usages table for the first chunk of heat map input report | |
| // type. | |
| std::optional<uint16_t> sync_report_offset_; | |
| // Offset index of usages table for the subsequent chunks of heat map input | |
| // report type. | |
| std::optional<uint16_t> sub_report_offset_; | |
| // Task queue. | |
| const std::unique_ptr<HeatmapChunkConsumerInterface> q_; | |
| }; | |
| } // namespace touchraw | |
| #endif // LIBTOUCHRAW_PARSER_H_ |