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