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 "HIDParser.h"
14#include "HIDReport.h"
15
16#include <new>
17#include <stdlib.h>
18#include <string.h>
19
20
21static uint8 sItemSize[4] = { 0, 1, 2, 4 };
22static int8 sUnitExponent[16] = {
23	// really just a 4 bit signed value
24	0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1
25};
26
27
28HIDParser::HIDParser(HIDDevice *device)
29	:	fDevice(device),
30		fUsesReportIDs(false),
31		fRootCollection(NULL)
32{
33}
34
35
36HIDParser::~HIDParser()
37{
38	_Reset();
39}
40
41
42status_t
43HIDParser::ParseReportDescriptor(const uint8 *reportDescriptor,
44	size_t descriptorLength)
45{
46	_Reset();
47
48	global_item_state globalState;
49	memset(&globalState, 0, sizeof(global_item_state));
50
51	local_item_state localState;
52	memset(&localState, 0, sizeof(local_item_state));
53
54	Vector<usage_value> usageStack;
55
56	fRootCollection = new(std::nothrow) HIDCollection(NULL, COLLECTION_LOGICAL,
57		localState);
58	if (fRootCollection == NULL) {
59		TRACE_ALWAYS("no memory to allocate root collection\n");
60		return B_NO_MEMORY;
61	}
62
63	HIDCollection *collection = fRootCollection;
64	const uint8 *pointer = reportDescriptor;
65	const uint8 *end = pointer + descriptorLength;
66
67	while (pointer < end) {
68		const item_prefix *item = (item_prefix *)pointer;
69		size_t itemSize = sItemSize[item->size];
70		uint32 data = 0;
71
72		if (item->type == ITEM_TYPE_LONG) {
73			long_item *longItem = (long_item *)item;
74			itemSize += longItem->data_size;
75		} else {
76			short_item *shortItem = (short_item *)item;
77			switch (itemSize) {
78				case 1:
79					data = shortItem->data.as_uint8[0];
80					break;
81
82				case 2:
83					data = shortItem->data.as_uint16[0];
84					break;
85
86				case 4:
87					data = shortItem->data.as_uint32;
88					break;
89
90				default:
91					break;
92			}
93		}
94
95		TRACE("got item: type: %s; size: %lu; tag: %u; data: %" B_PRIu32 "\n",
96			item->type == ITEM_TYPE_MAIN ? "main"
97			: item->type == ITEM_TYPE_GLOBAL ? "global"
98			: item->type == ITEM_TYPE_LOCAL ? "local" : "long",
99			itemSize, item->tag, data);
100
101		switch (item->type) {
102			case ITEM_TYPE_MAIN:
103			{
104				// preprocess the local state if relevant (usages for
105				// collections and report items)
106				if (item->tag != ITEM_TAG_MAIN_END_COLLECTION) {
107					// make all usages extended for easier later processing
108					for (int32 i = 0; i < usageStack.Count(); i++) {
109						if (usageStack[i].is_extended)
110							continue;
111						usageStack[i].u.s.usage_page = globalState.usage_page;
112						usageStack[i].is_extended = true;
113					}
114
115					localState.usage_stack = &usageStack[0];
116					localState.usage_stack_used = usageStack.Count();
117				}
118
119				if (item->tag == ITEM_TAG_MAIN_COLLECTION) {
120					HIDCollection *newCollection
121						= new(std::nothrow) HIDCollection(collection,
122							(uint8)data, localState);
123					if (newCollection == NULL) {
124						TRACE_ALWAYS("no memory to allocate new collection\n");
125						break;
126					}
127
128					collection->AddChild(newCollection);
129					collection = newCollection;
130				} else if (item->tag == ITEM_TAG_MAIN_END_COLLECTION) {
131					if (collection == fRootCollection) {
132						TRACE_ALWAYS("end collection with no open one\n");
133						break;
134					}
135
136					collection = collection->Parent();
137				} else {
138					uint8 reportType = HID_REPORT_TYPE_ANY;
139					switch (item->tag) {
140						case ITEM_TAG_MAIN_INPUT:
141							reportType = HID_REPORT_TYPE_INPUT;
142							break;
143
144						case ITEM_TAG_MAIN_OUTPUT:
145							reportType = HID_REPORT_TYPE_OUTPUT;
146							break;
147
148						case ITEM_TAG_MAIN_FEATURE:
149							reportType = HID_REPORT_TYPE_FEATURE;
150							break;
151
152						default:
153							TRACE_ALWAYS("unknown main item tag: 0x%02x\n",
154								item->tag);
155							break;
156					}
157
158					if (reportType == HID_REPORT_TYPE_ANY)
159						break;
160
161					HIDReport *target = _FindOrCreateReport(reportType,
162						globalState.report_id);
163					if (target == NULL)
164						break;
165
166					// fill in a sensible default if the index isn't set
167					if (!localState.designator_index_set) {
168						localState.designator_index
169							= localState.designator_minimum;
170					}
171
172					if (!localState.string_index_set)
173						localState.string_index = localState.string_minimum;
174
175					main_item_data *mainData = (main_item_data *)&data;
176					target->AddMainItem(globalState, localState, *mainData,
177						collection);
178				}
179
180				// reset the local item state
181				memset(&localState, 0, sizeof(local_item_state));
182				usageStack.MakeEmpty();
183				break;
184			}
185
186			case ITEM_TYPE_GLOBAL:
187			{
188				switch (item->tag) {
189					case ITEM_TAG_GLOBAL_USAGE_PAGE:
190						globalState.usage_page = data;
191						break;
192
193					case ITEM_TAG_GLOBAL_LOGICAL_MINIMUM:
194						globalState.logical_minimum = data;
195						break;
196
197					case ITEM_TAG_GLOBAL_LOGICAL_MAXIMUM:
198						globalState.logical_maximum = data;
199						break;
200
201					case ITEM_TAG_GLOBAL_PHYSICAL_MINIMUM:
202						globalState.physical_minimum = data;
203						break;
204
205					case ITEM_TAG_GLOBAL_PHYSICAL_MAXIMUM:
206						globalState.physical_maximum = data;
207						break;
208
209					case ITEM_TAG_GLOBAL_UNIT_EXPONENT:
210						globalState.unit_exponent = data;
211						break;
212
213					case ITEM_TAG_GLOBAL_UNIT:
214						globalState.unit = data;
215						break;
216
217					case ITEM_TAG_GLOBAL_REPORT_SIZE:
218						globalState.report_size = data;
219						break;
220
221					case ITEM_TAG_GLOBAL_REPORT_ID:
222						globalState.report_id = data;
223						fUsesReportIDs = true;
224						break;
225
226					case ITEM_TAG_GLOBAL_REPORT_COUNT:
227						globalState.report_count = data;
228						break;
229
230					case ITEM_TAG_GLOBAL_PUSH:
231					{
232						global_item_state *copy = (global_item_state *)malloc(
233							sizeof(global_item_state));
234						if (copy == NULL) {
235							TRACE_ALWAYS("out of memory for global push\n");
236							break;
237						}
238
239						memcpy(copy, &globalState, sizeof(global_item_state));
240						globalState.link = copy;
241						break;
242					}
243
244					case ITEM_TAG_GLOBAL_POP:
245					{
246						if (globalState.link == NULL) {
247							TRACE_ALWAYS("global pop without item on stack\n");
248							break;
249						}
250
251						global_item_state *link = globalState.link;
252						memcpy(&globalState, link, sizeof(global_item_state));
253						free(link);
254						break;
255					}
256
257					default:
258						TRACE_ALWAYS("unknown global item tag: 0x%02x\n",
259							item->tag);
260						break;
261				}
262
263				break;
264			}
265
266			case ITEM_TYPE_LOCAL:
267			{
268				switch (item->tag) {
269					case ITEM_TAG_LOCAL_USAGE:
270					{
271						usage_value value;
272						value.is_extended = itemSize == sizeof(uint32);
273						value.u.extended = data;
274
275						if (usageStack.PushBack(value) == B_NO_MEMORY) {
276							TRACE_ALWAYS("no memory when growing usages\n");
277							break;
278						}
279
280						break;
281					}
282
283					case ITEM_TAG_LOCAL_USAGE_MINIMUM:
284						localState.usage_minimum.u.extended = data;
285						localState.usage_minimum.is_extended
286							= itemSize == sizeof(uint32);
287						localState.usage_minimum_set = true;
288						break;
289
290					case ITEM_TAG_LOCAL_USAGE_MAXIMUM:
291						localState.usage_maximum.u.extended = data;
292						localState.usage_maximum.is_extended
293							= itemSize == sizeof(uint32);
294						localState.usage_maximum_set = true;
295
296						if (localState.usage_minimum.u.extended
297							<= localState.usage_maximum.u.extended) {
298
299							uint32 count = localState.usage_maximum.u.extended
300								- localState.usage_minimum.u.extended + 1;
301							usage_value value = localState.usage_minimum;
302
303							for (uint32 n = 0; n < count ; n++) {
304								if (usageStack.PushBack(value) == B_NO_MEMORY) {
305									TRACE_ALWAYS(
306										"no memory when growing usages\n");
307									break;
308								}
309								value.u.extended++;
310							}
311						}
312
313						localState.usage_minimum_set
314							= localState.usage_maximum_set = false;
315						break;
316
317					case ITEM_TAG_LOCAL_DESIGNATOR_INDEX:
318						localState.designator_index = data;
319						localState.designator_index_set = true;
320						break;
321
322					case ITEM_TAG_LOCAL_DESIGNATOR_MINIMUM:
323						localState.designator_minimum = data;
324						break;
325
326					case ITEM_TAG_LOCAL_DESIGNATOR_MAXIMUM:
327						localState.designator_maximum = data;
328						break;
329
330					case ITEM_TAG_LOCAL_STRING_INDEX:
331						localState.string_index = data;
332						localState.string_index_set = true;
333						break;
334
335					case ITEM_TAG_LOCAL_STRING_MINIMUM:
336						localState.string_minimum = data;
337						break;
338
339					case ITEM_TAG_LOCAL_STRING_MAXIMUM:
340						localState.string_maximum = data;
341						break;
342
343					default:
344						TRACE_ALWAYS("unknown local item tag: 0x%02x\n",
345							item->tag);
346						break;
347				}
348
349				break;
350			}
351
352			case ITEM_TYPE_LONG:
353			{
354				long_item *longItem = (long_item *)item;
355
356				// no long items are defined yet
357				switch (longItem->long_item_tag) {
358					default:
359						TRACE_ALWAYS("unknown long item tag: 0x%02x\n",
360							longItem->long_item_tag);
361						break;
362				}
363
364				break;
365			}
366		}
367
368		pointer += itemSize + sizeof(item_prefix);
369	}
370
371	global_item_state *state = globalState.link;
372	while (state != NULL) {
373		global_item_state *next = state->link;
374		free(state);
375		state = next;
376	}
377
378	return B_OK;
379}
380
381
382HIDReport *
383HIDParser::FindReport(uint8 type, uint8 id)
384{
385	for (int32 i = 0; i < fReports.Count(); i++) {
386		HIDReport *report = fReports[i];
387		if (report == NULL)
388			continue;
389
390		if ((report->Type() & type) != 0 && report->ID() == id)
391			return report;
392	}
393
394	return NULL;
395}
396
397
398uint8
399HIDParser::CountReports(uint8 type)
400{
401	uint8 count = 0;
402	for (int32 i = 0; i < fReports.Count(); i++) {
403		HIDReport *report = fReports[i];
404		if (report == NULL)
405			continue;
406
407		if (report->Type() & type)
408			count++;
409	}
410
411	return count;
412}
413
414
415HIDReport *
416HIDParser::ReportAt(uint8 type, uint8 index)
417{
418	for (int32 i = 0; i < fReports.Count(); i++) {
419		HIDReport *report = fReports[i];
420		if (report == NULL || (report->Type() & type) == 0)
421			continue;
422
423		if (index-- == 0)
424			return report;
425	}
426
427	return NULL;
428}
429
430
431size_t
432HIDParser::MaxReportSize()
433{
434	return MaxReportSize(HID_REPORT_TYPE_ANY);
435}
436
437
438size_t
439HIDParser::MaxReportSize(uint8 type)
440{
441	size_t maxSize = 0;
442	for (int32 i = 0; i < fReports.Count(); i++) {
443		HIDReport *report = fReports[i];
444		if (report == NULL)
445			continue;
446
447		if (type != HID_REPORT_TYPE_ANY && report->Type() != type)
448			continue;
449
450		if (report->ReportSize() > maxSize)
451			maxSize = report->ReportSize();
452	}
453
454	if (fUsesReportIDs)
455		maxSize++;
456
457	return maxSize;
458}
459
460
461void
462HIDParser::SetReport(status_t status, uint8 *report, size_t length)
463{
464	if (status != B_OK || length == 0) {
465		if (status == B_OK)
466			status = B_ERROR;
467
468		report = NULL;
469		length = 0;
470	}
471
472	uint8 targetID = 0;
473	if (fUsesReportIDs && status == B_OK) {
474		targetID = report[0];
475		report++;
476		length--;
477	}
478
479	// We need to notify all input reports, as we don't know who has waiting
480	// listeners. Anyone other than the target report also waiting for a
481	// transfer to happen needs to reschedule one now.
482	for (int32 i = 0; i < fReports.Count(); i++) {
483		if (fReports[i] == NULL
484			|| fReports[i]->Type() != HID_REPORT_TYPE_INPUT)
485			continue;
486
487		if (fReports[i]->ID() == targetID)
488			fReports[i]->SetReport(status, report, length);
489		else
490			fReports[i]->SetReport(B_INTERRUPTED, NULL, 0);
491	}
492}
493
494
495void
496HIDParser::PrintToStream()
497{
498	for (int32 i = 0; i < fReports.Count(); i++) {
499		HIDReport *report = fReports[i];
500		if (report == NULL)
501			continue;
502
503		report->PrintToStream();
504	}
505
506	fRootCollection->PrintToStream();
507}
508
509
510HIDReport *
511HIDParser::_FindOrCreateReport(uint8 type, uint8 id)
512{
513	HIDReport *report = FindReport(type, id);
514	if (report != NULL)
515		return report;
516
517	report = new(std::nothrow) HIDReport(this, type, id);
518	if (report == NULL) {
519		TRACE_ALWAYS("no memory when allocating report\n");
520		return NULL;
521	}
522
523	if (fReports.PushBack(report) == B_NO_MEMORY) {
524		TRACE_ALWAYS("no memory when growing report list\n");
525		delete report;
526		return NULL;
527	}
528
529	return report;
530}
531
532
533float
534HIDParser::_CalculateResolution(global_item_state *state)
535{
536	int64 physicalMinimum = state->physical_minimum;
537	int64 physicalMaximum = state->physical_maximum;
538	if (physicalMinimum == 0 && physicalMaximum == 0) {
539		physicalMinimum = state->logical_minimum;
540		physicalMaximum = state->logical_maximum;
541	}
542
543	int8 unitExponent = sUnitExponent[state->unit_exponent];
544
545	float power = 1;
546	if (unitExponent < 0) {
547		while (unitExponent++ < 0)
548			power /= 10;
549	} else {
550		while (unitExponent-- > 0)
551			power *= 10;
552	}
553
554	float divisor = (physicalMaximum - physicalMinimum) * power;
555	if (divisor == 0.0)
556		return 0.0;
557
558	return (state->logical_maximum - state->logical_minimum) / divisor;
559}
560
561
562void
563HIDParser::_Reset()
564{
565	for (int32 i = 0; i < fReports.Count(); i++)
566		delete fReports[i];
567
568	fReports.MakeEmpty();
569
570	delete fRootCollection;
571
572	fUsesReportIDs = false;
573	fRootCollection = NULL;
574}
575