From 684ba3688de17a3b8a6defd3139e1b8bf8971ef5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 23 Aug 2006 14:33:58 +0000 Subject: Add preliminary RFCOMM.Connect and RFCOMM.CancelConnect support --- hcid/dbus-api.txt | 4 +- hcid/dbus-rfcomm.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++++-- hcid/dbus-sdp.c | 71 ++++++++++++++++++++++++++++++ hcid/dbus.h | 4 ++ 4 files changed, 199 insertions(+), 6 deletions(-) diff --git a/hcid/dbus-api.txt b/hcid/dbus-api.txt index 35d60264..825314a1 100644 --- a/hcid/dbus-api.txt +++ b/hcid/dbus-api.txt @@ -893,8 +893,8 @@ Object path /org/bluez/{hci0,hci1,...} Methods string Connect(string address, string service) This creates a connection to a remote RFCOMM based - service. The service string can either be a UUID-16, - a UUID-32, a UUID-128 or a service abbreviation. + service. The service string can either be a UUID-128, + a service abbreviation or a record handle. The return value will be the path of the newly created RFCOMM TTY device (for example /dev/rfcomm0). diff --git a/hcid/dbus-rfcomm.c b/hcid/dbus-rfcomm.c index f4215f92..61a0365f 100644 --- a/hcid/dbus-rfcomm.c +++ b/hcid/dbus-rfcomm.c @@ -33,11 +33,15 @@ #include #include #include +#include #include #include #include #include +#include +#include + #include @@ -452,18 +456,132 @@ static struct rfcomm_node *rfcomm_bind(bdaddr_t *src, const char *bda, uint8_t c return node; } +static sdp_record_t *get_record_from_string (const char *dst, + const char *string) +{ + uuid_t short_uuid; + uuid_t *uuid; + sdp_record_t *rec = NULL; + unsigned int data0, data4; + unsigned short data1, data2, data3, data5; + long handle; + + /* Check if the string is a service name */ + short_uuid.value.uuid16 = sdp_str2svclass (string); + + if (short_uuid.value.uuid16) { + short_uuid.type = SDP_UUID16; + uuid = sdp_uuid_to_uuid128 (&short_uuid); + rec = find_record_by_uuid (dst, uuid); + } else if (sscanf (string, "%8x-%4hx-%4hx-%4hx-%8x%4hx", &data0, + &data1, &data2, &data3, &data4, &data5) == 6) { + data0 = htonl(data0); + data1 = htons(data1); + data2 = htons(data2); + data3 = htons(data3); + data4 = htonl(data4); + data5 = htons(data5); + + uuid = malloc (sizeof(uuid_t)); + uuid->type = SDP_UUID128; + memcpy (&uuid->value.uuid128.data[0], &data0, 4); + memcpy (&uuid->value.uuid128.data[4], &data1, 2); + memcpy (&uuid->value.uuid128.data[6], &data2, 2); + memcpy (&uuid->value.uuid128.data[8], &data3, 2); + memcpy (&uuid->value.uuid128.data[10], &data4, 4); + memcpy (&uuid->value.uuid128.data[14], &data5, 2); + + rec = find_record_by_uuid (dst, uuid); + } else if ((handle = strtol (string, (char **)NULL, 0))) { + rec = find_record_by_handle (dst, handle); + } + + return rec; +} + static DBusHandlerResult rfcomm_connect_req(DBusConnection *conn, DBusMessage *msg, void *data) { - error("RFCOMM.Connect not implemented"); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + int ch = -1; + const char *dst; + const char *string; + sdp_record_t *rec; + sdp_list_t *protos; + bdaddr_t bdaddr; + struct hci_dbus_data *dbus_data = data; + int err; + + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &dst, + DBUS_TYPE_STRING, &string, + DBUS_TYPE_INVALID)) + return error_invalid_arguments(conn, msg); + + hci_devba(dbus_data->dev_id, &bdaddr); + + rec = get_record_from_string (dst, string); + + if (!rec) + return error_record_does_not_exist (conn, msg); + + if (sdp_get_access_protos(rec, &protos) == 0) + ch = sdp_get_proto_port (protos, RFCOMM_UUID); + + if (ch == -1) + return error_record_does_not_exist (conn, msg); + + if (find_pending_connect(dst, ch)) + return error_connect_in_progress(conn, msg); + + if (rfcomm_connect(conn, msg, &bdaddr, dst, NULL, ch, &err) < 0) + return error_failed(conn, msg, err); + + return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult rfcomm_cancel_connect_req(DBusConnection *conn, DBusMessage *msg, void *data) { - error("RFCOMM.CancelConnect not implemented"); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + int ch = -1; + const char *dst; + const char *string; + sdp_record_t *rec; + sdp_list_t *protos; + bdaddr_t bdaddr; + struct hci_dbus_data *dbus_data = data; + DBusMessage *reply; + struct pending_connect *pending; + + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &dst, + DBUS_TYPE_STRING, &string, + DBUS_TYPE_INVALID)) + return error_invalid_arguments(conn, msg); + + hci_devba(dbus_data->dev_id, &bdaddr); + + rec = get_record_from_string (dst, string); + + if (!rec) + return error_record_does_not_exist (conn, msg); + + if (sdp_get_access_protos(rec, &protos) == 0) + ch = sdp_get_proto_port (protos, RFCOMM_UUID); + + if (ch == -1) + return error_record_does_not_exist (conn, msg); + + reply = dbus_message_new_method_return (msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + + pending = find_pending_connect(dst, ch); + if (!pending) + return error_connect_not_in_progress(conn, msg); + + pending->canceled = 1; + + return send_reply_and_unref(conn, reply); } static DBusHandlerResult rfcomm_connect_by_ch_req(DBusConnection *conn, diff --git a/hcid/dbus-sdp.c b/hcid/dbus-sdp.c index cfc4f4f2..2634a5ea 100644 --- a/hcid/dbus-sdp.c +++ b/hcid/dbus-sdp.c @@ -833,6 +833,77 @@ search_request: } +static int sdp_uuid_comp_func(const void *key1, const void *key2) +{ + const uuid_t *a = (const uuid_t *)key1; + const uuid_t *b = (const uuid_t *)key2; + + if (a->type != b->type) + return 1; + + switch (a->type) { + case SDP_UUID16: + return !(a->value.uuid16 == b->value.uuid16); + break; + case SDP_UUID32: + return !(a->value.uuid32 == b->value.uuid32); + break; + case SDP_UUID128: + return !memcmp(&a->value.uuid128, &b->value.uuid128, + sizeof(uint128_t)); + break; + } + return 1; +} + +sdp_record_t *find_record_by_uuid(const char *address, uuid_t *uuid) +{ + struct slist *lp, *lr; + struct service_provider *p; + struct service_record *r; + sdp_list_t *list = 0; + + + for (lp = sdp_cache; lp; lp = lp->next) { + p = lp->data; + if (strcmp(p->prov, address)) + continue; + + for (lr = p->lrec; lr; lr = lr->next) { + r = lr->data; + /* Check whether the record has the correct uuid */ + if (sdp_get_service_classes(r->record, &list) !=0) + continue; + + if (sdp_list_find (list, &uuid, sdp_uuid_comp_func)) + return r->record; + } + } + + return NULL; +} + +sdp_record_t *find_record_by_handle(const char *address, int handle) +{ + struct slist *lp, *lr; + struct service_provider *p; + struct service_record *r; + + for (lp = sdp_cache; lp; lp = lp->next) { + p = lp->data; + if (strcmp(p->prov, address)) + continue; + + for (lr = p->lrec; lr; lr = lr->next) { + r = lr->data; + if (r->record->handle == handle) + return r->record; + } + } + + return NULL; +} + static sdp_record_t *find_record(const char *address, int id) { struct slist *lp, *lr; diff --git a/hcid/dbus.h b/hcid/dbus.h index f33ebc3a..0e0e95bb 100644 --- a/hcid/dbus.h +++ b/hcid/dbus.h @@ -219,4 +219,8 @@ int disc_device_req_name(struct hci_dbus_data *dbus_data); int discoverable_timeout_handler(void *data); +sdp_record_t *find_record_by_uuid(const char *address, uuid_t *uuid); +sdp_record_t *find_record_by_handle(const char *address, int handle); +uint16_t sdp_str2svclass(const char *str); + #endif /* __H_BLUEZ_DBUS_H__ */ -- cgit