xref: /freebsd/usr.sbin/bluetooth/bthidd/parser.y (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
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$
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);
62 static	int	check_hid_device(hid_device_p hid_device);
63 static	void	free_hid_device	(hid_device_p hid_device);
64 
65 extern	int			 yylineno;
66 	char			*config_file = BTHIDD_CONFFILE;
67 	char			*hids_file   = BTHIDD_HIDSFILE;
68 
69 static	char			 buffer[1024];
70 static	int			 hid_descriptor_size;
71 static	hid_device_t		*hid_device = NULL;
72 static	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 
89 config:		line
90 		| config line
91 		;
92 
93 line:		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 
115 options:	option ';'
116 		| options option ';'
117 		;
118 
119 option:		bdaddr
120 		| control_psm
121 		| interrupt_psm
122 		| reconnect_initiate
123 		| battery_power
124 		| normally_connectable
125 		| hid_descriptor
126 		| parser_error
127 		;
128 
129 bdaddr:		T_BDADDR T_BDADDRSTRING
130 			{
131 			memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr));
132 			}
133 		;
134 
135 control_psm:	T_CONTROL_PSM T_HEXBYTE
136 			{
137 			hid_device->control_psm = $2;
138 			}
139 		;
140 
141 interrupt_psm:	T_INTERRUPT_PSM T_HEXBYTE
142 			{
143 			hid_device->interrupt_psm = $2;
144 			}
145 		;
146 
147 reconnect_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 
157 battery_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 
167 normally_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 
177 hid_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 
194 hid_descriptor_bytes: hid_descriptor_byte
195 		| hid_descriptor_bytes hid_descriptor_byte
196 		;
197 
198 hid_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 
209 parser_error:	T_ERROR
210 			{
211 				YYABORT;
212 			}
213 
214 %%
215 
216 /* Display parser error message */
217 void
218 yyerror(char const *message)
219 {
220 	SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno);
221 }
222 
223 /* Re-read config file */
224 int
225 read_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 */
256 void
257 clean_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 */
268 hid_device_p
269 get_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 */
281 hid_device_p
282 get_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 */
288 void
289 print_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 */
330 static int
331 check_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 */
358 static void
359 free_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 */
369 int
370 read_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 */
412 int
413 write_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