2 * module-murphy-ivi -- PulseAudio module for providing audio routing support
3 * Copyright (c) 2012, Intel Corporation.
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.
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.
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,
20 #include <sys/types.h>
25 #include <pulse/utf8.h>
26 #include <pulse/timeval.h>
27 #include <pulsecore/pulsecore-config.h>
28 #include <pulsecore/module.h>
29 #include <pulsecore/llist.h>
30 #include <pulsecore/idxset.h>
31 #include <pulsecore/hashmap.h>
32 #include <pulsecore/core-util.h>
33 #include <pulsecore/sink-input.h>
34 #include <pulsecore/source-output.h>
38 #define WITH_RESOURCES
41 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
42 #include <murphy/common/macros.h>
43 #include <murphy/common/mainloop.h>
44 #include <murphy/pulse/pulse-glue.h>
48 #include <murphy/resource/protocol.h>
49 #include <murphy/common/transport.h>
50 #include <murphy/resource/protocol.h>
51 #include <murphy/resource/data-types.h>
56 #include "stream-state.h"
60 #define INVALID_ID (~(uint32_t)0)
61 #define INVALID_INDEX (~(uint32_t)0)
62 #define INVALID_SEQNO (~(uint32_t)0)
63 #define INVALID_REQUEST (~(uint16_t)0)
65 #define DISCONNECTED -1
69 #define RESCOL_NAMES "rsetid,autorel,state,grant,pid,policy"
70 #define RESCOL_RSETID 0
71 #define RESCOL_AUTOREL 1
72 #define RESCOL_STATE 2
73 #define RESCOL_GRANT 3
75 #define RESCOL_POLICY 5
77 #define RSET_RELEASE 1
78 #define RSET_ACQUIRE 2
80 #define PUSH_VALUE(msg, tag, typ, val) \
81 mrp_msg_append(msg, MRP_MSG_TAG_##typ(RESPROTO_##tag, val))
83 #define PUSH_ATTRS(msg, rif, proplist) \
84 resource_push_attributes(msg, rif, proplist)
86 typedef struct resource_attribute resource_attribute;
87 typedef struct resource_request resource_request;
89 struct resource_attribute {
90 PA_LLIST_FIELDS(resource_attribute);
95 struct resource_request {
96 PA_LLIST_FIELDS(resource_request);
109 mrp_domctl_table_t *tables;
111 mrp_domctl_watch_t *watches;
112 pa_murphyif_watch_cb watchcb;
123 audio_resource_t inpres;
124 audio_resource_t outres;
125 #ifdef WITH_RESOURCES
126 mrp_transport_t *transp;
127 mrp_sockaddr_t saddr;
143 PA_LLIST_HEAD(resource_attribute, attrs);
144 PA_LLIST_HEAD(resource_request, reqs);
146 } resource_interface;
150 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
153 domctl_interface domctl;
154 resource_interface resource;
157 #ifdef WITH_RESOURCES
174 #ifdef WITH_RESOURCES
175 static mir_node *find_node_by_rsetid(struct userdata *, const char *);
179 static void domctl_connect_notify(mrp_domctl_t *,int,int,const char *,void *);
180 static void domctl_watch_notify(mrp_domctl_t *,mrp_domctl_data_t *,int,void *);
181 static void domctl_dump_data(mrp_domctl_data_t *);
184 #ifdef WITH_RESOURCES
185 static void resource_attribute_destroy(resource_interface *,
186 resource_attribute *);
187 static int resource_transport_connect(resource_interface *);
188 static void resource_xport_closed_evt(mrp_transport_t *, int, void *);
190 static mrp_msg_t *resource_create_request(uint32_t, mrp_resproto_request_t);
191 static pa_bool_t resource_send_message(resource_interface *, mrp_msg_t *,
192 uint32_t, uint16_t, uint32_t);
193 static pa_bool_t resource_set_create_node(struct userdata *, mir_node *,
194 pa_nodeset_resdef *, pa_bool_t);
195 static pa_bool_t resource_set_create_all(struct userdata *);
196 static pa_bool_t resource_set_destroy_node(struct userdata *, uint32_t);
197 static pa_bool_t resource_set_destroy_all(struct userdata *);
198 static void resource_set_notification(struct userdata *, const char *,
199 int, mrp_domctl_value_t **);
201 static pa_bool_t resource_push_attributes(mrp_msg_t *, resource_interface *,
204 static void resource_recv_msg(mrp_transport_t *, mrp_msg_t *, void *);
205 static void resource_recvfrom_msg(mrp_transport_t *, mrp_msg_t *,
206 mrp_sockaddr_t *, socklen_t, void *);
207 static void resource_set_create_response(struct userdata *, mir_node *,
208 mrp_msg_t *, void **);
209 static void resource_set_create_response_abort(struct userdata *,
210 mrp_msg_t *, void **);
212 static pa_bool_t resource_fetch_seqno(mrp_msg_t *, void **, uint32_t *);
213 static pa_bool_t resource_fetch_request(mrp_msg_t *, void **, uint16_t *);
214 static pa_bool_t resource_fetch_status(mrp_msg_t *, void **, int *);
215 static pa_bool_t resource_fetch_rset_id(mrp_msg_t *, void **, uint32_t*);
216 static pa_bool_t resource_fetch_rset_state(mrp_msg_t *, void **,
217 mrp_resproto_state_t *);
218 static pa_bool_t resource_fetch_rset_mask(mrp_msg_t *, void **,
219 mrp_resproto_state_t *);
221 static pa_bool_t resource_transport_create(struct userdata *, pa_murphyif *);
222 static void resource_transport_destroy(pa_murphyif *);
224 static void connect_attempt(pa_mainloop_api *, pa_time_event *,
225 const struct timeval *, void *);
226 static void schedule_connect(struct userdata *, resource_interface *);
227 static void cancel_schedule(struct userdata *, resource_interface *);
229 static int node_put_rset(struct userdata *, mir_node *, rset_data *);
230 static void node_enforce_resource_policy(struct userdata *, mir_node *,
232 static rset_data *rset_data_dup(rset_data *);
233 static void rset_data_copy(rset_data *, rset_data *);
234 static void rset_data_free(rset_data *);
236 static void pid_hashmap_free(void *, void *);
237 static int pid_hashmap_put(struct userdata *, const char *,
238 mir_node *, rset_data *);
239 static mir_node *pid_hashmap_get_node(struct userdata *, const char *);
240 static rset_data *pid_hashmap_get_rset(struct userdata *, const char *);
241 static mir_node *pid_hashmap_remove_node(struct userdata *, const char *);
242 static rset_data *pid_hashmap_remove_rset(struct userdata *, const char *);
245 static pa_proplist *get_node_proplist(struct userdata *, mir_node *);
246 static const char *get_node_pid(struct userdata *, mir_node *);
249 pa_murphyif *pa_murphyif_init(struct userdata *u,
250 const char *ctl_addr,
251 const char *res_addr)
253 pa_murphyif *murphyif;
254 domctl_interface *dif;
255 resource_interface *rif;
256 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
259 if (!(ml = mrp_mainloop_pulse_get(u->core->mainloop))) {
260 pa_log_error("Failed to set up murphy mainloop.");
264 #ifdef WITH_RESOURCES
267 murphyif = pa_xnew0(pa_murphyif, 1);
268 dif = &murphyif->domctl;
269 rif = &murphyif->resource;
271 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
275 dif->addr = pa_xstrdup(ctl_addr ? ctl_addr:MRP_DEFAULT_DOMCTL_ADDRESS);
279 rif->addr = pa_xstrdup(res_addr ? res_addr:RESPROTO_DEFAULT_ADDRESS);
280 #ifdef WITH_RESOURCES
281 rif->alen = mrp_transport_resolve(NULL, rif->addr, &rif->saddr,
282 sizeof(rif->saddr), &rif->atype);
283 if (rif->alen <= 0) {
284 pa_log("can't resolve resource transport address '%s'", rif->addr);
287 rif->inpres.tblidx = -1;
288 rif->outres.tblidx = -1;
289 rif->connect.period = 1 * PA_USEC_PER_SEC;
291 if (!resource_transport_create(u, murphyif)) {
292 pa_log("failed to create resource transport");
293 schedule_connect(u, rif);
296 if (resource_transport_connect(rif) == DISCONNECTED)
297 schedule_connect(u, rif);
301 rif->seqno.request = 1;
302 rif->nodes.rsetid = pa_hashmap_new(pa_idxset_string_hash_func,
303 pa_idxset_string_compare_func);
304 rif->nodes.pid = pa_hashmap_new(pa_idxset_string_hash_func,
305 pa_idxset_string_compare_func);
306 PA_LLIST_HEAD_INIT(resource_attribute, rif->attrs);
307 PA_LLIST_HEAD_INIT(resource_request, rif->reqs);
314 void pa_murphyif_done(struct userdata *u)
316 pa_murphyif *murphyif;
317 domctl_interface *dif;
318 resource_interface *rif;
319 #ifdef WITH_RESOURCES
320 resource_attribute *attr, *a;
321 resource_request *req, *r;
324 if (u && (murphyif = u->murphyif)) {
326 mrp_domctl_table_t *t;
327 mrp_domctl_watch_t *w;
330 dif = &murphyif->domctl;
332 mrp_domctl_destroy(dif->ctl);
334 if (dif->ntable > 0 && dif->tables) {
335 for (i = 0; i < dif->ntable; i++) {
337 pa_xfree((void *)t->table);
338 pa_xfree((void *)t->mql_columns);
339 pa_xfree((void *)t->mql_index);
341 pa_xfree(dif->tables);
344 if (dif->nwatch > 0 && dif->watches) {
345 for (i = 0; i < dif->nwatch; i++) {
346 w = dif->watches + i;
347 pa_xfree((void *)w->table);
348 pa_xfree((void *)w->mql_columns);
349 pa_xfree((void *)w->mql_where);
351 pa_xfree(dif->watches);
354 pa_xfree((void *)dif->addr);
357 #ifdef WITH_RESOURCES
358 rif = &murphyif->resource;
360 resource_transport_destroy(murphyif);
362 pa_hashmap_free(rif->nodes.rsetid, NULL, NULL);
363 pa_hashmap_free(rif->nodes.pid, pid_hashmap_free, NULL);
365 PA_LLIST_FOREACH_SAFE(attr, a, rif->attrs)
366 resource_attribute_destroy(rif, attr);
368 PA_LLIST_FOREACH_SAFE(req, r, rif->reqs)
371 pa_xfree((void *)rif->addr);
372 pa_xfree((void *)rif->inpres.name);
373 pa_xfree((void *)rif->outres.name);
375 mrp_mainloop_destroy(murphyif->ml);
382 void pa_murphyif_add_table(struct userdata *u,
387 pa_murphyif *murphyif;
388 domctl_interface *dif;
389 mrp_domctl_table_t *t;
396 pa_assert_se((murphyif = u->murphyif));
398 dif = &murphyif->domctl;
401 size = sizeof(mrp_domctl_table_t) * dif->ntable;
402 t = (dif->tables = pa_xrealloc(dif->tables, size)) + idx;
404 t->table = pa_xstrdup(table);
405 t->mql_columns = pa_xstrdup(columns);
406 t->mql_index = index ? pa_xstrdup(index) : NULL;
409 int pa_murphyif_add_watch(struct userdata *u,
415 pa_murphyif *murphyif;
416 domctl_interface *dif;
417 mrp_domctl_watch_t *w;
424 pa_assert(max_rows > 0 && max_rows < MQI_QUERY_RESULT_MAX);
425 pa_assert_se((murphyif = u->murphyif));
427 dif = &murphyif->domctl;
430 size = sizeof(mrp_domctl_watch_t) * dif->nwatch;
431 w = (dif->watches = pa_xrealloc(dif->watches, size)) + idx;
433 w->table = pa_xstrdup(table);
434 w->mql_columns = pa_xstrdup(columns);
435 w->mql_where = where ? pa_xstrdup(where) : NULL;
436 w->max_rows = max_rows;
441 void pa_murphyif_setup_domainctl(struct userdata *u, pa_murphyif_watch_cb wcb)
443 static const char *name = "pulse";
445 pa_murphyif *murphyif;
446 domctl_interface *dif;
450 pa_assert_se((murphyif = u->murphyif));
452 dif = &murphyif->domctl;
455 if (dif->ntable || dif->nwatch) {
456 dif->ctl = mrp_domctl_create(name, murphyif->ml,
457 dif->tables, dif->ntable,
458 dif->watches, dif->nwatch,
459 domctl_connect_notify,
460 domctl_watch_notify, u);
462 pa_log("failed to create '%s' domain controller", name);
466 if (!mrp_domctl_connect(dif->ctl, dif->addr, 0)) {
467 pa_log("failed to conect to murphyd");
472 pa_log_info("'%s' domain controller sucessfully created", name);
477 void pa_murphyif_add_audio_resource(struct userdata *u,
482 static const char *columns = RESCOL_NAMES;
483 static int maxrow = MQI_QUERY_RESULT_MAX - 1;
485 pa_murphyif *murphyif;
486 resource_interface *rif;
487 audio_resource_t *res;
491 pa_assert(dir == mir_input || dir == mir_output);
494 pa_assert_se((murphyif = u->murphyif));
495 rif = &murphyif->resource;
498 if (dir == mir_input) {
499 if (rif->inpres.name)
500 pa_log("attempt to register playback resource multiple time");
505 if (rif->outres.name)
506 pa_log("attempt to register recording resource multiple time");
512 res->name = pa_xstrdup(name);
514 snprintf(table, sizeof(table), "%s_users", name);
515 res->tblidx = pa_murphyif_add_watch(u, table, columns, NULL, maxrow);
520 void pa_murphyif_add_audio_attribute(struct userdata *u,
523 mqi_data_type_t type,
524 ... ) /* default value */
526 #ifdef WITH_RESOURCES
527 pa_murphyif *murphyif;
528 resource_interface *rif;
529 resource_attribute *attr;
530 mrp_attr_value_t *val;
536 pa_assert(type == mqi_string || type == mqi_integer ||
537 type == mqi_unsignd || type == mqi_floating);
539 pa_assert_se((murphyif = u->murphyif));
540 rif = &murphyif->resource;
542 attr = pa_xnew0(resource_attribute, 1);
543 val = &attr->def.value;
545 attr->prop = pa_xstrdup(propnam);
546 attr->def.name = pa_xstrdup(attrnam);
547 attr->def.type = type;
552 case mqi_string: val->string = pa_xstrdup(va_arg(ap, char *)); break;
553 case mqi_integer: val->integer = va_arg(ap, int32_t); break;
554 case mqi_unsignd: val->unsignd = va_arg(ap, uint32_t); break;
555 case mqi_floating: val->floating = va_arg(ap, double); break;
556 default: attr->def.type = mqi_error; break;
561 if (attr->def.type == mqi_error)
562 resource_attribute_destroy(rif, attr);
564 PA_LLIST_PREPEND(resource_attribute, rif->attrs, attr);
568 void pa_murphyif_create_resource_set(struct userdata *u,
570 pa_nodeset_resdef *resdef)
573 pa_murphyif *murphyif;
574 resource_interface *rif;
579 pa_assert((!node->loop && node->implement == mir_stream) ||
580 ( node->loop && node->implement == mir_device) );
581 pa_assert(node->direction == mir_input || node->direction == mir_output);
582 pa_assert(node->zone);
583 pa_assert(!node->rsetid);
585 pa_assert_se((core = u->core));
587 pa_assert_se((murphyif = u->murphyif));
588 rif = &murphyif->resource;
590 state = resource_transport_connect(rif);
595 resource_set_create_all(u);
599 node->localrset = resource_set_create_node(u, node, resdef, TRUE);
607 void pa_murphyif_destroy_resource_set(struct userdata *u, mir_node *node)
609 pa_murphyif *murphyif;
615 pa_assert_se((murphyif = u->murphyif));
617 if (node->localrset && node->rsetid) {
619 pa_murphyif_delete_node(u, node);
621 rsetid = strtoul(node->rsetid, &e, 10);
623 if (e == node->rsetid || *e) {
624 pa_log("can't destroy resource set: invalid rsetid '%s'",
628 if (resource_set_destroy_node(u, rsetid))
629 pa_log_debug("resource set %u destruction request", rsetid);
631 pa_log("falied to destroy resourse set %u for node '%s'",
632 rsetid, node->amname);
635 pa_xfree(node->rsetid);
637 node->localrset = FALSE;
643 int pa_murphyif_add_node(struct userdata *u, mir_node *node)
645 #ifdef WITH_RESOURCES
646 pa_murphyif *murphyif;
647 resource_interface *rif;
655 pa_assert_se((murphyif = u->murphyif));
657 rif = &murphyif->resource;
660 pa_log("can't register resource set for node '%s'.: missing rsetid",
663 else if (pa_streq(node->rsetid, PA_RESOURCE_SET_ID_PID)) {
664 if (!(pid = get_node_pid(u,node)))
665 pa_log("can't obtain PID for node '%s'", node->amname);
667 if (pid_hashmap_put(u, pid, node, NULL) == 0)
670 if ((rset = pid_hashmap_remove_rset(u, pid))) {
671 pa_log_debug("found resource-set %s for node '%s'",
672 rset->id, node->amname);
674 if (node_put_rset(u, node, rset) == 0) {
675 node_enforce_resource_policy(u, node, rset);
676 rset_data_free(rset);
680 pa_log("can't register resource set for node '%s': "
681 "failed to set rsetid", node->amname);
683 rset_data_free(rset);
686 pa_log("can't register resource set for node '%s': "
687 "conflicting pid", node->amname);
692 if (pa_hashmap_put(rif->nodes.rsetid, node->rsetid, node) == 0)
695 pa_log("can't register resource set for node '%s': conflicting "
696 "resource id '%s'", node->amname, node->rsetid);
706 void pa_murphyif_delete_node(struct userdata *u, mir_node *node)
708 #ifdef WITH_RESOURCES
709 pa_murphyif *murphyif;
710 resource_interface *rif;
717 pa_assert_se((murphyif = u->murphyif));
719 rif = &murphyif->resource;
722 if (pa_streq(node->rsetid, PA_RESOURCE_SET_ID_PID)) {
723 if ((pid = get_node_pid(u, node))) {
724 if (node == pid_hashmap_get_node(u, pid))
725 pid_hashmap_remove_node(u, pid);
727 pa_log("pid %s seems to have multiple resource sets. "
728 "Refuse to delete node %u (%s) from hashmap",
729 pid, node->index, node->amname);
734 deleted = pa_hashmap_remove(rif->nodes.rsetid, node->rsetid);
735 pa_assert(!deleted || deleted == node);
741 #ifdef WITH_RESOURCES
742 static mir_node *find_node_by_rsetid(struct userdata *u, const char *rsetid)
744 pa_murphyif *murphyif;
745 resource_interface *rif;
749 pa_assert_se((murphyif = u->murphyif));
751 rif = &murphyif->resource;
756 node = pa_hashmap_get(rif->nodes.rsetid, rsetid);
764 static void domctl_connect_notify(mrp_domctl_t *dc, int connected, int errcode,
765 const char *errmsg, void *user_data)
768 MRP_UNUSED(user_data);
771 pa_log_info("Successfully registered to Murphy.");
773 pa_log_error("Domain control Connection to Murphy failed (%d: %s).",
778 static void domctl_watch_notify(mrp_domctl_t *dc, mrp_domctl_data_t *tables,
779 int ntable, void *user_data)
781 struct userdata *u = (struct userdata *)user_data;
782 pa_murphyif *murphyif;
783 domctl_interface *dif;
784 resource_interface *rif;
785 mrp_domctl_data_t *t;
786 mrp_domctl_watch_t *w;
792 pa_assert(ntable > 0);
794 pa_assert_se((murphyif = u->murphyif));
796 dif = &murphyif->domctl;
797 rif = &murphyif->resource;
799 pa_log_info("Received change notification for %d tables.", ntable);
801 for (i = 0; i < ntable; i++) {
806 pa_assert(t->id >= 0);
807 pa_assert(t->id < dif->nwatch);
809 w = dif->watches + t->id;
811 #ifdef WITH_RESOURCES
812 if (t->id == rif->inpres.tblidx || t->id == rif->outres.tblidx) {
813 resource_set_notification(u, w->table, t->nrow, t->rows);
818 dif->watchcb(u, w->table, t->nrow, t->rows);
822 static void domctl_dump_data(mrp_domctl_data_t *table)
824 mrp_domctl_value_t *row;
830 pa_log_debug("Table #%d: %d rows x %d columns", table->id,
831 table->nrow, table->ncolumn);
833 for (i = 0; i < table->nrow; i++) {
834 row = table->rows[i];
838 for (j = 0, t = ""; j < table->ncolumn; j++, t = ", ") {
839 switch (row[j].type) {
840 case MRP_DOMCTL_STRING:
841 l = snprintf(p, n, "%s'%s'", t, row[j].str);
845 case MRP_DOMCTL_INTEGER:
846 l = snprintf(p, n, "%s%d", t, row[j].s32);
850 case MRP_DOMCTL_UNSIGNED:
851 l = snprintf(p, n, "%s%u", t, row[j].u32);
855 case MRP_DOMCTL_DOUBLE:
856 l = snprintf(p, n, "%s%f", t, row[j].dbl);
861 l = snprintf(p, n, "%s<invalid column 0x%x>",
868 pa_log_debug("row #%d: { %s }", i, buf);
873 #ifdef WITH_RESOURCES
874 static void resource_attribute_destroy(resource_interface *rif,
875 resource_attribute *attr)
879 PA_LLIST_REMOVE(resource_attribute, rif->attrs, attr);
881 pa_xfree((void *)attr->prop);
882 pa_xfree((void *)attr->def.name);
884 if (attr->def.type == mqi_string)
885 pa_xfree((void *)attr->def.value.string);
891 static int resource_transport_connect(resource_interface *rif)
900 if (!mrp_transport_connect(rif->transp, &rif->saddr, rif->alen))
901 status = DISCONNECTED;
903 pa_log_info("resource transport connected to '%s'", rif->addr);
904 rif->connected = TRUE;
912 static void resource_xport_closed_evt(mrp_transport_t *transp, int error,
915 struct userdata *u = (struct userdata *)void_u;
916 pa_murphyif *murphyif;
917 resource_interface *rif;
922 pa_assert_se((murphyif = u->murphyif));
924 rif = &murphyif->resource;
927 pa_log("Resource transport connection closed by peer");
929 pa_log("Resource transport connection closed with error %d (%s)",
930 error, strerror(error));
933 resource_transport_destroy(murphyif);
934 resource_set_destroy_all(u);
935 schedule_connect(u, rif);
938 static mrp_msg_t *resource_create_request(uint32_t seqno,
939 mrp_resproto_request_t req)
944 msg = mrp_msg_create(RESPROTO_SEQUENCE_NO , MRP_MSG_FIELD_UINT32, seqno,
945 RESPROTO_REQUEST_TYPE, MRP_MSG_FIELD_UINT16, type ,
946 RESPROTO_MESSAGE_END );
949 pa_log("can't to create new resource message");
954 static pa_bool_t resource_send_message(resource_interface *rif,
960 resource_request *req;
961 pa_bool_t success = TRUE;
963 if (!mrp_transport_send(rif->transp, msg)) {
964 pa_log("failed to send resource message");
968 req = pa_xnew0(resource_request, 1);
969 req->nodidx = nodidx;
973 PA_LLIST_PREPEND(resource_request, rif->reqs, req);
981 static pa_bool_t resource_set_create_node(struct userdata *u,
983 pa_nodeset_resdef *resdef,
987 pa_murphyif *murphyif;
988 resource_interface *rif;
989 resource_request *req;
999 pa_source_output *sout;
1000 audio_resource_t *res;
1002 mir_node_type type = 0;
1003 uint32_t audio_flags = 0;
1005 pa_proplist *proplist = NULL;
1006 pa_bool_t success = TRUE;
1010 pa_assert(node->index != PA_IDXSET_INVALID);
1011 pa_assert((!node->loop && node->implement == mir_stream) ||
1012 ( node->loop && node->implement == mir_device) );
1013 pa_assert(node->direction == mir_input || node->direction == mir_output);
1014 pa_assert(node->zone);
1015 pa_assert(!node->rsetid);
1017 pa_assert_se((core = u->core));
1019 if ((loop = node->loop)) {
1020 if (node->direction == mir_input) {
1021 sout = pa_idxset_get_by_index(core->source_outputs,
1022 loop->source_output_index);
1024 proplist = sout->proplist;
1027 sinp = pa_idxset_get_by_index(core->sink_inputs,
1028 loop->sink_input_index);
1030 proplist = sinp->proplist;
1032 if (proplist && (role = pa_proplist_gets(proplist, PA_PROP_MEDIA_ROLE))) {
1033 if ((map = pa_nodeset_get_map_by_role(u, role)))
1038 if (node->direction == mir_output) {
1039 if ((sout = pa_idxset_get_by_index(core->source_outputs, node->paidx)))
1040 proplist = sout->proplist;
1043 if ((sinp = pa_idxset_get_by_index(core->sink_inputs, node->paidx)))
1044 proplist = sinp->proplist;
1049 pa_assert_se((class = pa_nodeset_get_class(u, type)));
1050 pa_assert_se((murphyif = u->murphyif));
1051 rif = &murphyif->resource;
1053 reqid = RESPROTO_CREATE_RESOURCE_SET;
1054 seqno = rif->seqno.request++;
1055 res = (node->direction == mir_input) ? &rif->inpres : &rif->outres;
1057 pa_assert_se((resnam = res->name));
1059 rset_flags = RESPROTO_RSETFLAG_NOEVENTS;
1060 rset_flags |= (acquire ? RESPROTO_RSETFLAG_AUTOACQUIRE : 0);
1061 rset_flags |= (resdef ? resdef->flags.rset : 0);
1063 audio_flags = (resdef ? resdef->flags.audio : 0);
1065 priority = (resdef ? resdef->priority : 0);
1067 msg = resource_create_request(seqno, reqid);
1069 if (PUSH_VALUE(msg, RESOURCE_FLAGS , UINT32, rset_flags) &&
1070 PUSH_VALUE(msg, RESOURCE_PRIORITY, UINT32, priority) &&
1071 PUSH_VALUE(msg, CLASS_NAME , STRING, class) &&
1072 PUSH_VALUE(msg, ZONE_NAME , STRING, node->zone) &&
1073 PUSH_VALUE(msg, RESOURCE_NAME , STRING, resnam) &&
1074 PUSH_VALUE(msg, RESOURCE_FLAGS , UINT32, audio_flags) &&
1075 PUSH_VALUE(msg, ATTRIBUTE_NAME , STRING, "policy") &&
1076 PUSH_VALUE(msg, ATTRIBUTE_VALUE , STRING, "strict") &&
1077 PUSH_ATTRS(msg, rif, proplist) &&
1078 PUSH_VALUE(msg, SECTION_END , UINT8 , 0) )
1080 success = resource_send_message(rif, msg, node->index, reqid, seqno);
1088 pa_log_debug("requested resource set for '%s'", node->amname);
1090 pa_log_debug("failed to create resource set for '%s'", node->amname);
1095 static pa_bool_t resource_set_create_all(struct userdata *u)
1105 idx = PA_IDXSET_INVALID;
1107 while ((node = pa_nodeset_iterate_nodes(u, &idx))) {
1108 if ((node->implement == mir_stream && !node->loop) ||
1109 (node->implement == mir_device && node->loop) )
1111 if (!node->rsetid) {
1112 node->localrset = resource_set_create_node(u, node, NULL, FALSE);
1113 success &= node->localrset;
1121 static pa_bool_t resource_set_destroy_node(struct userdata *u, uint32_t rsetid)
1123 pa_murphyif *murphyif;
1124 resource_interface *rif;
1133 pa_assert_se((murphyif = u->murphyif));
1134 rif = &murphyif->resource;
1136 reqid = RESPROTO_DESTROY_RESOURCE_SET;
1137 seqno = rif->seqno.request++;
1138 nodidx = PA_IDXSET_INVALID;
1139 msg = resource_create_request(seqno, reqid);
1141 if (PUSH_VALUE(msg, RESOURCE_SET_ID, UINT32, rsetid))
1142 success = resource_send_message(rif, msg, nodidx, reqid, seqno);
1151 static pa_bool_t resource_set_destroy_all(struct userdata *u)
1153 pa_murphyif *murphyif;
1154 resource_interface *rif;
1162 pa_assert_se((murphyif = u->murphyif));
1164 rif = &murphyif->resource;
1168 idx = PA_IDXSET_INVALID;
1170 while ((node = pa_nodeset_iterate_nodes(u, &idx))) {
1171 if (node->implement == mir_stream && node->localrset) {
1172 pa_log_debug("destroying resource set for '%s'", node->amname);
1174 if (rif->connected && node->rsetid) {
1175 rsetid = strtoul(node->rsetid, &e, 10);
1177 if (e == node->rsetid || *e)
1180 success &= resource_set_destroy_node(u, rsetid);
1183 pa_xfree(node->rsetid);
1185 node->localrset = FALSE;
1186 node->rsetid = NULL;
1193 static void resource_set_notification(struct userdata *u,
1196 mrp_domctl_value_t **values)
1198 pa_murphyif *murphyif;
1199 resource_interface *rif;
1201 mrp_domctl_value_t *row;
1202 mrp_domctl_value_t *crsetid;
1203 mrp_domctl_value_t *cautorel;
1204 mrp_domctl_value_t *cstate;
1205 mrp_domctl_value_t *cgrant;
1206 mrp_domctl_value_t *cpid;
1207 mrp_domctl_value_t *cpolicy;
1211 rset_data rset, *rs;
1216 pa_assert_se((murphyif = u->murphyif));
1217 rif = &murphyif->resource;
1219 for (r = 0; r < nrow; r++) {
1221 crsetid = row + RESCOL_RSETID;
1222 cautorel = row + RESCOL_AUTOREL;
1223 cstate = row + RESCOL_STATE;
1224 cgrant = row + RESCOL_GRANT;
1225 cpid = row + RESCOL_PID;
1226 cpolicy = row + RESCOL_POLICY;
1228 if (crsetid->type != MRP_DOMCTL_UNSIGNED ||
1229 cautorel->type != MRP_DOMCTL_INTEGER ||
1230 cstate->type != MRP_DOMCTL_INTEGER ||
1231 cgrant->type != MRP_DOMCTL_INTEGER ||
1232 cpid->type != MRP_DOMCTL_STRING ||
1233 cpolicy->type != MRP_DOMCTL_STRING )
1235 pa_log("invalid field type in '%s' (%d|%d|%d|%d|%d|%d)", table,
1236 crsetid->type, cautorel->type, cstate->type,
1237 cgrant->type, cpid->type, cpolicy->type);
1241 snprintf(rsetid, sizeof(rsetid), "%d", crsetid->s32);
1245 rset.autorel = cautorel->s32;
1246 rset.state = cstate->s32;
1247 rset.grant = cgrant->s32;
1248 rset.policy = cpolicy->str;
1251 if (rset.autorel != 0 && rset.autorel != 1) {
1252 pa_log_debug("invalid autorel %d in table '%s'",
1253 rset.autorel, table);
1256 if (rset.state != RSET_RELEASE && rset.state != RSET_ACQUIRE) {
1257 pa_log_debug("invalid state %d in table '%s'", rset.state, table);
1260 if (rset.grant != 0 && rset.grant != 1) {
1261 pa_log_debug("invalid grant %d in table '%s'", rset.grant, table);
1265 if (!(node = find_node_by_rsetid(u, rset.id))) {
1267 pa_log_debug("can't find node for resource set %s "
1268 "(pid in resource set unknown)", rset.id);
1272 if ((node = pid_hashmap_remove_node(u, pid))) {
1273 pa_log_debug("found node %s for resource-set '%s'",
1274 node->amname, rset.id);
1276 if (node_put_rset(u, node, &rset) < 0) {
1277 pa_log("can't register resource set for node '%s': "
1278 "failed to set rsetid", node->amname);
1283 if (pid_hashmap_put(u, pid, NULL, rset_data_dup(&rset)) < 0) {
1284 if (!(rs = pid_hashmap_get_rset(u, pid)))
1285 pa_log("failed to add resource set to pid hash");
1287 if (!pa_streq(rs->id, rset.id)) {
1288 pa_log("process %s appears to have multiple resour"
1289 "ce sets (%s and %s)", pid, rs->id,rset.id);
1291 pa_log_debug("update resource-set %s data in "
1292 "pid hash (pid %s)", rs->id, pid);
1293 rset_data_copy(rs, &rset);
1297 pa_log_debug("can't find node for resource set %s. "
1298 "Beleive the stream will appear later on",
1306 pa_log_debug("resource notification for node '%s' autorel:%s state:%s "
1307 "grant:%s pid:%s policy:%s", node->amname,
1308 rset.autorel ? "yes":"no",
1309 rset.state == RSET_ACQUIRE ? "acquire":"release",
1310 rset.grant ? "yes":"no", pid, rset.policy);
1312 node_enforce_resource_policy(u, node, &rset);
1317 static pa_bool_t resource_push_attributes(mrp_msg_t *msg,
1318 resource_interface *rif,
1319 pa_proplist *proplist)
1321 resource_attribute *attr;
1335 PA_LLIST_FOREACH(attr, rif->attrs) {
1336 if (!PUSH_VALUE(msg, ATTRIBUTE_NAME, STRING, attr->def.name))
1340 sts = pa_proplist_get(proplist, attr->prop, &v.ptr, &size);
1344 switch (attr->def.type) {
1347 v.str = attr->def.value.string;
1348 else if (v.str[size-1] != '\0' || strlen(v.str) != (size-1) ||
1349 !pa_utf8_valid(v.str))
1351 if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, STRING, v.str))
1357 v.i32 = &attr->def.value.integer;
1358 else if (size != sizeof(*v.i32))
1360 if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, SINT8, *v.i32))
1366 v.u32 = &attr->def.value.unsignd;
1367 else if (size != sizeof(*v.u32))
1369 if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, SINT8, *v.u32))
1375 v.dbl = &attr->def.value.floating;
1376 else if (size != sizeof(*v.dbl))
1378 if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, SINT8, *v.dbl))
1382 default: /* we should never get here */
1392 static void resource_recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *void_u)
1394 return resource_recvfrom_msg(t, msg, NULL, 0, void_u);
1397 static void resource_recvfrom_msg(mrp_transport_t *transp, mrp_msg_t *msg,
1398 mrp_sockaddr_t *addr, socklen_t addrlen,
1401 struct userdata *u = (struct userdata *)void_u;
1403 pa_murphyif *murphyif;
1404 resource_interface *rif;
1409 resource_request *req, *n;
1414 MRP_UNUSED(addrlen);
1417 pa_assert_se((core = u->core));
1418 pa_assert_se((murphyif = u->murphyif));
1420 rif = &murphyif->resource;
1422 if (!resource_fetch_seqno (msg, &curs, &seqno) ||
1423 !resource_fetch_request (msg, &curs, &reqid) )
1425 pa_log("ignoring malformed message");
1429 PA_LLIST_FOREACH_SAFE(req, n, rif->reqs) {
1430 if (req->seqno <= seqno) {
1431 nodidx = req->nodidx;
1433 if (req->reqid == reqid) {
1434 PA_LLIST_REMOVE(resource_request, rif->reqs, req);
1438 if (!(node = mir_node_find_by_index(u, nodidx))) {
1439 if (reqid != RESPROTO_DESTROY_RESOURCE_SET) {
1440 pa_log("got response (reqid:%u seqno:%u) but can't "
1441 "find the corresponding node", reqid, seqno);
1442 resource_set_create_response_abort(u, msg, &curs);
1446 if (req->seqno < seqno) {
1447 pa_log("unanswered request %d", req->seqno);
1450 pa_log_debug("got response (reqid:%u seqno:%u "
1451 "node:'%s')", reqid, seqno,
1452 node ? node->amname : "<unknown>");
1455 case RESPROTO_CREATE_RESOURCE_SET:
1456 resource_set_create_response(u, node, msg, &curs);
1458 case RESPROTO_DESTROY_RESOURCE_SET:
1461 pa_log("ignoring unsupported resource request "
1467 } /* PA_LLIST_FOREACH_SAFE */
1472 static void resource_set_create_response(struct userdata *u, mir_node *node,
1473 mrp_msg_t *msg, void **pcursor)
1484 if (!resource_fetch_status(msg, pcursor, &status) || (status == 0 &&
1485 !resource_fetch_rset_id(msg, pcursor, &rsetid)))
1487 pa_log("ignoring malformed response to resource set creation");
1492 pa_log("creation of resource set failed. error code %u", status);
1496 node->rsetid = pa_sprintf_malloc("%d", rsetid);
1498 if (pa_murphyif_add_node(u, node) == 0) {
1499 pa_log_debug("resource set was successfully created");
1500 mir_node_print(node, buf, sizeof(buf));
1501 pa_log_debug("modified node:\n%s", buf);
1504 pa_log("failed to create resource set: "
1505 "conflicting resource set id");
1509 static void resource_set_create_response_abort(struct userdata *u,
1510 mrp_msg_t *msg, void **pcursor)
1519 if (!resource_fetch_status(msg, pcursor, &status) || (status == 0 &&
1520 !resource_fetch_rset_id(msg, pcursor, &rsetid)))
1522 pa_log("ignoring malformed response to resource set creation");
1527 pa_log("creation of resource set failed. error code %u", status);
1531 if (resource_set_destroy_node(u, rsetid))
1532 pa_log_debug("destroying resource set %u", rsetid);
1534 pa_log("attempt to destroy resource set %u failed", rsetid);
1538 static pa_bool_t resource_fetch_seqno(mrp_msg_t *msg,
1544 mrp_msg_value_t value;
1547 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1548 tag != RESPROTO_SEQUENCE_NO || type != MRP_MSG_FIELD_UINT32)
1550 *pseqno = INVALID_SEQNO;
1554 *pseqno = value.u32;
1559 static pa_bool_t resource_fetch_request(mrp_msg_t *msg,
1565 mrp_msg_value_t value;
1568 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1569 tag != RESPROTO_REQUEST_TYPE || type != MRP_MSG_FIELD_UINT16)
1571 *preqtype = INVALID_REQUEST;
1575 *preqtype = value.u16;
1579 static pa_bool_t resource_fetch_status(mrp_msg_t *msg,
1585 mrp_msg_value_t value;
1588 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1589 tag != RESPROTO_REQUEST_STATUS || type != MRP_MSG_FIELD_SINT16)
1595 *pstatus = value.s16;
1599 static pa_bool_t resource_fetch_rset_id(mrp_msg_t *msg,
1605 mrp_msg_value_t value;
1608 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1609 tag != RESPROTO_RESOURCE_SET_ID || type != MRP_MSG_FIELD_UINT32)
1619 static pa_bool_t resource_fetch_rset_state(mrp_msg_t *msg,
1621 mrp_resproto_state_t *pstate)
1625 mrp_msg_value_t value;
1628 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1629 tag != RESPROTO_RESOURCE_STATE || type != MRP_MSG_FIELD_UINT16)
1635 *pstate = value.u16;
1640 static pa_bool_t resource_fetch_rset_mask(mrp_msg_t *msg,
1642 mrp_resproto_state_t *pmask)
1646 mrp_msg_value_t value;
1649 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1650 tag != RESPROTO_RESOURCE_GRANT || type != MRP_MSG_FIELD_UINT32)
1660 static pa_bool_t resource_transport_create(struct userdata *u,
1661 pa_murphyif *murphyif)
1663 static mrp_transport_evt_t ev = {
1664 { .recvmsg = resource_recv_msg },
1665 { .recvmsgfrom = resource_recvfrom_msg },
1666 .closed = resource_xport_closed_evt,
1670 resource_interface *rif;
1673 pa_assert(murphyif);
1675 rif = &murphyif->resource;
1678 rif->transp = mrp_transport_create(murphyif->ml, rif->atype, &ev, u,0);
1680 return rif->transp ? TRUE : FALSE;
1683 static void resource_transport_destroy(pa_murphyif *murphyif)
1685 resource_interface *rif;
1687 pa_assert(murphyif);
1688 rif = &murphyif->resource;
1691 mrp_transport_destroy(rif->transp);
1694 rif->connected = FALSE;
1697 static void connect_attempt(pa_mainloop_api *a,
1699 const struct timeval *t,
1702 struct userdata *u = (struct userdata *)data;
1703 pa_murphyif *murphyif;
1704 resource_interface *rif;
1709 pa_assert_se((murphyif = u->murphyif));
1711 rif = &murphyif->resource;
1713 if (!resource_transport_create(u, murphyif))
1714 schedule_connect(u, rif);
1716 state = resource_transport_connect(rif);
1721 resource_set_create_all(u);
1722 cancel_schedule(u, rif);
1726 cancel_schedule(u, rif);
1730 schedule_connect(u, rif);
1736 static void schedule_connect(struct userdata *u, resource_interface *rif)
1739 pa_mainloop_api *mainloop;
1740 struct timeval when;
1745 pa_assert_se((core = u->core));
1746 pa_assert_se((mainloop = core->mainloop));
1748 pa_gettimeofday(&when);
1749 pa_timeval_add(&when, rif->connect.period);
1751 if ((tev = rif->connect.evt))
1752 mainloop->time_restart(tev, &when);
1754 rif->connect.evt = mainloop->time_new(mainloop, &when,
1755 connect_attempt, u);
1759 static void cancel_schedule(struct userdata *u, resource_interface *rif)
1762 pa_mainloop_api *mainloop;
1767 pa_assert_se((core = u->core));
1768 pa_assert_se((mainloop = core->mainloop));
1770 if ((tev = rif->connect.evt)) {
1771 mainloop->time_free(tev);
1772 rif->connect.evt = NULL;
1776 static int node_put_rset(struct userdata *u, mir_node *node, rset_data *rset)
1778 pa_murphyif *murphyif;
1779 resource_interface *rif;
1785 pa_assert(rset->id);
1787 pa_assert(node->implement == mir_stream);
1788 pa_assert(node->direction == mir_input || node->direction == mir_output);
1790 pa_assert_se((murphyif = u->murphyif));
1791 rif = &murphyif->resource;
1793 pa_log_debug("setting rsetid %s for node %s", rset->id, node->amname);
1795 pa_xfree(node->rsetid);
1796 node->rsetid = pa_xstrdup(rset->id);
1798 if (!(pl = get_node_proplist(u, node))) {
1799 pa_log("can't obtain property list for node %s", node->amname);
1803 if ((pa_proplist_sets(pl, PA_PROP_RESOURCE_SET_ID, node->rsetid) < 0)) {
1804 pa_log("failed to set '" PA_PROP_RESOURCE_SET_ID "' property "
1805 "of '%s' node", node->amname);
1809 if (pa_hashmap_put(rif->nodes.rsetid, node->rsetid, node) < 0) {
1810 pa_log("conflicting rsetid %s for %s", node->rsetid, node->amname);
1817 static void node_enforce_resource_policy(struct userdata *u,
1825 pa_assert(rset->policy);
1828 if (pa_streq(rset->policy, "relaxed"))
1829 req = PA_STREAM_RUN;
1831 if (rset->state == RSET_RELEASE)
1832 req = PA_STREAM_KILL;
1835 req = PA_STREAM_RUN;
1837 req = PA_STREAM_BLOCK;
1841 pa_stream_state_change(u, node, req);
1844 static rset_data *rset_data_dup(rset_data *orig)
1849 pa_assert(orig->id);
1850 pa_assert(orig->policy);
1852 dup = pa_xnew0(rset_data, 1);
1854 dup->id = pa_xstrdup(orig->id);
1855 dup->autorel = orig->autorel;
1856 dup->state = orig->state;
1857 dup->grant = orig->grant;
1858 dup->policy = pa_xstrdup(orig->policy);
1863 static void rset_data_copy(rset_data *dst, rset_data *src)
1870 pa_assert(src->policy);
1872 pa_xfree((void *)dst->id);
1873 pa_xfree((void *)dst->policy);
1875 dst->id = pa_xstrdup(src->id);
1876 dst->autorel = src->autorel;
1877 dst->state = src->state;
1878 dst->grant = src->grant;
1879 dst->policy = pa_xstrdup(src->policy);
1883 static void rset_data_free(rset_data *rset)
1886 pa_xfree((void *)rset->id);
1887 pa_xfree((void *)rset->policy);
1892 static void pid_hashmap_free(void *p, void *userdata)
1894 pid_hash *ph = (pid_hash *)p;
1899 pa_xfree((void *)ph->pid);
1900 rset_data_free(ph->rset);
1905 static int pid_hashmap_put(struct userdata *u, const char *pid,
1906 mir_node *node, rset_data *rset)
1908 pa_murphyif *murphyif;
1909 resource_interface *rif;
1914 pa_assert(node || rset);
1915 pa_assert_se((murphyif = u->murphyif));
1917 rif = &murphyif->resource;
1919 ph = pa_xnew0(pid_hash, 1);
1920 ph->pid = pa_xstrdup(pid);
1924 if (pa_hashmap_put(rif->nodes.pid, ph->pid, ph) == 0)
1927 pid_hashmap_free(ph, NULL);
1932 static mir_node *pid_hashmap_get_node(struct userdata *u, const char *pid)
1934 pa_murphyif *murphyif;
1935 resource_interface *rif;
1940 pa_assert(murphyif = u->murphyif);
1942 rif = &murphyif->resource;
1944 if ((ph = pa_hashmap_get(rif->nodes.pid, pid)))
1950 static rset_data *pid_hashmap_get_rset(struct userdata *u, const char *pid)
1952 pa_murphyif *murphyif;
1953 resource_interface *rif;
1958 pa_assert(murphyif = u->murphyif);
1960 rif = &murphyif->resource;
1962 if ((ph = pa_hashmap_get(rif->nodes.pid, pid)))
1968 static mir_node *pid_hashmap_remove_node(struct userdata *u, const char *pid)
1970 pa_murphyif *murphyif;
1971 resource_interface *rif;
1976 pa_assert_se((murphyif = u->murphyif));
1978 rif = &murphyif->resource;
1980 if (!(ph = pa_hashmap_remove(rif->nodes.pid, pid)))
1982 else if (!(node = ph->node))
1983 pa_hashmap_put(rif->nodes.pid, ph->pid, ph);
1985 pid_hashmap_free(ph, NULL);
1990 static rset_data *pid_hashmap_remove_rset(struct userdata *u, const char *pid)
1992 pa_murphyif *murphyif;
1993 resource_interface *rif;
2000 pa_assert_se((murphyif = u->murphyif));
2002 rif = &murphyif->resource;
2004 if (!(ph = pa_hashmap_remove(rif->nodes.pid, pid)))
2006 else if (!(rset = ph->rset))
2007 pa_hashmap_put(rif->nodes.pid, ph->pid, ph);
2010 pid_hashmap_free(ph, NULL);
2020 static pa_proplist *get_node_proplist(struct userdata *u, mir_node *node)
2024 pa_source_output *o;
2028 pa_assert_se((core = u->core));
2030 if (node->implement == mir_stream && node->paidx != PA_IDXSET_INVALID) {
2031 if (node->direction == mir_input) {
2032 if ((i = pa_idxset_get_by_index(core->sink_inputs, node->paidx)))
2035 else if (node->direction == mir_output) {
2036 if ((o = pa_idxset_get_by_index(core->source_outputs,node->paidx)))
2044 static const char *get_node_pid(struct userdata *u, mir_node *node)
2050 if (node && (pl = get_node_proplist(u, node)))
2051 return pa_proplist_gets(pl, PA_PROP_APPLICATION_PROCESS_ID);
2059 * indent-tabs-mode: nil