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