2 * connect / disconnect two subscriber ports
5 * Copyright (C) 1999 Takashi Iwai
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
28 #include <sys/ioctl.h>
29 #include <alsa/asoundlib.h>
35 #if SND_LIB_VER(1, 2, 15) < SND_LIB_VERSION
36 static void error_handler(const char *file, int line, const char *function, int errcode, const char *fmt, ...)
40 if (errcode == ENOENT) /* Ignore those misleading "warnings" */
43 fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
44 vfprintf(stderr, fmt, arg);
46 fprintf(stderr, ": %s", snd_strerror(err));
51 static snd_lib_log_handler_t original_log_handler;
52 static void log_handler(int prio, int interface, const char *file, int line, const char *function, int errcode, const char *fmt, va_list arg)
54 if (prio == SND_LOG_ERROR && errcode == ENOENT) /* Ignore those misleading "warnings" */
56 if (original_log_handler)
57 original_log_handler(prio, interface, file, line, function, errcode, fmt, arg);
61 static void usage(void)
63 printf(_("aconnect - ALSA sequencer connection manager\n"));
64 printf(_("Copyright (C) 1999-2000 Takashi Iwai\n"));
65 printf(_("Usage:\n"));
66 printf(_(" * Connection/disconnection between two ports\n"));
67 printf(_(" aconnect [-options] sender receiver\n"));
68 printf(_(" sender, receiver = client:port pair\n"));
69 printf(_(" -d,--disconnect disconnect\n"));
70 printf(_(" -e,--exclusive exclusive connection\n"));
71 printf(_(" -r,--real # convert real-time-stamp on queue\n"));
72 printf(_(" -t,--tick # convert tick-time-stamp on queue\n"));
73 printf(_(" * List connected ports (no subscription action)\n"));
74 printf(_(" aconnect -i|-o [-options]\n"));
75 printf(_(" -i,--input list input (readable) ports\n"));
76 printf(_(" -o,--output list output (writable) ports\n"));
77 printf(_(" -a,--all show inactive ports, too\n"));
78 printf(_(" -l,--list list current connections of each port\n"));
79 printf(_(" * Remove all exported connections\n"));
80 printf(_(" -x, --removeall\n"));
84 * check permission (capability) of specified port
90 #define perm_ok(cap,bits) (((cap) & (bits)) == (bits))
92 static int check_direction(snd_seq_port_info_t *pinfo, int bit)
94 int dir = snd_seq_port_info_get_direction(pinfo);
95 return !dir || (dir & bit);
98 static int check_permission(snd_seq_port_info_t *pinfo, int perm)
100 int cap = snd_seq_port_info_get_capability(pinfo);
102 if (cap & SND_SEQ_PORT_CAP_NO_EXPORT)
107 if (perm & LIST_INPUT) {
108 if (perm_ok(cap, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ) &&
109 check_direction(pinfo, SND_SEQ_PORT_DIR_INPUT))
112 if (perm & LIST_OUTPUT) {
113 if (perm_ok(cap, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE) &&
114 check_direction(pinfo, SND_SEQ_PORT_DIR_OUTPUT))
121 * list subscribers of specified type
123 static void list_each_subs(snd_seq_t *seq, snd_seq_query_subscribe_t *subs, int type, const char *msg)
126 snd_seq_query_subscribe_set_type(subs, type);
127 snd_seq_query_subscribe_set_index(subs, 0);
128 while (snd_seq_query_port_subscribers(seq, subs) >= 0) {
129 const snd_seq_addr_t *addr;
131 printf("\t%s: ", msg);
134 addr = snd_seq_query_subscribe_get_addr(subs);
135 printf("%d:%d", addr->client, addr->port);
136 if (snd_seq_query_subscribe_get_exclusive(subs))
138 if (snd_seq_query_subscribe_get_time_update(subs))
140 (snd_seq_query_subscribe_get_time_real(subs) ? "real" : "tick"),
141 snd_seq_query_subscribe_get_queue(subs));
142 snd_seq_query_subscribe_set_index(subs, snd_seq_query_subscribe_get_index(subs) + 1);
151 static void list_subscribers(snd_seq_t *seq, const snd_seq_addr_t *addr)
153 snd_seq_query_subscribe_t *subs;
154 snd_seq_query_subscribe_alloca(&subs);
155 snd_seq_query_subscribe_set_root(subs, addr);
156 list_each_subs(seq, subs, SND_SEQ_QUERY_SUBS_READ, _("Connecting To"));
157 list_each_subs(seq, subs, SND_SEQ_QUERY_SUBS_WRITE, _("Connected From"));
163 typedef void (*action_func_t)(snd_seq_t *seq, snd_seq_client_info_t *cinfo, snd_seq_port_info_t *pinfo, int count);
165 static void do_search_port(snd_seq_t *seq, int perm, action_func_t do_action)
167 snd_seq_client_info_t *cinfo;
168 snd_seq_port_info_t *pinfo;
171 snd_seq_client_info_alloca(&cinfo);
172 snd_seq_port_info_alloca(&pinfo);
173 snd_seq_client_info_set_client(cinfo, -1);
174 while (snd_seq_query_next_client(seq, cinfo) >= 0) {
175 /* reset query info */
176 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
177 snd_seq_port_info_set_port(pinfo, -1);
179 snd_seq_port_info_set_capability(pinfo, SND_SEQ_PORT_CAP_INACTIVE);
181 while (snd_seq_query_next_port(seq, pinfo) >= 0) {
182 if (check_permission(pinfo, perm)) {
183 do_action(seq, cinfo, pinfo, count);
187 snd_seq_port_info_set_capability(pinfo, SND_SEQ_PORT_CAP_INACTIVE);
193 static void print_port(snd_seq_t *seq ATTRIBUTE_UNUSED,
194 snd_seq_client_info_t *cinfo,
195 snd_seq_port_info_t *pinfo, int count)
198 int card = -1, pid = -1;
200 printf(_("client %d: '%s' [type=%s"),
201 snd_seq_client_info_get_client(cinfo),
202 snd_seq_client_info_get_name(cinfo),
203 (snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ?
204 _("user") : _("kernel")));
205 switch (snd_seq_client_info_get_midi_version(cinfo)) {
206 case SND_SEQ_CLIENT_UMP_MIDI_1_0:
207 printf(",UMP-MIDI1");
209 case SND_SEQ_CLIENT_UMP_MIDI_2_0:
210 printf(",UMP-MIDI2");
213 card = snd_seq_client_info_get_card(cinfo);
215 printf(",card=%d", card);
217 pid = snd_seq_client_info_get_pid(cinfo);
219 printf(",pid=%d", pid);
222 printf(" %3d '%-16s'",
223 snd_seq_port_info_get_port(pinfo),
224 snd_seq_port_info_get_name(pinfo));
225 if (snd_seq_port_info_get_capability(pinfo) & SND_SEQ_PORT_CAP_INACTIVE)
226 printf(" [INACTIVE]");
230 static void print_port_and_subs(snd_seq_t *seq, snd_seq_client_info_t *cinfo,
231 snd_seq_port_info_t *pinfo, int count)
233 print_port(seq, cinfo, pinfo, count);
234 list_subscribers(seq, snd_seq_port_info_get_addr(pinfo));
239 * remove all (exported) connections
241 static void remove_connection(snd_seq_t *seq,
242 snd_seq_client_info_t *info ATTRIBUTE_UNUSED,
243 snd_seq_port_info_t *pinfo,
244 int count ATTRIBUTE_UNUSED)
246 snd_seq_query_subscribe_t *query;
247 snd_seq_port_info_t *port;
248 snd_seq_port_subscribe_t *subs;
250 snd_seq_query_subscribe_alloca(&query);
251 snd_seq_query_subscribe_set_root(query, snd_seq_port_info_get_addr(pinfo));
252 snd_seq_query_subscribe_set_type(query, SND_SEQ_QUERY_SUBS_READ);
253 snd_seq_query_subscribe_set_index(query, 0);
255 snd_seq_port_info_alloca(&port);
256 snd_seq_port_subscribe_alloca(&subs);
258 while (snd_seq_query_port_subscribers(seq, query) >= 0) {
259 const snd_seq_addr_t *sender = snd_seq_query_subscribe_get_root(query);
260 const snd_seq_addr_t *dest = snd_seq_query_subscribe_get_addr(query);
262 if (snd_seq_get_any_port_info(seq, dest->client, dest->port, port) < 0 ||
263 !(snd_seq_port_info_get_capability(port) & SND_SEQ_PORT_CAP_SUBS_WRITE) ||
264 (snd_seq_port_info_get_capability(port) & SND_SEQ_PORT_CAP_NO_EXPORT)) {
265 snd_seq_query_subscribe_set_index(query, snd_seq_query_subscribe_get_index(query) + 1);
268 snd_seq_port_subscribe_set_queue(subs, snd_seq_query_subscribe_get_queue(query));
269 snd_seq_port_subscribe_set_sender(subs, sender);
270 snd_seq_port_subscribe_set_dest(subs, dest);
271 if (snd_seq_unsubscribe_port(seq, subs) < 0) {
272 snd_seq_query_subscribe_set_index(query, snd_seq_query_subscribe_get_index(query) + 1);
277 static void remove_all_connections(snd_seq_t *seq)
279 do_search_port(seq, 0, remove_connection);
288 SUBSCRIBE, UNSUBSCRIBE, LIST, REMOVE_ALL
291 #define ACONNECT_OPTS "dior:t:elxa"
293 static const struct option long_option[] = {
294 {"disconnect", 0, NULL, 'd'},
295 {"input", 0, NULL, 'i'},
296 {"output", 0, NULL, 'o'},
297 {"real", 1, NULL, 'r'},
298 {"tick", 1, NULL, 't'},
299 {"exclusive", 0, NULL, 'e'},
300 {"list", 0, NULL, 'l'},
301 {"removeall", 0, NULL, 'x'},
302 {"all", 0, NULL, 'a'},
306 int main(int argc, char **argv)
310 int queue = 0, convert_time = 0, convert_real = 0, exclusive = 0;
311 int command = SUBSCRIBE;
315 snd_seq_port_subscribe_t *subs;
316 snd_seq_addr_t sender, dest;
319 setlocale(LC_ALL, "");
323 while ((c = getopt_long(argc, argv, ACONNECT_OPTS, long_option, NULL)) != -1) {
326 command = UNSUBSCRIBE;
330 list_perm |= LIST_INPUT;
334 list_perm |= LIST_OUTPUT;
340 queue = atoi(optarg);
345 queue = atoi(optarg);
354 command = REMOVE_ALL;
366 if (snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
367 fprintf(stderr, _("can't open sequencer\n"));
371 #if SND_LIB_VER(1, 2, 15) < SND_LIB_VERSION
372 snd_lib_error_set_handler(error_handler);
374 original_log_handler = snd_lib_log_set_handler(log_handler);
379 do_search_port(seq, list_perm,
380 list_subs ? print_port_and_subs : print_port);
384 remove_all_connections(seq);
389 /* connection or disconnection */
391 if (optind + 2 > argc) {
397 if ((client = snd_seq_client_id(seq)) < 0) {
399 fprintf(stderr, _("can't get client id\n"));
403 /* set client info */
404 if (snd_seq_set_client_name(seq, "ALSA Connector") < 0) {
406 fprintf(stderr, _("can't set client info\n"));
410 /* set subscription */
411 if (snd_seq_parse_address(seq, &sender, argv[optind]) < 0) {
413 fprintf(stderr, _("invalid sender address %s\n"), argv[optind]);
416 if (snd_seq_parse_address(seq, &dest, argv[optind + 1]) < 0) {
418 fprintf(stderr, _("invalid destination address %s\n"), argv[optind + 1]);
421 snd_seq_port_subscribe_alloca(&subs);
422 snd_seq_port_subscribe_set_sender(subs, &sender);
423 snd_seq_port_subscribe_set_dest(subs, &dest);
424 snd_seq_port_subscribe_set_queue(subs, queue);
425 snd_seq_port_subscribe_set_exclusive(subs, exclusive);
426 snd_seq_port_subscribe_set_time_update(subs, convert_time);
427 snd_seq_port_subscribe_set_time_real(subs, convert_real);
429 if (command == UNSUBSCRIBE) {
430 if (snd_seq_get_port_subscription(seq, subs) < 0) {
432 fprintf(stderr, _("No subscription is found\n"));
435 if (snd_seq_unsubscribe_port(seq, subs) < 0) {
437 fprintf(stderr, _("Disconnection failed (%s)\n"), snd_strerror(errno));
441 if (snd_seq_get_port_subscription(seq, subs) == 0) {
443 fprintf(stderr, _("Connection is already subscribed\n"));
446 if (snd_seq_subscribe_port(seq, subs) < 0) {
448 fprintf(stderr, _("Connection failed (%s)\n"), snd_strerror(errno));