diff options
| -rw-r--r-- | serial/manager.c | 282 | 
1 files changed, 122 insertions, 160 deletions
| diff --git a/serial/manager.c b/serial/manager.c index cafe8590..1f7d7373 100644 --- a/serial/manager.c +++ b/serial/manager.c @@ -71,6 +71,8 @@ struct pending_connect {  	bdaddr_t	src;  	uint8_t		channel;  	guint		io_id;		/* GIOChannel watch id */ +	GIOChannel	*io;		/* GIOChannel for RFCOMM connect */ +	char		*dev;		/* tty device name */  	int		id;		/* RFCOMM device id */  	int		ntries;		/* Open attempts */  	int 		canceled;	/* Operation canceled */ @@ -95,16 +97,6 @@ static struct {  	{ NULL }  }; -typedef void (*open_notify_t) (int fd, int err, void *data); -typedef void (*udata_free_t) (void *data); -struct open_context { -	char		*dev; -	open_notify_t	notify; -	udata_free_t	ufree; -	void		*udata; -	int		ntries; -}; -  static DBusConnection *connection = NULL;  static GSList *pending_connects = NULL;  static int rfcomm_ctl = -1; @@ -123,6 +115,14 @@ static void pending_connect_free(struct pending_connect *pc)  		g_free(pc->adapter_path);  	if (pc->pcall)  		dbus_pending_call_unref(pc->pcall); +	if (pc->dev) +		g_free(pc->dev); +	if (pc->io_id > 0) +		g_source_remove(pc->io_id); +	if (pc->io) { +		g_io_channel_close(pc->io); +		g_io_channel_unref(pc->io); +	}  	g_free(pc);  } @@ -142,53 +142,113 @@ static struct pending_connect *find_pending_connect_by_pattern(const char *bda,  	return NULL;  } -static void open_context_free(void *data) +static void transaction_owner_exited(const char *name, void *data)  { -	struct open_context *oc = data; +	GSList *l, *tmp = NULL; +	debug("transaction owner %s exited", name); + +	/* Clean all pending calls that belongs to this owner */ +	for (l = pending_connects; l != NULL; l = l->next) { +		struct pending_connect *pc = l->data; +		if (strcmp(name, dbus_message_get_sender(pc->msg)) != 0) { +			tmp = g_slist_append(tmp, pc); +			continue; +		} + +		if (pc->pcall) +			dbus_pending_call_cancel(pc->pcall); + +		if (pc->id >= 0) +			rfcomm_release(pc->id); + +		pending_connect_free(pc); +	} + +	g_slist_free(pending_connects); +	pending_connects = tmp; +} + +static void pending_connect_remove(struct pending_connect *pc) +{ +	/* Remove the connection request owner */ +	name_listener_remove(pc->conn, dbus_message_get_sender(pc->msg), +				(name_cb_t) transaction_owner_exited, NULL); + +	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 (oc->ufree) -		oc->ufree(oc->udata); -	g_free(oc->dev); -	g_free(oc); +	if (err) { +		/* Max tries exceeded */ +		rfcomm_release(pc->id); +		err_connection_failed(pc->conn, pc->msg, strerror(err)); +		return; +	} + +	if (pc->canceled) { +		rfcomm_release(pc->id); +		err_connection_canceled(pc->conn, pc->msg); +		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); +	send_message_and_unref(pc->conn, reply); + +	/* Send the D-Bus signal */ +	dbus_connection_emit_signal(pc->conn, SERIAL_MANAGER_PATH, +			SERIAL_MANAGER_INTERFACE, "ServiceConnected" , +			DBUS_TYPE_STRING, &pc->dev, +			DBUS_TYPE_INVALID); + +	str2ba(pc->bda, &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 open_context *oc) +static gboolean open_continue(struct pending_connect *pc)  {  	int fd; -	fd = open(oc->dev, O_RDONLY | O_NOCTTY); +	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)", -				oc->dev, strerror(err), err); -		if (++oc->ntries >= MAX_OPEN_TRIES) { +				pc->dev, strerror(err), err); +		if (++pc->ntries >= MAX_OPEN_TRIES) {  			/* Reporting error */ -			oc->notify(fd, err, oc->udata); -			open_context_free(oc); +			open_notify(fd, err, pc); +			pending_connect_remove(pc);  			return FALSE;  		}  		return TRUE;  	}  	/* Connection succeeded */ -	oc->notify(fd, 0, oc->udata); -	open_context_free(oc); +	open_notify(fd, 0, pc); +	pending_connect_remove(pc);  	return FALSE;  } -int port_open(const char *dev, open_notify_t notify, -			void *udata, udata_free_t ufree) +int port_open(struct pending_connect *pc)  {  	int fd; -	fd = open(dev, O_RDONLY | O_NOCTTY); +	fd = open(pc->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(OPEN_WAIT, (GSourceFunc) open_continue, oc); +		g_timeout_add(OPEN_WAIT, (GSourceFunc) open_continue, pc);  		return -EINPROGRESS;  	} @@ -253,131 +313,40 @@ static int rfcomm_bind(bdaddr_t *src, bdaddr_t *dst, int16_t dev_id, uint8_t ch)  	return id;  } -static void open_notify(int fd, int err, void *data) -{ -	char port_name[16]; -	const char *pname = port_name; -	struct pending_connect *pc = data; -	DBusMessage *reply; -	bdaddr_t dst; - -	if (err) { -		/* Max tries exceeded */ -		rfcomm_release(pc->id); -		err_connection_failed(pc->conn, pc->msg, strerror(err)); -		return; -	} - -	if (pc->canceled) { -		rfcomm_release(pc->id); -		err_connection_canceled(pc->conn, pc->msg); -		return; -	} - -	snprintf(port_name, sizeof(port_name), "/dev/rfcomm%d", pc->id); - -	/* Reply to the requestor */ -	reply = dbus_message_new_method_return(pc->msg); -	dbus_message_append_args(reply, -			DBUS_TYPE_STRING, &pname, -			DBUS_TYPE_INVALID); -	send_message_and_unref(pc->conn, reply); - -	/* Send the D-Bus signal */ -	dbus_connection_emit_signal(pc->conn, SERIAL_MANAGER_PATH, -			SERIAL_MANAGER_INTERFACE, "ServiceConnected" , -			DBUS_TYPE_STRING, &pname, -			DBUS_TYPE_INVALID); - -	str2ba(pc->bda, &dst); - -	/* Add the RFCOMM connection listener */ -	port_add_listener(pc->conn, pc->id, &dst, fd, -			port_name, dbus_message_get_sender(pc->msg)); -} - -static void transaction_owner_exited(const char *name, void *data) -{ -	GSList *l, *tmp = NULL; -	debug("transaction owner %s exited", name); - -	/* Clean all pending calls that belongs to this owner */ -	for (l = pending_connects; l != NULL; l = l->next) { -		struct pending_connect *pc = l->data; -		if (strcmp(name, dbus_message_get_sender(pc->msg)) != 0) { -			tmp = g_slist_append(tmp, pc); -			continue; -		} - -		if (pc->pcall) -			dbus_pending_call_cancel(pc->pcall); - -		if (pc->io_id > 0) -			g_source_remove(pc->io_id); - -		if (pc->id >= 0) -			rfcomm_release(pc->id); - -		pending_connect_free(pc); -	} - -	g_slist_free(pending_connects); -	pending_connects = tmp; -} - -static void pending_connect_remove(struct pending_connect *pc) -{ -	/* Remove the connection request owner */ -	name_listener_remove(pc->conn, dbus_message_get_sender(pc->msg), -				(name_cb_t) transaction_owner_exited, NULL); - -	pending_connects = g_slist_remove(pending_connects, pc); -	pending_connect_free(pc); -} -  static gboolean rfcomm_connect_cb(GIOChannel *chan,  		GIOCondition cond, struct pending_connect *pc)  { -	char port_name[16];  	struct rfcomm_dev_req req; -	int sk, err, fd, close_chan = 1; +	int sk, err, fd, ret; +	socklen_t len;  	if (pc->canceled) {  		err_connection_canceled(pc->conn, pc->msg);  		goto fail;  	} -	sk = g_io_channel_unix_get_fd(chan); -  	if (cond & G_IO_NVAL) { -		close_chan = 0; -		err_connection_failed(pc->conn, pc->msg, -				"File descriptor is not open"); +		/* Avoid close invalid file descriptor */ +		g_io_channel_unref(pc->io); +		pc->io = NULL; +		err_connection_canceled(pc->conn, pc->msg);  		goto fail;  	} -	if (cond & (G_IO_ERR | G_IO_HUP)) { -		socklen_t len; -		int ret; - -		len = sizeof(ret); -		if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { -			err = errno; -			error("getsockopt(SO_ERROR): %s (%d)", -						strerror(err), err); -			err_connection_failed(pc->conn, -					pc->msg, strerror(err)); -			goto fail; -		} - -		if (ret != 0) { -			error("connect(): %s (%d)", strerror(ret), ret); -			err_connection_failed(pc->conn, pc->msg, strerror(ret)); -			goto fail; -		} +	sk = g_io_channel_unix_get_fd(chan); +	len = sizeof(ret); +	if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &ret, &len) < 0) { +		err = errno; +		error("getsockopt(SO_ERROR): %s (%d)", +				strerror(err), err); +		err_connection_failed(pc->conn, +				pc->msg, strerror(err)); +		goto fail; +	} -		error("Hangup on rfcomm socket"); -		err_connection_canceled(pc->conn, pc->msg); +	if (ret != 0) { +		error("connect(): %s (%d)", strerror(ret), ret); +		err_connection_failed(pc->conn, pc->msg, strerror(ret));  		goto fail;  	} @@ -397,30 +366,24 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan,  		err_connection_failed(pc->conn, pc->msg, strerror(err));  		goto fail;  	} +	pc->dev	= g_new0(char, 16); +	snprintf(pc->dev, 16, "/dev/rfcomm%d", pc->id); -	snprintf(port_name, sizeof(port_name), "/dev/rfcomm%d", pc->id);  	/* Addressing connect port */ -	fd = port_open(port_name, open_notify, pc, -			(udata_free_t) pending_connect_remove); -	if (fd < 0) { -		g_io_channel_close(chan); +	fd = port_open(pc); +	if (fd < 0)  		/* Open in progress: Wait the callback */  		return FALSE; -	}  	open_notify(fd, 0, pc);  fail:  	pending_connect_remove(pc); -	if (close_chan) -		g_io_channel_close(chan); -  	return FALSE;  }  static int rfcomm_connect(struct pending_connect *pc)  {  	struct sockaddr_rc addr; -	GIOChannel *io;  	int sk, err = 0;  	sk = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); @@ -438,8 +401,7 @@ static int rfcomm_connect(struct pending_connect *pc)  	if (set_nonblocking(sk) < 0)  		return -errno; -	io = g_io_channel_unix_new(sk); - +	pc->io = g_io_channel_unix_new(sk);  	addr.rc_family	= AF_BLUETOOTH;  	str2ba(pc->bda, &addr.rc_bdaddr);  	addr.rc_channel	= pc->channel; @@ -453,14 +415,14 @@ static int rfcomm_connect(struct pending_connect *pc)  		}  		debug("Connect in progress"); -		pc->io_id = g_io_add_watch(io, G_IO_OUT | G_IO_ERR | G_IO_NVAL | G_IO_HUP, -							(GIOFunc) rfcomm_connect_cb, pc); +		pc->io_id = g_io_add_watch(pc->io, +				G_IO_OUT | G_IO_ERR | G_IO_NVAL | G_IO_HUP, +				(GIOFunc) rfcomm_connect_cb, pc);  	} else {  		debug("Connect succeeded with first try"); -		(void) rfcomm_connect_cb(io, G_IO_OUT, pc); +		(void) rfcomm_connect_cb(pc->io, G_IO_OUT, pc);  	}  fail: -	g_io_channel_unref(io);  	return -err;  } | 
