1128080Semax%{
2128080Semax/*
3128080Semax * parser.y
4162128Semax */
5162128Semax
6162128Semax/*-
7330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
8330449Seadler *
9162128Semax * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
10128080Semax * All rights reserved.
11128080Semax *
12128080Semax * Redistribution and use in source and binary forms, with or without
13128080Semax * modification, are permitted provided that the following conditions
14128080Semax * are met:
15128080Semax * 1. Redistributions of source code must retain the above copyright
16128080Semax *    notice, this list of conditions and the following disclaimer.
17128080Semax * 2. Redistributions in binary form must reproduce the above copyright
18128080Semax *    notice, this list of conditions and the following disclaimer in the
19128080Semax *    documentation and/or other materials provided with the distribution.
20128080Semax *
21128080Semax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22128080Semax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23128080Semax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24128080Semax * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
25128080Semax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26128080Semax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27128080Semax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28128080Semax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29128080Semax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30128080Semax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31128080Semax * SUCH DAMAGE.
32128080Semax *
33162128Semax * $Id: parser.y,v 1.7 2006/09/07 21:06:53 max Exp $
34128080Semax * $FreeBSD: stable/11/usr.sbin/bluetooth/bthidd/parser.y 330449 2018-03-05 07:26:05Z eadler $
35128080Semax */
36128080Semax
37128080Semax#include <sys/queue.h>
38281210Stakawata#define L2CAP_SOCKET_CHECKED
39128080Semax#include <bluetooth.h>
40162128Semax#include <dev/usb/usb.h>
41162128Semax#include <dev/usb/usbhid.h>
42128080Semax#include <errno.h>
43128080Semax#include <limits.h>
44128080Semax#include <stdio.h>
45235789Sbapt#include <stdlib.h>
46128080Semax#include <string.h>
47128080Semax#include <unistd.h>
48137868Semax#include <usbhid.h>
49128080Semax
50128080Semax#ifndef BTHIDCONTROL
51128080Semax#include <stdarg.h>
52128080Semax#include <syslog.h>
53128080Semax#define	SYSLOG		syslog
54128080Semax#define	LOGCRIT		LOG_CRIT
55128080Semax#define	LOGERR		LOG_ERR
56128080Semax#define	LOGWARNING	LOG_WARNING
57128080Semax#define	EOL
58128080Semax#else
59128080Semax#define	SYSLOG		fprintf
60128080Semax#define	LOGCRIT		stderr
61128080Semax#define	LOGERR		stderr
62128080Semax#define	LOGWARNING	stderr
63137868Semax#define	EOL	"\n"
64128080Semax#endif /* ndef BTHIDCONTROL */
65128080Semax
66128080Semax#include "bthid_config.h"
67128080Semax
68128080Semax	int	yylex		(void);
69162128Semax	void	yyerror		(char const *);
70162128Semaxstatic	int32_t	check_hid_device(hid_device_p hid_device);
71128080Semaxstatic	void	free_hid_device	(hid_device_p hid_device);
72128080Semax
73162128Semaxextern	FILE			*yyin;
74128080Semaxextern	int			 yylineno;
75162128Semax	char const		*config_file = BTHIDD_CONFFILE;
76162128Semax	char const		*hids_file   = BTHIDD_HIDSFILE;
77128080Semax
78128080Semaxstatic	char			 buffer[1024];
79162128Semaxstatic	int32_t			 hid_descriptor_size;
80128080Semaxstatic	hid_device_t		*hid_device = NULL;
81128080Semaxstatic	LIST_HEAD(, hid_device)	 hid_devices;
82128080Semax
83128080Semax%}
84128080Semax
85128080Semax%union {
86128080Semax	bdaddr_t	bdaddr;
87162128Semax	int32_t		num;
88128080Semax}
89128080Semax
90128080Semax%token <bdaddr> T_BDADDRSTRING
91128080Semax%token <num>	T_HEXBYTE
92128080Semax%token T_DEVICE T_BDADDR T_CONTROL_PSM T_INTERRUPT_PSM T_RECONNECT_INITIATE
93128080Semax%token T_BATTERY_POWER T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR
94128080Semax%token T_TRUE T_FALSE T_ERROR
95128080Semax
96128080Semax%%
97128080Semax
98128080Semaxconfig:		line
99128080Semax		| config line
100128080Semax		;
101128080Semax
102128080Semaxline:		T_DEVICE
103128080Semax			{
104128080Semax			hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device));
105128080Semax			if (hid_device == NULL) {
106128080Semax				SYSLOG(LOGCRIT, "Could not allocate new " \
107128080Semax						"config entry" EOL);
108128080Semax				YYABORT;
109128080Semax			}
110128080Semax
111128080Semax			hid_device->new_device = 1;
112128080Semax			}
113128080Semax		'{' options '}'
114128080Semax			{
115128080Semax			if (check_hid_device(hid_device))
116128080Semax				LIST_INSERT_HEAD(&hid_devices,hid_device,next);
117128080Semax			else
118128080Semax				free_hid_device(hid_device);
119128080Semax
120128080Semax			hid_device = NULL;
121128080Semax			}
122128080Semax		;
123128080Semax
124128080Semaxoptions:	option ';'
125128080Semax		| options option ';'
126128080Semax		;
127128080Semax
128128080Semaxoption:		bdaddr
129128080Semax		| control_psm
130128080Semax		| interrupt_psm
131128080Semax		| reconnect_initiate
132128080Semax		| battery_power
133128080Semax		| normally_connectable
134128080Semax		| hid_descriptor
135128080Semax		| parser_error
136128080Semax		;
137128080Semax
138128080Semaxbdaddr:		T_BDADDR T_BDADDRSTRING
139128080Semax			{
140128080Semax			memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr));
141128080Semax			}
142128080Semax		;
143128080Semax
144128080Semaxcontrol_psm:	T_CONTROL_PSM T_HEXBYTE
145128080Semax			{
146128080Semax			hid_device->control_psm = $2;
147128080Semax			}
148128080Semax		;
149128080Semax
150128080Semaxinterrupt_psm:	T_INTERRUPT_PSM T_HEXBYTE
151128080Semax			{
152128080Semax			hid_device->interrupt_psm = $2;
153128080Semax			}
154128080Semax		;
155128080Semax
156128080Semaxreconnect_initiate: T_RECONNECT_INITIATE T_TRUE
157128080Semax			{
158128080Semax			hid_device->reconnect_initiate = 1;
159128080Semax			}
160128080Semax		| T_RECONNECT_INITIATE T_FALSE
161128080Semax			{
162128080Semax			hid_device->reconnect_initiate = 0;
163128080Semax			}
164128080Semax		;
165128080Semax
166128080Semaxbattery_power:	T_BATTERY_POWER T_TRUE
167128080Semax			{
168128080Semax			hid_device->battery_power = 1;
169128080Semax			}
170128080Semax		| T_BATTERY_POWER T_FALSE
171128080Semax			{
172128080Semax			hid_device->battery_power = 0;
173128080Semax			}
174128080Semax		;
175128080Semax
176128080Semaxnormally_connectable: T_NORMALLY_CONNECTABLE T_TRUE
177128080Semax			{
178128080Semax			hid_device->normally_connectable = 1;
179128080Semax			}
180128080Semax		| T_NORMALLY_CONNECTABLE T_FALSE
181128080Semax			{
182128080Semax			hid_device->normally_connectable = 0;
183128080Semax			}
184128080Semax		;
185128080Semax
186128080Semaxhid_descriptor:	T_HID_DESCRIPTOR
187128080Semax			{
188128080Semax			hid_descriptor_size = 0;
189128080Semax			}
190128080Semax		'{' hid_descriptor_bytes '}'
191128080Semax			{
192128080Semax			if (hid_device->desc != NULL)
193128080Semax				hid_dispose_report_desc(hid_device->desc);
194128080Semax
195162529Semax			hid_device->desc = hid_use_report_desc((unsigned char *) buffer, hid_descriptor_size);
196128080Semax			if (hid_device->desc == NULL) {
197128080Semax				SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL);
198128080Semax				YYABORT;
199128080Semax			}
200128080Semax			}
201128080Semax		;
202128080Semax
203128080Semaxhid_descriptor_bytes: hid_descriptor_byte
204128080Semax		| hid_descriptor_bytes hid_descriptor_byte
205128080Semax		;
206128080Semax
207128080Semaxhid_descriptor_byte: T_HEXBYTE
208128080Semax			{
209162128Semax			if (hid_descriptor_size >= (int32_t) sizeof(buffer)) {
210128080Semax				SYSLOG(LOGCRIT, "HID descriptor is too big" EOL);
211128080Semax				YYABORT;
212128080Semax			}
213128080Semax
214128080Semax			buffer[hid_descriptor_size ++] = $1;
215128080Semax			}
216128080Semax		;
217128080Semax
218128080Semaxparser_error:	T_ERROR
219128080Semax			{
220128080Semax				YYABORT;
221128080Semax			}
222128080Semax
223128080Semax%%
224128080Semax
225128080Semax/* Display parser error message */
226128080Semaxvoid
227128080Semaxyyerror(char const *message)
228128080Semax{
229128080Semax	SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno);
230128080Semax}
231128080Semax
232128080Semax/* Re-read config file */
233162128Semaxint32_t
234128080Semaxread_config_file(void)
235128080Semax{
236162128Semax	int32_t	e;
237128080Semax
238128080Semax	if (config_file == NULL) {
239128080Semax		SYSLOG(LOGERR, "Unknown config file name!" EOL);
240128080Semax		return (-1);
241128080Semax	}
242128080Semax
243128080Semax	if ((yyin = fopen(config_file, "r")) == NULL) {
244128080Semax		SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL,
245128080Semax				config_file, strerror(errno), errno);
246128080Semax		return (-1);
247128080Semax	}
248128080Semax
249128080Semax	clean_config();
250128080Semax	if (yyparse() < 0) {
251128080Semax		SYSLOG(LOGERR, "Could not parse config file '%s'" EOL,
252128080Semax				config_file);
253128080Semax		e = -1;
254128080Semax	} else
255128080Semax		e = 0;
256128080Semax
257128080Semax	fclose(yyin);
258128080Semax	yyin = NULL;
259128080Semax
260128080Semax	return (e);
261128080Semax}
262128080Semax
263128080Semax/* Clean config */
264128080Semaxvoid
265128080Semaxclean_config(void)
266128080Semax{
267128080Semax	while (!LIST_EMPTY(&hid_devices)) {
268162128Semax		hid_device_p	d = LIST_FIRST(&hid_devices);
269128080Semax
270162128Semax		LIST_REMOVE(d, next);
271162128Semax		free_hid_device(d);
272128080Semax	}
273128080Semax}
274128080Semax
275128080Semax/* Lookup config entry */
276128080Semaxhid_device_p
277128080Semaxget_hid_device(bdaddr_p bdaddr)
278128080Semax{
279162128Semax	hid_device_p	d;
280128080Semax
281162128Semax	LIST_FOREACH(d, &hid_devices, next)
282162128Semax		if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
283128080Semax			break;
284128080Semax
285162128Semax	return (d);
286128080Semax}
287128080Semax
288128080Semax/* Get next config entry */
289128080Semaxhid_device_p
290128080Semaxget_next_hid_device(hid_device_p d)
291128080Semax{
292128080Semax	return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next));
293128080Semax}
294128080Semax
295128080Semax/* Print config entry */
296128080Semaxvoid
297162128Semaxprint_hid_device(hid_device_p d, FILE *f)
298128080Semax{
299128080Semax	/* XXX FIXME hack! */
300128080Semax	struct report_desc {
301128080Semax		unsigned int	size;
302128080Semax		unsigned char	data[1];
303128080Semax	};
304128080Semax	/* XXX FIXME hack! */
305128080Semax
306162128Semax	struct report_desc	*desc = (struct report_desc *) d->desc;
307162128Semax	uint32_t		 i;
308128080Semax
309128080Semax	fprintf(f,
310128080Semax"device {\n"					\
311128080Semax"	bdaddr			%s;\n"		\
312128080Semax"	control_psm		0x%x;\n"	\
313140130Semax"	interrupt_psm		0x%x;\n"	\
314128080Semax"	reconnect_initiate	%s;\n"		\
315128080Semax"	battery_power		%s;\n"		\
316128080Semax"	normally_connectable	%s;\n"		\
317128080Semax"	hid_descriptor		{",
318162128Semax		bt_ntoa(&d->bdaddr, NULL),
319162128Semax		d->control_psm, d->interrupt_psm,
320162128Semax                d->reconnect_initiate? "true" : "false",
321162128Semax                d->battery_power? "true" : "false",
322162128Semax                d->normally_connectable? "true" : "false");
323128080Semax
324128080Semax	for (i = 0; i < desc->size; i ++) {
325128080Semax			if ((i % 8) == 0)
326146357Semax				fprintf(f, "\n		");
327128080Semax
328128080Semax			fprintf(f, "0x%2.2x ", desc->data[i]);
329128080Semax	}
330128080Semax
331146357Semax	fprintf(f,
332128080Semax"\n"		\
333128080Semax"	};\n"	\
334128080Semax"}\n");
335128080Semax}
336128080Semax
337128080Semax/* Check config entry */
338162128Semaxstatic int32_t
339162128Semaxcheck_hid_device(hid_device_p d)
340128080Semax{
341162128Semax	hid_data_t	hd;
342162128Semax	hid_item_t	hi;
343162128Semax	int32_t		page;
344162128Semax
345162128Semax	if (get_hid_device(&d->bdaddr) != NULL) {
346128080Semax		SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL,
347162128Semax				bt_ntoa(&d->bdaddr, NULL));
348128080Semax		return (0);
349128080Semax	}
350128080Semax
351162128Semax	if (d->control_psm == 0) {
352128080Semax		SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL);
353128080Semax		return (0);
354128080Semax	}
355128080Semax
356162128Semax	if (d->interrupt_psm == 0) {
357128080Semax		SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL);
358128080Semax		return (0);
359128080Semax	}
360128080Semax
361162128Semax	if (d->desc == NULL) {
362128080Semax		SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL);
363128080Semax		return (0);
364128080Semax	}
365128080Semax
366162128Semax	/* XXX somehow need to make sure descriptor is valid */
367162128Semax	for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) {
368162128Semax		switch (hi.kind) {
369162128Semax		case hid_collection:
370162128Semax		case hid_endcollection:
371162128Semax		case hid_output:
372162128Semax		case hid_feature:
373162128Semax			break;
374162128Semax
375162128Semax		case hid_input:
376162128Semax			/* Check if the device may send keystrokes */
377162128Semax			page = HID_PAGE(hi.usage);
378164700Semax			if (page == HUP_KEYBOARD)
379162128Semax				d->keyboard = 1;
380162128Semax			break;
381162128Semax		}
382162128Semax	}
383162128Semax	hid_end_parse(hd);
384162128Semax
385128080Semax	return (1);
386128080Semax}
387128080Semax
388128080Semax/* Free config entry */
389128080Semaxstatic void
390162128Semaxfree_hid_device(hid_device_p d)
391128080Semax{
392162128Semax	if (d->desc != NULL)
393162128Semax		hid_dispose_report_desc(d->desc);
394128080Semax
395162128Semax	memset(d, 0, sizeof(*d));
396162128Semax	free(d);
397128080Semax}
398128080Semax
399128080Semax/* Re-read hids file */
400162128Semaxint32_t
401128080Semaxread_hids_file(void)
402128080Semax{
403162128Semax	FILE		*f;
404162128Semax	hid_device_t	*d;
405162128Semax	char		*line;
406128080Semax	bdaddr_t	 bdaddr;
407162128Semax	int32_t		 lineno;
408128080Semax
409128080Semax	if (hids_file == NULL) {
410128080Semax		SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
411128080Semax		return (-1);
412128080Semax	}
413128080Semax
414128080Semax	if ((f = fopen(hids_file, "r")) == NULL) {
415128080Semax		if (errno == ENOENT)
416128080Semax			return (0);
417128080Semax
418128080Semax		SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
419128080Semax			hids_file, strerror(errno), errno);
420128080Semax		return (-1);
421128080Semax	}
422128080Semax
423128080Semax	for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) {
424128080Semax		if ((line = strtok(buffer, "\r\n\t ")) == NULL)
425128080Semax			continue; /* ignore empty lines */
426128080Semax
427128080Semax		if (!bt_aton(line, &bdaddr)) {
428128080Semax			SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \
429128080Semax				"%s:%d" EOL, hids_file, lineno);
430128080Semax			continue;
431128080Semax		}
432128080Semax
433162128Semax		if ((d = get_hid_device(&bdaddr)) != NULL)
434162128Semax			d->new_device = 0;
435128080Semax	}
436128080Semax
437128080Semax	fclose(f);
438128080Semax
439128080Semax	return (0);
440128080Semax}
441128080Semax
442128080Semax/* Write hids file */
443162128Semaxint32_t
444128080Semaxwrite_hids_file(void)
445128080Semax{
446128080Semax	char		 path[PATH_MAX];
447162128Semax	FILE		*f;
448162128Semax	hid_device_t	*d;
449128080Semax
450128080Semax	if (hids_file == NULL) {
451128080Semax		SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
452128080Semax		return (-1);
453128080Semax	}
454128080Semax
455128080Semax	snprintf(path, sizeof(path), "%s.new", hids_file);
456128080Semax
457128080Semax	if ((f = fopen(path, "w")) == NULL) {
458128080Semax		SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
459128080Semax			path, strerror(errno), errno);
460128080Semax		return (-1);
461128080Semax	}
462128080Semax
463162128Semax	LIST_FOREACH(d, &hid_devices, next)
464162128Semax		if (!d->new_device)
465162128Semax			fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL));
466128080Semax
467128080Semax	fclose(f);
468128080Semax
469128080Semax	if (rename(path, hids_file) < 0) {
470128080Semax		SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \
471128080Semax			"%s (%d)" EOL, path, hids_file, strerror(errno), errno);
472128080Semax		unlink(path);
473128080Semax		return (-1);
474128080Semax	}
475128080Semax
476128080Semax	return (0);
477128080Semax}
478128080Semax
479