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/module.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                if (printk_ratelimit())
 124                        pr_err("ALSA: seq_midi: MIDI output buffer overrun\n");
 125                return -ENOMEM;
 126        }
 127        if (snd_rawmidi_kernel_write(substream, buf, count) < count)
 128                return -EINVAL;
 129        return 0;
 130}
 131
 132static int event_process_midi(struct snd_seq_event *ev, int direct,
 133                              void *private_data, int atomic, int hop)
 134{
 135        struct seq_midisynth *msynth = private_data;
 136        unsigned char msg[10];  /* buffer for constructing midi messages */
 137        struct snd_rawmidi_substream *substream;
 138        int len;
 139
 140        if (snd_BUG_ON(!msynth))
 141                return -EINVAL;
 142        substream = msynth->output_rfile.output;
 143        if (substream == NULL)
 144                return -ENODEV;
 145        if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {        /* special case, to save space */
 146                if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
 147                        /* invalid event */
 148                        pr_debug("ALSA: seq_midi: invalid sysex event flags = 0x%x\n", ev->flags);
 149                        return 0;
 150                }
 151                snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream);
 152                snd_midi_event_reset_decode(msynth->parser);
 153        } else {
 154                if (msynth->parser == NULL)
 155                        return -EIO;
 156                len = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev);
 157                if (len < 0)
 158                        return 0;
 159                if (dump_midi(substream, msg, len) < 0)
 160                        snd_midi_event_reset_decode(msynth->parser);
 161        }
 162        return 0;
 163}
 164
 165
 166static int snd_seq_midisynth_new(struct seq_midisynth *msynth,
 167                                 struct snd_card *card,
 168                                 int device,
 169                                 int subdevice)
 170{
 171        if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &msynth->parser) < 0)
 172                return -ENOMEM;
 173        msynth->card = card;
 174        msynth->device = device;
 175        msynth->subdevice = subdevice;
 176        return 0;
 177}
 178
 179/* open associated midi device for input */
 180static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe *info)
 181{
 182        int err;
 183        struct seq_midisynth *msynth = private_data;
 184        struct snd_rawmidi_runtime *runtime;
 185        struct snd_rawmidi_params params;
 186
 187        /* open midi port */
 188        if ((err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
 189                                           msynth->subdevice,
 190                                           SNDRV_RAWMIDI_LFLG_INPUT,
 191                                           &msynth->input_rfile)) < 0) {
 192                pr_debug("ALSA: seq_midi: midi input open failed!!!\n");
 193                return err;
 194        }
 195        runtime = msynth->input_rfile.input->runtime;
 196        memset(&params, 0, sizeof(params));
 197        params.avail_min = 1;
 198        params.buffer_size = input_buffer_size;
 199        if ((err = snd_rawmidi_input_params(msynth->input_rfile.input, &params)) < 0) {
 200                snd_rawmidi_kernel_release(&msynth->input_rfile);
 201                return err;
 202        }
 203        snd_midi_event_reset_encode(msynth->parser);
 204        runtime->event = snd_midi_input_event;
 205        runtime->private_data = msynth;
 206        snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0);
 207        return 0;
 208}
 209
 210/* close associated midi device for input */
 211static int midisynth_unsubscribe(void *private_data, struct snd_seq_port_subscribe *info)
 212{
 213        int err;
 214        struct seq_midisynth *msynth = private_data;
 215
 216        if (snd_BUG_ON(!msynth->input_rfile.input))
 217                return -EINVAL;
 218        err = snd_rawmidi_kernel_release(&msynth->input_rfile);
 219        return err;
 220}
 221
 222/* open associated midi device for output */
 223static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info)
 224{
 225        int err;
 226        struct seq_midisynth *msynth = private_data;
 227        struct snd_rawmidi_params params;
 228
 229        /* open midi port */
 230        if ((err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
 231                                           msynth->subdevice,
 232                                           SNDRV_RAWMIDI_LFLG_OUTPUT,
 233                                           &msynth->output_rfile)) < 0) {
 234                pr_debug("ALSA: seq_midi: midi output open failed!!!\n");
 235                return err;
 236        }
 237        memset(&params, 0, sizeof(params));
 238        params.avail_min = 1;
 239        params.buffer_size = output_buffer_size;
 240        params.no_active_sensing = 1;
 241        if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, &params)) < 0) {
 242                snd_rawmidi_kernel_release(&msynth->output_rfile);
 243                return err;
 244        }
 245        snd_midi_event_reset_decode(msynth->parser);
 246        return 0;
 247}
 248
 249/* close associated midi device for output */
 250static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
 251{
 252        struct seq_midisynth *msynth = private_data;
 253
 254        if (snd_BUG_ON(!msynth->output_rfile.output))
 255                return -EINVAL;
 256        snd_rawmidi_drain_output(msynth->output_rfile.output);
 257        return snd_rawmidi_kernel_release(&msynth->output_rfile);
 258}
 259
 260/* delete given midi synth port */
 261static void snd_seq_midisynth_delete(struct seq_midisynth *msynth)
 262{
 263        if (msynth == NULL)
 264                return;
 265
 266        if (msynth->seq_client > 0) {
 267                /* delete port */
 268                snd_seq_event_port_detach(msynth->seq_client, msynth->seq_port);
 269        }
 270
 271        snd_midi_event_free(msynth->parser);
 272}
 273
 274/* register new midi synth port */
 275static int
 276snd_seq_midisynth_probe(struct device *_dev)
 277{
 278        struct snd_seq_device *dev = to_seq_dev(_dev);
 279        struct seq_midisynth_client *client;
 280        struct seq_midisynth *msynth, *ms;
 281        struct snd_seq_port_info *port;
 282        struct snd_rawmidi_info *info;
 283        struct snd_rawmidi *rmidi = dev->private_data;
 284        int newclient = 0;
 285        unsigned int p, ports;
 286        struct snd_seq_port_callback pcallbacks;
 287        struct snd_card *card = dev->card;
 288        int device = dev->device;
 289        unsigned int input_count = 0, output_count = 0;
 290
 291        if (snd_BUG_ON(!card || device < 0 || device >= SNDRV_RAWMIDI_DEVICES))
 292                return -EINVAL;
 293        info = kmalloc(sizeof(*info), GFP_KERNEL);
 294        if (! info)
 295                return -ENOMEM;
 296        info->device = device;
 297        info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
 298        info->subdevice = 0;
 299        if (snd_rawmidi_info_select(card, info) >= 0)
 300                output_count = info->subdevices_count;
 301        info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
 302        if (snd_rawmidi_info_select(card, info) >= 0) {
 303                input_count = info->subdevices_count;
 304        }
 305        ports = output_count;
 306        if (ports < input_count)
 307                ports = input_count;
 308        if (ports == 0) {
 309                kfree(info);
 310                return -ENODEV;
 311        }
 312        if (ports > (256 / SNDRV_RAWMIDI_DEVICES))
 313                ports = 256 / SNDRV_RAWMIDI_DEVICES;
 314
 315        mutex_lock(&register_mutex);
 316        client = synths[card->number];
 317        if (client == NULL) {
 318                newclient = 1;
 319                client = kzalloc(sizeof(*client), GFP_KERNEL);
 320                if (client == NULL) {
 321                        mutex_unlock(&register_mutex);
 322                        kfree(info);
 323                        return -ENOMEM;
 324                }
 325                client->seq_client =
 326                        snd_seq_create_kernel_client(
 327                                card, 0, "%s", card->shortname[0] ?
 328                                (const char *)card->shortname : "External MIDI");
 329                if (client->seq_client < 0) {
 330                        kfree(client);
 331                        mutex_unlock(&register_mutex);
 332                        kfree(info);
 333                        return -ENOMEM;
 334                }
 335        }
 336
 337        msynth = kcalloc(ports, sizeof(struct seq_midisynth), GFP_KERNEL);
 338        port = kmalloc(sizeof(*port), GFP_KERNEL);
 339        if (msynth == NULL || port == NULL)
 340                goto __nomem;
 341
 342        for (p = 0; p < ports; p++) {
 343                ms = &msynth[p];
 344
 345                if (snd_seq_midisynth_new(ms, card, device, p) < 0)
 346                        goto __nomem;
 347
 348                /* declare port */
 349                memset(port, 0, sizeof(*port));
 350                port->addr.client = client->seq_client;
 351                port->addr.port = device * (256 / SNDRV_RAWMIDI_DEVICES) + p;
 352                port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
 353                memset(info, 0, sizeof(*info));
 354                info->device = device;
 355                if (p < output_count)
 356                        info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
 357                else
 358                        info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
 359                info->subdevice = p;
 360                if (snd_rawmidi_info_select(card, info) >= 0)
 361                        strcpy(port->name, info->subname);
 362                if (! port->name[0]) {
 363                        if (info->name[0]) {
 364                                if (ports > 1)
 365                                        snprintf(port->name, sizeof(port->name), "%s-%u", info->name, p);
 366                                else
 367                                        snprintf(port->name, sizeof(port->name), "%s", info->name);
 368                        } else {
 369                                /* last resort */
 370                                if (ports > 1)
 371                                        sprintf(port->name, "MIDI %d-%d-%u", card->number, device, p);
 372                                else
 373                                        sprintf(port->name, "MIDI %d-%d", card->number, device);
 374                        }
 375                }
 376                if ((info->flags & SNDRV_RAWMIDI_INFO_OUTPUT) && p < output_count)
 377                        port->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
 378                if ((info->flags & SNDRV_RAWMIDI_INFO_INPUT) && p < input_count)
 379                        port->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
 380                if ((port->capability & (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ)) == (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ) &&
 381                    info->flags & SNDRV_RAWMIDI_INFO_DUPLEX)
 382                        port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
 383                port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
 384                        | SNDRV_SEQ_PORT_TYPE_HARDWARE
 385                        | SNDRV_SEQ_PORT_TYPE_PORT;
 386                port->midi_channels = 16;
 387                memset(&pcallbacks, 0, sizeof(pcallbacks));
 388                pcallbacks.owner = THIS_MODULE;
 389                pcallbacks.private_data = ms;
 390                pcallbacks.subscribe = midisynth_subscribe;
 391                pcallbacks.unsubscribe = midisynth_unsubscribe;
 392                pcallbacks.use = midisynth_use;
 393                pcallbacks.unuse = midisynth_unuse;
 394                pcallbacks.event_input = event_process_midi;
 395                port->kernel = &pcallbacks;
 396                if (rmidi->ops && rmidi->ops->get_port_info)
 397                        rmidi->ops->get_port_info(rmidi, p, port);
 398                if (snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_CREATE_PORT, port)<0)
 399                        goto __nomem;
 400                ms->seq_client = client->seq_client;
 401                ms->seq_port = port->addr.port;
 402        }
 403        client->ports_per_device[device] = ports;
 404        client->ports[device] = msynth;
 405        client->num_ports++;
 406        if (newclient)
 407                synths[card->number] = client;
 408        mutex_unlock(&register_mutex);
 409        kfree(info);
 410        kfree(port);
 411        return 0;       /* success */
 412
 413      __nomem:
 414        if (msynth != NULL) {
 415                for (p = 0; p < ports; p++)
 416                        snd_seq_midisynth_delete(&msynth[p]);
 417                kfree(msynth);
 418        }
 419        if (newclient) {
 420                snd_seq_delete_kernel_client(client->seq_client);
 421                kfree(client);
 422        }
 423        kfree(info);
 424        kfree(port);
 425        mutex_unlock(&register_mutex);
 426        return -ENOMEM;
 427}
 428
 429/* release midi synth port */
 430static int
 431snd_seq_midisynth_remove(struct device *_dev)
 432{
 433        struct snd_seq_device *dev = to_seq_dev(_dev);
 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
 462static struct snd_seq_driver seq_midisynth_driver = {
 463        .driver = {
 464                .name = KBUILD_MODNAME,
 465                .probe = snd_seq_midisynth_probe,
 466                .remove = snd_seq_midisynth_remove,
 467        },
 468        .id = SNDRV_SEQ_DEV_ID_MIDISYNTH,
 469        .argsize = 0,
 470};
 471
 472module_snd_seq_driver(seq_midisynth_driver);
 473