parser.y revision 162529
1128080Semax%{
2128080Semax/*
3128080Semax * parser.y
4162128Semax */
5162128Semax
6162128Semax/*-
7162128Semax * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
8128080Semax * All rights reserved.
9128080Semax *
10128080Semax * Redistribution and use in source and binary forms, with or without
11128080Semax * modification, are permitted provided that the following conditions
12128080Semax * are met:
13128080Semax * 1. Redistributions of source code must retain the above copyright
14128080Semax *    notice, this list of conditions and the following disclaimer.
15128080Semax * 2. Redistributions in binary form must reproduce the above copyright
16128080Semax *    notice, this list of conditions and the following disclaimer in the
17128080Semax *    documentation and/or other materials provided with the distribution.
18128080Semax *
19128080Semax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20128080Semax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21128080Semax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22128080Semax * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23128080Semax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24128080Semax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25128080Semax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26128080Semax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27128080Semax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28128080Semax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29128080Semax * SUCH DAMAGE.
30128080Semax *
31162128Semax * $Id: parser.y,v 1.7 2006/09/07 21:06:53 max Exp $
32128080Semax * $FreeBSD: head/usr.sbin/bluetooth/bthidd/parser.y 162529 2006-09-21 17:16:37Z emax $
33128080Semax */
34128080Semax
35128080Semax#include <sys/queue.h>
36128080Semax#include <bluetooth.h>
37162128Semax#include <dev/usb/usb.h>
38162128Semax#include <dev/usb/usbhid.h>
39128080Semax#include <errno.h>
40128080Semax#include <limits.h>
41128080Semax#include <stdio.h>
42128080Semax#include <string.h>
43128080Semax#include <unistd.h>
44137868Semax#include <usbhid.h>
45128080Semax
46128080Semax#ifndef BTHIDCONTROL
47128080Semax#include <stdarg.h>
48128080Semax#include <syslog.h>
49128080Semax#define	SYSLOG		syslog
50128080Semax#define	LOGCRIT		LOG_CRIT
51128080Semax#define	LOGERR		LOG_ERR
52128080Semax#define	LOGWARNING	LOG_WARNING
53128080Semax#define	EOL
54128080Semax#else
55128080Semax#define	SYSLOG		fprintf
56128080Semax#define	LOGCRIT		stderr
57128080Semax#define	LOGERR		stderr
58128080Semax#define	LOGWARNING	stderr
59137868Semax#define	EOL	"\n"
60128080Semax#endif /* ndef BTHIDCONTROL */
61128080Semax
62128080Semax#include "bthid_config.h"
63128080Semax
64128080Semax	int	yyparse		(void);
65128080Semax	int	yylex		(void);
66162128Semax	void	yyerror		(char const *);
67162128Semaxstatic	int32_t	check_hid_device(hid_device_p hid_device);
68128080Semaxstatic	void	free_hid_device	(hid_device_p hid_device);
69128080Semax
70162128Semaxextern	FILE			*yyin;
71128080Semaxextern	int			 yylineno;
72162128Semax	char const		*config_file = BTHIDD_CONFFILE;
73162128Semax	char const		*hids_file   = BTHIDD_HIDSFILE;
74128080Semax
75128080Semaxstatic	char			 buffer[1024];
76162128Semaxstatic	int32_t			 hid_descriptor_size;
77128080Semaxstatic	hid_device_t		*hid_device = NULL;
78128080Semaxstatic	LIST_HEAD(, hid_device)	 hid_devices;
79128080Semax
80128080Semax%}
81128080Semax
82128080Semax%union {
83128080Semax	bdaddr_t	bdaddr;
84162128Semax	int32_t		num;
85128080Semax}
86128080Semax
87128080Semax%token <bdaddr> T_BDADDRSTRING
88128080Semax%token <num>	T_HEXBYTE
89128080Semax%token T_DEVICE T_BDADDR T_CONTROL_PSM T_INTERRUPT_PSM T_RECONNECT_INITIATE
90128080Semax%token T_BATTERY_POWER T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR
91128080Semax%token T_TRUE T_FALSE T_ERROR
92128080Semax
93128080Semax%%
94128080Semax
95128080Semaxconfig:		line
96128080Semax		| config line
97128080Semax		;
98128080Semax
99128080Semaxline:		T_DEVICE
100128080Semax			{
101128080Semax			hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device));
102128080Semax			if (hid_device == NULL) {
103128080Semax				SYSLOG(LOGCRIT, "Could not allocate new " \
104128080Semax						"config entry" EOL);
105128080Semax				YYABORT;
106128080Semax			}
107128080Semax
108128080Semax			hid_device->new_device = 1;
109128080Semax			}
110128080Semax		'{' options '}'
111128080Semax			{
112128080Semax			if (check_hid_device(hid_device))
113128080Semax				LIST_INSERT_HEAD(&hid_devices,hid_device,next);
114128080Semax			else
115128080Semax				free_hid_device(hid_device);
116128080Semax
117128080Semax			hid_device = NULL;
118128080Semax			}
119128080Semax		;
120128080Semax
121128080Semaxoptions:	option ';'
122128080Semax		| options option ';'
123128080Semax		;
124128080Semax
125128080Semaxoption:		bdaddr
126128080Semax		| control_psm
127128080Semax		| interrupt_psm
128128080Semax		| reconnect_initiate
129128080Semax		| battery_power
130128080Semax		| normally_connectable
131128080Semax		| hid_descriptor
132128080Semax		| parser_error
133128080Semax		;
134128080Semax
135128080Semaxbdaddr:		T_BDADDR T_BDADDRSTRING
136128080Semax			{
137128080Semax			memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr));
138128080Semax			}
139128080Semax		;
140128080Semax
141128080Semaxcontrol_psm:	T_CONTROL_PSM T_HEXBYTE
142128080Semax			{
143128080Semax			hid_device->control_psm = $2;
144128080Semax			}
145128080Semax		;
146128080Semax
147128080Semaxinterrupt_psm:	T_INTERRUPT_PSM T_HEXBYTE
148128080Semax			{
149128080Semax			hid_device->interrupt_psm = $2;
150128080Semax			}
151128080Semax		;
152128080Semax
153128080Semaxreconnect_initiate: T_RECONNECT_INITIATE T_TRUE
154128080Semax			{
155128080Semax			hid_device->reconnect_initiate = 1;
156128080Semax			}
157128080Semax		| T_RECONNECT_INITIATE T_FALSE
158128080Semax			{
159128080Semax			hid_device->reconnect_initiate = 0;
160128080Semax			}
161128080Semax		;
162128080Semax
163128080Semaxbattery_power:	T_BATTERY_POWER T_TRUE
164128080Semax			{
165128080Semax			hid_device->battery_power = 1;
166128080Semax			}
167128080Semax		| T_BATTERY_POWER T_FALSE
168128080Semax			{
169128080Semax			hid_device->battery_power = 0;
170128080Semax			}
171128080Semax		;
172128080Semax
173128080Semaxnormally_connectable: T_NORMALLY_CONNECTABLE T_TRUE
174128080Semax			{
175128080Semax			hid_device->normally_connectable = 1;
176128080Semax			}
177128080Semax		| T_NORMALLY_CONNECTABLE T_FALSE
178128080Semax			{
179128080Semax			hid_device->normally_connectable = 0;
180128080Semax			}
181128080Semax		;
182128080Semax
183128080Semaxhid_descriptor:	T_HID_DESCRIPTOR
184128080Semax			{
185128080Semax			hid_descriptor_size = 0;
186128080Semax			}
187128080Semax		'{' hid_descriptor_bytes '}'
188128080Semax			{
189128080Semax			if (hid_device->desc != NULL)
190128080Semax				hid_dispose_report_desc(hid_device->desc);
191128080Semax
192162529Semax			hid_device->desc = hid_use_report_desc((unsigned char *) buffer, hid_descriptor_size);
193128080Semax			if (hid_device->desc == NULL) {
194128080Semax				SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL);
195128080Semax				YYABORT;
196128080Semax			}
197128080Semax			}
198128080Semax		;
199128080Semax
200128080Semaxhid_descriptor_bytes: hid_descriptor_byte
201128080Semax		| hid_descriptor_bytes hid_descriptor_byte
202128080Semax		;
203128080Semax
204128080Semaxhid_descriptor_byte: T_HEXBYTE
205128080Semax			{
206162128Semax			if (hid_descriptor_size >= (int32_t) sizeof(buffer)) {
207128080Semax				SYSLOG(LOGCRIT, "HID descriptor is too big" EOL);
208128080Semax				YYABORT;
209128080Semax			}
210128080Semax
211128080Semax			buffer[hid_descriptor_size ++] = $1;
212128080Semax			}
213128080Semax		;
214128080Semax
215128080Semaxparser_error:	T_ERROR
216128080Semax			{
217128080Semax				YYABORT;
218128080Semax			}
219128080Semax
220128080Semax%%
221128080Semax
222128080Semax/* Display parser error message */
223128080Semaxvoid
224128080Semaxyyerror(char const *message)
225128080Semax{
226128080Semax	SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno);
227128080Semax}
228128080Semax
229128080Semax/* Re-read config file */
230162128Semaxint32_t
231128080Semaxread_config_file(void)
232128080Semax{
233162128Semax	int32_t	e;
234128080Semax
235128080Semax	if (config_file == NULL) {
236128080Semax		SYSLOG(LOGERR, "Unknown config file name!" EOL);
237128080Semax		return (-1);
238128080Semax	}
239128080Semax
240128080Semax	if ((yyin = fopen(config_file, "r")) == NULL) {
241128080Semax		SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL,
242128080Semax				config_file, strerror(errno), errno);
243128080Semax		return (-1);
244128080Semax	}
245128080Semax
246128080Semax	clean_config();
247128080Semax	if (yyparse() < 0) {
248128080Semax		SYSLOG(LOGERR, "Could not parse config file '%s'" EOL,
249128080Semax				config_file);
250128080Semax		e = -1;
251128080Semax	} else
252128080Semax		e = 0;
253128080Semax
254128080Semax	fclose(yyin);
255128080Semax	yyin = NULL;
256128080Semax
257128080Semax	return (e);
258128080Semax}
259128080Semax
260128080Semax/* Clean config */
261128080Semaxvoid
262128080Semaxclean_config(void)
263128080Semax{
264128080Semax	while (!LIST_EMPTY(&hid_devices)) {
265162128Semax		hid_device_p	d = LIST_FIRST(&hid_devices);
266128080Semax
267162128Semax		LIST_REMOVE(d, next);
268162128Semax		free_hid_device(d);
269128080Semax	}
270128080Semax}
271128080Semax
272128080Semax/* Lookup config entry */
273128080Semaxhid_device_p
274128080Semaxget_hid_device(bdaddr_p bdaddr)
275128080Semax{
276162128Semax	hid_device_p	d;
277128080Semax
278162128Semax	LIST_FOREACH(d, &hid_devices, next)
279162128Semax		if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
280128080Semax			break;
281128080Semax
282162128Semax	return (d);
283128080Semax}
284128080Semax
285128080Semax/* Get next config entry */
286128080Semaxhid_device_p
287128080Semaxget_next_hid_device(hid_device_p d)
288128080Semax{
289128080Semax	return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next));
290128080Semax}
291128080Semax
292128080Semax/* Print config entry */
293128080Semaxvoid
294162128Semaxprint_hid_device(hid_device_p d, FILE *f)
295128080Semax{
296128080Semax	/* XXX FIXME hack! */
297128080Semax	struct report_desc {
298128080Semax		unsigned int	size;
299128080Semax		unsigned char	data[1];
300128080Semax	};
301128080Semax	/* XXX FIXME hack! */
302128080Semax
303162128Semax	struct report_desc	*desc = (struct report_desc *) d->desc;
304162128Semax	uint32_t		 i;
305128080Semax
306128080Semax	fprintf(f,
307128080Semax"device {\n"					\
308128080Semax"	bdaddr			%s;\n"		\
309128080Semax"	control_psm		0x%x;\n"	\
310140130Semax"	interrupt_psm		0x%x;\n"	\
311128080Semax"	reconnect_initiate	%s;\n"		\
312128080Semax"	battery_power		%s;\n"		\
313128080Semax"	normally_connectable	%s;\n"		\
314128080Semax"	hid_descriptor		{",
315162128Semax		bt_ntoa(&d->bdaddr, NULL),
316162128Semax		d->control_psm, d->interrupt_psm,
317162128Semax                d->reconnect_initiate? "true" : "false",
318162128Semax                d->battery_power? "true" : "false",
319162128Semax                d->normally_connectable? "true" : "false");
320128080Semax
321128080Semax	for (i = 0; i < desc->size; i ++) {
322128080Semax			if ((i % 8) == 0)
323146357Semax				fprintf(f, "\n		");
324128080Semax
325128080Semax			fprintf(f, "0x%2.2x ", desc->data[i]);
326128080Semax	}
327128080Semax
328146357Semax	fprintf(f,
329128080Semax"\n"		\
330128080Semax"	};\n"	\
331128080Semax"}\n");
332128080Semax}
333128080Semax
334128080Semax/* Check config entry */
335162128Semaxstatic int32_t
336162128Semaxcheck_hid_device(hid_device_p d)
337128080Semax{
338162128Semax	hid_data_t	hd;
339162128Semax	hid_item_t	hi;
340162128Semax	int32_t		page;
341162128Semax
342162128Semax	if (get_hid_device(&d->bdaddr) != NULL) {
343128080Semax		SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL,
344162128Semax				bt_ntoa(&d->bdaddr, NULL));
345128080Semax		return (0);
346128080Semax	}
347128080Semax
348162128Semax	if (d->control_psm == 0) {
349128080Semax		SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL);
350128080Semax		return (0);
351128080Semax	}
352128080Semax
353162128Semax	if (d->interrupt_psm == 0) {
354128080Semax		SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL);
355128080Semax		return (0);
356128080Semax	}
357128080Semax
358162128Semax	if (d->desc == NULL) {
359128080Semax		SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL);
360128080Semax		return (0);
361128080Semax	}
362128080Semax
363162128Semax	/* XXX somehow need to make sure descriptor is valid */
364162128Semax	for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) {
365162128Semax		switch (hi.kind) {
366162128Semax		case hid_collection:
367162128Semax		case hid_endcollection:
368162128Semax		case hid_output:
369162128Semax		case hid_feature:
370162128Semax			break;
371162128Semax
372162128Semax		case hid_input:
373162128Semax			/* Check if the device may send keystrokes */
374162128Semax			page = HID_PAGE(hi.usage);
375162128Semax			if (page == HUP_KEYBOARD || page == HUP_CONSUMER)
376162128Semax				d->keyboard = 1;
377162128Semax			break;
378162128Semax		}
379162128Semax	}
380162128Semax	hid_end_parse(hd);
381162128Semax
382128080Semax	return (1);
383128080Semax}
384128080Semax
385128080Semax/* Free config entry */
386128080Semaxstatic void
387162128Semaxfree_hid_device(hid_device_p d)
388128080Semax{
389162128Semax	if (d->desc != NULL)
390162128Semax		hid_dispose_report_desc(d->desc);
391128080Semax
392162128Semax	memset(d, 0, sizeof(*d));
393162128Semax	free(d);
394128080Semax}
395128080Semax
396128080Semax/* Re-read hids file */
397162128Semaxint32_t
398128080Semaxread_hids_file(void)
399128080Semax{
400162128Semax	FILE		*f;
401162128Semax	hid_device_t	*d;
402162128Semax	char		*line;
403128080Semax	bdaddr_t	 bdaddr;
404162128Semax	int32_t		 lineno;
405128080Semax
406128080Semax	if (hids_file == NULL) {
407128080Semax		SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
408128080Semax		return (-1);
409128080Semax	}
410128080Semax
411128080Semax	if ((f = fopen(hids_file, "r")) == NULL) {
412128080Semax		if (errno == ENOENT)
413128080Semax			return (0);
414128080Semax
415128080Semax		SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
416128080Semax			hids_file, strerror(errno), errno);
417128080Semax		return (-1);
418128080Semax	}
419128080Semax
420128080Semax	for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) {
421128080Semax		if ((line = strtok(buffer, "\r\n\t ")) == NULL)
422128080Semax			continue; /* ignore empty lines */
423128080Semax
424128080Semax		if (!bt_aton(line, &bdaddr)) {
425128080Semax			SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \
426128080Semax				"%s:%d" EOL, hids_file, lineno);
427128080Semax			continue;
428128080Semax		}
429128080Semax
430162128Semax		if ((d = get_hid_device(&bdaddr)) != NULL)
431162128Semax			d->new_device = 0;
432128080Semax	}
433128080Semax
434128080Semax	fclose(f);
435128080Semax
436128080Semax	return (0);
437128080Semax}
438128080Semax
439128080Semax/* Write hids file */
440162128Semaxint32_t
441128080Semaxwrite_hids_file(void)
442128080Semax{
443128080Semax	char		 path[PATH_MAX];
444162128Semax	FILE		*f;
445162128Semax	hid_device_t	*d;
446128080Semax
447128080Semax	if (hids_file == NULL) {
448128080Semax		SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
449128080Semax		return (-1);
450128080Semax	}
451128080Semax
452128080Semax	snprintf(path, sizeof(path), "%s.new", hids_file);
453128080Semax
454128080Semax	if ((f = fopen(path, "w")) == NULL) {
455128080Semax		SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
456128080Semax			path, strerror(errno), errno);
457128080Semax		return (-1);
458128080Semax	}
459128080Semax
460162128Semax	LIST_FOREACH(d, &hid_devices, next)
461162128Semax		if (!d->new_device)
462162128Semax			fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL));
463128080Semax
464128080Semax	fclose(f);
465128080Semax
466128080Semax	if (rename(path, hids_file) < 0) {
467128080Semax		SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \
468128080Semax			"%s (%d)" EOL, path, hids_file, strerror(errno), errno);
469128080Semax		unlink(path);
470128080Semax		return (-1);
471128080Semax	}
472128080Semax
473128080Semax	return (0);
474128080Semax}
475128080Semax
476