1 %{ 2 /* 3 * parser.y 4 */ 5 6 /*- 7 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 * $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 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 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 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 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 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 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 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 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 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