diff options
| author | Claudio Takahasi <claudio.takahasi@openbossa.org> | 2007-05-16 21:04:12 +0000 | 
|---|---|---|
| committer | Claudio Takahasi <claudio.takahasi@openbossa.org> | 2007-05-16 21:04:12 +0000 | 
| commit | 5caf7e18f9534e9cd16264026169016ccbcdb1fa (patch) | |
| tree | 63c3f9bc45320c26a3ad31994284b3b11e5338d0 /serial/port.c | |
| parent | 417006053c2c16cd8ae5b947d0a8daae4f151b94 (diff) | |
serial: moving functions to port.c and added port methods declaration
Diffstat (limited to 'serial/port.c')
| -rw-r--r-- | serial/port.c | 258 | 
1 files changed, 258 insertions, 0 deletions
diff --git a/serial/port.c b/serial/port.c index 80e3c6df..7d3b95c8 100644 --- a/serial/port.c +++ b/serial/port.c @@ -25,4 +25,262 @@  #include <config.h>  #endif +#include <errno.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <glib.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/rfcomm.h> + +#include "dbus.h" +#include "dbus-helper.h" +#include "logging.h" + +#include "error.h" +#include "manager.h"  #include "port.h" + +#define SERIAL_PORT_INTERFACE	"org.bluez.serial.Port" + +/* Waiting for udev to create the device node */ +#define MAX_OPEN_TRIES 		5 +#define OPEN_WAIT		300  /* ms */ + +struct rfcomm_node { +	int16_t		id;	/* RFCOMM device id */ +	char		*name;	/* 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  */ +}; + +struct open_context { +	char		*dev; +	open_notify_t	notify; +	udata_free_t	ufree; +	void		*udata; +	int		ntries; +}; + +static GSList *connected_nodes = NULL; + +static DBusHandlerResult port_connect(DBusConnection *conn, +					DBusMessage *msg, void *data) +{ +	/* FIXME: call port_open() */ + +	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult port_disconnect(DBusConnection *conn, +					DBusMessage *msg, void *data) +{ +	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusHandlerResult port_get_address(DBusConnection *conn, +					DBusMessage *msg, void *data) +{ +	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static DBusMethodVTable port_methods[] = { +	{ "Connect",	port_connect,		"",	""	}, +	{ "Disconnect",	port_disconnect,	"",	""	}, +	{ "GetAddress",	port_get_address,	"",	"s"	}, +	{ NULL, NULL, NULL, NULL }, +}; + +static DBusSignalVTable port_signals[] = { +	{ NULL, NULL } +}; + +static void rfcomm_node_free(struct rfcomm_node *node) +{ +	if (node->name) +		g_free(node->name); +	if (node->conn) +		dbus_connection_unref(node->conn); +	if (node->owner) +		g_free(node->owner); +	if (node->io) { +		g_source_remove(node->io_id); +		g_io_channel_unref(node->io); +	} +	g_free(node); +} + +static void connection_owner_exited(const char *name, struct rfcomm_node *node) +{ +	char path[MAX_PATH_LENGTH]; + +	debug("Connect requestor %s exited. Releasing %s node", +						name, node->name); + +	snprintf(path, MAX_PATH_LENGTH, "%s/rfcomm%d", SERIAL_MANAGER_PATH, node->id); +	rfcomm_release(node->id); +	dbus_connection_emit_signal(node->conn, SERIAL_MANAGER_PATH, +			SERIAL_MANAGER_INTERFACE, "ServiceDisconnected" , +			DBUS_TYPE_STRING, &node->name, +			DBUS_TYPE_INVALID); + +	connected_nodes = g_slist_remove(connected_nodes, node); +	dbus_connection_destroy_object_path(node->conn, path); +} + +static gboolean rfcomm_disconnect_cb(GIOChannel *io, +		GIOCondition cond, struct rfcomm_node *node) +{ +	debug("RFCOMM node %s was disconnected", node->name); + +	if (cond & (G_IO_ERR | G_IO_HUP)) +		g_io_channel_close(io); + +	name_listener_remove(node->conn, node->owner, +			(name_cb_t) connection_owner_exited, node); + +	dbus_connection_emit_signal(node->conn, SERIAL_MANAGER_PATH, +			SERIAL_MANAGER_INTERFACE, "ServiceDisconnected" , +			DBUS_TYPE_STRING, &node->name, +			DBUS_TYPE_INVALID); + +	connected_nodes = g_slist_remove(connected_nodes, node); +	rfcomm_node_free(node); + +	return FALSE; +} + +static void port_unregister(DBusConnection *conn, void *data) +{ +	struct rfcomm_node *node = data; + +	debug("Unregistered serial port: %s", node->name); + +	rfcomm_node_free(node); +} + +int port_register(DBusConnection *conn, int id, int fd, +		const char *name, const char *owner, char *ppath) +{ +	char path[MAX_PATH_LENGTH]; +	struct rfcomm_node *node; + +	node = g_new0(struct rfcomm_node, 1); +	node->id	= id; +	node->name	= g_strdup(name); +	node->conn	= dbus_connection_ref(conn); + +	snprintf(path, MAX_PATH_LENGTH, "%s/rfcomm%d", SERIAL_MANAGER_PATH, id); + +	if (!dbus_connection_create_object_path(conn, path, node, +						port_unregister)) { +		error("D-Bus failed to register %s path", path); +		rfcomm_node_free(node); +		return -1; +	} + +	if (!dbus_connection_register_interface(conn, path, +				SERIAL_PORT_INTERFACE, +				port_methods, +				port_signals, NULL)) { +		error("D-Bus failed to register %s interface", +				SERIAL_PORT_INTERFACE); +		dbus_connection_destroy_object_path(conn, path); +		return -1; +	} + +	info("Registered RFCOMM:%s, path:%s owner:%s", name, path, owner); + +	if (ppath) +		strcpy(ppath, path); + +	if (fd < 0) +		return 0; + +	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); + +	/* Serial port connection listener */ +	return name_listener_add(node->conn, owner, +			(name_cb_t) connection_owner_exited, node); +} + +const char *port_get_owner(DBusConnection *conn, int16_t id) +{ +	struct rfcomm_node *node; +	static char path[MAX_PATH_LENGTH]; + +	snprintf(path, MAX_PATH_LENGTH, "%s/rfcomm%d", SERIAL_MANAGER_PATH, id); + +	if (!dbus_connection_get_object_user_data(conn, path, (void *) &node) +			|| !node) { +		errno = ENOENT; +		return NULL; +	} + +	return node->owner; +} + +static gboolean open_continue(struct open_context *oc) +{ +	int fd; + +	fd = open(oc->dev, O_RDONLY | O_NOCTTY); +	if (fd < 0) { +		int err = errno; +		error("Could not open %s: %s (%d)", +				oc->dev, strerror(err), err); +		if (++oc->ntries >= MAX_OPEN_TRIES) { +			/* Reporting error */ +			oc->notify(fd, err, oc->udata); +			return FALSE; +		} +		return TRUE; +	} +	/* Connection succeeded */ +	oc->notify(fd, 0, oc->udata); +	return FALSE; +} + +static void open_context_free(void *data) +{ +	struct open_context *oc = data; + +	if (oc->ufree) +		oc->ufree(oc->udata); +	g_free(oc->dev); +	g_free(oc); +} + +int port_open(const char *dev, open_notify_t notify, void *udata, udata_free_t ufree) +{ +	int fd; + +	fd = open(dev, O_RDONLY | O_NOCTTY); +	if (fd < 0) { +		struct open_context *oc; +		oc	= g_new0(struct open_context, 1); +		oc->dev	= g_strdup(dev); +		oc->notify	= notify; +		oc->ufree	= ufree; +		oc->udata	= udata; +		g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, OPEN_WAIT, +			(GSourceFunc) open_continue, oc, open_context_free); +		return -EINPROGRESS; +	} + +	return fd; +}  | 
