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 | |
parent | 417006053c2c16cd8ae5b947d0a8daae4f151b94 (diff) |
serial: moving functions to port.c and added port methods declaration
Diffstat (limited to 'serial')
-rw-r--r-- | serial/manager.c | 493 | ||||
-rw-r--r-- | serial/manager.h | 6 | ||||
-rw-r--r-- | serial/port.c | 258 | ||||
-rw-r--r-- | serial/port.h | 11 |
4 files changed, 535 insertions, 233 deletions
diff --git a/serial/manager.c b/serial/manager.c index b5ced583..c33f51cb 100644 --- a/serial/manager.c +++ b/serial/manager.c @@ -48,26 +48,11 @@ #include "logging.h" #include "error.h" +#include "port.h" #include "manager.h" -#define SERIAL_MANAGER_PATH "/org/bluez/serial" -#define SERIAL_MANAGER_INTERFACE "org.bluez.serial.Manager" - #define BASE_UUID "00000000-0000-1000-8000-00805F9B34FB" -/* 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 pending_connect { DBusConnection *conn; DBusMessage *msg; @@ -101,7 +86,6 @@ static struct { }; static DBusConnection *connection = NULL; -static GSList *connected_nodes = NULL; static GSList *pending_connects = NULL; static int rfcomm_ctl = -1; @@ -120,32 +104,10 @@ static void pending_connect_free(struct pending_connect *pc) g_free(pc); } -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 struct rfcomm_node *find_node_by_name(const char *name) +static void pending_connect_remove(struct pending_connect *pc) { - GSList *l; - - for (l = connected_nodes; l != NULL; l = l->next) { - struct rfcomm_node *node = l->data; - if (!strcmp(node->name, name)) - return node; - } - - return NULL; + pending_connects = g_slist_remove(pending_connects, pc); + pending_connect_free(pc); } static struct pending_connect *find_pending_connect_by_pattern(const char *bda, @@ -176,7 +138,7 @@ static uint16_t str2class(const char *pattern) return 0; } -static int rfcomm_release(int16_t id) +int rfcomm_release(int16_t id) { struct rfcomm_dev_req req; @@ -202,102 +164,59 @@ static int rfcomm_release(int16_t id) return 0; } -static void connect_service_exited(const char *name, struct rfcomm_node *node) -{ - debug("Connect requestor %s exited. Releasing %s node", - name, node->name); - - 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); - rfcomm_node_free(node); -} - -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) connect_service_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 int add_rfcomm_node(GIOChannel *io, int id, const char *name, - DBusConnection *conn, const char *owner) +static int rfcomm_bind(bdaddr_t *src, bdaddr_t *dst, uint8_t ch) { - 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); - node->owner = g_strdup(owner); - node->io = io; + struct rfcomm_dev_req req; + int id; - node->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_NVAL | G_IO_HUP, - (GIOFunc) rfcomm_disconnect_cb, node); + memset(&req, 0, sizeof(req)); + req.dev_id = -1; + req.flags = 0; + bacpy(&req.src, src); + bacpy(&req.dst, dst); + req.channel = ch; - connected_nodes = g_slist_append(connected_nodes, node); + id = ioctl(rfcomm_ctl, RFCOMMCREATEDEV, &req); + if (id < 0) { + int err = errno; + error("RFCOMMCREATEDEV failed: %s (%d)", strerror(err), err); + return -err; + } - return name_listener_add(node->conn, owner, - (name_cb_t) connect_service_exited, node); + return id; } -static gboolean rfcomm_connect_cb_continue(struct pending_connect *pc) +static void open_notify(int fd, int err, void *data) { - const char *owner = dbus_message_get_sender(pc->msg); + char port_name[16]; + char path[MAX_PATH_LENGTH]; + const char *pname = port_name; + const char *ppath = path; + const char *owner; DBusMessage *reply; - char node_name[16]; - const char *pname = node_name; - int fd; + struct pending_connect *pc = data; + + if (err) { + /* Max tries exceeded */ + err_connection_failed(pc->conn, pc->msg, strerror(err)); + return; + } if (pc->canceled) { rfcomm_release(pc->id); err_connection_canceled(pc->conn, pc->msg); - goto fail; + return; } /* Check if the caller is still present */ + owner = dbus_message_get_sender(pc->msg); if (!dbus_bus_name_has_owner(pc->conn, owner, NULL)) { error("Connect requestor %s exited", owner); rfcomm_release(pc->id); - goto fail; - } - - snprintf(node_name, sizeof(node_name), "/dev/rfcomm%d", pc->id); - fd = open(node_name, O_RDONLY | O_NOCTTY); - if (fd < 0) { - int err = errno; - error("Could not open %s: %s (%d)", - node_name, strerror(err), err); - if (++pc->ntries >= MAX_OPEN_TRIES) { - rfcomm_release(pc->id); - err_connection_failed(pc->conn, pc->msg, strerror(err)); - goto fail; - } - return TRUE; + return; } - add_rfcomm_node(g_io_channel_unix_new(fd), pc->id, - node_name, pc->conn, owner); + snprintf(port_name, sizeof(port_name), "/dev/rfcomm%d", pc->id); /* Reply to the requestor */ reply = dbus_message_new_method_return(pc->msg); @@ -307,24 +226,23 @@ static gboolean rfcomm_connect_cb_continue(struct pending_connect *pc) send_message_and_unref(pc->conn, reply); /* Send the D-Bus signal */ + port_register(pc->conn, pc->id, fd, pname, owner, path); + dbus_connection_emit_signal(pc->conn, SERIAL_MANAGER_PATH, + SERIAL_MANAGER_INTERFACE, "PortCreated" , + DBUS_TYPE_STRING, &ppath, + DBUS_TYPE_INVALID); + dbus_connection_emit_signal(pc->conn, SERIAL_MANAGER_PATH, SERIAL_MANAGER_INTERFACE, "ServiceConnected" , DBUS_TYPE_STRING, &pname, - DBUS_TYPE_INVALID); - -fail: - pending_connects = g_slist_remove(pending_connects, pc); - pending_connect_free(pc); + DBUS_TYPE_INVALID); - return FALSE; } static gboolean rfcomm_connect_cb(GIOChannel *chan, GIOCondition cond, struct pending_connect *pc) { - DBusMessage *reply; - char node_name[16]; - const char *pname = node_name; + char port_name[16]; struct rfcomm_dev_req req; int sk, err, fd, close_chan = 1; @@ -384,37 +302,20 @@ static gboolean rfcomm_connect_cb(GIOChannel *chan, goto fail; } - - snprintf(node_name, sizeof(node_name), "/dev/rfcomm%d", pc->id); - - fd = open(node_name, O_RDONLY | O_NOCTTY); + 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_timeout_add(OPEN_WAIT, - (GSourceFunc) rfcomm_connect_cb_continue, pc); g_io_channel_close(chan); + /* Open in progress: Wait the callback */ return FALSE; } - add_rfcomm_node(g_io_channel_unix_new(fd), pc->id, node_name, - pc->conn, dbus_message_get_sender(pc->msg)); - - /* 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); - + open_notify(fd, 0, pc); fail: pending_connects = g_slist_remove(pending_connects, pc); pending_connect_free(pc); - if (close_chan) g_io_channel_close(chan); @@ -537,16 +438,48 @@ static void record_reply(DBusPendingCall *call, void *data) goto fail; } - pc->channel = ch; - err = rfcomm_connect(pc); - if (err < 0) { - error("RFCOMM connection failed"); - err_connection_failed(pc->conn, pc->msg, strerror(-err)); - goto fail; + if (dbus_message_has_member(pc->msg, "CreatePort")) { + char path[MAX_PATH_LENGTH]; + char port_name[16]; + const char *ppath = path; + DBusMessage *reply; + bdaddr_t dst; + + str2ba(pc->bda, &dst); + err = rfcomm_bind(&pc->src, &dst, ch); + if (err < 0) { + err_failed(pc->conn, pc->msg, strerror(-err)); + goto fail; + } + + snprintf(port_name, sizeof(port_name), "/dev/rfcomm%d", err); + port_register(pc->conn, err, -1, port_name, NULL, path); + + reply = dbus_message_new_method_return(pc->msg); + dbus_message_append_args(reply, + DBUS_TYPE_STRING, &ppath, + DBUS_TYPE_INVALID); + send_message_and_unref(pc->conn, reply); + + dbus_connection_emit_signal(pc->conn, SERIAL_MANAGER_PATH, + SERIAL_MANAGER_INTERFACE, "PortCreated" , + DBUS_TYPE_STRING, &ppath, + DBUS_TYPE_INVALID); + } else { + /* ConnectService */ + pc->channel = ch; + err = rfcomm_connect(pc); + if (err < 0) { + error("RFCOMM connection failed"); + err_connection_failed(pc->conn, pc->msg, strerror(-err)); + goto fail; + } + + /* Wait the connect callback */ + dbus_message_unref(reply); + return; } - dbus_message_unref(reply); - return; fail: dbus_message_unref(reply); pending_connects = g_slist_remove(pending_connects, pc); @@ -663,10 +596,153 @@ static int get_handles(struct pending_connect *pc, const char *uuid, return 0; } +static int pattern2uuid128(const char *pattern, char *uuid, size_t size) +{ + uint16_t cls; + + /* Friendly name */ + cls = str2class(pattern); + if (cls) { + uuid_t uuid16, uuid128; + + sdp_uuid16_create(&uuid16, cls); + sdp_uuid16_to_uuid128(&uuid128, &uuid16); + sdp_uuid2strn(&uuid128, uuid, size); + return 0; + } + + /* UUID 128*/ + if ((strlen(pattern) == 36) && + (strncasecmp(BASE_UUID, pattern, 3) == 0) && + (strncasecmp(BASE_UUID + 8, pattern + 8, 28) == 0)) { + + strncpy(uuid, pattern, size); + return 0; + } + + return -EINVAL; +} + +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 DBusHandlerResult create_port(DBusConnection *conn, DBusMessage *msg, void *data) { - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + struct pending_connect *pending, *pc; + DBusMessage *reply; + DBusError derr; + bdaddr_t src, dst; + char path[MAX_PATH_LENGTH]; + const char *bda, *pattern, *ppath = path; + long val; + int dev_id, err; + char port_name[16]; + char uuid[37]; + + dbus_error_init(&derr); + if (!dbus_message_get_args(msg, &derr, + DBUS_TYPE_STRING, &bda, + DBUS_TYPE_STRING, &pattern, + DBUS_TYPE_INVALID)) { + err_invalid_args(conn, msg, derr.message); + dbus_error_free(&derr); + return DBUS_HANDLER_RESULT_HANDLED; + } + + pending = find_pending_connect_by_pattern(bda, pattern); + if (pending) + return err_connection_in_progress(conn, msg); + + dev_id = hci_get_route(NULL); + if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) + return err_failed(conn, msg, "Adapter not available"); + + pc = g_new0(struct pending_connect, 1); + bacpy(&pc->src, &src); + pc->conn = dbus_connection_ref(conn); + pc->msg = dbus_message_ref(msg); + pc->bda = g_strdup(bda); + pc->pattern = g_strdup(pattern); + pc->adapter_path = g_malloc0(16); + snprintf(pc->adapter_path, 16, "/org/bluez/hci%d", dev_id); + + memset(uuid, 0, sizeof(uuid)); + + /* Friendly name or uuid128 */ + if (pattern2uuid128(pattern, uuid, sizeof(uuid)) == 0) { + if (get_handles(pc, uuid, handles_reply) < 0) { + pending_connect_free(pc); + return err_not_supported(conn, msg); + } + pending_connects = g_slist_append(pending_connects, pc); + return DBUS_HANDLER_RESULT_HANDLED; + } + + /* Record handle or channel */ + err = pattern2long(pattern, &val); + if (err < 0) { + pending_connect_free(pc); + return err_invalid_args(conn, msg, "invalid pattern"); + } + + /* Record handle: starts at 0x10000 */ + if (strncasecmp("0x", pattern, 2) == 0) { + if (val < 0x10000) { + pending_connect_free(pc); + return err_invalid_args(conn, msg, + "invalid record handle"); + } + + if (get_record(pc, val, record_reply) < 0) { + pending_connect_free(pc); + return err_not_supported(conn, msg); + } + pending_connects = g_slist_append(pending_connects, pc); + return DBUS_HANDLER_RESULT_HANDLED; + } + + pending_connect_free(pc); + /* RFCOMM Channel range: 1 - 30 */ + if (val < 1 || val > 30) + return err_invalid_args(conn, msg, + "invalid RFCOMM channel"); + + str2ba(bda, &dst); + err = rfcomm_bind(&src, &dst, val); + if (err < 0) + return err_failed(conn, msg, strerror(-err)); + + snprintf(port_name, sizeof(port_name), "/dev/rfcomm%d", err); + port_register(conn, err, -1, port_name, NULL, path); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return DBUS_HANDLER_RESULT_NEED_MEMORY; + dbus_message_append_args(reply, + DBUS_TYPE_STRING, &ppath, + DBUS_TYPE_INVALID); + send_message_and_unref(conn, reply); + + dbus_connection_emit_signal(conn, SERIAL_MANAGER_PATH, + SERIAL_MANAGER_INTERFACE, "PortCreated" , + DBUS_TYPE_STRING, &ppath, + DBUS_TYPE_INVALID); + return DBUS_HANDLER_RESULT_HANDLED; } static DBusHandlerResult list_ports(DBusConnection *conn, @@ -688,11 +764,9 @@ static DBusHandlerResult connect_service(DBusConnection *conn, DBusError derr; bdaddr_t src; const char *bda, *pattern; - char *endptr; long val; int dev_id, err; - uint16_t cls; - char tmp[37]; + char uuid[37]; dbus_error_init(&derr); if (!dbus_message_get_args(msg, &derr, @@ -721,39 +795,11 @@ static DBusHandlerResult connect_service(DBusConnection *conn, pc->adapter_path = g_malloc0(16); snprintf(pc->adapter_path, 16, "/org/bluez/hci%d", dev_id); - memset(tmp, 0, sizeof(tmp)); - - /* Friendly name */ - cls = str2class(pattern); - if (cls) { - uuid_t uuid16, uuid128; - - sdp_uuid16_create(&uuid16, cls); - sdp_uuid16_to_uuid128(&uuid128, &uuid16); - sdp_uuid2strn(&uuid128, tmp, sizeof(tmp)); - - if (get_handles(pc, tmp, handles_reply) < 0) { - pending_connect_free(pc); - return err_not_supported(conn, msg); - } - pending_connects = g_slist_append(pending_connects, pc); - return DBUS_HANDLER_RESULT_HANDLED; - } + memset(uuid, 0, sizeof(uuid)); - /* UUID 128*/ - if (strlen(pattern) == 36) { - strcpy(tmp, pattern); - tmp[4] = '0'; - tmp[5] = '0'; - tmp[6] = '0'; - tmp[7] = '0'; - - if (strcasecmp(BASE_UUID, tmp) != 0) { - pending_connect_free(pc); - return err_invalid_args(conn, msg, "invalid UUID"); - } - - if (get_handles(pc, pattern, handles_reply) < 0) { + /* Friendly name or uuid128 */ + if (pattern2uuid128(pattern, uuid, sizeof(uuid)) == 0) { + if (get_handles(pc, uuid, handles_reply) < 0) { pending_connect_free(pc); return err_not_supported(conn, msg); } @@ -761,12 +807,11 @@ static DBusHandlerResult connect_service(DBusConnection *conn, return DBUS_HANDLER_RESULT_HANDLED; } - errno = 0; - val = strtol(pattern, &endptr, 0); - if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || - (errno != 0 && val == 0) || (pattern == endptr)) { + /* Record handle or channel */ + err = pattern2long(pattern, &val); + if (err < 0) { pending_connect_free(pc); - return err_invalid_args(conn, msg, "Invalid pattern"); + return err_invalid_args(conn, msg, "invalid pattern"); } /* Record handle: starts at 0x10000 */ @@ -810,11 +855,10 @@ static DBusHandlerResult connect_service(DBusConnection *conn, static DBusHandlerResult disconnect_service(DBusConnection *conn, DBusMessage *msg, void *data) { - DBusMessage *reply; DBusError derr; - struct rfcomm_node *node; - const char *name; + const char *name, *owner; int err; + int id; dbus_error_init(&derr); if (!dbus_message_get_args(msg, &derr, @@ -825,34 +869,22 @@ static DBusHandlerResult disconnect_service(DBusConnection *conn, return DBUS_HANDLER_RESULT_HANDLED; } - node = find_node_by_name(name); - if (!node) - return err_does_not_exist(conn, msg, "Invalid node"); + if (sscanf(name, "/dev/rfcomm%d", &id) != 1) + return err_invalid_args(conn, msg, "invalid RFCOMM node"); - if (strcmp(node->owner, dbus_message_get_sender(msg)) != 0) - return err_not_authorized(conn, msg); + owner = port_get_owner(conn, id); + if (!owner) + return err_does_not_exist(conn, msg, "Invalid RFCOMM node"); - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; + if (strcmp(owner, dbus_message_get_sender(msg)) != 0) + return err_not_authorized(conn, msg); - err = rfcomm_release(node->id); - if (err < 0) { - dbus_message_unref(reply); + err = rfcomm_release(id); + if (err < 0) return err_failed(conn, msg, strerror(-err)); - } - - dbus_connection_emit_signal(conn, SERIAL_MANAGER_PATH, - SERIAL_MANAGER_INTERFACE, "ServiceDisconnected" , - DBUS_TYPE_STRING, &node->name, - DBUS_TYPE_INVALID); - - name_listener_remove(node->conn, node->owner, - (name_cb_t) connect_service_exited, node); - connected_nodes = g_slist_remove(connected_nodes, node); - rfcomm_node_free(node); - return send_message_and_unref(conn, reply); + return send_message_and_unref(conn, + dbus_message_new_method_return(msg)); } static DBusHandlerResult cancel_connect_service(DBusConnection *conn, @@ -888,13 +920,6 @@ static DBusHandlerResult cancel_connect_service(DBusConnection *conn, static void manager_unregister(DBusConnection *conn, void *data) { - if (connected_nodes) { - g_slist_foreach(connected_nodes, - (GFunc) rfcomm_node_free, NULL); - g_slist_free(connected_nodes); - connected_nodes = NULL; - } - if (pending_connects) { g_slist_foreach(pending_connects, (GFunc) pending_connect_free, NULL); @@ -916,6 +941,8 @@ static DBusMethodVTable manager_methods[] = { static DBusSignalVTable manager_signals[] = { { "ServiceConnected", "s" }, { "ServiceDisconnected", "s" }, + { "PortCreated", "s" }, + { "PortRemoved", "s" }, { NULL, NULL } }; diff --git a/serial/manager.h b/serial/manager.h index f09aa294..4004d81e 100644 --- a/serial/manager.h +++ b/serial/manager.h @@ -21,5 +21,11 @@ * */ +#define SERIAL_MANAGER_PATH "/org/bluez/serial" +#define SERIAL_MANAGER_INTERFACE "org.bluez.serial.Manager" + +#define MAX_PATH_LENGTH 32 + int serial_init(DBusConnection *conn); void serial_exit(void); +int rfcomm_release(int16_t id); 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; +} diff --git a/serial/port.h b/serial/port.h index e87dd676..9cab2847 100644 --- a/serial/port.h +++ b/serial/port.h @@ -20,3 +20,14 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ + +typedef void (*open_notify_t) (int fd, int err, void *data); +typedef void (*udata_free_t) (void *data); + +int port_register(DBusConnection *conn, int id, int fd, + const char *name, const char *owner, char *path); + +const char *port_get_owner(DBusConnection *conn, int16_t id); + +int port_open(const char *dev, open_notify_t notify, + void *data, udata_free_t ufree); |