ReportDescriptorParser.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. #region License
  2. /* Copyright 2011, 2013 James F. Bellinger <http://www.zer7.com/software/hidsharp>
  3. Permission to use, copy, modify, and/or distribute this software for any
  4. purpose with or without fee is hereby granted, provided that the above
  5. copyright notice and this permission notice appear in all copies.
  6. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  7. WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  8. MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  9. ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  10. WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  11. ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  12. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
  13. #endregion
  14. using System;
  15. using System.Collections.Generic;
  16. namespace HidSharp.ReportDescriptors.Parser
  17. {
  18. /// <summary>
  19. /// Parses HID report descriptors.
  20. /// </summary>
  21. public class ReportDescriptorParser
  22. {
  23. /// <summary>
  24. /// Initializes a new instance of the <see cref="ReportDescriptorParser"/> class.
  25. /// </summary>
  26. public ReportDescriptorParser()
  27. {
  28. RootCollection = new ReportCollection();
  29. GlobalItemStateStack = new List<IDictionary<GlobalItemTag, EncodedItem>>();
  30. LocalItemState = new List<KeyValuePair<LocalItemTag, uint>>();
  31. Reports = new List<Report>();
  32. Clear();
  33. }
  34. /// <summary>
  35. /// Resets the parser to its initial state.
  36. /// </summary>
  37. public void Clear()
  38. {
  39. CurrentCollection = RootCollection;
  40. RootCollection.Clear();
  41. GlobalItemStateStack.Clear();
  42. GlobalItemStateStack.Add(new Dictionary<GlobalItemTag, EncodedItem>());
  43. LocalItemState.Clear();
  44. Reports.Clear();
  45. ReportsUseID = false;
  46. }
  47. public EncodedItem GetGlobalItem(GlobalItemTag tag)
  48. {
  49. EncodedItem value;
  50. GlobalItemState.TryGetValue(tag, out value);
  51. return value;
  52. }
  53. public uint GetGlobalItemValue(GlobalItemTag tag)
  54. {
  55. EncodedItem item = GetGlobalItem(tag);
  56. return item != null ? item.DataValue : 0;
  57. }
  58. public Report GetReport(ReportType type, byte id)
  59. {
  60. Report report;
  61. if (!TryGetReport(type, id, out report)) { throw new ArgumentException("Report not found."); }
  62. return report;
  63. }
  64. public bool TryGetReport(ReportType type, byte id, out Report report)
  65. {
  66. for (int i = 0; i < Reports.Count; i++)
  67. {
  68. report = Reports[i];
  69. if (report.Type == type && report.ID == id) { return true; }
  70. }
  71. report = null; return false;
  72. }
  73. static int GetMaxLengthOfReports(IEnumerable<Report> reports)
  74. {
  75. int length = 0;
  76. foreach (Report report in reports) { length = Math.Max(length, report.Length); }
  77. return length;
  78. }
  79. public bool IsGlobalItemSet(GlobalItemTag tag)
  80. {
  81. return GlobalItemState.ContainsKey(tag);
  82. }
  83. /// <summary>
  84. /// Parses a raw HID report descriptor.
  85. /// </summary>
  86. /// <param name="buffer">The buffer containing the report descriptor.</param>
  87. public void Parse(byte[] buffer)
  88. {
  89. if (buffer == null) { throw new ArgumentNullException("buffer"); }
  90. Parse(buffer, 0, buffer.Length);
  91. }
  92. /// <summary>
  93. /// Parses a raw HID report descriptor.
  94. /// </summary>
  95. /// <param name="buffer">The buffer containing the report descriptor.</param>
  96. /// <param name="offset">The offset into the buffer to begin parsing from.</param>
  97. /// <param name="count">The number of bytes to parse.</param>
  98. public void Parse(byte[] buffer, int offset, int count)
  99. {
  100. var items = ReportDescriptors.EncodedItem.DecodeRaw(buffer, offset, count);
  101. Parse(items);
  102. }
  103. /// <summary>
  104. /// Parses all of the <see cref="EncodedItem"/> elements in a report descriptor.
  105. /// </summary>
  106. /// <param name="items">The items to parse.</param>
  107. public void Parse(IEnumerable<EncodedItem> items)
  108. {
  109. if (items == null) { throw new ArgumentNullException("items"); }
  110. foreach (EncodedItem item in items) { Parse(item); }
  111. }
  112. /// <summary>
  113. /// Parses a single <see cref="EncodedItem"/>.
  114. /// Call this repeatedly for every item to completely decode a report descriptor.
  115. /// </summary>
  116. /// <param name="item">The item to parse.</param>
  117. public void Parse(EncodedItem item)
  118. {
  119. if (item == null) { throw new ArgumentNullException("item"); }
  120. uint value = item.DataValue;
  121. switch (item.Type)
  122. {
  123. case ItemType.Main:
  124. ParseMain(item.TagForMain, value);
  125. LocalItemState.Clear();
  126. break;
  127. case ItemType.Local:
  128. switch (item.TagForLocal)
  129. {
  130. case LocalItemTag.Usage:
  131. case LocalItemTag.UsageMinimum:
  132. case LocalItemTag.UsageMaximum:
  133. if (value <= 0xffff) { value |= GetGlobalItemValue(GlobalItemTag.UsagePage) << 16; }
  134. break;
  135. }
  136. LocalItemState.Add(new KeyValuePair<LocalItemTag, uint>(item.TagForLocal, value));
  137. break;
  138. case ItemType.Global:
  139. switch (item.TagForGlobal)
  140. {
  141. case GlobalItemTag.Push:
  142. GlobalItemStateStack.Add(new Dictionary<GlobalItemTag, EncodedItem>(GlobalItemState));
  143. break;
  144. case GlobalItemTag.Pop:
  145. GlobalItemStateStack.RemoveAt(GlobalItemState.Count - 1);
  146. break;
  147. default:
  148. switch (item.TagForGlobal)
  149. {
  150. case GlobalItemTag.ReportID:
  151. ReportsUseID = true; break;
  152. }
  153. GlobalItemState[item.TagForGlobal] = item;
  154. break;
  155. }
  156. break;
  157. }
  158. }
  159. void ParseMain(MainItemTag tag, uint value)
  160. {
  161. LocalIndexes indexes = null;
  162. switch (tag)
  163. {
  164. case MainItemTag.Collection:
  165. ReportCollection collection = new ReportCollection();
  166. collection.Parent = CurrentCollection;
  167. collection.Type = (CollectionType)value;
  168. CurrentCollection = collection;
  169. indexes = collection.Indexes; break;
  170. case MainItemTag.EndCollection:
  171. CurrentCollection = CurrentCollection.Parent; break;
  172. case MainItemTag.Input:
  173. case MainItemTag.Output:
  174. case MainItemTag.Feature:
  175. ParseDataMain(tag, value, out indexes); break;
  176. }
  177. if (indexes != null) { ParseMainIndexes(indexes); }
  178. }
  179. static void AddIndex(List<KeyValuePair<int, uint>> list, int action, uint value)
  180. {
  181. list.Add(new KeyValuePair<int, uint>(action, value));
  182. }
  183. static void UpdateIndexMinimum(ref IndexBase index, uint value)
  184. {
  185. if (!(index is IndexRange)) { index = new IndexRange(); }
  186. ((IndexRange)index).Minimum = value;
  187. }
  188. static void UpdateIndexMaximum(ref IndexBase index, uint value)
  189. {
  190. if (!(index is IndexRange)) { index = new IndexRange(); }
  191. ((IndexRange)index).Maximum = value;
  192. }
  193. static void UpdateIndexList(List<uint> values, int delimiter,
  194. ref IndexBase index, uint value)
  195. {
  196. values.Add(value);
  197. UpdateIndexListCommit(values, delimiter, ref index);
  198. }
  199. static void UpdateIndexListCommit(List<uint> values, int delimiter,
  200. ref IndexBase index)
  201. {
  202. if (delimiter != 0 || values.Count == 0) { return; }
  203. if (!(index is IndexList)) { index = new IndexList(); }
  204. ((IndexList)index).Indices.Add(new List<uint>(values));
  205. values.Clear();
  206. }
  207. void ParseMainIndexes(LocalIndexes indexes)
  208. {
  209. int delimiter = 0;
  210. List<uint> designatorValues = new List<uint>(); IndexBase designator = IndexBase.Unset;
  211. List<uint> stringValues = new List<uint>(); IndexBase @string = IndexBase.Unset;
  212. List<uint> usageValues = new List<uint>(); IndexBase usage = IndexBase.Unset;
  213. foreach (KeyValuePair<LocalItemTag, uint> kvp in LocalItemState)
  214. {
  215. switch (kvp.Key)
  216. {
  217. case LocalItemTag.DesignatorMinimum: UpdateIndexMinimum(ref designator, kvp.Value); break;
  218. case LocalItemTag.StringMinimum: UpdateIndexMinimum(ref @string, kvp.Value); break;
  219. case LocalItemTag.UsageMinimum: UpdateIndexMinimum(ref usage, kvp.Value); break;
  220. case LocalItemTag.DesignatorMaximum: UpdateIndexMaximum(ref designator, kvp.Value); break;
  221. case LocalItemTag.StringMaximum: UpdateIndexMaximum(ref @string, kvp.Value); break;
  222. case LocalItemTag.UsageMaximum: UpdateIndexMaximum(ref usage, kvp.Value); break;
  223. case LocalItemTag.DesignatorIndex: UpdateIndexList(designatorValues, delimiter, ref designator, kvp.Value); break;
  224. case LocalItemTag.StringIndex: UpdateIndexList(stringValues, delimiter, ref @string, kvp.Value); break;
  225. case LocalItemTag.Usage: UpdateIndexList(usageValues, delimiter, ref usage, kvp.Value); break;
  226. case LocalItemTag.Delimiter:
  227. if (kvp.Value == 1)
  228. {
  229. if (delimiter++ == 0)
  230. {
  231. designatorValues.Clear();
  232. stringValues.Clear();
  233. usageValues.Clear();
  234. }
  235. }
  236. else if (kvp.Value == 0)
  237. {
  238. delimiter--;
  239. UpdateIndexListCommit(designatorValues, delimiter, ref designator);
  240. UpdateIndexListCommit(stringValues, delimiter, ref @string);
  241. UpdateIndexListCommit(usageValues, delimiter, ref usage);
  242. }
  243. break;
  244. }
  245. }
  246. indexes.Designator = designator;
  247. indexes.String = @string;
  248. indexes.Usage = usage;
  249. }
  250. void ParseDataMain(MainItemTag tag, uint value, out LocalIndexes indexes)
  251. {
  252. ReportSegment segment = new ReportSegment();
  253. segment.Flags = (DataMainItemFlags)value;
  254. segment.Parent = CurrentCollection;
  255. segment.ElementCount = (int)GetGlobalItemValue(GlobalItemTag.ReportCount);
  256. segment.ElementSize = (int)GetGlobalItemValue(GlobalItemTag.ReportSize);
  257. segment.Unit = new Units.Unit(GetGlobalItemValue(GlobalItemTag.Unit));
  258. segment.UnitExponent = Units.Unit.DecodeExponent(GetGlobalItemValue(GlobalItemTag.UnitExponent));
  259. indexes = segment.Indexes;
  260. EncodedItem logicalMinItem = GetGlobalItem(GlobalItemTag.LogicalMinimum);
  261. EncodedItem logicalMaxItem = GetGlobalItem(GlobalItemTag.LogicalMaximum);
  262. segment.LogicalIsSigned =
  263. (logicalMinItem != null && logicalMinItem.DataValueMayBeNegative) ||
  264. (logicalMaxItem != null && logicalMaxItem.DataValueMayBeNegative);
  265. int logicalMinimum = logicalMinItem == null ? 0 : segment.LogicalIsSigned ? logicalMinItem.DataValueSigned : (int)logicalMinItem.DataValue;
  266. int logicalMaximum = logicalMaxItem == null ? 0 : segment.LogicalIsSigned ? logicalMaxItem.DataValueSigned : (int)logicalMaxItem.DataValue;
  267. int physicalMinimum = (int)GetGlobalItemValue(GlobalItemTag.PhysicalMinimum);
  268. int physicalMaximum = (int)GetGlobalItemValue(GlobalItemTag.PhysicalMaximum);
  269. if (!IsGlobalItemSet(GlobalItemTag.PhysicalMinimum) ||
  270. !IsGlobalItemSet(GlobalItemTag.PhysicalMaximum) ||
  271. (physicalMinimum == 0 && physicalMaximum == 0))
  272. {
  273. physicalMinimum = logicalMinimum; physicalMaximum = logicalMaximum;
  274. }
  275. segment.LogicalMinimum = logicalMinimum; segment.LogicalMaximum = logicalMaximum;
  276. segment.PhysicalMinimum = physicalMinimum; segment.PhysicalMaximum = physicalMaximum;
  277. Report report;
  278. ReportType reportType
  279. = tag == MainItemTag.Output ? ReportType.Output
  280. : tag == MainItemTag.Feature ? ReportType.Feature
  281. : ReportType.Input;
  282. uint reportID = GetGlobalItemValue(GlobalItemTag.ReportID);
  283. if (!TryGetReport(reportType, (byte)reportID, out report))
  284. {
  285. report = new Report() { ID = (byte)reportID, Type = reportType };
  286. Reports.Add(report);
  287. }
  288. segment.Report = report;
  289. }
  290. public ReportCollection CurrentCollection
  291. {
  292. get;
  293. set;
  294. }
  295. public ReportCollection RootCollection
  296. {
  297. get;
  298. private set;
  299. }
  300. public IDictionary<GlobalItemTag, EncodedItem> GlobalItemState
  301. {
  302. get { return GlobalItemStateStack[GlobalItemStateStack.Count - 1]; }
  303. }
  304. public IList<IDictionary<GlobalItemTag, EncodedItem>> GlobalItemStateStack
  305. {
  306. get;
  307. private set;
  308. }
  309. public IList<KeyValuePair<LocalItemTag, uint>> LocalItemState
  310. {
  311. get;
  312. private set;
  313. }
  314. IEnumerable<Report> FilterReports(ReportType reportType)
  315. {
  316. foreach (Report report in Reports)
  317. {
  318. if (report.Type == reportType) { yield return report; }
  319. }
  320. }
  321. public IEnumerable<Report> InputReports
  322. {
  323. get { return FilterReports(ReportType.Input); }
  324. }
  325. /// <summary>
  326. /// The maximum input report length.
  327. /// The Report ID is not included in this length.
  328. /// </summary>
  329. public int InputReportMaxLength
  330. {
  331. get { return GetMaxLengthOfReports(InputReports); }
  332. }
  333. public IEnumerable<Report> OutputReports
  334. {
  335. get { return FilterReports(ReportType.Output); }
  336. }
  337. /// <summary>
  338. /// The maximum output report length.
  339. /// The Report ID is not included in this length.
  340. /// </summary>
  341. public int OutputReportMaxLength
  342. {
  343. get { return GetMaxLengthOfReports(OutputReports); }
  344. }
  345. public IEnumerable<Report> FeatureReports
  346. {
  347. get { return FilterReports(ReportType.Feature); }
  348. }
  349. /// <summary>
  350. /// The maximum feature report length.
  351. /// The Report ID is not included in this length.
  352. /// </summary>
  353. public int FeatureReportMaxLength
  354. {
  355. get { return GetMaxLengthOfReports(FeatureReports); }
  356. }
  357. public IList<Report> Reports
  358. {
  359. get;
  360. private set;
  361. }
  362. /// <summary>
  363. /// True if the device sends Report IDs.
  364. /// </summary>
  365. public bool ReportsUseID
  366. {
  367. get;
  368. set;
  369. }
  370. public IEnumerable<IEnumerable<uint>> InputUsages
  371. {
  372. get
  373. {
  374. foreach (Report report in InputReports)
  375. {
  376. foreach (ReportSegment segment in report.Segments)
  377. {
  378. IndexBase usages = segment.Indexes.Usage;
  379. for (int i = 0; i < usages.Count; i++)
  380. {
  381. yield return usages.ValuesFromIndex(i);
  382. }
  383. }
  384. }
  385. }
  386. }
  387. }
  388. }