1/*	$NetBSD: usage.c,v 1.8 2000/10/10 19:23:58 is Exp $	*/
2
3/*
4 * Copyright (c) 1999 Lennart Augustsson <augustss@netbsd.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD$");
31
32#include <sys/param.h>
33#include <assert.h>
34#include <ctype.h>
35#include <err.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39
40#include "usbhid.h"
41
42#define _PATH_HIDTABLE "/usr/share/misc/usb_hid_usages"
43
44struct usage_in_page {
45	const char *name;
46	int usage;
47};
48
49static struct usage_page {
50	const char *name;
51	int usage;
52	struct usage_in_page *page_contents;
53	int pagesize, pagesizemax;
54} *pages;
55static int npages, npagesmax;
56
57#ifdef DEBUG
58void
59dump_hid_table(void)
60{
61	int i, j;
62
63	for (i = 0; i < npages; i++) {
64		printf("%d\t%s\n", pages[i].usage, pages[i].name);
65		for (j = 0; j < pages[i].pagesize; j++) {
66			printf("\t%d\t%s\n", pages[i].page_contents[j].usage,
67			       pages[i].page_contents[j].name);
68		}
69	}
70}
71#endif
72
73void
74hid_init(const char *hidname)
75{
76	FILE *f;
77	char line[100], name[100], *p, *n;
78	int no;
79	int lineno;
80	struct usage_page *curpage = 0;
81
82	if (hidname == 0)
83		hidname = _PATH_HIDTABLE;
84
85	f = fopen(hidname, "r");
86	if (f == NULL)
87		err(1, "%s", hidname);
88	for (lineno = 1; ; lineno++) {
89		if (fgets(line, sizeof line, f) == NULL)
90			break;
91		if (line[0] == '#')
92			continue;
93		for (p = line; *p && isspace(*p); p++)
94			;
95		if (!*p)
96			continue;
97		if (sscanf(line, " * %[^\n]", name) == 1)
98			no = -1;
99		else if (sscanf(line, " 0x%x %[^\n]", &no, name) != 2 &&
100			 sscanf(line, " %d %[^\n]", &no, name) != 2)
101			errx(1, "file %s, line %d, syntax error",
102			     hidname, lineno);
103		for (p = name; *p; p++)
104			if (isspace(*p) || *p == '.')
105				*p = '_';
106		n = strdup(name);
107		if (!n)
108			err(1, "strdup");
109		if (isspace(line[0])) {
110			if (!curpage)
111				errx(1, "file %s, line %d, syntax error",
112				     hidname, lineno);
113			if (curpage->pagesize >= curpage->pagesizemax) {
114				curpage->pagesizemax += 10;
115				curpage->page_contents =
116					realloc(curpage->page_contents,
117						curpage->pagesizemax *
118						sizeof (struct usage_in_page));
119				if (!curpage->page_contents)
120					err(1, "realloc");
121			}
122			curpage->page_contents[curpage->pagesize].name = n;
123			curpage->page_contents[curpage->pagesize].usage = no;
124			curpage->pagesize++;
125		} else {
126			if (npages >= npagesmax) {
127				if (pages == 0) {
128					npagesmax = 5;
129					pages = malloc(npagesmax *
130						  sizeof (struct usage_page));
131				} else {
132					npagesmax += 5;
133					pages = realloc(pages,
134						   npagesmax *
135						   sizeof (struct usage_page));
136				}
137				if (!pages)
138					err(1, "alloc");
139			}
140			curpage = &pages[npages++];
141			curpage->name = n;
142			curpage->usage = no;
143			curpage->pagesize = 0;
144			curpage->pagesizemax = 10;
145			curpage->page_contents =
146				malloc(curpage->pagesizemax *
147				       sizeof (struct usage_in_page));
148			if (!curpage->page_contents)
149				err(1, "malloc");
150		}
151	}
152	fclose(f);
153#ifdef DEBUG
154	dump_hid_table();
155#endif
156}
157
158const char *
159hid_usage_page(int i)
160{
161	static char b[10];
162	int k;
163
164	if (!pages)
165		errx(1, "no hid table");
166
167	for (k = 0; k < npages; k++)
168		if (pages[k].usage == i)
169			return pages[k].name;
170	sprintf(b, "0x%04x", i);
171	return b;
172}
173
174const char *
175hid_usage_in_page(unsigned int u)
176{
177	int page = HID_PAGE(u);
178	int i = HID_USAGE(u);
179	static char b[100];
180	int j, k, us;
181
182	for (k = 0; k < npages; k++)
183		if (pages[k].usage == page)
184			break;
185	if (k >= npages)
186		goto bad;
187	for (j = 0; j < pages[k].pagesize; j++) {
188		us = pages[k].page_contents[j].usage;
189		if (us == -1) {
190			sprintf(b,
191			    fmtcheck(pages[k].page_contents[j].name, "%d"),
192			    i);
193			return b;
194		}
195		if (us == i)
196			return pages[k].page_contents[j].name;
197	}
198 bad:
199	sprintf(b, "0x%04x", i);
200	return b;
201}
202
203int
204hid_parse_usage_page(const char *name)
205{
206	int k;
207
208	if (!pages)
209		errx(1, "no hid table");
210
211	for (k = 0; k < npages; k++)
212		if (strcmp(pages[k].name, name) == 0)
213			return pages[k].usage;
214	return -1;
215}
216
217/* XXX handle hex */
218int
219hid_parse_usage_in_page(const char *name)
220{
221	const char *sep;
222	int k, j;
223	unsigned int l;
224
225	sep = strchr(name, ':');
226	if (sep == NULL)
227		return -1;
228	l = sep - name;
229	for (k = 0; k < npages; k++)
230		if (strncmp(pages[k].name, name, l) == 0)
231			goto found;
232	return -1;
233 found:
234	sep++;
235	for (j = 0; j < pages[k].pagesize; j++)
236		if (strcmp(pages[k].page_contents[j].name, sep) == 0)
237			return (pages[k].usage << 16) | pages[k].page_contents[j].usage;
238	return (-1);
239}
240