From 104de62a1315caeae550b83c5f9163277bdd5f9b Mon Sep 17 00:00:00 2001 From: "csharp@chromium.org" Date: Thu, 3 Jul 2014 18:20:04 +0000 Subject: [PATCH] Revert 281282 "Revert 281133 "chrome.hid: enrich model with repo..." Tests still failed, undoing revert. > Revert 281133 "chrome.hid: enrich model with report IDs" > > Speculative revert to try and fix Win browser tests. > > > chrome.hid: enrich model with report IDs > > > > - add report IDs and max report size > > - don't expose sensitive usages > > > > BUG=364423 > > R=rockot@chromium.org > > TESTS=run device_unittests (HidReportDescriptorTest) > > > > Review URL: https://codereview.chromium.org/317783010 > > TBR=jracle@logitech.com > > Review URL: https://codereview.chromium.org/369923002 TBR=csharp@chromium.org Review URL: https://codereview.chromium.org/364213005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@281300 0039d316-1c4b-4281-b951-d872f2087c98 --- .../extensions/api/hid/hid_device_manager.cc | 95 +- .../extensions/api/hid/hid_device_manager.h | 9 - chrome/common/extensions/api/hid.idl | 24 +- device/hid/hid.gyp | 4 +- device/hid/hid_collection_info.cc | 17 + device/hid/hid_collection_info.h | 29 + device/hid/hid_connection.cc | 180 +++- device/hid/hid_connection.h | 61 +- device/hid/hid_connection_linux.cc | 124 +-- device/hid/hid_connection_linux.h | 38 +- device/hid/hid_connection_mac.cc | 123 ++- device/hid/hid_connection_mac.h | 39 +- device/hid/hid_connection_win.cc | 132 +-- device/hid/hid_connection_win.h | 45 +- device/hid/hid_device_info.cc | 10 +- device/hid/hid_device_info.h | 19 +- device/hid/hid_report_descriptor.cc | 170 +++- device/hid/hid_report_descriptor.h | 11 +- device/hid/hid_report_descriptor_item.cc | 12 +- device/hid/hid_report_descriptor_unittest.cc | 1042 ++++++++------------ device/hid/hid_service_linux.cc | 5 +- device/hid/hid_service_mac.cc | 119 ++- device/hid/hid_service_win.cc | 68 +- device/hid/hid_usage_and_page.cc | 27 +- device/hid/hid_usage_and_page.h | 3 +- device/hid/hid_utils_mac.cc | 45 - device/hid/hid_utils_mac.h | 30 - 27 files changed, 1316 insertions(+), 1165 deletions(-) create mode 100644 device/hid/hid_collection_info.cc create mode 100644 device/hid/hid_collection_info.h rewrite device/hid/hid_report_descriptor_unittest.cc (94%) delete mode 100644 device/hid/hid_utils_mac.cc delete mode 100644 device/hid/hid_utils_mac.h diff --git a/chrome/browser/extensions/api/hid/hid_device_manager.cc b/chrome/browser/extensions/api/hid/hid_device_manager.cc index 45d82a6f87b4..90fb6ce85123 100644 --- a/chrome/browser/extensions/api/hid/hid_device_manager.cc +++ b/chrome/browser/extensions/api/hid/hid_device_manager.cc @@ -43,30 +43,53 @@ scoped_ptr HidDeviceManager::GetApiDevices( int resource_id = device_iter->first; device::HidDeviceId device_id = device_iter->second; device::HidDeviceInfo device_info; + if (hid_service->GetDeviceInfo(device_id, &device_info)) { if (device_info.vendor_id == vendor_id && - device_info.product_id == product_id && - IsDeviceAccessible(device_info)) { + device_info.product_id == product_id) { api::hid::HidDeviceInfo api_device_info; api_device_info.device_id = resource_id; api_device_info.vendor_id = device_info.vendor_id; api_device_info.product_id = device_info.product_id; - for (std::vector::const_iterator usage_iter = - device_info.usages.begin(); - usage_iter != device_info.usages.end(); - ++usage_iter) { - api::hid::HidUsageAndPage* usage_and_page = - new api::hid::HidUsageAndPage(); - usage_and_page->usage_page = (*usage_iter).usage_page; - usage_and_page->usage = (*usage_iter).usage; - linked_ptr usage_and_page_ptr( - usage_and_page); - api_device_info.usages.push_back(usage_and_page_ptr); + api_device_info.max_input_report_size = + device_info.max_input_report_size; + api_device_info.max_output_report_size = + device_info.max_output_report_size; + api_device_info.max_feature_report_size = + device_info.max_feature_report_size; + + for (std::vector::const_iterator + collections_iter = device_info.collections.begin(); + collections_iter != device_info.collections.end(); + ++collections_iter) { + device::HidCollectionInfo collection = *collections_iter; + + // Don't expose sensitive data. + if (collection.usage.IsProtected()) { + continue; + } + + api::hid::HidCollectionInfo* api_collection = + new api::hid::HidCollectionInfo(); + api_collection->usage_page = collection.usage.usage_page; + api_collection->usage = collection.usage.usage; + + api_collection->report_ids.resize(collection.report_ids.size()); + std::copy(collection.report_ids.begin(), + collection.report_ids.end(), + api_collection->report_ids.begin()); + + api_device_info.collections.push_back( + make_linked_ptr(api_collection)); } - api_devices->Append(api_device_info.ToValue().release()); + + // Expose devices with which user can communicate. + if (api_device_info.collections.size() > 0) + api_devices->Append(api_device_info.ToValue().release()); } } } + return scoped_ptr(api_devices); } @@ -116,48 +139,4 @@ void HidDeviceManager::UpdateDevices() { resource_ids_.swap(new_resource_ids); } -// static -// TODO(rockot): Add some tests for this. -bool HidDeviceManager::IsDeviceAccessible( - const device::HidDeviceInfo& device_info) { - for (std::vector::const_iterator iter = - device_info.usages.begin(); - iter != device_info.usages.end(); ++iter) { - if (!IsUsageAccessible(*iter)) { - return false; - } - } - return true; -} - -// static -bool HidDeviceManager::IsUsageAccessible( - const HidUsageAndPage& usage_and_page) { - if (usage_and_page.usage_page == HidUsageAndPage::kPageKeyboard) - return false; - - if (usage_and_page.usage_page != HidUsageAndPage::kPageGenericDesktop) - return true; - - uint16_t usage = usage_and_page.usage; - if (usage == HidUsageAndPage::kGenericDesktopPointer || - usage == HidUsageAndPage::kGenericDesktopMouse || - usage == HidUsageAndPage::kGenericDesktopKeyboard || - usage == HidUsageAndPage::kGenericDesktopKeypad) { - return false; - } - - if (usage >= HidUsageAndPage::kGenericDesktopSystemControl && - usage <= HidUsageAndPage::kGenericDesktopSystemWarmRestart) { - return false; - } - - if (usage >= HidUsageAndPage::kGenericDesktopSystemDock && - usage <= HidUsageAndPage::kGenericDesktopSystemDisplaySwap) { - return false; - } - - return true; -} - } // namespace extensions diff --git a/chrome/browser/extensions/api/hid/hid_device_manager.h b/chrome/browser/extensions/api/hid/hid_device_manager.h index c0abedd0ccf6..84a6e3022e53 100644 --- a/chrome/browser/extensions/api/hid/hid_device_manager.h +++ b/chrome/browser/extensions/api/hid/hid_device_manager.h @@ -44,15 +44,6 @@ class HidDeviceManager : public BrowserContextKeyedAPI { void UpdateDevices(); - // Determines if a given device interface should be accessible to API - // consumers. In order for a device interface to be accessible, ALL of its - // specified usages must be accessible. - static bool IsDeviceAccessible(const device::HidDeviceInfo& device_info); - - // Determines if a given usage is available to API consumers. This is used to - // blacklist usages which could be security or privacy concerns. - static bool IsUsageAccessible(const device::HidUsageAndPage& usage_and_page); - base::ThreadChecker thread_checker_; int next_resource_id_; diff --git a/chrome/common/extensions/api/hid.idl b/chrome/common/extensions/api/hid.idl index a718ace3883f..bf2cad4a91b2 100644 --- a/chrome/common/extensions/api/hid.idl +++ b/chrome/common/extensions/api/hid.idl @@ -6,15 +6,15 @@ // This API provides access to HID operations from within the context of an app. // Using this API, apps can function as drivers for hardware devices. namespace hid { - // HID usage pair. Each enumerated device interface exposes an array of - // these objects. Values correspond to those defined by the - // + +#include "device/hid/hid_usage_and_page.h" + +namespace device { + +struct HidCollectionInfo { + HidCollectionInfo(); + ~HidCollectionInfo(); + + // Collection's usage ID. + HidUsageAndPage usage; + + // HID report IDs which belong + // to this collection or to its + // embedded collections. + std::set report_ids; +}; + +} // namespace device" + +#endif // DEVICE_HID_HID_COLLECTION_INFO_H_ diff --git a/device/hid/hid_connection.cc b/device/hid/hid_connection.cc index c134bf2702e9..76809d3292c2 100644 --- a/device/hid/hid_connection.cc +++ b/device/hid/hid_connection.cc @@ -4,8 +4,183 @@ #include "device/hid/hid_connection.h" +#include + namespace device { +namespace { + +// Functor used to filter collections by report ID. +struct CollectionHasReportId { + explicit CollectionHasReportId(const uint8_t report_id) + : report_id_(report_id) {} + + bool operator()(const HidCollectionInfo& info) const { + if (info.report_ids.size() == 0 || + report_id_ == HidConnection::kNullReportId) + return false; + + if (report_id_ == HidConnection::kAnyReportId) + return true; + + return std::find(info.report_ids.begin(), + info.report_ids.end(), + report_id_) != info.report_ids.end(); + } + + private: + const uint8_t report_id_; +}; + +// Functor returning true if collection has a protected usage. +struct CollectionIsProtected { + bool operator()(const HidCollectionInfo& info) const { + return info.usage.IsProtected(); + } +}; + +bool FindCollectionByReportId(const HidDeviceInfo& device_info, + const uint8_t report_id, + HidCollectionInfo* collection_info) { + std::vector::const_iterator collection_iter = + std::find_if(device_info.collections.begin(), + device_info.collections.end(), + CollectionHasReportId(report_id)); + if (collection_iter != device_info.collections.end()) { + if (collection_info) { + *collection_info = *collection_iter; + } + return true; + } + + return false; +} + +bool HasReportId(const HidDeviceInfo& device_info) { + return FindCollectionByReportId( + device_info, HidConnection::kAnyReportId, NULL); +} + +bool HasProtectedCollection(const HidDeviceInfo& device_info) { + return std::find_if(device_info.collections.begin(), + device_info.collections.end(), + CollectionIsProtected()) != device_info.collections.end(); +} + +} // namespace + +HidConnection::HidConnection(const HidDeviceInfo& device_info) + : device_info_(device_info) { + has_protected_collection_ = HasProtectedCollection(device_info); + has_report_id_ = HasReportId(device_info); +} + +HidConnection::~HidConnection() { + DCHECK(thread_checker_.CalledOnValidThread()); +} + +void HidConnection::Read(scoped_refptr buffer, + const IOCallback& 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 (!has_report_id()) { + expected_buffer_size--; + } + if (buffer->size() < expected_buffer_size) { + // Receive buffer is too small. + callback.Run(false, 0); + return; + } + + PlatformRead(buffer, callback); +} + +void HidConnection::Write(uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (device_info_.max_output_report_size == 0) { + // The device does not support output reports. + callback.Run(false, 0); + return; + } + if (IsReportIdProtected(report_id)) { + callback.Run(false, 0); + return; + } + + PlatformWrite(report_id, buffer, callback); +} + +void HidConnection::GetFeatureReport( + uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (device_info_.max_feature_report_size == 0) { + // The device does not support feature reports. + callback.Run(false, 0); + return; + } + if (IsReportIdProtected(report_id)) { + callback.Run(false, 0); + return; + } + int expected_buffer_size = device_info_.max_feature_report_size; + if (!has_report_id()) { + expected_buffer_size--; + } + if (buffer->size() < expected_buffer_size) { + // Receive buffer is too small. + callback.Run(false, 0); + return; + } + + PlatformGetFeatureReport(report_id, buffer, callback); +} + +void HidConnection::SendFeatureReport( + uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (device_info_.max_feature_report_size == 0) { + // The device does not support feature reports. + callback.Run(false, 0); + return; + } + if (IsReportIdProtected(report_id)) { + callback.Run(false, 0); + return; + } + + PlatformSendFeatureReport(report_id, buffer, callback); +} + +bool HidConnection::CompleteRead(scoped_refptr buffer, + const IOCallback& callback) { + if (buffer->size() == 0 || IsReportIdProtected(buffer->data()[0])) { + return false; + } + + callback.Run(true, buffer->size()); + return true; +} + +bool HidConnection::IsReportIdProtected(const uint8_t report_id) { + HidCollectionInfo collection_info; + if (FindCollectionByReportId(device_info_, report_id, &collection_info)) { + return collection_info.usage.IsProtected(); + } + + return has_protected_collection(); +} + PendingHidReport::PendingHidReport() {} PendingHidReport::~PendingHidReport() {} @@ -14,9 +189,4 @@ PendingHidRead::PendingHidRead() {} PendingHidRead::~PendingHidRead() {} -HidConnection::HidConnection(const HidDeviceInfo& device_info) - : device_info_(device_info) {} - -HidConnection::~HidConnection() {} - } // namespace device diff --git a/device/hid/hid_connection.h b/device/hid/hid_connection.h index 5c08fbc5bbdf..963b89ff4fe1 100644 --- a/device/hid/hid_connection.h +++ b/device/hid/hid_connection.h @@ -9,6 +9,7 @@ #include "base/callback.h" #include "base/memory/ref_counted.h" +#include "base/threading/thread_checker.h" #include "device/hid/hid_device_info.h" #include "net/base/io_buffer.h" @@ -16,31 +17,65 @@ namespace device { class HidConnection : public base::RefCountedThreadSafe { public: - typedef base::Callback IOCallback; + enum SpecialReportIds { + kNullReportId = 0x00, + kAnyReportId = 0xFF, + }; - virtual void Read(scoped_refptr buffer, - const IOCallback& callback) = 0; - virtual void Write(uint8_t report_id, - scoped_refptr buffer, - const IOCallback& callback) = 0; - virtual void GetFeatureReport(uint8_t report_id, - scoped_refptr buffer, - const IOCallback& callback) = 0; - virtual void SendFeatureReport(uint8_t report_id, - scoped_refptr buffer, - const IOCallback& callback) = 0; + typedef base::Callback IOCallback; const HidDeviceInfo& device_info() const { return device_info_; } + bool has_protected_collection() const { return has_protected_collection_; } + bool has_report_id() const { return has_report_id_; } + const base::ThreadChecker& thread_checker() const { return thread_checker_; } + + void Read(scoped_refptr buffer, + const IOCallback& callback); + void Write(uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback); + void GetFeatureReport(uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback); + void SendFeatureReport(uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback); protected: friend class base::RefCountedThreadSafe; - friend struct HidDeviceInfo; explicit HidConnection(const HidDeviceInfo& device_info); virtual ~HidConnection(); + virtual void PlatformRead(scoped_refptr buffer, + const IOCallback& callback) = 0; + virtual void PlatformWrite(uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback) = 0; + virtual void PlatformGetFeatureReport( + uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback) = 0; + virtual void PlatformSendFeatureReport( + uint8_t report_id, + scoped_refptr buffer, + const IOCallback& 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 buffer, + const IOCallback& callback); + private: + bool IsReportIdProtected(const uint8_t report_id); + const HidDeviceInfo device_info_; + bool has_report_id_; + bool has_protected_collection_; + base::ThreadChecker thread_checker_; DISALLOW_COPY_AND_ASSIGN(HidConnection); }; diff --git a/device/hid/hid_connection_linux.cc b/device/hid/hid_connection_linux.cc index 331e3f30856a..0220c2e496ea 100644 --- a/device/hid/hid_connection_linux.cc +++ b/device/hid/hid_connection_linux.cc @@ -46,8 +46,6 @@ scoped_refptr CopyBufferWithReportId( HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info, std::string dev_node) : HidConnection(device_info) { - DCHECK(thread_checker_.CalledOnValidThread()); - int flags = base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE; @@ -88,49 +86,13 @@ HidConnectionLinux::HidConnectionLinux(HidDeviceInfo device_info, } HidConnectionLinux::~HidConnectionLinux() { - DCHECK(thread_checker_.CalledOnValidThread()); Disconnect(); + Flush(); } -void HidConnectionLinux::OnFileCanReadWithoutBlocking(int fd) { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK_EQ(fd, device_file_.GetPlatformFile()); - - uint8 buffer[1024] = {0}; - int bytes_read = - HANDLE_EINTR(read(device_file_.GetPlatformFile(), buffer, 1024)); - if (bytes_read < 0) { - if (errno == EAGAIN) { - return; - } - VPLOG(1) << "Read failed"; - Disconnect(); - return; - } - - PendingHidReport report; - report.buffer = new net::IOBufferWithSize(bytes_read); - memcpy(report.buffer->data(), buffer, bytes_read); - pending_reports_.push(report); - ProcessReadQueue(); -} - -void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) {} - -void HidConnectionLinux::Disconnect() { - DCHECK(thread_checker_.CalledOnValidThread()); - device_file_watcher_.StopWatchingFileDescriptor(); - device_file_.Close(); - while (!pending_reads_.empty()) { - PendingHidRead pending_read = pending_reads_.front(); - pending_reads_.pop(); - pending_read.callback.Run(false, 0); - } -} - -void HidConnectionLinux::Read(scoped_refptr buffer, - const IOCallback& callback) { - DCHECK(thread_checker_.CalledOnValidThread()); +void HidConnectionLinux::PlatformRead( + scoped_refptr buffer, + const IOCallback& callback) { PendingHidRead pending_read; pending_read.buffer = buffer; pending_read.callback = callback; @@ -138,10 +100,10 @@ void HidConnectionLinux::Read(scoped_refptr buffer, ProcessReadQueue(); } -void HidConnectionLinux::Write(uint8_t report_id, - scoped_refptr buffer, - const IOCallback& callback) { - DCHECK(thread_checker_.CalledOnValidThread()); +void HidConnectionLinux::PlatformWrite( + uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback) { // If report ID is non-zero, insert it into a new copy of the buffer. if (report_id != 0) buffer = CopyBufferWithReportId(buffer, report_id); @@ -156,12 +118,10 @@ void HidConnectionLinux::Write(uint8_t report_id, } } -void HidConnectionLinux::GetFeatureReport( +void HidConnectionLinux::PlatformGetFeatureReport( uint8_t report_id, scoped_refptr buffer, const IOCallback& callback) { - DCHECK(thread_checker_.CalledOnValidThread()); - if (buffer->size() == 0) { callback.Run(false, 0); return; @@ -180,11 +140,10 @@ void HidConnectionLinux::GetFeatureReport( } } -void HidConnectionLinux::SendFeatureReport( +void HidConnectionLinux::PlatformSendFeatureReport( uint8_t report_id, scoped_refptr buffer, const IOCallback& callback) { - DCHECK(thread_checker_.CalledOnValidThread()); if (report_id != 0) buffer = CopyBufferWithReportId(buffer, report_id); int result = ioctl(device_file_.GetPlatformFile(), @@ -198,17 +157,72 @@ void HidConnectionLinux::SendFeatureReport( } } +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)); + if (bytes_read < 0) { + if (errno == EAGAIN) { + return; + } + VPLOG(1) << "Read failed"; + Disconnect(); + return; + } + + scoped_refptr buffer = + new net::IOBufferWithSize(bytes_read); + memcpy(buffer->data(), raw_buffer, bytes_read); + + ProcessInputReport(buffer); +} + +void HidConnectionLinux::OnFileCanWriteWithoutBlocking(int fd) { +} + +void HidConnectionLinux::Disconnect() { + DCHECK(thread_checker().CalledOnValidThread()); + device_file_watcher_.StopWatchingFileDescriptor(); + device_file_.Close(); + + Flush(); +} + +void HidConnectionLinux::Flush() { + while (!pending_reads_.empty()) { + pending_reads_.front().callback.Run(false, 0); + pending_reads_.pop(); + } +} + +void HidConnectionLinux::ProcessInputReport( + scoped_refptr buffer) { + DCHECK(thread_checker().CalledOnValidThread()); + PendingHidReport report; + report.buffer = buffer; + pending_reports_.push(report); + ProcessReadQueue(); +} + void HidConnectionLinux::ProcessReadQueue() { + DCHECK(thread_checker().CalledOnValidThread()); while (pending_reads_.size() && pending_reports_.size()) { PendingHidRead read = pending_reads_.front(); - pending_reads_.pop(); PendingHidReport report = pending_reports_.front(); - if (report.buffer->size() > read.buffer->size()) { - read.callback.Run(false, report.buffer->size()); + + if (read.buffer->size() < report.buffer->size()) { + read.callback.Run(false, 0); + pending_reads_.pop(); } else { memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size()); pending_reports_.pop(); - read.callback.Run(true, report.buffer->size()); + + if (CompleteRead(report.buffer, read.callback)) { + pending_reads_.pop(); + } } } } diff --git a/device/hid/hid_connection_linux.h b/device/hid/hid_connection_linux.h index 108ad881a952..1f5a7a88cc5f 100644 --- a/device/hid/hid_connection_linux.h +++ b/device/hid/hid_connection_linux.h @@ -8,10 +8,8 @@ #include #include "base/files/file.h" -#include "base/memory/ref_counted.h" #include "base/message_loop/message_pump_libevent.h" #include "device/hid/hid_connection.h" -#include "device/hid/hid_device_info.h" namespace device { @@ -20,19 +18,22 @@ class HidConnectionLinux : public HidConnection, public: HidConnectionLinux(HidDeviceInfo device_info, std::string dev_node); - virtual void Read(scoped_refptr buffer, - const IOCallback& callback) OVERRIDE; - virtual void Write(uint8_t report_id, - scoped_refptr buffer, - const IOCallback& callback) OVERRIDE; - virtual void GetFeatureReport(uint8_t report_id, - scoped_refptr buffer, - const IOCallback& callback) OVERRIDE; - virtual void SendFeatureReport(uint8_t report_id, - scoped_refptr buffer, - const IOCallback& callback) OVERRIDE; - - // Implements base::MessagePumpLibevent::Watcher + // HidConnection implementation. + virtual void PlatformRead(scoped_refptr buffer, + const IOCallback& callback) OVERRIDE; + virtual void PlatformWrite(uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback) OVERRIDE; + virtual void PlatformGetFeatureReport( + uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback) OVERRIDE; + virtual void PlatformSendFeatureReport( + uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback) OVERRIDE; + + // base::MessagePumpLibevent::Watcher implementation. virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE; @@ -40,17 +41,18 @@ class HidConnectionLinux : public HidConnection, friend class base::RefCountedThreadSafe; virtual ~HidConnectionLinux(); - void ProcessReadQueue(); void Disconnect(); + void Flush(); + void ProcessInputReport(scoped_refptr buffer); + void ProcessReadQueue(); + base::File device_file_; base::MessagePumpLibevent::FileDescriptorWatcher device_file_watcher_; std::queue pending_reports_; std::queue pending_reads_; - base::ThreadChecker thread_checker_; - DISALLOW_COPY_AND_ASSIGN(HidConnectionLinux); }; diff --git a/device/hid/hid_connection_mac.cc b/device/hid/hid_connection_mac.cc index ce17df4cc9b3..521f287a62bb 100644 --- a/device/hid/hid_connection_mac.cc +++ b/device/hid/hid_connection_mac.cc @@ -7,7 +7,6 @@ #include "base/bind.h" #include "base/mac/foundation_util.h" #include "base/message_loop/message_loop.h" -#include "base/threading/thread_restrictions.h" #include "device/hid/hid_connection_mac.h" namespace device { @@ -15,85 +14,70 @@ namespace device { HidConnectionMac::HidConnectionMac(HidDeviceInfo device_info) : HidConnection(device_info), device_(device_info.device_id, base::scoped_policy::RETAIN) { - DCHECK(thread_checker_.CalledOnValidThread()); - message_loop_ = base::MessageLoopProxy::current(); DCHECK(device_.get()); - inbound_buffer_.reset((uint8_t*)malloc(device_info.input_report_size)); + inbound_buffer_.reset((uint8_t*)malloc(device_info.max_input_report_size)); IOHIDDeviceRegisterInputReportCallback(device_.get(), inbound_buffer_.get(), - device_info.input_report_size, + device_info.max_input_report_size, &HidConnectionMac::InputReportCallback, this); IOHIDDeviceOpen(device_, kIOHIDOptionsTypeNone); } HidConnectionMac::~HidConnectionMac() { - DCHECK(thread_checker_.CalledOnValidThread()); - - while (!pending_reads_.empty()) { - pending_reads_.front().callback.Run(false, 0); - pending_reads_.pop(); - } - IOHIDDeviceClose(device_, kIOHIDOptionsTypeNone); + Flush(); } -void HidConnectionMac::Read(scoped_refptr buffer, - const IOCallback& callback) { - DCHECK(thread_checker_.CalledOnValidThread()); +void HidConnectionMac::PlatformRead(scoped_refptr buffer, + const IOCallback& callback) { if (!device_) { callback.Run(false, 0); return; } - PendingHidRead read; - read.buffer = buffer; - read.callback = callback; - pending_reads_.push(read); + + PendingHidRead pending_read; + pending_read.buffer = buffer; + pending_read.callback = callback; + pending_reads_.push(pending_read); ProcessReadQueue(); } -void HidConnectionMac::Write(uint8_t report_id, - scoped_refptr buffer, - const IOCallback& callback) { - DCHECK(thread_checker_.CalledOnValidThread()); +void HidConnectionMac::PlatformWrite( + uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback) { WriteReport(kIOHIDReportTypeOutput, report_id, buffer, callback); } -void HidConnectionMac::GetFeatureReport( +void HidConnectionMac::PlatformGetFeatureReport( uint8_t report_id, scoped_refptr buffer, const IOCallback& callback) { - DCHECK(thread_checker_.CalledOnValidThread()); - if (device_info().feature_report_size == 0) { - callback.Run(false, 0); - return; - } - - if (buffer->size() < device_info().feature_report_size) { + if (!device_) { callback.Run(false, 0); return; } uint8_t* feature_report_buffer = reinterpret_cast(buffer->data()); - CFIndex feature_report_size = device_info().feature_report_size; + CFIndex max_feature_report_size = device_info().max_feature_report_size; IOReturn result = IOHIDDeviceGetReport(device_, kIOHIDReportTypeFeature, report_id, feature_report_buffer, - &feature_report_size); + &max_feature_report_size); if (result == kIOReturnSuccess) - callback.Run(true, feature_report_size); + callback.Run(true, max_feature_report_size); else callback.Run(false, 0); } -void HidConnectionMac::SendFeatureReport( +void HidConnectionMac::PlatformSendFeatureReport( uint8_t report_id, scoped_refptr buffer, const IOCallback& callback) { - DCHECK(thread_checker_.CalledOnValidThread()); WriteReport(kIOHIDReportTypeFeature, report_id, buffer, callback); } @@ -112,45 +96,18 @@ void HidConnectionMac::InputReportCallback(void* context, connection->message_loop_->PostTask( FROM_HERE, - base::Bind( - &HidConnectionMac::ProcessInputReport, connection, type, buffer)); -} - -void HidConnectionMac::ProcessReadQueue() { - DCHECK(thread_checker_.CalledOnValidThread()); - while (pending_reads_.size() && pending_reports_.size()) { - PendingHidRead read = pending_reads_.front(); - pending_reads_.pop(); - PendingHidReport report = pending_reports_.front(); - if (read.buffer->size() < report.buffer->size()) { - read.callback.Run(false, report.buffer->size()); - } else { - memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size()); - pending_reports_.pop(); - read.callback.Run(true, report.buffer->size()); - } - } -} - -void HidConnectionMac::ProcessInputReport( - IOHIDReportType type, - scoped_refptr buffer) { - DCHECK(thread_checker_.CalledOnValidThread()); - PendingHidReport report; - report.buffer = buffer; - pending_reports_.push(report); - ProcessReadQueue(); + base::Bind(&HidConnectionMac::ProcessInputReport, connection, buffer)); } void HidConnectionMac::WriteReport(IOHIDReportType type, uint8_t report_id, scoped_refptr buffer, const IOCallback& callback) { - DCHECK(thread_checker_.CalledOnValidThread()); if (!device_) { callback.Run(false, 0); return; } + scoped_refptr output_buffer; if (report_id != 0) { output_buffer = new net::IOBufferWithSize(buffer->size() + 1); @@ -173,4 +130,40 @@ void HidConnectionMac::WriteReport(IOHIDReportType type, } } +void HidConnectionMac::Flush() { + while (!pending_reads_.empty()) { + pending_reads_.front().callback.Run(false, 0); + pending_reads_.pop(); + } +} + +void HidConnectionMac::ProcessInputReport( + scoped_refptr buffer) { + DCHECK(thread_checker().CalledOnValidThread()); + PendingHidReport report; + report.buffer = buffer; + pending_reports_.push(report); + ProcessReadQueue(); +} + +void HidConnectionMac::ProcessReadQueue() { + DCHECK(thread_checker().CalledOnValidThread()); + while (pending_reads_.size() && pending_reports_.size()) { + PendingHidRead read = pending_reads_.front(); + PendingHidReport report = pending_reports_.front(); + + if (read.buffer->size() < report.buffer->size()) { + read.callback.Run(false, 0); + pending_reads_.pop(); + } else { + memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size()); + pending_reports_.pop(); + + if (CompleteRead(report.buffer, read.callback)) { + pending_reads_.pop(); + } + } + } +} + } // namespace device diff --git a/device/hid/hid_connection_mac.h b/device/hid/hid_connection_mac.h index c307fb6f878c..02dde044d66e 100644 --- a/device/hid/hid_connection_mac.h +++ b/device/hid/hid_connection_mac.h @@ -11,11 +11,8 @@ #include #include "base/mac/foundation_util.h" -#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" -#include "base/threading/thread_checker.h" #include "device/hid/hid_connection.h" -#include "device/hid/hid_device_info.h" namespace base { class MessageLoopProxy; @@ -31,17 +28,20 @@ class HidConnectionMac : public HidConnection { public: explicit HidConnectionMac(HidDeviceInfo device_info); - virtual void Read(scoped_refptr buffer, - const IOCallback& callback) OVERRIDE; - virtual void Write(uint8_t report_id, - scoped_refptr buffer, - const IOCallback& callback) OVERRIDE; - virtual void GetFeatureReport(uint8_t report_id, - scoped_refptr buffer, - const IOCallback& callback) OVERRIDE; - virtual void SendFeatureReport(uint8_t report_id, - scoped_refptr buffer, - const IOCallback& callback) OVERRIDE; + // HidConnection implementation. + virtual void PlatformRead(scoped_refptr buffer, + const IOCallback& callback) OVERRIDE; + virtual void PlatformWrite(uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback) OVERRIDE; + virtual void PlatformGetFeatureReport( + uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback) OVERRIDE; + virtual void PlatformSendFeatureReport( + uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback) OVERRIDE; private: virtual ~HidConnectionMac(); @@ -53,29 +53,26 @@ class HidConnectionMac : public HidConnection { uint32_t report_id, uint8_t* report_bytes, CFIndex report_length); - void ProcessReadQueue(); - void ProcessInputReport(IOHIDReportType type, - scoped_refptr buffer); void WriteReport(IOHIDReportType type, uint8_t report_id, scoped_refptr buffer, const IOCallback& callback); - scoped_refptr message_loop_; + void Flush(); + void ProcessInputReport(scoped_refptr buffer); + void ProcessReadQueue(); base::ScopedCFTypeRef device_; + scoped_refptr message_loop_; scoped_ptr inbound_buffer_; std::queue pending_reports_; std::queue pending_reads_; - base::ThreadChecker thread_checker_; - DISALLOW_COPY_AND_ASSIGN(HidConnectionMac); }; - } // namespace device #endif // DEVICE_HID_HID_CONNECTION_MAC_H_ diff --git a/device/hid/hid_connection_win.cc b/device/hid/hid_connection_win.cc index 17448f071823..767feacbf9b5 100644 --- a/device/hid/hid_connection_win.cc +++ b/device/hid/hid_connection_win.cc @@ -8,12 +8,7 @@ #include "base/files/file.h" #include "base/message_loop/message_loop.h" -#include "base/stl_util.h" -#include "base/threading/thread_restrictions.h" #include "base/win/object_watcher.h" -#include "base/win/scoped_handle.h" -#include "device/hid/hid_service.h" -#include "device/hid/hid_service_win.h" #define INITGUID @@ -103,7 +98,6 @@ void PendingHidTransfer::WillDestroyCurrentMessageLoop() { HidConnectionWin::HidConnectionWin(const HidDeviceInfo& device_info) : HidConnection(device_info) { - DCHECK(thread_checker_.CalledOnValidThread()); file_.Set(CreateFileA(device_info.device_id.c_str(), GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, @@ -124,40 +118,15 @@ HidConnectionWin::HidConnectionWin(const HidDeviceInfo& device_info) } } -bool HidConnectionWin::available() const { - return file_.IsValid(); -} - HidConnectionWin::~HidConnectionWin() { - DCHECK(thread_checker_.CalledOnValidThread()); CancelIo(file_.Get()); } -void HidConnectionWin::Read(scoped_refptr buffer, - const HidConnection::IOCallback& callback) { - DCHECK(thread_checker_.CalledOnValidThread()); - if (device_info().input_report_size == 0) { - // The device does not support input reports. - callback.Run(false, 0); - return; - } - - // This fairly awkward logic is correct: If Windows does not expect a device - // to supply a report ID in its input reports, it requires the buffer to be - // 1 byte larger than what the device actually sends. - int receive_buffer_size = device_info().input_report_size; - int expected_buffer_size = receive_buffer_size; - if (!device_info().has_report_id) - expected_buffer_size -= 1; - - if (buffer->size() < expected_buffer_size) { - callback.Run(false, 0); - return; - } +void HidConnectionWin::PlatformRead(scoped_refptr buffer, + const HidConnection::IOCallback& callback) { + scoped_refptr receive_buffer = + new net::IOBufferWithSize(device_info().max_input_report_size); - scoped_refptr receive_buffer(buffer); - if (receive_buffer_size != expected_buffer_size) - receive_buffer = new net::IOBufferWithSize(receive_buffer_size); scoped_refptr transfer( new PendingHidTransfer(this, buffer, receive_buffer, callback)); transfers_.insert(transfer); @@ -169,16 +138,10 @@ void HidConnectionWin::Read(scoped_refptr buffer, transfer->GetOverlapped())); } -void HidConnectionWin::Write(uint8_t report_id, - scoped_refptr buffer, - const HidConnection::IOCallback& callback) { - DCHECK(thread_checker_.CalledOnValidThread()); - if (device_info().output_report_size == 0) { - // The device does not support output reports. - callback.Run(false, 0); - return; - } - +void HidConnectionWin::PlatformWrite( + uint8_t report_id, + scoped_refptr buffer, + const HidConnection::IOCallback& callback) { // The Windows API always wants either a report ID (if supported) or // zero at the front of every output report. scoped_refptr output_buffer(buffer); @@ -197,32 +160,15 @@ void HidConnectionWin::Write(uint8_t report_id, transfer->GetOverlapped())); } -void HidConnectionWin::GetFeatureReport( +void HidConnectionWin::PlatformGetFeatureReport( uint8_t report_id, scoped_refptr buffer, const IOCallback& callback) { - DCHECK(thread_checker_.CalledOnValidThread()); - if (device_info().feature_report_size == 0) { - // The device does not support feature reports. - callback.Run(false, 0); - return; - } - - int receive_buffer_size = device_info().feature_report_size; - int expected_buffer_size = receive_buffer_size; - if (!device_info().has_report_id) - expected_buffer_size -= 1; - if (buffer->size() < expected_buffer_size) { - callback.Run(false, 0); - return; - } - - scoped_refptr receive_buffer(buffer); - if (receive_buffer_size != expected_buffer_size) - receive_buffer = new net::IOBufferWithSize(receive_buffer_size); - + scoped_refptr receive_buffer = + new net::IOBufferWithSize(device_info().max_feature_report_size); // The first byte of the destination buffer is the report ID being requested. receive_buffer->data()[0] = report_id; + scoped_refptr transfer( new PendingHidTransfer(this, buffer, receive_buffer, callback)); transfers_.insert(transfer); @@ -237,17 +183,10 @@ void HidConnectionWin::GetFeatureReport( transfer->GetOverlapped())); } -void HidConnectionWin::SendFeatureReport( +void HidConnectionWin::PlatformSendFeatureReport( uint8_t report_id, scoped_refptr buffer, const IOCallback& callback) { - DCHECK(thread_checker_.CalledOnValidThread()); - if (device_info().feature_report_size == 0) { - // The device does not support feature reports. - callback.Run(false, 0); - return; - } - // The Windows API always wants either a report ID (if supported) or // zero at the front of every output report. scoped_refptr output_buffer(buffer); @@ -270,25 +209,42 @@ void HidConnectionWin::SendFeatureReport( void HidConnectionWin::OnTransferFinished( scoped_refptr transfer) { - DWORD bytes_transferred; transfers_.erase(transfer); + + DWORD bytes_transferred; if (GetOverlappedResult( file_, transfer->GetOverlapped(), &bytes_transferred, FALSE)) { - if (bytes_transferred == 0) + if (bytes_transferred == 0) { transfer->callback_.Run(true, 0); - // If this is an input transfer and the receive buffer is not the same as - // the target buffer, 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 (transfer->receive_buffer_ && - transfer->receive_buffer_ != transfer->target_buffer_) { - // Move one byte forward. - --bytes_transferred; - memcpy(transfer->target_buffer_->data(), - transfer->receive_buffer_->data() + 1, - bytes_transferred); + return; } - transfer->callback_.Run(true, bytes_transferred); + + 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 (!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_, transfer->callback_); } else { transfer->callback_.Run(false, 0); } diff --git a/device/hid/hid_connection_win.h b/device/hid/hid_connection_win.h index 263897a7eef2..6706044fab8c 100644 --- a/device/hid/hid_connection_win.h +++ b/device/hid/hid_connection_win.h @@ -9,12 +9,8 @@ #include -#include "base/callback.h" -#include "base/memory/ref_counted.h" -#include "base/message_loop/message_loop.h" -#include "base/threading/thread_checker.h" +#include "base/win/scoped_handle.h" #include "device/hid/hid_connection.h" -#include "device/hid/hid_device_info.h" namespace device { @@ -24,30 +20,35 @@ class HidConnectionWin : public HidConnection { public: explicit HidConnectionWin(const HidDeviceInfo& device_info); - bool available() const; + // HidConnection implementation. + virtual void PlatformRead(scoped_refptr buffer, + const IOCallback& callback) OVERRIDE; + virtual void PlatformWrite(uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback) OVERRIDE; + virtual void PlatformGetFeatureReport( + uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback) OVERRIDE; + virtual void PlatformSendFeatureReport( + uint8_t report_id, + scoped_refptr buffer, + const IOCallback& callback) OVERRIDE; - virtual void Read(scoped_refptr buffer, - const IOCallback& callback) OVERRIDE; - virtual void Write(uint8_t report_id, - scoped_refptr buffer, - const IOCallback& callback) OVERRIDE; - virtual void GetFeatureReport(uint8_t report_id, - scoped_refptr buffer, - const IOCallback& callback) OVERRIDE; - virtual void SendFeatureReport(uint8_t report_id, - scoped_refptr buffer, - const IOCallback& callback) OVERRIDE; + private: + friend class HidServiceWin; + friend struct PendingHidTransfer; + + ~HidConnectionWin(); + + bool available() const { return file_.IsValid(); } void OnTransferFinished(scoped_refptr transfer); void OnTransferCanceled(scoped_refptr transfer); - private: - ~HidConnectionWin(); - base::win::ScopedHandle file_; - std::set > transfers_; - base::ThreadChecker thread_checker_; + std::set > transfers_; DISALLOW_COPY_AND_ASSIGN(HidConnectionWin); }; diff --git a/device/hid/hid_device_info.cc b/device/hid/hid_device_info.cc index 89be442b90ad..26891f8b95fd 100644 --- a/device/hid/hid_device_info.cc +++ b/device/hid/hid_device_info.cc @@ -12,13 +12,13 @@ const char kInvalidHidDeviceId[] = ""; HidDeviceInfo::HidDeviceInfo() : device_id(kInvalidHidDeviceId), - bus_type(kHIDBusTypeUSB), vendor_id(0), product_id(0), - input_report_size(0), - output_report_size(0), - feature_report_size(0), - has_report_id(false) {} + bus_type(kHIDBusTypeUSB), + max_input_report_size(0), + max_output_report_size(0), + max_feature_report_size(0) { +} HidDeviceInfo::~HidDeviceInfo() {} diff --git a/device/hid/hid_device_info.h b/device/hid/hid_device_info.h index 1b143c225353..2ed51ac4f52c 100644 --- a/device/hid/hid_device_info.h +++ b/device/hid/hid_device_info.h @@ -9,7 +9,7 @@ #include #include "build/build_config.h" -#include "device/hid/hid_usage_and_page.h" +#include "device/hid/hid_collection_info.h" #if defined(OS_MACOSX) #include @@ -34,20 +34,19 @@ struct HidDeviceInfo { HidDeviceInfo(); ~HidDeviceInfo(); + // Device identification. HidDeviceId device_id; - - HidBusType bus_type; uint16_t vendor_id; uint16_t product_id; - - int input_report_size; - int output_report_size; - int feature_report_size; - std::vector usages; - bool has_report_id; - std::string product_name; std::string serial_number; + HidBusType bus_type; + + // Top-Level Collections information. + std::vector collections; + int max_input_report_size; + int max_output_report_size; + int max_feature_report_size; }; } // namespace device diff --git a/device/hid/hid_report_descriptor.cc b/device/hid/hid_report_descriptor.cc index f2cb0f4901f1..a06d28421c54 100644 --- a/device/hid/hid_report_descriptor.cc +++ b/device/hid/hid_report_descriptor.cc @@ -8,6 +8,12 @@ namespace device { +namespace { + +const int kBitsPerByte = 8; + +} // namespace + HidReportDescriptor::HidReportDescriptor(const uint8_t* bytes, size_t size) { size_t header_index = 0; HidReportDescriptorItem* item = NULL; @@ -20,42 +26,148 @@ HidReportDescriptor::HidReportDescriptor(const uint8_t* bytes, size_t size) { HidReportDescriptor::~HidReportDescriptor() {} -void HidReportDescriptor::GetTopLevelCollections( - std::vector* topLevelCollections) { - DCHECK(topLevelCollections); - STLClearObject(topLevelCollections); +void HidReportDescriptor::GetDetails( + std::vector* top_level_collections, + int* max_input_report_size, + int* max_output_report_size, + int* max_feature_report_size) { + DCHECK(top_level_collections); + DCHECK(max_input_report_size); + DCHECK(max_output_report_size); + DCHECK(max_feature_report_size); + STLClearObject(top_level_collections); + + *max_input_report_size = 0; + *max_output_report_size = 0; + *max_feature_report_size = 0; + + // Global tags data: + HidUsageAndPage::Page current_usage_page = HidUsageAndPage::kPageUndefined; + int current_report_count = 0; + int cached_report_count = 0; + int current_report_size = 0; + int cached_report_size = 0; + int current_input_report_size = 0; + int current_output_report_size = 0; + int current_feature_report_size = 0; + + // Local tags data: + uint16_t current_usage = 0; for (std::vector >::const_iterator items_iter = items().begin(); items_iter != items().end(); ++items_iter) { - linked_ptr item = *items_iter; - - bool isTopLevelCollection = - item->tag() == HidReportDescriptorItem::kTagCollection && - item->parent() == NULL; - - if (isTopLevelCollection) { - uint16_t collection_usage = 0; - HidUsageAndPage::Page collection_usage_page = - HidUsageAndPage::kPageUndefined; - - HidReportDescriptorItem* usage = item->previous(); - if (usage && usage->tag() == HidReportDescriptorItem::kTagUsage) { - collection_usage = usage->GetShortData(); - } - - HidReportDescriptorItem* usage_page = usage->previous(); - if (usage_page && - usage_page->tag() == HidReportDescriptorItem::kTagUsagePage) { - collection_usage_page = - (HidUsageAndPage::Page)usage_page->GetShortData(); - } - - topLevelCollections->push_back( - HidUsageAndPage(collection_usage, collection_usage_page)); + linked_ptr current_item = *items_iter; + + switch (current_item->tag()) { + // Main tags: + case HidReportDescriptorItem::kTagCollection: + if (!current_item->parent()) { + // This is a top-level collection. + HidCollectionInfo collection; + collection.usage = HidUsageAndPage(current_usage, current_usage_page); + top_level_collections->push_back(collection); + } + break; + case HidReportDescriptorItem::kTagInput: + current_input_report_size += current_report_count * current_report_size; + break; + case HidReportDescriptorItem::kTagOutput: + current_output_report_size += + current_report_count * current_report_size; + break; + case HidReportDescriptorItem::kTagFeature: + current_feature_report_size += + current_report_count * current_report_size; + break; + + // Global tags: + case HidReportDescriptorItem::kTagUsagePage: + current_usage_page = + (HidUsageAndPage::Page)current_item->GetShortData(); + break; + case HidReportDescriptorItem::kTagReportId: + if (top_level_collections->size() > 0) { + // Store report ID. + top_level_collections->back().report_ids.insert( + current_item->GetShortData()); + + // We need to increase report sizes by report ID field length. + if (current_input_report_size > 0) + current_input_report_size += kBitsPerByte; + if (current_output_report_size > 0) + current_output_report_size += kBitsPerByte; + if (current_feature_report_size > 0) + current_feature_report_size += kBitsPerByte; + + // Update max report sizes. + *max_input_report_size = + std::max(*max_input_report_size, current_input_report_size); + *max_output_report_size = + std::max(*max_output_report_size, current_output_report_size); + *max_feature_report_size = + std::max(*max_feature_report_size, current_feature_report_size); + + // Set report sizes to be 1-byte long (report ID field). + current_input_report_size = 0; + current_output_report_size = 0; + current_feature_report_size = 0; + } + break; + case HidReportDescriptorItem::kTagReportCount: + current_report_count = current_item->GetShortData(); + break; + case HidReportDescriptorItem::kTagReportSize: + current_report_size = current_item->GetShortData(); + break; + case HidReportDescriptorItem::kTagPush: + // Cache report count and size. + cached_report_count = current_report_count; + cached_report_size = current_report_size; + break; + case HidReportDescriptorItem::kTagPop: + // Restore cache. + current_report_count = cached_report_count; + current_report_size = cached_report_size; + // Reset cache. + cached_report_count = 0; + cached_report_size = 0; + break; + + // Local tags: + case HidReportDescriptorItem::kTagUsage: + current_usage = current_item->GetShortData(); + break; + + default: + break; } } + + if (top_level_collections->size() > 0 && + top_level_collections->back().report_ids.size() > 0) { + // We need to increase report sizes by report ID field length. + if (current_input_report_size > 0) + current_input_report_size += kBitsPerByte; + if (current_output_report_size > 0) + current_output_report_size += kBitsPerByte; + if (current_feature_report_size > 0) + current_feature_report_size += kBitsPerByte; + } + + // Update max report sizes + *max_input_report_size = + std::max(*max_input_report_size, current_input_report_size); + *max_output_report_size = + std::max(*max_output_report_size, current_output_report_size); + *max_feature_report_size = + std::max(*max_feature_report_size, current_feature_report_size); + + // Convert bits into bytes + *max_input_report_size /= kBitsPerByte; + *max_output_report_size /= kBitsPerByte; + *max_feature_report_size /= kBitsPerByte; } } // namespace device diff --git a/device/hid/hid_report_descriptor.h b/device/hid/hid_report_descriptor.h index fa67fa43c291..94d90ad6ce69 100644 --- a/device/hid/hid_report_descriptor.h +++ b/device/hid/hid_report_descriptor.h @@ -8,8 +8,8 @@ #include #include "base/memory/linked_ptr.h" +#include "device/hid/hid_collection_info.h" #include "device/hid/hid_report_descriptor_item.h" -#include "device/hid/hid_usage_and_page.h" namespace device { @@ -25,9 +25,12 @@ class HidReportDescriptor { return items_; } - // Returns HID usages of top-level collections present in the descriptor. - void GetTopLevelCollections( - std::vector* topLevelCollections); + // Returns top-level collections present in the descriptor, + // together with max report sizes + void GetDetails(std::vector* top_level_collections, + int* max_input_report_size, + int* max_output_report_size, + int* max_feature_report_size); private: std::vector > items_; diff --git a/device/hid/hid_report_descriptor_item.cc b/device/hid/hid_report_descriptor_item.cc index bdd03ce0ebd3..60ba764bc93b 100644 --- a/device/hid/hid_report_descriptor_item.cc +++ b/device/hid/hid_report_descriptor_item.cc @@ -90,17 +90,17 @@ HidReportDescriptorItem::GetCollectionTypeFromValue(uint32_t value) { case 0x00: return kCollectionTypePhysical; case 0x01: - return kCollectionTypePhysical; + return kCollectionTypeApplication; case 0x02: - return kCollectionTypePhysical; + return kCollectionTypeLogical; case 0x03: - return kCollectionTypePhysical; + return kCollectionTypeReport; case 0x04: - return kCollectionTypePhysical; + return kCollectionTypeNamedArray; case 0x05: - return kCollectionTypePhysical; + return kCollectionTypeUsageSwitch; case 0x06: - return kCollectionTypePhysical; + return kCollectionTypeUsageModifier; default: break; } diff --git a/device/hid/hid_report_descriptor_unittest.cc b/device/hid/hid_report_descriptor_unittest.cc dissimilarity index 94% index 0d258891081e..619e682fed55 100644 --- a/device/hid/hid_report_descriptor_unittest.cc +++ b/device/hid/hid_report_descriptor_unittest.cc @@ -1,613 +1,429 @@ -// Copyright (c) 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include "device/hid/hid_report_descriptor.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using namespace testing; - -namespace device { - -namespace { - -std::ostream& operator<<(std::ostream& os, - const HidUsageAndPage::Page& usage_page) { - switch (usage_page) { - case HidUsageAndPage::kPageUndefined: - os << "Undefined"; - break; - case HidUsageAndPage::kPageGenericDesktop: - os << "Generic Desktop"; - break; - case HidUsageAndPage::kPageSimulation: - os << "Simulation"; - break; - case HidUsageAndPage::kPageVirtualReality: - os << "Virtual Reality"; - break; - case HidUsageAndPage::kPageSport: - os << "Sport"; - break; - case HidUsageAndPage::kPageGame: - os << "Game"; - break; - case HidUsageAndPage::kPageKeyboard: - os << "Keyboard"; - break; - case HidUsageAndPage::kPageLed: - os << "Led"; - break; - case HidUsageAndPage::kPageButton: - os << "Button"; - break; - case HidUsageAndPage::kPageOrdinal: - os << "Ordinal"; - break; - case HidUsageAndPage::kPageTelephony: - os << "Telephony"; - break; - case HidUsageAndPage::kPageConsumer: - os << "Consumer"; - break; - case HidUsageAndPage::kPageDigitizer: - os << "Digitizer"; - break; - case HidUsageAndPage::kPagePidPage: - os << "Pid Page"; - break; - case HidUsageAndPage::kPageUnicode: - os << "Unicode"; - break; - case HidUsageAndPage::kPageAlphanumericDisplay: - os << "Alphanumeric Display"; - break; - case HidUsageAndPage::kPageMedicalInstruments: - os << "Medical Instruments"; - break; - case HidUsageAndPage::kPageMonitor0: - os << "Monitor 0"; - break; - case HidUsageAndPage::kPageMonitor1: - os << "Monitor 1"; - break; - case HidUsageAndPage::kPageMonitor2: - os << "Monitor 2"; - break; - case HidUsageAndPage::kPageMonitor3: - os << "Monitor 3"; - break; - case HidUsageAndPage::kPagePower0: - os << "Power 0"; - break; - case HidUsageAndPage::kPagePower1: - os << "Power 1"; - break; - case HidUsageAndPage::kPagePower2: - os << "Power 2"; - break; - case HidUsageAndPage::kPagePower3: - os << "Power 3"; - break; - case HidUsageAndPage::kPageBarCodeScanner: - os << "Bar Code Scanner"; - break; - case HidUsageAndPage::kPageScale: - os << "Scale"; - break; - case HidUsageAndPage::kPageMagneticStripeReader: - os << "Magnetic Stripe Reader"; - break; - case HidUsageAndPage::kPageReservedPointOfSale: - os << "Reserved Point Of Sale"; - break; - case HidUsageAndPage::kPageCameraControl: - os << "Camera Control"; - break; - case HidUsageAndPage::kPageArcade: - os << "Arcade"; - break; - case HidUsageAndPage::kPageVendor: - os << "Vendor"; - break; - case HidUsageAndPage::kPageMediaCenter: - os << "Media Center"; - break; - default: - NOTREACHED(); - break; - } - return os; -} - -std::ostream& operator<<(std::ostream& os, - const HidUsageAndPage& usage_and_page) { - os << "Usage Page: " << usage_and_page.usage_page << ", Usage: " - << "0x" << std::hex << std::uppercase << usage_and_page.usage; - return os; -} - -std::ostream& operator<<(std::ostream& os, - const HidReportDescriptorItem::Tag& tag) { - switch (tag) { - case HidReportDescriptorItem::kTagDefault: - os << "Default"; - break; - case HidReportDescriptorItem::kTagInput: - os << "Input"; - break; - case HidReportDescriptorItem::kTagOutput: - os << "Output"; - break; - case HidReportDescriptorItem::kTagFeature: - os << "Feature"; - break; - case HidReportDescriptorItem::kTagCollection: - os << "Collection"; - break; - case HidReportDescriptorItem::kTagEndCollection: - os << "End Collection"; - break; - case HidReportDescriptorItem::kTagUsagePage: - os << "Usage Page"; - break; - case HidReportDescriptorItem::kTagLogicalMinimum: - os << "Logical Minimum"; - break; - case HidReportDescriptorItem::kTagLogicalMaximum: - os << "Logical Maximum"; - break; - case HidReportDescriptorItem::kTagPhysicalMinimum: - os << "Physical Minimum"; - break; - case HidReportDescriptorItem::kTagPhysicalMaximum: - os << "Physical Maximum"; - break; - case HidReportDescriptorItem::kTagUnitExponent: - os << "Unit Exponent"; - break; - case HidReportDescriptorItem::kTagUnit: - os << "Unit"; - break; - case HidReportDescriptorItem::kTagReportSize: - os << "Report Size"; - break; - case HidReportDescriptorItem::kTagReportId: - os << "Report ID"; - break; - case HidReportDescriptorItem::kTagReportCount: - os << "Report Count"; - break; - case HidReportDescriptorItem::kTagPush: - os << "Push"; - break; - case HidReportDescriptorItem::kTagPop: - os << "Pop"; - break; - case HidReportDescriptorItem::kTagUsage: - os << "Usage"; - break; - case HidReportDescriptorItem::kTagUsageMinimum: - os << "Usage Minimum"; - break; - case HidReportDescriptorItem::kTagUsageMaximum: - os << "Usage Maximum"; - break; - case HidReportDescriptorItem::kTagDesignatorIndex: - os << "Designator Index"; - break; - case HidReportDescriptorItem::kTagDesignatorMinimum: - os << "Designator Minimum"; - break; - case HidReportDescriptorItem::kTagDesignatorMaximum: - os << "Designator Maximum"; - break; - case HidReportDescriptorItem::kTagStringIndex: - os << "String Index"; - break; - case HidReportDescriptorItem::kTagStringMinimum: - os << "String Minimum"; - break; - case HidReportDescriptorItem::kTagStringMaximum: - os << "String Maximum"; - break; - case HidReportDescriptorItem::kTagDelimiter: - os << "Delimeter"; - break; - case HidReportDescriptorItem::kTagLong: - os << "Long"; - break; - default: - NOTREACHED(); - break; - } - - return os; -} - -std::ostream& operator<<(std::ostream& os, - const HidReportDescriptorItem::ReportInfo& data) { - if (data.data_or_constant) - os << "Con"; - else - os << "Dat"; - if (data.array_or_variable) - os << "|Arr"; - else - os << "|Var"; - if (data.absolute_or_relative) - os << "|Abs"; - else - os << "|Rel"; - if (data.wrap) - os << "|Wrp"; - else - os << "|NoWrp"; - if (data.linear) - os << "|NoLin"; - else - os << "|Lin"; - if (data.preferred) - os << "|NoPrf"; - else - os << "|Prf"; - if (data.null) - os << "|Null"; - else - os << "|NoNull"; - if (data.bit_field_or_buffer) - os << "|Buff"; - else - os << "|BitF"; - return os; -} - -std::ostream& operator<<(std::ostream& os, - const HidReportDescriptorItem::CollectionType& type) { - switch (type) { - case HidReportDescriptorItem::kCollectionTypePhysical: - os << "Physical"; - break; - case HidReportDescriptorItem::kCollectionTypeApplication: - os << "Application"; - break; - case HidReportDescriptorItem::kCollectionTypeLogical: - os << "Logical"; - break; - case HidReportDescriptorItem::kCollectionTypeReport: - os << "Report"; - break; - case HidReportDescriptorItem::kCollectionTypeNamedArray: - os << "Named Array"; - break; - case HidReportDescriptorItem::kCollectionTypeUsageSwitch: - os << "Usage Switch"; - break; - case HidReportDescriptorItem::kCollectionTypeUsageModifier: - os << "Usage Modifier"; - break; - case HidReportDescriptorItem::kCollectionTypeReserved: - os << "Reserved"; - break; - case HidReportDescriptorItem::kCollectionTypeVendor: - os << "Vendor"; - break; - default: - NOTREACHED(); - break; - } - return os; -} - -std::ostream& operator<<(std::ostream& os, - const HidReportDescriptorItem& item) { - HidReportDescriptorItem::Tag item_tag = item.tag(); - uint32_t data = item.GetShortData(); - - std::ostringstream sstr; - sstr << item_tag; - sstr << " ("; - - long pos = sstr.tellp(); - switch (item_tag) { - case HidReportDescriptorItem::kTagDefault: - case HidReportDescriptorItem::kTagEndCollection: - case HidReportDescriptorItem::kTagPush: - case HidReportDescriptorItem::kTagPop: - case HidReportDescriptorItem::kTagLong: - break; - - case HidReportDescriptorItem::kTagCollection: - sstr << HidReportDescriptorItem::GetCollectionTypeFromValue(data); - break; - - case HidReportDescriptorItem::kTagInput: - case HidReportDescriptorItem::kTagOutput: - case HidReportDescriptorItem::kTagFeature: - sstr << (HidReportDescriptorItem::ReportInfo&)data; - break; - - case HidReportDescriptorItem::kTagUsagePage: - sstr << (HidUsageAndPage::Page)data; - break; - - case HidReportDescriptorItem::kTagUsage: - case HidReportDescriptorItem::kTagReportId: - sstr << "0x" << std::hex << std::uppercase << data; - break; - - default: - sstr << data; - break; - } - if (pos == sstr.tellp()) { - std::string str = sstr.str(); - str.erase(str.end() - 2, str.end()); - os << str; - } else { - os << sstr.str() << ")"; - } - - return os; -} - -const char kIndentStep[] = " "; - -std::ostream& operator<<(std::ostream& os, - const HidReportDescriptor& descriptor) { - for (std::vector >::const_iterator - items_iter = descriptor.items().begin(); - items_iter != descriptor.items().end(); - ++items_iter) { - linked_ptr item = *items_iter; - size_t indentLevel = item->GetDepth(); - for (size_t i = 0; i < indentLevel; i++) - os << kIndentStep; - os << *item.get() << std::endl; - } - return os; -} - -// See 'E.6 Report Descriptor (Keyboard)' -// in HID specifications (v1.11) -const uint8_t kKeyboard[] = { - 0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0, 0x29, - 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, - 0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x05, 0x75, 0x01, 0x05, - 0x08, 0x19, 0x01, 0x29, 0x05, 0x91, 0x02, 0x95, 0x01, 0x75, 0x03, - 0x91, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05, - 0x07, 0x19, 0x00, 0x29, 0x65, 0x81, 0x00, 0xC0}; - -// See 'E.10 Report Descriptor (Mouse)' -// in HID specifications (v1.11) -const uint8_t kMouse[] = {0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, 0xA1, - 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, 0x15, 0x00, - 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, 0x81, 0x02, 0x95, - 0x01, 0x75, 0x05, 0x81, 0x01, 0x05, 0x01, 0x09, 0x30, - 0x09, 0x31, 0x15, 0x81, 0x25, 0x7F, 0x75, 0x08, 0x95, - 0x02, 0x81, 0x06, 0xC0, 0xC0}; - -const uint8_t kLogitechUnifyingReceiver[] = { - 0x06, 0x00, 0xFF, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x10, 0x75, 0x08, - 0x95, 0x06, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x09, 0x01, 0x81, 0x00, - 0x09, 0x01, 0x91, 0x00, 0xC0, 0x06, 0x00, 0xFF, 0x09, 0x02, 0xA1, - 0x01, 0x85, 0x11, 0x75, 0x08, 0x95, 0x13, 0x15, 0x00, 0x26, 0xFF, - 0x00, 0x09, 0x02, 0x81, 0x00, 0x09, 0x02, 0x91, 0x00, 0xC0, 0x06, - 0x00, 0xFF, 0x09, 0x04, 0xA1, 0x01, 0x85, 0x20, 0x75, 0x08, 0x95, - 0x0E, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x09, 0x41, 0x81, 0x00, 0x09, - 0x41, 0x91, 0x00, 0x85, 0x21, 0x95, 0x1F, 0x15, 0x00, 0x26, 0xFF, - 0x00, 0x09, 0x42, 0x81, 0x00, 0x09, 0x42, 0x91, 0x00, 0xC0}; - -} // namespace - -class HidReportDescriptorTest : public testing::Test { - - protected: - virtual void SetUp() OVERRIDE { descriptor_ = NULL; } - - virtual void TearDown() OVERRIDE { - if (descriptor_) { - delete descriptor_; - } - } - - public: - void ParseDescriptor(const std::string& expected, - const uint8_t* bytes, - size_t size) { - descriptor_ = new HidReportDescriptor(bytes, size); - - std::stringstream actual; - actual << *descriptor_; - - std::cout << "HID report descriptor:" << std::endl; - std::cout << actual.str(); - - // TODO(jracle@logitech.com): refactor string comparison in favor of - // testing individual fields. - ASSERT_EQ(expected, actual.str()); - } - - void GetTopLevelCollections(const std::vector& expected, - const uint8_t* bytes, - size_t size) { - descriptor_ = new HidReportDescriptor(bytes, size); - - std::vector actual; - descriptor_->GetTopLevelCollections(&actual); - - std::cout << "HID top-level collections:" << std::endl; - for (std::vector::const_iterator iter = actual.begin(); - iter != actual.end(); - ++iter) { - std::cout << *iter << std::endl; - } - - ASSERT_THAT(actual, ContainerEq(expected)); - } - - private: - HidReportDescriptor* descriptor_; -}; - -TEST_F(HidReportDescriptorTest, ParseDescriptor_Keyboard) { - const char expected[] = { - "Usage Page (Generic Desktop)\n" - "Usage (0x6)\n" - "Collection (Physical)\n" - " Usage Page (Keyboard)\n" - " Usage Minimum (224)\n" - " Usage Maximum (231)\n" - " Logical Minimum (0)\n" - " Logical Maximum (1)\n" - " Report Size (1)\n" - " Report Count (8)\n" - " Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n" - " Report Count (1)\n" - " Report Size (8)\n" - " Input (Con|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n" - " Report Count (5)\n" - " Report Size (1)\n" - " Usage Page (Led)\n" - " Usage Minimum (1)\n" - " Usage Maximum (5)\n" - " Output (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n" - " Report Count (1)\n" - " Report Size (3)\n" - " Output (Con|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n" - " Report Count (6)\n" - " Report Size (8)\n" - " Logical Minimum (0)\n" - " Logical Maximum (101)\n" - " Usage Page (Keyboard)\n" - " Usage Minimum (0)\n" - " Usage Maximum (101)\n" - " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n" - "End Collection\n"}; - - ParseDescriptor(std::string(expected), kKeyboard, sizeof(kKeyboard)); -} - -TEST_F(HidReportDescriptorTest, TopLevelCollections_Keyboard) { - HidUsageAndPage expected[] = { - HidUsageAndPage(0x06, HidUsageAndPage::kPageGenericDesktop)}; - - GetTopLevelCollections(std::vector( - expected, expected + ARRAYSIZE_UNSAFE(expected)), - kKeyboard, - sizeof(kKeyboard)); -} - -TEST_F(HidReportDescriptorTest, ParseDescriptor_Mouse) { - const char expected[] = { - "Usage Page (Generic Desktop)\n" - "Usage (0x2)\n" - "Collection (Physical)\n" - " Usage (0x1)\n" - " Collection (Physical)\n" - " Usage Page (Button)\n" - " Usage Minimum (1)\n" - " Usage Maximum (3)\n" - " Logical Minimum (0)\n" - " Logical Maximum (1)\n" - " Report Count (3)\n" - " Report Size (1)\n" - " Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n" - " Report Count (1)\n" - " Report Size (5)\n" - " Input (Con|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n" - " Usage Page (Generic Desktop)\n" - " Usage (0x30)\n" - " Usage (0x31)\n" - " Logical Minimum (129)\n" - " Logical Maximum (127)\n" - " Report Size (8)\n" - " Report Count (2)\n" - " Input (Dat|Arr|Abs|NoWrp|Lin|Prf|NoNull|BitF)\n" - " End Collection\n" - "End Collection\n"}; - - ParseDescriptor(std::string(expected), kMouse, sizeof(kMouse)); -} - -TEST_F(HidReportDescriptorTest, TopLevelCollections_Mouse) { - HidUsageAndPage expected[] = { - HidUsageAndPage(0x02, HidUsageAndPage::kPageGenericDesktop)}; - - GetTopLevelCollections(std::vector( - expected, expected + ARRAYSIZE_UNSAFE(expected)), - kMouse, - sizeof(kMouse)); -} - -TEST_F(HidReportDescriptorTest, ParseDescriptor_LogitechUnifyingReceiver) { - const char expected[] = { - "Usage Page (Vendor)\n" - "Usage (0x1)\n" - "Collection (Physical)\n" - " Report ID (0x10)\n" - " Report Size (8)\n" - " Report Count (6)\n" - " Logical Minimum (0)\n" - " Logical Maximum (255)\n" - " Usage (0x1)\n" - " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n" - " Usage (0x1)\n" - " Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n" - "End Collection\n" - "Usage Page (Vendor)\n" - "Usage (0x2)\n" - "Collection (Physical)\n" - " Report ID (0x11)\n" - " Report Size (8)\n" - " Report Count (19)\n" - " Logical Minimum (0)\n" - " Logical Maximum (255)\n" - " Usage (0x2)\n" - " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n" - " Usage (0x2)\n" - " Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n" - "End Collection\n" - "Usage Page (Vendor)\n" - "Usage (0x4)\n" - "Collection (Physical)\n" - " Report ID (0x20)\n" - " Report Size (8)\n" - " Report Count (14)\n" - " Logical Minimum (0)\n" - " Logical Maximum (255)\n" - " Usage (0x41)\n" - " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n" - " Usage (0x41)\n" - " Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n" - " Report ID (0x21)\n" - " Report Count (31)\n" - " Logical Minimum (0)\n" - " Logical Maximum (255)\n" - " Usage (0x42)\n" - " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n" - " Usage (0x42)\n" - " Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n" - "End Collection\n"}; - - ParseDescriptor(std::string(expected), - kLogitechUnifyingReceiver, - sizeof(kLogitechUnifyingReceiver)); -} - -TEST_F(HidReportDescriptorTest, TopLevelCollections_LogitechUnifyingReceiver) { - HidUsageAndPage expected[] = { - HidUsageAndPage(0x01, HidUsageAndPage::kPageVendor), - HidUsageAndPage(0x02, HidUsageAndPage::kPageVendor), - HidUsageAndPage(0x04, HidUsageAndPage::kPageVendor), }; - - GetTopLevelCollections(std::vector( - expected, expected + ARRAYSIZE_UNSAFE(expected)), - kLogitechUnifyingReceiver, - sizeof(kLogitechUnifyingReceiver)); -} - -} // namespace device +// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "device/hid/hid_report_descriptor.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using namespace testing; + +namespace device { + +namespace { + +// Digitizer descriptor from HID descriptor tool +// http://www.usb.org/developers/hidpage/dt2_4.zip +const uint8_t kDigitizer[] = { + 0x05, 0x0d, // Usage Page (Digitizer) + 0x09, 0x01, // Usage (0x1) + 0xa1, 0x01, // Collection (Application) + 0x85, 0x01, // Report ID (0x1) + 0x09, 0x21, // Usage (0x21) + 0xa1, 0x00, // Collection (Physical) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x30, // Usage (0x30) + 0x09, 0x31, // Usage (0x31) + 0x75, 0x10, // Report Size (16) + 0x95, 0x02, // Report Count (2) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xe0, 0x2e, // Logical Maximum (12000) + 0x35, 0x00, // Physical Minimum (0) + 0x45, 0x0c, // Physical Maximum (12) + 0x65, 0x13, // Unit (19) + 0x55, 0x00, // Unit Exponent (0) + 0xa4, // Push + 0x81, 0x02, // Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x05, 0x0d, // Usage Page (Digitizer) + 0x09, 0x32, // Usage (0x32) + 0x09, 0x44, // Usage (0x44) + 0x09, 0x42, // Usage (0x42) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x35, 0x00, // Physical Minimum (0) + 0x45, 0x01, // Physical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x03, // Report Count (3) + 0x65, 0x00, // Unit (0) + 0x81, 0x02, // Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x95, 0x01, // Report Count (1) + 0x75, 0x05, // Report Size (5) + 0x81, 0x03, // Input (Con|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0xc0, // End Collection + 0x85, 0x02, // Report ID (0x2) + 0x09, 0x20, // Usage (0x20) + 0xa1, 0x00, // Collection (Physical) + 0xb4, // Pop + 0xa4, // Push + 0x09, 0x30, // Usage (0x30) + 0x09, 0x31, // Usage (0x31) + 0x81, 0x02, // Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x05, 0x0d, // Usage Page (Digitizer) + 0x09, 0x32, // Usage (0x32) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x35, 0x00, // Physical Minimum (0) + 0x45, 0x01, // Physical Maximum (1) + 0x65, 0x00, // Unit (0) + 0x75, 0x01, // Report Size (1) + 0x81, 0x02, // Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x00, // Usage Minimum (0) + 0x29, 0x10, // Usage Maximum (16) + 0x25, 0x10, // Logical Maximum (16) + 0x75, 0x05, // Report Size (5) + 0x81, 0x40, // Input (Dat|Var|Rel|NoWrp|Lin|Prf|Null|BitF) + 0x75, 0x02, // Report Size (2) + 0x81, 0x01, // Input (Con|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0xc0, // End Collection + 0x85, 0x03, // Report ID (0x3) + 0x05, 0x0d, // Usage Page (Digitizer) + 0x09, 0x20, // Usage (0x20) + 0xa1, 0x00, // Collection (Physical) + 0xb4, // Pop + 0x09, 0x30, // Usage (0x30) + 0x09, 0x31, // Usage (0x31) + 0x81, 0x02, // Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x05, 0x0d, // Usage Page (Digitizer) + 0x09, 0x32, // Usage (0x32) + 0x09, 0x44, // Usage (0x44) + 0x75, 0x01, // Report Size (1) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x35, 0x00, // Physical Minimum (0) + 0x45, 0x01, // Physical Maximum (1) + 0x65, 0x00, // Unit (0) + 0x81, 0x02, // Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x95, 0x06, // Report Count (6) + 0x81, 0x03, // Input (Con|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x09, 0x30, // Usage (0x30) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x7f, // Logical Maximum (127) + 0x35, 0x00, // Physical Minimum (0) + 0x45, 0x2d, // Physical Maximum (45) + 0x67, 0x11, 0xe1, // Unit (57617) + 0x00, 0x00, // Default + 0x55, 0x04, // Unit Exponent (4) + 0x75, 0x08, // Report Size (8) + 0x95, 0x01, // Report Count (1) + 0x81, 0x12, // Input (Dat|Arr|Rel|NoWrp|NoLin|Prf|NoNull|BitF) + 0xc0, // End Collection + 0xc0 // End Collection +}; + +// Keyboard descriptor from HID descriptor tool +// http://www.usb.org/developers/hidpage/dt2_4.zip +const uint8_t kKeyboard[] = { + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x06, // Usage (0x6) + 0xa1, 0x01, // Collection (Application) + 0x05, 0x07, // Usage Page (Keyboard) + 0x19, 0xe0, // Usage Minimum (224) + 0x29, 0xe7, // Usage Maximum (231) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x08, // Report Count (8) + 0x81, 0x02, // Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x95, 0x01, // Report Count (1) + 0x75, 0x08, // Report Size (8) + 0x81, 0x03, // Input (Con|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x95, 0x05, // Report Count (5) + 0x75, 0x01, // Report Size (1) + 0x05, 0x08, // Usage Page (Led) + 0x19, 0x01, // Usage Minimum (1) + 0x29, 0x05, // Usage Maximum (5) + 0x91, 0x02, // Output (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x95, 0x01, // Report Count (1) + 0x75, 0x03, // Report Size (3) + 0x91, 0x03, // Output (Con|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x95, 0x06, // Report Count (6) + 0x75, 0x08, // Report Size (8) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x65, // Logical Maximum (101) + 0x05, 0x07, // Usage Page (Keyboard) + 0x19, 0x00, // Usage Minimum (0) + 0x29, 0x65, // Usage Maximum (101) + 0x81, 0x00, // Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0xc0 // End Collection +}; + +// Monitor descriptor from HID descriptor tool +// http://www.usb.org/developers/hidpage/dt2_4.zip +const uint8_t kMonitor[] = { + 0x05, 0x80, // Usage Page (Monitor 0) + 0x09, 0x01, // Usage (0x1) + 0xa1, 0x01, // Collection (Application) + 0x85, 0x01, // Report ID (0x1) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xff, 0x00, // Logical Maximum (255) + 0x75, 0x08, // Report Size (8) + 0x95, 0x80, // Report Count (128) + 0x09, 0x02, // Usage (0x2) + 0xb2, 0x02, 0x01, // Feature (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|Buff) + 0x85, 0x02, // Report ID (0x2) + 0x95, 0xf3, // Report Count (243) + 0x09, 0x03, // Usage (0x3) + 0xb2, 0x02, 0x01, // Feature (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|Buff) + 0x85, 0x03, // Report ID (0x3) + 0x05, 0x82, // Usage Page (Monitor 2) + 0x95, 0x01, // Report Count (1) + 0x75, 0x10, // Report Size (16) + 0x26, 0xc8, 0x00, // Logical Maximum (200) + 0x09, 0x10, // Usage (0x10) + 0xb1, 0x02, // Feature (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x85, 0x04, // Report ID (0x4) + 0x25, 0x64, // Logical Maximum (100) + 0x09, 0x12, // Usage (0x12) + 0xb1, 0x02, // Feature (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x95, 0x06, // Report Count (6) + 0x26, 0xff, 0x00, // Logical Maximum (255) + 0x09, 0x16, // Usage (0x16) + 0x09, 0x18, // Usage (0x18) + 0x09, 0x1a, // Usage (0x1A) + 0x09, 0x6c, // Usage (0x6C) + 0x09, 0x6e, // Usage (0x6E) + 0x09, 0x70, // Usage (0x70) + 0xb1, 0x02, // Feature (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x85, 0x05, // Report ID (0x5) + 0x25, 0x7f, // Logical Maximum (127) + 0x09, 0x20, // Usage (0x20) + 0x09, 0x22, // Usage (0x22) + 0x09, 0x30, // Usage (0x30) + 0x09, 0x32, // Usage (0x32) + 0x09, 0x42, // Usage (0x42) + 0x09, 0x44, // Usage (0x44) + 0xb1, 0x02, // Feature (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0xc0 // End Collection +}; + +// Mouse descriptor from HID descriptor tool +// http://www.usb.org/developers/hidpage/dt2_4.zip +const uint8_t kMouse[] = { + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x02, // Usage (0x2) + 0xa1, 0x01, // Collection (Application) + 0x09, 0x01, // Usage (0x1) + 0xa1, 0x00, // Collection (Physical) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (1) + 0x29, 0x03, // Usage Maximum (3) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x95, 0x03, // Report Count (3) + 0x75, 0x01, // Report Size (1) + 0x81, 0x02, // Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x95, 0x01, // Report Count (1) + 0x75, 0x05, // Report Size (5) + 0x81, 0x03, // Input (Con|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x05, 0x01, // Usage Page (Generic Desktop) + 0x09, 0x30, // Usage (0x30) + 0x09, 0x31, // Usage (0x31) + 0x15, 0x81, // Logical Minimum (129) + 0x25, 0x7f, // Logical Maximum (127) + 0x75, 0x08, // Report Size (8) + 0x95, 0x02, // Report Count (2) + 0x81, 0x06, // Input (Dat|Arr|Abs|NoWrp|Lin|Prf|NoNull|BitF) + 0xc0, // End Collection + 0xc0 // End Collection +}; + +// Logitech Unifying receiver descriptor +const uint8_t kLogitechUnifyingReceiver[] = { + 0x06, 0x00, 0xFF, // Usage Page (Vendor) + 0x09, 0x01, // Usage (0x1) + 0xA1, 0x01, // Collection (Application) + 0x85, 0x10, // Report ID (0x10) + 0x75, 0x08, // Report Size (8) + 0x95, 0x06, // Report Count (6) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x09, 0x01, // Usage (0x1) + 0x81, 0x00, // Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x09, 0x01, // Usage (0x1) + 0x91, 0x00, // Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0xC0, // End Collection + 0x06, 0x00, 0xFF, // Usage Page (Vendor) + 0x09, 0x02, // Usage (0x2) + 0xA1, 0x01, // Collection (Application) + 0x85, 0x11, // Report ID (0x11) + 0x75, 0x08, // Report Size (8) + 0x95, 0x13, // Report Count (19) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x09, 0x02, // Usage (0x2) + 0x81, 0x00, // Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x09, 0x02, // Usage (0x2) + 0x91, 0x00, // Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0xC0, // End Collection + 0x06, 0x00, 0xFF, // Usage Page (Vendor) + 0x09, 0x04, // Usage (0x4) + 0xA1, 0x01, // Collection (Application) + 0x85, 0x20, // Report ID (0x20) + 0x75, 0x08, // Report Size (8) + 0x95, 0x0E, // Report Count (14) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x09, 0x41, // Usage (0x41) + 0x81, 0x00, // Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x09, 0x41, // Usage (0x41) + 0x91, 0x00, // Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x85, 0x21, // Report ID (0x21) + 0x95, 0x1F, // Report Count (31) + 0x15, 0x00, // Logical Minimum (0) + 0x26, 0xFF, 0x00, // Logical Maximum (255) + 0x09, 0x42, // Usage (0x42) + 0x81, 0x00, // Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0x09, 0x42, // Usage (0x42) + 0x91, 0x00, // Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF) + 0xC0 // End Collection +}; + +} // namespace + +class HidReportDescriptorTest : public testing::Test { + + protected: + virtual void SetUp() OVERRIDE { descriptor_ = NULL; } + + virtual void TearDown() OVERRIDE { + if (descriptor_) { + delete descriptor_; + } + } + + public: + void ValidateDetails( + const std::vector& expected_collections, + const int expected_max_input_report_size, + const int expected_max_output_report_size, + const int expected_max_feature_report_size, + const uint8_t* bytes, + size_t size) { + descriptor_ = new HidReportDescriptor(bytes, size); + + std::vector actual_collections; + int actual_max_input_report_size; + int actual_max_output_report_size; + int actual_max_feature_report_size; + descriptor_->GetDetails(&actual_collections, + &actual_max_input_report_size, + &actual_max_output_report_size, + &actual_max_feature_report_size); + + ASSERT_EQ(expected_collections.size(), actual_collections.size()); + + std::vector::const_iterator actual_collections_iter = + actual_collections.begin(); + std::vector::const_iterator expected_collections_iter = + expected_collections.begin(); + + while (expected_collections_iter != expected_collections.end() && + actual_collections_iter != actual_collections.end()) { + HidCollectionInfo expected_collection = *expected_collections_iter; + HidCollectionInfo actual_collection = *actual_collections_iter; + + ASSERT_EQ(expected_collection.usage.usage_page, + actual_collection.usage.usage_page); + ASSERT_EQ(expected_collection.usage.usage, actual_collection.usage.usage); + ASSERT_THAT(actual_collection.report_ids, + ContainerEq(expected_collection.report_ids)); + + expected_collections_iter++; + actual_collections_iter++; + } + + ASSERT_EQ(expected_max_input_report_size, actual_max_input_report_size); + ASSERT_EQ(expected_max_output_report_size, actual_max_output_report_size); + ASSERT_EQ(expected_max_feature_report_size, actual_max_feature_report_size); + } + + private: + HidReportDescriptor* descriptor_; +}; + +TEST_F(HidReportDescriptorTest, ValidateDetails_Digitizer) { + HidCollectionInfo digitizer; + digitizer.usage = HidUsageAndPage(0x01, HidUsageAndPage::kPageDigitizer); + digitizer.report_ids.insert(1); + digitizer.report_ids.insert(2); + digitizer.report_ids.insert(3); + HidCollectionInfo expected[] = {digitizer}; + ValidateDetails(std::vector( + expected, expected + ARRAYSIZE_UNSAFE(expected)), + 7, + 0, + 0, + kDigitizer, + sizeof(kDigitizer)); +} + +TEST_F(HidReportDescriptorTest, ValidateDetails_Keyboard) { + HidCollectionInfo keyboard; + keyboard.usage = HidUsageAndPage(0x06, HidUsageAndPage::kPageGenericDesktop); + HidCollectionInfo expected[] = {keyboard}; + ValidateDetails(std::vector( + expected, expected + ARRAYSIZE_UNSAFE(expected)), + 8, + 1, + 0, + kKeyboard, + sizeof(kKeyboard)); +} + +TEST_F(HidReportDescriptorTest, ValidateDetails_Monitor) { + HidCollectionInfo monitor; + monitor.usage = HidUsageAndPage(0x01, HidUsageAndPage::kPageMonitor0); + monitor.report_ids.insert(1); + monitor.report_ids.insert(2); + monitor.report_ids.insert(3); + monitor.report_ids.insert(4); + monitor.report_ids.insert(5); + HidCollectionInfo expected[] = {monitor}; + ValidateDetails(std::vector( + expected, expected + ARRAYSIZE_UNSAFE(expected)), + 0, + 0, + 244, + kMonitor, + sizeof(kMonitor)); +} + +TEST_F(HidReportDescriptorTest, ValidateDetails_Mouse) { + HidCollectionInfo mouse; + mouse.usage = HidUsageAndPage(0x02, HidUsageAndPage::kPageGenericDesktop); + HidCollectionInfo expected[] = {mouse}; + ValidateDetails(std::vector( + expected, expected + ARRAYSIZE_UNSAFE(expected)), + 3, + 0, + 0, + kMouse, + sizeof(kMouse)); +} + +TEST_F(HidReportDescriptorTest, ValidateDetails_LogitechUnifyingReceiver) { + HidCollectionInfo hidpp_short; + hidpp_short.usage = HidUsageAndPage(0x01, HidUsageAndPage::kPageVendor); + hidpp_short.report_ids.insert(0x10); + HidCollectionInfo hidpp_long; + hidpp_long.usage = HidUsageAndPage(0x02, HidUsageAndPage::kPageVendor); + hidpp_long.report_ids.insert(0x11); + HidCollectionInfo hidpp_dj; + hidpp_dj.usage = HidUsageAndPage(0x04, HidUsageAndPage::kPageVendor); + hidpp_dj.report_ids.insert(0x20); + hidpp_dj.report_ids.insert(0x21); + + HidCollectionInfo expected[] = {hidpp_short, hidpp_long, hidpp_dj}; + ValidateDetails(std::vector( + expected, expected + ARRAYSIZE_UNSAFE(expected)), + 32, + 32, + 0, + kLogitechUnifyingReceiver, + sizeof(kLogitechUnifyingReceiver)); +} + +} // namespace device diff --git a/device/hid/hid_service_linux.cc b/device/hid/hid_service_linux.cc index d7ab5581b287..470afa82a4d4 100644 --- a/device/hid/hid_service_linux.cc +++ b/device/hid/hid_service_linux.cc @@ -148,7 +148,10 @@ void HidServiceLinux::OnDeviceAdded(udev_device* device) { device_file.Close(); HidReportDescriptor report_descriptor(rpt_desc.value, rpt_desc.size); - report_descriptor.GetTopLevelCollections(&device_info.usages); + report_descriptor.GetDetails(&device_info.collections, + &device_info.max_input_report_size, + &device_info.max_output_report_size, + &device_info.max_feature_report_size); AddDevice(device_info); } diff --git a/device/hid/hid_service_mac.cc b/device/hid/hid_service_mac.cc index ed85ec2d5cc9..98bfd9231a90 100644 --- a/device/hid/hid_service_mac.cc +++ b/device/hid/hid_service_mac.cc @@ -7,17 +7,19 @@ #include #include +#include #include #include #include "base/bind.h" #include "base/logging.h" +#include "base/mac/foundation_util.h" #include "base/message_loop/message_loop_proxy.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/sys_string_conversions.h" #include "base/threading/thread_restrictions.h" #include "device/hid/hid_connection_mac.h" -#include "device/hid/hid_utils_mac.h" namespace device { @@ -48,6 +50,89 @@ void EnumerateHidDevices(IOHIDManagerRef hid_manager, CFSetApplyFunction(devices, HidEnumerationBackInserter, device_list); } +bool TryGetHidIntProperty(IOHIDDeviceRef device, + CFStringRef key, + int32_t* result) { + CFNumberRef ref = + base::mac::CFCast(IOHIDDeviceGetProperty(device, key)); + return ref && CFNumberGetValue(ref, kCFNumberSInt32Type, result); +} + +int32_t GetHidIntProperty(IOHIDDeviceRef device, CFStringRef key) { + int32_t value; + if (TryGetHidIntProperty(device, key, &value)) + return value; + return 0; +} + +bool TryGetHidStringProperty(IOHIDDeviceRef device, + CFStringRef key, + std::string* result) { + CFStringRef ref = + base::mac::CFCast(IOHIDDeviceGetProperty(device, key)); + if (!ref) { + return false; + } + *result = base::SysCFStringRefToUTF8(ref); + return true; +} + +std::string GetHidStringProperty(IOHIDDeviceRef device, CFStringRef key) { + std::string value; + TryGetHidStringProperty(device, key, &value); + return value; +} + +void GetReportIds(IOHIDElementRef element, std::set& reportIDs) { + CFArrayRef children = IOHIDElementGetChildren(element); + if (!children) + return; + CFIndex childrenCount = CFArrayGetCount(children); + for (CFIndex j = 0; j < childrenCount; ++j) { + const IOHIDElementRef child = static_cast( + const_cast(CFArrayGetValueAtIndex(children, j))); + uint32_t reportID = IOHIDElementGetReportID(child); + if (reportID) { + reportIDs.insert(reportID); + } + GetReportIds(child, reportIDs); + } +} + +void GetCollectionInfos(IOHIDDeviceRef device, + std::vector* top_level_collections) { + STLClearObject(top_level_collections); + CFMutableDictionaryRef collections_filter = + CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + const int kCollectionTypeValue = kIOHIDElementTypeCollection; + CFNumberRef collection_type_id = CFNumberCreate( + kCFAllocatorDefault, kCFNumberIntType, &kCollectionTypeValue); + CFDictionarySetValue( + collections_filter, CFSTR(kIOHIDElementTypeKey), collection_type_id); + CFRelease(collection_type_id); + CFArrayRef collections = IOHIDDeviceCopyMatchingElements( + device, collections_filter, kIOHIDOptionsTypeNone); + CFIndex collectionsCount = CFArrayGetCount(collections); + for (CFIndex i = 0; i < collectionsCount; i++) { + const IOHIDElementRef collection = static_cast( + const_cast(CFArrayGetValueAtIndex(collections, i))); + // Top-Level Collection has no parent + if (IOHIDElementGetParent(collection) == 0) { + HidCollectionInfo collection_info; + HidUsageAndPage::Page page = static_cast( + IOHIDElementGetUsagePage(collection)); + uint16_t usage = IOHIDElementGetUsage(collection); + collection_info.usage = HidUsageAndPage(usage, page); + // Explore children recursively and retrieve their report IDs + GetReportIds(collection, collection_info.report_ids); + top_level_collections->push_back(collection_info); + } + } +} + } // namespace HidServiceMac::HidServiceMac() { @@ -136,41 +221,23 @@ void HidServiceMac::PlatformAddDevice(IOHIDDeviceRef hid_device) { // Note that our ownership of hid_device is implied if calling this method. // It is balanced in PlatformRemoveDevice. DCHECK(thread_checker_.CalledOnValidThread()); - HidDeviceInfo device_info; device_info.device_id = hid_device; device_info.vendor_id = GetHidIntProperty(hid_device, CFSTR(kIOHIDVendorIDKey)); device_info.product_id = GetHidIntProperty(hid_device, CFSTR(kIOHIDProductIDKey)); - device_info.input_report_size = - GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxInputReportSizeKey)); - device_info.output_report_size = - GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxOutputReportSizeKey)); - device_info.feature_report_size = - GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxFeatureReportSizeKey)); - CFTypeRef deviceUsagePairsRaw = - IOHIDDeviceGetProperty(hid_device, CFSTR(kIOHIDDeviceUsagePairsKey)); - CFArrayRef deviceUsagePairs = - base::mac::CFCast(deviceUsagePairsRaw); - CFIndex deviceUsagePairsCount = CFArrayGetCount(deviceUsagePairs); - for (CFIndex i = 0; i < deviceUsagePairsCount; i++) { - CFDictionaryRef deviceUsagePair = base::mac::CFCast( - CFArrayGetValueAtIndex(deviceUsagePairs, i)); - CFNumberRef usage_raw = base::mac::CFCast( - CFDictionaryGetValue(deviceUsagePair, CFSTR(kIOHIDDeviceUsageKey))); - uint16_t usage; - CFNumberGetValue(usage_raw, kCFNumberSInt32Type, &usage); - CFNumberRef page_raw = base::mac::CFCast( - CFDictionaryGetValue(deviceUsagePair, CFSTR(kIOHIDDeviceUsagePageKey))); - HidUsageAndPage::Page page; - CFNumberGetValue(page_raw, kCFNumberSInt32Type, &page); - device_info.usages.push_back(HidUsageAndPage(usage, page)); - } device_info.product_name = GetHidStringProperty(hid_device, CFSTR(kIOHIDProductKey)); device_info.serial_number = GetHidStringProperty(hid_device, CFSTR(kIOHIDSerialNumberKey)); + GetCollectionInfos(hid_device, &device_info.collections); + device_info.max_input_report_size = + GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxInputReportSizeKey)); + device_info.max_output_report_size = + GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxOutputReportSizeKey)); + device_info.max_feature_report_size = + GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxFeatureReportSizeKey)); AddDevice(device_info); } diff --git a/device/hid/hid_service_win.cc b/device/hid/hid_service_win.cc index 82477a528458..9f27cfff2699 100644 --- a/device/hid/hid_service_win.cc +++ b/device/hid/hid_service_win.cc @@ -187,38 +187,50 @@ void HidServiceWin::PlatformAddDevice(const std::string& device_path) { PHIDP_PREPARSED_DATA preparsed_data; if (HidD_GetPreparsedData(device_handle.Get(), &preparsed_data) && preparsed_data) { - HIDP_CAPS capabilities; + HIDP_CAPS capabilities = {0}; if (HidP_GetCaps(preparsed_data, &capabilities) == HIDP_STATUS_SUCCESS) { - device_info.input_report_size = capabilities.InputReportByteLength; - device_info.output_report_size = capabilities.OutputReportByteLength; - device_info.feature_report_size = capabilities.FeatureReportByteLength; - device_info.usages.push_back(HidUsageAndPage( - capabilities.Usage, - static_cast(capabilities.UsagePage))); - } - // Detect if the device supports report ids. - if (capabilities.NumberInputValueCaps > 0) { - scoped_ptr value_caps( - new HIDP_VALUE_CAPS[capabilities.NumberInputValueCaps]); - USHORT value_caps_length = capabilities.NumberInputValueCaps; - if (HidP_GetValueCaps(HidP_Input, &value_caps[0], &value_caps_length, - preparsed_data) == HIDP_STATUS_SUCCESS) { - device_info.has_report_id = (value_caps[0].ReportID != 0); - } - } - if (!device_info.has_report_id && capabilities.NumberInputButtonCaps > 0) - { - scoped_ptr button_caps( - new HIDP_BUTTON_CAPS[capabilities.NumberInputButtonCaps]); + device_info.max_input_report_size = capabilities.InputReportByteLength; + device_info.max_output_report_size = capabilities.OutputReportByteLength; + device_info.max_feature_report_size = + capabilities.FeatureReportByteLength; + HidCollectionInfo collection_info; + collection_info.usage = HidUsageAndPage( + capabilities.Usage, + static_cast(capabilities.UsagePage)); USHORT button_caps_length = capabilities.NumberInputButtonCaps; - if (HidP_GetButtonCaps(HidP_Input, - &button_caps[0], - &button_caps_length, - preparsed_data) == HIDP_STATUS_SUCCESS) { - device_info.has_report_id = (button_caps[0].ReportID != 0); + if (button_caps_length > 0) { + scoped_ptr button_caps( + new HIDP_BUTTON_CAPS[button_caps_length]); + if (HidP_GetButtonCaps(HidP_Input, + &button_caps[0], + &button_caps_length, + preparsed_data) == HIDP_STATUS_SUCCESS) { + for (int i = 0; i < button_caps_length; i++) { + int report_id = button_caps[i].ReportID; + if (report_id != 0) { + collection_info.report_ids.insert(report_id); + } + } + } } + USHORT value_caps_length = capabilities.NumberInputValueCaps; + if (value_caps_length > 0) { + scoped_ptr value_caps( + new HIDP_VALUE_CAPS[value_caps_length]); + if (HidP_GetValueCaps(HidP_Input, + &value_caps[0], + &value_caps_length, + preparsed_data) == HIDP_STATUS_SUCCESS) { + for (int i = 0; i < value_caps_length; i++) { + int report_id = value_caps[i].ReportID; + if (report_id != 0) { + collection_info.report_ids.insert(report_id); + } + } + } + } + device_info.collections.push_back(collection_info); } - HidD_FreePreparsedData(preparsed_data); } diff --git a/device/hid/hid_usage_and_page.cc b/device/hid/hid_usage_and_page.cc index 773346b7a5b7..079ec6623d6f 100644 --- a/device/hid/hid_usage_and_page.cc +++ b/device/hid/hid_usage_and_page.cc @@ -6,8 +6,31 @@ namespace device { -bool HidUsageAndPage::operator==(const HidUsageAndPage& other) const { - return usage == other.usage && usage_page == other.usage_page; +bool HidUsageAndPage::IsProtected() const { + if (usage_page == HidUsageAndPage::kPageKeyboard) + return true; + + if (usage_page != HidUsageAndPage::kPageGenericDesktop) + return false; + + if (usage == HidUsageAndPage::kGenericDesktopPointer || + usage == HidUsageAndPage::kGenericDesktopMouse || + usage == HidUsageAndPage::kGenericDesktopKeyboard || + usage == HidUsageAndPage::kGenericDesktopKeypad) { + return true; + } + + if (usage >= HidUsageAndPage::kGenericDesktopSystemControl && + usage <= HidUsageAndPage::kGenericDesktopSystemWarmRestart) { + return true; + } + + if (usage >= HidUsageAndPage::kGenericDesktopSystemDock && + usage <= HidUsageAndPage::kGenericDesktopSystemDisplaySwap) { + return true; + } + + return false; } } // namespace device diff --git a/device/hid/hid_usage_and_page.h b/device/hid/hid_usage_and_page.h index 98ac80d6b544..635e9b3cc419 100644 --- a/device/hid/hid_usage_and_page.h +++ b/device/hid/hid_usage_and_page.h @@ -126,7 +126,8 @@ struct HidUsageAndPage { uint16_t usage; Page usage_page; - bool operator==(const HidUsageAndPage& other) const; + // Indicates whether this usage is protected by Chrome. + bool IsProtected() const; }; } // namespace device diff --git a/device/hid/hid_utils_mac.cc b/device/hid/hid_utils_mac.cc deleted file mode 100644 index 46605d87bdb2..000000000000 --- a/device/hid/hid_utils_mac.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "device/hid/hid_utils_mac.h" - -#include "base/mac/foundation_util.h" -#include "base/strings/sys_string_conversions.h" - -namespace device { - -int32_t GetHidIntProperty(IOHIDDeviceRef device, CFStringRef key) { - int32_t value; - if (TryGetHidIntProperty(device, key, &value)) - return value; - return 0; -} - -std::string GetHidStringProperty(IOHIDDeviceRef device, CFStringRef key) { - std::string value; - TryGetHidStringProperty(device, key, &value); - return value; -} - -bool TryGetHidIntProperty(IOHIDDeviceRef device, - CFStringRef key, - int32_t* result) { - CFNumberRef ref = base::mac::CFCast( - IOHIDDeviceGetProperty(device, key)); - return ref && CFNumberGetValue(ref, kCFNumberSInt32Type, result); -} - -bool TryGetHidStringProperty(IOHIDDeviceRef device, - CFStringRef key, - std::string* result) { - CFStringRef ref = base::mac::CFCast( - IOHIDDeviceGetProperty(device, key)); - if (!ref) { - return false; - } - *result = base::SysCFStringRefToUTF8(ref); - return true; -} - -} // namespace device diff --git a/device/hid/hid_utils_mac.h b/device/hid/hid_utils_mac.h deleted file mode 100644 index e9b252462a5d..000000000000 --- a/device/hid/hid_utils_mac.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef DEVICE_HID_HID_UTILS_MAC_H_ -#define DEVICE_HID_HID_UTILS_MAC_H_ - -#include -#include -#include - -#include - -namespace device { - -int32_t GetHidIntProperty(IOHIDDeviceRef device, CFStringRef key); - -std::string GetHidStringProperty(IOHIDDeviceRef device, CFStringRef key); - -bool TryGetHidIntProperty(IOHIDDeviceRef device, - CFStringRef key, - int32_t* result); - -bool TryGetHidStringProperty(IOHIDDeviceRef device, - CFStringRef key, - std::string* result); - -} // namespace device - -#endif // DEVICE_HID_HID_UTILS_MAC_H_ -- 2.11.4.GIT