diff options
| -rw-r--r-- | serial/main.c | 105 | ||||
| -rw-r--r-- | serial/manager.c | 921 | ||||
| -rw-r--r-- | serial/port.c | 565 | ||||
| -rw-r--r-- | serial/port.h | 12 | 
4 files changed, 409 insertions, 1194 deletions
| diff --git a/serial/main.c b/serial/main.c index 40c49a7a..97b26925 100644 --- a/serial/main.c +++ b/serial/main.c @@ -28,128 +28,29 @@  #include <errno.h>  #include <sys/types.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/sdp.h> -  #include <gdbus.h>  #include "plugin.h" -#include "device.h" -#include "adapter.h"  #include "logging.h"  #include "manager.h" -#include "port.h" - -#define SERIAL_PORT_UUID	"00001101-0000-1000-8000-00805F9B34FB" -#define DIALUP_NET_UUID		"00001103-0000-1000-8000-00805F9B34FB" - -#define SERIAL_INTERFACE     "org.bluez.Serial" -#define ERROR_INVALID_ARGS   "org.bluez.Error.InvalidArguments" -#define ERROR_DOES_NOT_EXIST "org.bluez.Error.DoesNotExist" - -static DBusMessage *serial_connect(DBusConnection *conn, -					DBusMessage *msg, void *user_data) -{ -	struct btd_device *device = user_data; -	struct adapter *adapter = device_get_adapter(device); -	const char *target; -	const gchar *src, *dst; - -	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &target, -						DBUS_TYPE_INVALID) == FALSE) -		return NULL; - -	src = device_get_address(device); -	dst = adapter_get_address(adapter); - -	service_connect(conn, msg, src, dst, target); - -	return NULL; -} - -static DBusMessage *serial_disconnect(DBusConnection *conn, -					DBusMessage *msg, void *user_data) -{ -	const char *device, *sender; -	int err, id; - -	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &device, -						DBUS_TYPE_INVALID) == FALSE) -		return NULL; - -	sender = dbus_message_get_sender(msg); - -	if (sscanf(device, "/dev/rfcomm%d", &id) != 1) -		return g_dbus_create_error(msg, ERROR_INVALID_ARGS, NULL); - -	err = port_remove_listener(sender, device); -	if (err < 0) -		return g_dbus_create_error(msg, ERROR_DOES_NOT_EXIST, NULL); - -	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); -} - -static GDBusMethodTable serial_methods[] = { -	{ "Connect",    "s", "s", serial_connect, G_DBUS_METHOD_FLAG_ASYNC }, -	{ "Disconnect", "s", "",  serial_disconnect }, -	{ } -}; - -static DBusConnection *conn; - -static int serial_probe(struct btd_device_driver *driver, -			struct btd_device *device, GSList *records) -{ -	const gchar *path = device_get_path(device); -	DBG("path %s", path); - -	if (g_dbus_register_interface(conn, path, SERIAL_INTERFACE, -						serial_methods, NULL, NULL, -							device, NULL) == FALSE) -		return -1; - -	return 0; -} - -static void serial_remove(struct btd_device_driver *driver, -				struct btd_device *device) -{ -	const gchar *path = device_get_path(device); -	DBG("path %s", path); - -	g_dbus_unregister_interface(conn, path, SERIAL_INTERFACE); -} - -static struct btd_device_driver serial_driver = { -	.name	= "serial", -	.uuids	= BTD_UUIDS(SERIAL_PORT_UUID, DIALUP_NET_UUID), -	.probe	= serial_probe, -	.remove	= serial_remove, -};  static int serial_init(void)  { +	DBusConnection *conn; +  	conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);  	if (conn == NULL)  		return -EIO; -	if (serial_manager_init(conn) < 0) { -		dbus_connection_unref(conn); +	if (serial_manager_init(conn) < 0)  		return -EIO; -	} - -	btd_register_device_driver(&serial_driver);  	return 0;  }  static void serial_exit(void)  { -	btd_unregister_device_driver(&serial_driver); -  	serial_manager_exit(); - -	dbus_connection_unref(conn);  }  BLUETOOTH_PLUGIN_DEFINE("serial", serial_init, serial_exit) diff --git a/serial/manager.c b/serial/manager.c index 442c386e..06445dcb 100644 --- a/serial/manager.c +++ b/serial/manager.c @@ -52,6 +52,8 @@  #include <gdbus.h>  #include "../hcid/dbus-common.h" +#include "adapter.h" +#include "device.h"  #include "logging.h"  #include "textfile.h" @@ -63,45 +65,14 @@  #include "sdpd.h"  #include "glib-helper.h" -#define SERIAL_PROXY_INTERFACE		"org.bluez.serial.Proxy" -#define BUF_SIZE			1024 - -/* Waiting for udev to create the device node */ -#define MAX_OPEN_TRIES		5 -#define OPEN_WAIT		300	/* ms */ - -struct pending_connect { -	DBusConnection	*conn; -	DBusMessage	*msg; -	char		*adapter;	/* Adapter address */ -	char		*address;	/* Destination address  */ -	char		*pattern;	/* Connection request pattern */ -	uint8_t		channel; -	char		*dev;		/* tty device name */ -	int		id;		/* RFCOMM device id */ -	int		ntries;		/* Open attempts */ -	int		canceled;	/* Operation canceled */ -	guint		listener_id; -}; +#define SERIAL_PORT_NAME	"spp" +#define SERIAL_PORT_UUID	"00001101-0000-1000-8000-00805F9B34FB" -/* FIXME: Common file required */ -static struct { -	const char	*name; -	uint16_t	class; -} serial_services[] = { -	{ "vcp",	VIDEO_CONF_SVCLASS_ID		}, -	{ "pbap",	PBAP_SVCLASS_ID			}, -	{ "sap",	SAP_SVCLASS_ID			}, -	{ "ftp",	OBEX_FILETRANS_SVCLASS_ID	}, -	{ "bpp",	BASIC_PRINTING_SVCLASS_ID	}, -	{ "bip",	IMAGING_SVCLASS_ID		}, -	{ "synch",	IRMC_SYNC_SVCLASS_ID		}, -	{ "dun",	DIALUP_NET_SVCLASS_ID		}, -	{ "opp",	OBEX_OBJPUSH_SVCLASS_ID		}, -	{ "fax",	FAX_SVCLASS_ID			}, -	{ "spp",	SERIAL_PORT_SVCLASS_ID		}, -	{ NULL } -}; +#define DIALUP_NET_NAME		"dun" +#define DIALUP_NET_UUID		"00001103-0000-1000-8000-00805F9B34FB" + +#define SERIAL_PROXY_INTERFACE	"org.bluez.serial.Proxy" +#define BUF_SIZE		1024  typedef enum {  	TTY_PROXY, @@ -128,10 +99,7 @@ struct proxy {  };  static DBusConnection *connection = NULL; -static GSList *pending_connects = NULL; -static GSList *ports_paths = NULL;  static GSList *proxies = NULL; -static int rfcomm_ctl = -1;  static int sk_counter = 0;  static void disable_proxy(struct proxy *prx) @@ -160,251 +128,6 @@ static void proxy_free(struct proxy *prx)  	g_free(prx);  } -static void pending_connect_free(struct pending_connect *pc) -{ -	if (pc->conn) -		dbus_connection_unref(pc->conn); -	if (pc->msg) -		dbus_message_unref(pc->msg); -	if (pc->adapter) -		g_free(pc->adapter); -	if (pc->address) -		g_free(pc->address); -	if (pc->pattern) -		g_free(pc->pattern); -	if (pc->dev) -		g_free(pc->dev); -	g_free(pc); -} - -static struct pending_connect *find_pending_connect_by_pattern(const char *bda, -							const char *pattern) -{ -	GSList *l; - -	/* Pattern can be friendly name, uuid128, record handle or channel */ -	for (l = pending_connects; l != NULL; l = l->next) { -		struct pending_connect *pending = l->data; -		if (!strcasecmp(pending->address, bda) && -				!strcasecmp(pending->pattern, pattern)) -			return pending; -	} - -	return NULL; -} - -static void transaction_owner_exited(void *data) -{ -	struct pending_connect *pc = data; - -	debug("transaction owner exited"); - -	if (pc->id >= 0) -		rfcomm_release(pc->id); - -	pending_connects = g_slist_remove(pending_connects, pc); - -	pending_connect_free(pc); -} - -static void pending_connect_remove(struct pending_connect *pc) -{ -	g_dbus_remove_watch(pc->conn, pc->listener_id); -	pending_connects = g_slist_remove(pending_connects, pc); -	pending_connect_free(pc); -} - -static void open_notify(int fd, int err, struct pending_connect *pc) -{ -	DBusMessage *reply; -	bdaddr_t dst; - -	if (err) { -		/* Max tries exceeded */ -		rfcomm_release(pc->id); -		error_connection_attempt_failed(pc->conn, pc->msg, err); -		return; -	} - -	if (pc->canceled) { -		rfcomm_release(pc->id); -		error_canceled(pc->conn, pc->msg, "Connection canceled"); -		return; -	} - -	/* Reply to the requestor */ -	reply = dbus_message_new_method_return(pc->msg); -	dbus_message_append_args(reply, -			DBUS_TYPE_STRING, &pc->dev, -			DBUS_TYPE_INVALID); -	dbus_connection_send(pc->conn, reply, NULL); -	dbus_message_unref(reply); - -	/* Send the D-Bus signal */ -	g_dbus_emit_signal(pc->conn, SERIAL_MANAGER_PATH, -			SERIAL_MANAGER_INTERFACE, "ServiceConnected" , -			DBUS_TYPE_STRING, &pc->dev, -			DBUS_TYPE_INVALID); - -	str2ba(pc->address, &dst); - -	/* Add the RFCOMM connection listener */ -	port_add_listener(pc->conn, pc->id, &dst, fd, -				pc->dev, dbus_message_get_sender(pc->msg)); -} - -static gboolean open_continue(struct pending_connect *pc) -{ -	int fd; - -	if (!g_slist_find(pending_connects, pc)) -		return FALSE; /* Owner exited */ - -	fd = open(pc->dev, O_RDONLY | O_NOCTTY); -	if (fd < 0) { -		int err = errno; -		error("Could not open %s: %s (%d)", -				pc->dev, strerror(err), err); -		if (++pc->ntries >= MAX_OPEN_TRIES) { -			/* Reporting error */ -			open_notify(fd, err, pc); -			pending_connect_remove(pc); -			return FALSE; -		} -		return TRUE; -	} -	/* Connection succeeded */ -	open_notify(fd, 0, pc); -	pending_connect_remove(pc); -	return FALSE; -} - -int port_open(struct pending_connect *pc) -{ -	int fd; - -	fd = open(pc->dev, O_RDONLY | O_NOCTTY); -	if (fd < 0) { -		g_timeout_add(OPEN_WAIT, (GSourceFunc) open_continue, pc); -		return -EINPROGRESS; -	} - -	return fd; -} - -static uint16_t str2class(const char *pattern) -{ -	int i; - -	for (i = 0; serial_services[i].name; i++) { -		if (strcasecmp(serial_services[i].name, pattern) == 0) -			return serial_services[i].class; -	} - -	return 0; -} - -int rfcomm_release(int16_t id) -{ -	struct rfcomm_dev_req req; - -	memset(&req, 0, sizeof(req)); -	req.dev_id = id; - -	/* -	 * We are hitting a kernel bug inside RFCOMM code when -	 * RFCOMM_HANGUP_NOW bit is set on request's flags passed to -	 * ioctl(RFCOMMRELEASEDEV)! -	 */ -	req.flags = (1 << RFCOMM_HANGUP_NOW); - -	if (ioctl(rfcomm_ctl, RFCOMMRELEASEDEV, &req) < 0) { -		int err = errno; -		error("Can't release device %d: %s (%d)", -				id, strerror(err), err); -		return -err; -	} - -	return 0; -} - -static int rfcomm_bind(bdaddr_t *src, bdaddr_t *dst, int16_t dev_id, uint8_t ch) -{ -	struct rfcomm_dev_req req; -	int id; - -	memset(&req, 0, sizeof(req)); -	req.dev_id = dev_id; -	req.flags = 0; -	bacpy(&req.src, src); -	bacpy(&req.dst, dst); -	req.channel = ch; - -	id = ioctl(rfcomm_ctl, RFCOMMCREATEDEV, &req); -	if (id < 0) { -		int err = errno; -		error("RFCOMMCREATEDEV failed: %s (%d)", strerror(err), err); -		return -err; -	} - -	return id; -} - -static void rfcomm_connect_cb(GIOChannel *chan, int err_cb, const bdaddr_t *src, -			const bdaddr_t *dst, gpointer user_data) -{ -	struct pending_connect *pc = user_data; -	struct rfcomm_dev_req req; -	int sk, err, fd; - -	/* Owner exited? */ -	if (!g_slist_find(pending_connects, pc)) -		return; - -	if (pc->canceled) { -		error_canceled(pc->conn, pc->msg, "Connection canceled"); -		goto fail; -	} - -	if (err_cb < 0) { -		error("connect(): %s (%d)", strerror(-err_cb), -err_cb); -		error_connection_attempt_failed(pc->conn, pc->msg, -err_cb); -		goto fail; -	} - -	debug("rfcomm_connect_cb: connected"); - -	memset(&req, 0, sizeof(req)); -	req.dev_id = -1; -	req.flags = (1 << RFCOMM_REUSE_DLC); -	str2ba(pc->adapter, &req.src); -	str2ba(pc->address, &req.dst); -	req.channel = pc->channel; - -	sk = g_io_channel_unix_get_fd(chan); -	pc->id = ioctl(sk, RFCOMMCREATEDEV, &req); -	g_io_channel_close(chan); -	g_io_channel_unref(chan); -	if (pc->id < 0) { -		err = errno; -		error("ioctl(RFCOMMCREATEDEV): %s (%d)", strerror(err), err); -		error_connection_attempt_failed(pc->conn, pc->msg, err); -		goto fail; -	} -	pc->dev	= g_new0(char, 16); -	snprintf(pc->dev, 16, "/dev/rfcomm%d", pc->id); - -	/* Addressing connect port */ -	fd = port_open(pc); -	if (fd < 0) -		/* Open in progress: Wait the callback */ -		return; - -	open_notify(fd, 0, pc); -fail: -	pending_connect_remove(pc); -} -  static inline DBusMessage *does_not_exist(DBusMessage *msg,  					const char *description)  { @@ -425,354 +148,6 @@ static inline DBusMessage *failed(DBusMessage *msg, const char *description)  				description);  } -static DBusMessage *create_channel_port(DBusConnection *conn, -				DBusMessage *msg, const char *adapter, -				const char *address, const char *name, -				long channel, void *data) -{ -	char path[MAX_PATH_LENGTH], port_name[16]; -	const char *ppath = path; -	DBusMessage *reply; -	int err; -	bdaddr_t src, dst; - -	if (channel < 1 || channel > 30) -		return invalid_arguments(msg, "Invalid RFCOMM channel"); - -	str2ba(adapter, &src); -	str2ba(address, &dst); -	err = rfcomm_bind(&src, &dst, -1, channel); -	if (err < 0) -		return failed(msg, strerror(-err)); - -	snprintf(port_name, sizeof(port_name), "/dev/rfcomm%d", err); -	port_store(&src, &dst, err, channel, name); -	port_register(conn, err, &src, &dst, port_name, path, name); -	ports_paths = g_slist_append(ports_paths, g_strdup(path)); - -	reply = dbus_message_new_method_return(msg); -	if (!reply) -		return NULL; - -	dbus_message_append_args(reply, -			DBUS_TYPE_STRING, &ppath, -			DBUS_TYPE_INVALID); -	dbus_connection_send(conn, reply, NULL); -	dbus_message_unref(reply); - -	g_dbus_emit_signal(conn, SERIAL_MANAGER_PATH, -			SERIAL_MANAGER_INTERFACE, "PortCreated" , -			DBUS_TYPE_STRING, &ppath, -			DBUS_TYPE_INVALID); - -	return NULL; -} - -static DBusMessage *connect_pending(DBusConnection *conn, DBusMessage *msg, -					struct pending_connect *pc) -{ -	int err; -	bdaddr_t src, dst; - -	if (!g_slist_find(pending_connects, pc)) { -		pending_connects = g_slist_append(pending_connects, pc); -		pc->listener_id = g_dbus_add_disconnect_watch(conn, -						dbus_message_get_sender(msg), -						transaction_owner_exited, pc, -						NULL); -	} - -	str2ba(pc->adapter, &src); -	str2ba(pc->address, &dst); - -	err = bt_rfcomm_connect(&src, &dst, pc->channel, rfcomm_connect_cb, pc); -	if (err < 0) { -		const char *strerr = strerror(-err); -		error("RFCOMM connect failed: %s(%d)", strerr, -err); -		pending_connects = g_slist_remove(pending_connects, pc); -		pending_connect_free(pc); -		return g_dbus_create_error(msg, ERROR_INTERFACE -						".ConnectionAttemptFailed", -						"%s", strerror(-err)); -	} - -	return NULL; -} - -static void record_cb(sdp_list_t *recs, int err, gpointer data) -{ -	struct pending_connect *pc; -	sdp_record_t *rec = NULL; -	sdp_list_t *protos; -	int ch; -	bdaddr_t dst; - -	/* Owner exited? */ -	if (!g_slist_find(pending_connects, data)) -		return; - -	pc = data; -	if (pc->canceled) { -		error_canceled(pc->conn, pc->msg, "Connection canceled"); -		goto fail; -	} - -	if (err < 0) { -		error_connection_attempt_failed(pc->conn, pc->msg, -err); -		goto fail; -	} - -	if (!recs || !recs->data) { -		error_not_supported(pc->conn, pc->msg); -		error("Invalid service record length"); -		goto fail; -	} - -	rec = recs->data; -	if (sdp_get_access_protos(rec, &protos) < 0) { -		error_not_supported(pc->conn, pc->msg); -		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 < 1 || ch > 30) { -		error("Channel out of range: %d", ch); -		error_not_supported(pc->conn, pc->msg); -		goto fail; -	} - -	str2ba(pc->address, &dst); - -	if (dbus_message_has_member(pc->msg, "CreatePort")) { -		sdp_data_t *d; -		char *svcname = NULL; - -		d = sdp_data_get(rec, SDP_ATTR_SVCNAME_PRIMARY); -		if (d) { -			svcname = g_new0(char, d->unitSize); -			snprintf(svcname, d->unitSize, "%.*s", -					d->unitSize, d->val.str); -		} - -		create_channel_port(pc->conn, pc->msg, pc->adapter, -				pc->address, svcname, ch, data); - -		if (svcname) -			g_free(svcname); -	} else { -		/* ConnectService */ -		pc->channel = ch; - -		connect_pending(pc->conn, pc->msg, pc); - -		/* Wait the connect callback */ -		goto done; -	} - -fail: -	pending_connect_remove(pc); -done: -	if (recs) -		sdp_list_free(recs, (sdp_free_func_t) sdp_record_free); -} - -static int pattern2uuid(const char *pattern, uuid_t *uuid) -{ -	uint16_t cls; -	int i; - -	/* Friendly name */ -	cls = str2class(pattern); -	if (cls) { -		sdp_uuid16_create(uuid, cls); -		return 0; -	} - -	/* UUID 128*/ -	if (strlen(pattern) != 36) -		return -EINVAL; - -	for (i = 0; i < 36; i++) { -		if (i == 8 || i == 13 || i == 18 || i == 23) { -			if (pattern[i] != '-') -				return -EINVAL; - -		} else if (!isxdigit(pattern[i])) -			return -EINVAL; -	} - -	bt_string2uuid(uuid, pattern); -	return 0; -} - -static int pattern2long(const char *pattern, long *pval) -{ -	char *endptr; -	long val; - -	errno = 0; -	val = strtol(pattern, &endptr, 0); -	if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || -			(errno != 0 && val == 0) || (pattern == endptr)) { -		return -EINVAL; -	} - -	*pval = val; - -	return 0; -} - -static DBusMessage *search_uuid(DBusConnection *conn, DBusMessage *msg, -				const char *adapter, const char *address, -				const char *pattern, uuid_t *uuid, void *data) -{ -	struct pending_connect *pc; -	int err; -	bdaddr_t src, dst; - -	pc = g_new0(struct pending_connect, 1); -	pc->conn = dbus_connection_ref(conn); -	pc->msg = dbus_message_ref(msg); -	pc->adapter = g_strdup(adapter); -	pc->address = g_strdup(address); -	pc->id = -1; -	pc->pattern = g_strdup(pattern); - -	str2ba(adapter, &src); -	str2ba(address, &dst); - -	err = bt_search_service(&src, &dst, uuid, record_cb, pc, NULL); -	if (err < 0) { -		pending_connect_free(pc); -		return g_dbus_create_error(msg, ERROR_INTERFACE ".NotSuppported", -						"Not Supported"); -	} - -	pending_connects = g_slist_append(pending_connects, pc); -	pc->listener_id = g_dbus_add_disconnect_watch(conn, -					dbus_message_get_sender(msg), -					transaction_owner_exited, pc, NULL); - -	return NULL; -} - -static DBusMessage *create_port(DBusConnection *conn, -				DBusMessage *msg, void *data) -{ -	const char *address, *pattern; -	struct pending_connect *pending; -	long val; -	uuid_t uuid; -	char adp[18]; -	int dev_id; -	bdaddr_t src; - -	if (!dbus_message_get_args(msg, NULL, -				DBUS_TYPE_STRING, &address, -				DBUS_TYPE_STRING, &pattern, -				DBUS_TYPE_INVALID)) -		return NULL; - -	dev_id = hci_get_route(NULL); -	if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) -		return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAvailable", -						"Adapter not Available"); - -	pending = find_pending_connect_by_pattern(address, pattern); -	if (pending) -		return g_dbus_create_error(msg, ERROR_INTERFACE ".InProgress", -						"Connection in Progress"); - -	ba2str(&src, adp); - -	/* Friendly name or uuid128 */ -	if (pattern2uuid(pattern, &uuid) == 0) -		return search_uuid(conn, msg, adp, address, pattern, &uuid, -				data); - -	/* RFCOMM Channel */ -	if (pattern2long(pattern, &val) == 0) -		return create_channel_port(conn, msg, adp, address, pattern, -				val, data); - -	return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArgument", -					"Invalid pattern"); -} - -static void message_append_paths(DBusMessage *msg, const GSList *list) -{ -	const GSList *l; -	const char *path; -	DBusMessageIter iter, iter_array; - -	dbus_message_iter_init_append(msg, &iter); -	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, -			DBUS_TYPE_STRING_AS_STRING, &iter_array); - -	for (l = list; l; l = l->next) { -		path = l->data; -		dbus_message_iter_append_basic(&iter_array, -				DBUS_TYPE_STRING, &path); -	} - -	dbus_message_iter_close_container(&iter, &iter_array); -} - -static DBusMessage *list_ports(DBusConnection *conn, -				DBusMessage *msg, void *data) -{ -	DBusMessage *reply; - -	reply = dbus_message_new_method_return(msg); -	if (!reply) -		return NULL; - -	message_append_paths(reply, ports_paths); - -	return reply; -} - -static DBusMessage *remove_port(DBusConnection *conn, -				DBusMessage *msg, void *data) -{ -	struct rfcomm_dev_info di; -	const char *path; -	GSList *l; -	int16_t id; - -	if (!dbus_message_get_args(msg, NULL, -				DBUS_TYPE_STRING, &path, -				DBUS_TYPE_INVALID)) -		return NULL; - -	if (sscanf(path, SERIAL_MANAGER_PATH"/rfcomm%hd", &id) != 1) -		return does_not_exist(msg, "Invalid RFCOMM node"); - -	di.id = id; -	if (ioctl(rfcomm_ctl, RFCOMMGETDEVINFO, &di) < 0) -		return does_not_exist(msg, "Invalid RFCOMM node"); -	port_delete(&di.src, &di.dst, id); - -	if (port_unregister(path) < 0) -		return does_not_exist(msg, "Invalid RFCOMM node"); - -	g_dbus_emit_signal(conn, SERIAL_MANAGER_PATH, -			SERIAL_MANAGER_INTERFACE, "PortRemoved" , -			DBUS_TYPE_STRING, &path, -			DBUS_TYPE_INVALID); - -	l = g_slist_find_custom(ports_paths, path, (GCompareFunc) strcmp); -	if (l) { -		g_free(l->data); -		ports_paths = g_slist_remove(ports_paths, l->data); -	} - -	return dbus_message_new_method_return(msg); -} -  static void add_lang_attr(sdp_record_t *r)  {  	sdp_lang_attr_t base_lang; @@ -1615,223 +990,29 @@ static DBusMessage *remove_proxy(DBusConnection *conn,  	return dbus_message_new_method_return(msg);  } -static DBusMessage *connect_channel(DBusConnection *conn, DBusMessage *msg, -				const char *adapter, const char *address, -				const char *pattern, long channel, void *data) -{ -	struct pending_connect *pc; - -	if (channel < 1 || channel > 30) -		return invalid_arguments(msg, "Invalid RFCOMM channel"); - -	pc = g_new0(struct pending_connect, 1); -	pc->conn = dbus_connection_ref(conn); -	pc->msg = dbus_message_ref(msg); -	pc->adapter = g_strdup(adapter); -	pc->address = g_strdup(address); -	pc->id = -1; -	pc->pattern = g_strdup(pattern); -	pc->channel = channel; - -	return connect_pending(conn, msg, pc); -} - -DBusMessage *service_connect(DBusConnection *conn, DBusMessage *msg, -						const char *adapter, -						const char *address, -						const char *pattern) -{ -	int dev_id; -	bdaddr_t src; -	char adp[18]; -	uuid_t uuid; -	long val; - -	if (!adapter) -		dev_id = hci_get_route(NULL); -	else -		dev_id = hci_devid(adapter); - -	if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) -		return failed(msg, "Adapter not Available"); - -	ba2str(&src, adp); - -	/* Friendly name or uuid128 */ -	if (pattern2uuid(pattern, &uuid) == 0) -		return search_uuid(conn, msg, adp, address, -						pattern, &uuid, NULL); - -	/* RFCOMM Channel */ -	if (pattern2long(pattern, &val) == 0) -		return connect_channel(conn, msg, adp, address, -							pattern, val, NULL); - -	return invalid_arguments(msg, "Invalid Pattern"); -} - -static DBusMessage *connect_service(DBusConnection *conn, -					DBusMessage *msg, void *data) -{ -	const char *address, *pattern; - -	if (!dbus_message_get_args(msg, NULL, -				DBUS_TYPE_STRING, &address, -				DBUS_TYPE_STRING, &pattern, -				DBUS_TYPE_INVALID)) -		return NULL; - -	return service_connect(conn, msg, NULL, address, pattern); -} - -static DBusMessage *connect_service_from_adapter(DBusConnection *conn, -						DBusMessage *msg, void *data) -{ -	const char *adapter, *address, *pattern; - -	if (!dbus_message_get_args(msg, NULL, -				DBUS_TYPE_STRING, &adapter, -				DBUS_TYPE_STRING, &address, -				DBUS_TYPE_STRING, &pattern, -				DBUS_TYPE_INVALID)) -		return NULL; - -	return service_connect(conn, msg, adapter, address, pattern); -} - -static DBusMessage *disconnect_service(DBusConnection *conn, -					DBusMessage *msg, void *data) -{ -	const char *name; -	int err, id; - -	if (!dbus_message_get_args(msg, NULL, -				DBUS_TYPE_STRING, &name, -				DBUS_TYPE_INVALID)) -		return NULL; - -	if (sscanf(name, "/dev/rfcomm%d", &id) != 1) -		return invalid_arguments(msg, "Invalid RFCOMM node"); - -	err = port_remove_listener(dbus_message_get_sender(msg), name); -	if (err < 0) -		return does_not_exist(msg, "Invalid RFCOMM node"); - -	g_dbus_emit_signal(conn, SERIAL_MANAGER_PATH, -			SERIAL_MANAGER_INTERFACE, "ServiceDisconnected" , -			DBUS_TYPE_STRING, &name, -			DBUS_TYPE_INVALID); - -	return dbus_message_new_method_return(msg); -} - -static DBusMessage *cancel_connect_service(DBusConnection *conn, -					DBusMessage *msg, void *data) -{ -	struct pending_connect *pending; -	const char *bda, *pattern; - -	if (!dbus_message_get_args(msg, NULL, -				DBUS_TYPE_STRING, &bda, -				DBUS_TYPE_STRING, &pattern, -				DBUS_TYPE_INVALID)) -		return NULL; - -	pending = find_pending_connect_by_pattern(bda, pattern); -	if (!pending) -		return does_not_exist(msg, "No such connection request"); - -	pending->canceled = 1; - -	return dbus_message_new_method_return(msg); -} -  static void manager_unregister(void *data)  { -	if (pending_connects) { -		g_slist_foreach(pending_connects, -				(GFunc) pending_connect_free, NULL); -		g_slist_free(pending_connects); -		pending_connects = NULL; -	} -  	if (proxies) {  		g_slist_foreach(proxies,  				(GFunc) proxy_unregister, NULL);  		g_slist_free(proxies);  		proxies = NULL;  	} - -	if (ports_paths) { -		g_slist_foreach(ports_paths, -				(GFunc) g_free, NULL); -		g_slist_free(ports_paths); -		ports_paths = NULL; -	}  }  static GDBusMethodTable manager_methods[] = { -	{ "CreatePort",			"ss",	"s",	create_port, -							G_DBUS_METHOD_FLAG_ASYNC }, -	{ "ListPorts",			"",	"as",	list_ports }, -	{ "RemovePort",			"s",	"",	remove_port },  	{ "CreateProxy",		"ss",	"s",	create_proxy },  	{ "ListProxies",		"",	"as",	list_proxies },  	{ "RemoveProxy",		"s",	"",	remove_proxy }, -	{ "ConnectService",		"ss",	"s",	connect_service, -							G_DBUS_METHOD_FLAG_ASYNC }, -	{ "ConnectServiceFromAdapter",	"sss",	"s",	connect_service_from_adapter, -							G_DBUS_METHOD_FLAG_ASYNC }, -	{ "DisconnectService",		"s",	"",	disconnect_service }, -	{ "CancelConnectService",	"ss",	"",	cancel_connect_service },  	{ },  };  static GDBusSignalTable manager_signals[] = { -	{ "PortCreated",		"s"	}, -	{ "PortRemoved",		"s"	},  	{ "ProxyCreated",		"s"	},  	{ "ProxyRemoved",		"s"	}, -	{ "ServiceConnected",		"s"	}, -	{ "ServiceDisconnected",	"s"	},  	{ }  }; -static void parse_port(char *key, char *value, void *data) -{ -	char path[MAX_PATH_LENGTH], port_name[16], dst_addr[18], *svc; -	char *src_addr = data; -	bdaddr_t dst, src; -	int ch, id; - -	memset(dst_addr, 0, sizeof(dst_addr)); -	if (sscanf(key,"%17s#%d", dst_addr, &id) != 2) -		return; - -	if (sscanf(value,"%d:", &ch) != 1) -		return; - -	svc = strchr(value, ':'); -	if (svc && *svc) -		svc++; - -	str2ba(dst_addr, &dst); -	str2ba(src_addr, &src); - -	if (rfcomm_bind(&src, &dst, id, ch) < 0) -		return; - -	snprintf(port_name, sizeof(port_name), "/dev/rfcomm%d", id); - -	if (port_register(connection, id, &src, &dst, -				port_name, path, svc) < 0) { -		rfcomm_release(id); -		return; -	} - -	ports_paths = g_slist_append(ports_paths, g_strdup(path)); -} -  static void parse_proxy(char *key, char *value, void *data)  {  	char uuid128[MAX_LEN_UUID_STR], tmp[3]; @@ -1904,9 +1085,6 @@ static void register_stored(void)  		if (!isdigit(de->d_name[0]))  			continue; -		snprintf(filename, PATH_MAX, "%s/%s/serial", STORAGEDIR, de->d_name); -		textfile_foreach(filename, parse_port, de->d_name); -  		snprintf(filename, PATH_MAX, "%s/%s/proxy", STORAGEDIR, de->d_name);  		textfile_foreach(filename, parse_proxy, de->d_name);  	} @@ -1914,15 +1092,77 @@ static void register_stored(void)  	closedir(dir);  } -int serial_manager_init(DBusConnection *conn) +static int serial_probe(struct btd_device_driver *driver, +			struct btd_device *device, sdp_record_t *rec, +			const char *name)  { +	struct adapter *adapter = device_get_adapter(device); +	const gchar *path = device_get_path(device); +	sdp_list_t *protos; +	int ch; +	bdaddr_t src, dst; + +	DBG("path %s", path); + +	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 (rfcomm_ctl < 0) { -		rfcomm_ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM); -		if (rfcomm_ctl < 0) -			return -errno; +	if (ch < 1 || ch > 30) { +		error("Channel out of range: %d", ch); +		return -EINVAL;  	} +	str2ba(adapter->address, &src); +	str2ba(device_get_address(device), &dst); + +	return port_register(connection, path, &src, &dst, name, +			driver->uuids[0], ch); +} + +static void serial_remove(struct btd_device_driver *driver, +				struct btd_device *device) +{ +	const gchar *path = device_get_path(device); + +	DBG("path %s", path); + +	port_unregister(path, driver->uuids[0]); +} + +static int port_probe(struct btd_device_driver *driver, +			struct btd_device *device, GSList *records) +{ +	return serial_probe(driver, device, records->data, +			SERIAL_PORT_NAME); +} + +static int dialup_probe(struct btd_device_driver *driver, +			struct btd_device *device, GSList *records) +{ +	return serial_probe(driver, device, records->data, +			DIALUP_NET_NAME); +} + +static struct btd_device_driver serial_port_driver = { +	.name	= "serial-port", +	.uuids	= BTD_UUIDS(SERIAL_PORT_UUID), +	.probe	= port_probe, +	.remove	= serial_remove, +}; + +static struct btd_device_driver serial_dialup_driver = { +	.name	= "serial-dialup", +	.uuids	= BTD_UUIDS(DIALUP_NET_UUID), +	.probe	= dialup_probe, +	.remove	= serial_remove, +}; + +int serial_manager_init(DBusConnection *conn) +{  	if (!g_dbus_register_interface(conn, SERIAL_MANAGER_PATH,  					SERIAL_MANAGER_INTERFACE,  					manager_methods, manager_signals, NULL, @@ -1938,11 +1178,17 @@ int serial_manager_init(DBusConnection *conn)  	register_stored(); +	btd_register_device_driver(&serial_port_driver); +	btd_register_device_driver(&serial_dialup_driver); +  	return 0;  }  void serial_manager_exit(void)  { +	btd_unregister_device_driver(&serial_port_driver); +	btd_unregister_device_driver(&serial_dialup_driver); +  	g_dbus_unregister_interface(connection, SERIAL_MANAGER_PATH,  						SERIAL_MANAGER_INTERFACE); @@ -1950,7 +1196,4 @@ void serial_manager_exit(void)  	connection = NULL;  	port_release_all(); - -	if (rfcomm_ctl >= 0) -		close(rfcomm_ctl);  } diff --git a/serial/port.c b/serial/port.c index f0563244..7edd5301 100644 --- a/serial/port.c +++ b/serial/port.c @@ -31,6 +31,7 @@  #include <stdlib.h>  #include <string.h>  #include <termios.h> +#include <unistd.h>  #include <sys/ioctl.h>  #include <sys/types.h>  #include <sys/stat.h> @@ -46,359 +47,433 @@  #include "../hcid/dbus-common.h"  #include "logging.h" +#include "glib-helper.h"  #include "error.h"  #include "manager.h"  #include "storage.h"  #define SERIAL_PORT_INTERFACE	"org.bluez.serial.Port" +#define ERROR_INVALID_ARGS	"org.bluez.Error.InvalidArguments" +#define ERROR_DOES_NOT_EXIST	"org.bluez.Error.DoesNotExist" -struct rfcomm_node { -	int16_t		id;		/* RFCOMM device id */ +#define MAX_OPEN_TRIES		5 +#define OPEN_WAIT		300	/* ms */ + +struct serial_device { +	DBusConnection	*conn;		/* for name listener handling */  	bdaddr_t	src;		/* Source (local) address */  	bdaddr_t	dst;		/* Destination address */ -	char		*svcname;	/* RFCOMM service name */ -	char		*device;	/* RFCOMM device name */ -	DBusConnection	*conn;		/* for name listener handling */ -	char		*owner;		/* Bus name */ -	GIOChannel	*io;		/* Connected node IO Channel */ -	guint		io_id;		/* IO Channel ID */ +	char		*path;		/* Device path */ +	GSList		*ports;		/* Available ports */ +}; + +struct serial_port { +	DBusMessage	*msg;		/* for name listener handling */ +	int16_t		id;		/* RFCOMM device id */ +	uint8_t		channel;	/* RFCOMM channel */ +	char		*name;		/* service friendly name */ +	char		*uuid;		/* service identification */ +	char		*dev;		/* RFCOMM device name */  	guint		listener_id; +	struct serial_device *device;  }; -static GSList *connected_nodes = NULL; -static GSList *bound_nodes = NULL; +static GSList *devices = NULL; -static struct rfcomm_node *find_node_by_name(GSList *nodes, const char *dev) +static struct serial_device *find_device(GSList *devices, const char *path)  {  	GSList *l; -	for (l = nodes; l != NULL; l = l->next) { -		struct rfcomm_node *node = l->data; -		if (!strcmp(node->device, dev)) -			return node; +	for (l = devices; l != NULL; l = l->next) { +		struct serial_device *device = l->data; + +		if (!strcmp(device->path, path)) +			return device;  	}  	return NULL;  } -static DBusMessage *port_get_address(DBusConnection *conn, -					DBusMessage *msg, void *data) +static struct serial_port *find_port(GSList *ports, const char *pattern)  { -	struct rfcomm_node *node = data; -	DBusMessage *reply; -	char bda[18]; -	const char *pbda = bda; - -	reply = dbus_message_new_method_return(msg); -	if (!reply) -		return NULL; - -	ba2str(&node->dst, bda); -	dbus_message_append_args(reply, -			DBUS_TYPE_STRING, &pbda, -			DBUS_TYPE_INVALID); - -	return reply; -} +	GSList *l; -static DBusMessage *port_get_device(DBusConnection *conn, -					DBusMessage *msg, void *data) -{ -	struct rfcomm_node *node = data; -	DBusMessage *reply; +	for (l = ports; l != NULL; l = l->next) { +		struct serial_port *port = l->data; -	reply = dbus_message_new_method_return(msg); -	if (!reply) -		return NULL; +		if (!strcasecmp(port->name, pattern)) +			return port; -	dbus_message_append_args(reply, -			DBUS_TYPE_STRING, &node->device, -			DBUS_TYPE_INVALID); +		if (!strcasecmp(port->uuid, pattern)) +			return port; -	return reply; +		if (!strcmp(port->dev, pattern)) +			return port; +	} +	return NULL;  } -static DBusMessage *port_get_adapter(DBusConnection *conn, -				  DBusMessage *msg, void *data) +static int port_release(struct serial_port *port)  { -	struct rfcomm_node *node = data; -	DBusMessage *reply; -	char addr[18]; -	const char *paddr = addr; - -	ba2str(&node->src, addr); - -	reply = dbus_message_new_method_return(msg); -	if (!reply) -		return NULL; +	struct rfcomm_dev_req req; +	int rfcomm_ctl; +	int err = 0; + +	debug("Serial port %s released", port->dev); + +	rfcomm_ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_RFCOMM); +	if (rfcomm_ctl < 0) +		return -errno; + +	memset(&req, 0, sizeof(req)); +	req.dev_id = port->id; + +	/* +	 * We are hitting a kernel bug inside RFCOMM code when +	 * RFCOMM_HANGUP_NOW bit is set on request's flags passed to +	 * ioctl(RFCOMMRELEASEDEV)! +	 */ +	req.flags = (1 << RFCOMM_HANGUP_NOW); + +	if (ioctl(rfcomm_ctl, RFCOMMRELEASEDEV, &req) < 0) { +		err = errno; +		error("Can't release device %s: %s (%d)", +				port->dev, strerror(err), err); +	} -	dbus_message_append_args(reply, -			DBUS_TYPE_STRING, &paddr, -			DBUS_TYPE_INVALID); +	g_free(port->dev); +	port->dev = NULL; +	port->id = 0; +	close(rfcomm_ctl); +	return -err; +} -	return reply; +static void serial_port_free(struct serial_port *port) +{ +	if (port->id) +		port_release(port); +	g_free(port->name); +	g_free(port->uuid); +	g_free(port);  } +static void serial_device_free(struct serial_device *device) +{ +	g_free(device->path); +	if (device->conn) +		dbus_connection_unref(device->conn); +	g_free(device); +} -static DBusMessage *port_get_name(DBusConnection *conn, -			       DBusMessage *msg, void *data) +static void port_owner_exited(void *user_data)  { -	struct rfcomm_node *node = data; -	DBusMessage *reply; -	const char *pname; -	char *name = NULL; +	struct serial_port *port = user_data; -	reply = dbus_message_new_method_return(msg); -	if (!reply) -		return NULL; +	if (port->id) +		port_release(port); -	read_device_name(&node->src, &node->dst, &name); +	port->listener_id = 0; +} -	pname = (name ? name : ""); -	dbus_message_append_args(reply, -			DBUS_TYPE_STRING, &pname, -			DBUS_TYPE_INVALID); +static void path_unregister(void *data) +{ +	struct serial_device *device = data; -	if (name) -		g_free(name); +	info("Unregistered interface %s on path %s", SERIAL_PORT_INTERFACE, +		device->path); -	return reply; +	devices = g_slist_remove(devices, device); +	serial_device_free(device);  } -static DBusMessage *port_get_service_name(DBusConnection *conn, -				       DBusMessage *msg, void *data) +void port_release_all(void)  { -	struct rfcomm_node *node = data; -	DBusMessage *reply; +	g_slist_foreach(devices, (GFunc) serial_device_free, NULL); +	g_slist_free(devices); +} -	reply = dbus_message_new_method_return(msg); -	if (!reply) -		return NULL; +static inline DBusMessage *does_not_exist(DBusMessage *msg, +					const char *description) +{ +	return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExist", +				description); +} -	dbus_message_append_args(reply, -			DBUS_TYPE_STRING, &node->svcname, -			DBUS_TYPE_INVALID); +static inline DBusMessage *invalid_arguments(DBusMessage *msg, +					const char *description) +{ +	return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments", +				description); +} -	return reply; +static inline DBusMessage *failed(DBusMessage *msg, const char *description) +{ +	return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", +				description);  } -static DBusMessage *port_get_info(DBusConnection *conn, -				DBusMessage *msg, void *data) +static void open_notify(int fd, int err, struct serial_port *port)  { -	struct rfcomm_node *node = data; +	struct serial_device *device = port->device;  	DBusMessage *reply; -	DBusMessageIter iter, dict; -	char bda[18]; -	const char *pbda = bda; - -	reply = dbus_message_new_method_return(msg); -	if (!reply) -		return NULL; - -	dbus_message_iter_init_append(reply, &iter); - -	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, -			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING -			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING -			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); - -	dbus_message_iter_append_dict_entry(&dict, "device", -			DBUS_TYPE_STRING, &node->device); -	ba2str(&node->dst, bda); -	dbus_message_iter_append_dict_entry(&dict, "address", -			DBUS_TYPE_STRING, &pbda); - -	dbus_message_iter_close_container(&iter, &dict); +	if (err) { +		/* Max tries exceeded */ +		port_release(port); +		reply = failed(port->msg, strerror(err)); +	} else { +		reply = g_dbus_create_reply(port->msg, +				DBUS_TYPE_STRING, &port->dev, +				DBUS_TYPE_INVALID); +	} -	return reply; +	/* Reply to the requestor */ +	g_dbus_send_message(device->conn, reply);  } -static GDBusMethodTable port_methods[] = { -	{ "GetAddress",		"",	"s",	port_get_address }, -	{ "GetDevice",		"",	"s",	port_get_device }, -	{ "GetAdapter",		"",	"s",	port_get_adapter }, -	{ "GetName",		"",	"s",	port_get_name }, -	{ "GetServiceName",	"",	"s",	port_get_service_name }, -	{ "GetInfo",		"",	"a{sv}",port_get_info }, -	{ NULL, NULL, NULL, NULL }, -}; - -static GDBusSignalTable port_signals[] = { -	{ NULL, NULL } -}; - -static void rfcomm_node_free(struct rfcomm_node *node) +static gboolean open_continue(struct serial_port *port)  { -	if (node->device) -		g_free(node->device); -	if (node->conn) -		dbus_connection_unref(node->conn); -	if (node->owner) -		g_free(node->owner); -	if (node->svcname) -		g_free(node->svcname); -	if (node->io) { -		g_source_remove(node->io_id); -		g_io_channel_close(node->io); -		g_io_channel_unref(node->io); +	int fd; +	static int ntries = MAX_OPEN_TRIES; + +	if (!port->listener_id) +		return FALSE; /* Owner exited */ + +	fd = open(port->dev, O_RDONLY | O_NOCTTY); +	if (fd < 0) { +		int err = errno; +		error("Could not open %s: %s (%d)", +				port->dev, strerror(err), err); +		if (!--ntries) { +			/* Reporting error */ +			open_notify(fd, err, port); +			ntries = MAX_OPEN_TRIES; +			return FALSE; +		} +		return TRUE;  	} -	rfcomm_release(node->id); -	g_free(node); + +	/* Connection succeeded */ +	open_notify(fd, 0, port); +	return FALSE;  } -static void connection_owner_exited(void *user_data) +static int port_open(struct serial_port *port)  { -	struct rfcomm_node *node = user_data; +	int fd; -	debug("Connect requestor exited. Releasing %s node", -						node->device); - -	g_dbus_emit_signal(node->conn, SERIAL_MANAGER_PATH, -			SERIAL_MANAGER_INTERFACE, "ServiceDisconnected" , -			DBUS_TYPE_STRING, &node->device, -			DBUS_TYPE_INVALID); +	fd = open(port->dev, O_RDONLY | O_NOCTTY); +	if (fd < 0) { +		g_timeout_add(OPEN_WAIT, (GSourceFunc) open_continue, port); +		return -EINPROGRESS; +	} -	connected_nodes = g_slist_remove(connected_nodes, node); -	rfcomm_node_free(node); +	return fd;  } -static gboolean rfcomm_disconnect_cb(GIOChannel *io, -		GIOCondition cond, struct rfcomm_node *node) +static void rfcomm_connect_cb(GIOChannel *chan, int err_cb, const bdaddr_t *src, +			const bdaddr_t *dst, gpointer user_data)  { -	debug("RFCOMM node %s was disconnected", node->device); +	struct serial_port *port = user_data; +	struct serial_device *device = port->device; +	struct rfcomm_dev_req req; +	int sk, err, fd; +	DBusMessage *reply; -	g_dbus_remove_watch(node->conn, node->listener_id); +	/* Owner exited? */ +	if (!port->listener_id) +		return; -	g_dbus_emit_signal(node->conn, SERIAL_MANAGER_PATH, -			SERIAL_MANAGER_INTERFACE, "ServiceDisconnected" , -			DBUS_TYPE_STRING, &node->device, -			DBUS_TYPE_INVALID); +	if (err_cb < 0) { +		error("connect(): %s (%d)", strerror(-err_cb), -err_cb); +		reply = failed(port->msg, strerror(-err_cb)); +		goto fail; +	} -	connected_nodes = g_slist_remove(connected_nodes, node); -	rfcomm_node_free(node); +	memset(&req, 0, sizeof(req)); +	req.dev_id = -1; +	req.flags = (1 << RFCOMM_REUSE_DLC); +	bacpy(&req.src, &device->src); +	bacpy(&req.dst, &device->dst); +	req.channel = port->channel; + +	sk = g_io_channel_unix_get_fd(chan); +	port->id = ioctl(sk, RFCOMMCREATEDEV, &req); +	g_io_channel_close(chan); +	g_io_channel_unref(chan); +	if (port->id < 0) { +		err = errno; +		error("ioctl(RFCOMMCREATEDEV): %s (%d)", strerror(err), err); +		reply = failed(port->msg, strerror(-err_cb)); +		goto fail; +	} +	port->dev = g_strdup_printf("/dev/rfcomm%d", port->id); -	return FALSE; -} +	debug("Serial port %s created", port->dev); -static void port_handler_unregister(void *data) -{ -	struct rfcomm_node *node = data; +	/* Addressing connect port */ +	fd = port_open(port); +	if (fd < 0) +		/* Open in progress: Wait the callback */ +		return; -	debug("Unregistered serial port: %s", node->device); +	open_notify(fd, 0, port); +	return; -	bound_nodes = g_slist_remove(bound_nodes, node); -	rfcomm_node_free(node); +fail: +	g_dbus_send_message(device->conn, reply); +	g_dbus_remove_watch(device->conn, port->listener_id); +	port->listener_id = 0;  } -void port_add_listener(DBusConnection *conn, int16_t id, bdaddr_t *dst, -			int fd, const char *dev, const char *owner) +static DBusMessage *port_connect(DBusConnection *conn, +					DBusMessage *msg, void *user_data)  { -	struct rfcomm_node *node; - -	node = g_new0(struct rfcomm_node, 1); -	bacpy(&node->dst, dst); -	node->id	= id; -	node->device	= g_strdup(dev); -	node->conn	= dbus_connection_ref(conn); -	node->owner	= g_strdup(owner); -	node->io	= g_io_channel_unix_new(fd); -	node->io_id = g_io_add_watch(node->io, G_IO_ERR | G_IO_NVAL | G_IO_HUP, -					(GIOFunc) rfcomm_disconnect_cb, node); - -	connected_nodes = g_slist_append(connected_nodes, node); - -	/* Service connection listener */ -	node->listener_id = g_dbus_add_disconnect_watch(conn, owner, -						connection_owner_exited, node, -						NULL); -} +	struct serial_device *device = user_data; +	struct serial_port *port; +	const char *uuid; +	int err; -int port_remove_listener(const char *owner, const char *dev) -{ -	struct rfcomm_node *node; +	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &uuid, +						DBUS_TYPE_INVALID) == FALSE) +		return NULL; -	node = find_node_by_name(connected_nodes, dev); -	if (!node) -		return -ENOENT; -	if (strcmp(node->owner, owner) != 0) -		return -EPERM; +	port = find_port(device->ports, uuid); +	if (!port) +		return does_not_exist(msg, "Does not match"); -	g_dbus_remove_watch(node->conn, node->listener_id); +	if (port->listener_id) +		return failed(msg, "Port already in use"); -	connected_nodes = g_slist_remove(connected_nodes, node); -	rfcomm_node_free(node); +	port->listener_id = g_dbus_add_disconnect_watch(conn, +						dbus_message_get_sender(msg), +						port_owner_exited, port, +						NULL); +	port->msg = dbus_message_ref(msg); + +	err = bt_rfcomm_connect(&device->src, &device->dst, port->channel, +				rfcomm_connect_cb, port); +	if (err < 0) { +		error("RFCOMM connect failed: %s(%d)", strerror(-err), -err); +		g_dbus_remove_watch(conn, port->listener_id); +		port->listener_id = 0; +		return failed(msg, strerror(-err)); +	} -	return 0; +	return NULL;  } -void port_release_all(void) +static DBusMessage *port_disconnect(DBusConnection *conn, +					DBusMessage *msg, void *user_data)  { -	struct rfcomm_node *node; -	GSList *l; +	struct serial_device *device = user_data; +	struct serial_port *port; +	const char *dev; -	for (l = connected_nodes; l; l = l->next) { -		node = l->data; +	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &dev, +						DBUS_TYPE_INVALID) == FALSE) +		return NULL; -		connected_nodes = g_slist_remove(connected_nodes, node); -		rfcomm_node_free(node); -	} -} +	port = find_port(device->ports, dev); +	if (!port) +		return does_not_exist(msg, "Port does not exist"); -int port_register(DBusConnection *conn, int16_t id, bdaddr_t *src, -		  bdaddr_t *dst, const char *dev, char *ppath, const char *svc) -{ -	char path[MAX_PATH_LENGTH]; -	struct rfcomm_node *node; +	if (!port->listener_id) +		return failed(msg, "Not connected"); + +	if (port->id) +		port_release(port); -	node = g_new0(struct rfcomm_node, 1); -	bacpy(&node->dst, dst); -	bacpy(&node->src, src); -	node->id	= id; -	node->device	= g_strdup(dev); -	node->conn	= dbus_connection_ref(conn); -	node->svcname	= g_strdup(svc?:"Bluetooth RFCOMM port"); +	g_dbus_remove_watch(conn, port->listener_id); +	port->listener_id = 0; -	snprintf(path, MAX_PATH_LENGTH, "%s/rfcomm%hd", SERIAL_MANAGER_PATH, id); +	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} +static GDBusMethodTable port_methods[] = { +	{ "Connect",    "s", "s", port_connect, G_DBUS_METHOD_FLAG_ASYNC }, +	{ "Disconnect", "s", "",  port_disconnect }, +	{ } +}; + +static struct serial_device *create_serial_device(DBusConnection *conn, +					const char *path, bdaddr_t *src, +					bdaddr_t *dst) +{ +	struct serial_device *device; + +	device = g_new0(struct serial_device, 1); +	device->conn = dbus_connection_ref(conn); +	bacpy(&device->dst, dst); +	bacpy(&device->src, src); +	device->path = g_strdup(path);  	if (!g_dbus_register_interface(conn, path,  				SERIAL_PORT_INTERFACE, -				port_methods, port_signals, NULL, -				node, port_handler_unregister)) { +				port_methods, NULL, NULL, +				device, path_unregister)) {  		error("D-Bus failed to register %s interface",  				SERIAL_PORT_INTERFACE); -		rfcomm_node_free(node); -		return -1; +		serial_device_free(device); +		return NULL;  	} -	info("Registered RFCOMM:%s, path:%s", dev, path); +	info("Registered interface %s on path %s", +		SERIAL_PORT_INTERFACE, path); -	if (ppath) -		strcpy(ppath, path); +	return device; +} -	bound_nodes = g_slist_append(bound_nodes, node); +int port_register(DBusConnection *conn, const char *path, bdaddr_t *src, +		  bdaddr_t *dst, const char *name, const char *uuid, +		  uint8_t channel) +{ +	struct serial_device *device; +	struct serial_port *port; + +	device = find_device(devices, path); +	if (!device) { +		device = create_serial_device(conn, path, src, dst); +		if (!device) +			return -1; +		devices = g_slist_append(devices, device); +	} + +	if (find_port(device->ports, uuid)) +		return 0; + +	port = g_new0(struct serial_port, 1); +	port->name = g_strdup(name); +	port->uuid = g_strdup(uuid); +	port->channel = channel; +	port->device = device; + +	device->ports = g_slist_append(device->ports, port);  	return 0;  } -int port_unregister(const char *path) +int port_unregister(const char *path, const char *uuid)  { -	struct rfcomm_node *node; -	char dev[16]; -	int16_t id; +	struct serial_device *device; +	struct serial_port *port; -	if (sscanf(path, SERIAL_MANAGER_PATH"/rfcomm%hd", &id) != 1) +	device = find_device(devices, path); +	if (!device)  		return -ENOENT; -	snprintf(dev, sizeof(dev), "/dev/rfcomm%hd", id); -	node = find_node_by_name(bound_nodes, dev); -	if (!node) +	port = find_port(device->ports, uuid); +	if (!port)  		return -ENOENT; -	g_dbus_unregister_interface(node->conn, path, SERIAL_PORT_INTERFACE); +	device->ports = g_slist_remove(device->ports, port); +	serial_port_free(port); +	if (device->ports) +		return 0; + +	g_dbus_unregister_interface(device->conn, path, SERIAL_PORT_INTERFACE);  	return 0;  } diff --git a/serial/port.h b/serial/port.h index 5471525b..381abe0c 100644 --- a/serial/port.h +++ b/serial/port.h @@ -21,14 +21,10 @@   *   */ -int port_add_listener(DBusConnection *conn, int16_t id, bdaddr_t *dst, -			int fd, const char *dev, const char *owner); - -int port_remove_listener(const char *owner, const char *dev); -  void port_release_all(void); -int port_register(DBusConnection *conn, int16_t id, bdaddr_t *src, -		  bdaddr_t *dst, const char *dev, char *ppath, const char *svc); +int port_register(DBusConnection *conn, const char *path, bdaddr_t *src, +		  bdaddr_t *dst, const char *name, const char *uuid, +		  uint8_t channel); -int port_unregister(const char *path); +int port_unregister(const char *path, const char *uuid); | 
