parser.y revision 302408
1%{ 2/* 3 * parser.y 4 */ 5 6/*- 7 * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com> 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 * 31 * $Id: parser.y,v 1.7 2006/09/07 21:06:53 max Exp $ 32 * $FreeBSD: stable/11/usr.sbin/bluetooth/bthidd/parser.y 281210 2015-04-07 16:48:23Z takawata $ 33 */ 34 35#include <sys/queue.h> 36#define L2CAP_SOCKET_CHECKED 37#include <bluetooth.h> 38#include <dev/usb/usb.h> 39#include <dev/usb/usbhid.h> 40#include <errno.h> 41#include <limits.h> 42#include <stdio.h> 43#include <stdlib.h> 44#include <string.h> 45#include <unistd.h> 46#include <usbhid.h> 47 48#ifndef BTHIDCONTROL 49#include <stdarg.h> 50#include <syslog.h> 51#define SYSLOG syslog 52#define LOGCRIT LOG_CRIT 53#define LOGERR LOG_ERR 54#define LOGWARNING LOG_WARNING 55#define EOL 56#else 57#define SYSLOG fprintf 58#define LOGCRIT stderr 59#define LOGERR stderr 60#define LOGWARNING stderr 61#define EOL "\n" 62#endif /* ndef BTHIDCONTROL */ 63 64#include "bthid_config.h" 65 66 int yylex (void); 67 void yyerror (char const *); 68static int32_t check_hid_device(hid_device_p hid_device); 69static void free_hid_device (hid_device_p hid_device); 70 71extern FILE *yyin; 72extern int yylineno; 73 char const *config_file = BTHIDD_CONFFILE; 74 char const *hids_file = BTHIDD_HIDSFILE; 75 76static char buffer[1024]; 77static int32_t hid_descriptor_size; 78static hid_device_t *hid_device = NULL; 79static LIST_HEAD(, hid_device) hid_devices; 80 81%} 82 83%union { 84 bdaddr_t bdaddr; 85 int32_t num; 86} 87 88%token <bdaddr> T_BDADDRSTRING 89%token <num> T_HEXBYTE 90%token T_DEVICE T_BDADDR T_CONTROL_PSM T_INTERRUPT_PSM T_RECONNECT_INITIATE 91%token T_BATTERY_POWER T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR 92%token T_TRUE T_FALSE T_ERROR 93 94%% 95 96config: line 97 | config line 98 ; 99 100line: T_DEVICE 101 { 102 hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device)); 103 if (hid_device == NULL) { 104 SYSLOG(LOGCRIT, "Could not allocate new " \ 105 "config entry" EOL); 106 YYABORT; 107 } 108 109 hid_device->new_device = 1; 110 } 111 '{' options '}' 112 { 113 if (check_hid_device(hid_device)) 114 LIST_INSERT_HEAD(&hid_devices,hid_device,next); 115 else 116 free_hid_device(hid_device); 117 118 hid_device = NULL; 119 } 120 ; 121 122options: option ';' 123 | options option ';' 124 ; 125 126option: bdaddr 127 | control_psm 128 | interrupt_psm 129 | reconnect_initiate 130 | battery_power 131 | normally_connectable 132 | hid_descriptor 133 | parser_error 134 ; 135 136bdaddr: T_BDADDR T_BDADDRSTRING 137 { 138 memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr)); 139 } 140 ; 141 142control_psm: T_CONTROL_PSM T_HEXBYTE 143 { 144 hid_device->control_psm = $2; 145 } 146 ; 147 148interrupt_psm: T_INTERRUPT_PSM T_HEXBYTE 149 { 150 hid_device->interrupt_psm = $2; 151 } 152 ; 153 154reconnect_initiate: T_RECONNECT_INITIATE T_TRUE 155 { 156 hid_device->reconnect_initiate = 1; 157 } 158 | T_RECONNECT_INITIATE T_FALSE 159 { 160 hid_device->reconnect_initiate = 0; 161 } 162 ; 163 164battery_power: T_BATTERY_POWER T_TRUE 165 { 166 hid_device->battery_power = 1; 167 } 168 | T_BATTERY_POWER T_FALSE 169 { 170 hid_device->battery_power = 0; 171 } 172 ; 173 174normally_connectable: T_NORMALLY_CONNECTABLE T_TRUE 175 { 176 hid_device->normally_connectable = 1; 177 } 178 | T_NORMALLY_CONNECTABLE T_FALSE 179 { 180 hid_device->normally_connectable = 0; 181 } 182 ; 183 184hid_descriptor: T_HID_DESCRIPTOR 185 { 186 hid_descriptor_size = 0; 187 } 188 '{' hid_descriptor_bytes '}' 189 { 190 if (hid_device->desc != NULL) 191 hid_dispose_report_desc(hid_device->desc); 192 193 hid_device->desc = hid_use_report_desc((unsigned char *) buffer, hid_descriptor_size); 194 if (hid_device->desc == NULL) { 195 SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL); 196 YYABORT; 197 } 198 } 199 ; 200 201hid_descriptor_bytes: hid_descriptor_byte 202 | hid_descriptor_bytes hid_descriptor_byte 203 ; 204 205hid_descriptor_byte: T_HEXBYTE 206 { 207 if (hid_descriptor_size >= (int32_t) sizeof(buffer)) { 208 SYSLOG(LOGCRIT, "HID descriptor is too big" EOL); 209 YYABORT; 210 } 211 212 buffer[hid_descriptor_size ++] = $1; 213 } 214 ; 215 216parser_error: T_ERROR 217 { 218 YYABORT; 219 } 220 221%% 222 223/* Display parser error message */ 224void 225yyerror(char const *message) 226{ 227 SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno); 228} 229 230/* Re-read config file */ 231int32_t 232read_config_file(void) 233{ 234 int32_t e; 235 236 if (config_file == NULL) { 237 SYSLOG(LOGERR, "Unknown config file name!" EOL); 238 return (-1); 239 } 240 241 if ((yyin = fopen(config_file, "r")) == NULL) { 242 SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL, 243 config_file, strerror(errno), errno); 244 return (-1); 245 } 246 247 clean_config(); 248 if (yyparse() < 0) { 249 SYSLOG(LOGERR, "Could not parse config file '%s'" EOL, 250 config_file); 251 e = -1; 252 } else 253 e = 0; 254 255 fclose(yyin); 256 yyin = NULL; 257 258 return (e); 259} 260 261/* Clean config */ 262void 263clean_config(void) 264{ 265 while (!LIST_EMPTY(&hid_devices)) { 266 hid_device_p d = LIST_FIRST(&hid_devices); 267 268 LIST_REMOVE(d, next); 269 free_hid_device(d); 270 } 271} 272 273/* Lookup config entry */ 274hid_device_p 275get_hid_device(bdaddr_p bdaddr) 276{ 277 hid_device_p d; 278 279 LIST_FOREACH(d, &hid_devices, next) 280 if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0) 281 break; 282 283 return (d); 284} 285 286/* Get next config entry */ 287hid_device_p 288get_next_hid_device(hid_device_p d) 289{ 290 return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next)); 291} 292 293/* Print config entry */ 294void 295print_hid_device(hid_device_p d, FILE *f) 296{ 297 /* XXX FIXME hack! */ 298 struct report_desc { 299 unsigned int size; 300 unsigned char data[1]; 301 }; 302 /* XXX FIXME hack! */ 303 304 struct report_desc *desc = (struct report_desc *) d->desc; 305 uint32_t i; 306 307 fprintf(f, 308"device {\n" \ 309" bdaddr %s;\n" \ 310" control_psm 0x%x;\n" \ 311" interrupt_psm 0x%x;\n" \ 312" reconnect_initiate %s;\n" \ 313" battery_power %s;\n" \ 314" normally_connectable %s;\n" \ 315" hid_descriptor {", 316 bt_ntoa(&d->bdaddr, NULL), 317 d->control_psm, d->interrupt_psm, 318 d->reconnect_initiate? "true" : "false", 319 d->battery_power? "true" : "false", 320 d->normally_connectable? "true" : "false"); 321 322 for (i = 0; i < desc->size; i ++) { 323 if ((i % 8) == 0) 324 fprintf(f, "\n "); 325 326 fprintf(f, "0x%2.2x ", desc->data[i]); 327 } 328 329 fprintf(f, 330"\n" \ 331" };\n" \ 332"}\n"); 333} 334 335/* Check config entry */ 336static int32_t 337check_hid_device(hid_device_p d) 338{ 339 hid_data_t hd; 340 hid_item_t hi; 341 int32_t page; 342 343 if (get_hid_device(&d->bdaddr) != NULL) { 344 SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL, 345 bt_ntoa(&d->bdaddr, NULL)); 346 return (0); 347 } 348 349 if (d->control_psm == 0) { 350 SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL); 351 return (0); 352 } 353 354 if (d->interrupt_psm == 0) { 355 SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL); 356 return (0); 357 } 358 359 if (d->desc == NULL) { 360 SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL); 361 return (0); 362 } 363 364 /* XXX somehow need to make sure descriptor is valid */ 365 for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) { 366 switch (hi.kind) { 367 case hid_collection: 368 case hid_endcollection: 369 case hid_output: 370 case hid_feature: 371 break; 372 373 case hid_input: 374 /* Check if the device may send keystrokes */ 375 page = HID_PAGE(hi.usage); 376 if (page == HUP_KEYBOARD) 377 d->keyboard = 1; 378 break; 379 } 380 } 381 hid_end_parse(hd); 382 383 return (1); 384} 385 386/* Free config entry */ 387static void 388free_hid_device(hid_device_p d) 389{ 390 if (d->desc != NULL) 391 hid_dispose_report_desc(d->desc); 392 393 memset(d, 0, sizeof(*d)); 394 free(d); 395} 396 397/* Re-read hids file */ 398int32_t 399read_hids_file(void) 400{ 401 FILE *f; 402 hid_device_t *d; 403 char *line; 404 bdaddr_t bdaddr; 405 int32_t lineno; 406 407 if (hids_file == NULL) { 408 SYSLOG(LOGERR, "Unknown HIDs file name!" EOL); 409 return (-1); 410 } 411 412 if ((f = fopen(hids_file, "r")) == NULL) { 413 if (errno == ENOENT) 414 return (0); 415 416 SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL, 417 hids_file, strerror(errno), errno); 418 return (-1); 419 } 420 421 for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) { 422 if ((line = strtok(buffer, "\r\n\t ")) == NULL) 423 continue; /* ignore empty lines */ 424 425 if (!bt_aton(line, &bdaddr)) { 426 SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \ 427 "%s:%d" EOL, hids_file, lineno); 428 continue; 429 } 430 431 if ((d = get_hid_device(&bdaddr)) != NULL) 432 d->new_device = 0; 433 } 434 435 fclose(f); 436 437 return (0); 438} 439 440/* Write hids file */ 441int32_t 442write_hids_file(void) 443{ 444 char path[PATH_MAX]; 445 FILE *f; 446 hid_device_t *d; 447 448 if (hids_file == NULL) { 449 SYSLOG(LOGERR, "Unknown HIDs file name!" EOL); 450 return (-1); 451 } 452 453 snprintf(path, sizeof(path), "%s.new", hids_file); 454 455 if ((f = fopen(path, "w")) == NULL) { 456 SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL, 457 path, strerror(errno), errno); 458 return (-1); 459 } 460 461 LIST_FOREACH(d, &hid_devices, next) 462 if (!d->new_device) 463 fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL)); 464 465 fclose(f); 466 467 if (rename(path, hids_file) < 0) { 468 SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \ 469 "%s (%d)" EOL, path, hids_file, strerror(errno), errno); 470 unlink(path); 471 return (-1); 472 } 473 474 return (0); 475} 476 477