1 /*
2 * connect / disconnect two subscriber ports
3 * ver.0.1.3
4 *
5 * Copyright (C) 1999 Takashi Iwai
6 *
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.
10 *
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.
15 *
16 */
17
18 #include "aconfig.h"
19 #include <stdio.h>
20 #include <ctype.h>
21 #include <string.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <getopt.h>
26 #include <stdarg.h>
27 #include <locale.h>
28 #include <sys/ioctl.h>
29 #include <alsa/asoundlib.h>
30 #include "gettext.h"
31
32 #ifdef SND_SEQ_PORT_CAP_INACTIVE
33 #define HANDLE_SHOW_ALL
34 static int show_all;
35 #else
36 #define show_all 0
37 #endif
38
error_handler(const char * file,int line,const char * function,int err,const char * fmt,...)39 static void error_handler(const char *file, int line, const char *function, int err, const char *fmt, ...)
40 {
41 va_list arg;
42
43 if (err == ENOENT) /* Ignore those misleading "warnings" */
44 return;
45 va_start(arg, fmt);
46 fprintf(stderr, "ALSA lib %s:%i:(%s) ", file, line, function);
47 vfprintf(stderr, fmt, arg);
48 if (err)
49 fprintf(stderr, ": %s", snd_strerror(err));
50 putc('\n', stderr);
51 va_end(arg);
52 }
53
usage(void)54 static void usage(void)
55 {
56 printf(_("aconnect - ALSA sequencer connection manager\n"));
57 printf(_("Copyright (C) 1999-2000 Takashi Iwai\n"));
58 printf(_("Usage:\n"));
59 printf(_(" * Connection/disconnection between two ports\n"));
60 printf(_(" aconnect [-options] sender receiver\n"));
61 printf(_(" sender, receiver = client:port pair\n"));
62 printf(_(" -d,--disconnect disconnect\n"));
63 printf(_(" -e,--exclusive exclusive connection\n"));
64 printf(_(" -r,--real # convert real-time-stamp on queue\n"));
65 printf(_(" -t,--tick # convert tick-time-stamp on queue\n"));
66 printf(_(" * List connected ports (no subscription action)\n"));
67 printf(_(" aconnect -i|-o [-options]\n"));
68 printf(_(" -i,--input list input (readable) ports\n"));
69 printf(_(" -o,--output list output (writable) ports\n"));
70 #ifdef HANDLE_SHOW_ALL
71 printf(_(" -a,--all show inactive ports, too\n"));
72 #endif
73 printf(_(" -l,--list list current connections of each port\n"));
74 printf(_(" * Remove all exported connections\n"));
75 printf(_(" -x, --removeall\n"));
76 }
77
78 /*
79 * check permission (capability) of specified port
80 */
81
82 #define LIST_INPUT 1
83 #define LIST_OUTPUT 2
84
85 #define perm_ok(cap,bits) (((cap) & (bits)) == (bits))
86
87 #ifdef SND_SEQ_PORT_DIR_INPUT
check_direction(snd_seq_port_info_t * pinfo,int bit)88 static int check_direction(snd_seq_port_info_t *pinfo, int bit)
89 {
90 int dir = snd_seq_port_info_get_direction(pinfo);
91 return !dir || (dir & bit);
92 }
93 #else
94 #define check_direction(x, y) 1
95 #endif
96
check_permission(snd_seq_port_info_t * pinfo,int perm)97 static int check_permission(snd_seq_port_info_t *pinfo, int perm)
98 {
99 int cap = snd_seq_port_info_get_capability(pinfo);
100
101 if (cap & SND_SEQ_PORT_CAP_NO_EXPORT)
102 return 0;
103
104 if (!perm)
105 return 1;
106 if (perm & LIST_INPUT) {
107 if (perm_ok(cap, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ) &&
108 check_direction(pinfo, SND_SEQ_PORT_DIR_INPUT))
109 return 1;
110 }
111 if (perm & LIST_OUTPUT) {
112 if (perm_ok(cap, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE) &&
113 check_direction(pinfo, SND_SEQ_PORT_DIR_OUTPUT))
114 return 1;
115 }
116 return 0;
117 }
118
119 /*
120 * list subscribers of specified type
121 */
list_each_subs(snd_seq_t * seq,snd_seq_query_subscribe_t * subs,int type,const char * msg)122 static void list_each_subs(snd_seq_t *seq, snd_seq_query_subscribe_t *subs, int type, const char *msg)
123 {
124 int count = 0;
125 snd_seq_query_subscribe_set_type(subs, type);
126 snd_seq_query_subscribe_set_index(subs, 0);
127 while (snd_seq_query_port_subscribers(seq, subs) >= 0) {
128 const snd_seq_addr_t *addr;
129 if (count++ == 0)
130 printf("\t%s: ", msg);
131 else
132 printf(", ");
133 addr = snd_seq_query_subscribe_get_addr(subs);
134 printf("%d:%d", addr->client, addr->port);
135 if (snd_seq_query_subscribe_get_exclusive(subs))
136 printf("[ex]");
137 if (snd_seq_query_subscribe_get_time_update(subs))
138 printf("[%s:%d]",
139 (snd_seq_query_subscribe_get_time_real(subs) ? "real" : "tick"),
140 snd_seq_query_subscribe_get_queue(subs));
141 snd_seq_query_subscribe_set_index(subs, snd_seq_query_subscribe_get_index(subs) + 1);
142 }
143 if (count > 0)
144 printf("\n");
145 }
146
147 /*
148 * list subscribers
149 */
list_subscribers(snd_seq_t * seq,const snd_seq_addr_t * addr)150 static void list_subscribers(snd_seq_t *seq, const snd_seq_addr_t *addr)
151 {
152 snd_seq_query_subscribe_t *subs;
153 snd_seq_query_subscribe_alloca(&subs);
154 snd_seq_query_subscribe_set_root(subs, addr);
155 list_each_subs(seq, subs, SND_SEQ_QUERY_SUBS_READ, _("Connecting To"));
156 list_each_subs(seq, subs, SND_SEQ_QUERY_SUBS_WRITE, _("Connected From"));
157 }
158
159 /*
160 * search all ports
161 */
162 typedef void (*action_func_t)(snd_seq_t *seq, snd_seq_client_info_t *cinfo, snd_seq_port_info_t *pinfo, int count);
163
do_search_port(snd_seq_t * seq,int perm,action_func_t do_action)164 static void do_search_port(snd_seq_t *seq, int perm, action_func_t do_action)
165 {
166 snd_seq_client_info_t *cinfo;
167 snd_seq_port_info_t *pinfo;
168 int count;
169
170 snd_seq_client_info_alloca(&cinfo);
171 snd_seq_port_info_alloca(&pinfo);
172 snd_seq_client_info_set_client(cinfo, -1);
173 while (snd_seq_query_next_client(seq, cinfo) >= 0) {
174 /* reset query info */
175 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
176 snd_seq_port_info_set_port(pinfo, -1);
177 #ifdef HANDLE_SHOW_ALL
178 if (show_all)
179 snd_seq_port_info_set_capability(pinfo, SND_SEQ_PORT_CAP_INACTIVE);
180 #endif
181 count = 0;
182 while (snd_seq_query_next_port(seq, pinfo) >= 0) {
183 if (check_permission(pinfo, perm)) {
184 do_action(seq, cinfo, pinfo, count);
185 count++;
186 }
187 #ifdef HANDLE_SHOW_ALL
188 if (show_all)
189 snd_seq_port_info_set_capability(pinfo, SND_SEQ_PORT_CAP_INACTIVE);
190 #endif
191 }
192 }
193 }
194
195
print_port(snd_seq_t * seq ATTRIBUTE_UNUSED,snd_seq_client_info_t * cinfo,snd_seq_port_info_t * pinfo,int count)196 static void print_port(snd_seq_t *seq ATTRIBUTE_UNUSED,
197 snd_seq_client_info_t *cinfo,
198 snd_seq_port_info_t *pinfo, int count)
199 {
200 if (! count) {
201 int card = -1, pid = -1;
202
203 printf(_("client %d: '%s' [type=%s"),
204 snd_seq_client_info_get_client(cinfo),
205 snd_seq_client_info_get_name(cinfo),
206 (snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT ?
207 _("user") : _("kernel")));
208 #ifdef HAVE_SEQ_CLIENT_INFO_GET_MIDI_VERSION
209 switch (snd_seq_client_info_get_midi_version(cinfo)) {
210 case SND_SEQ_CLIENT_UMP_MIDI_1_0:
211 printf(",UMP-MIDI1");
212 break;
213 case SND_SEQ_CLIENT_UMP_MIDI_2_0:
214 printf(",UMP-MIDI2");
215 break;
216 }
217 #endif
218 #ifdef HANDLE_SHOW_ALL
219 if (snd_seq_port_info_get_capability(pinfo) & SND_SEQ_PORT_CAP_INACTIVE)
220 printf(",INACTIVE");
221 #endif
222 #ifdef HAVE_SEQ_CLIENT_INFO_GET_CARD
223 card = snd_seq_client_info_get_card(cinfo);
224 #endif
225 if (card != -1)
226 printf(",card=%d", card);
227
228 #ifdef HAVE_SEQ_CLIENT_INFO_GET_PID
229 pid = snd_seq_client_info_get_pid(cinfo);
230 #endif
231 if (pid != -1)
232 printf(",pid=%d", pid);
233 printf("]\n");
234 }
235 printf(" %3d '%-16s'\n",
236 snd_seq_port_info_get_port(pinfo),
237 snd_seq_port_info_get_name(pinfo));
238 }
239
print_port_and_subs(snd_seq_t * seq,snd_seq_client_info_t * cinfo,snd_seq_port_info_t * pinfo,int count)240 static void print_port_and_subs(snd_seq_t *seq, snd_seq_client_info_t *cinfo,
241 snd_seq_port_info_t *pinfo, int count)
242 {
243 print_port(seq, cinfo, pinfo, count);
244 list_subscribers(seq, snd_seq_port_info_get_addr(pinfo));
245 }
246
247
248 /*
249 * remove all (exported) connections
250 */
remove_connection(snd_seq_t * seq,snd_seq_client_info_t * info ATTRIBUTE_UNUSED,snd_seq_port_info_t * pinfo,int count ATTRIBUTE_UNUSED)251 static void remove_connection(snd_seq_t *seq,
252 snd_seq_client_info_t *info ATTRIBUTE_UNUSED,
253 snd_seq_port_info_t *pinfo,
254 int count ATTRIBUTE_UNUSED)
255 {
256 snd_seq_query_subscribe_t *query;
257 snd_seq_port_info_t *port;
258 snd_seq_port_subscribe_t *subs;
259
260 snd_seq_query_subscribe_alloca(&query);
261 snd_seq_query_subscribe_set_root(query, snd_seq_port_info_get_addr(pinfo));
262 snd_seq_query_subscribe_set_type(query, SND_SEQ_QUERY_SUBS_READ);
263 snd_seq_query_subscribe_set_index(query, 0);
264
265 snd_seq_port_info_alloca(&port);
266 snd_seq_port_subscribe_alloca(&subs);
267
268 while (snd_seq_query_port_subscribers(seq, query) >= 0) {
269 const snd_seq_addr_t *sender = snd_seq_query_subscribe_get_root(query);
270 const snd_seq_addr_t *dest = snd_seq_query_subscribe_get_addr(query);
271
272 if (snd_seq_get_any_port_info(seq, dest->client, dest->port, port) < 0 ||
273 !(snd_seq_port_info_get_capability(port) & SND_SEQ_PORT_CAP_SUBS_WRITE) ||
274 (snd_seq_port_info_get_capability(port) & SND_SEQ_PORT_CAP_NO_EXPORT)) {
275 snd_seq_query_subscribe_set_index(query, snd_seq_query_subscribe_get_index(query) + 1);
276 continue;
277 }
278 snd_seq_port_subscribe_set_queue(subs, snd_seq_query_subscribe_get_queue(query));
279 snd_seq_port_subscribe_set_sender(subs, sender);
280 snd_seq_port_subscribe_set_dest(subs, dest);
281 if (snd_seq_unsubscribe_port(seq, subs) < 0) {
282 snd_seq_query_subscribe_set_index(query, snd_seq_query_subscribe_get_index(query) + 1);
283 }
284 }
285 }
286
remove_all_connections(snd_seq_t * seq)287 static void remove_all_connections(snd_seq_t *seq)
288 {
289 do_search_port(seq, 0, remove_connection);
290 }
291
292
293 /*
294 * main..
295 */
296
297 enum {
298 SUBSCRIBE, UNSUBSCRIBE, LIST, REMOVE_ALL
299 };
300
301 #ifdef HANDLE_SHOW_ALL
302 #define ACONNECT_OPTS "dior:t:elxa"
303 #else
304 #define ACONNECT_OPTS "dior:t:elx"
305 #endif
306
307 static const struct option long_option[] = {
308 {"disconnect", 0, NULL, 'd'},
309 {"input", 0, NULL, 'i'},
310 {"output", 0, NULL, 'o'},
311 {"real", 1, NULL, 'r'},
312 {"tick", 1, NULL, 't'},
313 {"exclusive", 0, NULL, 'e'},
314 {"list", 0, NULL, 'l'},
315 {"removeall", 0, NULL, 'x'},
316 #ifdef HANDLE_SHOW_ALL
317 {"all", 0, NULL, 'a'},
318 #endif
319 {NULL, 0, NULL, 0},
320 };
321
main(int argc,char ** argv)322 int main(int argc, char **argv)
323 {
324 int c;
325 snd_seq_t *seq;
326 int queue = 0, convert_time = 0, convert_real = 0, exclusive = 0;
327 int command = SUBSCRIBE;
328 int list_perm = 0;
329 int client;
330 int list_subs = 0;
331 snd_seq_port_subscribe_t *subs;
332 snd_seq_addr_t sender, dest;
333
334 #ifdef ENABLE_NLS
335 setlocale(LC_ALL, "");
336 textdomain(PACKAGE);
337 #endif
338
339 while ((c = getopt_long(argc, argv, ACONNECT_OPTS, long_option, NULL)) != -1) {
340 switch (c) {
341 case 'd':
342 command = UNSUBSCRIBE;
343 break;
344 case 'i':
345 command = LIST;
346 list_perm |= LIST_INPUT;
347 break;
348 case 'o':
349 command = LIST;
350 list_perm |= LIST_OUTPUT;
351 break;
352 case 'e':
353 exclusive = 1;
354 break;
355 case 'r':
356 queue = atoi(optarg);
357 convert_time = 1;
358 convert_real = 1;
359 break;
360 case 't':
361 queue = atoi(optarg);
362 convert_time = 1;
363 convert_real = 0;
364 break;
365 case 'l':
366 command = LIST;
367 list_subs = 1;
368 break;
369 case 'x':
370 command = REMOVE_ALL;
371 break;
372 #ifdef HANDLE_SHOW_ALL
373 case 'a':
374 command = LIST;
375 show_all = 1;
376 break;
377 #endif
378 default:
379 usage();
380 exit(1);
381 }
382 }
383
384 if (snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
385 fprintf(stderr, _("can't open sequencer\n"));
386 return 1;
387 }
388
389 snd_lib_error_set_handler(error_handler);
390
391 switch (command) {
392 case LIST:
393 do_search_port(seq, list_perm,
394 list_subs ? print_port_and_subs : print_port);
395 snd_seq_close(seq);
396 return 0;
397 case REMOVE_ALL:
398 remove_all_connections(seq);
399 snd_seq_close(seq);
400 return 0;
401 }
402
403 /* connection or disconnection */
404
405 if (optind + 2 > argc) {
406 snd_seq_close(seq);
407 usage();
408 exit(1);
409 }
410
411 if ((client = snd_seq_client_id(seq)) < 0) {
412 snd_seq_close(seq);
413 fprintf(stderr, _("can't get client id\n"));
414 return 1;
415 }
416
417 /* set client info */
418 if (snd_seq_set_client_name(seq, "ALSA Connector") < 0) {
419 snd_seq_close(seq);
420 fprintf(stderr, _("can't set client info\n"));
421 return 1;
422 }
423
424 /* set subscription */
425 if (snd_seq_parse_address(seq, &sender, argv[optind]) < 0) {
426 snd_seq_close(seq);
427 fprintf(stderr, _("invalid sender address %s\n"), argv[optind]);
428 return 1;
429 }
430 if (snd_seq_parse_address(seq, &dest, argv[optind + 1]) < 0) {
431 snd_seq_close(seq);
432 fprintf(stderr, _("invalid destination address %s\n"), argv[optind + 1]);
433 return 1;
434 }
435 snd_seq_port_subscribe_alloca(&subs);
436 snd_seq_port_subscribe_set_sender(subs, &sender);
437 snd_seq_port_subscribe_set_dest(subs, &dest);
438 snd_seq_port_subscribe_set_queue(subs, queue);
439 snd_seq_port_subscribe_set_exclusive(subs, exclusive);
440 snd_seq_port_subscribe_set_time_update(subs, convert_time);
441 snd_seq_port_subscribe_set_time_real(subs, convert_real);
442
443 if (command == UNSUBSCRIBE) {
444 if (snd_seq_get_port_subscription(seq, subs) < 0) {
445 snd_seq_close(seq);
446 fprintf(stderr, _("No subscription is found\n"));
447 return 1;
448 }
449 if (snd_seq_unsubscribe_port(seq, subs) < 0) {
450 snd_seq_close(seq);
451 fprintf(stderr, _("Disconnection failed (%s)\n"), snd_strerror(errno));
452 return 1;
453 }
454 } else {
455 if (snd_seq_get_port_subscription(seq, subs) == 0) {
456 snd_seq_close(seq);
457 fprintf(stderr, _("Connection is already subscribed\n"));
458 return 1;
459 }
460 if (snd_seq_subscribe_port(seq, subs) < 0) {
461 snd_seq_close(seq);
462 fprintf(stderr, _("Connection failed (%s)\n"), snd_strerror(errno));
463 return 1;
464 }
465 }
466
467 snd_seq_close(seq);
468
469 return 0;
470 }
471