The Pedigree Project 0.1
HidReport.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "modules/drivers/common/hid/HidReport.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/utilities/utility.h"
23
24#include "modules/drivers/common/hid/HidUsages.h"
25#include "modules/drivers/common/hid/HidUtils.h"
26
27// Handy macro for mixing tag and type in a single value
28#define MIX_TYPE_N_TAG(type, tag) (type | (tag << 2))
29
30// Log macro that also outputs a number of tabs before the text
31#define TABBED_LOG(tabs, text) \
32 do { \
33 char* sTabs = new char[(tabs * 4) + 1]; \
34 ByteSet(sTabs, ' ', tabs * 4); \
35 sTabs[tabs * 4] = '\0'; \
36 DEBUG_LOG(sTabs << text); \
37 delete[] sTabs; \
38 } while (0)
39
40#define ITEM_LOG(tabs, type, value) TABBED_LOG(tabs, type << " (" << value << ")" << Hex)
41#define ITEM_LOG_DEC(tabs, type, value) TABBED_LOG(tabs, Dec << type << " (" << value << ")" << Hex)
42
43HidReport::HidReport()
44 : m_pRootCollection(nullptr),
45 m_ReportBits{},
46 m_OldReports{},
47 m_InputStates{},
48 m_HasReportIds(false),
49 m_Valid(false) {}
50
51HidReport::~HidReport() {
52 delete m_pRootCollection;
53 for (auto* report : m_OldReports)
54 delete[] report;
55 for (auto* input : m_InputStates) {
56 delete input;
57 }
58}
59HidReport::Collection::~Collection() {
60 for (auto* child : childs) {
61 if (child->type == CollectionChild)
62 delete child->pCollection;
63 else
64 delete child->pInputBlock;
65 delete child;
66 }
67}
68
69void HidReport::parseDescriptor(uint8_t* pDescriptor, size_t nDescriptorLength) {
70 if (!pDescriptor || !nDescriptorLength || m_pRootCollection)
71 return;
73 m_pRootCollection->pParent = nullptr;
74 // This will store all the values that change during the parsing
75 LocalState currentState;
76 LocalState globalStack[16];
77 size_t globalDepth = 0;
78
79 // Whether PhysMin and PhysMax form a pair
80 bool bPhysPair = false;
81 // Whether LogMin and LogMax form a pair
82 bool bLogPair = false;
83
84 // Pointer to the collection under which we are parsing
85 Collection* pCurrentCollection = m_pRootCollection;
86
87 // The depth of the Collection tree
88 size_t nDepth = 0;
89
90 // Parse every item in the descriptor
91 for (size_t i = 0; i < nDescriptorLength; i++) {
92 // A union and structure used to unpack the item's data
93 union {
94 struct {
95 uint8_t size : 2;
96 uint8_t type : 2;
97 uint8_t tag : 4;
98 } PACKED;
99 uint8_t raw;
100 } item;
101
102 item.raw = pDescriptor[i];
103 if (item.raw == 0xfe) {
104 if (nDescriptorLength - i < 3 || pDescriptor[i + 1] > nDescriptorLength - i - 3)
105 return;
106 i += 2 + pDescriptor[i + 1];
107 continue;
108 }
109 uint8_t size = item.size == 3 ? 4 : item.size;
110 if (size > nDescriptorLength - i - 1)
111 return;
112
113 // Get the value
114 uint32_t value = 0;
115 if (size == 1)
116 value = pDescriptor[i + 1];
117 else if (size == 2)
118 value = pDescriptor[i + 1] | (pDescriptor[i + 2] << 8);
119 else if (size == 4)
120 value = pDescriptor[i + 1] | (pDescriptor[i + 2] << 8) | (pDescriptor[i + 3] << 16) |
121 (pDescriptor[i + 4] << 24);
122
123 // Update the byte counter (we may hit a continue)
124 i += size;
125
126 // Don't allow for Main items (Input, Output, Feature, etc.) outside a
127 // collection
128 if (!pCurrentCollection)
129 if (item.type == MainItem && item.tag != CollectionItem)
130 continue;
131
132 // Check for type and tag to find which item do we have
133 switch (MIX_TYPE_N_TAG(item.type, item.tag)) {
134 // Main items
135 case MIX_TYPE_N_TAG(MainItem, InputItem): {
136 if (currentState.nReportSize <= 0 || currentState.nReportSize > 64 ||
137 currentState.nReportCount <= 0 || currentState.nReportCount > 1024)
138 return;
139 const uint8_t reportId = currentState.nReportID == -1 ? 0 : currentState.nReportID;
140 const size_t bits = currentState.nReportSize * currentState.nReportCount;
141 if (bits > 8192 - m_ReportBits[reportId])
142 return;
143 m_ReportBits[reportId] += bits;
144
145 // Create a new InputBlock and set the state and type
146 InputBlock* pBlock = new InputBlock();
147 pBlock->state = currentState;
148 if (value & InputConstant) {
149 pBlock->type = InputBlock::Constant;
150 ITEM_LOG(nDepth, "Input", "Constant");
151 } else {
152 if (value & InputVariable) {
153 if (value & InputRelative) {
154 pBlock->type = InputBlock::Relative;
155 ITEM_LOG(nDepth, "Input", "Data, Variable, Relative");
156 } else {
157 pBlock->type = InputBlock::Absolute;
158 ITEM_LOG(nDepth, "Input", "Data, Variable, Absolute");
159 }
160 } else {
161 pBlock->type = InputBlock::Array;
162 ITEM_LOG(nDepth, "Input", "Data, Array");
163 }
164 }
165
166 // Push it into the child vector
167 pCurrentCollection->childs.pushBack(new Collection::Child(pBlock));
168 break;
169 }
170 case MIX_TYPE_N_TAG(MainItem, CollectionItem): {
171 if (nDepth == 16)
172 return;
173 // Create a new Collection and set the state
174 Collection* pCollection = new Collection();
175 pCollection->pParent = pCurrentCollection;
176 pCollection->state = currentState;
177
178 // Push it into the child vector
179 if (pCurrentCollection)
180 pCurrentCollection->childs.pushBack(new Collection::Child(pCollection));
181
182 // We now entered the collection
183 pCurrentCollection = pCollection;
184 ITEM_LOG(nDepth, "Collection", value);
185 nDepth++;
186 break;
187 }
188 case MIX_TYPE_N_TAG(MainItem, EndCollectionItem):
189 // Move up to the parent
190 if (pCurrentCollection == m_pRootCollection || !nDepth)
191 return;
192 pCurrentCollection = pCurrentCollection->pParent;
193 nDepth--;
194 TABBED_LOG(nDepth, "End Collection");
195 break;
196
197 // Global items (set various global variables)
198 case MIX_TYPE_N_TAG(GlobalItem, UsagePageItem):
199 currentState.nUsagePage = value;
200 ITEM_LOG_DEC(nDepth, "Usage Page", value);
201 break;
202 case MIX_TYPE_N_TAG(GlobalItem, LogMinItem):
203 currentState.nLogMin = value;
204
205 if (currentState.nLogMax != ~0 && !bLogPair) {
206 HidUtils::fixNegativeMinimum(currentState.nLogMin, currentState.nLogMax);
207 bLogPair = true;
208 ITEM_LOG(nDepth, "Logical Minimum", currentState.nLogMin);
209 ITEM_LOG(nDepth, "Logical Maximum", currentState.nLogMax);
210 } else
211 bLogPair = false;
212 break;
213 case MIX_TYPE_N_TAG(GlobalItem, LogMaxItem):
214 currentState.nLogMax = value;
215
216 if (currentState.nLogMin != ~0 && !bLogPair) {
217 HidUtils::fixNegativeMinimum(currentState.nLogMin, currentState.nLogMax);
218 bLogPair = true;
219 ITEM_LOG(nDepth, "Logical Minimum", currentState.nLogMin);
220 ITEM_LOG(nDepth, "Logical Maximum", currentState.nLogMax);
221 } else
222 bLogPair = false;
223 break;
224 case MIX_TYPE_N_TAG(GlobalItem, PhysMinItem):
225 currentState.nPhysMin = value;
226
227 if (currentState.nPhysMax != ~0 && !bPhysPair) {
228 HidUtils::fixNegativeMinimum(currentState.nPhysMin, currentState.nPhysMax);
229 bPhysPair = true;
230 ITEM_LOG_DEC(nDepth, "Physical Minimum", currentState.nPhysMin);
231 ITEM_LOG_DEC(nDepth, "Physical Maximum", currentState.nPhysMax);
232 } else
233 bPhysPair = false;
234 break;
235 case MIX_TYPE_N_TAG(GlobalItem, PhysMaxItem):
236 currentState.nPhysMax = value;
237
238 if (currentState.nPhysMin != ~0 && !bPhysPair) {
239 HidUtils::fixNegativeMinimum(currentState.nPhysMin, currentState.nPhysMax);
240 bPhysPair = true;
241 ITEM_LOG_DEC(nDepth, "Physical Minimum", currentState.nPhysMin);
242 ITEM_LOG_DEC(nDepth, "Physical Maximum", currentState.nPhysMax);
243 } else
244 bPhysPair = false;
245 break;
246 case MIX_TYPE_N_TAG(GlobalItem, ReportSizeItem):
247 currentState.nReportSize = value;
248 ITEM_LOG_DEC(nDepth, "Report Size", value);
249 break;
250 case MIX_TYPE_N_TAG(GlobalItem, ReportIDItem):
251 if (!value || value > 255)
252 return;
253 currentState.nReportID = value;
254 m_HasReportIds = true;
255 ITEM_LOG_DEC(nDepth, "Report ID", value);
256 break;
257 case MIX_TYPE_N_TAG(GlobalItem, ReportCountItem):
258 currentState.nReportCount = value;
259 ITEM_LOG_DEC(nDepth, "Report Count", value);
260 break;
261
262 case MIX_TYPE_N_TAG(GlobalItem, PushItem):
263 if (globalDepth == 16)
264 return;
265 globalStack[globalDepth++].copyGlobals(currentState);
266 break;
267 case MIX_TYPE_N_TAG(GlobalItem, PopItem):
268 if (!globalDepth)
269 return;
270 currentState.copyGlobals(globalStack[--globalDepth]);
271 break;
272
273 // Local items (set various local variables, mostly usage-related)
274 case MIX_TYPE_N_TAG(LocalItem, UsageItem):
275 if (!currentState.pUsages)
276 currentState.pUsages = new Vector<size_t>();
277 currentState.pUsages->pushBack(value);
278 ITEM_LOG_DEC(nDepth, "Usage", value);
279 break;
280 case MIX_TYPE_N_TAG(LocalItem, UsageMinItem):
281 currentState.nUsageMin = value;
282 ITEM_LOG_DEC(nDepth, "Usage Minimum", value);
283 break;
284 case MIX_TYPE_N_TAG(LocalItem, UsageMaxItem):
285 currentState.nUsageMax = value;
286 ITEM_LOG_DEC(nDepth, "Usage Maximum", value);
287 break;
288 default:
289 ITEM_LOG_DEC(nDepth, "Unknown", item.type << " " << item.tag << " " << Hex << value);
290 }
291
292 // Reset the local values in currentState (will also delete the usage
293 // vector if it's not used)
294 if (item.type == MainItem)
295 currentState.resetLocalValues();
296 }
297 if (nDepth || globalDepth || pCurrentCollection != m_pRootCollection ||
298 (m_HasReportIds && m_ReportBits[0]))
299 return;
300 for (size_t id = 0; id < 256; ++id) {
301 if (m_ReportBits[id]) {
302 m_OldReports[id] = new uint8_t[(m_ReportBits[id] + 7) / 8]();
303 m_InputStates[id] = new HidUtils::InputState;
304 m_Valid = true;
305 }
306 }
307}
308
309void HidReport::feedInput(uint8_t* pBuffer, uint8_t*, size_t nBufferSize) {
310 if (!m_Valid || !pBuffer || !nBufferSize)
311 return;
312 const uint8_t reportId = m_HasReportIds ? *pBuffer++ : 0;
313 if (m_HasReportIds)
314 --nBufferSize;
315 const size_t reportBytes = (m_ReportBits[reportId] + 7) / 8;
316 if (!reportBytes || nBufferSize < reportBytes)
317 return;
318 size_t bitOffset = 0;
319 m_pRootCollection->feedInput(pBuffer, m_OldReports[reportId], reportBytes, bitOffset, reportId,
320 *m_InputStates[reportId]);
321 MemoryCopy(m_OldReports[reportId], pBuffer, reportBytes);
322}
323
324void HidReport::Collection::feedInput(uint8_t* pBuffer, uint8_t* pOldBuffer, size_t nBufferSize,
325 size_t& nBitOffset, uint8_t reportId,
326 HidUtils::InputState& inputState) {
327 // Send input to each child
328 for (size_t i = 0; i < childs.count(); i++) {
329 Child* pChild = childs[i];
330
331 // If it's a collection, just forward the arguments
332 if (pChild->type == CollectionChild) {
333 pChild->pCollection->feedInput(pBuffer, pOldBuffer, nBufferSize, nBitOffset, reportId,
334 inputState);
335 }
336
337 // If it's an input block, we need to send also a guessed device type
338 if (pChild->type == InputBlockChild) {
339 pChild->pInputBlock->feedInput(pBuffer, pOldBuffer, nBufferSize, nBitOffset,
340 guessInputDevice(), reportId, inputState);
341 }
342 }
343}
344
346 // Go all the way up looking for valid usages
347 Collection* pCollection = this;
348 while (pCollection) {
349 // Check for Mouse, Joystick, Keyboard and Keypad usages
350 if (pCollection->state.nUsagePage == HidUsagePages::GenericDesktop) {
351 if (pCollection->state.getUsageByIndex(0) == HidUsages::Mouse)
352 return Mouse;
353 if (pCollection->state.getUsageByIndex(0) == HidUsages::Joystick)
354 return Joystick;
355 if (pCollection->state.getUsageByIndex(0) == HidUsages::Keyboard)
356 return Keyboard;
357 if (pCollection->state.getUsageByIndex(0) == HidUsages::Keypad)
358 return Keyboard;
359 }
360
361 // Go up
362 pCollection = pCollection->pParent;
363 }
364
365 // We found nothing
366 return UnknownDevice;
367}
368
369void HidReport::InputBlock::feedInput(uint8_t* pBuffer, uint8_t* pOldBuffer, size_t nBufferSize,
370 size_t& nBitOffset, HidDeviceType deviceType,
371 uint8_t reportId, HidUtils::InputState& inputState) {
372 const uint8_t id = state.nReportID == -1 ? 0 : state.nReportID;
373 if (id != reportId)
374 return;
375
376 // Compute the size of this block
377 int64_t nBlockSize = state.nReportCount * state.nReportSize;
378
379 // We don't want to cross the end of the buffer and we can skip constant
380 // inputs
381 if ((nBitOffset + nBlockSize > nBufferSize * 8) || type == Constant) {
382 nBitOffset += nBlockSize;
383 return;
384 }
385
386 // Process each field
387 for (int64_t i = 0; i < state.nReportCount; i++) {
388 uint64_t nValue =
389 HidUtils::getBufferField(pBuffer, nBitOffset + i * state.nReportSize, state.nReportSize);
390 int64_t nRelativeValue = 0;
391 switch (type) {
392 case Absolute:
393 // Here we have to take the current absolute value and
394 // subtract it by the old value to get a relative value
395 nRelativeValue =
396 nValue - HidUtils::getBufferField(pOldBuffer, nBitOffset + i * state.nReportSize,
397 state.nReportSize);
398
399 if (nRelativeValue) {
400 HidUtils::sendInputToManager(deviceType, state.nUsagePage, state.getUsageByIndex(i),
401 nRelativeValue, inputState);
402 }
403 break;
404 case Relative:
405 // The actual value is relative
406 nRelativeValue = nValue;
407 if (state.nLogMin < 0 && state.nReportSize < 64 &&
408 (nValue & (uint64_t{1} << (state.nReportSize - 1))))
409 nRelativeValue = static_cast<int64_t>(nValue | (~uint64_t{0} << state.nReportSize));
410
411 if (nRelativeValue) {
412 HidUtils::sendInputToManager(deviceType, state.nUsagePage, state.getUsageByIndex(i),
413 nRelativeValue, inputState);
414 }
415 break;
416 case Array:
417 // A non-zero value in an array means a holded key/button
418 if (nValue) {
419 // Check if this array entry is new
420 bool bNew = true;
421 for (int64_t j = 0; j < state.nReportCount; j++) {
422 if (HidUtils::getBufferField(pOldBuffer, nBitOffset + j * state.nReportSize,
423 state.nReportSize) == nValue) {
424 bNew = false;
425 break;
426 }
427 }
428
429 // If it's new, we have a keyDown/buttonDown
430 if (bNew) {
431 HidUtils::sendInputToManager(deviceType, state.nUsagePage, nValue, 1, inputState);
432 }
433 }
434 break;
435 // This is to please GCC
436 case Constant:
437 break;
438 }
439 }
440
441 // Special case here: check for array entries that disapeared
442 // (keys/buttons that were released since last time)
443 if (type == Array) {
444 for (int64_t i = 0; i < state.nReportCount; i++) {
445 uint64_t nOldValue = HidUtils::getBufferField(pOldBuffer, nBitOffset + i * state.nReportSize,
446 state.nReportSize);
447 if (nOldValue) {
448 // Check if this array entry disapeared
449 bool bDisapeared = true;
450 for (int64_t j = 0; j < state.nReportCount; j++) {
451 if (HidUtils::getBufferField(pBuffer, nBitOffset + j * state.nReportSize,
452 state.nReportSize) == nOldValue) {
453 bDisapeared = false;
454 break;
455 }
456 }
457
458 // If it disapeared, we have a keyUp/buttonUp
459 if (bDisapeared) {
460 HidUtils::sendInputToManager(deviceType, state.nUsagePage, nOldValue, -1, inputState);
461 }
462 }
463 }
464 }
465
466 // Update the bit offset
467 nBitOffset += nBlockSize;
468}
469
471 : nUsagePage(~0),
472 nLogMin(~0),
473 nLogMax(~0),
474 nPhysMin(~0),
475 nPhysMax(~0),
476 nReportSize(~0),
477 nReportID(~0),
478 nReportCount(~0),
479 pUsages(0),
480 nUsageMin(~0),
481 nUsageMax(~0) {}
482
483HidReport::LocalState::~LocalState() {
484 // The parser is required to set pUsages to zero when it's actually
485 // used
486 if (pUsages)
487 delete pUsages;
488}
489
492 // The parser is required to set pUsages to zero when it's actually
493 // used
494 if (pUsages) {
495 delete pUsages;
496 pUsages = 0;
497 }
498 nUsageMin = ~0;
499 nUsageMax = ~0;
500}
501
503uint16_t HidReport::LocalState::getUsageByIndex(uint16_t nUsageIndex) {
504 // If there's an usage vector, return the usage value in it or zero
505 // if the index is not valid
506 if (pUsages)
507 return nUsageIndex < pUsages->count() ? (*pUsages)[nUsageIndex] : 0;
508
509 // The usage value if in range, 0 otherwise
510 return (nUsageMin + nUsageIndex) <= nUsageMax ? nUsageMin + nUsageIndex : 0;
511}
512
514// This assignment transfers the usage vector from the source state.
516 copyGlobals(s);
517 pUsages = s.pUsages;
518 nUsageMin = s.nUsageMin;
519 nUsageMax = s.nUsageMax;
520 s.pUsages = nullptr;
521 return *this;
522}
523void HidReport::LocalState::copyGlobals(const LocalState& s) {
524 nUsagePage = s.nUsagePage;
525 nLogMin = s.nLogMin;
526 nLogMax = s.nLogMax;
527 nPhysMin = s.nPhysMin;
528 nPhysMax = s.nPhysMax;
529 nReportSize = s.nReportSize;
530 nReportID = s.nReportID;
531 nReportCount = s.nReportCount;
532}
void parseDescriptor(uint8_t *pDescriptor, size_t nDescriptorLength)
Parses a HID report descriptor and stores the resulted data.
Definition HidReport.cc:69
void feedInput(uint8_t *pBuffer, uint8_t *pOldBuffer, size_t nBufferSize)
Feeds the input interpreter with a new input buffer.
Definition HidReport.cc:309
Collection * m_pRootCollection
The root collection, under which everything is.
Definition HidReport.h:161
Held input belongs to a report instance and is retired with that instance.
Definition HidUtils.h:32
A vector / dynamic array.
Definition Vector.h:33
@ Hex
Definition Log.h:124
void pushBack(const T &value)
Definition Vector.h:275
void fixNegativeMinimum(int64_t &nMin, int64_t nMax)
Converts.
Definition HidUtils.cc:74
uint64_t getBufferField(uint8_t *pBuffer, size_t nStart, size_t nLength)
Retrieves a field in a buffer.
Definition HidUtils.cc:65
void sendInputToManager(HidDeviceType deviceType, uint16_t nUsagePage, uint16_t nUsage, int64_t nRelativeValue, InputState &state)
Sends the input to the right handler in HidInputManager or InputManager.
Definition HidUtils.cc:108
Structure representing a Collection whitin a report.
Definition HidReport.h:126
LocalState state
The local state of the collection.
Definition HidReport.h:157
Collection * pParent
Our parent.
Definition HidReport.h:154
Vector< Child * > childs
All the childs in this collection.
Definition HidReport.h:151
void feedInput(uint8_t *pBuffer, uint8_t *pOldBuffer, size_t nBufferSize, size_t &nBitOffset, uint8_t reportId, HidUtils::InputState &inputState)
Definition HidReport.cc:324
HidDeviceType guessInputDevice()
Definition HidReport.cc:345
Structure representing an Input block whitin a Collection.
Definition HidReport.h:113
void feedInput(uint8_t *pBuffer, uint8_t *pOldBuffer, size_t nBufferSize, size_t &nBitOffset, HidDeviceType deviceType, uint8_t reportId, HidUtils::InputState &inputState)
Feeds input to this block.
Definition HidReport.cc:369
LocalState state
The local state of the input block.
Definition HidReport.h:122
enum HidReport::InputBlock::@23 type
The type of the input block.
Structure holding various global and local values for a Main item.
Definition HidReport.h:81
void resetLocalValues()
Resets the local values (called every time a Main item occurs)
Definition HidReport.cc:491
LocalState()
Constructor, sets all values to ~0 (invalid)
Definition HidReport.cc:470
uint16_t getUsageByIndex(uint16_t nUsageIndex)
Returns the usage number represented by the given index.
Definition HidReport.cc:503
LocalState & operator=(LocalState &s)
Copy constructor.
Definition HidReport.cc:515