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