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>
42 #include <audio-session-manager.h>
44 #include <murphy/common.h>
45 #include <murphy/core.h>
47 #include <murphy/resource/client-api.h>
49 #include "resource-asm/asm-bridge.h"
56 uint32_t current_handle;
58 bool monitor; /* if the client has set up a monitor resource */
68 /* asm-bridge management */
70 mrp_transport_t *mt; /* listening transport */
71 mrp_transport_t *t; /* connected transport */
74 /* resource management */
76 mrp_resource_client_t *resource_client;
79 char *playback_resource;
80 char *recording_resource;
83 uint32_t current_request;
85 /* murphy integration */
86 mrp_sighandler_t *sighandler;
94 request_type_server_event
102 mrp_resource_set_t *rset;
107 request_type_t rtype;
111 } resource_set_data_t;
118 ARG_ASM_TPORT_ADDRESS,
119 ARG_ASM_PLAYBACK_RESOURCE,
120 ARG_ASM_RECORDING_RESOURCE,
123 static int tport_setup(const char *address, asm_data_t *ctx);
125 static void *u_to_p(uint32_t u)
127 #ifdef __SIZEOF_POINTER__
128 #if __SIZEOF_POINTER__ == 8
139 static uint32_t p_to_u(const void *p)
141 #ifdef __SIZEOF_POINTER__
142 #if __SIZEOF_POINTER__ == 8
144 uint64_t big = (uint64_t) p;
145 o = big & 0xffffffff;
147 uint32_t o = (uint32_t) p;
156 static int int_comp(const void *key1, const void *key2)
162 static uint32_t int_hash(const void *key)
168 static void dump_incoming_msg(lib_to_asm_t *msg, asm_data_t *ctx)
172 mrp_log_info(" --> client id: %u", msg->instance_id);
173 mrp_log_info(" --> data handle: %d", msg->handle);
174 mrp_log_info(" --> request id: 0x%04x", msg->request_id);
175 mrp_log_info(" --> sound event: 0x%04x", msg->sound_event);
176 mrp_log_info(" --> system resource: 0x%04x", msg->system_resource);
177 mrp_log_info(" --> state: 0x%04x", msg->sound_state);
180 int n_cookie = msg->n_cookie_bytes;
183 mrp_log_info(" --> cookie: ")
184 for (i = 0; i < n_cookie, i++) {
185 mrp_log_info("0x%02x ", msg->cookie[i]);
194 static void dump_outgoing_msg(asm_to_lib_t *msg, asm_data_t *ctx)
198 mrp_log_info(" <-- client id: %u", msg->instance_id);
199 mrp_log_info(" <-- alloc handle: %d", msg->alloc_handle);
200 mrp_log_info(" <-- command handle: %d", msg->cmd_handle);
201 mrp_log_info(" <-- sound command: 0x%04x", msg->result_sound_command);
202 mrp_log_info(" <-- state: 0x%04x", msg->result_sound_state);
203 mrp_log_info(" <-- check privilege: %s",
204 msg->check_privilege ? "TRUE" : "FALSE");
208 static uint32_t encode_pid_handle(uint32_t pid, uint32_t handle) {
210 uint32_t max_pid = 4194304; /* 2^22 */
211 uint32_t max_handle = 1024; /* 2^10 */
213 if (pid > max_pid || handle > max_handle) {
217 return pid | (handle << 22);
220 static uint32_t get_pid(uint32_t data) {
221 uint32_t pid_mask = 0xffffffff >> 10;
223 return data & pid_mask;
227 static uint32_t get_handle(uint32_t data) {
234 static void htbl_free_set(void *key, void *object)
236 resource_set_data_t *d = object;
241 mrp_resource_set_destroy(d->rset);
247 static client_t *create_client(uint32_t pid) {
248 client_t *client = mrp_allocz(sizeof(client_t));
249 mrp_htbl_config_t set_conf;
254 set_conf.comp = int_comp;
255 set_conf.hash = int_hash;
256 set_conf.free = htbl_free_set;
257 set_conf.nbucket = 0;
258 set_conf.nentry = 10;
260 client->sets = mrp_htbl_create(&set_conf);
268 client->current_handle = 1;
275 static void event_cb(uint32_t request_id, mrp_resource_set_t *set, void *data)
277 resource_set_data_t *d = data;
278 asm_data_t *ctx = d->ctx;
280 mrp_log_info("Event CB: id %u, set %p", request_id, set);
281 mrp_log_info("Resource set %u.%u", d->pid, d->handle);
282 mrp_log_info("Advice 0x%08x, Grant 0x%08x",
283 mrp_get_resource_set_advice(d->rset),
284 mrp_get_resource_set_grant(d->rset));
287 case request_type_acquire:
290 mrp_log_info("callback for acquire request %u", request_id);
292 /* expecting next server events */
293 d->rtype = request_type_server_event;
295 reply.instance_id = d->pid;
296 reply.check_privilege = TRUE;
297 reply.alloc_handle = d->handle;
298 reply.cmd_handle = d->handle;
300 reply.result_sound_command = ASM_COMMAND_NONE;
302 /* TODO: check the mask properly */
303 if (mrp_get_resource_set_grant(d->rset))
304 reply.result_sound_state = ASM_STATE_PLAYING;
306 reply.result_sound_state = ASM_STATE_STOP;
308 d->rtype = request_type_server_event;
310 dump_outgoing_msg(&reply, ctx);
311 mrp_transport_senddata(d->ctx->t, &reply, TAG_ASM_TO_LIB);
314 case request_type_release:
317 mrp_log_info("callback for release request %u", request_id);
319 /* expecting next server events */
320 d->rtype = request_type_server_event;
322 reply.instance_id = d->pid;
323 reply.check_privilege = TRUE;
324 reply.alloc_handle = d->handle;
325 reply.cmd_handle = d->handle;
327 reply.result_sound_command = ASM_COMMAND_NONE;
328 reply.result_sound_state = ASM_STATE_STOP;
330 d->rtype = request_type_server_event;
332 /* stop processing server_events */
335 dump_outgoing_msg(&reply, ctx);
336 mrp_transport_senddata(d->ctx->t, &reply, TAG_ASM_TO_LIB);
339 case request_type_server_event:
341 asm_to_lib_cb_t reply;
342 mrp_log_info("callback for no request %u", request_id);
344 reply.instance_id = d->pid;
345 reply.handle = d->handle;
346 reply.callback_expected = FALSE;
348 /* TODO: get the client and see if there is the monitor
349 * resource present. If yes, tell the availability state changes
352 if (d->request_id == 0) {
353 /* We either haven't requested any resources or have
354 * given up the resources. Filter out events. */
358 /* TODO: check if the d->rset state has actually changed -> only
359 * process server side notifications in that case */
361 if (mrp_get_resource_set_grant(d->rset))
362 reply.sound_command = ASM_COMMAND_PLAY;
364 reply.sound_command = ASM_COMMAND_STOP;
366 /* FIXME: the player-player case needs to be solved here? */
367 reply.event_source = ASM_EVENT_SOURCE_RESOURCE_CONFLICT;
369 mrp_transport_senddata(d->ctx->t, &reply, TAG_ASM_TO_LIB_CB);
377 static asm_to_lib_t *process_msg(lib_to_asm_t *msg, asm_data_t *ctx)
379 pid_t pid = msg->instance_id;
383 reply = mrp_allocz(sizeof(asm_to_lib_t));
385 reply->instance_id = pid;
386 reply->check_privilege = TRUE;
388 reply->alloc_handle = msg->handle;
389 reply->cmd_handle = msg->handle;
391 reply->result_sound_command = ASM_COMMAND_NONE;
392 reply->result_sound_state = ASM_STATE_IGNORE;
394 switch(msg->request_id) {
395 case ASM_REQUEST_REGISTER:
399 char *resource = "invalid";
401 mrp_log_info("REQUEST: REGISTER");
403 /* see if the process already has a client object */
404 client = mrp_htbl_lookup(ctx->clients, u_to_p(pid));
407 client = create_client(pid);
408 mrp_htbl_insert(ctx->clients, u_to_p(pid), client);
412 /* From Murphy point of view this is actually an error case,
413 * since the application can only belong to one class. This is
414 * a Murphy limitation and should be fixed later. */
416 mrp_log_error("Application tried to register twice");
421 switch (msg->sound_event) {
424 case ASM_EVENT_SHARE_MMPLAYER:
428 case ASM_EVENT_SHARE_MMCAMCORDER:
432 case ASM_EVENT_SHARE_MMSOUND:
436 case ASM_EVENT_SHARE_OPENAL:
439 case ASM_EVENT_SHARE_AVSYSTEM:
442 case ASM_EVENT_EXCLUSIVE_MMPLAYER:
446 case ASM_EVENT_EXCLUSIVE_MMCAMCORDER:
450 case ASM_EVENT_EXCLUSIVE_MMSOUND:
454 case ASM_EVENT_EXCLUSIVE_OPENAL:
457 case ASM_EVENT_EXCLUSIVE_AVSYSTEM:
460 case ASM_EVENT_NOTIFY:
467 case ASM_EVENT_SHARE_FMRADIO:
470 case ASM_EVENT_EXCLUSIVE_FMRADIO:
473 case ASM_EVENT_EARJACK_UNPLUG:
474 resource = "earjack";
477 case ASM_EVENT_ALARM:
480 case ASM_EVENT_VIDEOCALL:
484 case ASM_EVENT_MONITOR:
485 resource = "monitor";
488 case ASM_EVENT_RICH_CALL:
496 if (strcmp(resource, "invalid") == 0) {
497 mrp_log_error("unknown resource type: %d", msg->sound_event);
501 uint32_t handle = client->current_handle++;
502 resource_set_data_t *d = mrp_allocz(sizeof(resource_set_data_t));
507 d->rtype = request_type_server_event;
510 if (strcmp(resource, "earjack") == 0) {
511 mrp_log_info("earjack status request was received");
514 else if (strcmp(resource, "monitor") == 0) {
515 mrp_log_info("monitor resource was received");
516 /* TODO: tell the available state changes to this pid
517 * via the monitor resource. */
518 client->monitor = TRUE;
522 /* a normal resource request */
524 d->rset = mrp_resource_set_create(ctx->resource_client, 0,
528 mrp_log_error("Failed to create resource set!");
532 if (mrp_resource_set_add_resource(d->rset,
533 ctx->playback_resource, shared, NULL, TRUE) < 0) {
534 mrp_log_error("Failed to add playback resource!");
535 mrp_resource_set_destroy(d->rset);
540 if (mrp_resource_set_add_resource(d->rset,
541 ctx->recording_resource, shared, NULL, TRUE) < 0) {
542 mrp_log_error("Failed to add recording resource!");
543 mrp_resource_set_destroy(d->rset);
548 if (mrp_application_class_add_resource_set(resource,
549 ctx->zone, d->rset, 0) < 0) {
550 mrp_log_error("Failed to put the rset in a class!");
551 mrp_resource_set_destroy(d->rset);
557 mrp_htbl_insert(client->sets, u_to_p(handle), d);
560 reply->alloc_handle = handle;
561 reply->cmd_handle = reply->alloc_handle;
563 reply->result_sound_state = ASM_STATE_WAITING;
564 reply->result_sound_command = ASM_COMMAND_NONE;
569 case ASM_REQUEST_UNREGISTER:
571 client_t *client = mrp_htbl_lookup(ctx->clients, u_to_p(pid));
573 mrp_log_info("REQUEST: UNREGISTER");
576 resource_set_data_t *d;
578 d = mrp_htbl_lookup(client->sets, u_to_p(msg->handle));
580 mrp_log_error("set '%u.%u' not found", pid, msg->handle);
585 /* this is a resource request with no associated
586 * murphy resource, meaning a monitor or earjack. */
588 mrp_log_info("unregistering special resource %s",
589 d->monitor ? "monitor" : "earjack");
592 client->monitor = FALSE;
594 /* TODO: what to do with the earjack unregister case? */
597 /* the resource set id destroyed when it's removed from the
599 mrp_htbl_remove(client->sets, u_to_p(msg->handle), TRUE);
602 if (client->n_sets <= 0) {
603 mrp_htbl_remove(ctx->clients, u_to_p(pid), TRUE);
608 /* TODO: free memory and check if the resource set is empty when
609 * the resource library supports it */
611 /* no reply needed! */
616 case ASM_REQUEST_SETSTATE:
618 client_t *client = mrp_htbl_lookup(ctx->clients, u_to_p(pid));
620 resource_set_data_t *d;
622 mrp_log_info("REQUEST: SET STATE");
625 mrp_log_error("client '%u' not found", pid);
629 d = mrp_htbl_lookup(client->sets, u_to_p(msg->handle));
630 if (!d || !d->rset) {
631 mrp_log_error("set '%u.%u' not found", pid, msg->handle);
635 d->request_id = ++ctx->current_request;
637 switch(msg->sound_state) {
638 case ASM_STATE_PLAYING:
640 d->rtype = request_type_acquire;
642 mrp_log_info("requesting acquisition of playback rights"
643 " for set '%u.%u' (id: %u)", pid, msg->handle,
646 mrp_resource_set_acquire(d->rset, d->request_id);
651 case ASM_STATE_PAUSE:
653 d->rtype = request_type_release;
655 mrp_log_info("requesting release of playback rights for"
656 " set '%u.%u' (id: %u)", pid, msg->handle,
659 mrp_resource_set_release(d->rset, d->request_id);
665 mrp_log_error("Unknown state: %d", msg->sound_state);
673 case ASM_REQUEST_GETSTATE:
674 mrp_log_info("REQUEST: GET STATE");
675 /* TODO: get the current resource state for msg->sound_event (which
676 * is the application class). Put it to reply->result_sound_state
679 case ASM_REQUEST_GETMYSTATE:
680 mrp_log_info("REQUEST: GET MY STATE");
681 /* TODO: get the current state for the process (resource set). Put
682 * it to reply->result_sound_state field. */
684 case ASM_REQUEST_EMERGENT_EXIT:
686 client_t *client = mrp_htbl_lookup(ctx->clients, u_to_p(pid));
688 mrp_log_info("REQUEST: EMERGENCY EXIT");
691 mrp_log_error("client '%u' not found", pid);
695 mrp_htbl_remove(ctx->clients, u_to_p(pid), TRUE);
700 case ASM_REQUEST_SET_SUBSESSION:
701 mrp_log_info("REQUEST: SET SUBSESSION");
703 case ASM_REQUEST_GET_SUBSESSION:
704 mrp_log_info("REQUEST: GET SUBSESSION");
707 mrp_log_info("REQUEST: UNKNOWN REQUEST");
714 /* TODO: need to write some sort of message back? */
723 static void process_cb_msg(lib_to_asm_cb_t *msg, asm_data_t *ctx)
729 /* TODO: this function might tell something to the resource library */
731 switch (msg->cb_result) {
732 case ASM_CB_RES_IGNORE:
735 case ASM_CB_RES_NONE:
738 case ASM_CB_RES_PAUSE:
741 case ASM_CB_RES_PLAYING:
744 case ASM_CB_RES_STOP:
748 mrp_log_error("unknown callback state %d", msg->cb_result);
752 mrp_log_info("client %d.%u ended in state '%s' after callback",
753 msg->instance_id, msg->handle, str);
757 static void recvdatafrom_evt(mrp_transport_t *t, void *data, uint16_t tag,
758 mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
760 asm_data_t *ctx = user_data;
768 lib_to_asm_t *msg = data;
771 /* client requests something from us */
773 dump_incoming_msg(msg, ctx);
775 reply = process_msg(msg, ctx);
777 dump_outgoing_msg(reply, ctx);
778 mrp_transport_senddata(t, reply, TAG_ASM_TO_LIB);
782 case TAG_LIB_TO_ASM_CB:
784 lib_to_asm_cb_t *msg = data;
786 /* client tells us which state it entered after preemption */
788 process_cb_msg(msg, ctx);
793 mrp_log_error("Unknown message received!");
797 mrp_data_free(data, tag);
801 static void recvdata_evt(mrp_transport_t *t, void *data, uint16_t tag, void *user_data)
803 recvdatafrom_evt(t, data, tag, NULL, 0, user_data);
808 static void closed_evt(mrp_transport_t *t, int error, void *user_data)
811 asm_data_t *ctx = user_data;
815 mrp_log_info("closed!");
817 mrp_transport_disconnect(t);
818 mrp_transport_destroy(t);
822 /* TODO: start the listening socket again and relaunch the binary? */
827 static void connection_evt(mrp_transport_t *lt, void *user_data)
829 asm_data_t *ctx = user_data;
831 mrp_log_info("connection!");
834 mrp_log_error("Already connected");
837 ctx->t = mrp_transport_accept(lt, ctx, 0);
840 /* close the listening socket, since we only have one client */
842 mrp_transport_destroy(lt);
848 static int tport_setup(const char *address, asm_data_t *ctx)
856 static mrp_transport_evt_t evt = {
857 { .recvdata = recvdata_evt },
858 { .recvdatafrom = recvdatafrom_evt },
859 .closed = closed_evt,
860 .connection = connection_evt
863 alen = mrp_transport_resolve(NULL, address, &addr, sizeof(addr), &atype);
866 mrp_log_error("Error resolving transport address");
870 /* remove the old socket if present */
872 if (strcmp(atype, "unxs") == 0) {
873 char *path = addr.unx.sun_path;
874 if (path[0] == '/') {
875 /* if local socket and file exists, remove it */
876 if (stat(path, &statbuf) == 0) {
877 if (S_ISSOCK(statbuf.st_mode)) {
878 if (unlink(path) < 0) {
879 mrp_log_error("error removing the socket");
884 mrp_log_error("a file where the socket should be created");
891 ctx->mt = mrp_transport_create(ctx->ctx->ml, atype, &evt, ctx,
892 MRP_TRANSPORT_MODE_CUSTOM | MRP_TRANSPORT_NONBLOCK);
894 if (ctx->mt == NULL) {
895 mrp_log_error("Failed to create the transport");
899 if (!mrp_transport_bind(ctx->mt, &addr, alen)) {
900 mrp_log_error("Failed to bind the transport to address '%s'", address);
905 if (!mrp_transport_listen(ctx->mt, 5)) {
906 mrp_log_error("Failed to listen to transport");
914 mrp_transport_destroy(ctx->mt);
921 static void signal_handler(mrp_mainloop_t *ml, mrp_sighandler_t *h,
922 int signum, void *user_data)
924 asm_data_t *ctx = user_data;
929 if (signum == SIGCHLD) {
930 /* wait for the child */
934 mrp_log_info("Received SIGCHLD, waiting for asm-bridge");
936 ret = waitpid(ctx->pid, NULL, WNOHANG);
938 if (ret == ctx->pid) {
939 mrp_log_warning("asm-bridge process died");
946 static void htbl_free_client(void *key, void *object)
950 client_t *client = object;
952 mrp_htbl_destroy(client->sets, TRUE);
954 /* TODO: free memory when resource API allows that */
958 static int asm_init(mrp_plugin_t *plugin)
960 mrp_plugin_arg_t *args = plugin->args;
961 asm_data_t *ctx = mrp_allocz(sizeof(asm_data_t));
963 mrp_htbl_config_t client_conf;
969 ctx->ctx = plugin->ctx;
970 ctx->address = args[ARG_ASM_TPORT_ADDRESS].str;
971 ctx->binary = args[ARG_ASM_BRIDGE].str;
972 ctx->zone = args[ARG_ASM_ZONE].str;
974 ctx->playback_resource = args[ARG_ASM_PLAYBACK_RESOURCE].str;
975 ctx->recording_resource = args[ARG_ASM_RECORDING_RESOURCE].str;
977 /* create the transport and put it to listen mode */
979 if (!mrp_msg_register_type(&asm_to_lib_descr)) {
980 mrp_log_error("Failed to register message type asm_to_lib");
984 if (!mrp_msg_register_type(&lib_to_asm_descr)) {
985 mrp_log_error("Failed to register message type lib_to_asm");
989 if (!mrp_msg_register_type(&asm_to_lib_cb_descr)) {
990 mrp_log_error("Failed to register message type asm_to_lib_cb");
994 if (!mrp_msg_register_type(&lib_to_asm_cb_descr)) {
995 mrp_log_error("Failed to register message type lib_to_asm_cb");
999 if (tport_setup(ctx->address, ctx) < 0) {
1003 /* listen to SIGCHLD signal */
1005 ctx->sighandler = mrp_add_sighandler(plugin->ctx->ml, SIGCHLD, signal_handler, ctx);
1007 if (!ctx->sighandler) {
1008 mrp_log_error("Failed to register signal handling");
1012 client_conf.comp = int_comp;
1013 client_conf.hash = int_hash;
1014 client_conf.free = htbl_free_client;
1015 client_conf.nbucket = 0;
1016 client_conf.nentry = 10;
1018 ctx->clients = mrp_htbl_create(&client_conf);
1020 if (!ctx->clients) {
1021 mrp_log_error("Error creating resource set hash table");
1025 /* create the client structure towards Murphy */
1027 ctx->resource_client = mrp_resource_client_create("ASM", ctx);
1029 if (!ctx->resource_client) {
1030 mrp_log_error("Failed to get a resource client");
1034 /* fork-exec the asm bridge binary */
1036 mrp_log_info("going to fork!");
1041 mrp_log_error("error launching asm-bridge");
1044 else if (pid == 0) {
1046 execl(ctx->binary, ctx->binary, ctx->address, NULL);
1052 mrp_log_info("child pid is %d", pid);
1062 kill(ctx->pid, SIGTERM);
1066 if (ctx->sighandler) {
1067 mrp_del_sighandler(ctx->sighandler);
1068 ctx->sighandler = NULL;
1071 if (ctx->resource_client) {
1072 mrp_resource_client_destroy(ctx->resource_client);
1081 static void asm_exit(mrp_plugin_t *plugin)
1083 asm_data_t *ctx = plugin->data;
1086 kill(ctx->pid, SIGTERM);
1091 mrp_transport_disconnect(ctx->mt);
1092 mrp_transport_destroy(ctx->mt);
1097 mrp_transport_disconnect(ctx->t);
1098 mrp_transport_destroy(ctx->t);
1102 if (ctx->sighandler) {
1103 mrp_del_sighandler(ctx->sighandler);
1104 ctx->sighandler = NULL;
1108 mrp_htbl_destroy(ctx->clients, TRUE);
1109 ctx->clients = NULL;
1112 mrp_resource_client_destroy(ctx->resource_client);
1115 plugin->data = NULL;
1119 #define ASM_DESCRIPTION "A plugin to handle SLP Audio Session Manager client requests."
1120 #define ASM_HELP "Audio Session Manager backend"
1121 #define ASM_VERSION MRP_VERSION_INT(0, 0, 1)
1122 #define ASM_AUTHORS "Ismo Puustinen <ismo.puustinen@intel.com>"
1124 static mrp_plugin_arg_t args[] = {
1125 MRP_PLUGIN_ARGIDX(ARG_ASM_BRIDGE, STRING, "asm_bridge", "/usr/sbin/asm-bridge"),
1126 MRP_PLUGIN_ARGIDX(ARG_ASM_ZONE, STRING, "zone", "default"),
1127 MRP_PLUGIN_ARGIDX(ARG_ASM_TPORT_ADDRESS, STRING, "tport_address", "unxs:/tmp/murphy/asm"),
1128 MRP_PLUGIN_ARGIDX(ARG_ASM_PLAYBACK_RESOURCE, STRING, "playback_resource", "audio_playback"),
1129 MRP_PLUGIN_ARGIDX(ARG_ASM_RECORDING_RESOURCE, STRING, "recording_resource", "audio_recording"),
1133 MURPHY_REGISTER_PLUGIN("resource-asm",
1134 ASM_VERSION, ASM_DESCRIPTION, ASM_AUTHORS, ASM_HELP,
1135 MRP_SINGLETON, asm_init, asm_exit,
1136 args, MRP_ARRAY_SIZE(args),
1137 NULL, 0, NULL, 0, NULL);