parser.y revision 162529
1128080Semax%{ 2128080Semax/* 3128080Semax * parser.y 4162128Semax */ 5162128Semax 6162128Semax/*- 7162128Semax * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com> 8128080Semax * All rights reserved. 9128080Semax * 10128080Semax * Redistribution and use in source and binary forms, with or without 11128080Semax * modification, are permitted provided that the following conditions 12128080Semax * are met: 13128080Semax * 1. Redistributions of source code must retain the above copyright 14128080Semax * notice, this list of conditions and the following disclaimer. 15128080Semax * 2. Redistributions in binary form must reproduce the above copyright 16128080Semax * notice, this list of conditions and the following disclaimer in the 17128080Semax * documentation and/or other materials provided with the distribution. 18128080Semax * 19128080Semax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20128080Semax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21128080Semax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22128080Semax * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23128080Semax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24128080Semax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25128080Semax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26128080Semax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27128080Semax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28128080Semax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29128080Semax * SUCH DAMAGE. 30128080Semax * 31162128Semax * $Id: parser.y,v 1.7 2006/09/07 21:06:53 max Exp $ 32128080Semax * $FreeBSD: head/usr.sbin/bluetooth/bthidd/parser.y 162529 2006-09-21 17:16:37Z emax $ 33128080Semax */ 34128080Semax 35128080Semax#include <sys/queue.h> 36128080Semax#include <bluetooth.h> 37162128Semax#include <dev/usb/usb.h> 38162128Semax#include <dev/usb/usbhid.h> 39128080Semax#include <errno.h> 40128080Semax#include <limits.h> 41128080Semax#include <stdio.h> 42128080Semax#include <string.h> 43128080Semax#include <unistd.h> 44137868Semax#include <usbhid.h> 45128080Semax 46128080Semax#ifndef BTHIDCONTROL 47128080Semax#include <stdarg.h> 48128080Semax#include <syslog.h> 49128080Semax#define SYSLOG syslog 50128080Semax#define LOGCRIT LOG_CRIT 51128080Semax#define LOGERR LOG_ERR 52128080Semax#define LOGWARNING LOG_WARNING 53128080Semax#define EOL 54128080Semax#else 55128080Semax#define SYSLOG fprintf 56128080Semax#define LOGCRIT stderr 57128080Semax#define LOGERR stderr 58128080Semax#define LOGWARNING stderr 59137868Semax#define EOL "\n" 60128080Semax#endif /* ndef BTHIDCONTROL */ 61128080Semax 62128080Semax#include "bthid_config.h" 63128080Semax 64128080Semax int yyparse (void); 65128080Semax int yylex (void); 66162128Semax void yyerror (char const *); 67162128Semaxstatic int32_t check_hid_device(hid_device_p hid_device); 68128080Semaxstatic void free_hid_device (hid_device_p hid_device); 69128080Semax 70162128Semaxextern FILE *yyin; 71128080Semaxextern int yylineno; 72162128Semax char const *config_file = BTHIDD_CONFFILE; 73162128Semax char const *hids_file = BTHIDD_HIDSFILE; 74128080Semax 75128080Semaxstatic char buffer[1024]; 76162128Semaxstatic int32_t hid_descriptor_size; 77128080Semaxstatic hid_device_t *hid_device = NULL; 78128080Semaxstatic LIST_HEAD(, hid_device) hid_devices; 79128080Semax 80128080Semax%} 81128080Semax 82128080Semax%union { 83128080Semax bdaddr_t bdaddr; 84162128Semax int32_t num; 85128080Semax} 86128080Semax 87128080Semax%token <bdaddr> T_BDADDRSTRING 88128080Semax%token <num> T_HEXBYTE 89128080Semax%token T_DEVICE T_BDADDR T_CONTROL_PSM T_INTERRUPT_PSM T_RECONNECT_INITIATE 90128080Semax%token T_BATTERY_POWER T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR 91128080Semax%token T_TRUE T_FALSE T_ERROR 92128080Semax 93128080Semax%% 94128080Semax 95128080Semaxconfig: line 96128080Semax | config line 97128080Semax ; 98128080Semax 99128080Semaxline: T_DEVICE 100128080Semax { 101128080Semax hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device)); 102128080Semax if (hid_device == NULL) { 103128080Semax SYSLOG(LOGCRIT, "Could not allocate new " \ 104128080Semax "config entry" EOL); 105128080Semax YYABORT; 106128080Semax } 107128080Semax 108128080Semax hid_device->new_device = 1; 109128080Semax } 110128080Semax '{' options '}' 111128080Semax { 112128080Semax if (check_hid_device(hid_device)) 113128080Semax LIST_INSERT_HEAD(&hid_devices,hid_device,next); 114128080Semax else 115128080Semax free_hid_device(hid_device); 116128080Semax 117128080Semax hid_device = NULL; 118128080Semax } 119128080Semax ; 120128080Semax 121128080Semaxoptions: option ';' 122128080Semax | options option ';' 123128080Semax ; 124128080Semax 125128080Semaxoption: bdaddr 126128080Semax | control_psm 127128080Semax | interrupt_psm 128128080Semax | reconnect_initiate 129128080Semax | battery_power 130128080Semax | normally_connectable 131128080Semax | hid_descriptor 132128080Semax | parser_error 133128080Semax ; 134128080Semax 135128080Semaxbdaddr: T_BDADDR T_BDADDRSTRING 136128080Semax { 137128080Semax memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr)); 138128080Semax } 139128080Semax ; 140128080Semax 141128080Semaxcontrol_psm: T_CONTROL_PSM T_HEXBYTE 142128080Semax { 143128080Semax hid_device->control_psm = $2; 144128080Semax } 145128080Semax ; 146128080Semax 147128080Semaxinterrupt_psm: T_INTERRUPT_PSM T_HEXBYTE 148128080Semax { 149128080Semax hid_device->interrupt_psm = $2; 150128080Semax } 151128080Semax ; 152128080Semax 153128080Semaxreconnect_initiate: T_RECONNECT_INITIATE T_TRUE 154128080Semax { 155128080Semax hid_device->reconnect_initiate = 1; 156128080Semax } 157128080Semax | T_RECONNECT_INITIATE T_FALSE 158128080Semax { 159128080Semax hid_device->reconnect_initiate = 0; 160128080Semax } 161128080Semax ; 162128080Semax 163128080Semaxbattery_power: T_BATTERY_POWER T_TRUE 164128080Semax { 165128080Semax hid_device->battery_power = 1; 166128080Semax } 167128080Semax | T_BATTERY_POWER T_FALSE 168128080Semax { 169128080Semax hid_device->battery_power = 0; 170128080Semax } 171128080Semax ; 172128080Semax 173128080Semaxnormally_connectable: T_NORMALLY_CONNECTABLE T_TRUE 174128080Semax { 175128080Semax hid_device->normally_connectable = 1; 176128080Semax } 177128080Semax | T_NORMALLY_CONNECTABLE T_FALSE 178128080Semax { 179128080Semax hid_device->normally_connectable = 0; 180128080Semax } 181128080Semax ; 182128080Semax 183128080Semaxhid_descriptor: T_HID_DESCRIPTOR 184128080Semax { 185128080Semax hid_descriptor_size = 0; 186128080Semax } 187128080Semax '{' hid_descriptor_bytes '}' 188128080Semax { 189128080Semax if (hid_device->desc != NULL) 190128080Semax hid_dispose_report_desc(hid_device->desc); 191128080Semax 192162529Semax hid_device->desc = hid_use_report_desc((unsigned char *) buffer, hid_descriptor_size); 193128080Semax if (hid_device->desc == NULL) { 194128080Semax SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL); 195128080Semax YYABORT; 196128080Semax } 197128080Semax } 198128080Semax ; 199128080Semax 200128080Semaxhid_descriptor_bytes: hid_descriptor_byte 201128080Semax | hid_descriptor_bytes hid_descriptor_byte 202128080Semax ; 203128080Semax 204128080Semaxhid_descriptor_byte: T_HEXBYTE 205128080Semax { 206162128Semax if (hid_descriptor_size >= (int32_t) sizeof(buffer)) { 207128080Semax SYSLOG(LOGCRIT, "HID descriptor is too big" EOL); 208128080Semax YYABORT; 209128080Semax } 210128080Semax 211128080Semax buffer[hid_descriptor_size ++] = $1; 212128080Semax } 213128080Semax ; 214128080Semax 215128080Semaxparser_error: T_ERROR 216128080Semax { 217128080Semax YYABORT; 218128080Semax } 219128080Semax 220128080Semax%% 221128080Semax 222128080Semax/* Display parser error message */ 223128080Semaxvoid 224128080Semaxyyerror(char const *message) 225128080Semax{ 226128080Semax SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno); 227128080Semax} 228128080Semax 229128080Semax/* Re-read config file */ 230162128Semaxint32_t 231128080Semaxread_config_file(void) 232128080Semax{ 233162128Semax int32_t e; 234128080Semax 235128080Semax if (config_file == NULL) { 236128080Semax SYSLOG(LOGERR, "Unknown config file name!" EOL); 237128080Semax return (-1); 238128080Semax } 239128080Semax 240128080Semax if ((yyin = fopen(config_file, "r")) == NULL) { 241128080Semax SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL, 242128080Semax config_file, strerror(errno), errno); 243128080Semax return (-1); 244128080Semax } 245128080Semax 246128080Semax clean_config(); 247128080Semax if (yyparse() < 0) { 248128080Semax SYSLOG(LOGERR, "Could not parse config file '%s'" EOL, 249128080Semax config_file); 250128080Semax e = -1; 251128080Semax } else 252128080Semax e = 0; 253128080Semax 254128080Semax fclose(yyin); 255128080Semax yyin = NULL; 256128080Semax 257128080Semax return (e); 258128080Semax} 259128080Semax 260128080Semax/* Clean config */ 261128080Semaxvoid 262128080Semaxclean_config(void) 263128080Semax{ 264128080Semax while (!LIST_EMPTY(&hid_devices)) { 265162128Semax hid_device_p d = LIST_FIRST(&hid_devices); 266128080Semax 267162128Semax LIST_REMOVE(d, next); 268162128Semax free_hid_device(d); 269128080Semax } 270128080Semax} 271128080Semax 272128080Semax/* Lookup config entry */ 273128080Semaxhid_device_p 274128080Semaxget_hid_device(bdaddr_p bdaddr) 275128080Semax{ 276162128Semax hid_device_p d; 277128080Semax 278162128Semax LIST_FOREACH(d, &hid_devices, next) 279162128Semax if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0) 280128080Semax break; 281128080Semax 282162128Semax return (d); 283128080Semax} 284128080Semax 285128080Semax/* Get next config entry */ 286128080Semaxhid_device_p 287128080Semaxget_next_hid_device(hid_device_p d) 288128080Semax{ 289128080Semax return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next)); 290128080Semax} 291128080Semax 292128080Semax/* Print config entry */ 293128080Semaxvoid 294162128Semaxprint_hid_device(hid_device_p d, FILE *f) 295128080Semax{ 296128080Semax /* XXX FIXME hack! */ 297128080Semax struct report_desc { 298128080Semax unsigned int size; 299128080Semax unsigned char data[1]; 300128080Semax }; 301128080Semax /* XXX FIXME hack! */ 302128080Semax 303162128Semax struct report_desc *desc = (struct report_desc *) d->desc; 304162128Semax uint32_t i; 305128080Semax 306128080Semax fprintf(f, 307128080Semax"device {\n" \ 308128080Semax" bdaddr %s;\n" \ 309128080Semax" control_psm 0x%x;\n" \ 310140130Semax" interrupt_psm 0x%x;\n" \ 311128080Semax" reconnect_initiate %s;\n" \ 312128080Semax" battery_power %s;\n" \ 313128080Semax" normally_connectable %s;\n" \ 314128080Semax" hid_descriptor {", 315162128Semax bt_ntoa(&d->bdaddr, NULL), 316162128Semax d->control_psm, d->interrupt_psm, 317162128Semax d->reconnect_initiate? "true" : "false", 318162128Semax d->battery_power? "true" : "false", 319162128Semax d->normally_connectable? "true" : "false"); 320128080Semax 321128080Semax for (i = 0; i < desc->size; i ++) { 322128080Semax if ((i % 8) == 0) 323146357Semax fprintf(f, "\n "); 324128080Semax 325128080Semax fprintf(f, "0x%2.2x ", desc->data[i]); 326128080Semax } 327128080Semax 328146357Semax fprintf(f, 329128080Semax"\n" \ 330128080Semax" };\n" \ 331128080Semax"}\n"); 332128080Semax} 333128080Semax 334128080Semax/* Check config entry */ 335162128Semaxstatic int32_t 336162128Semaxcheck_hid_device(hid_device_p d) 337128080Semax{ 338162128Semax hid_data_t hd; 339162128Semax hid_item_t hi; 340162128Semax int32_t page; 341162128Semax 342162128Semax if (get_hid_device(&d->bdaddr) != NULL) { 343128080Semax SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL, 344162128Semax bt_ntoa(&d->bdaddr, NULL)); 345128080Semax return (0); 346128080Semax } 347128080Semax 348162128Semax if (d->control_psm == 0) { 349128080Semax SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL); 350128080Semax return (0); 351128080Semax } 352128080Semax 353162128Semax if (d->interrupt_psm == 0) { 354128080Semax SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL); 355128080Semax return (0); 356128080Semax } 357128080Semax 358162128Semax if (d->desc == NULL) { 359128080Semax SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL); 360128080Semax return (0); 361128080Semax } 362128080Semax 363162128Semax /* XXX somehow need to make sure descriptor is valid */ 364162128Semax for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) { 365162128Semax switch (hi.kind) { 366162128Semax case hid_collection: 367162128Semax case hid_endcollection: 368162128Semax case hid_output: 369162128Semax case hid_feature: 370162128Semax break; 371162128Semax 372162128Semax case hid_input: 373162128Semax /* Check if the device may send keystrokes */ 374162128Semax page = HID_PAGE(hi.usage); 375162128Semax if (page == HUP_KEYBOARD || page == HUP_CONSUMER) 376162128Semax d->keyboard = 1; 377162128Semax break; 378162128Semax } 379162128Semax } 380162128Semax hid_end_parse(hd); 381162128Semax 382128080Semax return (1); 383128080Semax} 384128080Semax 385128080Semax/* Free config entry */ 386128080Semaxstatic void 387162128Semaxfree_hid_device(hid_device_p d) 388128080Semax{ 389162128Semax if (d->desc != NULL) 390162128Semax hid_dispose_report_desc(d->desc); 391128080Semax 392162128Semax memset(d, 0, sizeof(*d)); 393162128Semax free(d); 394128080Semax} 395128080Semax 396128080Semax/* Re-read hids file */ 397162128Semaxint32_t 398128080Semaxread_hids_file(void) 399128080Semax{ 400162128Semax FILE *f; 401162128Semax hid_device_t *d; 402162128Semax char *line; 403128080Semax bdaddr_t bdaddr; 404162128Semax int32_t lineno; 405128080Semax 406128080Semax if (hids_file == NULL) { 407128080Semax SYSLOG(LOGERR, "Unknown HIDs file name!" EOL); 408128080Semax return (-1); 409128080Semax } 410128080Semax 411128080Semax if ((f = fopen(hids_file, "r")) == NULL) { 412128080Semax if (errno == ENOENT) 413128080Semax return (0); 414128080Semax 415128080Semax SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL, 416128080Semax hids_file, strerror(errno), errno); 417128080Semax return (-1); 418128080Semax } 419128080Semax 420128080Semax for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) { 421128080Semax if ((line = strtok(buffer, "\r\n\t ")) == NULL) 422128080Semax continue; /* ignore empty lines */ 423128080Semax 424128080Semax if (!bt_aton(line, &bdaddr)) { 425128080Semax SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \ 426128080Semax "%s:%d" EOL, hids_file, lineno); 427128080Semax continue; 428128080Semax } 429128080Semax 430162128Semax if ((d = get_hid_device(&bdaddr)) != NULL) 431162128Semax d->new_device = 0; 432128080Semax } 433128080Semax 434128080Semax fclose(f); 435128080Semax 436128080Semax return (0); 437128080Semax} 438128080Semax 439128080Semax/* Write hids file */ 440162128Semaxint32_t 441128080Semaxwrite_hids_file(void) 442128080Semax{ 443128080Semax char path[PATH_MAX]; 444162128Semax FILE *f; 445162128Semax hid_device_t *d; 446128080Semax 447128080Semax if (hids_file == NULL) { 448128080Semax SYSLOG(LOGERR, "Unknown HIDs file name!" EOL); 449128080Semax return (-1); 450128080Semax } 451128080Semax 452128080Semax snprintf(path, sizeof(path), "%s.new", hids_file); 453128080Semax 454128080Semax if ((f = fopen(path, "w")) == NULL) { 455128080Semax SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL, 456128080Semax path, strerror(errno), errno); 457128080Semax return (-1); 458128080Semax } 459128080Semax 460162128Semax LIST_FOREACH(d, &hid_devices, next) 461162128Semax if (!d->new_device) 462162128Semax fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL)); 463128080Semax 464128080Semax fclose(f); 465128080Semax 466128080Semax if (rename(path, hids_file) < 0) { 467128080Semax SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \ 468128080Semax "%s (%d)" EOL, path, hids_file, strerror(errno), errno); 469128080Semax unlink(path); 470128080Semax return (-1); 471128080Semax } 472128080Semax 473128080Semax return (0); 474128080Semax} 475128080Semax 476