xref: /haiku/src/add-ons/kernel/drivers/input/hid_shared/HIDReport.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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 
22 HIDReport::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 
37 HIDReport::~HIDReport()
38 {
39 }
40 
41 
42 void
43 HIDReport::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 
122 			if (i < localState.usage_stack_used)
123 				usage = localState.usage_stack[i].u.extended;
124 		}
125 
126 		HIDReportItem *item = new(std::nothrow) HIDReportItem(this,
127 			fReportSize, globalState.report_size, mainData.data_constant == 0,
128 			mainData.array_variable == 0, mainData.relative != 0,
129 			logicalMinimum, logicalMaximum, usage);
130 		if (item == NULL)
131 			TRACE_ALWAYS("no memory when creating report item\n");
132 
133 		if (collection != NULL)
134 			collection->AddItem(item);
135 		else
136 			TRACE_ALWAYS("main item not part of a collection\n");
137 
138 		if (fItems.PushBack(item) == B_NO_MEMORY) {
139 			TRACE_ALWAYS("no memory when growing report item list\n");
140 		}
141 
142 		fReportSize += globalState.report_size;
143 	}
144 
145 }
146 
147 
148 void
149 HIDReport::SetReport(status_t status, uint8 *report, size_t length)
150 {
151 	fReportStatus = status;
152 	fCurrentReport = report;
153 	if (status == B_OK && length * 8 < fReportSize) {
154 		TRACE_ALWAYS("report of %lu bits too small, expected %" B_PRIu32
155 			" bits\n", length * 8, fReportSize);
156 		fReportStatus = B_ERROR;
157 	}
158 
159 #ifndef USERLAND_HID
160 	fConditionVariable.NotifyAll();
161 #endif
162 }
163 
164 
165 #ifndef USERLAND_HID
166 status_t
167 HIDReport::SendReport()
168 {
169 	size_t reportSize = ReportSize();
170 	uint8 *report = (uint8 *)malloc(reportSize);
171 	if (report == NULL)
172 		return B_NO_MEMORY;
173 
174 	fCurrentReport = report;
175 	memset(fCurrentReport, 0, reportSize);
176 
177 	for (int32 i = 0; i < fItems.Count(); i++) {
178 		HIDReportItem *item = fItems[i];
179 		if (item == NULL)
180 			continue;
181 
182 		item->Insert();
183 	}
184 
185 	status_t result = fParser->Device()->SendReport(this);
186 
187 	fCurrentReport = NULL;
188 	free(report);
189 	return result;
190 }
191 #endif // !USERLAND_HID
192 
193 
194 HIDReportItem *
195 HIDReport::ItemAt(uint32 index)
196 {
197 	if (index >= fItems.Count())
198 		return NULL;
199 	return fItems[index];
200 }
201 
202 
203 HIDReportItem *
204 HIDReport::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 
216 uint32 *
217 HIDReport::Usages()
218 {
219 	if (fUsages.Count() > 0)
220 		return &fUsages[0];
221 
222 	return NULL;
223 }
224 
225 
226 #ifndef USERLAND_HID
227 status_t
228 HIDReport::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 
255 void
256 HIDReport::DoneProcessing()
257 {
258 	atomic_add(&fBusyCount, -1);
259 }
260 #endif // !USERLAND_HID
261 
262 
263 void
264 HIDReport::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 
295 void
296 HIDReport::_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