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