2  * ALSA sequencer MIDI-through client
 
   3  * Copyright (c) 1999-2000 by Takashi Iwai <tiwai@suse.de>
 
   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.
 
  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.
 
  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
 
  21 #include <linux/init.h>
 
  22 #include <linux/slab.h>
 
  23 #include <linux/moduleparam.h>
 
  24 #include <sound/core.h>
 
  25 #include "seq_clientmgr.h"
 
  26 #include <sound/initval.h>
 
  27 #include <sound/asoundef.h>
 
  31   Sequencer MIDI-through client
 
  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.
 
  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.
 
  41         alias snd-seq-client-62  snd-seq-dummy
 
  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.
 
  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 /etc/modprobe.conf:
 
  51         option snd-seq-dummy ports=4
 
  53   The modle 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.
 
  62 MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
 
  63 MODULE_DESCRIPTION("ALSA sequencer MIDI-through client");
 
  64 MODULE_LICENSE("GPL");
 
  65 MODULE_ALIAS("snd-seq-client-" __stringify(SNDRV_SEQ_CLIENT_DUMMY));
 
  70 module_param(ports, int, 0444);
 
  71 MODULE_PARM_DESC(ports, "number of ports to be created");
 
  72 module_param(duplex, bool, 0444);
 
  73 MODULE_PARM_DESC(duplex, "create DUPLEX ports");
 
  75 struct snd_seq_dummy_port {
 
  82 static int my_client = -1;
 
  85  * unuse callback - send ALL_SOUNDS_OFF and RESET_CONTROLLERS events
 
  87  * Note: this callback is called only after all subscribers are removed.
 
  90 dummy_unuse(void *private_data, struct snd_seq_port_subscribe *info)
 
  92         struct snd_seq_dummy_port *p;
 
  94         struct snd_seq_event ev;
 
  97         memset(&ev, 0, sizeof(ev));
 
  99                 ev.source.port = p->connect;
 
 101                 ev.source.port = p->port;
 
 102         ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
 
 103         ev.type = SNDRV_SEQ_EVENT_CONTROLLER;
 
 104         for (i = 0; i < 16; i++) {
 
 105                 ev.data.control.channel = i;
 
 106                 ev.data.control.param = MIDI_CTL_ALL_SOUNDS_OFF;
 
 107                 snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
 
 108                 ev.data.control.param = MIDI_CTL_RESET_CONTROLLERS;
 
 109                 snd_seq_kernel_client_dispatch(p->client, &ev, 0, 0);
 
 115  * event input callback - just redirect events to subscribers
 
 118 dummy_input(struct snd_seq_event *ev, int direct, void *private_data,
 
 121         struct snd_seq_dummy_port *p;
 
 122         struct snd_seq_event tmpev;
 
 125         if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM ||
 
 126             ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
 
 127                 return 0; /* ignore system messages */
 
 130                 tmpev.source.port = p->connect;
 
 132                 tmpev.source.port = p->port;
 
 133         tmpev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
 
 134         return snd_seq_kernel_client_dispatch(p->client, &tmpev, atomic, hop);
 
 138  * free_private callback
 
 141 dummy_free(void *private_data)
 
 149 static struct snd_seq_dummy_port __init *
 
 150 create_port(int idx, int type)
 
 152         struct snd_seq_port_info pinfo;
 
 153         struct snd_seq_port_callback pcb;
 
 154         struct snd_seq_dummy_port *rec;
 
 156         if ((rec = kzalloc(sizeof(*rec), GFP_KERNEL)) == NULL)
 
 159         rec->client = my_client;
 
 160         rec->duplex = duplex;
 
 162         memset(&pinfo, 0, sizeof(pinfo));
 
 163         pinfo.addr.client = my_client;
 
 165                 sprintf(pinfo.name, "Midi Through Port-%d:%c", idx,
 
 168                 sprintf(pinfo.name, "Midi Through Port-%d", idx);
 
 169         pinfo.capability = SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
 
 170         pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
 
 172                 pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
 
 173         pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
 
 174                 | SNDRV_SEQ_PORT_TYPE_SOFTWARE
 
 175                 | SNDRV_SEQ_PORT_TYPE_PORT;
 
 176         memset(&pcb, 0, sizeof(pcb));
 
 177         pcb.owner = THIS_MODULE;
 
 178         pcb.unuse = dummy_unuse;
 
 179         pcb.event_input = dummy_input;
 
 180         pcb.private_free = dummy_free;
 
 181         pcb.private_data = rec;
 
 183         if (snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo) < 0) {
 
 187         rec->port = pinfo.addr.port;
 
 192  * register client and create ports
 
 195 register_client(void)
 
 197         struct snd_seq_dummy_port *rec1, *rec2;
 
 201                 snd_printk(KERN_ERR "invalid number of ports %d\n", ports);
 
 206         my_client = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_DUMMY,
 
 212         for (i = 0; i < ports; i++) {
 
 213                 rec1 = create_port(i, 0);
 
 215                         snd_seq_delete_kernel_client(my_client);
 
 219                         rec2 = create_port(i, 1);
 
 221                                 snd_seq_delete_kernel_client(my_client);
 
 224                         rec1->connect = rec2->port;
 
 225                         rec2->connect = rec1->port;
 
 233  * delete client if exists
 
 239                 snd_seq_delete_kernel_client(my_client);
 
 246 static int __init alsa_seq_dummy_init(void)
 
 249         snd_seq_autoload_lock();
 
 250         err = register_client();
 
 251         snd_seq_autoload_unlock();
 
 255 static void __exit alsa_seq_dummy_exit(void)
 
 260 module_init(alsa_seq_dummy_init)
 
 261 module_exit(alsa_seq_dummy_exit)