| // 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. |
| |
| #include "libtouchraw/reader.h" |
| |
| #include <utility> |
| |
| #include <base/logging.h> |
| |
| #include "libtouchraw/touchraw.h" |
| |
| namespace touchraw { |
| |
| // HID buffer maximum size 16kb. Be consistent with HID_MAX_BUFFER_SIZE defined |
| // in linux hid.h. |
| constexpr ssize_t kHIDMaxSize = 16384; |
| // Report id is the first byte of a HID report. |
| constexpr int kHIDReportIDIndex = 0; |
| |
| Reader::Reader(base::ScopedFD fd, |
| std::unique_ptr<HIDDataConsumerInterface> q, |
| std::unique_ptr<base::FileDescriptorWatcher::Controller> watcher) |
| : fd_(std::move(fd)), q_(std::move(q)), watcher_(std::move(watcher)) {} |
| |
| absl::Status Reader::Start() { |
| if (!base::SequencedTaskRunner::HasCurrentDefault()) { |
| return absl::FailedPreconditionError( |
| "sequenced task runner: default queue for the current thread is not " |
| "present"); |
| } |
| |
| if (!watcher_) { |
| watcher_ = base::FileDescriptorWatcher::WatchReadable( |
| fd_.get(), base::BindRepeating(&Reader::OnFileCanReadWithoutBlocking, |
| base::Unretained(this), fd_.get())); |
| |
| if (!watcher_) |
| return absl::UnavailableError( |
| "failed to create a file descriptor watcher"); |
| } |
| |
| LOG(INFO) << "Start watching device " << fd_.get(); |
| return absl::OkStatus(); |
| } |
| |
| void Reader::Stop() { |
| LOG(INFO) << "Stop watching device " << fd_.get(); |
| watcher_.reset(); |
| } |
| |
| void Reader::OnFileCanReadWithoutBlocking(int fd) { |
| if (fd != fd_.get()) { |
| LOG(ERROR) << "File descriptor does not match"; |
| return; |
| } |
| |
| uint8_t buf[kHIDMaxSize] = {0}; |
| int attempts = 3; |
| ssize_t read_size; |
| while (attempts-- > 0) { |
| read_size = read(fd, buf, kHIDMaxSize); |
| if (read_size < 0) { |
| if (errno == EINTR || errno == EAGAIN) |
| continue; |
| if (errno != ENODEV) |
| LOG(ERROR) << "Error reading fd " << fd << ": " << strerror(errno); |
| Stop(); |
| return; |
| } else { |
| break; |
| } |
| } |
| |
| ProcessData(buf, read_size); |
| } |
| |
| void Reader::ProcessData(const uint8_t* buf, const ssize_t read_size) { |
| auto hid_data = std::make_unique<HIDData>(); |
| |
| if (!buf || read_size == 0) { |
| LOG(WARNING) << "Invalid buffer or read size is zero"; |
| return; |
| } |
| |
| // Push HIDData into consumer queue. |
| hid_data->report_id = buf[kHIDReportIDIndex]; |
| hid_data->payload.assign(buf + 1, buf + read_size); |
| |
| // Dispatch. |
| q_->Push(std::move(hid_data)); |
| } |
| |
| } // namespace touchraw |