parser.y revision 235789
134192Sjdp%{
234192Sjdp/*
334192Sjdp * parser.y
434192Sjdp */
534192Sjdp
634192Sjdp/*-
734192Sjdp * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
834192Sjdp * All rights reserved.
934192Sjdp *
1034192Sjdp * Redistribution and use in source and binary forms, with or without
1134192Sjdp * modification, are permitted provided that the following conditions
1234192Sjdp * are met:
13262435Sbrueffer * 1. Redistributions of source code must retain the above copyright
1434192Sjdp *    notice, this list of conditions and the following disclaimer.
1534192Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1634192Sjdp *    notice, this list of conditions and the following disclaimer in the
1734192Sjdp *    documentation and/or other materials provided with the distribution.
1834192Sjdp *
1934192Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2034192Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2134192Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2234192Sjdp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2334192Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2434192Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2534192Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2634192Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2734192Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2834192Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2934192Sjdp * SUCH DAMAGE.
3034192Sjdp *
3134192Sjdp * $Id: parser.y,v 1.7 2006/09/07 21:06:53 max Exp $
3250476Speter * $FreeBSD: head/usr.sbin/bluetooth/bthidd/parser.y 235789 2012-05-22 16:33:10Z bapt $
3334192Sjdp */
3434192Sjdp
3534192Sjdp#include <sys/queue.h>
3634192Sjdp#include <bluetooth.h>
3734192Sjdp#include <dev/usb/usb.h>
3834192Sjdp#include <dev/usb/usbhid.h>
3934192Sjdp#include <errno.h>
4034192Sjdp#include <limits.h>
4134192Sjdp#include <stdio.h>
4234192Sjdp#include <stdlib.h>
4334192Sjdp#include <string.h>
4434192Sjdp#include <unistd.h>
4534192Sjdp#include <usbhid.h>
4634192Sjdp
47211413Skib#ifndef BTHIDCONTROL
4869793Sobrien#include <stdarg.h>
49110803Skan#include <syslog.h>
50119255Simp#define	SYSLOG		syslog
51110803Skan#define	LOGCRIT		LOG_CRIT
5234192Sjdp#define	LOGERR		LOG_ERR
5334192Sjdp#define	LOGWARNING	LOG_WARNING
5434192Sjdp#define	EOL
5534192Sjdp#else
5634192Sjdp#define	SYSLOG		fprintf
57225152Skib#define	LOGCRIT		stderr
5834192Sjdp#define	LOGERR		stderr
5934192Sjdp#define	LOGWARNING	stderr
6034192Sjdp#define	EOL	"\n"
6134192Sjdp#endif /* ndef BTHIDCONTROL */
6234192Sjdp
6334192Sjdp#include "bthid_config.h"
6434192Sjdp
6534192Sjdp	int	yylex		(void);
6634192Sjdp	void	yyerror		(char const *);
6734192Sjdpstatic	int32_t	check_hid_device(hid_device_p hid_device);
6834192Sjdpstatic	void	free_hid_device	(hid_device_p hid_device);
6934192Sjdp
7034192Sjdpextern	FILE			*yyin;
7134192Sjdpextern	int			 yylineno;
7234192Sjdp	char const		*config_file = BTHIDD_CONFFILE;
7334192Sjdp	char const		*hids_file   = BTHIDD_HIDSFILE;
7434192Sjdp
7534192Sjdpstatic	char			 buffer[1024];
7634192Sjdpstatic	int32_t			 hid_descriptor_size;
7734192Sjdpstatic	hid_device_t		*hid_device = NULL;
7834192Sjdpstatic	LIST_HEAD(, hid_device)	 hid_devices;
7934192Sjdp
8034192Sjdp%}
8134192Sjdp
8234192Sjdp%union {
8334192Sjdp	bdaddr_t	bdaddr;
8434192Sjdp	int32_t		num;
8534192Sjdp}
8634192Sjdp
8734192Sjdp%token <bdaddr> T_BDADDRSTRING
8834192Sjdp%token <num>	T_HEXBYTE
8934192Sjdp%token T_DEVICE T_BDADDR T_CONTROL_PSM T_INTERRUPT_PSM T_RECONNECT_INITIATE
9034192Sjdp%token T_BATTERY_POWER T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR
9134192Sjdp%token T_TRUE T_FALSE T_ERROR
9234192Sjdp
9334192Sjdp%%
9434192Sjdp
9534192Sjdpconfig:		line
9634192Sjdp		| config line
9734192Sjdp		;
9834192Sjdp
9934192Sjdpline:		T_DEVICE
10034192Sjdp			{
10134192Sjdp			hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device));
10234192Sjdp			if (hid_device == NULL) {
10334192Sjdp				SYSLOG(LOGCRIT, "Could not allocate new " \
10434192Sjdp						"config entry" EOL);
10534192Sjdp				YYABORT;
10634192Sjdp			}
10734192Sjdp
10834192Sjdp			hid_device->new_device = 1;
10934192Sjdp			}
11034192Sjdp		'{' options '}'
11134192Sjdp			{
11234192Sjdp			if (check_hid_device(hid_device))
11334192Sjdp				LIST_INSERT_HEAD(&hid_devices,hid_device,next);
11434192Sjdp			else
11534192Sjdp				free_hid_device(hid_device);
11634192Sjdp
11734192Sjdp			hid_device = NULL;
11834192Sjdp			}
11934192Sjdp		;
12034192Sjdp
12134192Sjdpoptions:	option ';'
12234192Sjdp		| options option ';'
12334192Sjdp		;
12434192Sjdp
12534192Sjdpoption:		bdaddr
12634192Sjdp		| control_psm
12734192Sjdp		| interrupt_psm
12834192Sjdp		| reconnect_initiate
12934192Sjdp		| battery_power
13034192Sjdp		| normally_connectable
13134192Sjdp		| hid_descriptor
13234192Sjdp		| parser_error
13334192Sjdp		;
13434192Sjdp
13534192Sjdpbdaddr:		T_BDADDR T_BDADDRSTRING
13634192Sjdp			{
13734192Sjdp			memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr));
13834192Sjdp			}
13934192Sjdp		;
140225152Skib
14134192Sjdpcontrol_psm:	T_CONTROL_PSM T_HEXBYTE
142211413Skib			{
143211413Skib			hid_device->control_psm = $2;
144211413Skib			}
145211413Skib		;
146211413Skib
147211413Skibinterrupt_psm:	T_INTERRUPT_PSM T_HEXBYTE
148211413Skib			{
149211413Skib			hid_device->interrupt_psm = $2;
150211413Skib			}
151211413Skib		;
152211413Skib
153211413Skibreconnect_initiate: T_RECONNECT_INITIATE T_TRUE
154211413Skib			{
155211413Skib			hid_device->reconnect_initiate = 1;
156211413Skib			}
157211413Skib		| T_RECONNECT_INITIATE T_FALSE
158211413Skib			{
159211413Skib			hid_device->reconnect_initiate = 0;
160211413Skib			}
161211413Skib		;
16234192Sjdp
16334192Sjdpbattery_power:	T_BATTERY_POWER T_TRUE
16434192Sjdp			{
16534192Sjdp			hid_device->battery_power = 1;
16634192Sjdp			}
16738816Sdfr		| T_BATTERY_POWER T_FALSE
16838816Sdfr			{
16934192Sjdp			hid_device->battery_power = 0;
17034192Sjdp			}
17134192Sjdp		;
17234192Sjdp
17334192Sjdpnormally_connectable: T_NORMALLY_CONNECTABLE T_TRUE
17434192Sjdp			{
17534192Sjdp			hid_device->normally_connectable = 1;
176211413Skib			}
17734192Sjdp		| T_NORMALLY_CONNECTABLE T_FALSE
17834192Sjdp			{
17934192Sjdp			hid_device->normally_connectable = 0;
18038816Sdfr			}
18134192Sjdp		;
18234192Sjdp
18334192Sjdphid_descriptor:	T_HID_DESCRIPTOR
18434192Sjdp			{
18534192Sjdp			hid_descriptor_size = 0;
18634192Sjdp			}
18734192Sjdp		'{' hid_descriptor_bytes '}'
188114625Sobrien			{
18934192Sjdp			if (hid_device->desc != NULL)
19034192Sjdp				hid_dispose_report_desc(hid_device->desc);
19134192Sjdp
19234192Sjdp			hid_device->desc = hid_use_report_desc((unsigned char *) buffer, hid_descriptor_size);
19334192Sjdp			if (hid_device->desc == NULL) {
19434192Sjdp				SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL);
19534192Sjdp				YYABORT;
19634192Sjdp			}
19734192Sjdp			}
19834192Sjdp		;
199114625Sobrien
20034192Sjdphid_descriptor_bytes: hid_descriptor_byte
20134192Sjdp		| hid_descriptor_bytes hid_descriptor_byte
20234192Sjdp		;
20334192Sjdp
20434192Sjdphid_descriptor_byte: T_HEXBYTE
20534192Sjdp			{
20634192Sjdp			if (hid_descriptor_size >= (int32_t) sizeof(buffer)) {
20734192Sjdp				SYSLOG(LOGCRIT, "HID descriptor is too big" EOL);
20834192Sjdp				YYABORT;
20934192Sjdp			}
21034192Sjdp
21134192Sjdp			buffer[hid_descriptor_size ++] = $1;
21234192Sjdp			}
21334192Sjdp		;
21434192Sjdp
21534192Sjdpparser_error:	T_ERROR
21634192Sjdp			{
21734192Sjdp				YYABORT;
21834192Sjdp			}
21934192Sjdp
22034192Sjdp%%
22134192Sjdp
22234192Sjdp/* Display parser error message */
22334192Sjdpvoid
22434192Sjdpyyerror(char const *message)
22534192Sjdp{
22634192Sjdp	SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno);
22734192Sjdp}
22834192Sjdp
22934192Sjdp/* Re-read config file */
23034192Sjdpint32_t
23134192Sjdpread_config_file(void)
23234192Sjdp{
23334192Sjdp	int32_t	e;
23434192Sjdp
23534192Sjdp	if (config_file == NULL) {
23634192Sjdp		SYSLOG(LOGERR, "Unknown config file name!" EOL);
23734192Sjdp		return (-1);
23834192Sjdp	}
23934192Sjdp
24034192Sjdp	if ((yyin = fopen(config_file, "r")) == NULL) {
24134192Sjdp		SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL,
24234192Sjdp				config_file, strerror(errno), errno);
24334192Sjdp		return (-1);
24434192Sjdp	}
24534192Sjdp
246154248Sjasone	clean_config();
247154248Sjasone	if (yyparse() < 0) {
248154248Sjasone		SYSLOG(LOGERR, "Could not parse config file '%s'" EOL,
249154248Sjasone				config_file);
250154248Sjasone		e = -1;
251154248Sjasone	} else
252154248Sjasone		e = 0;
253154248Sjasone
254154248Sjasone	fclose(yyin);
255154248Sjasone	yyin = NULL;
256154248Sjasone
257154248Sjasone	return (e);
258154248Sjasone}
259154248Sjasone
260154248Sjasone/* Clean config */
261154248Sjasonevoid
26234192Sjdpclean_config(void)
26334192Sjdp{
26434192Sjdp	while (!LIST_EMPTY(&hid_devices)) {
26534192Sjdp		hid_device_p	d = LIST_FIRST(&hid_devices);
26634192Sjdp
26734192Sjdp		LIST_REMOVE(d, next);
26834192Sjdp		free_hid_device(d);
26934192Sjdp	}
27034192Sjdp}
27134192Sjdp
27234192Sjdp/* Lookup config entry */
27334192Sjdphid_device_p
27434192Sjdpget_hid_device(bdaddr_p bdaddr)
27534192Sjdp{
27634192Sjdp	hid_device_p	d;
27734192Sjdp
27834192Sjdp	LIST_FOREACH(d, &hid_devices, next)
27934192Sjdp		if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
28034192Sjdp			break;
28134192Sjdp
28234192Sjdp	return (d);
28334192Sjdp}
28434192Sjdp
28534192Sjdp/* Get next config entry */
28634192Sjdphid_device_p
28734192Sjdpget_next_hid_device(hid_device_p d)
28834192Sjdp{
28934192Sjdp	return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next));
29034192Sjdp}
29134192Sjdp
29234192Sjdp/* Print config entry */
29334192Sjdpvoid
29434192Sjdpprint_hid_device(hid_device_p d, FILE *f)
29534192Sjdp{
29634192Sjdp	/* XXX FIXME hack! */
29734192Sjdp	struct report_desc {
29834192Sjdp		unsigned int	size;
29934192Sjdp		unsigned char	data[1];
30034192Sjdp	};
30134192Sjdp	/* XXX FIXME hack! */
30234192Sjdp
30334192Sjdp	struct report_desc	*desc = (struct report_desc *) d->desc;
30434192Sjdp	uint32_t		 i;
30534192Sjdp
30634192Sjdp	fprintf(f,
30734192Sjdp"device {\n"					\
30834192Sjdp"	bdaddr			%s;\n"		\
30934192Sjdp"	control_psm		0x%x;\n"	\
31034192Sjdp"	interrupt_psm		0x%x;\n"	\
31134192Sjdp"	reconnect_initiate	%s;\n"		\
31234192Sjdp"	battery_power		%s;\n"		\
31334192Sjdp"	normally_connectable	%s;\n"		\
31434192Sjdp"	hid_descriptor		{",
31534192Sjdp		bt_ntoa(&d->bdaddr, NULL),
31634192Sjdp		d->control_psm, d->interrupt_psm,
31734192Sjdp                d->reconnect_initiate? "true" : "false",
31834192Sjdp                d->battery_power? "true" : "false",
31934192Sjdp                d->normally_connectable? "true" : "false");
32034192Sjdp
32134192Sjdp	for (i = 0; i < desc->size; i ++) {
32234192Sjdp			if ((i % 8) == 0)
32334192Sjdp				fprintf(f, "\n		");
32434192Sjdp
32534192Sjdp			fprintf(f, "0x%2.2x ", desc->data[i]);
32634192Sjdp	}
32734192Sjdp
32834192Sjdp	fprintf(f,
32934192Sjdp"\n"		\
33034192Sjdp"	};\n"	\
33134192Sjdp"}\n");
33234192Sjdp}
33334192Sjdp
33434192Sjdp/* Check config entry */
33534192Sjdpstatic int32_t
33634192Sjdpcheck_hid_device(hid_device_p d)
33734192Sjdp{
33834192Sjdp	hid_data_t	hd;
33934192Sjdp	hid_item_t	hi;
34034192Sjdp	int32_t		page;
34134192Sjdp
34234192Sjdp	if (get_hid_device(&d->bdaddr) != NULL) {
34334192Sjdp		SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL,
34434192Sjdp				bt_ntoa(&d->bdaddr, NULL));
34534192Sjdp		return (0);
34634192Sjdp	}
34734192Sjdp
34834192Sjdp	if (d->control_psm == 0) {
34934192Sjdp		SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL);
35034192Sjdp		return (0);
35134192Sjdp	}
35234192Sjdp
35334192Sjdp	if (d->interrupt_psm == 0) {
35434192Sjdp		SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL);
35534192Sjdp		return (0);
35634192Sjdp	}
35734192Sjdp
35834192Sjdp	if (d->desc == NULL) {
35934192Sjdp		SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL);
36034192Sjdp		return (0);
36134192Sjdp	}
36234192Sjdp
36334192Sjdp	/* XXX somehow need to make sure descriptor is valid */
36434192Sjdp	for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) {
36534192Sjdp		switch (hi.kind) {
36634192Sjdp		case hid_collection:
36734192Sjdp		case hid_endcollection:
36834192Sjdp		case hid_output:
36934192Sjdp		case hid_feature:
37034192Sjdp			break;
37134192Sjdp
37234192Sjdp		case hid_input:
37334192Sjdp			/* Check if the device may send keystrokes */
37434192Sjdp			page = HID_PAGE(hi.usage);
37534192Sjdp			if (page == HUP_KEYBOARD)
37634192Sjdp				d->keyboard = 1;
37734192Sjdp			break;
37834192Sjdp		}
37934192Sjdp	}
38034192Sjdp	hid_end_parse(hd);
38134192Sjdp
38234192Sjdp	return (1);
38334192Sjdp}
38434192Sjdp
38534192Sjdp/* Free config entry */
38634192Sjdpstatic void
38734192Sjdpfree_hid_device(hid_device_p d)
388114625Sobrien{
38934192Sjdp	if (d->desc != NULL)
39034192Sjdp		hid_dispose_report_desc(d->desc);
39134192Sjdp
39234192Sjdp	memset(d, 0, sizeof(*d));
39334192Sjdp	free(d);
39434192Sjdp}
39534192Sjdp
39634192Sjdp/* Re-read hids file */
39734192Sjdpint32_t
39834192Sjdpread_hids_file(void)
39934192Sjdp{
40034192Sjdp	FILE		*f;
401114625Sobrien	hid_device_t	*d;
40234192Sjdp	char		*line;
40334192Sjdp	bdaddr_t	 bdaddr;
40434192Sjdp	int32_t		 lineno;
40534192Sjdp
40634192Sjdp	if (hids_file == NULL) {
40734192Sjdp		SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
40834192Sjdp		return (-1);
40934192Sjdp	}
41034192Sjdp
41134192Sjdp	if ((f = fopen(hids_file, "r")) == NULL) {
41234192Sjdp		if (errno == ENOENT)
41334192Sjdp			return (0);
41434192Sjdp
41534192Sjdp		SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
41634192Sjdp			hids_file, strerror(errno), errno);
41734192Sjdp		return (-1);
41834192Sjdp	}
41934192Sjdp
42034192Sjdp	for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) {
42134192Sjdp		if ((line = strtok(buffer, "\r\n\t ")) == NULL)
42234192Sjdp			continue; /* ignore empty lines */
42334192Sjdp
42434192Sjdp		if (!bt_aton(line, &bdaddr)) {
42534192Sjdp			SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \
42634192Sjdp				"%s:%d" EOL, hids_file, lineno);
42734192Sjdp			continue;
42834192Sjdp		}
42934192Sjdp
43034192Sjdp		if ((d = get_hid_device(&bdaddr)) != NULL)
43134192Sjdp			d->new_device = 0;
43234192Sjdp	}
43334192Sjdp
43434192Sjdp	fclose(f);
43534192Sjdp
43634192Sjdp	return (0);
43734192Sjdp}
43834192Sjdp
43934192Sjdp/* Write hids file */
44034192Sjdpint32_t
44134192Sjdpwrite_hids_file(void)
44234192Sjdp{
44334192Sjdp	char		 path[PATH_MAX];
44434192Sjdp	FILE		*f;
44534192Sjdp	hid_device_t	*d;
44634192Sjdp
44734192Sjdp	if (hids_file == NULL) {
44834192Sjdp		SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
44934192Sjdp		return (-1);
45034192Sjdp	}
45134192Sjdp
45234192Sjdp	snprintf(path, sizeof(path), "%s.new", hids_file);
45334192Sjdp
45434192Sjdp	if ((f = fopen(path, "w")) == NULL) {
45534192Sjdp		SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
45634192Sjdp			path, strerror(errno), errno);
45734192Sjdp		return (-1);
45834192Sjdp	}
45934192Sjdp
46034192Sjdp	LIST_FOREACH(d, &hid_devices, next)
46134192Sjdp		if (!d->new_device)
46234192Sjdp			fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL));
46334192Sjdp
46434192Sjdp	fclose(f);
46534192Sjdp
46634192Sjdp	if (rename(path, hids_file) < 0) {
46734192Sjdp		SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \
46834192Sjdp			"%s (%d)" EOL, path, hids_file, strerror(errno), errno);
46934192Sjdp		unlink(path);
47034192Sjdp		return (-1);
47134192Sjdp	}
47234192Sjdp
47334192Sjdp	return (0);
47434192Sjdp}
47534192Sjdp
47634192Sjdp