Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(71)

Side by Side Diff: device/hid/hid_report_descriptor_unittest.cc

Issue 256673002: HID: Support top-level collection usages in device info. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « device/hid/hid_report_descriptor_item.cc ('k') | device/hid/hid_service_linux.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 <sstream>
6
7 #include "device/hid/hid_report_descriptor.h"
8 #include "testing/gmock/include/gmock/gmock.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10
11 using namespace testing;
12
13 namespace device {
14
15 namespace {
16
17 std::ostream& operator<<(std::ostream& os,
18 const HidUsageAndPage::Page& usage_page) {
19 switch (usage_page) {
20 case HidUsageAndPage::kPageUndefined:
21 os << "Undefined";
22 break;
23 case HidUsageAndPage::kPageGenericDesktop:
24 os << "Generic Desktop";
25 break;
26 case HidUsageAndPage::kPageSimulation:
27 os << "Simulation";
28 break;
29 case HidUsageAndPage::kPageVirtualReality:
30 os << "Virtual Reality";
31 break;
32 case HidUsageAndPage::kPageSport:
33 os << "Sport";
34 break;
35 case HidUsageAndPage::kPageGame:
36 os << "Game";
37 break;
38 case HidUsageAndPage::kPageKeyboard:
39 os << "Keyboard";
40 break;
41 case HidUsageAndPage::kPageLed:
42 os << "Led";
43 break;
44 case HidUsageAndPage::kPageButton:
45 os << "Button";
46 break;
47 case HidUsageAndPage::kPageOrdinal:
48 os << "Ordinal";
49 break;
50 case HidUsageAndPage::kPageTelephony:
51 os << "Telephony";
52 break;
53 case HidUsageAndPage::kPageConsumer:
54 os << "Consumer";
55 break;
56 case HidUsageAndPage::kPageDigitizer:
57 os << "Digitizer";
58 break;
59 case HidUsageAndPage::kPagePidPage:
60 os << "Pid Page";
61 break;
62 case HidUsageAndPage::kPageUnicode:
63 os << "Unicode";
64 break;
65 case HidUsageAndPage::kPageAlphanumericDisplay:
66 os << "Alphanumeric Display";
67 break;
68 case HidUsageAndPage::kPageMedicalInstruments:
69 os << "Medical Instruments";
70 break;
71 case HidUsageAndPage::kPageMonitor0:
72 os << "Monitor 0";
73 break;
74 case HidUsageAndPage::kPageMonitor1:
75 os << "Monitor 1";
76 break;
77 case HidUsageAndPage::kPageMonitor2:
78 os << "Monitor 2";
79 break;
80 case HidUsageAndPage::kPageMonitor3:
81 os << "Monitor 3";
82 break;
83 case HidUsageAndPage::kPagePower0:
84 os << "Power 0";
85 break;
86 case HidUsageAndPage::kPagePower1:
87 os << "Power 1";
88 break;
89 case HidUsageAndPage::kPagePower2:
90 os << "Power 2";
91 break;
92 case HidUsageAndPage::kPagePower3:
93 os << "Power 3";
94 break;
95 case HidUsageAndPage::kPageBarCodeScanner:
96 os << "Bar Code Scanner";
97 break;
98 case HidUsageAndPage::kPageScale:
99 os << "Scale";
100 break;
101 case HidUsageAndPage::kPageMagneticStripeReader:
102 os << "Magnetic Stripe Reader";
103 break;
104 case HidUsageAndPage::kPageReservedPointOfSale:
105 os << "Reserved Point Of Sale";
106 break;
107 case HidUsageAndPage::kPageCameraControl:
108 os << "Camera Control";
109 break;
110 case HidUsageAndPage::kPageArcade:
111 os << "Arcade";
112 break;
113 case HidUsageAndPage::kPageVendor:
114 os << "Vendor";
115 break;
116 case HidUsageAndPage::kPageMediaCenter:
117 os << "Media Center";
118 break;
119 default:
120 NOTREACHED();
121 break;
122 }
123 return os;
124 }
125
126 std::ostream& operator<<(std::ostream& os,
127 const HidUsageAndPage& usage_and_page) {
128 os << "Usage Page: " << usage_and_page.usage_page << ", Usage: "
129 << "0x" << std::hex << std::uppercase << usage_and_page.usage;
130 return os;
131 }
132
133 std::ostream& operator<<(std::ostream& os,
134 const HidReportDescriptorItem::Tag& tag) {
135 switch (tag) {
136 case HidReportDescriptorItem::kTagDefault:
137 os << "Default";
138 break;
139 case HidReportDescriptorItem::kTagInput:
140 os << "Input";
141 break;
142 case HidReportDescriptorItem::kTagOutput:
143 os << "Output";
144 break;
145 case HidReportDescriptorItem::kTagFeature:
146 os << "Feature";
147 break;
148 case HidReportDescriptorItem::kTagCollection:
149 os << "Collection";
150 break;
151 case HidReportDescriptorItem::kTagEndCollection:
152 os << "End Collection";
153 break;
154 case HidReportDescriptorItem::kTagUsagePage:
155 os << "Usage Page";
156 break;
157 case HidReportDescriptorItem::kTagLogicalMinimum:
158 os << "Logical Minimum";
159 break;
160 case HidReportDescriptorItem::kTagLogicalMaximum:
161 os << "Logical Maximum";
162 break;
163 case HidReportDescriptorItem::kTagPhysicalMinimum:
164 os << "Physical Minimum";
165 break;
166 case HidReportDescriptorItem::kTagPhysicalMaximum:
167 os << "Physical Maximum";
168 break;
169 case HidReportDescriptorItem::kTagUnitExponent:
170 os << "Unit Exponent";
171 break;
172 case HidReportDescriptorItem::kTagUnit:
173 os << "Unit";
174 break;
175 case HidReportDescriptorItem::kTagReportSize:
176 os << "Report Size";
177 break;
178 case HidReportDescriptorItem::kTagReportId:
179 os << "Report ID";
180 break;
181 case HidReportDescriptorItem::kTagReportCount:
182 os << "Report Count";
183 break;
184 case HidReportDescriptorItem::kTagPush:
185 os << "Push";
186 break;
187 case HidReportDescriptorItem::kTagPop:
188 os << "Pop";
189 break;
190 case HidReportDescriptorItem::kTagUsage:
191 os << "Usage";
192 break;
193 case HidReportDescriptorItem::kTagUsageMinimum:
194 os << "Usage Minimum";
195 break;
196 case HidReportDescriptorItem::kTagUsageMaximum:
197 os << "Usage Maximum";
198 break;
199 case HidReportDescriptorItem::kTagDesignatorIndex:
200 os << "Designator Index";
201 break;
202 case HidReportDescriptorItem::kTagDesignatorMinimum:
203 os << "Designator Minimum";
204 break;
205 case HidReportDescriptorItem::kTagDesignatorMaximum:
206 os << "Designator Maximum";
207 break;
208 case HidReportDescriptorItem::kTagStringIndex:
209 os << "String Index";
210 break;
211 case HidReportDescriptorItem::kTagStringMinimum:
212 os << "String Minimum";
213 break;
214 case HidReportDescriptorItem::kTagStringMaximum:
215 os << "String Maximum";
216 break;
217 case HidReportDescriptorItem::kTagDelimiter:
218 os << "Delimeter";
219 break;
220 case HidReportDescriptorItem::kTagLong:
221 os << "Long";
222 break;
223 default:
224 NOTREACHED();
225 break;
226 }
227
228 return os;
229 }
230
231 std::ostream& operator<<(std::ostream& os,
232 const HidReportDescriptorItem::ReportInfo& data) {
233 if (data.data_or_constant)
234 os << "Con";
235 else
236 os << "Dat";
237 if (data.array_or_variable)
238 os << "|Arr";
239 else
240 os << "|Var";
241 if (data.absolute_or_relative)
242 os << "|Abs";
243 else
244 os << "|Rel";
245 if (data.wrap)
246 os << "|Wrp";
247 else
248 os << "|NoWrp";
249 if (data.linear)
250 os << "|NoLin";
251 else
252 os << "|Lin";
253 if (data.preferred)
254 os << "|NoPrf";
255 else
256 os << "|Prf";
257 if (data.null)
258 os << "|Null";
259 else
260 os << "|NoNull";
261 if (data.bit_field_or_buffer)
262 os << "|Buff";
263 else
264 os << "|BitF";
265 return os;
266 }
267
268 std::ostream& operator<<(std::ostream& os,
269 const HidReportDescriptorItem::CollectionType& type) {
270 switch (type) {
271 case HidReportDescriptorItem::kCollectionTypePhysical:
272 os << "Physical";
273 break;
274 case HidReportDescriptorItem::kCollectionTypeApplication:
275 os << "Application";
276 break;
277 case HidReportDescriptorItem::kCollectionTypeLogical:
278 os << "Logical";
279 break;
280 case HidReportDescriptorItem::kCollectionTypeReport:
281 os << "Report";
282 break;
283 case HidReportDescriptorItem::kCollectionTypeNamedArray:
284 os << "Named Array";
285 break;
286 case HidReportDescriptorItem::kCollectionTypeUsageSwitch:
287 os << "Usage Switch";
288 break;
289 case HidReportDescriptorItem::kCollectionTypeUsageModifier:
290 os << "Usage Modifier";
291 break;
292 case HidReportDescriptorItem::kCollectionTypeReserved:
293 os << "Reserved";
294 break;
295 case HidReportDescriptorItem::kCollectionTypeVendor:
296 os << "Vendor";
297 break;
298 default:
299 NOTREACHED();
300 break;
301 }
302 return os;
303 }
304
305 std::ostream& operator<<(std::ostream& os,
306 const HidReportDescriptorItem& item) {
307 HidReportDescriptorItem::Tag item_tag = item.tag();
308 uint32_t data = item.GetShortData();
309
310 std::ostringstream sstr;
311 sstr << item_tag;
312 sstr << " (";
313
314 long pos = sstr.tellp();
315 switch (item_tag) {
316 case HidReportDescriptorItem::kTagDefault:
317 case HidReportDescriptorItem::kTagEndCollection:
318 case HidReportDescriptorItem::kTagPush:
319 case HidReportDescriptorItem::kTagPop:
320 case HidReportDescriptorItem::kTagLong:
321 break;
322
323 case HidReportDescriptorItem::kTagCollection:
324 sstr << HidReportDescriptorItem::GetCollectionTypeFromValue(data);
325 break;
326
327 case HidReportDescriptorItem::kTagInput:
328 case HidReportDescriptorItem::kTagOutput:
329 case HidReportDescriptorItem::kTagFeature:
330 sstr << (HidReportDescriptorItem::ReportInfo&)data;
331 break;
332
333 case HidReportDescriptorItem::kTagUsagePage:
334 sstr << (HidUsageAndPage::Page)data;
335 break;
336
337 case HidReportDescriptorItem::kTagUsage:
338 case HidReportDescriptorItem::kTagReportId:
339 sstr << "0x" << std::hex << std::uppercase << data;
340 break;
341
342 default:
343 sstr << data;
344 break;
345 }
346 if (pos == sstr.tellp()) {
347 std::string str = sstr.str();
348 str.erase(str.end() - 2, str.end());
349 os << str;
350 } else {
351 os << sstr.str() << ")";
352 }
353
354 return os;
355 }
356
357 const char kIndentStep[] = " ";
358
359 std::ostream& operator<<(std::ostream& os,
360 const HidReportDescriptor& descriptor) {
361 for (std::vector<linked_ptr<HidReportDescriptorItem> >::const_iterator
362 items_iter = descriptor.items().begin();
363 items_iter != descriptor.items().end();
364 ++items_iter) {
365 linked_ptr<HidReportDescriptorItem> item = *items_iter;
366 size_t indentLevel = item->GetDepth();
367 for (size_t i = 0; i < indentLevel; i++)
368 os << kIndentStep;
369 os << *item.get() << std::endl;
370 }
371 return os;
372 }
373
374 // See 'E.6 Report Descriptor (Keyboard)'
375 // in HID specifications (v1.11)
376 const uint8_t kKeyboard[] = {
377 0x05, 0x01, 0x09, 0x06, 0xA1, 0x01, 0x05, 0x07, 0x19, 0xE0, 0x29,
378 0xE7, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02,
379 0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x05, 0x75, 0x01, 0x05,
380 0x08, 0x19, 0x01, 0x29, 0x05, 0x91, 0x02, 0x95, 0x01, 0x75, 0x03,
381 0x91, 0x01, 0x95, 0x06, 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05,
382 0x07, 0x19, 0x00, 0x29, 0x65, 0x81, 0x00, 0xC0};
383
384 // See 'E.10 Report Descriptor (Mouse)'
385 // in HID specifications (v1.11)
386 const uint8_t kMouse[] = {0x05, 0x01, 0x09, 0x02, 0xA1, 0x01, 0x09, 0x01, 0xA1,
387 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x03, 0x15, 0x00,
388 0x25, 0x01, 0x95, 0x03, 0x75, 0x01, 0x81, 0x02, 0x95,
389 0x01, 0x75, 0x05, 0x81, 0x01, 0x05, 0x01, 0x09, 0x30,
390 0x09, 0x31, 0x15, 0x81, 0x25, 0x7F, 0x75, 0x08, 0x95,
391 0x02, 0x81, 0x06, 0xC0, 0xC0};
392
393 const uint8_t kLogitechUnifyingReceiver[] = {
394 0x06, 0x00, 0xFF, 0x09, 0x01, 0xA1, 0x01, 0x85, 0x10, 0x75, 0x08,
395 0x95, 0x06, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x09, 0x01, 0x81, 0x00,
396 0x09, 0x01, 0x91, 0x00, 0xC0, 0x06, 0x00, 0xFF, 0x09, 0x02, 0xA1,
397 0x01, 0x85, 0x11, 0x75, 0x08, 0x95, 0x13, 0x15, 0x00, 0x26, 0xFF,
398 0x00, 0x09, 0x02, 0x81, 0x00, 0x09, 0x02, 0x91, 0x00, 0xC0, 0x06,
399 0x00, 0xFF, 0x09, 0x04, 0xA1, 0x01, 0x85, 0x20, 0x75, 0x08, 0x95,
400 0x0E, 0x15, 0x00, 0x26, 0xFF, 0x00, 0x09, 0x41, 0x81, 0x00, 0x09,
401 0x41, 0x91, 0x00, 0x85, 0x21, 0x95, 0x1F, 0x15, 0x00, 0x26, 0xFF,
402 0x00, 0x09, 0x42, 0x81, 0x00, 0x09, 0x42, 0x91, 0x00, 0xC0};
403
404 } // namespace
405
406 class HidReportDescriptorTest : public testing::Test {
407
408 protected:
409 virtual void SetUp() OVERRIDE { descriptor_ = NULL; }
410
411 virtual void TearDown() OVERRIDE {
412 if (descriptor_) {
413 delete descriptor_;
414 }
415 }
416
417 public:
418 void ParseDescriptor(const std::string& expected,
419 const uint8_t* bytes,
420 size_t size) {
421 descriptor_ = new HidReportDescriptor(bytes, size);
422
423 std::stringstream actual;
424 actual << *descriptor_;
425
426 std::cout << "HID report descriptor:" << std::endl;
427 std::cout << actual.str();
428
429 // TODO(jracle@logitech.com): refactor string comparison in favor of
430 // testing individual fields.
431 ASSERT_EQ(expected, actual.str());
432 }
433
434 void GetTopLevelCollections(const std::vector<HidUsageAndPage>& expected,
435 const uint8_t* bytes,
436 size_t size) {
437 descriptor_ = new HidReportDescriptor(bytes, size);
438
439 std::vector<HidUsageAndPage> actual;
440 descriptor_->GetTopLevelCollections(&actual);
441
442 std::cout << "HID top-level collections:" << std::endl;
443 for (std::vector<HidUsageAndPage>::const_iterator iter = actual.begin();
444 iter != actual.end();
445 ++iter) {
446 std::cout << *iter << std::endl;
447 }
448
449 ASSERT_THAT(actual, ContainerEq(expected));
450 }
451
452 private:
453 HidReportDescriptor* descriptor_;
454 };
455
456 TEST_F(HidReportDescriptorTest, ParseDescriptor_Keyboard) {
457 const char expected[] = {
458 "Usage Page (Generic Desktop)\n"
459 "Usage (0x6)\n"
460 "Collection (Physical)\n"
461 " Usage Page (Keyboard)\n"
462 " Usage Minimum (224)\n"
463 " Usage Maximum (231)\n"
464 " Logical Minimum (0)\n"
465 " Logical Maximum (1)\n"
466 " Report Size (1)\n"
467 " Report Count (8)\n"
468 " Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
469 " Report Count (1)\n"
470 " Report Size (8)\n"
471 " Input (Con|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
472 " Report Count (5)\n"
473 " Report Size (1)\n"
474 " Usage Page (Led)\n"
475 " Usage Minimum (1)\n"
476 " Usage Maximum (5)\n"
477 " Output (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
478 " Report Count (1)\n"
479 " Report Size (3)\n"
480 " Output (Con|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
481 " Report Count (6)\n"
482 " Report Size (8)\n"
483 " Logical Minimum (0)\n"
484 " Logical Maximum (101)\n"
485 " Usage Page (Keyboard)\n"
486 " Usage Minimum (0)\n"
487 " Usage Maximum (101)\n"
488 " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
489 "End Collection\n"};
490
491 ParseDescriptor(std::string(expected), kKeyboard, sizeof(kKeyboard));
492 }
493
494 TEST_F(HidReportDescriptorTest, TopLevelCollections_Keyboard) {
495 HidUsageAndPage expected[] = {
496 HidUsageAndPage(0x06, HidUsageAndPage::kPageGenericDesktop)};
497
498 GetTopLevelCollections(std::vector<HidUsageAndPage>(
499 expected, expected + ARRAYSIZE_UNSAFE(expected)),
500 kKeyboard,
501 sizeof(kKeyboard));
502 }
503
504 TEST_F(HidReportDescriptorTest, ParseDescriptor_Mouse) {
505 const char expected[] = {
506 "Usage Page (Generic Desktop)\n"
507 "Usage (0x2)\n"
508 "Collection (Physical)\n"
509 " Usage (0x1)\n"
510 " Collection (Physical)\n"
511 " Usage Page (Button)\n"
512 " Usage Minimum (1)\n"
513 " Usage Maximum (3)\n"
514 " Logical Minimum (0)\n"
515 " Logical Maximum (1)\n"
516 " Report Count (3)\n"
517 " Report Size (1)\n"
518 " Input (Dat|Arr|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
519 " Report Count (1)\n"
520 " Report Size (5)\n"
521 " Input (Con|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
522 " Usage Page (Generic Desktop)\n"
523 " Usage (0x30)\n"
524 " Usage (0x31)\n"
525 " Logical Minimum (129)\n"
526 " Logical Maximum (127)\n"
527 " Report Size (8)\n"
528 " Report Count (2)\n"
529 " Input (Dat|Arr|Abs|NoWrp|Lin|Prf|NoNull|BitF)\n"
530 " End Collection\n"
531 "End Collection\n"};
532
533 ParseDescriptor(std::string(expected), kMouse, sizeof(kMouse));
534 }
535
536 TEST_F(HidReportDescriptorTest, TopLevelCollections_Mouse) {
537 HidUsageAndPage expected[] = {
538 HidUsageAndPage(0x02, HidUsageAndPage::kPageGenericDesktop)};
539
540 GetTopLevelCollections(std::vector<HidUsageAndPage>(
541 expected, expected + ARRAYSIZE_UNSAFE(expected)),
542 kMouse,
543 sizeof(kMouse));
544 }
545
546 TEST_F(HidReportDescriptorTest, ParseDescriptor_LogitechUnifyingReceiver) {
547 const char expected[] = {
548 "Usage Page (Vendor)\n"
549 "Usage (0x1)\n"
550 "Collection (Physical)\n"
551 " Report ID (0x10)\n"
552 " Report Size (8)\n"
553 " Report Count (6)\n"
554 " Logical Minimum (0)\n"
555 " Logical Maximum (255)\n"
556 " Usage (0x1)\n"
557 " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
558 " Usage (0x1)\n"
559 " Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
560 "End Collection\n"
561 "Usage Page (Vendor)\n"
562 "Usage (0x2)\n"
563 "Collection (Physical)\n"
564 " Report ID (0x11)\n"
565 " Report Size (8)\n"
566 " Report Count (19)\n"
567 " Logical Minimum (0)\n"
568 " Logical Maximum (255)\n"
569 " Usage (0x2)\n"
570 " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
571 " Usage (0x2)\n"
572 " Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
573 "End Collection\n"
574 "Usage Page (Vendor)\n"
575 "Usage (0x4)\n"
576 "Collection (Physical)\n"
577 " Report ID (0x20)\n"
578 " Report Size (8)\n"
579 " Report Count (14)\n"
580 " Logical Minimum (0)\n"
581 " Logical Maximum (255)\n"
582 " Usage (0x41)\n"
583 " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
584 " Usage (0x41)\n"
585 " Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
586 " Report ID (0x21)\n"
587 " Report Count (31)\n"
588 " Logical Minimum (0)\n"
589 " Logical Maximum (255)\n"
590 " Usage (0x42)\n"
591 " Input (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
592 " Usage (0x42)\n"
593 " Output (Dat|Var|Rel|NoWrp|Lin|Prf|NoNull|BitF)\n"
594 "End Collection\n"};
595
596 ParseDescriptor(std::string(expected),
597 kLogitechUnifyingReceiver,
598 sizeof(kLogitechUnifyingReceiver));
599 }
600
601 TEST_F(HidReportDescriptorTest, TopLevelCollections_LogitechUnifyingReceiver) {
602 HidUsageAndPage expected[] = {
603 HidUsageAndPage(0x01, HidUsageAndPage::kPageVendor),
604 HidUsageAndPage(0x02, HidUsageAndPage::kPageVendor),
605 HidUsageAndPage(0x04, HidUsageAndPage::kPageVendor), };
606
607 GetTopLevelCollections(std::vector<HidUsageAndPage>(
608 expected, expected + ARRAYSIZE_UNSAFE(expected)),
609 kLogitechUnifyingReceiver,
610 sizeof(kLogitechUnifyingReceiver));
611 }
612
613 } // namespace device
OLDNEW
« no previous file with comments | « device/hid/hid_report_descriptor_item.cc ('k') | device/hid/hid_service_linux.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698