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