Blame view
sound/core/sound.c
10.5 KB
1da177e4c
|
1 2 |
/* * Advanced Linux Sound Architecture |
c1017a4cd
|
3 |
* Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
1da177e4c
|
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
* * * 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 * */ |
1da177e4c
|
21 22 23 |
#include <linux/init.h> #include <linux/slab.h> #include <linux/time.h> |
9a1a2a1d4
|
24 |
#include <linux/device.h> |
65a772172
|
25 |
#include <linux/module.h> |
1da177e4c
|
26 27 28 |
#include <sound/core.h> #include <sound/minors.h> #include <sound/info.h> |
1da177e4c
|
29 30 31 |
#include <sound/control.h> #include <sound/initval.h> #include <linux/kmod.h> |
1a60d4c5a
|
32 |
#include <linux/mutex.h> |
1da177e4c
|
33 |
|
1da177e4c
|
34 35 |
static int major = CONFIG_SND_MAJOR; int snd_major; |
c0d3fb39e
|
36 |
EXPORT_SYMBOL(snd_major); |
1da177e4c
|
37 |
static int cards_limit = 1; |
1da177e4c
|
38 |
|
c1017a4cd
|
39 |
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); |
1da177e4c
|
40 41 42 43 44 45 |
MODULE_DESCRIPTION("Advanced Linux Sound Architecture driver for soundcards."); MODULE_LICENSE("GPL"); module_param(major, int, 0444); MODULE_PARM_DESC(major, "Major # for sound driver."); module_param(cards_limit, int, 0444); MODULE_PARM_DESC(cards_limit, "Count of auto-loadable soundcards."); |
1da177e4c
|
46 47 48 49 50 51 52 |
MODULE_ALIAS_CHARDEV_MAJOR(CONFIG_SND_MAJOR); /* this one holds the actual max. card number currently available. * as default, it's identical with cards_limit option. when more * modules are loaded manually, this limit number increases, too. */ int snd_ecards_limit; |
c0d3fb39e
|
53 |
EXPORT_SYMBOL(snd_ecards_limit); |
1da177e4c
|
54 |
|
6983b7240
|
55 |
static struct snd_minor *snd_minors[SNDRV_OS_MINORS]; |
1a60d4c5a
|
56 |
static DEFINE_MUTEX(sound_mutex); |
1da177e4c
|
57 |
|
ee2da9978
|
58 |
#ifdef CONFIG_MODULES |
1da177e4c
|
59 60 61 62 63 64 |
/** * snd_request_card - try to load the card module * @card: the card number * * Tries to load the module "snd-card-X" for the given card number |
ee2da9978
|
65 |
* via request_module. Returns immediately if already loaded. |
1da177e4c
|
66 67 68 |
*/ void snd_request_card(int card) { |
746df9489
|
69 |
if (snd_card_locked(card)) |
1da177e4c
|
70 71 72 73 74 |
return; if (card < 0 || card >= cards_limit) return; request_module("snd-card-%i", card); } |
c0d3fb39e
|
75 |
EXPORT_SYMBOL(snd_request_card); |
1da177e4c
|
76 77 78 |
static void snd_request_other(int minor) { char *str; |
1da177e4c
|
79 80 81 82 83 84 85 |
switch (minor) { case SNDRV_MINOR_SEQUENCER: str = "snd-seq"; break; case SNDRV_MINOR_TIMER: str = "snd-timer"; break; default: return; } request_module(str); } |
ee2da9978
|
86 |
#endif /* modular kernel */ |
1da177e4c
|
87 |
|
f87135f56
|
88 89 90 91 92 93 94 |
/** * snd_lookup_minor_data - get user data of a registered device * @minor: the minor number * @type: device type (SNDRV_DEVICE_TYPE_XXX) * * Checks that a minor device with the specified type is registered, and returns * its user data pointer. |
a0830dbd4
|
95 96 97 98 |
* * This function increments the reference counter of the card instance * if an associated instance with the given minor number and type is found. * The caller must call snd_card_unref() appropriately later. |
eb7c06e8e
|
99 100 101 |
* * Return: The user data pointer if the specified device is found. %NULL * otherwise. |
f87135f56
|
102 103 104 105 106 |
*/ void *snd_lookup_minor_data(unsigned int minor, int type) { struct snd_minor *mreg; void *private_data; |
3a63e4442
|
107 |
if (minor >= ARRAY_SIZE(snd_minors)) |
f87135f56
|
108 |
return NULL; |
1a60d4c5a
|
109 |
mutex_lock(&sound_mutex); |
f87135f56
|
110 |
mreg = snd_minors[minor]; |
a0830dbd4
|
111 |
if (mreg && mreg->type == type) { |
f87135f56
|
112 |
private_data = mreg->private_data; |
8bb4d9ce0
|
113 |
if (private_data && mreg->card_ptr) |
f24640648
|
114 |
get_device(&mreg->card_ptr->card_dev); |
a0830dbd4
|
115 |
} else |
f87135f56
|
116 |
private_data = NULL; |
1a60d4c5a
|
117 |
mutex_unlock(&sound_mutex); |
f87135f56
|
118 119 |
return private_data; } |
c0d3fb39e
|
120 |
EXPORT_SYMBOL(snd_lookup_minor_data); |
4cf19b848
|
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
#ifdef CONFIG_MODULES static struct snd_minor *autoload_device(unsigned int minor) { int dev; mutex_unlock(&sound_mutex); /* release lock temporarily */ dev = SNDRV_MINOR_DEVICE(minor); if (dev == SNDRV_MINOR_CONTROL) { /* /dev/aloadC? */ int card = SNDRV_MINOR_CARD(minor); if (snd_cards[card] == NULL) snd_request_card(card); } else if (dev == SNDRV_MINOR_GLOBAL) { /* /dev/aloadSEQ */ snd_request_other(minor); } mutex_lock(&sound_mutex); /* reacuire lock */ return snd_minors[minor]; } #else /* !CONFIG_MODULES */ #define autoload_device(minor) NULL #endif /* CONFIG_MODULES */ static int snd_open(struct inode *inode, struct file *file) |
1da177e4c
|
144 |
{ |
332682b1c
|
145 |
unsigned int minor = iminor(inode); |
512bbd6a8
|
146 |
struct snd_minor *mptr = NULL; |
e84f9e57b
|
147 |
const struct file_operations *new_fops; |
1da177e4c
|
148 |
int err = 0; |
3a63e4442
|
149 |
if (minor >= ARRAY_SIZE(snd_minors)) |
332682b1c
|
150 |
return -ENODEV; |
4cf19b848
|
151 |
mutex_lock(&sound_mutex); |
332682b1c
|
152 153 |
mptr = snd_minors[minor]; if (mptr == NULL) { |
4cf19b848
|
154 155 156 |
mptr = autoload_device(minor); if (!mptr) { mutex_unlock(&sound_mutex); |
332682b1c
|
157 |
return -ENODEV; |
4cf19b848
|
158 |
} |
1da177e4c
|
159 |
} |
e84f9e57b
|
160 |
new_fops = fops_get(mptr->f_ops); |
4cf19b848
|
161 |
mutex_unlock(&sound_mutex); |
e84f9e57b
|
162 163 164 |
if (!new_fops) return -ENODEV; replace_fops(file, new_fops); |
4cf19b848
|
165 |
|
e84f9e57b
|
166 |
if (file->f_op->open) |
1da177e4c
|
167 |
err = file->f_op->open(inode, file); |
1da177e4c
|
168 169 |
return err; } |
9c2e08c59
|
170 |
static const struct file_operations snd_fops = |
1da177e4c
|
171 172 |
{ .owner = THIS_MODULE, |
6038f373a
|
173 174 |
.open = snd_open, .llseek = noop_llseek, |
1da177e4c
|
175 |
}; |
332682b1c
|
176 |
#ifdef CONFIG_SND_DYNAMIC_MINORS |
38ebb7034
|
177 |
static int snd_find_free_minor(int type, struct snd_card *card, int dev) |
332682b1c
|
178 179 |
{ int minor; |
03cfe6f57
|
180 181 182 183 184 |
/* static minors for module auto loading */ if (type == SNDRV_DEVICE_TYPE_SEQUENCER) return SNDRV_MINOR_SEQUENCER; if (type == SNDRV_DEVICE_TYPE_TIMER) return SNDRV_MINOR_TIMER; |
332682b1c
|
185 |
for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) { |
03cfe6f57
|
186 187 188 189 190 |
/* skip static minors still used for module auto loading */ if (SNDRV_MINOR_DEVICE(minor) == SNDRV_MINOR_CONTROL) continue; if (minor == SNDRV_MINOR_SEQUENCER || minor == SNDRV_MINOR_TIMER) |
332682b1c
|
191 192 193 194 195 196 197 |
continue; if (!snd_minors[minor]) return minor; } return -EBUSY; } #else |
38ebb7034
|
198 |
static int snd_find_free_minor(int type, struct snd_card *card, int dev) |
1da177e4c
|
199 200 201 202 203 204 205 206 207 |
{ int minor; switch (type) { case SNDRV_DEVICE_TYPE_SEQUENCER: case SNDRV_DEVICE_TYPE_TIMER: minor = type; break; case SNDRV_DEVICE_TYPE_CONTROL: |
7eaa943c8
|
208 209 |
if (snd_BUG_ON(!card)) return -EINVAL; |
1da177e4c
|
210 211 212 213 214 215 |
minor = SNDRV_MINOR(card->number, type); break; case SNDRV_DEVICE_TYPE_HWDEP: case SNDRV_DEVICE_TYPE_RAWMIDI: case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: case SNDRV_DEVICE_TYPE_PCM_CAPTURE: |
3eafc959b
|
216 |
case SNDRV_DEVICE_TYPE_COMPRESS: |
7eaa943c8
|
217 218 |
if (snd_BUG_ON(!card)) return -EINVAL; |
1da177e4c
|
219 220 221 222 223 |
minor = SNDRV_MINOR(card->number, type + dev); break; default: return -EINVAL; } |
7eaa943c8
|
224 225 |
if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS)) return -EINVAL; |
38ebb7034
|
226 227 |
if (snd_minors[minor]) return -EBUSY; |
1da177e4c
|
228 229 |
return minor; } |
332682b1c
|
230 |
#endif |
1da177e4c
|
231 232 |
/** |
40a4b2638
|
233 |
* snd_register_device - Register the ALSA device file for the card |
1da177e4c
|
234 235 236 |
* @type: the device type, SNDRV_DEVICE_TYPE_XXX * @card: the card instance * @dev: the device index |
2af677fc8
|
237 |
* @f_ops: the file operations |
f87135f56
|
238 |
* @private_data: user pointer for f_ops->open() |
40a4b2638
|
239 |
* @device: the device to register |
1da177e4c
|
240 241 242 243 |
* * Registers an ALSA device file for the given card. * The operators have to be set in reg parameter. * |
eb7c06e8e
|
244 |
* Return: Zero if successful, or a negative error code on failure. |
1da177e4c
|
245 |
*/ |
40a4b2638
|
246 247 248 |
int snd_register_device(int type, struct snd_card *card, int dev, const struct file_operations *f_ops, void *private_data, struct device *device) |
1da177e4c
|
249 |
{ |
332682b1c
|
250 |
int minor; |
92b7952da
|
251 |
int err = 0; |
512bbd6a8
|
252 |
struct snd_minor *preg; |
1da177e4c
|
253 |
|
40a4b2638
|
254 255 |
if (snd_BUG_ON(!device)) return -EINVAL; |
562b590d4
|
256 |
preg = kmalloc(sizeof *preg, GFP_KERNEL); |
1da177e4c
|
257 258 |
if (preg == NULL) return -ENOMEM; |
2af677fc8
|
259 |
preg->type = type; |
6983b7240
|
260 |
preg->card = card ? card->number : -1; |
1da177e4c
|
261 |
preg->device = dev; |
2af677fc8
|
262 |
preg->f_ops = f_ops; |
f87135f56
|
263 |
preg->private_data = private_data; |
a0830dbd4
|
264 |
preg->card_ptr = card; |
1a60d4c5a
|
265 |
mutex_lock(&sound_mutex); |
38ebb7034
|
266 |
minor = snd_find_free_minor(type, card, dev); |
332682b1c
|
267 |
if (minor < 0) { |
92b7952da
|
268 269 |
err = minor; goto error; |
1da177e4c
|
270 |
} |
92b7952da
|
271 |
|
40a4b2638
|
272 273 274 |
preg->dev = device; device->devt = MKDEV(major, minor); err = device_add(device); |
92b7952da
|
275 276 |
if (err < 0) goto error; |
2469049e7
|
277 |
|
92b7952da
|
278 279 |
snd_minors[minor] = preg; error: |
1a60d4c5a
|
280 |
mutex_unlock(&sound_mutex); |
92b7952da
|
281 282 283 |
if (err < 0) kfree(preg); return err; |
1da177e4c
|
284 |
} |
40a4b2638
|
285 |
EXPORT_SYMBOL(snd_register_device); |
c0d3fb39e
|
286 |
|
1da177e4c
|
287 288 |
/** * snd_unregister_device - unregister the device on the given card |
40a4b2638
|
289 |
* @dev: the device instance |
1da177e4c
|
290 291 292 293 |
* * Unregisters the device file already registered via * snd_register_device(). * |
eb7c06e8e
|
294 |
* Return: Zero if successful, or a negative error code on failure. |
1da177e4c
|
295 |
*/ |
40a4b2638
|
296 |
int snd_unregister_device(struct device *dev) |
1da177e4c
|
297 |
{ |
9d19f48cf
|
298 |
int minor; |
92b7952da
|
299 |
struct snd_minor *preg; |
1da177e4c
|
300 |
|
1a60d4c5a
|
301 |
mutex_lock(&sound_mutex); |
40a4b2638
|
302 303 304 305 306 307 308 309 |
for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor) { preg = snd_minors[minor]; if (preg && preg->dev == dev) { snd_minors[minor] = NULL; device_del(dev); kfree(preg); break; } |
1da177e4c
|
310 |
} |
1a60d4c5a
|
311 |
mutex_unlock(&sound_mutex); |
40a4b2638
|
312 313 |
if (minor >= ARRAY_SIZE(snd_minors)) return -ENOENT; |
1da177e4c
|
314 315 |
return 0; } |
c0d3fb39e
|
316 |
EXPORT_SYMBOL(snd_unregister_device); |
cd6a65036
|
317 |
#ifdef CONFIG_SND_PROC_FS |
1da177e4c
|
318 319 320 |
/* * INFO PART */ |
2af677fc8
|
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
static const char *snd_device_type_name(int type) { switch (type) { case SNDRV_DEVICE_TYPE_CONTROL: return "control"; case SNDRV_DEVICE_TYPE_HWDEP: return "hardware dependent"; case SNDRV_DEVICE_TYPE_RAWMIDI: return "raw midi"; case SNDRV_DEVICE_TYPE_PCM_PLAYBACK: return "digital audio playback"; case SNDRV_DEVICE_TYPE_PCM_CAPTURE: return "digital audio capture"; case SNDRV_DEVICE_TYPE_SEQUENCER: return "sequencer"; case SNDRV_DEVICE_TYPE_TIMER: return "timer"; default: return "?"; } } |
512bbd6a8
|
342 |
static void snd_minor_info_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) |
1da177e4c
|
343 |
{ |
6983b7240
|
344 |
int minor; |
512bbd6a8
|
345 |
struct snd_minor *mptr; |
1da177e4c
|
346 |
|
1a60d4c5a
|
347 |
mutex_lock(&sound_mutex); |
6983b7240
|
348 349 350 351 352 |
for (minor = 0; minor < SNDRV_OS_MINORS; ++minor) { if (!(mptr = snd_minors[minor])) continue; if (mptr->card >= 0) { if (mptr->device >= 0) |
d001544de
|
353 354 |
snd_iprintf(buffer, "%3i: [%2i-%2i]: %s ", |
6983b7240
|
355 356 357 |
minor, mptr->card, mptr->device, snd_device_type_name(mptr->type)); else |
d001544de
|
358 359 |
snd_iprintf(buffer, "%3i: [%2i] : %s ", |
6983b7240
|
360 361 362 |
minor, mptr->card, snd_device_type_name(mptr->type)); } else |
d001544de
|
363 364 |
snd_iprintf(buffer, "%3i: : %s ", minor, |
6983b7240
|
365 |
snd_device_type_name(mptr->type)); |
1da177e4c
|
366 |
} |
1a60d4c5a
|
367 |
mutex_unlock(&sound_mutex); |
1da177e4c
|
368 369 370 371 |
} int __init snd_minor_info_init(void) { |
512bbd6a8
|
372 |
struct snd_info_entry *entry; |
1da177e4c
|
373 374 |
entry = snd_info_create_module_entry(THIS_MODULE, "devices", NULL); |
b591b6e9e
|
375 376 377 378 |
if (!entry) return -ENOMEM; entry->c.text.read = snd_minor_info_read; return snd_info_register(entry); /* freed in error path */ |
1da177e4c
|
379 |
} |
cd6a65036
|
380 |
#endif /* CONFIG_SND_PROC_FS */ |
1da177e4c
|
381 382 383 384 385 386 387 |
/* * INIT PART */ static int __init alsa_sound_init(void) { |
1da177e4c
|
388 389 |
snd_major = major; snd_ecards_limit = cards_limit; |
1da177e4c
|
390 |
if (register_chrdev(major, "alsa", &snd_fops)) { |
f2f9307a4
|
391 392 |
pr_err("ALSA core: unable to register native major device number %d ", major); |
1da177e4c
|
393 394 |
return -EIO; } |
1da177e4c
|
395 |
if (snd_info_init() < 0) { |
1da177e4c
|
396 |
unregister_chrdev(major, "alsa"); |
1da177e4c
|
397 398 |
return -ENOMEM; } |
1da177e4c
|
399 |
#ifndef MODULE |
f2f9307a4
|
400 401 |
pr_info("Advanced Linux Sound Architecture Driver Initialized. "); |
1da177e4c
|
402 403 404 405 406 407 |
#endif return 0; } static void __exit alsa_sound_exit(void) { |
1da177e4c
|
408 |
snd_info_done(); |
68fc4fabc
|
409 |
unregister_chrdev(major, "alsa"); |
1da177e4c
|
410 |
} |
c181a13a4
|
411 412 |
subsys_initcall(alsa_sound_init); module_exit(alsa_sound_exit); |