1/*
2 *  Sequencer Interface - main file
3 *  Copyright (c) 2000 by Jaroslav Kysela <perex@perex.cz>
4 *                        Abramo Bagnara <abramo@alsa-project.org>
5 *
6 *
7 *   This library is free software; you can redistribute it and/or modify
8 *   it under the terms of the GNU Lesser General Public License as
9 *   published by the Free Software Foundation; either version 2.1 of
10 *   the License, or (at your option) any later version.
11 *
12 *   This program is distributed in the hope that it will be useful,
13 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 *   GNU Lesser General Public License for more details.
16 *
17 *   You should have received a copy of the GNU Lesser General Public
18 *   License along with this library; if not, write to the Free Software
19 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
20 *
21 */
22
23#include <fcntl.h>
24#include <sys/ioctl.h>
25#include "seq_local.h"
26
27#ifndef PIC
28/* entry for static linking */
29const char *_snd_module_seq_hw = "";
30#endif
31
32#ifndef DOC_HIDDEN
33#define SNDRV_FILE_SEQ		ALSA_DEVICE_DIRECTORY "seq"
34#define SNDRV_FILE_ALOADSEQ	ALOAD_DEVICE_DIRECTORY "aloadSEQ"
35#define SNDRV_SEQ_VERSION_MAX	SNDRV_PROTOCOL_VERSION(1, 0, 1)
36
37typedef struct {
38	int fd;
39} snd_seq_hw_t;
40#endif /* DOC_HIDDEN */
41
42static int snd_seq_hw_close(snd_seq_t *seq)
43{
44	snd_seq_hw_t *hw = seq->private_data;
45	int err = 0;
46
47	if (close(hw->fd)) {
48		err = -errno;
49		SYSERR("close failed\n");
50	}
51	free(hw);
52	return err;
53}
54
55static int snd_seq_hw_nonblock(snd_seq_t *seq, int nonblock)
56{
57	snd_seq_hw_t *hw = seq->private_data;
58	long flags;
59
60	if ((flags = fcntl(hw->fd, F_GETFL)) < 0) {
61		SYSERR("F_GETFL failed");
62		return -errno;
63	}
64	if (nonblock)
65		flags |= O_NONBLOCK;
66	else
67		flags &= ~O_NONBLOCK;
68	if (fcntl(hw->fd, F_SETFL, flags) < 0) {
69		SYSERR("F_SETFL for O_NONBLOCK failed");
70		return -errno;
71	}
72	return 0;
73}
74
75static int snd_seq_hw_client_id(snd_seq_t *seq)
76{
77	snd_seq_hw_t *hw = seq->private_data;
78	int client;
79	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_CLIENT_ID, &client) < 0) {
80		SYSERR("SNDRV_SEQ_IOCTL_CLIENT_ID failed");
81		return -errno;
82	}
83	return client;
84}
85
86static int snd_seq_hw_system_info(snd_seq_t *seq, snd_seq_system_info_t * info)
87{
88	snd_seq_hw_t *hw = seq->private_data;
89	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SYSTEM_INFO, info) < 0) {
90		SYSERR("SNDRV_SEQ_IOCTL_SYSTEM_INFO failed");
91		return -errno;
92	}
93	return 0;
94}
95
96static int snd_seq_hw_get_client_info(snd_seq_t *seq, snd_seq_client_info_t * info)
97{
98	snd_seq_hw_t *hw = seq->private_data;
99	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, info) < 0) {
100		/*SYSERR("SNDRV_SEQ_IOCTL_GET_CLIENT_INFO failed");*/
101		return -errno;
102	}
103	return 0;
104}
105
106static int snd_seq_hw_set_client_info(snd_seq_t *seq, snd_seq_client_info_t * info)
107{
108	snd_seq_hw_t *hw = seq->private_data;
109	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_CLIENT_INFO, info) < 0) {
110		/*SYSERR("SNDRV_SEQ_IOCTL_SET_CLIENT_INFO failed");*/
111		return -errno;
112	}
113	return 0;
114}
115
116static int snd_seq_hw_create_port(snd_seq_t *seq, snd_seq_port_info_t * port)
117{
118	snd_seq_hw_t *hw = seq->private_data;
119	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_CREATE_PORT, port) < 0) {
120		/*SYSERR("SNDRV_SEQ_IOCTL_CREATE_PORT failed");*/
121		return -errno;
122	}
123	return 0;
124}
125
126static int snd_seq_hw_delete_port(snd_seq_t *seq, snd_seq_port_info_t * port)
127{
128	snd_seq_hw_t *hw = seq->private_data;
129	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_DELETE_PORT, port) < 0) {
130		/*SYSERR("SNDRV_SEQ_IOCTL_DELETE_PORT failed");*/
131		return -errno;
132	}
133	return 0;
134}
135
136static int snd_seq_hw_get_port_info(snd_seq_t *seq, snd_seq_port_info_t * info)
137{
138	snd_seq_hw_t *hw = seq->private_data;
139	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_PORT_INFO, info) < 0) {
140		/*SYSERR("SNDRV_SEQ_IOCTL_GET_PORT_INFO failed");*/
141		return -errno;
142	}
143	return 0;
144}
145
146static int snd_seq_hw_set_port_info(snd_seq_t *seq, snd_seq_port_info_t * info)
147{
148	snd_seq_hw_t *hw = seq->private_data;
149	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_PORT_INFO, info) < 0) {
150		/*SYSERR("SNDRV_SEQ_IOCTL_SET_PORT_INFO failed");*/
151		return -errno;
152	}
153	return 0;
154}
155
156static int snd_seq_hw_get_port_subscription(snd_seq_t *seq, snd_seq_port_subscribe_t * sub)
157{
158	snd_seq_hw_t *hw = seq->private_data;
159	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION, sub) < 0) {
160		/*SYSERR("SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION failed");*/
161		return -errno;
162	}
163	return 0;
164}
165
166static int snd_seq_hw_subscribe_port(snd_seq_t *seq, snd_seq_port_subscribe_t * sub)
167{
168	snd_seq_hw_t *hw = seq->private_data;
169	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, sub) < 0) {
170		/*SYSERR("SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT failed");*/
171		return -errno;
172	}
173	return 0;
174}
175
176static int snd_seq_hw_unsubscribe_port(snd_seq_t *seq, snd_seq_port_subscribe_t * sub)
177{
178	snd_seq_hw_t *hw = seq->private_data;
179	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT, sub) < 0) {
180		/*SYSERR("SNDRV_SEQ_IOCTL_UNSUBSCRIBE_PORT failed");*/
181		return -errno;
182	}
183	return 0;
184}
185
186static int snd_seq_hw_query_port_subscribers(snd_seq_t *seq, snd_seq_query_subscribe_t * subs)
187{
188	snd_seq_hw_t *hw = seq->private_data;
189	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_QUERY_SUBS, subs) < 0) {
190		/*SYSERR("SNDRV_SEQ_IOCTL_QUERY_SUBS failed");*/
191		return -errno;
192	}
193	return 0;
194}
195
196static int snd_seq_hw_get_queue_status(snd_seq_t *seq, snd_seq_queue_status_t * status)
197{
198	snd_seq_hw_t *hw = seq->private_data;
199	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS, status) < 0) {
200		/*SYSERR("SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS failed");*/
201		return -errno;
202	}
203	return 0;
204}
205
206static int snd_seq_hw_get_queue_tempo(snd_seq_t *seq, snd_seq_queue_tempo_t * tempo)
207{
208	snd_seq_hw_t *hw = seq->private_data;
209	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO, tempo) < 0) {
210		/*SYSERR("SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO failed");*/
211		return -errno;
212	}
213	return 0;
214}
215
216static int snd_seq_hw_set_queue_tempo(snd_seq_t *seq, snd_seq_queue_tempo_t * tempo)
217{
218	snd_seq_hw_t *hw = seq->private_data;
219	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO, tempo) < 0) {
220		/*SYSERR("SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO failed");*/
221		return -errno;
222	}
223	return 0;
224}
225
226static int snd_seq_hw_get_queue_timer(snd_seq_t *seq, snd_seq_queue_timer_t * timer)
227{
228	snd_seq_hw_t *hw = seq->private_data;
229	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER, timer) < 0) {
230		/*SYSERR("SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER failed");*/
231		return -errno;
232	}
233	return 0;
234}
235
236static int snd_seq_hw_set_queue_timer(snd_seq_t *seq, snd_seq_queue_timer_t * timer)
237{
238	snd_seq_hw_t *hw = seq->private_data;
239	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER, timer) < 0) {
240		/*SYSERR("SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER failed");*/
241		return -errno;
242	}
243	return 0;
244}
245
246static int snd_seq_hw_get_queue_client(snd_seq_t *seq, snd_seq_queue_client_t * info)
247{
248	snd_seq_hw_t *hw = seq->private_data;
249	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT, info) < 0) {
250		/*SYSERR("SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT failed");*/
251		return -errno;
252	}
253	return 0;
254}
255
256static int snd_seq_hw_set_queue_client(snd_seq_t *seq, snd_seq_queue_client_t * info)
257{
258	snd_seq_hw_t *hw = seq->private_data;
259	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT, info) < 0) {
260		/*SYSERR("SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT failed");*/
261		return -errno;
262	}
263	return 0;
264}
265
266static int snd_seq_hw_create_queue(snd_seq_t *seq, snd_seq_queue_info_t *info)
267{
268	snd_seq_hw_t *hw = seq->private_data;
269	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_CREATE_QUEUE, info) < 0) {
270		/*SYSERR("SNDRV_SEQ_IOCTL_CREATE_QUEUE failed");*/
271		return -errno;
272	}
273	return 0;
274}
275
276static int snd_seq_hw_delete_queue(snd_seq_t *seq, snd_seq_queue_info_t *info)
277{
278	snd_seq_hw_t *hw = seq->private_data;
279	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_DELETE_QUEUE, info) < 0) {
280		/*SYSERR("SNDRV_SEQ_IOCTL_DELETE_QUEUE failed");*/
281		return -errno;
282	}
283	return 0;
284}
285
286static int snd_seq_hw_get_queue_info(snd_seq_t *seq, snd_seq_queue_info_t *info)
287{
288	snd_seq_hw_t *hw = seq->private_data;
289	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_QUEUE_INFO, info) < 0) {
290		/*SYSERR("SNDRV_SEQ_IOCTL_GET_QUEUE_INFO failed");*/
291		return -errno;
292	}
293	return 0;
294}
295
296static int snd_seq_hw_set_queue_info(snd_seq_t *seq, snd_seq_queue_info_t *info)
297{
298	snd_seq_hw_t *hw = seq->private_data;
299	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_QUEUE_INFO, info) < 0) {
300		/*SYSERR("SNDRV_SEQ_IOCTL_SET_QUEUE_INFO failed");*/
301		return -errno;
302	}
303	return 0;
304}
305
306static int snd_seq_hw_get_named_queue(snd_seq_t *seq, snd_seq_queue_info_t *info)
307{
308	snd_seq_hw_t *hw = seq->private_data;
309	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE, info) < 0) {
310		/*SYSERR("SNDRV_SEQ_IOCTL_GET_NAMED_QUEUE failed");*/
311		return -errno;
312	}
313	return 0;
314}
315
316static ssize_t snd_seq_hw_write(snd_seq_t *seq, void *buf, size_t len)
317{
318	snd_seq_hw_t *hw = seq->private_data;
319	ssize_t result = write(hw->fd, buf, len);
320	if (result < 0)
321		return -errno;
322	return result;
323}
324
325static ssize_t snd_seq_hw_read(snd_seq_t *seq, void *buf, size_t len)
326{
327	snd_seq_hw_t *hw = seq->private_data;
328	ssize_t result = read(hw->fd, buf, len);
329	if (result < 0)
330		return -errno;
331	return result;
332}
333
334static int snd_seq_hw_remove_events(snd_seq_t *seq, snd_seq_remove_events_t *rmp)
335{
336	snd_seq_hw_t *hw = seq->private_data;
337	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_REMOVE_EVENTS, rmp) < 0) {
338		/*SYSERR("SNDRV_SEQ_IOCTL_REMOVE_EVENTS failed");*/
339		return -errno;
340	}
341	return 0;
342}
343
344static int snd_seq_hw_get_client_pool(snd_seq_t *seq, snd_seq_client_pool_t *info)
345{
346	snd_seq_hw_t *hw = seq->private_data;
347	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_GET_CLIENT_POOL, info) < 0) {
348		/*SYSERR("SNDRV_SEQ_IOCTL_GET_CLIENT_POOL failed");*/
349		return -errno;
350	}
351	return 0;
352}
353
354static int snd_seq_hw_set_client_pool(snd_seq_t *seq, snd_seq_client_pool_t *info)
355{
356	snd_seq_hw_t *hw = seq->private_data;
357	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_SET_CLIENT_POOL, info) < 0) {
358		/*SYSERR("SNDRV_SEQ_IOCTL_SET_CLIENT_POOL failed");*/
359		return -errno;
360	}
361	return 0;
362}
363
364static int snd_seq_hw_query_next_client(snd_seq_t *seq, snd_seq_client_info_t *info)
365{
366	snd_seq_hw_t *hw = seq->private_data;
367	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT, info) < 0) {
368		/*SYSERR("SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT failed");*/
369		return -errno;
370	}
371	return 0;
372}
373
374static int snd_seq_hw_query_next_port(snd_seq_t *seq, snd_seq_port_info_t *info)
375{
376	snd_seq_hw_t *hw = seq->private_data;
377	if (ioctl(hw->fd, SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT, info) < 0) {
378		/*SYSERR("SNDRV_SEQ_IOCTL_QUERY_NEXT_PORT failed");*/
379		return -errno;
380	}
381	return 0;
382}
383
384static const snd_seq_ops_t snd_seq_hw_ops = {
385	.close = snd_seq_hw_close,
386	.nonblock = snd_seq_hw_nonblock,
387	.system_info = snd_seq_hw_system_info,
388	.get_client_info = snd_seq_hw_get_client_info,
389	.set_client_info = snd_seq_hw_set_client_info,
390	.create_port = snd_seq_hw_create_port,
391	.delete_port = snd_seq_hw_delete_port,
392	.get_port_info = snd_seq_hw_get_port_info,
393	.set_port_info = snd_seq_hw_set_port_info,
394	.get_port_subscription = snd_seq_hw_get_port_subscription,
395	.subscribe_port = snd_seq_hw_subscribe_port,
396	.unsubscribe_port = snd_seq_hw_unsubscribe_port,
397	.query_port_subscribers = snd_seq_hw_query_port_subscribers,
398	.get_queue_status = snd_seq_hw_get_queue_status,
399	.get_queue_tempo = snd_seq_hw_get_queue_tempo,
400	.set_queue_tempo = snd_seq_hw_set_queue_tempo,
401	.get_queue_timer = snd_seq_hw_get_queue_timer,
402	.set_queue_timer = snd_seq_hw_set_queue_timer,
403	.get_queue_client = snd_seq_hw_get_queue_client,
404	.set_queue_client = snd_seq_hw_set_queue_client,
405	.create_queue = snd_seq_hw_create_queue,
406	.delete_queue = snd_seq_hw_delete_queue,
407	.get_queue_info = snd_seq_hw_get_queue_info,
408	.set_queue_info = snd_seq_hw_set_queue_info,
409	.get_named_queue = snd_seq_hw_get_named_queue,
410	.write = snd_seq_hw_write,
411	.read = snd_seq_hw_read,
412	.remove_events = snd_seq_hw_remove_events,
413	.get_client_pool = snd_seq_hw_get_client_pool,
414	.set_client_pool = snd_seq_hw_set_client_pool,
415	.query_next_client = snd_seq_hw_query_next_client,
416	.query_next_port = snd_seq_hw_query_next_port,
417};
418
419int snd_seq_hw_open(snd_seq_t **handle, const char *name, int streams, int mode)
420{
421	int fd, ver, client, fmode, ret;
422	const char *filename;
423	snd_seq_t *seq;
424	snd_seq_hw_t *hw;
425
426	*handle = NULL;
427
428	switch (streams) {
429	case SND_SEQ_OPEN_OUTPUT:
430		fmode = O_WRONLY;
431		break;
432	case SND_SEQ_OPEN_INPUT:
433		fmode = O_RDONLY;
434		break;
435	case SND_SEQ_OPEN_DUPLEX:
436		fmode = O_RDWR;
437		break;
438	default:
439		assert(0);
440		return -EINVAL;
441	}
442
443	if (mode & SND_SEQ_NONBLOCK)
444		fmode |= O_NONBLOCK;
445
446	filename = SNDRV_FILE_SEQ;
447	fd = snd_open_device(filename, fmode);
448#ifdef SUPPORT_ALOAD
449	if (fd < 0) {
450		fd = snd_open_device(SNDRV_FILE_ALOADSEQ, fmode);
451		if (fd >= 0)
452			close(fd);
453		fd = snd_open_device(filename, fmode);
454	}
455#endif
456	if (fd < 0) {
457		SYSERR("open %s failed", filename);
458		return -errno;
459	}
460	if (ioctl(fd, SNDRV_SEQ_IOCTL_PVERSION, &ver) < 0) {
461		SYSERR("SNDRV_SEQ_IOCTL_PVERSION failed");
462		ret = -errno;
463		close(fd);
464		return ret;
465	}
466	if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_SEQ_VERSION_MAX)) {
467		close(fd);
468		return -SND_ERROR_INCOMPATIBLE_VERSION;
469	}
470	hw = calloc(1, sizeof(snd_seq_hw_t));
471	if (hw == NULL) {
472		close(fd);
473		return -ENOMEM;
474	}
475
476	seq = calloc(1, sizeof(snd_seq_t));
477	if (seq == NULL) {
478		free(hw);
479		close(fd);
480		return -ENOMEM;
481	}
482	hw->fd = fd;
483	if (streams & SND_SEQ_OPEN_OUTPUT) {
484		seq->obuf = (char *) malloc(seq->obufsize = SND_SEQ_OBUF_SIZE);
485		if (!seq->obuf) {
486			free(hw);
487			free(seq);
488			close(fd);
489			return -ENOMEM;
490		}
491	}
492	if (streams & SND_SEQ_OPEN_INPUT) {
493		seq->ibuf = (snd_seq_event_t *) calloc(sizeof(snd_seq_event_t), seq->ibufsize = SND_SEQ_IBUF_SIZE);
494		if (!seq->ibuf) {
495			free(seq->obuf);
496			free(hw);
497			free(seq);
498			close(fd);
499			return -ENOMEM;
500		}
501	}
502	if (name)
503		seq->name = strdup(name);
504	seq->type = SND_SEQ_TYPE_HW;
505	seq->streams = streams;
506	seq->mode = mode;
507	seq->tmpbuf = NULL;
508	seq->tmpbufsize = 0;
509	seq->poll_fd = fd;
510	seq->ops = &snd_seq_hw_ops;
511	seq->private_data = hw;
512	client = snd_seq_hw_client_id(seq);
513	if (client < 0) {
514		snd_seq_close(seq);
515		return client;
516	} else
517		seq->client = client;
518
519#ifdef SNDRV_SEQ_IOCTL_RUNNING_MODE
520	{
521		struct sndrv_seq_running_info run_mode;
522		/* check running mode */
523		memset(&run_mode, 0, sizeof(run_mode));
524		run_mode.client = client;
525#ifdef SNDRV_BIG_ENDIAN
526		run_mode.big_endian = 1;
527#else
528		run_mode.big_endian = 0;
529#endif
530		run_mode.cpu_mode = sizeof(long);
531		ioctl(fd, SNDRV_SEQ_IOCTL_RUNNING_MODE, &run_mode);
532	}
533#endif
534
535	*handle = seq;
536	return 0;
537}
538
539int _snd_seq_hw_open(snd_seq_t **handlep, char *name,
540		     snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf,
541		     int streams, int mode)
542{
543	snd_config_iterator_t i, next;
544	snd_config_for_each(i, next, conf) {
545		snd_config_t *n = snd_config_iterator_entry(i);
546		const char *id;
547		if (snd_config_get_id(n, &id) < 0)
548			continue;
549		if (strcmp(id, "comment") == 0)
550			continue;
551		if (strcmp(id, "type") == 0)
552			continue;
553		return -EINVAL;
554	}
555	return snd_seq_hw_open(handlep, name, streams, mode);
556}
557SND_DLSYM_BUILD_VERSION(_snd_seq_hw_open, SND_SEQ_DLSYM_VERSION);
558