OLD | NEW |
---|---|
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "device/hid/hid_connection.h" | 5 #include "device/hid/hid_connection.h" |
6 | 6 |
7 #include <algorithm> | |
8 | |
7 namespace device { | 9 namespace device { |
8 | 10 |
11 namespace { | |
12 | |
13 const uint8_t kNullReportId = 0x00; | |
14 const uint8_t kInvalidReportId = 0xFF; | |
Ken Rockot(use gerrit already)
2014/06/19 19:29:56
Shouldn't this be kAnyReportId given the usage?
jracle (use Gerrit)
2014/06/19 21:39:36
Right, that's indeed what it means.
On 2014/06/19
| |
15 | |
16 struct CollectionHasReportId { | |
17 explicit CollectionHasReportId(const uint8_t report_id) | |
18 : report_id_(report_id) {} | |
19 | |
20 bool operator()(const HidCollectionInfo& info) const { | |
21 if (info.report_ids.size() == 0 || report_id_ == kNullReportId) | |
22 return false; | |
23 | |
24 if (report_id_ == kInvalidReportId) | |
25 return true; | |
26 | |
27 return std::find(info.report_ids.begin(), | |
28 info.report_ids.end(), | |
29 report_id_) != info.report_ids.end(); | |
30 } | |
31 | |
32 private: | |
33 const uint8_t report_id_; | |
34 }; | |
35 | |
36 struct CollectionIsProtected { | |
37 bool operator()(const HidCollectionInfo& info) const { | |
38 return info.usage.IsProtected(); | |
39 } | |
40 }; | |
41 | |
42 bool HasReportId(const HidDeviceInfo& device_info, | |
Ken Rockot(use gerrit already)
2014/06/19 19:29:56
This doesn't quite do what the name implies. How a
jracle (use Gerrit)
2014/06/19 21:39:36
Thanks so much for noticing it!
See, indeed defau
| |
43 const uint8_t report_id = kInvalidReportId, | |
44 HidCollectionInfo* collection_info = NULL) { | |
45 std::vector<HidCollectionInfo>::const_iterator collection_iter = | |
46 std::find_if(device_info.collections.begin(), | |
47 device_info.collections.end(), | |
48 CollectionHasReportId(report_id)); | |
49 if (collection_iter != device_info.collections.end()) { | |
50 if (collection_info) { | |
51 *collection_info = *collection_iter; | |
52 } | |
53 return true; | |
54 } | |
55 | |
56 return false; | |
57 } | |
58 | |
59 bool HasProtectedCollection(const HidDeviceInfo& device_info) { | |
60 return std::find_if(device_info.collections.begin(), | |
61 device_info.collections.end(), | |
62 CollectionIsProtected()) != device_info.collections.end(); | |
63 } | |
64 | |
65 } // namespace | |
66 | |
67 HidConnection::HidConnection(const HidDeviceInfo& device_info) | |
68 : device_info_(device_info) { | |
69 DCHECK(thread_checker_.CalledOnValidThread()); | |
70 has_protected_collection_ = HasProtectedCollection(device_info); | |
71 has_report_id_ = HasReportId(device_info); | |
jracle (use Gerrit)
2014/06/11 11:20:04
I prefer to cache this info..
Ken Rockot(use gerrit already)
2014/06/19 19:29:56
Sure, sounds fine to me.
jracle (use Gerrit)
2014/06/19 21:39:36
Thx.
On 2014/06/19 19:29:56, Ken Rockot wrote:
| |
72 } | |
73 | |
74 HidConnection::~HidConnection() { | |
75 DCHECK(thread_checker_.CalledOnValidThread()); | |
76 } | |
77 | |
78 void HidConnection::Read(scoped_refptr<net::IOBufferWithSize> buffer, | |
79 const IOCallback& callback) { | |
80 DCHECK(thread_checker_.CalledOnValidThread()); | |
81 if (device_info_.max_input_report_size == 0) { | |
82 // The device does not support input reports. | |
83 callback.Run(false, 0); | |
84 return; | |
85 } | |
86 int expected_buffer_size = device_info_.max_input_report_size; | |
87 if (!has_report_id()) { | |
88 expected_buffer_size--; | |
89 } | |
90 if (buffer->size() < expected_buffer_size) { | |
91 // Receive buffer is too small. | |
92 callback.Run(false, 0); | |
93 return; | |
94 } | |
95 | |
96 PlatformRead(buffer, callback); | |
97 } | |
98 | |
99 void HidConnection::Write(uint8_t report_id, | |
100 scoped_refptr<net::IOBufferWithSize> buffer, | |
101 const IOCallback& callback) { | |
102 DCHECK(thread_checker_.CalledOnValidThread()); | |
103 if (device_info_.max_output_report_size == 0) { | |
104 // The device does not support output reports. | |
105 callback.Run(false, 0); | |
106 return; | |
107 } | |
108 if (IsReportIdProtected(report_id)) { | |
jracle (use Gerrit)
2014/06/11 11:20:04
We need to prevent R/W of protected reports.
| |
109 callback.Run(false, 0); | |
110 return; | |
111 } | |
112 | |
113 PlatformWrite(report_id, buffer, callback); | |
114 } | |
115 | |
116 void HidConnection::GetFeatureReport( | |
117 uint8_t report_id, | |
118 scoped_refptr<net::IOBufferWithSize> buffer, | |
119 const IOCallback& callback) { | |
120 DCHECK(thread_checker_.CalledOnValidThread()); | |
121 if (device_info_.max_feature_report_size == 0) { | |
122 // The device does not support feature reports. | |
123 callback.Run(false, 0); | |
124 return; | |
125 } | |
126 if (IsReportIdProtected(report_id)) { | |
127 callback.Run(false, 0); | |
128 return; | |
129 } | |
130 int expected_buffer_size = device_info_.max_feature_report_size; | |
131 if (!has_report_id()) { | |
132 expected_buffer_size--; | |
133 } | |
134 if (buffer->size() < expected_buffer_size) { | |
135 // Receive buffer is too small. | |
136 callback.Run(false, 0); | |
137 return; | |
138 } | |
139 | |
140 PlatformGetFeatureReport(report_id, buffer, callback); | |
141 } | |
142 | |
143 void HidConnection::SendFeatureReport( | |
144 uint8_t report_id, | |
145 scoped_refptr<net::IOBufferWithSize> buffer, | |
146 const IOCallback& callback) { | |
147 DCHECK(thread_checker_.CalledOnValidThread()); | |
148 if (device_info_.max_feature_report_size == 0) { | |
149 // The device does not support feature reports. | |
150 callback.Run(false, 0); | |
151 return; | |
152 } | |
153 if (IsReportIdProtected(report_id)) { | |
154 callback.Run(false, 0); | |
155 return; | |
156 } | |
157 | |
158 PlatformSendFeatureReport(report_id, buffer, callback); | |
159 } | |
160 | |
161 bool HidConnection::FilterInputReport( | |
jracle (use Gerrit)
2014/06/11 11:20:04
This is new filtering method, used together with n
| |
162 scoped_refptr<net::IOBufferWithSize> buffer, | |
163 const IOCallback& callback) { | |
164 if (buffer->size() == 0) { | |
165 return false; | |
166 } | |
167 | |
168 if (IsReportIdProtected(buffer->data()[0])) { | |
169 callback.Run(true, 0); | |
170 return true; | |
171 } | |
172 | |
173 return false; | |
174 } | |
175 | |
176 bool HidConnection::IsReportIdProtected(const uint8_t report_id) { | |
177 HidCollectionInfo collection_info; | |
178 if (HasReportId(device_info_, report_id, &collection_info)) { | |
179 return collection_info.usage.IsProtected(); | |
180 } | |
181 | |
182 return has_protected_collection(); | |
183 } | |
184 | |
9 PendingHidReport::PendingHidReport() {} | 185 PendingHidReport::PendingHidReport() {} |
10 | 186 |
11 PendingHidReport::~PendingHidReport() {} | 187 PendingHidReport::~PendingHidReport() {} |
12 | 188 |
13 PendingHidRead::PendingHidRead() {} | 189 PendingHidRead::PendingHidRead() {} |
14 | 190 |
15 PendingHidRead::~PendingHidRead() {} | 191 PendingHidRead::~PendingHidRead() {} |
16 | 192 |
17 HidConnection::HidConnection(const HidDeviceInfo& device_info) | 193 HidConnection2::HidConnection2(const HidDeviceInfo& device_info) |
18 : device_info_(device_info) {} | 194 : HidConnection(device_info) { |
195 } | |
19 | 196 |
20 HidConnection::~HidConnection() {} | 197 HidConnection2::~HidConnection2() { |
198 Flush(); | |
199 } | |
200 | |
201 void HidConnection2::PlatformRead(scoped_refptr<net::IOBufferWithSize> buffer, | |
Ken Rockot(use gerrit already)
2014/06/19 19:29:56
Ah, I see why you created this temporary class now
jracle (use Gerrit)
2014/06/19 21:39:36
Yes sorry, this was to eliminate duplication.
Hen
| |
202 const IOCallback& callback) { | |
203 PendingHidRead pending_read; | |
204 pending_read.buffer = buffer; | |
205 pending_read.callback = callback; | |
206 pending_reads_.push(pending_read); | |
207 ProcessReadQueue(); | |
208 } | |
209 | |
210 void HidConnection2::ProcessInputReport( | |
211 scoped_refptr<net::IOBufferWithSize> buffer) { | |
212 DCHECK(thread_checker().CalledOnValidThread()); | |
213 PendingHidReport report; | |
214 report.buffer = buffer; | |
215 pending_reports_.push(report); | |
216 ProcessReadQueue(); | |
217 } | |
218 | |
219 void HidConnection2::Flush() { | |
220 while (!pending_reads_.empty()) { | |
221 pending_reads_.front().callback.Run(false, 0); | |
222 pending_reads_.pop(); | |
223 } | |
224 } | |
225 | |
226 void HidConnection2::ProcessReadQueue() { | |
227 DCHECK(thread_checker().CalledOnValidThread()); | |
228 while (pending_reads_.size() && pending_reports_.size()) { | |
229 PendingHidRead read = pending_reads_.front(); | |
230 pending_reads_.pop(); | |
231 PendingHidReport report = pending_reports_.front(); | |
232 | |
233 if (read.buffer->size() < report.buffer->size()) { | |
234 read.callback.Run(false, report.buffer->size()); | |
235 } else { | |
236 memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size()); | |
237 pending_reports_.pop(); | |
238 | |
239 if (FilterInputReport(report.buffer, read.callback)) { | |
240 return; | |
241 } | |
242 | |
243 read.callback.Run(true, report.buffer->size()); | |
244 } | |
245 } | |
246 } | |
21 | 247 |
22 } // namespace device | 248 } // namespace device |
OLD | NEW |