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