linux/sound/core/seq/seq_midi.c
<<
>>
Prefs
   1/*
   2 *   Generic MIDI synth driver for ALSA sequencer
   3 *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
   4 *                         Jaroslav Kysela <perex@perex.cz>
   5 *
   6 *   This program is free software; you can redistribute it and/or modify
   7 *   it under the terms of the GNU General Public License as published by
   8 *   the Free Software Foundation; either version 2 of the License, or
   9 *   (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 General Public License for more details.
  15 *
  16 *   You should have received a copy of the GNU General Public License
  17 *   along with this program; if not, write to the Free Software
  18 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  19 *
  20 */
  21 
  22/* 
  23Possible options for midisynth module:
  24        - automatic opening of midi ports on first received event or subscription
  25          (close will be performed when client leaves)
  26*/
  27
  28
  29#include <linux/init.h>
  30#include <linux/slab.h>
  31#include <linux/errno.h>
  32#include <linux/string.h>
  33#include <linux/moduleparam.h>
  34#include <linux/mutex.h>
  35#include <sound/core.h>
  36#include <sound/rawmidi.h>
  37#include <sound/seq_kernel.h>
  38#include <sound/seq_device.h>
  39#include <sound/seq_midi_event.h>
  40#include <sound/initval.h>
  41
  42MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@perex.cz>");
  43MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth.");
  44MODULE_LICENSE("GPL");
  45static int output_buffer_size = PAGE_SIZE;
  46module_param(output_buffer_size, int, 0644);
  47MODULE_PARM_DESC(output_buffer_size, "Output buffer size in bytes.");
  48static int input_buffer_size = PAGE_SIZE;
  49module_param(input_buffer_size, int, 0644);
  50MODULE_PARM_DESC(input_buffer_size, "Input buffer size in bytes.");
  51
  52/* data for this midi synth driver */
  53struct seq_midisynth {
  54        struct snd_card *card;
  55        int device;
  56        int subdevice;
  57        struct snd_rawmidi_file input_rfile;
  58        struct snd_rawmidi_file output_rfile;
  59        int seq_client;
  60        int seq_port;
  61        struct snd_midi_event *parser;
  62};
  63
  64struct seq_midisynth_client {
  65        int seq_client;
  66        int num_ports;
  67        int ports_per_device[SNDRV_RAWMIDI_DEVICES];
  68        struct seq_midisynth *ports[SNDRV_RAWMIDI_DEVICES];
  69};
  70
  71static struct seq_midisynth_client *synths[SNDRV_CARDS];
  72static DEFINE_MUTEX(register_mutex);
  73
  74/* handle rawmidi input event (MIDI v1.0 stream) */
  75static void snd_midi_input_event(struct snd_rawmidi_substream *substream)
  76{
  77        struct snd_rawmidi_runtime *runtime;
  78        struct seq_midisynth *msynth;
  79        struct snd_seq_event ev;
  80        char buf[16], *pbuf;
  81        long res, count;
  82
  83        if (substream == NULL)
  84                return;
  85        runtime = substream->runtime;
  86        msynth = runtime->private_data;
  87        if (msynth == NULL)
  88                return;
  89        memset(&ev, 0, sizeof(ev));
  90        while (runtime->avail > 0) {
  91                res = snd_rawmidi_kernel_read(substream, buf, sizeof(buf));
  92                if (res <= 0)
  93                        continue;
  94                if (msynth->parser == NULL)
  95                        continue;
  96                pbuf = buf;
  97                while (res > 0) {
  98                        count = snd_midi_event_encode(msynth->parser, pbuf, res, &ev);
  99                        if (count < 0)
 100                                break;
 101                        pbuf += count;
 102                        res -= count;
 103                        if (ev.type != SNDRV_SEQ_EVENT_NONE) {
 104                                ev.source.port = msynth->seq_port;
 105                                ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
 106                                snd_seq_kernel_client_dispatch(msynth->seq_client, &ev, 1, 0);
 107                                /* clear event and reset header */
 108                                memset(&ev, 0, sizeof(ev));
 109                        }
 110                }
 111        }
 112}
 113
 114static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, int count)
 115{
 116        struct snd_rawmidi_runtime *runtime;
 117        int tmp;
 118
 119        if (snd_BUG_ON(!substream || !buf))
 120                return -EINVAL;
 121        runtime = substream->runtime;
 122        if ((tmp = runtime->avail) < count) {
 123                snd_printd("warning, output event was lost (count = %i, available = %i)\n", count, tmp);
 124                return -ENOMEM;
 125        }
 126        if (snd_rawmidi_kernel_write(substream, buf, count) < count)
 127                return -EINVAL;
 128        return 0;
 129}
 130
 131static int event_process_midi(struct snd_seq_event *ev, int direct,
 132                              void *private_data, int atomic, int hop)
 133{
 134        struct seq_midisynth *msynth = private_data;
 135        unsigned char msg[10];  /* buffer for constructing midi messages */
 136        struct snd_rawmidi_substream *substream;
 137        int len;
 138
 139        if (snd_BUG_ON(!msynth))
 140                return -EINVAL;
 141        substream = msynth->output_rfile.output;
 142        if (substream == NULL)
 143                return -ENODEV;
 144        if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {        /* special case, to save space */
 145                if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
 146                        /* invalid event */
 147                        snd_printd("seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
 148                        return 0;
 149                }
 150                snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream);
 151                snd_midi_event_reset_decode(msynth->parser);
 152        } else {
 153                if (msynth->parser == NULL)
 154                        return -EIO;
 155                len = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev);
 156                if (len < 0)
 157                        return 0;
 158                if (dump_midi(substream, msg, len) < 0)
 159                        snd_midi_event_reset_decode(msynth->parser);
 160        }
 161        return 0;
 162}
 163
 164
 165static int snd_seq_midisynth_new(struct seq_midisynth *msynth,
 166                                 struct snd_card *card,
 167                                 int device,
 168                                 int subdevice)
 169{
 170        if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &msynth->parser) < 0)
 171                return -ENOMEM;
 172        msynth->card = card;
 173        msynth->device = device;
 174        msynth->subdevice = subdevice;
 175        return 0;
 176}
 177
 178/* open associated midi device for input */
 179static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe *info)
 180{
 181        int err;
 182        struct seq_midisynth *msynth = private_data;
 183        struct snd_rawmidi_runtime *runtime;
 184        struct snd_rawmidi_params params;
 185
 186        /* open midi port */
 187        if ((err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
 188                                           msynth->subdevice,
 189                                           SNDRV_RAWMIDI_LFLG_INPUT,
 190                                           &msynth->input_rfile)) < 0) {
 191                snd_printd("midi input open failed!!!\n");
 192                return err;
 193        }
 194        runtime = msynth->input_rfile.input->runtime;
 195        memset(&params, 0, sizeof(params));
 196        params.avail_min = 1;
 197        params.buffer_size = input_buffer_size;
 198        if ((err = snd_rawmidi_input_params(msynth->input_rfile.input, &params)) < 0) {
 199                snd_rawmidi_kernel_release(&msynth->input_rfile);
 200                return err;
 201        }
 202        snd_midi_event_reset_encode(msynth->parser);
 203        runtime->event = snd_midi_input_event;
 204        runtime->private_data = msynth;
 205        snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0);
 206        return 0;
 207}
 208
 209/* close associated midi device for input */
 210static int midisynth_unsubscribe(void *private_data, struct snd_seq_port_subscribe *info)
 211{
 212        int err;
 213        struct seq_midisynth *msynth = private_data;
 214
 215        if (snd_BUG_ON(!msynth->input_rfile.input))
 216                return -EINVAL;
 217        err = snd_rawmidi_kernel_release(&msynth->input_rfile);
 218        return err;
 219}
 220
 221/* open associated midi device for output */
 222static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info)
 223{
 224        int err;
 225        struct seq_midisynth *msynth = private_data;
 226        struct snd_rawmidi_params params;
 227
 228        /* open midi port */
 229        if ((err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
 230                                           msynth->subdevice,
 231                                           SNDRV_RAWMIDI_LFLG_OUTPUT,
 232                                           &msynth->output_rfile)) < 0) {
 233                snd_printd("midi output open failed!!!\n");
 234                return err;
 235        }
 236        memset(&params, 0, sizeof(params));
 237        params.avail_min = 1;
 238        params.buffer_size = output_buffer_size;
 239        if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, &params)) < 0) {
 240                snd_rawmidi_kernel_release(&msynth->output_rfile);
 241                return err;
 242        }
 243        snd_midi_event_reset_decode(msynth->parser);
 244        return 0;
 245}
 246
 247/* close associated midi device for output */
 248static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
 249{
 250        struct seq_midisynth *msynth = private_data;
 251        unsigned char buf = 0xff; /* MIDI reset */
 252
 253        if (snd_BUG_ON(!msynth->output_rfile.output))
 254                return -EINVAL;
 255        /* sending single MIDI reset message to shut the device up */
 256        snd_rawmidi_kernel_write(msynth->output_rfile.output, &buf, 1);
 257        snd_rawmidi_drain_output(msynth->output_rfile.output);
 258        return snd_rawmidi_kernel_release(&msynth->output_rfile);
 259}
 260
 261/* delete given midi synth port */
 262static void snd_seq_midisynth_delete(struct seq_midisynth *msynth)
 263{
 264        if (msynth == NULL)
 265                return;
 266
 267        if (msynth->seq_client > 0) {
 268                /* delete port */
 269                snd_seq_event_port_detach(msynth->seq_client, msynth->seq_port);
 270        }
 271
 272        if (msynth->parser)
 273                snd_midi_event_free(msynth->parser);
 274}
 275
 276/* register new midi synth port */
 277static int
 278snd_seq_midisynth_register_port(struct snd_seq_device *dev)
 279{
 280        struct seq_midisynth_client *client;
 281        struct seq_midisynth *msynth, *ms;
 282        struct snd_seq_port_info *port;
 283        struct snd_rawmidi_info *info;
 284        struct snd_rawmidi *rmidi = dev->private_data;
 285        int newclient = 0;
 286        unsigned int p, ports;
 287        struct snd_seq_port_callback pcallbacks;
 288        struct snd_card *card = dev->card;
 289        int device = dev->device;
 290        unsigned int input_count = 0, output_count = 0;
 291
 292        if (snd_BUG_ON(!card || device < 0 || device >= SNDRV_RAWMIDI_DEVICES))
 293                return -EINVAL;
 294        info = kmalloc(sizeof(*info), GFP_KERNEL);
 295        if (! info)
 296                return -ENOMEM;
 297        info->device = device;
 298        info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
 299        info->subdevice = 0;
 300        if (snd_rawmidi_info_select(card, info) >= 0)
 301                output_count = info->subdevices_count;
 302        info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
 303        if (snd_rawmidi_info_select(card, info) >= 0) {
 304                input_count = info->subdevices_count;
 305        }
 306        ports = output_count;
 307        if (ports < input_count)
 308                ports = input_count;
 309        if (ports == 0) {
 310                kfree(info);
 311                return -ENODEV;
 312        }
 313        if (ports > (256 / SNDRV_RAWMIDI_DEVICES))
 314                ports = 256 / SNDRV_RAWMIDI_DEVICES;
 315
 316        mutex_lock(&register_mutex);
 317        client = synths[card->number];
 318        if (client == NULL) {
 319                newclient = 1;
 320                client = kzalloc(sizeof(*client), GFP_KERNEL);
 321                if (client == NULL) {
 322                        mutex_unlock(&register_mutex);
 323                        kfree(info);
 324                        return -ENOMEM;
 325                }
 326                client->seq_client =
 327                        snd_seq_create_kernel_client(
 328                                card, 0, "%s", card->shortname[0] ?
 329                                (const char *)card->shortname : "External MIDI");
 330                if (client->seq_client < 0) {
 331                        kfree(client);
 332                        mutex_unlock(&register_mutex);
 333                        kfree(info);
 334                        return -ENOMEM;
 335                }
 336        }
 337
 338        msynth = kcalloc(ports, sizeof(struct seq_midisynth), GFP_KERNEL);
 339        port = kmalloc(sizeof(*port), GFP_KERNEL);
 340        if (msynth == NULL || port == NULL)
 341                goto __nomem;
 342
 343        for (p = 0; p < ports; p++) {
 344                ms = &msynth[p];
 345
 346                if (snd_seq_midisynth_new(ms, card, device, p) < 0)
 347                        goto __nomem;
 348
 349                /* declare port */
 350                memset(port, 0, sizeof(*port));
 351                port->addr.client = client->seq_client;
 352                port->addr.port = device * (256 / SNDRV_RAWMIDI_DEVICES) + p;
 353                port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
 354                memset(info, 0, sizeof(*info));
 355                info->device = device;
 356                if (p < output_count)
 357                        info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
 358                else
 359                        info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
 360                info->subdevice = p;
 361                if (snd_rawmidi_info_select(card, info) >= 0)
 362                        strcpy(port->name, info->subname);
 363                if (! port->name[0]) {
 364                        if (info->name[0]) {
 365                                if (ports > 1)
 366                                        snprintf(port->name, sizeof(port->name), "%s-%d", info->name, p);
 367                                else
 368                                        snprintf(port->name, sizeof(port->name), "%s", info->name);
 369                        } else {
 370                                /* last resort */
 371                                if (ports > 1)
 372                                        sprintf(port->name, "MIDI %d-%d-%d", card->number, device, p);
 373                                else
 374                                        sprintf(port->name, "MIDI %d-%d", card->number, device);
 375                        }
 376                }
 377                if ((info->flags & SNDRV_RAWMIDI_INFO_OUTPUT) && p < output_count)
 378                        port->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
 379                if ((info->flags & SNDRV_RAWMIDI_INFO_INPUT) && p < input_count)
 380                        port->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
 381                if ((port->capability & (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ)) == (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ) &&
 382                    info->flags & SNDRV_RAWMIDI_INFO_DUPLEX)
 383                        port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
 384                port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
 385                        | SNDRV_SEQ_PORT_TYPE_HARDWARE
 386                        | SNDRV_SEQ_PORT_TYPE_PORT;
 387                port->midi_channels = 16;
 388                memset(&pcallbacks, 0, sizeof(pcallbacks));
 389                pcallbacks.owner = THIS_MODULE;
 390                pcallbacks.private_data = ms;
 391                pcallbacks.subscribe = midisynth_subscribe;
 392                pcallbacks.unsubscribe = midisynth_unsubscribe;
 393                pcallbacks.use = midisynth_use;
 394                pcallbacks.unuse = midisynth_unuse;
 395                pcallbacks.event_input = event_process_midi;
 396                port->kernel = &pcallbacks;
 397                if (rmidi->ops && rmidi->ops->get_port_info)
 398                        rmidi->ops->get_port_info(rmidi, p, port);
 399                if (snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_CREATE_PORT, port)<0)
 400                        goto __nomem;
 401                ms->seq_client = client->seq_client;
 402                ms->seq_port = port->addr.port;
 403        }
 404        client->ports_per_device[device] = ports;
 405        client->ports[device] = msynth;
 406        client->num_ports++;
 407        if (newclient)
 408                synths[card->number] = client;
 409        mutex_unlock(&register_mutex);
 410        kfree(info);
 411        kfree(port);
 412        return 0;       /* success */
 413
 414      __nomem:
 415        if (msynth != NULL) {
 416                for (p = 0; p < ports; p++)
 417                        snd_seq_midisynth_delete(&msynth[p]);
 418                kfree(msynth);
 419        }
 420        if (newclient) {
 421                snd_seq_delete_kernel_client(client->seq_client);
 422                kfree(client);
 423        }
 424        kfree(info);
 425        kfree(port);
 426        mutex_unlock(&register_mutex);
 427        return -ENOMEM;
 428}
 429
 430/* release midi synth port */
 431static int
 432snd_seq_midisynth_unregister_port(struct snd_seq_device *dev)
 433{
 434        struct seq_midisynth_client *client;
 435        struct seq_midisynth *msynth;
 436        struct snd_card *card = dev->card;
 437        int device = dev->device, p, ports;
 438        
 439        mutex_lock(&register_mutex);
 440        client = synths[card->number];
 441        if (client == NULL || client->ports[device] == NULL) {
 442                mutex_unlock(&register_mutex);
 443                return -ENODEV;
 444        }
 445        ports = client->ports_per_device[device];
 446        client->ports_per_device[device] = 0;
 447        msynth = client->ports[device];
 448        client->ports[device] = NULL;
 449        for (p = 0; p < ports; p++)
 450                snd_seq_midisynth_delete(&msynth[p]);
 451        kfree(msynth);
 452        client->num_ports--;
 453        if (client->num_ports <= 0) {
 454                snd_seq_delete_kernel_client(client->seq_client);
 455                synths[card->number] = NULL;
 456                kfree(client);
 457        }
 458        mutex_unlock(&register_mutex);
 459        return 0;
 460}
 461
 462
 463static int __init alsa_seq_midi_init(void)
 464{
 465        static struct snd_seq_dev_ops ops = {
 466                snd_seq_midisynth_register_port,
 467                snd_seq_midisynth_unregister_port,
 468        };
 469        memset(&synths, 0, sizeof(synths));
 470        snd_seq_autoload_lock();
 471        snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0);
 472        snd_seq_autoload_unlock();
 473        return 0;
 474}
 475
 476static void __exit alsa_seq_midi_exit(void)
 477{
 478        snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH);
 479}
 480
 481module_init(alsa_seq_midi_init)
 482module_exit(alsa_seq_midi_exit)
 483
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.