linux/sound/core/seq/seq_virmidi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  Virtual Raw MIDI client on Sequencer
   4 *
   5 *  Copyright (c) 2000 by Takashi Iwai <tiwai@suse.de>,
   6 *                        Jaroslav Kysela <perex@perex.cz>
   7 */
   8
   9/*
  10 * Virtual Raw MIDI client
  11 *
  12 * The virtual rawmidi client is a sequencer client which associate
  13 * a rawmidi device file.  The created rawmidi device file can be
  14 * accessed as a normal raw midi, but its MIDI source and destination
  15 * are arbitrary.  For example, a user-client software synth connected
  16 * to this port can be used as a normal midi device as well.
  17 *
  18 * The virtual rawmidi device accepts also multiple opens.  Each file
  19 * has its own input buffer, so that no conflict would occur.  The drain
  20 * of input/output buffer acts only to the local buffer.
  21 *
  22 */
  23
  24#include <linux/init.h>
  25#include <linux/wait.h>
  26#include <linux/module.h>
  27#include <linux/slab.h>
  28#include <sound/core.h>
  29#include <sound/rawmidi.h>
  30#include <sound/info.h>
  31#include <sound/control.h>
  32#include <sound/minors.h>
  33#include <sound/seq_kernel.h>
  34#include <sound/seq_midi_event.h>
  35#include <sound/seq_virmidi.h>
  36
  37MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
  38MODULE_DESCRIPTION("Virtual Raw MIDI client on Sequencer");
  39MODULE_LICENSE("GPL");
  40
  41/*
  42 * initialize an event record
  43 */
  44static void snd_virmidi_init_event(struct snd_virmidi *vmidi,
  45                                   struct snd_seq_event *ev)
  46{
  47        memset(ev, 0, sizeof(*ev));
  48        ev->source.port = vmidi->port;
  49        switch (vmidi->seq_mode) {
  50        case SNDRV_VIRMIDI_SEQ_DISPATCH:
  51                ev->dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
  52                break;
  53        case SNDRV_VIRMIDI_SEQ_ATTACH:
  54                /* FIXME: source and destination are same - not good.. */
  55                ev->dest.client = vmidi->client;
  56                ev->dest.port = vmidi->port;
  57                break;
  58        }
  59        ev->type = SNDRV_SEQ_EVENT_NONE;
  60}
  61
  62/*
  63 * decode input event and put to read buffer of each opened file
  64 */
  65static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev,
  66                                         struct snd_seq_event *ev,
  67                                         bool atomic)
  68{
  69        struct snd_virmidi *vmidi;
  70        unsigned char msg[4];
  71        int len;
  72
  73        if (atomic)
  74                read_lock(&rdev->filelist_lock);
  75        else
  76                down_read(&rdev->filelist_sem);
  77        list_for_each_entry(vmidi, &rdev->filelist, list) {
  78                if (!READ_ONCE(vmidi->trigger))
  79                        continue;
  80                if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
  81                        if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
  82                                continue;
  83                        snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)snd_rawmidi_receive, vmidi->substream);
  84                        snd_midi_event_reset_decode(vmidi->parser);
  85                } else {
  86                        len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev);
  87                        if (len > 0)
  88                                snd_rawmidi_receive(vmidi->substream, msg, len);
  89                }
  90        }
  91        if (atomic)
  92                read_unlock(&rdev->filelist_lock);
  93        else
  94                up_read(&rdev->filelist_sem);
  95
  96        return 0;
  97}
  98
  99/*
 100 * event handler of virmidi port
 101 */
 102static int snd_virmidi_event_input(struct snd_seq_event *ev, int direct,
 103                                   void *private_data, int atomic, int hop)
 104{
 105        struct snd_virmidi_dev *rdev;
 106
 107        rdev = private_data;
 108        if (!(rdev->flags & SNDRV_VIRMIDI_USE))
 109                return 0; /* ignored */
 110        return snd_virmidi_dev_receive_event(rdev, ev, atomic);
 111}
 112
 113/*
 114 * trigger rawmidi stream for input
 115 */
 116static void snd_virmidi_input_trigger(struct snd_rawmidi_substream *substream, int up)
 117{
 118        struct snd_virmidi *vmidi = substream->runtime->private_data;
 119
 120        WRITE_ONCE(vmidi->trigger, !!up);
 121}
 122
 123/* process rawmidi bytes and send events;
 124 * we need no lock here for vmidi->event since it's handled only in this work
 125 */
 126static void snd_vmidi_output_work(struct work_struct *work)
 127{
 128        struct snd_virmidi *vmidi;
 129        struct snd_rawmidi_substream *substream;
 130        unsigned char input;
 131        int ret;
 132
 133        vmidi = container_of(work, struct snd_virmidi, output_work);
 134        substream = vmidi->substream;
 135
 136        /* discard the outputs in dispatch mode unless subscribed */
 137        if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
 138            !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
 139                snd_rawmidi_proceed(substream);
 140                return;
 141        }
 142
 143        while (READ_ONCE(vmidi->trigger)) {
 144                if (snd_rawmidi_transmit(substream, &input, 1) != 1)
 145                        break;
 146                if (!snd_midi_event_encode_byte(vmidi->parser, input,
 147                                                &vmidi->event))
 148                        continue;
 149                if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
 150                        ret = snd_seq_kernel_client_dispatch(vmidi->client,
 151                                                             &vmidi->event,
 152                                                             false, 0);
 153                        vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
 154                        if (ret < 0)
 155                                break;
 156                }
 157                /* rawmidi input might be huge, allow to have a break */
 158                cond_resched();
 159        }
 160}
 161
 162/*
 163 * trigger rawmidi stream for output
 164 */
 165static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)
 166{
 167        struct snd_virmidi *vmidi = substream->runtime->private_data;
 168
 169        WRITE_ONCE(vmidi->trigger, !!up);
 170        if (up)
 171                queue_work(system_highpri_wq, &vmidi->output_work);
 172}
 173
 174/*
 175 * open rawmidi handle for input
 176 */
 177static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream)
 178{
 179        struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
 180        struct snd_rawmidi_runtime *runtime = substream->runtime;
 181        struct snd_virmidi *vmidi;
 182
 183        vmidi = kzalloc(sizeof(*vmidi), GFP_KERNEL);
 184        if (vmidi == NULL)
 185                return -ENOMEM;
 186        vmidi->substream = substream;
 187        if (snd_midi_event_new(0, &vmidi->parser) < 0) {
 188                kfree(vmidi);
 189                return -ENOMEM;
 190        }
 191        vmidi->seq_mode = rdev->seq_mode;
 192        vmidi->client = rdev->client;
 193        vmidi->port = rdev->port;       
 194        runtime->private_data = vmidi;
 195        down_write(&rdev->filelist_sem);
 196        write_lock_irq(&rdev->filelist_lock);
 197        list_add_tail(&vmidi->list, &rdev->filelist);
 198        write_unlock_irq(&rdev->filelist_lock);
 199        up_write(&rdev->filelist_sem);
 200        vmidi->rdev = rdev;
 201        return 0;
 202}
 203
 204/*
 205 * open rawmidi handle for output
 206 */
 207static int snd_virmidi_output_open(struct snd_rawmidi_substream *substream)
 208{
 209        struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
 210        struct snd_rawmidi_runtime *runtime = substream->runtime;
 211        struct snd_virmidi *vmidi;
 212
 213        vmidi = kzalloc(sizeof(*vmidi), GFP_KERNEL);
 214        if (vmidi == NULL)
 215                return -ENOMEM;
 216        vmidi->substream = substream;
 217        if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &vmidi->parser) < 0) {
 218                kfree(vmidi);
 219                return -ENOMEM;
 220        }
 221        vmidi->seq_mode = rdev->seq_mode;
 222        vmidi->client = rdev->client;
 223        vmidi->port = rdev->port;
 224        snd_virmidi_init_event(vmidi, &vmidi->event);
 225        vmidi->rdev = rdev;
 226        INIT_WORK(&vmidi->output_work, snd_vmidi_output_work);
 227        runtime->private_data = vmidi;
 228        return 0;
 229}
 230
 231/*
 232 * close rawmidi handle for input
 233 */
 234static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream)
 235{
 236        struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
 237        struct snd_virmidi *vmidi = substream->runtime->private_data;
 238
 239        down_write(&rdev->filelist_sem);
 240        write_lock_irq(&rdev->filelist_lock);
 241        list_del(&vmidi->list);
 242        write_unlock_irq(&rdev->filelist_lock);
 243        up_write(&rdev->filelist_sem);
 244        snd_midi_event_free(vmidi->parser);
 245        substream->runtime->private_data = NULL;
 246        kfree(vmidi);
 247        return 0;
 248}
 249
 250/*
 251 * close rawmidi handle for output
 252 */
 253static int snd_virmidi_output_close(struct snd_rawmidi_substream *substream)
 254{
 255        struct snd_virmidi *vmidi = substream->runtime->private_data;
 256
 257        WRITE_ONCE(vmidi->trigger, false); /* to be sure */
 258        cancel_work_sync(&vmidi->output_work);
 259        snd_midi_event_free(vmidi->parser);
 260        substream->runtime->private_data = NULL;
 261        kfree(vmidi);
 262        return 0;
 263}
 264
 265/*
 266 * subscribe callback - allow output to rawmidi device
 267 */
 268static int snd_virmidi_subscribe(void *private_data,
 269                                 struct snd_seq_port_subscribe *info)
 270{
 271        struct snd_virmidi_dev *rdev;
 272
 273        rdev = private_data;
 274        if (!try_module_get(rdev->card->module))
 275                return -EFAULT;
 276        rdev->flags |= SNDRV_VIRMIDI_SUBSCRIBE;
 277        return 0;
 278}
 279
 280/*
 281 * unsubscribe callback - disallow output to rawmidi device
 282 */
 283static int snd_virmidi_unsubscribe(void *private_data,
 284                                   struct snd_seq_port_subscribe *info)
 285{
 286        struct snd_virmidi_dev *rdev;
 287
 288        rdev = private_data;
 289        rdev->flags &= ~SNDRV_VIRMIDI_SUBSCRIBE;
 290        module_put(rdev->card->module);
 291        return 0;
 292}
 293
 294
 295/*
 296 * use callback - allow input to rawmidi device
 297 */
 298static int snd_virmidi_use(void *private_data,
 299                           struct snd_seq_port_subscribe *info)
 300{
 301        struct snd_virmidi_dev *rdev;
 302
 303        rdev = private_data;
 304        if (!try_module_get(rdev->card->module))
 305                return -EFAULT;
 306        rdev->flags |= SNDRV_VIRMIDI_USE;
 307        return 0;
 308}
 309
 310/*
 311 * unuse callback - disallow input to rawmidi device
 312 */
 313static int snd_virmidi_unuse(void *private_data,
 314                             struct snd_seq_port_subscribe *info)
 315{
 316        struct snd_virmidi_dev *rdev;
 317
 318        rdev = private_data;
 319        rdev->flags &= ~SNDRV_VIRMIDI_USE;
 320        module_put(rdev->card->module);
 321        return 0;
 322}
 323
 324
 325/*
 326 *  Register functions
 327 */
 328
 329static const struct snd_rawmidi_ops snd_virmidi_input_ops = {
 330        .open = snd_virmidi_input_open,
 331        .close = snd_virmidi_input_close,
 332        .trigger = snd_virmidi_input_trigger,
 333};
 334
 335static const struct snd_rawmidi_ops snd_virmidi_output_ops = {
 336        .open = snd_virmidi_output_open,
 337        .close = snd_virmidi_output_close,
 338        .trigger = snd_virmidi_output_trigger,
 339};
 340
 341/*
 342 * create a sequencer client and a port
 343 */
 344static int snd_virmidi_dev_attach_seq(struct snd_virmidi_dev *rdev)
 345{
 346        int client;
 347        struct snd_seq_port_callback pcallbacks;
 348        struct snd_seq_port_info *pinfo;
 349        int err;
 350
 351        if (rdev->client >= 0)
 352                return 0;
 353
 354        pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
 355        if (!pinfo) {
 356                err = -ENOMEM;
 357                goto __error;
 358        }
 359
 360        client = snd_seq_create_kernel_client(rdev->card, rdev->device,
 361                                              "%s %d-%d", rdev->rmidi->name,
 362                                              rdev->card->number,
 363                                              rdev->device);
 364        if (client < 0) {
 365                err = client;
 366                goto __error;
 367        }
 368        rdev->client = client;
 369
 370        /* create a port */
 371        pinfo->addr.client = client;
 372        sprintf(pinfo->name, "VirMIDI %d-%d", rdev->card->number, rdev->device);
 373        /* set all capabilities */
 374        pinfo->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
 375        pinfo->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
 376        pinfo->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
 377        pinfo->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
 378                | SNDRV_SEQ_PORT_TYPE_SOFTWARE
 379                | SNDRV_SEQ_PORT_TYPE_PORT;
 380        pinfo->midi_channels = 16;
 381        memset(&pcallbacks, 0, sizeof(pcallbacks));
 382        pcallbacks.owner = THIS_MODULE;
 383        pcallbacks.private_data = rdev;
 384        pcallbacks.subscribe = snd_virmidi_subscribe;
 385        pcallbacks.unsubscribe = snd_virmidi_unsubscribe;
 386        pcallbacks.use = snd_virmidi_use;
 387        pcallbacks.unuse = snd_virmidi_unuse;
 388        pcallbacks.event_input = snd_virmidi_event_input;
 389        pinfo->kernel = &pcallbacks;
 390        err = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, pinfo);
 391        if (err < 0) {
 392                snd_seq_delete_kernel_client(client);
 393                rdev->client = -1;
 394                goto __error;
 395        }
 396
 397        rdev->port = pinfo->addr.port;
 398        err = 0; /* success */
 399
 400 __error:
 401        kfree(pinfo);
 402        return err;
 403}
 404
 405
 406/*
 407 * release the sequencer client
 408 */
 409static void snd_virmidi_dev_detach_seq(struct snd_virmidi_dev *rdev)
 410{
 411        if (rdev->client >= 0) {
 412                snd_seq_delete_kernel_client(rdev->client);
 413                rdev->client = -1;
 414        }
 415}
 416
 417/*
 418 * register the device
 419 */
 420static int snd_virmidi_dev_register(struct snd_rawmidi *rmidi)
 421{
 422        struct snd_virmidi_dev *rdev = rmidi->private_data;
 423        int err;
 424
 425        switch (rdev->seq_mode) {
 426        case SNDRV_VIRMIDI_SEQ_DISPATCH:
 427                err = snd_virmidi_dev_attach_seq(rdev);
 428                if (err < 0)
 429                        return err;
 430                break;
 431        case SNDRV_VIRMIDI_SEQ_ATTACH:
 432                if (rdev->client == 0)
 433                        return -EINVAL;
 434                /* should check presence of port more strictly.. */
 435                break;
 436        default:
 437                pr_err("ALSA: seq_virmidi: seq_mode is not set: %d\n", rdev->seq_mode);
 438                return -EINVAL;
 439        }
 440        return 0;
 441}
 442
 443
 444/*
 445 * unregister the device
 446 */
 447static int snd_virmidi_dev_unregister(struct snd_rawmidi *rmidi)
 448{
 449        struct snd_virmidi_dev *rdev = rmidi->private_data;
 450
 451        if (rdev->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH)
 452                snd_virmidi_dev_detach_seq(rdev);
 453        return 0;
 454}
 455
 456/*
 457 *
 458 */
 459static const struct snd_rawmidi_global_ops snd_virmidi_global_ops = {
 460        .dev_register = snd_virmidi_dev_register,
 461        .dev_unregister = snd_virmidi_dev_unregister,
 462};
 463
 464/*
 465 * free device
 466 */
 467static void snd_virmidi_free(struct snd_rawmidi *rmidi)
 468{
 469        struct snd_virmidi_dev *rdev = rmidi->private_data;
 470        kfree(rdev);
 471}
 472
 473/*
 474 * create a new device
 475 *
 476 */
 477/* exported */
 478int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmidi)
 479{
 480        struct snd_rawmidi *rmidi;
 481        struct snd_virmidi_dev *rdev;
 482        int err;
 483        
 484        *rrmidi = NULL;
 485        err = snd_rawmidi_new(card, "VirMidi", device,
 486                              16,       /* may be configurable */
 487                              16,       /* may be configurable */
 488                              &rmidi);
 489        if (err < 0)
 490                return err;
 491        strcpy(rmidi->name, rmidi->id);
 492        rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
 493        if (rdev == NULL) {
 494                snd_device_free(card, rmidi);
 495                return -ENOMEM;
 496        }
 497        rdev->card = card;
 498        rdev->rmidi = rmidi;
 499        rdev->device = device;
 500        rdev->client = -1;
 501        init_rwsem(&rdev->filelist_sem);
 502        rwlock_init(&rdev->filelist_lock);
 503        INIT_LIST_HEAD(&rdev->filelist);
 504        rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
 505        rmidi->private_data = rdev;
 506        rmidi->private_free = snd_virmidi_free;
 507        rmidi->ops = &snd_virmidi_global_ops;
 508        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_virmidi_input_ops);
 509        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_virmidi_output_ops);
 510        rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT |
 511                            SNDRV_RAWMIDI_INFO_OUTPUT |
 512                            SNDRV_RAWMIDI_INFO_DUPLEX;
 513        *rrmidi = rmidi;
 514        return 0;
 515}
 516EXPORT_SYMBOL(snd_virmidi_new);
 517