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