1/*
2 * Copyright 2009-2011, Michael Lotz, mmlr@mlotz.ch.
3 * Distributed under the terms of the MIT License.
4 */
5
6#ifndef USERLAND_HID
7#include "Driver.h"
8#else
9#include "UserlandHID.h"
10#endif
11
12#include "HIDCollection.h"
13#include "HIDReport.h"
14#include "HIDReportItem.h"
15
16#include <new>
17#include <stdlib.h>
18#include <string.h>
19
20
21HIDCollection::HIDCollection(HIDCollection *parent, uint8 type,
22	local_item_state &localState)
23	:	fParent(parent),
24		fType(type),
25		fStringID(localState.string_index),
26		fPhysicalID(localState.designator_index),
27		fChildCount(0),
28		fChildren(NULL),
29		fItemCount(0),
30		fItemsAllocated(0),
31		fItems(NULL)
32{
33	usage_value usageValue;
34	if (localState.usage_stack != NULL && localState.usage_stack_used > 0)
35		usageValue.u.extended = localState.usage_stack[0].u.extended;
36	else if (localState.usage_minimum_set)
37		usageValue.u.extended = localState.usage_minimum.u.extended;
38	else if (localState.usage_maximum_set)
39		usageValue.u.extended = localState.usage_maximum.u.extended;
40	else if (type == COLLECTION_LOGICAL) {
41		// this is just a logical grouping collection
42		usageValue.u.extended = 0;
43	} else {
44		TRACE_ALWAYS("none of the possible usages for the collection are "
45			"set\n");
46	}
47
48	fUsage = usageValue.u.extended;
49}
50
51
52HIDCollection::~HIDCollection()
53{
54	for (uint32 i = 0; i < fChildCount; i++)
55		delete fChildren[i];
56	free(fChildren);
57	free(fItems);
58}
59
60
61uint16
62HIDCollection::UsagePage()
63{
64	usage_value value;
65	value.u.extended = fUsage;
66	return value.u.s.usage_page;
67}
68
69
70uint16
71HIDCollection::UsageID()
72{
73	usage_value value;
74	value.u.extended = fUsage;
75	return value.u.s.usage_id;
76}
77
78
79status_t
80HIDCollection::AddChild(HIDCollection *child)
81{
82	HIDCollection **newChildren = (HIDCollection **)realloc(fChildren,
83		(fChildCount + 1) * sizeof(HIDCollection *));
84	if (newChildren == NULL) {
85		TRACE_ALWAYS("no memory when trying to resize collection child list\n");
86		return B_NO_MEMORY;
87	}
88
89	fChildren = newChildren;
90	fChildren[fChildCount++] = child;
91	return B_OK;
92}
93
94
95HIDCollection *
96HIDCollection::ChildAt(uint32 index)
97{
98	if (index >= fChildCount)
99		return NULL;
100
101	return fChildren[index];
102}
103
104
105uint32
106HIDCollection::CountChildrenFlat(uint8 type)
107{
108	uint32 count = 0;
109	if (type == COLLECTION_ALL || fType == type)
110		count++;
111
112	for (uint32 i = 0; i < fChildCount; i++) {
113		HIDCollection *child = fChildren[i];
114		if (child == NULL)
115			continue;
116
117		count += child->CountChildrenFlat(type);
118	}
119
120	return count;
121}
122
123
124HIDCollection *
125HIDCollection::ChildAtFlat(uint8 type, uint32 index)
126{
127	return _ChildAtFlat(type, index);
128}
129
130
131void
132HIDCollection::AddItem(HIDReportItem *item)
133{
134	if (fItemCount >= fItemsAllocated) {
135		fItemsAllocated += 10;
136		HIDReportItem **newItems = (HIDReportItem **)realloc(fItems,
137			fItemsAllocated * sizeof(HIDReportItem *));
138		if (newItems == NULL) {
139			TRACE_ALWAYS("no memory when trying to resize collection items\n");
140			fItemsAllocated -= 10;
141			return;
142		}
143
144		fItems = newItems;
145	}
146
147	fItems[fItemCount++] = item;
148}
149
150
151HIDReportItem *
152HIDCollection::ItemAt(uint32 index)
153{
154	if (index >= fItemCount)
155		return NULL;
156
157	return fItems[index];
158}
159
160
161uint32
162HIDCollection::CountItemsFlat()
163{
164	uint32 count = fItemCount;
165
166	for (uint32 i = 0; i < fChildCount; i++) {
167		HIDCollection *child = fChildren[i];
168		if (child != NULL)
169			count += child->CountItemsFlat();
170	}
171
172	return count;
173}
174
175
176HIDReportItem *
177HIDCollection::ItemAtFlat(uint32 index)
178{
179	return _ItemAtFlat(index);
180}
181
182
183void
184HIDCollection::PrintToStream(uint32 indentLevel)
185{
186	char indent[indentLevel + 1];
187	memset(indent, '\t', indentLevel);
188	indent[indentLevel] = 0;
189
190	const char *typeName = "unknown";
191	switch (fType) {
192		case COLLECTION_PHYSICAL:
193			typeName = "physical";
194			break;
195		case COLLECTION_APPLICATION:
196			typeName = "application";
197			break;
198		case COLLECTION_LOGICAL:
199			typeName = "logical";
200			break;
201		case COLLECTION_REPORT:
202			typeName = "report";
203			break;
204		case COLLECTION_NAMED_ARRAY:
205			typeName = "named array";
206			break;
207		case COLLECTION_USAGE_SWITCH:
208			typeName = "usage switch";
209			break;
210		case COLLECTION_USAGE_MODIFIER:
211			typeName = "usage modifier";
212			break;
213	}
214
215	TRACE_ALWAYS("%sHIDCollection %p\n", indent, this);
216	TRACE_ALWAYS("%s\ttype: %u %s\n", indent, fType, typeName);
217	TRACE_ALWAYS("%s\tusage: 0x%08" B_PRIx32 "\n", indent, fUsage);
218	TRACE_ALWAYS("%s\tstring id: %u\n", indent, fStringID);
219	TRACE_ALWAYS("%s\tphysical id: %u\n", indent, fPhysicalID);
220
221	TRACE_ALWAYS("%s\titem count: %" B_PRIu32 "\n", indent, fItemCount);
222	for (uint32 i = 0; i < fItemCount; i++) {
223		HIDReportItem *item = fItems[i];
224		if (item != NULL)
225			item->PrintToStream(indentLevel + 1);
226	}
227
228	TRACE_ALWAYS("%s\tchild count: %" B_PRIu32 "\n", indent, fChildCount);
229	for (uint32 i = 0; i < fChildCount; i++) {
230		HIDCollection *child = fChildren[i];
231		if (child != NULL)
232			child->PrintToStream(indentLevel + 1);
233	}
234}
235
236
237HIDCollection *
238HIDCollection::_ChildAtFlat(uint8 type, uint32 &index)
239{
240	if (type == COLLECTION_ALL || fType == type) {
241		if (index == 0)
242			return this;
243
244		index--;
245	}
246
247	for (uint32 i = 0; i < fChildCount; i++) {
248		HIDCollection *child = fChildren[i];
249		if (child == NULL)
250			continue;
251
252		HIDCollection *result = child->_ChildAtFlat(type, index);
253		if (result != NULL)
254			return result;
255	}
256
257	return NULL;
258}
259
260
261HIDReportItem *
262HIDCollection::_ItemAtFlat(uint32 &index)
263{
264	if (index < fItemCount)
265		return fItems[index];
266
267	index -= fItemCount;
268
269	for (uint32 i = 0; i < fChildCount; i++) {
270		HIDCollection *child = fChildren[i];
271		if (child == NULL)
272			continue;
273
274		HIDReportItem *result = child->_ItemAtFlat(index);
275		if (result != NULL)
276			return result;
277	}
278
279	return NULL;
280}
281
282
283void
284HIDCollection::BuildReportList(uint8 reportType,
285	HIDReport **reportList, uint32 &reportCount)
286{
287
288	for (uint32 i = 0; i < fItemCount; i++) {
289		HIDReportItem *item = fItems[i];
290		if (item == NULL)
291			continue;
292
293		HIDReport *report = item->Report();
294		if (reportType != HID_REPORT_TYPE_ANY && report->Type() != reportType)
295			continue;
296
297		bool found = false;
298		for (uint32 j = 0; j < reportCount; j++) {
299			if (reportList[j] == report) {
300				found = true;
301				break;
302			}
303		}
304
305		if (found)
306			continue;
307
308		reportList[reportCount++] = report;
309	}
310
311	for (uint32 i = 0; i < fChildCount; i++) {
312		HIDCollection *child = fChildren[i];
313		if (child == NULL)
314			continue;
315
316		child->BuildReportList(reportType, reportList, reportCount);
317	}
318}
319