xref: /freebsd/usr.sbin/bluetooth/bthidd/parser.y (revision 6ae1554a5d9b318f8ad53ccc39fa5a961403da73)
1 %{
2 /*
3  * parser.y
4  */
5 
6 /*-
7  * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  * $Id: parser.y,v 1.7 2006/09/07 21:06:53 max Exp $
32  * $FreeBSD$
33  */
34 
35 #include <sys/queue.h>
36 #define L2CAP_SOCKET_CHECKED
37 #include <bluetooth.h>
38 #include <dev/usb/usb.h>
39 #include <dev/usb/usbhid.h>
40 #include <errno.h>
41 #include <limits.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <usbhid.h>
47 
48 #ifndef BTHIDCONTROL
49 #include <stdarg.h>
50 #include <syslog.h>
51 #define	SYSLOG		syslog
52 #define	LOGCRIT		LOG_CRIT
53 #define	LOGERR		LOG_ERR
54 #define	LOGWARNING	LOG_WARNING
55 #define	EOL
56 #else
57 #define	SYSLOG		fprintf
58 #define	LOGCRIT		stderr
59 #define	LOGERR		stderr
60 #define	LOGWARNING	stderr
61 #define	EOL	"\n"
62 #endif /* ndef BTHIDCONTROL */
63 
64 #include "bthid_config.h"
65 
66 	int	yylex		(void);
67 	void	yyerror		(char const *);
68 static	int32_t	check_hid_device(hid_device_p hid_device);
69 static	void	free_hid_device	(hid_device_p hid_device);
70 
71 extern	FILE			*yyin;
72 extern	int			 yylineno;
73 	char const		*config_file = BTHIDD_CONFFILE;
74 	char const		*hids_file   = BTHIDD_HIDSFILE;
75 
76 static	char			 buffer[1024];
77 static	int32_t			 hid_descriptor_size;
78 static	hid_device_t		*hid_device = NULL;
79 static	LIST_HEAD(, hid_device)	 hid_devices;
80 
81 %}
82 
83 %union {
84 	bdaddr_t	bdaddr;
85 	int32_t		num;
86 }
87 
88 %token <bdaddr> T_BDADDRSTRING
89 %token <num>	T_HEXBYTE
90 %token T_DEVICE T_BDADDR T_CONTROL_PSM T_INTERRUPT_PSM T_RECONNECT_INITIATE
91 %token T_BATTERY_POWER T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR
92 %token T_TRUE T_FALSE T_ERROR
93 
94 %%
95 
96 config:		line
97 		| config line
98 		;
99 
100 line:		T_DEVICE
101 			{
102 			hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device));
103 			if (hid_device == NULL) {
104 				SYSLOG(LOGCRIT, "Could not allocate new " \
105 						"config entry" EOL);
106 				YYABORT;
107 			}
108 
109 			hid_device->new_device = 1;
110 			}
111 		'{' options '}'
112 			{
113 			if (check_hid_device(hid_device))
114 				LIST_INSERT_HEAD(&hid_devices,hid_device,next);
115 			else
116 				free_hid_device(hid_device);
117 
118 			hid_device = NULL;
119 			}
120 		;
121 
122 options:	option ';'
123 		| options option ';'
124 		;
125 
126 option:		bdaddr
127 		| control_psm
128 		| interrupt_psm
129 		| reconnect_initiate
130 		| battery_power
131 		| normally_connectable
132 		| hid_descriptor
133 		| parser_error
134 		;
135 
136 bdaddr:		T_BDADDR T_BDADDRSTRING
137 			{
138 			memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr));
139 			}
140 		;
141 
142 control_psm:	T_CONTROL_PSM T_HEXBYTE
143 			{
144 			hid_device->control_psm = $2;
145 			}
146 		;
147 
148 interrupt_psm:	T_INTERRUPT_PSM T_HEXBYTE
149 			{
150 			hid_device->interrupt_psm = $2;
151 			}
152 		;
153 
154 reconnect_initiate: T_RECONNECT_INITIATE T_TRUE
155 			{
156 			hid_device->reconnect_initiate = 1;
157 			}
158 		| T_RECONNECT_INITIATE T_FALSE
159 			{
160 			hid_device->reconnect_initiate = 0;
161 			}
162 		;
163 
164 battery_power:	T_BATTERY_POWER T_TRUE
165 			{
166 			hid_device->battery_power = 1;
167 			}
168 		| T_BATTERY_POWER T_FALSE
169 			{
170 			hid_device->battery_power = 0;
171 			}
172 		;
173 
174 normally_connectable: T_NORMALLY_CONNECTABLE T_TRUE
175 			{
176 			hid_device->normally_connectable = 1;
177 			}
178 		| T_NORMALLY_CONNECTABLE T_FALSE
179 			{
180 			hid_device->normally_connectable = 0;
181 			}
182 		;
183 
184 hid_descriptor:	T_HID_DESCRIPTOR
185 			{
186 			hid_descriptor_size = 0;
187 			}
188 		'{' hid_descriptor_bytes '}'
189 			{
190 			if (hid_device->desc != NULL)
191 				hid_dispose_report_desc(hid_device->desc);
192 
193 			hid_device->desc = hid_use_report_desc((unsigned char *) buffer, hid_descriptor_size);
194 			if (hid_device->desc == NULL) {
195 				SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL);
196 				YYABORT;
197 			}
198 			}
199 		;
200 
201 hid_descriptor_bytes: hid_descriptor_byte
202 		| hid_descriptor_bytes hid_descriptor_byte
203 		;
204 
205 hid_descriptor_byte: T_HEXBYTE
206 			{
207 			if (hid_descriptor_size >= (int32_t) sizeof(buffer)) {
208 				SYSLOG(LOGCRIT, "HID descriptor is too big" EOL);
209 				YYABORT;
210 			}
211 
212 			buffer[hid_descriptor_size ++] = $1;
213 			}
214 		;
215 
216 parser_error:	T_ERROR
217 			{
218 				YYABORT;
219 			}
220 
221 %%
222 
223 /* Display parser error message */
224 void
225 yyerror(char const *message)
226 {
227 	SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno);
228 }
229 
230 /* Re-read config file */
231 int32_t
232 read_config_file(void)
233 {
234 	int32_t	e;
235 
236 	if (config_file == NULL) {
237 		SYSLOG(LOGERR, "Unknown config file name!" EOL);
238 		return (-1);
239 	}
240 
241 	if ((yyin = fopen(config_file, "r")) == NULL) {
242 		SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL,
243 				config_file, strerror(errno), errno);
244 		return (-1);
245 	}
246 
247 	clean_config();
248 	if (yyparse() < 0) {
249 		SYSLOG(LOGERR, "Could not parse config file '%s'" EOL,
250 				config_file);
251 		e = -1;
252 	} else
253 		e = 0;
254 
255 	fclose(yyin);
256 	yyin = NULL;
257 
258 	return (e);
259 }
260 
261 /* Clean config */
262 void
263 clean_config(void)
264 {
265 	while (!LIST_EMPTY(&hid_devices)) {
266 		hid_device_p	d = LIST_FIRST(&hid_devices);
267 
268 		LIST_REMOVE(d, next);
269 		free_hid_device(d);
270 	}
271 }
272 
273 /* Lookup config entry */
274 hid_device_p
275 get_hid_device(bdaddr_p bdaddr)
276 {
277 	hid_device_p	d;
278 
279 	LIST_FOREACH(d, &hid_devices, next)
280 		if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
281 			break;
282 
283 	return (d);
284 }
285 
286 /* Get next config entry */
287 hid_device_p
288 get_next_hid_device(hid_device_p d)
289 {
290 	return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next));
291 }
292 
293 /* Print config entry */
294 void
295 print_hid_device(hid_device_p d, FILE *f)
296 {
297 	/* XXX FIXME hack! */
298 	struct report_desc {
299 		unsigned int	size;
300 		unsigned char	data[1];
301 	};
302 	/* XXX FIXME hack! */
303 
304 	struct report_desc	*desc = (struct report_desc *) d->desc;
305 	uint32_t		 i;
306 
307 	fprintf(f,
308 "device {\n"					\
309 "	bdaddr			%s;\n"		\
310 "	control_psm		0x%x;\n"	\
311 "	interrupt_psm		0x%x;\n"	\
312 "	reconnect_initiate	%s;\n"		\
313 "	battery_power		%s;\n"		\
314 "	normally_connectable	%s;\n"		\
315 "	hid_descriptor		{",
316 		bt_ntoa(&d->bdaddr, NULL),
317 		d->control_psm, d->interrupt_psm,
318                 d->reconnect_initiate? "true" : "false",
319                 d->battery_power? "true" : "false",
320                 d->normally_connectable? "true" : "false");
321 
322 	for (i = 0; i < desc->size; i ++) {
323 			if ((i % 8) == 0)
324 				fprintf(f, "\n		");
325 
326 			fprintf(f, "0x%2.2x ", desc->data[i]);
327 	}
328 
329 	fprintf(f,
330 "\n"		\
331 "	};\n"	\
332 "}\n");
333 }
334 
335 /* Check config entry */
336 static int32_t
337 check_hid_device(hid_device_p d)
338 {
339 	hid_data_t	hd;
340 	hid_item_t	hi;
341 	int32_t		page;
342 
343 	if (get_hid_device(&d->bdaddr) != NULL) {
344 		SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL,
345 				bt_ntoa(&d->bdaddr, NULL));
346 		return (0);
347 	}
348 
349 	if (d->control_psm == 0) {
350 		SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL);
351 		return (0);
352 	}
353 
354 	if (d->interrupt_psm == 0) {
355 		SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL);
356 		return (0);
357 	}
358 
359 	if (d->desc == NULL) {
360 		SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL);
361 		return (0);
362 	}
363 
364 	/* XXX somehow need to make sure descriptor is valid */
365 	for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) {
366 		switch (hi.kind) {
367 		case hid_collection:
368 		case hid_endcollection:
369 		case hid_output:
370 		case hid_feature:
371 			break;
372 
373 		case hid_input:
374 			/* Check if the device may send keystrokes */
375 			page = HID_PAGE(hi.usage);
376 			if (page == HUP_KEYBOARD)
377 				d->keyboard = 1;
378 			break;
379 		}
380 	}
381 	hid_end_parse(hd);
382 
383 	return (1);
384 }
385 
386 /* Free config entry */
387 static void
388 free_hid_device(hid_device_p d)
389 {
390 	if (d->desc != NULL)
391 		hid_dispose_report_desc(d->desc);
392 
393 	memset(d, 0, sizeof(*d));
394 	free(d);
395 }
396 
397 /* Re-read hids file */
398 int32_t
399 read_hids_file(void)
400 {
401 	FILE		*f;
402 	hid_device_t	*d;
403 	char		*line;
404 	bdaddr_t	 bdaddr;
405 	int32_t		 lineno;
406 
407 	if (hids_file == NULL) {
408 		SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
409 		return (-1);
410 	}
411 
412 	if ((f = fopen(hids_file, "r")) == NULL) {
413 		if (errno == ENOENT)
414 			return (0);
415 
416 		SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
417 			hids_file, strerror(errno), errno);
418 		return (-1);
419 	}
420 
421 	for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) {
422 		if ((line = strtok(buffer, "\r\n\t ")) == NULL)
423 			continue; /* ignore empty lines */
424 
425 		if (!bt_aton(line, &bdaddr)) {
426 			SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \
427 				"%s:%d" EOL, hids_file, lineno);
428 			continue;
429 		}
430 
431 		if ((d = get_hid_device(&bdaddr)) != NULL)
432 			d->new_device = 0;
433 	}
434 
435 	fclose(f);
436 
437 	return (0);
438 }
439 
440 /* Write hids file */
441 int32_t
442 write_hids_file(void)
443 {
444 	char		 path[PATH_MAX];
445 	FILE		*f;
446 	hid_device_t	*d;
447 
448 	if (hids_file == NULL) {
449 		SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
450 		return (-1);
451 	}
452 
453 	snprintf(path, sizeof(path), "%s.new", hids_file);
454 
455 	if ((f = fopen(path, "w")) == NULL) {
456 		SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
457 			path, strerror(errno), errno);
458 		return (-1);
459 	}
460 
461 	LIST_FOREACH(d, &hid_devices, next)
462 		if (!d->new_device)
463 			fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL));
464 
465 	fclose(f);
466 
467 	if (rename(path, hids_file) < 0) {
468 		SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \
469 			"%s (%d)" EOL, path, hids_file, strerror(errno), errno);
470 		unlink(path);
471 		return (-1);
472 	}
473 
474 	return (0);
475 }
476 
477