xref: /haiku/src/add-ons/kernel/drivers/input/hid_shared/HIDParser.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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 					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 
382 HIDReport *
383 HIDParser::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 
398 uint8
399 HIDParser::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 
415 HIDReport *
416 HIDParser::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 
431 size_t
432 HIDParser::MaxReportSize()
433 {
434 	size_t maxSize = 0;
435 	for (int32 i = 0; i < fReports.Count(); i++) {
436 		HIDReport *report = fReports[i];
437 		if (report == NULL)
438 			continue;
439 
440 		if (report->ReportSize() > maxSize)
441 			maxSize = report->ReportSize();
442 	}
443 
444 	if (fUsesReportIDs)
445 		maxSize++;
446 
447 	return maxSize;
448 }
449 
450 
451 void
452 HIDParser::SetReport(status_t status, uint8 *report, size_t length)
453 {
454 	if (status != B_OK || length == 0) {
455 		if (status == B_OK)
456 			status = B_ERROR;
457 
458 		report = NULL;
459 		length = 0;
460 	}
461 
462 	uint8 targetID = 0;
463 	if (fUsesReportIDs && status == B_OK) {
464 		targetID = report[0];
465 		report++;
466 		length--;
467 	}
468 
469 	// We need to notify all input reports, as we don't know who has waiting
470 	// listeners. Anyone other than the target report also waiting for a
471 	// transfer to happen needs to reschedule one now.
472 	for (int32 i = 0; i < fReports.Count(); i++) {
473 		if (fReports[i] == NULL
474 			|| fReports[i]->Type() != HID_REPORT_TYPE_INPUT)
475 			continue;
476 
477 		if (fReports[i]->ID() == targetID)
478 			fReports[i]->SetReport(status, report, length);
479 		else
480 			fReports[i]->SetReport(B_INTERRUPTED, NULL, 0);
481 	}
482 }
483 
484 
485 void
486 HIDParser::PrintToStream()
487 {
488 	for (int32 i = 0; i < fReports.Count(); i++) {
489 		HIDReport *report = fReports[i];
490 		if (report == NULL)
491 			continue;
492 
493 		report->PrintToStream();
494 	}
495 
496 	fRootCollection->PrintToStream();
497 }
498 
499 
500 HIDReport *
501 HIDParser::_FindOrCreateReport(uint8 type, uint8 id)
502 {
503 	HIDReport *report = FindReport(type, id);
504 	if (report != NULL)
505 		return report;
506 
507 	report = new(std::nothrow) HIDReport(this, type, id);
508 	if (report == NULL) {
509 		TRACE_ALWAYS("no memory when allocating report\n");
510 		return NULL;
511 	}
512 
513 	if (fReports.PushBack(report) == B_NO_MEMORY) {
514 		TRACE_ALWAYS("no memory when growing report list\n");
515 		delete report;
516 		return NULL;
517 	}
518 
519 	return report;
520 }
521 
522 
523 float
524 HIDParser::_CalculateResolution(global_item_state *state)
525 {
526 	int64 physicalMinimum = state->physical_minimum;
527 	int64 physicalMaximum = state->physical_maximum;
528 	if (physicalMinimum == 0 && physicalMaximum == 0) {
529 		physicalMinimum = state->logical_minimum;
530 		physicalMaximum = state->logical_maximum;
531 	}
532 
533 	int8 unitExponent = sUnitExponent[state->unit_exponent];
534 
535 	float power = 1;
536 	if (unitExponent < 0) {
537 		while (unitExponent++ < 0)
538 			power /= 10;
539 	} else {
540 		while (unitExponent-- > 0)
541 			power *= 10;
542 	}
543 
544 	float divisor = (physicalMaximum - physicalMinimum) * power;
545 	if (divisor == 0.0)
546 		return 0.0;
547 
548 	return (state->logical_maximum - state->logical_minimum) / divisor;
549 }
550 
551 
552 void
553 HIDParser::_Reset()
554 {
555 
556 	for (int32 i = 0; i < fReports.Count(); i++)
557 		delete fReports[i];
558 
559 	fReports.MakeEmpty();
560 
561 	delete fRootCollection;
562 
563 	fUsesReportIDs = false;
564 	fRootCollection = NULL;
565 }
566