From 325e555092762577864e2772af3f49931dbfc3f9 Mon Sep 17 00:00:00 2001 From: Aki Niemi Date: Sun, 14 Nov 2010 18:25:52 +0200 Subject: gisi: Refactor the client API The new client API is a convenience wrapper on the modem API for clients. --- gisi/client.c | 881 +++++++++------------------------------------------------- gisi/client.h | 91 ++---- 2 files changed, 155 insertions(+), 817 deletions(-) (limited to 'gisi') 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 #include #include -#include -#include -#include -#include #include -#include "phonet.h" #include -#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 #include -#include -#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 } -- cgit v1.2.3