2 * Copyright (c) 2012, Intel Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Intel Corporation nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 #include <sys/types.h>
37 #include <sys/socket.h>
43 #include <audio-session-manager.h>
45 #include <murphy/common.h>
46 #include <murphy/core.h>
47 #include <murphy/core/plugin.h>
49 #include <murphy/resource/client-api.h>
51 #include "resource-asm/asm-bridge.h"
53 #define DEFAULT_TRANSPORT "unxs:/tmp/murphy/asm"
54 #define TYPE_MAP_SIZE 21
60 uint32_t current_handle;
62 bool monitor; /* if the client has set up a monitor resource */
72 /* asm-bridge management */
74 mrp_transport_t *mt; /* listening transport */
75 mrp_transport_t *t; /* connected transport */
78 /* resource management */
80 mrp_resource_client_t *resource_client;
84 char *audio_recording;
86 char *video_recording;
89 uint32_t current_request;
91 /* murphy integration */
92 mrp_sighandler_t *sighandler;
100 request_type_server_event
108 mrp_resource_set_t *rset;
112 ASM_sound_states_t requested_state;
113 ASM_sound_states_t granted_state;
116 request_type_t rtype;
120 } resource_set_data_t;
127 ARG_ASM_TPORT_ADDRESS,
128 ARG_ASM_AUDIO_PLAYBACK,
129 ARG_ASM_AUDIO_RECORDING,
130 ARG_ASM_VIDEO_PLAYBACK,
131 ARG_ASM_VIDEO_RECORDING,
132 ARG_ASM_EVENT_CONFIG,
137 * mapping entry of a single ASM event to a resource set
140 * Currently it is not possible to specify resource-specific
141 * mandatory or shared flags; they will be applied to all resources
142 * within the set. strict/relaxed policy attributes will be applied
143 * only to audio playback/recording resources.
145 * Although for the sake of completeness currently it is possible to
146 * mark a resource in the mapping as optional, probably this never
147 * makes any sense, as there is no notion or mechanism in the ASM API
148 * to request or communicate any optionality to the client.
152 char *asm_event; /* ASM event as a string */
153 char *rset_class; /* mapped application class */
154 unsigned rset_mask; /* involved resources (A/V,P/R)*/
155 bool mandatory; /* requires resources ? */
156 bool shared; /* agrees to share access */
157 uint32_t priority; /* priority within class */
158 bool strict; /* strict or relaxed policy ? */
162 #define EVENT_PREFIX "ASM_EVENT_"
164 static const char *asm_event_name[] = {
165 "ASM_EVENT_SHARE_MMPLAYER",
166 "ASM_EVENT_SHARE_MMCAMCORDER",
167 "ASM_EVENT_SHARE_MMSOUND",
168 "ASM_EVENT_SHARE_OPENAL",
169 "ASM_EVENT_SHARE_AVSYSTEM",
170 "ASM_EVENT_EXCLUSIVE_MMPLAYER",
171 "ASM_EVENT_EXCLUSIVE_MMCAMCORDER",
172 "ASM_EVENT_EXCLUSIVE_MMSOUND",
173 "ASM_EVENT_EXCLUSIVE_OPENAL",
174 "ASM_EVENT_EXCLUSIVE_AVSYSTEM",
177 "ASM_EVENT_SHARE_FMRADIO",
178 "ASM_EVENT_EXCLUSIVE_FMRADIO",
179 "ASM_EVENT_EARJACK_UNPLUG",
181 "ASM_EVENT_VIDEOCALL",
183 "ASM_EVENT_RICH_CALL",
184 "ASM_EVENT_EMERGENCY",
185 "ASM_EVENT_EXCLUSIVE_RESOURCE",
190 #define MAP_EVENT(_event, _class, _resmask, _shared, _strict) \
191 [ASM_EVENT_##_event] = { \
192 asm_event: #_event, \
193 rset_class: _class, \
194 rset_mask: _resmask, \
200 #define MANDATORY TRUE
204 #define NONE 0x0 /* no resources */
205 #define AP 0x1 /* audio playback */
206 #define AR 0x2 /* audio recording */
207 #define VP 0x4 /* video playback */
208 #define VR 0x8 /* video recording */
209 #define APR (AP|AR) /* audio playback/recording */
210 #define VPR (VP|VR) /* video playback/recording */
211 #define AVP (AP|VP) /* audio/video playback */
212 #define AVPR (AP|AR|VP|VR) /* audio/video playback/record */
214 static rset_class_data_t type_map[] = {
215 MAP_EVENT(SHARE_MMPLAYER , "player" , AVP , SHARED, !STRICT),
216 MAP_EVENT(SHARE_MMCAMCORDER , "camera" , AVPR, SHARED, !STRICT),
217 MAP_EVENT(SHARE_MMSOUND , "event" , AP , SHARED, !STRICT),
218 MAP_EVENT(SHARE_OPENAL , "game" , AVP , SHARED, !STRICT),
219 MAP_EVENT(SHARE_AVSYSTEM , "event" , AP , SHARED, !STRICT),
220 MAP_EVENT(EXCLUSIVE_MMPLAYER , "player" , AVP , !SHARED, !STRICT),
221 MAP_EVENT(EXCLUSIVE_MMCAMCORDER, "camera" , AVPR, !SHARED, !STRICT),
222 MAP_EVENT(EXCLUSIVE_MMSOUND , "event" , AP , !SHARED, !STRICT),
223 MAP_EVENT(EXCLUSIVE_OPENAL , "game" , AVP , !SHARED, !STRICT),
224 MAP_EVENT(EXCLUSIVE_AVSYSTEM , "event" , AP , !SHARED, !STRICT),
225 MAP_EVENT(NOTIFY , "event" , AVP , !SHARED, !STRICT),
226 MAP_EVENT(CALL , "phone" , APR , !SHARED, !STRICT),
227 MAP_EVENT(SHARE_FMRADIO , "radio" , AP , SHARED, !STRICT),
228 MAP_EVENT(EXCLUSIVE_FMRADIO , "radio" , AP , !SHARED, !STRICT),
229 MAP_EVENT(EARJACK_UNPLUG , "earjack", NONE, SHARED, !STRICT),
230 MAP_EVENT(ALARM , "alert" , AP , !SHARED, !STRICT),
231 MAP_EVENT(VIDEOCALL , "phone" , AVPR, !SHARED, !STRICT),
232 MAP_EVENT(MONITOR , "monitor", NONE, SHARED, !STRICT),
233 MAP_EVENT(RICH_CALL , "phone" , AVPR, !SHARED, !STRICT),
234 MAP_EVENT(EMERGENCY , "phone" , AVPR, !SHARED, !STRICT),
235 MAP_EVENT(EXCLUSIVE_RESOURCE , "player" , APR, !SHARED, !STRICT),
237 { NULL, NULL, NONE, FALSE, FALSE, FALSE, 0 }
241 static int max_event_name;
244 static int asm_event(const char *name, int len)
252 for (i = 0; asm_event_name[i] != NULL; i++) {
253 event = asm_event_name[i];
255 if (!strncasecmp(name, event, len) && event[len] == '\0')
258 event = asm_event_name[i] + sizeof(EVENT_PREFIX) - 1;
260 if (!strncasecmp(name, event, len) && event[len] == '\0')
268 static int init_event_config(void)
270 rset_class_data_t *data;
273 for (i = 0, data = type_map; data->asm_event != NULL; i++, data++) {
274 data->asm_event = mrp_strdup(data->asm_event);
275 data->rset_class = mrp_strdup(data->rset_class);
277 if (data->asm_event == NULL || data->rset_class == NULL)
285 static int parse_config(rset_class_data_t *data, char *config)
287 char *s, *colon, *f, *comma, *end;
292 * Parse a configuration entry of the form: 'class:flag1,...,flagn, where
293 * the possible flags are:
295 * mandatory/optional: whether resources are mandatory
296 * shared/exclusive: whether need exclusive resource ownership
297 * strict/reaxed: apply strict or reaxed policies
298 * priority: an integer priority between 0 and 255
300 * Eg. player:mandatory,exclusive,5,relaxed configures the ASM event to
301 * use the player application class, take mandatory (audio) resources
302 * in exclusive mode with priority 5, and ask relaxed policy.
307 mrp_debug("parsing config entry %s = '%s'", data->asm_event, config);
309 colon = strchr(s, ':');
312 mrp_log_error("Missing app. class name for ASM event '%s'.",
317 len = colon - (config) + 1;
318 mrp_free(data->rset_class);
319 data->rset_class = mrp_datadup(config, len);
320 data->rset_class[len - 1] = '\0';
322 mrp_debug("class name: '%s'", data->rset_class);
325 comma = strchr(f, ',');
329 # define MATCHES(_f, _n, _l) ({ \
330 mrp_debug("comparing flag '%*.*s' to '%s'...", \
332 (!strncmp(_f, _n, _l) && \
333 (_f[_l] == '\0' || _f[_l] == ','));})
335 len = (comma ? comma - f : (int)strlen(f));
337 if (MATCHES(f, "mandatory", len)) data->mandatory = TRUE;
338 else if (MATCHES(f, "optional" , len)) data->mandatory = FALSE;
339 else if (MATCHES(f, "shared" , len)) data->shared = TRUE;
340 else if (MATCHES(f, "exclusive", len)) data->shared = FALSE;
341 else if (MATCHES(f, "strict", len)) data->strict = TRUE;
342 else if (MATCHES(f, "relaxed", len)) data->strict = FALSE;
343 else if (MATCHES(f, "none" , len)) mask = NONE;
344 else if (MATCHES(f, "AP" , len)) mask |= AP;
345 else if (MATCHES(f, "AR" , len)) mask |= AR;
346 else if (MATCHES(f, "VP" , len)) mask |= VP;
347 else if (MATCHES(f, "VR" , len)) mask |= VR;
348 else if (MATCHES(f, "APR" , len)) mask |= APR;
349 else if (MATCHES(f, "VPR" , len)) mask |= VPR;
350 else if (MATCHES(f, "AVP" , len)) mask |= AVP;
351 else if (MATCHES(f, "AVPR" , len)) mask |= AVPR;
352 else if (MATCHES(f, "audio_playback" , len)) mask |= AP;
353 else if (MATCHES(f, "audio_recording", len)) mask |= AR;
354 else if (MATCHES(f, "video_playback" , len)) mask |= VP;
355 else if (MATCHES(f, "video_recording", len)) mask |= VR;
357 data->priority = strtoul(f, &end, 10);
358 if (end && *end && *end != ',' && *end != ';') {
359 mrp_log_error("Invalid flag or priority (%s) for event %s.",
363 if (data->priority > 256) {
364 mrp_log_error("Out of range priority (%d) for event %s.",
365 data->priority, data->asm_event);
369 /* if at the end stop, otherwise go to the next flag */
370 f = (comma ? comma + 1 : NULL);
371 comma = (f ? strchr(f, ',') : NULL);
376 if (mask != 0x80000000)
377 data->rset_mask = (mask & 0x0fffffff);
383 static int parse_event_config(mrp_plugin_arg_t *events)
385 rset_class_data_t *data;
386 mrp_plugin_arg_t *cfg;
389 if (events == NULL || events->rest.args == NULL)
392 mrp_plugin_foreach_undecl_arg(events, cfg) {
393 if (cfg->type != MRP_PLUGIN_ARG_TYPE_STRING) {
394 mrp_log_warning("Ignoring non-string configuration for '%s'.",
399 evt = asm_event(cfg->key, 0);
402 mrp_log_error("Ignoring configuration for unknown ASM event '%s'.",
407 data = type_map + evt;
409 if (!parse_config(data, cfg->str)) {
410 mrp_log_error("Failed to parse configuration '%s' for ASM event "
411 "'%s'.", cfg->str, cfg->key);
420 static void dump_event_config(void)
422 rset_class_data_t *data;
424 char resmask[16], *rmp;
426 if (max_event_name <= 0) {
427 for (i = 0, data = type_map; i < ASM_EVENT_MAX; i++, data++)
428 if (data->asm_event != NULL &&
429 (l = strlen(data->asm_event)) > max_event_name)
433 mrp_debug("event mapping:");
434 for (i = 0, data = type_map; i < ASM_EVENT_MAX; i++, data++) {
437 if (!(data->rset_mask & APR)) *rmp++ = '-';
439 if (data->rset_mask & AP) *rmp++ = 'P';
440 if (data->rset_mask & AR) *rmp++ = 'R';
444 if (!(data->rset_mask & VPR)) *rmp++ = '-';
446 if (data->rset_mask & VP) *rmp++ = 'P';
447 if (data->rset_mask & VR) *rmp++ = 'R';
450 mrp_debug("%*.*s: %s (%s, prio %d, %s/%s, %s)",
451 max_event_name, max_event_name, data->asm_event,
452 data->rset_class, resmask, data->priority,
453 data->mandatory ? "m" : "o", data->shared ? "s" : "e",
454 data->strict ? "strict" : "relaxed");
459 static void *u_to_p(uint32_t u)
461 #ifdef __SIZEOF_POINTER__
462 #if __SIZEOF_POINTER__ == 8
474 static uint32_t p_to_u(const void *p)
476 #ifdef __SIZEOF_POINTER__
477 #if __SIZEOF_POINTER__ == 8
479 uint64_t big = (uint64_t) p;
480 o = big & 0xffffffff;
482 uint32_t o = (uint32_t) p;
491 static int int_comp(const void *key1, const void *key2)
497 static uint32_t int_hash(const void *key)
503 static const rset_class_data_t *map_slp_media_type_to_murphy(
504 ASM_sound_events_t media_type)
506 /* check if the event is within the bounds */
507 if (media_type <= ASM_EVENT_NONE || media_type >= ASM_EVENT_MAX)
510 /* check that we don't overflow */
511 if (media_type > TYPE_MAP_SIZE)
514 return &type_map[media_type];
518 static void dump_incoming_msg(lib_to_asm_t *msg, asm_data_t *ctx)
522 mrp_log_info(" --> client id: %u", msg->instance_id);
523 mrp_log_info(" --> data handle: %d", msg->handle);
524 mrp_log_info(" --> request id: 0x%04x", msg->request_id);
525 mrp_log_info(" --> sound event: 0x%04x", msg->sound_event);
526 mrp_log_info(" --> system resource: 0x%04x", msg->system_resource);
527 mrp_log_info(" --> state: 0x%04x", msg->sound_state);
530 int n_cookie = msg->n_cookie_bytes;
533 mrp_log_info(" --> cookie: ")
534 for (i = 0; i < n_cookie, i++) {
535 mrp_log_info("0x%02x ", msg->cookie[i]);
544 static void dump_outgoing_msg(asm_to_lib_t *msg, asm_data_t *ctx)
548 mrp_log_info(" <-- client id: %u", msg->instance_id);
549 mrp_log_info(" <-- alloc handle: %d", msg->alloc_handle);
550 mrp_log_info(" <-- command handle: %d", msg->cmd_handle);
551 mrp_log_info(" <-- sound command: 0x%04x", msg->result_sound_command);
552 mrp_log_info(" <-- state: 0x%04x", msg->result_sound_state);
553 mrp_log_info(" <-- former event: %d", msg->former_sound_event);
554 mrp_log_info(" <-- check privilege: %s",
555 msg->check_privilege ? "TRUE" : "FALSE");
559 static void dump_outgoing_cb_msg(asm_to_lib_cb_t *msg, asm_data_t *ctx)
563 mrp_log_info(" <-- client id: %u", msg->instance_id);
564 mrp_log_info(" <-- handle: %d", msg->handle);
565 mrp_log_info(" <-- expect callback: %d", msg->callback_expected);
566 mrp_log_info(" <-- sound command: 0x%04x", msg->sound_command);
567 mrp_log_info(" <-- event source: 0x%04x", msg->event_source);
572 static uint32_t encode_pid_handle(uint32_t pid, uint32_t handle) {
574 uint32_t max_pid = 4194304; /* 2^22 */
575 uint32_t max_handle = 1024; /* 2^10 */
577 if (pid > max_pid || handle > max_handle) {
581 return pid | (handle << 22);
584 static uint32_t get_pid(uint32_t data) {
585 uint32_t pid_mask = 0xffffffff >> 10;
587 return data & pid_mask;
591 static uint32_t get_handle(uint32_t data) {
598 static void htbl_free_set(void *key, void *object)
600 resource_set_data_t *d = (resource_set_data_t *) object;
605 mrp_resource_set_destroy(d->rset);
611 static client_t *create_client(uint32_t pid) {
612 client_t *client = (client_t *) mrp_allocz(sizeof(client_t));
613 mrp_htbl_config_t set_conf;
618 set_conf.comp = int_comp;
619 set_conf.hash = int_hash;
620 set_conf.free = htbl_free_set;
621 set_conf.nbucket = 0;
622 set_conf.nentry = 10;
624 client->sets = mrp_htbl_create(&set_conf);
632 client->current_handle = 1;
639 static void event_cb(uint32_t request_id, mrp_resource_set_t *set, void *data)
641 resource_set_data_t *d = (resource_set_data_t *) data;
642 asm_data_t *ctx = d->ctx;
644 mrp_log_info("Event CB: id %u, set %p", request_id, set);
645 mrp_log_info("Resource set %u.%u", d->pid, d->handle);
646 mrp_log_info("Advice 0x%08x, Grant 0x%08x",
647 mrp_get_resource_set_advice(d->rset),
648 mrp_get_resource_set_grant(d->rset));
651 case request_type_acquire:
654 mrp_log_info("callback for acquire request %u", request_id);
656 /* expecting next server events */
657 d->rtype = request_type_server_event;
659 reply.instance_id = d->pid;
660 reply.check_privilege = TRUE;
661 reply.alloc_handle = d->handle;
662 reply.cmd_handle = d->handle;
664 reply.result_sound_state = ASM_STATE_IGNORE;
665 reply.former_sound_event = ASM_EVENT_NONE;
667 /* ASM doesn't support optional resources. Thus, if we are granted
668 * anything, we are granted the whole set we requested. */
669 if (mrp_get_resource_set_grant(d->rset)) {
670 reply.result_sound_command = ASM_COMMAND_PLAY;
671 d->granted_state = d->requested_state;
674 reply.result_sound_command = ASM_COMMAND_STOP;
677 d->rtype = request_type_server_event;
679 /* only send reply when "PLAYING" state was requested ->
680 * this happens when acquire request is done */
681 dump_outgoing_msg(&reply, ctx);
682 mrp_transport_senddata(ctx->t, &reply, TAG_ASM_TO_LIB);
685 case request_type_release:
687 mrp_log_info("callback for release request %u", request_id);
689 /* expecting next server events */
690 d->rtype = request_type_server_event;
692 /* set up event filtering */
697 case request_type_server_event:
699 asm_to_lib_cb_t reply;
700 mrp_log_info("callback for no request %u", request_id);
701 uint32_t grant = mrp_get_resource_set_grant(d->rset);
703 reply.instance_id = d->pid;
704 reply.handle = d->handle;
706 /* TODO: get the client and see if there is the monitor
707 * resource present. If yes, tell the availability state changes
710 if (d->request_id == 0) {
711 /* We either haven't requested any resources or have
712 * given up the resources. Filter out events. */
716 /* check if the d->rset state has actually changed -> only
717 * process server side notifications in that case */
718 if ((grant && d->granted_state == ASM_STATE_PLAYING) ||
719 (!grant && d->granted_state != ASM_STATE_PLAYING)) {
720 mrp_log_info("state didn't change -> ignoring");
725 reply.sound_command = ASM_COMMAND_RESUME;
726 /* ASM doesn't send callback to RESUME commands */
727 reply.callback_expected = FALSE;
730 reply.sound_command = ASM_COMMAND_PAUSE;
731 reply.callback_expected = TRUE;
734 /* FIXME: the player-player case needs to be solved here? */
735 reply.event_source = ASM_EVENT_SOURCE_OTHER_PLAYER_APP;
737 dump_outgoing_cb_msg(&reply, ctx);
738 mrp_transport_senddata(ctx->t, &reply, TAG_ASM_TO_LIB_CB);
746 static asm_to_lib_t *process_msg(lib_to_asm_t *msg, asm_data_t *ctx)
748 pid_t pid = msg->instance_id;
754 reply = (asm_to_lib_t *) mrp_allocz(sizeof(asm_to_lib_t));
759 reply->instance_id = pid;
760 reply->check_privilege = TRUE;
762 reply->alloc_handle = msg->handle;
763 reply->cmd_handle = msg->handle;
765 reply->result_sound_command = ASM_COMMAND_NONE;
766 reply->result_sound_state = ASM_STATE_IGNORE;
767 reply->former_sound_event = ASM_EVENT_NONE;
769 switch(msg->request_id) {
770 case ASM_REQUEST_REGISTER:
773 resource_set_data_t *d;
775 const rset_class_data_t *rset_data;
777 mrp_log_info("REQUEST: REGISTER");
779 /* see if the process already has a client object */
780 client = (client_t *) mrp_htbl_lookup(ctx->clients, u_to_p(pid));
783 client = create_client(pid);
787 mrp_htbl_insert(ctx->clients, u_to_p(pid), client);
791 /* From Murphy point of view this is actually an error case,
792 * since the application can only belong to one class. This is
793 * a Murphy limitation and should be fixed later. */
795 mrp_log_error("Application tried to register twice");
800 rset_data = map_slp_media_type_to_murphy(
801 (ASM_sound_events_t) msg->sound_event);
804 mrp_log_error("unknown resource type: %d", msg->sound_event);
808 handle = client->current_handle++;
809 d = (resource_set_data_t *) mrp_allocz(sizeof(resource_set_data_t));
817 d->rtype = request_type_server_event;
819 d->requested_state = ASM_STATE_WAITING;
820 d->granted_state = ASM_STATE_WAITING;
822 if (strcmp(rset_data->rset_class, "earjack") == 0) {
823 mrp_log_info("earjack status request was received");
826 else if (strcmp(rset_data->rset_class, "monitor") == 0) {
827 mrp_log_info("monitor resource was received");
828 /* TODO: tell the available state changes to this pid
829 * via the monitor resource. */
830 client->monitor = TRUE;
834 /* a normal resource request */
836 /* we have to do a separate resource set for each request
837 * (even originating from the same client), since they are
838 * of the same resource type (audio_playback). */
839 d->rset = mrp_resource_set_create(ctx->resource_client, 0,
840 rset_data->priority, event_cb, d);
843 mrp_log_error("Failed to create resource set!");
848 snprintf(pidbuf, sizeof(pidbuf), "%u", d->pid);
849 attrs[0].type = mqi_string;
850 attrs[0].name = "pid";
851 attrs[0].value.string = pidbuf;
852 attrs[1].type = mqi_string;
853 attrs[1].name = "policy";
854 attrs[1].value.string = rset_data->strict ? "strict":"relaxed";
855 attrs[2].name = NULL;
857 if (rset_data->rset_mask & AP) {
858 if (mrp_resource_set_add_resource(d->rset,
862 rset_data->mandatory) < 0) {
863 mrp_log_error("Failed to add audio playback resource!");
864 mrp_resource_set_destroy(d->rset);
870 if (rset_data->rset_mask & AR) {
871 if (mrp_resource_set_add_resource(d->rset,
872 ctx->audio_recording, rset_data->shared,
874 rset_data->mandatory) < 0) {
875 mrp_log_error("Failed to add audio record resource!");
876 mrp_resource_set_destroy(d->rset);
882 if (rset_data->rset_mask & VP) {
883 if (mrp_resource_set_add_resource(d->rset,
887 rset_data->mandatory) < 0) {
888 mrp_log_error("Failed to add video playback resource!");
889 mrp_resource_set_destroy(d->rset);
895 if (rset_data->rset_mask & VR) {
896 if (mrp_resource_set_add_resource(d->rset,
897 ctx->video_recording, rset_data->shared,
899 rset_data->mandatory) < 0) {
900 mrp_log_error("Failed to add video record resource!");
901 mrp_resource_set_destroy(d->rset);
907 if (mrp_application_class_add_resource_set(
908 rset_data->rset_class, ctx->zone, d->rset, 0) < 0) {
909 mrp_log_error("Failed to put the rset in a class!");
910 mrp_resource_set_destroy(d->rset);
916 mrp_htbl_insert(client->sets, u_to_p(handle), d);
919 reply->alloc_handle = handle;
920 reply->cmd_handle = reply->alloc_handle;
922 reply->result_sound_state = ASM_STATE_WAITING;
923 reply->former_sound_event = ASM_EVENT_NONE;
926 case ASM_REQUEST_UNREGISTER:
928 client_t *client = (client_t *)
929 mrp_htbl_lookup(ctx->clients, u_to_p(pid));
931 mrp_log_info("REQUEST: UNREGISTER");
934 resource_set_data_t *d;
936 d = (resource_set_data_t *)
937 mrp_htbl_lookup(client->sets, u_to_p(msg->handle));
939 mrp_log_error("set '%u.%u' not found", pid,
945 /* this is a resource request with no associated
946 * murphy resource, meaning a monitor or earjack. */
948 mrp_log_info("unregistering special resource %s",
949 d->monitor ? "monitor" : "earjack");
952 client->monitor = FALSE;
954 /* TODO: what to do with the earjack unregister case? */
957 /* the resource set id destroyed when it's removed from the
959 mrp_htbl_remove(client->sets, u_to_p(msg->handle), TRUE);
962 if (client->n_sets <= 0) {
963 mrp_htbl_remove(ctx->clients, u_to_p(pid), TRUE);
968 /* no reply needed! */
973 case ASM_REQUEST_SETSTATE:
976 (client_t *) mrp_htbl_lookup(ctx->clients, u_to_p(pid));
978 resource_set_data_t *d;
980 mrp_log_info("REQUEST: SET STATE");
983 mrp_log_error("client '%u' not found", pid);
987 d = (resource_set_data_t *)
988 mrp_htbl_lookup(client->sets, u_to_p(msg->handle));
989 if (!d || !d->rset) {
990 mrp_log_error("set '%u.%u' not found", pid, msg->handle);
994 d->request_id = ++ctx->current_request;
995 d->requested_state = (ASM_sound_states_t) msg->sound_state;
997 switch(msg->sound_state) {
998 case ASM_STATE_PLAYING:
1000 /* requests done for "PLAYING" state need a reply, others don't */
1001 d->rtype = request_type_acquire;
1003 mrp_log_info("requesting acquisition of playback rights"
1004 " for set '%u.%u' (id: %u)", pid, msg->handle,
1007 mrp_resource_set_acquire(d->rset, d->request_id);
1011 case ASM_STATE_STOP:
1012 case ASM_STATE_PAUSE:
1014 d->rtype = request_type_release;
1016 mrp_log_info("requesting release of playback rights for"
1017 " set '%u.%u' (id: %u)", pid, msg->handle,
1020 mrp_resource_set_release(d->rset, d->request_id);
1026 mrp_log_error("Unknown state: %d", msg->sound_state);
1032 case ASM_REQUEST_GETSTATE:
1034 const rset_class_data_t *rset_data;
1036 rset_data = map_slp_media_type_to_murphy(
1037 (ASM_sound_events_t) msg->sound_event);
1039 mrp_log_info("REQUEST: GET STATE for %s",
1040 rset_data ? rset_data->rset_class : "NULL");
1042 /* TODO: get the status for rset_data->rset_class . */
1043 reply->result_sound_state = ASM_STATE_IGNORE;
1044 reply->former_sound_event = ASM_EVENT_NONE;
1048 case ASM_REQUEST_GETMYSTATE:
1050 client_t *client = (client_t *)
1051 mrp_htbl_lookup(ctx->clients, u_to_p(pid));
1052 resource_set_data_t *d;
1054 mrp_log_info("REQUEST: GET MY STATE");
1057 mrp_log_error("client '%u' not found", pid);
1061 d = (resource_set_data_t *)
1062 mrp_htbl_lookup(client->sets, u_to_p(msg->handle));
1063 if (!d || !d->rset) {
1064 mrp_log_error("set '%u.%u' not found", pid, msg->handle);
1068 reply->result_sound_state = d->granted_state;
1069 reply->former_sound_event = ASM_EVENT_NONE;
1072 case ASM_REQUEST_EMERGENT_EXIT:
1074 client_t *client = (client_t *)
1075 mrp_htbl_lookup(ctx->clients, u_to_p(pid));
1077 mrp_log_info("REQUEST: EMERGENCY EXIT");
1080 mrp_log_error("client '%u' not found", pid);
1084 mrp_htbl_remove(ctx->clients, u_to_p(pid), TRUE);
1088 case ASM_REQUEST_SET_SUBSESSION:
1089 mrp_log_info("REQUEST: SET SUBSESSION");
1091 case ASM_REQUEST_GET_SUBSESSION:
1092 mrp_log_info("REQUEST: GET SUBSESSION");
1095 mrp_log_info("REQUEST: UNKNOWN REQUEST");
1102 /* write some message back to avoid client locking */
1112 static void process_cb_msg(lib_to_asm_cb_t *msg, asm_data_t *ctx)
1118 /* TODO: this function might tell something to the resource library */
1120 switch (msg->cb_result) {
1121 case ASM_CB_RES_IGNORE:
1124 case ASM_CB_RES_NONE:
1127 case ASM_CB_RES_PAUSE:
1130 case ASM_CB_RES_PLAYING:
1133 case ASM_CB_RES_STOP:
1137 mrp_log_error("unknown callback state %d", msg->cb_result);
1141 mrp_log_info("client %d.%u ended in state '%s' after callback",
1142 msg->instance_id, msg->handle, str);
1146 static void recvdatafrom_evt(mrp_transport_t *t, void *data, uint16_t tag,
1147 mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
1149 asm_data_t *ctx = (asm_data_t *) user_data;
1152 MRP_UNUSED(addrlen);
1155 case TAG_LIB_TO_ASM:
1157 lib_to_asm_t *msg = (lib_to_asm_t *) data;
1158 asm_to_lib_t *reply;
1160 /* client requests something from us */
1162 dump_incoming_msg(msg, ctx);
1164 reply = process_msg(msg, ctx);
1166 dump_outgoing_msg(reply, ctx);
1167 mrp_transport_senddata(t, reply, TAG_ASM_TO_LIB);
1173 case TAG_LIB_TO_ASM_CB:
1175 lib_to_asm_cb_t *msg = (lib_to_asm_cb_t *) data;
1177 /* client tells us which state it entered after preemption */
1179 process_cb_msg(msg, ctx);
1185 mrp_log_error("Unknown message received!");
1189 mrp_data_free(data, tag);
1193 static void recvdata_evt(mrp_transport_t *t, void *data, uint16_t tag, void *user_data)
1195 recvdatafrom_evt(t, data, tag, NULL, 0, user_data);
1200 static void closed_evt(mrp_transport_t *t, int error, void *user_data)
1203 asm_data_t *ctx = (asm_data_t *) user_data;
1207 mrp_log_info("closed!");
1209 mrp_transport_disconnect(t);
1210 mrp_transport_destroy(t);
1214 /* TODO: start the listening socket again and relaunch the binary? */
1219 static void connection_evt(mrp_transport_t *lt, void *user_data)
1221 asm_data_t *ctx = (asm_data_t *) user_data;
1223 mrp_log_info("connection!");
1226 mrp_log_error("Already connected");
1229 ctx->t = mrp_transport_accept(lt, ctx, 0);
1232 /* close the listening socket, since we only have one client */
1234 mrp_transport_destroy(lt);
1239 static int tport_setup(const char *address, asm_data_t *ctx)
1243 mrp_sockaddr_t addr;
1245 struct stat statbuf;
1247 static mrp_transport_evt_t evt = {
1248 { .recvdata = recvdata_evt },
1249 { .recvdatafrom = recvdatafrom_evt },
1250 .closed = closed_evt,
1251 .connection = connection_evt
1254 alen = mrp_transport_resolve(NULL, address, &addr, sizeof(addr), &atype);
1257 mrp_log_error("Error resolving transport address");
1261 /* remove the old socket if present */
1263 if (strcmp(atype, "unxs") == 0) {
1264 char *path = addr.unx.sun_path;
1265 if (path[0] == '/') {
1266 /* if local socket and file exists, remove it */
1267 if (stat(path, &statbuf) == 0) {
1268 if (S_ISSOCK(statbuf.st_mode)) {
1269 if (unlink(path) < 0) {
1270 mrp_log_error("error removing the socket");
1275 mrp_log_error("a file where the socket should be created");
1282 ctx->mt = mrp_transport_create(ctx->ctx->ml, atype, &evt, ctx,
1283 MRP_TRANSPORT_MODE_DATA | MRP_TRANSPORT_NONBLOCK);
1285 if (ctx->mt == NULL) {
1286 mrp_log_error("Failed to create the transport");
1290 if (!mrp_transport_bind(ctx->mt, &addr, alen)) {
1291 mrp_log_error("Failed to bind the transport to address '%s'", address);
1296 if (!mrp_transport_listen(ctx->mt, 5)) {
1297 mrp_log_error("Failed to listen to transport");
1305 mrp_transport_destroy(ctx->mt);
1312 static void signal_handler(mrp_sighandler_t *h, int signum, void *user_data)
1314 asm_data_t *ctx = (asm_data_t *) user_data;
1318 if (signum == SIGCHLD) {
1319 /* wait for the child */
1323 mrp_log_info("Received SIGCHLD, waiting for asm-bridge");
1325 ret = waitpid(ctx->pid, NULL, WNOHANG);
1327 if (ret == ctx->pid) {
1328 mrp_log_warning("asm-bridge process died");
1335 static void htbl_free_client(void *key, void *object)
1339 client_t *client = (client_t *) object;
1341 mrp_htbl_destroy(client->sets, TRUE);
1343 /* TODO: free memory when resource API allows that */
1347 static int close_fds()
1351 int newin, newout, newerr;
1354 /* Closing all file descriptors in a protable way is tricky, so improve
1355 this function as we go. */
1357 maxfd = sysconf(_SC_OPEN_MAX);
1359 for (i = 0; i < maxfd; i++) {
1360 if (i != fileno(stdin) && i != fileno(stdout) && i != fileno(stderr))
1364 /* redirect the streams to /dev/null */
1366 newin = open("/dev/null", O_RDONLY);
1367 newout = open("/dev/null", O_WRONLY);
1368 newerr = open("/dev/null", O_WRONLY);
1370 if (newin < 0 || newout < 0 || newerr < 0)
1373 if (dup2(newin, fileno(stdin)) < 0 ||
1374 dup2(newout, fileno(stdout)) < 0 ||
1375 dup2(newerr, fileno(stderr)) < 0)
1388 static int asm_init(mrp_plugin_t *plugin)
1390 mrp_plugin_arg_t *args = plugin->args;
1391 asm_data_t *ctx = (asm_data_t *) mrp_allocz(sizeof(asm_data_t));
1393 mrp_htbl_config_t client_conf;
1399 ctx->ctx = plugin->ctx;
1400 ctx->address = args[ARG_ASM_TPORT_ADDRESS].str;
1401 ctx->binary = args[ARG_ASM_BRIDGE].str;
1402 ctx->zone = args[ARG_ASM_ZONE].str;
1403 ctx->log = args[ARG_ASM_BRIDGE_LOG].str;
1405 ctx->audio_playback = args[ARG_ASM_AUDIO_PLAYBACK].str;
1406 ctx->audio_recording = args[ARG_ASM_AUDIO_RECORDING].str;
1407 ctx->video_playback = args[ARG_ASM_VIDEO_PLAYBACK].str;
1408 ctx->video_recording = args[ARG_ASM_VIDEO_RECORDING].str;
1410 if (!init_event_config()) {
1411 mrp_log_error("Failed to initialize event/class mapping.");
1415 if (!parse_event_config(&args[ARG_ASM_EVENT_CONFIG])) {
1416 mrp_log_error("Failed to parse event mapping configuration.");
1420 dump_event_config();
1422 /* create the transport and put it to listen mode */
1424 if (!mrp_msg_register_type(&asm_to_lib_descr)) {
1425 mrp_log_error("Failed to register message type asm_to_lib");
1429 if (!mrp_msg_register_type(&lib_to_asm_descr)) {
1430 mrp_log_error("Failed to register message type lib_to_asm");
1434 if (!mrp_msg_register_type(&asm_to_lib_cb_descr)) {
1435 mrp_log_error("Failed to register message type asm_to_lib_cb");
1439 if (!mrp_msg_register_type(&lib_to_asm_cb_descr)) {
1440 mrp_log_error("Failed to register message type lib_to_asm_cb");
1444 if (tport_setup(ctx->address, ctx) < 0) {
1448 /* listen to SIGCHLD signal */
1450 ctx->sighandler = mrp_add_sighandler(plugin->ctx->ml, SIGCHLD, signal_handler, ctx);
1452 if (!ctx->sighandler) {
1453 mrp_log_error("Failed to register signal handling");
1457 client_conf.comp = int_comp;
1458 client_conf.hash = int_hash;
1459 client_conf.free = htbl_free_client;
1460 client_conf.nbucket = 0;
1461 client_conf.nentry = 10;
1463 ctx->clients = mrp_htbl_create(&client_conf);
1465 if (!ctx->clients) {
1466 mrp_log_error("Error creating resource set hash table");
1470 /* create the client structure towards Murphy */
1472 ctx->resource_client = mrp_resource_client_create("ASM", ctx);
1474 if (!ctx->resource_client) {
1475 mrp_log_error("Failed to get a resource client");
1479 /* fork-exec the asm bridge binary */
1481 mrp_log_info("going to fork!");
1486 mrp_log_error("error launching asm-bridge");
1489 else if (pid == 0) {
1491 if (close_fds() < 0) {
1492 mrp_log_error("close_fds() failed");
1495 if (ctx->log != NULL)
1496 setenv(ASM_BRIDGE_LOG_ENVVAR, ctx->log, 1);
1497 execl(ctx->binary, ctx->binary, ctx->address, NULL);
1503 mrp_log_info("child pid is %d", pid);
1516 kill(ctx->pid, SIGTERM);
1520 if (ctx->sighandler) {
1521 mrp_del_sighandler(ctx->sighandler);
1522 ctx->sighandler = NULL;
1525 if (ctx->resource_client) {
1526 mrp_resource_client_destroy(ctx->resource_client);
1535 static void asm_exit(mrp_plugin_t *plugin)
1537 asm_data_t *ctx = (asm_data_t *) plugin->data;
1540 kill(ctx->pid, SIGTERM);
1545 mrp_transport_disconnect(ctx->mt);
1546 mrp_transport_destroy(ctx->mt);
1551 mrp_transport_disconnect(ctx->t);
1552 mrp_transport_destroy(ctx->t);
1556 if (ctx->sighandler) {
1557 mrp_del_sighandler(ctx->sighandler);
1558 ctx->sighandler = NULL;
1562 mrp_htbl_destroy(ctx->clients, TRUE);
1563 ctx->clients = NULL;
1566 mrp_resource_client_destroy(ctx->resource_client);
1569 plugin->data = NULL;
1573 #define ASM_DESCRIPTION "A plugin to handle SLP Audio Session Manager client requests."
1574 #define ASM_HELP "Audio Session Manager backend"
1575 #define ASM_VERSION MRP_VERSION_INT(0, 0, 1)
1576 #define ASM_AUTHORS "Ismo Puustinen <ismo.puustinen@intel.com>"
1578 static mrp_plugin_arg_t args[] = {
1579 MRP_PLUGIN_ARGIDX(ARG_ASM_BRIDGE, STRING, "asm_bridge", "/usr/sbin/asm-bridge"),
1580 MRP_PLUGIN_ARGIDX(ARG_ASM_BRIDGE_LOG, STRING, "asm_bridge_log", NULL),
1581 MRP_PLUGIN_ARGIDX(ARG_ASM_ZONE, STRING, "zone", "default"),
1582 MRP_PLUGIN_ARGIDX(ARG_ASM_TPORT_ADDRESS, STRING, "tport_address", DEFAULT_TRANSPORT),
1583 MRP_PLUGIN_ARGIDX(ARG_ASM_AUDIO_PLAYBACK, STRING, "audio_playback", "audio_playback"),
1584 MRP_PLUGIN_ARGIDX(ARG_ASM_AUDIO_RECORDING, STRING, "audio_recording", "audio_recording"),
1585 MRP_PLUGIN_ARGIDX(ARG_ASM_VIDEO_PLAYBACK, STRING, "video_playback", "video_playback"),
1586 MRP_PLUGIN_ARGIDX(ARG_ASM_VIDEO_RECORDING, STRING, "video_recording", "video_recording"),
1587 MRP_PLUGIN_ARGIDX(ARG_ASM_EVENT_CONFIG, UNDECL, NULL, NULL)
1591 MURPHY_REGISTER_PLUGIN("resource-asm",
1592 ASM_VERSION, ASM_DESCRIPTION, ASM_AUTHORS, ASM_HELP,
1593 MRP_SINGLETON, asm_init, asm_exit,
1594 args, MRP_ARRAY_SIZE(args),
1595 NULL, 0, NULL, 0, NULL);