alsa-utils/seq/aconnect/aconnect.c

444 lines
11 KiB
C
Raw Normal View History

/*
* 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 <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdarg.h>
#include <sys/ioctl.h>
#include <sys/asoundlib.h>
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;
2000-09-11 18:03:51 +02:00
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) {
2000-09-11 18:03:51 +02:00
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;
2000-12-01 12:49:54 +01:00
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;
2000-12-01 12:49:54 +01:00
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);
}
}
2000-11-20 21:11:13 +01:00
if (snd_seq_open(&seq, "hw", 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;
}