static int serial_probe(struct btd_device *device, const char *uuid) { struct btd_adapter *adapter = device_get_adapter(device); const gchar *path = device_get_path(device); sdp_list_t *protos; int ch; bdaddr_t src, dst; const sdp_record_t *rec; DBG("path %s: %s", path, uuid); rec = btd_device_get_record(device, uuid); if (!rec) return -EINVAL; if (sdp_get_access_protos(rec, &protos) < 0) return -EINVAL; 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); if (ch < 1 || ch > 30) { error("Channel out of range: %d", ch); return -EINVAL; } adapter_get_address(adapter, &src); device_get_address(device, &dst); return port_register(connection, path, &src, &dst, uuid, ch); }
gboolean gatt_parse_record(const sdp_record_t *rec, uuid_t *prim_uuid, uint16_t *psm, uint16_t *start, uint16_t *end) { sdp_list_t *list; uuid_t uuid; gboolean ret; if (sdp_get_service_classes(rec, &list) < 0) return FALSE; memcpy(&uuid, list->data, sizeof(uuid)); sdp_list_free(list, free); if (sdp_get_access_protos(rec, &list) < 0) return FALSE; ret = parse_proto_params(list, psm, start, end); sdp_list_foreach(list, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(list, NULL); /* FIXME: replace by bt_uuid_t after uuid_t/sdp code cleanup */ if (ret && prim_uuid) memcpy(prim_uuid, &uuid, sizeof(uuid_t)); return ret; }
static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data) { struct serial_port *port = user_data; struct serial_device *device = port->device; sdp_record_t *record = NULL; sdp_list_t *protos; DBusMessage *reply; GError *gerr = NULL; if (!port->listener_id) { reply = NULL; goto failed; } if (err < 0) { error("Unable to get service record: %s (%d)", strerror(-err), -err); reply = failed(port->msg, strerror(-err)); goto failed; } if (!recs || !recs->data) { error("No record found"); reply = failed(port->msg, "No record found"); goto failed; } record = recs->data; if (sdp_get_access_protos(record, &protos) < 0) { error("Unable to get access protos from port record"); reply = failed(port->msg, "Invalid channel"); goto failed; } port->channel = sdp_get_proto_port(protos, RFCOMM_UUID); sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL); sdp_list_free(protos, NULL); port->io = bt_io_connect(BT_IO_RFCOMM, rfcomm_connect_cb, port, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, &device->src, BT_IO_OPT_DEST_BDADDR, &device->dst, BT_IO_OPT_CHANNEL, port->channel, BT_IO_OPT_INVALID); if (!port->io) { error("%s", gerr->message); reply = failed(port->msg, gerr->message); g_error_free(gerr); goto failed; } return; failed: g_dbus_remove_watch(device->conn, port->listener_id); port->listener_id = 0; g_dbus_send_message(device->conn, reply); }
/* * 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; }
// the resulting xml output is based on the already used xml parser QByteArray parseSdpRecord(sdp_record_t *record) { if (!record || !record->attrlist) return QByteArray(); QByteArray xmlOutput; xmlOutput.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<record>\n"); sdp_list_foreach(record->attrlist, parseAttribute, &xmlOutput); xmlOutput.append("</record>"); return xmlOutput; }
static int headset_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 *record; sdp_list_t *protos; int ch; bdaddr_t src, dst; DBG("path %s", path); if (!g_slist_find_custom(uuids, HSP_HS_UUID, (GCompareFunc) strcasecmp)) return -EINVAL; record = btd_device_get_record(device, uuids->data); if (!record || sdp_get_access_protos(record, &protos) < 0) { error("Invalid record"); return -EINVAL; } 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); if (ch <= 0) { error("Invalid RFCOMM channel"); return -EINVAL; } adapter_get_address(adapter, &src); device_get_address(device, &dst); return fake_input_register(connection, device, path, &src, &dst, HSP_HS_UUID, ch); }
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); }
static void search_callback(uint8_t type, uint16_t status, uint8_t *rsp, size_t size, void *user_data) { struct callback_data *callback = user_data; unsigned int scanned, bytesleft = size; int seqlen = 0; uint8_t dataType, channel = 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; 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; } sdp_record_free(rec); if (ch > 0) { channel = ch; break; } scanned += recsize; rsp += recsize; bytesleft -= recsize; } while (scanned < size && bytesleft > 0); if (channel == 0) goto failed; callback->session->channel = channel; callback->session->io = rfcomm_connect(&callback->session->src, &callback->session->dst, channel, rfcomm_callback, callback); if (callback->session->io != NULL) { sdp_close(callback->sdp); return; } failed: sdp_close(callback->sdp); g_set_error(&gerr, OBEX_IO_ERROR, -EIO, "Unable to find service record"); callback->func(callback->session, gerr, callback->data); g_clear_error(&gerr); session_unref(callback->session); g_free(callback); }
static void get_record_cb(sdp_list_t *recs, int err, gpointer user_data) { struct audio_device *dev = user_data; struct gateway *gw = dev->gateway; int ch; sdp_list_t *protos, *classes; uuid_t uuid; GIOChannel *io; GError *gerr = NULL; if (err < 0) { error("Unable to get service record: %s (%d)", strerror(-err), -err); goto fail; } if (!recs || !recs->data) { error("No records found"); err = -EIO; goto fail; } if (sdp_get_service_classes(recs->data, &classes) < 0) { error("Unable to get service classes from record"); err = -EINVAL; goto fail; } if (sdp_get_access_protos(recs->data, &protos) < 0) { error("Unable to get access protocols from record"); err = -ENODATA; goto fail; } gw->version = get_remote_profile_version(recs->data); if (gw->version == 0) { error("Unable to get profile version from record"); err = -EINVAL; goto fail; } memcpy(&uuid, classes->data, sizeof(uuid)); sdp_list_free(classes, free); if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 || uuid.value.uuid16 != HANDSFREE_AGW_SVCLASS_ID) { sdp_list_free(protos, NULL); error("Invalid service record or not HFP"); err = -EIO; goto fail; } 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); if (ch <= 0) { error("Unable to extract RFCOMM channel from service record"); err = -EIO; goto fail; } io = bt_io_connect(rfcomm_connect_cb, dev, NULL, &gerr, BT_IO_OPT_SOURCE_BDADDR, &dev->src, BT_IO_OPT_DEST_BDADDR, &dev->dst, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_CHANNEL, ch, BT_IO_OPT_INVALID); if (!io) { error("Unable to connect: %s", gerr->message); goto fail; } g_io_channel_unref(io); return; fail: if (gw->msg) { DBusMessage *reply = btd_error_failed(gw->msg, gerr ? gerr->message : strerror(-err)); g_dbus_send_message(btd_get_dbus_connection(), reply); } gateway_close(dev); if (gerr) g_error_free(gerr); }