/* * connect / disconnect two subscriber ports * ver.0.1.3 * * Copyright (C) 1999 Takashi Iwai * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #include #include static void error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...) { va_list arg; if (err == ENOENT) /* Ignore those misleading "warnings" */ return; va_start(arg, fmt); fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function); vfprintf(stderr, fmt, arg); if (err) fprintf(stderr, ": %s", snd_strerror(err)); putc('\n', stderr); va_end(arg); } static void usage(void) { fprintf(stderr, "aconnect - ALSA sequencer connection manager\n"); fprintf(stderr, "Copyright (C) 1999-2000 Takashi Iwai\n"); fprintf(stderr, "Usage:\n"); fprintf(stderr, " * Connection/disconnection betwen two ports\n"); fprintf(stderr, " aconnect [-options] sender receiver\n"); fprintf(stderr, " sender, receiver = client:port pair\n"); fprintf(stderr, " -d,--disconnect disconnect\n"); fprintf(stderr, " -e,--exclusive exclusive connection\n"); fprintf(stderr, " -r,--real # convert real-time-stamp on queue\n"); fprintf(stderr, " -t,--tick # convert tick-time-stamp on queue\n"); fprintf(stderr, " -g,--group name set the group name\n"); fprintf(stderr, " * List connected ports (no subscription action)\n"); fprintf(stderr, " aconnect -i|-o [-options]\n"); fprintf(stderr, " -i,--input list input (readable) ports\n"); fprintf(stderr, " -o,--output list output (writable) ports\n"); fprintf(stderr, " -g,--group name specify the group name\n"); fprintf(stderr, " -l,--list list current connections of each port\n"); fprintf(stderr, " * Remove all exported connections\n"); fprintf(stderr, " -x, --removeall\n"); } /* * parse command line to client:port * NB: the given string will be broken. */ static int parse_address(snd_seq_t *seq, snd_seq_addr_t *addr, char *arg) { char *p; int client, port; if ((p = strpbrk(arg, ":.")) == NULL) return -1; if ((port = atoi(p + 1)) < 0) return -1; addr->port = port; if (isdigit(*arg)) { client = atoi(arg); if (client < 0) return -1; addr->client = client; } else { /* convert from the name */ snd_seq_client_info_t cinfo; int len; *p = 0; len = strlen(arg); if (len <= 0) return -1; cinfo.client = -1; while (snd_seq_query_next_client(seq, &cinfo) >= 0) { if (! strncmp(cinfo.name, arg, len)) { addr->client = cinfo.client; return 0; } } return -1; /* not found */ } return 0; } /* * check permission (capability) of specified port */ static int check_permission(snd_seq_port_info_t *pinfo, char *group, int perm) { if ((pinfo->capability & perm) == perm && ! (pinfo->capability & SND_SEQ_PORT_CAP_NO_EXPORT)) return 1; if (*group && (pinfo->cap_group & perm) == perm && ! (pinfo->cap_group & SND_SEQ_PORT_CAP_NO_EXPORT)) return 1; return 0; } /* * list subscribers of specified type */ static void list_each_subs(snd_seq_t *seq, snd_seq_query_subs_t *subs, int type, char *msg) { subs->type = type; subs->index = 0; while (snd_seq_query_port_subscribers(seq, subs) >= 0) { if (subs->index == 0) printf("\t%s: ", msg); else printf(", "); printf("%d:%d", subs->addr.client, subs->addr.port); if (subs->exclusive) printf("[ex]"); if (subs->convert_time) printf("[%s:%d]", (subs->realtime ? "real" : "tick"), subs->queue); subs->index++; } if (subs->index) printf("\n"); } /* * list subscribers */ static void list_subscribers(snd_seq_t *seq, int client, int port) { snd_seq_query_subs_t subs; memset(&subs, 0, sizeof(subs)); subs.client = client; subs.port = port; list_each_subs(seq, &subs, SND_SEQ_QUERY_SUBS_READ, "Connecting To"); list_each_subs(seq, &subs, SND_SEQ_QUERY_SUBS_WRITE, "Connected From"); } /* * search all ports */ typedef void (*action_func_t)(snd_seq_t *seq, snd_seq_client_info_t *cinfo, snd_seq_port_info_t *pinfo, int count); static void do_search_port(snd_seq_t *seq, char *group, int perm, action_func_t do_action) { snd_seq_client_info_t cinfo; snd_seq_port_info_t pinfo; int count; cinfo.client = -1; while (snd_seq_query_next_client(seq, &cinfo) >= 0) { /* reset query info */ pinfo.client = cinfo.client; pinfo.port = -1; count = 0; while (snd_seq_query_next_port(seq, &pinfo) >= 0) { if (*group && strcmp(pinfo.group, group)) continue; if (check_permission(&pinfo, group, perm)) { do_action(seq, &cinfo, &pinfo, count); count++; } } } } static void print_port(snd_seq_t *seq, snd_seq_client_info_t *cinfo, snd_seq_port_info_t *pinfo, int count) { if (! count) { printf("client %d: '%s' [group=%s] [type=%s]\n", cinfo->client, cinfo->name, cinfo->group, (cinfo->type == USER_CLIENT ? "user" : "kernel")); } printf(" %3d '%-16s' [group=%s]\n", pinfo->port, pinfo->name, pinfo->group); } static void print_port_and_subs(snd_seq_t *seq, snd_seq_client_info_t *cinfo, snd_seq_port_info_t *pinfo, int count) { print_port(seq, cinfo, pinfo, count); list_subscribers(seq, pinfo->client, pinfo->port); } /* * list all ports */ static void list_ports(snd_seq_t *seq, char *group, int perm, int list_subs) { if (list_subs) do_search_port(seq, group, perm, print_port_and_subs); else do_search_port(seq, group, perm, print_port); } /* * remove all (exported) connections */ static void remove_connection(snd_seq_t *seq, snd_seq_client_info_t *cinfo, snd_seq_port_info_t *pinfo, int count) { snd_seq_query_subs_t query; memset(&query, 0, sizeof(query)); query.client = pinfo->client; query.port = pinfo->port; query.type = SND_SEQ_QUERY_SUBS_READ; query.index = 0; for (query.index = 0; snd_seq_query_port_subscribers(seq, &query) >= 0; query.index++) { snd_seq_port_info_t port; snd_seq_port_subscribe_t subs; if (snd_seq_get_any_port_info(seq, query.addr.client, query.addr.port, &port) < 0) continue; if (!(port.capability & SND_SEQ_PORT_CAP_SUBS_READ)) continue; if (port.capability & SND_SEQ_PORT_CAP_NO_EXPORT) continue; memset(&subs, 0, sizeof(subs)); subs.queue = query.queue; subs.sender.client = query.client; subs.sender.port = query.port; subs.dest.client = query.addr.client; subs.dest.port = query.addr.port; snd_seq_unsubscribe_port(seq, &subs); } query.type = SND_SEQ_QUERY_SUBS_WRITE; query.index = 0; for (query.index = 0; snd_seq_query_port_subscribers(seq, &query) >= 0; query.index++) { snd_seq_port_info_t port; snd_seq_port_subscribe_t subs; if (snd_seq_get_any_port_info(seq, query.addr.client, query.addr.port, &port) < 0) continue; if (!(port.capability & SND_SEQ_PORT_CAP_SUBS_WRITE)) continue; if (port.capability & SND_SEQ_PORT_CAP_NO_EXPORT) continue; memset(&subs, 0, sizeof(subs)); subs.queue = query.queue; subs.sender.client = query.addr.client; subs.sender.port = query.addr.port; subs.dest.client = query.client; subs.dest.port = query.port; snd_seq_unsubscribe_port(seq, &subs); } } static void remove_all_connections(snd_seq_t *seq) { do_search_port(seq, "", 0, remove_connection); } /* * main.. */ enum { SUBSCRIBE, UNSUBSCRIBE, LIST_INPUT, LIST_OUTPUT, REMOVE_ALL }; static struct option long_option[] = { {"disconnect", 0, NULL, 'd'}, {"input", 0, NULL, 'i'}, {"output", 0, NULL, 'o'}, {"group", 1, NULL, 'g'}, {"real", 1, NULL, 'r'}, {"tick", 1, NULL, 't'}, {"exclusive", 0, NULL, 'e'}, {"list", 0, NULL, 'l'}, {"removeall", 0, NULL, 'x'}, {NULL, 0, NULL, 0}, }; int main(int argc, char **argv) { int c; snd_seq_t *seq; int queue = 0, convert_time = 0, convert_real = 0, exclusive = 0; int command = SUBSCRIBE; char *group = ""; int client; int list_subs = 0; snd_seq_client_info_t cinfo; snd_seq_port_subscribe_t subs; while ((c = getopt_long(argc, argv, "diog:r:t:elx", long_option, NULL)) != -1) { switch (c) { case 'd': command = UNSUBSCRIBE; break; case 'i': command = LIST_INPUT; break; case 'o': command = LIST_OUTPUT; break; case 'g': group = optarg; break; case 'e': exclusive = 1; break; case 'r': queue = atoi(optarg); convert_time = 1; convert_real = 1; break; case 't': queue = atoi(optarg); convert_time = 1; convert_real = 0; break; case 'l': list_subs = 1; break; case 'x': command = REMOVE_ALL; break; default: usage(); exit(1); } } if (snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) { fprintf(stderr, "can't open sequencer\n"); return 1; } snd_lib_error_set_handler(error_handler); switch (command) { case LIST_INPUT: list_ports(seq, group, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, list_subs); snd_seq_close(seq); return 0; case LIST_OUTPUT: list_ports(seq, group, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, list_subs); snd_seq_close(seq); return 0; case REMOVE_ALL: remove_all_connections(seq); snd_seq_close(seq); return 0; } /* connection or disconnection */ if (optind + 2 > argc) { snd_seq_close(seq); usage(); exit(1); } if ((client = snd_seq_client_id(seq)) < 0) { snd_seq_close(seq); fprintf(stderr, "can't get client id\n"); return 1; } /* set client info */ memset(&cinfo, 0, sizeof(cinfo)); cinfo.client = client; cinfo.type = USER_CLIENT; strcpy(cinfo.name, "ALSA Connector"); strncpy(cinfo.group, group, sizeof(cinfo.group) - 1); if (snd_seq_set_client_info(seq, &cinfo) < 0) { snd_seq_close(seq); fprintf(stderr, "can't set client info\n"); return 0; } /* set subscription */ memset(&subs, 0, sizeof(subs)); if (parse_address(seq, &subs.sender, argv[optind]) < 0) { fprintf(stderr, "invalid sender address %s\n", argv[optind]); return 1; } if (parse_address(seq, &subs.dest, argv[optind + 1]) < 0) { fprintf(stderr, "invalid destination address %s\n", argv[optind + 1]); return 1; } subs.queue = queue; subs.exclusive = exclusive; subs.convert_time = convert_time; subs.realtime = convert_real; if (command == UNSUBSCRIBE) { if (snd_seq_get_port_subscription(seq, &subs) < 0) { snd_seq_close(seq); fprintf(stderr, "No subscription is found\n"); return 1; } if (snd_seq_unsubscribe_port(seq, &subs) < 0) { snd_seq_close(seq); fprintf(stderr, "Disconnection failed (%s)\n", snd_strerror(errno)); return 1; } } else { if (snd_seq_get_port_subscription(seq, &subs) == 0) { snd_seq_close(seq); fprintf(stderr, "Connection is already subscribed\n"); return 1; } if (snd_seq_subscribe_port(seq, &subs) < 0) { snd_seq_close(seq); fprintf(stderr, "Connection failed (%s)\n", snd_strerror(errno)); return 1; } } snd_seq_close(seq); return 0; }