source: mainline/uspace/lib/usbhid/src/hidpath.c

Last change on this file was 09ab0a9a, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Fix vertical spacing with new Ccheck revision.

  • Property mode set to 100644
File size: 10.6 KB
Line 
1/*
2 * Copyright (c) 2011 Matej Klonfar
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libusbhid
30 * @{
31 */
32/** @file
33 * HID report descriptor and report data parser implementation.
34 */
35#include <usb/hid/hidparser.h>
36#include <errno.h>
37#include <stdio.h>
38#include <mem.h>
39#include <stdlib.h>
40#include <usb/debug.h>
41#include <assert.h>
42
43/**
44 * Compares two usages if they are same or not or one of the usages is not
45 * set.
46 *
47 * @param usage1
48 * @param usage2
49 * @return boolean
50 */
51#define USB_HID_SAME_USAGE(usage1, usage2) \
52 ((usage1 == usage2) || (usage1 == 0) || (usage2 == 0))
53
54/**
55 * Compares two usage pages if they are same or not or one of them is not set.
56 *
57 * @param page1
58 * @param page2
59 * @return boolean
60 */
61#define USB_HID_SAME_USAGE_PAGE(page1, page2) \
62 ((page1 == page2) || (page1 == 0) || (page2 == 0))
63
64/**
65 * Appends one item (couple of usage_path and usage) into the usage path
66 * structure
67 *
68 * @param usage_path Usage path structure
69 * @param usage_page Usage page constant
70 * @param usage Usage constant
71 * @return Error code
72 */
73errno_t usb_hid_report_path_append_item(usb_hid_report_path_t *usage_path,
74 int32_t usage_page, int32_t usage)
75{
76 usb_hid_report_usage_path_t *item =
77 malloc(sizeof(usb_hid_report_usage_path_t));
78
79 if (item == NULL) {
80 return ENOMEM;
81 }
82 link_initialize(&item->rpath_items_link);
83
84 item->usage = usage;
85 item->usage_page = usage_page;
86 item->flags = 0;
87
88 list_append (&item->rpath_items_link, &usage_path->items);
89 usage_path->depth++;
90 return EOK;
91}
92
93/**
94 * Removes last item from the usage path structure
95 * @param usage_path
96 * @return void
97 */
98void usb_hid_report_remove_last_item(usb_hid_report_path_t *usage_path)
99{
100 link_t *item_link;
101 usb_hid_report_usage_path_t *item;
102
103 if (!list_empty(&usage_path->items)) {
104 item_link = list_last(&usage_path->items);
105 item = list_get_instance(item_link,
106 usb_hid_report_usage_path_t, rpath_items_link);
107 list_remove(item_link);
108 usage_path->depth--;
109 free(item);
110 }
111}
112
113/**
114 * Nulls last item of the usage path structure.
115 *
116 * @param usage_path
117 * @return void
118 */
119void usb_hid_report_null_last_item(usb_hid_report_path_t *usage_path)
120{
121 usb_hid_report_usage_path_t *item;
122
123 if (!list_empty(&usage_path->items)) {
124 item = list_get_instance(list_last(&usage_path->items),
125 usb_hid_report_usage_path_t, rpath_items_link);
126
127 memset(item, 0, sizeof(usb_hid_report_usage_path_t));
128 }
129}
130
131/**
132 * Modifies last item of usage path structure by given usage page or usage
133 *
134 * @param usage_path Opaque usage path structure
135 * @param tag Class of currently processed tag (Usage page tag falls into Global
136 * class but Usage tag into the Local)
137 * @param data Value of the processed tag
138 * @return void
139 */
140void usb_hid_report_set_last_item(usb_hid_report_path_t *usage_path,
141 int32_t tag, int32_t data)
142{
143 usb_hid_report_usage_path_t *item;
144
145 if (!list_empty(&usage_path->items)) {
146 item = list_get_instance(list_last(&usage_path->items),
147 usb_hid_report_usage_path_t, rpath_items_link);
148
149 switch (tag) {
150 case USB_HID_TAG_CLASS_GLOBAL:
151 item->usage_page = data;
152 break;
153 case USB_HID_TAG_CLASS_LOCAL:
154 item->usage = data;
155 break;
156 }
157 }
158}
159
160void usb_hid_print_usage_path(usb_hid_report_path_t *path)
161{
162 usb_log_debug("USAGE_PATH FOR RId(%d):", path->report_id);
163 usb_log_debug("\tLENGTH: %d", path->depth);
164
165 list_foreach(path->items, rpath_items_link,
166 usb_hid_report_usage_path_t, path_item) {
167
168 usb_log_debug("\tUSAGE_PAGE: %X", path_item->usage_page);
169 usb_log_debug("\tUSAGE: %X", path_item->usage);
170 usb_log_debug("\tFLAGS: %d", path_item->flags);
171 }
172}
173
174/** Compare two usage paths structures
175 *
176 * @param report_path Usage path structure to compare with @path
177 * @param path Usage patrh structure to compare
178 * @param flags Flags determining the mode of comparison
179 *
180 * @return 0 if both paths are identical, non zero number otherwise
181 *
182 */
183int usb_hid_report_compare_usage_path(usb_hid_report_path_t *report_path,
184 usb_hid_report_path_t *path, int flags)
185{
186 usb_hid_report_usage_path_t *report_item;
187 usb_hid_report_usage_path_t *path_item;
188
189 link_t *report_link;
190 link_t *path_link;
191
192 int only_page;
193
194 if (report_path->report_id != path->report_id) {
195 if (path->report_id != 0) {
196 return 1;
197 }
198 }
199
200 // Empty path match all others
201 if (path->depth == 0) {
202 return 0;
203 }
204
205 if ((only_page = flags & USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY) != 0) {
206 flags -= USB_HID_PATH_COMPARE_USAGE_PAGE_ONLY;
207 }
208
209 switch (flags) {
210 case USB_HID_PATH_COMPARE_ANYWHERE:
211 /*
212 * Path is somewhere in report_path
213 */
214 if (path->depth != 1) {
215 return 1;
216 }
217
218 path_link = list_first(&path->items);
219 path_item = list_get_instance(path_link,
220 usb_hid_report_usage_path_t, rpath_items_link);
221
222 list_foreach(report_path->items, rpath_items_link,
223 usb_hid_report_usage_path_t, report_item) {
224 if (USB_HID_SAME_USAGE_PAGE(report_item->usage_page,
225 path_item->usage_page)) {
226
227 if (only_page == 0) {
228 if (USB_HID_SAME_USAGE(report_item->usage,
229 path_item->usage))
230 return 0;
231 } else {
232 return 0;
233 }
234 }
235 }
236
237 return 1;
238
239 case USB_HID_PATH_COMPARE_STRICT:
240 /*
241 * The paths must be identical
242 */
243 if (report_path->depth != path->depth) {
244 return 1;
245 }
246 /* Fallthrough */
247
248 case USB_HID_PATH_COMPARE_BEGIN:
249 /*
250 * Path is prefix of the report_path
251 */
252 report_link = report_path->items.head.next;
253 path_link = path->items.head.next;
254
255 while ((report_link != &report_path->items.head) &&
256 (path_link != &path->items.head)) {
257
258 report_item = list_get_instance(report_link,
259 usb_hid_report_usage_path_t, rpath_items_link);
260
261 path_item = list_get_instance(path_link,
262 usb_hid_report_usage_path_t, rpath_items_link);
263
264 if (!USB_HID_SAME_USAGE_PAGE(report_item->usage_page,
265 path_item->usage_page) || ((only_page == 0) &&
266 !USB_HID_SAME_USAGE(report_item->usage,
267 path_item->usage))) {
268 return 1;
269 } else {
270 report_link = report_link->next;
271 path_link = path_link->next;
272 }
273 }
274
275 if ((((flags & USB_HID_PATH_COMPARE_BEGIN) != 0) &&
276 (path_link == &path->items.head)) ||
277 ((report_link == &report_path->items.head) &&
278 (path_link == &path->items.head))) {
279 return 0;
280 } else {
281 return 1;
282 }
283 break;
284
285 case USB_HID_PATH_COMPARE_END:
286 /*
287 * Path is suffix of report_path
288 */
289 report_link = report_path->items.head.prev;
290 path_link = path->items.head.prev;
291
292 if (list_empty(&path->items)) {
293 return 0;
294 }
295
296 while ((report_link != &report_path->items.head) &&
297 (path_link != &path->items.head)) {
298 report_item = list_get_instance(report_link,
299 usb_hid_report_usage_path_t, rpath_items_link);
300
301 path_item = list_get_instance(path_link,
302 usb_hid_report_usage_path_t, rpath_items_link);
303
304 if (!USB_HID_SAME_USAGE_PAGE(report_item->usage_page,
305 path_item->usage_page) || ((only_page == 0) &&
306 !USB_HID_SAME_USAGE(report_item->usage,
307 path_item->usage))) {
308 return 1;
309 } else {
310 report_link = report_link->prev;
311 path_link = path_link->prev;
312 }
313 }
314
315 if (path_link == &path->items.head) {
316 return 0;
317 } else {
318 return 1;
319 }
320 break;
321
322 default:
323 return -1;
324 }
325}
326
327/**
328 * Allocates and initializes new usage path structure.
329 *
330 * @return Initialized usage path structure
331 */
332usb_hid_report_path_t *usb_hid_report_path(void)
333{
334 usb_hid_report_path_t *path;
335 path = malloc(sizeof(usb_hid_report_path_t));
336 if (path == NULL) {
337 return NULL;
338 } else {
339 path->depth = 0;
340 path->report_id = 0;
341 link_initialize(&path->cpath_link);
342 list_initialize(&path->items);
343 return path;
344 }
345}
346
347/**
348 * Releases given usage path structure.
349 *
350 * @param path usage path structure to release
351 * @return void
352 */
353void usb_hid_report_path_free(usb_hid_report_path_t *path)
354{
355 if (path == NULL)
356 return;
357 while (!list_empty(&path->items)) {
358 usb_hid_report_remove_last_item(path);
359 }
360
361 assert_link_not_used(&path->cpath_link);
362 free(path);
363}
364
365/**
366 * Clone content of given usage path to the new one
367 *
368 * @param usage_path Usage path structure to clone
369 * @return New copy of given usage path structure
370 */
371usb_hid_report_path_t *usb_hid_report_path_clone(
372 usb_hid_report_path_t *usage_path)
373{
374 usb_hid_report_usage_path_t *new_path_item;
375 usb_hid_report_path_t *new_usage_path = usb_hid_report_path ();
376
377 if (new_usage_path == NULL) {
378 return NULL;
379 }
380
381 new_usage_path->report_id = usage_path->report_id;
382
383 if (list_empty(&usage_path->items)) {
384 return new_usage_path;
385 }
386
387 list_foreach(usage_path->items, rpath_items_link,
388 usb_hid_report_usage_path_t, path_item) {
389
390 new_path_item = malloc(sizeof(usb_hid_report_usage_path_t));
391 if (new_path_item == NULL) {
392 return NULL;
393 }
394
395 link_initialize(&new_path_item->rpath_items_link);
396 new_path_item->usage_page = path_item->usage_page;
397 new_path_item->usage = path_item->usage;
398 new_path_item->flags = path_item->flags;
399
400 list_append(&new_path_item->rpath_items_link,
401 &new_usage_path->items);
402 new_usage_path->depth++;
403 }
404
405 return new_usage_path;
406}
407
408/**
409 * Sets report id in usage path structure
410 *
411 * @param path Usage path structure
412 * @param report_id Report id to set
413 * @return Error code
414 */
415errno_t usb_hid_report_path_set_report_id(usb_hid_report_path_t *path,
416 uint8_t report_id)
417{
418 if (path == NULL) {
419 return EINVAL;
420 }
421
422 path->report_id = report_id;
423 return EOK;
424}
425
426/**
427 * @}
428 */
Note: See TracBrowser for help on using the repository browser.