static sdp_record_t *proxy_record_new(const char *uuid128, uint8_t channel) { sdp_list_t *apseq, *aproto, *profiles, *proto[2], *root, *svclass_id; uuid_t uuid, root_uuid, l2cap, rfcomm; sdp_profile_desc_t profile; sdp_record_t *record; sdp_data_t *ch; record = sdp_record_alloc(); if (!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_list_free(root, NULL); bt_string2uuid(&uuid, uuid128); svclass_id = sdp_list_append(NULL, &uuid); sdp_set_service_classes(record, svclass_id); sdp_list_free(svclass_id, NULL); sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID); profile.version = 0x0100; profiles = sdp_list_append(NULL, &profile); sdp_set_profile_descs(record, profiles); sdp_list_free(profiles, NULL); sdp_uuid16_create(&l2cap, L2CAP_UUID); proto[0] = sdp_list_append(NULL, &l2cap); apseq = sdp_list_append(NULL, proto[0]); sdp_uuid16_create(&rfcomm, RFCOMM_UUID); proto[1] = sdp_list_append(NULL, &rfcomm); ch = sdp_data_alloc(SDP_UINT8, &channel); proto[1] = sdp_list_append(proto[1], ch); apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(NULL, apseq); sdp_set_access_protos(record, aproto); add_lang_attr(record); sdp_set_info_attr(record, "Port Proxy Entity", NULL, "Port Proxy Entity"); sdp_data_free(ch); sdp_list_free(proto[0], NULL); sdp_list_free(proto[1], NULL); sdp_list_free(apseq, NULL); sdp_list_free(aproto, NULL); return record; }
/* * dosdpregistration - Care for the proper SDP record sent to the "sdpd" * so that other BT devices can discover the HID service * Parameters: none; Return value: 0 = OK, >0 = failure */ int dosdpregistration ( void ) { sdp_record_t record; sdp_session_t *session; 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; void *dtds[2], *values[2], *dtds2[2], *values2[2]; int i, leng[2]; uint8_t dtd=SDP_UINT16, dtd2=SDP_UINT8, dtd_data=SDP_TEXT_STR8, hid_spec_type=0x22; uint16_t hid_attr_lang[]={0x409, 0x100}, ctrl=PSMHIDCTL, intr=PSMHIDINT, hid_attr[]={0x100, 0x111, 0x40, 0x00, 0x01, 0x01}, // Assigned to SDP 0x200...0x205 - see HID SPEC for // details. Those values seem to work fine... // "it\'s a kind of magic" numbers. hid_attr2[]={0x100, 0x0}; // Connect to SDP server on localhost, to publish service information session = sdp_connect ( BDADDR_ANY, BDADDR_LOCAL, 0 ); if ( ! session ) { fprintf ( stderr, "Failed to connect to SDP server: %s\n", strerror ( errno ) ); return 1; } memset(&record, 0, sizeof(sdp_record_t)); record.handle = 0xffffffff; // With 0xffffffff, we get assigned the first free record >= 0x10000 // Make HID service visible (add to PUBLIC BROWSE GROUP) sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(0, &root_uuid); sdp_set_browse_groups(&record, root); // Language Information to be added add_lang_attr(&record); // The descriptor for the keyboard sdp_uuid16_create(&hidkb_uuid, HID_SVCLASS_ID); svclass_id = sdp_list_append(0, &hidkb_uuid); sdp_set_service_classes(&record, svclass_id); // And information about the HID profile used sdp_uuid16_create(&profile[0].uuid, HIDP_UUID /*HID_PROFILE_ID*/); profile[0].version = 0x0100; pfseq = sdp_list_append(0, profile); sdp_set_profile_descs(&record, pfseq); // We are using L2CAP, so add an info about that 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]); // And about our purpose, the HID protocol data transfer 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); // Set service name, description sdp_set_info_attr(&record, HIDINFO_NAME, HIDINFO_PROV, HIDINFO_DESC); // Add a few HID-specifid pieces of information // See the HID spec for details what those codes 0x200+something // are good for... we send a fixed set of info that seems to work sdp_attr_add_new(&record, SDP_ATTR_HID_DEVICE_RELEASE_NUMBER, SDP_UINT16, &hid_attr[0]); /* Opt */ sdp_attr_add_new(&record, SDP_ATTR_HID_PARSER_VERSION, SDP_UINT16, &hid_attr[1]); /* Mand */ sdp_attr_add_new(&record, SDP_ATTR_HID_DEVICE_SUBCLASS, SDP_UINT8, &hid_attr[2]); /* Mand */ sdp_attr_add_new(&record, SDP_ATTR_HID_COUNTRY_CODE, SDP_UINT8, &hid_attr[3]); /* Mand */ sdp_attr_add_new(&record, SDP_ATTR_HID_VIRTUAL_CABLE, SDP_BOOL, &hid_attr[4]); /* Mand */ sdp_attr_add_new(&record, SDP_ATTR_HID_RECONNECT_INITIATE, SDP_BOOL, &hid_attr[5]); /* Mand */ // Add the HID descriptor (describing the virtual device) as code // SDP_ATTR_HID_DESCRIPTOR_LIST (0x206 IIRC) dtds[0] = &dtd2; values[0] = &hid_spec_type; dtd_data= SDPRECORD_BYTES <= 255 ? SDP_TEXT_STR8 : SDP_TEXT_STR16 ; dtds[1] = &dtd_data; values[1] = (uint8_t *) SDPRECORD; leng[0] = 0; leng[1] = SDPRECORD_BYTES; 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); // and continue adding further data bytes for 0x206+x values 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_PROFILE_VERSION, SDP_UINT16, &hid_attr2[0] ); sdp_attr_add_new ( &record, SDP_ATTR_HID_BOOT_DEVICE, SDP_UINT16, &hid_attr2[1] ); // Submit our IDEA of a SDP record to the "sdpd" if (sdp_record_register(session, &record, SDP_RECORD_PERSIST) < 0) { fprintf ( stderr, "Service Record registration failed\n" ); return -1; } // Store the service handle retrieved from there for reference (i.e., // deleting the service info when this program terminates) sdphandle = record.handle; fprintf ( stdout, "HID keyboard/mouse service registered\n" ); return 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; }
/** * Creates the SDP record for BD remtoe HID emulation. Probably works with PS3 only (experimental) */ static sdp_record_t *create_hid_bdremote_record() { sdp_record_t *sdp_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 *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; 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 value_int = 0; static const uint16_t ctrl = 0x11; static const uint16_t intr = 0x13; static const uint16_t hid_release_num = 0x100; static const uint16_t hid_parser_version = 0x111; static const uint8_t hid_dev_subclass = 0x0c; static const uint8_t hid_country_code = 0x21; static const uint8_t hid_virtual_cable = 0x1; static const uint8_t hid_reconn_initiate = 0x1; static const uint8_t hid_sdp_disable = 0x0; static const uint8_t hid_batt_power = 0x1; static const uint8_t hid_remote_wake = 0x1; static const uint16_t hid_profile_version = 0x100; static const uint16_t hid_superv_timeout = 0x1f40; static const uint8_t hid_normally_connectable = 0x0; static const uint8_t hid_boot_device = 0x10; const uint8_t hid_spec[] = { /* PS3 blu-ray remote */ 0x05, 0x01, /* Usage Page (Generic Desktop) */ 0x09, 0x05, /* Usage ID (Game Pad) */ 0xa1, 0x01, /* Collection (Application) */ 0x85, 0x01, /* Report ID (1) */ 0x15, 0x00, /* Logical Minimum (0) */ 0x26, 0xff, 0x00, /* Logical Maximum (255) */ 0x75, 0x08, /* Report Size (8) */ 0x95, 0x0b, /* Report Count (11) */ 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ 0x09, 0x01, /* Usage (Vendor Usage 1) */ 0x81, 0x00, /* Input (Data,Array,Absolute) */ 0x85, 0x01, /* Report ID (1) */ 0x95, 0x0b, /* Report Count (11) */ 0x09, 0x01, /* Usage (Vendor Usage 1) */ 0xb1, 0x00, /* Feature (Data,Array,Absolute) */ 0x85, 0x02, /* Report ID (2) */ 0x95, 0x0b, /* Report Count (11) */ 0x09, 0x01, /* Usage (Vendor Usage 1) */ 0xb1, 0x00, /* Feature (Data,Array,Absolute) */ 0x85, 0x03, /* Report ID (3) */ 0x95, 0x0b, /* Report Count (11) */ 0x09, 0x01, /* Usage (Vendor Usage 1) */ 0xb1, 0x00, /* Feature (Data,Array,Absolute) */ 0x85, 0x04, /* Report ID (4) */ 0x95, 0x0b, /* Report Count (11) */ 0x09, 0x01, /* Usage (Vendor Usage 1) */ 0xb1, 0x00, /* Feature (Data,Array,Absolute) */ 0x85, 0x05, /* Report ID (5) */ 0x95, 0x0b, /* Report count (11) */ 0x09, 0x01, /* Usage (Vendor Usage 1) */ 0xb1, 0x00, /* Feature (Data,Array,Absolute) */ 0x85, 0x06, /* Report ID (6) */ 0x95, 0x0b, /* Report count (11) */ 0x09, 0x01, /* Usage (Vendor Usage 1) */ 0xb1, 0x00, /* Feature (Data,Array,Absolute) */ 0xc0 /* End Collection */ }; sdp_record = sdp_record_alloc(); if (!sdp_record) { return NULL; } 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, "Android Bluetooth Keyboard", "", "HID device over Bluetooth for Android"); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_DEVICE_RELEASE_NUMBER, SDP_UINT16, &hid_release_num); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_PARSER_VERSION, SDP_UINT16, &hid_parser_version); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_DEVICE_SUBCLASS, SDP_UINT8, &hid_dev_subclass); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_COUNTRY_CODE, SDP_UINT8, &hid_country_code); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_VIRTUAL_CABLE, SDP_BOOL, &hid_virtual_cable); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_RECONNECT_INITIATE, SDP_BOOL, &hid_reconn_initiate); 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_BOOL, &hid_sdp_disable); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_BATTERY_POWER, SDP_BOOL, &hid_batt_power); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_REMOTE_WAKEUP, SDP_BOOL, &hid_remote_wake); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_PROFILE_VERSION, SDP_UINT16, &hid_profile_version); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_SUPERVISION_TIMEOUT, SDP_UINT16, &hid_superv_timeout); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_NORMALLY_CONNECTABLE, SDP_BOOL, &hid_normally_connectable); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_BOOT_DEVICE, SDP_BOOL, &hid_boot_device); return sdp_record; }
/** * Creates the SDP record for HID emulation. Works well with many different * clients: windows, linux (ubuntu), PS3, and motorola android devices. */ static sdp_record_t *create_hid_generic_record() { sdp_record_t *sdp_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 *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; 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 value_int = 0; static const uint16_t ctrl = 0x11; static const uint16_t intr = 0x13; static const uint16_t hid_release_num = 0x100; static const uint16_t hid_parser_version = 0x111; static const uint8_t hid_dev_subclass = 0x40; static const uint8_t hid_country_code = 0x4; static const uint8_t hid_virtual_cable = 0x0; static const uint8_t hid_reconn_initiate = 0x1; static const uint8_t hid_sdp_disable = 0x0; static const uint8_t hid_batt_power = 0x1; static const uint8_t hid_remote_wake = 0x0; static const uint16_t hid_profile_version = 0x100; static const uint16_t hid_superv_timeout = 0x1f40; static const uint8_t hid_normally_connectable = 0x0; static const uint8_t hid_boot_device = 0x1; static const uint8_t hid_spec[] = { /* Generic keyboard */ 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x06, /* Usage (Keyboard), */ 0xA1, 0x01, /* Collection (Application), */ 0x85, 0x01, /* Report ID (1), */ 0x05, 0x07, /* Usage Page (Keyboard), */ 0x19, 0xE0, /* Usage Minimum (KB Leftcontrol), */ 0x29, 0xE7, /* Usage Maximum (KB Right GUI), */ 0x15, 0x00, /* Logical Minimum (0), */ 0x25, 0x01, /* Logical Maximum (1), */ 0x75, 0x01, /* Report Size (1), */ 0x95, 0x08, /* Report Count (8), */ 0x81, 0x02, /* Input (Variable), */ 0x95, 0x01, /* Report Count (1), */ 0x75, 0x08, /* Report Size (8), */ 0x81, 0x01, /* Input (Constant), */ 0x95, 0x05, /* Report Count (5), */ 0x75, 0x01, /* Report Size (1), */ 0x05, 0x08, /* Usage Page (LED), */ 0x19, 0x01, /* Usage Minimum (01h), */ 0x29, 0x05, /* Usage Maximum (05h), */ 0x91, 0x02, /* Output (Variable), */ 0x95, 0x01, /* Report Count (1), */ 0x75, 0x03, /* Report Size (3), */ 0x91, 0x01, /* Output (Constant), */ 0x95, 0x06, /* Report Count (6), */ 0x75, 0x08, /* Report Size (8), */ 0x15, 0x00, /* Logical Minimum (0), */ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ 0x05, 0x07, /* Usage Page (Keyboard), */ 0x19, 0x00, /* Usage Minimum (None), */ 0x29, 0xE7, /* Usage Maximum (KB Right GUI), */ 0x81, 0x00, /* Input, */ /* Consumer specific - volume / mute */ 0xC0, /* End Collection, */ 0x05, 0x0C, /* Usage Page (Consumer), */ 0x09, 0x01, /* Usage (Consumer Control), */ 0xA1, 0x01, /* Collection (Application), */ 0x85, 0x01, /* Report ID (1), */ 0x09, 0xE0, /* Usage (Volume), */ 0x15, 0xE8, /* Logical Minimum (-24), */ 0x25, 0x18, /* Logical Maximum (24), */ 0x75, 0x07, /* Report Size (7), */ 0x95, 0x01, /* Report Count (1), */ 0x81, 0x06, /* Input (Variable, Relative), */ 0x15, 0x00, /* Logical Minimum (0), */ 0x25, 0x01, /* Logical Maximum (1), */ 0x75, 0x01, /* Report Size (1), */ 0x09, 0xE2, /* Usage (Mute), */ 0x81, 0x06, /* Input (Variable, Relative), */ 0xC0, /* End Collection, */ /* Consumer specific - media controls */ 0x05, 0x0C, /* Usage Page (Consumer), */ 0x09, 0x01, /* Usage (Consumer Control), */ 0xA1, 0x01, /* Collection (Application), */ 0x85, 0x03, /* Report ID (3), */ 0x15, 0x00, /* Logical Minimum (0), */ 0x25, 0x01, /* Logical Maximum (1), */ 0x75, 0x01, /* Report Size (1), */ 0x95, 0x01, /* Report Count (1), */ 0x0A, 0x27, 0x02, /* Usage (AC Refresh), */ 0x81, 0x06, /* Input (Variable, Relative), */ 0x0A, 0x94, 0x01, /* Usage (AL Local Machine Brwsr), */ 0x81, 0x06, /* Input (Variable, Relative), */ 0x0A, 0x23, 0x02, /* Usage (AC Home), */ 0x81, 0x06, /* Input (Variable, Relative), */ 0x0A, 0x8A, 0x01, /* Usage (AL Email Reader), */ 0x81, 0x06, /* Input (Variable, Relative), */ 0x0A, 0x92, 0x01, /* Usage (AL Calculator), */ 0x81, 0x06, /* Input (Variable, Relative), */ 0x0A, 0x26, 0x02, /* Usage (AC Stop), */ 0x81, 0x06, /* Input (Variable, Relative), */ 0x0A, 0x25, 0x02, /* Usage (AC Forward), */ 0x81, 0x06, /* Input (Variable, Relative), */ 0x0A, 0x24, 0x02, /* Usage (AC Back), */ 0x81, 0x06, /* Input (Variable, Relative), */ 0x09, 0xB5, /* Usage (Scan Next Track), */ 0x81, 0x06, /* Input (Variable, Relative), */ 0x09, 0xB6, /* Usage (Scan Previous Track), */ 0x81, 0x06, /* Input (Variable, Relative), */ 0x09, 0xCD, /* Usage (Play Pause), */ 0x81, 0x06, /* Input (Variable, Relative), */ 0x09, 0xB7, /* Usage (Stop), */ 0x81, 0x06, /* Input (Variable, Relative), */ 0x0A, 0x83, 0x01, /* Usage (AL Consumer Control Config), */ 0x81, 0x06, /* Input (Variable, Relative), */ 0x95, 0x0B, /* Report Count (11), */ 0x81, 0x01, /* Input (Constant), */ 0xC0, /* End Collection */ /* Generic mouse pointer */ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ 0x09, 0x02, /* USAGE (Mouse) */ 0xa1, 0x01, /* COLLECTION (Application) */ 0x85, 0x02, /* Report ID (2), */ 0x09, 0x01, /* USAGE (Pointer) */ 0xa1, 0x00, /* COLLECTION (Physical) */ 0x05, 0x09, /* USAGE_PAGE (Button) */ 0x19, 0x01, /* USAGE_MINIMUM (Button 1) */ 0x29, 0x03, /* USAGE_MAXIMUM (Button 3) */ 0x15, 0x00, /* LOGICAL_MINIMUM (0) */ 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */ 0x95, 0x03, /* REPORT_COUNT (3) */ 0x75, 0x01, /* REPORT_SIZE (1) */ 0x81, 0x02, /* INPUT (Data,Var,Abs) */ 0x95, 0x01, /* REPORT_COUNT (1) */ 0x75, 0x05, /* REPORT_SIZE (5) */ 0x81, 0x03, /* INPUT (Cnst,Var,Abs) */ 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */ 0x09, 0x30, /* USAGE (X) */ 0x09, 0x31, /* USAGE (Y) */ 0x15, 0x81, /* LOGICAL_MINIMUM (-127) */ 0x25, 0x7f, /* LOGICAL_MAXIMUM (127) */ 0x75, 0x08, /* REPORT_SIZE (8) */ 0x95, 0x02, /* REPORT_COUNT (2) */ 0x81, 0x06, /* INPUT (Data,Var,Rel) */ 0xc0, /* END_COLLECTION */ 0xc0 /* END_COLLECTION */ }; sdp_record = sdp_record_alloc(); if (!sdp_record) { return NULL; } 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, "Android Bluetooth Keyboard", "", "HID device over Bluetooth for Android"); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_DEVICE_RELEASE_NUMBER, SDP_UINT16, &hid_release_num); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_PARSER_VERSION, SDP_UINT16, &hid_parser_version); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_DEVICE_SUBCLASS, SDP_UINT8, &hid_dev_subclass); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_COUNTRY_CODE, SDP_UINT8, &hid_country_code); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_VIRTUAL_CABLE, SDP_BOOL, &hid_virtual_cable); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_RECONNECT_INITIATE, SDP_BOOL, &hid_reconn_initiate); 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_BOOL, &hid_sdp_disable); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_BATTERY_POWER, SDP_BOOL, &hid_batt_power); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_REMOTE_WAKEUP, SDP_BOOL, &hid_remote_wake); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_PROFILE_VERSION, SDP_UINT16, &hid_profile_version); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_SUPERVISION_TIMEOUT, SDP_UINT16, &hid_superv_timeout); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_NORMALLY_CONNECTABLE, SDP_BOOL, &hid_normally_connectable); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_BOOT_DEVICE, SDP_BOOL, &hid_boot_device); return sdp_record; }
/** * Creates the SDP record for PS3 keypad HID emulation. Works well with many different * clients: windows, linux (ubuntu), PS3, and motorola android devices. */ static sdp_record_t *create_hid_ps3_keypad_record() { sdp_record_t *sdp_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 *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; 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 value_int = 0; static const uint16_t ctrl = 0x11; static const uint16_t intr = 0x13; static const uint16_t hid_release_num = 0x100; static const uint16_t hid_parser_version = 0x111; static const uint8_t hid_dev_subclass = 0x40; static const uint8_t hid_country_code = 0x4; static const uint8_t hid_virtual_cable = 0x0; static const uint8_t hid_reconn_initiate = 0x1; static const uint8_t hid_sdp_disable = 0x0; static const uint8_t hid_batt_power = 0x1; static const uint8_t hid_remote_wake = 0x0; static const uint16_t hid_profile_version = 0x100; static const uint16_t hid_superv_timeout = 0x1f40; static const uint8_t hid_normally_connectable = 0x0; static const uint8_t hid_boot_device = 0x1; static const uint8_t hid_spec[] = { /* Pointer (mouse) */ 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x02, /* Usage (Mouse), */ 0xA1, 0x01, /* Collection (Application), */ 0x85, 0x02, /* Report ID (2), */ 0x09, 0x01, /* Usage (Pointer), */ 0xA1, 0x00, /* Collection (Physical), */ 0x05, 0x09, /* Usage Page (Button), */ 0x19, 0x01, /* Usage Minimum (01h), */ 0x29, 0x02, /* Usage Maximum (02h), */ 0x15, 0x00, /* Logical Minimum (0), */ 0x25, 0x01, /* Logical Maximum (1), */ 0x95, 0x02, /* Report Count (2), */ 0x75, 0x01, /* Report Size (1), */ 0x81, 0x02, /* Input (Variable), */ 0x95, 0x01, /* Report Count (1), */ 0x75, 0x06, /* Report Size (6), */ 0x81, 0x03, /* Input (Constant, Variable), */ 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x30, /* Usage (X), */ 0x09, 0x31, /* Usage (Y), */ 0x15, 0x81, /* Logical Minimum (-127), */ 0x25, 0x7F, /* Logical Maximum (127), */ 0x75, 0x08, /* Report Size (8), */ 0x95, 0x02, /* Report Count (2), */ 0x81, 0x06, /* Input (Variable, Relative), */ 0x75, 0x08, /* Report Size (8), */ 0x95, 0x01, /* Report Count (1), */ 0x81, 0x01, /* Input (Constant), */ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ 0x09, 0x20, /* Usage (20h), */ 0x15, 0x00, /* Logical Minimum (0), */ 0x26, 0xFF, 0x0F, /* Logical Maximum (4095), */ 0x75, 0x0C, /* Report Size (12), */ 0x95, 0x02, /* Report Count (2), */ 0x81, 0x02, /* Input (Variable), */ 0x09, 0x21, /* Usage (21h), */ 0x15, 0x00, /* Logical Minimum (0), */ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ 0x75, 0x08, /* Report Size (8), */ 0x95, 0x01, /* Report Count (1), */ 0x81, 0x02, /* Input (Variable), */ 0xC0, /* End Collection, */ 0xC0, /* End Collection, */ /* Keyboard */ 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x06, /* Usage (Keyboard), */ 0xA1, 0x01, /* Collection (Application), */ 0x85, 0x01, /* Report ID (1), */ 0x05, 0x07, /* Usage Page (Keyboard), */ 0x19, 0xE0, /* Usage Minimum (KB Leftcontrol), */ 0x29, 0xE7, /* Usage Maximum (KB Right GUI), */ 0x15, 0x00, /* Logical Minimum (0), */ 0x25, 0x01, /* Logical Maximum (1), */ 0x75, 0x01, /* Report Size (1), */ 0x95, 0x08, /* Report Count (8), */ 0x81, 0x02, /* Input (Variable), */ 0x95, 0x01, /* Report Count (1), */ 0x75, 0x08, /* Report Size (8), */ 0x81, 0x01, /* Input (Constant), */ 0x95, 0x05, /* Report Count (5), */ 0x75, 0x01, /* Report Size (1), */ 0x05, 0x08, /* Usage Page (LED), */ 0x19, 0x01, /* Usage Minimum (01h), */ 0x29, 0x05, /* Usage Maximum (05h), */ 0x91, 0x02, /* Output (Variable), */ 0x95, 0x01, /* Report Count (1), */ 0x75, 0x03, /* Report Size (3), */ 0x91, 0x01, /* Output (Constant), */ 0x95, 0x06, /* Report Count (6), */ 0x75, 0x08, /* Report Size (8), */ 0x15, 0x00, /* Logical Minimum (0), */ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ 0x05, 0x07, /* Usage Page (Keyboard), */ 0x19, 0x00, /* Usage Minimum (None), */ 0x29, 0xE7, /* Usage Maximum (KB Right GUI), */ 0x81, 0x00, /* Input, */ 0xC0, /* End Collection, */ 0x06, 0x01, 0xFF, /* Usage Page (FF01h), */ 0x09, 0x20, /* Usage (20h), */ 0xA1, 0x01, /* Collection (Application), */ 0x09, 0x21, /* Usage (21h), */ 0x85, 0x03, /* Report ID (3), */ 0x75, 0x08, /* Report Size (8), */ 0x95, 0x01, /* Report Count (1), */ 0x15, 0x00, /* Logical Minimum (0), */ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ 0x09, 0x22, /* Usage (22h), */ 0x81, 0x02, /* Input (Variable), */ 0x75, 0x08, /* Report Size (8), */ 0x95, 0x01, /* Report Count (1), */ 0x15, 0x00, /* Logical Minimum (0), */ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ 0x09, 0x23, /* Usage (23h), */ 0x81, 0x02, /* Input (Variable), */ 0x75, 0x08, /* Report Size (8), */ 0x95, 0x06, /* Report Count (6), */ 0x81, 0x01, /* Input (Constant), */ 0x09, 0x24, /* Usage (24h), */ 0x15, 0x00, /* Logical Minimum (0), */ 0x25, 0x01, /* Logical Maximum (1), */ 0x75, 0x08, /* Report Size (8), */ 0x95, 0x01, /* Report Count (1), */ 0x91, 0x02, /* Output (Variable), */ 0xC0, /* End Collection, */ /* ?? PS3 proprietary? */ 0x06, 0x02, 0xFF, /* Usage Page (FF02h), */ 0x09, 0x20, /* Usage (20h), */ 0xA1, 0x01, /* Collection (Application), */ 0x09, 0x21, /* Usage (21h), */ 0x85, 0x04, /* Report ID (4), */ 0x15, 0x00, /* Logical Minimum (0), */ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ 0x75, 0x08, /* Report Size (8), */ 0x95, 0x0F, /* Report Count (15), */ 0xB1, 0x02, /* Feature (Variable), */ 0x09, 0x22, /* Usage (22h), */ 0x85, 0x05, /* Report ID (5), */ 0x15, 0x00, /* Logical Minimum (0), */ 0x26, 0xFF, 0x00, /* Logical Maximum (255), */ 0x75, 0x08, /* Report Size (8), */ 0x95, 0x16, /* Report Count (22), */ 0xB1, 0x02, /* Feature (Variable), */ 0xC0 /* End Collection */ }; sdp_record = sdp_record_alloc(); if (!sdp_record) { return NULL; } 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, "Android PS3 Bluetooth Keypad", "", "PS3 compatible HID device over Bluetooth for Android"); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_DEVICE_RELEASE_NUMBER, SDP_UINT16, &hid_release_num); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_PARSER_VERSION, SDP_UINT16, &hid_parser_version); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_DEVICE_SUBCLASS, SDP_UINT8, &hid_dev_subclass); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_COUNTRY_CODE, SDP_UINT8, &hid_country_code); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_VIRTUAL_CABLE, SDP_BOOL, &hid_virtual_cable); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_RECONNECT_INITIATE, SDP_BOOL, &hid_reconn_initiate); 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_BOOL, &hid_sdp_disable); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_BATTERY_POWER, SDP_BOOL, &hid_batt_power); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_REMOTE_WAKEUP, SDP_BOOL, &hid_remote_wake); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_PROFILE_VERSION, SDP_UINT16, &hid_profile_version); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_SUPERVISION_TIMEOUT, SDP_UINT16, &hid_superv_timeout); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_NORMALLY_CONNECTABLE, SDP_BOOL, &hid_normally_connectable); sdp_attr_add_new(sdp_record, SDP_ATTR_HID_BOOT_DEVICE, SDP_BOOL, &hid_boot_device); return sdp_record; }
/* * 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); } }