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 { ...@@ -12,8 +12,7 @@ namespace {
// Functor used to filter collections by report ID. // Functor used to filter collections by report ID.
struct CollectionHasReportId { struct CollectionHasReportId {
explicit CollectionHasReportId(const uint8_t report_id) explicit CollectionHasReportId(uint8_t report_id) : report_id_(report_id) {}
: report_id_(report_id) {}
bool operator()(const HidCollectionInfo& info) const { bool operator()(const HidCollectionInfo& info) const {
if (info.report_ids.size() == 0 || if (info.report_ids.size() == 0 ||
...@@ -40,7 +39,7 @@ struct CollectionIsProtected { ...@@ -40,7 +39,7 @@ struct CollectionIsProtected {
}; };
bool FindCollectionByReportId(const HidDeviceInfo& device_info, bool FindCollectionByReportId(const HidDeviceInfo& device_info,
const uint8_t report_id, uint8_t report_id,
HidCollectionInfo* collection_info) { HidCollectionInfo* collection_info) {
std::vector<HidCollectionInfo>::const_iterator collection_iter = std::vector<HidCollectionInfo>::const_iterator collection_iter =
std::find_if(device_info.collections.begin(), std::find_if(device_info.collections.begin(),
...@@ -73,103 +72,104 @@ HidConnection::~HidConnection() { ...@@ -73,103 +72,104 @@ HidConnection::~HidConnection() {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
} }
void HidConnection::Read(scoped_refptr<net::IOBufferWithSize> buffer, void HidConnection::Read(const ReadCallback& callback) {
const IOCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
if (device_info_.max_input_report_size == 0) { if (device_info_.max_input_report_size == 0) {
// The device does not support input reports. VLOG(1) << "This device does not support input reports.";
callback.Run(false, 0); callback.Run(false, NULL, 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);
return; return;
} }
PlatformRead(buffer, callback); PlatformRead(callback);
} }
void HidConnection::Write(uint8_t report_id, void HidConnection::Write(scoped_refptr<net::IOBuffer> buffer,
scoped_refptr<net::IOBufferWithSize> buffer, size_t size,
const IOCallback& callback) { const WriteCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
if (device_info_.max_output_report_size == 0) { if (device_info_.max_output_report_size == 0) {
// The device does not support output reports. VLOG(1) << "This device does not support output reports.";
callback.Run(false, 0); 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; return;
} }
if (IsReportIdProtected(report_id)) { if (IsReportIdProtected(report_id)) {
callback.Run(false, 0); VLOG(1) << "Attempt to set a protected output report.";
callback.Run(false);
return; return;
} }
PlatformWrite(report_id, buffer, callback); PlatformWrite(buffer, size, callback);
} }
void HidConnection::GetFeatureReport( void HidConnection::GetFeatureReport(uint8_t report_id,
uint8_t report_id, const ReadCallback& callback) {
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
if (device_info_.max_feature_report_size == 0) { if (device_info_.max_feature_report_size == 0) {
// The device does not support feature reports. VLOG(1) << "This device does not support feature reports.";
callback.Run(false, 0); callback.Run(false, NULL, 0);
return; return;
} }
if (IsReportIdProtected(report_id)) { if (device_info().has_report_id != (report_id != 0)) {
callback.Run(false, 0); VLOG(1) << "Invalid feature report ID.";
callback.Run(false, NULL, 0);
return; return;
} }
int expected_buffer_size = device_info_.max_feature_report_size; if (IsReportIdProtected(report_id)) {
if (device_info().has_report_id) { VLOG(1) << "Attempt to get a protected feature report.";
expected_buffer_size++; callback.Run(false, NULL, 0);
}
if (buffer->size() < expected_buffer_size) {
// Receive buffer is too small.
callback.Run(false, 0);
return; return;
} }
PlatformGetFeatureReport(report_id, buffer, callback); PlatformGetFeatureReport(report_id, callback);
} }
void HidConnection::SendFeatureReport( void HidConnection::SendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
uint8_t report_id, size_t size,
scoped_refptr<net::IOBufferWithSize> buffer, const WriteCallback& callback) {
const IOCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
if (device_info_.max_feature_report_size == 0) { if (device_info_.max_feature_report_size == 0) {
// The device does not support feature reports. VLOG(1) << "This device does not support feature reports.";
callback.Run(false, 0); 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; return;
} }
if (IsReportIdProtected(report_id)) { if (IsReportIdProtected(report_id)) {
callback.Run(false, 0); VLOG(1) << "Attempt to set a protected feature report.";
callback.Run(false);
return; return;
} }
PlatformSendFeatureReport(report_id, buffer, callback); PlatformSendFeatureReport(buffer, size, callback);
} }
bool HidConnection::CompleteRead(scoped_refptr<net::IOBufferWithSize> buffer, bool HidConnection::CompleteRead(scoped_refptr<net::IOBuffer> buffer,
int bytes_read, size_t size,
const IOCallback& callback) { const ReadCallback& callback) {
DCHECK_LE(bytes_read, buffer->size()); DCHECK_GE(size, 1u);
uint8_t report_id = buffer->data()[0];
if (bytes_read == 0 || IsReportIdProtected(buffer->data()[0])) { if (IsReportIdProtected(report_id)) {
VLOG(1) << "Filtered a protected input report.";
return false; return false;
} }
callback.Run(true, bytes_read); callback.Run(true, buffer, size);
return true; return true;
} }
bool HidConnection::IsReportIdProtected(const uint8_t report_id) { bool HidConnection::IsReportIdProtected(uint8_t report_id) {
HidCollectionInfo collection_info; HidCollectionInfo collection_info;
if (FindCollectionByReportId(device_info_, report_id, &collection_info)) { if (FindCollectionByReportId(device_info_, report_id, &collection_info)) {
return collection_info.usage.IsProtected(); return collection_info.usage.IsProtected();
......
...@@ -22,23 +22,33 @@ class HidConnection : public base::RefCountedThreadSafe<HidConnection> { ...@@ -22,23 +22,33 @@ class HidConnection : public base::RefCountedThreadSafe<HidConnection> {
kAnyReportId = 0xFF, 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_; } const HidDeviceInfo& device_info() const { return device_info_; }
bool has_protected_collection() const { return has_protected_collection_; } bool has_protected_collection() const { return has_protected_collection_; }
const base::ThreadChecker& thread_checker() const { return thread_checker_; } const base::ThreadChecker& thread_checker() const { return thread_checker_; }
void Read(scoped_refptr<net::IOBufferWithSize> buffer, // The report ID (or 0 if report IDs are not supported by the device) is
const IOCallback& callback); // always returned in the first byte of the buffer.
void Write(uint8_t report_id, void Read(const ReadCallback& callback);
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback); // The report ID (or 0 if report IDs are not supported by the device) is
void GetFeatureReport(uint8_t report_id, // always expected in the first byte of the buffer.
scoped_refptr<net::IOBufferWithSize> buffer, void Write(scoped_refptr<net::IOBuffer> buffer,
const IOCallback& callback); size_t size,
void SendFeatureReport(uint8_t report_id, const WriteCallback& callback);
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& 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: protected:
friend class base::RefCountedThreadSafe<HidConnection>; friend class base::RefCountedThreadSafe<HidConnection>;
...@@ -46,31 +56,27 @@ class HidConnection : public base::RefCountedThreadSafe<HidConnection> { ...@@ -46,31 +56,27 @@ class HidConnection : public base::RefCountedThreadSafe<HidConnection> {
explicit HidConnection(const HidDeviceInfo& device_info); explicit HidConnection(const HidDeviceInfo& device_info);
virtual ~HidConnection(); virtual ~HidConnection();
virtual void PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer, virtual void PlatformRead(const ReadCallback& callback) = 0;
const IOCallback& callback) = 0; virtual void PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
virtual void PlatformWrite(uint8_t report_id, size_t size,
scoped_refptr<net::IOBufferWithSize> buffer, const WriteCallback& callback) = 0;
const IOCallback& callback) = 0; virtual void PlatformGetFeatureReport(uint8_t report_id,
virtual void PlatformGetFeatureReport( const ReadCallback& callback) = 0;
uint8_t report_id, virtual void PlatformSendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
scoped_refptr<net::IOBufferWithSize> buffer, size_t size,
const IOCallback& callback) = 0; const WriteCallback& callback) = 0;
virtual void PlatformSendFeatureReport(
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) = 0;
// PlatformRead implementation must call this method on read // PlatformRead implementation must call this method on read
// success, rather than directly running the callback. // success, rather than directly running the callback.
// In case incoming buffer is empty or protected, it is filtered // In case incoming buffer is empty or protected, it is filtered
// and this method returns false. Otherwise it runs the callback // and this method returns false. Otherwise it runs the callback
// and returns true. // and returns true.
bool CompleteRead(scoped_refptr<net::IOBufferWithSize> buffer, bool CompleteRead(scoped_refptr<net::IOBuffer> buffer,
int bytes_read, size_t size,
const IOCallback& callback); const ReadCallback& callback);
private: private:
bool IsReportIdProtected(const uint8_t report_id); bool IsReportIdProtected(uint8_t report_id);
const HidDeviceInfo device_info_; const HidDeviceInfo device_info_;
bool has_protected_collection_; bool has_protected_collection_;
...@@ -83,15 +89,15 @@ struct PendingHidReport { ...@@ -83,15 +89,15 @@ struct PendingHidReport {
PendingHidReport(); PendingHidReport();
~PendingHidReport(); ~PendingHidReport();
scoped_refptr<net::IOBufferWithSize> buffer; scoped_refptr<net::IOBuffer> buffer;
size_t size;
}; };
struct PendingHidRead { struct PendingHidRead {
PendingHidRead(); PendingHidRead();
~PendingHidRead(); ~PendingHidRead();
scoped_refptr<net::IOBufferWithSize> buffer; HidConnection::ReadCallback callback;
HidConnection::IOCallback callback;
}; };
} // namespace device } // namespace device
......
...@@ -29,21 +29,6 @@ ...@@ -29,21 +29,6 @@
namespace device { 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, HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info,
std::string dev_node) std::string dev_node)
: HidConnection(device_info) { : HidConnection(device_info) {
...@@ -91,73 +76,67 @@ HidConnectionLinux::~HidConnectionLinux() { ...@@ -91,73 +76,67 @@ HidConnectionLinux::~HidConnectionLinux() {
Flush(); Flush();
} }
void HidConnectionLinux::PlatformRead( void HidConnectionLinux::PlatformRead(const ReadCallback& callback) {
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) {
PendingHidRead pending_read; PendingHidRead pending_read;
pending_read.buffer = buffer;
pending_read.callback = callback; pending_read.callback = callback;
pending_reads_.push(pending_read); pending_reads_.push(pending_read);
ProcessReadQueue(); ProcessReadQueue();
} }
void HidConnectionLinux::PlatformWrite( void HidConnectionLinux::PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
uint8_t report_id, size_t size,
scoped_refptr<net::IOBufferWithSize> buffer, const WriteCallback& callback) {
const IOCallback& callback) { // Linux expects the first byte of the buffer to always be a report ID so the
// Linux always expects the first byte of the buffer to be the report ID. // buffer can be used directly.
buffer = CopyBufferWithReportId(buffer, report_id); const ssize_t bytes_written =
const int bytes_written = HANDLE_EINTR( HANDLE_EINTR(write(device_file_.GetPlatformFile(), buffer->data(), size));
write(device_file_.GetPlatformFile(), buffer->data(), buffer->size()));
if (bytes_written < 0) { if (bytes_written < 0) {
VPLOG(1) << "Write failed"; VPLOG(1) << "Write failed";
Disconnect(); Disconnect();
callback.Run(false, 0); callback.Run(false);
} else { } else {
if (bytes_written != buffer->size()) { if (static_cast<size_t>(bytes_written) != size) {
LOG(WARNING) << "Incomplete HID write: " LOG(WARNING) << "Incomplete HID write: " << bytes_written
<< bytes_written << " != " << buffer->size(); << " != " << size;
} }
callback.Run(true, bytes_written == 0 ? 0 : bytes_written - 1); callback.Run(true);
} }
} }
void HidConnectionLinux::PlatformGetFeatureReport( void HidConnectionLinux::PlatformGetFeatureReport(
uint8_t report_id, uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer, const ReadCallback& callback) {
const IOCallback& callback) { // The first byte of the destination buffer is the report ID being requested
if (buffer->size() == 0) { // and is overwritten by the feature report.
callback.Run(false, 0); DCHECK_GT(device_info().max_feature_report_size, 0);
return; scoped_refptr<net::IOBufferWithSize> buffer(
} new net::IOBufferWithSize(device_info().max_feature_report_size));
// The first byte of the destination buffer is the report ID being requested.
buffer->data()[0] = report_id; buffer->data()[0] = report_id;
int result = ioctl(device_file_.GetPlatformFile(), int result = ioctl(device_file_.GetPlatformFile(),
HIDIOCGFEATURE(buffer->size()), HIDIOCGFEATURE(buffer->size()),
buffer->data()); buffer->data());
if (result < 0) { if (result < 0) {
VPLOG(1) << "Failed to get feature report"; VPLOG(1) << "Failed to get feature report";
callback.Run(false, 0); callback.Run(false, NULL, 0);
} else { } else {
callback.Run(true, result); callback.Run(true, buffer, result);
} }
} }
void HidConnectionLinux::PlatformSendFeatureReport( void HidConnectionLinux::PlatformSendFeatureReport(
uint8_t report_id, scoped_refptr<net::IOBuffer> buffer,
scoped_refptr<net::IOBufferWithSize> buffer, size_t size,
const IOCallback& callback) { const WriteCallback& callback) {
if (report_id != 0) // Linux expects the first byte of the buffer to always be a report ID so the
buffer = CopyBufferWithReportId(buffer, report_id); // buffer can be used directly.
int result = ioctl(device_file_.GetPlatformFile(), int result = ioctl(
HIDIOCSFEATURE(buffer->size()), device_file_.GetPlatformFile(), HIDIOCSFEATURE(size), buffer->data());
buffer->data());
if (result < 0) { if (result < 0) {
VPLOG(1) << "Failed to send feature report"; VPLOG(1) << "Failed to send feature report";
callback.Run(false, 0); callback.Run(false);
} else { } else {
callback.Run(true, result); callback.Run(true);
} }
} }
...@@ -165,9 +144,19 @@ void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) { ...@@ -165,9 +144,19 @@ void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) {
DCHECK(thread_checker().CalledOnValidThread()); DCHECK(thread_checker().CalledOnValidThread());
DCHECK_EQ(fd, device_file_.GetPlatformFile()); DCHECK_EQ(fd, device_file_.GetPlatformFile());
uint8 raw_buffer[1024] = {0}; size_t expected_report_size = device_info().max_input_report_size + 1;
int bytes_read = scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(expected_report_size));
HANDLE_EINTR(read(device_file_.GetPlatformFile(), raw_buffer, 1024)); 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 (bytes_read < 0) {
if (errno == EAGAIN) { if (errno == EAGAIN) {
return; return;
...@@ -176,12 +165,12 @@ void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) { ...@@ -176,12 +165,12 @@ void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) {
Disconnect(); Disconnect();
return; return;
} }
if (!device_info().has_report_id) {
// Include the byte prepended earlier.
bytes_read++;
}
scoped_refptr<net::IOBufferWithSize> buffer = ProcessInputReport(buffer, bytes_read);
new net::IOBufferWithSize(bytes_read);
memcpy(buffer->data(), raw_buffer, bytes_read);
ProcessInputReport(buffer);
} }
void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) { void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) {
...@@ -197,16 +186,17 @@ void HidConnectionLinux::Disconnect() { ...@@ -197,16 +186,17 @@ void HidConnectionLinux::Disconnect() {
void HidConnectionLinux::Flush() { void HidConnectionLinux::Flush() {
while (!pending_reads_.empty()) { while (!pending_reads_.empty()) {
pending_reads_.front().callback.Run(false, 0); pending_reads_.front().callback.Run(false, NULL, 0);
pending_reads_.pop(); pending_reads_.pop();
} }
} }
void HidConnectionLinux::ProcessInputReport( void HidConnectionLinux::ProcessInputReport(scoped_refptr<net::IOBuffer> buffer,
scoped_refptr<net::IOBufferWithSize> buffer) { size_t size) {
DCHECK(thread_checker().CalledOnValidThread()); DCHECK(thread_checker().CalledOnValidThread());
PendingHidReport report; PendingHidReport report;
report.buffer = buffer; report.buffer = buffer;
report.size = size;
pending_reports_.push(report); pending_reports_.push(report);
ProcessReadQueue(); ProcessReadQueue();
} }
...@@ -217,16 +207,9 @@ void HidConnectionLinux::ProcessReadQueue() { ...@@ -217,16 +207,9 @@ void HidConnectionLinux::ProcessReadQueue() {
PendingHidRead read = pending_reads_.front(); PendingHidRead read = pending_reads_.front();
PendingHidReport report = pending_reports_.front(); PendingHidReport report = pending_reports_.front();
if (read.buffer->size() < report.buffer->size()) { pending_reports_.pop();
read.callback.Run(false, 0); if (CompleteRead(report.buffer, report.size, read.callback)) {
pending_reads_.pop(); 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, ...@@ -18,33 +18,30 @@ class HidConnectionLinux : public HidConnection,
public: public:
HidConnectionLinux(HidDeviceInfo device_info, std::string dev_node); HidConnectionLinux(HidDeviceInfo device_info, std::string dev_node);
private:
friend class base::RefCountedThreadSafe<HidConnectionLinux>;
virtual ~HidConnectionLinux();
// HidConnection implementation. // HidConnection implementation.
virtual void PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer, virtual void PlatformRead(const ReadCallback& callback) OVERRIDE;
const IOCallback& callback) OVERRIDE; virtual void PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
virtual void PlatformWrite(uint8_t report_id, size_t size,
scoped_refptr<net::IOBufferWithSize> buffer, const WriteCallback& callback) OVERRIDE;
const IOCallback& callback) OVERRIDE; virtual void PlatformGetFeatureReport(uint8_t report_id,
virtual void PlatformGetFeatureReport( const ReadCallback& callback) OVERRIDE;
uint8_t report_id,
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) OVERRIDE;
virtual void PlatformSendFeatureReport( virtual void PlatformSendFeatureReport(
uint8_t report_id, scoped_refptr<net::IOBuffer> buffer,
scoped_refptr<net::IOBufferWithSize> buffer, size_t size,
const IOCallback& callback) OVERRIDE; const WriteCallback& callback) OVERRIDE;
// base::MessagePumpLibevent::Watcher implementation. // base::MessagePumpLibevent::Watcher implementation.
virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE; virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
private:
friend class base::RefCountedThreadSafe<HidConnectionLinux>;
virtual ~HidConnectionLinux();
void Disconnect(); void Disconnect();
void Flush(); void Flush();
void ProcessInputReport(scoped_refptr<net::IOBufferWithSize> buffer); void ProcessInputReport(scoped_refptr<net::IOBuffer> buffer, size_t size);
void ProcessReadQueue(); void ProcessReadQueue();
base::File device_file_; base::File device_file_;
......
...@@ -21,7 +21,7 @@ HidConnectionMac::HidConnectionMac(HidDeviceInfo device_info) ...@@ -21,7 +21,7 @@ HidConnectionMac::HidConnectionMac(HidDeviceInfo device_info)
if (device_info.has_report_id) { if (device_info.has_report_id) {
expected_report_size++; expected_report_size++;
} }
inbound_buffer_.reset((uint8_t*)malloc(expected_report_size)); inbound_buffer_.reset(new uint8_t[expected_report_size]);
IOHIDDeviceRegisterInputReportCallback(device_.get(), IOHIDDeviceRegisterInputReportCallback(device_.get(),
inbound_buffer_.get(), inbound_buffer_.get(),
expected_report_size, expected_report_size,
...@@ -35,54 +35,52 @@ HidConnectionMac::~HidConnectionMac() { ...@@ -35,54 +35,52 @@ HidConnectionMac::~HidConnectionMac() {
Flush(); Flush();
} }
void HidConnectionMac::PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer, void HidConnectionMac::PlatformRead(const ReadCallback& callback) {
const IOCallback& callback) {
if (!device_) { if (!device_) {
callback.Run(false, 0); callback.Run(false, NULL, 0);
return; return;
} }
PendingHidRead pending_read; PendingHidRead pending_read;
pending_read.buffer = buffer;
pending_read.callback = callback; pending_read.callback = callback;
pending_reads_.push(pending_read); pending_reads_.push(pending_read);
ProcessReadQueue(); ProcessReadQueue();
} }
void HidConnectionMac::PlatformWrite( void HidConnectionMac::PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
uint8_t report_id, size_t size,
scoped_refptr<net::IOBufferWithSize> buffer, const WriteCallback& callback) {
const IOCallback& callback) { WriteReport(kIOHIDReportTypeOutput, buffer, size, callback);
WriteReport(kIOHIDReportTypeOutput, report_id, buffer, callback);
} }
void HidConnectionMac::PlatformGetFeatureReport( void HidConnectionMac::PlatformGetFeatureReport(uint8_t report_id,
uint8_t report_id, const ReadCallback& callback) {
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) {
if (!device_) { if (!device_) {
callback.Run(false, 0); callback.Run(false, NULL, 0);
return; 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(); CFIndex report_size = buffer->size();
IOReturn result = IOHIDDeviceGetReport(device_, IOReturn result =
kIOHIDReportTypeFeature, IOHIDDeviceGetReport(device_,
report_id, kIOHIDReportTypeFeature,
feature_report_buffer, report_id,
&report_size); reinterpret_cast<uint8_t*>(buffer->data()),
if (result == kIOReturnSuccess) &report_size);
callback.Run(true, report_size); if (result == kIOReturnSuccess) {
else callback.Run(true, buffer, report_size);
callback.Run(false, 0); } else {
callback.Run(false, NULL, 0);
}
} }
void HidConnectionMac::PlatformSendFeatureReport( void HidConnectionMac::PlatformSendFeatureReport(
uint8_t report_id, scoped_refptr<net::IOBuffer> buffer,
scoped_refptr<net::IOBufferWithSize> buffer, size_t size,
const IOCallback& callback) { const WriteCallback& callback) {
WriteReport(kIOHIDReportTypeFeature, report_id, buffer, callback); WriteReport(kIOHIDReportTypeFeature, buffer, size, callback);
} }
void HidConnectionMac::InputReportCallback(void* context, void HidConnectionMac::InputReportCallback(void* context,
...@@ -93,10 +91,16 @@ void HidConnectionMac::InputReportCallback(void* context, ...@@ -93,10 +91,16 @@ void HidConnectionMac::InputReportCallback(void* context,
uint8_t* report_bytes, uint8_t* report_bytes,
CFIndex report_length) { CFIndex report_length) {
HidConnectionMac* connection = static_cast<HidConnectionMac*>(context); HidConnectionMac* connection = static_cast<HidConnectionMac*>(context);
// report_id is already contained in report_bytes
scoped_refptr<net::IOBufferWithSize> buffer; scoped_refptr<net::IOBufferWithSize> buffer;
buffer = new net::IOBufferWithSize(report_length); if (connection->device_info().has_report_id) {
memcpy(buffer->data(), report_bytes, report_length); // 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( connection->message_loop_->PostTask(
FROM_HERE, FROM_HERE,
...@@ -104,39 +108,36 @@ void HidConnectionMac::InputReportCallback(void* context, ...@@ -104,39 +108,36 @@ void HidConnectionMac::InputReportCallback(void* context,
} }
void HidConnectionMac::WriteReport(IOHIDReportType type, void HidConnectionMac::WriteReport(IOHIDReportType type,
uint8_t report_id, scoped_refptr<net::IOBuffer> buffer,
scoped_refptr<net::IOBufferWithSize> buffer, size_t size,
const IOCallback& callback) { const WriteCallback& callback) {
if (!device_) { if (!device_) {
callback.Run(false, 0); callback.Run(false);
return; return;
} }
scoped_refptr<net::IOBufferWithSize> output_buffer; uint8_t* data = reinterpret_cast<uint8_t*>(buffer->data());
if (report_id != 0) { DCHECK(size >= 1);
output_buffer = new net::IOBufferWithSize(buffer->size() + 1); uint8_t report_id = data[0];
output_buffer->data()[0] = static_cast<uint8_t>(report_id); if (report_id == 0) {
memcpy(output_buffer->data() + 1, buffer->data(), buffer->size()); // OS X only expects the first byte of the buffer to be the report ID if the
} else { // report ID is non-zero.
output_buffer = new net::IOBufferWithSize(buffer->size()); ++data;
memcpy(output_buffer->data(), buffer->data(), buffer->size()); --size;
} }
IOReturn res = IOReturn res =
IOHIDDeviceSetReport(device_.get(), IOHIDDeviceSetReport(device_.get(), type, report_id, data, size);
type, if (res == kIOReturnSuccess) {
report_id, callback.Run(true);
reinterpret_cast<uint8_t*>(output_buffer->data()),
output_buffer->size());
if (res != kIOReturnSuccess) {
callback.Run(false, 0);
} else { } else {
callback.Run(true, output_buffer->size()); callback.Run(false);
} }
} }
void HidConnectionMac::Flush() { void HidConnectionMac::Flush() {
while (!pending_reads_.empty()) { while (!pending_reads_.empty()) {
pending_reads_.front().callback.Run(false, 0); pending_reads_.front().callback.Run(false, NULL, 0);
pending_reads_.pop(); pending_reads_.pop();
} }
} }
...@@ -146,6 +147,7 @@ void HidConnectionMac::ProcessInputReport( ...@@ -146,6 +147,7 @@ void HidConnectionMac::ProcessInputReport(
DCHECK(thread_checker().CalledOnValidThread()); DCHECK(thread_checker().CalledOnValidThread());
PendingHidReport report; PendingHidReport report;
report.buffer = buffer; report.buffer = buffer;
report.size = buffer->size();
pending_reports_.push(report); pending_reports_.push(report);
ProcessReadQueue(); ProcessReadQueue();
} }
...@@ -156,16 +158,9 @@ void HidConnectionMac::ProcessReadQueue() { ...@@ -156,16 +158,9 @@ void HidConnectionMac::ProcessReadQueue() {
PendingHidRead read = pending_reads_.front(); PendingHidRead read = pending_reads_.front();
PendingHidReport report = pending_reports_.front(); PendingHidReport report = pending_reports_.front();
if (read.buffer->size() < report.buffer->size()) { pending_reports_.pop();
read.callback.Run(false, 0); if (CompleteRead(report.buffer, report.size, read.callback)) {
pending_reads_.pop(); 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 { ...@@ -28,24 +28,21 @@ class HidConnectionMac : public HidConnection {
public: public:
explicit HidConnectionMac(HidDeviceInfo device_info); 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: private:
virtual ~HidConnectionMac(); 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, static void InputReportCallback(void* context,
IOReturn result, IOReturn result,
void* sender, void* sender,
...@@ -55,9 +52,9 @@ class HidConnectionMac : public HidConnection { ...@@ -55,9 +52,9 @@ class HidConnectionMac : public HidConnection {
CFIndex report_length); CFIndex report_length);
void WriteReport(IOHIDReportType type, void WriteReport(IOHIDReportType type,
uint8_t report_id, scoped_refptr<net::IOBuffer> buffer,
scoped_refptr<net::IOBufferWithSize> buffer, size_t size,
const IOCallback& callback); const WriteCallback& callback);
void Flush(); void Flush();
void ProcessInputReport(scoped_refptr<net::IOBufferWithSize> buffer); void ProcessInputReport(scoped_refptr<net::IOBufferWithSize> buffer);
...@@ -65,7 +62,7 @@ class HidConnectionMac : public HidConnection { ...@@ -65,7 +62,7 @@ class HidConnectionMac : public HidConnection {
base::ScopedCFTypeRef<IOHIDDeviceRef> device_; base::ScopedCFTypeRef<IOHIDDeviceRef> device_;
scoped_refptr<base::MessageLoopProxy> message_loop_; 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<PendingHidReport> pending_reports_;
std::queue<PendingHidRead> pending_reads_; std::queue<PendingHidRead> pending_reads_;
......
...@@ -24,12 +24,23 @@ using net::IOBufferWithSize; ...@@ -24,12 +24,23 @@ using net::IOBufferWithSize;
class TestCompletionCallback { class TestCompletionCallback {
public: public:
TestCompletionCallback() TestCompletionCallback()
: callback_(base::Bind(&TestCompletionCallback::SetResult, : read_callback_(base::Bind(&TestCompletionCallback::SetReadResult,
base::Unretained(this))) {} 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; result_ = success;
transferred_ = size;
run_loop_.Quit(); run_loop_.Quit();
} }
...@@ -38,14 +49,20 @@ class TestCompletionCallback { ...@@ -38,14 +49,20 @@ class TestCompletionCallback {
return result_; return result_;
} }
const HidConnection::IOCallback& callback() const { return callback_; } const HidConnection::ReadCallback& read_callback() { return read_callback_; }
size_t transferred() const { return transferred_; } const HidConnection::WriteCallback write_callback() {
return write_callback_;
}
scoped_refptr<net::IOBuffer> buffer() const { return buffer_; }
size_t size() const { return size_; }
private: private:
const HidConnection::IOCallback callback_;
base::RunLoop run_loop_; base::RunLoop run_loop_;
bool result_; bool result_;
size_t transferred_; size_t size_;
scoped_refptr<net::IOBuffer> buffer_;
HidConnection::ReadCallback read_callback_;
HidConnection::WriteCallback write_callback_;
}; };
} // namespace } // namespace
...@@ -113,20 +130,24 @@ TEST_F(HidConnectionTest, ReadWrite) { ...@@ -113,20 +130,24 @@ TEST_F(HidConnectionTest, ReadWrite) {
ASSERT_TRUE(conn); ASSERT_TRUE(conn);
for (int i = 0; i < 8; ++i) { for (int i = 0; i < 8; ++i) {
scoped_refptr<IOBufferWithSize> write_buffer(new IOBufferWithSize(8)); scoped_refptr<IOBufferWithSize> buffer(new IOBufferWithSize(9));
*(int64_t*)write_buffer->data() = i; buffer->data()[0] = 0;
for (int j = 1; j < buffer->size(); ++j) {
buffer->data()[j] = i + j - 1;
}
TestCompletionCallback write_callback; 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_TRUE(write_callback.WaitForResult());
ASSERT_EQ(8UL, write_callback.transferred());
scoped_refptr<IOBufferWithSize> read_buffer(new IOBufferWithSize(8));
TestCompletionCallback read_callback; TestCompletionCallback read_callback;
conn->Read(read_buffer, read_callback.callback()); conn->Read(read_callback.read_callback());
ASSERT_TRUE(read_callback.WaitForResult()); ASSERT_TRUE(read_callback.WaitForResult());
ASSERT_EQ(8UL, read_callback.transferred()); ASSERT_EQ(9UL, read_callback.size());
ASSERT_EQ(i, *(int64_t*)read_buffer->data()); 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]);
}
} }
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <cstring> #include <cstring>
#include "base/bind.h"
#include "base/files/file.h" #include "base/files/file.h"
#include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop.h"
#include "base/win/object_watcher.h" #include "base/win/object_watcher.h"
...@@ -27,10 +28,10 @@ namespace device { ...@@ -27,10 +28,10 @@ namespace device {
struct PendingHidTransfer : public base::RefCounted<PendingHidTransfer>, struct PendingHidTransfer : public base::RefCounted<PendingHidTransfer>,
public base::win::ObjectWatcher::Delegate, public base::win::ObjectWatcher::Delegate,
public base::MessageLoop::DestructionObserver { public base::MessageLoop::DestructionObserver {
PendingHidTransfer(scoped_refptr<HidConnectionWin> connection, typedef base::Callback<void(PendingHidTransfer*, bool)> Callback;
scoped_refptr<net::IOBufferWithSize> target_buffer,
scoped_refptr<net::IOBufferWithSize> receive_buffer, PendingHidTransfer(scoped_refptr<net::IOBuffer> buffer,
HidConnection::IOCallback callback); const Callback& callback);
void TakeResultFromWindowsAPI(BOOL result); void TakeResultFromWindowsAPI(BOOL result);
...@@ -42,10 +43,10 @@ struct PendingHidTransfer : public base::RefCounted<PendingHidTransfer>, ...@@ -42,10 +43,10 @@ struct PendingHidTransfer : public base::RefCounted<PendingHidTransfer>,
// Implements base::MessageLoop::DestructionObserver // Implements base::MessageLoop::DestructionObserver
virtual void WillDestroyCurrentMessageLoop() OVERRIDE; virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
scoped_refptr<HidConnectionWin> connection_; // The buffer isn't used by this object but it's important that a reference
scoped_refptr<net::IOBufferWithSize> target_buffer_; // to it is held until the transfer completes.
scoped_refptr<net::IOBufferWithSize> receive_buffer_; scoped_refptr<net::IOBuffer> buffer_;
HidConnection::IOCallback callback_; Callback callback_;
OVERLAPPED overlapped_; OVERLAPPED overlapped_;
base::win::ScopedHandle event_; base::win::ScopedHandle event_;
base::win::ObjectWatcher watcher_; base::win::ObjectWatcher watcher_;
...@@ -59,13 +60,9 @@ struct PendingHidTransfer : public base::RefCounted<PendingHidTransfer>, ...@@ -59,13 +60,9 @@ struct PendingHidTransfer : public base::RefCounted<PendingHidTransfer>,
}; };
PendingHidTransfer::PendingHidTransfer( PendingHidTransfer::PendingHidTransfer(
scoped_refptr<HidConnectionWin> connection, scoped_refptr<net::IOBuffer> buffer,
scoped_refptr<net::IOBufferWithSize> target_buffer, const PendingHidTransfer::Callback& callback)
scoped_refptr<net::IOBufferWithSize> receive_buffer, : buffer_(buffer),
HidConnection::IOCallback callback)
: connection_(connection),
target_buffer_(target_buffer),
receive_buffer_(receive_buffer),
callback_(callback), callback_(callback),
event_(CreateEvent(NULL, FALSE, FALSE, NULL)) { event_(CreateEvent(NULL, FALSE, FALSE, NULL)) {
memset(&overlapped_, 0, sizeof(OVERLAPPED)); memset(&overlapped_, 0, sizeof(OVERLAPPED));
...@@ -77,23 +74,26 @@ PendingHidTransfer::~PendingHidTransfer() { ...@@ -77,23 +74,26 @@ PendingHidTransfer::~PendingHidTransfer() {
} }
void PendingHidTransfer::TakeResultFromWindowsAPI(BOOL result) { void PendingHidTransfer::TakeResultFromWindowsAPI(BOOL result) {
if (result || GetLastError() != ERROR_IO_PENDING) { if (result) {
connection_->OnTransferFinished(this); callback_.Run(this, true);
} else { } else if (GetLastError() == ERROR_IO_PENDING) {
base::MessageLoop::current()->AddDestructionObserver(this); base::MessageLoop::current()->AddDestructionObserver(this);
AddRef(); AddRef();
watcher_.StartWatching(event_.Get(), this); watcher_.StartWatching(event_.Get(), this);
} else {
VPLOG(1) << "HID transfer failed";
callback_.Run(this, false);
} }
} }
void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle) { void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle) {
connection_->OnTransferFinished(this); callback_.Run(this, true);
Release(); Release();
} }
void PendingHidTransfer::WillDestroyCurrentMessageLoop() { void PendingHidTransfer::WillDestroyCurrentMessageLoop() {
watcher_.StopWatching(); watcher_.StopWatching();
connection_->OnTransferCanceled(this); callback_.Run(this, false);
} }
HidConnectionWin::HidConnectionWin(const HidDeviceInfo& device_info) HidConnectionWin::HidConnectionWin(const HidDeviceInfo& device_info)
...@@ -122,146 +122,139 @@ HidConnectionWin::~HidConnectionWin() { ...@@ -122,146 +122,139 @@ HidConnectionWin::~HidConnectionWin() {
CancelIo(file_.Get()); CancelIo(file_.Get());
} }
void HidConnectionWin::PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer, void HidConnectionWin::PlatformRead(
const HidConnection::IOCallback& callback) { const HidConnection::ReadCallback& callback) {
// Windows will always include the report ID (including zero if report IDs // Windows will always include the report ID (including zero if report IDs
// are not in use) in the buffer. // are not in use) in the buffer.
scoped_refptr<net::IOBufferWithSize> receive_buffer = scoped_refptr<net::IOBufferWithSize> buffer =
new net::IOBufferWithSize(device_info().max_input_report_size + 1); new net::IOBufferWithSize(device_info().max_input_report_size + 1);
scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer(
scoped_refptr<PendingHidTransfer> transfer( buffer,
new PendingHidTransfer(this, buffer, receive_buffer, callback)); base::Bind(&HidConnectionWin::OnReadComplete, this, buffer, callback)));
transfers_.insert(transfer); transfers_.insert(transfer);
transfer->TakeResultFromWindowsAPI( transfer->TakeResultFromWindowsAPI(
ReadFile(file_.Get(), ReadFile(file_.Get(),
receive_buffer->data(), buffer->data(),
static_cast<DWORD>(receive_buffer->size()), static_cast<DWORD>(buffer->size()),
NULL, NULL,
transfer->GetOverlapped())); transfer->GetOverlapped()));
} }
void HidConnectionWin::PlatformWrite( void HidConnectionWin::PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
uint8_t report_id, size_t size,
scoped_refptr<net::IOBufferWithSize> buffer, const WriteCallback& callback) {
const HidConnection::IOCallback& callback) {
// The Windows API always wants either a report ID (if supported) or // The Windows API always wants either a report ID (if supported) or
// zero at the front of every output report. // zero at the front of every output report.
scoped_refptr<net::IOBufferWithSize> output_buffer( scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer(
new net::IOBufferWithSize(buffer->size() + 1)); buffer, base::Bind(&HidConnectionWin::OnWriteComplete, this, callback)));
output_buffer->data()[0] = report_id;
memcpy(output_buffer->data() + 1, buffer->data(), buffer->size());
scoped_refptr<PendingHidTransfer> transfer(
new PendingHidTransfer(this, output_buffer, NULL, callback));
transfers_.insert(transfer); transfers_.insert(transfer);
transfer->TakeResultFromWindowsAPI( transfer->TakeResultFromWindowsAPI(WriteFile(file_.Get(),
WriteFile(file_.Get(), buffer->data(),
output_buffer->data(), static_cast<DWORD>(size),
static_cast<DWORD>(output_buffer->size()), NULL,
NULL, transfer->GetOverlapped()));
transfer->GetOverlapped()));
} }
void HidConnectionWin::PlatformGetFeatureReport( void HidConnectionWin::PlatformGetFeatureReport(uint8_t report_id,
uint8_t report_id, const ReadCallback& callback) {
scoped_refptr<net::IOBufferWithSize> buffer,
const IOCallback& callback) {
int expected_report_size = device_info().max_feature_report_size;
if (device_info().has_report_id) {
expected_report_size++;
}
scoped_refptr<net::IOBufferWithSize> receive_buffer =
new net::IOBufferWithSize(expected_report_size);
// The first byte of the destination buffer is the report ID being requested. // The first byte of the destination buffer is the report ID being requested.
receive_buffer->data()[0] = report_id; scoped_refptr<net::IOBufferWithSize> buffer =
new net::IOBufferWithSize(device_info().max_feature_report_size + 1);
scoped_refptr<PendingHidTransfer> transfer( buffer->data()[0] = report_id;
new PendingHidTransfer(this, buffer, receive_buffer, callback));
scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer(
buffer,
base::Bind(
&HidConnectionWin::OnReadFeatureComplete, this, buffer, callback)));
transfers_.insert(transfer); transfers_.insert(transfer);
transfer->TakeResultFromWindowsAPI( transfer->TakeResultFromWindowsAPI(
DeviceIoControl(file_.Get(), DeviceIoControl(file_.Get(),
IOCTL_HID_GET_FEATURE, IOCTL_HID_GET_FEATURE,
NULL, NULL,
0, 0,
receive_buffer->data(), buffer->data(),
static_cast<DWORD>(receive_buffer->size()), static_cast<DWORD>(buffer->size()),
NULL, NULL,
transfer->GetOverlapped())); transfer->GetOverlapped()));
} }
void HidConnectionWin::PlatformSendFeatureReport( void HidConnectionWin::PlatformSendFeatureReport(
uint8_t report_id, scoped_refptr<net::IOBuffer> buffer,
scoped_refptr<net::IOBufferWithSize> buffer, size_t size,
const IOCallback& callback) { const WriteCallback& callback) {
// The Windows API always wants either a report ID (if supported) or // The Windows API always wants either a report ID (if supported) or
// zero at the front of every output report. // zero at the front of every output report.
scoped_refptr<net::IOBufferWithSize> output_buffer(buffer); scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer(
output_buffer = new net::IOBufferWithSize(buffer->size() + 1); buffer, base::Bind(&HidConnectionWin::OnWriteComplete, this, callback)));
output_buffer->data()[0] = report_id;
memcpy(output_buffer->data() + 1, buffer->data(), buffer->size());
scoped_refptr<PendingHidTransfer> transfer(
new PendingHidTransfer(this, output_buffer, NULL, callback));
transfer->TakeResultFromWindowsAPI( transfer->TakeResultFromWindowsAPI(
DeviceIoControl(file_.Get(), DeviceIoControl(file_.Get(),
IOCTL_HID_SET_FEATURE, IOCTL_HID_SET_FEATURE,
output_buffer->data(), buffer->data(),
static_cast<DWORD>(output_buffer->size()), static_cast<DWORD>(size),
NULL, NULL,
0, 0,
NULL, NULL,
transfer->GetOverlapped())); transfer->GetOverlapped()));
} }
void HidConnectionWin::OnTransferFinished( void HidConnectionWin::OnReadComplete(scoped_refptr<net::IOBuffer> buffer,
scoped_refptr<PendingHidTransfer> transfer) { const ReadCallback& callback,
transfers_.erase(transfer); PendingHidTransfer* transfer,
bool signaled) {
if (!signaled) {
callback.Run(false, NULL, 0);
return;
}
DWORD bytes_transferred; DWORD bytes_transferred;
if (GetOverlappedResult( if (GetOverlappedResult(
file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) { file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) {
if (bytes_transferred == 0) { CompleteRead(buffer, bytes_transferred, callback);
transfer->callback_.Run(true, 0);
return;
}
if (transfer->receive_buffer_) {
// If owner HID top-level collection does not have report ID, we need to
// copy the receive buffer into the target buffer, discarding the first
// byte. This is because the target buffer's owner is not expecting a
// report ID but Windows will always provide one.
if (!device_info().has_report_id) {
uint8_t report_id = transfer->receive_buffer_->data()[0];
// Assert first byte is 0x00
if (report_id != HidConnection::kNullReportId) {
VLOG(1) << "Unexpected report ID in HID report:" << report_id;
transfer->callback_.Run(false, 0);
} else {
// Move one byte forward.
--bytes_transferred;
memcpy(transfer->target_buffer_->data(),
transfer->receive_buffer_->data() + 1,
bytes_transferred);
}
} else {
memcpy(transfer->target_buffer_->data(),
transfer->receive_buffer_->data(),
bytes_transferred);
}
}
CompleteRead(
transfer->target_buffer_, bytes_transferred, transfer->callback_);
} else { } else {
VPLOG(1) << "HID transfer failed"; VPLOG(1) << "HID read failed";
transfer->callback_.Run(false, 0); callback.Run(false, NULL, 0);
}
}
void HidConnectionWin::OnReadFeatureComplete(
scoped_refptr<net::IOBuffer> buffer,
const ReadCallback& callback,
PendingHidTransfer* transfer,
bool signaled) {
if (!signaled) {
callback.Run(false, NULL, 0);
return;
}
DWORD bytes_transferred;
if (GetOverlappedResult(
file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) {
scoped_refptr<net::IOBuffer> new_buffer(
new net::IOBuffer(bytes_transferred - 1));
memcpy(new_buffer->data(), buffer->data() + 1, bytes_transferred - 1);
CompleteRead(new_buffer, bytes_transferred, callback);
} else {
VPLOG(1) << "HID read failed";
callback.Run(false, NULL, 0);
} }
} }
void HidConnectionWin::OnTransferCanceled( void HidConnectionWin::OnWriteComplete(const WriteCallback& callback,
scoped_refptr<PendingHidTransfer> transfer) { PendingHidTransfer* transfer,
transfers_.erase(transfer); bool signaled) {
transfer->callback_.Run(false, 0); if (!signaled) {
callback.Run(false);
return;
}
DWORD bytes_transferred;
if (GetOverlappedResult(
file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) {
callback.Run(true);
} else {
VPLOG(1) << "HID write failed";
callback.Run(false);
}
} }
} // namespace device } // namespace device
...@@ -20,31 +20,37 @@ class HidConnectionWin : public HidConnection { ...@@ -20,31 +20,37 @@ class HidConnectionWin : public HidConnection {
public: public:
explicit HidConnectionWin(const HidDeviceInfo& device_info); 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: private:
friend class HidServiceWin; friend class HidServiceWin;
friend struct PendingHidTransfer; friend struct PendingHidTransfer;
~HidConnectionWin(); ~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(); } bool available() const { return file_.IsValid(); }
void OnTransferFinished(scoped_refptr<PendingHidTransfer> transfer); void OnReadComplete(scoped_refptr<net::IOBuffer> buffer,
void OnTransferCanceled(scoped_refptr<PendingHidTransfer> transfer); 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_; base::win::ScopedHandle file_;
......
...@@ -183,36 +183,24 @@ void HidReceiveFunction::AsyncWorkStart() { ...@@ -183,36 +183,24 @@ void HidReceiveFunction::AsyncWorkStart() {
} }
scoped_refptr<device::HidConnection> connection = resource->connection(); scoped_refptr<device::HidConnection> connection = resource->connection();
has_report_id_ = connection->device_info().has_report_id; connection->Read(base::Bind(&HidReceiveFunction::OnFinished, this));
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));
} }
void HidReceiveFunction::OnFinished(bool success, size_t bytes) { void HidReceiveFunction::OnFinished(bool success,
scoped_refptr<net::IOBuffer> buffer,
size_t size) {
if (!success) { if (!success) {
CompleteWithError(kErrorTransfer); CompleteWithError(kErrorTransfer);
return; return;
} }
int report_id = 0; DCHECK_GE(size, 1u);
const char* data = buffer_->data(); int report_id = reinterpret_cast<uint8_t*>(buffer->data())[0];
if (has_report_id_) {
if (bytes < 1) {
CompleteWithError(kErrorTransfer);
return;
}
report_id = data[0];
data++;
bytes--;
}
scoped_ptr<base::ListValue> result(new base::ListValue()); scoped_ptr<base::ListValue> result(new base::ListValue());
result->Append(new base::FundamentalValue(report_id)); 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()); SetResultList(result.Pass());
AsyncWorkCompleted(); AsyncWorkCompleted();
} }
...@@ -237,14 +225,15 @@ void HidSendFunction::AsyncWorkStart() { ...@@ -237,14 +225,15 @@ void HidSendFunction::AsyncWorkStart() {
} }
scoped_refptr<net::IOBufferWithSize> buffer( scoped_refptr<net::IOBufferWithSize> buffer(
new net::IOBufferWithSize(parameters_->data.size())); new net::IOBufferWithSize(parameters_->data.size() + 1));
memcpy(buffer->data(), parameters_->data.c_str(), parameters_->data.size()); buffer->data()[0] = static_cast<uint8_t>(parameters_->report_id);
resource->connection()->Write(static_cast<uint8_t>(parameters_->report_id), memcpy(
buffer, buffer->data() + 1, parameters_->data.c_str(), parameters_->data.size());
base::Bind(&HidSendFunction::OnFinished, this)); 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) { if (!success) {
CompleteWithError(kErrorTransfer); CompleteWithError(kErrorTransfer);
return; return;
...@@ -272,21 +261,21 @@ void HidReceiveFeatureReportFunction::AsyncWorkStart() { ...@@ -272,21 +261,21 @@ void HidReceiveFeatureReportFunction::AsyncWorkStart() {
} }
scoped_refptr<device::HidConnection> connection = resource->connection(); scoped_refptr<device::HidConnection> connection = resource->connection();
const int size = connection->device_info().max_feature_report_size;
buffer_ = new net::IOBufferWithSize(size);
connection->GetFeatureReport( connection->GetFeatureReport(
static_cast<uint8_t>(parameters_->report_id), static_cast<uint8_t>(parameters_->report_id),
buffer_,
base::Bind(&HidReceiveFeatureReportFunction::OnFinished, this)); 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) { if (!success) {
CompleteWithError(kErrorTransfer); CompleteWithError(kErrorTransfer);
return; return;
} }
SetResult(base::BinaryValue::CreateWithCopiedBuffer(buffer_->data(), bytes)); SetResult(base::BinaryValue::CreateWithCopiedBuffer(buffer->data(), size));
AsyncWorkCompleted(); AsyncWorkCompleted();
} }
...@@ -308,16 +297,19 @@ void HidSendFeatureReportFunction::AsyncWorkStart() { ...@@ -308,16 +297,19 @@ void HidSendFeatureReportFunction::AsyncWorkStart() {
CompleteWithError(kErrorConnectionNotFound); CompleteWithError(kErrorConnectionNotFound);
return; return;
} }
scoped_refptr<net::IOBufferWithSize> buffer( scoped_refptr<net::IOBufferWithSize> buffer(
new net::IOBufferWithSize(parameters_->data.size())); new net::IOBufferWithSize(parameters_->data.size() + 1));
memcpy(buffer->data(), parameters_->data.c_str(), parameters_->data.size()); buffer->data()[0] = static_cast<uint8_t>(parameters_->report_id);
memcpy(
buffer->data() + 1, parameters_->data.c_str(), parameters_->data.size());
resource->connection()->SendFeatureReport( resource->connection()->SendFeatureReport(
static_cast<uint8_t>(parameters_->report_id),
buffer, buffer,
buffer->size(),
base::Bind(&HidSendFeatureReportFunction::OnFinished, this)); base::Bind(&HidSendFeatureReportFunction::OnFinished, this));
} }
void HidSendFeatureReportFunction::OnFinished(bool success, size_t bytes) { void HidSendFeatureReportFunction::OnFinished(bool success) {
if (!success) { if (!success) {
CompleteWithError(kErrorTransfer); CompleteWithError(kErrorTransfer);
return; return;
......
...@@ -112,10 +112,10 @@ class HidReceiveFunction : public HidAsyncApiFunction { ...@@ -112,10 +112,10 @@ class HidReceiveFunction : public HidAsyncApiFunction {
private: private:
virtual ~HidReceiveFunction(); 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_; scoped_ptr<core_api::hid::Receive::Params> parameters_;
DISALLOW_COPY_AND_ASSIGN(HidReceiveFunction); DISALLOW_COPY_AND_ASSIGN(HidReceiveFunction);
...@@ -134,7 +134,7 @@ class HidSendFunction : public HidAsyncApiFunction { ...@@ -134,7 +134,7 @@ class HidSendFunction : public HidAsyncApiFunction {
private: private:
virtual ~HidSendFunction(); virtual ~HidSendFunction();
void OnFinished(bool success, size_t bytes); void OnFinished(bool success);
scoped_ptr<core_api::hid::Send::Params> parameters_; scoped_ptr<core_api::hid::Send::Params> parameters_;
...@@ -155,9 +155,10 @@ class HidReceiveFeatureReportFunction : public HidAsyncApiFunction { ...@@ -155,9 +155,10 @@ class HidReceiveFeatureReportFunction : public HidAsyncApiFunction {
private: private:
virtual ~HidReceiveFeatureReportFunction(); 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_; scoped_ptr<core_api::hid::ReceiveFeatureReport::Params> parameters_;
DISALLOW_COPY_AND_ASSIGN(HidReceiveFeatureReportFunction); DISALLOW_COPY_AND_ASSIGN(HidReceiveFeatureReportFunction);
...@@ -176,7 +177,7 @@ class HidSendFeatureReportFunction : public HidAsyncApiFunction { ...@@ -176,7 +177,7 @@ class HidSendFeatureReportFunction : public HidAsyncApiFunction {
private: private:
virtual ~HidSendFeatureReportFunction(); virtual ~HidSendFeatureReportFunction();
void OnFinished(bool success, size_t bytes); void OnFinished(bool success);
scoped_ptr<core_api::hid::SendFeatureReport::Params> parameters_; 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