summaryrefslogtreecommitdiffstats
path: root/hcid
diff options
context:
space:
mode:
authorClaudio Takahasi <claudio.takahasi@openbossa.org>2006-04-20 17:18:19 +0000
committerClaudio Takahasi <claudio.takahasi@openbossa.org>2006-04-20 17:18:19 +0000
commit8bfc6e7cd8210dd2bf6acc9e84b33c299e8f5c6a (patch)
treecd29d558bea4858dd0803c7ae111dc8f80fcf002 /hcid
parentd7d52c07bd5c6ef4d1f411dc2ef57502ed028125 (diff)
Added D-Bus SDP methods skeleton
Diffstat (limited to 'hcid')
-rw-r--r--hcid/dbus-adapter.c4
-rw-r--r--hcid/dbus-error.c16
-rw-r--r--hcid/dbus-sdp.c501
-rw-r--r--hcid/dbus.h17
4 files changed, 537 insertions, 1 deletions
diff --git a/hcid/dbus-adapter.c b/hcid/dbus-adapter.c
index 039b0672..513d875c 100644
--- a/hcid/dbus-adapter.c
+++ b/hcid/dbus-adapter.c
@@ -2214,6 +2214,8 @@ DBusHandlerResult msg_func_device(DBusConnection *conn, DBusMessage *msg, void *
return handle_security_method(conn, msg, data);
else if (!strcmp(RFCOMM_INTERFACE, iface))
return handle_rfcomm_method(conn, msg, data);
- else
+ else if (!strcmp(SDP_INTERFACE, iface))
+ return handle_sdp_method(conn, msg, data);
+ else
return error_not_implemented(conn, msg);
}
diff --git a/hcid/dbus-error.c b/hcid/dbus-error.c
index 5407da15..bcd1b109 100644
--- a/hcid/dbus-error.c
+++ b/hcid/dbus-error.c
@@ -208,3 +208,19 @@ DBusHandlerResult error_binding_does_not_exist(DBusConnection *conn, DBusMessage
{
return error_does_not_exist(conn, msg, "Binding does not exist");
}
+
+DBusHandlerResult error_service_already_exists(DBusConnection *conn, DBusMessage *msg)
+{
+ return error_already_exists(conn, msg, "Service already exists");
+}
+
+DBusHandlerResult error_service_does_not_exist(DBusConnection *conn, DBusMessage *msg)
+{
+ return error_does_not_exist(conn, msg, "Service does not exist");
+}
+
+DBusHandlerResult error_service_search_in_progress(DBusConnection *conn, DBusMessage *msg)
+{
+ return error_in_progress(conn, msg, "Service search in progress");
+}
+
diff --git a/hcid/dbus-sdp.c b/hcid/dbus-sdp.c
index f3bf47c4..e2a46fb8 100644
--- a/hcid/dbus-sdp.c
+++ b/hcid/dbus-sdp.c
@@ -27,3 +27,504 @@
#include <stdio.h>
#include <errno.h>
+#include <unistd.h>
+
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+#include <dbus/dbus.h>
+
+#include "dbus.h"
+#include "hcid.h"
+#include "textfile.h"
+
+struct dbus_sdp_record {
+ char *owner; /* null for remote services or unique name if local */
+ bdaddr_t provider; /* remote Bluetooth address or local address */
+ char *name; /* service name */
+ uint32_t identifier;
+ uint16_t uuid;
+ uint8_t channel;
+};
+
+/* list of remote and local service records */
+static struct slist *sdp_records = NULL;
+
+struct search_request_info *search_request_info_new(bdaddr_t *dba, DBusMessage *msg)
+{
+ struct search_request_info *search = malloc(sizeof(struct search_request_info));
+ if (!search)
+ return NULL;
+
+ memset(search, 0, sizeof(*search));
+
+ bacpy(&search->bdaddr, dba);
+ search->rq = dbus_message_ref(msg);
+
+ return search;
+}
+void search_request_info_free(struct search_request_info *search)
+{
+ if (search->rq)
+ dbus_message_unref(search->rq);
+ if (search->session)
+ free(search->session);
+ if (search->io)
+ free(search->io);
+
+ free(search);
+}
+
+void dbus_sdp_record_free(struct dbus_sdp_record *rec)
+{
+ if (rec->owner)
+ free(rec->owner);
+ if (rec->name)
+ free(rec->name);
+
+ free(rec);
+}
+
+static void id2str(uint32_t id, char *dst)
+{
+ snprintf(dst, 9, "%2.2X%2.2X%2.2X%2.2X", (id >> 24) & 0xFF,
+ (id >> 16) & 0xFF, (id >> 8) & 0xFF, id & 0xFF);
+ debug ("%s, identifier:%s", __PRETTY_FUNCTION__, dst);
+}
+
+static uint32_t str2id(const char *id)
+{
+ return atoi(id);
+}
+
+
+static uint32_t generate_new_id(const bdaddr_t *provider, uint8_t channel)
+{
+ /* FIXME: generate the pseudo random id */
+ return 1;
+}
+
+struct dbus_sdp_record *dbus_sdp_record_new(const char *owner, bdaddr_t *provider,
+ const char *name, uint32_t uuid, uint8_t channel)
+{
+ struct dbus_sdp_record *rec;
+
+ /* FIXME: validate the arguments */
+
+ rec = malloc(sizeof(struct dbus_sdp_record));
+ if (!rec)
+ return NULL;
+
+ memset(rec, 0, sizeof(*rec));
+ if (owner) {
+ rec->owner = strdup(owner);
+ if (!rec->owner)
+ goto mem_fail;
+ }
+
+ bacpy(&rec->provider, provider);
+
+ rec->name = strdup(name);
+ if(!rec->name)
+ goto mem_fail;
+
+ rec->uuid = uuid;
+ rec->channel = channel;
+ rec->identifier = generate_new_id(provider, channel);
+
+ return rec;
+
+mem_fail:
+ dbus_sdp_record_free(rec);
+ return NULL;
+}
+
+static void owner_exited(const char *owner, struct hci_dbus_data *dbus_data)
+{
+ struct slist *cur, *next;
+
+ debug("SDP provider owner %s exited", owner);
+
+ for (cur = sdp_records; cur != NULL; cur = next) {
+ struct dbus_sdp_record *rec = cur->data;
+
+ next = cur->next;
+
+ if(!rec->owner)
+ continue;
+
+ if (strcmp(rec->owner, owner))
+ continue;
+
+ sdp_records = slist_remove(sdp_records, rec);
+ dbus_sdp_record_free(rec);
+ }
+}
+
+static int record_cmp(const struct dbus_sdp_record *a, const struct dbus_sdp_record *b)
+{
+ int ret;
+
+ if (b->owner) {
+ if (!a->owner)
+ return -1;
+ ret = strcmp(a->owner, b->owner);
+ if (ret)
+ return ret;
+ }
+
+ if (bacmp(&b->provider, BDADDR_ANY)) {
+ if (!bacmp(&a->provider, BDADDR_ANY))
+ return -1;
+ ret = bacmp(&a->provider, &b->provider);
+ if (ret)
+ return ret;
+ }
+
+ if (b->uuid) {
+ ret = (a->uuid - b->uuid);
+ if (ret)
+ return ret;
+ }
+
+ if (b->channel) {
+ ret = (a->channel - b->channel);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static gboolean sdp_client_connection_cb(GIOChannel *chan, GIOCondition cond, struct hci_dbus_data *dbus_data)
+{
+ debug("%s, line:%d condition:%d", __PRETTY_FUNCTION__, __LINE__, cond);
+ /* FIXME: send the request */
+ return FALSE;
+}
+
+int dbus_sdp_connect(struct hci_dbus_data *dbus_data, const bdaddr_t *sba,
+ const bdaddr_t *dba, uint32_t flags, int *err)
+{
+ struct sockaddr_l2 sa;
+ sdp_session_t *session = malloc(sizeof(sdp_session_t));
+ if (!session) {
+ if (err)
+ *err = ENOMEM;
+ return -1;
+ }
+
+ memset(session, 0, sizeof(*session));
+ session->flags = flags;
+
+ // create L2CAP connection
+ session->sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ session->local = 0;
+ if (session->sock >= 0) {
+ sa.l2_family = AF_BLUETOOTH;
+ sa.l2_psm = 0;
+ if (bacmp(sba, BDADDR_ANY) != 0) {
+ sa.l2_bdaddr = *sba;
+ if (bind(session->sock, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+ goto fail;
+ }
+ if (flags & SDP_WAIT_ON_CLOSE) {
+ struct linger l = { .l_onoff = 1, .l_linger = 1 };
+ setsockopt(session->sock, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
+ }
+ sa.l2_psm = htobs(SDP_PSM);
+ sa.l2_bdaddr = *dba;
+
+ debug("%s, line:%d connecting ...", __PRETTY_FUNCTION__, __LINE__);
+
+ dbus_data->search->io = g_io_channel_unix_new(session->sock);
+
+ fcntl(session->sock, F_SETFL, fcntl(session->sock, F_GETFL, 0)|O_NONBLOCK);
+ if (connect(session->sock, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+ if ( !(errno == EAGAIN || errno == EINPROGRESS)) {
+ error("connect() failed:%s (%d)", strerror(errno), errno);
+ goto fail;
+ }
+ g_io_add_watch(dbus_data->search->io, G_IO_OUT,
+ (GIOFunc)sdp_client_connection_cb, dbus_data);
+ } else {
+ debug("Connect completed in the first attempt");
+ sdp_client_connection_cb(dbus_data->search->io, G_IO_OUT, dbus_data);
+ }
+
+ dbus_data->search->session = session;
+ return 0;
+ }
+fail:
+ if (err)
+ *err = errno;
+
+ if (session->sock >= 0)
+ close(session->sock);
+ free(session);
+ errno = *err;
+
+ return -1;
+}
+
+static DBusHandlerResult get_identifiers(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ char filename[PATH_MAX + 1];
+ struct hci_dbus_data *dbus_data = data;
+ struct dbus_sdp_record *rec;
+ struct slist *l;
+ const char *peer;
+ char *str;
+ DBusMessage *reply;
+ DBusMessageIter iter, array_iter;
+ DBusError err;
+ bdaddr_t sba, dba;
+ uint32_t flags = 0;
+ int conn_err, found = 0;
+
+ dbus_error_init(&err);
+
+ dbus_message_get_args(msg, &err,
+ DBUS_TYPE_STRING, &peer,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_error_is_set(&err)) {
+ error("Can't extract message arguments:%s", err.message);
+ dbus_error_free(&err);
+ return error_invalid_arguments(conn, msg);
+ }
+
+ str2ba(peer, &dba);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ /* check the cache */
+ dbus_message_iter_init_append(reply, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &array_iter);
+
+ for (l = sdp_records; l; l = l->next) {
+ char id_str[9];
+ char *id_ptr = id_str;
+
+ rec = l->data;
+ if (bacmp(&rec->provider, &dba))
+ continue;
+ id2str(rec->identifier, id_ptr);
+ dbus_message_iter_append_basic(&array_iter,
+ DBUS_TYPE_STRING, &id_ptr);
+ found = 1;
+ }
+
+ dbus_message_iter_close_container(&iter, &array_iter);
+
+ if (found)
+ return send_reply_and_unref(conn, reply);
+
+ dbus_message_unref(reply);
+
+ if (dbus_data->search)
+ return error_service_search_in_progress(conn, msg);
+
+ /* check if it is a unknown address */
+ snprintf(filename, PATH_MAX, "%s/%s/lastseen", STORAGEDIR, dbus_data->address);
+
+ str = textfile_get(filename, peer);
+ if (!str)
+ return error_unknown_address(conn, msg);
+
+ free(str);
+
+ /* FIXME: if found, when it is invalid/expired? */
+
+ /* FIXME: check if there is an active connection */
+
+ /* Check if there is an inquiry/bonding in progress */
+
+ /* Background search */
+ dbus_data->search = search_request_info_new(&dba, msg);
+ if (!dbus_data->search)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ hci_devba(dbus_data->dev_id, &sba);
+
+ if (dbus_sdp_connect(dbus_data, &sba, &dba, flags, &conn_err) < 0) {
+ search_request_info_free(dbus_data->search);
+ dbus_data->search = NULL;
+ return error_failed(conn, msg, conn_err);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult get_identifiers_by_service(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult get_uuid(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult get_name(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static DBusHandlerResult register_rfcomm(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct hci_dbus_data *dbus_data = data;
+ struct dbus_sdp_record *rec, ref;
+ DBusMessage *reply;
+ DBusError err;
+ const char *owner, *name;
+ bdaddr_t provider;
+ char id_str[9];
+ char *id_ptr = id_str;
+ uint8_t channel;
+
+ owner = dbus_message_get_sender(msg);
+
+ dbus_error_init(&err);
+ dbus_message_get_args(msg, &err,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_BYTE, &channel,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_error_is_set(&err)) {
+ error("Can't extract message arguments:%s", err.message);
+ dbus_error_free(&err);
+ return error_invalid_arguments(conn, msg);
+ }
+
+ hci_devba(dbus_data->dev_id, &provider);
+
+ rec = dbus_sdp_record_new(owner, &provider, name, RFCOMM_UUID, channel);
+ if (!rec)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ if (slist_find(sdp_records, &rec, (cmp_func_t)record_cmp)) {
+ dbus_sdp_record_free(rec);
+ return error_service_already_exists(conn, msg);
+ }
+
+ id2str(rec->identifier, id_ptr);
+ reply = dbus_message_new_method_return(msg);
+ dbus_message_append_args(msg,
+ DBUS_TYPE_STRING, &id_ptr,
+ DBUS_TYPE_INVALID);
+ if (!reply) {
+ dbus_sdp_record_free(rec);
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ /* FIXME: register the service */
+
+ /* Only add a D-Bus unique name listener if there isn't one already registered */
+ memset(&ref, 0, sizeof(ref));
+ bacpy(&ref.provider, BDADDR_ANY);
+
+ if (!slist_find(sdp_records, &ref, (cmp_func_t)record_cmp))
+ name_listener_add(conn, rec->owner, (name_cb_t)owner_exited, dbus_data);
+
+ sdp_records = slist_append(sdp_records, rec);
+
+ return send_reply_and_unref(conn, reply);
+}
+
+static DBusHandlerResult unregister_rfcomm(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct hci_dbus_data *dbus_data = data;
+ struct dbus_sdp_record *rec, ref;
+ struct slist *match;
+ DBusMessage *reply;
+ DBusError err;
+ const char *owner, *identifier;
+
+ owner = dbus_message_get_sender(msg);
+
+ dbus_error_init(&err);
+ dbus_message_get_args(msg, &err,
+ DBUS_TYPE_STRING, &identifier,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_error_is_set(&err)) {
+ error("Can't extract message arguments:%s", err.message);
+ dbus_error_free(&err);
+ return error_invalid_arguments(conn, msg);
+ }
+
+ memset(&ref, 0, sizeof(ref));
+
+ ref.uuid = RFCOMM_UUID;
+ ref.identifier = str2id(identifier);
+ hci_devba(dbus_data->dev_id, &ref.provider);
+
+ match = slist_find(sdp_records, &ref, (cmp_func_t)record_cmp);
+ if (!match)
+ return error_service_does_not_exist(conn, msg);
+
+ rec = match->data;
+
+ if (strcmp(rec->owner, owner))
+ return error_not_authorized(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ /* FIXME: unregister the service */
+
+ sdp_records = slist_remove(sdp_records, rec);
+ dbus_sdp_record_free(rec);
+
+ bacpy(&ref.provider, BDADDR_ANY);
+ ref.uuid = 0x0000;
+
+ /* Only remove the D-Bus unique name listener if there are no more record using this name */
+ if (!slist_find(sdp_records, &ref, (cmp_func_t)record_cmp))
+ name_listener_remove(conn, ref.name, (name_cb_t)owner_exited, dbus_data);
+
+ return send_reply_and_unref(conn, reply);
+}
+
+static struct service_data sdp_services[] = {
+ { "GetIdentifiers", get_identifiers },
+ { "GetIdentifiersByService", get_identifiers_by_service },
+ { "GetUUID", get_uuid },
+ { "GetName", get_name },
+ { "RegisterRFCOMM", register_rfcomm },
+ { "UnregisterRFCOMM", unregister_rfcomm },
+ { NULL, NULL }
+};
+
+DBusHandlerResult handle_sdp_method(DBusConnection *conn, DBusMessage *msg, void *data)
+{
+ service_handler_func_t handler;
+
+ handler = find_service_handler(sdp_services, msg);
+
+ if (handler)
+ return handler(conn, msg, data);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
diff --git a/hcid/dbus.h b/hcid/dbus.h
index da6eeba7..aab6f354 100644
--- a/hcid/dbus.h
+++ b/hcid/dbus.h
@@ -27,7 +27,9 @@
#include <stdint.h>
#include <dbus/dbus.h>
#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
#include "list.h"
+#include "glib-ectomy.h"
#define BASE_PATH "/org/bluez"
#define BASE_INTERFACE "org.bluez"
@@ -44,6 +46,8 @@
#define RFCOMM_INTERFACE BASE_INTERFACE ".RFCOMM"
+#define SDP_INTERFACE BASE_INTERFACE ".SDP"
+
#define MANAGER_PATH_MASK (1 << 15)
#define ADAPTER_PATH_MASK (1 << 14)
@@ -97,6 +101,13 @@ struct bonding_request_info {
int disconnect; /* disconnect after finish */
};
+struct search_request_info {
+ bdaddr_t bdaddr;
+ DBusMessage *rq;
+ GIOChannel *io;
+ sdp_session_t *session;
+};
+
struct active_conn_info {
bdaddr_t bdaddr;
uint16_t handle;
@@ -115,6 +126,7 @@ struct hci_dbus_data {
char *requestor_name; /* requestor unique name */
struct slist *passkey_agents;
struct bonding_request_info *bonding;
+ struct search_request_info *search;
struct slist *active_conn;
int pairing_active;
};
@@ -166,6 +178,9 @@ DBusHandlerResult error_record_does_not_exist(DBusConnection *conn, DBusMessage
DBusHandlerResult error_passkey_agent_already_exists(DBusConnection *conn, DBusMessage *msg);
DBusHandlerResult error_passkey_agent_does_not_exist(DBusConnection *conn, DBusMessage *msg);
DBusHandlerResult error_binding_does_not_exist(DBusConnection *conn, DBusMessage *msg);
+DBusHandlerResult error_service_already_exists(DBusConnection *conn, DBusMessage *msg);
+DBusHandlerResult error_service_does_not_exist(DBusConnection *conn, DBusMessage *msg);
+DBusHandlerResult error_service_search_in_progress(DBusConnection *conn, DBusMessage *msg);
DBusHandlerResult error_connect_canceled(DBusConnection *conn, DBusMessage *msg);
typedef void (*name_cb_t)(const char *name, void *user_data);
@@ -179,6 +194,8 @@ DBusHandlerResult handle_security_method(DBusConnection *conn, DBusMessage *msg,
DBusHandlerResult handle_rfcomm_method(DBusConnection *conn, DBusMessage *msg, void *data);
+DBusHandlerResult handle_sdp_method(DBusConnection *conn, DBusMessage *msg, void *data);
+
service_handler_func_t find_service_handler(struct service_data *services, DBusMessage *msg);
int handle_passkey_request(int dev, const char *path, bdaddr_t *sba, bdaddr_t *dba);