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 "HIDReportItem.h"
13#include "HIDReport.h"
14
15#include <string.h>
16
17HIDReportItem::HIDReportItem(HIDReport *report, uint32 bitOffset,
18	uint8 bitLength, bool hasData, bool isArray, bool isRelative,
19	uint32 minimum, uint32 maximum, uint32 usageMinimum, uint32 usageMaximum)
20	:	fReport(report),
21		fByteOffset(bitOffset / 8),
22		fShift(bitOffset % 8),
23		fMask(~(0xffffffff << bitLength)),
24		fBitCount(bitLength),
25		fByteCount((fShift + fBitCount + 7) / 8),
26		fHasData(hasData),
27		fArray(isArray),
28		fRelative(isRelative),
29		fMinimum(minimum),
30		fMaximum(maximum),
31		fUsageMinimum(usageMinimum),
32		fUsageMaximum(usageMaximum),
33		fData(0),
34		fValid(false)
35{
36}
37
38
39uint16
40HIDReportItem::UsagePage()
41{
42	usage_value value;
43	value.u.extended = fUsageMinimum;
44	return value.u.s.usage_page;
45}
46
47
48uint16
49HIDReportItem::UsageID()
50{
51	usage_value value;
52	value.u.extended = fUsageMinimum;
53	return value.u.s.usage_id;
54}
55
56
57status_t
58HIDReportItem::Extract()
59{
60	// The specs restrict items to span at most across 4 bytes, which means
61	// that we can always just byte-align, copy four bytes and then shift and
62	// mask as needed.
63	uint8 *report = fReport->CurrentReport();
64	if (report == NULL)
65		return B_NO_INIT;
66
67	memcpy(&fData, report + fByteOffset, fByteCount);
68	fData >>= fShift;
69	fData &= fMask;
70
71	if (Signed()) {
72		// sign extend if needed.
73		if ((fData & ~(fMask >> 1)) != 0)
74			fData |= ~fMask;
75
76		fValid = (int32)fData >= (int32)fMinimum
77			&& (int32)fData <= (int32)fMaximum;
78	} else
79		fValid = fData >= fMinimum && fData <= fMaximum;
80
81	return B_OK;
82}
83
84
85status_t
86HIDReportItem::Insert()
87{
88	uint8 *report = fReport->CurrentReport();
89	if (report == NULL)
90		return B_NO_INIT;
91
92	uint32 value;
93	memcpy(&value, report + fByteOffset, fByteCount);
94	value &= ~(fMask << fShift);
95
96	if (fValid)
97		value |= (fData & fMask) << fShift;
98
99	memcpy(report + fByteOffset, &value, fByteCount);
100	return B_OK;
101}
102
103
104status_t
105HIDReportItem::SetData(uint32 data)
106{
107	fData = data;
108
109	if (Signed()) {
110		fValid = (int32)fData >= (int32)fMinimum
111			&& (int32)fData <= (int32)fMaximum;
112	} else
113		fValid = fData >= fMinimum && fData <= fMaximum;
114
115	return fValid ? B_OK : B_BAD_VALUE;
116}
117
118
119uint32
120HIDReportItem::ScaledData(uint8 scaleToBits, bool toBeSigned)
121{
122	uint32 source;
123	if (Signed() != toBeSigned) {
124		if (toBeSigned)
125			source = (uint32)((int32)fData - (fMaximum + 1) / 2) & fMask;
126		else
127			source = (uint32)((int32)fData - (int32)fMinimum);
128	} else
129		source = fData & fMask;
130
131	if (fBitCount == scaleToBits)
132		return source;
133
134	int8 shift;
135	uint32 result = 0;
136	do {
137		shift = scaleToBits - fBitCount;
138		if (shift > 0) {
139			result |= source << shift;
140			scaleToBits = shift;
141		} else
142			result |= source >> -shift;
143
144	} while (shift > 0);
145
146	return result;
147}
148
149
150uint32
151HIDReportItem::ScaledRangeData(uint32 minimum, uint32 maximum)
152{
153	uint64 zeroBasedData;
154	if (Signed())
155		zeroBasedData = (int32)fData - (int32)fMinimum;
156	else
157		zeroBasedData = fData - fMinimum;
158
159	return zeroBasedData * (maximum - minimum + 1) / (fMaximum - fMinimum + 1)
160		+ minimum;
161}
162
163
164float
165HIDReportItem::ScaledFloatData()
166{
167	if (Signed()) {
168		return (double)((int32)fData - (int32)fMinimum)
169			/ (fMaximum - (int32)fMinimum);
170	}
171
172	return (double)(fData - fMinimum) / (fMaximum - fMinimum);
173}
174
175
176void
177HIDReportItem::PrintToStream(uint32 indentLevel)
178{
179	char indent[indentLevel + 1];
180	memset(indent, '\t', indentLevel);
181	indent[indentLevel] = 0;
182
183	TRACE_ALWAYS("%sHIDReportItem %p\n", indent, this);
184	TRACE_ALWAYS("%s\tbyte offset: %" B_PRIu32 "\n", indent, fByteOffset);
185	TRACE_ALWAYS("%s\tshift: %u\n", indent, fShift);
186	TRACE_ALWAYS("%s\tmask: 0x%08" B_PRIx32 "\n", indent, fMask);
187	TRACE_ALWAYS("%s\thas data: %s\n", indent, fHasData ? "yes" : "no");
188	TRACE_ALWAYS("%s\tarray: %s\n", indent, fArray ? "yes" : "no");
189	TRACE_ALWAYS("%s\trelative: %s\n", indent, fRelative ? "yes" : "no");
190	TRACE_ALWAYS("%s\tminimum: %" B_PRIu32 "\n", indent, fMinimum);
191	TRACE_ALWAYS("%s\tmaximum: %" B_PRIu32 "\n", indent, fMaximum);
192	TRACE_ALWAYS("%s\tusage minimum: 0x%08" B_PRIx32 "\n", indent, fUsageMinimum);
193	TRACE_ALWAYS("%s\tusage maximum: 0x%08" B_PRIx32 "\n", indent, fUsageMaximum);
194}
195