| /* |
| * Initialization routines |
| * Copyright (c) by Jaroslav Kysela <perex@suse.cz> |
| * |
| * |
| * 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 |
| * |
| */ |
| |
| #define __NO_VERSION__ |
| #include <sound/driver.h> |
| #include <linux/init.h> |
| #include <linux/slab.h> |
| #include <linux/time.h> |
| #include <sound/core.h> |
| #include <sound/control.h> |
| #include <sound/info.h> |
| |
| int snd_cards_count = 0; |
| static unsigned int snd_cards_lock = 0; /* locked for registering/using */ |
| snd_card_t *snd_cards[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = NULL}; |
| rwlock_t snd_card_rwlock = RW_LOCK_UNLOCKED; |
| |
| #ifdef CONFIG_SND_OSSEMUL |
| int (*snd_mixer_oss_notify_callback)(snd_card_t *card, int free_flag); |
| #endif |
| |
| static void snd_card_id_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) |
| { |
| snd_iprintf(buffer, "%s\n", entry->card->id); |
| } |
| |
| snd_card_t *snd_card_new(int idx, const char *xid, |
| struct module *module, int extra_size) |
| { |
| snd_card_t *card; |
| snd_info_entry_t *entry; |
| int err; |
| |
| if (extra_size < 0) |
| extra_size = 0; |
| card = (snd_card_t *) snd_kcalloc(sizeof(snd_card_t) + extra_size, GFP_KERNEL); |
| if (card == NULL) |
| return NULL; |
| if (xid) { |
| if (!snd_info_check_reserved_words(xid)) |
| goto __error; |
| strncpy(card->id, xid, sizeof(card->id) - 1); |
| } |
| write_lock(&snd_card_rwlock); |
| if (idx < 0) { |
| int idx2; |
| for (idx2 = 0; idx2 < snd_ecards_limit; idx2++) |
| if (!(snd_cards_lock & (1 << idx2))) { |
| idx = idx2; |
| break; |
| } |
| } else if (idx < snd_ecards_limit) { |
| if (snd_cards_lock & (1 << idx)) |
| idx = -1; /* invalid */ |
| } |
| if (idx < 0 || idx >= snd_ecards_limit) { |
| write_unlock(&snd_card_rwlock); |
| if (idx >= snd_ecards_limit) |
| snd_printk("card %i is out of range (0-%i)\n", idx, snd_ecards_limit-1); |
| goto __error; |
| } |
| snd_cards_lock |= 1 << idx; /* lock it */ |
| write_unlock(&snd_card_rwlock); |
| card->number = idx; |
| if (!card->id[0]) |
| sprintf(card->id, "card%i", card->number); |
| card->module = module; |
| INIT_LIST_HEAD(&card->devices); |
| rwlock_init(&card->control_rwlock); |
| rwlock_init(&card->control_owner_lock); |
| INIT_LIST_HEAD(&card->controls); |
| INIT_LIST_HEAD(&card->ctl_files); |
| #ifdef CONFIG_PM |
| init_MUTEX(&card->power_lock); |
| init_waitqueue_head(&card->power_sleep); |
| #endif |
| /* the control interface cannot be accessed from the user space until */ |
| /* snd_cards_bitmask and snd_cards are set with snd_card_register */ |
| if ((err = snd_ctl_register(card)) < 0) { |
| snd_printd("unable to register control minors\n"); |
| goto __error; |
| } |
| if ((err = snd_info_card_register(card)) < 0) { |
| snd_printd("unable to register card info\n"); |
| goto __error_ctl; |
| } |
| if ((entry = snd_info_create_card_entry(card, "id", card->proc_root)) == NULL) { |
| snd_printd("unable to create card entry\n"); |
| goto __error_info; |
| } |
| entry->content = SNDRV_INFO_CONTENT_TEXT; |
| entry->c.text.read_size = PAGE_SIZE; |
| entry->c.text.read = snd_card_id_read; |
| if (snd_info_register(entry) < 0) { |
| snd_info_free_entry(entry); |
| goto __error_info; |
| } |
| card->proc_id = entry; |
| if (extra_size > 0) |
| card->private_data = (char *)card + sizeof(snd_card_t); |
| return card; |
| |
| __error_info: |
| snd_info_card_unregister(card); |
| __error_ctl: |
| snd_ctl_unregister(card); |
| __error: |
| kfree(card); |
| return NULL; |
| } |
| |
| int snd_card_free(snd_card_t * card) |
| { |
| if (card == NULL) |
| return -EINVAL; |
| write_lock(&snd_card_rwlock); |
| snd_cards[card->number] = NULL; |
| snd_cards_count--; |
| write_unlock(&snd_card_rwlock); |
| #ifdef CONFIG_SND_OSSEMUL |
| if (snd_mixer_oss_notify_callback) |
| snd_mixer_oss_notify_callback(card, 1); |
| #endif |
| if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) { |
| snd_printk("unable to free all devices (pre)\n"); |
| /* Fatal, but this situation should never occur */ |
| } |
| if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) { |
| snd_printk("unable to free all devices (normal)\n"); |
| /* Fatal, but this situation should never occur */ |
| } |
| if (snd_ctl_unregister(card) < 0) { |
| snd_printk("unable to unregister control minors\n"); |
| /* Not fatal error */ |
| } |
| if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) { |
| snd_printk("unable to free all devices (post)\n"); |
| /* Fatal, but this situation should never occur */ |
| } |
| if (card->private_free) |
| card->private_free(card); |
| snd_info_free_entry(card->proc_id); |
| if (snd_info_card_unregister(card) < 0) { |
| snd_printk("unable to unregister card info\n"); |
| /* Not fatal error */ |
| } |
| write_lock(&snd_card_rwlock); |
| snd_cards_lock &= ~(1 << card->number); |
| write_unlock(&snd_card_rwlock); |
| kfree(card); |
| return 0; |
| } |
| |
| int snd_card_register(snd_card_t * card) |
| { |
| int err; |
| |
| snd_runtime_check(card != NULL, return -EINVAL); |
| if ((err = snd_device_register_all(card)) < 0) |
| return err; |
| write_lock(&snd_card_rwlock); |
| snd_cards[card->number] = card; |
| snd_cards_count++; |
| write_unlock(&snd_card_rwlock); |
| #ifdef CONFIG_SND_OSSEMUL |
| if (snd_mixer_oss_notify_callback) |
| snd_mixer_oss_notify_callback(card, 0); |
| #endif |
| return 0; |
| } |
| |
| static snd_info_entry_t *snd_card_info_entry = NULL; |
| |
| static void snd_card_info_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer) |
| { |
| int idx, count; |
| snd_card_t *card; |
| |
| for (idx = count = 0; idx < SNDRV_CARDS; idx++) { |
| read_lock(&snd_card_rwlock); |
| if ((card = snd_cards[idx]) != NULL) { |
| count++; |
| snd_iprintf(buffer, "%i [%-15s]: %s - %s\n", |
| idx, |
| card->id, |
| card->driver, |
| card->shortname); |
| snd_iprintf(buffer, " %s\n", |
| card->longname); |
| } |
| read_unlock(&snd_card_rwlock); |
| } |
| if (!count) |
| snd_iprintf(buffer, "--- no soundcards ---\n"); |
| } |
| |
| #ifdef CONFIG_SND_OSSEMUL |
| |
| void snd_card_info_read_oss(snd_info_buffer_t * buffer) |
| { |
| int idx, count; |
| snd_card_t *card; |
| |
| for (idx = count = 0; idx < SNDRV_CARDS; idx++) { |
| read_lock(&snd_card_rwlock); |
| if ((card = snd_cards[idx]) != NULL) { |
| count++; |
| snd_iprintf(buffer, "%s\n", card->longname); |
| } |
| read_unlock(&snd_card_rwlock); |
| } |
| if (!count) { |
| snd_iprintf(buffer, "--- no soundcards ---\n"); |
| } |
| } |
| |
| #endif |
| |
| int __init snd_card_info_init(void) |
| { |
| snd_info_entry_t *entry; |
| |
| entry = snd_info_create_module_entry(THIS_MODULE, "cards", NULL); |
| snd_runtime_check(entry != NULL, return -ENOMEM); |
| entry->content = SNDRV_INFO_CONTENT_TEXT; |
| entry->c.text.read_size = PAGE_SIZE; |
| entry->c.text.read = snd_card_info_read; |
| if (snd_info_register(entry) < 0) { |
| snd_info_free_entry(entry); |
| return -ENOMEM; |
| } |
| snd_card_info_entry = entry; |
| |
| return 0; |
| } |
| |
| int __exit snd_card_info_done(void) |
| { |
| if (snd_card_info_entry) |
| snd_info_unregister(snd_card_info_entry); |
| return 0; |
| } |
| |
| int snd_component_add(snd_card_t *card, const char *component) |
| { |
| char *ptr; |
| int len = strlen(component); |
| |
| ptr = strstr(card->components, component); |
| if (ptr != NULL) { |
| if (ptr[len] == '\0' || ptr[len] == ' ') /* already there */ |
| return 1; |
| } |
| if (strlen(card->components) + 1 + len + 1 > sizeof(card->components)) { |
| snd_BUG(); |
| return -ENOMEM; |
| } |
| if (card->components[0] != '\0') |
| strcat(card->components, " "); |
| strcat(card->components, component); |
| return 0; |
| } |
| |
| #ifdef CONFIG_PM |
| /* the power lock must be active before call */ |
| void snd_power_wait(snd_card_t *card) |
| { |
| wait_queue_t wait; |
| |
| init_waitqueue_entry(&wait, current); |
| add_wait_queue(&card->power_sleep, &wait); |
| snd_power_unlock(card); |
| schedule_timeout(30 * HZ); |
| remove_wait_queue(&card->power_sleep, &wait); |
| snd_power_lock(card); |
| } |
| #endif /* CONFIG_PM */ |