gisi: Refactor the server API
authorAki Niemi <aki.niemi@nokia.com>
Sun, 14 Nov 2010 16:26:42 +0000 (18:26 +0200)
committerAki Niemi <aki.niemi@nokia.com>
Wed, 22 Dec 2010 15:13:46 +0000 (17:13 +0200)
The new server API is a convenience wrapper on the modem API for
servers.

gisi/server.c
gisi/server.h

index 3d96e2d..45c523d 100644 (file)
 #include <stdint.h>
 #include <string.h>
 #include <stdlib.h>
-#include <assert.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-#include <sys/ioctl.h>
 #include <errno.h>
-#include "phonet.h"
 #include <glib.h>
+#include "phonet.h"
 
-#include "socket.h"
 #include "server.h"
 
-#define PN_NAMESERVICE         0xDB
-#define PNS_NAME_ADD_REQ       0x05
-
-struct _GIsiIncoming {
-       struct sockaddr_pn spn;
-       uint8_t trans_id;
+struct pending_data {
+       GIsiServer *server;
+       GIsiNotifyFunc notify;
+       void *data;
 };
 
 struct _GIsiServer {
        GIsiModem *modem;
+       GIsiVersion version;
        uint8_t resource;
-       struct {
-               int major;
-               int minor;
-       } version;
-
-       /* Callbacks */
-       int fd;
-       guint source;
-       GIsiRequestFunc func[256];
-       void *data[256];
-
-       /* Debugging */
-       GIsiDebugFunc debug_func;
-       void *debug_data;
+       GSList *pending;
 };
 
-static gboolean g_isi_server_callback(GIOChannel *channel, GIOCondition cond,
-                                       gpointer data);
-
-/**
- * Create an ISI server.
- * @param resource PhoNet resource ID for the server
- * @return NULL on error (see errno), a GIsiServer pointer on success,
- */
-GIsiServer *g_isi_server_create(GIsiModem *modem, uint8_t resource,
-                               uint8_t major, uint8_t minor)
+static void pending_notify(const GIsiMessage *msg, void *data)
 {
-       void *ptr;
-       GIsiServer *self;
-       GIOChannel *channel;
-
-       if (G_UNLIKELY(posix_memalign(&ptr, 256, sizeof(*self))))
-               abort();
-
-       self = ptr;
-       memset(self, 0, sizeof(*self));
-       self->resource = resource;
-       self->version.major = major;
-       self->version.minor = minor;
-       self->modem = modem;
-       self->debug_func = NULL;
-
-       channel = phonet_new(modem, resource);
-       if (channel == NULL) {
-               free(self);
-               return NULL;
-       }
+       struct pending_data *pd = data;
 
-       self->fd = g_io_channel_unix_get_fd(channel);
-       self->source = g_io_add_watch(channel,
-                                       G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
-                                       g_isi_server_callback, self);
-       g_io_channel_unref(channel);
-       return self;
-}
-
-/**
- * Returns the resource associated with @a server
- * @param server server for the resource
- * @return PhoNet resource ID for the server
- */
-uint8_t g_isi_server_resource(GIsiServer *server)
-{
-       return server->resource;
-}
-
-/**
- * Set a debugging function for @a server. This function will be
- * called whenever an ISI protocol message is sent or received.
- * @param server server to debug
- * @param func debug function
- * @param opaque user data
- */
-void g_isi_server_set_debug(GIsiServer *server, GIsiDebugFunc func,
-                               void *opaque)
-{
-       if (server == NULL)
-               return;
-
-       server->debug_func = func;
-       server->debug_data = opaque;
-}
-
-/**
- * Destroys an ISI server, cancels all pending transactions and subscriptions.
- * @param server server to destroy
- */
-void g_isi_server_destroy(GIsiServer *server)
-{
-       if (server == NULL)
+       if (!pd)
                return;
 
-       g_source_remove(server->source);
-       free(server);
+       if (pd->notify)
+               pd->notify(msg, pd->data);
 }
 
-/**
- * Request the server name from the name server.
- */
-void
-g_isi_server_add_name(GIsiServer *self)
+uint8_t g_isi_server_resource(GIsiServer *server)
 {
-       uint16_t object = 0;
-
-       if (self == NULL)
-               return;
-
-       if (ioctl(self->fd, SIOCPNGETOBJECT, &object) < 0) {
-               g_warning("%s: %s", "ioctl(SIOCPNGETOBJECT)", strerror(errno));
-       } else {
-               struct sockaddr_pn spn = {
-                       .spn_family = PF_PHONET,
-                       .spn_dev = 0,   /* PN_DEV_HOST */
-                       .spn_resource = PN_NAMESERVICE,
-               };
-               uint8_t req[] = {
-                       0, PNS_NAME_ADD_REQ, 0, 0,
-                       0, 0, 0, self->resource,        /* name */
-                       object >> 8, object & 0xff,     /* device/object */
-                       0, 0,
-               };
-
-               if (sendto(self->fd, req, sizeof(req), 0,
-                               (void *)&spn, sizeof(spn)) != sizeof(req)) {
-                       g_warning("%s: %s", "sendto(PN_NAMESERVICE)",
-                                 strerror(errno));
-               }
-       }
+       return server ? server->resource : 0;
 }
 
-/**
- * Make an ISI request and register a callback to process the response(s) to
- * the resulting transaction.
- * @param self ISI server (from g_isi_server_create())
- * @param buf pointer to request payload
- * @param len request payload byte length
- * @param irq information from incoming request
- */
-int g_isi_respond(GIsiServer *self, const void *data, size_t len,
-                       GIsiIncoming *irq)
+GIsiModem *g_isi_server_modem(GIsiServer *server)
 {
-       const struct iovec iov = {
-               .iov_base = (void *)data,
-               .iov_len = len,
-       };
-
-       if (self->debug_func)
-               self->debug_func(data, len, self->debug_data);
-
-       return g_isi_vrespond(self, &iov, 1, irq);
+       return server ? server->modem : 0;
 }
 
-/**
- * Make an ISI request and register a callback to process the response(s) to
- * the resulting transaction.
- * @param self ISI server (from g_isi_server_create())
- * @param iov scatter-gather array to the request payload
- * @param iovlen number of vectors in the scatter-gather array
- * @param irq information from incoming request
- */
-int g_isi_vrespond(GIsiServer *self, const struct iovec *iov, size_t iovlen,
-                       GIsiIncoming *irq)
+GIsiServer *g_isi_server_create(GIsiModem *modem, uint8_t resource,
+                               GIsiVersion *version)
 {
-       struct iovec _iov[1 + iovlen];
-       const struct msghdr msg = {
-               .msg_name = (void *)&irq->spn,
-               .msg_namelen = sizeof(irq->spn),
-               .msg_iov = (struct iovec *)_iov,
-               .msg_iovlen = 1 + iovlen,
-               .msg_control = NULL,
-               .msg_controllen = 0,
-               .msg_flags = 0,
-       };
-       ssize_t ret;
-       size_t i, len;
-
-       if (self == NULL) {
-               errno = EINVAL;
-               return -1;
-       }
+       GIsiServer *server;
 
-       if (irq == NULL) {
+       if (!modem) {
                errno = EINVAL;
-               return -1;
+               return NULL;
        }
 
-       _iov[0].iov_base = &irq->trans_id;
-       _iov[0].iov_len = 1;
-       for (i = 0, len = 1; i < iovlen; i++) {
-               _iov[1 + i] = iov[i];
-               len += iov[i].iov_len;
+       server  = g_try_new0(GIsiServer, 1);
+       if (!server) {
+               errno = ENOMEM;
+               return NULL;
        }
 
-       ret = sendmsg(self->fd, &msg, MSG_NOSIGNAL);
+       if (version)
+               memcpy(&server->version, version, sizeof(GIsiVersion));
 
-       g_free(irq);
+       server->resource = resource;
+       server->modem = modem;
+       server->pending = NULL;
 
-       return ret;
+       return server;
 }
 
-/**
- * Prepare to handle given request type for the resource that an ISI server
- * is associated with. If the same type was already handled, the old
- * handler is overriden.
- * @param self ISI server (from g_isi_server_create())
- * @param type request message type
- * @param cb callback to process received requests
- * @param data data for the callback
- * @return 0 on success, -1 upon an error.
- */
-int g_isi_server_handle(GIsiServer *self, uint8_t type,
-                       GIsiRequestFunc cb, void *data)
+static void foreach_destroy(gpointer value, gpointer user)
 {
-       if (self == NULL || cb == NULL) {
-               errno = EINVAL;
-               return -1;
-       }
+       GIsiPending *op = value;
+       GIsiServer *server = user;
 
-       self->func[type] = cb;
-       self->data[type] = data;
-       return 0;
-}
+       if (!op || !server)
+               return;
 
-/**
- * Remove handler from a given request type.
- * @param server ISI server (from g_isi_server_create())
- * @param type indication type.
- */
-void g_isi_server_unhandle(GIsiServer *self, uint8_t type)
-{
-       if (self)
-               self->func[type] = NULL;
+       server->pending = g_slist_remove(server->pending, op);
+       g_isi_pending_remove(op);
 }
 
-
-static void generic_error_response(GIsiServer *self,
-                       uint8_t trans_id, uint8_t error, uint8_t message_id,
-                       void *addr, socklen_t addrlen)
+void g_isi_server_destroy(GIsiServer *server)
 {
-       uint8_t common[] = { trans_id, 0xF0, error, message_id };
+       if (!server)
+               return;
 
-       sendto(self->fd, common, sizeof(common), MSG_NOSIGNAL, addr, addrlen);
+       g_slist_foreach(server->pending, foreach_destroy, server);
+       g_slist_free(server->pending);
+       g_free(server);
 }
 
-static void process_message(GIsiServer *self, int len)
+int g_isi_server_send(GIsiServer *server, const GIsiMessage *req,
+                       const void *__restrict buf, size_t len)
 {
-       uint8_t msg[len + 1];
-       struct sockaddr_pn addr;
-       socklen_t addrlen = sizeof(addr);
-       uint8_t message_id;
-       GIsiRequestFunc func;
-       void *data;
+       if (!server)
+               return -EINVAL;
 
-       len = recvfrom(self->fd, msg, sizeof(msg), MSG_DONTWAIT,
-                       (void *)&addr, &addrlen);
+       return g_isi_response_send(server->modem, req, buf, len);
+}
 
-       if (len < 2 || addr.spn_resource != self->resource)
-               return;
+int g_isi_server_vsend(GIsiServer *server, const GIsiMessage *req,
+                       const struct iovec *iov, size_t iovlen)
+{
+       if (!server)
+               return -EINVAL;
 
-       if (self->debug_func)
-               self->debug_func(msg + 1, len - 1, self->debug_data);
+       return g_isi_response_vsend(server->modem, req, iov, iovlen);
+}
 
-       message_id = msg[1];
-       func = self->func[message_id];
-       data = self->data[message_id];
+static struct pending_data *pending_data_create(GIsiServer *server,
+                                               GIsiNotifyFunc notify,
+                                               void *data)
+{
+       struct pending_data *pd;
 
-       if (func) {
-               GIsiIncoming *irq = g_new0(GIsiIncoming, 1);
+       if (!server) {
+               errno = EINVAL;
+               return NULL;
+       }
 
-               if (irq) {
-                       irq->spn = addr;
-                       irq->trans_id = msg[0];
-                       func(self, msg + 1, len - 1, irq, data);
-                       return;
-               }
+       pd = g_try_new0(struct pending_data, 1);
+       if (!pd) {
+               errno = ENOMEM;
+               return NULL;
        }
 
-       /* Respond with COMMON MESSAGE COMM_SERVICE_NOT_AUTHENTICATED_RESP */
-       generic_error_response(self, msg[0], 0x17, msg[1], &addr, addrlen);
+       pd->server = server;
+       pd->notify = notify;
+       pd->data = data;
+
+       return pd;
 }
 
-/* Data callback */
-static gboolean g_isi_server_callback(GIOChannel *channel, GIOCondition cond,
-                                       gpointer opaque)
+GIsiPending *g_isi_server_handle(GIsiServer *server, uint8_t type,
+                                       GIsiNotifyFunc notify, void *data)
 {
-       if (cond & (G_IO_NVAL|G_IO_HUP)) {
-               g_warning("Unexpected event on Phonet channel %p", channel);
-               return FALSE;
-       }
+       struct pending_data *pd;
+       GIsiPending *op;
 
-       process_message(opaque, phonet_peek_length(channel));
+       pd = pending_data_create(server, notify, data);
+       if (!pd)
+               return NULL;
+
+       op = g_isi_service_bind(server->modem, server->resource, type,
+                               pending_notify, pd, g_free);
+       if (!op) {
+               g_free(pd);
+               return NULL;
+       }
 
-       return TRUE;
+       server->pending = g_slist_append(server->pending, op);
+       return op;
 }
index f22214f..8f3ac65 100644 (file)
@@ -27,42 +27,28 @@ extern "C" {
 #endif
 
 #include <stdint.h>
-#include <gisi/modem.h>
+#include <sys/uio.h>
+
+#include "message.h"
+#include "modem.h"
 
 struct _GIsiServer;
 typedef struct _GIsiServer GIsiServer;
 
-struct _GIsiIncoming;
-typedef struct _GIsiIncoming GIsiIncoming;
-
-typedef gboolean (*GIsiRequestFunc)(GIsiServer *server,
-                                       const void *restrict data, size_t len,
-                                       GIsiIncoming *, void *opaque);
-
 GIsiServer *g_isi_server_create(GIsiModem *modem, uint8_t resource,
-                               uint8_t major, uint8_t minor);
-
+                               GIsiVersion *version);
 uint8_t g_isi_server_resource(GIsiServer *server);
-
-void g_isi_server_set_debug(GIsiServer *server, GIsiDebugFunc func,
-                               void *opaque);
-
+GIsiModem *g_isi_server_modem(GIsiServer *server);
 void g_isi_server_destroy(GIsiServer *server);
 
-void g_isi_server_add_name(GIsiServer *self);
-
-int g_isi_respond(GIsiServer *server, const void *data, size_t len,
-                       GIsiIncoming *irq);
-
-struct iovec;
-
-int g_isi_vrespond(GIsiServer *server, const struct iovec *iov,
-                       size_t iovlen, GIsiIncoming *irq);
+int g_isi_server_send(GIsiServer *server, const GIsiMessage *req,
+                       const void *__restrict data, size_t len);
 
-int g_isi_server_handle(GIsiServer *server, uint8_t type,
-                       GIsiRequestFunc func, void *opaque);
+int g_isi_server_vsend(GIsiServer *server, const GIsiMessage *req,
+                       const struct iovec *iov, size_t iovlen);
 
-void g_isi_server_unhandle(GIsiServer *server, uint8_t type);
+GIsiPending *g_isi_server_handle(GIsiServer *server, uint8_t type,
+                                       GIsiNotifyFunc notify, void *data);
 
 #ifdef __cplusplus
 }