diff options
Diffstat (limited to 'sound/core')
-rw-r--r-- | sound/core/control.c | 2 | ||||
-rw-r--r-- | sound/core/init.c | 169 | ||||
-rw-r--r-- | sound/core/misc.c | 2 | ||||
-rw-r--r-- | sound/core/pcm.c | 99 | ||||
-rw-r--r-- | sound/core/pcm_lib.c | 3 | ||||
-rw-r--r-- | sound/core/pcm_native.c | 15 | ||||
-rw-r--r-- | sound/core/vmaster.c | 46 |
7 files changed, 228 insertions, 108 deletions
diff --git a/sound/core/control.c b/sound/core/control.c index 819a5c579a39..2487a6bb1c54 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -1313,7 +1313,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, err = -EPERM; goto __kctl_end; } - err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv); + err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv); if (err > 0) { up_read(&card->controls_rwsem); snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id); diff --git a/sound/core/init.c b/sound/core/init.c index 3ac49b1b7cb8..068cf08d3ffb 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -480,74 +480,104 @@ int snd_card_free(struct snd_card *card) EXPORT_SYMBOL(snd_card_free); -static void snd_card_set_id_no_lock(struct snd_card *card, const char *nid) +/* retrieve the last word of shortname or longname */ +static const char *retrieve_id_from_card_name(const char *name) { - int i, len, idx_flag = 0, loops = SNDRV_CARDS; - const char *spos, *src; - char *id; - - if (nid == NULL) { - id = card->shortname; - spos = src = id; - while (*id != '\0') { - if (*id == ' ') - spos = id + 1; - id++; - } - } else { - spos = src = nid; + const char *spos = name; + + while (*name) { + if (isspace(*name) && isalnum(name[1])) + spos = name + 1; + name++; } - id = card->id; - while (*spos != '\0' && !isalnum(*spos)) - spos++; - if (isdigit(*spos)) - *id++ = isalpha(src[0]) ? src[0] : 'D'; - while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) { - if (isalnum(*spos)) - *id++ = *spos; - spos++; + return spos; +} + +/* return true if the given id string doesn't conflict any other card ids */ +static bool card_id_ok(struct snd_card *card, const char *id) +{ + int i; + if (!snd_info_check_reserved_words(id)) + return false; + for (i = 0; i < snd_ecards_limit; i++) { + if (snd_cards[i] && snd_cards[i] != card && + !strcmp(snd_cards[i]->id, id)) + return false; } - *id = '\0'; + return true; +} - id = card->id; +/* copy to card->id only with valid letters from nid */ +static void copy_valid_id_string(struct snd_card *card, const char *src, + const char *nid) +{ + char *id = card->id; + + while (*nid && !isalnum(*nid)) + nid++; + if (isdigit(*nid)) + *id++ = isalpha(*src) ? *src : 'D'; + while (*nid && (size_t)(id - card->id) < sizeof(card->id) - 1) { + if (isalnum(*nid)) + *id++ = *nid; + nid++; + } + *id = 0; +} + +/* Set card->id from the given string + * If the string conflicts with other ids, add a suffix to make it unique. + */ +static void snd_card_set_id_no_lock(struct snd_card *card, const char *src, + const char *nid) +{ + int len, loops; + bool with_suffix; + bool is_default = false; + char *id; - if (*id == '\0') + copy_valid_id_string(card, src, nid); + id = card->id; + + again: + /* use "Default" for obviously invalid strings + * ("card" conflicts with proc directories) + */ + if (!*id || !strncmp(id, "card", 4)) { strcpy(id, "Default"); + is_default = true; + } - while (1) { - if (loops-- == 0) { - snd_printk(KERN_ERR "unable to set card id (%s)\n", id); - strcpy(card->id, card->proc_root->name); - return; - } - if (!snd_info_check_reserved_words(id)) - goto __change; - for (i = 0; i < snd_ecards_limit; i++) { - if (snd_cards[i] && !strcmp(snd_cards[i]->id, id)) - goto __change; - } - break; + with_suffix = false; + for (loops = 0; loops < SNDRV_CARDS; loops++) { + if (card_id_ok(card, id)) + return; /* OK */ - __change: len = strlen(id); - if (idx_flag) { - if (id[len-1] != '9') - id[len-1]++; - else - id[len-1] = 'A'; - } else if ((size_t)len <= sizeof(card->id) - 3) { - strcat(id, "_1"); - idx_flag++; + if (!with_suffix) { + /* add the "_X" suffix */ + char *spos = id + len; + if (len > sizeof(card->id) - 3) + spos = id + sizeof(card->id) - 3; + strcpy(spos, "_1"); + with_suffix = true; } else { - spos = id + len - 2; - if ((size_t)len <= sizeof(card->id) - 2) - spos++; - *(char *)spos++ = '_'; - *(char *)spos++ = '1'; - *(char *)spos++ = '\0'; - idx_flag++; + /* modify the existing suffix */ + if (id[len - 1] != '9') + id[len - 1]++; + else + id[len - 1] = 'A'; } } + /* fallback to the default id */ + if (!is_default) { + *id = 0; + goto again; + } + /* last resort... */ + snd_printk(KERN_ERR "unable to set card id (%s)\n", id); + if (card->proc_root->name) + strcpy(card->id, card->proc_root->name); } /** @@ -564,7 +594,7 @@ void snd_card_set_id(struct snd_card *card, const char *nid) if (card->id[0] != '\0') return; mutex_lock(&snd_card_mutex); - snd_card_set_id_no_lock(card, nid); + snd_card_set_id_no_lock(card, nid, nid); mutex_unlock(&snd_card_mutex); } EXPORT_SYMBOL(snd_card_set_id); @@ -596,22 +626,12 @@ card_id_store_attr(struct device *dev, struct device_attribute *attr, memcpy(buf1, buf, copy); buf1[copy] = '\0'; mutex_lock(&snd_card_mutex); - if (!snd_info_check_reserved_words(buf1)) { - __exist: + if (!card_id_ok(NULL, buf1)) { mutex_unlock(&snd_card_mutex); return -EEXIST; } - for (idx = 0; idx < snd_ecards_limit; idx++) { - if (snd_cards[idx] && !strcmp(snd_cards[idx]->id, buf1)) { - if (card == snd_cards[idx]) - goto __ok; - else - goto __exist; - } - } strcpy(card->id, buf1); snd_info_card_id_change(card); -__ok: mutex_unlock(&snd_card_mutex); return count; @@ -665,7 +685,18 @@ int snd_card_register(struct snd_card *card) mutex_unlock(&snd_card_mutex); return 0; } - snd_card_set_id_no_lock(card, card->id[0] == '\0' ? NULL : card->id); + if (*card->id) { + /* make a unique id name from the given string */ + char tmpid[sizeof(card->id)]; + memcpy(tmpid, card->id, sizeof(card->id)); + snd_card_set_id_no_lock(card, tmpid, tmpid); + } else { + /* create an id from either shortname or longname */ + const char *src; + src = *card->shortname ? card->shortname : card->longname; + snd_card_set_id_no_lock(card, src, + retrieve_id_from_card_name(src)); + } snd_cards[card->number] = card; mutex_unlock(&snd_card_mutex); init_info_for_card(card); diff --git a/sound/core/misc.c b/sound/core/misc.c index 465f0ce772cb..768167925409 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -72,7 +72,7 @@ void __snd_printk(unsigned int level, const char *path, int line, char verbose_fmt[] = KERN_DEFAULT "ALSA %s:%d %pV"; #endif -#ifdef CONFIG_SND_DEBUG +#ifdef CONFIG_SND_DEBUG if (debug < level) return; #endif diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 8928ca871c22..6e4bfcc14254 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -650,7 +650,7 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) pstr->stream = stream; pstr->pcm = pcm; pstr->substream_count = substream_count; - if (substream_count > 0) { + if (substream_count > 0 && !pcm->internal) { err = snd_pcm_stream_proc_init(pstr); if (err < 0) { snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n"); @@ -674,15 +674,18 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) pstr->substream = substream; else prev->next = substream; - err = snd_pcm_substream_proc_init(substream); - if (err < 0) { - snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n"); - if (prev == NULL) - pstr->substream = NULL; - else - prev->next = NULL; - kfree(substream); - return err; + + if (!pcm->internal) { + err = snd_pcm_substream_proc_init(substream); + if (err < 0) { + snd_printk(KERN_ERR "Error in snd_pcm_stream_proc_init\n"); + if (prev == NULL) + pstr->substream = NULL; + else + prev->next = NULL; + kfree(substream); + return err; + } } substream->group = &substream->self_group; spin_lock_init(&substream->self_group.lock); @@ -696,25 +699,9 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) EXPORT_SYMBOL(snd_pcm_new_stream); -/** - * snd_pcm_new - create a new PCM instance - * @card: the card instance - * @id: the id string - * @device: the device index (zero based) - * @playback_count: the number of substreams for playback - * @capture_count: the number of substreams for capture - * @rpcm: the pointer to store the new pcm instance - * - * Creates a new PCM instance. - * - * The pcm operators have to be set afterwards to the new instance - * via snd_pcm_set_ops(). - * - * Returns zero if successful, or a negative error code on failure. - */ -int snd_pcm_new(struct snd_card *card, const char *id, int device, - int playback_count, int capture_count, - struct snd_pcm ** rpcm) +static int _snd_pcm_new(struct snd_card *card, const char *id, int device, + int playback_count, int capture_count, bool internal, + struct snd_pcm **rpcm) { struct snd_pcm *pcm; int err; @@ -735,6 +722,7 @@ int snd_pcm_new(struct snd_card *card, const char *id, int device, } pcm->card = card; pcm->device = device; + pcm->internal = internal; if (id) strlcpy(pcm->id, id, sizeof(pcm->id)); if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) { @@ -756,8 +744,59 @@ int snd_pcm_new(struct snd_card *card, const char *id, int device, return 0; } +/** + * snd_pcm_new - create a new PCM instance + * @card: the card instance + * @id: the id string + * @device: the device index (zero based) + * @playback_count: the number of substreams for playback + * @capture_count: the number of substreams for capture + * @rpcm: the pointer to store the new pcm instance + * + * Creates a new PCM instance. + * + * The pcm operators have to be set afterwards to the new instance + * via snd_pcm_set_ops(). + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_new(struct snd_card *card, const char *id, int device, + int playback_count, int capture_count, struct snd_pcm **rpcm) +{ + return _snd_pcm_new(card, id, device, playback_count, capture_count, + false, rpcm); +} EXPORT_SYMBOL(snd_pcm_new); +/** + * snd_pcm_new_internal - create a new internal PCM instance + * @card: the card instance + * @id: the id string + * @device: the device index (zero based - shared with normal PCMs) + * @playback_count: the number of substreams for playback + * @capture_count: the number of substreams for capture + * @rpcm: the pointer to store the new pcm instance + * + * Creates a new internal PCM instance with no userspace device or procfs + * entries. This is used by ASoC Back End PCMs in order to create a PCM that + * will only be used internally by kernel drivers. i.e. it cannot be opened + * by userspace. It provides existing ASoC components drivers with a substream + * and access to any private data. + * + * The pcm operators have to be set afterwards to the new instance + * via snd_pcm_set_ops(). + * + * Returns zero if successful, or a negative error code on failure. + */ +int snd_pcm_new_internal(struct snd_card *card, const char *id, int device, + int playback_count, int capture_count, + struct snd_pcm **rpcm) +{ + return _snd_pcm_new(card, id, device, playback_count, capture_count, + true, rpcm); +} +EXPORT_SYMBOL(snd_pcm_new_internal); + static void snd_pcm_free_stream(struct snd_pcm_str * pstr) { struct snd_pcm_substream *substream, *substream_next; @@ -994,7 +1033,7 @@ static int snd_pcm_dev_register(struct snd_device *device) } for (cidx = 0; cidx < 2; cidx++) { int devtype = -1; - if (pcm->streams[cidx].substream == NULL) + if (pcm->streams[cidx].substream == NULL || pcm->internal) continue; switch (cidx) { case SNDRV_PCM_STREAM_PLAYBACK: diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 3420bd3da5d7..4d18941178e6 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1029,7 +1029,8 @@ static int snd_interval_ratden(struct snd_interval *i, * * Returns non-zero if the value is changed, zero if not changed. */ -int snd_interval_list(struct snd_interval *i, unsigned int count, unsigned int *list, unsigned int mask) +int snd_interval_list(struct snd_interval *i, unsigned int count, + const unsigned int *list, unsigned int mask) { unsigned int k; struct snd_interval list_range; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 25ed9fe41b89..3fe99e644eb8 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -1586,12 +1586,18 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) struct file *file; struct snd_pcm_file *pcm_file; struct snd_pcm_substream *substream1; + struct snd_pcm_group *group; file = snd_pcm_file_fd(fd); if (!file) return -EBADFD; pcm_file = file->private_data; substream1 = pcm_file->substream; + group = kmalloc(sizeof(*group), GFP_KERNEL); + if (!group) { + res = -ENOMEM; + goto _nolock; + } down_write(&snd_pcm_link_rwsem); write_lock_irq(&snd_pcm_link_rwlock); if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN || @@ -1604,11 +1610,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) goto _end; } if (!snd_pcm_stream_linked(substream)) { - substream->group = kmalloc(sizeof(struct snd_pcm_group), GFP_ATOMIC); - if (substream->group == NULL) { - res = -ENOMEM; - goto _end; - } + substream->group = group; spin_lock_init(&substream->group->lock); INIT_LIST_HEAD(&substream->group->substreams); list_add_tail(&substream->link_list, &substream->group->substreams); @@ -1620,7 +1622,10 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd) _end: write_unlock_irq(&snd_pcm_link_rwlock); up_write(&snd_pcm_link_rwsem); + _nolock: fput(file); + if (res < 0) + kfree(group); return res; } diff --git a/sound/core/vmaster.c b/sound/core/vmaster.c index 130cfe677d60..14a286a7bf2b 100644 --- a/sound/core/vmaster.c +++ b/sound/core/vmaster.c @@ -37,6 +37,8 @@ struct link_master { struct link_ctl_info info; int val; /* the master value */ unsigned int tlv[4]; + void (*hook)(void *private_data, int); + void *hook_private_data; }; /* @@ -126,7 +128,9 @@ static int master_init(struct link_master *master) master->info.count = 1; /* always mono */ /* set full volume as default (= no attenuation) */ master->val = master->info.max_val; - return 0; + if (master->hook) + master->hook(master->hook_private_data, master->val); + return 1; } return -ENOENT; } @@ -329,6 +333,8 @@ static int master_put(struct snd_kcontrol *kcontrol, slave_put_val(slave, uval); } kfree(uval); + if (master->hook && !err) + master->hook(master->hook_private_data, master->val); return 1; } @@ -408,3 +414,41 @@ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, return kctl; } EXPORT_SYMBOL(snd_ctl_make_virtual_master); + +/** + * snd_ctl_add_vmaster_hook - Add a hook to a vmaster control + * @kcontrol: vmaster kctl element + * @hook: the hook function + * + * Adds the given hook to the vmaster control element so that it's called + * at each time when the value is changed. + */ +int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kcontrol, + void (*hook)(void *private_data, int), + void *private_data) +{ + struct link_master *master = snd_kcontrol_chip(kcontrol); + master->hook = hook; + master->hook_private_data = private_data; + return 0; +} +EXPORT_SYMBOL_GPL(snd_ctl_add_vmaster_hook); + +/** + * snd_ctl_sync_vmaster_hook - Sync the vmaster hook + * @kcontrol: vmaster kctl element + * + * Call the hook function to synchronize with the current value of the given + * vmaster element. NOP when NULL is passed to @kcontrol or the hook doesn't + * exist. + */ +void snd_ctl_sync_vmaster_hook(struct snd_kcontrol *kcontrol) +{ + struct link_master *master; + if (!kcontrol) + return; + master = snd_kcontrol_chip(kcontrol); + if (master->hook) + master->hook(master->hook_private_data, master->val); +} +EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster_hook); |