Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * ALSA sequencer MIDI-through client
0004  * Copyright (c) 1999-2000 by Takashi Iwai <tiwai@suse.de>
0005  */
0006 
0007 #include <linux/init.h>
0008 #include <linux/slab.h>
0009 #include <linux/module.h>
0010 #include <sound/core.h>
0011 #include "seq_clientmgr.h"
0012 #include <sound/initval.h>
0013 #include <sound/asoundef.h>
0014 
0015 /*
0016 
0017   Sequencer MIDI-through client
0018 
0019   This gives a simple midi-through client.  All the normal input events
0020   are redirected to output port immediately.
0021   The routing can be done via aconnect program in alsa-utils.
0022 
0023   Each client has a static client number 14 (= SNDRV_SEQ_CLIENT_DUMMY).
0024   If you want to auto-load this module, you may add the following alias
0025   in your /etc/conf.modules file.
0026 
0027     alias snd-seq-client-14  snd-seq-dummy
0028 
0029   The module is loaded on demand for client 14, or /proc/asound/seq/
0030   is accessed.  If you don't need this module to be loaded, alias
0031   snd-seq-client-14 as "off".  This will help modprobe.
0032 
0033   The number of ports to be created can be specified via the module
0034   parameter "ports".  For example, to create four ports, add the
0035   following option in a configuration file under /etc/modprobe.d/:
0036 
0037     option snd-seq-dummy ports=4
0038 
0039   The model option "duplex=1" enables duplex operation to the port.
0040   In duplex mode, a pair of ports are created instead of single port,
0041   and events are tunneled between pair-ports.  For example, input to
0042   port A is sent to output port of another port B and vice versa.
0043   In duplex mode, each port has DUPLEX capability.
0044 
0045  */
0046 
0047 
0048 MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
0049 MODULE_DESCRIPTION("ALSA sequencer MIDI-through client");
0050 MODULE_LICENSE("GPL");
0051 MODULE_ALIAS("snd-seq-client-" __stringify(SNDRV_SEQ_CLIENT_DUMMY));
0052 
0053 static int ports = 1;
0054 static bool duplex;
0055 
0056 module_param(ports, int, 0444);
0057 MODULE_PARM_DESC(ports, "number of ports to be created");
0058 module_param(duplex, bool, 0444);
0059 MODULE_PARM_DESC(duplex, "create DUPLEX ports");
0060 
0061 struct snd_seq_dummy_port {
0062     int client;
0063     int port;
0064     int duplex;
0065     int connect;
0066 };
0067 
0068 static int my_client = -1;
0069 
0070 /*
0071  * event input callback - just redirect events to subscribers
0072  */
0073 static int
0074 dummy_input(struct snd_seq_event *ev, int direct, void *private_data,
0075         int atomic, int hop)
0076 {
0077     struct snd_seq_dummy_port *p;
0078     struct snd_seq_event tmpev;
0079 
0080     p = private_data;
0081     if (ev->source.client == SNDRV_SEQ_CLIENT_SYSTEM ||
0082         ev->type == SNDRV_SEQ_EVENT_KERNEL_ERROR)
0083         return 0; /* ignore system messages */
0084     tmpev = *ev;
0085     if (p->duplex)
0086         tmpev.source.port = p->connect;
0087     else
0088         tmpev.source.port = p->port;
0089     tmpev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
0090     return snd_seq_kernel_client_dispatch(p->client, &tmpev, atomic, hop);
0091 }
0092 
0093 /*
0094  * free_private callback
0095  */
0096 static void
0097 dummy_free(void *private_data)
0098 {
0099     kfree(private_data);
0100 }
0101 
0102 /*
0103  * create a port
0104  */
0105 static struct snd_seq_dummy_port __init *
0106 create_port(int idx, int type)
0107 {
0108     struct snd_seq_port_info pinfo;
0109     struct snd_seq_port_callback pcb;
0110     struct snd_seq_dummy_port *rec;
0111 
0112     rec = kzalloc(sizeof(*rec), GFP_KERNEL);
0113     if (!rec)
0114         return NULL;
0115 
0116     rec->client = my_client;
0117     rec->duplex = duplex;
0118     rec->connect = 0;
0119     memset(&pinfo, 0, sizeof(pinfo));
0120     pinfo.addr.client = my_client;
0121     if (duplex)
0122         sprintf(pinfo.name, "Midi Through Port-%d:%c", idx,
0123             (type ? 'B' : 'A'));
0124     else
0125         sprintf(pinfo.name, "Midi Through Port-%d", idx);
0126     pinfo.capability = SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
0127     pinfo.capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
0128     if (duplex)
0129         pinfo.capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
0130     pinfo.type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
0131         | SNDRV_SEQ_PORT_TYPE_SOFTWARE
0132         | SNDRV_SEQ_PORT_TYPE_PORT;
0133     memset(&pcb, 0, sizeof(pcb));
0134     pcb.owner = THIS_MODULE;
0135     pcb.event_input = dummy_input;
0136     pcb.private_free = dummy_free;
0137     pcb.private_data = rec;
0138     pinfo.kernel = &pcb;
0139     if (snd_seq_kernel_client_ctl(my_client, SNDRV_SEQ_IOCTL_CREATE_PORT, &pinfo) < 0) {
0140         kfree(rec);
0141         return NULL;
0142     }
0143     rec->port = pinfo.addr.port;
0144     return rec;
0145 }
0146 
0147 /*
0148  * register client and create ports
0149  */
0150 static int __init
0151 register_client(void)
0152 {
0153     struct snd_seq_dummy_port *rec1, *rec2;
0154     int i;
0155 
0156     if (ports < 1) {
0157         pr_err("ALSA: seq_dummy: invalid number of ports %d\n", ports);
0158         return -EINVAL;
0159     }
0160 
0161     /* create client */
0162     my_client = snd_seq_create_kernel_client(NULL, SNDRV_SEQ_CLIENT_DUMMY,
0163                          "Midi Through");
0164     if (my_client < 0)
0165         return my_client;
0166 
0167     /* create ports */
0168     for (i = 0; i < ports; i++) {
0169         rec1 = create_port(i, 0);
0170         if (rec1 == NULL) {
0171             snd_seq_delete_kernel_client(my_client);
0172             return -ENOMEM;
0173         }
0174         if (duplex) {
0175             rec2 = create_port(i, 1);
0176             if (rec2 == NULL) {
0177                 snd_seq_delete_kernel_client(my_client);
0178                 return -ENOMEM;
0179             }
0180             rec1->connect = rec2->port;
0181             rec2->connect = rec1->port;
0182         }
0183     }
0184 
0185     return 0;
0186 }
0187 
0188 /*
0189  * delete client if exists
0190  */
0191 static void __exit
0192 delete_client(void)
0193 {
0194     if (my_client >= 0)
0195         snd_seq_delete_kernel_client(my_client);
0196 }
0197 
0198 /*
0199  *  Init part
0200  */
0201 
0202 static int __init alsa_seq_dummy_init(void)
0203 {
0204     return register_client();
0205 }
0206 
0207 static void __exit alsa_seq_dummy_exit(void)
0208 {
0209     delete_client();
0210 }
0211 
0212 module_init(alsa_seq_dummy_init)
0213 module_exit(alsa_seq_dummy_exit)