linux/sound/core/seq/seq_dummy.c
<<
>>
Prefs
   1/*
   2 * ALSA sequencer MIDI-through client
   3 * Copyright (c) 1999-2000 by Takashi Iwai <tiwai@suse.de>
   4 *
   5 *   This program is free software; you can redistribute it and/or modify
   6 *   it under the terms of the GNU General Public License as published by
   7 *   the Free Software Foundation; either version 2 of the License, or
   8 *   (at your option) any later version.
   9 *
  10 *   This program is distributed in the hope that it will be useful,
  11 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 *   GNU General Public License for more details.
  14 *
  15 *   You should have received a copy of the GNU General Public License
  16 *   along with this program; if not, write to the Free Software
  17 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18 *
  19 */
  20
  21#include <linux/init.h>
  22#include <linux/slab.h>
  23#include <linux/module.h>
  24#include <sound/core.h>
  25#include "seq_clientmgr.h"
  26#include <sound/initval.h>
  27#include <sound/asoundef.h>
  28
  29/*
  30
  31  Sequencer MIDI-through client
  32
  33  This gives a simple midi-through client.  All the normal input events
  34  are redirected to output port immediately.
  35  The routing can be done via aconnect program in alsa-utils.
  36
  37  Each client has a static client number 62 (= SNDRV_SEQ_CLIENT_DUMMY).
  38  If you want to auto-load this module, you may add the following alias
  39  in your /etc/conf.modules file.
  40
  41        alias snd-seq-client-62  snd-seq-dummy
  42
  43  The module is loaded on demand for client 62, or /proc/asound/seq/
  44  is accessed.  If you don't need this module to be loaded, alias
  45  snd-seq-client-62 as "off".  This will help modprobe.
  46
  47  The number of ports to be created can be specified via the module
  48  parameter "ports".  For example, to create four ports, add the
  49  following option in a configuration file under /etc/modprobe.d/:
  50
  51        option snd-seq-dummy ports=4
  52
  53  The model option "duplex=1" enables duplex operation to the port.
  54  In duplex mode, a pair of ports are created instead of single port,
  55  and events are tunneled between pair-ports.  For example, input to
  56  port A is sent to output port of another port B and vice versa.
  57  In duplex mode, each port has DUPLEX capability.
  58
  59 */
  60
  61
  62MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
  63MODULE_DESCRIPTION("ALSA sequencer MIDI-through client");
  64MODULE_LICENSE("GPL");
  65MODULE_ALIAS("snd-seq-client-" __stringify(SNDRV_SEQ_CLIENT_DUMMY));
  66
  67static int ports = 1;
  68static bool duplex;
  69
  70module_param(ports, int, 0444);
  71MODULE_PARM_DESC(ports, "number of ports to be created");
  72module_param(duplex, bool, 0444);
  73MODULE_PARM_DESC(duplex, "create DUPLEX ports");
  74
  75struct snd_seq_dummy_port {
  76        int client;
  77        int port;
  78        int duplex;
  79        int connect;
  80};
  81
  82static int my_client = -1;
  83
  84/*
  85 * event input callback - just redirect events to subscribers
  86 */
  87static int
  88dummy_input(struct snd_seq_event *ev, int direct, void *private_data,
  89            int atomic, int hop)
  90{
  91        struct snd_seq_dummy_port *p;
  92        struct snd_seq_event tmpev;
  93
  94        p = private_data;
  95        if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM ||
  96            ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
  97                return 0; /* ignore system messages */
  98        tmpev = *ev;
  99        if (p->duplex)
 100                tmpev.source.port = p->connect;
 101        else
 102                tmpev.source.port = p->port;
 103        tmpev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
 104        return snd_seq_kernel_client_dispatch(p->client, &tmpev, atomic, hop);
 105}
 106
 107/*
 108 * free_private callback
 109 */
 110static void
 111dummy_free(void *private_data)
 112{
 113        kfree(private_data);
 114}
 115
 116/*
 117 * create a port
 118 */
 119static struct snd_seq_dummy_port __init *
 120create_port(int idx, int type)
 121{
 122        struct snd_seq_port_info pinfo;
 123        struct snd_seq_port_callback pcb;
 124        struct snd_seq_dummy_port *rec;
 125
 126        if ((rec = kzalloc(sizeof(*rec), GFP_KERNEL)) == NULL)
 127                return NULL;
 128
 129        rec->client = my_client;
 130        rec->duplex = duplex;
 131        rec->connect = 0;
 132        memset(&pinfo, 0, sizeof(pinfo));
 133        pinfo.addr.client = my_client;
 134        if (duplex)
 135                sprintf(pinfo.name, "Midi Through Port-%d:%c", idx,
 136                        (type ? 'B' : 'A'));
 137        else
 138                sprintf(pinfo.name, "Midi Through Port-%d", idx);
 139        pinfo.capability = SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
 140        pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
 141        if (duplex)
 142                pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
 143        pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
 144                | SNDRV_SEQ_PORT_TYPE_SOFTWARE
 145                | SNDRV_SEQ_PORT_TYPE_PORT;
 146        memset(&pcb, 0, sizeof(pcb));
 147        pcb.owner = THIS_MODULE;
 148        pcb.event_input = dummy_input;
 149        pcb.private_free = dummy_free;
 150        pcb.private_data = rec;
 151        pinfo.kernel = &pcb;
 152        if (snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo) < 0) {
 153                kfree(rec);
 154                return NULL;
 155        }
 156        rec->port = pinfo.addr.port;
 157        return rec;
 158}
 159
 160/*
 161 * register client and create ports
 162 */
 163static int __init
 164register_client(void)
 165{
 166        struct snd_seq_dummy_port *rec1, *rec2;
 167        int i;
 168
 169        if (ports < 1) {
 170                pr_err("ALSA: seq_dummy: invalid number of ports %d\n", ports);
 171                return -EINVAL;
 172        }
 173
 174        /* create client */
 175        my_client = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_DUMMY,
 176                                                 "Midi Through");
 177        if (my_client < 0)
 178                return my_client;
 179
 180        /* create ports */
 181        for (i = 0; i < ports; i++) {
 182                rec1 = create_port(i, 0);
 183                if (rec1 == NULL) {
 184                        snd_seq_delete_kernel_client(my_client);
 185                        return -ENOMEM;
 186                }
 187                if (duplex) {
 188                        rec2 = create_port(i, 1);
 189                        if (rec2 == NULL) {
 190                                snd_seq_delete_kernel_client(my_client);
 191                                return -ENOMEM;
 192                        }
 193                        rec1->connect = rec2->port;
 194                        rec2->connect = rec1->port;
 195                }
 196        }
 197
 198        return 0;
 199}
 200
 201/*
 202 * delete client if exists
 203 */
 204static void __exit
 205delete_client(void)
 206{
 207        if (my_client >= 0)
 208                snd_seq_delete_kernel_client(my_client);
 209}
 210
 211/*
 212 *  Init part
 213 */
 214
 215static int __init alsa_seq_dummy_init(void)
 216{
 217        return register_client();
 218}
 219
 220static void __exit alsa_seq_dummy_exit(void)
 221{
 222        delete_client();
 223}
 224
 225module_init(alsa_seq_dummy_init)
 226module_exit(alsa_seq_dummy_exit)
 227