summaryrefslogtreecommitdiffstats
path: root/hcid
diff options
context:
space:
mode:
Diffstat (limited to 'hcid')
-rw-r--r--hcid/dbus-error.c5
-rw-r--r--hcid/dbus-rfcomm.c297
-rw-r--r--hcid/dbus.h1
3 files changed, 303 insertions, 0 deletions
diff --git a/hcid/dbus-error.c b/hcid/dbus-error.c
index 412468a1..28cc9328 100644
--- a/hcid/dbus-error.c
+++ b/hcid/dbus-error.c
@@ -156,3 +156,8 @@ DBusHandlerResult error_passkey_agent_does_not_exist(DBusConnection *conn, DBusM
{
return error_does_not_exist(conn, msg, "Passkey agent does not exist");
}
+
+DBusHandlerResult error_binding_does_not_exist(DBusConnection *conn, DBusMessage *msg)
+{
+ return error_does_not_exist(conn, msg, "Binding does not exist");
+}
diff --git a/hcid/dbus-rfcomm.c b/hcid/dbus-rfcomm.c
index 700649e6..e51bdd5f 100644
--- a/hcid/dbus-rfcomm.c
+++ b/hcid/dbus-rfcomm.c
@@ -27,13 +27,310 @@
#include <stdio.h>
#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
#include <dbus/dbus.h>
+#include "hcid.h"
+#include "list.h"
+#include "glib-ectomy.h"
#include "dbus.h"
+static int rfcomm_ctl = -1;
+
+struct rfcomm_node {
+ int16_t id; /* Device id */
+ char name[16]; /* Node filename */
+
+ /* The following members are only valid for connected nodes */
+ GIOChannel *io; /* IO Channel for the connection */
+ char *owner; /* D-Bus name that created the node */
+ int canceled; /* User canceled the connection */
+};
+
+static struct slist *bound_nodes = NULL;
+
+static char *rfcomm_node_name_from_id(int16_t id, char *dev, size_t len)
+{
+ snprintf(dev, len, "/dev/rfcomm%d", id);
+ return dev;
+}
+
+static struct rfcomm_node *find_node_by_name(struct slist *nodes, const char *name)
+{
+ struct slist *l;
+
+ for (l = nodes; l != NULL; l = l->next) {
+ struct rfcomm_node *node = l->data;
+ if (!strcmp(node->name, name))
+ return node;
+ }
+
+ return NULL;
+}
+
+static int rfcomm_release(struct rfcomm_node *node, int *err)
+{
+ struct rfcomm_dev_req req;
+
+ debug("rfcomm_release(%s)", node->name);
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = node->id;
+
+ if (ioctl(rfcomm_ctl, RFCOMMRELEASEDEV, &req) < 0) {
+ if (err)
+ *err = errno;
+ error("Can't release device %d: %s (%d)", node->id,
+ strerror(errno), errno);
+ return -1;
+ }
+
+ bound_nodes = slist_remove(bound_nodes, node);
+
+ free(node);
+
+ return 0;
+}
+
+static struct rfcomm_node *rfcomm_bind(bdaddr_t *src, const char *bda, uint8_t ch, int *err)
+{
+ struct rfcomm_dev_req req;
+ struct rfcomm_node *node;
+
+ debug("rfcomm_bind(%s, %d)", bda, ch);
+
+ memset(&req, 0, sizeof(req));
+ req.dev_id = -1;
+ req.flags = 0;
+ bacpy(&req.src, src);
+
+ str2ba(bda, &req.dst);
+ req.channel = ch;
+
+ node = malloc(sizeof(struct rfcomm_node));
+ if (!node) {
+ if (err)
+ *err = ENOMEM;
+ return NULL;
+ }
+
+ memset(node, 0, sizeof(struct rfcomm_node));
+
+ node->id = ioctl(rfcomm_ctl, RFCOMMCREATEDEV, &req);
+ if (node->id < 0) {
+ if (err)
+ *err = errno;
+ error("RFCOMMCREATEDEV failed: %s (%d)", strerror(errno), errno);
+ return NULL;
+ }
+
+ rfcomm_node_name_from_id(node->id, node->name, sizeof(node->name));
+ bound_nodes = slist_append(bound_nodes, node);
+
+ return node;
+}
+
+
+static DBusHandlerResult rfcomm_connect_req(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult rfcomm_cancel_connect_req(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult rfcomm_connect_by_ch_req(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult rfcomm_cancel_connect_by_ch_req(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult rfcomm_disconnect_req(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult rfcomm_bind_req(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult rfcomm_bind_by_ch_req(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ bdaddr_t bdaddr;
+ DBusMessage *reply = NULL;
+ uint8_t ch;
+ int err;
+ const char *dst, *name;
+ struct hci_dbus_data *dbus_data = data;
+ struct rfcomm_node *node = NULL;
+
+ hci_devba(dbus_data->dev_id, &bdaddr);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &dst,
+ DBUS_TYPE_BYTE, &ch,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+
+ node = rfcomm_bind(&bdaddr, dst, ch, &err);
+ if (!node)
+ return error_failed(conn, msg, err);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ goto need_memory;
+
+ name = node->name;
+ if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ goto need_memory;
+
+ return send_reply_and_unref(conn, reply);
+
+need_memory:
+ if (reply)
+ dbus_message_unref(reply);
+ if (node)
+ rfcomm_release(node, NULL);
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
+static DBusHandlerResult rfcomm_release_req(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ const char *name;
+ struct rfcomm_node *node;
+ int err;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ return error_invalid_arguments(conn, msg);
+
+ node = find_node_by_name(bound_nodes, name);
+ if (!node)
+ return error_binding_does_not_exist(conn, msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ if (rfcomm_release(node, &err) < 0) {
+ dbus_message_unref(reply);
+ return error_failed(conn, msg, err);
+ }
+
+ return send_reply_and_unref(conn, reply);
+}
+
+static DBusHandlerResult rfcomm_list_bindings_req(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ bdaddr_t bdaddr;
+ DBusMessage *reply;
+ DBusMessageIter iter, sub;
+ struct hci_dbus_data *dbus_data = data;
+ struct slist *l;
+
+ hci_devba(dbus_data->dev_id, &bdaddr);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
+ dbus_message_unref(reply);
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ for (l = bound_nodes; l != NULL; l = l->next) {
+ struct rfcomm_node *node = l->data;
+ struct rfcomm_dev_info di = { id: node->id };
+ char *name = node->name;
+
+ if (ioctl(rfcomm_ctl, RFCOMMGETDEVINFO, &di) < 0) {
+ error("RFCOMMGETDEVINFO(%d): %s (%d)",
+ node->id, strerror(errno), errno);
+ continue;
+ }
+
+ /* Ignore nodes not specific to this adapter */
+ if (bacmp(&di.src, &bdaddr))
+ continue;
+
+ dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &name);
+ }
+
+ if (!dbus_message_iter_close_container(&iter, &sub)) {
+ dbus_message_unref(reply);
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ return send_reply_and_unref(conn, reply);
+}
+
+static struct service_data rfcomm_services[] = {
+ { "Connect", rfcomm_connect_req, },
+ { "CancelConnect", rfcomm_cancel_connect_req, },
+ { "ConnectByChannel", rfcomm_connect_by_ch_req, },
+ { "CancelConnectByChannel", rfcomm_cancel_connect_by_ch_req, },
+ { "Disconnect", rfcomm_disconnect_req, },
+ { "Bind", rfcomm_bind_req, },
+ { "BindByChannel", rfcomm_bind_by_ch_req, },
+ { "Release", rfcomm_release_req, },
+ { "ListBindings", rfcomm_list_bindings_req, },
+ { NULL, NULL, }
+};
+
DBusHandlerResult handle_rfcomm_method(DBusConnection *conn, DBusMessage *msg,
void *data)
{
+ service_handler_func_t handler;
+
+ if (!data) {
+ error("RFCOMM method called with NULL data pointer!");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ /* Initialize the RFCOMM control socket if has not yet been done */
+ if (rfcomm_ctl < 0) {
+ rfcomm_ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM);
+ if (rfcomm_ctl < 0)
+ return error_failed(conn, msg, errno);
+ }
+
+ handler = find_service_handler(rfcomm_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 ec2efd93..16fe2ab3 100644
--- a/hcid/dbus.h
+++ b/hcid/dbus.h
@@ -139,6 +139,7 @@ DBusHandlerResult error_discover_in_progress(DBusConnection *conn, DBusMessage *
DBusHandlerResult error_record_does_not_exist(DBusConnection *conn, DBusMessage *msg);
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);
typedef void (*name_cb_t)(const char *name, void *user_data);