static int extract_hid_record(sdp_record_t *rec, struct hidp_connadd_req *req) { sdp_data_t *pdlist; uint8_t attr_val; int err; err = create_hid_dev_name(rec, req); if (err < 0) DBG("No valid Service Name or Service Description found"); pdlist = sdp_data_get(rec, SDP_ATTR_HID_PARSER_VERSION); req->parser = pdlist ? pdlist->val.uint16 : 0x0100; pdlist = sdp_data_get(rec, SDP_ATTR_HID_DEVICE_SUBCLASS); req->subclass = pdlist ? pdlist->val.uint8 : 0; pdlist = sdp_data_get(rec, SDP_ATTR_HID_COUNTRY_CODE); req->country = pdlist ? pdlist->val.uint8 : 0; pdlist = sdp_data_get(rec, SDP_ATTR_HID_VIRTUAL_CABLE); attr_val = pdlist ? pdlist->val.uint8 : 0; if (attr_val) req->flags |= (1 << HIDP_VIRTUAL_CABLE_UNPLUG); pdlist = sdp_data_get(rec, SDP_ATTR_HID_BOOT_DEVICE); attr_val = pdlist ? pdlist->val.uint8 : 0; if (attr_val) req->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE); err = extract_hid_desc_data(rec, req); if (err < 0) return err; return 0; }
static void records_cb(sdp_list_t *recs, int err, gpointer data) { struct pending_reply *pr = data; int len; sdp_data_t *d; sdp_record_t *rec = NULL; char name[MAX_NAME_SIZE], *desc = NULL; if (err < 0) { error_connection_attempt_failed(pr->conn, pr->msg, -err); goto fail; } if (!recs || !recs->data) { error_not_supported(pr->conn, pr->msg); error("Invalid PAN service record"); goto fail; } rec = recs->data; /* Concat remote name and service name */ memset(name, 0, MAX_NAME_SIZE); if (read_remote_name(&pr->src, &pr->dst, name, MAX_NAME_SIZE) < 0) len = 0; else len = strlen(name); d = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY); if (d) { snprintf(name + len, MAX_NAME_SIZE - len, len ? " (%.*s)" : "%.*s", d->unitSize, d->val.str); } /* Extract service description from record */ d = sdp_data_get(rec, SDP_ATTR_SVCDESC_PRIMARY); if (d) { desc = g_new0(char, d->unitSize); snprintf(desc, d->unitSize, "%.*s", d->unitSize, d->val.str); } sdp_list_free(recs, (sdp_free_func_t) sdp_record_free); if (connection_register(pr->path, &pr->src, &pr->dst, pr->id, name, desc) < 0) { error_failed(pr->conn, pr->msg, "D-Bus path registration failed"); goto fail; } connection_store(pr->path, FALSE); connection_paths = g_slist_append(connection_paths, g_strdup(pr->path)); create_path(pr->conn, pr->msg, pr->path, "ConnectionCreated"); fail: g_free(desc); pending_reply_free(pr); }
/* * Extract attribute identifiers from the request PDU. * Clients could request a subset of attributes (by id) * from a service record, instead of the whole set. The * requested identifiers are present in the PDU form of * the request */ static int extract_attrs(sdp_record_t *rec, sdp_list_t *seq, sdp_buf_t *buf) { sdp_buf_t pdu; if (!rec) return SDP_INVALID_RECORD_HANDLE; if (seq) { SDPDBG("Entries in attr seq : %d", sdp_list_len(seq)); } else { SDPDBG("NULL attribute descriptor"); } if (seq == NULL) { SDPDBG("Attribute sequence is NULL"); return 0; } sdp_gen_record_pdu(rec, &pdu); for (; seq; seq = seq->next) { struct attrid *aid = seq->data; SDPDBG("AttrDataType : %d", aid->dtd); if (aid->dtd == SDP_UINT16) { uint16_t attr = bt_get_unaligned((uint16_t *)&aid->uint16); sdp_data_t *a = (sdp_data_t *)sdp_data_get(rec, attr); if (a) sdp_append_to_pdu(buf, a); } else if (aid->dtd == SDP_UINT32) { uint32_t range = bt_get_unaligned((uint32_t *)&aid->uint32); uint16_t attr; uint16_t low = (0xffff0000 & range) >> 16; uint16_t high = 0x0000ffff & range; sdp_data_t *data; SDPDBG("attr range : 0x%x", range); SDPDBG("Low id : 0x%x", low); SDPDBG("High id : 0x%x", high); if (low == 0x0000 && high == 0xffff && pdu.data_size <= buf->buf_size) { /* copy it */ memcpy(buf->data, pdu.data, pdu.data_size); buf->data_size = pdu.data_size; break; } /* (else) sub-range of attributes */ for (attr = low; attr < high; attr++) { data = sdp_data_get(rec, attr); if (data) sdp_append_to_pdu(buf, data); } data = sdp_data_get(rec, high); if (data) sdp_append_to_pdu(buf, data); } else {
/* * Extract attribute identifiers from the request PDU. * Clients could request a subset of attributes (by id) * from a service record, instead of the whole set. The * requested identifiers are present in the PDU form of * the request */ static int extract_attrs(sdp_record_t *rec, sdp_list_t *seq, uint8_t dtd, sdp_buf_t *buf) { if (!rec) return SDP_INVALID_RECORD_HANDLE; #ifdef SDP_DEBUG if (seq) SDPDBG("Entries in attr seq : %d\n", sdp_list_len(seq)); else SDPDBG("NULL attribute descriptor\n"); SDPDBG("AttrDataType : %d\n", dtd); #endif if (seq == NULL) { SDPDBG("Attribute sequence is NULL\n"); return 0; } if (dtd == SDP_UINT16) for (; seq; seq = seq->next) { uint16_t attr = sdp_get_unaligned((uint16_t *)seq->data); sdp_data_t *a = (sdp_data_t *)sdp_data_get(rec, attr); if (a) sdp_append_to_pdu(buf, a); } else if (dtd == SDP_UINT32) { sdp_buf_t pdu; sdp_gen_record_pdu(rec, &pdu); for (; seq; seq = seq->next) { uint32_t range = sdp_get_unaligned((uint32_t *)seq->data); uint16_t attr; uint16_t low = (0xffff0000 & range) >> 16; uint16_t high = 0x0000ffff & range; SDPDBG("attr range : 0x%x\n", range); SDPDBG("Low id : 0x%x\n", low); SDPDBG("High id : 0x%x\n", high); if (low == 0x0000 && high == 0xffff && pdu.data_size <= buf->buf_size) { /* copy it */ memcpy(buf->data, pdu.data, pdu.data_size); buf->data_size = pdu.data_size; break; } /* (else) sub-range of attributes */ for (attr = low; attr <= high; attr++) { sdp_data_t *a = (sdp_data_t *)sdp_data_get(rec, attr); if (a) sdp_append_to_pdu(buf, a); } } free(pdu.data); } else {
static void extract_hid_record(sdp_record_t *rec, struct hidp_connadd_req *req) { sdp_data_t *pdlist, *pdlist2; uint8_t attr_val; pdlist = sdp_data_get(rec, 0x0101); pdlist2 = sdp_data_get(rec, 0x0102); if (pdlist) { if (pdlist2) { if (strncmp(pdlist->val.str, pdlist2->val.str, 5)) { strncpy(req->name, pdlist2->val.str, 127); strcat(req->name, " "); } strncat(req->name, pdlist->val.str, 127 - strlen(req->name)); } else strncpy(req->name, pdlist->val.str, 127); } else { pdlist2 = sdp_data_get(rec, 0x0100); if (pdlist2) strncpy(req->name, pdlist2->val.str, 127); } pdlist = sdp_data_get(rec, SDP_ATTR_HID_PARSER_VERSION); req->parser = pdlist ? pdlist->val.uint16 : 0x0100; pdlist = sdp_data_get(rec, SDP_ATTR_HID_DEVICE_SUBCLASS); req->subclass = pdlist ? pdlist->val.uint8 : 0; pdlist = sdp_data_get(rec, SDP_ATTR_HID_COUNTRY_CODE); req->country = pdlist ? pdlist->val.uint8 : 0; pdlist = sdp_data_get(rec, SDP_ATTR_HID_VIRTUAL_CABLE); attr_val = pdlist ? pdlist->val.uint8 : 0; if (attr_val) req->flags |= (1 << HIDP_VIRTUAL_CABLE_UNPLUG); pdlist = sdp_data_get(rec, SDP_ATTR_HID_BOOT_DEVICE); attr_val = pdlist ? pdlist->val.uint8 : 0; if (attr_val) req->flags |= (1 << HIDP_BOOT_PROTOCOL_MODE); pdlist = sdp_data_get(rec, SDP_ATTR_HID_DESCRIPTOR_LIST); if (pdlist) { pdlist = pdlist->val.dataseq; pdlist = pdlist->val.dataseq; pdlist = pdlist->next; req->rd_data = g_try_malloc0(pdlist->unitSize); if (req->rd_data) { memcpy(req->rd_data, (unsigned char *) pdlist->val.str, pdlist->unitSize); req->rd_size = pdlist->unitSize; epox_endian_quirk(req->rd_data, req->rd_size); } } }
static void hid_sdp_did_search_cb(sdp_list_t *recs, int err, gpointer data) { struct hid_device *dev = data; sdp_list_t *list; uuid_t uuid; DBG(""); if (err < 0) { error("hidhost: Unable to get Device ID SDP record: %s", strerror(-err)); goto fail; } if (!recs || !recs->data) { error("hidhost: No Device ID SDP records found"); goto fail; } for (list = recs; list; list = list->next) { sdp_record_t *rec = list->data; sdp_data_t *data; data = sdp_data_get(rec, SDP_ATTR_VENDOR_ID); if (data) dev->vendor = data->val.uint16; data = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID); if (data) dev->product = data->val.uint16; data = sdp_data_get(rec, SDP_ATTR_VERSION); if (data) dev->version = data->val.uint16; } sdp_uuid16_create(&uuid, HID_SVCLASS_ID); if (bt_search_service(&adapter_addr, &dev->dst, &uuid, hid_sdp_search_cb, dev, NULL, 0) < 0) { error("hidhost: Failed to search SDP details"); goto fail; } return; fail: bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); hid_device_remove(dev); }
static int get_add_prot_desc_list(const sdp_record_t *rec, uint16_t *psm) { sdp_data_t *pdl, *p0, *p1; if (!psm) return -1; pdl = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST); if (!pdl || pdl->dtd != SDP_SEQ8) return -1; pdl = pdl->val.dataseq; if (pdl->dtd != SDP_SEQ8) return -1; p0 = pdl->val.dataseq; if (get_prot_desc_entry(p0, L2CAP_UUID, psm) < 0) return -1; p1 = p0->next; if (get_prot_desc_entry(p1, MCAP_DATA_UUID, NULL) < 0) return -1; return 0; }
static gboolean hdp_get_add_prot_desc_list(const sdp_record_t *rec, guint16 *psm) { sdp_data_t *pdl, *p0, *p1; if (psm == NULL) return TRUE; pdl = sdp_data_get(rec, SDP_ATTR_ADD_PROTO_DESC_LIST); if (pdl == NULL || pdl->dtd != SDP_SEQ8) return FALSE; pdl = pdl->val.dataseq; if (pdl->dtd != SDP_SEQ8) return FALSE; p0 = pdl->val.dataseq; if (!get_prot_desc_entry(p0, L2CAP_UUID, psm)) return FALSE; p1 = p0->next; if (!get_prot_desc_entry(p1, MCAP_DATA_UUID, NULL)) return FALSE; return TRUE; }
static int hid_device_probe(struct btd_device *device, GSList *uuids) { struct btd_adapter *adapter = device_get_adapter(device); const gchar *path = device_get_path(device); const sdp_record_t *rec = btd_device_get_record(device, uuids->data); bdaddr_t src, dst; sdp_data_t *pdlist; DBG("path %s", path); if (!rec) return -1; adapter_get_address(adapter, &src); device_get_address(device, &dst); if (rec) pdlist = sdp_data_get(rec, SDP_ATTR_HID_SDP_DISABLE); if (pdlist && pdlist->val.uint8) { DBG("cancel discovery is issued"); bt_cancel_discovery(&src, &dst); } return input_device_register(connection, device, path, &src, &dst, HID_UUID, rec->handle, idle_timeout * 60); }
void populateServiceRecord(JNIEnv *env, jobject serviceRecord, sdp_record_t* sdpRecord, sdp_list_t* attributeList) { jclass serviceRecordImplClass = (*env)->GetObjectClass(env, serviceRecord); debug("populateServiceRecord"); jmethodID populateAttributeValueID = getGetMethodID(env, serviceRecordImplClass, "populateAttributeValue", "(ILjavax/bluetooth/DataElement;)V"); if (populateAttributeValueID == NULL) { return; } int attrCount = 0; for(; attributeList; attributeList = attributeList->next) { jint attributeID=*(uint16_t*)attributeList->data; sdp_data_t *data = sdp_data_get(sdpRecord, (uint16_t)attributeID); if (data) { jobject dataElement = createDataElement(env, data); if ((*env)->ExceptionCheck(env)) { break; } if (dataElement == NULL) { break; } (*env)->CallVoidMethod(env, serviceRecord, populateAttributeValueID, attributeID, dataElement); if ((*env)->ExceptionCheck(env)) { break; } attrCount ++; } } Edebug("attrCount %i", attrCount); }
static bool is_device_sdp_disable(const sdp_record_t *rec) { sdp_data_t *data; data = sdp_data_get(rec, SDP_ATTR_HID_SDP_DISABLE); return data && data->val.uint8; }
static gboolean get_mdep_from_rec(const sdp_record_t *rec, uint8_t role, uint16_t d_type, uint8_t *mdep, char **desc) { DBG(""); sdp_data_t *list, *feat; DBG(""); if (!desc && !mdep) return TRUE; list = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST); if (list->dtd != SDP_SEQ8 && list->dtd != SDP_SEQ16 && list->dtd != SDP_SEQ32) return FALSE; for (feat = list->val.dataseq; feat; feat = feat->next) { sdp_data_t *data_type, *mdepid, *role_t, *desc_t; if (feat->dtd != SDP_SEQ8 && feat->dtd != SDP_SEQ16 && feat->dtd != SDP_SEQ32) continue; mdepid = feat->val.dataseq; if (!mdepid) continue; data_type = mdepid->next; if (!data_type) continue; role_t = data_type->next; if (!role_t) continue; desc_t = role_t->next; if (data_type->dtd != SDP_UINT16 || mdepid->dtd != SDP_UINT8 || role_t->dtd != SDP_UINT8) continue; if (data_type->val.uint16 != d_type || !check_role(role_t->val.uint8, role)) continue; if (mdep) *mdep = mdepid->val.uint8; if (desc && desc_t && (desc_t->dtd == SDP_TEXT_STR8 || desc_t->dtd == SDP_TEXT_STR16 || desc_t->dtd == SDP_TEXT_STR32)) *desc = g_strdup(desc_t->val.str); return TRUE; } return FALSE; }
static void extract_hid_props(struct input_device *idev, const sdp_record_t *rec) { /* Extract HID connectability */ bool reconnect_initiate, normally_connectable; sdp_data_t *pdlist; /* HIDNormallyConnectable is optional and assumed FALSE * if not present. */ pdlist = sdp_data_get(rec, SDP_ATTR_HID_RECONNECT_INITIATE); reconnect_initiate = pdlist ? pdlist->val.uint8 : TRUE; pdlist = sdp_data_get(rec, SDP_ATTR_HID_NORMALLY_CONNECTABLE); normally_connectable = pdlist ? pdlist->val.uint8 : FALSE; /* Update local values */ idev->reconnect_mode = hid_reconnection_mode(reconnect_initiate, normally_connectable); }
static gboolean get_mdep_from_rec(const sdp_record_t *rec, uint8_t role, uint16_t d_type, uint8_t *mdep, char **desc) { sdp_data_t *list, *feat; if (desc == NULL && mdep == NULL) return TRUE; list = sdp_data_get(rec, SDP_ATTR_SUPPORTED_FEATURES_LIST); if (list == NULL || !SDP_IS_SEQ(list->dtd)) return FALSE; for (feat = list->val.dataseq; feat; feat = feat->next) { sdp_data_t *data_type, *mdepid, *role_t, *desc_t; if (!SDP_IS_SEQ(feat->dtd)) continue; mdepid = feat->val.dataseq; if (mdepid == NULL) continue; data_type = mdepid->next; if (data_type == NULL) continue; role_t = data_type->next; if (role_t == NULL) continue; desc_t = role_t->next; if (data_type->dtd != SDP_UINT16 || mdepid->dtd != SDP_UINT8 || role_t->dtd != SDP_UINT8) continue; if (data_type->val.uint16 != d_type || !check_role(role_t->val.uint8, role)) continue; if (mdep != NULL) *mdep = mdepid->val.uint8; if (desc != NULL && desc_t != NULL && SDP_IS_TEXT_STR(desc_t->dtd)) *desc = g_strdup(desc_t->val.str); return TRUE; } return FALSE; }
/* * Taken from gnome-phone-manager */ static int get_rfcomm_channel(sdp_record_t *rec, int only_gnapplet) { int channel = -1; sdp_list_t *protos = NULL; sdp_data_t *data; char name[64]; if (sdp_get_access_protos(rec, &protos) != 0) goto end; data = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY); if (data) snprintf(name, sizeof(name), "%.*s", data->unitSize, data->val.str); if (name == NULL) goto end; /* * If we're only supposed to check for gnapplet, do it here * We ignore it if we're not supposed to check for it. */ if (only_gnapplet != 0) { if (strcmp(name, "gnapplet") == 0) channel = sdp_get_proto_port(protos, RFCOMM_UUID); goto end; } /* * We can't seem to connect to the PC Suite channel. */ if (strstr(name, "Nokia PC Suite") != NULL) goto end; /* * And that type of channel on Nokia Symbian phones doesn't * work either. */ if (strstr(name, "Bluetooth Serial Port") != NULL) goto end; /* * Avoid the m-Router channel, same as the PC Suite on Sony Ericsson * phones. */ if (strstr(name, "m-Router Connectivity") != NULL) goto end; channel = sdp_get_proto_port(protos, RFCOMM_UUID); end: sdp_list_foreach(protos, (sdp_list_func_t)sdp_list_free, 0); sdp_list_free(protos, 0); return channel; }
int add_record_to_server(const bdaddr_t *src, sdp_record_t *rec) { sdp_data_t *data; sdp_list_t *pattern; if (rec->handle == 0xffffffff) { rec->handle = sdp_next_handle(); if (rec->handle < 0x10000) return -ENOSPC; } else { if (sdp_record_find(rec->handle)) return -EEXIST; } DBG("Adding record with handle 0x%05x", rec->handle); sdp_record_add(src, rec); data = sdp_data_alloc(SDP_UINT32, &rec->handle); sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, data); if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) { uuid_t uuid; sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); sdp_pattern_add_uuid(rec, &uuid); } for (pattern = rec->pattern; pattern; pattern = pattern->next) { char uuid[32]; if (pattern->data == NULL) continue; sdp_uuid2strn((uuid_t *) pattern->data, uuid, sizeof(uuid)); DBG("Record pattern UUID %s", uuid); } update_db_timestamp(); return 0; }
static const void *bluetooth_getattribute(guint id, int attribute_id) { GSList *l; sdp_data_t *data; for (l = sessions; l; l = l->next) { struct bluetooth_session *session = l->data; if (session->id != id) continue; if (session->sdp_record == NULL) break; /* Read version since UUID is already known */ if (attribute_id == SDP_ATTR_PFILE_DESC_LIST) { sdp_list_t *descs; void *ret = NULL; if (sdp_get_profile_descs(session->sdp_record, &descs) < 0) return NULL; if (descs && descs->data) { sdp_profile_desc_t *desc = descs->data; ret = GINT_TO_POINTER(desc->version); } sdp_list_free(descs, free); return ret; } data = sdp_data_get(session->sdp_record, attribute_id); if (!data) break; return &data->val; } return NULL; }
/* See HID profile specification v1.0, "7.11.6 HIDDescriptorList" for details * on the attribute format. */ static int extract_hid_desc_data(sdp_record_t *rec, struct hidp_connadd_req *req) { sdp_data_t *d; d = sdp_data_get(rec, SDP_ATTR_HID_DESCRIPTOR_LIST); if (!d) goto invalid_desc; if (!SDP_IS_SEQ(d->dtd)) goto invalid_desc; /* First HIDDescriptor */ d = d->val.dataseq; if (!SDP_IS_SEQ(d->dtd)) goto invalid_desc; /* ClassDescriptorType */ d = d->val.dataseq; if (d->dtd != SDP_UINT8) goto invalid_desc; /* ClassDescriptorData */ d = d->next; if (!d || !SDP_IS_TEXT_STR(d->dtd)) goto invalid_desc; req->rd_data = g_try_malloc0(d->unitSize); if (req->rd_data) { memcpy(req->rd_data, d->val.str, d->unitSize); req->rd_size = d->unitSize; epox_endian_quirk(req->rd_data, req->rd_size); } return 0; invalid_desc: error("Missing or invalid HIDDescriptorList SDP attribute"); return -EINVAL; }
static int get_prot_desc_list(const sdp_record_t *rec, uint16_t *psm, uint16_t *version) { sdp_data_t *pdl, *p0, *p1; if (!psm && !version) return -1; pdl = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST); if (!pdl || !SDP_IS_SEQ(pdl->dtd)) return -1; p0 = pdl->val.dataseq; if (get_prot_desc_entry(p0, L2CAP_UUID, psm) < 0) return -1; p1 = p0->next; if (get_prot_desc_entry(p1, MCAP_CTRL_UUID, version) < 0) return -1; return 0; }
static const void *bluetooth_getattribute(guint id, int attribute_id) { GSList *l; sdp_data_t *data; for (l = sessions; l; l = l->next) { struct bluetooth_session *session = l->data; if (session->id != id) continue; if (session->sdp_record == NULL) break; data = sdp_data_get(session->sdp_record, attribute_id); if (!data) break; return &data->val; } return NULL; }
static gboolean hdp_get_prot_desc_list(const sdp_record_t *rec, guint16 *psm, guint16 *version) { sdp_data_t *pdl, *p0, *p1; if (psm == NULL && version == NULL) return TRUE; pdl = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST); if (pdl == NULL || !SDP_IS_SEQ(pdl->dtd)) return FALSE; p0 = pdl->val.dataseq; if (!get_prot_desc_entry(p0, L2CAP_UUID, psm)) return FALSE; p1 = p0->next; if (!get_prot_desc_entry(p1, MCAP_CTRL_UUID, version)) return FALSE; return TRUE; }
static gboolean mcap_get_prot_desc_list(const sdp_record_t *rec, guint16 *psm, guint16 *version) { DBG(""); sdp_data_t *pdl, *p0, *p1; if (!psm && !version) return TRUE; pdl = sdp_data_get(rec, SDP_ATTR_PROTO_DESC_LIST); if (pdl->dtd != SDP_SEQ8 && pdl->dtd != SDP_SEQ16 && pdl->dtd != SDP_SEQ32) return FALSE; p0 = pdl->val.dataseq; if (!get_prot_desc_entry(p0, L2CAP_UUID, psm)) return FALSE; p1 = p0->next; if (!get_prot_desc_entry(p1, MCAP_CTRL_UUID, version)) return FALSE; return TRUE; }
int get_sdp_device_info(const bdaddr_t *src, const bdaddr_t *dst, struct hidp_connadd_req *req) { struct sockaddr_l2 addr; socklen_t addrlen; bdaddr_t bdaddr; uint32_t range = 0x0000ffff; sdp_session_t *s; sdp_list_t *search, *attrid, *pnp_rsp; sdp_record_t *rec; sdp_data_t *pdlist; uuid_t svclass; int err; s = sdp_connect(src, dst, SDP_RETRY_IF_BUSY | SDP_WAIT_ON_CLOSE); if (!s) return -1; sdp_uuid16_create(&svclass, PNP_INFO_SVCLASS_ID); search = sdp_list_append(NULL, &svclass); attrid = sdp_list_append(NULL, &range); err = sdp_service_search_attr_req(s, search, SDP_ATTR_REQ_RANGE, attrid, &pnp_rsp); sdp_list_free(search, NULL); sdp_list_free(attrid, NULL); memset(&addr, 0, sizeof(addr)); addrlen = sizeof(addr); if (getsockname(s->sock, (struct sockaddr *) &addr, &addrlen) < 0) bacpy(&bdaddr, src); else bacpy(&bdaddr, &addr.l2_bdaddr); sdp_close(s); if (err) return -1; if (pnp_rsp) { rec = (sdp_record_t *) pnp_rsp->data; pdlist = sdp_data_get(rec, 0x0201); req->vendor = pdlist ? pdlist->val.uint16 : 0x0000; pdlist = sdp_data_get(rec, 0x0202); req->product = pdlist ? pdlist->val.uint16 : 0x0000; pdlist = sdp_data_get(rec, 0x0203); req->version = pdlist ? pdlist->val.uint16 : 0x0000; sdp_record_free(rec); } req->parser = 0x0100; req->subclass = 0xc0; req->country = 0; if (bacmp(&bdaddr, BDADDR_ANY)) store_device_info(&bdaddr, dst, req); return 0; }
// Detect whether A2DP Sink is present at the destination or not static int detect_a2dp(bdaddr_t *src, bdaddr_t *dst, unsigned short *psm, unsigned long *flags) { sdp_session_t *sess; sdp_list_t *attrid, *search, *seq, *next; sdp_data_t *pdlist; uuid_t group; uint32_t range = 0x0000ffff; int err; int tries; tries = 0; while(!(sess = sdp_connect(src, dst, SDP_RETRY_IF_BUSY))) { DBG("retrying sdp connect: %s", strerror(errno)); sleep(1); if(++tries > 10) { break; } } if (!sess) { DBG( "Warning: failed to connect to SDP server: %s", strerror(errno)); if(psm) *psm = 25; if(flags) *flags = 0; return 0; } /* 0x1108->all? 0x1101->rf sink 0x111e->handsfree 0x1108->headset */ sdp_uuid16_create(&group, 0x110d); search = sdp_list_append(0, &group); attrid = sdp_list_append(0, &range); err = sdp_service_search_attr_req(sess, search, SDP_ATTR_REQ_RANGE, attrid, &seq); sdp_list_free(search, 0); sdp_list_free(attrid, 0); if (err) { DBG( "Service Search failed: %s", strerror(errno)); sdp_close(sess); return -1; } for (; seq; seq = next) { sdp_record_t *rec = (sdp_record_t *) seq->data; DBG( "Found A2DP Sink"); if (psm) *psm = 25; next = seq->next; free(seq); sdp_record_free(rec); } sdp_uuid16_create(&group, PNP_INFO_SVCLASS_ID); search = sdp_list_append(0, &group); attrid = sdp_list_append(0, &range); err = sdp_service_search_attr_req(sess, search, SDP_ATTR_REQ_RANGE, attrid, &seq); sdp_list_free(search, 0); sdp_list_free(attrid, 0); if (err) goto done; if (flags) *flags = 0; for (; seq; seq = next) { sdp_record_t *rec = (sdp_record_t *) seq->data; uint16_t vendor, product, version; pdlist = sdp_data_get(rec, 0x0201); vendor = pdlist ? pdlist->val.uint16 : 0x0000; pdlist = sdp_data_get(rec, 0x0202); product = pdlist ? pdlist->val.uint16 : 0x0000; pdlist = sdp_data_get(rec, 0x0203); version = pdlist ? pdlist->val.uint16 : 0x0000; DBG( "Product ID %04x:%04x:%04x", vendor, product, version); if (vendor == 0x1310 && product == 0x0100 && version == 0x0104) { DBG( "Enabling GCT media payload workaround"); if (flags) *flags |= NONSPECAUDIO; } next = seq->next; free(seq); sdp_record_free(rec); } done: sdp_close(sess); return 0; }
static void hid_sdp_search_cb(sdp_list_t *recs, int err, gpointer data) { struct hid_device *dev = data; sdp_list_t *list; GError *gerr = NULL; DBG(""); if (err < 0) { error("hidhost: Unable to get SDP record: %s", strerror(-err)); goto fail; } if (!recs || !recs->data) { error("hidhost: No SDP records found"); goto fail; } for (list = recs; list != NULL; list = list->next) { sdp_record_t *rec = list->data; sdp_data_t *data; data = sdp_data_get(rec, SDP_ATTR_HID_COUNTRY_CODE); if (data) dev->country = data->val.uint8; data = sdp_data_get(rec, SDP_ATTR_HID_DEVICE_SUBCLASS); if (data) dev->subclass = data->val.uint8; data = sdp_data_get(rec, SDP_ATTR_HID_BOOT_DEVICE); if (data) dev->boot_dev = data->val.uint8; data = sdp_data_get(rec, SDP_ATTR_HID_DESCRIPTOR_LIST); if (data) { if (!SDP_IS_SEQ(data->dtd)) goto fail; /* First HIDDescriptor */ data = data->val.dataseq; if (!SDP_IS_SEQ(data->dtd)) goto fail; /* ClassDescriptorType */ data = data->val.dataseq; if (data->dtd != SDP_UINT8) goto fail; /* ClassDescriptorData */ data = data->next; if (!data || !SDP_IS_TEXT_STR(data->dtd)) goto fail; dev->rd_size = data->unitSize; dev->rd_data = g_memdup(data->val.str, data->unitSize); } } if (dev->ctrl_io) { if (uhid_create(dev) < 0) goto fail; return; } dev->ctrl_io = bt_io_connect(control_connect_cb, dev, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, &adapter_addr, BT_IO_OPT_DEST_BDADDR, &dev->dst, BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, BT_IO_OPT_INVALID); if (gerr) { error("hidhost: Failed to connect control channel (%s)", gerr->message); g_error_free(gerr); goto fail; } return; fail: bt_hid_notify_state(dev, HAL_HIDHOST_STATE_DISCONNECTED); hid_device_remove(dev); }
static void search_callback(uint8_t type, uint16_t status, uint8_t *rsp, size_t size, void *user_data) { struct bluetooth_session *session = user_data; unsigned int scanned, bytesleft = size; int seqlen = 0; uint8_t dataType; uint16_t port = 0; GError *gerr = NULL; if (status || type != SDP_SVC_SEARCH_ATTR_RSP) goto failed; scanned = sdp_extract_seqtype(rsp, bytesleft, &dataType, &seqlen); if (!scanned || !seqlen) goto failed; rsp += scanned; bytesleft -= scanned; do { sdp_record_t *rec; sdp_list_t *protos; sdp_data_t *data; int recsize, ch = -1; recsize = 0; rec = sdp_extract_pdu(rsp, bytesleft, &recsize); if (!rec) break; if (!recsize) { sdp_record_free(rec); break; } if (!sdp_get_access_protos(rec, &protos)) { ch = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); protos = NULL; } data = sdp_data_get(rec, 0x0200); /* PSM must be odd and lsb of upper byte must be 0 */ if (data != NULL && (data->val.uint16 & 0x0101) == 0x0001) ch = data->val.uint16; sdp_record_free(rec); if (ch > 0) { port = ch; break; } scanned += recsize; rsp += recsize; bytesleft -= recsize; } while (scanned < size && bytesleft > 0); if (port == 0) goto failed; session->port = port; g_io_channel_set_close_on_unref(session->io, FALSE); g_io_channel_unref(session->io); session->io = transport_connect(&session->src, &session->dst, port, transport_callback, session); if (session->io != NULL) { sdp_close(session->sdp); session->sdp = NULL; return; } failed: if (session->io != NULL) { g_io_channel_shutdown(session->io, TRUE, NULL); g_io_channel_unref(session->io); session->io = NULL; } g_set_error(&gerr, OBC_BT_ERROR, -EIO, "Unable to find service record"); if (session->func) session->func(session->io, gerr, session->user_data); g_clear_error(&gerr); session_destroy(session); }
/* * Add the newly created service record to the service repository */ int service_register_req(sdp_req_t *req, sdp_buf_t *rsp) { int scanned = 0; sdp_data_t *handle; uint8_t *p = req->buf + sizeof(sdp_pdu_hdr_t); int bufsize = req->len - sizeof(sdp_pdu_hdr_t); sdp_record_t *rec; req->flags = *p++; if (req->flags & SDP_DEVICE_RECORD) { bacpy(&req->device, (bdaddr_t *) p); p += sizeof(bdaddr_t); bufsize -= sizeof(bdaddr_t); } /* save image of PDU: we need it when clients request this attribute */ rec = extract_pdu_server(&req->device, p, bufsize, 0xffffffff, &scanned); if (!rec) goto invalid; if (rec->handle == 0xffffffff) { rec->handle = sdp_next_handle(); if (rec->handle < 0x10000) { sdp_record_free(rec); goto invalid; } } else { if (sdp_record_find(rec->handle)) { /* extract_pdu_server will add the record handle * if it is missing. So instead of failing, skip * the record adding to avoid duplication. */ goto success; } } sdp_record_add(&req->device, rec); if (!(req->flags & SDP_RECORD_PERSIST)) sdp_svcdb_set_collectable(rec, req->sock); handle = sdp_data_alloc(SDP_UINT32, &rec->handle); sdp_attr_replace(rec, SDP_ATTR_RECORD_HANDLE, handle); success: /* if the browse group descriptor is NULL, * ensure that the record belongs to the ROOT group */ if (sdp_data_get(rec, SDP_ATTR_BROWSE_GRP_LIST) == NULL) { uuid_t uuid; sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP); sdp_pattern_add_uuid(rec, &uuid); } update_db_timestamp(); /* Build a rsp buffer */ bt_put_be32(rec->handle, rsp->data); rsp->data_size = sizeof(uint32_t); return 0; invalid: bt_put_be16(SDP_INVALID_SYNTAX, rsp->data); rsp->data_size = sizeof(uint16_t); return -1; }