parser.y revision 330449
11573Srgrimes%{
23070Spst/*
31573Srgrimes * parser.y
41573Srgrimes */
51573Srgrimes
61573Srgrimes/*-
71573Srgrimes * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
81573Srgrimes *
91573Srgrimes * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
101573Srgrimes * All rights reserved.
111573Srgrimes *
121573Srgrimes * Redistribution and use in source and binary forms, with or without
133070Spst * modification, are permitted provided that the following conditions
141573Srgrimes * are met:
151573Srgrimes * 1. Redistributions of source code must retain the above copyright
161573Srgrimes *    notice, this list of conditions and the following disclaimer.
171573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
181573Srgrimes *    notice, this list of conditions and the following disclaimer in the
191573Srgrimes *    documentation and/or other materials provided with the distribution.
201573Srgrimes *
211573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
221573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
251573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2692986Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2792986Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29113977Snectar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30145633Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311573Srgrimes * SUCH DAMAGE.
321573Srgrimes *
331573Srgrimes * $Id: parser.y,v 1.7 2006/09/07 21:06:53 max Exp $
341573Srgrimes * $FreeBSD: stable/11/usr.sbin/bluetooth/bthidd/parser.y 330449 2018-03-05 07:26:05Z eadler $
351573Srgrimes */
361573Srgrimes
371573Srgrimes#include <sys/queue.h>
38145512Sume#define L2CAP_SOCKET_CHECKED
39145633Sume#include <bluetooth.h>
401573Srgrimes#include <dev/usb/usb.h>
4165532Snectar#include <dev/usb/usbhid.h>
4265532Snectar#include <errno.h>
4317903Speter#include <limits.h>
4417903Speter#include <stdio.h>
45113977Snectar#include <stdlib.h>
46145602Sume#include <string.h>
47158115Sume#include <unistd.h>
48158115Sume#include <usbhid.h>
49158115Sume
501573Srgrimes#ifndef BTHIDCONTROL
5165532Snectar#include <stdarg.h>
5265532Snectar#include <syslog.h>
5365532Snectar#define	SYSLOG		syslog
5465532Snectar#define	LOGCRIT		LOG_CRIT
5565532Snectar#define	LOGERR		LOG_ERR
5665532Snectar#define	LOGWARNING	LOG_WARNING
571991Swollman#define	EOL
58157779Sume#else
59157779Sume#define	SYSLOG		fprintf
60145512Sume#define	LOGCRIT		stderr
6165532Snectar#define	LOGERR		stderr
62145512Sume#define	LOGWARNING	stderr
6365532Snectar#define	EOL	"\n"
6465532Snectar#endif /* ndef BTHIDCONTROL */
6565532Snectar
661991Swollman#include "bthid_config.h"
67158115Sume
68158115Sume	int	yylex		(void);
69158115Sume	void	yyerror		(char const *);
70158115Sumestatic	int32_t	check_hid_device(hid_device_p hid_device);
71158115Sumestatic	void	free_hid_device	(hid_device_p hid_device);
721991Swollman
73157779Sumeextern	FILE			*yyin;
74157779Sumeextern	int			 yylineno;
75157779Sume	char const		*config_file = BTHIDD_CONFFILE;
76145633Sume	char const		*hids_file   = BTHIDD_HIDSFILE;
77145633Sume
78157779Sumestatic	char			 buffer[1024];
791991Swollmanstatic	int32_t			 hid_descriptor_size;
80157779Sumestatic	hid_device_t		*hid_device = NULL;
81157779Sumestatic	LIST_HEAD(, hid_device)	 hid_devices;
8217903Speter
83157779Sume%}
84157779Sume
85157779Sume%union {
86157779Sume	bdaddr_t	bdaddr;
87157779Sume	int32_t		num;
88157779Sume}
89145633Sume
90157779Sume%token <bdaddr> T_BDADDRSTRING
91157779Sume%token <num>	T_HEXBYTE
92157779Sume%token T_DEVICE T_BDADDR T_CONTROL_PSM T_INTERRUPT_PSM T_RECONNECT_INITIATE
93145633Sume%token T_BATTERY_POWER T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR
94145633Sume%token T_TRUE T_FALSE T_ERROR
95145633Sume
96157779Sume%%
97145633Sume
98157779Sumeconfig:		line
99145633Sume		| config line
100145633Sume		;
101157779Sume
102157779Sumeline:		T_DEVICE
103157779Sume			{
104145633Sume			hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device));
105157779Sume			if (hid_device == NULL) {
106157779Sume				SYSLOG(LOGCRIT, "Could not allocate new " \
107157779Sume						"config entry" EOL);
108157779Sume				YYABORT;
109145633Sume			}
110157779Sume
111157779Sume			hid_device->new_device = 1;
112157779Sume			}
113157779Sume		'{' options '}'
114157779Sume			{
115157779Sume			if (check_hid_device(hid_device))
116157779Sume				LIST_INSERT_HEAD(&hid_devices,hid_device,next);
117157779Sume			else
118157779Sume				free_hid_device(hid_device);
119157779Sume
120157779Sume			hid_device = NULL;
121157779Sume			}
122157779Sume		;
123157779Sume
124157779Sumeoptions:	option ';'
125157779Sume		| options option ';'
126157779Sume		;
127157779Sume
128157779Sumeoption:		bdaddr
129157779Sume		| control_psm
130157779Sume		| interrupt_psm
131157779Sume		| reconnect_initiate
132157779Sume		| battery_power
133157779Sume		| normally_connectable
134157779Sume		| hid_descriptor
135157779Sume		| parser_error
136157779Sume		;
137157779Sume
138157779Sumebdaddr:		T_BDADDR T_BDADDRSTRING
139157779Sume			{
140157779Sume			memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr));
141157779Sume			}
142157779Sume		;
143157779Sume
144157779Sumecontrol_psm:	T_CONTROL_PSM T_HEXBYTE
145157779Sume			{
146157779Sume			hid_device->control_psm = $2;
147157779Sume			}
148157779Sume		;
149157779Sume
150157779Sumeinterrupt_psm:	T_INTERRUPT_PSM T_HEXBYTE
151157779Sume			{
152157779Sume			hid_device->interrupt_psm = $2;
153157779Sume			}
154157779Sume		;
155157779Sume
156157779Sumereconnect_initiate: T_RECONNECT_INITIATE T_TRUE
157157779Sume			{
158157779Sume			hid_device->reconnect_initiate = 1;
159157779Sume			}
160157779Sume		| T_RECONNECT_INITIATE T_FALSE
161145633Sume			{
162145633Sume			hid_device->reconnect_initiate = 0;
163158115Sume			}
164157779Sume		;
165158115Sume
166158115Sumebattery_power:	T_BATTERY_POWER T_TRUE
167158115Sume			{
168158115Sume			hid_device->battery_power = 1;
169158115Sume			}
170158115Sume		| T_BATTERY_POWER T_FALSE
171158115Sume			{
172158791Sume			hid_device->battery_power = 0;
173158791Sume			}
174158791Sume		;
175158115Sume
176158115Sumenormally_connectable: T_NORMALLY_CONNECTABLE T_TRUE
177158115Sume			{
178158115Sume			hid_device->normally_connectable = 1;
179158115Sume			}
180158115Sume		| T_NORMALLY_CONNECTABLE T_FALSE
181158115Sume			{
182158115Sume			hid_device->normally_connectable = 0;
183158115Sume			}
184158115Sume		;
185158115Sume
186158115Sumehid_descriptor:	T_HID_DESCRIPTOR
187158115Sume			{
188158115Sume			hid_descriptor_size = 0;
189158115Sume			}
190158115Sume		'{' hid_descriptor_bytes '}'
191158115Sume			{
192158115Sume			if (hid_device->desc != NULL)
193158115Sume				hid_dispose_report_desc(hid_device->desc);
194158115Sume
195158115Sume			hid_device->desc = hid_use_report_desc((unsigned char *) buffer, hid_descriptor_size);
196158115Sume			if (hid_device->desc == NULL) {
197158115Sume				SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL);
198158115Sume				YYABORT;
199158115Sume			}
200158115Sume			}
201158115Sume		;
202158115Sume
203158115Sumehid_descriptor_bytes: hid_descriptor_byte
204158115Sume		| hid_descriptor_bytes hid_descriptor_byte
205158115Sume		;
206158115Sume
207158115Sumehid_descriptor_byte: T_HEXBYTE
208158115Sume			{
209158115Sume			if (hid_descriptor_size >= (int32_t) sizeof(buffer)) {
210158115Sume				SYSLOG(LOGCRIT, "HID descriptor is too big" EOL);
211158115Sume				YYABORT;
212158115Sume			}
213158115Sume
214158115Sume			buffer[hid_descriptor_size ++] = $1;
215158115Sume			}
216158115Sume		;
217158115Sume
218158115Sumeparser_error:	T_ERROR
219158791Sume			{
220158791Sume				YYABORT;
221158115Sume			}
222158115Sume
223158115Sume%%
224158791Sume
225158791Sume/* Display parser error message */
226158115Sumevoid
227158115Sumeyyerror(char const *message)
228158115Sume{
229158115Sume	SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno);
230158115Sume}
231158115Sume
232158115Sume/* Re-read config file */
233158115Sumeint32_t
234158115Sumeread_config_file(void)
235158115Sume{
236158115Sume	int32_t	e;
237158115Sume
238158115Sume	if (config_file == NULL) {
239158115Sume		SYSLOG(LOGERR, "Unknown config file name!" EOL);
240158115Sume		return (-1);
241158115Sume	}
242158115Sume
243158115Sume	if ((yyin = fopen(config_file, "r")) == NULL) {
244158115Sume		SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL,
245158791Sume				config_file, strerror(errno), errno);
246158791Sume		return (-1);
247158115Sume	}
248158791Sume
249158115Sume	clean_config();
250158115Sume	if (yyparse() < 0) {
251158115Sume		SYSLOG(LOGERR, "Could not parse config file '%s'" EOL,
252158115Sume				config_file);
253158115Sume		e = -1;
254158115Sume	} else
255158115Sume		e = 0;
256158115Sume
257158115Sume	fclose(yyin);
258158115Sume	yyin = NULL;
259158115Sume
260158115Sume	return (e);
261158115Sume}
262158115Sume
263158115Sume/* Clean config */
264158115Sumevoid
265158115Sumeclean_config(void)
266158115Sume{
267158791Sume	while (!LIST_EMPTY(&hid_devices)) {
268158791Sume		hid_device_p	d = LIST_FIRST(&hid_devices);
269158791Sume
270158115Sume		LIST_REMOVE(d, next);
271158115Sume		free_hid_device(d);
272158115Sume	}
273158115Sume}
274158115Sume
275158115Sume/* Lookup config entry */
276158115Sumehid_device_p
277158115Sumeget_hid_device(bdaddr_p bdaddr)
278158115Sume{
279158115Sume	hid_device_p	d;
280158115Sume
281158115Sume	LIST_FOREACH(d, &hid_devices, next)
282158791Sume		if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
283158791Sume			break;
284158115Sume
285158115Sume	return (d);
286158115Sume}
287158115Sume
288158115Sume/* Get next config entry */
289158115Sumehid_device_p
290158115Sumeget_next_hid_device(hid_device_p d)
291158115Sume{
292158115Sume	return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next));
293158115Sume}
294158115Sume
295158115Sume/* Print config entry */
296158115Sumevoid
297158115Sumeprint_hid_device(hid_device_p d, FILE *f)
298158115Sume{
299158115Sume	/* XXX FIXME hack! */
300158115Sume	struct report_desc {
301158115Sume		unsigned int	size;
302158115Sume		unsigned char	data[1];
303158115Sume	};
304158115Sume	/* XXX FIXME hack! */
305158115Sume
306158115Sume	struct report_desc	*desc = (struct report_desc *) d->desc;
307158115Sume	uint32_t		 i;
308158115Sume
309158115Sume	fprintf(f,
310158115Sume"device {\n"					\
311158115Sume"	bdaddr			%s;\n"		\
312158115Sume"	control_psm		0x%x;\n"	\
313158115Sume"	interrupt_psm		0x%x;\n"	\
314158115Sume"	reconnect_initiate	%s;\n"		\
315158115Sume"	battery_power		%s;\n"		\
316158115Sume"	normally_connectable	%s;\n"		\
317158115Sume"	hid_descriptor		{",
318158115Sume		bt_ntoa(&d->bdaddr, NULL),
319158115Sume		d->control_psm, d->interrupt_psm,
320158115Sume                d->reconnect_initiate? "true" : "false",
321158115Sume                d->battery_power? "true" : "false",
322158115Sume                d->normally_connectable? "true" : "false");
323158115Sume
324158115Sume	for (i = 0; i < desc->size; i ++) {
325158115Sume			if ((i % 8) == 0)
326158115Sume				fprintf(f, "\n		");
327158115Sume
328158115Sume			fprintf(f, "0x%2.2x ", desc->data[i]);
329158115Sume	}
330158115Sume
331158115Sume	fprintf(f,
332158115Sume"\n"		\
333158115Sume"	};\n"	\
334158115Sume"}\n");
335158115Sume}
336158115Sume
337158115Sume/* Check config entry */
338158115Sumestatic int32_t
339158115Sumecheck_hid_device(hid_device_p d)
340158115Sume{
341158115Sume	hid_data_t	hd;
342158115Sume	hid_item_t	hi;
343158115Sume	int32_t		page;
344158115Sume
345158115Sume	if (get_hid_device(&d->bdaddr) != NULL) {
346158115Sume		SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL,
347158115Sume				bt_ntoa(&d->bdaddr, NULL));
348158115Sume		return (0);
349158115Sume	}
350158115Sume
351158115Sume	if (d->control_psm == 0) {
352158115Sume		SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL);
353158115Sume		return (0);
354158115Sume	}
355158115Sume
356158115Sume	if (d->interrupt_psm == 0) {
357158115Sume		SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL);
358158115Sume		return (0);
359158115Sume	}
360158115Sume
361158115Sume	if (d->desc == NULL) {
362158115Sume		SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL);
363158115Sume		return (0);
364158115Sume	}
365158115Sume
366158115Sume	/* XXX somehow need to make sure descriptor is valid */
367158115Sume	for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) {
368158115Sume		switch (hi.kind) {
369158115Sume		case hid_collection:
370158115Sume		case hid_endcollection:
371158115Sume		case hid_output:
372158115Sume		case hid_feature:
373158791Sume			break;
374158791Sume
375158791Sume		case hid_input:
376158115Sume			/* Check if the device may send keystrokes */
377158115Sume			page = HID_PAGE(hi.usage);
378158115Sume			if (page == HUP_KEYBOARD)
379158115Sume				d->keyboard = 1;
380158115Sume			break;
381158115Sume		}
382158115Sume	}
383158115Sume	hid_end_parse(hd);
384158115Sume
385158115Sume	return (1);
386158115Sume}
387158115Sume
388158115Sume/* Free config entry */
389158791Sumestatic void
390158791Sumefree_hid_device(hid_device_p d)
391158115Sume{
392158115Sume	if (d->desc != NULL)
393158115Sume		hid_dispose_report_desc(d->desc);
394158115Sume
395158115Sume	memset(d, 0, sizeof(*d));
396158115Sume	free(d);
397158115Sume}
398158115Sume
399158115Sume/* Re-read hids file */
400158115Sumeint32_t
401158115Sumeread_hids_file(void)
402158115Sume{
403158115Sume	FILE		*f;
404158115Sume	hid_device_t	*d;
405158115Sume	char		*line;
406158115Sume	bdaddr_t	 bdaddr;
407158115Sume	int32_t		 lineno;
408158115Sume
409158115Sume	if (hids_file == NULL) {
410158115Sume		SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
411158115Sume		return (-1);
412158115Sume	}
413158115Sume
414158115Sume	if ((f = fopen(hids_file, "r")) == NULL) {
415158115Sume		if (errno == ENOENT)
416158115Sume			return (0);
417158115Sume
418158115Sume		SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
419158115Sume			hids_file, strerror(errno), errno);
420158115Sume		return (-1);
421158115Sume	}
422158115Sume
423158115Sume	for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) {
424158115Sume		if ((line = strtok(buffer, "\r\n\t ")) == NULL)
425158115Sume			continue; /* ignore empty lines */
426158115Sume
427158115Sume		if (!bt_aton(line, &bdaddr)) {
428158115Sume			SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \
429158115Sume				"%s:%d" EOL, hids_file, lineno);
430158115Sume			continue;
431158115Sume		}
432158115Sume
433158115Sume		if ((d = get_hid_device(&bdaddr)) != NULL)
434158115Sume			d->new_device = 0;
435158115Sume	}
436158115Sume
437158115Sume	fclose(f);
438158115Sume
439157779Sume	return (0);
440158115Sume}
441157779Sume
442157779Sume/* Write hids file */
443157779Sumeint32_t
444157779Sumewrite_hids_file(void)
445157779Sume{
446157779Sume	char		 path[PATH_MAX];
447157779Sume	FILE		*f;
448157779Sume	hid_device_t	*d;
449157779Sume
450157779Sume	if (hids_file == NULL) {
451157779Sume		SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
452157779Sume		return (-1);
453157779Sume	}
454157779Sume
455157779Sume	snprintf(path, sizeof(path), "%s.new", hids_file);
456157779Sume
457157779Sume	if ((f = fopen(path, "w")) == NULL) {
458157779Sume		SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
459157779Sume			path, strerror(errno), errno);
460157779Sume		return (-1);
461157779Sume	}
462157779Sume
463157779Sume	LIST_FOREACH(d, &hid_devices, next)
464157779Sume		if (!d->new_device)
465157779Sume			fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL));
466157779Sume
467157779Sume	fclose(f);
468157779Sume
469157779Sume	if (rename(path, hids_file) < 0) {
470157779Sume		SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \
471157779Sume			"%s (%d)" EOL, path, hids_file, strerror(errno), errno);
472157779Sume		unlink(path);
473157779Sume		return (-1);
474157779Sume	}
475157779Sume
476157779Sume	return (0);
477157779Sume}
478157779Sume
479157779Sume