blob: e4b5b39ca0aceb31d767aefa3827c037e9d0b327 [file] [log] [blame]
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "hidraw_device.h"
#include <vector>
#include <inttypes.h>
#include <libudev.h>
#include <base/bind.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include "atrus_device.h"
#include "scoped_udev_handle.h"
#include "util.h"
namespace atrusctl {
namespace {
const int kMaxQueryReadRetries = 10;
const int kHidMaxOutputReportSizeBytes = 3;
const int kHidMaxInputReportSizeBytes = 61;
const uint8_t kHidReportId = 0x07;
} // namespace
HIDRawDevice::HIDRawDevice() {}
bool HIDRawDevice::OpenConnection(const ConnectCallback& callback) {
HIDConnection connection;
if (!connection.Open(path_)) {
return false;
}
callback.Run(connection);
connection.Close();
return true;
}
void HIDRawDevice::Query(const uint16_t command,
const QueryCompleteCallback& callback) {
HIDRawDevice::QueryResult result;
HIDMessage request(kHidReportId, command);
HIDMessage response;
bool ret = OpenConnection(
base::Bind(&HIDRawDevice::ExecuteQueryWrapper, base::Unretained(this),
base::ConstRef(request), &response, &result));
if (!ret) {
result = kQueryError;
}
callback.Run(result, request, response);
}
void HIDRawDevice::ExecuteQueryWrapper(const HIDMessage& request,
HIDMessage* response,
HIDRawDevice::QueryResult* result,
const HIDConnection& connection) {
*result = ExecuteQuery(request, response, connection);
}
HIDRawDevice::QueryResult HIDRawDevice::ExecuteQuery(
const HIDMessage& request,
HIDMessage* response,
const HIDConnection& connection) {
// Write request to HID device
std::vector<uint8_t> request_buffer(kHidMaxOutputReportSizeBytes);
if (!request.PackIntoBuffer(&request_buffer) ||
!connection.Write(request_buffer)) {
return kQueryError;
}
// Read response from HID device, try |kMaxQueryReadRetries| times
for (int retry = 0; retry < kMaxQueryReadRetries; retry++) {
bool timeout;
if (!connection.WaitUntilReadable(&timeout)) {
return (timeout ? kQueryTimeout : kQueryError);
}
std::vector<uint8_t> response_buffer(kHidMaxInputReportSizeBytes);
if (!connection.Read(&response_buffer, kHidMaxInputReportSizeBytes) ||
!response->UnpackFromBuffer(response_buffer)) {
return kQueryError;
}
// Check if response header matches request
if (response->Validate(request)) {
return kQuerySuccess;
}
// Got unknown response, retry read
}
// Reached |kMaxQueryReadRetries|, the device only responded with unknown data
return kQueryUnknownResponse;
}
} // namespace atrusctl