add support for implicit audio resources
[profile/ivi/pulseaudio-module-murphy-ivi.git] / murphy / murphyif.c
1 /*
2  * module-murphy-ivi -- PulseAudio module for providing audio routing support
3  * Copyright (c) 2012, Intel Corporation.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU Lesser General Public License,
7  * version 2.1, as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.
12  * See the GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston,
17  * MA 02110-1301 USA.
18  *
19  */
20 #include <sys/types.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <errno.h>
24
25 #include <pulse/utf8.h>
26 #include <pulsecore/pulsecore-config.h>
27 #include <pulsecore/module.h>
28 #include <pulsecore/llist.h>
29 #include <pulsecore/idxset.h>
30 #include <pulsecore/hashmap.h>
31 #include <pulsecore/core-util.h>
32 #include <pulsecore/sink-input.h>
33 #include <pulsecore/source-output.h>
34
35 #ifdef WITH_MURPHYIF
36 #define WITH_DOMCTL
37 #define WITH_RESOURCES
38 #endif
39
40 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
41 #include <murphy/common/macros.h>
42 #include <murphy/common/mainloop.h>
43 #include <murphy/pulse/pulse-glue.h>
44 #endif
45
46 #ifdef WITH_RESOURCES
47 #include <murphy/resource/protocol.h>
48 #include <murphy/common/transport.h>
49 #include <murphy/resource/protocol.h>
50 #include <murphy/resource/data-types.h>
51 #endif
52
53 #include "murphyif.h"
54 #include "node.h"
55
56 #ifdef WITH_RESOURCES
57 #define INVALID_ID      (~(uint32_t)0)
58 #define INVALID_INDEX   (~(uint32_t)0)
59 #define INVALID_SEQNO   (~(uint32_t)0)
60 #define INVALID_REQUEST (~(uint16_t)0)
61
62 #define PUSH_VALUE(msg, tag, typ, val) \
63     mrp_msg_append(msg, MRP_MSG_TAG_##typ(RESPROTO_##tag, val))
64
65 #define PUSH_ATTRS(msg, rif, proplist)                  \
66     resource_push_attributes(msg, rif, proplist)
67
68 typedef struct resource_attribute  resource_attribute;
69 typedef struct resource_request    resource_request;
70
71 struct resource_attribute {
72     PA_LLIST_FIELDS(resource_attribute);
73     const char *prop;
74     mrp_attr_t  def;
75 };
76
77 struct resource_request {
78     PA_LLIST_FIELDS(resource_request);
79     uint32_t nodidx;
80     uint16_t reqid;
81     uint32_t seqno;
82 };
83
84 #endif
85
86 typedef struct {
87     const char           *addr;
88 #ifdef WITH_DOMCTL
89     mrp_domctl_t         *ctl;
90     int                   ntable;
91     mrp_domctl_table_t   *tables;
92     int                   nwatch;
93     mrp_domctl_watch_t   *watches;
94     pa_murphyif_watch_cb  watchcb;
95 #endif
96 } domctl_interface;
97
98 typedef struct {
99     const char      *addr;
100     const char      *inpres;
101     const char      *outres;
102 #ifdef WITH_RESOURCES
103     mrp_transport_t *transp;
104     mrp_sockaddr_t   saddr;
105     socklen_t        alen;
106     const char      *atype;
107     pa_bool_t        connected;
108     struct {
109         uint32_t request;
110         uint32_t reply;
111     }                seqno;
112     pa_hashmap      *nodes;
113     PA_LLIST_HEAD(resource_attribute, attrs);
114     PA_LLIST_HEAD(resource_request, reqs);
115 #endif
116 } resource_interface;
117
118
119 struct pa_murphyif {
120 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
121     mrp_mainloop_t *ml;
122 #endif
123     domctl_interface domctl;
124     resource_interface resource;
125     pa_hashmap *nodes;
126 };
127
128
129 #ifdef WITH_DOMCTL
130 static void domctl_connect_notify(mrp_domctl_t *,int,int,const char *,void *);
131 static void domctl_watch_notify(mrp_domctl_t *,mrp_domctl_data_t *,int,void *);
132 static void domctl_dump_data(mrp_domctl_data_t *);
133 #endif
134
135 #ifdef WITH_RESOURCES
136 static void       resource_attribute_destroy(resource_interface *,
137                                              resource_attribute *);
138 static pa_bool_t  resource_transport_connect(resource_interface *);
139 static void       resource_xport_closed_evt(mrp_transport_t *, int, void *);
140
141 static mrp_msg_t *resource_create_request(uint32_t, mrp_resproto_request_t);
142 static pa_bool_t  resource_send_message(resource_interface *, mrp_msg_t *,
143                                         uint32_t, uint16_t, uint32_t);
144 static pa_bool_t  resource_set_create(struct userdata *, uint32_t,
145                                       mir_direction, const char *,
146                                       const char *, uint32_t, pa_proplist *);
147 static pa_bool_t  resource_set_destroy(struct userdata *, uint32_t);
148 static pa_bool_t  resource_set_acquire(struct userdata *, uint32_t, uint32_t);
149 static pa_bool_t  resource_push_attributes(mrp_msg_t *, resource_interface *,
150                                            pa_proplist *);
151
152 static void       resource_recv_msg(mrp_transport_t *, mrp_msg_t *, void *);
153 static void       resource_recvfrom_msg(mrp_transport_t *, mrp_msg_t *,
154                                         mrp_sockaddr_t *, socklen_t, void *);
155 static void       resource_set_create_response(struct userdata *, mir_node *,
156                                                mrp_msg_t *, void **);
157
158 static pa_bool_t  resource_fetch_seqno(mrp_msg_t *, void **, uint32_t *);
159 static pa_bool_t  resource_fetch_request(mrp_msg_t *, void **, uint16_t *);
160 static pa_bool_t  resource_fetch_status(mrp_msg_t *, void **, int *);
161 static pa_bool_t  resource_fetch_rset_id(mrp_msg_t *, void **, uint32_t*);
162 static pa_bool_t  resource_fetch_rset_state(mrp_msg_t *, void **,
163                                             mrp_resproto_state_t *);
164 static pa_bool_t  resource_fetch_rset_mask(mrp_msg_t *, void **,
165                                            mrp_resproto_state_t *);
166 #endif
167
168
169 pa_murphyif *pa_murphyif_init(struct userdata *u,
170                               const char *ctl_addr,
171                               const char *res_addr)
172 {
173 #ifdef WITH_RESOURCES
174     static mrp_transport_evt_t ev = {
175         { .recvmsg     = resource_recv_msg },
176         { .recvmsgfrom = resource_recvfrom_msg },
177         .closed        = resource_xport_closed_evt,
178         .connection    = NULL
179     };
180 #endif
181
182     pa_murphyif *murphyif;
183     domctl_interface *dif;
184     resource_interface *rif;
185 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
186     mrp_mainloop_t *ml;
187
188     if (!(ml = mrp_mainloop_pulse_get(u->core->mainloop))) {
189         pa_log_error("Failed to set up murphy mainloop.");
190         return NULL;
191     }
192 #endif
193 #ifdef WITH_RESOURCES
194 #endif
195
196     murphyif = pa_xnew0(pa_murphyif, 1);
197     dif = &murphyif->domctl;
198     rif = &murphyif->resource;
199
200 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
201     murphyif->ml = ml;
202 #endif
203
204     dif->addr = pa_xstrdup(ctl_addr ? ctl_addr:MRP_DEFAULT_DOMCTL_ADDRESS);
205 #ifdef WITH_DOMCTL
206 #endif
207
208     rif->addr = pa_xstrdup(res_addr ? res_addr:RESPROTO_DEFAULT_ADDRESS);
209 #ifdef WITH_RESOURCES
210     rif->alen = mrp_transport_resolve(NULL, rif->addr, &rif->saddr,
211                                       sizeof(rif->saddr), &rif->atype);
212     if (rif->alen <= 0) {
213         pa_log("can't resolve resource transport address '%s'", rif->addr);
214     }
215     else {
216         rif->transp = mrp_transport_create(murphyif->ml, rif->atype, &ev, u,0);
217
218         if ((rif->transp))
219             resource_transport_connect(rif);
220         else
221             pa_log("failed to create resource transport");
222     }    
223
224     rif->seqno.request = 1;
225     rif->nodes = pa_hashmap_new(pa_idxset_trivial_hash_func,
226                                 pa_idxset_trivial_compare_func);
227     PA_LLIST_HEAD_INIT(resource_attribute, rif->attrs);
228     PA_LLIST_HEAD_INIT(resource_request, rif->reqs);
229 #endif
230
231     murphyif->nodes = pa_hashmap_new(pa_idxset_trivial_hash_func,
232                                      pa_idxset_trivial_compare_func);
233     return murphyif;
234 }
235
236
237 void pa_murphyif_done(struct userdata *u)
238 {
239     pa_murphyif *murphyif;
240     domctl_interface *dif;
241     resource_interface *rif;
242 #ifdef WITH_RESOURCES
243     resource_attribute *attr, *a;
244     resource_request *req, *r;
245 #endif
246
247     if (u && (murphyif = u->murphyif)) {
248 #ifdef WITH_DOMCTL
249         mrp_domctl_table_t *t;
250         mrp_domctl_watch_t *w;
251         int i;
252
253         dif = &murphyif->domctl;
254         rif = &murphyif->resource;
255
256         mrp_domctl_destroy(dif->ctl);
257         mrp_mainloop_destroy(murphyif->ml);
258
259         if (dif->ntable > 0 && dif->tables) {
260             for (i = 0;  i < dif->ntable;  i++) {
261                 t = dif->tables + i;
262                 pa_xfree((void *)t->table);
263                 pa_xfree((void *)t->mql_columns);
264                 pa_xfree((void *)t->mql_index);
265             }
266             pa_xfree(dif->tables);
267         }
268
269         if (dif->nwatch > 0 && dif->watches) {
270             for (i = 0;  i < dif->nwatch;  i++) {
271                 w = dif->watches + i;
272                 pa_xfree((void *)w->table);
273                 pa_xfree((void *)w->mql_columns);
274                 pa_xfree((void *)w->mql_where);
275             }
276             pa_xfree(dif->watches);
277         }
278 #endif
279
280 #ifdef WITH_RESOURCES
281         pa_xfree((void *)rif->atype);
282         pa_hashmap_free(rif->nodes, NULL, NULL);
283
284         PA_LLIST_FOREACH_SAFE(attr, a, rif->attrs)
285             resource_attribute_destroy(rif, attr);
286
287         PA_LLIST_FOREACH_SAFE(req, r, rif->reqs)
288             pa_xfree(req);
289 #endif
290
291         pa_xfree((void *)dif->addr);
292         pa_xfree((void *)rif->addr);
293
294         pa_hashmap_free(murphyif->nodes, NULL, NULL);
295
296         pa_xfree(murphyif);
297     }
298 }
299
300
301
302 void pa_murphyif_add_table(struct userdata *u,
303                            const char *table,
304                            const char *columns,
305                            const char *index)
306 {
307     pa_murphyif *murphyif;
308     domctl_interface *dif;
309     mrp_domctl_table_t *t;
310     size_t size;
311     size_t idx;
312     
313     pa_assert(u);
314     pa_assert(table);
315     pa_assert(columns);
316     pa_assert_se((murphyif = u->murphyif));
317
318     dif = &murphyif->domctl;
319
320     idx = dif->ntable++;
321     size = sizeof(mrp_domctl_table_t) * dif->ntable;
322     t = (dif->tables = pa_xrealloc(dif->tables, size)) + idx;
323
324     t->table = pa_xstrdup(table);
325     t->mql_columns = pa_xstrdup(columns);
326     t->mql_index = index ? pa_xstrdup(index) : NULL;
327 }
328
329 void pa_murphyif_add_watch(struct userdata *u,
330                            const char *table,
331                            const char *columns,
332                            const char *where,
333                            int max_rows)
334 {
335     pa_murphyif *murphyif;
336     domctl_interface *dif;
337     mrp_domctl_watch_t *w;
338     size_t size;
339     size_t idx;
340     
341     pa_assert(u);
342     pa_assert(table);
343     pa_assert(columns);
344     pa_assert(max_rows > 0 && max_rows < MQI_QUERY_RESULT_MAX);
345     pa_assert_se((murphyif = u->murphyif));
346
347     dif = &murphyif->domctl;
348
349     idx = dif->nwatch++;
350     size = sizeof(mrp_domctl_watch_t) * dif->nwatch;
351     w = (dif->watches = pa_xrealloc(dif->watches, size)) + idx;
352
353     w->table = pa_xstrdup(table);
354     w->mql_columns = pa_xstrdup(columns);
355     w->mql_where = where ? pa_xstrdup(where) : NULL;
356     w->max_rows = max_rows;
357 }
358
359 void pa_murphyif_setup_domainctl(struct userdata *u, pa_murphyif_watch_cb wcb)
360 {
361     static const char *name = "pulse";
362
363     pa_murphyif *murphyif;
364     domctl_interface *dif;
365
366     pa_assert(u);
367     pa_assert(wcb);
368     pa_assert_se((murphyif = u->murphyif));
369
370     dif = &murphyif->domctl;
371
372 #ifdef WITH_DOMCTL
373     if (dif->ntable || dif->nwatch) {
374         dif->ctl = mrp_domctl_create(name, murphyif->ml,
375                                      dif->tables, dif->ntable,
376                                      dif->watches, dif->nwatch,
377                                      domctl_connect_notify,
378                                      domctl_watch_notify, u);
379         if (!dif->ctl) {
380             pa_log("failed to create '%s' domain controller", name);
381             return;
382         }
383
384         if (!mrp_domctl_connect(dif->ctl, dif->addr, 0)) {
385             pa_log("failed to conect to murphyd");
386             return;
387         }
388
389         dif->watchcb = wcb;
390         pa_log_info("'%s' domain controller sucessfully created", name);
391     }
392 #endif
393 }
394
395 void  pa_murphyif_add_audio_resource(struct userdata *u,
396                                      mir_direction dir,
397                                      const char *name)
398 {
399     pa_murphyif *murphyif;
400     resource_interface *rif;
401
402     pa_assert(u);
403     pa_assert(dir == mir_input || dir == mir_output);
404     pa_assert(name);
405
406     pa_assert_se((murphyif = u->murphyif));
407     rif = &murphyif->resource;
408
409     if (dir == mir_input) {
410         if (rif->inpres)
411             pa_log("attempt to register playback resource multiple time");
412         else
413             rif->inpres = pa_xstrdup(name);
414     }
415     else {
416         if (rif->outres)
417             pa_log("attempt to register recording resource multiple time");
418         else
419             rif->outres = pa_xstrdup(name);
420     }
421 }
422
423 void pa_murphyif_add_audio_attribute(struct userdata *u,
424                                      const char *propnam,
425                                      const char *attrnam,
426                                      mqi_data_type_t type,
427                                      ... ) /* default value */
428 {
429 #ifdef WITH_RESOURCES
430     pa_murphyif *murphyif;
431     resource_interface *rif;
432     resource_attribute *attr;
433     mrp_attr_value_t *val;
434     va_list ap;
435
436     pa_assert(u);
437     pa_assert(propnam);
438     pa_assert(attrnam);
439     pa_assert(type == mqi_string  || type == mqi_integer ||
440               type == mqi_unsignd || type == mqi_floating);
441
442     pa_assert_se((murphyif = u->murphyif));
443     rif = &murphyif->resource;
444
445     attr = pa_xnew0(resource_attribute, 1);
446     val  = &attr->def.value;
447
448     attr->prop = pa_xstrdup(propnam);
449     attr->def.name = pa_xstrdup(attrnam);
450     attr->def.type = type;
451
452     va_start(ap, type);
453
454     switch (type){
455     case mqi_string:   val->string    = pa_xstrdup(va_arg(ap, char *));  break;
456     case mqi_integer:  val->integer   = va_arg(ap, int32_t);             break;
457     case mqi_unsignd:  val->unsignd   = va_arg(ap, uint32_t);            break;
458     case mqi_floating: val->floating  = va_arg(ap, double);              break;
459     default:           attr->def.type = mqi_error;                       break;
460     }
461
462     va_end(ap);
463
464      if (attr->def.type == mqi_error)
465          resource_attribute_destroy(rif, attr);
466      else
467          PA_LLIST_PREPEND(resource_attribute, rif->attrs, attr);
468 #endif
469 }
470
471 void pa_murphyif_create_resource_set(struct userdata *u, mir_node *node)
472 {
473     pa_core *core;
474     pa_murphyif *murphyif;
475     resource_interface *rif;
476     const char *class;
477     uint32_t audio_flags = 0;
478     pa_proplist *proplist = NULL;
479     pa_sink_input *sinp;
480     pa_source_output *sout;
481
482     pa_assert(u);
483     pa_assert(node);
484     pa_assert(node->implement = mir_stream);
485     pa_assert(node->direction == mir_input || node->direction == mir_output);
486     pa_assert(node->zone);
487     pa_assert(!node->rsetid);
488
489     pa_assert_se((core = u->core));
490     pa_assert_se((class = pa_nodeset_get_class(u, node->type)));
491
492     pa_assert_se((murphyif = u->murphyif));
493     rif = &murphyif->resource;
494
495     resource_transport_connect(rif);
496
497     if (node->direction == mir_output) {
498         if ((sout = pa_idxset_get_by_index(core->source_outputs, node->paidx)))
499             proplist = sout->proplist;
500     }
501     else {
502         if ((sinp = pa_idxset_get_by_index(core->sink_inputs, node->paidx)))
503             proplist = sinp->proplist;
504     }
505
506     node->localrset = resource_set_create(u, node->index, node->direction,
507                                           class, node->zone, audio_flags,
508                                           proplist);
509 }
510
511 void pa_murphyif_destroy_resource_set(struct userdata *u, mir_node *node)
512 {
513     pa_murphyif *murphyif;
514     uint32_t rsetid;
515     char *e;
516
517     pa_assert(u);
518     pa_assert(node);
519     pa_assert_se((murphyif = u->murphyif));
520
521     if (node->localrset && node->rsetid) {
522         rsetid = strtoul(node->rsetid, &e, 10);
523
524         if (e == node->rsetid || *e) {
525             pa_log("can't destroy resource set: invalid rsetid '%s'",
526                    node->rsetid);
527         }
528         else {
529             if (resource_set_destroy(u, rsetid))
530                 pa_log_debug("resource set %u destruction request", rsetid);
531             else {
532                 pa_log("falied to destroy resourse set %u for node '%s'",
533                        rsetid, node->amname);
534             }
535         }
536
537         pa_murphyif_delete_node(u, node);
538     }
539 }
540
541 int pa_murphyif_add_node(struct userdata *u, mir_node *node)
542 {
543 #ifdef WITH_RESOURCES
544     pa_murphyif *murphyif;
545
546     pa_assert(u);
547     pa_assert(node);
548     pa_assert(node->implement == mir_stream);
549
550     pa_assert_se((murphyif = u->murphyif));
551
552     if (!node->rsetid) {
553         pa_log("can't register resource set for node '%s'.: missing rsetid",
554                node->amname);
555     }
556     else {
557         if (pa_hashmap_put(murphyif->nodes, node->rsetid, node) == 0)
558             return 0;
559         else {
560             pa_log("can't register resource set for node '%s': conflicting "
561                    "resource id '%s'", node->amname, node->rsetid);
562         } 
563     }
564
565     return -1;
566 #else
567     return 0;
568 #endif
569 }
570
571 void pa_murphyif_delete_node(struct userdata *u, mir_node *node)
572 {
573 #ifdef WITH_RESOURCES
574     pa_murphyif *murphyif;
575     mir_node *deleted;
576
577     pa_assert(u);
578     pa_assert(node);
579     pa_assert(node->implement == mir_stream);
580
581     pa_assert_se((murphyif = u->murphyif));
582
583     if (node->rsetid) {
584         deleted = pa_hashmap_remove(murphyif->nodes, node->rsetid);
585         pa_assert(deleted == node);
586     }
587 #endif
588 }
589
590 mir_node *pa_murphyif_find_node(struct userdata *u, const char *rsetid)
591 {
592 #ifdef WITH_RESOURCES
593     pa_murphyif *murphyif;
594     mir_node *node;
595
596     pa_assert(u);
597     pa_assert_se((murphyif = u->murphyif));
598
599     if (!rsetid)
600         node = NULL;
601     else
602         node = pa_hashmap_get(murphyif->nodes, rsetid);
603
604     return node;
605 #else
606     return NULL;
607 #endif
608 }
609
610
611 #ifdef WITH_DOMCTL
612 static void domctl_connect_notify(mrp_domctl_t *dc, int connected, int errcode,
613                                   const char *errmsg, void *user_data)
614 {
615     MRP_UNUSED(dc);
616     MRP_UNUSED(user_data);
617
618     if (connected) {
619         pa_log_info("Successfully registered to Murphy.");
620     }
621     else
622         pa_log_error("Connection to Murphy failed (%d: %s).", errcode, errmsg);
623 }
624
625 static void domctl_watch_notify(mrp_domctl_t *dc, mrp_domctl_data_t *tables,
626                                 int ntable, void *user_data)
627 {
628     struct userdata *u = (struct userdata *)user_data;
629     pa_murphyif *murphyif;
630     domctl_interface *dif;
631     mrp_domctl_data_t *t;
632     mrp_domctl_watch_t *w;
633     int i;
634
635     MRP_UNUSED(dc);
636
637     pa_assert(tables);
638     pa_assert(ntable > 0);
639     pa_assert(u);
640     pa_assert_se((murphyif = u->murphyif));
641
642     dif = &murphyif->domctl;
643
644     pa_log_info("Received change notification for %d tables.", ntable);
645
646     for (i = 0; i < ntable; i++) {
647         t = tables + i;
648
649         domctl_dump_data(t);
650
651         pa_assert(t->id >= 0);
652         pa_assert(t->id < dif->nwatch);
653
654         w = dif->watches + t->id;
655
656         dif->watchcb(u, w->table, t->nrow, t->rows);
657     }
658 }
659
660 static void domctl_dump_data(mrp_domctl_data_t *table)
661 {
662     mrp_domctl_value_t *row;
663     int                 i, j;
664     char                buf[1024], *p;
665     const char         *t;
666     int                 n, l;
667
668     pa_log_debug("Table #%d: %d rows x %d columns", table->id,
669            table->nrow, table->ncolumn);
670
671     for (i = 0; i < table->nrow; i++) {
672         row = table->rows[i];
673         p   = buf;
674         n   = sizeof(buf);
675
676         for (j = 0, t = ""; j < table->ncolumn; j++, t = ", ") {
677             switch (row[j].type) {
678             case MRP_DOMCTL_STRING:
679                 l  = snprintf(p, n, "%s'%s'", t, row[j].str);
680                 p += l;
681                 n -= l;
682                 break;
683             case MRP_DOMCTL_INTEGER:
684                 l  = snprintf(p, n, "%s%d", t, row[j].s32);
685                 p += l;
686                 n -= l;
687                 break;
688             case MRP_DOMCTL_UNSIGNED:
689                 l  = snprintf(p, n, "%s%u", t, row[j].u32);
690                 p += l;
691                 n -= l;
692                 break;
693             case MRP_DOMCTL_DOUBLE:
694                 l  = snprintf(p, n, "%s%f", t, row[j].dbl);
695                 p += l;
696                 n -= l;
697                 break;
698             default:
699                 l  = snprintf(p, n, "%s<invalid column 0x%x>",
700                               t, row[j].type);
701                 p += l;
702                 n -= l;
703             }
704         }
705
706         pa_log_debug("row #%d: { %s }", i, buf);
707     }
708 }
709 #endif
710
711 #ifdef WITH_RESOURCES
712 static void resource_attribute_destroy(resource_interface *rif,
713                                        resource_attribute *attr)
714 {
715     if (attr) {
716        if (rif)
717            PA_LLIST_REMOVE(resource_attribute, rif->attrs, attr);
718
719        pa_xfree((void *)attr->prop);
720        pa_xfree((void *)attr->def.name);
721
722        if (attr->def.type == mqi_string)
723            pa_xfree((void *)attr->def.value.string);
724
725        pa_xfree(attr);
726     }
727 }
728
729 static pa_bool_t resource_transport_connect(resource_interface *rif)
730 {
731     pa_assert(rif);
732
733     if (!rif->connected) {
734         if (mrp_transport_connect(rif->transp, &rif->saddr, rif->alen)) {
735             pa_log_info("resource transport connected to '%s'", rif->addr);
736             rif->connected = TRUE;
737         }
738         else {
739             pa_log("can't connect resource transport to '%s'", rif->addr);
740             return FALSE;
741         }
742     }
743
744     return TRUE;
745 }
746
747 static void resource_xport_closed_evt(mrp_transport_t *transp, int error,
748                                       void *void_u)
749 {
750     struct userdata *u = (struct userdata *)void_u;
751     pa_murphyif *murphyif;
752     resource_interface *rif;
753
754     MRP_UNUSED(transp);
755
756     pa_assert(u);
757     pa_assert_se((murphyif = u->murphyif));
758
759     rif = &murphyif->resource;
760
761     if (!error)
762         pa_log("peer has closed the resource transport connection");
763     else {
764         pa_log("resource transport connection closed with error %d (%s)",
765                error, strerror(error));
766     }
767
768     rif->connected = FALSE;
769 }
770
771 static mrp_msg_t *resource_create_request(uint32_t seqno,
772                                           mrp_resproto_request_t req)
773 {
774     uint16_t   type  = req;
775     mrp_msg_t *msg;
776
777     msg = mrp_msg_create(RESPROTO_SEQUENCE_NO , MRP_MSG_FIELD_UINT32, seqno,
778                          RESPROTO_REQUEST_TYPE, MRP_MSG_FIELD_UINT16, type ,
779                          RESPROTO_MESSAGE_END                               );
780
781     if (!msg)
782         pa_log("can't to create new resource message");
783  
784     return msg;
785 }
786
787 static pa_bool_t resource_send_message(resource_interface *rif,
788                                        mrp_msg_t          *msg,
789                                        uint32_t            nodidx,
790                                        uint16_t            reqid,
791                                        uint32_t            seqno)
792 {
793     resource_request *req;
794     pa_bool_t success = TRUE;
795
796     if (!mrp_transport_send(rif->transp, msg)) {
797         pa_log("failed to send resource message");
798         success = FALSE;
799     }
800     else {
801         req = pa_xnew0(resource_request, 1);
802         req->nodidx = nodidx;
803         req->reqid  = reqid;
804         req->seqno  = seqno;
805
806         PA_LLIST_PREPEND(resource_request, rif->reqs, req);
807     }
808
809     mrp_msg_unref(msg);
810
811     return success;
812 }
813
814
815 static pa_bool_t resource_set_create(struct userdata *u,
816                                      uint32_t nodidx,
817                                      mir_direction dir,
818                                      const char *class,
819                                      const char *zone,
820                                      uint32_t audio_flags,
821                                      pa_proplist *proplist)
822 {
823     static uint32_t rset_flags = 0 /* RESPROTO_RSETFLAG_AUTORELEASE */ ;
824
825     pa_murphyif *murphyif;
826     resource_interface *rif;
827     resource_request *req;
828     mrp_msg_t *msg;
829     uint16_t reqid;
830     uint32_t seqno;
831     const char *resnam;
832     pa_bool_t success = TRUE;
833
834     pa_assert(u);
835     pa_assert(nodidx != PA_IDXSET_INVALID);
836     pa_assert(dir == mir_input || dir == mir_output);
837     pa_assert(class);
838     pa_assert(zone);
839
840     pa_assert_se((murphyif = u->murphyif));
841     rif = &murphyif->resource;
842
843     reqid  = RESPROTO_CREATE_RESOURCE_SET;
844     seqno  = rif->seqno.request++;
845     resnam = (dir == mir_input) ? rif->inpres : rif->outres;
846
847     pa_assert(resnam);
848
849     msg = resource_create_request(seqno, reqid);
850
851     if (PUSH_VALUE(msg,   RESOURCE_FLAGS   , UINT32, rset_flags)  &&
852         PUSH_VALUE(msg,   RESOURCE_PRIORITY, UINT32, 0)           &&
853         PUSH_VALUE(msg,   CLASS_NAME       , STRING, class)       &&
854         PUSH_VALUE(msg,   ZONE_NAME        , STRING, zone)        &&
855         PUSH_VALUE(msg,   RESOURCE_NAME    , STRING, resnam)      &&
856         PUSH_VALUE(msg,   RESOURCE_FLAGS   , UINT32, audio_flags) &&
857         PUSH_ATTRS(msg,   rif, proplist)                          &&
858         PUSH_VALUE(msg,   SECTION_END      , UINT8 , 0)            )
859     {
860         success = resource_send_message(rif, msg, nodidx, reqid, seqno);
861     }
862     else {
863         success = FALSE;
864         mrp_msg_unref(msg);
865     }
866
867     return success;
868 }
869
870 static pa_bool_t resource_set_destroy(struct userdata *u, uint32_t rsetid)
871 {
872     pa_murphyif *murphyif;
873     resource_interface *rif;
874     mrp_msg_t *msg;
875     uint16_t reqid;
876     uint32_t seqno;
877     uint32_t nodidx;
878     pa_bool_t success;
879
880     pa_assert(u);
881
882     pa_assert_se((murphyif = u->murphyif));
883     rif = &murphyif->resource;
884
885     reqid = RESPROTO_DESTROY_RESOURCE_SET;
886     seqno = rif->seqno.request++;
887     nodidx = PA_IDXSET_INVALID;
888     msg = resource_create_request(seqno, reqid);
889
890     if (PUSH_VALUE(msg, RESOURCE_SET_ID, UINT32, rsetid))
891         success = resource_send_message(rif, msg, nodidx, reqid, seqno);
892     else {
893         success = FALSE;
894         mrp_msg_unref(msg);
895     }
896
897     return success;
898 }
899
900 static pa_bool_t resource_set_acquire(struct userdata *u,
901                                       uint32_t nodidx,
902                                       uint32_t rsetid)
903 {
904     pa_murphyif *murphyif;
905     resource_interface *rif;
906     mrp_msg_t *msg;
907     uint16_t reqid;
908     uint32_t seqno;
909     pa_bool_t success;
910
911     pa_assert(u);
912
913     pa_assert_se((murphyif = u->murphyif));
914     rif = &murphyif->resource;
915
916     reqid = RESPROTO_ACQUIRE_RESOURCE_SET;
917     seqno = rif->seqno.request++;
918     msg = resource_create_request(seqno, reqid);
919
920     if (PUSH_VALUE(msg, RESOURCE_SET_ID, UINT32, rsetid))
921         success = resource_send_message(rif, msg, nodidx, reqid, seqno);
922     else {
923         success = FALSE;
924         mrp_msg_unref(msg);
925     }
926
927     return success;
928 }
929
930 static pa_bool_t resource_push_attributes(mrp_msg_t *msg,
931                                           resource_interface *rif,
932                                           pa_proplist *proplist)
933 {
934     resource_attribute *attr;
935     union {
936         const void *ptr;
937         const char *str;
938         int32_t    *i32;
939         uint32_t   *u32;
940         double     *dbl;
941     } v;
942     size_t size;
943     int sts;
944
945     pa_assert(msg);
946     pa_assert(rif);
947
948     PA_LLIST_FOREACH(attr, rif->attrs) {
949         if (!PUSH_VALUE(msg, ATTRIBUTE_NAME, STRING, attr->def.name))
950             return FALSE;
951
952         if (proplist)
953             sts = pa_proplist_get(proplist, attr->prop, &v.ptr, &size);
954         else
955             sts = -1;
956
957         switch (attr->def.type) {
958         case mqi_string:
959             if (sts < 0)
960                 v.str = attr->def.value.string;
961             else if (v.str[size-1] != '\0' || strlen(v.str) != (size-1) ||
962                      !pa_utf8_valid(v.str))
963                 return FALSE;
964             if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, STRING, v.str))
965                 return FALSE;
966             break;
967
968         case mqi_integer:
969             if (sts < 0)
970                 v.i32 = &attr->def.value.integer;
971             else if (size != sizeof(*v.i32))
972                 return FALSE;
973             if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, SINT8, *v.i32))
974                 return FALSE;
975             break;
976             
977         case mqi_unsignd:
978             if (sts < 0)
979                 v.u32 = &attr->def.value.unsignd;
980             else if (size != sizeof(*v.u32))
981                 return FALSE;
982             if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, SINT8, *v.u32))
983                 return FALSE;
984             break;
985             
986         case mqi_floating:
987             if (sts < 0)
988                 v.dbl = &attr->def.value.floating;
989             else if (size != sizeof(*v.dbl))
990                 return FALSE;
991             if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, SINT8, *v.dbl))
992                 return FALSE;
993             break;
994
995         default: /* we should never get here */
996             return FALSE;
997         }
998     }
999
1000     return TRUE;
1001 }
1002
1003
1004
1005 static void resource_recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *void_u)
1006 {
1007     return resource_recvfrom_msg(t, msg, NULL, 0, void_u);
1008 }
1009
1010 static void resource_recvfrom_msg(mrp_transport_t *transp, mrp_msg_t *msg,
1011                                   mrp_sockaddr_t *addr, socklen_t addrlen,
1012                                   void *void_u)
1013 {
1014     struct userdata *u = (struct userdata *)void_u;
1015     pa_core *core;
1016     pa_murphyif *murphyif;
1017     resource_interface *rif;
1018     void     *curs = NULL;
1019     uint32_t  seqno;
1020     uint16_t  reqid;
1021     uint32_t  nodidx;
1022     resource_request *req, *n;
1023     mir_node *node;
1024
1025     MRP_UNUSED(transp);
1026     MRP_UNUSED(addr);
1027     MRP_UNUSED(addrlen);
1028
1029     pa_assert(u);
1030     pa_assert_se((core = u->core));
1031     pa_assert_se((murphyif = u->murphyif));
1032
1033     rif = &murphyif->resource;
1034
1035     if (!resource_fetch_seqno   (msg, &curs, &seqno) ||
1036         !resource_fetch_request (msg, &curs, &reqid)   )
1037     {
1038         pa_log("ignoring malformed message");
1039         return;
1040     }
1041
1042     PA_LLIST_FOREACH_SAFE(req, n, rif->reqs) {
1043         if (req->seqno <= seqno) {
1044             nodidx = req->nodidx;
1045             
1046             if (req->reqid == reqid) {
1047                 PA_LLIST_REMOVE(resource_request, rif->reqs, req);
1048                 pa_xfree(req);
1049             }
1050             
1051             if (!(node = mir_node_find_by_index(u, nodidx))) {
1052                 if (reqid != RESPROTO_DESTROY_RESOURCE_SET) {
1053                     pa_log("got response (reqid:%u seqno:%u) but can't "
1054                            "find the corresponding node", reqid, seqno);
1055                 }
1056             }
1057             else {
1058                 if (req->seqno < seqno) {
1059                     pa_log("unanswered request %d", req->seqno);
1060                 }
1061                 else {
1062                     pa_log_debug("got response (reqid:%u seqno:%u "
1063                                  "node:'%s')", reqid, seqno,
1064                                  node ? node->amname : "<unknown>");
1065                     
1066                     switch (reqid) {
1067                     case RESPROTO_CREATE_RESOURCE_SET:
1068                         resource_set_create_response(u,node,msg,&curs);
1069                         break;
1070 #if 0
1071                     case RESPROTO_ACQUIRE_RESOURCE_SET:
1072                         resource_set_acquire_response(u,node,msg,&curs);
1073                         break;
1074                     case RESPROTO_RELEASE_RESOURCE_SET:
1075                         resource_set_release_response(u,node,msg,&curs);
1076                         break;
1077                     case RESPROTO_RESOURCES_EVENT:
1078                         resource_event(u, seqno, msg, &curs);
1079                         break;
1080 #endif
1081                     default:
1082                         pa_log("ignoring unsupported resource request "
1083                                "type %u", reqid);
1084                         break;
1085                     }
1086                 }
1087             }
1088         } /* PA_LLIST_FOREACH_SAFE */
1089     }
1090 }
1091
1092 static void resource_set_create_response(struct userdata *u, mir_node *node,
1093                                          mrp_msg_t *msg, void **pcursor)
1094 {
1095     int status;
1096     uint32_t rsetid;
1097     char buf[4096];
1098
1099     pa_assert(u);
1100     pa_assert(node);
1101     pa_assert(msg);
1102     pa_assert(pcursor);
1103
1104     if (!resource_fetch_status(msg, pcursor, &status) || (status == 0 &&
1105         !resource_fetch_rset_id(msg, pcursor, &rsetid)))
1106     {
1107         pa_log("ignoring malformed response to resource set creation");
1108         return;
1109     }
1110
1111     if (status) {
1112         pa_log("creation of resource set failed. error code %u", status);
1113         return;
1114     }
1115
1116     node->rsetid = pa_sprintf_malloc("%d", rsetid);
1117                 
1118     if (pa_murphyif_add_node(u, node) == 0) {
1119         pa_log_debug("resource set was successfully created");
1120         mir_node_print(node, buf, sizeof(buf));
1121         pa_log_debug("modified node:\n%s", buf);
1122
1123         if (resource_set_acquire(u, node->index, rsetid))
1124             pa_log_debug("acquire request sent");
1125         else
1126             pa_log("failed to send acquire request");
1127     }
1128     else {
1129         pa_log("failed to create resource set: "
1130                "conflicting resource set id");
1131     }
1132 }
1133
1134
1135 static pa_bool_t resource_fetch_seqno(mrp_msg_t *msg,
1136                                       void **pcursor,
1137                                       uint32_t *pseqno)
1138 {
1139     uint16_t tag;
1140     uint16_t type;
1141     mrp_msg_value_t value;
1142     size_t size;
1143
1144     if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1145         tag != RESPROTO_SEQUENCE_NO || type != MRP_MSG_FIELD_UINT32)
1146     {
1147         *pseqno = INVALID_SEQNO;
1148         return false;
1149     }
1150
1151     *pseqno = value.u32;
1152     return true;
1153 }
1154
1155
1156 static pa_bool_t resource_fetch_request(mrp_msg_t *msg,
1157                                         void **pcursor,
1158                                         uint16_t *preqtype)
1159 {
1160     uint16_t tag;
1161     uint16_t type;
1162     mrp_msg_value_t value;
1163     size_t size;
1164
1165     if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1166         tag != RESPROTO_REQUEST_TYPE || type != MRP_MSG_FIELD_UINT16)
1167     {
1168         *preqtype = INVALID_REQUEST;
1169         return false;
1170     }
1171
1172     *preqtype = value.u16;
1173     return true;
1174 }
1175
1176 static pa_bool_t resource_fetch_status(mrp_msg_t *msg,
1177                                        void **pcursor,
1178                                        int *pstatus)
1179 {
1180     uint16_t tag;
1181     uint16_t type;
1182     mrp_msg_value_t value;
1183     size_t size;
1184
1185     if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1186         tag != RESPROTO_REQUEST_STATUS || type != MRP_MSG_FIELD_SINT16)
1187     {
1188         *pstatus = EINVAL;
1189         return FALSE;
1190     }
1191
1192     *pstatus = value.s16;
1193     return TRUE;
1194 }
1195
1196 static pa_bool_t resource_fetch_rset_id(mrp_msg_t *msg,
1197                                         void **pcursor,
1198                                         uint32_t *pid)
1199 {
1200     uint16_t tag;
1201     uint16_t type;
1202     mrp_msg_value_t value;
1203     size_t size;
1204
1205     if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1206         tag != RESPROTO_RESOURCE_SET_ID || type != MRP_MSG_FIELD_UINT32)
1207     {
1208         *pid = INVALID_ID;
1209         return FALSE;
1210     }
1211
1212     *pid = value.u32;
1213     return TRUE;
1214 }
1215
1216 static pa_bool_t resource_fetch_rset_state(mrp_msg_t *msg,
1217                                            void **pcursor,
1218                                            mrp_resproto_state_t *pstate)
1219 {
1220     uint16_t tag;
1221     uint16_t type;
1222     mrp_msg_value_t value;
1223     size_t size;
1224
1225     if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1226         tag != RESPROTO_RESOURCE_STATE || type != MRP_MSG_FIELD_UINT16)
1227     {
1228         *pstate = 0;
1229         return FALSE;
1230     }
1231
1232     *pstate = value.u16;
1233     return TRUE;
1234 }
1235
1236
1237 static pa_bool_t resource_fetch_rset_mask(mrp_msg_t *msg,
1238                                           void **pcursor,
1239                                           mrp_resproto_state_t *pmask)
1240 {
1241     uint16_t tag;
1242     uint16_t type;
1243     mrp_msg_value_t value;
1244     size_t size;
1245
1246     if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1247         tag != RESPROTO_RESOURCE_GRANT || type != MRP_MSG_FIELD_UINT32)
1248     {
1249         *pmask = 0;
1250         return FALSE;
1251     }
1252
1253     *pmask = value.u32;
1254     return TRUE;
1255 }
1256
1257
1258 #endif
1259
1260 /*
1261  * Local Variables:
1262  * c-basic-offset: 4
1263  * indent-tabs-mode: nil
1264  * End:
1265  *
1266  */