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 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 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 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 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 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 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 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 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 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