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