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