diff options
| author | Johan Hedberg <johan.hedberg@nokia.com> | 2006-03-15 22:01:31 +0000 | 
|---|---|---|
| committer | Johan Hedberg <johan.hedberg@nokia.com> | 2006-03-15 22:01:31 +0000 | 
| commit | 585bc7db19e7d4aac3f22607df3f7a1b20fbe506 (patch) | |
| tree | 0624d685f90bd81a5582c56071715ed9dfa1b1ee | |
| parent | d6a16516a9f6deae8342f00e8186b02d0019a1e1 (diff) | |
First round of RFCOMM D-Bus API implementation
| -rw-r--r-- | hcid/dbus-error.c | 5 | ||||
| -rw-r--r-- | hcid/dbus-rfcomm.c | 297 | ||||
| -rw-r--r-- | hcid/dbus.h | 1 | 
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); | 
