xref: /haiku/src/add-ons/kernel/drivers/input/hid_shared/HIDReport.cpp (revision 1aa97652e09a9df14ca7f63b32c29514282aaa69)
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 		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 
40 HIDReport::~HIDReport()
41 {
42 	free(fItems);
43 }
44 
45 
46 void
47 HIDReport::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 > physicalMaximum)
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: %" B_PRId32 "\n", logicalMinimum);
77 	TRACE("\t\tlogical_maximum: %" B_PRId32 "\n", logicalMaximum);
78 	TRACE("\t\tphysical_minimum: %" B_PRId32 "\n", physicalMinimum);
79 	TRACE("\t\tphysical_maximum: %" B_PRId32 "\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: %" B_PRIu32 "\n", globalState.report_size);
83 	TRACE("\t\treport_count: %" B_PRIu32 "\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 (%" B_PRIu32 ")\n", localState.usage_stack_used);
88 	for (uint32 i = 0; i < localState.usage_stack_used; i++) {
89 		TRACE("\t\t\t0x%08" B_PRIx32 "\n",
90 			localState.usage_stack[i].u.extended);
91 	}
92 
93 	TRACE("\t\tusage_minimum: 0x%08" B_PRIx32 "\n",
94 		localState.usage_minimum.u.extended);
95 	TRACE("\t\tusage_maximum: 0x%08" B_PRIu32 "\n",
96 		localState.usage_maximum.u.extended);
97 	TRACE("\t\tdesignator_index: %" B_PRIu32 "\n",
98 		localState.designator_index);
99 	TRACE("\t\tdesignator_minimum: %" B_PRIu32 "\n",
100 		localState.designator_minimum);
101 	TRACE("\t\tdesignator_maximum: %" B_PRIu32 "\n",
102 		localState.designator_maximum);
103 	TRACE("\t\tstring_index: %u\n", localState.string_index);
104 	TRACE("\t\tstring_minimum: %u\n", localState.string_minimum);
105 	TRACE("\t\tstring_maximum: %u\n", localState.string_maximum);
106 
107 	uint32 usageMinimum = 0, usageMaximum = 0;
108 	if (mainData.array_variable == 0) {
109 		usageMinimum = localState.usage_minimum.u.extended;
110 		usageMaximum = localState.usage_maximum.u.extended;
111 	}
112 
113 	uint32 usageRangeIndex = 0;
114 	for (uint32 i = 0; i < globalState.report_count; i++) {
115 		if (fItemsUsed >= fItemsAllocated) {
116 			fItemsAllocated += 10;
117 			HIDReportItem **newItems = (HIDReportItem **)realloc(fItems,
118 				sizeof(HIDReportItem *) * fItemsAllocated);
119 			if (newItems == NULL) {
120 				TRACE_ALWAYS("no memory when growing report item list\n");
121 				fItemsAllocated -= 10;
122 				return;
123 			}
124 
125 			fItems = newItems;
126 		}
127 
128 		if (mainData.array_variable == 1) {
129 			usage_value usage;
130 			if (i < localState.usage_stack_used)
131 				usage = localState.usage_stack[i];
132 			else {
133 				usage = localState.usage_minimum;
134 				usage.u.extended += usageRangeIndex++;
135 				if (usage.u.extended > localState.usage_maximum.u.extended)
136 					usage.u.extended = localState.usage_maximum.u.extended;
137 			}
138 
139 			usageMinimum = usageMaximum = usage.u.extended;
140 		}
141 
142 		fItems[fItemsUsed] = new(std::nothrow) HIDReportItem(this,
143 			fReportSize, globalState.report_size, mainData.data_constant == 0,
144 			mainData.array_variable == 0, mainData.relative != 0,
145 			logicalMinimum, logicalMaximum, usageMinimum, usageMaximum);
146 		if (fItems[fItemsUsed] == NULL)
147 			TRACE_ALWAYS("no memory when creating report item\n");
148 
149 		if (collection != NULL)
150 			collection->AddItem(fItems[fItemsUsed]);
151 		else
152 			TRACE_ALWAYS("main item not part of a collection\n");
153 
154 		fReportSize += globalState.report_size;
155 		fItemsUsed++;
156 	}
157 }
158 
159 
160 void
161 HIDReport::SetReport(status_t status, uint8 *report, size_t length)
162 {
163 	fReportStatus = status;
164 	fCurrentReport = report;
165 	if (status == B_OK && length * 8 < fReportSize) {
166 		TRACE_ALWAYS("report of %lu bits too small, expected %" B_PRIu32
167 			" bits\n", length * 8, fReportSize);
168 		fReportStatus = B_ERROR;
169 	}
170 
171 #ifndef USERLAND_HID
172 	fConditionVariable.NotifyAll();
173 #endif
174 }
175 
176 
177 #ifndef USERLAND_HID
178 status_t
179 HIDReport::SendReport()
180 {
181 	size_t reportSize = ReportSize();
182 	uint8 *report = (uint8 *)malloc(reportSize);
183 	if (report == NULL)
184 		return B_NO_MEMORY;
185 
186 	fCurrentReport = report;
187 	memset(fCurrentReport, 0, reportSize);
188 
189 	for (uint32 i = 0; i < fItemsUsed; i++) {
190 		HIDReportItem *item = fItems[i];
191 		if (item == NULL)
192 			continue;
193 
194 		item->Insert();
195 	}
196 
197 	status_t result = fParser->Device()->SendReport(this);
198 
199 	fCurrentReport = NULL;
200 	free(report);
201 	return result;
202 }
203 #endif // !USERLAND_HID
204 
205 
206 HIDReportItem *
207 HIDReport::ItemAt(uint32 index)
208 {
209 	if (index >= fItemsUsed)
210 		return NULL;
211 	return fItems[index];
212 }
213 
214 
215 HIDReportItem *
216 HIDReport::FindItem(uint16 usagePage, uint16 usageID)
217 {
218 	for (uint32 i = 0; i < fItemsUsed; i++) {
219 		if (fItems[i]->UsagePage() == usagePage
220 			&& fItems[i]->UsageID() == usageID)
221 			return fItems[i];
222 	}
223 
224 	return NULL;
225 }
226 
227 
228 #ifndef USERLAND_HID
229 status_t
230 HIDReport::WaitForReport(bigtime_t timeout)
231 {
232 	while (atomic_get(&fBusyCount) != 0)
233 		snooze(1000);
234 
235 	ConditionVariableEntry conditionVariableEntry;
236 	fConditionVariable.Add(&conditionVariableEntry);
237 	status_t result = fParser->Device()->MaybeScheduleTransfer(this);
238 	if (result != B_OK) {
239 		TRACE_ALWAYS("scheduling transfer failed\n");
240 		conditionVariableEntry.Wait(B_RELATIVE_TIMEOUT, 0);
241 		return result;
242 	}
243 
244 	result = conditionVariableEntry.Wait(B_RELATIVE_TIMEOUT, timeout);
245 	TRACE("waiting for report returned with result: %s\n", strerror(result));
246 	if (result != B_OK)
247 		return result;
248 
249 	if (fReportStatus != B_OK)
250 		return fReportStatus;
251 
252 	atomic_add(&fBusyCount, 1);
253 	return B_OK;
254 }
255 
256 
257 void
258 HIDReport::DoneProcessing()
259 {
260 	atomic_add(&fBusyCount, -1);
261 }
262 #endif // !USERLAND_HID
263 
264 
265 void
266 HIDReport::PrintToStream()
267 {
268 	TRACE_ALWAYS("HIDReport %p\n", this);
269 
270 	const char *typeName = "unknown";
271 	switch (fType) {
272 		case HID_REPORT_TYPE_INPUT:
273 			typeName = "input";
274 			break;
275 		case HID_REPORT_TYPE_OUTPUT:
276 			typeName = "output";
277 			break;
278 		case HID_REPORT_TYPE_FEATURE:
279 			typeName = "feature";
280 			break;
281 	}
282 
283 	TRACE_ALWAYS("\ttype: %u %s\n", fType, typeName);
284 	TRACE_ALWAYS("\treport id: %u\n", fReportID);
285 	TRACE_ALWAYS("\treport size: %" B_PRIu32 " bits = %" B_PRIu32 " bytes\n",
286 		fReportSize, (fReportSize + 7) / 8);
287 
288 	TRACE_ALWAYS("\titem count: %" B_PRIu32 "\n", fItemsUsed);
289 	for (uint32 i = 0; i < fItemsUsed; i++) {
290 		HIDReportItem *item = fItems[i];
291 		if (item != NULL)
292 			item->PrintToStream(1);
293 	}
294 }
295 
296 
297 void
298 HIDReport::_SignExtend(uint32 &minimum, uint32 &maximum)
299 {
300 	uint32 mask = 0x80000000;
301 	for (uint8 i = 0; i < 4; i++) {
302 		if (minimum & mask) {
303 			minimum |= mask;
304 			if (maximum & mask)
305 				maximum |= mask;
306 			return;
307 		}
308 
309 		mask >>= 8;
310 		mask |= 0xff000000;
311 	}
312 }
313