mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-29 23:24:20 +08:00
4056 lines
110 KiB
C
4056 lines
110 KiB
C
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2001-2002 Nokia Corporation
|
|
* Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
|
|
* Copyright (C) 2002-2008 Marcel Holtmann <marcel@holtmann.org>
|
|
* Copyright (C) 2002-2003 Stephen Crane <steve.crane@rococosoft.com>
|
|
* Copyright (C) 2002-2003 Jean Tourrilhes <jt@hpl.hp.com>
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <getopt.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include <bluetooth/bluetooth.h>
|
|
#include <bluetooth/hci.h>
|
|
#include <bluetooth/hci_lib.h>
|
|
#include <bluetooth/sdp.h>
|
|
#include <bluetooth/sdp_lib.h>
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include "sdp-xml.h"
|
|
|
|
#ifndef APPLE_AGENT_SVCLASS_ID
|
|
#define APPLE_AGENT_SVCLASS_ID 0x2112
|
|
#endif
|
|
|
|
#define for_each_opt(opt, long, short) while ((opt=getopt_long(argc, argv, short ? short:"+", long, 0)) != -1)
|
|
|
|
/*
|
|
* Convert a string to a BDADDR, with a few "enhancements" - Jean II
|
|
*/
|
|
static int estr2ba(char *str, bdaddr_t *ba)
|
|
{
|
|
/* Only trap "local", "any" is already dealt with */
|
|
if(!strcmp(str, "local")) {
|
|
bacpy(ba, BDADDR_LOCAL);
|
|
return 0;
|
|
}
|
|
return str2ba(str, ba);
|
|
}
|
|
|
|
#define DEFAULT_VIEW 0 /* Display only known attribute */
|
|
#define TREE_VIEW 1 /* Display full attribute tree */
|
|
#define RAW_VIEW 2 /* Display raw tree */
|
|
#define XML_VIEW 3 /* Display xml tree */
|
|
|
|
/* Pass args to the inquiry/search handler */
|
|
struct search_context {
|
|
char *svc; /* Service */
|
|
uuid_t group; /* Browse group */
|
|
int view; /* View mode */
|
|
uint32_t handle; /* Service record handle */
|
|
};
|
|
|
|
typedef int (*handler_t)(bdaddr_t *bdaddr, struct search_context *arg);
|
|
|
|
static char UUID_str[MAX_LEN_UUID_STR];
|
|
static bdaddr_t interface;
|
|
|
|
/* Definition of attribute members */
|
|
struct member_def {
|
|
char *name;
|
|
};
|
|
|
|
/* Definition of an attribute */
|
|
struct attrib_def {
|
|
int num; /* Numeric ID - 16 bits */
|
|
char *name; /* User readable name */
|
|
struct member_def *members; /* Definition of attribute args */
|
|
int member_max; /* Max of attribute arg definitions */
|
|
};
|
|
|
|
/* Definition of a service or protocol */
|
|
struct uuid_def {
|
|
int num; /* Numeric ID - 16 bits */
|
|
char *name; /* User readable name */
|
|
struct attrib_def *attribs; /* Specific attribute definitions */
|
|
int attrib_max; /* Max of attribute definitions */
|
|
};
|
|
|
|
/* Context information about current attribute */
|
|
struct attrib_context {
|
|
struct uuid_def *service; /* Service UUID, if known */
|
|
struct attrib_def *attrib; /* Description of the attribute */
|
|
int member_index; /* Index of current attribute member */
|
|
};
|
|
|
|
/* Context information about the whole service */
|
|
struct service_context {
|
|
struct uuid_def *service; /* Service UUID, if known */
|
|
};
|
|
|
|
/* Allow us to do nice formatting of the lists */
|
|
static char *indent_spaces = " ";
|
|
|
|
/* ID of the service attribute.
|
|
* Most attributes after 0x200 are defined based on the service, so
|
|
* we need to find what is the service (which is messy) - Jean II */
|
|
#define SERVICE_ATTR 0x1
|
|
|
|
/* Definition of the optional arguments in protocol list */
|
|
static struct member_def protocol_members[] = {
|
|
{ "Protocol" },
|
|
{ "Channel/Port" },
|
|
{ "Version" },
|
|
};
|
|
|
|
/* Definition of the optional arguments in profile list */
|
|
static struct member_def profile_members[] = {
|
|
{ "Profile" },
|
|
{ "Version" },
|
|
};
|
|
|
|
/* Definition of the optional arguments in Language list */
|
|
static struct member_def language_members[] = {
|
|
{ "Code ISO639" },
|
|
{ "Encoding" },
|
|
{ "Base Offset" },
|
|
};
|
|
|
|
/* Name of the various common attributes. See BT assigned numbers */
|
|
static struct attrib_def attrib_names[] = {
|
|
{ 0x0, "ServiceRecordHandle", NULL, 0 },
|
|
{ 0x1, "ServiceClassIDList", NULL, 0 },
|
|
{ 0x2, "ServiceRecordState", NULL, 0 },
|
|
{ 0x3, "ServiceID", NULL, 0 },
|
|
{ 0x4, "ProtocolDescriptorList",
|
|
protocol_members, sizeof(protocol_members)/sizeof(struct member_def) },
|
|
{ 0x5, "BrowseGroupList", NULL, 0 },
|
|
{ 0x6, "LanguageBaseAttributeIDList",
|
|
language_members, sizeof(language_members)/sizeof(struct member_def) },
|
|
{ 0x7, "ServiceInfoTimeToLive", NULL, 0 },
|
|
{ 0x8, "ServiceAvailability", NULL, 0 },
|
|
{ 0x9, "BluetoothProfileDescriptorList",
|
|
profile_members, sizeof(profile_members)/sizeof(struct member_def) },
|
|
{ 0xA, "DocumentationURL", NULL, 0 },
|
|
{ 0xB, "ClientExecutableURL", NULL, 0 },
|
|
{ 0xC, "IconURL", NULL, 0 },
|
|
{ 0xD, "AdditionalProtocolDescriptorLists", NULL, 0 },
|
|
/* Definitions after that are tricky (per profile or offset) */
|
|
};
|
|
|
|
const int attrib_max = sizeof(attrib_names)/sizeof(struct attrib_def);
|
|
|
|
/* Name of the various SPD attributes. See BT assigned numbers */
|
|
static struct attrib_def sdp_attrib_names[] = {
|
|
{ 0x200, "VersionNumberList", NULL, 0 },
|
|
{ 0x201, "ServiceDatabaseState", NULL, 0 },
|
|
};
|
|
|
|
/* Name of the various SPD attributes. See BT assigned numbers */
|
|
static struct attrib_def browse_attrib_names[] = {
|
|
{ 0x200, "GroupID", NULL, 0 },
|
|
};
|
|
|
|
/* Name of the various Device ID attributes. See Device Id spec. */
|
|
static struct attrib_def did_attrib_names[] = {
|
|
{ 0x200, "SpecificationID", NULL, 0 },
|
|
{ 0x201, "VendorID", NULL, 0 },
|
|
{ 0x202, "ProductID", NULL, 0 },
|
|
{ 0x203, "Version", NULL, 0 },
|
|
{ 0x204, "PrimaryRecord", NULL, 0 },
|
|
{ 0x205, "VendorIDSource", NULL, 0 },
|
|
};
|
|
|
|
/* Name of the various HID attributes. See HID spec. */
|
|
static struct attrib_def hid_attrib_names[] = {
|
|
{ 0x200, "DeviceReleaseNum", NULL, 0 },
|
|
{ 0x201, "ParserVersion", NULL, 0 },
|
|
{ 0x202, "DeviceSubclass", NULL, 0 },
|
|
{ 0x203, "CountryCode", NULL, 0 },
|
|
{ 0x204, "VirtualCable", NULL, 0 },
|
|
{ 0x205, "ReconnectInitiate", NULL, 0 },
|
|
{ 0x206, "DescriptorList", NULL, 0 },
|
|
{ 0x207, "LangIDBaseList", NULL, 0 },
|
|
{ 0x208, "SDPDisable", NULL, 0 },
|
|
{ 0x209, "BatteryPower", NULL, 0 },
|
|
{ 0x20a, "RemoteWakeup", NULL, 0 },
|
|
{ 0x20b, "ProfileVersion", NULL, 0 },
|
|
{ 0x20c, "SupervisionTimeout", NULL, 0 },
|
|
{ 0x20d, "NormallyConnectable", NULL, 0 },
|
|
{ 0x20e, "BootDevice", NULL, 0 },
|
|
};
|
|
|
|
/* Name of the various PAN attributes. See BT assigned numbers */
|
|
/* Note : those need to be double checked - Jean II */
|
|
static struct attrib_def pan_attrib_names[] = {
|
|
{ 0x200, "IpSubnet", NULL, 0 }, /* Obsolete ??? */
|
|
{ 0x30A, "SecurityDescription", NULL, 0 },
|
|
{ 0x30B, "NetAccessType", NULL, 0 },
|
|
{ 0x30C, "MaxNetAccessrate", NULL, 0 },
|
|
{ 0x30D, "IPv4Subnet", NULL, 0 },
|
|
{ 0x30E, "IPv6Subnet", NULL, 0 },
|
|
};
|
|
|
|
/* Name of the various Generic-Audio attributes. See BT assigned numbers */
|
|
/* Note : totally untested - Jean II */
|
|
static struct attrib_def audio_attrib_names[] = {
|
|
{ 0x302, "Remote audio volume control", NULL, 0 },
|
|
};
|
|
|
|
/* Same for the UUIDs. See BT assigned numbers */
|
|
static struct uuid_def uuid16_names[] = {
|
|
/* -- Protocols -- */
|
|
{ 0x0001, "SDP", NULL, 0 },
|
|
{ 0x0002, "UDP", NULL, 0 },
|
|
{ 0x0003, "RFCOMM", NULL, 0 },
|
|
{ 0x0004, "TCP", NULL, 0 },
|
|
{ 0x0005, "TCS-BIN", NULL, 0 },
|
|
{ 0x0006, "TCS-AT", NULL, 0 },
|
|
{ 0x0008, "OBEX", NULL, 0 },
|
|
{ 0x0009, "IP", NULL, 0 },
|
|
{ 0x000a, "FTP", NULL, 0 },
|
|
{ 0x000c, "HTTP", NULL, 0 },
|
|
{ 0x000e, "WSP", NULL, 0 },
|
|
{ 0x000f, "BNEP", NULL, 0 },
|
|
{ 0x0010, "UPnP/ESDP", NULL, 0 },
|
|
{ 0x0011, "HIDP", NULL, 0 },
|
|
{ 0x0012, "HardcopyControlChannel", NULL, 0 },
|
|
{ 0x0014, "HardcopyDataChannel", NULL, 0 },
|
|
{ 0x0016, "HardcopyNotification", NULL, 0 },
|
|
{ 0x0017, "AVCTP", NULL, 0 },
|
|
{ 0x0019, "AVDTP", NULL, 0 },
|
|
{ 0x001b, "CMTP", NULL, 0 },
|
|
{ 0x001d, "UDI_C-Plane", NULL, 0 },
|
|
{ 0x0100, "L2CAP", NULL, 0 },
|
|
/* -- Services -- */
|
|
{ 0x1000, "ServiceDiscoveryServerServiceClassID",
|
|
sdp_attrib_names, sizeof(sdp_attrib_names)/sizeof(struct attrib_def) },
|
|
{ 0x1001, "BrowseGroupDescriptorServiceClassID",
|
|
browse_attrib_names, sizeof(browse_attrib_names)/sizeof(struct attrib_def) },
|
|
{ 0x1002, "PublicBrowseGroup", NULL, 0 },
|
|
{ 0x1101, "SerialPort", NULL, 0 },
|
|
{ 0x1102, "LANAccessUsingPPP", NULL, 0 },
|
|
{ 0x1103, "DialupNetworking (DUN)", NULL, 0 },
|
|
{ 0x1104, "IrMCSync", NULL, 0 },
|
|
{ 0x1105, "OBEXObjectPush", NULL, 0 },
|
|
{ 0x1106, "OBEXFileTransfer", NULL, 0 },
|
|
{ 0x1107, "IrMCSyncCommand", NULL, 0 },
|
|
{ 0x1108, "Headset",
|
|
audio_attrib_names, sizeof(audio_attrib_names)/sizeof(struct attrib_def) },
|
|
{ 0x1109, "CordlessTelephony", NULL, 0 },
|
|
{ 0x110a, "AudioSource", NULL, 0 },
|
|
{ 0x110b, "AudioSink", NULL, 0 },
|
|
{ 0x110c, "RemoteControlTarget", NULL, 0 },
|
|
{ 0x110d, "AdvancedAudio", NULL, 0 },
|
|
{ 0x110e, "RemoteControl", NULL, 0 },
|
|
{ 0x110f, "VideoConferencing", NULL, 0 },
|
|
{ 0x1110, "Intercom", NULL, 0 },
|
|
{ 0x1111, "Fax", NULL, 0 },
|
|
{ 0x1112, "HeadsetAudioGateway", NULL, 0 },
|
|
{ 0x1113, "WAP", NULL, 0 },
|
|
{ 0x1114, "WAP Client", NULL, 0 },
|
|
{ 0x1115, "PANU (PAN/BNEP)",
|
|
pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) },
|
|
{ 0x1116, "NAP (PAN/BNEP)",
|
|
pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) },
|
|
{ 0x1117, "GN (PAN/BNEP)",
|
|
pan_attrib_names, sizeof(pan_attrib_names)/sizeof(struct attrib_def) },
|
|
{ 0x1118, "DirectPrinting (BPP)", NULL, 0 },
|
|
{ 0x1119, "ReferencePrinting (BPP)", NULL, 0 },
|
|
{ 0x111a, "Imaging (BIP)", NULL, 0 },
|
|
{ 0x111b, "ImagingResponder (BIP)", NULL, 0 },
|
|
{ 0x111c, "ImagingAutomaticArchive (BIP)", NULL, 0 },
|
|
{ 0x111d, "ImagingReferencedObjects (BIP)", NULL, 0 },
|
|
{ 0x111e, "Handsfree", NULL, 0 },
|
|
{ 0x111f, "HandsfreeAudioGateway", NULL, 0 },
|
|
{ 0x1120, "DirectPrintingReferenceObjectsService (BPP)", NULL, 0 },
|
|
{ 0x1121, "ReflectedUI (BPP)", NULL, 0 },
|
|
{ 0x1122, "BasicPrinting (BPP)", NULL, 0 },
|
|
{ 0x1123, "PrintingStatus (BPP)", NULL, 0 },
|
|
{ 0x1124, "HumanInterfaceDeviceService (HID)",
|
|
hid_attrib_names, sizeof(hid_attrib_names)/sizeof(struct attrib_def) },
|
|
{ 0x1125, "HardcopyCableReplacement (HCR)", NULL, 0 },
|
|
{ 0x1126, "HCR_Print (HCR)", NULL, 0 },
|
|
{ 0x1127, "HCR_Scan (HCR)", NULL, 0 },
|
|
{ 0x1128, "Common ISDN Access (CIP)", NULL, 0 },
|
|
{ 0x1129, "VideoConferencingGW (VCP)", NULL, 0 },
|
|
{ 0x112a, "UDI-MT", NULL, 0 },
|
|
{ 0x112b, "UDI-TA", NULL, 0 },
|
|
{ 0x112c, "Audio/Video", NULL, 0 },
|
|
{ 0x112d, "SIM Access (SAP)", NULL, 0 },
|
|
{ 0x112e, "Phonebook Access (PBAP) - PCE", NULL, 0 },
|
|
{ 0x112f, "Phonebook Access (PBAP) - PSE", NULL, 0 },
|
|
{ 0x1130, "Phonebook Access (PBAP)", NULL, 0 },
|
|
/* ... */
|
|
{ 0x1200, "PnPInformation",
|
|
did_attrib_names, sizeof(did_attrib_names)/sizeof(struct attrib_def) },
|
|
{ 0x1201, "GenericNetworking", NULL, 0 },
|
|
{ 0x1202, "GenericFileTransfer", NULL, 0 },
|
|
{ 0x1203, "GenericAudio",
|
|
audio_attrib_names, sizeof(audio_attrib_names)/sizeof(struct attrib_def) },
|
|
{ 0x1204, "GenericTelephony", NULL, 0 },
|
|
/* ... */
|
|
{ 0x1303, "VideoSource", NULL, 0 },
|
|
{ 0x1304, "VideoSink", NULL, 0 },
|
|
{ 0x1305, "VideoDistribution", NULL, 0 },
|
|
{ 0x1400, "MDP", NULL, 0 },
|
|
{ 0x1401, "MDPSource", NULL, 0 },
|
|
{ 0x1402, "MDPSink", NULL, 0 },
|
|
{ 0x2112, "AppleAgent", NULL, 0 },
|
|
};
|
|
|
|
static const int uuid16_max = sizeof(uuid16_names)/sizeof(struct uuid_def);
|
|
|
|
static void sdp_data_printf(sdp_data_t *, struct attrib_context *, int);
|
|
|
|
/*
|
|
* Parse a UUID.
|
|
* The BT assigned numbers only list UUID16, so I'm not sure the
|
|
* other types will ever get used...
|
|
*/
|
|
static void sdp_uuid_printf(uuid_t *uuid, struct attrib_context *context, int indent)
|
|
{
|
|
if (uuid) {
|
|
if (uuid->type == SDP_UUID16) {
|
|
uint16_t uuidNum = uuid->value.uuid16;
|
|
struct uuid_def *uuidDef = NULL;
|
|
int i;
|
|
|
|
for (i = 0; i < uuid16_max; i++)
|
|
if (uuid16_names[i].num == uuidNum) {
|
|
uuidDef = &uuid16_names[i];
|
|
break;
|
|
}
|
|
|
|
/* Check if it's the service attribute */
|
|
if (context->attrib && context->attrib->num == SERVICE_ATTR) {
|
|
/* We got the service ID !!! */
|
|
context->service = uuidDef;
|
|
}
|
|
|
|
if (uuidDef)
|
|
printf("%.*sUUID16 : 0x%.4x - %s\n",
|
|
indent, indent_spaces, uuidNum, uuidDef->name);
|
|
else
|
|
printf("%.*sUUID16 : 0x%.4x\n",
|
|
indent, indent_spaces, uuidNum);
|
|
} else if (uuid->type == SDP_UUID32) {
|
|
struct uuid_def *uuidDef = NULL;
|
|
int i;
|
|
|
|
if (!(uuid->value.uuid32 & 0xffff0000)) {
|
|
uint16_t uuidNum = uuid->value.uuid32;
|
|
for (i = 0; i < uuid16_max; i++)
|
|
if (uuid16_names[i].num == uuidNum) {
|
|
uuidDef = &uuid16_names[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (uuidDef)
|
|
printf("%.*sUUID32 : 0x%.8x - %s\n",
|
|
indent, indent_spaces, uuid->value.uuid32, uuidDef->name);
|
|
else
|
|
printf("%.*sUUID32 : 0x%.8x\n",
|
|
indent, indent_spaces, uuid->value.uuid32);
|
|
} else if (uuid->type == SDP_UUID128) {
|
|
unsigned int data0;
|
|
unsigned short data1;
|
|
unsigned short data2;
|
|
unsigned short data3;
|
|
unsigned int data4;
|
|
unsigned short data5;
|
|
|
|
memcpy(&data0, &uuid->value.uuid128.data[0], 4);
|
|
memcpy(&data1, &uuid->value.uuid128.data[4], 2);
|
|
memcpy(&data2, &uuid->value.uuid128.data[6], 2);
|
|
memcpy(&data3, &uuid->value.uuid128.data[8], 2);
|
|
memcpy(&data4, &uuid->value.uuid128.data[10], 4);
|
|
memcpy(&data5, &uuid->value.uuid128.data[14], 2);
|
|
|
|
printf("%.*sUUID128 : 0x%.8x-%.4x-%.4x-%.4x-%.8x-%.4x\n",
|
|
indent, indent_spaces,
|
|
ntohl(data0), ntohs(data1), ntohs(data2),
|
|
ntohs(data3), ntohl(data4), ntohs(data5));
|
|
} else
|
|
printf("%.*sEnum type of UUID not set\n",
|
|
indent, indent_spaces);
|
|
} else
|
|
printf("%.*sNull passed to print UUID\n",
|
|
indent, indent_spaces);
|
|
}
|
|
|
|
/*
|
|
* Parse a sequence of data elements (i.e. a list)
|
|
*/
|
|
static void printf_dataseq(sdp_data_t * pData, struct attrib_context *context, int indent)
|
|
{
|
|
sdp_data_t *sdpdata = NULL;
|
|
|
|
sdpdata = pData;
|
|
if (sdpdata) {
|
|
context->member_index = 0;
|
|
do {
|
|
sdp_data_printf(sdpdata, context, indent + 2);
|
|
sdpdata = sdpdata->next;
|
|
context->member_index++;
|
|
} while (sdpdata);
|
|
} else {
|
|
printf("%.*sBroken dataseq link\n", indent, indent_spaces);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parse a single data element (either in the attribute or in a data
|
|
* sequence).
|
|
*/
|
|
static void sdp_data_printf(sdp_data_t *sdpdata, struct attrib_context *context, int indent)
|
|
{
|
|
char *member_name = NULL;
|
|
|
|
/* Find member name. Almost black magic ;-) */
|
|
if (context && context->attrib && context->attrib->members &&
|
|
context->member_index < context->attrib->member_max) {
|
|
member_name = context->attrib->members[context->member_index].name;
|
|
}
|
|
|
|
switch (sdpdata->dtd) {
|
|
case SDP_DATA_NIL:
|
|
printf("%.*sNil\n", indent, indent_spaces);
|
|
break;
|
|
case SDP_BOOL:
|
|
case SDP_UINT8:
|
|
case SDP_UINT16:
|
|
case SDP_UINT32:
|
|
case SDP_UINT64:
|
|
case SDP_UINT128:
|
|
case SDP_INT8:
|
|
case SDP_INT16:
|
|
case SDP_INT32:
|
|
case SDP_INT64:
|
|
case SDP_INT128:
|
|
if (member_name) {
|
|
printf("%.*s%s (Integer) : 0x%x\n",
|
|
indent, indent_spaces, member_name, sdpdata->val.uint32);
|
|
} else {
|
|
printf("%.*sInteger : 0x%x\n", indent, indent_spaces,
|
|
sdpdata->val.uint32);
|
|
}
|
|
break;
|
|
|
|
case SDP_UUID16:
|
|
case SDP_UUID32:
|
|
case SDP_UUID128:
|
|
//printf("%.*sUUID\n", indent, indent_spaces);
|
|
sdp_uuid_printf(&sdpdata->val.uuid, context, indent);
|
|
break;
|
|
|
|
case SDP_TEXT_STR8:
|
|
case SDP_TEXT_STR16:
|
|
case SDP_TEXT_STR32:
|
|
if (sdpdata->unitSize > strlen(sdpdata->val.str)) {
|
|
int i;
|
|
printf("%.*sData :", indent, indent_spaces);
|
|
for (i = 0; i < sdpdata->unitSize; i++)
|
|
printf(" %02x", (unsigned char) sdpdata->val.str[i]);
|
|
printf("\n");
|
|
} else
|
|
printf("%.*sText : \"%s\"\n", indent, indent_spaces, sdpdata->val.str);
|
|
break;
|
|
case SDP_URL_STR8:
|
|
case SDP_URL_STR16:
|
|
case SDP_URL_STR32:
|
|
printf("%.*sURL : %s\n", indent, indent_spaces, sdpdata->val.str);
|
|
break;
|
|
|
|
case SDP_SEQ8:
|
|
case SDP_SEQ16:
|
|
case SDP_SEQ32:
|
|
printf("%.*sData Sequence\n", indent, indent_spaces);
|
|
printf_dataseq(sdpdata->val.dataseq, context, indent);
|
|
break;
|
|
|
|
case SDP_ALT8:
|
|
case SDP_ALT16:
|
|
case SDP_ALT32:
|
|
printf("%.*sData Sequence Alternates\n", indent, indent_spaces);
|
|
printf_dataseq(sdpdata->val.dataseq, context, indent);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parse a single attribute.
|
|
*/
|
|
static void print_tree_attr_func(void *value, void *userData)
|
|
{
|
|
sdp_data_t *sdpdata = NULL;
|
|
uint16_t attrId;
|
|
struct service_context *service = (struct service_context *) userData;
|
|
struct attrib_context context;
|
|
struct attrib_def *attrDef = NULL;
|
|
int i;
|
|
|
|
sdpdata = (sdp_data_t *)value;
|
|
attrId = sdpdata->attrId;
|
|
/* Search amongst the generic attributes */
|
|
for (i = 0; i < attrib_max; i++)
|
|
if (attrib_names[i].num == attrId) {
|
|
attrDef = &attrib_names[i];
|
|
break;
|
|
}
|
|
/* Search amongst the specific attributes of this service */
|
|
if ((attrDef == NULL) && (service->service != NULL) &&
|
|
(service->service->attribs != NULL)) {
|
|
struct attrib_def *svc_attribs = service->service->attribs;
|
|
int svc_attrib_max = service->service->attrib_max;
|
|
for (i = 0; i < svc_attrib_max; i++)
|
|
if (svc_attribs[i].num == attrId) {
|
|
attrDef = &svc_attribs[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (attrDef)
|
|
printf("Attribute Identifier : 0x%x - %s\n", attrId, attrDef->name);
|
|
else
|
|
printf("Attribute Identifier : 0x%x\n", attrId);
|
|
/* Build context */
|
|
context.service = service->service;
|
|
context.attrib = attrDef;
|
|
context.member_index = 0;
|
|
/* Parse attribute members */
|
|
if (sdpdata)
|
|
sdp_data_printf(sdpdata, &context, 2);
|
|
else
|
|
printf(" NULL value\n");
|
|
/* Update service */
|
|
service->service = context.service;
|
|
}
|
|
|
|
/*
|
|
* Main entry point of this library. Parse a SDP record.
|
|
* We assume the record has already been read, parsed and cached
|
|
* locally. Jean II
|
|
*/
|
|
static void print_tree_attr(sdp_record_t *rec)
|
|
{
|
|
if (rec && rec->attrlist) {
|
|
struct service_context service = { NULL };
|
|
sdp_list_foreach(rec->attrlist, print_tree_attr_func, &service);
|
|
}
|
|
}
|
|
|
|
static void print_raw_data(sdp_data_t *data, int indent)
|
|
{
|
|
struct uuid_def *def;
|
|
int i, hex;
|
|
|
|
if (!data)
|
|
return;
|
|
|
|
for (i = 0; i < indent; i++)
|
|
printf("\t");
|
|
|
|
switch (data->dtd) {
|
|
case SDP_DATA_NIL:
|
|
printf("NIL\n");
|
|
break;
|
|
case SDP_BOOL:
|
|
printf("Bool %s\n", data->val.uint8 ? "True" : "False");
|
|
break;
|
|
case SDP_UINT8:
|
|
printf("UINT8 0x%02x\n", data->val.uint8);
|
|
break;
|
|
case SDP_UINT16:
|
|
printf("UINT16 0x%04x\n", data->val.uint16);
|
|
break;
|
|
case SDP_UINT32:
|
|
printf("UINT32 0x%08x\n", data->val.uint32);
|
|
break;
|
|
case SDP_UINT64:
|
|
printf("UINT64 0x%016jx\n", data->val.uint64);
|
|
break;
|
|
case SDP_UINT128:
|
|
printf("UINT128 ...\n");
|
|
break;
|
|
case SDP_INT8:
|
|
printf("INT8 %d\n", data->val.int8);
|
|
break;
|
|
case SDP_INT16:
|
|
printf("INT16 %d\n", data->val.int16);
|
|
break;
|
|
case SDP_INT32:
|
|
printf("INT32 %d\n", data->val.int32);
|
|
break;
|
|
case SDP_INT64:
|
|
printf("INT64 %jd\n", data->val.int64);
|
|
break;
|
|
case SDP_INT128:
|
|
printf("INT128 ...\n");
|
|
break;
|
|
case SDP_UUID16:
|
|
case SDP_UUID32:
|
|
case SDP_UUID128:
|
|
switch (data->val.uuid.type) {
|
|
case SDP_UUID16:
|
|
def = NULL;
|
|
for (i = 0; i < uuid16_max; i++)
|
|
if (uuid16_names[i].num == data->val.uuid.value.uuid16) {
|
|
def = &uuid16_names[i];
|
|
break;
|
|
}
|
|
if (def)
|
|
printf("UUID16 0x%04x - %s\n", data->val.uuid.value.uuid16, def->name);
|
|
else
|
|
printf("UUID16 0x%04x\n", data->val.uuid.value.uuid16);
|
|
break;
|
|
case SDP_UUID32:
|
|
def = NULL;
|
|
if (!(data->val.uuid.value.uuid32 & 0xffff0000)) {
|
|
uint16_t value = data->val.uuid.value.uuid32;
|
|
for (i = 0; i < uuid16_max; i++)
|
|
if (uuid16_names[i].num == value) {
|
|
def = &uuid16_names[i];
|
|
break;
|
|
}
|
|
}
|
|
if (def)
|
|
printf("UUID32 0x%08x - %s\n", data->val.uuid.value.uuid32, def->name);
|
|
else
|
|
printf("UUID32 0x%08x\n", data->val.uuid.value.uuid32);
|
|
break;
|
|
case SDP_UUID128:
|
|
printf("UUID128 ");
|
|
for (i = 0; i < 16; i++) {
|
|
switch (i) {
|
|
case 4:
|
|
case 6:
|
|
case 8:
|
|
case 10:
|
|
printf("-");
|
|
break;
|
|
}
|
|
printf("%02x", (unsigned char ) data->val.uuid.value.uuid128.data[i]);
|
|
}
|
|
printf("\n");
|
|
break;
|
|
default:
|
|
printf("UUID type 0x%02x\n", data->val.uuid.type);
|
|
break;
|
|
}
|
|
break;
|
|
case SDP_TEXT_STR8:
|
|
case SDP_TEXT_STR16:
|
|
case SDP_TEXT_STR32:
|
|
hex = 0;
|
|
for (i = 0; i < data->unitSize; i++) {
|
|
if (i == (data->unitSize - 1) && data->val.str[i] == '\0')
|
|
break;
|
|
if (!isprint(data->val.str[i])) {
|
|
hex = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (hex) {
|
|
printf("Data");
|
|
for (i = 0; i < data->unitSize; i++)
|
|
printf(" %02x", (unsigned char) data->val.str[i]);
|
|
} else {
|
|
printf("String ");
|
|
for (i = 0; i < data->unitSize; i++)
|
|
printf("%c", data->val.str[i]);
|
|
}
|
|
printf("\n");
|
|
break;
|
|
case SDP_URL_STR8:
|
|
case SDP_URL_STR16:
|
|
case SDP_URL_STR32:
|
|
printf("URL %s\n", data->val.str);
|
|
break;
|
|
case SDP_SEQ8:
|
|
case SDP_SEQ16:
|
|
case SDP_SEQ32:
|
|
printf("Sequence\n");
|
|
print_raw_data(data->val.dataseq, indent + 1);
|
|
break;
|
|
case SDP_ALT8:
|
|
case SDP_ALT16:
|
|
case SDP_ALT32:
|
|
printf("Alternate\n");
|
|
print_raw_data(data->val.dataseq, indent + 1);
|
|
break;
|
|
default:
|
|
printf("Unknown type 0x%02x\n", data->dtd);
|
|
break;
|
|
}
|
|
|
|
print_raw_data(data->next, indent);
|
|
}
|
|
|
|
static void print_raw_attr_func(void *value, void *userData)
|
|
{
|
|
sdp_data_t *data = (sdp_data_t *) value;
|
|
struct attrib_def *def = NULL;
|
|
int i;
|
|
|
|
/* Search amongst the generic attributes */
|
|
for (i = 0; i < attrib_max; i++)
|
|
if (attrib_names[i].num == data->attrId) {
|
|
def = &attrib_names[i];
|
|
break;
|
|
}
|
|
|
|
if (def)
|
|
printf("\tAttribute 0x%04x - %s\n", data->attrId, def->name);
|
|
else
|
|
printf("\tAttribute 0x%04x\n", data->attrId);
|
|
|
|
if (data)
|
|
print_raw_data(data, 2);
|
|
else
|
|
printf(" NULL value\n");
|
|
}
|
|
|
|
static void print_raw_attr(sdp_record_t *rec)
|
|
{
|
|
if (rec && rec->attrlist) {
|
|
printf("Sequence\n");
|
|
sdp_list_foreach(rec->attrlist, print_raw_attr_func, 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set attributes with single values in SDP record
|
|
* Jean II
|
|
*/
|
|
static int set_attrib(sdp_session_t *sess, uint32_t handle, uint16_t attrib, char *value)
|
|
{
|
|
sdp_list_t *attrid_list;
|
|
uint32_t range = 0x0000ffff;
|
|
sdp_record_t *rec;
|
|
int ret;
|
|
|
|
/* Get the old SDP record */
|
|
attrid_list = sdp_list_append(NULL, &range);
|
|
rec = sdp_service_attr_req(sess, handle, SDP_ATTR_REQ_RANGE, attrid_list);
|
|
sdp_list_free(attrid_list, NULL);
|
|
|
|
if (!rec) {
|
|
printf("Service get request failed.\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Check the type of attribute */
|
|
if (!strncasecmp(value, "u0x", 3)) {
|
|
/* UUID16 */
|
|
uint16_t value_int = 0;
|
|
uuid_t value_uuid;
|
|
value_int = strtoul(value + 3, NULL, 16);
|
|
sdp_uuid16_create(&value_uuid, value_int);
|
|
printf("Adding attrib 0x%X uuid16 0x%X to record 0x%X\n",
|
|
attrib, value_int, handle);
|
|
|
|
sdp_attr_add_new(rec, attrib, SDP_UUID16, &value_uuid.value.uuid16);
|
|
} else if (!strncasecmp(value, "0x", 2)) {
|
|
/* Int */
|
|
uint32_t value_int;
|
|
value_int = strtoul(value + 2, NULL, 16);
|
|
printf("Adding attrib 0x%X int 0x%X to record 0x%X\n",
|
|
attrib, value_int, handle);
|
|
|
|
sdp_attr_add_new(rec, attrib, SDP_UINT32, &value_int);
|
|
} else {
|
|
/* String */
|
|
printf("Adding attrib 0x%X string \"%s\" to record 0x%X\n",
|
|
attrib, value, handle);
|
|
|
|
/* Add/Update our attribute to the record */
|
|
sdp_attr_add_new(rec, attrib, SDP_TEXT_STR8, value);
|
|
}
|
|
|
|
/* Update on the server */
|
|
ret = sdp_device_record_update(sess, &interface, rec);
|
|
if (ret < 0)
|
|
printf("Service Record update failed (%d).\n", errno);
|
|
sdp_record_free(rec);
|
|
return ret;
|
|
}
|
|
|
|
static struct option set_options[] = {
|
|
{ "help", 0, 0, 'h' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
static char *set_help =
|
|
"Usage:\n"
|
|
"\tget record_handle attrib_id attrib_value\n";
|
|
|
|
/*
|
|
* Add an attribute to an existing SDP record on the local SDP server
|
|
*/
|
|
static int cmd_setattr(int argc, char **argv)
|
|
{
|
|
int opt, status;
|
|
uint32_t handle;
|
|
uint16_t attrib;
|
|
sdp_session_t *sess;
|
|
|
|
for_each_opt(opt, set_options, NULL) {
|
|
switch(opt) {
|
|
default:
|
|
printf(set_help);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc < 3) {
|
|
printf(set_help);
|
|
return -1;
|
|
}
|
|
|
|
/* Convert command line args */
|
|
handle = strtoul(argv[0], NULL, 16);
|
|
attrib = strtoul(argv[1], NULL, 16);
|
|
|
|
/* Do it */
|
|
sess = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
|
|
if (!sess)
|
|
return -1;
|
|
|
|
status = set_attrib(sess, handle, attrib, argv[2]);
|
|
sdp_close(sess);
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* We do only simple data sequences. Sequence of sequences is a pain ;-)
|
|
* Jean II
|
|
*/
|
|
static int set_attribseq(sdp_session_t *session, uint32_t handle, uint16_t attrib, int argc, char **argv)
|
|
{
|
|
sdp_list_t *attrid_list;
|
|
uint32_t range = 0x0000ffff;
|
|
sdp_record_t *rec;
|
|
sdp_data_t *pSequenceHolder = NULL;
|
|
void **dtdArray;
|
|
void **valueArray;
|
|
void **allocArray;
|
|
uint8_t uuid16 = SDP_UUID16;
|
|
uint8_t uint32 = SDP_UINT32;
|
|
uint8_t str8 = SDP_TEXT_STR8;
|
|
int i, ret = 0;
|
|
|
|
/* Get the old SDP record */
|
|
attrid_list = sdp_list_append(NULL, &range);
|
|
rec = sdp_service_attr_req(session, handle, SDP_ATTR_REQ_RANGE, attrid_list);
|
|
sdp_list_free(attrid_list, NULL);
|
|
|
|
if (!rec) {
|
|
printf("Service get request failed.\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Create arrays */
|
|
dtdArray = (void **)malloc(argc * sizeof(void *));
|
|
valueArray = (void **)malloc(argc * sizeof(void *));
|
|
allocArray = (void **)malloc(argc * sizeof(void *));
|
|
|
|
/* Loop on all args, add them in arrays */
|
|
for (i = 0; i < argc; i++) {
|
|
/* Check the type of attribute */
|
|
if (!strncasecmp(argv[i], "u0x", 3)) {
|
|
/* UUID16 */
|
|
uint16_t value_int = strtoul((argv[i]) + 3, NULL, 16);
|
|
uuid_t *value_uuid = (uuid_t *) malloc(sizeof(uuid_t));
|
|
allocArray[i] = value_uuid;
|
|
sdp_uuid16_create(value_uuid, value_int);
|
|
|
|
printf("Adding uuid16 0x%X to record 0x%X\n", value_int, handle);
|
|
dtdArray[i] = &uuid16;
|
|
valueArray[i] = &value_uuid->value.uuid16;
|
|
} else if (!strncasecmp(argv[i], "0x", 2)) {
|
|
/* Int */
|
|
uint32_t *value_int = (uint32_t *) malloc(sizeof(int));
|
|
allocArray[i] = value_int;
|
|
*value_int = strtoul((argv[i]) + 2, NULL, 16);
|
|
|
|
printf("Adding int 0x%X to record 0x%X\n", *value_int, handle);
|
|
dtdArray[i] = &uint32;
|
|
valueArray[i] = value_int;
|
|
} else {
|
|
/* String */
|
|
printf("Adding string \"%s\" to record 0x%X\n", argv[i], handle);
|
|
dtdArray[i] = &str8;
|
|
valueArray[i] = argv[i];
|
|
}
|
|
}
|
|
|
|
/* Add this sequence to the attrib list */
|
|
pSequenceHolder = sdp_seq_alloc(dtdArray, valueArray, argc);
|
|
if (pSequenceHolder) {
|
|
sdp_attr_replace(rec, attrib, pSequenceHolder);
|
|
|
|
/* Update on the server */
|
|
ret = sdp_device_record_update(session, &interface, rec);
|
|
if (ret < 0)
|
|
printf("Service Record update failed (%d).\n", errno);
|
|
} else
|
|
printf("Failed to create pSequenceHolder\n");
|
|
|
|
/* Cleanup */
|
|
for (i = 0; i < argc; i++)
|
|
free(allocArray[i]);
|
|
|
|
free(dtdArray);
|
|
free(valueArray);
|
|
free(allocArray);
|
|
|
|
sdp_record_free(rec);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct option seq_options[] = {
|
|
{ "help", 0, 0, 'h' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
static char *seq_help =
|
|
"Usage:\n"
|
|
"\tget record_handle attrib_id attrib_values\n";
|
|
|
|
/*
|
|
* Add an attribute sequence to an existing SDP record
|
|
* on the local SDP server
|
|
*/
|
|
static int cmd_setseq(int argc, char **argv)
|
|
{
|
|
int opt, status;
|
|
uint32_t handle;
|
|
uint16_t attrib;
|
|
sdp_session_t *sess;
|
|
|
|
for_each_opt(opt, seq_options, NULL) {
|
|
switch(opt) {
|
|
default:
|
|
printf(seq_help);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc < 3) {
|
|
printf(seq_help);
|
|
return -1;
|
|
}
|
|
|
|
/* Convert command line args */
|
|
handle = strtoul(argv[0], NULL, 16);
|
|
attrib = strtoul(argv[1], NULL, 16);
|
|
|
|
argc -= 2;
|
|
argv += 2;
|
|
|
|
/* Do it */
|
|
sess = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
|
|
if (!sess)
|
|
return -1;
|
|
|
|
status = set_attribseq(sess, handle, attrib, argc, argv);
|
|
sdp_close(sess);
|
|
|
|
return status;
|
|
}
|
|
|
|
static void print_service_class(void *value, void *userData)
|
|
{
|
|
char ServiceClassUUID_str[MAX_LEN_SERVICECLASS_UUID_STR];
|
|
uuid_t *uuid = (uuid_t *)value;
|
|
|
|
sdp_uuid2strn(uuid, UUID_str, MAX_LEN_UUID_STR);
|
|
sdp_svclass_uuid2strn(uuid, ServiceClassUUID_str, MAX_LEN_SERVICECLASS_UUID_STR);
|
|
if (uuid->type != SDP_UUID128)
|
|
printf(" \"%s\" (0x%s)\n", ServiceClassUUID_str, UUID_str);
|
|
else
|
|
printf(" UUID 128: %s\n", UUID_str);
|
|
}
|
|
|
|
static void print_service_desc(void *value, void *user)
|
|
{
|
|
char str[MAX_LEN_PROTOCOL_UUID_STR];
|
|
sdp_data_t *p = (sdp_data_t *)value, *s;
|
|
int i = 0, proto = 0;
|
|
|
|
for (; p; p = p->next, i++) {
|
|
switch (p->dtd) {
|
|
case SDP_UUID16:
|
|
case SDP_UUID32:
|
|
case SDP_UUID128:
|
|
sdp_uuid2strn(&p->val.uuid, UUID_str, MAX_LEN_UUID_STR);
|
|
sdp_proto_uuid2strn(&p->val.uuid, str, sizeof(str));
|
|
proto = sdp_uuid_to_proto(&p->val.uuid);
|
|
printf(" \"%s\" (0x%s)\n", str, UUID_str);
|
|
break;
|
|
case SDP_UINT8:
|
|
if (proto == RFCOMM_UUID)
|
|
printf(" Channel: %d\n", p->val.uint8);
|
|
else
|
|
printf(" uint8: 0x%x\n", p->val.uint8);
|
|
break;
|
|
case SDP_UINT16:
|
|
if (proto == L2CAP_UUID) {
|
|
if (i == 1)
|
|
printf(" PSM: %d\n", p->val.uint16);
|
|
else
|
|
printf(" Version: 0x%04x\n", p->val.uint16);
|
|
} else if (proto == BNEP_UUID)
|
|
if (i == 1)
|
|
printf(" Version: 0x%04x\n", p->val.uint16);
|
|
else
|
|
printf(" uint16: 0x%x\n", p->val.uint16);
|
|
else
|
|
printf(" uint16: 0x%x\n", p->val.uint16);
|
|
break;
|
|
case SDP_SEQ16:
|
|
printf(" SEQ16:");
|
|
for (s = p->val.dataseq; s; s = s->next)
|
|
printf(" %x", s->val.uint16);
|
|
printf("\n");
|
|
break;
|
|
case SDP_SEQ8:
|
|
printf(" SEQ8:");
|
|
for (s = p->val.dataseq; s; s = s->next)
|
|
printf(" %x", s->val.uint8);
|
|
printf("\n");
|
|
break;
|
|
default:
|
|
printf(" FIXME: dtd=0%x\n", p->dtd);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void print_lang_attr(void *value, void *user)
|
|
{
|
|
sdp_lang_attr_t *lang = (sdp_lang_attr_t *)value;
|
|
printf(" code_ISO639: 0x%02x\n", lang->code_ISO639);
|
|
printf(" encoding: 0x%02x\n", lang->encoding);
|
|
printf(" base_offset: 0x%02x\n", lang->base_offset);
|
|
}
|
|
|
|
static void print_access_protos(void *value, void *userData)
|
|
{
|
|
sdp_list_t *protDescSeq = (sdp_list_t *)value;
|
|
sdp_list_foreach(protDescSeq, print_service_desc, 0);
|
|
}
|
|
|
|
static void print_profile_desc(void *value, void *userData)
|
|
{
|
|
sdp_profile_desc_t *desc = (sdp_profile_desc_t *)value;
|
|
char str[MAX_LEN_PROFILEDESCRIPTOR_UUID_STR];
|
|
|
|
sdp_uuid2strn(&desc->uuid, UUID_str, MAX_LEN_UUID_STR);
|
|
sdp_profile_uuid2strn(&desc->uuid, str, MAX_LEN_PROFILEDESCRIPTOR_UUID_STR);
|
|
|
|
printf(" \"%s\" (0x%s)\n", str, UUID_str);
|
|
if (desc->version)
|
|
printf(" Version: 0x%04x\n", desc->version);
|
|
}
|
|
|
|
/*
|
|
* Parse a SDP record in user friendly form.
|
|
*/
|
|
static void print_service_attr(sdp_record_t *rec)
|
|
{
|
|
sdp_list_t *list = 0, *proto = 0;
|
|
|
|
sdp_record_print(rec);
|
|
|
|
printf("Service RecHandle: 0x%x\n", rec->handle);
|
|
|
|
if (sdp_get_service_classes(rec, &list) == 0) {
|
|
printf("Service Class ID List:\n");
|
|
sdp_list_foreach(list, print_service_class, 0);
|
|
sdp_list_free(list, free);
|
|
}
|
|
if (sdp_get_access_protos(rec, &proto) == 0) {
|
|
printf("Protocol Descriptor List:\n");
|
|
sdp_list_foreach(proto, print_access_protos, 0);
|
|
sdp_list_foreach(proto, (sdp_list_func_t)sdp_list_free, 0);
|
|
sdp_list_free(proto, 0);
|
|
}
|
|
if (sdp_get_lang_attr(rec, &list) == 0) {
|
|
printf("Language Base Attr List:\n");
|
|
sdp_list_foreach(list, print_lang_attr, 0);
|
|
sdp_list_free(list, free);
|
|
}
|
|
if (sdp_get_profile_descs(rec, &list) == 0) {
|
|
printf("Profile Descriptor List:\n");
|
|
sdp_list_foreach(list, print_profile_desc, 0);
|
|
sdp_list_free(list, free);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Support for Service (de)registration
|
|
*/
|
|
typedef struct {
|
|
uint32_t handle;
|
|
char *name;
|
|
char *provider;
|
|
char *desc;
|
|
unsigned int class;
|
|
unsigned int profile;
|
|
uint16_t psm;
|
|
uint8_t channel;
|
|
uint8_t network;
|
|
} svc_info_t;
|
|
|
|
static void add_lang_attr(sdp_record_t *r)
|
|
{
|
|
sdp_lang_attr_t base_lang;
|
|
sdp_list_t *langs = 0;
|
|
|
|
/* UTF-8 MIBenum (http://www.iana.org/assignments/character-sets) */
|
|
base_lang.code_ISO639 = (0x65 << 8) | 0x6e;
|
|
base_lang.encoding = 106;
|
|
base_lang.base_offset = SDP_PRIMARY_LANG_BASE;
|
|
langs = sdp_list_append(0, &base_lang);
|
|
sdp_set_lang_attr(r, langs);
|
|
sdp_list_free(langs, 0);
|
|
}
|
|
|
|
static int add_sp(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *apseq, *proto[2], *profiles, *root, *aproto;
|
|
uuid_t root_uuid, sp_uuid, l2cap, rfcomm;
|
|
sdp_profile_desc_t profile;
|
|
sdp_record_t record;
|
|
uint8_t u8 = si->channel ? si->channel : 1;
|
|
sdp_data_t *channel;
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
sdp_list_free(root, 0);
|
|
|
|
sdp_uuid16_create(&sp_uuid, SERIAL_PORT_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &sp_uuid);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
sdp_list_free(svclass_id, 0);
|
|
|
|
sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
|
|
profile.version = 0x0100;
|
|
profiles = sdp_list_append(0, &profile);
|
|
sdp_set_profile_descs(&record, profiles);
|
|
sdp_list_free(profiles, 0);
|
|
|
|
sdp_uuid16_create(&l2cap, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
|
|
proto[1] = sdp_list_append(0, &rfcomm);
|
|
channel = sdp_data_alloc(SDP_UINT8, &u8);
|
|
proto[1] = sdp_list_append(proto[1], channel);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
add_lang_attr(&record);
|
|
|
|
sdp_set_info_attr(&record, "Serial Port", 0, "COM Port");
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
printf("Serial Port service registered\n");
|
|
|
|
end:
|
|
sdp_data_free(channel);
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_dun(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root, *aproto;
|
|
uuid_t rootu, dun, gn, l2cap, rfcomm;
|
|
sdp_profile_desc_t profile;
|
|
sdp_list_t *proto[2];
|
|
sdp_record_t record;
|
|
uint8_t u8 = si->channel ? si->channel : 2;
|
|
sdp_data_t *channel;
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&rootu, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &rootu);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&dun, DIALUP_NET_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &dun);
|
|
sdp_uuid16_create(&gn, GENERIC_NETWORKING_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(svclass_id, &gn);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile.uuid, DIALUP_NET_PROFILE_ID);
|
|
profile.version = 0x0100;
|
|
pfseq = sdp_list_append(0, &profile);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&rfcomm, RFCOMM_UUID);
|
|
proto[1] = sdp_list_append(0, &rfcomm);
|
|
channel = sdp_data_alloc(SDP_UINT8, &u8);
|
|
proto[1] = sdp_list_append(proto[1], channel);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
sdp_set_info_attr(&record, "Dial-Up Networking", 0, 0);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
printf("Dial-Up Networking service registered\n");
|
|
|
|
end:
|
|
sdp_data_free(channel);
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_fax(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, fax_uuid, tel_uuid, l2cap_uuid, rfcomm_uuid;
|
|
sdp_profile_desc_t profile;
|
|
sdp_list_t *aproto, *proto[2];
|
|
sdp_record_t record;
|
|
uint8_t u8 = si->channel? si->channel : 3;
|
|
sdp_data_t *channel;
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&fax_uuid, FAX_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &fax_uuid);
|
|
sdp_uuid16_create(&tel_uuid, GENERIC_TELEPHONY_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(svclass_id, &tel_uuid);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile.uuid, FAX_PROFILE_ID);
|
|
profile.version = 0x0100;
|
|
pfseq = sdp_list_append(0, &profile);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap_uuid);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
|
|
proto[1] = sdp_list_append(0, &rfcomm_uuid);
|
|
channel = sdp_data_alloc(SDP_UINT8, &u8);
|
|
proto[1] = sdp_list_append(proto[1], channel);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
sdp_set_info_attr(&record, "Fax", 0, 0);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
printf("Fax service registered\n");
|
|
end:
|
|
sdp_data_free(channel);
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
return ret;
|
|
}
|
|
|
|
static int add_lan(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
|
|
sdp_profile_desc_t profile;
|
|
sdp_list_t *aproto, *proto[2];
|
|
sdp_record_t record;
|
|
uint8_t u8 = si->channel ? si->channel : 4;
|
|
sdp_data_t *channel;
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&svclass_uuid, LAN_ACCESS_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &svclass_uuid);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile.uuid, LAN_ACCESS_PROFILE_ID);
|
|
profile.version = 0x0100;
|
|
pfseq = sdp_list_append(0, &profile);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap_uuid);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
|
|
proto[1] = sdp_list_append(0, &rfcomm_uuid);
|
|
channel = sdp_data_alloc(SDP_UINT8, &u8);
|
|
proto[1] = sdp_list_append(proto[1], channel);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
sdp_set_info_attr(&record, "LAN Access over PPP", 0, 0);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
printf("LAN Access service registered\n");
|
|
|
|
end:
|
|
sdp_data_free(channel);
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_headset(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
|
|
sdp_profile_desc_t profile;
|
|
sdp_list_t *aproto, *proto[2];
|
|
sdp_record_t record;
|
|
uint8_t u8 = si->channel ? si->channel : 5;
|
|
sdp_data_t *channel;
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&svclass_uuid, HEADSET_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &svclass_uuid);
|
|
sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile.uuid, HEADSET_PROFILE_ID);
|
|
profile.version = 0x0100;
|
|
pfseq = sdp_list_append(0, &profile);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap_uuid);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
|
|
proto[1] = sdp_list_append(0, &rfcomm_uuid);
|
|
channel = sdp_data_alloc(SDP_UINT8, &u8);
|
|
proto[1] = sdp_list_append(proto[1], channel);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
sdp_set_info_attr(&record, "Headset", 0, 0);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
printf("Headset service registered\n");
|
|
|
|
end:
|
|
sdp_data_free(channel);
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_handsfree(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
|
|
sdp_profile_desc_t profile;
|
|
sdp_list_t *aproto, *proto[2];
|
|
sdp_record_t record;
|
|
uint8_t u8 = si->channel ? si->channel : 6;
|
|
uint16_t u16 = 0x31;
|
|
sdp_data_t *channel, *features;
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&svclass_uuid, HANDSFREE_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &svclass_uuid);
|
|
sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
|
|
profile.version = 0x0101;
|
|
pfseq = sdp_list_append(0, &profile);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap_uuid);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
|
|
proto[1] = sdp_list_append(0, &rfcomm_uuid);
|
|
channel = sdp_data_alloc(SDP_UINT8, &u8);
|
|
proto[1] = sdp_list_append(proto[1], channel);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
features = sdp_data_alloc(SDP_UINT16, &u16);
|
|
sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
sdp_set_info_attr(&record, "Handsfree", 0, 0);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
printf("Handsfree service registered\n");
|
|
|
|
end:
|
|
sdp_data_free(channel);
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_handsfree_ag(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
|
|
sdp_profile_desc_t profile;
|
|
sdp_list_t *aproto, *proto[2];
|
|
sdp_record_t record;
|
|
uint8_t u8 = si->channel ? si->channel : 7;
|
|
uint16_t u16 = 0x17;
|
|
sdp_data_t *channel, *features;
|
|
uint8_t netid = si->network ? si->network : 0x01; // ???? profile document
|
|
sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&svclass_uuid, HANDSFREE_AGW_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &svclass_uuid);
|
|
sdp_uuid16_create(&ga_svclass_uuid, GENERIC_AUDIO_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile.uuid, HANDSFREE_PROFILE_ID);
|
|
profile.version = 0x0100;
|
|
pfseq = sdp_list_append(0, &profile);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap_uuid);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
|
|
proto[1] = sdp_list_append(0, &rfcomm_uuid);
|
|
channel = sdp_data_alloc(SDP_UINT8, &u8);
|
|
proto[1] = sdp_list_append(proto[1], channel);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
features = sdp_data_alloc(SDP_UINT16, &u16);
|
|
sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
sdp_set_info_attr(&record, "Voice Gateway", 0, 0);
|
|
|
|
sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
|
|
|
|
if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
printf("Handsfree AG service registered\n");
|
|
|
|
end:
|
|
sdp_data_free(channel);
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_simaccess(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, svclass_uuid, ga_svclass_uuid, l2cap_uuid, rfcomm_uuid;
|
|
sdp_profile_desc_t profile;
|
|
sdp_list_t *aproto, *proto[2];
|
|
sdp_record_t record;
|
|
uint8_t u8 = si->channel? si->channel : 8;
|
|
uint16_t u16 = 0x31;
|
|
sdp_data_t *channel, *features;
|
|
int ret = 0;
|
|
|
|
memset((void *)&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&svclass_uuid, SAP_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &svclass_uuid);
|
|
sdp_uuid16_create(&ga_svclass_uuid, GENERIC_TELEPHONY_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(svclass_id, &ga_svclass_uuid);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile.uuid, SAP_PROFILE_ID);
|
|
profile.version = 0x0101;
|
|
pfseq = sdp_list_append(0, &profile);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap_uuid);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
|
|
proto[1] = sdp_list_append(0, &rfcomm_uuid);
|
|
channel = sdp_data_alloc(SDP_UINT8, &u8);
|
|
proto[1] = sdp_list_append(proto[1], channel);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
features = sdp_data_alloc(SDP_UINT16, &u16);
|
|
sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
sdp_set_info_attr(&record, "SIM Access", 0, 0);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
printf("SIM Access service registered\n");
|
|
|
|
end:
|
|
sdp_data_free(channel);
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_opush(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
|
|
sdp_profile_desc_t profile[1];
|
|
sdp_list_t *aproto, *proto[3];
|
|
sdp_record_t record;
|
|
uint8_t chan = si->channel ? si->channel : 9;
|
|
sdp_data_t *channel;
|
|
uint8_t formats[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
|
|
//uint8_t formats[] = { 0xff };
|
|
void *dtds[sizeof(formats)], *values[sizeof(formats)];
|
|
int i;
|
|
uint8_t dtd = SDP_UINT8;
|
|
sdp_data_t *sflist;
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&opush_uuid, OBEX_OBJPUSH_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &opush_uuid);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile[0].uuid, OBEX_OBJPUSH_PROFILE_ID);
|
|
profile[0].version = 0x0100;
|
|
pfseq = sdp_list_append(0, profile);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap_uuid);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
|
|
proto[1] = sdp_list_append(0, &rfcomm_uuid);
|
|
channel = sdp_data_alloc(SDP_UINT8, &chan);
|
|
proto[1] = sdp_list_append(proto[1], channel);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
sdp_uuid16_create(&obex_uuid, OBEX_UUID);
|
|
proto[2] = sdp_list_append(0, &obex_uuid);
|
|
apseq = sdp_list_append(apseq, proto[2]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
for (i = 0; i < sizeof(formats); i++) {
|
|
dtds[i] = &dtd;
|
|
values[i] = &formats[i];
|
|
}
|
|
sflist = sdp_seq_alloc(dtds, values, sizeof(formats));
|
|
sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FORMATS_LIST, sflist);
|
|
|
|
sdp_set_info_attr(&record, "OBEX Object Push", 0, 0);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
printf("OBEX Object Push service registered\n");
|
|
|
|
end:
|
|
sdp_data_free(channel);
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(proto[2], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_ftp(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, ftrn_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
|
|
sdp_profile_desc_t profile[1];
|
|
sdp_list_t *aproto, *proto[3];
|
|
sdp_record_t record;
|
|
uint8_t u8 = si->channel ? si->channel: 10;
|
|
sdp_data_t *channel;
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&ftrn_uuid, OBEX_FILETRANS_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &ftrn_uuid);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile[0].uuid, OBEX_FILETRANS_PROFILE_ID);
|
|
profile[0].version = 0x0100;
|
|
pfseq = sdp_list_append(0, &profile[0]);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap_uuid);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
|
|
proto[1] = sdp_list_append(0, &rfcomm_uuid);
|
|
channel = sdp_data_alloc(SDP_UINT8, &u8);
|
|
proto[1] = sdp_list_append(proto[1], channel);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
sdp_uuid16_create(&obex_uuid, OBEX_UUID);
|
|
proto[2] = sdp_list_append(0, &obex_uuid);
|
|
apseq = sdp_list_append(apseq, proto[2]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
sdp_set_info_attr(&record, "OBEX File Transfer", 0, 0);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
printf("OBEX File Transfer service registered\n");
|
|
|
|
end:
|
|
sdp_data_free(channel);
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(proto[2], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_directprint(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, opush_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
|
|
sdp_profile_desc_t profile[1];
|
|
sdp_list_t *aproto, *proto[3];
|
|
sdp_record_t record;
|
|
uint8_t chan = si->channel ? si->channel : 12;
|
|
sdp_data_t *channel;
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&opush_uuid, DIRECT_PRINTING_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &opush_uuid);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile[0].uuid, BASIC_PRINTING_PROFILE_ID);
|
|
profile[0].version = 0x0100;
|
|
pfseq = sdp_list_append(0, profile);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap_uuid);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
|
|
proto[1] = sdp_list_append(0, &rfcomm_uuid);
|
|
channel = sdp_data_alloc(SDP_UINT8, &chan);
|
|
proto[1] = sdp_list_append(proto[1], channel);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
sdp_uuid16_create(&obex_uuid, OBEX_UUID);
|
|
proto[2] = sdp_list_append(0, &obex_uuid);
|
|
apseq = sdp_list_append(apseq, proto[2]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
sdp_set_info_attr(&record, "Direct Printing", 0, 0);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
printf("Direct Printing service registered\n");
|
|
|
|
end:
|
|
sdp_data_free(channel);
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(proto[2], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_nap(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
|
|
sdp_profile_desc_t profile[1];
|
|
sdp_list_t *aproto, *proto[2];
|
|
sdp_record_t record;
|
|
uint16_t lp = 0x000f, ver = 0x0100;
|
|
sdp_data_t *psm, *version;
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&ftrn_uuid, NAP_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &ftrn_uuid);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
|
|
profile[0].version = 0x0100;
|
|
pfseq = sdp_list_append(0, &profile[0]);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap_uuid);
|
|
psm = sdp_data_alloc(SDP_UINT16, &lp);
|
|
proto[0] = sdp_list_append(proto[0], psm);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
|
|
proto[1] = sdp_list_append(0, &bnep_uuid);
|
|
version = sdp_data_alloc(SDP_UINT16, &ver);
|
|
proto[1] = sdp_list_append(proto[1], version);
|
|
|
|
{
|
|
uint16_t ptype[4] = { 0x0010, 0x0020, 0x0030, 0x0040 };
|
|
sdp_data_t *head, *pseq;
|
|
int p;
|
|
|
|
for (p = 0, head = NULL; p < 4; p++) {
|
|
sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]);
|
|
head = sdp_seq_append(head, data);
|
|
}
|
|
pseq = sdp_data_alloc(SDP_SEQ16, head);
|
|
proto[1] = sdp_list_append(proto[1], pseq);
|
|
}
|
|
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
sdp_set_info_attr(&record, "Network Access Point Service", 0, 0);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
printf("NAP service registered\n");
|
|
|
|
end:
|
|
sdp_data_free(version);
|
|
sdp_data_free(psm);
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_gn(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
|
|
sdp_profile_desc_t profile[1];
|
|
sdp_list_t *aproto, *proto[2];
|
|
sdp_record_t record;
|
|
uint16_t lp = 0x000f, ver = 0x0100;
|
|
sdp_data_t *psm, *version;
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&ftrn_uuid, GN_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &ftrn_uuid);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID);
|
|
profile[0].version = 0x0100;
|
|
pfseq = sdp_list_append(0, &profile[0]);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap_uuid);
|
|
psm = sdp_data_alloc(SDP_UINT16, &lp);
|
|
proto[0] = sdp_list_append(proto[0], psm);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
|
|
proto[1] = sdp_list_append(0, &bnep_uuid);
|
|
version = sdp_data_alloc(SDP_UINT16, &ver);
|
|
proto[1] = sdp_list_append(proto[1], version);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
sdp_set_info_attr(&record, "Group Network Service", 0, 0);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
printf("GN service registered\n");
|
|
|
|
end:
|
|
sdp_data_free(version);
|
|
sdp_data_free(psm);
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_panu(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, ftrn_uuid, l2cap_uuid, bnep_uuid;
|
|
sdp_profile_desc_t profile[1];
|
|
sdp_list_t *aproto, *proto[2];
|
|
sdp_record_t record;
|
|
uint16_t lp = 0x000f, ver = 0x0100;
|
|
sdp_data_t *psm, *version;
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(NULL, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
sdp_list_free(root, NULL);
|
|
|
|
sdp_uuid16_create(&ftrn_uuid, PANU_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(NULL, &ftrn_uuid);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
sdp_list_free(svclass_id, NULL);
|
|
|
|
sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
|
|
profile[0].version = 0x0100;
|
|
pfseq = sdp_list_append(NULL, &profile[0]);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
sdp_list_free(pfseq, NULL);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(NULL, &l2cap_uuid);
|
|
psm = sdp_data_alloc(SDP_UINT16, &lp);
|
|
proto[0] = sdp_list_append(proto[0], psm);
|
|
apseq = sdp_list_append(NULL, proto[0]);
|
|
|
|
sdp_uuid16_create(&bnep_uuid, BNEP_UUID);
|
|
proto[1] = sdp_list_append(NULL, &bnep_uuid);
|
|
version = sdp_data_alloc(SDP_UINT16, &ver);
|
|
proto[1] = sdp_list_append(proto[1], version);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
aproto = sdp_list_append(NULL, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
sdp_set_info_attr(&record, "PAN User", NULL, NULL);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
printf("PANU service registered\n");
|
|
|
|
end:
|
|
sdp_data_free(version);
|
|
sdp_data_free(psm);
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_hid_keyb(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_record_t record;
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, hidkb_uuid, l2cap_uuid, hidp_uuid;
|
|
sdp_profile_desc_t profile[1];
|
|
sdp_list_t *aproto, *proto[3];
|
|
sdp_data_t *psm, *lang_lst, *lang_lst2, *hid_spec_lst, *hid_spec_lst2;
|
|
int i;
|
|
uint8_t dtd = SDP_UINT16;
|
|
uint8_t dtd2 = SDP_UINT8;
|
|
uint8_t dtd_data = SDP_TEXT_STR8;
|
|
void *dtds[2];
|
|
void *values[2];
|
|
void *dtds2[2];
|
|
void *values2[2];
|
|
int leng[2];
|
|
uint8_t hid_spec_type = 0x22;
|
|
uint16_t hid_attr_lang[] = { 0x409, 0x100 };
|
|
static const uint16_t ctrl = 0x11;
|
|
static const uint16_t intr = 0x13;
|
|
static const uint16_t hid_attr[] = { 0x100, 0x111, 0x40, 0x0d, 0x01, 0x01 };
|
|
static const uint16_t hid_attr2[] = { 0x0, 0x01, 0x100, 0x1f40, 0x01, 0x01 };
|
|
const uint8_t hid_spec[] = {
|
|
0x05, 0x01, // usage page
|
|
0x09, 0x06, // keyboard
|
|
0xa1, 0x01, // key codes
|
|
0x85, 0x01, // minimum
|
|
0x05, 0x07, // max
|
|
0x19, 0xe0, // logical min
|
|
0x29, 0xe7, // logical max
|
|
0x15, 0x00, // report size
|
|
0x25, 0x01, // report count
|
|
0x75, 0x01, // input data variable absolute
|
|
0x95, 0x08, // report count
|
|
0x81, 0x02, // report size
|
|
0x75, 0x08,
|
|
0x95, 0x01,
|
|
0x81, 0x01,
|
|
0x75, 0x01,
|
|
0x95, 0x05,
|
|
0x05, 0x08,
|
|
0x19, 0x01,
|
|
0x29, 0x05,
|
|
0x91, 0x02,
|
|
0x75, 0x03,
|
|
0x95, 0x01,
|
|
0x91, 0x01,
|
|
0x75, 0x08,
|
|
0x95, 0x06,
|
|
0x15, 0x00,
|
|
0x26, 0xff,
|
|
0x00, 0x05,
|
|
0x07, 0x19,
|
|
0x00, 0x2a,
|
|
0xff, 0x00,
|
|
0x81, 0x00,
|
|
0x75, 0x01,
|
|
0x95, 0x01,
|
|
0x15, 0x00,
|
|
0x25, 0x01,
|
|
0x05, 0x0c,
|
|
0x09, 0xb8,
|
|
0x81, 0x06,
|
|
0x09, 0xe2,
|
|
0x81, 0x06,
|
|
0x09, 0xe9,
|
|
0x81, 0x02,
|
|
0x09, 0xea,
|
|
0x81, 0x02,
|
|
0x75, 0x01,
|
|
0x95, 0x04,
|
|
0x81, 0x01,
|
|
0xc0 // end tag
|
|
};
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
add_lang_attr(&record);
|
|
|
|
sdp_uuid16_create(&hidkb_uuid, HID_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &hidkb_uuid);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile[0].uuid, HID_PROFILE_ID);
|
|
profile[0].version = 0x0100;
|
|
pfseq = sdp_list_append(0, profile);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
/* protocols */
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto[1] = sdp_list_append(0, &l2cap_uuid);
|
|
psm = sdp_data_alloc(SDP_UINT16, &ctrl);
|
|
proto[1] = sdp_list_append(proto[1], psm);
|
|
apseq = sdp_list_append(0, proto[1]);
|
|
|
|
sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
|
|
proto[2] = sdp_list_append(0, &hidp_uuid);
|
|
apseq = sdp_list_append(apseq, proto[2]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
/* additional protocols */
|
|
proto[1] = sdp_list_append(0, &l2cap_uuid);
|
|
psm = sdp_data_alloc(SDP_UINT16, &intr);
|
|
proto[1] = sdp_list_append(proto[1], psm);
|
|
apseq = sdp_list_append(0, proto[1]);
|
|
|
|
sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
|
|
proto[2] = sdp_list_append(0, &hidp_uuid);
|
|
apseq = sdp_list_append(apseq, proto[2]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_add_access_protos(&record, aproto);
|
|
|
|
sdp_set_info_attr(&record, "HID Keyboard", NULL, NULL);
|
|
|
|
for (i = 0; i < sizeof(hid_attr) / 2; i++)
|
|
sdp_attr_add_new(&record,
|
|
SDP_ATTR_HID_DEVICE_RELEASE_NUMBER + i,
|
|
SDP_UINT16, &hid_attr[i]);
|
|
|
|
dtds[0] = &dtd2;
|
|
values[0] = &hid_spec_type;
|
|
dtds[1] = &dtd_data;
|
|
values[1] = (uint8_t *) hid_spec;
|
|
leng[0] = 0;
|
|
leng[1] = sizeof(hid_spec);
|
|
hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2);
|
|
hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst);
|
|
sdp_attr_add(&record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2);
|
|
|
|
for (i = 0; i < sizeof(hid_attr_lang) / 2; i++) {
|
|
dtds2[i] = &dtd;
|
|
values2[i] = &hid_attr_lang[i];
|
|
}
|
|
|
|
lang_lst = sdp_seq_alloc(dtds2, values2, sizeof(hid_attr_lang) / 2);
|
|
lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst);
|
|
sdp_attr_add(&record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2);
|
|
|
|
sdp_attr_add_new(&record, SDP_ATTR_HID_SDP_DISABLE, SDP_UINT16, &hid_attr2[0]);
|
|
|
|
for (i = 0; i < sizeof(hid_attr2) / 2 - 1; i++)
|
|
sdp_attr_add_new(&record, SDP_ATTR_HID_REMOTE_WAKEUP + i,
|
|
SDP_UINT16, &hid_attr2[i + 1]);
|
|
|
|
if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
return -1;
|
|
}
|
|
|
|
printf("HID keyboard service registered\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int add_hid_wiimote(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_record_t record;
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, hid_uuid, l2cap_uuid, hidp_uuid;
|
|
sdp_profile_desc_t profile[1];
|
|
sdp_list_t *aproto, *proto[3];
|
|
sdp_data_t *psm, *lang_lst, *lang_lst2, *hid_spec_lst, *hid_spec_lst2;
|
|
int i;
|
|
uint8_t dtd = SDP_UINT16;
|
|
uint8_t dtd2 = SDP_UINT8;
|
|
uint8_t dtd_data = SDP_TEXT_STR8;
|
|
void *dtds[2];
|
|
void *values[2];
|
|
void *dtds2[2];
|
|
void *values2[2];
|
|
int leng[2];
|
|
uint8_t hid_spec_type = 0x22;
|
|
uint16_t hid_attr_lang[] = { 0x409, 0x100 };
|
|
uint16_t ctrl = 0x11, intr = 0x13;
|
|
uint16_t hid_release = 0x0100, parser_version = 0x0111;
|
|
uint8_t subclass = 0x04, country = 0x33;
|
|
uint8_t virtual_cable = 0, reconnect = 1, sdp_disable = 0;
|
|
uint8_t battery = 1, remote_wakeup = 1;
|
|
uint16_t profile_version = 0x0100, superv_timeout = 0x0c80;
|
|
uint8_t norm_connect = 0, boot_device = 0;
|
|
const uint8_t hid_spec[] = {
|
|
0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x10,
|
|
0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95,
|
|
0x01, 0x06, 0x00, 0xff, 0x09, 0x01, 0x91, 0x00,
|
|
0x85, 0x11, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
|
|
0x85, 0x12, 0x95, 0x02, 0x09, 0x01, 0x91, 0x00,
|
|
0x85, 0x13, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
|
|
0x85, 0x14, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
|
|
0x85, 0x15, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
|
|
0x85, 0x16, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00,
|
|
0x85, 0x17, 0x95, 0x06, 0x09, 0x01, 0x91, 0x00,
|
|
0x85, 0x18, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00,
|
|
0x85, 0x19, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
|
|
0x85, 0x1a, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00,
|
|
0x85, 0x20, 0x95, 0x06, 0x09, 0x01, 0x81, 0x00,
|
|
0x85, 0x21, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
|
|
0x85, 0x22, 0x95, 0x04, 0x09, 0x01, 0x81, 0x00,
|
|
0x85, 0x30, 0x95, 0x02, 0x09, 0x01, 0x81, 0x00,
|
|
0x85, 0x31, 0x95, 0x05, 0x09, 0x01, 0x81, 0x00,
|
|
0x85, 0x32, 0x95, 0x0a, 0x09, 0x01, 0x81, 0x00,
|
|
0x85, 0x33, 0x95, 0x11, 0x09, 0x01, 0x81, 0x00,
|
|
0x85, 0x34, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
|
|
0x85, 0x35, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
|
|
0x85, 0x36, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
|
|
0x85, 0x37, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
|
|
0x85, 0x3d, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
|
|
0x85, 0x3e, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
|
|
0x85, 0x3f, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00,
|
|
0xc0, 0x00
|
|
};
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(NULL, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&hid_uuid, HID_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(NULL, &hid_uuid);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile[0].uuid, HID_PROFILE_ID);
|
|
profile[0].version = 0x0100;
|
|
pfseq = sdp_list_append(NULL, profile);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto[1] = sdp_list_append(0, &l2cap_uuid);
|
|
psm = sdp_data_alloc(SDP_UINT16, &ctrl);
|
|
proto[1] = sdp_list_append(proto[1], psm);
|
|
apseq = sdp_list_append(0, proto[1]);
|
|
|
|
sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
|
|
proto[2] = sdp_list_append(0, &hidp_uuid);
|
|
apseq = sdp_list_append(apseq, proto[2]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
proto[1] = sdp_list_append(0, &l2cap_uuid);
|
|
psm = sdp_data_alloc(SDP_UINT16, &intr);
|
|
proto[1] = sdp_list_append(proto[1], psm);
|
|
apseq = sdp_list_append(0, proto[1]);
|
|
|
|
sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
|
|
proto[2] = sdp_list_append(0, &hidp_uuid);
|
|
apseq = sdp_list_append(apseq, proto[2]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_add_access_protos(&record, aproto);
|
|
|
|
add_lang_attr(&record);
|
|
|
|
sdp_set_info_attr(&record, "Nintendo RVL-CNT-01",
|
|
"Nintendo", "Nintendo RVL-CNT-01");
|
|
|
|
sdp_attr_add_new(&record, SDP_ATTR_HID_DEVICE_RELEASE_NUMBER,
|
|
SDP_UINT16, &hid_release);
|
|
|
|
sdp_attr_add_new(&record, SDP_ATTR_HID_PARSER_VERSION,
|
|
SDP_UINT16, &parser_version);
|
|
|
|
sdp_attr_add_new(&record, SDP_ATTR_HID_DEVICE_SUBCLASS,
|
|
SDP_UINT8, &subclass);
|
|
|
|
sdp_attr_add_new(&record, SDP_ATTR_HID_COUNTRY_CODE,
|
|
SDP_UINT8, &country);
|
|
|
|
sdp_attr_add_new(&record, SDP_ATTR_HID_VIRTUAL_CABLE,
|
|
SDP_BOOL, &virtual_cable);
|
|
|
|
sdp_attr_add_new(&record, SDP_ATTR_HID_RECONNECT_INITIATE,
|
|
SDP_BOOL, &reconnect);
|
|
|
|
dtds[0] = &dtd2;
|
|
values[0] = &hid_spec_type;
|
|
dtds[1] = &dtd_data;
|
|
values[1] = (uint8_t *) hid_spec;
|
|
leng[0] = 0;
|
|
leng[1] = sizeof(hid_spec);
|
|
hid_spec_lst = sdp_seq_alloc_with_length(dtds, values, leng, 2);
|
|
hid_spec_lst2 = sdp_data_alloc(SDP_SEQ8, hid_spec_lst);
|
|
sdp_attr_add(&record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_spec_lst2);
|
|
|
|
for (i = 0; i < sizeof(hid_attr_lang) / 2; i++) {
|
|
dtds2[i] = &dtd;
|
|
values2[i] = &hid_attr_lang[i];
|
|
}
|
|
|
|
lang_lst = sdp_seq_alloc(dtds2, values2, sizeof(hid_attr_lang) / 2);
|
|
lang_lst2 = sdp_data_alloc(SDP_SEQ8, lang_lst);
|
|
sdp_attr_add(&record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2);
|
|
|
|
sdp_attr_add_new(&record, SDP_ATTR_HID_SDP_DISABLE,
|
|
SDP_BOOL, &sdp_disable);
|
|
|
|
sdp_attr_add_new(&record, SDP_ATTR_HID_BATTERY_POWER,
|
|
SDP_BOOL, &battery);
|
|
|
|
sdp_attr_add_new(&record, SDP_ATTR_HID_REMOTE_WAKEUP,
|
|
SDP_BOOL, &remote_wakeup);
|
|
|
|
sdp_attr_add_new(&record, SDP_ATTR_HID_PROFILE_VERSION,
|
|
SDP_UINT16, &profile_version);
|
|
|
|
sdp_attr_add_new(&record, SDP_ATTR_HID_SUPERVISION_TIMEOUT,
|
|
SDP_UINT16, &superv_timeout);
|
|
|
|
sdp_attr_add_new(&record, SDP_ATTR_HID_NORMALLY_CONNECTABLE,
|
|
SDP_BOOL, &norm_connect);
|
|
|
|
sdp_attr_add_new(&record, SDP_ATTR_HID_BOOT_DEVICE,
|
|
SDP_BOOL, &boot_device);
|
|
|
|
if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
return -1;
|
|
}
|
|
|
|
printf("Wii-Mote service registered\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int add_cip(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, l2cap, cmtp, cip;
|
|
sdp_profile_desc_t profile[1];
|
|
sdp_list_t *aproto, *proto[2];
|
|
sdp_record_t record;
|
|
uint16_t psm = si->psm ? si->psm : 0x1001;
|
|
uint8_t netid = si->network ? si->network : 0x02; // 0x02 = ISDN, 0x03 = GSM
|
|
sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&cip, CIP_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &cip);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile[0].uuid, CIP_PROFILE_ID);
|
|
profile[0].version = 0x0100;
|
|
pfseq = sdp_list_append(0, &profile[0]);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
proto[0] = sdp_list_append(proto[0], sdp_data_alloc(SDP_UINT16, &psm));
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&cmtp, CMTP_UUID);
|
|
proto[1] = sdp_list_append(0, &cmtp);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
|
|
|
|
sdp_set_info_attr(&record, "Common ISDN Access", 0, 0);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
printf("CIP service registered\n");
|
|
|
|
end:
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
sdp_data_free(network);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_ctp(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, l2cap, tcsbin, ctp;
|
|
sdp_profile_desc_t profile[1];
|
|
sdp_list_t *aproto, *proto[2];
|
|
sdp_record_t record;
|
|
uint8_t netid = si->network ? si->network : 0x02; // 0x01-0x07 cf. p120 profile document
|
|
sdp_data_t *network = sdp_data_alloc(SDP_UINT8, &netid);
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&ctp, CORDLESS_TELEPHONY_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &ctp);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile[0].uuid, CORDLESS_TELEPHONY_PROFILE_ID);
|
|
profile[0].version = 0x0100;
|
|
pfseq = sdp_list_append(0, &profile[0]);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&tcsbin, TCS_BIN_UUID);
|
|
proto[1] = sdp_list_append(0, &tcsbin);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
sdp_attr_add(&record, SDP_ATTR_EXTERNAL_NETWORK, network);
|
|
|
|
sdp_set_info_attr(&record, "Cordless Telephony", 0, 0);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
printf("CTP service registered\n");
|
|
|
|
end:
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
sdp_data_free(network);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_a2source(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, l2cap, avdtp, a2src;
|
|
sdp_profile_desc_t profile[1];
|
|
sdp_list_t *aproto, *proto[2];
|
|
sdp_record_t record;
|
|
sdp_data_t *psm, *version;
|
|
uint16_t lp = 0x0019, ver = 0x0100;
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&a2src, AUDIO_SOURCE_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &a2src);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
|
|
profile[0].version = 0x0100;
|
|
pfseq = sdp_list_append(0, &profile[0]);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap);
|
|
psm = sdp_data_alloc(SDP_UINT16, &lp);
|
|
proto[0] = sdp_list_append(proto[0], psm);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&avdtp, AVDTP_UUID);
|
|
proto[1] = sdp_list_append(0, &avdtp);
|
|
version = sdp_data_alloc(SDP_UINT16, &ver);
|
|
proto[1] = sdp_list_append(proto[1], version);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
sdp_set_info_attr(&record, "Audio Source", 0, 0);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto done;
|
|
}
|
|
|
|
printf("Audio source service registered\n");
|
|
|
|
done:
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_a2sink(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, l2cap, avdtp, a2snk;
|
|
sdp_profile_desc_t profile[1];
|
|
sdp_list_t *aproto, *proto[2];
|
|
sdp_record_t record;
|
|
sdp_data_t *psm, *version;
|
|
uint16_t lp = 0x0019, ver = 0x0100;
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&a2snk, AUDIO_SINK_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &a2snk);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
|
|
profile[0].version = 0x0100;
|
|
pfseq = sdp_list_append(0, &profile[0]);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap);
|
|
psm = sdp_data_alloc(SDP_UINT16, &lp);
|
|
proto[0] = sdp_list_append(proto[0], psm);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&avdtp, AVDTP_UUID);
|
|
proto[1] = sdp_list_append(0, &avdtp);
|
|
version = sdp_data_alloc(SDP_UINT16, &ver);
|
|
proto[1] = sdp_list_append(proto[1], version);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
sdp_set_info_attr(&record, "Audio Sink", 0, 0);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto done;
|
|
}
|
|
|
|
printf("Audio sink service registered\n");
|
|
|
|
done:
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_avrct(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, l2cap, avctp, avrct;
|
|
sdp_profile_desc_t profile[1];
|
|
sdp_list_t *aproto, *proto[2];
|
|
sdp_record_t record;
|
|
sdp_data_t *psm, *version, *features;
|
|
uint16_t lp = 0x0017, ver = 0x0100, feat = 0x000f;
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&avrct, AV_REMOTE_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &avrct);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
|
|
profile[0].version = 0x0100;
|
|
pfseq = sdp_list_append(0, &profile[0]);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap);
|
|
psm = sdp_data_alloc(SDP_UINT16, &lp);
|
|
proto[0] = sdp_list_append(proto[0], psm);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&avctp, AVCTP_UUID);
|
|
proto[1] = sdp_list_append(0, &avctp);
|
|
version = sdp_data_alloc(SDP_UINT16, &ver);
|
|
proto[1] = sdp_list_append(proto[1], version);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
features = sdp_data_alloc(SDP_UINT16, &feat);
|
|
sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
|
|
|
|
sdp_set_info_attr(&record, "AVRCP CT", 0, 0);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto done;
|
|
}
|
|
|
|
printf("Remote control service registered\n");
|
|
|
|
done:
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_avrtg(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
|
|
uuid_t root_uuid, l2cap, avctp, avrtg;
|
|
sdp_profile_desc_t profile[1];
|
|
sdp_list_t *aproto, *proto[2];
|
|
sdp_record_t record;
|
|
sdp_data_t *psm, *version, *features;
|
|
uint16_t lp = 0x0017, ver = 0x0100, feat = 0x000f;
|
|
int ret = 0;
|
|
|
|
memset(&record, 0, sizeof(sdp_record_t));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(0, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
|
|
svclass_id = sdp_list_append(0, &avrtg);
|
|
sdp_set_service_classes(&record, svclass_id);
|
|
|
|
sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
|
|
profile[0].version = 0x0100;
|
|
pfseq = sdp_list_append(0, &profile[0]);
|
|
sdp_set_profile_descs(&record, pfseq);
|
|
|
|
sdp_uuid16_create(&l2cap, L2CAP_UUID);
|
|
proto[0] = sdp_list_append(0, &l2cap);
|
|
psm = sdp_data_alloc(SDP_UINT16, &lp);
|
|
proto[0] = sdp_list_append(proto[0], psm);
|
|
apseq = sdp_list_append(0, proto[0]);
|
|
|
|
sdp_uuid16_create(&avctp, AVCTP_UUID);
|
|
proto[1] = sdp_list_append(0, &avctp);
|
|
version = sdp_data_alloc(SDP_UINT16, &ver);
|
|
proto[1] = sdp_list_append(proto[1], version);
|
|
apseq = sdp_list_append(apseq, proto[1]);
|
|
|
|
aproto = sdp_list_append(0, apseq);
|
|
sdp_set_access_protos(&record, aproto);
|
|
|
|
features = sdp_data_alloc(SDP_UINT16, &feat);
|
|
sdp_attr_add(&record, SDP_ATTR_SUPPORTED_FEATURES, features);
|
|
|
|
sdp_set_info_attr(&record, "AVRCP TG", 0, 0);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
ret = -1;
|
|
goto done;
|
|
}
|
|
|
|
printf("Remote target service registered\n");
|
|
|
|
done:
|
|
sdp_list_free(proto[0], 0);
|
|
sdp_list_free(proto[1], 0);
|
|
sdp_list_free(apseq, 0);
|
|
sdp_list_free(aproto, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int add_udi_ue(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_record_t record;
|
|
sdp_list_t *root, *svclass, *proto;
|
|
uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
|
|
uint8_t channel = si->channel ? si->channel: 18;
|
|
|
|
memset(&record, 0, sizeof(record));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(NULL, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
sdp_list_free(root, NULL);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
|
|
|
|
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
|
|
proto = sdp_list_append(proto, sdp_list_append(
|
|
sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
|
|
|
|
sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
|
|
|
|
sdp_uuid16_create(&svclass_uuid, UDI_MT_SVCLASS_ID);
|
|
svclass = sdp_list_append(NULL, &svclass_uuid);
|
|
sdp_set_service_classes(&record, svclass);
|
|
sdp_list_free(svclass, NULL);
|
|
|
|
sdp_set_info_attr(&record, "UDI UE", NULL, NULL);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
return -1;
|
|
}
|
|
|
|
printf("UDI UE service registered\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int add_udi_te(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_record_t record;
|
|
sdp_list_t *root, *svclass, *proto;
|
|
uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
|
|
uint8_t channel = si->channel ? si->channel: 19;
|
|
|
|
memset(&record, 0, sizeof(record));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(NULL, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
sdp_list_free(root, NULL);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
|
|
|
|
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
|
|
proto = sdp_list_append(proto, sdp_list_append(
|
|
sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
|
|
|
|
sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
|
|
|
|
sdp_uuid16_create(&svclass_uuid, UDI_TA_SVCLASS_ID);
|
|
svclass = sdp_list_append(NULL, &svclass_uuid);
|
|
sdp_set_service_classes(&record, svclass);
|
|
sdp_list_free(svclass, NULL);
|
|
|
|
sdp_set_info_attr(&record, "UDI TE", NULL, NULL);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
return -1;
|
|
}
|
|
|
|
printf("UDI TE service registered\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned char sr1_uuid[] = { 0xbc, 0x19, 0x9c, 0x24, 0x95, 0x8b, 0x4c, 0xc0,
|
|
0xa2, 0xcb, 0xfd, 0x8a, 0x30, 0xbf, 0x32, 0x06 };
|
|
|
|
static int add_sr1(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_record_t record;
|
|
sdp_list_t *root, *svclass;
|
|
uuid_t root_uuid, svclass_uuid;
|
|
|
|
memset(&record, 0, sizeof(record));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(NULL, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid128_create(&svclass_uuid, (void *) sr1_uuid);
|
|
svclass = sdp_list_append(NULL, &svclass_uuid);
|
|
sdp_set_service_classes(&record, svclass);
|
|
|
|
sdp_set_info_attr(&record, "TOSHIBA SR-1", NULL, NULL);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
return -1;
|
|
}
|
|
|
|
printf("Toshiba Speech Recognition SR-1 service record registered\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned char syncmls_uuid[] = { 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x10, 0x00,
|
|
0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x02 };
|
|
|
|
static unsigned char syncmlc_uuid[] = { 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x00,
|
|
0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x02 };
|
|
|
|
static int add_syncml(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_record_t record;
|
|
sdp_list_t *root, *svclass, *proto;
|
|
uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid, obex_uuid;
|
|
uint8_t channel = si->channel ? si->channel: 15;
|
|
|
|
memset(&record, 0, sizeof(record));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(NULL, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid128_create(&svclass_uuid, (void *) syncmlc_uuid);
|
|
svclass = sdp_list_append(NULL, &svclass_uuid);
|
|
sdp_set_service_classes(&record, svclass);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
|
|
|
|
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
|
|
proto = sdp_list_append(proto, sdp_list_append(
|
|
sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
|
|
|
|
sdp_uuid16_create(&obex_uuid, OBEX_UUID);
|
|
proto = sdp_list_append(proto, sdp_list_append(NULL, &obex_uuid));
|
|
|
|
sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
|
|
|
|
sdp_set_info_attr(&record, "SyncML Client", NULL, NULL);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
return -1;
|
|
}
|
|
|
|
printf("SyncML Client service record registered\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned char async_uuid[] = { 0x03, 0x50, 0x27, 0x8F, 0x3D, 0xCA, 0x4E, 0x62,
|
|
0x83, 0x1D, 0xA4, 0x11, 0x65, 0xFF, 0x90, 0x6C };
|
|
|
|
static int add_activesync(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_record_t record;
|
|
sdp_list_t *root, *svclass, *proto;
|
|
uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
|
|
uint8_t channel = si->channel ? si->channel: 21;
|
|
|
|
memset(&record, 0, sizeof(record));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(NULL, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
|
|
|
|
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
|
|
proto = sdp_list_append(proto, sdp_list_append(
|
|
sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
|
|
|
|
sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
|
|
|
|
sdp_uuid128_create(&svclass_uuid, (void *) async_uuid);
|
|
svclass = sdp_list_append(NULL, &svclass_uuid);
|
|
sdp_set_service_classes(&record, svclass);
|
|
|
|
sdp_set_info_attr(&record, "Microsoft ActiveSync", NULL, NULL);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
return -1;
|
|
}
|
|
|
|
printf("ActiveSync service record registered\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned char hotsync_uuid[] = { 0xD8, 0x0C, 0xF9, 0xEA, 0x13, 0x4C, 0x11, 0xD5,
|
|
0x83, 0xCE, 0x00, 0x30, 0x65, 0x7C, 0x54, 0x3C };
|
|
|
|
static int add_hotsync(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_record_t record;
|
|
sdp_list_t *root, *svclass, *proto;
|
|
uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
|
|
uint8_t channel = si->channel ? si->channel: 22;
|
|
|
|
memset(&record, 0, sizeof(record));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(NULL, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
|
|
|
|
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
|
|
proto = sdp_list_append(proto, sdp_list_append(
|
|
sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
|
|
|
|
sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
|
|
|
|
sdp_uuid128_create(&svclass_uuid, (void *) hotsync_uuid);
|
|
svclass = sdp_list_append(NULL, &svclass_uuid);
|
|
sdp_set_service_classes(&record, svclass);
|
|
|
|
sdp_set_info_attr(&record, "PalmOS HotSync", NULL, NULL);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
return -1;
|
|
}
|
|
|
|
printf("HotSync service record registered\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned char palmos_uuid[] = { 0xF5, 0xBE, 0xB6, 0x51, 0x41, 0x71, 0x40, 0x51,
|
|
0xAC, 0xF5, 0x6C, 0xA7, 0x20, 0x22, 0x42, 0xF0 };
|
|
|
|
static int add_palmos(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_record_t record;
|
|
sdp_list_t *root, *svclass;
|
|
uuid_t root_uuid, svclass_uuid;
|
|
|
|
memset(&record, 0, sizeof(record));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(NULL, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid128_create(&svclass_uuid, (void *) palmos_uuid);
|
|
svclass = sdp_list_append(NULL, &svclass_uuid);
|
|
sdp_set_service_classes(&record, svclass);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
return -1;
|
|
}
|
|
|
|
printf("PalmOS service record registered\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned char nokid_uuid[] = { 0x00, 0x00, 0x55, 0x55, 0x00, 0x00, 0x10, 0x00,
|
|
0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
|
|
|
|
static int add_nokiaid(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_record_t record;
|
|
sdp_list_t *root, *svclass;
|
|
uuid_t root_uuid, svclass_uuid;
|
|
uint16_t verid = 0x005f;
|
|
sdp_data_t *version = sdp_data_alloc(SDP_UINT16, &verid);
|
|
|
|
memset(&record, 0, sizeof(record));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(NULL, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid128_create(&svclass_uuid, (void *) nokid_uuid);
|
|
svclass = sdp_list_append(NULL, &svclass_uuid);
|
|
sdp_set_service_classes(&record, svclass);
|
|
|
|
sdp_attr_add(&record, SDP_ATTR_SERVICE_VERSION, version);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
sdp_data_free(version);
|
|
return -1;
|
|
}
|
|
|
|
printf("Nokia ID service record registered\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned char pcsuite_uuid[] = { 0x00, 0x00, 0x50, 0x02, 0x00, 0x00, 0x10, 0x00,
|
|
0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
|
|
|
|
static int add_pcsuite(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_record_t record;
|
|
sdp_list_t *root, *svclass, *proto;
|
|
uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid;
|
|
uint8_t channel = si->channel ? si->channel: 14;
|
|
|
|
memset(&record, 0, sizeof(record));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(NULL, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
|
|
|
|
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
|
|
proto = sdp_list_append(proto, sdp_list_append(
|
|
sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
|
|
|
|
sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
|
|
|
|
sdp_uuid128_create(&svclass_uuid, (void *) pcsuite_uuid);
|
|
svclass = sdp_list_append(NULL, &svclass_uuid);
|
|
sdp_set_service_classes(&record, svclass);
|
|
|
|
sdp_set_info_attr(&record, "Nokia PC Suite", NULL, NULL);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
return -1;
|
|
}
|
|
|
|
printf("Nokia PC Suite service registered\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned char nftp_uuid[] = { 0x00, 0x00, 0x50, 0x05, 0x00, 0x00, 0x10, 0x00,
|
|
0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
|
|
|
|
static unsigned char nsyncml_uuid[] = { 0x00, 0x00, 0x56, 0x01, 0x00, 0x00, 0x10, 0x00,
|
|
0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
|
|
|
|
static unsigned char ngage_uuid[] = { 0x00, 0x00, 0x13, 0x01, 0x00, 0x00, 0x10, 0x00,
|
|
0x80, 0x00, 0x00, 0x02, 0xEE, 0x00, 0x00, 0x01 };
|
|
|
|
static unsigned char apple_uuid[] = { 0xf0, 0x72, 0x2e, 0x20, 0x0f, 0x8b, 0x4e, 0x90,
|
|
0x8c, 0xc2, 0x1b, 0x46, 0xf5, 0xf2, 0xef, 0xe2 };
|
|
|
|
static int add_apple(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_record_t record;
|
|
sdp_list_t *root;
|
|
uuid_t root_uuid;
|
|
uint32_t attr783 = 0x00000000;
|
|
uint32_t attr785 = 0x00000002;
|
|
uint16_t attr786 = 0x1234;
|
|
|
|
memset(&record, 0, sizeof(record));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(NULL, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_attr_add_new(&record, 0x0780, SDP_UUID128, (void *) apple_uuid);
|
|
sdp_attr_add_new(&record, 0x0781, SDP_TEXT_STR8, (void *) "Macmini");
|
|
sdp_attr_add_new(&record, 0x0782, SDP_TEXT_STR8, (void *) "PowerMac10,1");
|
|
sdp_attr_add_new(&record, 0x0783, SDP_UINT32, (void *) &attr783);
|
|
sdp_attr_add_new(&record, 0x0784, SDP_TEXT_STR8, (void *) "1.6.6f22");
|
|
sdp_attr_add_new(&record, 0x0785, SDP_UINT32, (void *) &attr785);
|
|
sdp_attr_add_new(&record, 0x0786, SDP_UUID16, (void *) &attr786);
|
|
|
|
sdp_set_info_attr(&record, "Apple Macintosh Attributes", NULL, NULL);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
return -1;
|
|
}
|
|
|
|
printf("Apple attribute service registered\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int add_isync(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_record_t record;
|
|
sdp_list_t *root, *svclass, *proto;
|
|
uuid_t root_uuid, svclass_uuid, serial_uuid, l2cap_uuid, rfcomm_uuid;
|
|
uint8_t channel = si->channel ? si->channel : 16;
|
|
|
|
memset(&record, 0, sizeof(record));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(NULL, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto = sdp_list_append(NULL, sdp_list_append(NULL, &l2cap_uuid));
|
|
|
|
sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
|
|
proto = sdp_list_append(proto, sdp_list_append(
|
|
sdp_list_append(NULL, &rfcomm_uuid), sdp_data_alloc(SDP_UINT8, &channel)));
|
|
|
|
sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
|
|
|
|
sdp_uuid16_create(&serial_uuid, SERIAL_PORT_SVCLASS_ID);
|
|
svclass = sdp_list_append(NULL, &serial_uuid);
|
|
|
|
sdp_uuid16_create(&svclass_uuid, APPLE_AGENT_SVCLASS_ID);
|
|
svclass = sdp_list_append(svclass, &svclass_uuid);
|
|
|
|
sdp_set_service_classes(&record, svclass);
|
|
|
|
sdp_set_info_attr(&record, "AppleAgent", "Bluetooth acceptor", "Apple Computer Ltd.");
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
return -1;
|
|
}
|
|
|
|
printf("Apple iSync service registered\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int add_semchla(sdp_session_t *session, svc_info_t *si)
|
|
{
|
|
sdp_record_t record;
|
|
sdp_profile_desc_t profile;
|
|
sdp_list_t *root, *svclass, *proto, *profiles;
|
|
uuid_t root_uuid, service_uuid, l2cap_uuid, semchla_uuid;
|
|
uint16_t psm = 0xf0f9;
|
|
|
|
memset(&record, 0, sizeof(record));
|
|
record.handle = si->handle;
|
|
|
|
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
|
|
root = sdp_list_append(NULL, &root_uuid);
|
|
sdp_set_browse_groups(&record, root);
|
|
|
|
sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
|
|
proto = sdp_list_append(NULL, sdp_list_append(
|
|
sdp_list_append(NULL, &l2cap_uuid), sdp_data_alloc(SDP_UINT16, &psm)));
|
|
|
|
sdp_uuid32_create(&semchla_uuid, 0x8e770300);
|
|
proto = sdp_list_append(proto, sdp_list_append(NULL, &semchla_uuid));
|
|
|
|
sdp_set_access_protos(&record, sdp_list_append(NULL, proto));
|
|
|
|
sdp_uuid32_create(&service_uuid, 0x8e771301);
|
|
svclass = sdp_list_append(NULL, &service_uuid);
|
|
|
|
sdp_set_service_classes(&record, svclass);
|
|
|
|
sdp_uuid32_create(&profile.uuid, 0x8e771302); // Headset
|
|
//sdp_uuid32_create(&profile.uuid, 0x8e771303); // Phone
|
|
profile.version = 0x0100;
|
|
profiles = sdp_list_append(NULL, &profile);
|
|
sdp_set_profile_descs(&record, profiles);
|
|
|
|
sdp_set_info_attr(&record, "SEMC HLA", NULL, NULL);
|
|
|
|
if (sdp_device_record_register(session, &interface, &record, SDP_RECORD_PERSIST) < 0) {
|
|
printf("Service Record registration failed\n");
|
|
return -1;
|
|
}
|
|
|
|
/* SEMC High Level Authentication */
|
|
printf("SEMC HLA service registered\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct {
|
|
char *name;
|
|
uint32_t class;
|
|
int (*add)(sdp_session_t *sess, svc_info_t *si);
|
|
unsigned char *uuid;
|
|
} service[] = {
|
|
{ "DID", PNP_INFO_SVCLASS_ID, NULL, },
|
|
|
|
{ "SP", SERIAL_PORT_SVCLASS_ID, add_sp },
|
|
{ "DUN", DIALUP_NET_SVCLASS_ID, add_dun },
|
|
{ "LAN", LAN_ACCESS_SVCLASS_ID, add_lan },
|
|
{ "FAX", FAX_SVCLASS_ID, add_fax },
|
|
{ "OPUSH", OBEX_OBJPUSH_SVCLASS_ID, add_opush },
|
|
{ "FTP", OBEX_FILETRANS_SVCLASS_ID, add_ftp },
|
|
{ "PRINT", DIRECT_PRINTING_SVCLASS_ID, add_directprint },
|
|
|
|
{ "HS", HEADSET_SVCLASS_ID, add_headset },
|
|
{ "HF", HANDSFREE_SVCLASS_ID, add_handsfree },
|
|
{ "HFAG", HANDSFREE_AGW_SVCLASS_ID, add_handsfree_ag},
|
|
{ "SAP", SAP_SVCLASS_ID, add_simaccess },
|
|
|
|
{ "NAP", NAP_SVCLASS_ID, add_nap },
|
|
{ "GN", GN_SVCLASS_ID, add_gn },
|
|
{ "PANU", PANU_SVCLASS_ID, add_panu },
|
|
|
|
{ "HCRP", HCR_SVCLASS_ID, NULL },
|
|
{ "HID", HID_SVCLASS_ID, NULL },
|
|
{ "KEYB", HID_SVCLASS_ID, add_hid_keyb },
|
|
{ "WIIMOTE", HID_SVCLASS_ID, add_hid_wiimote },
|
|
{ "CIP", CIP_SVCLASS_ID, add_cip },
|
|
{ "CTP", CORDLESS_TELEPHONY_SVCLASS_ID, add_ctp },
|
|
|
|
{ "A2SRC", AUDIO_SOURCE_SVCLASS_ID, add_a2source },
|
|
{ "A2SNK", AUDIO_SINK_SVCLASS_ID, add_a2sink },
|
|
{ "AVRCT", AV_REMOTE_SVCLASS_ID, add_avrct },
|
|
{ "AVRTG", AV_REMOTE_TARGET_SVCLASS_ID, add_avrtg },
|
|
|
|
{ "UDIUE", UDI_MT_SVCLASS_ID, add_udi_ue },
|
|
{ "UDITE", UDI_TA_SVCLASS_ID, add_udi_te },
|
|
|
|
{ "SEMCHLA", 0x8e771301, add_semchla },
|
|
|
|
{ "SR1", 0, add_sr1, sr1_uuid },
|
|
{ "SYNCML", 0, add_syncml, syncmlc_uuid },
|
|
{ "SYNCMLSERV", 0, NULL, syncmls_uuid },
|
|
{ "ACTIVESYNC", 0, add_activesync, async_uuid },
|
|
{ "HOTSYNC", 0, add_hotsync, hotsync_uuid },
|
|
{ "PALMOS", 0, add_palmos, palmos_uuid },
|
|
{ "NOKID", 0, add_nokiaid, nokid_uuid },
|
|
{ "PCSUITE", 0, add_pcsuite, pcsuite_uuid },
|
|
{ "NFTP", 0, NULL, nftp_uuid },
|
|
{ "NSYNCML", 0, NULL, nsyncml_uuid },
|
|
{ "NGAGE", 0, NULL, ngage_uuid },
|
|
{ "APPLE", 0, add_apple, apple_uuid },
|
|
|
|
{ "ISYNC", APPLE_AGENT_SVCLASS_ID, add_isync, },
|
|
|
|
{ 0 }
|
|
};
|
|
|
|
/* Add local service */
|
|
static int add_service(bdaddr_t *bdaddr, svc_info_t *si)
|
|
{
|
|
sdp_session_t *sess;
|
|
int i, ret = -1;
|
|
|
|
if (!si->name)
|
|
return -1;
|
|
|
|
sess = sdp_connect(&interface, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
|
|
if (!sess)
|
|
return -1;
|
|
|
|
for (i = 0; service[i].name; i++)
|
|
if (!strcasecmp(service[i].name, si->name)) {
|
|
if (service[i].add)
|
|
ret = service[i].add(sess, si);
|
|
goto done;
|
|
}
|
|
|
|
printf("Unknown service name: %s\n", si->name);
|
|
|
|
done:
|
|
free(si->name);
|
|
sdp_close(sess);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct option add_options[] = {
|
|
{ "help", 0, 0, 'h' },
|
|
{ "handle", 1, 0, 'r' },
|
|
{ "psm", 1, 0, 'p' },
|
|
{ "channel", 1, 0, 'c' },
|
|
{ "network", 1, 0, 'n' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
static char *add_help =
|
|
"Usage:\n"
|
|
"\tadd [--handle=RECORD_HANDLE --channel=CHANNEL] service\n";
|
|
|
|
static int cmd_add(int argc, char **argv)
|
|
{
|
|
svc_info_t si;
|
|
int opt;
|
|
|
|
memset(&si, 0, sizeof(si));
|
|
si.handle = 0xffffffff;
|
|
|
|
for_each_opt(opt, add_options, 0) {
|
|
switch (opt) {
|
|
case 'r':
|
|
if (strncasecmp(optarg, "0x", 2))
|
|
si.handle = atoi(optarg);
|
|
else
|
|
si.handle = strtol(optarg + 2, NULL, 16);
|
|
break;
|
|
case 'p':
|
|
if (strncasecmp(optarg, "0x", 2))
|
|
si.psm = atoi(optarg);
|
|
else
|
|
si.psm = strtol(optarg + 2, NULL, 16);
|
|
break;
|
|
case 'c':
|
|
if (strncasecmp(optarg, "0x", 2))
|
|
si.channel = atoi(optarg);
|
|
else
|
|
si.channel = strtol(optarg + 2, NULL, 16);
|
|
break;
|
|
case 'n':
|
|
if (strncasecmp(optarg, "0x", 2))
|
|
si.network = atoi(optarg);
|
|
else
|
|
si.network = strtol(optarg + 2, NULL, 16);
|
|
break;
|
|
default:
|
|
printf(add_help);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc < 1) {
|
|
printf(add_help);
|
|
return -1;
|
|
}
|
|
|
|
si.name = strdup(argv[0]);
|
|
|
|
return add_service(0, &si);
|
|
}
|
|
|
|
/* Delete local service */
|
|
static int del_service(bdaddr_t *bdaddr, void *arg)
|
|
{
|
|
uint32_t handle, range = 0x0000ffff;
|
|
sdp_list_t *attr;
|
|
sdp_session_t *sess;
|
|
sdp_record_t *rec;
|
|
|
|
if (!arg) {
|
|
printf("Record handle was not specified.\n");
|
|
return -1;
|
|
}
|
|
|
|
sess = sdp_connect(&interface, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);
|
|
if (!sess) {
|
|
printf("No local SDP server!\n");
|
|
return -1;
|
|
}
|
|
|
|
handle = strtoul((char *)arg, 0, 16);
|
|
attr = sdp_list_append(0, &range);
|
|
rec = sdp_service_attr_req(sess, handle, SDP_ATTR_REQ_RANGE, attr);
|
|
sdp_list_free(attr, 0);
|
|
|
|
if (!rec) {
|
|
printf("Service Record not found.\n");
|
|
sdp_close(sess);
|
|
return -1;
|
|
}
|
|
|
|
if (sdp_device_record_unregister(sess, &interface, rec)) {
|
|
printf("Failed to unregister service record: %s\n", strerror(errno));
|
|
sdp_close(sess);
|
|
return -1;
|
|
}
|
|
|
|
printf("Service Record deleted.\n");
|
|
sdp_close(sess);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct option del_options[] = {
|
|
{ "help", 0, 0, 'h' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
static char *del_help =
|
|
"Usage:\n"
|
|
"\tdel record_handle\n";
|
|
|
|
static int cmd_del(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
for_each_opt(opt, del_options, 0) {
|
|
switch (opt) {
|
|
default:
|
|
printf(del_help);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc < 1) {
|
|
printf(del_help);
|
|
return -1;
|
|
}
|
|
|
|
return del_service(NULL, argv[0]);
|
|
}
|
|
|
|
/*
|
|
* Perform an inquiry and search/browse all peer found.
|
|
*/
|
|
static void inquiry(handler_t handler, void *arg)
|
|
{
|
|
inquiry_info ii[20];
|
|
uint8_t count = 0;
|
|
int i;
|
|
|
|
printf("Inquiring ...\n");
|
|
if (sdp_general_inquiry(ii, 20, 8, &count) < 0) {
|
|
printf("Inquiry failed\n");
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < count; i++)
|
|
handler(&ii[i].bdaddr, arg);
|
|
}
|
|
|
|
static void doprintf(void *data, const char *str)
|
|
{
|
|
printf(str);
|
|
}
|
|
|
|
/*
|
|
* Search for a specific SDP service
|
|
*/
|
|
static int do_search(bdaddr_t *bdaddr, struct search_context *context)
|
|
{
|
|
sdp_list_t *attrid, *search, *seq, *next;
|
|
uint32_t range = 0x0000ffff;
|
|
char str[20];
|
|
sdp_session_t *sess;
|
|
|
|
if (!bdaddr) {
|
|
inquiry(do_search, context);
|
|
return 0;
|
|
}
|
|
|
|
sess = sdp_connect(&interface, bdaddr, SDP_RETRY_IF_BUSY);
|
|
ba2str(bdaddr, str);
|
|
if (!sess) {
|
|
printf("Failed to connect to SDP server on %s: %s\n", str, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
if (context->view != RAW_VIEW) {
|
|
if (context->svc)
|
|
printf("Searching for %s on %s ...\n", context->svc, str);
|
|
else
|
|
printf("Browsing %s ...\n", str);
|
|
}
|
|
|
|
attrid = sdp_list_append(0, &range);
|
|
search = sdp_list_append(0, &context->group);
|
|
if (sdp_service_search_attr_req(sess, search, SDP_ATTR_REQ_RANGE, attrid, &seq)) {
|
|
printf("Service Search failed: %s\n", strerror(errno));
|
|
sdp_close(sess);
|
|
return -1;
|
|
}
|
|
sdp_list_free(attrid, 0);
|
|
sdp_list_free(search, 0);
|
|
|
|
for (; seq; seq = next) {
|
|
sdp_record_t *rec = (sdp_record_t *) seq->data;
|
|
struct search_context sub_context;
|
|
|
|
switch (context->view) {
|
|
case DEFAULT_VIEW:
|
|
/* Display user friendly form */
|
|
print_service_attr(rec);
|
|
printf("\n");
|
|
break;
|
|
case TREE_VIEW:
|
|
/* Display full tree */
|
|
print_tree_attr(rec);
|
|
printf("\n");
|
|
break;
|
|
case XML_VIEW:
|
|
/* Display raw XML tree */
|
|
convert_sdp_record_to_xml(rec, 0, doprintf);
|
|
break;
|
|
default:
|
|
/* Display raw tree */
|
|
print_raw_attr(rec);
|
|
break;
|
|
}
|
|
|
|
if (sdp_get_group_id(rec, &sub_context.group) != -1) {
|
|
/* Set the subcontext for browsing the sub tree */
|
|
memcpy(&sub_context, context, sizeof(struct search_context));
|
|
/* Browse the next level down if not done */
|
|
if (sub_context.group.value.uuid16 != context->group.value.uuid16)
|
|
do_search(bdaddr, &sub_context);
|
|
}
|
|
next = seq->next;
|
|
free(seq);
|
|
sdp_record_free(rec);
|
|
}
|
|
|
|
sdp_close(sess);
|
|
return 0;
|
|
}
|
|
|
|
static struct option browse_options[] = {
|
|
{ "help", 0, 0, 'h' },
|
|
{ "tree", 0, 0, 't' },
|
|
{ "raw", 0, 0, 'r' },
|
|
{ "xml", 0, 0, 'x' },
|
|
{ "uuid", 1, 0, 'u' },
|
|
{ "l2cap", 0, 0, 'l' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
static char *browse_help =
|
|
"Usage:\n"
|
|
"\tbrowse [--tree] [--raw] [--xml] [--uuid uuid] [--l2cap] [bdaddr]\n";
|
|
|
|
/*
|
|
* Browse the full SDP database (i.e. list all services starting from the
|
|
* root/top-level).
|
|
*/
|
|
static int cmd_browse(int argc, char **argv)
|
|
{
|
|
struct search_context context;
|
|
int opt, num;
|
|
|
|
/* Initialise context */
|
|
memset(&context, '\0', sizeof(struct search_context));
|
|
/* We want to browse the top-level/root */
|
|
sdp_uuid16_create(&context.group, PUBLIC_BROWSE_GROUP);
|
|
|
|
for_each_opt(opt, browse_options, 0) {
|
|
switch (opt) {
|
|
case 't':
|
|
context.view = TREE_VIEW;
|
|
break;
|
|
case 'r':
|
|
context.view = RAW_VIEW;
|
|
break;
|
|
case 'x':
|
|
context.view = XML_VIEW;
|
|
break;
|
|
case 'u':
|
|
if (sscanf(optarg, "%i", &num) != 1 || num < 0 || num > 0xffff) {
|
|
printf("Invalid uuid %s\n", optarg);
|
|
return -1;
|
|
}
|
|
sdp_uuid16_create(&context.group, num);
|
|
break;
|
|
case 'l':
|
|
sdp_uuid16_create(&context.group, L2CAP_UUID);
|
|
break;
|
|
default:
|
|
printf(browse_help);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc >= 1) {
|
|
bdaddr_t bdaddr;
|
|
estr2ba(argv[0], &bdaddr);
|
|
return do_search(&bdaddr, &context);
|
|
}
|
|
|
|
return do_search(NULL, &context);
|
|
}
|
|
|
|
static struct option search_options[] = {
|
|
{ "help", 0, 0, 'h' },
|
|
{ "bdaddr", 1, 0, 'b' },
|
|
{ "tree", 0, 0, 't' },
|
|
{ "raw", 0, 0, 'r' },
|
|
{ "xml", 0, 0, 'x' },
|
|
{ 0, 0, 0, 0}
|
|
};
|
|
|
|
static char *search_help =
|
|
"Usage:\n"
|
|
"\tsearch [--bdaddr bdaddr] [--tree] [--raw] [--xml] SERVICE\n"
|
|
"SERVICE is a name (string) or UUID (0x1002)\n";
|
|
|
|
/*
|
|
* Search for a specific SDP service
|
|
*
|
|
* Note : we should support multiple services on the command line :
|
|
* sdptool search 0x0100 0x000f 0x1002
|
|
* (this would search a service supporting both L2CAP and BNEP directly in
|
|
* the top level browse group)
|
|
*/
|
|
static int cmd_search(int argc, char **argv)
|
|
{
|
|
struct search_context context;
|
|
unsigned char *uuid = NULL;
|
|
uint32_t class = 0;
|
|
bdaddr_t bdaddr;
|
|
int has_addr = 0;
|
|
int i;
|
|
int opt;
|
|
|
|
/* Initialise context */
|
|
memset(&context, '\0', sizeof(struct search_context));
|
|
|
|
for_each_opt(opt, search_options, 0) {
|
|
switch (opt) {
|
|
case 'b':
|
|
estr2ba(optarg, &bdaddr);
|
|
has_addr = 1;
|
|
break;
|
|
case 't':
|
|
context.view = TREE_VIEW;
|
|
break;
|
|
case 'r':
|
|
context.view = RAW_VIEW;
|
|
break;
|
|
case 'x':
|
|
context.view = XML_VIEW;
|
|
break;
|
|
default:
|
|
printf(search_help);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc < 1) {
|
|
printf(search_help);
|
|
return -1;
|
|
}
|
|
|
|
/* Note : we need to find a way to support search combining
|
|
* multiple services */
|
|
context.svc = strdup(argv[0]);
|
|
if (!strncasecmp(context.svc, "0x", 2)) {
|
|
int num;
|
|
/* This is a UUID16, just convert to int */
|
|
sscanf(context.svc + 2, "%X", &num);
|
|
class = num;
|
|
printf("Class 0x%X\n", class);
|
|
} else {
|
|
/* Convert class name to an UUID */
|
|
|
|
for (i = 0; service[i].name; i++)
|
|
if (strcasecmp(context.svc, service[i].name) == 0) {
|
|
class = service[i].class;
|
|
uuid = service[i].uuid;
|
|
break;
|
|
}
|
|
if (!class && !uuid) {
|
|
printf("Unknown service %s\n", context.svc);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (class) {
|
|
if (class & 0xffff0000)
|
|
sdp_uuid32_create(&context.group, class);
|
|
else {
|
|
uint16_t class16 = class & 0xffff;
|
|
sdp_uuid16_create(&context.group, class16);
|
|
}
|
|
} else
|
|
sdp_uuid128_create(&context.group, uuid);
|
|
|
|
if (has_addr)
|
|
return do_search(&bdaddr, &context);
|
|
|
|
return do_search(NULL, &context);
|
|
}
|
|
|
|
/*
|
|
* Show how to get a specific SDP record by its handle.
|
|
* Not really useful to the user, just show how it can be done...
|
|
*/
|
|
static int get_service(bdaddr_t *bdaddr, struct search_context *context, int quite)
|
|
{
|
|
sdp_list_t *attrid;
|
|
uint32_t range = 0x0000ffff;
|
|
sdp_record_t *rec;
|
|
sdp_session_t *session = sdp_connect(&interface, bdaddr, SDP_RETRY_IF_BUSY);
|
|
|
|
if (!session) {
|
|
char str[20];
|
|
ba2str(bdaddr, str);
|
|
printf("Failed to connect to SDP server on %s: %s\n", str, strerror(errno));
|
|
return -1;
|
|
}
|
|
|
|
attrid = sdp_list_append(0, &range);
|
|
rec = sdp_service_attr_req(session, context->handle, SDP_ATTR_REQ_RANGE, attrid);
|
|
sdp_list_free(attrid, 0);
|
|
sdp_close(session);
|
|
|
|
if (!rec) {
|
|
if (!quite) {
|
|
printf("Service get request failed.\n");
|
|
return -1;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
switch (context->view) {
|
|
case DEFAULT_VIEW:
|
|
/* Display user friendly form */
|
|
print_service_attr(rec);
|
|
printf("\n");
|
|
break;
|
|
case TREE_VIEW:
|
|
/* Display full tree */
|
|
print_tree_attr(rec);
|
|
printf("\n");
|
|
break;
|
|
case XML_VIEW:
|
|
/* Display raw XML tree */
|
|
convert_sdp_record_to_xml(rec, 0, doprintf);
|
|
break;
|
|
default:
|
|
/* Display raw tree */
|
|
print_raw_attr(rec);
|
|
break;
|
|
}
|
|
|
|
sdp_record_free(rec);
|
|
return 0;
|
|
}
|
|
|
|
static struct option records_options[] = {
|
|
{ "help", 0, 0, 'h' },
|
|
{ "tree", 0, 0, 't' },
|
|
{ "raw", 0, 0, 'r' },
|
|
{ "xml", 0, 0, 'x' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
static char *records_help =
|
|
"Usage:\n"
|
|
"\trecords [--tree] [--raw] [--xml] bdaddr\n";
|
|
|
|
/*
|
|
* Request possible SDP service records
|
|
*/
|
|
static int cmd_records(int argc, char **argv)
|
|
{
|
|
struct search_context context;
|
|
uint32_t base[] = { 0x10000, 0x10300, 0x10500,
|
|
0x1002e, 0x110b, 0x90000, 0x2008000,
|
|
0x4000000, 0x100000, 0x1000000, 0x4f491100 };
|
|
bdaddr_t bdaddr;
|
|
int i, n, opt, err = 0, num = 32;
|
|
|
|
/* Initialise context */
|
|
memset(&context, '\0', sizeof(struct search_context));
|
|
|
|
for_each_opt(opt, records_options, 0) {
|
|
switch (opt) {
|
|
case 't':
|
|
context.view = TREE_VIEW;
|
|
break;
|
|
case 'r':
|
|
context.view = RAW_VIEW;
|
|
break;
|
|
case 'x':
|
|
context.view = XML_VIEW;
|
|
break;
|
|
default:
|
|
printf(records_help);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc < 1) {
|
|
printf(records_help);
|
|
return -1;
|
|
}
|
|
|
|
/* Convert command line parameters */
|
|
estr2ba(argv[0], &bdaddr);
|
|
|
|
for (i = 0; i < sizeof(base) / sizeof(uint32_t); i++)
|
|
for (n = 0; n < num; n++) {
|
|
context.handle = base[i] + n;
|
|
err = get_service(&bdaddr, &context, 1);
|
|
if (err < 0)
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
return 0;
|
|
}
|
|
|
|
static struct option get_options[] = {
|
|
{ "help", 0, 0, 'h' },
|
|
{ "bdaddr", 1, 0, 'b' },
|
|
{ "tree", 0, 0, 't' },
|
|
{ "raw", 0, 0, 'r' },
|
|
{ "xml", 0, 0, 'x' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
static char *get_help =
|
|
"Usage:\n"
|
|
"\tget [--tree] [--raw] [--xml] [--bdaddr bdaddr] record_handle\n";
|
|
|
|
/*
|
|
* Get a specific SDP record on the local SDP server
|
|
*/
|
|
static int cmd_get(int argc, char **argv)
|
|
{
|
|
struct search_context context;
|
|
bdaddr_t bdaddr;
|
|
int has_addr = 0;
|
|
int opt;
|
|
|
|
/* Initialise context */
|
|
memset(&context, '\0', sizeof(struct search_context));
|
|
|
|
for_each_opt(opt, get_options, 0) {
|
|
switch (opt) {
|
|
case 'b':
|
|
estr2ba(optarg, &bdaddr);
|
|
has_addr = 1;
|
|
break;
|
|
case 't':
|
|
context.view = TREE_VIEW;
|
|
break;
|
|
case 'r':
|
|
context.view = RAW_VIEW;
|
|
break;
|
|
case 'x':
|
|
context.view = XML_VIEW;
|
|
break;
|
|
default:
|
|
printf(get_help);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc < 1) {
|
|
printf(get_help);
|
|
return -1;
|
|
}
|
|
|
|
/* Convert command line parameters */
|
|
context.handle = strtoul(argv[0], 0, 16);
|
|
|
|
return get_service(has_addr ? &bdaddr : BDADDR_LOCAL, &context, 0);
|
|
}
|
|
|
|
static struct {
|
|
char *cmd;
|
|
int (*func)(int argc, char **argv);
|
|
char *doc;
|
|
} command[] = {
|
|
{ "search", cmd_search, "Search for a service" },
|
|
{ "browse", cmd_browse, "Browse all available services" },
|
|
{ "records", cmd_records, "Request all records" },
|
|
{ "add", cmd_add, "Add local service" },
|
|
{ "del", cmd_del, "Delete local service" },
|
|
{ "get", cmd_get, "Get local service" },
|
|
{ "setattr", cmd_setattr, "Set/Add attribute to a SDP record" },
|
|
{ "setseq", cmd_setseq, "Set/Add attribute sequence to a SDP record" },
|
|
{ 0, 0, 0 }
|
|
};
|
|
|
|
static void usage(void)
|
|
{
|
|
int i, pos = 0;
|
|
|
|
printf("sdptool - SDP tool v%s\n", VERSION);
|
|
printf("Usage:\n"
|
|
"\tsdptool [options] <command> [command parameters]\n");
|
|
printf("Options:\n"
|
|
"\t-h\t\tDisplay help\n"
|
|
"\t-i\t\tSpecify source interface\n");
|
|
|
|
printf("Commands:\n");
|
|
for (i = 0; command[i].cmd; i++)
|
|
printf("\t%-4s\t\t%s\n", command[i].cmd, command[i].doc);
|
|
|
|
printf("\nServices:\n\t");
|
|
for (i = 0; service[i].name; i++) {
|
|
printf("%s ", service[i].name);
|
|
pos += strlen(service[i].name) + 1;
|
|
if (pos > 60) {
|
|
printf("\n\t");
|
|
pos = 0;
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
static struct option main_options[] = {
|
|
{ "help", 0, 0, 'h' },
|
|
{ "device", 1, 0, 'i' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int i, opt;
|
|
|
|
bacpy(&interface, BDADDR_ANY);
|
|
|
|
while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
|
|
switch(opt) {
|
|
case 'i':
|
|
if (!strncmp(optarg, "hci", 3))
|
|
hci_devba(atoi(optarg + 3), &interface);
|
|
else
|
|
str2ba(optarg, &interface);
|
|
break;
|
|
|
|
case 'h':
|
|
usage();
|
|
exit(0);
|
|
|
|
default:
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
argc -= optind;
|
|
argv += optind;
|
|
optind = 0;
|
|
|
|
if (argc < 1) {
|
|
usage();
|
|
exit(1);
|
|
}
|
|
|
|
for (i = 0; command[i].cmd; i++)
|
|
if (strncmp(command[i].cmd, argv[0], 4) == 0)
|
|
return command[i].func(argc, argv);
|
|
|
|
return 1;
|
|
}
|