blob: a06d28421c5451cc626b8af3bc03747838463f11 [file] [log] [blame]
// 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_report_descriptor.h"
#include "base/stl_util.h"
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;
while (header_index < size) {
item = new HidReportDescriptorItem(&bytes[header_index], item);
items_.push_back(linked_ptr<HidReportDescriptorItem>(item));
header_index += item->GetSize();
}
}
HidReportDescriptor::~HidReportDescriptor() {}
void HidReportDescriptor::GetDetails(
std::vector<HidCollectionInfo>* 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<linked_ptr<HidReportDescriptorItem> >::const_iterator
items_iter = items().begin();
items_iter != items().end();
++items_iter) {
linked_ptr<HidReportDescriptorItem> 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