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