Blame view

sound/core/seq/seq_midi.c 14 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
  /*
   *   Generic MIDI synth driver for ALSA sequencer
   *   Copyright (c) 1998 by Frank van de Pol <fvdpol@coil.demon.nl>
c1017a4cd   Jaroslav Kysela   [ALSA] Changed Ja...
4
   *                         Jaroslav Kysela <perex@perex.cz>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License as published by
   *   the Free Software Foundation; either version 2 of the License, or
   *   (at your option) any later version.
   *
   *   This program is distributed in the hope that it will be useful,
   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *   GNU General Public License for more details.
   *
   *   You should have received a copy of the GNU General Public License
   *   along with this program; if not, write to the Free Software
   *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
   *
   */
   
  /* 
  Possible options for midisynth module:
  	- automatic opening of midi ports on first received event or subscription
  	  (close will be performed when client leaves)
  */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
30
31
  #include <linux/init.h>
  #include <linux/slab.h>
  #include <linux/errno.h>
  #include <linux/string.h>
  #include <linux/moduleparam.h>
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
32
  #include <linux/mutex.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33
34
35
36
37
38
  #include <sound/core.h>
  #include <sound/rawmidi.h>
  #include <sound/seq_kernel.h>
  #include <sound/seq_device.h>
  #include <sound/seq_midi_event.h>
  #include <sound/initval.h>
c1017a4cd   Jaroslav Kysela   [ALSA] Changed Ja...
39
  MODULE_AUTHOR("Frank van de Pol <fvdpol@coil.demon.nl>, Jaroslav Kysela <perex@perex.cz>");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
40
41
42
43
44
45
46
47
48
49
  MODULE_DESCRIPTION("Advanced Linux Sound Architecture sequencer MIDI synth.");
  MODULE_LICENSE("GPL");
  static int output_buffer_size = PAGE_SIZE;
  module_param(output_buffer_size, int, 0644);
  MODULE_PARM_DESC(output_buffer_size, "Output buffer size in bytes.");
  static int input_buffer_size = PAGE_SIZE;
  module_param(input_buffer_size, int, 0644);
  MODULE_PARM_DESC(input_buffer_size, "Input buffer size in bytes.");
  
  /* data for this midi synth driver */
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
50
51
  struct seq_midisynth {
  	struct snd_card *card;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
  	int device;
  	int subdevice;
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
54
55
  	struct snd_rawmidi_file input_rfile;
  	struct snd_rawmidi_file output_rfile;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
57
  	int seq_client;
  	int seq_port;
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
58
59
  	struct snd_midi_event *parser;
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60

c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
61
  struct seq_midisynth_client {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
  	int seq_client;
  	int num_ports;
  	int ports_per_device[SNDRV_RAWMIDI_DEVICES];
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
65
66
   	struct seq_midisynth *ports[SNDRV_RAWMIDI_DEVICES];
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67

c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
68
  static struct seq_midisynth_client *synths[SNDRV_CARDS];
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
69
  static DEFINE_MUTEX(register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70
71
  
  /* handle rawmidi input event (MIDI v1.0 stream) */
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
72
  static void snd_midi_input_event(struct snd_rawmidi_substream *substream)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
  {
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
74
75
76
  	struct snd_rawmidi_runtime *runtime;
  	struct seq_midisynth *msynth;
  	struct snd_seq_event ev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
78
79
80
81
82
  	char buf[16], *pbuf;
  	long res, count;
  
  	if (substream == NULL)
  		return;
  	runtime = substream->runtime;
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
83
  	msynth = runtime->private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
  	if (msynth == NULL)
  		return;
  	memset(&ev, 0, sizeof(ev));
  	while (runtime->avail > 0) {
  		res = snd_rawmidi_kernel_read(substream, buf, sizeof(buf));
  		if (res <= 0)
  			continue;
  		if (msynth->parser == NULL)
  			continue;
  		pbuf = buf;
  		while (res > 0) {
  			count = snd_midi_event_encode(msynth->parser, pbuf, res, &ev);
  			if (count < 0)
  				break;
  			pbuf += count;
  			res -= count;
  			if (ev.type != SNDRV_SEQ_EVENT_NONE) {
  				ev.source.port = msynth->seq_port;
  				ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
  				snd_seq_kernel_client_dispatch(msynth->seq_client, &ev, 1, 0);
  				/* clear event and reset header */
  				memset(&ev, 0, sizeof(ev));
  			}
  		}
  	}
  }
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
110
  static int dump_midi(struct snd_rawmidi_substream *substream, const char *buf, int count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
  {
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
112
  	struct snd_rawmidi_runtime *runtime;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
113
  	int tmp;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
114
115
  	if (snd_BUG_ON(!substream || !buf))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
  	runtime = substream->runtime;
  	if ((tmp = runtime->avail) < count) {
f907ed94f   Clemens Ladisch   seq-midi: always ...
118
119
120
  		if (printk_ratelimit())
  			snd_printk(KERN_ERR "MIDI output buffer overrun
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
122
123
124
125
126
  		return -ENOMEM;
  	}
  	if (snd_rawmidi_kernel_write(substream, buf, count) < count)
  		return -EINVAL;
  	return 0;
  }
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
127
  static int event_process_midi(struct snd_seq_event *ev, int direct,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
  			      void *private_data, int atomic, int hop)
  {
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
130
  	struct seq_midisynth *msynth = private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
  	unsigned char msg[10];	/* buffer for constructing midi messages */
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
132
  	struct snd_rawmidi_substream *substream;
d06e4c400   Clemens Ladisch   [ALSA] seq-midi -...
133
  	int len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
134

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
135
136
  	if (snd_BUG_ON(!msynth))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
138
139
140
141
142
143
144
145
146
  	substream = msynth->output_rfile.output;
  	if (substream == NULL)
  		return -ENODEV;
  	if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {	/* special case, to save space */
  		if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE) {
  			/* invalid event */
  			snd_printd("seq_midi: invalid sysex event flags = 0x%x
  ", ev->flags);
  			return 0;
  		}
d06e4c400   Clemens Ladisch   [ALSA] seq-midi -...
147
  		snd_seq_dump_var_event(ev, (snd_seq_dump_func_t)dump_midi, substream);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
148
  		snd_midi_event_reset_decode(msynth->parser);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
150
151
  	} else {
  		if (msynth->parser == NULL)
  			return -EIO;
d06e4c400   Clemens Ladisch   [ALSA] seq-midi -...
152
153
154
155
  		len = snd_midi_event_decode(msynth->parser, msg, sizeof(msg), ev);
  		if (len < 0)
  			return 0;
  		if (dump_midi(substream, msg, len) < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
  			snd_midi_event_reset_decode(msynth->parser);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
158
159
  	}
  	return 0;
  }
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
160
161
  static int snd_seq_midisynth_new(struct seq_midisynth *msynth,
  				 struct snd_card *card,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
162
163
164
165
166
167
168
169
170
171
172
173
  				 int device,
  				 int subdevice)
  {
  	if (snd_midi_event_new(MAX_MIDI_EVENT_BUF, &msynth->parser) < 0)
  		return -ENOMEM;
  	msynth->card = card;
  	msynth->device = device;
  	msynth->subdevice = subdevice;
  	return 0;
  }
  
  /* open associated midi device for input */
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
174
  static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribe *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
  {
  	int err;
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
177
178
179
  	struct seq_midisynth *msynth = private_data;
  	struct snd_rawmidi_runtime *runtime;
  	struct snd_rawmidi_params params;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
  
  	/* open midi port */
f87135f56   Clemens Ladisch   [ALSA] dynamic mi...
182
183
184
185
  	if ((err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
  					   msynth->subdevice,
  					   SNDRV_RAWMIDI_LFLG_INPUT,
  					   &msynth->input_rfile)) < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
  		snd_printd("midi input open failed!!!
  ");
  		return err;
  	}
  	runtime = msynth->input_rfile.input->runtime;
  	memset(&params, 0, sizeof(params));
  	params.avail_min = 1;
  	params.buffer_size = input_buffer_size;
  	if ((err = snd_rawmidi_input_params(msynth->input_rfile.input, &params)) < 0) {
  		snd_rawmidi_kernel_release(&msynth->input_rfile);
  		return err;
  	}
  	snd_midi_event_reset_encode(msynth->parser);
  	runtime->event = snd_midi_input_event;
  	runtime->private_data = msynth;
  	snd_rawmidi_kernel_read(msynth->input_rfile.input, NULL, 0);
  	return 0;
  }
  
  /* close associated midi device for input */
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
206
  static int midisynth_unsubscribe(void *private_data, struct snd_seq_port_subscribe *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
207
208
  {
  	int err;
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
209
  	struct seq_midisynth *msynth = private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
210

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
211
212
  	if (snd_BUG_ON(!msynth->input_rfile.input))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
214
215
216
217
  	err = snd_rawmidi_kernel_release(&msynth->input_rfile);
  	return err;
  }
  
  /* open associated midi device for output */
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
218
  static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
220
  {
  	int err;
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
221
222
  	struct seq_midisynth *msynth = private_data;
  	struct snd_rawmidi_params params;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
224
  
  	/* open midi port */
f87135f56   Clemens Ladisch   [ALSA] dynamic mi...
225
226
227
228
  	if ((err = snd_rawmidi_kernel_open(msynth->card, msynth->device,
  					   msynth->subdevice,
  					   SNDRV_RAWMIDI_LFLG_OUTPUT,
  					   &msynth->output_rfile)) < 0) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
232
233
234
235
  		snd_printd("midi output open failed!!!
  ");
  		return err;
  	}
  	memset(&params, 0, sizeof(params));
  	params.avail_min = 1;
  	params.buffer_size = output_buffer_size;
2d4b84201   Clemens Ladisch   sound: rawmidi: d...
236
  	params.no_active_sensing = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
238
239
240
241
242
243
244
245
  	if ((err = snd_rawmidi_output_params(msynth->output_rfile.output, &params)) < 0) {
  		snd_rawmidi_kernel_release(&msynth->output_rfile);
  		return err;
  	}
  	snd_midi_event_reset_decode(msynth->parser);
  	return 0;
  }
  
  /* close associated midi device for output */
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
246
  static int midisynth_unuse(void *private_data, struct snd_seq_port_subscribe *info)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
  {
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
248
  	struct seq_midisynth *msynth = private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249

7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
250
251
  	if (snd_BUG_ON(!msynth->output_rfile.output))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
255
256
  	snd_rawmidi_drain_output(msynth->output_rfile.output);
  	return snd_rawmidi_kernel_release(&msynth->output_rfile);
  }
  
  /* delete given midi synth port */
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
257
  static void snd_seq_midisynth_delete(struct seq_midisynth *msynth)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
258
259
260
261
262
263
264
265
266
267
268
269
  {
  	if (msynth == NULL)
  		return;
  
  	if (msynth->seq_client > 0) {
  		/* delete port */
  		snd_seq_event_port_detach(msynth->seq_client, msynth->seq_port);
  	}
  
  	if (msynth->parser)
  		snd_midi_event_free(msynth->parser);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
271
  /* register new midi synth port */
  static int
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
272
  snd_seq_midisynth_register_port(struct snd_seq_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
  {
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
274
275
276
277
  	struct seq_midisynth_client *client;
  	struct seq_midisynth *msynth, *ms;
  	struct snd_seq_port_info *port;
  	struct snd_rawmidi_info *info;
a7b928ac5   Clemens Ladisch   [ALSA] rawmidi: a...
278
  	struct snd_rawmidi *rmidi = dev->private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
279
280
  	int newclient = 0;
  	unsigned int p, ports;
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
281
282
  	struct snd_seq_port_callback pcallbacks;
  	struct snd_card *card = dev->card;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283
284
  	int device = dev->device;
  	unsigned int input_count = 0, output_count = 0;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
285
286
  	if (snd_BUG_ON(!card || device < 0 || device >= SNDRV_RAWMIDI_DEVICES))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
  	info = kmalloc(sizeof(*info), GFP_KERNEL);
  	if (! info)
  		return -ENOMEM;
  	info->device = device;
  	info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
  	info->subdevice = 0;
  	if (snd_rawmidi_info_select(card, info) >= 0)
  		output_count = info->subdevices_count;
  	info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
  	if (snd_rawmidi_info_select(card, info) >= 0) {
  		input_count = info->subdevices_count;
  	}
  	ports = output_count;
  	if (ports < input_count)
  		ports = input_count;
  	if (ports == 0) {
  		kfree(info);
  		return -ENODEV;
  	}
  	if (ports > (256 / SNDRV_RAWMIDI_DEVICES))
  		ports = 256 / SNDRV_RAWMIDI_DEVICES;
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
308
  	mutex_lock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
309
310
311
  	client = synths[card->number];
  	if (client == NULL) {
  		newclient = 1;
ecca82b4b   Takashi Iwai   [ALSA] Replace wi...
312
  		client = kzalloc(sizeof(*client), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
313
  		if (client == NULL) {
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
314
  			mutex_unlock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
316
317
  			kfree(info);
  			return -ENOMEM;
  		}
7b6d92451   Clemens Ladisch   [ALSA] seq: set c...
318
319
  		client->seq_client =
  			snd_seq_create_kernel_client(
78fc030bd   Alan Horstmann   [ALSA] Change seq...
320
321
  				card, 0, "%s", card->shortname[0] ?
  				(const char *)card->shortname : "External MIDI");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
322
323
  		if (client->seq_client < 0) {
  			kfree(client);
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
324
  			mutex_unlock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325
326
327
  			kfree(info);
  			return -ENOMEM;
  		}
7b6d92451   Clemens Ladisch   [ALSA] seq: set c...
328
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
329

c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
330
  	msynth = kcalloc(ports, sizeof(struct seq_midisynth), GFP_KERNEL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
  	port = kmalloc(sizeof(*port), GFP_KERNEL);
  	if (msynth == NULL || port == NULL)
  		goto __nomem;
  
  	for (p = 0; p < ports; p++) {
  		ms = &msynth[p];
  
  		if (snd_seq_midisynth_new(ms, card, device, p) < 0)
  			goto __nomem;
  
  		/* declare port */
  		memset(port, 0, sizeof(*port));
  		port->addr.client = client->seq_client;
  		port->addr.port = device * (256 / SNDRV_RAWMIDI_DEVICES) + p;
  		port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
  		memset(info, 0, sizeof(*info));
  		info->device = device;
  		if (p < output_count)
  			info->stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
  		else
  			info->stream = SNDRV_RAWMIDI_STREAM_INPUT;
  		info->subdevice = p;
  		if (snd_rawmidi_info_select(card, info) >= 0)
  			strcpy(port->name, info->subname);
  		if (! port->name[0]) {
  			if (info->name[0]) {
  				if (ports > 1)
  					snprintf(port->name, sizeof(port->name), "%s-%d", info->name, p);
  				else
  					snprintf(port->name, sizeof(port->name), "%s", info->name);
  			} else {
  				/* last resort */
  				if (ports > 1)
  					sprintf(port->name, "MIDI %d-%d-%d", card->number, device, p);
  				else
  					sprintf(port->name, "MIDI %d-%d", card->number, device);
  			}
  		}
  		if ((info->flags & SNDRV_RAWMIDI_INFO_OUTPUT) && p < output_count)
  			port->capability |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SYNC_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
  		if ((info->flags & SNDRV_RAWMIDI_INFO_INPUT) && p < input_count)
  			port->capability |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SYNC_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
  		if ((port->capability & (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ)) == (SNDRV_SEQ_PORT_CAP_WRITE|SNDRV_SEQ_PORT_CAP_READ) &&
  		    info->flags & SNDRV_RAWMIDI_INFO_DUPLEX)
  			port->capability |= SNDRV_SEQ_PORT_CAP_DUPLEX;
450047a78   Clemens Ladisch   [ALSA] add more s...
376
377
378
  		port->type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
  			| SNDRV_SEQ_PORT_TYPE_HARDWARE
  			| SNDRV_SEQ_PORT_TYPE_PORT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
380
381
382
383
384
385
386
387
388
  		port->midi_channels = 16;
  		memset(&pcallbacks, 0, sizeof(pcallbacks));
  		pcallbacks.owner = THIS_MODULE;
  		pcallbacks.private_data = ms;
  		pcallbacks.subscribe = midisynth_subscribe;
  		pcallbacks.unsubscribe = midisynth_unsubscribe;
  		pcallbacks.use = midisynth_use;
  		pcallbacks.unuse = midisynth_unuse;
  		pcallbacks.event_input = event_process_midi;
  		port->kernel = &pcallbacks;
a7b928ac5   Clemens Ladisch   [ALSA] rawmidi: a...
389
390
  		if (rmidi->ops && rmidi->ops->get_port_info)
  			rmidi->ops->get_port_info(rmidi, p, port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
391
392
393
394
395
396
397
398
399
400
  		if (snd_seq_kernel_client_ctl(client->seq_client, SNDRV_SEQ_IOCTL_CREATE_PORT, port)<0)
  			goto __nomem;
  		ms->seq_client = client->seq_client;
  		ms->seq_port = port->addr.port;
  	}
  	client->ports_per_device[device] = ports;
  	client->ports[device] = msynth;
  	client->num_ports++;
  	if (newclient)
  		synths[card->number] = client;
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
401
  	mutex_unlock(&register_mutex);
51f633dad   Takashi Iwai   [ALSA] Fix memory...
402
403
  	kfree(info);
  	kfree(port);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
404
405
406
407
408
409
410
411
412
413
414
415
416
417
  	return 0;	/* success */
  
        __nomem:
  	if (msynth != NULL) {
  	      	for (p = 0; p < ports; p++)
  	      		snd_seq_midisynth_delete(&msynth[p]);
  		kfree(msynth);
  	}
  	if (newclient) {
  		snd_seq_delete_kernel_client(client->seq_client);
  		kfree(client);
  	}
  	kfree(info);
  	kfree(port);
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
418
  	mutex_unlock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
419
420
421
422
423
  	return -ENOMEM;
  }
  
  /* release midi synth port */
  static int
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
424
  snd_seq_midisynth_unregister_port(struct snd_seq_device *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
425
  {
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
426
427
428
  	struct seq_midisynth_client *client;
  	struct seq_midisynth *msynth;
  	struct snd_card *card = dev->card;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
429
430
  	int device = dev->device, p, ports;
  	
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
431
  	mutex_lock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
432
433
  	client = synths[card->number];
  	if (client == NULL || client->ports[device] == NULL) {
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
434
  		mutex_unlock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
435
436
437
438
439
440
  		return -ENODEV;
  	}
  	ports = client->ports_per_device[device];
  	client->ports_per_device[device] = 0;
  	msynth = client->ports[device];
  	client->ports[device] = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
441
442
443
  	for (p = 0; p < ports; p++)
  		snd_seq_midisynth_delete(&msynth[p]);
  	kfree(msynth);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
444
445
446
447
448
449
  	client->num_ports--;
  	if (client->num_ports <= 0) {
  		snd_seq_delete_kernel_client(client->seq_client);
  		synths[card->number] = NULL;
  		kfree(client);
  	}
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
450
  	mutex_unlock(&register_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
451
452
453
454
455
456
  	return 0;
  }
  
  
  static int __init alsa_seq_midi_init(void)
  {
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
457
  	static struct snd_seq_dev_ops ops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
  		snd_seq_midisynth_register_port,
  		snd_seq_midisynth_unregister_port,
  	};
  	memset(&synths, 0, sizeof(synths));
  	snd_seq_autoload_lock();
  	snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0);
  	snd_seq_autoload_unlock();
  	return 0;
  }
  
  static void __exit alsa_seq_midi_exit(void)
  {
  	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH);
  }
  
  module_init(alsa_seq_midi_init)
  module_exit(alsa_seq_midi_exit)