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