~cyphermox/ubuntu/natty/ofono/release-0.41

« back to all changes in this revision

Viewing changes to gisi/client.c

  • Committer: Mathieu Trudel-Lapierre
  • Date: 2011-02-11 02:17:20 UTC
  • mfrom: (1.3.2 upstream)
  • Revision ID: mathieu-tl@ubuntu.com-20110211021720-cvxc3erw1keomunj
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
#include <stdint.h>
28
28
#include <string.h>
29
29
#include <stdlib.h>
30
 
#include <search.h>
31
 
#include <sys/types.h>
32
 
#include <sys/socket.h>
33
 
#include <sys/uio.h>
34
30
#include <errno.h>
35
 
#include "phonet.h"
36
31
#include <glib.h>
37
32
 
38
 
#include "socket.h"
39
33
#include "client.h"
40
34
 
41
 
#define PN_COMMGR                       0x10
42
 
#define PNS_SUBSCRIBED_RESOURCES_IND    0x10
43
 
 
44
 
static const struct sockaddr_pn commgr = {
45
 
        .spn_family = AF_PHONET,
46
 
        .spn_resource = PN_COMMGR,
47
 
};
48
 
 
49
 
struct _GIsiRequest {
50
 
        unsigned int id; /* don't move, see g_isi_cmp */
51
 
        GIsiClient *client;
52
 
        guint timeout;
53
 
        GIsiResponseFunc func;
54
 
        void *data;
55
 
        GDestroyNotify notify;
56
 
};
57
 
 
58
 
struct _GIsiIndication {
59
 
        unsigned int type; /* don't move, see g_isi_cmp */
60
 
        GIsiIndicationFunc func;
61
 
        void *data;
62
 
};
63
 
typedef struct _GIsiIndication GIsiIndication;
64
 
 
65
35
struct _GIsiClient {
 
36
        GIsiModem *modem;
 
37
        unsigned timeout;
66
38
        uint8_t resource;
67
 
        uint16_t server_obj;
68
 
        struct {
69
 
                int major;
70
 
                int minor;
71
 
        } version;
72
 
        GIsiModem *modem;
73
 
        int error;
74
 
 
75
 
        /* Requests */
76
 
        struct {
77
 
                int fd;
78
 
                guint source;
79
 
                unsigned int last; /* last used transaction ID */
80
 
                void *pending;
81
 
        } reqs;
82
 
 
83
 
        /* Indications */
84
 
        struct {
85
 
                int fd;
86
 
                guint source;
87
 
                unsigned int count;
88
 
                void *subs;
89
 
        } inds;
90
 
 
91
 
        /* Debugging */
92
 
        GIsiDebugFunc debug_func;
93
 
        void *debug_data;
94
39
};
95
40
 
96
 
static gboolean g_isi_callback(GIOChannel *channel, GIOCondition cond,
97
 
                                gpointer data);
98
 
static gboolean g_isi_timeout(gpointer data);
99
 
 
100
 
static void g_isi_vdebug(const struct iovec *__restrict iov,
101
 
                                size_t iovlen, size_t total_len,
102
 
                                GIsiDebugFunc func, void *data)
103
 
{
104
 
        uint8_t debug[total_len];
105
 
        uint8_t *ptr = debug;
106
 
        size_t i;
107
 
 
108
 
        for (i = 0; i < iovlen; i++) {
109
 
                memcpy(ptr, iov[i].iov_base, iov[i].iov_len);
110
 
                ptr += iov[i].iov_len;
111
 
        }
112
 
 
113
 
        func(debug, total_len, data);
114
 
}
115
 
 
116
 
 
117
 
static int g_isi_cmp(const void *a, const void *b)
118
 
{
119
 
        const unsigned int *ua = (const unsigned int *)a;
120
 
        const unsigned int *ub = (const unsigned int *)b;
121
 
 
122
 
        return *ua - *ub;
123
 
}
124
 
 
125
 
/**
126
 
 * Create an ISI client.
127
 
 * @param resource PhoNet resource ID for the client
128
 
 * @return NULL on error (see errno), a GIsiClient pointer on success,
129
 
 */
 
41
uint8_t g_isi_client_resource(GIsiClient *client)
 
42
{
 
43
        return client != NULL ? client->resource : 0;
 
44
}
 
45
 
 
46
GIsiModem *g_isi_client_modem(GIsiClient *client)
 
47
{
 
48
        return client != NULL ? client->modem : NULL;
 
49
}
 
50
 
130
51
GIsiClient *g_isi_client_create(GIsiModem *modem, uint8_t resource)
131
52
{
132
53
        GIsiClient *client;
133
 
        GIOChannel *channel;
 
54
 
 
55
        if (modem == NULL) {
 
56
                errno = EINVAL;
 
57
                return NULL;
 
58
        }
134
59
 
135
60
        client  = g_try_new0(GIsiClient, 1);
136
 
        if (!client) {
 
61
        if (client == NULL) {
137
62
                errno = ENOMEM;
138
63
                return NULL;
139
64
        }
140
65
 
 
66
        client->timeout = G_ISI_CLIENT_DEFAULT_TIMEOUT;
141
67
        client->resource = resource;
142
 
        client->version.major = -1;
143
 
        client->version.minor = -1;
144
68
        client->modem = modem;
145
 
        client->error = 0;
146
 
        client->debug_func = NULL;
147
 
 
148
 
        client->reqs.last = 0;
149
 
        client->reqs.pending = NULL;
150
 
 
151
 
        client->inds.count = 0;
152
 
        client->inds.subs = NULL;
153
 
 
154
 
        channel = phonet_new(modem, resource);
155
 
        if (!channel) {
156
 
                g_free(client);
157
 
                return NULL;
158
 
        }
159
 
        client->reqs.fd = g_io_channel_unix_get_fd(channel);
160
 
        client->reqs.source = g_io_add_watch(channel,
161
 
                                        G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
162
 
                                        g_isi_callback, client);
163
 
        g_io_channel_unref(channel);
164
69
 
165
70
        return client;
166
71
}
167
72
 
168
 
/**
169
 
 * Set the ISI resource version of @a client.
170
 
 * @param client client for the resource
171
 
 * @param major ISI major version
172
 
 * @param minor ISI minor version
173
 
 */
174
 
void g_isi_version_set(GIsiClient *client, int major, int minor)
175
 
{
176
 
        if (!client)
177
 
                return;
178
 
 
179
 
        client->version.major = major;
180
 
        client->version.minor = minor;
181
 
}
182
 
 
183
 
/**
184
 
 * Returns the ISI major version of the resource associated with @a
185
 
 * client.
186
 
 * @param client client for the resource
187
 
 * @return major version, -1 if not available
188
 
 */
189
 
int g_isi_version_major(GIsiClient *client)
190
 
{
191
 
        return client ? client->version.major : -1;
192
 
}
193
 
 
194
 
/**
195
 
 * Returns the ISI minor version of the resource associated with @a
196
 
 * client.
197
 
 * @param client client for the resource
198
 
 * @return minor version, -1 if not available
199
 
 */
200
 
int g_isi_version_minor(GIsiClient *client)
201
 
{
202
 
        return client ? client->version.minor : -1;
203
 
}
204
 
 
205
 
/**
206
 
 * Set the server object for the resource associated with @a
207
 
 * client.
208
 
 * @param client client for the resource
209
 
 * @param server object
210
 
 */
211
 
void g_isi_server_object_set(GIsiClient *client, uint16_t obj)
212
 
{
213
 
        if (!client)
214
 
                return;
215
 
 
216
 
        client->server_obj = obj;
217
 
}
218
 
 
219
 
/**
220
 
 * Returns the server object for the the resource associated with @a
221
 
 * client.
222
 
 * @param client client for the resource
223
 
 * @return server object
224
 
 */
225
 
uint8_t g_isi_server_object(GIsiClient *client)
226
 
{
227
 
        return client ? client->server_obj : 0;
228
 
}
229
 
 
230
 
/**
231
 
 * Returns the resource associated with @a client
232
 
 * @param client client for the resource
233
 
 * @return PhoNet resource ID for the client
234
 
 */
235
 
uint8_t g_isi_client_resource(GIsiClient *client)
236
 
{
237
 
        return client ? client->resource : 0;
238
 
}
239
 
 
240
 
/**
241
 
 * Set a debugging function for @a client. This function will be
242
 
 * called whenever an ISI protocol message is sent or received.
243
 
 * @param client client to debug
244
 
 * @param func debug function
245
 
 * @param opaque user data
246
 
 */
247
 
void g_isi_client_set_debug(GIsiClient *client, GIsiDebugFunc func,
248
 
                                void *opaque)
249
 
{
250
 
        if (!client)
251
 
                return;
252
 
 
253
 
        client->debug_func = func;
254
 
        client->debug_data = opaque;
255
 
}
256
 
 
257
 
static void g_isi_cleanup_req(void *data)
258
 
{
259
 
        GIsiRequest *req = data;
260
 
 
261
 
        if (!req)
262
 
                return;
263
 
 
264
 
        /* Finalize any pending requests */
265
 
        req->client->error = ESHUTDOWN;
266
 
        if (req->func)
267
 
                req->func(req->client, NULL, 0, 0, req->data);
268
 
        req->client->error = 0;
269
 
 
270
 
        if (req->notify)
271
 
                req->notify(req->data);
272
 
 
273
 
        if (req->timeout > 0)
274
 
                g_source_remove(req->timeout);
275
 
 
276
 
        g_free(req);
277
 
}
278
 
 
279
 
static void g_isi_cleanup_ind(void *data)
280
 
{
281
 
        GIsiIndication *ind = data;
282
 
 
283
 
        if (!ind)
284
 
                return;
285
 
 
286
 
        g_free(ind);
287
 
}
288
 
 
289
 
/**
290
 
 * Destroys an ISI client, cancels all pending transactions and subscriptions.
291
 
 * @param client client to destroy (may be NULL)
292
 
 */
 
73
void g_isi_client_reset(GIsiClient *client)
 
74
{
 
75
        g_isi_remove_pending_by_owner(client->modem, client->resource, client);
 
76
};
 
77
 
293
78
void g_isi_client_destroy(GIsiClient *client)
294
79
{
295
 
        if (!client)
 
80
        if (client == NULL)
296
81
                return;
297
82
 
298
 
        tdestroy(client->reqs.pending, g_isi_cleanup_req);
299
 
        if (client->reqs.source > 0)
300
 
                g_source_remove(client->reqs.source);
301
 
 
302
 
        tdestroy(client->inds.subs, g_isi_cleanup_ind);
303
 
        client->inds.subs = NULL;
304
 
        client->inds.count = 0;
305
 
        g_isi_commit_subscriptions(client);
306
 
        if (client->inds.source > 0)
307
 
                g_source_remove(client->inds.source);
308
 
 
 
83
        g_isi_client_reset(client);
309
84
        g_free(client);
310
85
}
311
86
 
312
 
/**
313
 
 * Make an ISI request and register a callback to process the response(s) to
314
 
 * the resulting transaction.
315
 
 * @param cl ISI client (from g_isi_client_create())
316
 
 * @param buf pointer to request payload
317
 
 * @param len request payload byte length
318
 
 * @param timeout timeout in seconds
319
 
 * @param cb callback to process response(s)
320
 
 * @param opaque data for the callback
321
 
 */
322
 
GIsiRequest *g_isi_request_make(GIsiClient *client, const void *__restrict buf,
323
 
                                size_t len, unsigned timeout,
324
 
                                GIsiResponseFunc cb, void *opaque)
325
 
{
326
 
        return g_isi_send(client, buf, len, timeout, cb, opaque, NULL);
327
 
}
328
 
 
329
 
/**
330
 
 * Make an ISI request and register a callback to process the response(s) to
331
 
 * the resulting transaction.
332
 
 * @param cl ISI client (from g_isi_client_create())
333
 
 * @param iov scatter-gather array to the request payload
334
 
 * @param iovlen number of vectors in the scatter-gather array
335
 
 * @param timeout timeout in seconds
336
 
 * @param cb callback to process response(s)
337
 
 * @param opaque data for the callback
338
 
 */
339
 
GIsiRequest *g_isi_request_vmake(GIsiClient *client, const struct iovec *iov,
340
 
                                        size_t iovlen, unsigned timeout,
341
 
                                        GIsiResponseFunc func, void *opaque)
342
 
{
343
 
        return g_isi_vsend(client, iov, iovlen, timeout, func, opaque, NULL);
344
 
}
345
 
 
346
 
/**
347
 
 * Send an ISI request to a specific Phonet address and register a callback
348
 
 * to process the response(s) to the resulting transaction.
349
 
 *
350
 
 * @param client ISI client (from g_isi_client_create())
351
 
 * @param dst Phonet destination address
352
 
 * @param buf pointer to request payload
353
 
 * @param len request payload byte length
354
 
 * @param timeout timeout in seconds
355
 
 * @param cb callback to process response(s)
356
 
 * @param opaque data for the callback
357
 
 * @param notify finalizer function for the @a opaque data (may be NULL)
358
 
 *
359
 
 * @return
360
 
 * A pointer to a newly created GIsiRequest.
361
 
 *
362
 
 * @errors
363
 
 * If an error occurs, @a errno is set accordingly and a NULL pointer is
364
 
 * returned.
365
 
 */
366
 
GIsiRequest *g_isi_sendto(GIsiClient *client,
367
 
                                struct sockaddr_pn *dst,
 
87
void g_isi_client_set_timeout(GIsiClient *client, unsigned timeout)
 
88
{
 
89
        if (client == NULL)
 
90
                return;
 
91
 
 
92
        client->timeout = timeout;
 
93
}
 
94
 
 
95
gboolean g_isi_client_send(GIsiClient *client,
 
96
                        const void *__restrict msg, size_t len,
 
97
                        GIsiNotifyFunc notify, void *data,
 
98
                        GDestroyNotify destroy)
 
99
{
 
100
        GIsiPending *op;
 
101
 
 
102
        op = g_isi_request_send(client->modem, client->resource, msg, len,
 
103
                                client->timeout, notify, data, destroy);
 
104
 
 
105
        g_isi_pending_set_owner(op, client);
 
106
 
 
107
        return op != NULL;
 
108
}
 
109
 
 
110
gboolean g_isi_client_send_with_timeout(GIsiClient *client,
368
111
                                const void *__restrict buf, size_t len,
369
112
                                unsigned timeout,
370
 
                                GIsiResponseFunc cb, void *opaque,
371
 
                                GDestroyNotify notify)
372
 
{
373
 
        const struct iovec iov = {
374
 
                .iov_base = (void *)buf,
375
 
                .iov_len = len,
376
 
        };
377
 
 
378
 
        return g_isi_vsendto(client, dst, &iov, 1, timeout, cb, opaque, notify);
379
 
}
380
 
 
381
 
 
382
 
/**
383
 
 * Send an ISI request and register a callback to process the response(s) to
384
 
 * the resulting transaction.
385
 
 *
386
 
 * @param cl ISI client (from g_isi_client_create())
387
 
 * @param buf pointer to request payload
388
 
 * @param len request payload byte length
389
 
 * @param timeout timeout in seconds
390
 
 * @param cb callback to process response(s)
391
 
 * @param opaque data for the callback
392
 
 * @param notify finalizer function for the @a opaque data (may be NULL)
393
 
 *
394
 
 * @return
395
 
 * A pointer to a newly created GIsiRequest.
396
 
 *
397
 
 * @errors
398
 
 * If an error occurs, @a errno is set accordingly and a NULL pointer is
399
 
 * returned.
400
 
 */
401
 
GIsiRequest *g_isi_send(GIsiClient *client,
402
 
                        const void *__restrict buf, size_t len,
403
 
                        unsigned timeout,
404
 
                        GIsiResponseFunc cb, void *opaque,
405
 
                        GDestroyNotify notify)
406
 
{
407
 
        const struct iovec iov = {
408
 
                .iov_base = (void *)buf,
409
 
                .iov_len = len,
410
 
        };
411
 
 
412
 
        return g_isi_vsend(client, &iov, 1, timeout, cb, opaque, notify);
413
 
}
414
 
 
415
 
 
416
 
/**
417
 
 * Send an ISI request to a specific Phonet address and register a callback
418
 
 * to process the response(s) to the resulting transaction.
419
 
 *
420
 
 * @param client ISI client (from g_isi_client_create())
421
 
 * @param dst Phonet destination address
422
 
 * @param iov scatter-gather array to the request payload
423
 
 * @param iovlen number of vectors in the scatter-gather array
424
 
 * @param timeout timeout in seconds
425
 
 * @param cb callback to process response(s)
426
 
 * @param opaque data for the callback
427
 
 * @param notify finalizer function for the @a opaque data (may be NULL)
428
 
 *
429
 
 * @return
430
 
 * A pointer to a newly created GIsiRequest.
431
 
 *
432
 
 * @errors
433
 
 * If an error occurs, @a errno is set accordingly and a NULL pointer is
434
 
 * returned.
435
 
 */
436
 
GIsiRequest *g_isi_vsendto(GIsiClient *client,
437
 
                                struct sockaddr_pn *dst,
438
 
                                const struct iovec *__restrict iov,
439
 
                                size_t iovlen, unsigned timeout,
440
 
                                GIsiResponseFunc cb, void *opaque,
441
 
                                GDestroyNotify notify)
442
 
{
443
 
        struct iovec _iov[1 + iovlen];
444
 
        struct msghdr msg = {
445
 
                .msg_name = (void *)dst,
446
 
                .msg_namelen = sizeof(*dst),
447
 
                .msg_iov = _iov,
448
 
                .msg_iovlen = 1 + iovlen,
449
 
                .msg_control = NULL,
450
 
                .msg_controllen = 0,
451
 
                .msg_flags = 0,
452
 
        };
453
 
        ssize_t ret;
454
 
        size_t i, len;
455
 
        unsigned int key;
456
 
        uint8_t id;
457
 
 
458
 
        GIsiRequest *req = NULL;
459
 
        GIsiRequest **old;
460
 
 
461
 
        if (!client) {
462
 
                errno = EINVAL;
463
 
                return NULL;
464
 
        }
465
 
 
466
 
        key = 1 + ((client->reqs.last + 1) % 255);
467
 
 
468
 
        if (cb) {
469
 
                req = g_try_new0(GIsiRequest, 1);
470
 
                if (!req) {
471
 
                        errno = ENOMEM;
472
 
                        return NULL;
473
 
                }
474
 
 
475
 
                req->client = client;
476
 
                req->id = key;
477
 
                req->func = cb;
478
 
                req->data = opaque;
479
 
                req->notify = notify;
480
 
 
481
 
                old = tsearch(req, &client->reqs.pending, g_isi_cmp);
482
 
                if (!old) {
483
 
                        errno = ENOMEM;
484
 
                        goto error;
485
 
                }
486
 
                if (*old == req)
487
 
                        old = NULL;
488
 
 
489
 
        } else
490
 
                old = tfind(&key, &client->reqs.pending, g_isi_cmp);
491
 
 
492
 
        if (old) {
493
 
                /* FIXME: perhaps retry with randomized access after
494
 
                 * initial miss. Although if the rate at which
495
 
                 * requests are sent is so high that the transaction
496
 
                 * ID wraps it's likely there is something wrong and
497
 
                 * we might as well fail here. */
498
 
                errno = EBUSY;
499
 
                goto error;
500
 
        }
501
 
 
502
 
        id = key;
503
 
        _iov[0].iov_base = &id;
504
 
        _iov[0].iov_len = 1;
505
 
 
506
 
        for (i = 0, len = 1; i < iovlen; i++) {
507
 
                _iov[1 + i] = iov[i];
508
 
                len += iov[i].iov_len;
509
 
        }
510
 
 
511
 
        if (client->debug_func)
512
 
                g_isi_vdebug(iov, iovlen, len - 1, client->debug_func,
513
 
                                client->debug_data);
514
 
 
515
 
        ret = sendmsg(client->reqs.fd, &msg, MSG_NOSIGNAL);
516
 
        if (ret == -1)
517
 
                goto error;
518
 
 
519
 
        if (ret != (ssize_t)len) {
520
 
                errno = EMSGSIZE;
521
 
                goto error;
522
 
        }
523
 
 
524
 
        if (req && timeout)
525
 
                req->timeout = g_timeout_add_seconds(timeout, g_isi_timeout,
526
 
                                                        req);
527
 
        client->reqs.last = key;
528
 
        return req;
529
 
 
530
 
error:
531
 
        tdelete(req, &client->reqs.pending, g_isi_cmp);
532
 
        g_free(req);
533
 
 
534
 
        return NULL;
535
 
}
536
 
 
537
 
/**
538
 
 * Send an ISI request and register a callback to process the response(s) to
539
 
 * the resulting transaction.
540
 
 *
541
 
 * @param cl ISI client (from g_isi_client_create())
542
 
 * @param iov scatter-gather array to the request payload
543
 
 * @param iovlen number of vectors in the scatter-gather array
544
 
 * @param timeout timeout in seconds
545
 
 * @param cb callback to process response(s)
546
 
 * @param opaque data for the callback
547
 
 * @param notify finalizer function for the @a opaque data (may be NULL)
548
 
 *
549
 
 * @return
550
 
 * A pointer to a newly created GIsiRequest.
551
 
 *
552
 
 * @errors
553
 
 * If an error occurs, @a errno is set accordingly and a NULL pointer is
554
 
 * returned.
555
 
 */
556
 
GIsiRequest *g_isi_vsend(GIsiClient *client,
557
 
                                const struct iovec *__restrict iov,
558
 
                                size_t iovlen, unsigned timeout,
559
 
                                GIsiResponseFunc cb, void *opaque,
560
 
                                GDestroyNotify notify)
561
 
{
562
 
        struct sockaddr_pn dst = {
563
 
                .spn_family = AF_PHONET,
564
 
        };
565
 
 
566
 
        if (!client) {
567
 
                errno = EINVAL;
568
 
                return NULL;
569
 
        }
570
 
 
571
 
        dst.spn_resource = client->resource;
572
 
 
573
 
        return g_isi_vsendto(client, &dst, iov, iovlen, timeout,
574
 
                                cb, opaque, notify);
575
 
}
576
 
 
577
 
/**
578
 
 * Cancels a pending request, i.e. stop waiting for responses and cancels the
579
 
 * timeout.
580
 
 * @param req request to cancel
581
 
 */
582
 
void g_isi_request_cancel(GIsiRequest *req)
583
 
{
584
 
        if (!req)
585
 
                return;
586
 
 
587
 
        if (req->timeout > 0)
588
 
                g_source_remove(req->timeout);
589
 
 
590
 
        tdelete(req, &req->client->reqs.pending, g_isi_cmp);
591
 
 
592
 
        if (req->notify)
593
 
                req->notify(req->data);
594
 
 
595
 
        g_free(req);
596
 
}
597
 
 
598
 
static uint8_t *__msg;
599
 
static void build_subscribe_msg(const void *nodep,
600
 
                                const VISIT which,
601
 
                                const int depth)
602
 
{
603
 
        GIsiIndication *ind = *(GIsiIndication **)nodep;
604
 
        uint8_t res = ind->type >> 8;
605
 
 
606
 
        switch (which) {
607
 
        case postorder:
608
 
        case leaf:
609
 
                if (__msg[2] && res == __msg[2+__msg[2]])
610
 
                        break;
611
 
                __msg[2]++;
612
 
                __msg[2+__msg[2]] = res;
613
 
                break;
614
 
        default:
615
 
                break;
616
 
        }
617
 
}
618
 
 
619
 
/**
620
 
 * Subscribe indications from the modem.
621
 
 * @param client ISI client (from g_isi_client_create())
622
 
 * @return 0 on success, a system error code otherwise.
623
 
 */
624
 
int g_isi_commit_subscriptions(GIsiClient *client)
625
 
{
626
 
        GIOChannel *channel;
627
 
        uint8_t msg[3+256] = {
628
 
                0, PNS_SUBSCRIBED_RESOURCES_IND,
629
 
                0,
630
 
        };
631
 
 
632
 
        if (!client)
633
 
                return -EINVAL;
634
 
 
635
 
        if (!client->inds.source) {
636
 
                if (client->inds.count == 0)
637
 
                        return 0;
638
 
 
639
 
                channel = phonet_new(client->modem, PN_COMMGR);
640
 
                if (!channel)
641
 
                        return -errno;
642
 
 
643
 
                client->inds.fd = g_io_channel_unix_get_fd(channel);
644
 
 
645
 
                client->inds.source = g_io_add_watch(channel,
646
 
                                                G_IO_IN|G_IO_ERR|
647
 
                                                G_IO_HUP|G_IO_NVAL,
648
 
                                                g_isi_callback, client);
649
 
 
650
 
                g_io_channel_unref(channel);
651
 
        }
652
 
 
653
 
        __msg = msg;
654
 
        twalk(client->inds.subs, build_subscribe_msg);
655
 
 
656
 
        /* Subscribe by sending an indication */
657
 
        sendto(client->inds.fd, msg, 3+msg[2], MSG_NOSIGNAL, (void *)&commgr,
658
 
                sizeof(commgr));
659
 
        return 0;
660
 
}
661
 
 
662
 
/**
663
 
 * Add subscription for a given indication type from the given resource.
664
 
 * If the same type was already subscribed, the old subscription
665
 
 * is overriden. Subscriptions for newly added resources do not become
666
 
 * effective until g_isi_commit_subscriptions() has been called.
667
 
 * @param client ISI client (from g_isi_client_create())
668
 
 * @param res resource id
669
 
 * @param type indication type
670
 
 * @param cb callback to process received indications
671
 
 * @param data data for the callback
672
 
 * @return 0 on success, a system error code otherwise.
673
 
 */
674
 
int g_isi_add_subscription(GIsiClient *client, uint8_t res, uint8_t type,
675
 
                                GIsiIndicationFunc cb, void *data)
676
 
{
677
 
        GIsiIndication *ind;
678
 
        GIsiIndication **old;
679
 
 
680
 
        if (client == NULL || cb == NULL)
681
 
                return -EINVAL;
682
 
 
683
 
        ind = g_try_new0(GIsiIndication, 1);
684
 
        if (!ind)
685
 
                return -ENOMEM;
686
 
 
687
 
        ind->type = (res << 8) | type;
688
 
 
689
 
        old = tsearch(ind, &client->inds.subs, g_isi_cmp);
690
 
        if (!old) {
691
 
                g_free(ind);
692
 
                return -ENOMEM;
693
 
        }
694
 
 
695
 
        /* FIXME: This overrides any existing subscription. We should
696
 
         * enable multiple subscriptions to a single indication in
697
 
         * order to allow efficient client sharing. */
698
 
        if (*old != ind) {
699
 
                g_free(ind);
700
 
                ind = *old;
701
 
        } else
702
 
                client->inds.count++;
703
 
 
704
 
        ind->func = cb;
705
 
        ind->data = data;
706
 
 
707
 
        return 0;
708
 
}
709
 
 
710
 
/**
711
 
 * Subscribe to a given indication type for the resource that an ISI client
712
 
 * is associated with. If the same type was already subscribed, the old
713
 
 * subscription is overriden. For multiple subscriptions,
714
 
 * g_isi_add_subcription() and g_isi_commit_subscriptions() should be used
715
 
 * instead.
716
 
 * @param cl ISI client (from g_isi_client_create())
717
 
 * @param type indication type
718
 
 * @param cb callback to process received indications
719
 
 * @param data data for the callback
720
 
 * @return 0 on success, a system error code otherwise.
721
 
 */
722
 
int g_isi_subscribe(GIsiClient *client, uint8_t type,
723
 
                        GIsiIndicationFunc cb, void *data)
724
 
{
725
 
        int ret;
726
 
 
727
 
        if (!client)
728
 
                return -EINVAL;
729
 
 
730
 
        ret = g_isi_add_subscription(client, client->resource, type, cb, data);
731
 
        if (ret)
732
 
                return ret;
733
 
 
734
 
        return g_isi_commit_subscriptions(client);
735
 
}
736
 
 
737
 
/**
738
 
 * Remove subscription for a given indication type from the given resource.
739
 
 * g_isi_commit_subcsriptions() should be called after modifications to
740
 
 * cancel unnecessary resource subscriptions from the modem.
741
 
 * @param client ISI client (from g_isi_client_create())
742
 
 * @param res resource id
743
 
 * @param type indication type
744
 
 */
745
 
void g_isi_remove_subscription(GIsiClient *client, uint8_t res, uint8_t type)
746
 
{
747
 
        void *ret;
748
 
        GIsiIndication *ind;
749
 
        unsigned int id = (res << 8) | type;
750
 
 
751
 
        if (!client)
752
 
                return;
753
 
 
754
 
        ret = tfind(&id, &client->inds.subs, g_isi_cmp);
755
 
        if (!ret)
756
 
                return;
757
 
 
758
 
        ind = *(GIsiIndication **)ret;
759
 
        tdelete(ind, &client->inds.subs, g_isi_cmp);
760
 
        client->inds.count--;
761
 
        g_free(ind);
762
 
}
763
 
 
764
 
/**
765
 
 * Unsubscribe from a given indication type. For removing multiple
766
 
 * subscriptions, g_isi_remove_subcription() and
767
 
 * g_isi_commit_subscriptions() should be used instead.
768
 
 * @param client ISI client (from g_isi_client_create())
769
 
 * @param type indication type.
770
 
 */
771
 
void g_isi_unsubscribe(GIsiClient *client, uint8_t type)
772
 
{
773
 
        if (!client)
774
 
                return;
775
 
 
776
 
        g_isi_remove_subscription(client, client->resource, type);
777
 
        g_isi_commit_subscriptions(client);
778
 
}
779
 
 
780
 
static void g_isi_dispatch_indication(GIsiClient *client, uint8_t res,
781
 
                                        uint16_t obj, uint8_t *msg,
782
 
                                        size_t len)
783
 
{
784
 
        void *ret;
785
 
        GIsiIndication *ind;
786
 
        unsigned type = (res << 8) | msg[0];
787
 
 
788
 
        ret = tfind(&type, &client->inds.subs, g_isi_cmp);
789
 
        if (!ret)
790
 
                return;
791
 
 
792
 
        ind = *(GIsiIndication **)ret;
793
 
 
794
 
        if (ind->func)
795
 
                ind->func(client, msg, len, obj, ind->data);
796
 
}
797
 
 
798
 
static void g_isi_dispatch_response(GIsiClient *client, uint8_t res,
799
 
                                        uint16_t obj, uint8_t *msg,
800
 
                                        size_t len)
801
 
{
802
 
        void *ret;
803
 
        GIsiRequest *req;
804
 
        unsigned id = msg[0];
805
 
 
806
 
        ret = tfind(&id, &client->reqs.pending, g_isi_cmp);
807
 
        if (!ret) {
808
 
                /* This could either be an unsolicited response, which
809
 
                 * we will ignore, or an incoming request, which we
810
 
                 * handle just like an incoming indication */
811
 
                g_isi_dispatch_indication(client, res, obj, msg + 1, len - 1);
812
 
                return;
813
 
        }
814
 
 
815
 
        req = *(GIsiRequest **)ret;
816
 
 
817
 
        if (!req->func || req->func(client, msg + 1, len - 1, obj, req->data))
818
 
                g_isi_request_cancel(req);
819
 
}
820
 
 
821
 
/* Data callback for both responses and indications */
822
 
static gboolean g_isi_callback(GIOChannel *channel, GIOCondition cond,
823
 
                                gpointer data)
824
 
{
825
 
        GIsiClient *client = data;
826
 
        int fd = g_io_channel_unix_get_fd(channel);
827
 
        int len;
828
 
 
829
 
        if (cond & (G_IO_NVAL|G_IO_HUP)) {
830
 
                g_warning("Unexpected event on Phonet channel %p", channel);
831
 
                return FALSE;
832
 
        }
833
 
 
834
 
        len = phonet_peek_length(channel);
835
 
 
836
 
        if (len > 0) {
837
 
                uint32_t buf[(len + 3) / 4];
838
 
                uint8_t *msg;
839
 
                uint16_t obj;
840
 
                uint8_t res;
841
 
 
842
 
                len = phonet_read(channel, buf, len, &obj, &res);
843
 
                if (len < 2)
844
 
                        return TRUE;
845
 
 
846
 
                msg = (uint8_t *)buf;
847
 
 
848
 
                if (client->debug_func)
849
 
                        client->debug_func(msg + 1, len - 1,
850
 
                                                client->debug_data);
851
 
 
852
 
                if (fd == client->reqs.fd)
853
 
                        g_isi_dispatch_response(client, res, obj, msg, len);
854
 
                else
855
 
                        /* Transaction field at first byte is
856
 
                         * discarded with indications */
857
 
                        g_isi_dispatch_indication(client, res, obj, msg + 1,
858
 
                                                        len - 1);
859
 
        }
860
 
        return TRUE;
861
 
}
862
 
 
863
 
static gboolean g_isi_timeout(gpointer data)
864
 
{
865
 
        GIsiRequest *req = data;
866
 
 
867
 
        req->client->error = ETIMEDOUT;
868
 
        if (req->func)
869
 
                req->func(req->client, NULL, 0, 0, req->data);
870
 
        req->client->error = 0;
871
 
 
872
 
        g_isi_request_cancel(req);
873
 
        return FALSE;
874
 
}
875
 
 
876
 
int g_isi_client_error(const GIsiClient *client)
877
 
{
878
 
        return -client->error;
 
113
                                GIsiNotifyFunc notify, void *data,
 
114
                                GDestroyNotify destroy)
 
115
{
 
116
        GIsiPending *op;
 
117
 
 
118
        op = g_isi_request_send(client->modem, client->resource, buf, len,
 
119
                                timeout, notify, data, destroy);
 
120
 
 
121
        g_isi_pending_set_owner(op, client);
 
122
 
 
123
        return op != NULL;
 
124
}
 
125
 
 
126
gboolean g_isi_client_vsend(GIsiClient *client,
 
127
                        const struct iovec *iov, size_t iovlen,
 
128
                        GIsiNotifyFunc notify, void *data,
 
129
                        GDestroyNotify destroy)
 
130
{
 
131
        GIsiPending *op;
 
132
 
 
133
        op = g_isi_request_vsend(client->modem, client->resource, iov, iovlen,
 
134
                                client->timeout, notify, data, destroy);
 
135
 
 
136
        g_isi_pending_set_owner(op, client);
 
137
 
 
138
        return op != NULL;
 
139
}
 
140
 
 
141
gboolean g_isi_client_vsend_with_timeout(GIsiClient *client,
 
142
                                const struct iovec *__restrict iov,
 
143
                                size_t iovlen, unsigned timeout,
 
144
                                GIsiNotifyFunc notify, void *data,
 
145
                                GDestroyNotify destroy)
 
146
{
 
147
        GIsiPending *op;
 
148
 
 
149
        op = g_isi_request_vsend(client->modem, client->resource, iov, iovlen,
 
150
                                        timeout, notify, data, destroy);
 
151
 
 
152
        g_isi_pending_set_owner(op, client);
 
153
 
 
154
        return op != NULL;
 
155
}
 
156
 
 
157
gboolean g_isi_client_ind_subscribe(GIsiClient *client, uint8_t type,
 
158
                                        GIsiNotifyFunc notify, void *data)
 
159
{
 
160
        GIsiPending *op;
 
161
 
 
162
        op = g_isi_ind_subscribe(client->modem, client->resource, type,
 
163
                                        notify, data, NULL);
 
164
 
 
165
        g_isi_pending_set_owner(op, client);
 
166
 
 
167
        return op != NULL;
 
168
}
 
169
 
 
170
gboolean g_isi_client_ntf_subscribe(GIsiClient *client, uint8_t type,
 
171
                                        GIsiNotifyFunc notify, void *data)
 
172
{
 
173
        GIsiPending *op;
 
174
 
 
175
        op = g_isi_ntf_subscribe(client->modem, client->resource, type,
 
176
                                        notify, data, NULL);
 
177
 
 
178
        g_isi_pending_set_owner(op, client);
 
179
 
 
180
        return op != NULL;
 
181
}
 
182
 
 
183
gboolean g_isi_client_verify(GIsiClient *client, GIsiNotifyFunc notify,
 
184
                                        void *data, GDestroyNotify destroy)
 
185
{
 
186
        GIsiPending *op;
 
187
 
 
188
        op = g_isi_resource_ping(client->modem, client->resource,
 
189
                                        notify, data, destroy);
 
190
 
 
191
        g_isi_pending_set_owner(op, client);
 
192
 
 
193
        return op != NULL;
879
194
}