NetBSD/usr.bin/rfcomm_sppd/rfcomm_sdp.c
gdamore a5c89047c0 Initial import of bluetooth stack on behalf of Iain Hibbert. (plunky@,
NetBSD Foundation Membership still pending.)  This stack was written by
Iain under sponsorship from Itronix Inc.

The stack includes support for rfcomm networking (networking via your
bluetooth enabled cell phone), hid devices (keyboards/mice), and headsets.

Drivers for both PCMCIA and USB bluetooth controllers are included.
2006-06-19 15:44:33 +00:00

301 lines
9.0 KiB
C

/* $NetBSD: rfcomm_sdp.c,v 1.1 2006/06/19 15:44:56 gdamore Exp $ */
/*-
* Copyright (c) 2006 Itronix Inc.
* 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.
* 3. The name of Itronix Inc. may not be used to endorse
* or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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.
*/
/*
* rfcomm_sdp.c
*
* Copyright (c) 2003 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: rfcomm_sdp.c,v 1.1 2006/06/19 15:44:56 gdamore Exp $
* $FreeBSD: src/usr.bin/bluetooth/rfcomm_sppd/rfcomm_sdp.c,v 1.2 2004/04/09 23:26:16 emax Exp $
*/
#include <sys/cdefs.h>
__RCSID("$NetBSD: rfcomm_sdp.c,v 1.1 2006/06/19 15:44:56 gdamore Exp $");
#include <bluetooth.h>
#include <errno.h>
#include <sdp.h>
#include <stdio.h>
#include "rfcomm_sdp.h"
#undef PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE
#define PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE 256
#undef PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE
#define PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE 12
static int rfcomm_proto_list_parse (uint8_t const *start, uint8_t const *end,
uint8_t *channel, int *error);
/*
* Lookup RFCOMM channel number in the Protocol Descriptor List
*/
#undef rfcomm_channel_lookup_exit
#define rfcomm_channel_lookup_exit(e) { \
if (error != NULL) \
*error = (e); \
if (ss != NULL) { \
sdp_close(ss); \
ss = NULL; \
} \
return (((e) == 0)? 0 : -1); \
}
int
rfcomm_channel_lookup(bdaddr_t const *local, bdaddr_t const *remote,
int service, uint8_t *channel, int *error)
{
uint8_t buffer[PROTOCOL_DESCRIPTOR_LIST_BUFFER_SIZE];
void *ss = NULL;
uint16_t serv = (uint16_t) service;
uint32_t attr = SDP_ATTR_RANGE(
SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
sdp_attr_t proto = { SDP_ATTR_INVALID,0,sizeof(buffer),buffer };
uint32_t type, len;
if (local == NULL)
local = BDADDR_ANY;
if (remote == NULL || channel == NULL)
rfcomm_channel_lookup_exit(EINVAL);
if ((ss = sdp_open(local, remote)) == NULL)
rfcomm_channel_lookup_exit(ENOMEM);
if (sdp_error(ss) != 0)
rfcomm_channel_lookup_exit(sdp_error(ss));
if (sdp_search(ss, 1, &serv, 1, &attr, 1, &proto) != 0)
rfcomm_channel_lookup_exit(sdp_error(ss));
if (proto.flags != SDP_ATTR_OK)
rfcomm_channel_lookup_exit(ENOATTR);
sdp_close(ss);
ss = NULL;
/*
* If it is possible for more than one kind of protocol stack to be
* used to gain access to the service, the ProtocolDescriptorList
* takes the form of a data element alternative. We always use the
* first protocol stack.
*
* A minimal Protocol Descriptor List for RFCOMM based service would
* look like
*
* seq8 len8 - 2 bytes
* seq8 len8 - 2 bytes
* uuid16 value16 - 3 bytes L2CAP
* seq8 len8 - 2 bytes
* uuid16 value16 - 3 bytes RFCOMM
* uint8 value8 - 2 bytes RFCOMM param #1
* =========
* 14 bytes
*
* Lets not count first [seq8 len8] wrapper, so the minimal size of
* the Protocol Descriptor List (the data we are actually interested
* in) for RFCOMM based service would be 12 bytes.
*/
if (proto.vlen < PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE)
rfcomm_channel_lookup_exit(EINVAL);
SDP_GET8(type, proto.value);
if (type == SDP_DATA_ALT8) {
SDP_GET8(len, proto.value);
} else if (type == SDP_DATA_ALT16) {
SDP_GET16(len, proto.value);
} else if (type == SDP_DATA_ALT32) {
SDP_GET32(len, proto.value);
} else
len = 0;
if (len > 0)
SDP_GET8(type, proto.value);
switch (type) {
case SDP_DATA_SEQ8:
SDP_GET8(len, proto.value);
break;
case SDP_DATA_SEQ16:
SDP_GET16(len, proto.value);
break;
case SDP_DATA_SEQ32:
SDP_GET32(len, proto.value);
break;
default:
rfcomm_channel_lookup_exit(ENOATTR);
/* NOT REACHED */
}
if (len < PROTOCOL_DESCRIPTOR_LIST_MINIMAL_SIZE)
rfcomm_channel_lookup_exit(EINVAL);
return (rfcomm_proto_list_parse(proto.value,
buffer + proto.vlen, channel, error));
}
/*
* Parse protocol descriptor list
*
* The ProtocolDescriptorList attribute describes one or more protocol
* stacks that may be used to gain access to the service described by
* the service record. If the ProtocolDescriptorList describes a single
* stack, it takes the form of a data element sequence in which each
* element of the sequence is a protocol descriptor.
*/
#undef rfcomm_proto_list_parse_exit
#define rfcomm_proto_list_parse_exit(e) { \
if (error != NULL) \
*error = (e); \
return (((e) == 0)? 0 : -1); \
}
static int
rfcomm_proto_list_parse(uint8_t const *start, uint8_t const *end,
uint8_t *channel, int *error)
{
int type, len, value;
while (start < end) {
/*
* Parse protocol descriptor
*
* A protocol descriptor identifies a communications protocol
* and provides protocol specific parameters. A protocol
* descriptor is represented as a data element sequence. The
* first data element in the sequence must be the UUID that
* identifies the protocol. Additional data elements optionally
* provide protocol specific information, such as the L2CAP
* protocol/service multiplexer (PSM) and the RFCOMM server
* channel number (CN).
*/
/* We must have at least one byte (type) */
if (end - start < 1)
rfcomm_proto_list_parse_exit(EINVAL)
SDP_GET8(type, start);
switch (type) {
case SDP_DATA_SEQ8:
SDP_GET8(len, start);
break;
case SDP_DATA_SEQ16:
SDP_GET16(len, start);
break;
case SDP_DATA_SEQ32:
SDP_GET32(len, start);
break;
default:
rfcomm_proto_list_parse_exit(ENOATTR)
/* NOT REACHED */
}
/* We must have at least 3 bytes (type + UUID16) */
if (end - start < 3)
rfcomm_proto_list_parse_exit(EINVAL);
/* Get protocol UUID */
SDP_GET8(type, start); len -= sizeof(uint8_t);
switch (type) {
case SDP_DATA_UUID16:
SDP_GET16(value, start); len -= sizeof(uint16_t);
if (value != SDP_UUID_PROTOCOL_RFCOMM)
goto next_protocol;
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:
rfcomm_proto_list_parse_exit(ENOATTR);
/* NOT REACHED */
}
/*
* First protocol specific parameter for RFCOMM procotol must
* be uint8 that represents RFCOMM channel number. So we must
* have at least two bytes.
*/
if (end - start < 2)
rfcomm_proto_list_parse_exit(EINVAL);
SDP_GET8(type, start);
if (type != SDP_DATA_UINT8)
rfcomm_proto_list_parse_exit(ENOATTR);
SDP_GET8(*channel, start);
rfcomm_proto_list_parse_exit(0);
/* NOT REACHED */
next_protocol:
start += len;
}
/*
* If we got here then it means we could not find RFCOMM protocol
* descriptor, but the reply format was actually valid.
*/
rfcomm_proto_list_parse_exit(ENOATTR);
}