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"
59 #define INVALID_ID (~(uint32_t)0)
60 #define INVALID_INDEX (~(uint32_t)0)
61 #define INVALID_SEQNO (~(uint32_t)0)
62 #define INVALID_REQUEST (~(uint16_t)0)
64 #define DISCONNECTED -1
68 #define RESCOL_NAMES "rsetid,autorel,state,grant,pid,policy"
69 #define RESCOL_RSETID 0
70 #define RESCOL_AUTOREL 1
71 #define RESCOL_STATE 2
72 #define RESCOL_GRANT 3
74 #define RESCOL_POLICY 5
76 #define RSET_RELEASE 1
77 #define RSET_ACQUIRE 2
79 #define PUSH_VALUE(msg, tag, typ, val) \
80 mrp_msg_append(msg, MRP_MSG_TAG_##typ(RESPROTO_##tag, val))
82 #define PUSH_ATTRS(msg, rif, proplist) \
83 resource_push_attributes(msg, rif, proplist)
85 typedef struct resource_attribute resource_attribute;
86 typedef struct resource_request resource_request;
88 struct resource_attribute {
89 PA_LLIST_FIELDS(resource_attribute);
94 struct resource_request {
95 PA_LLIST_FIELDS(resource_request);
108 mrp_domctl_table_t *tables;
110 mrp_domctl_watch_t *watches;
111 pa_murphyif_watch_cb watchcb;
122 audio_resource_t inpres;
123 audio_resource_t outres;
124 #ifdef WITH_RESOURCES
125 mrp_transport_t *transp;
126 mrp_sockaddr_t saddr;
142 PA_LLIST_HEAD(resource_attribute, attrs);
143 PA_LLIST_HEAD(resource_request, reqs);
145 } resource_interface;
149 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
152 domctl_interface domctl;
153 resource_interface resource;
156 #ifdef WITH_RESOURCES
164 #ifdef WITH_RESOURCES
165 static mir_node *find_node_by_rsetid(struct userdata *, const char *);
169 static void domctl_connect_notify(mrp_domctl_t *,int,int,const char *,void *);
170 static void domctl_watch_notify(mrp_domctl_t *,mrp_domctl_data_t *,int,void *);
171 static void domctl_dump_data(mrp_domctl_data_t *);
174 #ifdef WITH_RESOURCES
175 static void resource_attribute_destroy(resource_interface *,
176 resource_attribute *);
177 static int resource_transport_connect(resource_interface *);
178 static void resource_xport_closed_evt(mrp_transport_t *, int, void *);
180 static mrp_msg_t *resource_create_request(uint32_t, mrp_resproto_request_t);
181 static pa_bool_t resource_send_message(resource_interface *, mrp_msg_t *,
182 uint32_t, uint16_t, uint32_t);
183 static pa_bool_t resource_set_create_node(struct userdata *, mir_node *,
184 pa_nodeset_resdef *, pa_bool_t);
185 static pa_bool_t resource_set_create_all(struct userdata *);
186 static pa_bool_t resource_set_destroy_node(struct userdata *, uint32_t);
187 static pa_bool_t resource_set_destroy_all(struct userdata *);
188 static void resource_set_notification(struct userdata *, const char *,
189 int, mrp_domctl_value_t **);
191 static pa_bool_t resource_push_attributes(mrp_msg_t *, resource_interface *,
194 static void resource_recv_msg(mrp_transport_t *, mrp_msg_t *, void *);
195 static void resource_recvfrom_msg(mrp_transport_t *, mrp_msg_t *,
196 mrp_sockaddr_t *, socklen_t, void *);
197 static void resource_set_create_response(struct userdata *, mir_node *,
198 mrp_msg_t *, void **);
199 static void resource_set_create_response_abort(struct userdata *,
200 mrp_msg_t *, void **);
202 static pa_bool_t resource_fetch_seqno(mrp_msg_t *, void **, uint32_t *);
203 static pa_bool_t resource_fetch_request(mrp_msg_t *, void **, uint16_t *);
204 static pa_bool_t resource_fetch_status(mrp_msg_t *, void **, int *);
205 static pa_bool_t resource_fetch_rset_id(mrp_msg_t *, void **, uint32_t*);
206 static pa_bool_t resource_fetch_rset_state(mrp_msg_t *, void **,
207 mrp_resproto_state_t *);
208 static pa_bool_t resource_fetch_rset_mask(mrp_msg_t *, void **,
209 mrp_resproto_state_t *);
211 static pa_bool_t resource_transport_create(struct userdata *, pa_murphyif *);
212 static void resource_transport_destroy(pa_murphyif *);
214 static void connect_attempt(pa_mainloop_api *, pa_time_event *,
215 const struct timeval *, void *);
216 static void schedule_connect(struct userdata *, resource_interface *);
217 static void cancel_schedule(struct userdata *, resource_interface *);
219 static void pid_hashmap_free(void *, void *);
220 static int pid_hashmap_put(struct userdata *, const char *, mir_node *);
221 static mir_node *pid_hashmap_get(struct userdata *, const char *);
222 static mir_node *pid_hashmap_remove(struct userdata *, const char *);
225 static pa_proplist *get_node_proplist(struct userdata *, mir_node *);
226 static const char *get_node_pid(struct userdata *, mir_node *);
229 pa_murphyif *pa_murphyif_init(struct userdata *u,
230 const char *ctl_addr,
231 const char *res_addr)
233 pa_murphyif *murphyif;
234 domctl_interface *dif;
235 resource_interface *rif;
236 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
239 if (!(ml = mrp_mainloop_pulse_get(u->core->mainloop))) {
240 pa_log_error("Failed to set up murphy mainloop.");
244 #ifdef WITH_RESOURCES
247 murphyif = pa_xnew0(pa_murphyif, 1);
248 dif = &murphyif->domctl;
249 rif = &murphyif->resource;
251 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
255 dif->addr = pa_xstrdup(ctl_addr ? ctl_addr:MRP_DEFAULT_DOMCTL_ADDRESS);
259 rif->addr = pa_xstrdup(res_addr ? res_addr:RESPROTO_DEFAULT_ADDRESS);
260 #ifdef WITH_RESOURCES
261 rif->alen = mrp_transport_resolve(NULL, rif->addr, &rif->saddr,
262 sizeof(rif->saddr), &rif->atype);
263 if (rif->alen <= 0) {
264 pa_log("can't resolve resource transport address '%s'", rif->addr);
267 rif->inpres.tblidx = -1;
268 rif->outres.tblidx = -1;
269 rif->connect.period = 1 * PA_USEC_PER_SEC;
271 if (!resource_transport_create(u, murphyif)) {
272 pa_log("failed to create resource transport");
273 schedule_connect(u, rif);
276 if (resource_transport_connect(rif) == DISCONNECTED)
277 schedule_connect(u, rif);
281 rif->seqno.request = 1;
282 rif->nodes.rsetid = pa_hashmap_new(pa_idxset_string_hash_func,
283 pa_idxset_string_compare_func);
284 rif->nodes.pid = pa_hashmap_new(pa_idxset_string_hash_func,
285 pa_idxset_string_compare_func);
286 PA_LLIST_HEAD_INIT(resource_attribute, rif->attrs);
287 PA_LLIST_HEAD_INIT(resource_request, rif->reqs);
294 void pa_murphyif_done(struct userdata *u)
296 pa_murphyif *murphyif;
297 domctl_interface *dif;
298 resource_interface *rif;
299 #ifdef WITH_RESOURCES
300 resource_attribute *attr, *a;
301 resource_request *req, *r;
304 if (u && (murphyif = u->murphyif)) {
306 mrp_domctl_table_t *t;
307 mrp_domctl_watch_t *w;
310 dif = &murphyif->domctl;
312 mrp_domctl_destroy(dif->ctl);
313 mrp_mainloop_destroy(murphyif->ml);
315 if (dif->ntable > 0 && dif->tables) {
316 for (i = 0; i < dif->ntable; i++) {
318 pa_xfree((void *)t->table);
319 pa_xfree((void *)t->mql_columns);
320 pa_xfree((void *)t->mql_index);
322 pa_xfree(dif->tables);
325 if (dif->nwatch > 0 && dif->watches) {
326 for (i = 0; i < dif->nwatch; i++) {
327 w = dif->watches + i;
328 pa_xfree((void *)w->table);
329 pa_xfree((void *)w->mql_columns);
330 pa_xfree((void *)w->mql_where);
332 pa_xfree(dif->watches);
335 pa_xfree((void *)dif->addr);
338 #ifdef WITH_RESOURCES
339 rif = &murphyif->resource;
341 resource_transport_destroy(murphyif);
343 pa_xfree((void *)rif->atype);
344 pa_hashmap_free(rif->nodes.rsetid, NULL, NULL);
345 pa_hashmap_free(rif->nodes.pid, pid_hashmap_free, NULL);
347 PA_LLIST_FOREACH_SAFE(attr, a, rif->attrs)
348 resource_attribute_destroy(rif, attr);
350 PA_LLIST_FOREACH_SAFE(req, r, rif->reqs)
353 pa_xfree((void *)rif->addr);
354 pa_xfree((void *)rif->inpres.name);
355 pa_xfree((void *)rif->outres.name);
363 void pa_murphyif_add_table(struct userdata *u,
368 pa_murphyif *murphyif;
369 domctl_interface *dif;
370 mrp_domctl_table_t *t;
377 pa_assert_se((murphyif = u->murphyif));
379 dif = &murphyif->domctl;
382 size = sizeof(mrp_domctl_table_t) * dif->ntable;
383 t = (dif->tables = pa_xrealloc(dif->tables, size)) + idx;
385 t->table = pa_xstrdup(table);
386 t->mql_columns = pa_xstrdup(columns);
387 t->mql_index = index ? pa_xstrdup(index) : NULL;
390 int pa_murphyif_add_watch(struct userdata *u,
396 pa_murphyif *murphyif;
397 domctl_interface *dif;
398 mrp_domctl_watch_t *w;
405 pa_assert(max_rows > 0 && max_rows < MQI_QUERY_RESULT_MAX);
406 pa_assert_se((murphyif = u->murphyif));
408 dif = &murphyif->domctl;
411 size = sizeof(mrp_domctl_watch_t) * dif->nwatch;
412 w = (dif->watches = pa_xrealloc(dif->watches, size)) + idx;
414 w->table = pa_xstrdup(table);
415 w->mql_columns = pa_xstrdup(columns);
416 w->mql_where = where ? pa_xstrdup(where) : NULL;
417 w->max_rows = max_rows;
422 void pa_murphyif_setup_domainctl(struct userdata *u, pa_murphyif_watch_cb wcb)
424 static const char *name = "pulse";
426 pa_murphyif *murphyif;
427 domctl_interface *dif;
431 pa_assert_se((murphyif = u->murphyif));
433 dif = &murphyif->domctl;
436 if (dif->ntable || dif->nwatch) {
437 dif->ctl = mrp_domctl_create(name, murphyif->ml,
438 dif->tables, dif->ntable,
439 dif->watches, dif->nwatch,
440 domctl_connect_notify,
441 domctl_watch_notify, u);
443 pa_log("failed to create '%s' domain controller", name);
447 if (!mrp_domctl_connect(dif->ctl, dif->addr, 0)) {
448 pa_log("failed to conect to murphyd");
453 pa_log_info("'%s' domain controller sucessfully created", name);
458 void pa_murphyif_add_audio_resource(struct userdata *u,
463 static const char *columns = RESCOL_NAMES;
464 static int maxrow = MQI_QUERY_RESULT_MAX - 1;
466 pa_murphyif *murphyif;
467 resource_interface *rif;
468 audio_resource_t *res;
472 pa_assert(dir == mir_input || dir == mir_output);
475 pa_assert_se((murphyif = u->murphyif));
476 rif = &murphyif->resource;
479 if (dir == mir_input) {
480 if (rif->inpres.name)
481 pa_log("attempt to register playback resource multiple time");
486 if (rif->outres.name)
487 pa_log("attempt to register recording resource multiple time");
493 res->name = pa_xstrdup(name);
495 snprintf(table, sizeof(table), "%s_users", name);
496 res->tblidx = pa_murphyif_add_watch(u, table, columns, NULL, maxrow);
501 void pa_murphyif_add_audio_attribute(struct userdata *u,
504 mqi_data_type_t type,
505 ... ) /* default value */
507 #ifdef WITH_RESOURCES
508 pa_murphyif *murphyif;
509 resource_interface *rif;
510 resource_attribute *attr;
511 mrp_attr_value_t *val;
517 pa_assert(type == mqi_string || type == mqi_integer ||
518 type == mqi_unsignd || type == mqi_floating);
520 pa_assert_se((murphyif = u->murphyif));
521 rif = &murphyif->resource;
523 attr = pa_xnew0(resource_attribute, 1);
524 val = &attr->def.value;
526 attr->prop = pa_xstrdup(propnam);
527 attr->def.name = pa_xstrdup(attrnam);
528 attr->def.type = type;
533 case mqi_string: val->string = pa_xstrdup(va_arg(ap, char *)); break;
534 case mqi_integer: val->integer = va_arg(ap, int32_t); break;
535 case mqi_unsignd: val->unsignd = va_arg(ap, uint32_t); break;
536 case mqi_floating: val->floating = va_arg(ap, double); break;
537 default: attr->def.type = mqi_error; break;
542 if (attr->def.type == mqi_error)
543 resource_attribute_destroy(rif, attr);
545 PA_LLIST_PREPEND(resource_attribute, rif->attrs, attr);
549 void pa_murphyif_create_resource_set(struct userdata *u,
551 pa_nodeset_resdef *resdef)
554 pa_murphyif *murphyif;
555 resource_interface *rif;
561 pa_assert(node->implement == mir_stream);
562 pa_assert(node->direction == mir_input || node->direction == mir_output);
563 pa_assert(node->zone);
564 pa_assert(!node->rsetid);
566 pa_assert_se((core = u->core));
567 pa_assert_se((class = pa_nodeset_get_class(u, node->type)));
569 pa_assert_se((murphyif = u->murphyif));
570 rif = &murphyif->resource;
572 state = resource_transport_connect(rif);
577 resource_set_create_all(u);
581 node->localrset = resource_set_create_node(u, node, resdef, TRUE);
589 void pa_murphyif_destroy_resource_set(struct userdata *u, mir_node *node)
591 pa_murphyif *murphyif;
597 pa_assert_se((murphyif = u->murphyif));
599 if (node->localrset && node->rsetid) {
600 rsetid = strtoul(node->rsetid, &e, 10);
602 if (e == node->rsetid || *e) {
603 pa_log("can't destroy resource set: invalid rsetid '%s'",
607 if (resource_set_destroy_node(u, rsetid))
608 pa_log_debug("resource set %u destruction request", rsetid);
610 pa_log("falied to destroy resourse set %u for node '%s'",
611 rsetid, node->amname);
614 pa_xfree(node->rsetid);
616 node->localrset = FALSE;
620 pa_murphyif_delete_node(u, node);
624 int pa_murphyif_add_node(struct userdata *u, mir_node *node)
626 #ifdef WITH_RESOURCES
627 pa_murphyif *murphyif;
628 resource_interface *rif;
633 pa_assert(node->implement == mir_stream);
635 pa_assert_se((murphyif = u->murphyif));
637 rif = &murphyif->resource;
640 pa_log("can't register resource set for node '%s'.: missing rsetid",
643 else if (pa_streq(node->rsetid, PA_RESOURCE_SET_ID_PID)) {
644 if ((pid = get_node_pid(u, node)) && pid_hashmap_put(u, pid,node) == 0)
647 pa_log("can't register resource set for node '%s': "
648 "conflicting or unset pid", node->amname);
652 if (pa_hashmap_put(rif->nodes.rsetid, node->rsetid, node) == 0)
655 pa_log("can't register resource set for node '%s': conflicting "
656 "resource id '%s'", node->amname, node->rsetid);
666 void pa_murphyif_delete_node(struct userdata *u, mir_node *node)
668 #ifdef WITH_RESOURCES
669 pa_murphyif *murphyif;
670 resource_interface *rif;
676 pa_assert(node->implement == mir_stream);
678 pa_assert_se((murphyif = u->murphyif));
680 rif = &murphyif->resource;
683 if (pa_streq(node->rsetid, PA_RESOURCE_SET_ID_PID)) {
684 if ((pid = get_node_pid(u, node))) {
685 deleted = pid_hashmap_remove(u, pid);
686 pa_assert(!deleted || deleted == node);
690 deleted = pa_hashmap_remove(rif->nodes.rsetid, node->rsetid);
691 pa_assert(!deleted || deleted == node);
697 #ifdef WITH_RESOURCES
698 static mir_node *find_node_by_rsetid(struct userdata *u, const char *rsetid)
700 pa_murphyif *murphyif;
701 resource_interface *rif;
705 pa_assert_se((murphyif = u->murphyif));
707 rif = &murphyif->resource;
712 node = pa_hashmap_get(rif->nodes.rsetid, rsetid);
720 static void domctl_connect_notify(mrp_domctl_t *dc, int connected, int errcode,
721 const char *errmsg, void *user_data)
724 MRP_UNUSED(user_data);
727 pa_log_info("Successfully registered to Murphy.");
729 pa_log_error("Domain control Connection to Murphy failed (%d: %s).",
734 static void domctl_watch_notify(mrp_domctl_t *dc, mrp_domctl_data_t *tables,
735 int ntable, void *user_data)
737 struct userdata *u = (struct userdata *)user_data;
738 pa_murphyif *murphyif;
739 domctl_interface *dif;
740 resource_interface *rif;
741 mrp_domctl_data_t *t;
742 mrp_domctl_watch_t *w;
748 pa_assert(ntable > 0);
750 pa_assert_se((murphyif = u->murphyif));
752 dif = &murphyif->domctl;
753 rif = &murphyif->resource;
755 pa_log_info("Received change notification for %d tables.", ntable);
757 for (i = 0; i < ntable; i++) {
762 pa_assert(t->id >= 0);
763 pa_assert(t->id < dif->nwatch);
765 w = dif->watches + t->id;
767 #ifdef WITH_RESOURCES
768 if (t->id == rif->inpres.tblidx || t->id == rif->outres.tblidx) {
769 resource_set_notification(u, w->table, t->nrow, t->rows);
774 dif->watchcb(u, w->table, t->nrow, t->rows);
778 static void domctl_dump_data(mrp_domctl_data_t *table)
780 mrp_domctl_value_t *row;
786 pa_log_debug("Table #%d: %d rows x %d columns", table->id,
787 table->nrow, table->ncolumn);
789 for (i = 0; i < table->nrow; i++) {
790 row = table->rows[i];
794 for (j = 0, t = ""; j < table->ncolumn; j++, t = ", ") {
795 switch (row[j].type) {
796 case MRP_DOMCTL_STRING:
797 l = snprintf(p, n, "%s'%s'", t, row[j].str);
801 case MRP_DOMCTL_INTEGER:
802 l = snprintf(p, n, "%s%d", t, row[j].s32);
806 case MRP_DOMCTL_UNSIGNED:
807 l = snprintf(p, n, "%s%u", t, row[j].u32);
811 case MRP_DOMCTL_DOUBLE:
812 l = snprintf(p, n, "%s%f", t, row[j].dbl);
817 l = snprintf(p, n, "%s<invalid column 0x%x>",
824 pa_log_debug("row #%d: { %s }", i, buf);
829 #ifdef WITH_RESOURCES
830 static void resource_attribute_destroy(resource_interface *rif,
831 resource_attribute *attr)
835 PA_LLIST_REMOVE(resource_attribute, rif->attrs, attr);
837 pa_xfree((void *)attr->prop);
838 pa_xfree((void *)attr->def.name);
840 if (attr->def.type == mqi_string)
841 pa_xfree((void *)attr->def.value.string);
847 static int resource_transport_connect(resource_interface *rif)
856 if (!mrp_transport_connect(rif->transp, &rif->saddr, rif->alen))
857 status = DISCONNECTED;
859 pa_log_info("resource transport connected to '%s'", rif->addr);
860 rif->connected = TRUE;
868 static void resource_xport_closed_evt(mrp_transport_t *transp, int error,
871 struct userdata *u = (struct userdata *)void_u;
872 pa_murphyif *murphyif;
873 resource_interface *rif;
878 pa_assert_se((murphyif = u->murphyif));
880 rif = &murphyif->resource;
883 pa_log("Resource transport connection closed by peer");
885 pa_log("Resource transport connection closed with error %d (%s)",
886 error, strerror(error));
889 resource_transport_destroy(murphyif);
890 resource_set_destroy_all(u);
891 schedule_connect(u, rif);
894 static mrp_msg_t *resource_create_request(uint32_t seqno,
895 mrp_resproto_request_t req)
900 msg = mrp_msg_create(RESPROTO_SEQUENCE_NO , MRP_MSG_FIELD_UINT32, seqno,
901 RESPROTO_REQUEST_TYPE, MRP_MSG_FIELD_UINT16, type ,
902 RESPROTO_MESSAGE_END );
905 pa_log("can't to create new resource message");
910 static pa_bool_t resource_send_message(resource_interface *rif,
916 resource_request *req;
917 pa_bool_t success = TRUE;
919 if (!mrp_transport_send(rif->transp, msg)) {
920 pa_log("failed to send resource message");
924 req = pa_xnew0(resource_request, 1);
925 req->nodidx = nodidx;
929 PA_LLIST_PREPEND(resource_request, rif->reqs, req);
937 static pa_bool_t resource_set_create_node(struct userdata *u,
939 pa_nodeset_resdef *resdef,
943 pa_murphyif *murphyif;
944 resource_interface *rif;
945 resource_request *req;
952 pa_source_output *sout;
953 audio_resource_t *res;
955 uint32_t audio_flags = 0;
957 pa_proplist *proplist = NULL;
958 pa_bool_t success = TRUE;
962 pa_assert(node->index != PA_IDXSET_INVALID);
963 pa_assert(node->implement == mir_stream);
964 pa_assert(node->direction == mir_input || node->direction == mir_output);
965 pa_assert(node->zone);
966 pa_assert(!node->rsetid);
968 pa_assert_se((core = u->core));
969 pa_assert_se((class = pa_nodeset_get_class(u, node->type)));
971 if (node->direction == mir_output) {
972 if ((sout = pa_idxset_get_by_index(core->source_outputs, node->paidx)))
973 proplist = sout->proplist;
976 if ((sinp = pa_idxset_get_by_index(core->sink_inputs, node->paidx)))
977 proplist = sinp->proplist;
980 pa_assert_se((murphyif = u->murphyif));
981 rif = &murphyif->resource;
983 reqid = RESPROTO_CREATE_RESOURCE_SET;
984 seqno = rif->seqno.request++;
985 res = (node->direction == mir_input) ? &rif->inpres : &rif->outres;
987 pa_assert_se((resnam = res->name));
989 rset_flags = RESPROTO_RSETFLAG_NOEVENTS;
990 rset_flags |= (acquire ? RESPROTO_RSETFLAG_AUTOACQUIRE : 0);
991 rset_flags |= (resdef ? resdef->flags.rset : 0);
993 audio_flags = (resdef ? resdef->flags.audio : 0);
995 priority = (resdef ? resdef->priority : 0);
997 msg = resource_create_request(seqno, reqid);
999 if (PUSH_VALUE(msg, RESOURCE_FLAGS , UINT32, rset_flags) &&
1000 PUSH_VALUE(msg, RESOURCE_PRIORITY, UINT32, priority) &&
1001 PUSH_VALUE(msg, CLASS_NAME , STRING, class) &&
1002 PUSH_VALUE(msg, ZONE_NAME , STRING, node->zone) &&
1003 PUSH_VALUE(msg, RESOURCE_NAME , STRING, resnam) &&
1004 PUSH_VALUE(msg, RESOURCE_FLAGS , UINT32, audio_flags) &&
1005 PUSH_VALUE(msg, ATTRIBUTE_NAME , STRING, "policy") &&
1006 PUSH_VALUE(msg, ATTRIBUTE_VALUE , STRING, "strict") &&
1007 PUSH_ATTRS(msg, rif, proplist) &&
1008 PUSH_VALUE(msg, SECTION_END , UINT8 , 0) )
1010 success = resource_send_message(rif, msg, node->index, reqid, seqno);
1018 pa_log_debug("requested resource set for '%s'", node->amname);
1020 pa_log_debug("failed to create resource set for '%s'", node->amname);
1025 static pa_bool_t resource_set_create_all(struct userdata *u)
1035 idx = PA_IDXSET_INVALID;
1037 while ((node = pa_nodeset_iterate_nodes(u, &idx))) {
1038 if (node->implement == mir_stream && !node->rsetid) {
1039 node->localrset = resource_set_create_node(u, node, NULL, FALSE);
1040 success &= node->localrset;
1047 static pa_bool_t resource_set_destroy_node(struct userdata *u, uint32_t rsetid)
1049 pa_murphyif *murphyif;
1050 resource_interface *rif;
1059 pa_assert_se((murphyif = u->murphyif));
1060 rif = &murphyif->resource;
1062 reqid = RESPROTO_DESTROY_RESOURCE_SET;
1063 seqno = rif->seqno.request++;
1064 nodidx = PA_IDXSET_INVALID;
1065 msg = resource_create_request(seqno, reqid);
1067 if (PUSH_VALUE(msg, RESOURCE_SET_ID, UINT32, rsetid))
1068 success = resource_send_message(rif, msg, nodidx, reqid, seqno);
1077 static pa_bool_t resource_set_destroy_all(struct userdata *u)
1079 pa_murphyif *murphyif;
1080 resource_interface *rif;
1088 pa_assert_se((murphyif = u->murphyif));
1090 rif = &murphyif->resource;
1094 idx = PA_IDXSET_INVALID;
1096 while ((node = pa_nodeset_iterate_nodes(u, &idx))) {
1097 if (node->implement == mir_stream && node->localrset) {
1098 pa_log_debug("destroying resource set for '%s'", node->amname);
1100 if (rif->connected && node->rsetid) {
1101 rsetid = strtoul(node->rsetid, &e, 10);
1103 if (e == node->rsetid || *e)
1106 success &= resource_set_destroy_node(u, rsetid);
1109 pa_xfree(node->rsetid);
1111 node->localrset = FALSE;
1112 node->rsetid = NULL;
1119 static void resource_set_notification(struct userdata *u,
1122 mrp_domctl_value_t **values)
1124 pa_murphyif *murphyif;
1125 resource_interface *rif;
1127 mrp_domctl_value_t *row;
1128 mrp_domctl_value_t *crsetid;
1129 mrp_domctl_value_t *cautorel;
1130 mrp_domctl_value_t *cstate;
1131 mrp_domctl_value_t *cgrant;
1132 mrp_domctl_value_t *cpid;
1133 mrp_domctl_value_t *cpolicy;
1147 pa_assert_se((murphyif = u->murphyif));
1148 rif = &murphyif->resource;
1150 for (r = 0; r < nrow; r++) {
1152 crsetid = row + RESCOL_RSETID;
1153 cautorel = row + RESCOL_AUTOREL;
1154 cstate = row + RESCOL_STATE;
1155 cgrant = row + RESCOL_GRANT;
1156 cpid = row + RESCOL_PID;
1157 cpolicy = row + RESCOL_POLICY;
1159 if (crsetid->type != MRP_DOMCTL_UNSIGNED ||
1160 cautorel->type != MRP_DOMCTL_INTEGER ||
1161 cstate->type != MRP_DOMCTL_INTEGER ||
1162 cgrant->type != MRP_DOMCTL_INTEGER ||
1163 cpid->type != MRP_DOMCTL_STRING ||
1164 cpolicy->type != MRP_DOMCTL_STRING )
1166 pa_log("invalid field type in '%s' (%d|%d|%d|%d|%d|%d)", table,
1167 crsetid->type, cautorel->type, cstate->type,
1168 cgrant->type, cpid->type, cpolicy->type);
1172 autorel = cautorel->s32;
1173 state = cstate->s32;
1174 grant = cgrant->s32;
1176 policy = cpolicy->str;
1178 snprintf(rsetid, sizeof(rsetid), "%d", crsetid->s32);
1180 if ((node = find_node_by_rsetid(u, rsetid)))
1181 pa_assert(node->implement == mir_stream);
1183 if (!pid || !(node = pid_hashmap_remove(u, pid))) {
1184 pa_log_debug("can't find node for resource set %s (pid %s)",
1189 pa_assert(node->implement == mir_stream);
1190 pa_assert(node->direction == mir_input ||
1191 node->direction == mir_output);
1193 pa_log_debug("setting rsetid %s for node %s", rsetid,node->amname);
1195 pa_xfree(node->rsetid);
1196 node->rsetid = pa_xstrdup(rsetid);
1198 if (!(pl = get_node_proplist(u, node))) {
1199 pa_log("can't obtain property list for node %s", node->amname);
1203 if ((pa_proplist_sets(pl, PA_PROP_RESOURCE_SET_ID, rsetid) < 0)) {
1204 pa_log("failed to set '" PA_PROP_RESOURCE_SET_ID "' property "
1205 "of '%s' node", node->amname);
1209 if (pa_hashmap_put(rif->nodes.rsetid, node->rsetid, node) < 0) {
1210 pa_log("conflicting rsetid %s for %s", rsetid, node->amname);
1215 if (autorel != 0 && autorel != 1) {
1216 pa_log_debug("invalid autorel %d in table '%s'", autorel, table);
1219 if (state != RSET_RELEASE && state != RSET_ACQUIRE) {
1220 pa_log_debug("invalid state %d in table '%s'", state, table);
1223 if (grant != 0 && grant != 1) {
1224 pa_log_debug("invalid grant %d in table '%s'", grant, table);
1228 pa_log_debug("resource notification for node '%s' autorel:%s state:%s "
1229 "grant:%s pid:%s policy:%s", node->amname,
1230 autorel ? "yes":"no",
1231 state == RSET_ACQUIRE ? "acquire":"release",
1232 grant ? "yes":"no", pid, policy);
1234 if (pa_streq(policy, "relaxed"))
1235 req = PA_STREAM_RUN;
1237 if (state == RSET_RELEASE)
1238 req = PA_STREAM_KILL;
1241 req = PA_STREAM_RUN;
1243 req = PA_STREAM_BLOCK;
1247 pa_stream_state_change(u, node, req);
1252 static pa_bool_t resource_push_attributes(mrp_msg_t *msg,
1253 resource_interface *rif,
1254 pa_proplist *proplist)
1256 resource_attribute *attr;
1270 PA_LLIST_FOREACH(attr, rif->attrs) {
1271 if (!PUSH_VALUE(msg, ATTRIBUTE_NAME, STRING, attr->def.name))
1275 sts = pa_proplist_get(proplist, attr->prop, &v.ptr, &size);
1279 switch (attr->def.type) {
1282 v.str = attr->def.value.string;
1283 else if (v.str[size-1] != '\0' || strlen(v.str) != (size-1) ||
1284 !pa_utf8_valid(v.str))
1286 if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, STRING, v.str))
1292 v.i32 = &attr->def.value.integer;
1293 else if (size != sizeof(*v.i32))
1295 if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, SINT8, *v.i32))
1301 v.u32 = &attr->def.value.unsignd;
1302 else if (size != sizeof(*v.u32))
1304 if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, SINT8, *v.u32))
1310 v.dbl = &attr->def.value.floating;
1311 else if (size != sizeof(*v.dbl))
1313 if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, SINT8, *v.dbl))
1317 default: /* we should never get here */
1327 static void resource_recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *void_u)
1329 return resource_recvfrom_msg(t, msg, NULL, 0, void_u);
1332 static void resource_recvfrom_msg(mrp_transport_t *transp, mrp_msg_t *msg,
1333 mrp_sockaddr_t *addr, socklen_t addrlen,
1336 struct userdata *u = (struct userdata *)void_u;
1338 pa_murphyif *murphyif;
1339 resource_interface *rif;
1344 resource_request *req, *n;
1349 MRP_UNUSED(addrlen);
1352 pa_assert_se((core = u->core));
1353 pa_assert_se((murphyif = u->murphyif));
1355 rif = &murphyif->resource;
1357 if (!resource_fetch_seqno (msg, &curs, &seqno) ||
1358 !resource_fetch_request (msg, &curs, &reqid) )
1360 pa_log("ignoring malformed message");
1364 PA_LLIST_FOREACH_SAFE(req, n, rif->reqs) {
1365 if (req->seqno <= seqno) {
1366 nodidx = req->nodidx;
1368 if (req->reqid == reqid) {
1369 PA_LLIST_REMOVE(resource_request, rif->reqs, req);
1373 if (!(node = mir_node_find_by_index(u, nodidx))) {
1374 if (reqid != RESPROTO_DESTROY_RESOURCE_SET) {
1375 pa_log("got response (reqid:%u seqno:%u) but can't "
1376 "find the corresponding node", reqid, seqno);
1377 resource_set_create_response_abort(u, msg, &curs);
1381 if (req->seqno < seqno) {
1382 pa_log("unanswered request %d", req->seqno);
1385 pa_log_debug("got response (reqid:%u seqno:%u "
1386 "node:'%s')", reqid, seqno,
1387 node ? node->amname : "<unknown>");
1390 case RESPROTO_CREATE_RESOURCE_SET:
1391 resource_set_create_response(u, node, msg, &curs);
1393 case RESPROTO_DESTROY_RESOURCE_SET:
1396 pa_log("ignoring unsupported resource request "
1402 } /* PA_LLIST_FOREACH_SAFE */
1407 static void resource_set_create_response(struct userdata *u, mir_node *node,
1408 mrp_msg_t *msg, void **pcursor)
1419 if (!resource_fetch_status(msg, pcursor, &status) || (status == 0 &&
1420 !resource_fetch_rset_id(msg, pcursor, &rsetid)))
1422 pa_log("ignoring malformed response to resource set creation");
1427 pa_log("creation of resource set failed. error code %u", status);
1431 node->rsetid = pa_sprintf_malloc("%d", rsetid);
1433 if (pa_murphyif_add_node(u, node) == 0) {
1434 pa_log_debug("resource set was successfully created");
1435 mir_node_print(node, buf, sizeof(buf));
1436 pa_log_debug("modified node:\n%s", buf);
1439 pa_log("failed to create resource set: "
1440 "conflicting resource set id");
1444 static void resource_set_create_response_abort(struct userdata *u,
1445 mrp_msg_t *msg, void **pcursor)
1454 if (!resource_fetch_status(msg, pcursor, &status) || (status == 0 &&
1455 !resource_fetch_rset_id(msg, pcursor, &rsetid)))
1457 pa_log("ignoring malformed response to resource set creation");
1462 pa_log("creation of resource set failed. error code %u", status);
1466 if (resource_set_destroy_node(u, rsetid))
1467 pa_log_debug("destroying resource set %u", rsetid);
1469 pa_log("attempt to destroy resource set %u failed", rsetid);
1473 static pa_bool_t resource_fetch_seqno(mrp_msg_t *msg,
1479 mrp_msg_value_t value;
1482 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1483 tag != RESPROTO_SEQUENCE_NO || type != MRP_MSG_FIELD_UINT32)
1485 *pseqno = INVALID_SEQNO;
1489 *pseqno = value.u32;
1494 static pa_bool_t resource_fetch_request(mrp_msg_t *msg,
1500 mrp_msg_value_t value;
1503 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1504 tag != RESPROTO_REQUEST_TYPE || type != MRP_MSG_FIELD_UINT16)
1506 *preqtype = INVALID_REQUEST;
1510 *preqtype = value.u16;
1514 static pa_bool_t resource_fetch_status(mrp_msg_t *msg,
1520 mrp_msg_value_t value;
1523 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1524 tag != RESPROTO_REQUEST_STATUS || type != MRP_MSG_FIELD_SINT16)
1530 *pstatus = value.s16;
1534 static pa_bool_t resource_fetch_rset_id(mrp_msg_t *msg,
1540 mrp_msg_value_t value;
1543 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1544 tag != RESPROTO_RESOURCE_SET_ID || type != MRP_MSG_FIELD_UINT32)
1554 static pa_bool_t resource_fetch_rset_state(mrp_msg_t *msg,
1556 mrp_resproto_state_t *pstate)
1560 mrp_msg_value_t value;
1563 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1564 tag != RESPROTO_RESOURCE_STATE || type != MRP_MSG_FIELD_UINT16)
1570 *pstate = value.u16;
1575 static pa_bool_t resource_fetch_rset_mask(mrp_msg_t *msg,
1577 mrp_resproto_state_t *pmask)
1581 mrp_msg_value_t value;
1584 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1585 tag != RESPROTO_RESOURCE_GRANT || type != MRP_MSG_FIELD_UINT32)
1595 static pa_bool_t resource_transport_create(struct userdata *u,
1596 pa_murphyif *murphyif)
1598 static mrp_transport_evt_t ev = {
1599 { .recvmsg = resource_recv_msg },
1600 { .recvmsgfrom = resource_recvfrom_msg },
1601 .closed = resource_xport_closed_evt,
1605 resource_interface *rif;
1608 pa_assert(murphyif);
1610 rif = &murphyif->resource;
1613 rif->transp = mrp_transport_create(murphyif->ml, rif->atype, &ev, u,0);
1615 return rif->transp ? TRUE : FALSE;
1618 static void resource_transport_destroy(pa_murphyif *murphyif)
1620 resource_interface *rif;
1622 pa_assert(murphyif);
1623 rif = &murphyif->resource;
1626 mrp_transport_destroy(rif->transp);
1629 rif->connected = FALSE;
1632 static void connect_attempt(pa_mainloop_api *a,
1634 const struct timeval *t,
1637 struct userdata *u = (struct userdata *)data;
1638 pa_murphyif *murphyif;
1639 resource_interface *rif;
1644 pa_assert_se((murphyif = u->murphyif));
1646 rif = &murphyif->resource;
1648 if (!resource_transport_create(u, murphyif))
1649 schedule_connect(u, rif);
1651 state = resource_transport_connect(rif);
1656 resource_set_create_all(u);
1657 cancel_schedule(u, rif);
1661 cancel_schedule(u, rif);
1665 schedule_connect(u, rif);
1671 static void schedule_connect(struct userdata *u, resource_interface *rif)
1674 pa_mainloop_api *mainloop;
1675 struct timeval when;
1680 pa_assert_se((core = u->core));
1681 pa_assert_se((mainloop = core->mainloop));
1683 pa_gettimeofday(&when);
1684 pa_timeval_add(&when, rif->connect.period);
1686 if ((tev = rif->connect.evt))
1687 mainloop->time_restart(tev, &when);
1689 rif->connect.evt = mainloop->time_new(mainloop, &when,
1690 connect_attempt, u);
1694 static void cancel_schedule(struct userdata *u, resource_interface *rif)
1697 pa_mainloop_api *mainloop;
1702 pa_assert_se((core = u->core));
1703 pa_assert_se((mainloop = core->mainloop));
1705 if ((tev = rif->connect.evt)) {
1706 mainloop->time_free(tev);
1707 rif->connect.evt = NULL;
1711 static void pid_hashmap_free(void *p, void *userdata)
1713 pid_hash *ph = (pid_hash *)p;
1718 pa_xfree((void *)ph->pid);
1723 static int pid_hashmap_put(struct userdata *u, const char *pid, mir_node *node)
1725 pa_murphyif *murphyif;
1726 resource_interface *rif;
1732 pa_assert_se((murphyif = u->murphyif));
1734 rif = &murphyif->resource;
1736 ph = pa_xnew0(pid_hash, 1);
1737 ph->pid = pa_xstrdup(pid);
1740 if (pa_hashmap_put(rif->nodes.pid, ph->pid, ph) == 0)
1743 pid_hashmap_free(ph, NULL);
1748 static mir_node *pid_hashmap_get(struct userdata *u, const char *pid)
1750 pa_murphyif *murphyif;
1751 resource_interface *rif;
1756 pa_assert(murphyif = u->murphyif);
1758 rif = &murphyif->resource;
1760 if ((ph = pa_hashmap_get(rif->nodes.pid, pid)))
1766 static mir_node *pid_hashmap_remove(struct userdata *u, const char *pid)
1768 pa_murphyif *murphyif;
1769 resource_interface *rif;
1774 pa_assert_se((murphyif = u->murphyif));
1776 rif = &murphyif->resource;
1778 if (!(ph = pa_hashmap_remove(rif->nodes.pid, pid)))
1782 pid_hashmap_free(ph, NULL);
1790 static pa_proplist *get_node_proplist(struct userdata *u, mir_node *node)
1794 pa_source_output *o;
1798 pa_assert_se((core = u->core));
1800 if (node->implement == mir_stream && node->paidx != PA_IDXSET_INVALID) {
1801 if (node->direction == mir_input) {
1802 if ((i = pa_idxset_get_by_index(core->sink_inputs, node->paidx)))
1805 else if (node->direction == mir_output) {
1806 if ((o = pa_idxset_get_by_index(core->source_outputs,node->paidx)))
1814 static const char *get_node_pid(struct userdata *u, mir_node *node)
1820 if (node && (pl = get_node_proplist(u, node)))
1821 return pa_proplist_gets(pl, PA_PROP_APPLICATION_PROCESS_ID);
1829 * indent-tabs-mode: nil