summaryrefslogtreecommitdiffstats
path: root/gisi
diff options
context:
space:
mode:
authorAki Niemi <aki.niemi@nokia.com>2010-11-14 18:25:52 +0200
committerAki Niemi <aki.niemi@nokia.com>2010-12-22 17:13:46 +0200
commit325e555092762577864e2772af3f49931dbfc3f9 (patch)
tree56b0a72275511e4887ce52ffe87960b2d356892c /gisi
parent0ccb96f9aba0b51ffad5874ba99f4d8c1395933a (diff)
downloadofono-325e555092762577864e2772af3f49931dbfc3f9.tar.bz2
gisi: Refactor the client API
The new client API is a convenience wrapper on the modem API for clients.
Diffstat (limited to 'gisi')
-rw-r--r--gisi/client.c881
-rw-r--r--gisi/client.h91
2 files changed, 155 insertions, 817 deletions
diff --git a/gisi/client.c b/gisi/client.c
index 8c0cc72e..d1fad2ea 100644
--- a/gisi/client.c
+++ b/gisi/client.c
@@ -27,853 +27,250 @@
#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;
}
diff --git a/gisi/client.h b/gisi/client.h
index 16c459f0..51acf0aa 100644
--- a/gisi/client.h
+++ b/gisi/client.h
@@ -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
}