xref: /haiku/src/add-ons/kernel/drivers/input/hid_shared/HIDParser.cpp (revision 4a32f48e70297d9a634646f01e08c2f451ecd6bd)
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 
21 static uint8 sItemSize[4] = { 0, 1, 2, 4 };
22 static 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 
28 HIDParser::HIDParser(HIDDevice *device)
29 	:	fDevice(device),
30 		fUsesReportIDs(false),
31 		fReportCount(0),
32 		fReports(NULL),
33 		fRootCollection(NULL)
34 {
35 }
36 
37 
38 HIDParser::~HIDParser()
39 {
40 	_Reset();
41 }
42 
43 
44 status_t
45 HIDParser::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: %" B_PRIu32 "\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 
393 HIDReport *
394 HIDParser::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 
409 uint8
410 HIDParser::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 
426 HIDReport *
427 HIDParser::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 
442 size_t
443 HIDParser::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 
462 void
463 HIDParser::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 
496 void
497 HIDParser::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 
511 HIDReport *
512 HIDParser::_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 
538 float
539 HIDParser::_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 
567 void
568 HIDParser::_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