parser.y revision 235789
134192Sjdp%{ 234192Sjdp/* 334192Sjdp * parser.y 434192Sjdp */ 534192Sjdp 634192Sjdp/*- 734192Sjdp * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com> 834192Sjdp * All rights reserved. 934192Sjdp * 1034192Sjdp * Redistribution and use in source and binary forms, with or without 1134192Sjdp * modification, are permitted provided that the following conditions 1234192Sjdp * are met: 13262435Sbrueffer * 1. Redistributions of source code must retain the above copyright 1434192Sjdp * notice, this list of conditions and the following disclaimer. 1534192Sjdp * 2. Redistributions in binary form must reproduce the above copyright 1634192Sjdp * notice, this list of conditions and the following disclaimer in the 1734192Sjdp * documentation and/or other materials provided with the distribution. 1834192Sjdp * 1934192Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 2034192Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2134192Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2234192Sjdp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2334192Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2434192Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2534192Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2634192Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2734192Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2834192Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2934192Sjdp * SUCH DAMAGE. 3034192Sjdp * 3134192Sjdp * $Id: parser.y,v 1.7 2006/09/07 21:06:53 max Exp $ 3250476Speter * $FreeBSD: head/usr.sbin/bluetooth/bthidd/parser.y 235789 2012-05-22 16:33:10Z bapt $ 3334192Sjdp */ 3434192Sjdp 3534192Sjdp#include <sys/queue.h> 3634192Sjdp#include <bluetooth.h> 3734192Sjdp#include <dev/usb/usb.h> 3834192Sjdp#include <dev/usb/usbhid.h> 3934192Sjdp#include <errno.h> 4034192Sjdp#include <limits.h> 4134192Sjdp#include <stdio.h> 4234192Sjdp#include <stdlib.h> 4334192Sjdp#include <string.h> 4434192Sjdp#include <unistd.h> 4534192Sjdp#include <usbhid.h> 4634192Sjdp 47211413Skib#ifndef BTHIDCONTROL 4869793Sobrien#include <stdarg.h> 49110803Skan#include <syslog.h> 50119255Simp#define SYSLOG syslog 51110803Skan#define LOGCRIT LOG_CRIT 5234192Sjdp#define LOGERR LOG_ERR 5334192Sjdp#define LOGWARNING LOG_WARNING 5434192Sjdp#define EOL 5534192Sjdp#else 5634192Sjdp#define SYSLOG fprintf 57225152Skib#define LOGCRIT stderr 5834192Sjdp#define LOGERR stderr 5934192Sjdp#define LOGWARNING stderr 6034192Sjdp#define EOL "\n" 6134192Sjdp#endif /* ndef BTHIDCONTROL */ 6234192Sjdp 6334192Sjdp#include "bthid_config.h" 6434192Sjdp 6534192Sjdp int yylex (void); 6634192Sjdp void yyerror (char const *); 6734192Sjdpstatic int32_t check_hid_device(hid_device_p hid_device); 6834192Sjdpstatic void free_hid_device (hid_device_p hid_device); 6934192Sjdp 7034192Sjdpextern FILE *yyin; 7134192Sjdpextern int yylineno; 7234192Sjdp char const *config_file = BTHIDD_CONFFILE; 7334192Sjdp char const *hids_file = BTHIDD_HIDSFILE; 7434192Sjdp 7534192Sjdpstatic char buffer[1024]; 7634192Sjdpstatic int32_t hid_descriptor_size; 7734192Sjdpstatic hid_device_t *hid_device = NULL; 7834192Sjdpstatic LIST_HEAD(, hid_device) hid_devices; 7934192Sjdp 8034192Sjdp%} 8134192Sjdp 8234192Sjdp%union { 8334192Sjdp bdaddr_t bdaddr; 8434192Sjdp int32_t num; 8534192Sjdp} 8634192Sjdp 8734192Sjdp%token <bdaddr> T_BDADDRSTRING 8834192Sjdp%token <num> T_HEXBYTE 8934192Sjdp%token T_DEVICE T_BDADDR T_CONTROL_PSM T_INTERRUPT_PSM T_RECONNECT_INITIATE 9034192Sjdp%token T_BATTERY_POWER T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR 9134192Sjdp%token T_TRUE T_FALSE T_ERROR 9234192Sjdp 9334192Sjdp%% 9434192Sjdp 9534192Sjdpconfig: line 9634192Sjdp | config line 9734192Sjdp ; 9834192Sjdp 9934192Sjdpline: T_DEVICE 10034192Sjdp { 10134192Sjdp hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device)); 10234192Sjdp if (hid_device == NULL) { 10334192Sjdp SYSLOG(LOGCRIT, "Could not allocate new " \ 10434192Sjdp "config entry" EOL); 10534192Sjdp YYABORT; 10634192Sjdp } 10734192Sjdp 10834192Sjdp hid_device->new_device = 1; 10934192Sjdp } 11034192Sjdp '{' options '}' 11134192Sjdp { 11234192Sjdp if (check_hid_device(hid_device)) 11334192Sjdp LIST_INSERT_HEAD(&hid_devices,hid_device,next); 11434192Sjdp else 11534192Sjdp free_hid_device(hid_device); 11634192Sjdp 11734192Sjdp hid_device = NULL; 11834192Sjdp } 11934192Sjdp ; 12034192Sjdp 12134192Sjdpoptions: option ';' 12234192Sjdp | options option ';' 12334192Sjdp ; 12434192Sjdp 12534192Sjdpoption: bdaddr 12634192Sjdp | control_psm 12734192Sjdp | interrupt_psm 12834192Sjdp | reconnect_initiate 12934192Sjdp | battery_power 13034192Sjdp | normally_connectable 13134192Sjdp | hid_descriptor 13234192Sjdp | parser_error 13334192Sjdp ; 13434192Sjdp 13534192Sjdpbdaddr: T_BDADDR T_BDADDRSTRING 13634192Sjdp { 13734192Sjdp memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr)); 13834192Sjdp } 13934192Sjdp ; 140225152Skib 14134192Sjdpcontrol_psm: T_CONTROL_PSM T_HEXBYTE 142211413Skib { 143211413Skib hid_device->control_psm = $2; 144211413Skib } 145211413Skib ; 146211413Skib 147211413Skibinterrupt_psm: T_INTERRUPT_PSM T_HEXBYTE 148211413Skib { 149211413Skib hid_device->interrupt_psm = $2; 150211413Skib } 151211413Skib ; 152211413Skib 153211413Skibreconnect_initiate: T_RECONNECT_INITIATE T_TRUE 154211413Skib { 155211413Skib hid_device->reconnect_initiate = 1; 156211413Skib } 157211413Skib | T_RECONNECT_INITIATE T_FALSE 158211413Skib { 159211413Skib hid_device->reconnect_initiate = 0; 160211413Skib } 161211413Skib ; 16234192Sjdp 16334192Sjdpbattery_power: T_BATTERY_POWER T_TRUE 16434192Sjdp { 16534192Sjdp hid_device->battery_power = 1; 16634192Sjdp } 16738816Sdfr | T_BATTERY_POWER T_FALSE 16838816Sdfr { 16934192Sjdp hid_device->battery_power = 0; 17034192Sjdp } 17134192Sjdp ; 17234192Sjdp 17334192Sjdpnormally_connectable: T_NORMALLY_CONNECTABLE T_TRUE 17434192Sjdp { 17534192Sjdp hid_device->normally_connectable = 1; 176211413Skib } 17734192Sjdp | T_NORMALLY_CONNECTABLE T_FALSE 17834192Sjdp { 17934192Sjdp hid_device->normally_connectable = 0; 18038816Sdfr } 18134192Sjdp ; 18234192Sjdp 18334192Sjdphid_descriptor: T_HID_DESCRIPTOR 18434192Sjdp { 18534192Sjdp hid_descriptor_size = 0; 18634192Sjdp } 18734192Sjdp '{' hid_descriptor_bytes '}' 188114625Sobrien { 18934192Sjdp if (hid_device->desc != NULL) 19034192Sjdp hid_dispose_report_desc(hid_device->desc); 19134192Sjdp 19234192Sjdp hid_device->desc = hid_use_report_desc((unsigned char *) buffer, hid_descriptor_size); 19334192Sjdp if (hid_device->desc == NULL) { 19434192Sjdp SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL); 19534192Sjdp YYABORT; 19634192Sjdp } 19734192Sjdp } 19834192Sjdp ; 199114625Sobrien 20034192Sjdphid_descriptor_bytes: hid_descriptor_byte 20134192Sjdp | hid_descriptor_bytes hid_descriptor_byte 20234192Sjdp ; 20334192Sjdp 20434192Sjdphid_descriptor_byte: T_HEXBYTE 20534192Sjdp { 20634192Sjdp if (hid_descriptor_size >= (int32_t) sizeof(buffer)) { 20734192Sjdp SYSLOG(LOGCRIT, "HID descriptor is too big" EOL); 20834192Sjdp YYABORT; 20934192Sjdp } 21034192Sjdp 21134192Sjdp buffer[hid_descriptor_size ++] = $1; 21234192Sjdp } 21334192Sjdp ; 21434192Sjdp 21534192Sjdpparser_error: T_ERROR 21634192Sjdp { 21734192Sjdp YYABORT; 21834192Sjdp } 21934192Sjdp 22034192Sjdp%% 22134192Sjdp 22234192Sjdp/* Display parser error message */ 22334192Sjdpvoid 22434192Sjdpyyerror(char const *message) 22534192Sjdp{ 22634192Sjdp SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno); 22734192Sjdp} 22834192Sjdp 22934192Sjdp/* Re-read config file */ 23034192Sjdpint32_t 23134192Sjdpread_config_file(void) 23234192Sjdp{ 23334192Sjdp int32_t e; 23434192Sjdp 23534192Sjdp if (config_file == NULL) { 23634192Sjdp SYSLOG(LOGERR, "Unknown config file name!" EOL); 23734192Sjdp return (-1); 23834192Sjdp } 23934192Sjdp 24034192Sjdp if ((yyin = fopen(config_file, "r")) == NULL) { 24134192Sjdp SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL, 24234192Sjdp config_file, strerror(errno), errno); 24334192Sjdp return (-1); 24434192Sjdp } 24534192Sjdp 246154248Sjasone clean_config(); 247154248Sjasone if (yyparse() < 0) { 248154248Sjasone SYSLOG(LOGERR, "Could not parse config file '%s'" EOL, 249154248Sjasone config_file); 250154248Sjasone e = -1; 251154248Sjasone } else 252154248Sjasone e = 0; 253154248Sjasone 254154248Sjasone fclose(yyin); 255154248Sjasone yyin = NULL; 256154248Sjasone 257154248Sjasone return (e); 258154248Sjasone} 259154248Sjasone 260154248Sjasone/* Clean config */ 261154248Sjasonevoid 26234192Sjdpclean_config(void) 26334192Sjdp{ 26434192Sjdp while (!LIST_EMPTY(&hid_devices)) { 26534192Sjdp hid_device_p d = LIST_FIRST(&hid_devices); 26634192Sjdp 26734192Sjdp LIST_REMOVE(d, next); 26834192Sjdp free_hid_device(d); 26934192Sjdp } 27034192Sjdp} 27134192Sjdp 27234192Sjdp/* Lookup config entry */ 27334192Sjdphid_device_p 27434192Sjdpget_hid_device(bdaddr_p bdaddr) 27534192Sjdp{ 27634192Sjdp hid_device_p d; 27734192Sjdp 27834192Sjdp LIST_FOREACH(d, &hid_devices, next) 27934192Sjdp if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0) 28034192Sjdp break; 28134192Sjdp 28234192Sjdp return (d); 28334192Sjdp} 28434192Sjdp 28534192Sjdp/* Get next config entry */ 28634192Sjdphid_device_p 28734192Sjdpget_next_hid_device(hid_device_p d) 28834192Sjdp{ 28934192Sjdp return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next)); 29034192Sjdp} 29134192Sjdp 29234192Sjdp/* Print config entry */ 29334192Sjdpvoid 29434192Sjdpprint_hid_device(hid_device_p d, FILE *f) 29534192Sjdp{ 29634192Sjdp /* XXX FIXME hack! */ 29734192Sjdp struct report_desc { 29834192Sjdp unsigned int size; 29934192Sjdp unsigned char data[1]; 30034192Sjdp }; 30134192Sjdp /* XXX FIXME hack! */ 30234192Sjdp 30334192Sjdp struct report_desc *desc = (struct report_desc *) d->desc; 30434192Sjdp uint32_t i; 30534192Sjdp 30634192Sjdp fprintf(f, 30734192Sjdp"device {\n" \ 30834192Sjdp" bdaddr %s;\n" \ 30934192Sjdp" control_psm 0x%x;\n" \ 31034192Sjdp" interrupt_psm 0x%x;\n" \ 31134192Sjdp" reconnect_initiate %s;\n" \ 31234192Sjdp" battery_power %s;\n" \ 31334192Sjdp" normally_connectable %s;\n" \ 31434192Sjdp" hid_descriptor {", 31534192Sjdp bt_ntoa(&d->bdaddr, NULL), 31634192Sjdp d->control_psm, d->interrupt_psm, 31734192Sjdp d->reconnect_initiate? "true" : "false", 31834192Sjdp d->battery_power? "true" : "false", 31934192Sjdp d->normally_connectable? "true" : "false"); 32034192Sjdp 32134192Sjdp for (i = 0; i < desc->size; i ++) { 32234192Sjdp if ((i % 8) == 0) 32334192Sjdp fprintf(f, "\n "); 32434192Sjdp 32534192Sjdp fprintf(f, "0x%2.2x ", desc->data[i]); 32634192Sjdp } 32734192Sjdp 32834192Sjdp fprintf(f, 32934192Sjdp"\n" \ 33034192Sjdp" };\n" \ 33134192Sjdp"}\n"); 33234192Sjdp} 33334192Sjdp 33434192Sjdp/* Check config entry */ 33534192Sjdpstatic int32_t 33634192Sjdpcheck_hid_device(hid_device_p d) 33734192Sjdp{ 33834192Sjdp hid_data_t hd; 33934192Sjdp hid_item_t hi; 34034192Sjdp int32_t page; 34134192Sjdp 34234192Sjdp if (get_hid_device(&d->bdaddr) != NULL) { 34334192Sjdp SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL, 34434192Sjdp bt_ntoa(&d->bdaddr, NULL)); 34534192Sjdp return (0); 34634192Sjdp } 34734192Sjdp 34834192Sjdp if (d->control_psm == 0) { 34934192Sjdp SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL); 35034192Sjdp return (0); 35134192Sjdp } 35234192Sjdp 35334192Sjdp if (d->interrupt_psm == 0) { 35434192Sjdp SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL); 35534192Sjdp return (0); 35634192Sjdp } 35734192Sjdp 35834192Sjdp if (d->desc == NULL) { 35934192Sjdp SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL); 36034192Sjdp return (0); 36134192Sjdp } 36234192Sjdp 36334192Sjdp /* XXX somehow need to make sure descriptor is valid */ 36434192Sjdp for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) { 36534192Sjdp switch (hi.kind) { 36634192Sjdp case hid_collection: 36734192Sjdp case hid_endcollection: 36834192Sjdp case hid_output: 36934192Sjdp case hid_feature: 37034192Sjdp break; 37134192Sjdp 37234192Sjdp case hid_input: 37334192Sjdp /* Check if the device may send keystrokes */ 37434192Sjdp page = HID_PAGE(hi.usage); 37534192Sjdp if (page == HUP_KEYBOARD) 37634192Sjdp d->keyboard = 1; 37734192Sjdp break; 37834192Sjdp } 37934192Sjdp } 38034192Sjdp hid_end_parse(hd); 38134192Sjdp 38234192Sjdp return (1); 38334192Sjdp} 38434192Sjdp 38534192Sjdp/* Free config entry */ 38634192Sjdpstatic void 38734192Sjdpfree_hid_device(hid_device_p d) 388114625Sobrien{ 38934192Sjdp if (d->desc != NULL) 39034192Sjdp hid_dispose_report_desc(d->desc); 39134192Sjdp 39234192Sjdp memset(d, 0, sizeof(*d)); 39334192Sjdp free(d); 39434192Sjdp} 39534192Sjdp 39634192Sjdp/* Re-read hids file */ 39734192Sjdpint32_t 39834192Sjdpread_hids_file(void) 39934192Sjdp{ 40034192Sjdp FILE *f; 401114625Sobrien hid_device_t *d; 40234192Sjdp char *line; 40334192Sjdp bdaddr_t bdaddr; 40434192Sjdp int32_t lineno; 40534192Sjdp 40634192Sjdp if (hids_file == NULL) { 40734192Sjdp SYSLOG(LOGERR, "Unknown HIDs file name!" EOL); 40834192Sjdp return (-1); 40934192Sjdp } 41034192Sjdp 41134192Sjdp if ((f = fopen(hids_file, "r")) == NULL) { 41234192Sjdp if (errno == ENOENT) 41334192Sjdp return (0); 41434192Sjdp 41534192Sjdp SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL, 41634192Sjdp hids_file, strerror(errno), errno); 41734192Sjdp return (-1); 41834192Sjdp } 41934192Sjdp 42034192Sjdp for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) { 42134192Sjdp if ((line = strtok(buffer, "\r\n\t ")) == NULL) 42234192Sjdp continue; /* ignore empty lines */ 42334192Sjdp 42434192Sjdp if (!bt_aton(line, &bdaddr)) { 42534192Sjdp SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \ 42634192Sjdp "%s:%d" EOL, hids_file, lineno); 42734192Sjdp continue; 42834192Sjdp } 42934192Sjdp 43034192Sjdp if ((d = get_hid_device(&bdaddr)) != NULL) 43134192Sjdp d->new_device = 0; 43234192Sjdp } 43334192Sjdp 43434192Sjdp fclose(f); 43534192Sjdp 43634192Sjdp return (0); 43734192Sjdp} 43834192Sjdp 43934192Sjdp/* Write hids file */ 44034192Sjdpint32_t 44134192Sjdpwrite_hids_file(void) 44234192Sjdp{ 44334192Sjdp char path[PATH_MAX]; 44434192Sjdp FILE *f; 44534192Sjdp hid_device_t *d; 44634192Sjdp 44734192Sjdp if (hids_file == NULL) { 44834192Sjdp SYSLOG(LOGERR, "Unknown HIDs file name!" EOL); 44934192Sjdp return (-1); 45034192Sjdp } 45134192Sjdp 45234192Sjdp snprintf(path, sizeof(path), "%s.new", hids_file); 45334192Sjdp 45434192Sjdp if ((f = fopen(path, "w")) == NULL) { 45534192Sjdp SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL, 45634192Sjdp path, strerror(errno), errno); 45734192Sjdp return (-1); 45834192Sjdp } 45934192Sjdp 46034192Sjdp LIST_FOREACH(d, &hid_devices, next) 46134192Sjdp if (!d->new_device) 46234192Sjdp fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL)); 46334192Sjdp 46434192Sjdp fclose(f); 46534192Sjdp 46634192Sjdp if (rename(path, hids_file) < 0) { 46734192Sjdp SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \ 46834192Sjdp "%s (%d)" EOL, path, hids_file, strerror(errno), errno); 46934192Sjdp unlink(path); 47034192Sjdp return (-1); 47134192Sjdp } 47234192Sjdp 47334192Sjdp return (0); 47434192Sjdp} 47534192Sjdp 47634192Sjdp