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