summaryrefslogtreecommitdiffstats
path: root/usr.sbin/bluetooth/sdpcontrol/search.c
diff options
context:
space:
mode:
authoremax <emax@FreeBSD.org>2003-10-12 22:04:24 +0000
committeremax <emax@FreeBSD.org>2003-10-12 22:04:24 +0000
commit41bb0e8fd2568243020852e22a6d176bccfa60cd (patch)
tree0ae0c2be63f9f9161693789721b96beb9cabcc77 /usr.sbin/bluetooth/sdpcontrol/search.c
parent66feac7937e372f502539e7d443aee80a25abe16 (diff)
downloadFreeBSD-src-41bb0e8fd2568243020852e22a6d176bccfa60cd.zip
FreeBSD-src-41bb0e8fd2568243020852e22a6d176bccfa60cd.tar.gz
Update Bluetooth code.
Reviewed by: M. Warner Losh <imp@bsdimp.com>; John Hay <jhay@freebsd.org> Approved by: M. Warner Losh <imp@bsdimp.com> (mentor)
Diffstat (limited to 'usr.sbin/bluetooth/sdpcontrol/search.c')
-rw-r--r--usr.sbin/bluetooth/sdpcontrol/search.c707
1 files changed, 707 insertions, 0 deletions
diff --git a/usr.sbin/bluetooth/sdpcontrol/search.c b/usr.sbin/bluetooth/sdpcontrol/search.c
new file mode 100644
index 0000000..7180952
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpcontrol/search.c
@@ -0,0 +1,707 @@
+/*
+ * search.c
+ *
+ * Copyright (c) 2001-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: search.c,v 1.2 2003/09/08 17:35:15 max Exp $
+ * $FreeBSD$
+ */
+
+#include <bluetooth.h>
+#include <ctype.h>
+#include <sdp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "sdpcontrol.h"
+
+/* List of the attributes we are looking for */
+static u_int32_t attrs[] =
+{
+ SDP_ATTR_RANGE( SDP_ATTR_SERVICE_RECORD_HANDLE,
+ SDP_ATTR_SERVICE_RECORD_HANDLE),
+ SDP_ATTR_RANGE( SDP_ATTR_SERVICE_CLASS_ID_LIST,
+ SDP_ATTR_SERVICE_CLASS_ID_LIST),
+ SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
+ SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
+ SDP_ATTR_RANGE( SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,
+ SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST)
+};
+#define attrs_len (sizeof(attrs)/sizeof(attrs[0]))
+
+/* Buffer for the attributes */
+#define NRECS 25 /* request this much records from the SDP server */
+#define BSIZE 256 /* one attribute buffer size */
+static u_int8_t buffer[NRECS * attrs_len][BSIZE];
+
+/* SDP attributes */
+static sdp_attr_t values[NRECS * attrs_len];
+#define values_len (sizeof(values)/sizeof(values[0]))
+
+/*
+ * Print Service Class ID List
+ *
+ * The ServiceClassIDList attribute consists of a data element sequence in
+ * which each data element is a UUID representing the service classes that
+ * a given service record conforms to. The UUIDs are listed in order from
+ * the most specific class to the most general class. The ServiceClassIDList
+ * must contain at least one service class UUID.
+ */
+
+static void
+print_service_class_id_list(u_int8_t const *start, u_int8_t const *end)
+{
+ u_int32_t type, len, value;
+
+ if (end - start < 2) {
+ fprintf(stderr, "Invalid Service Class ID List. " \
+ "Too short, len=%d\n", end - start);
+ return;
+ }
+
+ 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:
+ fprintf(stderr, "Invalid Service Class ID List. " \
+ "Not a sequence, type=%#x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+
+ while (start < end) {
+ SDP_GET8(type, start);
+ switch (type) {
+ case SDP_DATA_UUID16:
+ SDP_GET16(value, start);
+ fprintf(stdout, "\t%s (%#4.4x)\n",
+ sdp_uuid2desc(value), value);
+ break;
+
+ case SDP_DATA_UUID32:
+ SDP_GET32(value, start);
+ fprintf(stdout, "\t%#8.8x\n", value);
+ break;
+
+ case SDP_DATA_UUID128: {
+ int128_t uuid;
+
+ SDP_GET128(&uuid, start);
+ fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
+ *(u_int32_t *)&uuid.b[0],
+ *(u_int16_t *)&uuid.b[4],
+ *(u_int16_t *)&uuid.b[6],
+ *(u_int16_t *)&uuid.b[8],
+ *(u_int16_t *)&uuid.b[10],
+ *(u_int32_t *)&uuid.b[12]);
+ } break;
+
+ default:
+ fprintf(stderr, "Invalid Service Class ID List. " \
+ "Not a UUID, type=%#x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+ }
+} /* print_service_class_id_list */
+
+/*
+ * Print Protocol Descriptor List
+ *
+ * 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. Each protocol descriptor is, in turn, a data element
+ * sequence whose first element is a UUID identifying the protocol and whose
+ * successive elements are protocol-specific parameters. The protocol
+ * descriptors are listed in order from the lowest layer protocol to the
+ * highest layer protocol used to gain access to the service. 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 where each member is a data element sequence as described above.
+ */
+
+static void
+print_protocol_descriptor(u_int8_t const *start, u_int8_t const *end)
+{
+ union {
+ uint8_t uint8;
+ uint16_t uint16;
+ uint32_t uint32;
+ uint64_t uint64;
+ int128_t int128;
+ } value;
+ u_int32_t type, len, param;
+
+ /* Get Protocol UUID */
+ SDP_GET8(type, start);
+ switch (type) {
+ case SDP_DATA_UUID16:
+ SDP_GET16(value.uint16, start);
+ fprintf(stdout, "\t%s (%#4.4x)\n", sdp_uuid2desc(value.uint16),
+ value.uint16);
+ break;
+
+ case SDP_DATA_UUID32:
+ SDP_GET32(value.uint32, start);
+ fprintf(stdout, "\t%#8.8x\n", value.uint32);
+ break;
+
+ case SDP_DATA_UUID128:
+ SDP_GET128(&value.int128, start);
+ fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
+ *(u_int32_t *)&value.int128.b[0],
+ *(u_int16_t *)&value.int128.b[4],
+ *(u_int16_t *)&value.int128.b[6],
+ *(u_int16_t *)&value.int128.b[8],
+ *(u_int16_t *)&value.int128.b[10],
+ *(u_int32_t *)&value.int128.b[12]);
+ break;
+
+ default:
+ fprintf(stderr, "Invalid Protocol Descriptor. " \
+ "Not a UUID, type=%#x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+
+ /* Protocol specific parameters */
+ for (param = 1; start < end; param ++) {
+ fprintf(stdout, "\t\tProtocol specific parameter #%d: ", param);
+
+ SDP_GET8(type, start);
+ switch (type) {
+ case SDP_DATA_NIL:
+ fprintf(stdout, "nil\n");
+ break;
+
+ case SDP_DATA_UINT8:
+ case SDP_DATA_INT8:
+ case SDP_DATA_BOOL:
+ SDP_GET8(value.uint8, start);
+ fprintf(stdout, "u/int8/bool %u\n", value.uint8);
+ break;
+
+ case SDP_DATA_UINT16:
+ case SDP_DATA_INT16:
+ case SDP_DATA_UUID16:
+ SDP_GET16(value.uint16, start);
+ fprintf(stdout, "u/int/uuid16 %u\n", value.uint16);
+ break;
+
+ case SDP_DATA_UINT32:
+ case SDP_DATA_INT32:
+ case SDP_DATA_UUID32:
+ SDP_GET32(value.uint32, start);
+ fprintf(stdout, "u/int/uuid32 %u\n", value.uint32);
+ break;
+
+ case SDP_DATA_UINT64:
+ case SDP_DATA_INT64:
+ SDP_GET64(value.uint64, start);
+ fprintf(stdout, "u/int64 %llu\n", value.uint64);
+ break;
+
+ case SDP_DATA_UINT128:
+ case SDP_DATA_INT128:
+ case SDP_DATA_UUID128:
+ SDP_GET128(&value.int128, start);
+ fprintf(stdout, "u/int/uuid128 %#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
+ *(u_int32_t *)&value.int128.b[0],
+ *(u_int16_t *)&value.int128.b[4],
+ *(u_int16_t *)&value.int128.b[6],
+ *(u_int16_t *)&value.int128.b[8],
+ *(u_int16_t *)&value.int128.b[10],
+ *(u_int32_t *)&value.int128.b[12]);
+ break;
+
+ case SDP_DATA_STR8:
+ case SDP_DATA_URL8:
+ SDP_GET8(len, start);
+ fprintf(stdout, "%*.*s\n", len, len, (char *) start);
+ start += len;
+ break;
+
+ case SDP_DATA_STR16:
+ case SDP_DATA_URL16:
+ SDP_GET16(len, start);
+ fprintf(stdout, "%*.*s\n", len, len, (char *) start);
+ start += len;
+ break;
+
+ case SDP_DATA_STR32:
+ case SDP_DATA_URL32:
+ SDP_GET32(len, start);
+ fprintf(stdout, "%*.*s\n", len, len, (char *) start);
+ start += len;
+ break;
+
+ case SDP_DATA_SEQ8:
+ case SDP_DATA_ALT8:
+ SDP_GET8(len, start);
+ for (; len > 0; start ++, len --)
+ fprintf(stdout, "%#2.2x ", *start);
+ fprintf(stdout, "\n");
+ break;
+
+ case SDP_DATA_SEQ16:
+ case SDP_DATA_ALT16:
+ SDP_GET16(len, start);
+ for (; len > 0; start ++, len --)
+ fprintf(stdout, "%#2.2x ", *start);
+ fprintf(stdout, "\n");
+ break;
+
+ case SDP_DATA_SEQ32:
+ case SDP_DATA_ALT32:
+ SDP_GET32(len, start);
+ for (; len > 0; start ++, len --)
+ fprintf(stdout, "%#2.2x ", *start);
+ fprintf(stdout, "\n");
+ break;
+
+ default:
+ fprintf(stderr, "Invalid Protocol Descriptor. " \
+ "Unknown data type: %#02x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+ }
+} /* print_protocol_descriptor */
+
+static void
+print_protocol_descriptor_list(u_int8_t const *start, u_int8_t const *end)
+{
+ u_int32_t type, len;
+
+ if (end - start < 2) {
+ fprintf(stderr, "Invalid Protocol Descriptor List. " \
+ "Too short, len=%d\n", end - start);
+ return;
+ }
+
+ 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:
+ fprintf(stderr, "Invalid Protocol Descriptor List. " \
+ "Not a sequence, type=%#x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+
+ while (start < end) {
+ 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:
+ fprintf(stderr, "Invalid Protocol Descriptor List. " \
+ "Not a sequence, type=%#x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+
+ print_protocol_descriptor(start, start + len);
+ start += len;
+ }
+} /* print_protocol_descriptor_list */
+
+/*
+ * Print Bluetooth Profile Descriptor List
+ *
+ * The BluetoothProfileDescriptorList attribute consists of a data element
+ * sequence in which each element is a profile descriptor that contains
+ * information about a Bluetooth profile to which the service represented by
+ * this service record conforms. Each profile descriptor is a data element
+ * sequence whose first element is the UUID assigned to the profile and whose
+ * second element is a 16-bit profile version number. Each version of a profile
+ * is assigned a 16-bit unsigned integer profile version number, which consists
+ * of two 8-bit fields. The higher-order 8 bits contain the major version
+ * number field and the lower-order 8 bits contain the minor version number
+ * field.
+ */
+
+static void
+print_bluetooth_profile_descriptor_list(u_int8_t const *start, u_int8_t const *end)
+{
+ u_int32_t type, len, value;
+
+ if (end - start < 2) {
+ fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \
+ "Too short, len=%d\n", end - start);
+ return;
+ }
+
+ 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:
+ fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \
+ "Not a sequence, type=%#x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+
+ while (start < end) {
+ 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:
+ fprintf(stderr, "Invalid Bluetooth Profile " \
+ "Descriptor List. " \
+ "Not a sequence, type=%#x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+
+ /* Get UUID */
+ SDP_GET8(type, start);
+ switch (type) {
+ case SDP_DATA_UUID16:
+ SDP_GET16(value, start);
+ fprintf(stdout, "\t%s (%#4.4x) ",
+ sdp_uuid2desc(value), value);
+ break;
+
+ case SDP_DATA_UUID32:
+ SDP_GET32(value, start);
+ fprintf(stdout, "\t%#8.8x ", value);
+ break;
+
+ case SDP_DATA_UUID128: {
+ int128_t uuid;
+
+ SDP_GET128(&uuid, start);
+ fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x ",
+ *(u_int32_t *)&uuid.b[0],
+ *(u_int16_t *)&uuid.b[4],
+ *(u_int16_t *)&uuid.b[6],
+ *(u_int16_t *)&uuid.b[8],
+ *(u_int16_t *)&uuid.b[10],
+ *(u_int32_t *)&uuid.b[12]);
+ } break;
+
+ default:
+ fprintf(stderr, "Invalid Bluetooth Profile " \
+ "Descriptor List. " \
+ "Not a UUID, type=%#x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+
+ /* Get version */
+ SDP_GET8(type, start);
+ if (type != SDP_DATA_UINT16) {
+ fprintf(stderr, "Invalid Bluetooth Profile " \
+ "Descriptor List. " \
+ "Invalid version type=%#x\n", type);
+ return;
+ }
+
+ SDP_GET16(value, start);
+ fprintf(stdout, "ver. %d.%d\n",
+ (value >> 8) & 0xff, value & 0xff);
+ }
+} /* print_bluetooth_profile_descriptor_list */
+
+/* Perform SDP search command */
+static int
+do_sdp_search(void *xs, int argc, char **argv)
+{
+ char *ep = NULL;
+ int32_t n, type, value;
+ u_int16_t service;
+
+ /* Parse command line arguments */
+ switch (argc) {
+ case 1:
+ n = strtoul(argv[0], &ep, 16);
+ if (*ep != 0) {
+ switch (tolower(argv[0][0])) {
+ case 'c': /* CIP/CTP */
+ switch (tolower(argv[0][1])) {
+ case 'i':
+ service = SDP_SERVICE_CLASS_COMMON_ISDN_ACCESS;
+ break;
+
+ case 't':
+ service = SDP_SERVICE_CLASS_CORDLESS_TELEPHONY;
+ break;
+
+ default:
+ return (USAGE);
+ /* NOT REACHED */
+ }
+ break;
+
+ case 'd': /* DialUp Networking */
+ service = SDP_SERVICE_CLASS_DIALUP_NETWORKING;
+ break;
+
+ case 'f': /* Fax/OBEX File Transfer */
+ switch (tolower(argv[0][1])) {
+ case 'a':
+ service = SDP_SERVICE_CLASS_FAX;
+ break;
+
+ case 't':
+ service = SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER;
+ break;
+
+ default:
+ return (USAGE);
+ /* NOT REACHED */
+ }
+ break;
+
+ case 'g': /* GN */
+ service = SDP_SERVICE_CLASS_GN;
+ break;
+
+ case 'h': /* Headset/HID */
+ switch (tolower(argv[0][1])) {
+ case 'i':
+ service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
+ break;
+
+ case 's':
+ service = SDP_SERVICE_CLASS_HEADSET;
+ break;
+
+ default:
+ return (USAGE);
+ /* NOT REACHED */
+ }
+ break;
+
+ case 'l': /* LAN Access Using PPP */
+ service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP;
+ break;
+
+ case 'n': /* NAP */
+ service = SDP_SERVICE_CLASS_NAP;
+ break;
+
+ case 'o': /* OBEX Object Push */
+ service = SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH;
+ break;
+
+ case 's': /* Serial Port */
+ service = SDP_SERVICE_CLASS_SERIAL_PORT;
+ break;
+
+ default:
+ return (USAGE);
+ /* NOT REACHED */
+ }
+ } else
+ service = (u_int16_t) n;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ if (service < SDP_SERVICE_CLASS_SERVICE_DISCOVERY_SERVER)
+ return (USAGE);
+
+ /* Initialize attribute values array */
+ for (n = 0; n < values_len; n ++) {
+ values[n].flags = SDP_ATTR_INVALID;
+ values[n].attr = 0;
+ values[n].vlen = BSIZE;
+ values[n].value = buffer[n];
+ }
+
+ /* Do SDP Service Search Attribute Request */
+ n = sdp_search(xs, 1, &service, attrs_len, attrs, values_len, values);
+ if (n != 0)
+ return (ERROR);
+
+ /* Print attributes values */
+ for (n = 0; n < values_len; n ++) {
+ if (values[n].flags != SDP_ATTR_OK)
+ break;
+
+ switch (values[n].attr) {
+ case SDP_ATTR_SERVICE_RECORD_HANDLE:
+ fprintf(stdout, "\n");
+ if (values[n].vlen == 5) {
+ SDP_GET8(type, values[n].value);
+ if (type == SDP_DATA_UINT32) {
+ SDP_GET32(value, values[n].value);
+ fprintf(stdout, "Record Handle: " \
+ "%#8.8x\n", value);
+ } else
+ fprintf(stderr, "Invalid type=%#x " \
+ "Record Handle " \
+ "attribute!\n", type);
+ } else
+ fprintf(stderr, "Invalid size=%d for Record " \
+ "Handle attribute\n",
+ values[n].vlen);
+ break;
+
+ case SDP_ATTR_SERVICE_CLASS_ID_LIST:
+ fprintf(stdout, "Service Class ID List:\n");
+ print_service_class_id_list(values[n].value,
+ values[n].value + values[n].vlen);
+ break;
+
+ case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
+ fprintf(stdout, "Protocol Descriptor List:\n");
+ print_protocol_descriptor_list(values[n].value,
+ values[n].value + values[n].vlen);
+ break;
+
+ case SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST:
+ fprintf(stdout, "Bluetooth Profile Descriptor List:\n");
+ print_bluetooth_profile_descriptor_list(values[n].value,
+ values[n].value + values[n].vlen);
+ break;
+
+ default:
+ fprintf(stderr, "Unexpected attribute ID=%#4.4x\n",
+ values[n].attr);
+ break;
+ }
+ }
+
+ return (OK);
+} /* do_sdp_search */
+
+/* Perform SDP browse command */
+static int
+do_sdp_browse(void *xs, int argc, char **argv)
+{
+#undef _STR
+#undef STR
+#define _STR(x) #x
+#define STR(x) _STR(x)
+
+ static char const * const av[] = {
+ STR(SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP),
+ NULL
+ };
+
+ switch (argc) {
+ case 0:
+ argc = 1;
+ argv = (char **) av;
+ /* FALL THROUGH */
+ case 1:
+ return (do_sdp_search(xs, argc, argv));
+ }
+
+ return (USAGE);
+} /* do_sdp_browse */
+
+/* List of SDP commands */
+struct sdp_command sdp_commands[] = {
+{
+"Browse [<Group>]",
+"Browse for services. The <Group> parameter is a 16-bit UUID of the group\n" \
+"to browse. If omitted <Group> is set to Public Browse Group.\n\n" \
+"\t<Group> - xxxx; 16-bit UUID of the group to browse\n",
+do_sdp_browse
+},
+{
+"Search <Service>",
+"Search for the <Service>. The <Service> parameter is a 16-bit UUID of the\n" \
+"service to search for. For some services it is possible to use service name\n"\
+"instead of service UUID\n\n" \
+"\t<Service> - xxxx; 16-bit UUID of the service to search for\n\n" \
+"\tKnown service names\n" \
+"\t===================\n" \
+"\tCIP - Common ISDN Access\n" \
+"\tCTP - Cordless Telephony\n" \
+"\tDUN - DialUp Networking\n" \
+"\tFAX - Fax\n" \
+"\tFTRN - OBEX File Transfer\n" \
+"\tGN - GN\n" \
+"\tHID - Human Interface Device\n" \
+"\tHSET - Headset\n" \
+"\tLAN - LAN Access Using PPP\n" \
+"\tNAP - Network Access Point\n" \
+"\tOPUSH - OBEX Object Push\n" \
+"\tSP - Serial Port\n",
+do_sdp_search
+},
+{ NULL, NULL, NULL }
+};
+
OpenPOWER on IntegriCloud