Commit 0e9bf9bc authored by reillyg's avatar reillyg Committed by Commit bot

Don't pass buffers to HidConnection::Read because it knows the size.

The HidConnection knows the appropriate read buffer size and the
platform-specific implementation may already have a buffer that it is
waiting to return to the next caller. Passing a buffer in is therefore
unnecessary, one can be provided in the callback.

By standardizing on a buffer format which always includes the report ID
as the first byte (even when it is zero) most of the buffer copying can
be removed except in the case of OS X's persistent read buffer and
getting feature reports on Windows.

BUG=

Review URL: https://codereview.chromium.org/499713002

Cr-Commit-Position: refs/heads/master@{#291954}
parent eb08cced
......@@ -12,8 +12,7 @@ namespace {
// Functor used to filter collections by report ID.
struct CollectionHasReportId {
explicit CollectionHasReportId(const uint8_t report_id)
: report_id_(report_id) {}
explicit CollectionHasReportId(uint8_t report_id) : report_id_(report_id) {}
bool operator()(const HidCollectionInfo& info) const {
if (info.report_ids.size() == 0 ||
......@@ -40,7 +39,7 @@ struct CollectionIsProtected {
};
bool FindCollectionByReportId(const HidDeviceInfo& device_info,
const uint8_t report_id,
uint8_t report_id,
HidCollectionInfo* collection_info) {
std::vector<HidCollectionInfo>::const_iterator collection_iter =
std::find_if(device_info.collections.begin(),
......@@ -73,103 +72,104 @@ HidConnection::~HidConnection() {
DCHECK(thread_checker_.CalledOnValidThread());
}
void HidConnection::Read(scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) {
void HidConnection::Read(const ReadCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
if (device_info_.max_input_report_size == 0) {
// The device does not support input reports.
callback.Run(false, 0);
return;
}
int expected_buffer_size = device_info_.max_input_report_size;
if (device_info().has_report_id) {
expected_buffer_size++;
}
if (buffer->size() < expected_buffer_size) {
// Receive buffer is too small.
callback.Run(false, 0);
VLOG(1) << "This device does not support input reports.";
callback.Run(false, NULL, 0);
return;
}
PlatformRead(buffer, callback);
PlatformRead(callback);
}
void HidConnection::Write(uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) {
void HidConnection::Write(scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
if (device_info_.max_output_report_size == 0) {
// The device does not support output reports.
callback.Run(false, 0);
VLOG(1) << "This device does not support output reports.";
callback.Run(false);
return;
}
DCHECK_GE(size, 1u);
uint8_t report_id = buffer->data()[0];
if (device_info().has_report_id != (report_id != 0)) {
VLOG(1) << "Invalid output report ID.";
callback.Run(false);
return;
}
if (IsReportIdProtected(report_id)) {
callback.Run(false, 0);
VLOG(1) << "Attempt to set a protected output report.";
callback.Run(false);
return;
}
PlatformWrite(report_id, buffer, callback);
PlatformWrite(buffer, size, callback);
}
void HidConnection::GetFeatureReport(
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) {
void HidConnection::GetFeatureReport(uint8_t report_id,
const ReadCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
if (device_info_.max_feature_report_size == 0) {
// The device does not support feature reports.
callback.Run(false, 0);
VLOG(1) << "This device does not support feature reports.";
callback.Run(false, NULL, 0);
return;
}
if (IsReportIdProtected(report_id)) {
callback.Run(false, 0);
if (device_info().has_report_id != (report_id != 0)) {
VLOG(1) << "Invalid feature report ID.";
callback.Run(false, NULL, 0);
return;
}
int expected_buffer_size = device_info_.max_feature_report_size;
if (device_info().has_report_id) {
expected_buffer_size++;
}
if (buffer->size() < expected_buffer_size) {
// Receive buffer is too small.
callback.Run(false, 0);
if (IsReportIdProtected(report_id)) {
VLOG(1) << "Attempt to get a protected feature report.";
callback.Run(false, NULL, 0);
return;
}
PlatformGetFeatureReport(report_id, buffer, callback);
PlatformGetFeatureReport(report_id, callback);
}
void HidConnection::SendFeatureReport(
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) {
void HidConnection::SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
if (device_info_.max_feature_report_size == 0) {
// The device does not support feature reports.
callback.Run(false, 0);
VLOG(1) << "This device does not support feature reports.";
callback.Run(false);
return;
}
DCHECK_GE(size, 1u);
uint8_t report_id = buffer->data()[0];
if (device_info().has_report_id != (report_id != 0)) {
VLOG(1) << "Invalid feature report ID.";
callback.Run(false);
return;
}
if (IsReportIdProtected(report_id)) {
callback.Run(false, 0);
VLOG(1) << "Attempt to set a protected feature report.";
callback.Run(false);
return;
}
PlatformSendFeatureReport(report_id, buffer, callback);
PlatformSendFeatureReport(buffer, size, callback);
}
bool HidConnection::CompleteRead(scoped_refptr<net::IOBufferWithSize> buffer,
int bytes_read,
const IOCallback& callback) {
DCHECK_LE(bytes_read, buffer->size());
if (bytes_read == 0 || IsReportIdProtected(buffer->data()[0])) {
bool HidConnection::CompleteRead(scoped_refptr<net::IOBuffer> buffer,
size_t size,
const ReadCallback& callback) {
DCHECK_GE(size, 1u);
uint8_t report_id = buffer->data()[0];
if (IsReportIdProtected(report_id)) {
VLOG(1) << "Filtered a protected input report.";
return false;
}
callback.Run(true, bytes_read);
callback.Run(true, buffer, size);
return true;
}
bool HidConnection::IsReportIdProtected(const uint8_t report_id) {
bool HidConnection::IsReportIdProtected(uint8_t report_id) {
HidCollectionInfo collection_info;
if (FindCollectionByReportId(device_info_, report_id, &collection_info)) {
return collection_info.usage.IsProtected();
......
......@@ -22,23 +22,33 @@ class HidConnection : public base::RefCountedThreadSafe<HidConnection> {
kAnyReportId = 0xFF,
};
typedef base::Callback<void(bool success, size_t size)> IOCallback;
typedef base::Callback<
void(bool success, scoped_refptr<net::IOBuffer> buffer, size_t size)>
ReadCallback;
typedef base::Callback<void(bool success)> WriteCallback;
const HidDeviceInfo& device_info() const { return device_info_; }
bool has_protected_collection() const { return has_protected_collection_; }
const base::ThreadChecker& thread_checker() const { return thread_checker_; }
void Read(scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback);
void Write(uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback);
void GetFeatureReport(uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback);
void SendFeatureReport(uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback);
// The report ID (or 0 if report IDs are not supported by the device) is
// always returned in the first byte of the buffer.
void Read(const ReadCallback& callback);
// The report ID (or 0 if report IDs are not supported by the device) is
// always expected in the first byte of the buffer.
void Write(scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback);
// The report ID is not returned in the buffer.
void GetFeatureReport(uint8_t report_id, const ReadCallback& callback);
// The report ID (or 0 if report IDs are not supported by the device) is
// always expected in the first byte of the buffer.
void SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback);
protected:
friend class base::RefCountedThreadSafe<HidConnection>;
......@@ -46,31 +56,27 @@ class HidConnection : public base::RefCountedThreadSafe<HidConnection> {
explicit HidConnection(const HidDeviceInfo& device_info);
virtual ~HidConnection();
virtual void PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) = 0;
virtual void PlatformWrite(uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) = 0;
virtual void PlatformGetFeatureReport(
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) = 0;
virtual void PlatformSendFeatureReport(
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) = 0;
virtual void PlatformRead(const ReadCallback& callback) = 0;
virtual void PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback) = 0;
virtual void PlatformGetFeatureReport(uint8_t report_id,
const ReadCallback& callback) = 0;
virtual void PlatformSendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback) = 0;
// PlatformRead implementation must call this method on read
// success, rather than directly running the callback.
// In case incoming buffer is empty or protected, it is filtered
// and this method returns false. Otherwise it runs the callback
// and returns true.
bool CompleteRead(scoped_refptr<net::IOBufferWithSize> buffer,
int bytes_read,
const IOCallback& callback);
bool CompleteRead(scoped_refptr<net::IOBuffer> buffer,
size_t size,
const ReadCallback& callback);
private:
bool IsReportIdProtected(const uint8_t report_id);
bool IsReportIdProtected(uint8_t report_id);
const HidDeviceInfo device_info_;
bool has_protected_collection_;
......@@ -83,15 +89,15 @@ struct PendingHidReport {
PendingHidReport();
~PendingHidReport();
scoped_refptr<net::IOBufferWithSize> buffer;
scoped_refptr<net::IOBuffer> buffer;
size_t size;
};
struct PendingHidRead {
PendingHidRead();
~PendingHidRead();
scoped_refptr<net::IOBufferWithSize> buffer;
HidConnection::IOCallback callback;
HidConnection::ReadCallback callback;
};
} // namespace device
......
......@@ -29,21 +29,6 @@
namespace device {
namespace {
// Copies a buffer into a new one with a report ID byte inserted at the front.
scoped_refptr<net::IOBufferWithSize> CopyBufferWithReportId(
scoped_refptr<net::IOBufferWithSize> buffer,
uint8_t report_id) {
scoped_refptr<net::IOBufferWithSize> new_buffer(
new net::IOBufferWithSize(buffer->size() + 1));
new_buffer->data()[0] = report_id;
memcpy(new_buffer->data() + 1, buffer->data(), buffer->size());
return new_buffer;
}
} // namespace
HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info,
std::string dev_node)
: HidConnection(device_info) {
......@@ -91,73 +76,67 @@ HidConnectionLinux::~HidConnectionLinux() {
Flush();
}
void HidConnectionLinux::PlatformRead(
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) {
void HidConnectionLinux::PlatformRead(const ReadCallback& callback) {
PendingHidRead pending_read;
pending_read.buffer = buffer;
pending_read.callback = callback;
pending_reads_.push(pending_read);
ProcessReadQueue();
}
void HidConnectionLinux::PlatformWrite(
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) {
// Linux always expects the first byte of the buffer to be the report ID.
buffer = CopyBufferWithReportId(buffer, report_id);
const int bytes_written = HANDLE_EINTR(
write(device_file_.GetPlatformFile(), buffer->data(), buffer->size()));
void HidConnectionLinux::PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback) {
// Linux expects the first byte of the buffer to always be a report ID so the
// buffer can be used directly.
const ssize_t bytes_written =
HANDLE_EINTR(write(device_file_.GetPlatformFile(), buffer->data(), size));
if (bytes_written < 0) {
VPLOG(1) << "Write failed";
Disconnect();
callback.Run(false, 0);
callback.Run(false);
} else {
if (bytes_written != buffer->size()) {
LOG(WARNING) << "Incomplete HID write: "
<< bytes_written << " != " << buffer->size();
if (static_cast<size_t>(bytes_written) != size) {
LOG(WARNING) << "Incomplete HID write: " << bytes_written
<< " != " << size;
}
callback.Run(true, bytes_written == 0 ? 0 : bytes_written - 1);
callback.Run(true);
}
}
void HidConnectionLinux::PlatformGetFeatureReport(
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) {
if (buffer->size() == 0) {
callback.Run(false, 0);
return;
}
// The first byte of the destination buffer is the report ID being requested.
const ReadCallback& callback) {
// The first byte of the destination buffer is the report ID being requested
// and is overwritten by the feature report.
DCHECK_GT(device_info().max_feature_report_size, 0);
scoped_refptr<net::IOBufferWithSize> buffer(
new net::IOBufferWithSize(device_info().max_feature_report_size));
buffer->data()[0] = report_id;
int result = ioctl(device_file_.GetPlatformFile(),
HIDIOCGFEATURE(buffer->size()),
buffer->data());
if (result < 0) {
VPLOG(1) << "Failed to get feature report";
callback.Run(false, 0);
callback.Run(false, NULL, 0);
} else {
callback.Run(true, result);
callback.Run(true, buffer, result);
}
}
void HidConnectionLinux::PlatformSendFeatureReport(
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) {
if (report_id != 0)
buffer = CopyBufferWithReportId(buffer, report_id);
int result = ioctl(device_file_.GetPlatformFile(),
HIDIOCSFEATURE(buffer->size()),
buffer->data());
scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback) {
// Linux expects the first byte of the buffer to always be a report ID so the
// buffer can be used directly.
int result = ioctl(
device_file_.GetPlatformFile(), HIDIOCSFEATURE(size), buffer->data());
if (result < 0) {
VPLOG(1) << "Failed to send feature report";
callback.Run(false, 0);
callback.Run(false);
} else {
callback.Run(true, result);
callback.Run(true);
}
}
......@@ -165,9 +144,19 @@ void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) {
DCHECK(thread_checker().CalledOnValidThread());
DCHECK_EQ(fd, device_file_.GetPlatformFile());
uint8 raw_buffer[1024] = {0};
int bytes_read =
HANDLE_EINTR(read(device_file_.GetPlatformFile(), raw_buffer, 1024));
size_t expected_report_size = device_info().max_input_report_size + 1;
scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(expected_report_size));
char* data = buffer->data();
if (!device_info().has_report_id) {
// Linux will not prefix the buffer with a report ID if they are not used
// by the device.
data[0] = 0;
data++;
expected_report_size--;
}
ssize_t bytes_read = HANDLE_EINTR(
read(device_file_.GetPlatformFile(), data, expected_report_size));
if (bytes_read < 0) {
if (errno == EAGAIN) {
return;
......@@ -176,12 +165,12 @@ void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) {
Disconnect();
return;
}
if (!device_info().has_report_id) {
// Include the byte prepended earlier.
bytes_read++;
}
scoped_refptr<net::IOBufferWithSize> buffer =
new net::IOBufferWithSize(bytes_read);
memcpy(buffer->data(), raw_buffer, bytes_read);
ProcessInputReport(buffer);
ProcessInputReport(buffer, bytes_read);
}
void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) {
......@@ -197,16 +186,17 @@ void HidConnectionLinux::Disconnect() {
void HidConnectionLinux::Flush() {
while (!pending_reads_.empty()) {
pending_reads_.front().callback.Run(false, 0);
pending_reads_.front().callback.Run(false, NULL, 0);
pending_reads_.pop();
}
}
void HidConnectionLinux::ProcessInputReport(
scoped_refptr<net::IOBufferWithSize> buffer) {
void HidConnectionLinux::ProcessInputReport(scoped_refptr<net::IOBuffer> buffer,
size_t size) {
DCHECK(thread_checker().CalledOnValidThread());
PendingHidReport report;
report.buffer = buffer;
report.size = size;
pending_reports_.push(report);
ProcessReadQueue();
}
......@@ -217,16 +207,9 @@ void HidConnectionLinux::ProcessReadQueue() {
PendingHidRead read = pending_reads_.front();
PendingHidReport report = pending_reports_.front();
if (read.buffer->size() < report.buffer->size()) {
read.callback.Run(false, 0);
pending_reports_.pop();
if (CompleteRead(report.buffer, report.size, read.callback)) {
pending_reads_.pop();
} else {
memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size());
pending_reports_.pop();
if (CompleteRead(read.buffer, report.buffer->size(), read.callback)) {
pending_reads_.pop();
}
}
}
}
......
......@@ -18,33 +18,30 @@ class HidConnectionLinux : public HidConnection,
public:
HidConnectionLinux(HidDeviceInfo device_info, std::string dev_node);
private:
friend class base::RefCountedThreadSafe<HidConnectionLinux>;
virtual ~HidConnectionLinux();
// HidConnection implementation.
virtual void PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) OVERRIDE;
virtual void PlatformWrite(uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) OVERRIDE;
virtual void PlatformGetFeatureReport(
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) OVERRIDE;
virtual void PlatformRead(const ReadCallback& callback) OVERRIDE;
virtual void PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback) OVERRIDE;
virtual void PlatformGetFeatureReport(uint8_t report_id,
const ReadCallback& callback) OVERRIDE;
virtual void PlatformSendFeatureReport(
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) OVERRIDE;
scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback) OVERRIDE;
// base::MessagePumpLibevent::Watcher implementation.
virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
private:
friend class base::RefCountedThreadSafe<HidConnectionLinux>;
virtual ~HidConnectionLinux();
void Disconnect();
void Flush();
void ProcessInputReport(scoped_refptr<net::IOBufferWithSize> buffer);
void ProcessInputReport(scoped_refptr<net::IOBuffer> buffer, size_t size);
void ProcessReadQueue();
base::File device_file_;
......
......@@ -21,7 +21,7 @@ HidConnectionMac::HidConnectionMac(HidDeviceInfo device_info)
if (device_info.has_report_id) {
expected_report_size++;
}
inbound_buffer_.reset((uint8_t*)malloc(expected_report_size));
inbound_buffer_.reset(new uint8_t[expected_report_size]);
IOHIDDeviceRegisterInputReportCallback(device_.get(),
inbound_buffer_.get(),
expected_report_size,
......@@ -35,54 +35,52 @@ HidConnectionMac::~HidConnectionMac() {
Flush();
}
void HidConnectionMac::PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) {
void HidConnectionMac::PlatformRead(const ReadCallback& callback) {
if (!device_) {
callback.Run(false, 0);
callback.Run(false, NULL, 0);
return;
}
PendingHidRead pending_read;
pending_read.buffer = buffer;
pending_read.callback = callback;
pending_reads_.push(pending_read);
ProcessReadQueue();
}
void HidConnectionMac::PlatformWrite(
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) {
WriteReport(kIOHIDReportTypeOutput, report_id, buffer, callback);
void HidConnectionMac::PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback) {
WriteReport(kIOHIDReportTypeOutput, buffer, size, callback);
}
void HidConnectionMac::PlatformGetFeatureReport(
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) {
void HidConnectionMac::PlatformGetFeatureReport(uint8_t report_id,
const ReadCallback& callback) {
if (!device_) {
callback.Run(false, 0);
callback.Run(false, NULL, 0);
return;
}
uint8_t* feature_report_buffer = reinterpret_cast<uint8_t*>(buffer->data());
scoped_refptr<net::IOBufferWithSize> buffer(
new net::IOBufferWithSize(device_info().max_feature_report_size));
CFIndex report_size = buffer->size();
IOReturn result = IOHIDDeviceGetReport(device_,
kIOHIDReportTypeFeature,
report_id,
feature_report_buffer,
&report_size);
if (result == kIOReturnSuccess)
callback.Run(true, report_size);
else
callback.Run(false, 0);
IOReturn result =
IOHIDDeviceGetReport(device_,
kIOHIDReportTypeFeature,
report_id,
reinterpret_cast<uint8_t*>(buffer->data()),
&report_size);
if (result == kIOReturnSuccess) {
callback.Run(true, buffer, report_size);
} else {
callback.Run(false, NULL, 0);
}
}
void HidConnectionMac::PlatformSendFeatureReport(
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) {
WriteReport(kIOHIDReportTypeFeature, report_id, buffer, callback);
scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback) {
WriteReport(kIOHIDReportTypeFeature, buffer, size, callback);
}
void HidConnectionMac::InputReportCallback(void* context,
......@@ -93,10 +91,16 @@ void HidConnectionMac::InputReportCallback(void* context,
uint8_t* report_bytes,
CFIndex report_length) {
HidConnectionMac* connection = static_cast<HidConnectionMac*>(context);
// report_id is already contained in report_bytes
scoped_refptr<net::IOBufferWithSize> buffer;
buffer = new net::IOBufferWithSize(report_length);
memcpy(buffer->data(), report_bytes, report_length);
if (connection->device_info().has_report_id) {
// report_id is already contained in report_bytes
buffer = new net::IOBufferWithSize(report_length);
memcpy(buffer->data(), report_bytes, report_length);
} else {
buffer = new net::IOBufferWithSize(report_length + 1);
buffer->data()[0] = 0;
memcpy(buffer->data() + 1, report_bytes, report_length);
}
connection->message_loop_->PostTask(
FROM_HERE,
......@@ -104,39 +108,36 @@ void HidConnectionMac::InputReportCallback(void* context,
}
void HidConnectionMac::WriteReport(IOHIDReportType type,
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) {
scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback) {
if (!device_) {
callback.Run(false, 0);
callback.Run(false);
return;
}
scoped_refptr<net::IOBufferWithSize> output_buffer;
if (report_id != 0) {
output_buffer = new net::IOBufferWithSize(buffer->size() + 1);
output_buffer->data()[0] = static_cast<uint8_t>(report_id);
memcpy(output_buffer->data() + 1, buffer->data(), buffer->size());
} else {
output_buffer = new net::IOBufferWithSize(buffer->size());
memcpy(output_buffer->data(), buffer->data(), buffer->size());
uint8_t* data = reinterpret_cast<uint8_t*>(buffer->data());
DCHECK(size >= 1);
uint8_t report_id = data[0];
if (report_id == 0) {
// OS X only expects the first byte of the buffer to be the report ID if the
// report ID is non-zero.
++data;
--size;
}
IOReturn res =
IOHIDDeviceSetReport(device_.get(),
type,
report_id,
reinterpret_cast<uint8_t*>(output_buffer->data()),
output_buffer->size());
if (res != kIOReturnSuccess) {
callback.Run(false, 0);
IOHIDDeviceSetReport(device_.get(), type, report_id, data, size);
if (res == kIOReturnSuccess) {
callback.Run(true);
} else {
callback.Run(true, output_buffer->size());
callback.Run(false);
}
}
void HidConnectionMac::Flush() {
while (!pending_reads_.empty()) {
pending_reads_.front().callback.Run(false, 0);
pending_reads_.front().callback.Run(false, NULL, 0);
pending_reads_.pop();
}
}
......@@ -146,6 +147,7 @@ void HidConnectionMac::ProcessInputReport(
DCHECK(thread_checker().CalledOnValidThread());
PendingHidReport report;
report.buffer = buffer;
report.size = buffer->size();
pending_reports_.push(report);
ProcessReadQueue();
}
......@@ -156,16 +158,9 @@ void HidConnectionMac::ProcessReadQueue() {
PendingHidRead read = pending_reads_.front();
PendingHidReport report = pending_reports_.front();
if (read.buffer->size() < report.buffer->size()) {
read.callback.Run(false, 0);
pending_reports_.pop();
if (CompleteRead(report.buffer, report.size, read.callback)) {
pending_reads_.pop();
} else {
memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size());
pending_reports_.pop();
if (CompleteRead(read.buffer, report.buffer->size(), read.callback)) {
pending_reads_.pop();
}
}
}
}
......
......@@ -28,24 +28,21 @@ class HidConnectionMac : public HidConnection {
public:
explicit HidConnectionMac(HidDeviceInfo device_info);
// HidConnection implementation.
virtual void PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) OVERRIDE;
virtual void PlatformWrite(uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) OVERRIDE;
virtual void PlatformGetFeatureReport(
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) OVERRIDE;
virtual void PlatformSendFeatureReport(
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) OVERRIDE;
private:
virtual ~HidConnectionMac();
// HidConnection implementation.
virtual void PlatformRead(const ReadCallback& callback) OVERRIDE;
virtual void PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback) OVERRIDE;
virtual void PlatformGetFeatureReport(uint8_t report_id,
const ReadCallback& callback) OVERRIDE;
virtual void PlatformSendFeatureReport(
scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback) OVERRIDE;
static void InputReportCallback(void* context,
IOReturn result,
void* sender,
......@@ -55,9 +52,9 @@ class HidConnectionMac : public HidConnection {
CFIndex report_length);
void WriteReport(IOHIDReportType type,
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback);
scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback);
void Flush();
void ProcessInputReport(scoped_refptr<net::IOBufferWithSize> buffer);
......@@ -65,7 +62,7 @@ class HidConnectionMac : public HidConnection {
base::ScopedCFTypeRef<IOHIDDeviceRef> device_;
scoped_refptr<base::MessageLoopProxy> message_loop_;
scoped_ptr<uint8_t, base::FreeDeleter> inbound_buffer_;
scoped_ptr<uint8_t[]> inbound_buffer_;
std::queue<PendingHidReport> pending_reports_;
std::queue<PendingHidRead> pending_reads_;
......
......@@ -24,12 +24,23 @@ using net::IOBufferWithSize;
class TestCompletionCallback {
public:
TestCompletionCallback()
: callback_(base::Bind(&TestCompletionCallback::SetResult,
base::Unretained(this))) {}
: read_callback_(base::Bind(&TestCompletionCallback::SetReadResult,
base::Unretained(this))),
write_callback_(base::Bind(&TestCompletionCallback::SetWriteResult,
base::Unretained(this))) {}
~TestCompletionCallback() {}
void SetReadResult(bool success,
scoped_refptr<net::IOBuffer> buffer,
size_t size) {
result_ = success;
buffer_ = buffer;
size_ = size;
run_loop_.Quit();
}
void SetResult(bool success, size_t size) {
void SetWriteResult(bool success) {
result_ = success;
transferred_ = size;
run_loop_.Quit();
}
......@@ -38,14 +49,20 @@ class TestCompletionCallback {
return result_;
}
const HidConnection::IOCallback& callback() const { return callback_; }
size_t transferred() const { return transferred_; }
const HidConnection::ReadCallback& read_callback() { return read_callback_; }
const HidConnection::WriteCallback write_callback() {
return write_callback_;
}
scoped_refptr<net::IOBuffer> buffer() const { return buffer_; }
size_t size() const { return size_; }
private:
const HidConnection::IOCallback callback_;
base::RunLoop run_loop_;
bool result_;
size_t transferred_;
size_t size_;
scoped_refptr<net::IOBuffer> buffer_;
HidConnection::ReadCallback read_callback_;
HidConnection::WriteCallback write_callback_;
};
} // namespace
......@@ -113,20 +130,24 @@ TEST_F(HidConnectionTest, ReadWrite) {
ASSERT_TRUE(conn);
for (int i = 0; i < 8; ++i) {
scoped_refptr<IOBufferWithSize> write_buffer(new IOBufferWithSize(8));
*(int64_t*)write_buffer->data() = i;
scoped_refptr<IOBufferWithSize> buffer(new IOBufferWithSize(9));
buffer->data()[0] = 0;
for (int j = 1; j < buffer->size(); ++j) {
buffer->data()[j] = i + j - 1;
}
TestCompletionCallback write_callback;
conn->Write(0, write_buffer, write_callback.callback());
conn->Write(buffer, buffer->size(), write_callback.write_callback());
ASSERT_TRUE(write_callback.WaitForResult());
ASSERT_EQ(8UL, write_callback.transferred());
scoped_refptr<IOBufferWithSize> read_buffer(new IOBufferWithSize(8));
TestCompletionCallback read_callback;
conn->Read(read_buffer, read_callback.callback());
conn->Read(read_callback.read_callback());
ASSERT_TRUE(read_callback.WaitForResult());
ASSERT_EQ(8UL, read_callback.transferred());
ASSERT_EQ(i, *(int64_t*)read_buffer->data());
ASSERT_EQ(9UL, read_callback.size());
ASSERT_EQ(0, read_callback.buffer()->data()[0]);
for (int j = 1; j < buffer->size(); ++j) {
ASSERT_EQ(i + j - 1, read_callback.buffer()->data()[j]);
}
}
}
......
This diff is collapsed.
......@@ -20,31 +20,37 @@ class HidConnectionWin : public HidConnection {
public:
explicit HidConnectionWin(const HidDeviceInfo& device_info);
// HidConnection implementation.
virtual void PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) OVERRIDE;
virtual void PlatformWrite(uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) OVERRIDE;
virtual void PlatformGetFeatureReport(
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) OVERRIDE;
virtual void PlatformSendFeatureReport(
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) OVERRIDE;
private:
friend class HidServiceWin;
friend struct PendingHidTransfer;
~HidConnectionWin();
// HidConnection implementation.
virtual void PlatformRead(const ReadCallback& callback) OVERRIDE;
virtual void PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback) OVERRIDE;
virtual void PlatformGetFeatureReport(uint8_t report_id,
const ReadCallback& callback) OVERRIDE;
virtual void PlatformSendFeatureReport(
scoped_refptr<net::IOBuffer> buffer,
size_t size,
const WriteCallback& callback) OVERRIDE;
bool available() const { return file_.IsValid(); }
void OnTransferFinished(scoped_refptr<PendingHidTransfer> transfer);
void OnTransferCanceled(scoped_refptr<PendingHidTransfer> transfer);
void OnReadComplete(scoped_refptr<net::IOBuffer> buffer,
const ReadCallback& callback,
PendingHidTransfer* transfer,
bool signaled);
void OnReadFeatureComplete(scoped_refptr<net::IOBuffer> buffer,
const ReadCallback& callback,
PendingHidTransfer* transfer,
bool signaled);
void OnWriteComplete(const WriteCallback& callback,
PendingHidTransfer* transfer,
bool signaled);
base::win::ScopedHandle file_;
......
......@@ -183,36 +183,24 @@ void HidReceiveFunction::AsyncWorkStart() {
}
scoped_refptr<device::HidConnection> connection = resource->connection();
has_report_id_ = connection->device_info().has_report_id;
int size = connection->device_info().max_input_report_size;
if (has_report_id_) {
++size; // One byte at the beginning of the buffer for the report ID.
}
buffer_ = new net::IOBufferWithSize(size);
connection->Read(buffer_, base::Bind(&HidReceiveFunction::OnFinished, this));
connection->Read(base::Bind(&HidReceiveFunction::OnFinished, this));
}
void HidReceiveFunction::OnFinished(bool success, size_t bytes) {
void HidReceiveFunction::OnFinished(bool success,
scoped_refptr<net::IOBuffer> buffer,
size_t size) {
if (!success) {
CompleteWithError(kErrorTransfer);
return;
}
int report_id = 0;
const char* data = buffer_->data();
if (has_report_id_) {
if (bytes < 1) {
CompleteWithError(kErrorTransfer);
return;
}
report_id = data[0];
data++;
bytes--;
}
DCHECK_GE(size, 1u);
int report_id = reinterpret_cast<uint8_t*>(buffer->data())[0];
scoped_ptr<base::ListValue> result(new base::ListValue());
result->Append(new base::FundamentalValue(report_id));
result->Append(base::BinaryValue::CreateWithCopiedBuffer(data, bytes));
result->Append(
base::BinaryValue::CreateWithCopiedBuffer(buffer->data() + 1, size - 1));
SetResultList(result.Pass());
AsyncWorkCompleted();
}
......@@ -237,14 +225,15 @@ void HidSendFunction::AsyncWorkStart() {
}
scoped_refptr<net::IOBufferWithSize> buffer(
new net::IOBufferWithSize(parameters_->data.size()));
memcpy(buffer->data(), parameters_->data.c_str(), parameters_->data.size());
resource->connection()->Write(static_cast<uint8_t>(parameters_->report_id),
buffer,
base::Bind(&HidSendFunction::OnFinished, this));
new net::IOBufferWithSize(parameters_->data.size() + 1));
buffer->data()[0] = static_cast<uint8_t>(parameters_->report_id);
memcpy(
buffer->data() + 1, parameters_->data.c_str(), parameters_->data.size());
resource->connection()->Write(
buffer, buffer->size(), base::Bind(&HidSendFunction::OnFinished, this));
}
void HidSendFunction::OnFinished(bool success, size_t bytes) {
void HidSendFunction::OnFinished(bool success) {
if (!success) {
CompleteWithError(kErrorTransfer);
return;
......@@ -272,21 +261,21 @@ void HidReceiveFeatureReportFunction::AsyncWorkStart() {
}
scoped_refptr<device::HidConnection> connection = resource->connection();
const int size = connection->device_info().max_feature_report_size;
buffer_ = new net::IOBufferWithSize(size);
connection->GetFeatureReport(
static_cast<uint8_t>(parameters_->report_id),
buffer_,
base::Bind(&HidReceiveFeatureReportFunction::OnFinished, this));
}
void HidReceiveFeatureReportFunction::OnFinished(bool success, size_t bytes) {
void HidReceiveFeatureReportFunction::OnFinished(
bool success,
scoped_refptr<net::IOBuffer> buffer,
size_t size) {
if (!success) {
CompleteWithError(kErrorTransfer);
return;
}
SetResult(base::BinaryValue::CreateWithCopiedBuffer(buffer_->data(), bytes));
SetResult(base::BinaryValue::CreateWithCopiedBuffer(buffer->data(), size));
AsyncWorkCompleted();
}
......@@ -308,16 +297,19 @@ void HidSendFeatureReportFunction::AsyncWorkStart() {
CompleteWithError(kErrorConnectionNotFound);
return;
}
scoped_refptr<net::IOBufferWithSize> buffer(
new net::IOBufferWithSize(parameters_->data.size()));
memcpy(buffer->data(), parameters_->data.c_str(), parameters_->data.size());
new net::IOBufferWithSize(parameters_->data.size() + 1));
buffer->data()[0] = static_cast<uint8_t>(parameters_->report_id);
memcpy(
buffer->data() + 1, parameters_->data.c_str(), parameters_->data.size());
resource->connection()->SendFeatureReport(
static_cast<uint8_t>(parameters_->report_id),
buffer,
buffer->size(),
base::Bind(&HidSendFeatureReportFunction::OnFinished, this));
}
void HidSendFeatureReportFunction::OnFinished(bool success, size_t bytes) {
void HidSendFeatureReportFunction::OnFinished(bool success) {
if (!success) {
CompleteWithError(kErrorTransfer);
return;
......
......@@ -112,10 +112,10 @@ class HidReceiveFunction : public HidAsyncApiFunction {
private:
virtual ~HidReceiveFunction();
void OnFinished(bool success, size_t bytes);
void OnFinished(bool success,
scoped_refptr<net::IOBuffer> buffer,
size_t size);
bool has_report_id_;
scoped_refptr<net::IOBufferWithSize> buffer_;
scoped_ptr<core_api::hid::Receive::Params> parameters_;
DISALLOW_COPY_AND_ASSIGN(HidReceiveFunction);
......@@ -134,7 +134,7 @@ class HidSendFunction : public HidAsyncApiFunction {
private:
virtual ~HidSendFunction();
void OnFinished(bool success, size_t bytes);
void OnFinished(bool success);
scoped_ptr<core_api::hid::Send::Params> parameters_;
......@@ -155,9 +155,10 @@ class HidReceiveFeatureReportFunction : public HidAsyncApiFunction {
private:
virtual ~HidReceiveFeatureReportFunction();
void OnFinished(bool success, size_t bytes);
void OnFinished(bool success,
scoped_refptr<net::IOBuffer> buffer,
size_t size);
scoped_refptr<net::IOBufferWithSize> buffer_;
scoped_ptr<core_api::hid::ReceiveFeatureReport::Params> parameters_;
DISALLOW_COPY_AND_ASSIGN(HidReceiveFeatureReportFunction);
......@@ -176,7 +177,7 @@ class HidSendFeatureReportFunction : public HidAsyncApiFunction {
private:
virtual ~HidSendFeatureReportFunction();
void OnFinished(bool success, size_t bytes);
void OnFinished(bool success);
scoped_ptr<core_api::hid::SendFeatureReport::Params> parameters_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment