1d5ac70f0Sopenharmony_ci/*
2d5ac70f0Sopenharmony_ci *  RawMIDI - Virtual (sequencer mode)
3d5ac70f0Sopenharmony_ci *  Copyright (c) 2003 by Takashi Iwai <tiwai@suse.de>
4d5ac70f0Sopenharmony_ci *
5d5ac70f0Sopenharmony_ci *
6d5ac70f0Sopenharmony_ci *   This library is free software; you can redistribute it and/or modify
7d5ac70f0Sopenharmony_ci *   it under the terms of the GNU Lesser General Public License as
8d5ac70f0Sopenharmony_ci *   published by the Free Software Foundation; either version 2.1 of
9d5ac70f0Sopenharmony_ci *   the License, or (at your option) any later version.
10d5ac70f0Sopenharmony_ci *
11d5ac70f0Sopenharmony_ci *   This program is distributed in the hope that it will be useful,
12d5ac70f0Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13d5ac70f0Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14d5ac70f0Sopenharmony_ci *   GNU Lesser General Public License for more details.
15d5ac70f0Sopenharmony_ci *
16d5ac70f0Sopenharmony_ci *   You should have received a copy of the GNU Lesser General Public
17d5ac70f0Sopenharmony_ci *   License along with this library; if not, write to the Free Software
18d5ac70f0Sopenharmony_ci *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19d5ac70f0Sopenharmony_ci *
20d5ac70f0Sopenharmony_ci */
21d5ac70f0Sopenharmony_ci
22d5ac70f0Sopenharmony_ci#include "rawmidi_local.h"
23d5ac70f0Sopenharmony_ci#include <unistd.h>
24d5ac70f0Sopenharmony_ci#include <string.h>
25d5ac70f0Sopenharmony_ci#include <fcntl.h>
26d5ac70f0Sopenharmony_ci#include <sys/ioctl.h>
27d5ac70f0Sopenharmony_ci#include "seq.h"
28d5ac70f0Sopenharmony_ci#include "seq_midi_event.h"
29d5ac70f0Sopenharmony_ci
30d5ac70f0Sopenharmony_ci#ifndef PIC
31d5ac70f0Sopenharmony_ci/* entry for static linking */
32d5ac70f0Sopenharmony_ciconst char *_snd_module_rawmidi_virt = "";
33d5ac70f0Sopenharmony_ci#endif
34d5ac70f0Sopenharmony_ci
35d5ac70f0Sopenharmony_ci
36d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
37d5ac70f0Sopenharmony_citypedef struct {
38d5ac70f0Sopenharmony_ci	int open;
39d5ac70f0Sopenharmony_ci
40d5ac70f0Sopenharmony_ci	snd_seq_t *handle;
41d5ac70f0Sopenharmony_ci	int port;
42d5ac70f0Sopenharmony_ci
43d5ac70f0Sopenharmony_ci	snd_midi_event_t *midi_event;
44d5ac70f0Sopenharmony_ci
45d5ac70f0Sopenharmony_ci	snd_seq_event_t *in_event;
46d5ac70f0Sopenharmony_ci	int in_buf_size;
47d5ac70f0Sopenharmony_ci	int in_buf_ofs;
48d5ac70f0Sopenharmony_ci	char *in_buf_ptr;
49d5ac70f0Sopenharmony_ci	char in_tmp_buf[16];
50d5ac70f0Sopenharmony_ci
51d5ac70f0Sopenharmony_ci	snd_seq_event_t out_event;
52d5ac70f0Sopenharmony_ci	int pending;
53d5ac70f0Sopenharmony_ci} snd_rawmidi_virtual_t;
54d5ac70f0Sopenharmony_ci
55d5ac70f0Sopenharmony_ciint _snd_seq_open_lconf(snd_seq_t **seqp, const char *name,
56d5ac70f0Sopenharmony_ci			int streams, int mode, snd_config_t *lconf,
57d5ac70f0Sopenharmony_ci			snd_config_t *parent_conf);
58d5ac70f0Sopenharmony_ci#endif
59d5ac70f0Sopenharmony_ci
60d5ac70f0Sopenharmony_cistatic int snd_rawmidi_virtual_close(snd_rawmidi_t *rmidi)
61d5ac70f0Sopenharmony_ci{
62d5ac70f0Sopenharmony_ci	snd_rawmidi_virtual_t *virt = rmidi->private_data;
63d5ac70f0Sopenharmony_ci	virt->open--;
64d5ac70f0Sopenharmony_ci	if (virt->open)
65d5ac70f0Sopenharmony_ci		return 0;
66d5ac70f0Sopenharmony_ci	snd_seq_close(virt->handle);
67d5ac70f0Sopenharmony_ci	if (virt->midi_event)
68d5ac70f0Sopenharmony_ci		snd_midi_event_free(virt->midi_event);
69d5ac70f0Sopenharmony_ci	free(virt);
70d5ac70f0Sopenharmony_ci	return 0;
71d5ac70f0Sopenharmony_ci}
72d5ac70f0Sopenharmony_ci
73d5ac70f0Sopenharmony_cistatic int snd_rawmidi_virtual_nonblock(snd_rawmidi_t *rmidi, int nonblock)
74d5ac70f0Sopenharmony_ci{
75d5ac70f0Sopenharmony_ci	snd_rawmidi_virtual_t *virt = rmidi->private_data;
76d5ac70f0Sopenharmony_ci
77d5ac70f0Sopenharmony_ci	return snd_seq_nonblock(virt->handle, nonblock);
78d5ac70f0Sopenharmony_ci}
79d5ac70f0Sopenharmony_ci
80d5ac70f0Sopenharmony_cistatic int snd_rawmidi_virtual_info(snd_rawmidi_t *rmidi, snd_rawmidi_info_t * info)
81d5ac70f0Sopenharmony_ci{
82d5ac70f0Sopenharmony_ci	// snd_rawmidi_virtual_t *virt = rmidi->private_data;
83d5ac70f0Sopenharmony_ci
84d5ac70f0Sopenharmony_ci	info->stream = rmidi->stream;
85d5ac70f0Sopenharmony_ci	/* FIXME: what values should be there? */
86d5ac70f0Sopenharmony_ci	info->card = 0;
87d5ac70f0Sopenharmony_ci	info->device = 0;
88d5ac70f0Sopenharmony_ci	info->subdevice = 0;
89d5ac70f0Sopenharmony_ci	info->flags = 0;
90d5ac70f0Sopenharmony_ci	strcpy((char *)info->id, "Virtual");
91d5ac70f0Sopenharmony_ci	strcpy((char *)info->name, "Virtual RawMIDI");
92d5ac70f0Sopenharmony_ci	strcpy((char *)info->subname, "Virtual RawMIDI");
93d5ac70f0Sopenharmony_ci	info->subdevices_count = 1;
94d5ac70f0Sopenharmony_ci	info->subdevices_avail = 0;
95d5ac70f0Sopenharmony_ci	return 0;
96d5ac70f0Sopenharmony_ci}
97d5ac70f0Sopenharmony_ci
98d5ac70f0Sopenharmony_cistatic int snd_rawmidi_virtual_input_params(snd_rawmidi_virtual_t *virt, snd_rawmidi_params_t *params)
99d5ac70f0Sopenharmony_ci{
100d5ac70f0Sopenharmony_ci	int err;
101d5ac70f0Sopenharmony_ci
102d5ac70f0Sopenharmony_ci	// snd_rawmidi_drain_input(substream);
103d5ac70f0Sopenharmony_ci	if (params->buffer_size < sizeof(snd_seq_event_t) ||
104d5ac70f0Sopenharmony_ci	    params->buffer_size > 1024L * 1024L) {
105d5ac70f0Sopenharmony_ci		return -EINVAL;
106d5ac70f0Sopenharmony_ci	}
107d5ac70f0Sopenharmony_ci	if (params->buffer_size != snd_seq_get_input_buffer_size(virt->handle)) {
108d5ac70f0Sopenharmony_ci		err = snd_seq_set_input_buffer_size(virt->handle, params->buffer_size);
109d5ac70f0Sopenharmony_ci		if (err < 0)
110d5ac70f0Sopenharmony_ci			return err;
111d5ac70f0Sopenharmony_ci		params->buffer_size = snd_seq_get_input_buffer_size(virt->handle);
112d5ac70f0Sopenharmony_ci		/* FIXME: input pool size? */
113d5ac70f0Sopenharmony_ci	}
114d5ac70f0Sopenharmony_ci	return 0;
115d5ac70f0Sopenharmony_ci}
116d5ac70f0Sopenharmony_ci
117d5ac70f0Sopenharmony_ci
118d5ac70f0Sopenharmony_cistatic int snd_rawmidi_virtual_output_params(snd_rawmidi_virtual_t *virt, snd_rawmidi_params_t *params)
119d5ac70f0Sopenharmony_ci{
120d5ac70f0Sopenharmony_ci	int err;
121d5ac70f0Sopenharmony_ci
122d5ac70f0Sopenharmony_ci	// snd_rawmidi_drain_output(substream);
123d5ac70f0Sopenharmony_ci	if (params->buffer_size < sizeof(snd_seq_event_t) ||
124d5ac70f0Sopenharmony_ci	    params->buffer_size > 1024L * 1024L) {
125d5ac70f0Sopenharmony_ci		return -EINVAL;
126d5ac70f0Sopenharmony_ci	}
127d5ac70f0Sopenharmony_ci	if (params->buffer_size != snd_seq_get_output_buffer_size(virt->handle)) {
128d5ac70f0Sopenharmony_ci		err = snd_seq_set_output_buffer_size(virt->handle, params->buffer_size);
129d5ac70f0Sopenharmony_ci		if (err < 0)
130d5ac70f0Sopenharmony_ci			return err;
131d5ac70f0Sopenharmony_ci		params->buffer_size = snd_seq_get_output_buffer_size(virt->handle);
132d5ac70f0Sopenharmony_ci	}
133d5ac70f0Sopenharmony_ci	return 0;
134d5ac70f0Sopenharmony_ci}
135d5ac70f0Sopenharmony_ci
136d5ac70f0Sopenharmony_ci
137d5ac70f0Sopenharmony_cistatic int snd_rawmidi_virtual_params(snd_rawmidi_t *rmidi, snd_rawmidi_params_t * params)
138d5ac70f0Sopenharmony_ci{
139d5ac70f0Sopenharmony_ci	snd_rawmidi_virtual_t *virt = rmidi->private_data;
140d5ac70f0Sopenharmony_ci	params->stream = rmidi->stream;
141d5ac70f0Sopenharmony_ci
142d5ac70f0Sopenharmony_ci	if (rmidi->stream == SND_RAWMIDI_STREAM_INPUT)
143d5ac70f0Sopenharmony_ci		return snd_rawmidi_virtual_input_params(virt, params);
144d5ac70f0Sopenharmony_ci	else
145d5ac70f0Sopenharmony_ci		return snd_rawmidi_virtual_output_params(virt, params);
146d5ac70f0Sopenharmony_ci}
147d5ac70f0Sopenharmony_ci
148d5ac70f0Sopenharmony_cistatic int snd_rawmidi_virtual_status(snd_rawmidi_t *rmidi, snd_rawmidi_status_t * status)
149d5ac70f0Sopenharmony_ci{
150d5ac70f0Sopenharmony_ci	// snd_rawmidi_virtual_t *virt = rmidi->private_data;
151d5ac70f0Sopenharmony_ci	memset(status, 0, sizeof(*status));
152d5ac70f0Sopenharmony_ci	status->stream = rmidi->stream;
153d5ac70f0Sopenharmony_ci	return 0;
154d5ac70f0Sopenharmony_ci}
155d5ac70f0Sopenharmony_ci
156d5ac70f0Sopenharmony_cistatic int snd_rawmidi_virtual_drop(snd_rawmidi_t *rmidi)
157d5ac70f0Sopenharmony_ci{
158d5ac70f0Sopenharmony_ci	snd_rawmidi_virtual_t *virt = rmidi->private_data;
159d5ac70f0Sopenharmony_ci	if (rmidi->stream == SND_RAWMIDI_STREAM_OUTPUT) {
160d5ac70f0Sopenharmony_ci		snd_seq_drop_output(virt->handle);
161d5ac70f0Sopenharmony_ci		snd_midi_event_reset_encode(virt->midi_event);
162d5ac70f0Sopenharmony_ci		virt->pending = 0;
163d5ac70f0Sopenharmony_ci	} else {
164d5ac70f0Sopenharmony_ci		snd_seq_drop_input(virt->handle);
165d5ac70f0Sopenharmony_ci		snd_midi_event_reset_decode(virt->midi_event);
166d5ac70f0Sopenharmony_ci		virt->in_buf_ofs = 0;
167d5ac70f0Sopenharmony_ci	}
168d5ac70f0Sopenharmony_ci	return 0;
169d5ac70f0Sopenharmony_ci}
170d5ac70f0Sopenharmony_ci
171d5ac70f0Sopenharmony_cistatic int snd_rawmidi_virtual_drain(snd_rawmidi_t *rmidi)
172d5ac70f0Sopenharmony_ci{
173d5ac70f0Sopenharmony_ci	snd_rawmidi_virtual_t *virt = rmidi->private_data;
174d5ac70f0Sopenharmony_ci	int err;
175d5ac70f0Sopenharmony_ci
176d5ac70f0Sopenharmony_ci	if (rmidi->stream == SND_RAWMIDI_STREAM_OUTPUT) {
177d5ac70f0Sopenharmony_ci		if (virt->pending) {
178d5ac70f0Sopenharmony_ci			err = snd_seq_event_output(virt->handle, &virt->out_event);
179d5ac70f0Sopenharmony_ci			if (err < 0)
180d5ac70f0Sopenharmony_ci				return err;
181d5ac70f0Sopenharmony_ci			virt->pending = 0;
182d5ac70f0Sopenharmony_ci		}
183d5ac70f0Sopenharmony_ci		snd_seq_drain_output(virt->handle);
184d5ac70f0Sopenharmony_ci		snd_seq_sync_output_queue(virt->handle);
185d5ac70f0Sopenharmony_ci	}
186d5ac70f0Sopenharmony_ci	return snd_rawmidi_virtual_drop(rmidi);
187d5ac70f0Sopenharmony_ci}
188d5ac70f0Sopenharmony_ci
189d5ac70f0Sopenharmony_cistatic ssize_t snd_rawmidi_virtual_write(snd_rawmidi_t *rmidi, const void *buffer, size_t size)
190d5ac70f0Sopenharmony_ci{
191d5ac70f0Sopenharmony_ci	snd_rawmidi_virtual_t *virt = rmidi->private_data;
192d5ac70f0Sopenharmony_ci	ssize_t result = 0;
193d5ac70f0Sopenharmony_ci	ssize_t size1;
194d5ac70f0Sopenharmony_ci	int err;
195d5ac70f0Sopenharmony_ci
196d5ac70f0Sopenharmony_ci	if (virt->pending) {
197d5ac70f0Sopenharmony_ci		err = snd_seq_event_output(virt->handle, &virt->out_event);
198d5ac70f0Sopenharmony_ci		if (err < 0) {
199d5ac70f0Sopenharmony_ci			if (err != -EAGAIN)
200d5ac70f0Sopenharmony_ci				/* we got some fatal error. removing this event
201d5ac70f0Sopenharmony_ci				 * at the next time
202d5ac70f0Sopenharmony_ci				 */
203d5ac70f0Sopenharmony_ci				virt->pending = 0;
204d5ac70f0Sopenharmony_ci			return err;
205d5ac70f0Sopenharmony_ci		}
206d5ac70f0Sopenharmony_ci		virt->pending = 0;
207d5ac70f0Sopenharmony_ci	}
208d5ac70f0Sopenharmony_ci
209d5ac70f0Sopenharmony_ci	while (size > 0) {
210d5ac70f0Sopenharmony_ci		size1 = snd_midi_event_encode(virt->midi_event, buffer, size, &virt->out_event);
211d5ac70f0Sopenharmony_ci		if (size1 <= 0)
212d5ac70f0Sopenharmony_ci			break;
213d5ac70f0Sopenharmony_ci		size -= size1;
214d5ac70f0Sopenharmony_ci		result += size1;
215d5ac70f0Sopenharmony_ci		buffer += size1;
216d5ac70f0Sopenharmony_ci		if (virt->out_event.type == SND_SEQ_EVENT_NONE)
217d5ac70f0Sopenharmony_ci			continue;
218d5ac70f0Sopenharmony_ci		snd_seq_ev_set_subs(&virt->out_event);
219d5ac70f0Sopenharmony_ci		snd_seq_ev_set_source(&virt->out_event, virt->port);
220d5ac70f0Sopenharmony_ci		snd_seq_ev_set_direct(&virt->out_event);
221d5ac70f0Sopenharmony_ci		err = snd_seq_event_output(virt->handle, &virt->out_event);
222d5ac70f0Sopenharmony_ci		if (err < 0) {
223d5ac70f0Sopenharmony_ci			virt->pending = 1;
224d5ac70f0Sopenharmony_ci			return result > 0 ? result : err;
225d5ac70f0Sopenharmony_ci		}
226d5ac70f0Sopenharmony_ci	}
227d5ac70f0Sopenharmony_ci
228d5ac70f0Sopenharmony_ci	if (result > 0)
229d5ac70f0Sopenharmony_ci		snd_seq_drain_output(virt->handle);
230d5ac70f0Sopenharmony_ci
231d5ac70f0Sopenharmony_ci	return result;
232d5ac70f0Sopenharmony_ci}
233d5ac70f0Sopenharmony_ci
234d5ac70f0Sopenharmony_cistatic ssize_t snd_rawmidi_virtual_read(snd_rawmidi_t *rmidi, void *buffer, size_t size)
235d5ac70f0Sopenharmony_ci{
236d5ac70f0Sopenharmony_ci	snd_rawmidi_virtual_t *virt = rmidi->private_data;
237d5ac70f0Sopenharmony_ci	ssize_t result = 0;
238d5ac70f0Sopenharmony_ci	int size1, err;
239d5ac70f0Sopenharmony_ci
240d5ac70f0Sopenharmony_ci	while (size > 0) {
241d5ac70f0Sopenharmony_ci		if (! virt->in_buf_ofs) {
242d5ac70f0Sopenharmony_ci			err = snd_seq_event_input_pending(virt->handle, 1);
243d5ac70f0Sopenharmony_ci			if (err <= 0 && result > 0)
244d5ac70f0Sopenharmony_ci				return result;
245d5ac70f0Sopenharmony_ci			err = snd_seq_event_input(virt->handle, &virt->in_event);
246d5ac70f0Sopenharmony_ci			if (err < 0)
247d5ac70f0Sopenharmony_ci				return result > 0 ? result : err;
248d5ac70f0Sopenharmony_ci
249d5ac70f0Sopenharmony_ci			if (virt->in_event->type == SND_SEQ_EVENT_SYSEX) {
250d5ac70f0Sopenharmony_ci				virt->in_buf_ptr = virt->in_event->data.ext.ptr;
251d5ac70f0Sopenharmony_ci				virt->in_buf_size = virt->in_event->data.ext.len;
252d5ac70f0Sopenharmony_ci			} else {
253d5ac70f0Sopenharmony_ci				virt->in_buf_ptr = virt->in_tmp_buf;
254d5ac70f0Sopenharmony_ci				virt->in_buf_size = snd_midi_event_decode(virt->midi_event,
255d5ac70f0Sopenharmony_ci									  (unsigned char *)virt->in_tmp_buf,
256d5ac70f0Sopenharmony_ci									  sizeof(virt->in_tmp_buf),
257d5ac70f0Sopenharmony_ci									  virt->in_event);
258d5ac70f0Sopenharmony_ci			}
259d5ac70f0Sopenharmony_ci			if (virt->in_buf_size <= 0)
260d5ac70f0Sopenharmony_ci				continue;
261d5ac70f0Sopenharmony_ci		}
262d5ac70f0Sopenharmony_ci		size1 = virt->in_buf_size - virt->in_buf_ofs;
263d5ac70f0Sopenharmony_ci		if ((size_t)size1 > size) {
264d5ac70f0Sopenharmony_ci			memcpy(buffer, virt->in_buf_ptr + virt->in_buf_ofs, size);
265d5ac70f0Sopenharmony_ci			virt->in_buf_ofs += size;
266d5ac70f0Sopenharmony_ci			result += size;
267d5ac70f0Sopenharmony_ci			break;
268d5ac70f0Sopenharmony_ci		}
269d5ac70f0Sopenharmony_ci		memcpy(buffer, virt->in_buf_ptr + virt->in_buf_ofs, size1);
270d5ac70f0Sopenharmony_ci		size -= size1;
271d5ac70f0Sopenharmony_ci		result += size1;
272d5ac70f0Sopenharmony_ci		buffer += size1;
273d5ac70f0Sopenharmony_ci		virt->in_buf_ofs = 0;
274d5ac70f0Sopenharmony_ci	}
275d5ac70f0Sopenharmony_ci
276d5ac70f0Sopenharmony_ci	return result;
277d5ac70f0Sopenharmony_ci}
278d5ac70f0Sopenharmony_ci
279d5ac70f0Sopenharmony_cistatic const snd_rawmidi_ops_t snd_rawmidi_virtual_ops = {
280d5ac70f0Sopenharmony_ci	.close = snd_rawmidi_virtual_close,
281d5ac70f0Sopenharmony_ci	.nonblock = snd_rawmidi_virtual_nonblock,
282d5ac70f0Sopenharmony_ci	.info = snd_rawmidi_virtual_info,
283d5ac70f0Sopenharmony_ci	.params = snd_rawmidi_virtual_params,
284d5ac70f0Sopenharmony_ci	.status = snd_rawmidi_virtual_status,
285d5ac70f0Sopenharmony_ci	.drop = snd_rawmidi_virtual_drop,
286d5ac70f0Sopenharmony_ci	.drain = snd_rawmidi_virtual_drain,
287d5ac70f0Sopenharmony_ci	.write = snd_rawmidi_virtual_write,
288d5ac70f0Sopenharmony_ci	.read = snd_rawmidi_virtual_read,
289d5ac70f0Sopenharmony_ci};
290d5ac70f0Sopenharmony_ci
291d5ac70f0Sopenharmony_ci
292d5ac70f0Sopenharmony_ci/*! \page rawmidi RawMidi interface
293d5ac70f0Sopenharmony_ci
294d5ac70f0Sopenharmony_ci\section rawmidi_virt Virtual RawMidi interface
295d5ac70f0Sopenharmony_ci
296d5ac70f0Sopenharmony_ciThe "virtual" plugin creates a virtual RawMidi instance on the ALSA
297d5ac70f0Sopenharmony_cisequencer, which can be accessed through the connection of the sequencer
298d5ac70f0Sopenharmony_ciports.
299d5ac70f0Sopenharmony_ciThere is no connection established as default.
300d5ac70f0Sopenharmony_ci
301d5ac70f0Sopenharmony_ciFor creating a virtual RawMidi instance, pass "virtual" as its name at
302d5ac70f0Sopenharmony_cicreation.
303d5ac70f0Sopenharmony_ci
304d5ac70f0Sopenharmony_ciExample:
305d5ac70f0Sopenharmony_ci\code
306d5ac70f0Sopenharmony_cisnd_rawmidi_open(&read_handle, &write_handle, "virtual", 0);
307d5ac70f0Sopenharmony_ci\endcode
308d5ac70f0Sopenharmony_ci
309d5ac70f0Sopenharmony_ci*/
310d5ac70f0Sopenharmony_ci
311d5ac70f0Sopenharmony_ciint snd_rawmidi_virtual_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
312d5ac70f0Sopenharmony_ci			     const char *name, snd_seq_t *seq_handle, int port,
313d5ac70f0Sopenharmony_ci			     int merge, int mode)
314d5ac70f0Sopenharmony_ci{
315d5ac70f0Sopenharmony_ci	int err;
316d5ac70f0Sopenharmony_ci	snd_rawmidi_t *rmidi = NULL;
317d5ac70f0Sopenharmony_ci	snd_rawmidi_virtual_t *virt = NULL;
318d5ac70f0Sopenharmony_ci	struct pollfd pfd;
319d5ac70f0Sopenharmony_ci
320d5ac70f0Sopenharmony_ci	if (inputp)
321d5ac70f0Sopenharmony_ci		*inputp = 0;
322d5ac70f0Sopenharmony_ci	if (outputp)
323d5ac70f0Sopenharmony_ci		*outputp = 0;
324d5ac70f0Sopenharmony_ci
325d5ac70f0Sopenharmony_ci	virt = calloc(1, sizeof(*virt));
326d5ac70f0Sopenharmony_ci	if (virt == NULL) {
327d5ac70f0Sopenharmony_ci		err = -ENOMEM;
328d5ac70f0Sopenharmony_ci		goto _err;
329d5ac70f0Sopenharmony_ci	}
330d5ac70f0Sopenharmony_ci	virt->handle = seq_handle;
331d5ac70f0Sopenharmony_ci	virt->port = port;
332d5ac70f0Sopenharmony_ci	err = snd_midi_event_new(256, &virt->midi_event);
333d5ac70f0Sopenharmony_ci	if (err < 0)
334d5ac70f0Sopenharmony_ci		goto _err;
335d5ac70f0Sopenharmony_ci	snd_midi_event_init(virt->midi_event);
336d5ac70f0Sopenharmony_ci	snd_midi_event_no_status(virt->midi_event, !merge);
337d5ac70f0Sopenharmony_ci
338d5ac70f0Sopenharmony_ci	if (inputp) {
339d5ac70f0Sopenharmony_ci		rmidi = calloc(1, sizeof(*rmidi));
340d5ac70f0Sopenharmony_ci		if (rmidi == NULL) {
341d5ac70f0Sopenharmony_ci			err = -ENOMEM;
342d5ac70f0Sopenharmony_ci			goto _err;
343d5ac70f0Sopenharmony_ci		}
344d5ac70f0Sopenharmony_ci		if (name)
345d5ac70f0Sopenharmony_ci			rmidi->name = strdup(name);
346d5ac70f0Sopenharmony_ci		rmidi->type = SND_RAWMIDI_TYPE_VIRTUAL;
347d5ac70f0Sopenharmony_ci		rmidi->stream = SND_RAWMIDI_STREAM_INPUT;
348d5ac70f0Sopenharmony_ci		rmidi->mode = mode;
349d5ac70f0Sopenharmony_ci		err = snd_seq_poll_descriptors(seq_handle, &pfd, 1, POLLIN);
350d5ac70f0Sopenharmony_ci		if (err < 0)
351d5ac70f0Sopenharmony_ci			goto _err;
352d5ac70f0Sopenharmony_ci		rmidi->poll_fd = pfd.fd;
353d5ac70f0Sopenharmony_ci		rmidi->ops = &snd_rawmidi_virtual_ops;
354d5ac70f0Sopenharmony_ci		rmidi->private_data = virt;
355d5ac70f0Sopenharmony_ci		virt->open++;
356d5ac70f0Sopenharmony_ci		*inputp = rmidi;
357d5ac70f0Sopenharmony_ci	}
358d5ac70f0Sopenharmony_ci	if (outputp) {
359d5ac70f0Sopenharmony_ci		rmidi = calloc(1, sizeof(*rmidi));
360d5ac70f0Sopenharmony_ci		if (rmidi == NULL) {
361d5ac70f0Sopenharmony_ci			err = -ENOMEM;
362d5ac70f0Sopenharmony_ci			goto _err;
363d5ac70f0Sopenharmony_ci		}
364d5ac70f0Sopenharmony_ci		if (name)
365d5ac70f0Sopenharmony_ci			rmidi->name = strdup(name);
366d5ac70f0Sopenharmony_ci		rmidi->type = SND_RAWMIDI_TYPE_VIRTUAL;
367d5ac70f0Sopenharmony_ci		rmidi->stream = SND_RAWMIDI_STREAM_OUTPUT;
368d5ac70f0Sopenharmony_ci		rmidi->mode = mode;
369d5ac70f0Sopenharmony_ci		err = snd_seq_poll_descriptors(seq_handle, &pfd, 1, POLLOUT);
370d5ac70f0Sopenharmony_ci		if (err < 0)
371d5ac70f0Sopenharmony_ci			goto _err;
372d5ac70f0Sopenharmony_ci		rmidi->poll_fd = pfd.fd;
373d5ac70f0Sopenharmony_ci		rmidi->ops = &snd_rawmidi_virtual_ops;
374d5ac70f0Sopenharmony_ci		rmidi->private_data = virt;
375d5ac70f0Sopenharmony_ci		virt->open++;
376d5ac70f0Sopenharmony_ci		*outputp = rmidi;
377d5ac70f0Sopenharmony_ci	}
378d5ac70f0Sopenharmony_ci
379d5ac70f0Sopenharmony_ci	return 0;
380d5ac70f0Sopenharmony_ci
381d5ac70f0Sopenharmony_ci _err:
382d5ac70f0Sopenharmony_ci	if (seq_handle)
383d5ac70f0Sopenharmony_ci		snd_seq_close(seq_handle);
384d5ac70f0Sopenharmony_ci	if (virt) {
385d5ac70f0Sopenharmony_ci		if (virt->midi_event)
386d5ac70f0Sopenharmony_ci			snd_midi_event_free(virt->midi_event);
387d5ac70f0Sopenharmony_ci		free(virt);
388d5ac70f0Sopenharmony_ci	}
389d5ac70f0Sopenharmony_ci	if (inputp)
390d5ac70f0Sopenharmony_ci		free(*inputp);
391d5ac70f0Sopenharmony_ci	if (outputp)
392d5ac70f0Sopenharmony_ci		free(*outputp);
393d5ac70f0Sopenharmony_ci	free(rmidi);
394d5ac70f0Sopenharmony_ci	return err;
395d5ac70f0Sopenharmony_ci}
396d5ac70f0Sopenharmony_ci
397d5ac70f0Sopenharmony_ciint _snd_rawmidi_virtual_open(snd_rawmidi_t **inputp, snd_rawmidi_t **outputp,
398d5ac70f0Sopenharmony_ci			   char *name, snd_config_t *root ATTRIBUTE_UNUSED,
399d5ac70f0Sopenharmony_ci			   snd_config_t *conf, int mode)
400d5ac70f0Sopenharmony_ci{
401d5ac70f0Sopenharmony_ci	snd_config_iterator_t i, next;
402d5ac70f0Sopenharmony_ci	const char *slave_str = NULL;
403d5ac70f0Sopenharmony_ci	int err;
404d5ac70f0Sopenharmony_ci	int streams, seq_mode;
405d5ac70f0Sopenharmony_ci	int merge = 1;
406d5ac70f0Sopenharmony_ci	int port;
407d5ac70f0Sopenharmony_ci	unsigned int caps;
408d5ac70f0Sopenharmony_ci	snd_seq_t *seq_handle;
409d5ac70f0Sopenharmony_ci
410d5ac70f0Sopenharmony_ci	snd_config_for_each(i, next, conf) {
411d5ac70f0Sopenharmony_ci		snd_config_t *n = snd_config_iterator_entry(i);
412d5ac70f0Sopenharmony_ci		const char *id;
413d5ac70f0Sopenharmony_ci		if (snd_config_get_id(n, &id) < 0)
414d5ac70f0Sopenharmony_ci			continue;
415d5ac70f0Sopenharmony_ci		if (snd_rawmidi_conf_generic_id(id))
416d5ac70f0Sopenharmony_ci			continue;
417d5ac70f0Sopenharmony_ci		if (strcmp(id, "slave") == 0) {
418d5ac70f0Sopenharmony_ci			err = snd_config_get_string(n, &slave_str);
419d5ac70f0Sopenharmony_ci			if (err < 0)
420d5ac70f0Sopenharmony_ci				return err;
421d5ac70f0Sopenharmony_ci			continue;
422d5ac70f0Sopenharmony_ci		}
423d5ac70f0Sopenharmony_ci		if (strcmp(id, "merge") == 0) {
424d5ac70f0Sopenharmony_ci			merge = snd_config_get_bool(n);
425d5ac70f0Sopenharmony_ci			continue;
426d5ac70f0Sopenharmony_ci		}
427d5ac70f0Sopenharmony_ci		return -EINVAL;
428d5ac70f0Sopenharmony_ci	}
429d5ac70f0Sopenharmony_ci
430d5ac70f0Sopenharmony_ci	streams = 0;
431d5ac70f0Sopenharmony_ci	if (inputp)
432d5ac70f0Sopenharmony_ci		streams |= SND_SEQ_OPEN_INPUT;
433d5ac70f0Sopenharmony_ci	if (outputp)
434d5ac70f0Sopenharmony_ci		streams |= SND_SEQ_OPEN_OUTPUT;
435d5ac70f0Sopenharmony_ci	if (! streams)
436d5ac70f0Sopenharmony_ci		return -EINVAL;
437d5ac70f0Sopenharmony_ci
438d5ac70f0Sopenharmony_ci	seq_mode = 0;
439d5ac70f0Sopenharmony_ci	if (mode & SND_RAWMIDI_NONBLOCK)
440d5ac70f0Sopenharmony_ci		seq_mode |= SND_SEQ_NONBLOCK;
441d5ac70f0Sopenharmony_ci
442d5ac70f0Sopenharmony_ci	if (! slave_str)
443d5ac70f0Sopenharmony_ci		slave_str = "default";
444d5ac70f0Sopenharmony_ci	err = _snd_seq_open_lconf(&seq_handle, slave_str, streams, seq_mode,
445d5ac70f0Sopenharmony_ci				  root, conf);
446d5ac70f0Sopenharmony_ci	if (err < 0)
447d5ac70f0Sopenharmony_ci		return err;
448d5ac70f0Sopenharmony_ci
449d5ac70f0Sopenharmony_ci	caps = 0;
450d5ac70f0Sopenharmony_ci	if (inputp)
451d5ac70f0Sopenharmony_ci		caps |= SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SYNC_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
452d5ac70f0Sopenharmony_ci	if (outputp)
453d5ac70f0Sopenharmony_ci		caps |= SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SYNC_READ | SND_SEQ_PORT_CAP_SUBS_READ;
454d5ac70f0Sopenharmony_ci	if (inputp && outputp)
455d5ac70f0Sopenharmony_ci		caps |= SNDRV_SEQ_PORT_CAP_DUPLEX;
456d5ac70f0Sopenharmony_ci
457d5ac70f0Sopenharmony_ci	port = snd_seq_create_simple_port(seq_handle, "Virtual RawMIDI",
458d5ac70f0Sopenharmony_ci					  caps, SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC);
459d5ac70f0Sopenharmony_ci	if (port < 0) {
460d5ac70f0Sopenharmony_ci		snd_seq_close(seq_handle);
461d5ac70f0Sopenharmony_ci		return port;
462d5ac70f0Sopenharmony_ci	}
463d5ac70f0Sopenharmony_ci
464d5ac70f0Sopenharmony_ci	return snd_rawmidi_virtual_open(inputp, outputp, name, seq_handle, port,
465d5ac70f0Sopenharmony_ci				     merge, mode);
466d5ac70f0Sopenharmony_ci}
467d5ac70f0Sopenharmony_ci
468d5ac70f0Sopenharmony_ci#ifndef DOC_HIDDEN
469d5ac70f0Sopenharmony_ciSND_DLSYM_BUILD_VERSION(_snd_rawmidi_virtual_open, SND_RAWMIDI_DLSYM_VERSION);
470d5ac70f0Sopenharmony_ci#endif
471