• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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