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