summaryrefslogtreecommitdiffstats
path: root/usr.sbin/bluetooth/bthidd/parser.y
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/bluetooth/bthidd/parser.y')
-rw-r--r--usr.sbin/bluetooth/bthidd/parser.y475
1 files changed, 475 insertions, 0 deletions
diff --git a/usr.sbin/bluetooth/bthidd/parser.y b/usr.sbin/bluetooth/bthidd/parser.y
new file mode 100644
index 0000000..50468f4
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/parser.y
@@ -0,0 +1,475 @@
+%{
+/*
+ * parser.y
+ */
+
+/*-
+ * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: parser.y,v 1.7 2006/09/07 21:06:53 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <usbhid.h>
+
+#ifndef BTHIDCONTROL
+#include <stdarg.h>
+#include <syslog.h>
+#define SYSLOG syslog
+#define LOGCRIT LOG_CRIT
+#define LOGERR LOG_ERR
+#define LOGWARNING LOG_WARNING
+#define EOL
+#else
+#define SYSLOG fprintf
+#define LOGCRIT stderr
+#define LOGERR stderr
+#define LOGWARNING stderr
+#define EOL "\n"
+#endif /* ndef BTHIDCONTROL */
+
+#include "bthid_config.h"
+
+ int yylex (void);
+ void yyerror (char const *);
+static int32_t check_hid_device(hid_device_p hid_device);
+static void free_hid_device (hid_device_p hid_device);
+
+extern FILE *yyin;
+extern int yylineno;
+ char const *config_file = BTHIDD_CONFFILE;
+ char const *hids_file = BTHIDD_HIDSFILE;
+
+static char buffer[1024];
+static int32_t hid_descriptor_size;
+static hid_device_t *hid_device = NULL;
+static LIST_HEAD(, hid_device) hid_devices;
+
+%}
+
+%union {
+ bdaddr_t bdaddr;
+ int32_t num;
+}
+
+%token <bdaddr> T_BDADDRSTRING
+%token <num> T_HEXBYTE
+%token T_DEVICE T_BDADDR T_CONTROL_PSM T_INTERRUPT_PSM T_RECONNECT_INITIATE
+%token T_BATTERY_POWER T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR
+%token T_TRUE T_FALSE T_ERROR
+
+%%
+
+config: line
+ | config line
+ ;
+
+line: T_DEVICE
+ {
+ hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device));
+ if (hid_device == NULL) {
+ SYSLOG(LOGCRIT, "Could not allocate new " \
+ "config entry" EOL);
+ YYABORT;
+ }
+
+ hid_device->new_device = 1;
+ }
+ '{' options '}'
+ {
+ if (check_hid_device(hid_device))
+ LIST_INSERT_HEAD(&hid_devices,hid_device,next);
+ else
+ free_hid_device(hid_device);
+
+ hid_device = NULL;
+ }
+ ;
+
+options: option ';'
+ | options option ';'
+ ;
+
+option: bdaddr
+ | control_psm
+ | interrupt_psm
+ | reconnect_initiate
+ | battery_power
+ | normally_connectable
+ | hid_descriptor
+ | parser_error
+ ;
+
+bdaddr: T_BDADDR T_BDADDRSTRING
+ {
+ memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr));
+ }
+ ;
+
+control_psm: T_CONTROL_PSM T_HEXBYTE
+ {
+ hid_device->control_psm = $2;
+ }
+ ;
+
+interrupt_psm: T_INTERRUPT_PSM T_HEXBYTE
+ {
+ hid_device->interrupt_psm = $2;
+ }
+ ;
+
+reconnect_initiate: T_RECONNECT_INITIATE T_TRUE
+ {
+ hid_device->reconnect_initiate = 1;
+ }
+ | T_RECONNECT_INITIATE T_FALSE
+ {
+ hid_device->reconnect_initiate = 0;
+ }
+ ;
+
+battery_power: T_BATTERY_POWER T_TRUE
+ {
+ hid_device->battery_power = 1;
+ }
+ | T_BATTERY_POWER T_FALSE
+ {
+ hid_device->battery_power = 0;
+ }
+ ;
+
+normally_connectable: T_NORMALLY_CONNECTABLE T_TRUE
+ {
+ hid_device->normally_connectable = 1;
+ }
+ | T_NORMALLY_CONNECTABLE T_FALSE
+ {
+ hid_device->normally_connectable = 0;
+ }
+ ;
+
+hid_descriptor: T_HID_DESCRIPTOR
+ {
+ hid_descriptor_size = 0;
+ }
+ '{' hid_descriptor_bytes '}'
+ {
+ if (hid_device->desc != NULL)
+ hid_dispose_report_desc(hid_device->desc);
+
+ hid_device->desc = hid_use_report_desc((unsigned char *) buffer, hid_descriptor_size);
+ if (hid_device->desc == NULL) {
+ SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL);
+ YYABORT;
+ }
+ }
+ ;
+
+hid_descriptor_bytes: hid_descriptor_byte
+ | hid_descriptor_bytes hid_descriptor_byte
+ ;
+
+hid_descriptor_byte: T_HEXBYTE
+ {
+ if (hid_descriptor_size >= (int32_t) sizeof(buffer)) {
+ SYSLOG(LOGCRIT, "HID descriptor is too big" EOL);
+ YYABORT;
+ }
+
+ buffer[hid_descriptor_size ++] = $1;
+ }
+ ;
+
+parser_error: T_ERROR
+ {
+ YYABORT;
+ }
+
+%%
+
+/* Display parser error message */
+void
+yyerror(char const *message)
+{
+ SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno);
+}
+
+/* Re-read config file */
+int32_t
+read_config_file(void)
+{
+ int32_t e;
+
+ if (config_file == NULL) {
+ SYSLOG(LOGERR, "Unknown config file name!" EOL);
+ return (-1);
+ }
+
+ if ((yyin = fopen(config_file, "r")) == NULL) {
+ SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL,
+ config_file, strerror(errno), errno);
+ return (-1);
+ }
+
+ clean_config();
+ if (yyparse() < 0) {
+ SYSLOG(LOGERR, "Could not parse config file '%s'" EOL,
+ config_file);
+ e = -1;
+ } else
+ e = 0;
+
+ fclose(yyin);
+ yyin = NULL;
+
+ return (e);
+}
+
+/* Clean config */
+void
+clean_config(void)
+{
+ while (!LIST_EMPTY(&hid_devices)) {
+ hid_device_p d = LIST_FIRST(&hid_devices);
+
+ LIST_REMOVE(d, next);
+ free_hid_device(d);
+ }
+}
+
+/* Lookup config entry */
+hid_device_p
+get_hid_device(bdaddr_p bdaddr)
+{
+ hid_device_p d;
+
+ LIST_FOREACH(d, &hid_devices, next)
+ if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
+ break;
+
+ return (d);
+}
+
+/* Get next config entry */
+hid_device_p
+get_next_hid_device(hid_device_p d)
+{
+ return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next));
+}
+
+/* Print config entry */
+void
+print_hid_device(hid_device_p d, FILE *f)
+{
+ /* XXX FIXME hack! */
+ struct report_desc {
+ unsigned int size;
+ unsigned char data[1];
+ };
+ /* XXX FIXME hack! */
+
+ struct report_desc *desc = (struct report_desc *) d->desc;
+ uint32_t i;
+
+ fprintf(f,
+"device {\n" \
+" bdaddr %s;\n" \
+" control_psm 0x%x;\n" \
+" interrupt_psm 0x%x;\n" \
+" reconnect_initiate %s;\n" \
+" battery_power %s;\n" \
+" normally_connectable %s;\n" \
+" hid_descriptor {",
+ bt_ntoa(&d->bdaddr, NULL),
+ d->control_psm, d->interrupt_psm,
+ d->reconnect_initiate? "true" : "false",
+ d->battery_power? "true" : "false",
+ d->normally_connectable? "true" : "false");
+
+ for (i = 0; i < desc->size; i ++) {
+ if ((i % 8) == 0)
+ fprintf(f, "\n ");
+
+ fprintf(f, "0x%2.2x ", desc->data[i]);
+ }
+
+ fprintf(f,
+"\n" \
+" };\n" \
+"}\n");
+}
+
+/* Check config entry */
+static int32_t
+check_hid_device(hid_device_p d)
+{
+ hid_data_t hd;
+ hid_item_t hi;
+ int32_t page;
+
+ if (get_hid_device(&d->bdaddr) != NULL) {
+ SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL,
+ bt_ntoa(&d->bdaddr, NULL));
+ return (0);
+ }
+
+ if (d->control_psm == 0) {
+ SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL);
+ return (0);
+ }
+
+ if (d->interrupt_psm == 0) {
+ SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL);
+ return (0);
+ }
+
+ if (d->desc == NULL) {
+ SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL);
+ return (0);
+ }
+
+ /* XXX somehow need to make sure descriptor is valid */
+ for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) {
+ switch (hi.kind) {
+ case hid_collection:
+ case hid_endcollection:
+ case hid_output:
+ case hid_feature:
+ break;
+
+ case hid_input:
+ /* Check if the device may send keystrokes */
+ page = HID_PAGE(hi.usage);
+ if (page == HUP_KEYBOARD)
+ d->keyboard = 1;
+ break;
+ }
+ }
+ hid_end_parse(hd);
+
+ return (1);
+}
+
+/* Free config entry */
+static void
+free_hid_device(hid_device_p d)
+{
+ if (d->desc != NULL)
+ hid_dispose_report_desc(d->desc);
+
+ memset(d, 0, sizeof(*d));
+ free(d);
+}
+
+/* Re-read hids file */
+int32_t
+read_hids_file(void)
+{
+ FILE *f;
+ hid_device_t *d;
+ char *line;
+ bdaddr_t bdaddr;
+ int32_t lineno;
+
+ if (hids_file == NULL) {
+ SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
+ return (-1);
+ }
+
+ if ((f = fopen(hids_file, "r")) == NULL) {
+ if (errno == ENOENT)
+ return (0);
+
+ SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
+ hids_file, strerror(errno), errno);
+ return (-1);
+ }
+
+ for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) {
+ if ((line = strtok(buffer, "\r\n\t ")) == NULL)
+ continue; /* ignore empty lines */
+
+ if (!bt_aton(line, &bdaddr)) {
+ SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \
+ "%s:%d" EOL, hids_file, lineno);
+ continue;
+ }
+
+ if ((d = get_hid_device(&bdaddr)) != NULL)
+ d->new_device = 0;
+ }
+
+ fclose(f);
+
+ return (0);
+}
+
+/* Write hids file */
+int32_t
+write_hids_file(void)
+{
+ char path[PATH_MAX];
+ FILE *f;
+ hid_device_t *d;
+
+ if (hids_file == NULL) {
+ SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
+ return (-1);
+ }
+
+ snprintf(path, sizeof(path), "%s.new", hids_file);
+
+ if ((f = fopen(path, "w")) == NULL) {
+ SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
+ path, strerror(errno), errno);
+ return (-1);
+ }
+
+ LIST_FOREACH(d, &hid_devices, next)
+ if (!d->new_device)
+ fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL));
+
+ fclose(f);
+
+ if (rename(path, hids_file) < 0) {
+ SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \
+ "%s (%d)" EOL, path, hids_file, strerror(errno), errno);
+ unlink(path);
+ return (-1);
+ }
+
+ return (0);
+}
+
OpenPOWER on IntegriCloud