Blame view

sound/core/seq_device.c 7.76 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
  /*
   *  ALSA sequencer device management
   *  Copyright (c) 1999 by Takashi Iwai <tiwai@suse.de>
   *
   *   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
   *
   *
   *----------------------------------------------------------------
   *
   * This device handler separates the card driver module from sequencer
   * stuff (sequencer core, synth drivers, etc), so that user can avoid
   * to spend unnecessary resources e.g. if he needs only listening to
   * MP3s.
   *
   * The card (or lowlevel) driver creates a sequencer device entry
   * via snd_seq_device_new().  This is an entry pointer to communicate
   * with the sequencer device "driver", which is involved with the
   * actual part to communicate with the sequencer core.
   * Each sequencer device entry has an id string and the corresponding
   * driver with the same id is loaded when required.  For example,
   * lowlevel codes to access emu8000 chip on sbawe card are included in
   * emu8000-synth module.  To activate this module, the hardware
   * resources like i/o port are passed via snd_seq_device argument.
   *
   */
7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
38
  #include <linux/device.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
  #include <linux/init.h>
da155d5b4   Paul Gortmaker   sound: Add module...
40
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
43
44
45
46
47
  #include <sound/core.h>
  #include <sound/info.h>
  #include <sound/seq_device.h>
  #include <sound/seq_kernel.h>
  #include <sound/initval.h>
  #include <linux/kmod.h>
  #include <linux/slab.h>
1a60d4c5a   Ingo Molnar   [ALSA] semaphore ...
48
  #include <linux/mutex.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
49
50
51
52
  
  MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
  MODULE_DESCRIPTION("ALSA sequencer device management");
  MODULE_LICENSE("GPL");
7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
53
54
55
56
57
58
59
  /*
   * bus definition
   */
  static int snd_seq_bus_match(struct device *dev, struct device_driver *drv)
  {
  	struct snd_seq_device *sdev = to_seq_dev(dev);
  	struct snd_seq_driver *sdrv = to_seq_drv(drv);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60

7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
61
62
63
  	return strcmp(sdrv->id, sdev->id) == 0 &&
  		sdrv->argsize == sdev->argsize;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64

7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
65
66
67
  static struct bus_type snd_seq_bus_type = {
  	.name = "snd_seq",
  	.match = snd_seq_bus_match,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68
  };
7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
69
70
71
  /*
   * proc interface -- just for compatibility
   */
cd6a65036   Jie Yang   ALSA: replace CON...
72
  #ifdef CONFIG_SND_PROC_FS
6581f4e74   Takashi Iwai   [ALSA] Remove zer...
73
  static struct snd_info_entry *info_entry;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74

7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
75
76
77
78
  static int print_dev_info(struct device *dev, void *data)
  {
  	struct snd_seq_device *sdev = to_seq_dev(dev);
  	struct snd_info_buffer *buffer = data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79

7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
80
81
82
83
84
85
  	snd_iprintf(buffer, "snd-%s,%s,%d
  ", sdev->id,
  		    dev->driver ? "loaded" : "empty",
  		    dev->driver ? 1 : 0);
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
86

c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
87
88
  static void snd_seq_device_info(struct snd_info_entry *entry,
  				struct snd_info_buffer *buffer)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
  {
7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
90
  	bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
  }
04f141a88   Takashi Iwai   [ALSA] Optimize f...
92
  #endif
7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
93

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
96
  /*
   * load all registered drivers (called from seq_clientmgr.c)
   */
ee2da9978   Johannes Berg   ALSA: remove CONF...
97
  #ifdef CONFIG_MODULES
54a721abd   Takashi Iwai   ALSA: seq: Drop s...
98
  /* flag to block auto-loading */
68ab61084   Takashi Iwai   ALSA: seq: bind s...
99
  static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100

7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
101
  static int request_seq_drv(struct device *dev, void *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
  {
7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
103
  	struct snd_seq_device *sdev = to_seq_dev(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104

7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
105
106
107
  	if (!dev->driver)
  		request_module("snd-%s", sdev->id);
  	return 0;
68ab61084   Takashi Iwai   ALSA: seq: bind s...
108
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
109

7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
110
  static void autoload_drivers(struct work_struct *work)
68ab61084   Takashi Iwai   ALSA: seq: bind s...
111
  {
7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
112
113
114
115
116
  	/* avoid reentrance */
  	if (atomic_inc_return(&snd_seq_in_init) == 1)
  		bus_for_each_dev(&snd_seq_bus_type, NULL, NULL,
  				 request_seq_drv);
  	atomic_dec(&snd_seq_in_init);
68ab61084   Takashi Iwai   ALSA: seq: bind s...
117
  }
7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
118
  static DECLARE_WORK(autoload_work, autoload_drivers);
68ab61084   Takashi Iwai   ALSA: seq: bind s...
119
120
  static void queue_autoload_drivers(void)
  {
7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
121
  	schedule_work(&autoload_work);
68ab61084   Takashi Iwai   ALSA: seq: bind s...
122
123
124
125
126
127
128
129
130
131
  }
  
  void snd_seq_autoload_init(void)
  {
  	atomic_dec(&snd_seq_in_init);
  #ifdef CONFIG_SND_SEQUENCER_MODULE
  	/* initial autoload only when snd-seq is a module */
  	queue_autoload_drivers();
  #endif
  }
b6a42670e   Takashi Iwai   ALSA: seq: Move E...
132
  EXPORT_SYMBOL(snd_seq_autoload_init);
b6a42670e   Takashi Iwai   ALSA: seq: Move E...
133

54a721abd   Takashi Iwai   ALSA: seq: Drop s...
134
135
136
137
138
  void snd_seq_autoload_exit(void)
  {
  	atomic_inc(&snd_seq_in_init);
  }
  EXPORT_SYMBOL(snd_seq_autoload_exit);
68ab61084   Takashi Iwai   ALSA: seq: bind s...
139
140
  void snd_seq_device_load_drivers(void)
  {
68ab61084   Takashi Iwai   ALSA: seq: bind s...
141
142
  	queue_autoload_drivers();
  	flush_work(&autoload_work);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
143
  }
b6a42670e   Takashi Iwai   ALSA: seq: Move E...
144
  EXPORT_SYMBOL(snd_seq_device_load_drivers);
fc27fe7e8   Takashi Iwai   ALSA: seq: Cancel...
145
  #define cancel_autoload_drivers()	cancel_work_sync(&autoload_work)
72496edcf   Takashi Iwai   ALSA: seq: Don't ...
146
  #else
7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
147
  #define queue_autoload_drivers() /* NOP */
fc27fe7e8   Takashi Iwai   ALSA: seq: Cancel...
148
  #define cancel_autoload_drivers() /* NOP */
72496edcf   Takashi Iwai   ALSA: seq: Don't ...
149
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
150
151
  
  /*
7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
152
153
154
155
156
   * device management
   */
  static int snd_seq_device_dev_free(struct snd_device *device)
  {
  	struct snd_seq_device *dev = device->device_data;
fc27fe7e8   Takashi Iwai   ALSA: seq: Cancel...
157
  	cancel_autoload_drivers();
7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
  	put_device(&dev->dev);
  	return 0;
  }
  
  static int snd_seq_device_dev_register(struct snd_device *device)
  {
  	struct snd_seq_device *dev = device->device_data;
  	int err;
  
  	err = device_add(&dev->dev);
  	if (err < 0)
  		return err;
  	if (!dev->dev.driver)
  		queue_autoload_drivers();
  	return 0;
  }
  
  static int snd_seq_device_dev_disconnect(struct snd_device *device)
  {
  	struct snd_seq_device *dev = device->device_data;
  
  	device_del(&dev->dev);
  	return 0;
  }
  
  static void snd_seq_dev_release(struct device *dev)
  {
  	struct snd_seq_device *sdev = to_seq_dev(dev);
  
  	if (sdev->private_free)
  		sdev->private_free(sdev);
  	kfree(sdev);
  }
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
   * register a sequencer device
f2f9307a4   Takashi Iwai   ALSA: core: Use s...
194
   * card = card info
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
196
197
198
   * device = device number (if any)
   * id = id of driver
   * result = return pointer (NULL allowed if unnecessary)
   */
af03c243a   Takashi Iwai   ALSA: seq: Clean ...
199
200
  int snd_seq_device_new(struct snd_card *card, int device, const char *id,
  		       int argsize, struct snd_seq_device **result)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
201
  {
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
202
  	struct snd_seq_device *dev;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
  	int err;
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
204
  	static struct snd_device_ops dops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
207
  		.dev_free = snd_seq_device_dev_free,
  		.dev_register = snd_seq_device_dev_register,
  		.dev_disconnect = snd_seq_device_dev_disconnect,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
209
210
211
  	};
  
  	if (result)
  		*result = NULL;
7eaa943c8   Takashi Iwai   ALSA: Kill snd_as...
212
213
  	if (snd_BUG_ON(!id))
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214

7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
215
216
  	dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL);
  	if (!dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
219
220
221
  
  	/* set up device info */
  	dev->card = card;
  	dev->device = device;
af03c243a   Takashi Iwai   ALSA: seq: Clean ...
222
  	dev->id = id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
  	dev->argsize = argsize;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224

7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
225
226
227
228
229
  	device_initialize(&dev->dev);
  	dev->dev.parent = &card->card_dev;
  	dev->dev.bus = &snd_seq_bus_type;
  	dev->dev.release = snd_seq_dev_release;
  	dev_set_name(&dev->dev, "%s-%d-%d", dev->id, card->number, device);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230

7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
231
232
233
234
  	/* add this device to the list */
  	err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops);
  	if (err < 0) {
  		put_device(&dev->dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
236
237
238
239
240
241
242
  		return err;
  	}
  	
  	if (result)
  		*result = dev;
  
  	return 0;
  }
b6a42670e   Takashi Iwai   ALSA: seq: Move E...
243
  EXPORT_SYMBOL(snd_seq_device_new);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244
245
  
  /*
056622053   Takashi Iwai   ALSA: seq: Define...
246
   * driver registration
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
247
   */
056622053   Takashi Iwai   ALSA: seq: Define...
248
  int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
  {
056622053   Takashi Iwai   ALSA: seq: Define...
250
  	if (WARN_ON(!drv->driver.name || !drv->id))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
251
  		return -EINVAL;
056622053   Takashi Iwai   ALSA: seq: Define...
252
253
254
  	drv->driver.bus = &snd_seq_bus_type;
  	drv->driver.owner = mod;
  	return driver_register(&drv->driver);
7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
255
  }
056622053   Takashi Iwai   ALSA: seq: Define...
256
  EXPORT_SYMBOL_GPL(__snd_seq_driver_register);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
257

056622053   Takashi Iwai   ALSA: seq: Define...
258
  void snd_seq_driver_unregister(struct snd_seq_driver *drv)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
259
  {
056622053   Takashi Iwai   ALSA: seq: Define...
260
  	driver_unregister(&drv->driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
261
  }
056622053   Takashi Iwai   ALSA: seq: Define...
262
  EXPORT_SYMBOL_GPL(snd_seq_driver_unregister);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
264
265
266
  
  /*
   * module part
   */
7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
267
  static int __init seq_dev_proc_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
268
  {
b816db9d3   Takashi Iwai   ALSA: core: Fix r...
269
  #ifdef CONFIG_SND_PROC_FS
c7e0b5bf9   Takashi Iwai   [ALSA] Remove xxx...
270
271
  	info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers",
  						  snd_seq_root);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
273
274
  	if (info_entry == NULL)
  		return -ENOMEM;
  	info_entry->content = SNDRV_INFO_CONTENT_TEXT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
275
276
277
278
279
  	info_entry->c.text.read = snd_seq_device_info;
  	if (snd_info_register(info_entry) < 0) {
  		snd_info_free_entry(info_entry);
  		return -ENOMEM;
  	}
04f141a88   Takashi Iwai   [ALSA] Optimize f...
280
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
282
  	return 0;
  }
7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
283
284
285
286
287
288
289
290
291
292
293
294
  static int __init alsa_seq_device_init(void)
  {
  	int err;
  
  	err = bus_register(&snd_seq_bus_type);
  	if (err < 0)
  		return err;
  	err = seq_dev_proc_init();
  	if (err < 0)
  		bus_unregister(&snd_seq_bus_type);
  	return err;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
296
  static void __exit alsa_seq_device_exit(void)
  {
68ab61084   Takashi Iwai   ALSA: seq: bind s...
297
298
299
  #ifdef CONFIG_MODULES
  	cancel_work_sync(&autoload_work);
  #endif
b816db9d3   Takashi Iwai   ALSA: core: Fix r...
300
  #ifdef CONFIG_SND_PROC_FS
746d4a02e   Takashi Iwai   [ALSA] Fix discon...
301
  	snd_info_free_entry(info_entry);
04f141a88   Takashi Iwai   [ALSA] Optimize f...
302
  #endif
7c37ae5c6   Takashi Iwai   ALSA: seq: Rewrit...
303
  	bus_unregister(&snd_seq_bus_type);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
304
  }
4945f1fdc   Takashi Iwai   ALSA: seq: Fix in...
305
  subsys_initcall(alsa_seq_device_init)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
  module_exit(alsa_seq_device_exit)