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

gisi/client.c
gisi/client.h

index 8c0cc72..d1fad2e 100644 (file)
 #include <stdint.h>
 #include <string.h>
 #include <stdlib.h>
-#include <search.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
 #include <errno.h>
-#include "phonet.h"
 #include <glib.h>
 
-#include "socket.h"
 #include "client.h"
 
-#define PN_COMMGR                      0x10
-#define PNS_SUBSCRIBED_RESOURCES_IND   0x10
-
-static const struct sockaddr_pn commgr = {
-       .spn_family = AF_PHONET,
-       .spn_resource = PN_COMMGR,
-};
-
-struct _GIsiRequest {
-       unsigned int id; /* don't move, see g_isi_cmp */
+struct pending_data {
        GIsiClient *client;
-       guint timeout;
-       GIsiResponseFunc func;
+       GIsiNotifyFunc notify;
        void *data;
-       GDestroyNotify notify;
+       GDestroyNotify destroy;
 };
 
-struct _GIsiIndication {
-       unsigned int type; /* don't move, see g_isi_cmp */
-       GIsiIndicationFunc func;
-       void *data;
-};
-typedef struct _GIsiIndication GIsiIndication;
-
 struct _GIsiClient {
-       uint8_t resource;
-       uint16_t server_obj;
-       struct {
-               int major;
-               int minor;
-       } version;
        GIsiModem *modem;
-       int error;
-
-       /* Requests */
-       struct {
-               int fd;
-               guint source;
-               unsigned int last; /* last used transaction ID */
-               void *pending;
-       } reqs;
-
-       /* Indications */
-       struct {
-               int fd;
-               guint source;
-               unsigned int count;
-               void *subs;
-       } inds;
-
-       /* Debugging */
-       GIsiDebugFunc debug_func;
-       void *debug_data;
+       uint8_t resource;
+       GSList *pending;
 };
 
-static gboolean g_isi_callback(GIOChannel *channel, GIOCondition cond,
-                               gpointer data);
-static gboolean g_isi_timeout(gpointer data);
-
-static void g_isi_vdebug(const struct iovec *__restrict iov,
-                               size_t iovlen, size_t total_len,
-                               GIsiDebugFunc func, void *data)
+static void pending_destroy(gpointer data)
 {
-       uint8_t debug[total_len];
-       uint8_t *ptr = debug;
-       size_t i;
-
-       for (i = 0; i < iovlen; i++) {
-               memcpy(ptr, iov[i].iov_base, iov[i].iov_len);
-               ptr += iov[i].iov_len;
-       }
-
-       func(debug, total_len, data);
-}
+       struct pending_data *pd = data;
 
+       if (!pd)
+               return;
 
-static int g_isi_cmp(const void *a, const void *b)
-{
-       const unsigned int *ua = (const unsigned int *)a;
-       const unsigned int *ub = (const unsigned int *)b;
+       if (pd->destroy)
+               pd->destroy(pd->data);
 
-       return *ua - *ub;
+       g_free(pd);
 }
 
-/**
- * Create an ISI client.
- * @param resource PhoNet resource ID for the client
- * @return NULL on error (see errno), a GIsiClient pointer on success,
- */
-GIsiClient *g_isi_client_create(GIsiModem *modem, uint8_t resource)
+static void pending_resp_notify(const GIsiMessage *msg, void *data)
 {
-       GIsiClient *client;
-       GIOChannel *channel;
-
-       client  = g_try_new0(GIsiClient, 1);
-       if (client == NULL) {
-               errno = ENOMEM;
-               return NULL;
-       }
-
-       client->resource = resource;
-       client->version.major = -1;
-       client->version.minor = -1;
-       client->modem = modem;
-       client->error = 0;
-       client->debug_func = NULL;
-
-       client->reqs.last = 0;
-       client->reqs.pending = NULL;
+       struct pending_data *pd = data;
 
-       client->inds.count = 0;
-       client->inds.subs = NULL;
-
-       channel = phonet_new(modem, resource);
-       if (channel == NULL) {
-               g_free(client);
-               return NULL;
-       }
-       client->reqs.fd = g_io_channel_unix_get_fd(channel);
-       client->reqs.source = g_io_add_watch(channel,
-                                       G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
-                                       g_isi_callback, client);
-       g_io_channel_unref(channel);
-
-       return client;
-}
-
-/**
- * Set the ISI resource version of @a client.
- * @param client client for the resource
- * @param major ISI major version
- * @param minor ISI minor version
- */
-void g_isi_version_set(GIsiClient *client, int major, int minor)
-{
-       if (client == NULL)
+       if (!pd)
                return;
 
-       client->version.major = major;
-       client->version.minor = minor;
-}
+       if (pd->notify)
+               pd->notify(msg, pd->data);
 
-/**
- * Returns the ISI major version of the resource associated with @a
- * client.
- * @param client client for the resource
- * @return major version, -1 if not available
- */
-int g_isi_version_major(GIsiClient *client)
-{
-       return client ? client->version.major : -1;
+       pd->client->pending = g_slist_remove(pd->client->pending,
+                                               g_isi_pending_from_msg(msg));
 }
 
-/**
- * Returns the ISI minor version of the resource associated with @a
- * client.
- * @param client client for the resource
- * @return minor version, -1 if not available
- */
-int g_isi_version_minor(GIsiClient *client)
+static void pending_notify(const GIsiMessage *msg, void *data)
 {
-       return client ? client->version.minor : -1;
-}
+       struct pending_data *pd = data;
 
-/**
- * Set the server object for the resource associated with @a
- * client.
- * @param client client for the resource
- * @param server object
- */
-void g_isi_server_object_set(GIsiClient *client, uint16_t obj)
-{
-       if (client == NULL)
+       if (!pd)
                return;
 
-       client->server_obj = obj;
+       if (pd->notify)
+               pd->notify(msg, pd->data);
 }
 
-/**
- * Returns the server object for the the resource associated with @a
- * client.
- * @param client client for the resource
- * @return server object
- */
-uint8_t g_isi_server_object(GIsiClient *client)
-{
-       return client ? client->server_obj : 0;
-}
-
-/**
- * Returns the resource associated with @a client
- * @param client client for the resource
- * @return PhoNet resource ID for the client
- */
 uint8_t g_isi_client_resource(GIsiClient *client)
 {
        return client ? client->resource : 0;
 }
 
-/**
- * Set a debugging function for @a client. This function will be
- * called whenever an ISI protocol message is sent or received.
- * @param client client to debug
- * @param func debug function
- * @param opaque user data
- */
-void g_isi_client_set_debug(GIsiClient *client, GIsiDebugFunc func,
-                               void *opaque)
+GIsiModem *g_isi_client_modem(GIsiClient *client)
 {
-       if (client == NULL)
-               return;
-
-       client->debug_func = func;
-       client->debug_data = opaque;
+       return client ? client->modem : NULL;
 }
 
-static void g_isi_cleanup_req(void *data)
+GIsiClient *g_isi_client_create(GIsiModem *modem, uint8_t resource)
 {
-       GIsiRequest *req = data;
-
-       if (req == NULL)
-               return;
+       GIsiClient *client;
 
-       /* Finalize any pending requests */
-       req->client->error = ESHUTDOWN;
-       if (req->func)
-               req->func(req->client, NULL, 0, 0, req->data);
-       req->client->error = 0;
+       if (!modem) {
+               errno = EINVAL;
+               return NULL;
+       }
 
-       if (req->notify)
-               req->notify(req->data);
+       client  = g_try_new0(GIsiClient, 1);
+       if (!client) {
+               errno = ENOMEM;
+               return NULL;
+       }
 
-       if (req->timeout > 0)
-               g_source_remove(req->timeout);
+       client->resource = resource;
+       client->modem = modem;
+       client->pending = NULL;
 
-       g_free(req);
+       return client;
 }
 
-static void g_isi_cleanup_ind(void *data)
+static void foreach_destroy(gpointer value, gpointer user)
 {
-       GIsiIndication *ind = data;
+       GIsiPending *op = value;
+       GIsiClient *client = user;
 
-       if (ind == NULL)
+       if (!op || !client)
                return;
 
-       g_free(ind);
+       client->pending = g_slist_remove(client->pending, op);
+       g_isi_pending_remove(op);
 }
 
-/**
- * Destroys an ISI client, cancels all pending transactions and subscriptions.
- * @param client client to destroy (may be NULL)
- */
 void g_isi_client_destroy(GIsiClient *client)
 {
        if (client == NULL)
                return;
 
-       tdestroy(client->reqs.pending, g_isi_cleanup_req);
-       if (client->reqs.source > 0)
-               g_source_remove(client->reqs.source);
-
-       tdestroy(client->inds.subs, g_isi_cleanup_ind);
-       client->inds.subs = NULL;
-       client->inds.count = 0;
-       g_isi_commit_subscriptions(client);
-       if (client->inds.source > 0)
-               g_source_remove(client->inds.source);
-
+       g_slist_foreach(client->pending, foreach_destroy, client);
+       g_slist_free(client->pending);
        g_free(client);
 }
 
-/**
- * Make an ISI request and register a callback to process the response(s) to
- * the resulting transaction.
- * @param cl ISI client (from g_isi_client_create())
- * @param buf pointer to request payload
- * @param len request payload byte length
- * @param timeout timeout in seconds
- * @param cb callback to process response(s)
- * @param opaque data for the callback
- */
-GIsiRequest *g_isi_request_make(GIsiClient *client, const void *__restrict buf,
-                               size_t len, unsigned timeout,
-                               GIsiResponseFunc cb, void *opaque)
+static struct pending_data *pending_data_create(GIsiClient *client,
+                                               GIsiNotifyFunc notify,
+                                               void *data,
+                                               GDestroyNotify destroy)
 {
-       return g_isi_send(client, buf, len, timeout, cb, opaque, NULL);
-}
-
-/**
- * Make an ISI request and register a callback to process the response(s) to
- * the resulting transaction.
- * @param cl ISI client (from g_isi_client_create())
- * @param iov scatter-gather array to the request payload
- * @param iovlen number of vectors in the scatter-gather array
- * @param timeout timeout in seconds
- * @param cb callback to process response(s)
- * @param opaque data for the callback
- */
-GIsiRequest *g_isi_request_vmake(GIsiClient *client, const struct iovec *iov,
-                                       size_t iovlen, unsigned timeout,
-                                       GIsiResponseFunc func, void *opaque)
-{
-       return g_isi_vsend(client, iov, iovlen, timeout, func, opaque, NULL);
-}
-
-/**
- * Send an ISI request to a specific Phonet address and register a callback
- * to process the response(s) to the resulting transaction.
- *
- * @param client ISI client (from g_isi_client_create())
- * @param dst Phonet destination address
- * @param buf pointer to request payload
- * @param len request payload byte length
- * @param timeout timeout in seconds
- * @param cb callback to process response(s)
- * @param opaque data for the callback
- * @param notify finalizer function for the @a opaque data (may be NULL)
- *
- * @return
- * A pointer to a newly created GIsiRequest.
- *
- * @errors
- * If an error occurs, @a errno is set accordingly and a NULL pointer is
- * returned.
- */
-GIsiRequest *g_isi_sendto(GIsiClient *client,
-                               struct sockaddr_pn *dst,
-                               const void *__restrict buf, size_t len,
-                               unsigned timeout,
-                               GIsiResponseFunc cb, void *opaque,
-                               GDestroyNotify notify)
-{
-       const struct iovec iov = {
-               .iov_base = (void *)buf,
-               .iov_len = len,
-       };
-
-       return g_isi_vsendto(client, dst, &iov, 1, timeout, cb, opaque, notify);
-}
-
-
-/**
- * Send an ISI request and register a callback to process the response(s) to
- * the resulting transaction.
- *
- * @param cl ISI client (from g_isi_client_create())
- * @param buf pointer to request payload
- * @param len request payload byte length
- * @param timeout timeout in seconds
- * @param cb callback to process response(s)
- * @param opaque data for the callback
- * @param notify finalizer function for the @a opaque data (may be NULL)
- *
- * @return
- * A pointer to a newly created GIsiRequest.
- *
- * @errors
- * If an error occurs, @a errno is set accordingly and a NULL pointer is
- * returned.
- */
-GIsiRequest *g_isi_send(GIsiClient *client,
-                       const void *__restrict buf, size_t len,
-                       unsigned timeout,
-                       GIsiResponseFunc cb, void *opaque,
-                       GDestroyNotify notify)
-{
-       const struct iovec iov = {
-               .iov_base = (void *)buf,
-               .iov_len = len,
-       };
-
-       return g_isi_vsend(client, &iov, 1, timeout, cb, opaque, notify);
-}
-
-
-/**
- * Send an ISI request to a specific Phonet address and register a callback
- * to process the response(s) to the resulting transaction.
- *
- * @param client ISI client (from g_isi_client_create())
- * @param dst Phonet destination address
- * @param iov scatter-gather array to the request payload
- * @param iovlen number of vectors in the scatter-gather array
- * @param timeout timeout in seconds
- * @param cb callback to process response(s)
- * @param opaque data for the callback
- * @param notify finalizer function for the @a opaque data (may be NULL)
- *
- * @return
- * A pointer to a newly created GIsiRequest.
- *
- * @errors
- * If an error occurs, @a errno is set accordingly and a NULL pointer is
- * returned.
- */
-GIsiRequest *g_isi_vsendto(GIsiClient *client,
-                               struct sockaddr_pn *dst,
-                               const struct iovec *__restrict iov,
-                               size_t iovlen, unsigned timeout,
-                               GIsiResponseFunc cb, void *opaque,
-                               GDestroyNotify notify)
-{
-       struct iovec _iov[1 + iovlen];
-       struct msghdr msg = {
-               .msg_name = (void *)dst,
-               .msg_namelen = sizeof(*dst),
-               .msg_iov = _iov,
-               .msg_iovlen = 1 + iovlen,
-               .msg_control = NULL,
-               .msg_controllen = 0,
-               .msg_flags = 0,
-       };
-       ssize_t ret;
-       size_t i, len;
-       unsigned int key;
-       uint8_t id;
-
-       GIsiRequest *req = NULL;
-       GIsiRequest **old;
+       struct pending_data *pd;
 
        if (client == NULL) {
                errno = EINVAL;
                return NULL;
        }
 
-       key = 1 + ((client->reqs.last + 1) % 255);
-
-       if (cb) {
-               req = g_try_new0(GIsiRequest, 1);
-               if (req == NULL) {
-                       errno = ENOMEM;
-                       return NULL;
-               }
-
-               req->client = client;
-               req->id = key;
-               req->func = cb;
-               req->data = opaque;
-               req->notify = notify;
-
-               old = tsearch(req, &client->reqs.pending, g_isi_cmp);
-               if (old == NULL) {
-                       errno = ENOMEM;
-                       goto error;
-               }
-               if (*old == req)
-                       old = NULL;
-
-       } else
-               old = tfind(&key, &client->reqs.pending, g_isi_cmp);
-
-       if (old) {
-               /* FIXME: perhaps retry with randomized access after
-                * initial miss. Although if the rate at which
-                * requests are sent is so high that the transaction
-                * ID wraps it's likely there is something wrong and
-                * we might as well fail here. */
-               errno = EBUSY;
-               goto error;
+       pd = g_try_new0(struct pending_data, 1);
+       if (!pd) {
+               errno = ENOMEM;
+               return NULL;
        }
 
-       id = key;
-       _iov[0].iov_base = &id;
-       _iov[0].iov_len = 1;
+       pd->client = client;
+       pd->notify = notify;
+       pd->data = data;
+       pd->destroy = destroy;
 
-       for (i = 0, len = 1; i < iovlen; i++) {
-               _iov[1 + i] = iov[i];
-               len += iov[i].iov_len;
-       }
+       return pd;
+}
 
-       if (client->debug_func)
-               g_isi_vdebug(iov, iovlen, len - 1, client->debug_func,
-                               client->debug_data);
+GIsiPending *g_isi_client_send(GIsiClient *client, const void *__restrict buf,
+                               size_t len, unsigned timeout,
+                               GIsiNotifyFunc notify, void *data,
+                               GDestroyNotify destroy)
+{
+       struct pending_data *pd;
+       GIsiPending *op;
 
-       ret = sendmsg(client->reqs.fd, &msg, MSG_NOSIGNAL);
-       if (ret == -1)
-               goto error;
+       pd = pending_data_create(client, notify, data, destroy);
+       if (!pd)
+               return NULL;
 
-       if (ret != (ssize_t)len) {
-               errno = EMSGSIZE;
-               goto error;
+       op = g_isi_request_send(client->modem, client->resource, buf, len, timeout,
+                               pending_resp_notify, pd, pending_destroy);
+       if (!op) {
+               g_free(pd);
+               return NULL;
        }
 
-       if (req && timeout)
-               req->timeout = g_timeout_add_seconds(timeout, g_isi_timeout,
-                                                       req);
-       client->reqs.last = key;
-       return req;
-
-error:
-       tdelete(req, &client->reqs.pending, g_isi_cmp);
-       g_free(req);
-
-       return NULL;
+       client->pending = g_slist_append(client->pending, op);
+       return op;
 }
 
-/**
- * Send an ISI request and register a callback to process the response(s) to
- * the resulting transaction.
- *
- * @param cl ISI client (from g_isi_client_create())
- * @param iov scatter-gather array to the request payload
- * @param iovlen number of vectors in the scatter-gather array
- * @param timeout timeout in seconds
- * @param cb callback to process response(s)
- * @param opaque data for the callback
- * @param notify finalizer function for the @a opaque data (may be NULL)
- *
- * @return
- * A pointer to a newly created GIsiRequest.
- *
- * @errors
- * If an error occurs, @a errno is set accordingly and a NULL pointer is
- * returned.
- */
-GIsiRequest *g_isi_vsend(GIsiClient *client,
+GIsiPending *g_isi_client_vsend(GIsiClient *client,
                                const struct iovec *__restrict iov,
                                size_t iovlen, unsigned timeout,
-                               GIsiResponseFunc cb, void *opaque,
-                               GDestroyNotify notify)
+                               GIsiNotifyFunc notify, void *data,
+                               GDestroyNotify destroy)
 {
-       struct sockaddr_pn dst = {
-               .spn_family = AF_PHONET,
-       };
+       struct pending_data *pd;
+       GIsiPending *op;
 
-       if (client == NULL) {
-               errno = EINVAL;
+       pd = pending_data_create(client, notify, data, destroy);
+       if (!pd)
                return NULL;
-       }
-
-       dst.spn_resource = client->resource;
-
-       return g_isi_vsendto(client, &dst, iov, iovlen, timeout,
-                               cb, opaque, notify);
-}
-
-/**
- * Cancels a pending request, i.e. stop waiting for responses and cancels the
- * timeout.
- * @param req request to cancel
- */
-void g_isi_request_cancel(GIsiRequest *req)
-{
-       if (req == NULL)
-               return;
 
-       if (req->timeout > 0)
-               g_source_remove(req->timeout);
-
-       tdelete(req, &req->client->reqs.pending, g_isi_cmp);
-
-       if (req->notify)
-               req->notify(req->data);
-
-       g_free(req);
-}
-
-static uint8_t *__msg;
-static void build_subscribe_msg(const void *nodep,
-                               const VISIT which,
-                               const int depth)
-{
-       GIsiIndication *ind = *(GIsiIndication **)nodep;
-       uint8_t res = ind->type >> 8;
-
-       switch (which) {
-       case postorder:
-       case leaf:
-               if (__msg[2] && res == __msg[2+__msg[2]])
-                       break;
-               __msg[2]++;
-               __msg[2+__msg[2]] = res;
-               break;
-       default:
-               break;
-       }
-}
-
-/**
- * Subscribe indications from the modem.
- * @param client ISI client (from g_isi_client_create())
- * @return 0 on success, a system error code otherwise.
- */
-int g_isi_commit_subscriptions(GIsiClient *client)
-{
-       GIOChannel *channel;
-       uint8_t msg[3+256] = {
-               0, PNS_SUBSCRIBED_RESOURCES_IND,
-               0,
-       };
-
-       if (client == NULL)
-               return -EINVAL;
-
-       if (!client->inds.source) {
-               if (client->inds.count == 0)
-                       return 0;
-
-               channel = phonet_new(client->modem, PN_COMMGR);
-               if (channel == NULL)
-                       return -errno;
-
-               client->inds.fd = g_io_channel_unix_get_fd(channel);
-
-               client->inds.source = g_io_add_watch(channel,
-                                               G_IO_IN|G_IO_ERR|
-                                               G_IO_HUP|G_IO_NVAL,
-                                               g_isi_callback, client);
-
-               g_io_channel_unref(channel);
+       op = g_isi_request_vsend(client->modem, client->resource, iov, iovlen,
+                                       timeout, pending_resp_notify, pd,
+                                       pending_destroy);
+       if (!op) {
+               g_free(pd);
+               return NULL;
        }
 
-       __msg = msg;
-       twalk(client->inds.subs, build_subscribe_msg);
-
-       /* Subscribe by sending an indication */
-       sendto(client->inds.fd, msg, 3+msg[2], MSG_NOSIGNAL, (void *)&commgr,
-               sizeof(commgr));
-       return 0;
+       client->pending = g_slist_append(client->pending, op);
+       return op;
 }
 
-/**
- * Add subscription for a given indication type from the given resource.
- * If the same type was already subscribed, the old subscription
- * is overriden. Subscriptions for newly added resources do not become
- * effective until g_isi_commit_subscriptions() has been called.
- * @param client ISI client (from g_isi_client_create())
- * @param res resource id
- * @param type indication type
- * @param cb callback to process received indications
- * @param data data for the callback
- * @return 0 on success, a system error code otherwise.
- */
-int g_isi_add_subscription(GIsiClient *client, uint8_t res, uint8_t type,
-                               GIsiIndicationFunc cb, void *data)
+GIsiPending *g_isi_client_ind_subscribe(GIsiClient *client, uint8_t type,
+                                       GIsiNotifyFunc notify, void *data)
 {
-       GIsiIndication *ind;
-       GIsiIndication **old;
-
-       if (client == NULL || cb == NULL)
-               return -EINVAL;
+       struct pending_data *pd;
+       GIsiPending *op;
 
-       ind = g_try_new0(GIsiIndication, 1);
-       if (ind == NULL)
-               return -ENOMEM;
-
-       ind->type = (res << 8) | type;
+       pd = pending_data_create(client, notify, data, NULL);
+       if (!pd)
+               return NULL;
 
-       old = tsearch(ind, &client->inds.subs, g_isi_cmp);
-       if (old == NULL) {
-               g_free(ind);
-               return -ENOMEM;
+       op = g_isi_ind_subscribe(client->modem, client->resource, type,
+                                       pending_notify, pd, pending_destroy);
+       if (!op) {
+               g_free(pd);
+               return NULL;
        }
 
-       /* FIXME: This overrides any existing subscription. We should
-        * enable multiple subscriptions to a single indication in
-        * order to allow efficient client sharing. */
-       if (*old != ind) {
-               g_free(ind);
-               ind = *old;
-       } else
-               client->inds.count++;
-
-       ind->func = cb;
-       ind->data = data;
-
-       return 0;
-}
-
-/**
- * Subscribe to a given indication type for the resource that an ISI client
- * is associated with. If the same type was already subscribed, the old
- * subscription is overriden. For multiple subscriptions,
- * g_isi_add_subcription() and g_isi_commit_subscriptions() should be used
- * instead.
- * @param cl ISI client (from g_isi_client_create())
- * @param type indication type
- * @param cb callback to process received indications
- * @param data data for the callback
- * @return 0 on success, a system error code otherwise.
- */
-int g_isi_subscribe(GIsiClient *client, uint8_t type,
-                       GIsiIndicationFunc cb, void *data)
-{
-       int ret;
-
-       if (client == NULL)
-               return -EINVAL;
-
-       ret = g_isi_add_subscription(client, client->resource, type, cb, data);
-       if (ret)
-               return ret;
-
-       return g_isi_commit_subscriptions(client);
-}
-
-/**
- * Remove subscription for a given indication type from the given resource.
- * g_isi_commit_subcsriptions() should be called after modifications to
- * cancel unnecessary resource subscriptions from the modem.
- * @param client ISI client (from g_isi_client_create())
- * @param res resource id
- * @param type indication type
- */
-void g_isi_remove_subscription(GIsiClient *client, uint8_t res, uint8_t type)
-{
-       void *ret;
-       GIsiIndication *ind;
-       unsigned int id = (res << 8) | type;
-
-       if (client == NULL)
-               return;
-
-       ret = tfind(&id, &client->inds.subs, g_isi_cmp);
-       if (ret == NULL)
-               return;
-
-       ind = *(GIsiIndication **)ret;
-       tdelete(ind, &client->inds.subs, g_isi_cmp);
-       client->inds.count--;
-       g_free(ind);
+       client->pending = g_slist_append(client->pending, op);
+       return op;
 }
 
-/**
- * Unsubscribe from a given indication type. For removing multiple
- * subscriptions, g_isi_remove_subcription() and
- * g_isi_commit_subscriptions() should be used instead.
- * @param client ISI client (from g_isi_client_create())
- * @param type indication type.
- */
-void g_isi_unsubscribe(GIsiClient *client, uint8_t type)
+GIsiPending *g_isi_client_ntf_subscribe(GIsiClient *client, uint8_t type,
+                                       GIsiNotifyFunc notify, void *data)
 {
-       if (client == NULL)
-               return;
+       struct pending_data *pd;
+       GIsiPending *op;
 
-       g_isi_remove_subscription(client, client->resource, type);
-       g_isi_commit_subscriptions(client);
-}
-
-static void g_isi_dispatch_indication(GIsiClient *client, uint8_t res,
-                                       uint16_t obj, uint8_t *msg,
-                                       size_t len)
-{
-       void *ret;
-       GIsiIndication *ind;
-       unsigned type = (res << 8) | msg[0];
-
-       ret = tfind(&type, &client->inds.subs, g_isi_cmp);
-       if (ret == NULL)
-               return;
-
-       ind = *(GIsiIndication **)ret;
-
-       if (ind->func)
-               ind->func(client, msg, len, obj, ind->data);
-}
+       pd = pending_data_create(client, notify, data, NULL);
+       if (!pd)
+               return NULL;
 
-static void g_isi_dispatch_response(GIsiClient *client, uint8_t res,
-                                       uint16_t obj, uint8_t *msg,
-                                       size_t len)
-{
-       void *ret;
-       GIsiRequest *req;
-       unsigned id = msg[0];
-
-       ret = tfind(&id, &client->reqs.pending, g_isi_cmp);
-       if (ret == NULL) {
-               /* This could either be an unsolicited response, which
-                * we will ignore, or an incoming request, which we
-                * handle just like an incoming indication */
-               g_isi_dispatch_indication(client, res, obj, msg + 1, len - 1);
-               return;
+       op = g_isi_ntf_subscribe(client->modem, client->resource, type,
+                                       pending_notify, pd, pending_destroy);
+       if (!op) {
+               g_free(pd);
+               return NULL;
        }
 
-       req = *(GIsiRequest **)ret;
-
-       if (!req->func || req->func(client, msg + 1, len - 1, obj, req->data))
-               g_isi_request_cancel(req);
+       client->pending = g_slist_append(client->pending, op);
+       return op;
 }
 
-/* Data callback for both responses and indications */
-static gboolean g_isi_callback(GIOChannel *channel, GIOCondition cond,
-                               gpointer data)
+GIsiPending *g_isi_client_verify(GIsiClient *client, GIsiNotifyFunc notify,
+                                       void *data, GDestroyNotify destroy)
 {
-       GIsiClient *client = data;
-       int fd = g_io_channel_unix_get_fd(channel);
-       int len;
-
-       if (cond & (G_IO_NVAL|G_IO_HUP)) {
-               g_warning("Unexpected event on Phonet channel %p", channel);
-               return FALSE;
-       }
-
-       len = phonet_peek_length(channel);
+       struct pending_data *pd;
+       GIsiPending *op;
 
-       if (len > 0) {
-               uint32_t buf[(len + 3) / 4];
-               uint8_t *msg;
-               uint16_t obj;
-               uint8_t res;
-
-               len = phonet_read(channel, buf, len, &obj, &res);
-               if (len < 2)
-                       return TRUE;
-
-               msg = (uint8_t *)buf;
-
-               if (client->debug_func)
-                       client->debug_func(msg + 1, len - 1,
-                                               client->debug_data);
+       pd = pending_data_create(client, notify, data, destroy);
+       if (!pd)
+               return NULL;
 
-               if (fd == client->reqs.fd)
-                       g_isi_dispatch_response(client, res, obj, msg, len);
-               else
-                       /* Transaction field at first byte is
-                        * discarded with indications */
-                       g_isi_dispatch_indication(client, res, obj, msg + 1,
-                                                       len - 1);
+       op = g_isi_resource_ping(client->modem, client->resource,
+                                       pending_resp_notify, pd,
+                                       pending_destroy);
+       if (!op) {
+               g_free(pd);
+               return NULL;
        }
-       return TRUE;
-}
 
-static gboolean g_isi_timeout(gpointer data)
-{
-       GIsiRequest *req = data;
-
-       req->client->error = ETIMEDOUT;
-       if (req->func)
-               req->func(req->client, NULL, 0, 0, req->data);
-       req->client->error = 0;
-
-       g_isi_request_cancel(req);
-       return FALSE;
-}
-
-int g_isi_client_error(const GIsiClient *client)
-{
-       return -client->error;
+       client->pending = g_slist_append(client->pending, op);
+       return op;
 }
index 16c459f..51acf0a 100644 (file)
@@ -28,93 +28,34 @@ extern "C" {
 
 #include <stdint.h>
 #include <glib/gtypes.h>
-#include <gisi/modem.h>
-#include "phonet.h"
+
+#include "modem.h"
 
 struct _GIsiClient;
 typedef struct _GIsiClient GIsiClient;
 
-struct _GIsiRequest;
-typedef struct _GIsiRequest GIsiRequest;
-
-typedef void (*GIsiVerifyFunc)(GIsiClient *client, gboolean alive,
-                               uint16_t object, void *opaque);
-
-typedef gboolean (*GIsiResponseFunc)(GIsiClient *client,
-                                       const void *restrict data, size_t len,
-                                       uint16_t object, void *opaque);
-
-typedef void (*GIsiIndicationFunc) (GIsiClient *client,
-                                       const void *restrict data, size_t len,
-                                       uint16_t object, void *opaque);
-
 GIsiClient *g_isi_client_create(GIsiModem *modem, uint8_t resource);
-
-GIsiRequest *g_isi_verify(GIsiClient *client, GIsiVerifyFunc func,
-                               void *opaque);
-
-GIsiRequest *g_isi_verify_resource(GIsiClient *client, uint8_t resource,
-                                       GIsiVerifyFunc func, void *opaque);
-
+GIsiModem *g_isi_client_modem(GIsiClient *client);
 uint8_t g_isi_client_resource(GIsiClient *client);
-
-void g_isi_version_set(GIsiClient *client, int major, int minor);
-int g_isi_version_major(GIsiClient *client);
-int g_isi_version_minor(GIsiClient *client);
-
-void g_isi_server_object_set(GIsiClient *client, uint16_t obj);
-uint8_t g_isi_server_object(GIsiClient *client);
-
-void g_isi_client_set_debug(GIsiClient *client, GIsiDebugFunc func,
-                               void *opaque);
-
 void g_isi_client_destroy(GIsiClient *client);
 
-int g_isi_client_error(const GIsiClient *client);
-
-GIsiRequest *g_isi_request_make(GIsiClient *client, const void *data,
+GIsiPending *g_isi_client_send(GIsiClient *client, const void *__restrict msg,
                                size_t len, unsigned timeout,
-                               GIsiResponseFunc func, void *opaque);
-struct iovec;
-GIsiRequest *g_isi_request_vmake(GIsiClient *client, const struct iovec *iov,
-                                       size_t iovlen, unsigned timeout,
-                                       GIsiResponseFunc func, void *opaque);
-
-GIsiRequest *g_isi_sendto(GIsiClient *client,
-                               struct sockaddr_pn *dst,
-                               const void *data, size_t len,
-                               unsigned timeout,
-                               GIsiResponseFunc func, void *opaque,
-                               GDestroyNotify notify);
-
-GIsiRequest *g_isi_send(GIsiClient *client, const void *data, size_t len,
-                       unsigned timeout,
-                       GIsiResponseFunc func, void *opaque,
-                       GDestroyNotify notify);
-
-GIsiRequest *g_isi_vsendto(GIsiClient *client,
-                               struct sockaddr_pn *dst,
-                               const struct iovec *iov, size_t iovlen,
-                               unsigned timeout,
-                               GIsiResponseFunc func, void *opaque,
-                               GDestroyNotify notify);
-
-GIsiRequest *g_isi_vsend(GIsiClient *client,
-                               const struct iovec *iov, size_t iovlen,
-                               unsigned timeout,
-                               GIsiResponseFunc func, void *opaque,
-                               GDestroyNotify notify);
+                               GIsiNotifyFunc notify, void *data,
+                               GDestroyNotify destroy);
 
-void g_isi_request_cancel(GIsiRequest *req);
+GIsiPending *g_isi_client_vsend(GIsiClient *client, const struct iovec *iov,
+                               size_t iovlen, unsigned timeout,
+                               GIsiNotifyFunc notify, void *data,
+                               GDestroyNotify destroy);
 
-int g_isi_commit_subscriptions(GIsiClient *client);
-int g_isi_add_subscription(GIsiClient *client, uint8_t res, uint8_t type,
-                               GIsiIndicationFunc cb, void *data);
-void g_isi_remove_subscription(GIsiClient *client, uint8_t res, uint8_t type);
+GIsiPending *g_isi_client_ind_subscribe(GIsiClient *client, uint8_t type,
+                                       GIsiNotifyFunc notify, void *data);
+GIsiPending *g_isi_client_ntf_subscribe(GIsiClient *client, uint8_t type,
+                                       GIsiNotifyFunc notify, void *data);
 
-int g_isi_subscribe(GIsiClient *client, uint8_t type,
-                       GIsiIndicationFunc func, void *opaque);
-void g_isi_unsubscribe(GIsiClient *client, uint8_t type);
+GIsiPending *g_isi_client_verify(GIsiClient *client, GIsiNotifyFunc notify,
+                                       void *data, GDestroyNotify destroy);
 
 #ifdef __cplusplus
 }