~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/core/seq/seq_dummy.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php