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