parser.y revision 128080
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: head/usr.sbin/bluetooth/bthidd/parser.y 128080 2004-04-10 00:18:00Z emax $ 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); 62static int check_hid_device(hid_device_p hid_device); 63static void free_hid_device (hid_device_p hid_device); 64 65extern int yylineno; 66 char *config_file = BTHIDD_CONFFILE; 67 char *hids_file = BTHIDD_HIDSFILE; 68 69static char buffer[1024]; 70static int hid_descriptor_size; 71static hid_device_t *hid_device = NULL; 72static 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 89config: line 90 | config line 91 ; 92 93line: 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 115options: option ';' 116 | options option ';' 117 ; 118 119option: bdaddr 120 | control_psm 121 | interrupt_psm 122 | reconnect_initiate 123 | battery_power 124 | normally_connectable 125 | hid_descriptor 126 | parser_error 127 ; 128 129bdaddr: T_BDADDR T_BDADDRSTRING 130 { 131 memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr)); 132 } 133 ; 134 135control_psm: T_CONTROL_PSM T_HEXBYTE 136 { 137 hid_device->control_psm = $2; 138 } 139 ; 140 141interrupt_psm: T_INTERRUPT_PSM T_HEXBYTE 142 { 143 hid_device->interrupt_psm = $2; 144 } 145 ; 146 147reconnect_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 157battery_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 167normally_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 177hid_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 194hid_descriptor_bytes: hid_descriptor_byte 195 | hid_descriptor_bytes hid_descriptor_byte 196 ; 197 198hid_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 209parser_error: T_ERROR 210 { 211 YYABORT; 212 } 213 214%% 215 216/* Display parser error message */ 217void 218yyerror(char const *message) 219{ 220 SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno); 221} 222 223/* Re-read config file */ 224int 225read_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 */ 256void 257clean_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 */ 268hid_device_p 269get_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 */ 281hid_device_p 282get_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 */ 288void 289print_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 */ 330static int 331check_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 */ 358static void 359free_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 */ 369int 370read_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 */ 412int 413write_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