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