diff options
Diffstat (limited to 'src/pdispatch.c')
| -rw-r--r-- | src/pdispatch.c | 149 | 
1 files changed, 149 insertions, 0 deletions
diff --git a/src/pdispatch.c b/src/pdispatch.c new file mode 100644 index 00000000..48b6639d --- /dev/null +++ b/src/pdispatch.c @@ -0,0 +1,149 @@ +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include "pdispatch.h" +#include "protocol-native-spec.h" + +struct reply_info { +    struct pdispatch *pdispatch; +    struct reply_info *next, *previous; +    int (*callback)(struct pdispatch *pd, uint32_t command, uint32_t tag, struct tagstruct *t, void *userdata); +    void *userdata; +    uint32_t tag; +    void *mainloop_timeout; +}; + +struct pdispatch { +    struct pa_mainloop_api *mainloop; +    const struct pdispatch_command *command_table; +    unsigned n_commands; +    struct reply_info *replies; +}; + +static void reply_info_free(struct reply_info *r) { +    assert(r && r->pdispatch && r->pdispatch->mainloop); +    r->pdispatch->mainloop->cancel_time(r->pdispatch->mainloop, r->mainloop_timeout); + +    if (r->previous) +        r->previous->next = r->next; +    else +        r->pdispatch->replies = r->next; + +    if (r->next) +        r->next->previous = r->previous; +     +    free(r); +} + +struct pdispatch* pdispatch_new(struct pa_mainloop_api *mainloop, const struct pdispatch_command*table, unsigned entries) { +    struct pdispatch *pd; +    assert(mainloop); + +    assert((entries && table) || (!entries && !table)); +     +    pd = malloc(sizeof(struct pdispatch)); +    assert(pd); +    pd->mainloop = mainloop; +    pd->command_table = table; +    pd->n_commands = entries; +    return pd; +} + +void pdispatch_free(struct pdispatch *pd) { +    assert(pd); +    while (pd->replies) +        reply_info_free(pd->replies); +    free(pd); +} + +int pdispatch_run(struct pdispatch *pd, struct packet*packet, void *userdata) { +    uint32_t tag, command; +    assert(pd && packet); +    struct tagstruct *ts = NULL; +    assert(pd && packet && packet->data); + +    if (packet->length <= 8) +        goto fail; + +    ts = tagstruct_new(packet->data, packet->length); +    assert(ts); +     +    if (tagstruct_getu32(ts, &command) < 0 || +        tagstruct_getu32(ts, &tag) < 0) +        goto fail; + +    if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) { +        struct reply_info *r; +        int done = 0; + +        for (r = pd->replies; r; r = r->next) { +            if (r->tag == tag) { +                int ret = r->callback(r->pdispatch, command, tag, ts, r->userdata); +                reply_info_free(r); +                 +                if (ret < 0) +                    goto fail; +                 +                done = 1; +                break; +            } +        } + +        if (!done) +            goto fail; + +    } else if (pd->command_table && command < pd->n_commands) { +        const struct pdispatch_command *c = pd->command_table+command; + +        if (!c->proc) +            goto fail; +         +        if (c->proc(pd, command, tag, ts, userdata) < 0) +            goto fail; +    } else +        goto fail; +     +    tagstruct_free(ts);     +         +    return 0; + +fail: +    if (ts) +        tagstruct_free(ts);     + +    fprintf(stderr, "protocol-native: invalid packet.\n"); +    return -1; +} + +static void timeout_callback(struct pa_mainloop_api*m, void *id, const struct timeval *tv, void *userdata) { +    struct reply_info*r = userdata; +    assert (r && r->mainloop_timeout == id && r->pdispatch && r->pdispatch->mainloop == m && r->callback); + +    r->callback(r->pdispatch, PA_COMMAND_TIMEOUT, r->tag, NULL, r->userdata); +    reply_info_free(r); +} + +void pdispatch_register_reply(struct pdispatch *pd, uint32_t tag, int timeout, int (*cb)(struct pdispatch *pd, uint32_t command, uint32_t tag, struct tagstruct *t, void *userdata), void *userdata) { +    struct reply_info *r; +    struct timeval tv; +    assert(pd && cb); + +    r = malloc(sizeof(struct reply_info)); +    assert(r); +    r->pdispatch = pd; +    r->callback = cb; +    r->userdata = userdata; +    r->tag = tag; + +    gettimeofday(&tv, NULL); +    tv.tv_sec += timeout; + +    r->mainloop_timeout = pd->mainloop->source_time(pd->mainloop, &tv, timeout_callback, r); +    assert(r->mainloop_timeout); + +    r->previous = NULL; +    r->next = pd->replies; +    if (r->next) +        r->next->previous = r; +    pd->replies = r; +}  | 
