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 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 96 config: line 97 | config line 98 ; 99 100 line: 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 122 options: option ';' 123 | options option ';' 124 ; 125 126 option: bdaddr 127 | control_psm 128 | interrupt_psm 129 | reconnect_initiate 130 | battery_power 131 | normally_connectable 132 | hid_descriptor 133 | parser_error 134 ; 135 136 bdaddr: T_BDADDR T_BDADDRSTRING 137 { 138 memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr)); 139 } 140 ; 141 142 control_psm: T_CONTROL_PSM T_HEXBYTE 143 { 144 hid_device->control_psm = $2; 145 } 146 ; 147 148 interrupt_psm: T_INTERRUPT_PSM T_HEXBYTE 149 { 150 hid_device->interrupt_psm = $2; 151 } 152 ; 153 154 reconnect_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 164 battery_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 174 normally_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 184 hid_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 201 hid_descriptor_bytes: hid_descriptor_byte 202 | hid_descriptor_bytes hid_descriptor_byte 203 ; 204 205 hid_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 216 parser_error: T_ERROR 217 { 218 YYABORT; 219 } 220 221 %% 222 223 /* Display parser error message */ 224 void 225 yyerror(char const *message) 226 { 227 SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno); 228 } 229 230 /* Re-read config file */ 231 int32_t 232 read_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 */ 262 void 263 clean_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 */ 274 hid_device_p 275 get_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 */ 287 hid_device_p 288 get_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 */ 294 void 295 print_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 */ 336 static int32_t 337 check_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 */ 387 static void 388 free_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 */ 398 int32_t 399 read_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 */ 441 int32_t 442 write_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