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                } else {
  85                        len = snd_midi_event_decode(vmidi->parser, msg, sizeof(msg), ev);
  86                        if (len > 0)
  87                                snd_rawmidi_receive(vmidi->substream, msg, len);
  88                }
  89        }
  90        if (atomic)
  91                read_unlock(&rdev->filelist_lock);
  92        else
  93                up_read(&rdev->filelist_sem);
  94
  95        return 0;
  96}
  97
  98/*
  99 * event handler of virmidi port
 100 */
 101static int snd_virmidi_event_input(struct snd_seq_event *ev, int direct,
 102                                   void *private_data, int atomic, int hop)
 103{
 104        struct snd_virmidi_dev *rdev;
 105
 106        rdev = private_data;
 107        if (!(rdev->flags & SNDRV_VIRMIDI_USE))
 108                return 0; /* ignored */
 109        return snd_virmidi_dev_receive_event(rdev, ev, atomic);
 110}
 111
 112/*
 113 * trigger rawmidi stream for input
 114 */
 115static void snd_virmidi_input_trigger(struct snd_rawmidi_substream *substream, int up)
 116{
 117        struct snd_virmidi *vmidi = substream->runtime->private_data;
 118
 119        WRITE_ONCE(vmidi->trigger, !!up);
 120}
 121
 122/* process rawmidi bytes and send events;
 123 * we need no lock here for vmidi->event since it's handled only in this work
 124 */
 125static void snd_vmidi_output_work(struct work_struct *work)
 126{
 127        struct snd_virmidi *vmidi;
 128        struct snd_rawmidi_substream *substream;
 129        unsigned char input;
 130        int ret;
 131
 132        vmidi = container_of(work, struct snd_virmidi, output_work);
 133        substream = vmidi->substream;
 134
 135        /* discard the outputs in dispatch mode unless subscribed */
 136        if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
 137            !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
 138                snd_rawmidi_proceed(substream);
 139                return;
 140        }
 141
 142        while (READ_ONCE(vmidi->trigger)) {
 143                if (snd_rawmidi_transmit(substream, &input, 1) != 1)
 144                        break;
 145                if (!snd_midi_event_encode_byte(vmidi->parser, input,
 146                                                &vmidi->event))
 147                        continue;
 148                if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
 149                        ret = snd_seq_kernel_client_dispatch(vmidi->client,
 150                                                             &vmidi->event,
 151                                                             false, 0);
 152                        vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
 153                        if (ret < 0)
 154                                break;
 155                }
 156                /* rawmidi input might be huge, allow to have a break */
 157                cond_resched();
 158        }
 159}
 160
 161/*
 162 * trigger rawmidi stream for output
 163 */
 164static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream, int up)
 165{
 166        struct snd_virmidi *vmidi = substream->runtime->private_data;
 167
 168        WRITE_ONCE(vmidi->trigger, !!up);
 169        if (up)
 170                queue_work(system_highpri_wq, &vmidi->output_work);
 171}
 172
 173/*
 174 * open rawmidi handle for input
 175 */
 176static int snd_virmidi_input_open(struct snd_rawmidi_substream *substream)
 177{
 178        struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
 179        struct snd_rawmidi_runtime *runtime = substream->runtime;
 180        struct snd_virmidi *vmidi;
 181
 182        vmidi = kzalloc(sizeof(*vmidi), GFP_KERNEL);
 183        if (vmidi == NULL)
 184                return -ENOMEM;
 185        vmidi->substream = substream;
 186        if (snd_midi_event_new(0, &vmidi->parser) < 0) {
 187                kfree(vmidi);
 188                return -ENOMEM;
 189        }
 190        vmidi->seq_mode = rdev->seq_mode;
 191        vmidi->client = rdev->client;
 192        vmidi->port = rdev->port;       
 193        runtime->private_data = vmidi;
 194        down_write(&rdev->filelist_sem);
 195        write_lock_irq(&rdev->filelist_lock);
 196        list_add_tail(&vmidi->list, &rdev->filelist);
 197        write_unlock_irq(&rdev->filelist_lock);
 198        up_write(&rdev->filelist_sem);
 199        vmidi->rdev = rdev;
 200        return 0;
 201}
 202
 203/*
 204 * open rawmidi handle for output
 205 */
 206static int snd_virmidi_output_open(struct snd_rawmidi_substream *substream)
 207{
 208        struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
 209        struct snd_rawmidi_runtime *runtime = substream->runtime;
 210        struct snd_virmidi *vmidi;
 211
 212        vmidi = kzalloc(sizeof(*vmidi), GFP_KERNEL);
 213        if (vmidi == NULL)
 214                return -ENOMEM;
 215        vmidi->substream = substream;
 216        if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &vmidi->parser) < 0) {
 217                kfree(vmidi);
 218                return -ENOMEM;
 219        }
 220        vmidi->seq_mode = rdev->seq_mode;
 221        vmidi->client = rdev->client;
 222        vmidi->port = rdev->port;
 223        snd_virmidi_init_event(vmidi, &vmidi->event);
 224        vmidi->rdev = rdev;
 225        INIT_WORK(&vmidi->output_work, snd_vmidi_output_work);
 226        runtime->private_data = vmidi;
 227        return 0;
 228}
 229
 230/*
 231 * close rawmidi handle for input
 232 */
 233static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream)
 234{
 235        struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
 236        struct snd_virmidi *vmidi = substream->runtime->private_data;
 237
 238        down_write(&rdev->filelist_sem);
 239        write_lock_irq(&rdev->filelist_lock);
 240        list_del(&vmidi->list);
 241        write_unlock_irq(&rdev->filelist_lock);
 242        up_write(&rdev->filelist_sem);
 243        snd_midi_event_free(vmidi->parser);
 244        substream->runtime->private_data = NULL;
 245        kfree(vmidi);
 246        return 0;
 247}
 248
 249/*
 250 * close rawmidi handle for output
 251 */
 252static int snd_virmidi_output_close(struct snd_rawmidi_substream *substream)
 253{
 254        struct snd_virmidi *vmidi = substream->runtime->private_data;
 255
 256        WRITE_ONCE(vmidi->trigger, false); /* to be sure */
 257        cancel_work_sync(&vmidi->output_work);
 258        snd_midi_event_free(vmidi->parser);
 259        substream->runtime->private_data = NULL;
 260        kfree(vmidi);
 261        return 0;
 262}
 263
 264/*
 265 * subscribe callback - allow output to rawmidi device
 266 */
 267static int snd_virmidi_subscribe(void *private_data,
 268                                 struct snd_seq_port_subscribe *info)
 269{
 270        struct snd_virmidi_dev *rdev;
 271
 272        rdev = private_data;
 273        if (!try_module_get(rdev->card->module))
 274                return -EFAULT;
 275        rdev->flags |= SNDRV_VIRMIDI_SUBSCRIBE;
 276        return 0;
 277}
 278
 279/*
 280 * unsubscribe callback - disallow output to rawmidi device
 281 */
 282static int snd_virmidi_unsubscribe(void *private_data,
 283                                   struct snd_seq_port_subscribe *info)
 284{
 285        struct snd_virmidi_dev *rdev;
 286
 287        rdev = private_data;
 288        rdev->flags &= ~SNDRV_VIRMIDI_SUBSCRIBE;
 289        module_put(rdev->card->module);
 290        return 0;
 291}
 292
 293
 294/*
 295 * use callback - allow input to rawmidi device
 296 */
 297static int snd_virmidi_use(void *private_data,
 298                           struct snd_seq_port_subscribe *info)
 299{
 300        struct snd_virmidi_dev *rdev;
 301
 302        rdev = private_data;
 303        if (!try_module_get(rdev->card->module))
 304                return -EFAULT;
 305        rdev->flags |= SNDRV_VIRMIDI_USE;
 306        return 0;
 307}
 308
 309/*
 310 * unuse callback - disallow input to rawmidi device
 311 */
 312static int snd_virmidi_unuse(void *private_data,
 313                             struct snd_seq_port_subscribe *info)
 314{
 315        struct snd_virmidi_dev *rdev;
 316
 317        rdev = private_data;
 318        rdev->flags &= ~SNDRV_VIRMIDI_USE;
 319        module_put(rdev->card->module);
 320        return 0;
 321}
 322
 323
 324/*
 325 *  Register functions
 326 */
 327
 328static const struct snd_rawmidi_ops snd_virmidi_input_ops = {
 329        .open = snd_virmidi_input_open,
 330        .close = snd_virmidi_input_close,
 331        .trigger = snd_virmidi_input_trigger,
 332};
 333
 334static const struct snd_rawmidi_ops snd_virmidi_output_ops = {
 335        .open = snd_virmidi_output_open,
 336        .close = snd_virmidi_output_close,
 337        .trigger = snd_virmidi_output_trigger,
 338};
 339
 340/*
 341 * create a sequencer client and a port
 342 */
 343static int snd_virmidi_dev_attach_seq(struct snd_virmidi_dev *rdev)
 344{
 345        int client;
 346        struct snd_seq_port_callback pcallbacks;
 347        struct snd_seq_port_info *pinfo;
 348        int err;
 349
 350        if (rdev->client >= 0)
 351                return 0;
 352
 353        pinfo = kzalloc(sizeof(*pinfo), GFP_KERNEL);
 354        if (!pinfo) {
 355                err = -ENOMEM;
 356                goto __error;
 357        }
 358
 359        client = snd_seq_create_kernel_client(rdev->card, rdev->device,
 360                                              "%s %d-%d", rdev->rmidi->name,
 361                                              rdev->card->number,
 362                                              rdev->device);
 363        if (client < 0) {
 364                err = client;
 365                goto __error;
 366        }
 367        rdev->client = client;
 368
 369        /* create a port */
 370        pinfo->addr.client = client;
 371        sprintf(pinfo->name, "VirMIDI %d-%d", rdev->card->number, rdev->device);
 372        /* set all capabilities */
 373        pinfo->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
 374        pinfo->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
 375        pinfo->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
 376        pinfo->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
 377                | SNDRV_SEQ_PORT_TYPE_SOFTWARE
 378                | SNDRV_SEQ_PORT_TYPE_PORT;
 379        pinfo->midi_channels = 16;
 380        memset(&pcallbacks, 0, sizeof(pcallbacks));
 381        pcallbacks.owner = THIS_MODULE;
 382        pcallbacks.private_data = rdev;
 383        pcallbacks.subscribe = snd_virmidi_subscribe;
 384        pcallbacks.unsubscribe = snd_virmidi_unsubscribe;
 385        pcallbacks.use = snd_virmidi_use;
 386        pcallbacks.unuse = snd_virmidi_unuse;
 387        pcallbacks.event_input = snd_virmidi_event_input;
 388        pinfo->kernel = &pcallbacks;
 389        err = snd_seq_kernel_client_ctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, pinfo);
 390        if (err < 0) {
 391                snd_seq_delete_kernel_client(client);
 392                rdev->client = -1;
 393                goto __error;
 394        }
 395
 396        rdev->port = pinfo->addr.port;
 397        err = 0; /* success */
 398
 399 __error:
 400        kfree(pinfo);
 401        return err;
 402}
 403
 404
 405/*
 406 * release the sequencer client
 407 */
 408static void snd_virmidi_dev_detach_seq(struct snd_virmidi_dev *rdev)
 409{
 410        if (rdev->client >= 0) {
 411                snd_seq_delete_kernel_client(rdev->client);
 412                rdev->client = -1;
 413        }
 414}
 415
 416/*
 417 * register the device
 418 */
 419static int snd_virmidi_dev_register(struct snd_rawmidi *rmidi)
 420{
 421        struct snd_virmidi_dev *rdev = rmidi->private_data;
 422        int err;
 423
 424        switch (rdev->seq_mode) {
 425        case SNDRV_VIRMIDI_SEQ_DISPATCH:
 426                err = snd_virmidi_dev_attach_seq(rdev);
 427                if (err < 0)
 428                        return err;
 429                break;
 430        case SNDRV_VIRMIDI_SEQ_ATTACH:
 431                if (rdev->client == 0)
 432                        return -EINVAL;
 433                /* should check presence of port more strictly.. */
 434                break;
 435        default:
 436                pr_err("ALSA: seq_virmidi: seq_mode is not set: %d\n", rdev->seq_mode);
 437                return -EINVAL;
 438        }
 439        return 0;
 440}
 441
 442
 443/*
 444 * unregister the device
 445 */
 446static int snd_virmidi_dev_unregister(struct snd_rawmidi *rmidi)
 447{
 448        struct snd_virmidi_dev *rdev = rmidi->private_data;
 449
 450        if (rdev->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH)
 451                snd_virmidi_dev_detach_seq(rdev);
 452        return 0;
 453}
 454
 455/*
 456 *
 457 */
 458static const struct snd_rawmidi_global_ops snd_virmidi_global_ops = {
 459        .dev_register = snd_virmidi_dev_register,
 460        .dev_unregister = snd_virmidi_dev_unregister,
 461};
 462
 463/*
 464 * free device
 465 */
 466static void snd_virmidi_free(struct snd_rawmidi *rmidi)
 467{
 468        struct snd_virmidi_dev *rdev = rmidi->private_data;
 469        kfree(rdev);
 470}
 471
 472/*
 473 * create a new device
 474 *
 475 */
 476/* exported */
 477int snd_virmidi_new(struct snd_card *card, int device, struct snd_rawmidi **rrmidi)
 478{
 479        struct snd_rawmidi *rmidi;
 480        struct snd_virmidi_dev *rdev;
 481        int err;
 482        
 483        *rrmidi = NULL;
 484        if ((err = snd_rawmidi_new(card, "VirMidi", device,
 485                                   16,  /* may be configurable */
 486                                   16,  /* may be configurable */
 487                                   &rmidi)) < 0)
 488                return err;
 489        strcpy(rmidi->name, rmidi->id);
 490        rdev = kzalloc(sizeof(*rdev), GFP_KERNEL);
 491        if (rdev == NULL) {
 492                snd_device_free(card, rmidi);
 493                return -ENOMEM;
 494        }
 495        rdev->card = card;
 496        rdev->rmidi = rmidi;
 497        rdev->device = device;
 498        rdev->client = -1;
 499        init_rwsem(&rdev->filelist_sem);
 500        rwlock_init(&rdev->filelist_lock);
 501        INIT_LIST_HEAD(&rdev->filelist);
 502        rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
 503        rmidi->private_data = rdev;
 504        rmidi->private_free = snd_virmidi_free;
 505        rmidi->ops = &snd_virmidi_global_ops;
 506        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_virmidi_input_ops);
 507        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_virmidi_output_ops);
 508        rmidi->info_flags = SNDRV_RAWMIDI_INFO_INPUT |
 509                            SNDRV_RAWMIDI_INFO_OUTPUT |
 510                            SNDRV_RAWMIDI_INFO_DUPLEX;
 511        *rrmidi = rmidi;
 512        return 0;
 513}
 514EXPORT_SYMBOL(snd_virmidi_new);
 515