Path: blob/main/usr.sbin/bluetooth/bthidcontrol/sdp.c
103829 views
/*-1* sdp.c2*3* SPDX-License-Identifier: BSD-2-Clause4*5* Copyright (c) 2004 Maksim Yevmenkin <[email protected]>6* All rights reserved.7*8* Redistribution and use in source and binary forms, with or without9* modification, are permitted provided that the following conditions10* are met:11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions and the following disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND18* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE21* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL22* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS23* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)24* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT25* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY26* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF27* SUCH DAMAGE.28*29* $Id: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $30*/3132#include <sys/param.h>33#include <sys/queue.h>34#include <sys/sysctl.h>35#define L2CAP_SOCKET_CHECKED36#include <bluetooth.h>37#include <dev/usb/usb.h>38#include <dev/usb/usbhid.h>39#include <errno.h>40#include <sdp.h>41#include <stdio.h>42#include <string.h>43#include <usbhid.h>44#include "bthid_config.h"45#include "bthidcontrol.h"4647static int32_t hid_sdp_query (bdaddr_t const *local, struct hid_device *hd, int32_t *error);48static int32_t hid_sdp_parse_protocol_descriptor_list (sdp_attr_p a);49static int32_t hid_sdp_parse_hid_descriptor (sdp_attr_p a);50static int32_t hid_sdp_parse_boolean (sdp_attr_p a);5152/*53* Hard coded attribute IDs taken from the54* DEVICE IDENTIFICATION PROFILE SPECIFICATION V13 p.1255*/5657#define SDP_ATTR_DEVICE_ID_SERVICE_VENDORID 0x020158#define SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID 0x020259#define SDP_ATTR_DEVICE_ID_SERVICE_VERSION 0x020360#define SDP_ATTR_DEVICE_ID_RANGE SDP_ATTR_RANGE( \61SDP_ATTR_DEVICE_ID_SERVICE_VENDORID, SDP_ATTR_DEVICE_ID_SERVICE_VERSION )6263static uint16_t service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;64static uint16_t service_devid = SDP_SERVICE_CLASS_PNP_INFORMATION;65static uint32_t attrs_devid = SDP_ATTR_DEVICE_ID_RANGE;6667static uint32_t attrs[] = {68SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,69SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),70SDP_ATTR_RANGE (SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,71SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),72SDP_ATTR_RANGE( 0x0205, /* HIDReconnectInitiate */730x0205),74SDP_ATTR_RANGE( 0x0206, /* HIDDescriptorList */750x0206),76SDP_ATTR_RANGE( 0x0209, /* HIDBatteryPower */770x0209),78SDP_ATTR_RANGE( 0x020d, /* HIDNormallyConnectable */790x020d)80};81#define nattrs nitems(attrs)8283static sdp_attr_t values[8];84#define nvalues nitems(values)8586static uint8_t buffer[nvalues][512];8788/*89* Query remote device90*/9192#undef hid_sdp_query_exit93#define hid_sdp_query_exit(e) { \94if (error != NULL) \95*error = (e); \96if (ss != NULL) { \97sdp_close(ss); \98ss = NULL; \99} \100return (((e) == 0)? 0 : -1); \101}102103static void104hid_init_return_values() {105int i;106for (i = 0; i < nvalues; i ++) {107values[i].flags = SDP_ATTR_INVALID;108values[i].attr = 0;109values[i].vlen = sizeof(buffer[i]);110values[i].value = buffer[i];111}112}113114static int32_t115hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)116{117void *ss = NULL;118uint8_t *hid_descriptor = NULL, *v;119int32_t i, control_psm = -1, interrupt_psm = -1,120reconnect_initiate = -1,121normally_connectable = 0, battery_power = 0,122hid_descriptor_length = -1, type;123int16_t vendor_id = 0, product_id = 0, version = 0;124bdaddr_t sdp_local;125char devname[HCI_DEVNAME_SIZE];126127if (local == NULL)128local = NG_HCI_BDADDR_ANY;129if (hd == NULL)130hid_sdp_query_exit(EINVAL);131132hid_init_return_values();133134if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)135hid_sdp_query_exit(ENOMEM);136if (sdp_error(ss) != 0)137hid_sdp_query_exit(sdp_error(ss));138if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)139hid_sdp_query_exit(sdp_error(ss));140141for (i = 0; i < nvalues; i ++) {142if (values[i].flags != SDP_ATTR_OK)143continue;144145switch (values[i].attr) {146case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:147control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);148break;149150case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:151interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);152break;153154case 0x0205: /* HIDReconnectInitiate */155reconnect_initiate = hid_sdp_parse_boolean(&values[i]);156break;157158case 0x0206: /* HIDDescriptorList */159if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {160hid_descriptor = values[i].value;161hid_descriptor_length = values[i].vlen;162}163break;164165case 0x0209: /* HIDBatteryPower */166battery_power = hid_sdp_parse_boolean(&values[i]);167break;168169case 0x020d: /* HIDNormallyConnectable */170normally_connectable = hid_sdp_parse_boolean(&values[i]);171break;172}173}174175hid_init_return_values();176177if (sdp_search(ss, 1, &service_devid, 1, &attrs_devid, nvalues, values) != 0)178hid_sdp_query_exit(sdp_error(ss));179180/* Try extract HCI bdaddr from opened SDP session */181if (sdp_get_lcaddr(ss, &sdp_local) != 0 ||182bt_devname(devname, &sdp_local) == 0)183hid_sdp_query_exit(ENOATTR);184185sdp_close(ss);186ss = NULL;187188/* If search is successful, scan through return vals */189for (i = 0; i < 3; i ++ ) {190if (values[i].flags == SDP_ATTR_INVALID )191continue;192193/* Expecting tag + uint16_t on all 3 attributes */194if (values[i].vlen != 3)195continue;196197/* Make sure, we're reading a uint16_t */198v = values[i].value;199SDP_GET8(type, v);200if (type != SDP_DATA_UINT16 )201continue;202203switch (values[i].attr) {204case SDP_ATTR_DEVICE_ID_SERVICE_VENDORID:205SDP_GET16(vendor_id, v);206break;207case SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID:208SDP_GET16(product_id, v);209break;210case SDP_ATTR_DEVICE_ID_SERVICE_VERSION:211SDP_GET16(version, v);212break;213default:214break;215}216}217218if (control_psm == -1 || interrupt_psm == -1 ||219reconnect_initiate == -1 ||220hid_descriptor == NULL || hid_descriptor_length == -1)221hid_sdp_query_exit(ENOATTR);222hd->name = bt_devremote_name_gen(devname, &hd->bdaddr);223hd->vendor_id = vendor_id;224hd->product_id = product_id;225hd->version = version;226hd->control_psm = control_psm;227hd->interrupt_psm = interrupt_psm;228hd->reconnect_initiate = reconnect_initiate? 1 : 0;229hd->battery_power = battery_power? 1 : 0;230hd->normally_connectable = normally_connectable? 1 : 0;231hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);232if (hd->desc == NULL)233hid_sdp_query_exit(ENOMEM);234235return (0);236}237238/*239* seq len 2240* seq len 2241* uuid value 3242* uint16 value 3243* seq len 2244* uuid value 3245*/246247static int32_t248hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)249{250uint8_t *ptr = a->value;251uint8_t *end = a->value + a->vlen;252int32_t type, len, uuid, psm;253254if (end - ptr < 15)255return (-1);256257if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {258SDP_GET8(type, ptr);259switch (type) {260case SDP_DATA_SEQ8:261SDP_GET8(len, ptr);262break;263264case SDP_DATA_SEQ16:265SDP_GET16(len, ptr);266break;267268case SDP_DATA_SEQ32:269SDP_GET32(len, ptr);270break;271272default:273return (-1);274}275if (ptr + len > end)276return (-1);277}278279SDP_GET8(type, ptr);280switch (type) {281case SDP_DATA_SEQ8:282SDP_GET8(len, ptr);283break;284285case SDP_DATA_SEQ16:286SDP_GET16(len, ptr);287break;288289case SDP_DATA_SEQ32:290SDP_GET32(len, ptr);291break;292293default:294return (-1);295}296if (ptr + len > end)297return (-1);298299/* Protocol */300SDP_GET8(type, ptr);301switch (type) {302case SDP_DATA_SEQ8:303SDP_GET8(len, ptr);304break;305306case SDP_DATA_SEQ16:307SDP_GET16(len, ptr);308break;309310case SDP_DATA_SEQ32:311SDP_GET32(len, ptr);312break;313314default:315return (-1);316}317if (ptr + len > end)318return (-1);319320/* UUID */321if (ptr + 3 > end)322return (-1);323SDP_GET8(type, ptr);324switch (type) {325case SDP_DATA_UUID16:326SDP_GET16(uuid, ptr);327if (uuid != SDP_UUID_PROTOCOL_L2CAP)328return (-1);329break;330331case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */332case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */333default:334return (-1);335}336337/* PSM */338if (ptr + 3 > end)339return (-1);340SDP_GET8(type, ptr);341if (type != SDP_DATA_UINT16)342return (-1);343SDP_GET16(psm, ptr);344345return (psm);346}347348/*349* seq len 2350* seq len 2351* uint8 value8 2352* str value 3353*/354355static int32_t356hid_sdp_parse_hid_descriptor(sdp_attr_p a)357{358uint8_t *ptr = a->value;359uint8_t *end = a->value + a->vlen;360int32_t type, len, descriptor_type;361362if (end - ptr < 9)363return (-1);364365SDP_GET8(type, ptr);366switch (type) {367case SDP_DATA_SEQ8:368SDP_GET8(len, ptr);369break;370371case SDP_DATA_SEQ16:372SDP_GET16(len, ptr);373break;374375case SDP_DATA_SEQ32:376SDP_GET32(len, ptr);377break;378379default:380return (-1);381}382if (ptr + len > end)383return (-1);384385while (ptr < end) {386/* Descriptor */387SDP_GET8(type, ptr);388switch (type) {389case SDP_DATA_SEQ8:390if (ptr + 1 > end)391return (-1);392SDP_GET8(len, ptr);393break;394395case SDP_DATA_SEQ16:396if (ptr + 2 > end)397return (-1);398SDP_GET16(len, ptr);399break;400401case SDP_DATA_SEQ32:402if (ptr + 4 > end)403return (-1);404SDP_GET32(len, ptr);405break;406407default:408return (-1);409}410411/* Descripor type */412if (ptr + 1 > end)413return (-1);414SDP_GET8(type, ptr);415if (type != SDP_DATA_UINT8 || ptr + 1 > end)416return (-1);417SDP_GET8(descriptor_type, ptr);418419/* Descriptor value */420if (ptr + 1 > end)421return (-1);422SDP_GET8(type, ptr);423switch (type) {424case SDP_DATA_STR8:425if (ptr + 1 > end)426return (-1);427SDP_GET8(len, ptr);428break;429430case SDP_DATA_STR16:431if (ptr + 2 > end)432return (-1);433SDP_GET16(len, ptr);434break;435436case SDP_DATA_STR32:437if (ptr + 4 > end)438return (-1);439SDP_GET32(len, ptr);440break;441442default:443return (-1);444}445if (ptr + len > end)446return (-1);447448if (descriptor_type == UDESC_REPORT && len > 0) {449a->value = ptr;450a->vlen = len;451452return (0);453}454455ptr += len;456}457458return (-1);459}460461/* bool8 int8 */462static int32_t463hid_sdp_parse_boolean(sdp_attr_p a)464{465if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)466return (-1);467468return (a->value[1]);469}470471/* Perform SDP query */472static int32_t473hid_query(bdaddr_t *bdaddr, int argc, char **argv)474{475struct hid_device hd;476int e;477478memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));479if (hid_sdp_query(NULL, &hd, &e) < 0) {480fprintf(stderr, "Could not perform SDP query on the " \481"device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),482strerror(e), e);483return (FAILED);484}485486print_hid_device(&hd, stdout);487488return (OK);489}490491struct bthid_command sdp_commands[] =492{493{494"Query",495"Perform SDP query to the specified device and print HID configuration entry\n"\496"for the device. The configuration entry should be appended to the Bluetooth\n"\497"HID daemon configuration file and the daemon should be restarted.\n",498hid_query499},500{ NULL, NULL, NULL }501};502503504505