Index: head/usr.sbin/bluetooth/bthidcontrol/sdp.c =================================================================== --- head/usr.sbin/bluetooth/bthidcontrol/sdp.c (revision 322438) +++ head/usr.sbin/bluetooth/bthidcontrol/sdp.c (revision 322439) @@ -1,435 +1,492 @@ /* * sdp.c * * Copyright (c) 2004 Maksim Yevmenkin * 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: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $ * $FreeBSD$ */ #include #define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include "bthid_config.h" #include "bthidcontrol.h" static int32_t hid_sdp_query (bdaddr_t const *local, struct hid_device *hd, int32_t *error); static int32_t hid_sdp_parse_protocol_descriptor_list (sdp_attr_p a); static int32_t hid_sdp_parse_hid_descriptor (sdp_attr_p a); static int32_t hid_sdp_parse_boolean (sdp_attr_p a); +/* + * Hard coded attibute IDs taken from the + * DEVICE IDENTIFICATION PROFILE SPECIFICATION V13 p.12 + */ + +#define SDP_ATTR_DEVICE_ID_SERVICE_VENDORID 0x0201 +#define SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID 0x0202 +#define SDP_ATTR_DEVICE_ID_SERVICE_VERSION 0x0203 +#define SDP_ATTR_DEVICE_ID_RANGE SDP_ATTR_RANGE( \ + SDP_ATTR_DEVICE_ID_SERVICE_VENDORID, SDP_ATTR_DEVICE_ID_SERVICE_VERSION ) + static uint16_t service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE; +static uint16_t service_devid = SDP_SERVICE_CLASS_PNP_INFORMATION; +static uint32_t attrs_devid = SDP_ATTR_DEVICE_ID_RANGE; static uint32_t attrs[] = { SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST), SDP_ATTR_RANGE (SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS, SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS), SDP_ATTR_RANGE( 0x0205, /* HIDReconnectInitiate */ 0x0205), SDP_ATTR_RANGE( 0x0206, /* HIDDescriptorList */ 0x0206), SDP_ATTR_RANGE( 0x0209, /* HIDBatteryPower */ 0x0209), SDP_ATTR_RANGE( 0x020d, /* HIDNormallyConnectable */ 0x020d) }; #define nattrs (sizeof(attrs)/sizeof(attrs[0])) static sdp_attr_t values[8]; #define nvalues (sizeof(values)/sizeof(values[0])) static uint8_t buffer[nvalues][512]; /* * Query remote device */ #undef hid_sdp_query_exit #define hid_sdp_query_exit(e) { \ if (error != NULL) \ *error = (e); \ if (ss != NULL) { \ sdp_close(ss); \ ss = NULL; \ } \ return (((e) == 0)? 0 : -1); \ } +static void +hid_init_return_values() { + int i; + for (i = 0; i < nvalues; i ++) { + values[i].flags = SDP_ATTR_INVALID; + values[i].attr = 0; + values[i].vlen = sizeof(buffer[i]); + values[i].value = buffer[i]; + } +} + static int32_t hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error) { void *ss = NULL; - uint8_t *hid_descriptor = NULL; + uint8_t *hid_descriptor = NULL, *v; int32_t i, control_psm = -1, interrupt_psm = -1, reconnect_initiate = -1, normally_connectable = 0, battery_power = 0, - hid_descriptor_length = -1; + hid_descriptor_length = -1, type; + int16_t vendor_id = 0, product_id = 0, version = 0; if (local == NULL) local = NG_HCI_BDADDR_ANY; if (hd == NULL) hid_sdp_query_exit(EINVAL); - for (i = 0; i < nvalues; i ++) { - values[i].flags = SDP_ATTR_INVALID; - values[i].attr = 0; - values[i].vlen = sizeof(buffer[i]); - values[i].value = buffer[i]; - } + hid_init_return_values(); if ((ss = sdp_open(local, &hd->bdaddr)) == NULL) hid_sdp_query_exit(ENOMEM); if (sdp_error(ss) != 0) hid_sdp_query_exit(sdp_error(ss)); if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0) hid_sdp_query_exit(sdp_error(ss)); - sdp_close(ss); - ss = NULL; - for (i = 0; i < nvalues; i ++) { if (values[i].flags != SDP_ATTR_OK) continue; switch (values[i].attr) { case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST: control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]); break; case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS: interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]); break; case 0x0205: /* HIDReconnectInitiate */ reconnect_initiate = hid_sdp_parse_boolean(&values[i]); break; case 0x0206: /* HIDDescriptorList */ if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) { hid_descriptor = values[i].value; hid_descriptor_length = values[i].vlen; } break; case 0x0209: /* HIDBatteryPower */ battery_power = hid_sdp_parse_boolean(&values[i]); break; case 0x020d: /* HIDNormallyConnectable */ normally_connectable = hid_sdp_parse_boolean(&values[i]); break; } } + hid_init_return_values(); + + if (sdp_search(ss, 1, &service_devid, 1, &attrs_devid, nvalues, values) != 0) + hid_sdp_query_exit(sdp_error(ss)); + + sdp_close(ss); + ss = NULL; + + /* If search is successful, scan through return vals */ + for (i = 0; i < 3; i ++ ) { + if (values[i].flags == SDP_ATTR_INVALID ) + continue; + + /* Expecting tag + uint16_t on all 3 attributes */ + if (values[i].vlen != 3) + continue; + + /* Make sure, we're reading a uint16_t */ + v = values[i].value; + SDP_GET8(type, v); + if (type != SDP_DATA_UINT16 ) + continue; + + switch (values[i].attr) { + case SDP_ATTR_DEVICE_ID_SERVICE_VENDORID: + SDP_GET16(vendor_id, v); + break; + case SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID: + SDP_GET16(product_id, v); + break; + case SDP_ATTR_DEVICE_ID_SERVICE_VERSION: + SDP_GET16(version, v); + break; + default: + break; + } + } + if (control_psm == -1 || interrupt_psm == -1 || reconnect_initiate == -1 || hid_descriptor == NULL || hid_descriptor_length == -1) hid_sdp_query_exit(ENOATTR); - + hd->vendor_id = vendor_id; + hd->product_id = product_id; + hd->version = version; hd->control_psm = control_psm; hd->interrupt_psm = interrupt_psm; hd->reconnect_initiate = reconnect_initiate? 1 : 0; hd->battery_power = battery_power? 1 : 0; hd->normally_connectable = normally_connectable? 1 : 0; hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length); if (hd->desc == NULL) hid_sdp_query_exit(ENOMEM); return (0); } /* * seq len 2 * seq len 2 * uuid value 3 * uint16 value 3 * seq len 2 * uuid value 3 */ static int32_t hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a) { uint8_t *ptr = a->value; uint8_t *end = a->value + a->vlen; int32_t type, len, uuid, psm; if (end - ptr < 15) return (-1); if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) { SDP_GET8(type, ptr); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(len, ptr); break; case SDP_DATA_SEQ16: SDP_GET16(len, ptr); break; case SDP_DATA_SEQ32: SDP_GET32(len, ptr); break; default: return (-1); } if (ptr + len > end) return (-1); } SDP_GET8(type, ptr); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(len, ptr); break; case SDP_DATA_SEQ16: SDP_GET16(len, ptr); break; case SDP_DATA_SEQ32: SDP_GET32(len, ptr); break; default: return (-1); } if (ptr + len > end) return (-1); /* Protocol */ SDP_GET8(type, ptr); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(len, ptr); break; case SDP_DATA_SEQ16: SDP_GET16(len, ptr); break; case SDP_DATA_SEQ32: SDP_GET32(len, ptr); break; default: return (-1); } if (ptr + len > end) return (-1); /* UUID */ if (ptr + 3 > end) return (-1); SDP_GET8(type, ptr); switch (type) { case SDP_DATA_UUID16: SDP_GET16(uuid, ptr); if (uuid != SDP_UUID_PROTOCOL_L2CAP) return (-1); break; case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */ case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */ default: return (-1); } /* PSM */ if (ptr + 3 > end) return (-1); SDP_GET8(type, ptr); if (type != SDP_DATA_UINT16) return (-1); SDP_GET16(psm, ptr); return (psm); } /* * seq len 2 * seq len 2 * uint8 value8 2 * str value 3 */ static int32_t hid_sdp_parse_hid_descriptor(sdp_attr_p a) { uint8_t *ptr = a->value; uint8_t *end = a->value + a->vlen; int32_t type, len, descriptor_type; if (end - ptr < 9) return (-1); SDP_GET8(type, ptr); switch (type) { case SDP_DATA_SEQ8: SDP_GET8(len, ptr); break; case SDP_DATA_SEQ16: SDP_GET16(len, ptr); break; case SDP_DATA_SEQ32: SDP_GET32(len, ptr); break; default: return (-1); } if (ptr + len > end) return (-1); while (ptr < end) { /* Descriptor */ SDP_GET8(type, ptr); switch (type) { case SDP_DATA_SEQ8: if (ptr + 1 > end) return (-1); SDP_GET8(len, ptr); break; case SDP_DATA_SEQ16: if (ptr + 2 > end) return (-1); SDP_GET16(len, ptr); break; case SDP_DATA_SEQ32: if (ptr + 4 > end) return (-1); SDP_GET32(len, ptr); break; default: return (-1); } /* Descripor type */ if (ptr + 1 > end) return (-1); SDP_GET8(type, ptr); if (type != SDP_DATA_UINT8 || ptr + 1 > end) return (-1); SDP_GET8(descriptor_type, ptr); /* Descriptor value */ if (ptr + 1 > end) return (-1); SDP_GET8(type, ptr); switch (type) { case SDP_DATA_STR8: if (ptr + 1 > end) return (-1); SDP_GET8(len, ptr); break; case SDP_DATA_STR16: if (ptr + 2 > end) return (-1); SDP_GET16(len, ptr); break; case SDP_DATA_STR32: if (ptr + 4 > end) return (-1); SDP_GET32(len, ptr); break; default: return (-1); } if (ptr + len > end) return (-1); if (descriptor_type == UDESC_REPORT && len > 0) { a->value = ptr; a->vlen = len; return (0); } ptr += len; } return (-1); } /* bool8 int8 */ static int32_t hid_sdp_parse_boolean(sdp_attr_p a) { if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL) return (-1); return (a->value[1]); } /* Perform SDP query */ static int32_t hid_query(bdaddr_t *bdaddr, int argc, char **argv) { struct hid_device hd; int e; memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr)); if (hid_sdp_query(NULL, &hd, &e) < 0) { fprintf(stderr, "Could not perform SDP query on the " \ "device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL), strerror(e), e); return (FAILED); } print_hid_device(&hd, stdout); return (OK); } struct bthid_command sdp_commands[] = { { "Query", "Perform SDP query to the specified device and print HID configuration entry\n"\ "for the device. The configuration entry should be appended to the Bluetooth\n"\ "HID daemon configuration file and the daemon should be restarted.\n", hid_query }, { NULL, NULL, NULL } }; Index: head/usr.sbin/bluetooth/bthidd/bthid_config.h =================================================================== --- head/usr.sbin/bluetooth/bthidd/bthid_config.h (revision 322438) +++ head/usr.sbin/bluetooth/bthidd/bthid_config.h (revision 322439) @@ -1,70 +1,73 @@ /* * bthid_config.h */ /*- * Copyright (c) 2006 Maksim Yevmenkin * 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: bthid_config.h,v 1.4 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #ifndef _BTHID_CONFIG_H_ #define _BTHID_CONFIG_H_ 1 #define BTHIDD_CONFFILE "/etc/bluetooth/bthidd.conf" #define BTHIDD_HIDSFILE "/var/db/bthidd.hids" struct hid_device { bdaddr_t bdaddr; /* HID device BDADDR */ uint16_t control_psm; /* control PSM */ uint16_t interrupt_psm; /* interrupt PSM */ + uint16_t vendor_id; /* primary vendor id */ + uint16_t product_id; + uint16_t version; unsigned new_device : 1; unsigned reconnect_initiate : 1; unsigned battery_power : 1; unsigned normally_connectable : 1; unsigned keyboard : 1; unsigned reserved : 11; report_desc_t desc; /* HID report descriptor */ LIST_ENTRY(hid_device) next; /* link to the next */ }; typedef struct hid_device hid_device_t; typedef struct hid_device * hid_device_p; extern char const *config_file; extern char const *hids_file; int32_t read_config_file (void); void clean_config (void); hid_device_p get_hid_device (bdaddr_p bdaddr); hid_device_p get_next_hid_device (hid_device_p d); void print_hid_device (hid_device_p hid_device, FILE *f); int32_t read_hids_file (void); int32_t write_hids_file (void); #endif /* ndef _BTHID_CONFIG_H_ */ Index: head/usr.sbin/bluetooth/bthidd/bthidd.conf.sample =================================================================== --- head/usr.sbin/bluetooth/bthidd/bthidd.conf.sample (revision 322438) +++ head/usr.sbin/bluetooth/bthidd/bthidd.conf.sample (revision 322439) @@ -1,72 +1,78 @@ # $FreeBSD$ device { bdaddr 00:50:f2:e5:68:84; + vendor_id 0x0000; + product_id 0x0000; + version 0x0000; control_psm 0x11; interrupt_psm 0x13; reconnect_initiate true; normally_connectable false; hid_descriptor { 0x05 0x01 0x09 0x02 0xa1 0x01 0x85 0x02 0x09 0x01 0xa1 0x00 0x05 0x09 0x19 0x01 0x29 0x05 0x15 0x00 0x25 0x01 0x75 0x01 0x95 0x05 0x81 0x02 0x75 0x03 0x95 0x01 0x81 0x01 0x05 0x01 0x09 0x30 0x09 0x31 0x09 0x38 0x15 0x81 0x25 0x7f 0x75 0x08 0x95 0x03 0x81 0x06 0xc0 0xc0 0x05 0x0c 0x09 0x01 0xa1 0x01 0x85 0x03 0x05 0x01 0x09 0x02 0xa1 0x02 0x06 0x00 0xff 0x15 0x00 0x25 0x03 0x95 0x01 0x75 0x02 0x0a 0x01 0xfe 0x81 0x02 0x75 0x06 0x81 0x01 0xc0 0xc0 }; } device { bdaddr 00:50:f2:e3:fb:e1; + vendor_id 0x0000; + product_id 0x0000; + version 0x0000; control_psm 0x11; interrupt_psm 0x13; reconnect_initiate true; normally_connectable false; hid_descriptor { 0x05 0x01 0x09 0x06 0xa1 0x01 0x85 0x01 0x05 0x08 0x19 0x01 0x29 0x03 0x15 0x00 0x25 0x01 0x75 0x01 0x95 0x03 0x91 0x02 0x09 0x4b 0x95 0x01 0x91 0x02 0x95 0x04 0x91 0x01 0x05 0x07 0x19 0xe0 0x29 0xe7 0x95 0x08 0x81 0x02 0x75 0x08 0x95 0x01 0x81 0x01 0x19 0x00 0x29 0x91 0x26 0xff 0x00 0x95 0x06 0x81 0x00 0xc0 0x05 0x0c 0x09 0x01 0xa1 0x01 0x85 0x02 0x05 0x0c 0x15 0x00 0x25 0x01 0x75 0x01 0x95 0x1c 0x09 0xe2 0x09 0xb7 0x09 0xcd 0x09 0xea 0x09 0xe9 0x09 0xb6 0x09 0xb5 0x0a 0x83 0x01 0x0a 0x1a 0x02 0x0a 0x79 0x02 0x0a 0xab 0x01 0x0a 0x08 0x02 0x0a 0x02 0x02 0x0a 0x03 0x02 0x0a 0x07 0x02 0x0a 0x01 0x02 0x0a 0x92 0x01 0x0a 0x9c 0x01 0x09 0x95 0x0a 0x23 0x02 0x0a 0x89 0x02 0x0a 0x8b 0x02 0x0a 0x8c 0x02 0x0a 0x8a 0x01 0x0a 0x99 0x01 0x0a 0xa7 0x01 0x0a 0xb6 0x01 0x0a 0xb7 0x01 0x81 0x02 0x75 0x01 0x95 0x04 0x81 0x01 0x06 0x00 0xff 0x0a 0x02 0xff 0x26 0xff 0x00 0x95 0x01 0x75 0x08 0x81 0x02 0xc0 0x05 0x01 0x09 0x80 0xa1 0x01 0x85 0x03 0x19 0x81 0x29 0x83 0x25 0x01 0x95 0x03 0x75 0x01 0x81 0x02 0x95 0x05 0x81 0x01 0xc0 0x05 0x0c 0x09 0x01 0xa1 0x01 0x85 0x04 0x05 0x01 0x09 0x06 0xa1 0x02 0x06 0x00 0xff 0x15 0x00 0x25 0x03 0x95 0x01 0x75 0x02 0x0a 0x01 0xfe 0x81 0x02 0x75 0x06 0x81 0x01 0xc0 0xc0 0x05 0x0c 0x09 0x01 0xa1 0x01 0x85 0x05 0x05 0x01 0x09 0x06 0xa1 0x02 0x06 0x00 0xff 0x25 0x01 0x75 0x01 0x95 0x02 0x0a 0x03 0xfe 0x0a 0x04 0xfe 0x81 0x02 0x95 0x06 0x81 0x01 0xc0 0xc0 0x05 0x0c 0x09 0x01 0xa1 0x01 0x85 0xff 0x05 0x06 0x95 0x01 0x75 0x02 0x19 0x24 0x29 0x26 0x81 0x02 0x75 0x06 0x81 0x01 0xc0 }; } Index: head/usr.sbin/bluetooth/bthidd/lexer.l =================================================================== --- head/usr.sbin/bluetooth/bthidd/lexer.l (revision 322438) +++ head/usr.sbin/bluetooth/bthidd/lexer.l (revision 322439) @@ -1,106 +1,122 @@ %{ /* * lexer.l */ /*- * Copyright (c) 2006 Maksim Yevmenkin * 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: lexer.l,v 1.3 2006/09/07 21:06:53 max Exp $ * $FreeBSD$ */ #define L2CAP_SOCKET_CHECKED #include #include #include "parser.h" int yylex (void); #define YY_DECL int yylex(void) %} %option yylineno noyywrap nounput noinput delim [ \t\n] ws {delim}+ empty {delim}* comment \#.* hexdigit [0-9a-fA-F] hexbyte {hexdigit}{hexdigit}? +hexword {hexdigit}{hexdigit}?{hexdigit}?{hexdigit}? device_word device bdaddr_word bdaddr +vendor_id_word vendor_id +product_id_word product_id +version_word version control_psm_word control_psm interrupt_psm_word interrupt_psm reconnect_initiate_word reconnect_initiate battery_power_word battery_power normally_connectable_word normally_connectable hid_descriptor_word hid_descriptor true_word true false_word false bdaddrstring {hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte} hexbytestring 0x{hexbyte} +hexwordstring 0x{hexword} %% \; return (';'); \: return (':'); \{ return ('{'); \} return ('}'); {ws} ; {empty} ; {comment} ; {device_word} return (T_DEVICE); {bdaddr_word} return (T_BDADDR); +{vendor_id_word} return (T_VENDOR_ID); +{product_id_word} return (T_PRODUCT_ID); +{version_word} return (T_VERSION); {control_psm_word} return (T_CONTROL_PSM); {interrupt_psm_word} return (T_INTERRUPT_PSM); {reconnect_initiate_word} return (T_RECONNECT_INITIATE); {battery_power_word} return (T_BATTERY_POWER); {normally_connectable_word} return (T_NORMALLY_CONNECTABLE); {hid_descriptor_word} return (T_HID_DESCRIPTOR); {true_word} return (T_TRUE); {false_word} return (T_FALSE); {bdaddrstring} { return (bt_aton(yytext, &yylval.bdaddr)? T_BDADDRSTRING : T_ERROR); } {hexbytestring} { char *ep; yylval.num = strtoul(yytext, &ep, 16); return (*ep == '\0'? T_HEXBYTE : T_ERROR); + } + +{hexwordstring} { + char *ep; + + yylval.num = strtoul(yytext, &ep, 16); + + return (*ep == '\0'? T_HEXWORD : T_ERROR); } . return (T_ERROR); %% Index: head/usr.sbin/bluetooth/bthidd/parser.y =================================================================== --- head/usr.sbin/bluetooth/bthidd/parser.y (revision 322438) +++ head/usr.sbin/bluetooth/bthidd/parser.y (revision 322439) @@ -1,476 +1,503 @@ %{ /* * parser.y */ /*- * Copyright (c) 2006 Maksim Yevmenkin * 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 #define L2CAP_SOCKET_CHECKED #include #include #include #include #include #include #include #include #include #include #ifndef BTHIDCONTROL #include #include #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 T_BDADDRSTRING %token 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_HEXWORD +%token T_DEVICE T_BDADDR T_VENDOR_ID T_PRODUCT_ID T_VERSION T_CONTROL_PSM +%token T_INTERRUPT_PSM T_RECONNECT_INITIATE T_BATTERY_POWER +%token 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 + | vendor_id + | product_id + | version | 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)); } ; +vendor_id: T_VENDOR_ID T_HEXWORD + { + hid_device->vendor_id = $2; + } + ; + +product_id: T_PRODUCT_ID T_HEXWORD + { + hid_device->product_id = $2; + } + ; + +version: T_VERSION T_HEXWORD + { + hid_device->version = $2; + } + ; + 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" \ +" vendor_id 0x%04x;\n" \ +" product_id 0x%04x;\n" \ +" version 0x%04x;\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->vendor_id, d->product_id, d->version, 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); }