1/*
2 * Copyright 2009, 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 "HIDDevice.h"
14#include "HIDReport.h"
15#include "HIDReportItem.h"
16
17#include <new>
18#include <stdlib.h>
19#include <string.h>
20
21
22HIDReport::HIDReport(HIDParser *parser, uint8 type, uint8 id)
23	:	fParser(parser),
24		fType(type),
25		fReportID(id),
26		fReportSize(0),
27		fItemsUsed(0),
28		fItemsAllocated(0),
29		fItems(NULL),
30		fReportStatus(B_NO_INIT),
31		fCurrentReport(NULL),
32		fBusyCount(0)
33{
34#ifndef USERLAND_HID
35	fConditionVariable.Init(this, "hid report");
36#endif
37}
38
39
40HIDReport::~HIDReport()
41{
42	free(fItems);
43}
44
45
46void
47HIDReport::AddMainItem(global_item_state &globalState,
48	local_item_state &localState, main_item_data &mainData,
49	HIDCollection *collection)
50{
51	TRACE("adding main item to report of type 0x%02x with id 0x%02x\n",
52		fType, fReportID);
53	TRACE("\tmain data:\n");
54	TRACE("\t\t%s\n", mainData.data_constant ? "constant" : "data");
55	TRACE("\t\t%s\n", mainData.array_variable ? "variable" : "array");
56	TRACE("\t\t%s\n", mainData.relative ? "relative" : "absolute");
57	TRACE("\t\t%swrap\n", mainData.wrap ? "" : "no-");
58	TRACE("\t\t%slinear\n", mainData.non_linear ? "non-" : "");
59	TRACE("\t\t%spreferred state\n", mainData.no_preferred ? "no " : "");
60	TRACE("\t\t%s null\n", mainData.null_state ? "has" : "no");
61	TRACE("\t\t%svolatile\n", mainData.is_volatile ? "" : "non-");
62	TRACE("\t\t%s\n", mainData.bits_bytes ? "bit array" : "buffered bytes");
63
64	uint32 logicalMinimum = globalState.logical_minimum;
65	uint32 logicalMaximum = globalState.logical_maximum;
66	if (logicalMinimum > logicalMaximum)
67		_SignExtend(logicalMinimum, logicalMaximum);
68
69	uint32 physicalMinimum = globalState.physical_minimum;
70	uint32 physicalMaximum = globalState.physical_maximum;
71	if (physicalMinimum > logicalMaximum)
72		_SignExtend(physicalMinimum, physicalMaximum);
73
74	TRACE("\tglobal state:\n");
75	TRACE("\t\tusage_page: 0x%x\n", globalState.usage_page);
76	TRACE("\t\tlogical_minimum: %ld\n", logicalMinimum);
77	TRACE("\t\tlogical_maximum: %ld\n", logicalMaximum);
78	TRACE("\t\tphysical_minimum: %ld\n", physicalMinimum);
79	TRACE("\t\tphysical_maximum: %ld\n", physicalMaximum);
80	TRACE("\t\tunit_exponent: %d\n", globalState.unit_exponent);
81	TRACE("\t\tunit: %d\n", globalState.unit);
82	TRACE("\t\treport_size: %lu\n", globalState.report_size);
83	TRACE("\t\treport_count: %lu\n", globalState.report_count);
84	TRACE("\t\treport_id: %u\n", globalState.report_id);
85
86	TRACE("\tlocal state:\n");
87	TRACE("\t\tusage stack (%lu)\n", localState.usage_stack_used);
88	for (uint32 i = 0; i < localState.usage_stack_used; i++) {
89		TRACE("\t\t\t0x%08lx\n", localState.usage_stack[i].u.extended);
90	}
91
92	TRACE("\t\tusage_minimum: 0x%08lx\n", localState.usage_minimum.u.extended);
93	TRACE("\t\tusage_maximum: 0x%08lx\n", localState.usage_maximum.u.extended);
94	TRACE("\t\tdesignator_index: %lu\n", localState.designator_index);
95	TRACE("\t\tdesignator_minimum: %lu\n", localState.designator_minimum);
96	TRACE("\t\tdesignator_maximum: %lu\n", localState.designator_maximum);
97	TRACE("\t\tstring_index: %u\n", localState.string_index);
98	TRACE("\t\tstring_minimum: %u\n", localState.string_minimum);
99	TRACE("\t\tstring_maximum: %u\n", localState.string_maximum);
100
101	uint32 usageMinimum = 0, usageMaximum = 0;
102	if (mainData.array_variable == 0) {
103		usageMinimum = localState.usage_minimum.u.extended;
104		usageMaximum = localState.usage_maximum.u.extended;
105	}
106
107	uint32 usageRangeIndex = 0;
108	for (uint32 i = 0; i < globalState.report_count; i++) {
109		if (fItemsUsed >= fItemsAllocated) {
110			fItemsAllocated += 10;
111			HIDReportItem **newItems = (HIDReportItem **)realloc(fItems,
112				sizeof(HIDReportItem *) * fItemsAllocated);
113			if (newItems == NULL) {
114				TRACE_ALWAYS("no memory when growing report item list\n");
115				fItemsAllocated -= 10;
116				return;
117			}
118
119			fItems = newItems;
120		}
121
122		if (mainData.array_variable == 1) {
123			usage_value usage;
124			if (i < localState.usage_stack_used)
125				usage = localState.usage_stack[i];
126			else {
127				usage = localState.usage_minimum;
128				usage.u.extended += usageRangeIndex++;
129				if (usage.u.extended > localState.usage_maximum.u.extended)
130					usage.u.extended = localState.usage_maximum.u.extended;
131			}
132
133			usageMinimum = usageMaximum = usage.u.extended;
134		}
135
136		fItems[fItemsUsed] = new(std::nothrow) HIDReportItem(this,
137			fReportSize, globalState.report_size, mainData.data_constant == 0,
138			mainData.array_variable == 0, mainData.relative != 0,
139			logicalMinimum, logicalMaximum, usageMinimum, usageMaximum);
140		if (fItems[fItemsUsed] == NULL)
141			TRACE_ALWAYS("no memory when creating report item\n");
142
143		if (collection != NULL)
144			collection->AddItem(fItems[fItemsUsed]);
145		else
146			TRACE_ALWAYS("main item not part of a collection\n");
147
148		fReportSize += globalState.report_size;
149		fItemsUsed++;
150	}
151}
152
153
154void
155HIDReport::SetReport(status_t status, uint8 *report, size_t length)
156{
157	fReportStatus = status;
158	fCurrentReport = report;
159	if (status == B_OK && length * 8 < fReportSize) {
160		TRACE_ALWAYS("report of %lu bits too small, expected %" B_PRIu32 " bits\n",
161			length * 8, fReportSize);
162		fReportStatus = B_ERROR;
163	}
164
165#ifndef USERLAND_HID
166	fConditionVariable.NotifyAll();
167#endif
168}
169
170
171#ifndef USERLAND_HID
172status_t
173HIDReport::SendReport()
174{
175	size_t reportSize = ReportSize();
176	uint8 *report = (uint8 *)malloc(reportSize);
177	if (report == NULL)
178		return B_NO_MEMORY;
179
180	fCurrentReport = report;
181	memset(fCurrentReport, 0, reportSize);
182
183	for (uint32 i = 0; i < fItemsUsed; i++) {
184		HIDReportItem *item = fItems[i];
185		if (item == NULL)
186			continue;
187
188		item->Insert();
189	}
190
191	status_t result = fParser->Device()->SendReport(this);
192
193	fCurrentReport = NULL;
194	free(report);
195	return result;
196}
197#endif // !USERLAND_HID
198
199
200HIDReportItem *
201HIDReport::ItemAt(uint32 index)
202{
203	if (index >= fItemsUsed)
204		return NULL;
205	return fItems[index];
206}
207
208
209HIDReportItem *
210HIDReport::FindItem(uint16 usagePage, uint16 usageID)
211{
212	for (uint32 i = 0; i < fItemsUsed; i++) {
213		if (fItems[i]->UsagePage() == usagePage
214			&& fItems[i]->UsageID() == usageID)
215			return fItems[i];
216	}
217
218	return NULL;
219}
220
221
222#ifndef USERLAND_HID
223status_t
224HIDReport::WaitForReport(bigtime_t timeout)
225{
226	while (atomic_get(&fBusyCount) != 0)
227		snooze(1000);
228
229	ConditionVariableEntry conditionVariableEntry;
230	fConditionVariable.Add(&conditionVariableEntry);
231	status_t result = fParser->Device()->MaybeScheduleTransfer();
232	if (result != B_OK) {
233		TRACE_ALWAYS("scheduling transfer failed\n");
234		conditionVariableEntry.Wait(B_RELATIVE_TIMEOUT, 0);
235		return result;
236	}
237
238	result = conditionVariableEntry.Wait(B_RELATIVE_TIMEOUT, timeout);
239	TRACE("waiting for report returned with result: %s\n", strerror(result));
240	if (result != B_OK)
241		return result;
242
243	if (fReportStatus != B_OK)
244		return fReportStatus;
245
246	atomic_add(&fBusyCount, 1);
247	return B_OK;
248}
249
250
251void
252HIDReport::DoneProcessing()
253{
254	atomic_add(&fBusyCount, -1);
255}
256#endif // !USERLAND_HID
257
258
259void
260HIDReport::PrintToStream()
261{
262	TRACE_ALWAYS("HIDReport %p\n", this);
263
264	const char *typeName = "unknown";
265	switch (fType) {
266		case HID_REPORT_TYPE_INPUT:
267			typeName = "input";
268			break;
269		case HID_REPORT_TYPE_OUTPUT:
270			typeName = "output";
271			break;
272		case HID_REPORT_TYPE_FEATURE:
273			typeName = "feature";
274			break;
275	}
276
277	TRACE_ALWAYS("\ttype: %u %s\n", fType, typeName);
278	TRACE_ALWAYS("\treport id: %u\n", fReportID);
279	TRACE_ALWAYS("\treport size: %" B_PRIu32 " bits = %" B_PRIu32 " bytes\n",
280		fReportSize, (fReportSize + 7) / 8);
281
282	TRACE_ALWAYS("\titem count: %" B_PRIu32 "\n", fItemsUsed);
283	for (uint32 i = 0; i < fItemsUsed; i++) {
284		HIDReportItem *item = fItems[i];
285		if (item != NULL)
286			item->PrintToStream(1);
287	}
288}
289
290
291void
292HIDReport::_SignExtend(uint32 &minimum, uint32 &maximum)
293{
294	uint32 mask = 0x80000000;
295	for (uint8 i = 0; i < 4; i++) {
296		if (minimum & mask) {
297			minimum |= mask;
298			if (maximum & mask)
299				maximum |= mask;
300			return;
301		}
302
303		mask >>= 8;
304		mask |= 0xff000000;
305	}
306}
307