1*9816111aSmpi /* $OpenBSD: usage.c,v 1.17 2018/07/09 08:57:04 mpi Exp $ */
27517eab2Snate /* $NetBSD: usage.c,v 1.1 2001/12/28 17:45:27 augustss Exp $ */
3be8f1931Spvalchev
4be8f1931Spvalchev /*
5be8f1931Spvalchev * Copyright (c) 1999 Lennart Augustsson <augustss@netbsd.org>
6be8f1931Spvalchev * All rights reserved.
7be8f1931Spvalchev *
8be8f1931Spvalchev * Redistribution and use in source and binary forms, with or without
9be8f1931Spvalchev * modification, are permitted provided that the following conditions
10be8f1931Spvalchev * are met:
11be8f1931Spvalchev * 1. Redistributions of source code must retain the above copyright
12be8f1931Spvalchev * notice, this list of conditions and the following disclaimer.
13be8f1931Spvalchev * 2. Redistributions in binary form must reproduce the above copyright
14be8f1931Spvalchev * notice, this list of conditions and the following disclaimer in the
15be8f1931Spvalchev * documentation and/or other materials provided with the distribution.
16be8f1931Spvalchev *
17be8f1931Spvalchev * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18be8f1931Spvalchev * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19be8f1931Spvalchev * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20be8f1931Spvalchev * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21be8f1931Spvalchev * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22be8f1931Spvalchev * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23be8f1931Spvalchev * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24be8f1931Spvalchev * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25be8f1931Spvalchev * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26be8f1931Spvalchev * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27be8f1931Spvalchev * SUCH DAMAGE.
28be8f1931Spvalchev */
29be8f1931Spvalchev
30be8f1931Spvalchev #include <ctype.h>
31be8f1931Spvalchev #include <err.h>
32be8f1931Spvalchev #include <stdio.h>
33be8f1931Spvalchev #include <stdlib.h>
34be8f1931Spvalchev #include <string.h>
3578e5c336Sderaadt #include <errno.h>
36be8f1931Spvalchev
37be8f1931Spvalchev #include "usbhid.h"
38be8f1931Spvalchev
39be8f1931Spvalchev #define _PATH_HIDTABLE "/usr/share/misc/usb_hid_usages"
40be8f1931Spvalchev
41be8f1931Spvalchev struct usage_in_page {
42be8f1931Spvalchev const char *name;
43be8f1931Spvalchev int usage;
44be8f1931Spvalchev };
45be8f1931Spvalchev
46be8f1931Spvalchev static struct usage_page {
47be8f1931Spvalchev const char *name;
48be8f1931Spvalchev int usage;
49be8f1931Spvalchev struct usage_in_page *page_contents;
50be8f1931Spvalchev int pagesize, pagesizemax;
51be8f1931Spvalchev } *pages;
52be8f1931Spvalchev static int npages, npagesmax;
53be8f1931Spvalchev
54be8f1931Spvalchev #ifdef DEBUG
55be8f1931Spvalchev void
dump_hid_table(void)56be8f1931Spvalchev dump_hid_table(void)
57be8f1931Spvalchev {
58be8f1931Spvalchev int i, j;
59be8f1931Spvalchev
60be8f1931Spvalchev for (i = 0; i < npages; i++) {
61be8f1931Spvalchev printf("%d\t%s\n", pages[i].usage, pages[i].name);
62be8f1931Spvalchev for (j = 0; j < pages[i].pagesize; j++) {
63be8f1931Spvalchev printf("\t%d\t%s\n", pages[i].page_contents[j].usage,
64be8f1931Spvalchev pages[i].page_contents[j].name);
65be8f1931Spvalchev }
66be8f1931Spvalchev }
67be8f1931Spvalchev }
68be8f1931Spvalchev #endif
69be8f1931Spvalchev
70be8f1931Spvalchev void
hid_init(const char * hidname)71be8f1931Spvalchev hid_init(const char *hidname)
72be8f1931Spvalchev {
7378e5c336Sderaadt if (hid_start(hidname) == -1)
7478e5c336Sderaadt errx(1, "hid_init: failed");
7578e5c336Sderaadt }
76be8f1931Spvalchev
7778e5c336Sderaadt int
hid_start(const char * hidname)7878e5c336Sderaadt hid_start(const char *hidname)
7978e5c336Sderaadt {
8078e5c336Sderaadt char line[100], name[100], *p, *n;
8178e5c336Sderaadt struct usage_page *curpage = NULL;
8278e5c336Sderaadt int lineno, no;
8378e5c336Sderaadt FILE *f;
8478e5c336Sderaadt
8578e5c336Sderaadt if (hidname == NULL)
86be8f1931Spvalchev hidname = _PATH_HIDTABLE;
87be8f1931Spvalchev
88be8f1931Spvalchev f = fopen(hidname, "r");
89be8f1931Spvalchev if (f == NULL)
9078e5c336Sderaadt return -1;
91be8f1931Spvalchev for (lineno = 1; ; lineno++) {
92be8f1931Spvalchev if (fgets(line, sizeof line, f) == NULL)
93be8f1931Spvalchev break;
94be8f1931Spvalchev if (line[0] == '#')
95be8f1931Spvalchev continue;
9680c62621Sderaadt for (p = line; isspace((unsigned char)*p); p++)
97be8f1931Spvalchev ;
98be8f1931Spvalchev if (!*p)
99be8f1931Spvalchev continue;
10088a81472Smatthieu if (sscanf(line, " * %99[^\n]", name) == 1)
101be8f1931Spvalchev no = -1;
10288a81472Smatthieu else if (sscanf(line, " 0x%x %99[^\n]", &no, name) != 2 &&
10378e5c336Sderaadt sscanf(line, " %d %99[^\n]", &no, name) != 2) {
10478e5c336Sderaadt warnx("file %s, line %d, syntax error",
105be8f1931Spvalchev hidname, lineno);
10678e5c336Sderaadt errno = EINVAL;
10778e5c336Sderaadt goto fail;
10878e5c336Sderaadt }
109be8f1931Spvalchev for (p = name; *p; p++)
11080c62621Sderaadt if (isspace((unsigned char)*p) || *p == '.')
111be8f1931Spvalchev *p = '_';
112be8f1931Spvalchev n = strdup(name);
113be8f1931Spvalchev if (!n)
11478e5c336Sderaadt goto fail;
11578e5c336Sderaadt
11680c62621Sderaadt if (isspace((unsigned char)line[0])) {
11778e5c336Sderaadt if (!curpage) {
11878e5c336Sderaadt warnx("file %s, line %d, syntax error",
119be8f1931Spvalchev hidname, lineno);
12078e5c336Sderaadt free(n);
12178e5c336Sderaadt errno = EINVAL;
12278e5c336Sderaadt goto fail;
12378e5c336Sderaadt }
124be8f1931Spvalchev if (curpage->pagesize >= curpage->pagesizemax) {
12578e5c336Sderaadt void *new;
12678e5c336Sderaadt int len;
12778e5c336Sderaadt
12878e5c336Sderaadt len = curpage->pagesizemax + 10;
129770282b7Sderaadt new = reallocarray(curpage->page_contents,
130770282b7Sderaadt len, sizeof(struct usage_in_page));
131fcfd87e4Smatthieu if (!new) {
13278e5c336Sderaadt free(curpage->page_contents);
13378e5c336Sderaadt curpage->page_contents = NULL;
13478e5c336Sderaadt free(n);
13578e5c336Sderaadt goto fail;
13678e5c336Sderaadt }
13778e5c336Sderaadt curpage->pagesizemax = len;
13878e5c336Sderaadt curpage->page_contents = new;
139be8f1931Spvalchev }
140be8f1931Spvalchev curpage->page_contents[curpage->pagesize].name = n;
141be8f1931Spvalchev curpage->page_contents[curpage->pagesize].usage = no;
142be8f1931Spvalchev curpage->pagesize++;
143be8f1931Spvalchev } else {
144be8f1931Spvalchev if (npages >= npagesmax) {
14578e5c336Sderaadt int len;
14678e5c336Sderaadt void *new;
14778e5c336Sderaadt
14878e5c336Sderaadt if (pages == NULL) {
14978e5c336Sderaadt len = 5;
15078e5c336Sderaadt pages = calloc(len,
151be8f1931Spvalchev sizeof (struct usage_page));
152be8f1931Spvalchev } else {
15378e5c336Sderaadt len = npagesmax * 5;
154770282b7Sderaadt new = reallocarray(pages,
155770282b7Sderaadt len, sizeof(struct usage_page));
1568efd7ebeSjaredy if (!new) {
1578efd7ebeSjaredy free(n);
15878e5c336Sderaadt goto fail;
1598efd7ebeSjaredy }
16078e5c336Sderaadt pages = new;
16178e5c336Sderaadt bzero(pages + npagesmax,
162cbbaf2e7Spat (len - npagesmax) *
163cbbaf2e7Spat sizeof(struct usage_page));
164be8f1931Spvalchev }
16578e5c336Sderaadt if (!pages) {
16678e5c336Sderaadt free(n);
16778e5c336Sderaadt goto fail;
16878e5c336Sderaadt }
16978e5c336Sderaadt npagesmax = len;
170be8f1931Spvalchev }
171be8f1931Spvalchev curpage = &pages[npages++];
172be8f1931Spvalchev curpage->name = n;
173be8f1931Spvalchev curpage->usage = no;
174be8f1931Spvalchev curpage->pagesize = 0;
175be8f1931Spvalchev curpage->pagesizemax = 10;
1767de199b6Sderaadt curpage->page_contents = calloc(curpage->pagesizemax,
177be8f1931Spvalchev sizeof (struct usage_in_page));
178be8f1931Spvalchev if (!curpage->page_contents)
17978e5c336Sderaadt goto fail;
180be8f1931Spvalchev }
181be8f1931Spvalchev }
182be8f1931Spvalchev fclose(f);
183be8f1931Spvalchev #ifdef DEBUG
184be8f1931Spvalchev dump_hid_table();
185be8f1931Spvalchev #endif
18678e5c336Sderaadt return 0;
18778e5c336Sderaadt
18878e5c336Sderaadt fail:
18978e5c336Sderaadt if (f)
19078e5c336Sderaadt fclose(f);
1918efd7ebeSjaredy if (pages) {
192d2fda68cSdhill for (no = 0; no < npages; no++) {
19378e5c336Sderaadt if (pages[no].name)
19478e5c336Sderaadt free((char *)pages[no].name);
19578e5c336Sderaadt if (pages[no].page_contents)
19678e5c336Sderaadt free((char *)pages[no].page_contents);
19778e5c336Sderaadt }
19878e5c336Sderaadt free(pages);
19978e5c336Sderaadt pages = NULL;
2008efd7ebeSjaredy }
20178e5c336Sderaadt npages = 0;
20278e5c336Sderaadt npagesmax = 0;
20378e5c336Sderaadt return -1;
204be8f1931Spvalchev }
205be8f1931Spvalchev
206be8f1931Spvalchev const char *
hid_usage_page(int i)207be8f1931Spvalchev hid_usage_page(int i)
208be8f1931Spvalchev {
209be8f1931Spvalchev static char b[10];
210be8f1931Spvalchev int k;
211be8f1931Spvalchev
212be8f1931Spvalchev if (!pages)
21378e5c336Sderaadt return NULL;
214be8f1931Spvalchev
215be8f1931Spvalchev for (k = 0; k < npages; k++)
216be8f1931Spvalchev if (pages[k].usage == i)
217be8f1931Spvalchev return pages[k].name;
218d4d01661Sderaadt snprintf(b, sizeof b, "0x%04x", i);
219be8f1931Spvalchev return b;
220be8f1931Spvalchev }
221be8f1931Spvalchev
222be8f1931Spvalchev const char *
hid_usage_in_page(unsigned int u)223be8f1931Spvalchev hid_usage_in_page(unsigned int u)
224be8f1931Spvalchev {
22578e5c336Sderaadt int i = HID_USAGE(u), j, k, us;
226be8f1931Spvalchev int page = HID_PAGE(u);
227be8f1931Spvalchev static char b[100];
228be8f1931Spvalchev
229be8f1931Spvalchev for (k = 0; k < npages; k++)
230be8f1931Spvalchev if (pages[k].usage == page)
231be8f1931Spvalchev break;
232be8f1931Spvalchev if (k >= npages)
233be8f1931Spvalchev goto bad;
234be8f1931Spvalchev for (j = 0; j < pages[k].pagesize; j++) {
235be8f1931Spvalchev us = pages[k].page_contents[j].usage;
236be8f1931Spvalchev if (us == -1) {
2371da7bb78Sderaadt snprintf(b, sizeof b,
238be8f1931Spvalchev pages[k].page_contents[j].name, i);
239be8f1931Spvalchev return b;
240be8f1931Spvalchev }
241be8f1931Spvalchev if (us == i)
242be8f1931Spvalchev return pages[k].page_contents[j].name;
243be8f1931Spvalchev }
244be8f1931Spvalchev bad:
245d4d01661Sderaadt snprintf(b, sizeof b, "0x%04x", i);
246be8f1931Spvalchev return b;
247be8f1931Spvalchev }
248be8f1931Spvalchev
249be8f1931Spvalchev int
hid_parse_usage_page(const char * name)250be8f1931Spvalchev hid_parse_usage_page(const char *name)
251be8f1931Spvalchev {
252be8f1931Spvalchev int k;
253be8f1931Spvalchev
254be8f1931Spvalchev if (!pages)
255691235adSmiod return 0;
256be8f1931Spvalchev
257be8f1931Spvalchev for (k = 0; k < npages; k++)
258be8f1931Spvalchev if (strcmp(pages[k].name, name) == 0)
259be8f1931Spvalchev return pages[k].usage;
260be8f1931Spvalchev return -1;
261be8f1931Spvalchev }
262be8f1931Spvalchev
263be8f1931Spvalchev /* XXX handle hex */
264be8f1931Spvalchev int
hid_parse_usage_in_page(const char * name)265be8f1931Spvalchev hid_parse_usage_in_page(const char *name)
266be8f1931Spvalchev {
267*9816111aSmpi const char *sep, *fmtsep, *errstr, *fmtname;
268be8f1931Spvalchev unsigned int l;
269*9816111aSmpi int k, j, us, pu, len;
270be8f1931Spvalchev
2717517eab2Snate sep = strchr(name, ':');
272be8f1931Spvalchev if (sep == NULL)
273be8f1931Spvalchev return -1;
274be8f1931Spvalchev l = sep - name;
275be8f1931Spvalchev for (k = 0; k < npages; k++)
276be8f1931Spvalchev if (strncmp(pages[k].name, name, l) == 0)
277be8f1931Spvalchev goto found;
278be8f1931Spvalchev return -1;
279be8f1931Spvalchev found:
280be8f1931Spvalchev sep++;
281*9816111aSmpi for (j = 0; j < pages[k].pagesize; j++) {
282*9816111aSmpi us = pages[k].page_contents[j].usage;
283*9816111aSmpi if (us == -1) {
284*9816111aSmpi fmtname = pages[k].page_contents[j].name;
285*9816111aSmpi fmtsep = strchr(fmtname, '%');
286*9816111aSmpi len = fmtsep - fmtname;
287*9816111aSmpi if (fmtsep != NULL && strncmp(sep, fmtname, len) == 0) {
288*9816111aSmpi pu = strtonum(sep + len, 0x1, 0xFFFF, &errstr);
289*9816111aSmpi if (errstr == NULL)
290*9816111aSmpi return (pages[k].usage << 16) | pu;
291*9816111aSmpi }
292*9816111aSmpi }
293be8f1931Spvalchev if (strcmp(pages[k].page_contents[j].name, sep) == 0)
29478e5c336Sderaadt return (pages[k].usage << 16) |
29578e5c336Sderaadt pages[k].page_contents[j].usage;
296*9816111aSmpi }
29778e5c336Sderaadt return -1;
298be8f1931Spvalchev }
299