xref: /freebsd/usr.sbin/bluetooth/bthidd/parser.y (revision 39beb93c3f8bdbf72a61fda42300b5ebed7390c8)
1 %{
2 /*
3  * parser.y
4  */
5 
6 /*-
7  * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * $Id: parser.y,v 1.7 2006/09/07 21:06:53 max Exp $
32  * $FreeBSD$
33  */
34 
35 #include <sys/queue.h>
36 #include <bluetooth.h>
37 #include <dev/usb/usb.h>
38 #include <dev/usb/usbhid.h>
39 #include <errno.h>
40 #include <limits.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <usbhid.h>
45 
46 #ifndef BTHIDCONTROL
47 #include <stdarg.h>
48 #include <syslog.h>
49 #define	SYSLOG		syslog
50 #define	LOGCRIT		LOG_CRIT
51 #define	LOGERR		LOG_ERR
52 #define	LOGWARNING	LOG_WARNING
53 #define	EOL
54 #else
55 #define	SYSLOG		fprintf
56 #define	LOGCRIT		stderr
57 #define	LOGERR		stderr
58 #define	LOGWARNING	stderr
59 #define	EOL	"\n"
60 #endif /* ndef BTHIDCONTROL */
61 
62 #include "bthid_config.h"
63 
64 	int	yyparse		(void);
65 	int	yylex		(void);
66 	void	yyerror		(char const *);
67 static	int32_t	check_hid_device(hid_device_p hid_device);
68 static	void	free_hid_device	(hid_device_p hid_device);
69 
70 extern	FILE			*yyin;
71 extern	int			 yylineno;
72 	char const		*config_file = BTHIDD_CONFFILE;
73 	char const		*hids_file   = BTHIDD_HIDSFILE;
74 
75 static	char			 buffer[1024];
76 static	int32_t			 hid_descriptor_size;
77 static	hid_device_t		*hid_device = NULL;
78 static	LIST_HEAD(, hid_device)	 hid_devices;
79 
80 %}
81 
82 %union {
83 	bdaddr_t	bdaddr;
84 	int32_t		num;
85 }
86 
87 %token <bdaddr> T_BDADDRSTRING
88 %token <num>	T_HEXBYTE
89 %token T_DEVICE T_BDADDR T_CONTROL_PSM T_INTERRUPT_PSM T_RECONNECT_INITIATE
90 %token T_BATTERY_POWER T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR
91 %token T_TRUE T_FALSE T_ERROR
92 
93 %%
94 
95 config:		line
96 		| config line
97 		;
98 
99 line:		T_DEVICE
100 			{
101 			hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device));
102 			if (hid_device == NULL) {
103 				SYSLOG(LOGCRIT, "Could not allocate new " \
104 						"config entry" EOL);
105 				YYABORT;
106 			}
107 
108 			hid_device->new_device = 1;
109 			}
110 		'{' options '}'
111 			{
112 			if (check_hid_device(hid_device))
113 				LIST_INSERT_HEAD(&hid_devices,hid_device,next);
114 			else
115 				free_hid_device(hid_device);
116 
117 			hid_device = NULL;
118 			}
119 		;
120 
121 options:	option ';'
122 		| options option ';'
123 		;
124 
125 option:		bdaddr
126 		| control_psm
127 		| interrupt_psm
128 		| reconnect_initiate
129 		| battery_power
130 		| normally_connectable
131 		| hid_descriptor
132 		| parser_error
133 		;
134 
135 bdaddr:		T_BDADDR T_BDADDRSTRING
136 			{
137 			memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr));
138 			}
139 		;
140 
141 control_psm:	T_CONTROL_PSM T_HEXBYTE
142 			{
143 			hid_device->control_psm = $2;
144 			}
145 		;
146 
147 interrupt_psm:	T_INTERRUPT_PSM T_HEXBYTE
148 			{
149 			hid_device->interrupt_psm = $2;
150 			}
151 		;
152 
153 reconnect_initiate: T_RECONNECT_INITIATE T_TRUE
154 			{
155 			hid_device->reconnect_initiate = 1;
156 			}
157 		| T_RECONNECT_INITIATE T_FALSE
158 			{
159 			hid_device->reconnect_initiate = 0;
160 			}
161 		;
162 
163 battery_power:	T_BATTERY_POWER T_TRUE
164 			{
165 			hid_device->battery_power = 1;
166 			}
167 		| T_BATTERY_POWER T_FALSE
168 			{
169 			hid_device->battery_power = 0;
170 			}
171 		;
172 
173 normally_connectable: T_NORMALLY_CONNECTABLE T_TRUE
174 			{
175 			hid_device->normally_connectable = 1;
176 			}
177 		| T_NORMALLY_CONNECTABLE T_FALSE
178 			{
179 			hid_device->normally_connectable = 0;
180 			}
181 		;
182 
183 hid_descriptor:	T_HID_DESCRIPTOR
184 			{
185 			hid_descriptor_size = 0;
186 			}
187 		'{' hid_descriptor_bytes '}'
188 			{
189 			if (hid_device->desc != NULL)
190 				hid_dispose_report_desc(hid_device->desc);
191 
192 			hid_device->desc = hid_use_report_desc((unsigned char *) buffer, hid_descriptor_size);
193 			if (hid_device->desc == NULL) {
194 				SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL);
195 				YYABORT;
196 			}
197 			}
198 		;
199 
200 hid_descriptor_bytes: hid_descriptor_byte
201 		| hid_descriptor_bytes hid_descriptor_byte
202 		;
203 
204 hid_descriptor_byte: T_HEXBYTE
205 			{
206 			if (hid_descriptor_size >= (int32_t) sizeof(buffer)) {
207 				SYSLOG(LOGCRIT, "HID descriptor is too big" EOL);
208 				YYABORT;
209 			}
210 
211 			buffer[hid_descriptor_size ++] = $1;
212 			}
213 		;
214 
215 parser_error:	T_ERROR
216 			{
217 				YYABORT;
218 			}
219 
220 %%
221 
222 /* Display parser error message */
223 void
224 yyerror(char const *message)
225 {
226 	SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno);
227 }
228 
229 /* Re-read config file */
230 int32_t
231 read_config_file(void)
232 {
233 	int32_t	e;
234 
235 	if (config_file == NULL) {
236 		SYSLOG(LOGERR, "Unknown config file name!" EOL);
237 		return (-1);
238 	}
239 
240 	if ((yyin = fopen(config_file, "r")) == NULL) {
241 		SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL,
242 				config_file, strerror(errno), errno);
243 		return (-1);
244 	}
245 
246 	clean_config();
247 	if (yyparse() < 0) {
248 		SYSLOG(LOGERR, "Could not parse config file '%s'" EOL,
249 				config_file);
250 		e = -1;
251 	} else
252 		e = 0;
253 
254 	fclose(yyin);
255 	yyin = NULL;
256 
257 	return (e);
258 }
259 
260 /* Clean config */
261 void
262 clean_config(void)
263 {
264 	while (!LIST_EMPTY(&hid_devices)) {
265 		hid_device_p	d = LIST_FIRST(&hid_devices);
266 
267 		LIST_REMOVE(d, next);
268 		free_hid_device(d);
269 	}
270 }
271 
272 /* Lookup config entry */
273 hid_device_p
274 get_hid_device(bdaddr_p bdaddr)
275 {
276 	hid_device_p	d;
277 
278 	LIST_FOREACH(d, &hid_devices, next)
279 		if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
280 			break;
281 
282 	return (d);
283 }
284 
285 /* Get next config entry */
286 hid_device_p
287 get_next_hid_device(hid_device_p d)
288 {
289 	return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next));
290 }
291 
292 /* Print config entry */
293 void
294 print_hid_device(hid_device_p d, FILE *f)
295 {
296 	/* XXX FIXME hack! */
297 	struct report_desc {
298 		unsigned int	size;
299 		unsigned char	data[1];
300 	};
301 	/* XXX FIXME hack! */
302 
303 	struct report_desc	*desc = (struct report_desc *) d->desc;
304 	uint32_t		 i;
305 
306 	fprintf(f,
307 "device {\n"					\
308 "	bdaddr			%s;\n"		\
309 "	control_psm		0x%x;\n"	\
310 "	interrupt_psm		0x%x;\n"	\
311 "	reconnect_initiate	%s;\n"		\
312 "	battery_power		%s;\n"		\
313 "	normally_connectable	%s;\n"		\
314 "	hid_descriptor		{",
315 		bt_ntoa(&d->bdaddr, NULL),
316 		d->control_psm, d->interrupt_psm,
317                 d->reconnect_initiate? "true" : "false",
318                 d->battery_power? "true" : "false",
319                 d->normally_connectable? "true" : "false");
320 
321 	for (i = 0; i < desc->size; i ++) {
322 			if ((i % 8) == 0)
323 				fprintf(f, "\n		");
324 
325 			fprintf(f, "0x%2.2x ", desc->data[i]);
326 	}
327 
328 	fprintf(f,
329 "\n"		\
330 "	};\n"	\
331 "}\n");
332 }
333 
334 /* Check config entry */
335 static int32_t
336 check_hid_device(hid_device_p d)
337 {
338 	hid_data_t	hd;
339 	hid_item_t	hi;
340 	int32_t		page;
341 
342 	if (get_hid_device(&d->bdaddr) != NULL) {
343 		SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL,
344 				bt_ntoa(&d->bdaddr, NULL));
345 		return (0);
346 	}
347 
348 	if (d->control_psm == 0) {
349 		SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL);
350 		return (0);
351 	}
352 
353 	if (d->interrupt_psm == 0) {
354 		SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL);
355 		return (0);
356 	}
357 
358 	if (d->desc == NULL) {
359 		SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL);
360 		return (0);
361 	}
362 
363 	/* XXX somehow need to make sure descriptor is valid */
364 	for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) {
365 		switch (hi.kind) {
366 		case hid_collection:
367 		case hid_endcollection:
368 		case hid_output:
369 		case hid_feature:
370 			break;
371 
372 		case hid_input:
373 			/* Check if the device may send keystrokes */
374 			page = HID_PAGE(hi.usage);
375 			if (page == HUP_KEYBOARD)
376 				d->keyboard = 1;
377 			break;
378 		}
379 	}
380 	hid_end_parse(hd);
381 
382 	return (1);
383 }
384 
385 /* Free config entry */
386 static void
387 free_hid_device(hid_device_p d)
388 {
389 	if (d->desc != NULL)
390 		hid_dispose_report_desc(d->desc);
391 
392 	memset(d, 0, sizeof(*d));
393 	free(d);
394 }
395 
396 /* Re-read hids file */
397 int32_t
398 read_hids_file(void)
399 {
400 	FILE		*f;
401 	hid_device_t	*d;
402 	char		*line;
403 	bdaddr_t	 bdaddr;
404 	int32_t		 lineno;
405 
406 	if (hids_file == NULL) {
407 		SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
408 		return (-1);
409 	}
410 
411 	if ((f = fopen(hids_file, "r")) == NULL) {
412 		if (errno == ENOENT)
413 			return (0);
414 
415 		SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
416 			hids_file, strerror(errno), errno);
417 		return (-1);
418 	}
419 
420 	for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) {
421 		if ((line = strtok(buffer, "\r\n\t ")) == NULL)
422 			continue; /* ignore empty lines */
423 
424 		if (!bt_aton(line, &bdaddr)) {
425 			SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \
426 				"%s:%d" EOL, hids_file, lineno);
427 			continue;
428 		}
429 
430 		if ((d = get_hid_device(&bdaddr)) != NULL)
431 			d->new_device = 0;
432 	}
433 
434 	fclose(f);
435 
436 	return (0);
437 }
438 
439 /* Write hids file */
440 int32_t
441 write_hids_file(void)
442 {
443 	char		 path[PATH_MAX];
444 	FILE		*f;
445 	hid_device_t	*d;
446 
447 	if (hids_file == NULL) {
448 		SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
449 		return (-1);
450 	}
451 
452 	snprintf(path, sizeof(path), "%s.new", hids_file);
453 
454 	if ((f = fopen(path, "w")) == NULL) {
455 		SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
456 			path, strerror(errno), errno);
457 		return (-1);
458 	}
459 
460 	LIST_FOREACH(d, &hid_devices, next)
461 		if (!d->new_device)
462 			fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL));
463 
464 	fclose(f);
465 
466 	if (rename(path, hids_file) < 0) {
467 		SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \
468 			"%s (%d)" EOL, path, hids_file, strerror(errno), errno);
469 		unlink(path);
470 		return (-1);
471 	}
472 
473 	return (0);
474 }
475 
476