Пример #1
0
int register_record(int descriptor_is_relative){


		// Connect to to local SDP-Servicer on localhost, to publish
		// the new service record.

		// Session will be established.
		sdp_session_t *session;

		session = sdp_connect ( BDADDR_ANY, BDADDR_LOCAL, SDP_RETRY_IF_BUSY );

			if ( ! session )
			{

				//	LOGCAT:
				__android_log_print(ANDROID_LOG_DEBUG, TAG, "SDP: Connection to SDP failed.");

				return CODE_SDP_SESSION_FAILED;
			}

				//	LOGCAT:
				__android_log_print(ANDROID_LOG_DEBUG, TAG, "SDP: Connection to SDP established.");

				//	Service record, to be included in the registry of the SDP-Server
				sdp_record_t record;

				//	To create the record, several lists must be put together
				sdp_list_t *languages, *service_class_id, *profile_sequence, *access_protocol_sequence, *root_list, *access_protocol, *protocol_list[3];

				//	The uuid_t data type is used to represent the 128-bit UUID that
				//	is used as un universal unique identifier. SEE THE THESIS, SUBSECTION 2.2.2
				uuid_t root_uuid, hid_uuid, l2cap_uuid, hidp_uuid;

				//	BluetoothProfileDescriptorList. SEE THE THESIS, SUBSECTION 4.2.6
				sdp_profile_desc_t sdp_profile[1];

				//	LanguageBaseAttributeIDList, SEE THE THESIS, SUBSECTION 4.2.4
				sdp_lang_attr_t base_language;

				//	The sdp_data_t structure stores information in a service record
				sdp_data_t *psm, *hid_descriptor_list, *hid_descriptor_formatted_list, *language_base_list, *language_base_formatted_list;

				//	SDP-specific types: 8- and 16- bit unsigned integers and a 8-bit string
				uint8_t data_type_uint8 	= SDP_UINT8;
				uint8_t data_type_uint16 	= SDP_UINT16;
				uint8_t data_type_str8	 	= SDP_TEXT_STR8;


				int length[2];

				//	arrays used to form the sdp specific strcuture of sequence attribites: descriptor and language base
				void *data_types[2] , *descriptor_array[2], *language_base_array[2];

				memset(&record, 0, sizeof(sdp_record_t));

				//	Assigning the Record Handle, SEE THE THESIS, SUBSECTION 4.2.2
				record.handle = handle;

				// Putting together attributes, common to all services.
				// SEE THE THESIS, SECTION 4.2

			    // Setting the Browse Group and making
				// the service record publicly available.
				// SEE THE THESIS, SUBSECTION 4.2.3
			    sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
			    root_list = sdp_list_append(0, &root_uuid);
			    sdp_set_browse_groups( &record, root_list );


				// The LanguageBaseAttributeIDList
			    // SEE THE THESIS, SUBSECTION 4.2.4
				base_language.code_ISO639 = (0x65 << 8) | 0x6e;
				base_language.base_offset = SDP_PRIMARY_LANG_BASE;
				base_language.encoding = 106;

				languages = sdp_list_append(0, &base_language);
				sdp_set_lang_attr(&record, languages);
				sdp_list_free(languages, 0);


				//	The ServiceClassIDList
				//	SEE THE THESIS, SUBSECTION 4.2.1
				sdp_uuid16_create(&hid_uuid, HID_SVCLASS_ID);
				service_class_id = sdp_list_append(0, &hid_uuid);
				sdp_set_service_classes(&record, service_class_id);

				// The BluetoothProfileDescriptorList
				// SEE THE THESIS, SUBSECTION 4.2.6
				sdp_uuid16_create(&sdp_profile[0].uuid, HID_PROFILE_ID);
				sdp_profile[0].version = 0x0100;
				profile_sequence = sdp_list_append(0, sdp_profile);
				sdp_set_profile_descs(&record, profile_sequence);


				// The ProtocolDescriptorList
				// SEE THE THESIS, SUBSECTION 4.2.5

				//	We set information about:

				//	A) Control Channel ->
				//	B) Interruption Channel

				//	We use the protocol_list to bind information
				//	about L2CAP and HID stack level

				//	A) Control Channel (PSM: 0x11)
				// 	1) Set info on L2CAP
				sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
				protocol_list[1] = sdp_list_append(0, &l2cap_uuid);
				psm = sdp_data_alloc(SDP_UINT16, &control_channel_psm);
				protocol_list[1] = sdp_list_append(protocol_list[1], psm);
				access_protocol_sequence = sdp_list_append(0, protocol_list[1]);
				// 	2) Set info on HID
				sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
				protocol_list[2] = sdp_list_append(0, &hidp_uuid);
				access_protocol_sequence = sdp_list_append(access_protocol_sequence, protocol_list[2]);
				// bind to DecriptorList
				access_protocol = sdp_list_append(0, access_protocol_sequence);
				sdp_set_access_protos(&record, access_protocol);

				//	B) Interruption Channel (PSM: 0x13)
				// 	1) Set info on L2CAP
				protocol_list[1] = sdp_list_append(0, &l2cap_uuid);
				psm = sdp_data_alloc(SDP_UINT16, &interruption_channel_psm);
				protocol_list[1] = sdp_list_append(protocol_list[1], psm);
				access_protocol_sequence = sdp_list_append(0, protocol_list[1]);
				// 	2) Set info on HID
				sdp_uuid16_create(&hidp_uuid, HIDP_UUID);
				protocol_list[2] = sdp_list_append(0, &hidp_uuid);
				access_protocol_sequence = sdp_list_append(access_protocol_sequence, protocol_list[2]);

				access_protocol = sdp_list_append(0, access_protocol_sequence);
				sdp_set_add_access_protos(&record, access_protocol);



				//	Setting the information on device, author and service,
				//	throuth the human-readable attributes
				//  SEE THE THESIS, SUBSECTION 4.2.4
				sdp_set_info_attr(&record, SERVICE_NAME, SERVICE_INFO, AUTHOR_INFO);

				// After putting together the attributes, common to all services,
				// we bind the attributes that are specific to the HID service.
				// SEE THE THESIS, SECTION 4.3

				//	Adding all HID attributes (mandatory or optional)
				//	explained in the THESIS (4.3.1 - 4.3.13)

				//  SEE THE THESIS, SUBSECTION 4.3.1
				if(descriptor_is_relative){

					sdp_attr_add_new(	&record,
										SDP_ATTR_HID_DEVICE_RELEASE_NUMBER,
										SDP_UINT16,
										&attr_release_number_mouse_keyboard);

				}else{

					sdp_attr_add_new(	&record,
										SDP_ATTR_HID_DEVICE_RELEASE_NUMBER,
										SDP_UINT16,
										&attr_release_number_pointer_keyboard);

				}



					//	 SEE THE THESIS, SUBSECTION 4.3.2
					sdp_attr_add_new(	&record,
										SDP_ATTR_HID_PARSER_VERSION,
										SDP_UINT16,
										&attr_parser_version);

					//  SEE THE THESIS, SUBSECTION 4.3.3
					sdp_attr_add_new(	&record,
										SDP_ATTR_HID_DEVICE_SUBCLASS,
										SDP_UINT8,
										&attr_device_subclass);

					//  SEE THE THESIS, SUBSECTION 4.3.4
					sdp_attr_add_new(	&record,
										SDP_ATTR_HID_COUNTRY_CODE,
										SDP_UINT8,
										&attr_country_code);

					//  SEE THE THESIS, SUBSECTION 4.3.5
					sdp_attr_add_new(	&record,
										SDP_ATTR_HID_VIRTUAL_CABLE,
										SDP_BOOL,
										&attr_virtual_cable);

					//  SEE THE THESIS, SUBSECTION 4.3.6
					sdp_attr_add_new(	&record,
										SDP_ATTR_HID_RECONNECT_INITIATE,
										SDP_BOOL,
										&attr_reconnect_initiate);


					//	SEE THE THESIS, SUBSECTION 4.3.13

					//	Adding the HIDDescriptorList attribute is more complex,
					//  thus it is data sequence.
					//
					//	The attribute contains two things:
					// 	A) The descriptor type as unsigned integer (In our case it is a report descriptor)
					//  B) The two descriptors as string sequence (keyboard + mouse or keyboard + pointer)

					data_types[0] = &data_type_uint8;
					data_types[1] = &data_type_str8;

					// We check the descriptor_is_realtive value to
					// find out, if the keyboard + mouse or keyboard + pointer
					// service description is to be added.
					if(descriptor_is_relative){

					descriptor_array[0] = &hid_descriptor_type;
					descriptor_array[1] =(uint8_t *) attr_hid_descriptor_relative;
					length[0] = 0;
					length[1] = sizeof(attr_hid_descriptor_relative);

					}else{

					descriptor_array[0] = &hid_descriptor_type;
					descriptor_array[1] =(uint8_t *) attr_hid_descriptor_absolute;
					length[0] = 0;
					length[1] = sizeof(attr_hid_descriptor_absolute);

					}
					//	We bind the array containing the type and the array containing the values
					//  with the  sdp_seq_alloc_with_length function
					hid_descriptor_list = sdp_seq_alloc_with_length( data_types, descriptor_array, length, 2 );

					//	The newly created array contains all information, so it is used to create
					//  the list from the right SDP-specific type (SDP_SEQ8)
					hid_descriptor_formatted_list = sdp_data_alloc( SDP_SEQ8, hid_descriptor_list );
					//	Finally the list is added as un SDP-attribute
					sdp_attr_add( &record, SDP_ATTR_HID_DESCRIPTOR_LIST, hid_descriptor_formatted_list );
					// Done!

					//	The same is also done with the HIDLANGIDBaseList
					//	SEE THE THESIS, SUBSECTION 4.3.12
					data_types[0] = &data_type_uint16;
					data_types[1] = &data_type_uint16;
					language_base_array[0] = &attr_language_base[0];
					language_base_array[1] = &attr_language_base[1];

					//sizeof(attr_language_base) is devided by two, since values are 16-bit integers (not 8-bit like with the HIDDecriptorList)
					language_base_list = sdp_seq_alloc(data_types, language_base_array, sizeof(attr_language_base) / 2);
					language_base_formatted_list = sdp_data_alloc(SDP_SEQ8, language_base_list);
					sdp_attr_add(&record, SDP_ATTR_HID_LANG_ID_BASE_LIST, language_base_formatted_list);
					// Done!

					//	 SEE THE THESIS, SUBSECTION 4.3.7
					sdp_attr_add_new(	&record,
										SDP_ATTR_HID_SDP_DISABLE,
										SDP_BOOL,
										&attr_sdp_disable);

					//	 SEE THE THESIS, SUBSECTION 4.3.8
					sdp_attr_add_new(	&record,
										SDP_ATTR_HID_BATTERY_POWER,
										SDP_BOOL,
										&attr_battery_power);

					//	 SEE THE THESIS, SUBSECTION 4.3.9
					sdp_attr_add_new(	&record,
										SDP_ATTR_HID_REMOTE_WAKEUP,
										SDP_BOOL,
										&attr_remote_wake);

					//	 SEE THE THESIS, SUBSECTION 4.3.10
					sdp_attr_add_new(	&record,
										SDP_ATTR_HID_PROFILE_VERSION,
										SDP_UINT16,
										&attr_profile_version);

//					sdp_attr_add_new(	&record,
//										SDP_ATTR_HID_SUPERVISION_TIMEOUT,
//										SDP_UINT16,
//										&attr_supervision_timeout);

					//	 SEE THE THESIS, SUBSECTION 4.3.6
					sdp_attr_add_new(	&record,
										SDP_ATTR_HID_NORMALLY_CONNECTABLE,
										SDP_BOOL,
										&attr_normally_connectable);

					//	 SEE THE THESIS, SUBSECTION 4.3.11
					sdp_attr_add_new(	&record,
										SDP_ATTR_HID_BOOT_DEVICE,
										SDP_BOOL,
										&attr_boot_device);


					//	At last the record is good to go and could be added to the sdp registry!
					if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) {

						//	LOGCAT:
						__android_log_print(ANDROID_LOG_DEBUG, TAG, "SDP: Registration of new record failed.");
						return CODE_RECORD_REGISTRATION_FAILED;

					}

					//	LOGCAT:
					__android_log_print(ANDROID_LOG_DEBUG, TAG, "SDP: Registration of new record successful.");

			sdp_close(session);

			return CODE_RECORD_REGISTERED;
	
}
Пример #2
0
static sdp_record_t *a2dp_record(uint8_t type, uint16_t avdtp_ver)
{
	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
	uuid_t root_uuid, l2cap_uuid, avdtp_uuid, a2dp_uuid;
	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 = AVDTP_UUID;
	uint16_t a2dp_ver = 0x0102, feat = 0x000f;

#ifdef ANDROID
	feat = 0x0001;
#endif
	record = sdp_record_alloc();
	if (!record)
		return NULL;

	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
	root = sdp_list_append(0, &root_uuid);
	sdp_set_browse_groups(record, root);

	if (type == AVDTP_SEP_TYPE_SOURCE)
		sdp_uuid16_create(&a2dp_uuid, AUDIO_SOURCE_SVCLASS_ID);
	else
		sdp_uuid16_create(&a2dp_uuid, AUDIO_SINK_SVCLASS_ID);
	svclass_id = sdp_list_append(0, &a2dp_uuid);
	sdp_set_service_classes(record, svclass_id);

	sdp_uuid16_create(&profile[0].uuid, ADVANCED_AUDIO_PROFILE_ID);
	profile[0].version = a2dp_ver;
	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(&avdtp_uuid, AVDTP_UUID);
	proto[1] = sdp_list_append(0, &avdtp_uuid);
	version = sdp_data_alloc(SDP_UINT16, &avdtp_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);

	if (type == AVDTP_SEP_TYPE_SOURCE)
		sdp_set_info_attr(record, "Audio Source", 0, 0);
	else
		sdp_set_info_attr(record, "Audio Sink", 0, 0);

	free(psm);
	free(version);
	sdp_list_free(proto[0], 0);
	sdp_list_free(proto[1], 0);
	sdp_list_free(apseq, 0);
	sdp_list_free(pfseq, 0);
	sdp_list_free(aproto, 0);
	sdp_list_free(root, 0);
	sdp_list_free(svclass_id, 0);

	return record;
}
Пример #3
0
static gboolean register_service_protocols(struct hdp_adapter *adapter,
						sdp_record_t *sdp_record)
{
	gboolean ret;
	uuid_t l2cap_uuid, mcap_c_uuid;
	sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
	sdp_list_t *access_proto_list = NULL;
	sdp_data_t *psm = NULL, *mcap_ver = NULL;
	uint16_t version = MCAP_VERSION;

	/* set l2cap information */
	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
	l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
	if (l2cap_list == NULL) {
		ret = FALSE;
		goto end;
	}

	psm = sdp_data_alloc(SDP_UINT16, &adapter->ccpsm);
	if (psm == NULL) {
		ret = FALSE;
		goto end;
	}

	if (sdp_list_append(l2cap_list, psm) == NULL) {
		ret = FALSE;
		goto end;
	}

	proto_list = sdp_list_append(NULL, l2cap_list);
	if (proto_list == NULL) {
		ret = FALSE;
		goto end;
	}

	/* set mcap information */
	sdp_uuid16_create(&mcap_c_uuid, MCAP_CTRL_UUID);
	mcap_list = sdp_list_append(NULL, &mcap_c_uuid);
	if (mcap_list == NULL) {
		ret = FALSE;
		goto end;
	}

	mcap_ver = sdp_data_alloc(SDP_UINT16, &version);
	if (mcap_ver == NULL) {
		ret = FALSE;
		goto end;
	}

	if (sdp_list_append(mcap_list, mcap_ver) == NULL) {
		ret = FALSE;
		goto end;
	}

	if (sdp_list_append(proto_list, mcap_list) == NULL) {
		ret = FALSE;
		goto end;
	}

	/* attach protocol information to service record */
	access_proto_list = sdp_list_append(NULL, proto_list);
	if (access_proto_list == NULL) {
		ret = FALSE;
		goto end;
	}

	if (sdp_set_access_protos(sdp_record, access_proto_list) < 0) {
		ret = FALSE;
		goto end;
	}
	ret = TRUE;

end:
	if (l2cap_list != NULL)
		sdp_list_free(l2cap_list, NULL);
	if (mcap_list != NULL)
		sdp_list_free(mcap_list, NULL);
	if (proto_list != NULL)
		sdp_list_free(proto_list, NULL);
	if (access_proto_list != NULL)
		sdp_list_free(access_proto_list, NULL);
	if (psm != NULL)
		sdp_data_free(psm);
	if (mcap_ver != NULL)
		sdp_data_free(mcap_ver);

	return ret;
}
Пример #4
0
static int add_service(sdp_session_t *session, uint32_t *handle, uint8_t rfcomm_channel)
{
	int ret = 0;
	unsigned char service_uuid_int[] = CARBOT_BLUETOOTH_SDP_UUID;
	const char *service_name = "Carbot Soul";
	const char *service_dsc = "General Purpose Android-Auto Interface";
	const char *service_prov = "Ubergrund.com";

	uuid_t root_uuid;
	uuid_t rfcomm_uuid, l2cap_uuid, svc_uuid;
	sdp_list_t *root_list;
	sdp_list_t *rfcomm_list = 0, *l2cap_list = 0, *proto_list = 0, *access_proto_list = 0, *service_list = 0;

	sdp_data_t *channel = 0;
	sdp_record_t *rec;
	// connect to the local SDP server, register the service record, and
	// disconnect

	if (!session) {
		logDebug("Bad local SDP session\n");
		return -1;
	}
	rec = sdp_record_alloc();

	// set the general service ID
	sdp_uuid128_create(&svc_uuid, &service_uuid_int);
	service_list = sdp_list_append(0, &svc_uuid);
	sdp_set_service_classes(rec, service_list);
	sdp_set_service_id(rec, svc_uuid);

	// make the service record publicly browsable
	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
	root_list = sdp_list_append(0, &root_uuid);
	sdp_set_browse_groups(rec, root_list);

	// set l2cap information
	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
	l2cap_list = sdp_list_append(0, &l2cap_uuid);
	proto_list = sdp_list_append(0, l2cap_list);

	// set rfcomm information
	sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
	rfcomm_list = sdp_list_append(0, &rfcomm_uuid);
	channel = sdp_data_alloc(SDP_UINT8, &rfcomm_channel);
	sdp_list_append(rfcomm_list, channel);
	sdp_list_append(proto_list, rfcomm_list);

	// attach protocol information to service record
	access_proto_list = sdp_list_append(0, proto_list);
	sdp_set_access_protos(rec, access_proto_list);

	// set the name, provider, and description
	sdp_set_info_attr(rec, service_name, service_prov, service_dsc);

	ret = sdp_record_register(session, rec, 0);

	if (ret < 0) {
		logError("Service registration failed\n");
	} else {
		*handle = rec->handle;
	}

	// cleanup
	sdp_data_free(channel);
	sdp_list_free(l2cap_list, 0);
	sdp_list_free(rfcomm_list, 0);
	sdp_list_free(root_list, 0);
	sdp_list_free(proto_list, 0);
	sdp_list_free(access_proto_list, 0);
	sdp_list_free(service_list, 0);

	sdp_record_free(rec);

	return ret;
}
Пример #5
0
static sdp_record_t *server_record_new(const char *name, uint16_t id)
{
    sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
    uuid_t root_uuid, pan, l2cap, bnep;
    sdp_profile_desc_t profile[1];
    sdp_list_t *proto[2];
    sdp_data_t *v, *p;
    uint16_t psm = BNEP_PSM, version = 0x0100;
    uint16_t security_desc = (security ? 0x0001 : 0x0000);
    uint16_t net_access_type = 0xfffe;
    uint32_t max_net_access_rate = 0;
    const char *desc = "Network service";
    sdp_record_t *record;

    record = sdp_record_alloc();
    if (!record)
        return NULL;

    record->attrlist = NULL;
    record->pattern = NULL;

    switch (id) {
    case BNEP_SVC_NAP:
        sdp_uuid16_create(&pan, NAP_SVCLASS_ID);
        svclass = sdp_list_append(NULL, &pan);
        sdp_set_service_classes(record, svclass);

        sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
        profile[0].version = 0x0100;
        pfseq = sdp_list_append(NULL, &profile[0]);
        sdp_set_profile_descs(record, pfseq);

        sdp_set_info_attr(record, name, NULL, desc);

        sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE,
                         SDP_UINT16, &net_access_type);
        sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE,
                         SDP_UINT32, &max_net_access_rate);
        break;
    case BNEP_SVC_GN:
        sdp_uuid16_create(&pan, GN_SVCLASS_ID);
        svclass = sdp_list_append(NULL, &pan);
        sdp_set_service_classes(record, svclass);

        sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID);
        profile[0].version = 0x0100;
        pfseq = sdp_list_append(NULL, &profile[0]);
        sdp_set_profile_descs(record, pfseq);

        sdp_set_info_attr(record, name, NULL, desc);
        break;
    case BNEP_SVC_PANU:
        sdp_uuid16_create(&pan, PANU_SVCLASS_ID);
        svclass = sdp_list_append(NULL, &pan);
        sdp_set_service_classes(record, svclass);

        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_set_info_attr(record, name, NULL, desc);
        break;
    default:
        sdp_record_free(record);
        return NULL;
    }

    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, L2CAP_UUID);
    proto[0] = sdp_list_append(NULL, &l2cap);
    p = sdp_data_alloc(SDP_UINT16, &psm);
    proto[0] = sdp_list_append(proto[0], p);
    apseq    = sdp_list_append(NULL, proto[0]);

    sdp_uuid16_create(&bnep, BNEP_UUID);
    proto[1] = sdp_list_append(NULL, &bnep);
    v = sdp_data_alloc(SDP_UINT16, &version);
    proto[1] = sdp_list_append(proto[1], v);

    /* Supported protocols */
    {
        uint16_t ptype[] = {
            0x0800,  /* IPv4 */
            0x0806,  /* ARP */
        };
        sdp_data_t *head, *pseq;
        int p;

        for (p = 0, head = NULL; p < 2; p++) {
            sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]);
            if (head)
                sdp_seq_append(head, data);
            else
                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(NULL, apseq);
    sdp_set_access_protos(record, aproto);

    add_lang_attr(record);

    sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC,
                     SDP_UINT16, &security_desc);

    sdp_data_free(p);
    sdp_data_free(v);
    sdp_list_free(apseq, NULL);
    sdp_list_free(root, NULL);
    sdp_list_free(aproto, NULL);
    sdp_list_free(proto[0], NULL);
    sdp_list_free(proto[1], NULL);
    sdp_list_free(svclass, NULL);
    sdp_list_free(pfseq, NULL);

    return record;
}
Пример #6
0
static sdp_record_t *hfp_ag_record(uint8_t ch, uint32_t feat)
{
	sdp_list_t *svclass_id, *pfseq, *apseq, *root;
	uuid_t root_uuid, svclass_uuid, ga_svclass_uuid;
	uuid_t l2cap_uuid, rfcomm_uuid;
	sdp_profile_desc_t profile;
	sdp_list_t *aproto, *proto[2];
	sdp_record_t *record;
	sdp_data_t *channel, *features;
	uint8_t netid = 0x01;
	uint16_t sdpfeat;
	sdp_data_t *network;

	record = sdp_record_alloc();
	if (!record)
		return NULL;

	network = sdp_data_alloc(SDP_UINT8, &netid);
	if (!network) {
		sdp_record_free(record);
		return NULL;
	}

	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 = 0x0105;
	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, &ch);
	proto[1] = sdp_list_append(proto[1], channel);
	apseq = sdp_list_append(apseq, proto[1]);

	sdpfeat = (uint16_t) feat & 0xF;
	features = sdp_data_alloc(SDP_UINT16, &sdpfeat);
	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, "Hands-Free Audio Gateway", 0, 0);

	sdp_attr_add(record, SDP_ATTR_EXTERNAL_NETWORK, network);

	sdp_data_free(channel);
	sdp_list_free(proto[0], 0);
	sdp_list_free(proto[1], 0);
	sdp_list_free(apseq, 0);
	sdp_list_free(pfseq, 0);
	sdp_list_free(aproto, 0);
	sdp_list_free(root, 0);
	sdp_list_free(svclass_id, 0);

	return record;
}
Пример #7
0
static sdp_record_t *avrcp_tg_record(void)
{
	sdp_list_t *svclass_id, *pfseq, *apseq, *root, *apseq_browsing;
	uuid_t root_uuid, l2cap, avctp, avrtg;
	sdp_profile_desc_t profile[1];
	sdp_list_t *aproto_control, *proto_control[2];
	sdp_record_t *record;
	sdp_data_t *psm_control, *version, *features, *psm_browsing;
	sdp_list_t *aproto_browsing, *proto_browsing[2] = {0};
	uint16_t lp = AVCTP_CONTROL_PSM;
	uint16_t lp_browsing = AVCTP_BROWSING_PSM;
	uint16_t avrcp_ver = 0x0104, avctp_ver = 0x0103;
	uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
					AVRCP_FEATURE_CATEGORY_2 |
					AVRCP_FEATURE_CATEGORY_3 |
					AVRCP_FEATURE_CATEGORY_4 |
					AVRCP_FEATURE_BROWSING |
					AVRCP_FEATURE_PLAYER_SETTINGS );

	record = sdp_record_alloc();
	if (!record)
		return NULL;

	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
	root = sdp_list_append(0, &root_uuid);
	sdp_set_browse_groups(record, root);

	/* Service Class ID List */
	sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
	svclass_id = sdp_list_append(0, &avrtg);
	sdp_set_service_classes(record, svclass_id);

	/* Protocol Descriptor List */
	sdp_uuid16_create(&l2cap, L2CAP_UUID);
	proto_control[0] = sdp_list_append(0, &l2cap);
	psm_control = sdp_data_alloc(SDP_UINT16, &lp);
	proto_control[0] = sdp_list_append(proto_control[0], psm_control);
	apseq = sdp_list_append(0, proto_control[0]);

	sdp_uuid16_create(&avctp, AVCTP_UUID);
	proto_control[1] = sdp_list_append(0, &avctp);
	version = sdp_data_alloc(SDP_UINT16, &avctp_ver);
	proto_control[1] = sdp_list_append(proto_control[1], version);
	apseq = sdp_list_append(apseq, proto_control[1]);

	aproto_control = sdp_list_append(0, apseq);
	sdp_set_access_protos(record, aproto_control);
	proto_browsing[0] = sdp_list_append(0, &l2cap);
	psm_browsing = sdp_data_alloc(SDP_UINT16, &lp_browsing);
	proto_browsing[0] = sdp_list_append(proto_browsing[0], psm_browsing);
	apseq_browsing = sdp_list_append(0, proto_browsing[0]);

	proto_browsing[1] = sdp_list_append(0, &avctp);
	proto_browsing[1] = sdp_list_append(proto_browsing[1], version);
	apseq_browsing = sdp_list_append(apseq_browsing, proto_browsing[1]);

	aproto_browsing = sdp_list_append(0, apseq_browsing);
	sdp_set_add_access_protos(record, aproto_browsing);

	/* Bluetooth Profile Descriptor List */
	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
	profile[0].version = avrcp_ver;
	pfseq = sdp_list_append(0, &profile[0]);
	sdp_set_profile_descs(record, pfseq);

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

	free(psm_browsing);
	sdp_list_free(proto_browsing[0], 0);
	sdp_list_free(proto_browsing[1], 0);
	sdp_list_free(apseq_browsing, 0);
	sdp_list_free(aproto_browsing, 0);

	free(psm_control);
	free(version);
	sdp_list_free(proto_control[0], 0);
	sdp_list_free(proto_control[1], 0);
	sdp_list_free(apseq, 0);
	sdp_list_free(aproto_control, 0);
	sdp_list_free(pfseq, 0);
	sdp_list_free(root, 0);
	sdp_list_free(svclass_id, 0);

	return record;
}
Пример #8
0
sdp_session_t* io_rfcomm_advertise(uint8_t ch, const char *name, const char *desc, const uint32_t *uuid128) {
    uuid_t root_uuid, rfcomm_uuid, svc_uuid, svc_class_uuid, l2cap_uuid;
    sdp_list_t *rfcomm_list = 0,
                *root_list = 0,
                 *proto_list = 0,
                  *access_proto_list = 0,
                   *svc_class_list = 0,
                    *profile_list = 0,
                     *l2cap_list = 0; //*l2cap_list
    sdp_data_t *channel = 0;
    sdp_profile_desc_t profile;
    sdp_record_t record = { 0 };
    sdp_session_t *session = 0;

    //set general service ID
    sdp_uuid128_create(&svc_uuid, &uuid128);
    sdp_set_service_id(&record, svc_uuid);

    // set the service class
    sdp_uuid16_create(&svc_class_uuid, SERIAL_PORT_SVCLASS_ID);
    svc_class_list = sdp_list_append(0, &svc_class_uuid);
    sdp_set_service_classes(&record, svc_class_list);

    // set the Bluetooth profile information
    sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
    profile.version = 0x0100;
    profile_list = sdp_list_append(0, &profile);
    sdp_set_profile_descs(&record, profile_list);

    // make the service record publicly browsable
    sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
    root_list = sdp_list_append(0, &root_uuid);
    sdp_set_browse_groups( &record, root_list );

    //set l2cap information
    sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
    l2cap_list = sdp_list_append( 0, &l2cap_uuid );
    proto_list = sdp_list_append( 0, l2cap_list );

    // register rfcomm channel for rfcomm sockets
    sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID);
    channel = sdp_data_alloc(SDP_UINT8, &ch);
    rfcomm_list = sdp_list_append( 0, &rfcomm_uuid );
    sdp_list_append( rfcomm_list, channel );
    sdp_list_append( proto_list, rfcomm_list );

    access_proto_list = sdp_list_append(0, proto_list);
    sdp_set_access_protos(&record, access_proto_list);

    //set name, provider, description
    sdp_set_info_attr(&record, name, "", desc);

    //connect to local SDP server, register service record, and disconnect
    session = sdp_connect(BDADDR_ANY, BDADDR_LOCAL, 0);
    sdp_record_register(session, &record, 0);

    //cleanup
    sdp_data_free(channel);
    sdp_list_free(l2cap_list, 0);
    sdp_list_free( rfcomm_list, 0 );
    sdp_list_free( root_list, 0 );
    sdp_list_free( access_proto_list, 0 );

    return session;
}
Пример #9
0
/*
 *  add keyboard descriptor
 */
void sdp_add_keyboard()
{
	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 *channel, *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;
	sdp_session_t *session;
	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 uint8_t ctrl = 0x11;
	static const uint8_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};
	// taken from Apple Wireless Keyboard
	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
	};


	if (!sdp_session) {
		printf("%s: sdp_session invalid\n", (char*)__func__);
		exit(-1);
	}
	session = sdp_session;

	sdp_record = sdp_record_alloc();
	if (!sdp_record) {
		perror("add_keyboard sdp_record_alloc: ");
		exit(-1);
	}

	memset((void*)sdp_record, 0, sizeof(sdp_record_t));
	sdp_record->handle = 0xffffffff;
	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
	root = sdp_list_append(0, &root_uuid);
	sdp_set_browse_groups(sdp_record, root);

	add_lang_attr(sdp_record);
	
	sdp_uuid16_create(&hidkb_uuid, HID_SVCLASS_ID);
	svclass_id = sdp_list_append(0, &hidkb_uuid);
	sdp_set_service_classes(sdp_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(sdp_record, pfseq);

	// PROTO
	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
	proto[1] = sdp_list_append(0, &l2cap_uuid);
	channel = sdp_data_alloc(SDP_UINT8, &ctrl);
	proto[1] = sdp_list_append(proto[1], channel);
	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(sdp_record, aproto);

	// ATTR_ADD_PROTO
	proto[1] = sdp_list_append(0, &l2cap_uuid);
	channel = sdp_data_alloc(SDP_UINT8, &intr);
	proto[1] = sdp_list_append(proto[1], channel);
	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(sdp_record, aproto);
	
	sdp_set_info_attr(sdp_record, "Collin's Fake Bluetooth Keyboard", 
		"MUlliNER.ORG", "http://www.mulliner.org/bluetooth/");

	for (i = 0; i < sizeof(hid_attr)/2; i++) {
		sdp_attr_add_new(sdp_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(sdp_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(sdp_record, SDP_ATTR_HID_LANG_ID_BASE_LIST, lang_lst2);

	sdp_attr_add_new(sdp_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(sdp_record, SDP_ATTR_HID_REMOTE_WAKEUP+i, SDP_UINT16, &hid_attr2[i+1]);
	}
	
	if (sdp_record_register(session, sdp_record, 0) < 0) {
		printf("%s: HID Device (Keyboard) Service Record registration failed\n", (char*)__func__);
		exit(-1);
	}
}
Пример #10
0
static sdp_record_t *avrcp_tg_record()
{
	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 = AVCTP_PSM;
	uint16_t avrcp_ver = 0x0100, avctp_ver = 0x0103, feat = 0x000f;

	record = sdp_record_alloc();
	if (!record)
		return NULL;

	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
	root = sdp_list_append(0, &root_uuid);
	sdp_set_browse_groups(record, root);

	/* Service Class ID List */
	sdp_uuid16_create(&avrtg, AV_REMOTE_TARGET_SVCLASS_ID);
	svclass_id = sdp_list_append(0, &avrtg);
	sdp_set_service_classes(record, svclass_id);

	/* Protocol Descriptor List */
	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, &avctp_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);

	/* Bluetooth Profile Descriptor List */
	sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
	profile[0].version = avrcp_ver;
	pfseq = sdp_list_append(0, &profile[0]);
	sdp_set_profile_descs(record, pfseq);

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

	free(psm);
	free(version);
	sdp_list_free(proto[0], 0);
	sdp_list_free(proto[1], 0);
	sdp_list_free(apseq, 0);
	sdp_list_free(aproto, 0);
	sdp_list_free(pfseq, 0);
	sdp_list_free(root, 0);
	sdp_list_free(svclass_id, 0);

	return record;
}
Пример #11
0
static int register_service_protocols(sdp_record_t *rec,
					struct health_app *app)
{
	uuid_t l2cap_uuid, mcap_c_uuid;
	sdp_list_t *l2cap_list, *proto_list = NULL, *mcap_list = NULL;
	sdp_list_t *access_proto_list = NULL;
	sdp_data_t *psm = NULL, *mcap_ver = NULL;
	uint32_t ccpsm;
	uint16_t version = MCAP_VERSION;
	GError *err = NULL;
	int ret = -1;

	DBG("");

	/* set l2cap information */
	sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
	l2cap_list = sdp_list_append(NULL, &l2cap_uuid);
	if (!l2cap_list)
		goto fail;

	ccpsm = mcap_get_ctrl_psm(mcap, &err);
	if (err)
		goto fail;

	psm = sdp_data_alloc(SDP_UINT16, &ccpsm);
	if (!psm)
		goto fail;

	if (!sdp_list_append(l2cap_list, psm))
		goto fail;

	proto_list = sdp_list_append(NULL, l2cap_list);
	if (!proto_list)
		goto fail;

	/* set mcap information */
	sdp_uuid16_create(&mcap_c_uuid, MCAP_CTRL_UUID);
	mcap_list = sdp_list_append(NULL, &mcap_c_uuid);
	if (!mcap_list)
		goto fail;

	mcap_ver = sdp_data_alloc(SDP_UINT16, &version);
	if (!mcap_ver)
		goto fail;

	if (!sdp_list_append(mcap_list, mcap_ver))
		goto fail;

	if (!sdp_list_append(proto_list, mcap_list))
		goto fail;

	/* attach protocol information to service record */
	access_proto_list = sdp_list_append(NULL, proto_list);
	if (!access_proto_list)
		goto fail;

	sdp_set_access_protos(rec, access_proto_list);
	ret = 0;

fail:
	sdp_list_free(l2cap_list, NULL);
	sdp_list_free(mcap_list, NULL);
	sdp_list_free(proto_list, NULL);
	sdp_list_free(access_proto_list, NULL);

	if (psm)
		sdp_data_free(psm);

	if (mcap_ver)
		sdp_data_free(mcap_ver);

	if (err)
		g_error_free(err);

	return ret;
}
Пример #12
0
static sdp_record_t *panu_record(void)
{
    sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
    uuid_t root_uuid, panu, l2cap, bnep;
    sdp_profile_desc_t profile[1];
    sdp_list_t *proto[2];
    sdp_data_t *v, *p;
    uint16_t psm = BNEP_PSM, version = 0x0100;
    uint16_t security = 0x0001, type = 0xfffe;
    uint32_t rate = 0;
    const char *desc = "PAN User", *name = "Network Service";
    sdp_record_t *record;
    uint16_t ptype[] = { 0x0800, /* IPv4 */ 0x0806,  /* ARP */ };
    sdp_data_t *head, *pseq, *data;

    record = sdp_record_alloc();
    if (!record)
        return NULL;

    record->attrlist = NULL;
    record->pattern = NULL;

    sdp_uuid16_create(&panu, PANU_SVCLASS_ID);
    svclass = sdp_list_append(NULL, &panu);
    sdp_set_service_classes(record, svclass);

    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_set_info_attr(record, name, NULL, desc);
    sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE, SDP_UINT16, &type);
    sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE,
                     SDP_UINT32, &rate);

    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, L2CAP_UUID);
    proto[0] = sdp_list_append(NULL, &l2cap);
    p = sdp_data_alloc(SDP_UINT16, &psm);
    proto[0] = sdp_list_append(proto[0], p);
    apseq = sdp_list_append(NULL, proto[0]);

    sdp_uuid16_create(&bnep, BNEP_UUID);
    proto[1] = sdp_list_append(NULL, &bnep);
    v = sdp_data_alloc(SDP_UINT16, &version);
    proto[1] = sdp_list_append(proto[1], v);

    head = sdp_data_alloc(SDP_UINT16, &ptype[0]);
    data = sdp_data_alloc(SDP_UINT16, &ptype[1]);
    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(NULL, apseq);
    sdp_set_access_protos(record, aproto);
    sdp_add_lang_attr(record);
    sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security);

    sdp_data_free(p);
    sdp_data_free(v);
    sdp_list_free(apseq, NULL);
    sdp_list_free(root, NULL);
    sdp_list_free(aproto, NULL);
    sdp_list_free(proto[0], NULL);
    sdp_list_free(proto[1], NULL);
    sdp_list_free(svclass, NULL);
    sdp_list_free(pfseq, NULL);

    return record;
}