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