1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "device/hid/hid_connection.h" 6 7 #include <algorithm> 8 9 namespace device { 10 11 namespace { 12 13 // Functor used to filter collections by report ID. 14 struct CollectionHasReportId { 15 explicit CollectionHasReportId(uint8_t report_id) : report_id_(report_id) {} 16 17 bool operator()(const HidCollectionInfo& info) const { 18 if (info.report_ids.size() == 0 || 19 report_id_ == HidConnection::kNullReportId) 20 return false; 21 22 if (report_id_ == HidConnection::kAnyReportId) 23 return true; 24 25 return std::find(info.report_ids.begin(), 26 info.report_ids.end(), 27 report_id_) != info.report_ids.end(); 28 } 29 30 private: 31 const uint8_t report_id_; 32 }; 33 34 // Functor returning true if collection has a protected usage. 35 struct CollectionIsProtected { 36 bool operator()(const HidCollectionInfo& info) const { 37 return info.usage.IsProtected(); 38 } 39 }; 40 41 bool FindCollectionByReportId(const HidDeviceInfo& device_info, 42 uint8_t report_id, 43 HidCollectionInfo* collection_info) { 44 std::vector<HidCollectionInfo>::const_iterator collection_iter = 45 std::find_if(device_info.collections.begin(), 46 device_info.collections.end(), 47 CollectionHasReportId(report_id)); 48 if (collection_iter != device_info.collections.end()) { 49 if (collection_info) { 50 *collection_info = *collection_iter; 51 } 52 return true; 53 } 54 55 return false; 56 } 57 58 bool HasProtectedCollection(const HidDeviceInfo& device_info) { 59 return std::find_if(device_info.collections.begin(), 60 device_info.collections.end(), 61 CollectionIsProtected()) != device_info.collections.end(); 62 } 63 64 } // namespace 65 66 HidConnection::HidConnection(const HidDeviceInfo& device_info) 67 : device_info_(device_info), closed_(false) { 68 has_protected_collection_ = HasProtectedCollection(device_info); 69 } 70 71 HidConnection::~HidConnection() { 72 DCHECK(thread_checker_.CalledOnValidThread()); 73 DCHECK(closed_); 74 } 75 76 void HidConnection::Close() { 77 DCHECK(thread_checker_.CalledOnValidThread()); 78 DCHECK(!closed_); 79 80 PlatformClose(); 81 closed_ = true; 82 } 83 84 void HidConnection::Read(const ReadCallback& callback) { 85 DCHECK(thread_checker_.CalledOnValidThread()); 86 if (device_info_.max_input_report_size == 0) { 87 VLOG(1) << "This device does not support input reports."; 88 callback.Run(false, NULL, 0); 89 return; 90 } 91 92 PlatformRead(callback); 93 } 94 95 void HidConnection::Write(scoped_refptr<net::IOBuffer> buffer, 96 size_t size, 97 const WriteCallback& callback) { 98 DCHECK(thread_checker_.CalledOnValidThread()); 99 if (device_info_.max_output_report_size == 0) { 100 VLOG(1) << "This device does not support output reports."; 101 callback.Run(false); 102 return; 103 } 104 DCHECK_GE(size, 1u); 105 uint8_t report_id = buffer->data()[0]; 106 if (device_info().has_report_id != (report_id != 0)) { 107 VLOG(1) << "Invalid output report ID."; 108 callback.Run(false); 109 return; 110 } 111 if (IsReportIdProtected(report_id)) { 112 VLOG(1) << "Attempt to set a protected output report."; 113 callback.Run(false); 114 return; 115 } 116 117 PlatformWrite(buffer, size, callback); 118 } 119 120 void HidConnection::GetFeatureReport(uint8_t report_id, 121 const ReadCallback& callback) { 122 DCHECK(thread_checker_.CalledOnValidThread()); 123 if (device_info_.max_feature_report_size == 0) { 124 VLOG(1) << "This device does not support feature reports."; 125 callback.Run(false, NULL, 0); 126 return; 127 } 128 if (device_info().has_report_id != (report_id != 0)) { 129 VLOG(1) << "Invalid feature report ID."; 130 callback.Run(false, NULL, 0); 131 return; 132 } 133 if (IsReportIdProtected(report_id)) { 134 VLOG(1) << "Attempt to get a protected feature report."; 135 callback.Run(false, NULL, 0); 136 return; 137 } 138 139 PlatformGetFeatureReport(report_id, callback); 140 } 141 142 void HidConnection::SendFeatureReport(scoped_refptr<net::IOBuffer> buffer, 143 size_t size, 144 const WriteCallback& callback) { 145 DCHECK(thread_checker_.CalledOnValidThread()); 146 if (device_info_.max_feature_report_size == 0) { 147 VLOG(1) << "This device does not support feature reports."; 148 callback.Run(false); 149 return; 150 } 151 DCHECK_GE(size, 1u); 152 uint8_t report_id = buffer->data()[0]; 153 if (device_info().has_report_id != (report_id != 0)) { 154 VLOG(1) << "Invalid feature report ID."; 155 callback.Run(false); 156 return; 157 } 158 if (IsReportIdProtected(report_id)) { 159 VLOG(1) << "Attempt to set a protected feature report."; 160 callback.Run(false); 161 return; 162 } 163 164 PlatformSendFeatureReport(buffer, size, callback); 165 } 166 167 bool HidConnection::CompleteRead(scoped_refptr<net::IOBuffer> buffer, 168 size_t size, 169 const ReadCallback& callback) { 170 DCHECK_GE(size, 1u); 171 uint8_t report_id = buffer->data()[0]; 172 if (IsReportIdProtected(report_id)) { 173 VLOG(1) << "Filtered a protected input report."; 174 return false; 175 } 176 177 callback.Run(true, buffer, size); 178 return true; 179 } 180 181 bool HidConnection::IsReportIdProtected(uint8_t report_id) { 182 HidCollectionInfo collection_info; 183 if (FindCollectionByReportId(device_info_, report_id, &collection_info)) { 184 return collection_info.usage.IsProtected(); 185 } 186 187 return has_protected_collection(); 188 } 189 190 PendingHidReport::PendingHidReport() {} 191 192 PendingHidReport::~PendingHidReport() {} 193 194 PendingHidRead::PendingHidRead() {} 195 196 PendingHidRead::~PendingHidRead() {} 197 198 } // namespace device 199