/** * snd_ctl_notify - Send notification to user-space for a control change * @card: the card to send notification * @mask: the event mask, SNDRV_CTL_EVENT_* * @id: the ctl element id to send notification * * This function adds an event record with the given id and mask, appends * to the list and wakes up the user-space for notification. This can be * called in the atomic context.
*/ void snd_ctl_notify(struct snd_card *card, unsignedint mask, struct snd_ctl_elem_id *id)
{ struct snd_ctl_file *ctl; struct snd_kctl_event *ev;
if (snd_BUG_ON(!card || !id)) return; if (card->shutdown) return;
guard(read_lock_irqsave)(&card->controls_rwlock); #if IS_ENABLED(CONFIG_SND_MIXER_OSS)
card->mixer_oss_change_count++; #endif
list_for_each_entry(ctl, &card->ctl_files, list) { if (!ctl->subscribed) continue;
scoped_guard(spinlock, &ctl->read_lock) {
list_for_each_entry(ev, &ctl->events, list) { if (ev->id.numid == id->numid) {
ev->mask |= mask; goto _found;
}
}
ev = kzalloc(sizeof(*ev), GFP_ATOMIC); if (ev) {
ev->id = *id;
ev->mask = mask;
list_add_tail(&ev->list, &ctl->events);
} else {
dev_err(card->dev, "No memory available to allocate event\n");
}
_found:
wake_up(&ctl->change_sleep);
}
snd_kill_fasync(ctl->fasync, SIGIO, POLL_IN);
}
}
EXPORT_SYMBOL(snd_ctl_notify);
/** * snd_ctl_notify_one - Send notification to user-space for a control change * @card: the card to send notification * @mask: the event mask, SNDRV_CTL_EVENT_* * @kctl: the pointer with the control instance * @ioff: the additional offset to the control index * * This function calls snd_ctl_notify() and does additional jobs * like LED state changes.
*/ void snd_ctl_notify_one(struct snd_card *card, unsignedint mask, struct snd_kcontrol *kctl, unsignedint ioff)
{ struct snd_ctl_elem_id id = kctl->id; struct snd_ctl_layer_ops *lops;
/** * snd_ctl_new - create a new control instance with some elements * @kctl: the pointer to store new control instance * @count: the number of elements in this control * @access: the default access flags for elements in this control * @file: given when locking these elements * * Allocates a memory object for a new control instance. The instance has * elements as many as the given number (@count). Each element has given * access permissions (@access). Each element is locked when @file is given. * * Return: 0 on success, error code on failure
*/ staticint snd_ctl_new(struct snd_kcontrol **kctl, unsignedint count, unsignedint access, struct snd_ctl_file *file)
{ unsignedint idx;
if (count == 0 || count > MAX_CONTROL_COUNT) return -EINVAL;
*kctl = kzalloc(struct_size(*kctl, vd, count), GFP_KERNEL); if (!*kctl) return -ENOMEM;
/** * snd_ctl_new1 - create a control instance from the template * @ncontrol: the initialization record * @private_data: the private data to set * * Allocates a new struct snd_kcontrol instance and initialize from the given * template. When the access field of ncontrol is 0, it's assumed as * READWRITE access. When the count field is 0, it's assumes as one. * * Return: The pointer of the newly generated instance, or %NULL on failure.
*/ struct snd_kcontrol *snd_ctl_new1(conststruct snd_kcontrol_new *ncontrol, void *private_data)
{ struct snd_kcontrol *kctl; unsignedint count; unsignedint access; int err;
if (snd_BUG_ON(!ncontrol || !ncontrol->info)) return NULL;
count = ncontrol->count; if (count == 0)
count = 1;
/* The 'numid' member is decided when calling snd_ctl_add(). */
kctl->id.iface = ncontrol->iface;
kctl->id.device = ncontrol->device;
kctl->id.subdevice = ncontrol->subdevice; if (ncontrol->name) {
strscpy(kctl->id.name, ncontrol->name, sizeof(kctl->id.name)); if (strcmp(ncontrol->name, kctl->id.name) != 0)
pr_warn("ALSA: Control name '%s' truncated to '%s'\n",
ncontrol->name, kctl->id.name);
}
kctl->id.index = ncontrol->index;
/** * snd_ctl_free_one - release the control instance * @kcontrol: the control instance * * Releases the control instance created via snd_ctl_new() * or snd_ctl_new1(). * Don't call this after the control was added to the card.
*/ void snd_ctl_free_one(struct snd_kcontrol *kcontrol)
{ if (kcontrol) { if (kcontrol->private_free)
kcontrol->private_free(kcontrol);
kfree(kcontrol);
}
}
EXPORT_SYMBOL(snd_ctl_free_one);
while (snd_ctl_remove_numid_conflict(card, count)) { if (--iter == 0) { /* this situation is very unlikely */
dev_err(card->dev, "unable to allocate new control numid\n"); return -ENOMEM;
}
} return 0;
}
/* check whether the given id is contained in the given kctl */ staticbool elem_id_matches(conststruct snd_kcontrol *kctl, conststruct snd_ctl_elem_id *id)
{ return kctl->id.iface == id->iface &&
kctl->id.device == id->device &&
kctl->id.subdevice == id->subdevice &&
!strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)) &&
kctl->id.index <= id->index &&
kctl->id.index + kctl->count > id->index;
}
#ifdef CONFIG_SND_CTL_FAST_LOOKUP /* Compute a hash key for the corresponding ctl id * It's for the name lookup, hence the numid is excluded. * The hash key is bound in LONG_MAX to be used for Xarray key.
*/ #define MULTIPLIER 37 staticunsignedlong get_ctl_id_hash(conststruct snd_ctl_elem_id *id)
{ int i; unsignedlong h;
h = id->iface;
h = MULTIPLIER * h + id->device;
h = MULTIPLIER * h + id->subdevice; for (i = 0; i < SNDRV_CTL_ELEM_ID_NAME_MAXLEN && id->name[i]; i++)
h = MULTIPLIER * h + id->name[i];
h = MULTIPLIER * h + id->index;
h &= LONG_MAX; return h;
}
/* add hash entries to numid and ctl xarray tables */ staticvoid add_hash_entries(struct snd_card *card, struct snd_kcontrol *kcontrol)
{ struct snd_ctl_elem_id id = kcontrol->id; int i;
/** * snd_ctl_add - add the control instance to the card * @card: the card instance * @kcontrol: the control instance to add * * Adds the control instance created via snd_ctl_new() or * snd_ctl_new1() to the given card. Assigns also an unique * numid used for fast search. * * It frees automatically the control which cannot be added. * * Return: Zero if successful, or a negative error code on failure. *
*/ int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol)
{ return snd_ctl_add_replace(card, kcontrol, CTL_ADD_EXCLUSIVE);
}
EXPORT_SYMBOL(snd_ctl_add);
/** * snd_ctl_replace - replace the control instance of the card * @card: the card instance * @kcontrol: the control instance to replace * @add_on_replace: add the control if not already added * * Replaces the given control. If the given control does not exist * and the add_on_replace flag is set, the control is added. If the * control exists, it is destroyed first. * * It frees automatically the control which cannot be added or replaced. * * Return: Zero if successful, or a negative error code on failure.
*/ int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, bool add_on_replace)
{ return snd_ctl_add_replace(card, kcontrol,
add_on_replace ? CTL_ADD_ON_REPLACE : CTL_REPLACE);
}
EXPORT_SYMBOL(snd_ctl_replace);
/** * snd_ctl_remove - remove the control from the card and release it * @card: the card instance * @kcontrol: the control instance to remove * * Removes the control from the card and then releases the instance. * You don't need to call snd_ctl_free_one(). * Passing NULL to @kcontrol argument is allowed as noop. * * Return: 0 if successful, or a negative error code on failure. * * Note that this function takes card->controls_rwsem lock internally.
*/ int snd_ctl_remove(struct snd_card *card, struct snd_kcontrol *kcontrol)
{ if (!kcontrol) return 0;
guard(rwsem_write)(&card->controls_rwsem); return snd_ctl_remove_locked(card, kcontrol);
}
EXPORT_SYMBOL(snd_ctl_remove);
/** * snd_ctl_remove_id - remove the control of the given id and release it * @card: the card instance * @id: the control id to remove * * Finds the control instance with the given id, removes it from the * card list and releases it. * * Return: 0 if successful, or a negative error code on failure.
*/ int snd_ctl_remove_id(struct snd_card *card, struct snd_ctl_elem_id *id)
{ struct snd_kcontrol *kctl;
/** * snd_ctl_remove_user_ctl - remove and release the unlocked user control * @file: active control handle * @id: the control id to remove * * Finds the control instance with the given id, removes it from the * card list and releases it. * * Return: 0 if successful, or a negative error code on failure.
*/ staticint snd_ctl_remove_user_ctl(struct snd_ctl_file * file, struct snd_ctl_elem_id *id)
{ struct snd_card *card = file->card; struct snd_kcontrol *kctl; int idx;
guard(rwsem_write)(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, id); if (kctl == NULL) return -ENOENT; if (!(kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_USER)) return -EINVAL; for (idx = 0; idx < kctl->count; idx++) if (kctl->vd[idx].owner != NULL && kctl->vd[idx].owner != file) return -EBUSY; return snd_ctl_remove_locked(card, kctl);
}
/** * snd_ctl_activate_id - activate/inactivate the control of the given id * @card: the card instance * @id: the control id to activate/inactivate * @active: non-zero to activate * * Finds the control instance with the given id, and activate or * inactivate the control together with notification, if changed. * The given ID data is filled with full information. * * Return: 0 if unchanged, 1 if changed, or a negative error code on failure.
*/ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, int active)
{ struct snd_kcontrol *kctl; struct snd_kcontrol_volatile *vd; unsignedint index_offset; int ret;
/** * snd_ctl_rename_id - replace the id of a control on the card * @card: the card instance * @src_id: the old id * @dst_id: the new id * * Finds the control with the old id from the card, and replaces the * id with the new one. * * The function tries to keep the already assigned numid while replacing * the rest. * * Note that this function should be used only in the card initialization * phase. Calling after the card instantiation may cause issues with * user-space expecting persistent numids. * * Return: Zero if successful, or a negative error code on failure.
*/ int snd_ctl_rename_id(struct snd_card *card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id)
{ struct snd_kcontrol *kctl; int saved_numid;
/** * snd_ctl_rename - rename the control on the card * @card: the card instance * @kctl: the control to rename * @name: the new name * * Renames the specified control on the card to the new name. * * Note that this function takes card->controls_rwsem lock internally.
*/ void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl, constchar *name)
{
guard(rwsem_write)(&card->controls_rwsem);
remove_hash_entries(card, kctl);
if (strscpy(kctl->id.name, name, sizeof(kctl->id.name)) < 0)
pr_warn("ALSA: Renamed control new name '%s' truncated to '%s'\n",
name, kctl->id.name);
/** * snd_ctl_find_numid - find the control instance with the given number-id * @card: the card instance * @numid: the number-id to search * * Finds the control instance with the given number-id from the card. * * Return: The pointer of the instance if found, or %NULL if not. * * Note that this function takes card->controls_rwlock lock internally.
*/ struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsignedint numid)
{ if (snd_BUG_ON(!card || !numid)) return NULL;
/** * snd_ctl_find_id - find the control instance with the given id * @card: the card instance * @id: the id to search * * Finds the control instance with the given id from the card. * * Return: The pointer of the instance if found, or %NULL if not. * * Note that this function takes card->controls_rwlock lock internally.
*/ struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card, conststruct snd_ctl_elem_id *id)
{ struct snd_kcontrol *kctl;
if (snd_BUG_ON(!card || !id)) return NULL;
if (id->numid != 0) return snd_ctl_find_numid(card, id->numid); #ifdef CONFIG_SND_CTL_FAST_LOOKUP
kctl = xa_load(&card->ctl_hash, get_ctl_id_hash(id)); if (kctl && elem_id_matches(kctl, id)) return kctl; if (!card->ctl_hash_collision) return NULL; /* we can rely on only hash table */ #endif /* no matching in hash table - try all as the last resort */
guard(read_lock_irqsave)(&card->controls_rwlock);
list_for_each_entry(kctl, &card->controls, list) if (elem_id_matches(kctl, id)) return kctl;
/* fill the remaining snd_ctl_elem_value data with the given pattern */ staticvoid fill_remaining_elem_value(struct snd_ctl_elem_value *control, struct snd_ctl_elem_info *info,
u32 pattern)
{
size_t offset = value_sizes[info->type] * info->count;
if (lval < lmin || lval > lmax) { if (print_error)
dev_err(card->dev, "control %i:%i:%i:%s:%i: value out of range %lld (%lld/%lld) at count %i\n",
control->id.iface, control->id.device,
control->id.subdevice, control->id.name,
control->id.index, lval, lmin, lmax, i); return -EINVAL;
} if (lstep) {
div64_u64_rem(lval, lstep, &rem); if (rem) { if (print_error)
dev_err(card->dev, "control %i:%i:%i:%s:%i: unaligned value %lld (step %lld) at count %i\n",
control->id.iface, control->id.device,
control->id.subdevice, control->id.name,
control->id.index, lval, lstep, i); return -EINVAL;
}
}
return 0;
}
/* check whether the all input values are valid for the given elem value */ staticint sanity_check_input_values(struct snd_card *card, conststruct snd_ctl_elem_value *control, conststruct snd_ctl_elem_info *info, bool print_error)
{ int i, ret;
switch (info->type) { case SNDRV_CTL_ELEM_TYPE_BOOLEAN: case SNDRV_CTL_ELEM_TYPE_INTEGER: case SNDRV_CTL_ELEM_TYPE_INTEGER64: case SNDRV_CTL_ELEM_TYPE_ENUMERATED: for (i = 0; i < info->count; i++) {
ret = sanity_check_int_value(card, control, info, i,
print_error); if (ret < 0) return ret;
} break; default: break;
}
return 0;
}
/* perform sanity checks to the given snd_ctl_elem_value object */ staticint sanity_check_elem_value(struct snd_card *card, conststruct snd_ctl_elem_value *control, conststruct snd_ctl_elem_info *info,
u32 pattern)
{
size_t offset; int ret;
u32 *p;
ret = sanity_check_input_values(card, control, info, true); if (ret < 0) return ret;
/* check whether the remaining area kept untouched */
offset = value_sizes[info->type] * info->count;
offset = DIV_ROUND_UP(offset, sizeof(u32));
p = (u32 *)control->value.bytes.data + offset; for (; offset < sizeof(control->value) / sizeof(u32); offset++, p++) { if (*p != pattern) {
ret = -EINVAL; break;
}
*p = 0; /* clear the checked area */
}
#ifdef CONFIG_SND_CTL_DEBUG /* info is needed only for validation */
memset(&info, 0, sizeof(info));
info.id = control->id;
ret = __snd_ctl_elem_info(card, kctl, &info, NULL); if (ret < 0) return ret; #endif
control = memdup_user(_control, sizeof(*control)); if (IS_ERR(control)) return PTR_ERR(control);
result = snd_power_ref_and_wait(card); if (result) return result;
result = snd_ctl_elem_read(card, control);
snd_power_unref(card); if (result < 0) return result;
if (copy_to_user(_control, control, sizeof(*control))) return -EFAULT; return result;
}
if (copy_from_user(&id, _id, sizeof(id))) return -EFAULT;
guard(rwsem_write)(&card->controls_rwsem);
kctl = snd_ctl_find_id(card, &id); if (!kctl) return -ENOENT;
vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)]; if (!vd->owner) return -EINVAL; if (vd->owner != file) return -EPERM;
vd->owner = NULL; return 0;
}
struct user_element { struct snd_ctl_elem_info info; struct snd_card *card; char *elem_data; /* element data */ unsignedlong elem_data_size; /* size of element data in bytes */ void *tlv_data; /* TLV data */ unsignedlong tlv_data_size; /* TLV data size */ void *priv_data; /* private data (like strings for enumerated type) */
};
// check whether the addition (in bytes) of user ctl element may overflow the limit. staticbool check_user_elem_overflow(struct snd_card *card, ssize_t add)
{ return (ssize_t)card->user_ctl_alloc_size + add > max_user_ctl_alloc_size;
}
if (!*info->id.name) return -EINVAL; if (strnlen(info->id.name, sizeof(info->id.name)) >= sizeof(info->id.name)) return -EINVAL;
/* Delete a control to replace them if needed. */ if (replace) {
info->id.numid = 0;
err = snd_ctl_remove_user_ctl(file, &info->id); if (err) return err;
}
/* Check the number of elements for this userspace control. */
count = info->owner; if (count == 0)
count = 1; if (count > MAX_CONTROL_COUNT) return -EINVAL;
/* In initial state, nothing is available as TLV container. */ if (access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
access |= SNDRV_CTL_ELEM_ACCESS_USER;
/* * Check information and calculate the size of data specific to * this userspace control.
*/ /* pass NULL to card for suppressing error messages */
err = snd_ctl_check_elem_info(NULL, info); if (err < 0) return err; /* user-space control doesn't allow zero-size data */ if (info->count < 1) return -EINVAL;
private_size = value_sizes[info->type] * info->count;
alloc_size = compute_user_elem_size(private_size, count);
guard(rwsem_write)(&card->controls_rwsem); if (check_user_elem_overflow(card, alloc_size)) return -ENOMEM;
/* * Keep memory object for this userspace control. After passing this * code block, the instance should be freed by snd_ctl_free_one(). * * Note that these elements in this control are locked.
*/
err = snd_ctl_new(&kctl, count, access, file); if (err < 0) return err;
memcpy(&kctl->id, &info->id, sizeof(kctl->id));
ue = kzalloc(alloc_size, GFP_KERNEL); if (!ue) {
kfree(kctl); return -ENOMEM;
}
kctl->private_data = ue;
kctl->private_free = snd_ctl_elem_user_free;
// increment the allocated size; decremented again at private_free.
card->user_ctl_alloc_size += alloc_size;
/* Set private data for this userspace control. */
ue->card = card;
ue->info = *info;
ue->info.access = 0;
ue->elem_data = (char *)ue + sizeof(*ue);
ue->elem_data_size = private_size; if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
err = snd_ctl_elem_init_enum_names(ue); if (err < 0) {
snd_ctl_free_one(kctl); return err;
}
}
/* Set callback functions. */ if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED)
kctl->info = snd_ctl_elem_user_enum_info; else
kctl->info = snd_ctl_elem_user_info; if (access & SNDRV_CTL_ELEM_ACCESS_READ)
kctl->get = snd_ctl_elem_user_get; if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
kctl->put = snd_ctl_elem_user_put; if (access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
kctl->tlv.c = snd_ctl_elem_user_tlv;
/* This function manage to free the instance on failure. */
err = __snd_ctl_add_replace(card, kctl, CTL_ADD_EXCLUSIVE); if (err < 0) {
snd_ctl_free_one(kctl); return err;
}
offset = snd_ctl_get_ioff(kctl, &info->id);
snd_ctl_build_ioff(&info->id, kctl, offset); /* * Here we cannot fill any field for the number of elements added by * this operation because there're no specific fields. The usage of * 'owner' field for this purpose may cause any bugs to userspace * applications because the field originally means PID of a process * which locks the element.
*/ return 0;
}
staticint snd_ctl_elem_add_user(struct snd_ctl_file *file, struct snd_ctl_elem_info __user *_info, int replace)
{ struct snd_ctl_elem_info info; int err;
if (copy_from_user(&info, _info, sizeof(info))) return -EFAULT;
err = snd_ctl_elem_add(file, &info, replace); if (err < 0) return err; if (copy_to_user(_info, &info, sizeof(info))) {
snd_ctl_remove_user_ctl(file, &info.id); return -EFAULT;
}
/* Check support of the request for this element. */ for (i = 0; i < ARRAY_SIZE(pairs); ++i) { if (op_flag == pairs[i].op && (vd->access & pairs[i].perm)) break;
} if (i == ARRAY_SIZE(pairs)) return -ENXIO;
if (kctl->tlv.c == NULL) return -ENXIO;
/* Write and command operations are not allowed for locked element. */ if (op_flag != SNDRV_CTL_TLV_OP_READ &&
vd->owner != NULL && vd->owner != file) return -EPERM;
if (copy_from_user(&header, buf, sizeof(header))) return -EFAULT;
/* In design of control core, numerical ID starts at 1. */ if (header.numid == 0) return -EINVAL;
/* At least, container should include type and length fields. */ if (header.length < sizeof(unsignedint) * 2) return -EINVAL;
container_size = header.length;
container = buf->tlv;
kctl = snd_ctl_find_numid(file->card, header.numid); if (kctl == NULL) return -ENOENT;
/* Calculate index of the element in this set. */
id = kctl->id;
snd_ctl_build_ioff(&id, kctl, header.numid - id.numid);
vd = &kctl->vd[snd_ctl_get_ioff(kctl, &id)];
ctl = file->private_data; if (!ctl->subscribed) return 0;
poll_wait(file, &ctl->change_sleep, wait);
mask = 0; if (!list_empty(&ctl->events))
mask |= EPOLLIN | EPOLLRDNORM;
return mask;
}
/* * register the device-specific control-ioctls. * called from each device manager like pcm.c, hwdep.c, etc.
*/ staticint _snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn, struct list_head *lists)
{ struct snd_kctl_ioctl *pn;
/** * snd_ctl_register_ioctl - register the device-specific control-ioctls * @fcn: ioctl callback function * * called from each device manager like pcm.c, hwdep.c, etc. * * Return: zero if successful, or a negative error code
*/ int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn)
{ return _snd_ctl_register_ioctl(fcn, &snd_control_ioctls);
}
EXPORT_SYMBOL(snd_ctl_register_ioctl);
#ifdef CONFIG_COMPAT /** * snd_ctl_register_ioctl_compat - register the device-specific 32bit compat * control-ioctls * @fcn: ioctl callback function * * Return: zero if successful, or a negative error code
*/ int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{ return _snd_ctl_register_ioctl(fcn, &snd_control_compat_ioctls);
}
EXPORT_SYMBOL(snd_ctl_register_ioctl_compat); #endif
/** * snd_ctl_unregister_ioctl - de-register the device-specific control-ioctls * @fcn: ioctl callback function to unregister * * Return: zero if successful, or a negative error code
*/ int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn)
{ return _snd_ctl_unregister_ioctl(fcn, &snd_control_ioctls);
}
EXPORT_SYMBOL(snd_ctl_unregister_ioctl);
#ifdef CONFIG_COMPAT /** * snd_ctl_unregister_ioctl_compat - de-register the device-specific compat * 32bit control-ioctls * @fcn: ioctl callback function to unregister * * Return: zero if successful, or a negative error code
*/ int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn)
{ return _snd_ctl_unregister_ioctl(fcn, &snd_control_compat_ioctls);
}
EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat); #endif
ctl = file->private_data; return snd_fasync_helper(fd, file, on, &ctl->fasync);
}
/* return the preferred subdevice number if already assigned; * otherwise return -1
*/ int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type)
{ struct snd_ctl_file *kctl; int subdevice = -1;
/** * snd_ctl_request_layer - request to use the layer * @module_name: Name of the kernel module (NULL == build-in) * * Return: zero if successful, or an error code when the module cannot be loaded
*/ int snd_ctl_request_layer(constchar *module_name)
{ struct snd_ctl_layer_ops *lops;
if (module_name == NULL) return 0;
scoped_guard(rwsem_read, &snd_ctl_layer_rwsem) { for (lops = snd_ctl_layer; lops; lops = lops->next) if (strcmp(lops->module_name, module_name) == 0) return 0;
} return request_module(module_name);
}
EXPORT_SYMBOL_GPL(snd_ctl_request_layer);
/** * snd_ctl_register_layer - register new control layer * @lops: operation structure * * The new layer can track all control elements and do additional * operations on top (like audio LED handling).
*/ void snd_ctl_register_layer(struct snd_ctl_layer_ops *lops)
{ struct snd_card *card; int card_number;
/** * snd_ctl_disconnect_layer - disconnect control layer * @lops: operation structure * * It is expected that the information about tracked cards * is freed before this call (the disconnect callback is * not called here).
*/ void snd_ctl_disconnect_layer(struct snd_ctl_layer_ops *lops)
{ struct snd_ctl_layer_ops *lops2, *prev_lops2;
/* call lops under rwsems; called from snd_ctl_dev_*() below() */ #define call_snd_ctl_lops(_card, _op) \ do { \ struct snd_ctl_layer_ops *lops; \
guard(rwsem_read)(&(_card)->controls_rwsem); \
guard(rwsem_read)(&snd_ctl_layer_rwsem); \ for (lops = snd_ctl_layer; lops; lops = lops->next) \
lops->_op(_card); \
} while (0)
/* * registration of the control device
*/ staticint snd_ctl_dev_register(struct snd_device *device)
{ struct snd_card *card = device->device_data; int err;
/** * snd_ctl_boolean_mono_info - Helper function for a standard boolean info * callback with a mono channel * @kcontrol: the kcontrol instance * @uinfo: info to store * * This is a function that can be used as info callback for a standard * boolean control with a single mono channel. * * Return: Zero (always successful)
*/ int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1; return 0;
}
EXPORT_SYMBOL(snd_ctl_boolean_mono_info);
/** * snd_ctl_boolean_stereo_info - Helper function for a standard boolean info * callback with stereo two channels * @kcontrol: the kcontrol instance * @uinfo: info to store * * This is a function that can be used as info callback for a standard * boolean control with stereo two channels. * * Return: Zero (always successful)
*/ int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1; return 0;
}
EXPORT_SYMBOL(snd_ctl_boolean_stereo_info);
/** * snd_ctl_enum_info - fills the info structure for an enumerated control * @info: the structure to be filled * @channels: the number of the control's channels; often one * @items: the number of control values; also the size of @names * @names: an array containing the names of all control values * * Sets all required fields in @info to their appropriate values.
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.24 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.