Linux bluetooth development
 help / color / mirror / Atom feed
* Re: [Bluez-users] adding sdp service record attributes
@ 2005-03-16  1:21 Ka Kin Cheung
  2005-03-16  9:37 ` Marcel Holtmann
  0 siblings, 1 reply; 7+ messages in thread
From: Ka Kin Cheung @ 2005-03-16  1:21 UTC (permalink / raw)
  To: bluez-users

[-- Attachment #1: Type: text/plain, Size: 2918 bytes --]

Hi Marcel!
    Sorry for that. Maybe I rephrase the question like this.
    Now, I'm still struggling about how to add sdp service profiles in my Linux PC. And the most important thing is to let my computer to act as headset so that T610 can connect to it. I know that to add the service, using add_headset function is the way. But I find that only using this function is not enough, and I also find that there is a command called "setattr" that sets or adds the attributes to SDP record. Then I wish to know that if the setattr command, or set_attrib function in sdptool.c is necessary. If so, in the setattr command, there are two parameters that I've to enter, attrib_id and attrib_value. I wish to know what are the corresponding values of these two parameters from the following after I've typed "sdptool get --tree <recordhandle>"?
Attribute Identifier : 0x0 - ServiceRecordHandle
  Integer : 0x10002
Attribute Identifier : 0x1 - ServiceClassIDList
  Data Sequence
    UUID16 : 0x1108 - Headset
    UUID16 : 0x1203 - GenericAudio
Attribute Identifier : 0x4 - ProtocolDescriptorList
  Data Sequence
    Data Sequence
      UUID16 : 0x0100 - L2CAP
    Data Sequence
      UUID16 : 0x0003 - RFCOMM
      Channel/Port (Integer) : 0x5
Attribute Identifier : 0x5 - BrowseGroupList
  Data Sequence
    UUID16 : 0x1002 - PublicBrowseGroup (SDP)
Attribute Identifier : 0x9 - BluetoothProfileDescriptorList
  Data Sequence
    Data Sequence
      UUID16 : 0x1108 - Headset
      Version (Integer) : 0x100
Attribute Identifier : 0x100
     Lastly, I wish to konw that if I can let my PC to act as headset for T610, after I've added the profile in it, is there any program needed to install in T610 so that it can connect to the PC?
     Hope that this can be clearer to you. Thank you very much.
Michael
Marcel Holtmann <marcel@holtmann.org> wrote:
Hi Michael,

> I'm trying to add sdp service profile using sdptool, and I found
> one that is to add attribute to a SDP record. I wish to know that is
> that true? If so, I wish to know what the values should be put
> according to this command "sdptool get record_handle attrib_id
> attrib_value"? Here is the result when I typed "sdptool get --tree
> record_handle". I wish to know that what corresponds attrib_id and
> attrib_value?

I have no idea what you really wanna know. Try to rephrase your
question.

Regards

Marcel




-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now.
http://ads.osdn.com/?ad_id=6595&alloc_id=14396&op=click
_______________________________________________
Bluez-users mailing list
Bluez-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-users



---------------------------------
Yahoo! 工具列 - 內置防止彈出視窗工能!

[-- Attachment #2: Type: text/html, Size: 3649 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread
* Re: [Bluez-users] adding sdp service record attributes
@ 2005-03-18 10:44 Ka Kin Cheung
  0 siblings, 0 replies; 7+ messages in thread
From: Ka Kin Cheung @ 2005-03-18 10:44 UTC (permalink / raw)
  To: bluez-users

[-- Attachment #1: Type: text/plain, Size: 1118 bytes --]

Hi all!
    I've coded myself about how to add SDP services
and its attributes. But everytime I reboot Linux, the
service record handle and attribute do not exist, so I
have to run my program every time so that the record
and attribute are set. Also, even though I added the
headset service in my PC, T610 still searched my PC as
computer, not headset instead. So, I wish to ask 3
questions:
1.Is there any extra thing I have to put so that the
service record and attributes are set in my PC
permanently and not being erased after reboot?
2.After I add the service record and attribute, I wish
to let T610 to search my PC as headset. So, is there
any extra thing that I have to put in my code?
3.Is it possible that to browse the services in T610,
and even other mobile devices, and then add the
service records and attributes automatically?
    Attached is my code that I've written referencing
to sdptool.c. Hope you can give me solution about it.
Thank you very much.
Michael

_______________________________________________________________________
Yahoo! 工具列 - 內置防止彈出視窗工能!
http://toolbar.yahoo.com.hk

[-- Attachment #2: bic_sdp2.c --]
[-- Type: application/octet-stream, Size: 42632 bytes --]

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <getopt.h>
#include <netinet/in.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>

#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);
}

/* Pass args to the inquiry/search handler */
struct search_context {
	char		*svc;		/* Service */
	uuid_t		group;		/* Browse group */
	int		tree;		/* Display full attribute tree */
	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 (Service Discovery Protocol)", 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 (PAN/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)",
		sdp_attrib_names, sizeof(sdp_attrib_names)/sizeof(struct attrib_def) },
	{ 0x1001, "BrowseGroupDescriptorServiceClassID (SDP)",
		browse_attrib_names, sizeof(browse_attrib_names)/sizeof(struct attrib_def) },
	{ 0x1002, "PublicBrowseGroup (SDP)", 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 },
	/* ... */
	{ 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 },
	{ 0x112d, "SIM Access (SAP)", 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 },
};

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->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 
			//int i=0;
			printf("%.*sText : \"%s\"\n", indent, indent_spaces, sdpdata->val.str);
            /*for (i=0; SvcNameConv[i].sdp_data_struct.val.str; i++)
				if (!strcasecmp(SvcNameConv[i]->sdp_data_struct->val.str, sdpdata->val.str)
					strcpy(si->name, SvcNameConv[i]->service.name);  */
		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;
	}
	//return add_service(&bdaddr, &si);
}

/*
 * Parse a single attribute.
 */
static void sdp_attr_printf_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 sdp_printf_service_attr(sdp_record_t *rec)
{
	if (rec && rec->attrlist) {
		struct service_context service = { NULL };
		sdp_list_foreach(rec->attrlist, sdp_attr_printf_func, &service);
	}
}

/*
 * 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;
		
	/* Get the old SDP record */
	attrid_list = sdp_list_append(NULL, &range);
        	printf("Now we are setting attribute\n");
	rec = sdp_service_attr_req(sess, handle, SDP_ATTR_REQ_RANGE, attrid_list);
	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 */
	if (sdp_record_update(sess, rec)) {
		printf("Service Record update failed (%d).\n", errno);
		return -1;
	}
	return 0;
}

/*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";


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;
	}

	handle = strtoul(argv[0], NULL, 16);
	attrib = strtoul(argv[1], NULL, 16);

	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;

	/* 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);

	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 = (int *)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 */
		if (sdp_record_update(session, rec)) {
			printf("Service Record update failed (%d).\n", errno);
			return -1;
		}
	} else {
		printf("Failed to create pSequenceHolder\n");
	}

	/* Cleanup */
	for (i = 0; i < argc; i++)
		free(allocArray[i]);
	free(dtdArray);
	free(valueArray);

	return 0;
}

/*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";

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;
	}

	handle = strtoul(argv[0], NULL, 16);
	attrib = strtoul(argv[1], NULL, 16);

	argc -= 2;
	argv += 2;

	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);
	printf("  \"%s\" (0x%s)\n", ServiceClassUUID_str, 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_free(proto, (sdp_free_func_t)sdp_data_free);
	}
	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 {
	char *name;
	char *provider;
	char *desc;
	
	unsigned int class;
	unsigned int profile;
	unsigned int channel;
} svc_info_t;

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_record_t record;
	sdp_profile_desc_t profile;
	uint8_t u8 = si->channel? si->channel: 1;
	sdp_data_t *channel;
	int ret = 0;

	memset((void *)&record, 0, sizeof(sdp_record_t));
	record.handle = 0xffffffff;
	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);

	sdp_set_info_attr(&record, "Serial Port", 0, 0);

	if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {
		printf("Service Record registration failed\n");
		ret = -1;
		goto end;
	}
	printf("Serial Port service registered\n");
	printf("Service name: %s \n", si->name);
	printf("Service provider: %s \n", si->provider);
	printf("Service desc: %s \n", si->desc);
	printf("Service class: %d \n", si->class);
	printf("Service profile: %d \n", si->profile);
	printf("Service channel: %d \n", si->channel);
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_record_t record;
	sdp_profile_desc_t profile;
	sdp_list_t *aproto, *proto[2];
	uint8_t u8 = si->channel? si->channel: 5;
	sdp_data_t *channel;
	int ret = 0;

	memset((void *)&record, 0, sizeof(sdp_record_t));
	record.handle = 0xffffffff;
	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_record_register(session, &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_record_t record;	
	sdp_profile_desc_t profile;
	sdp_list_t *aproto, *proto[2];
	uint8_t u8 = si->channel? si->channel: 3;
	uint16_t u16 = 0x31;
	sdp_data_t *channel, *features;	
	int ret = 0;

	memset((void *)&record, 0, sizeof(sdp_record_t));
	record.handle = 0xffffffff;
	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_record_register(session, &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_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_record_t record;	
	sdp_profile_desc_t profile;
	sdp_list_t *aproto, *proto[2];
	uint8_t u8 = si->channel? si->channel: 3;
	uint16_t u16 = 0x31;
	sdp_data_t *channel, *features;	
	int ret = 0;

	memset((void *)&record, 0, sizeof(sdp_record_t));
	record.handle = 0xffffffff;
	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_record_register(session, &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_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: 4;
	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((void *)&record, 0, sizeof(sdp_record_t));
	record.handle = 0xffffffff;
	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_record_register(session, &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_file_trans(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: 4;
	sdp_data_t *channel;
	int ret = 0;

	memset((void *)&record, 0, sizeof(sdp_record_t));
	record.handle = 0xffffffff;
	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_record_register(session, &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;
}

struct {
	char		*name;
	uint16_t	class;
	int		(*add)(sdp_session_t *sess, svc_info_t *si);
} service[] = {
	{ "SP",		SERIAL_PORT_SVCLASS_ID,		add_sp		},
	{ "OPUSH",	OBEX_OBJPUSH_SVCLASS_ID,	add_opush	},
	{ "FTRN",	OBEX_FILETRANS_SVCLASS_ID,	add_file_trans	},
	{ "HS",		HEADSET_SVCLASS_ID,		add_headset	},
	{ "HF",		HANDSFREE_SVCLASS_ID,		add_handsfree	},
	{ "SAP",	SAP_SVCLASS_ID,			add_simaccess	},
	{ 0 }
};

/* Add local service */
static int add_service(bdaddr_t *bdaddr, svc_info_t *si)
{
	int i;
	sdp_session_t *sess = sdp_connect(&interface, BDADDR_LOCAL, SDP_RETRY_IF_BUSY);

	if (!sess)
		return -1;
	if (si->name)
		for (i = 0; service[i].name; i++)
			if (!strcasecmp(service[i].name, si->name)) {
				int ret = -1;
				if (service[i].add)
					ret = service[i].add(sess, si);
                                printf("sdp service is added\n");
                                //free(si->name);
				sdp_close(sess);
				return ret;
			}
	printf("Unknown service name: %s\n", si->name);
	free(si->name);
	sdp_close(sess);
	return -1;
}

//return add_service(0, &si);

/*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 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->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;

		if (context->tree) {
			sdp_printf_service_attr(rec);
		} else {
			print_service_attr(rec);
		}
		printf("\n");
		
		if (sdp_get_group_id(rec, &sub_context.group) != -1) {
			memcpy(&sub_context, context, sizeof(struct search_context));
			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' },
	{ 0, 0, 0, 0 }
};*/

typedef struct {
		bdaddr_t *bdaddr;
		uint32_t handle;
		uint16_t attrib;
		char *attri_value;
} profile;

int main()
{
	profile T610profile;
    svc_info_t T610svc;
	bdaddr_t ba;
    char *str="00:0A:D9:DD:D8:A8";
    estr2ba(str, &ba);
	sdp_session_t *sess;
	printf("Welcome to bic_sdp\n");
	T610profile.handle=0x10001;
	T610profile.attrib=0x100;
    T610profile.attri_value="0x1108";
	printf("T610 profile is added\n");
	T610svc.class=0x1108;
	printf("Service name is added\n");
    T610svc.profile=0x1108;
	T610svc.channel=3;
    T610svc.name="HS";
	add_service(T610profile.bdaddr, &T610svc);
	printf("Now attribute will be set\n");
	sess = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
	set_attrib(sess, T610profile.handle, T610profile.attrib, T610profile.attri_value);
    printf("attributes are set\n");
	return 0;
}

^ permalink raw reply	[flat|nested] 7+ messages in thread
* Re: [Bluez-users] adding sdp service record attributes
@ 2005-03-16 13:57 Ka Kin Cheung
  2005-03-16 14:08 ` Marcel Holtmann
  0 siblings, 1 reply; 7+ messages in thread
From: Ka Kin Cheung @ 2005-03-16 13:57 UTC (permalink / raw)
  To: bluez-users

[-- Attachment #1: Type: text/plain, Size: 3025 bytes --]

Hi Marcel!
    Thanks for your reply. But I wish to know that from the result I've put in the last email, which belong to attrib_id and attrib_value respectively? I just want to know about this and then I'll put the correspnding values in my program to add it.  You can find the result of "sdptool get --tree " from last email. Thank you very much.
Michael
Marcel Holtmann <marcel@holtmann.org> wrote:
Hi Michael,

> Sorry for that. Maybe I rephrase the question like this.
> Now, I'm still struggling about how to add sdp service profiles in
> my Linux PC. And the most important thing is to let my computer to act
> as headset so that T610 can connect to it. I know that to add the
> service, using add_headset function is the way. But I find that only
> using this function is not enough, and I also find that there is a
> command called "setattr" that sets or adds the attributes to SDP
> record. Then I wish to know that if the setattr command, or set_attrib
> function in sdptool.c is necessary. If so, in the setattr command,
> there are two parameters that I've to enter, attrib_id and
> attrib_value. I wish to know what are the corresponding values of
> these two parameters from the following after I've typed "sdptool get
> --tree "?
> Attribute Identifier : 0x0 - ServiceRecordHandle
> Integer : 0x10002
> Attribute Identifier : 0x1 - ServiceClassIDList
> Data Sequence
> UUID16 : 0x1108 - Headset
> UUID16 : 0x1203 - GenericAudio
> Attribute Identifier : 0x4 - ProtocolDescriptorList
> Data Sequence
> Data Sequence
> UUID16 : 0x0100 - L2CAP
> Data Sequence
> UUID16 : 0x0003 - RFCOMM
> Channel/Port (Integer) : 0x5
> Attribute Identifier : 0x5 - BrowseGroupList
> Data Sequence
> UUID16 : 0x1002 - PublicBrowseGroup (SDP)
> Attribute Identifier : 0x9 - BluetoothProfileDescriptorList
> Data Sequence
> Data Sequence
> UUID16 : 0x1108 - Headset
> Version (Integer) : 0x100
> Attribute Identifier : 0x100
> Lastly, I wish to konw that if I can let my PC to act as headset
> for T610, after I've added the profile in it, is there any program
> needed to install in T610 so that it can connect to the PC?
> Hope that this can be clearer to you. Thank you very much.

now I get what you want. In general adding SDP records with sdptool is
only for testing purpose. You should include you own code for it, like
we do for pand or dund. However if you think that the add_headset() in
sdptool misses an attribute then fix this and send in a patch for it.

Regards

Marcel




-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now.
http://ads.osdn.com/?ad_id=6595&alloc_id=14396&op=click
_______________________________________________
Bluez-users mailing list
Bluez-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/bluez-users



---------------------------------
Yahoo! 工具列 - 內置防止彈出視窗工能!

[-- Attachment #2: Type: text/html, Size: 3590 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread
* [Bluez-users] adding sdp service record attributes
@ 2005-03-15 15:22 Ka Kin Cheung
  2005-03-15 16:09 ` Marcel Holtmann
  0 siblings, 1 reply; 7+ messages in thread
From: Ka Kin Cheung @ 2005-03-15 15:22 UTC (permalink / raw)
  To: bluez-user list

[-- Attachment #1: Type: text/plain, Size: 1243 bytes --]

Hi all!
    I'm trying to add sdp service profile using sdptool, and I found one that is to add attribute to a SDP record. I wish to know that is that true? If so, I wish to know what the values should be put according to this command "sdptool get record_handle attrib_id attrib_value"? Here is the result when I typed "sdptool get --tree record_handle". I wish to know that what corresponds attrib_id and attrib_value?
    Thank you very much.
Michael
 
Attribute Identifier : 0x0 - ServiceRecordHandle
  Integer : 0x10002
Attribute Identifier : 0x1 - ServiceClassIDList
  Data Sequence
    UUID16 : 0x1108 - Headset
    UUID16 : 0x1203 - GenericAudio
Attribute Identifier : 0x4 - ProtocolDescriptorList
  Data Sequence
    Data Sequence
      UUID16 : 0x0100 - L2CAP
    Data Sequence
      UUID16 : 0x0003 - RFCOMM
      Channel/Port (Integer) : 0x5
Attribute Identifier : 0x5 - BrowseGroupList
  Data Sequence
    UUID16 : 0x1002 - PublicBrowseGroup (SDP)
Attribute Identifier : 0x9 - BluetoothProfileDescriptorList
  Data Sequence
    Data Sequence
      UUID16 : 0x1108 - Headset
      Version (Integer) : 0x100
Attribute Identifier : 0x100
  Text : "Headset"




---------------------------------
Yahoo! 工具列 - 內置防止彈出視窗工能!

[-- Attachment #2: Type: text/html, Size: 1697 bytes --]

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2005-03-18 10:44 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-03-16  1:21 [Bluez-users] adding sdp service record attributes Ka Kin Cheung
2005-03-16  9:37 ` Marcel Holtmann
  -- strict thread matches above, loose matches on Subject: below --
2005-03-18 10:44 Ka Kin Cheung
2005-03-16 13:57 Ka Kin Cheung
2005-03-16 14:08 ` Marcel Holtmann
2005-03-15 15:22 Ka Kin Cheung
2005-03-15 16:09 ` Marcel Holtmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox