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