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