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