/* * a subset of information returned via ctl info callback
*/ struct link_ctl_info {
snd_ctl_elem_type_t type; /* value type */ int count; /* item count */ int min_val, max_val; /* min, max values */
};
/* * link master - this contains a list of follower controls that are * identical types, i.e. info returns the same value type and value * ranges, but may have different number of counts. * * The master control is so far only mono volume/switch for simplicity. * The same value will be applied to all followers.
*/ struct link_master { struct list_head followers; struct link_ctl_info info; int val; /* the master value */ unsignedint tlv[4]; void (*hook)(void *private_data, int); void *hook_private_data;
};
/* * link follower - this contains a follower control element * * It fakes the control callbacks with additional attenuation by the * master control. A follower may have either one or two channels.
*/
struct link_follower { struct list_head list; struct link_master *master; struct link_ctl_info info; int vals[2]; /* current values */ unsignedint flags; struct snd_kcontrol *kctl; /* original kcontrol pointer */ struct snd_kcontrol follower; /* the copy of original control entry */
};
/* get the follower ctl info and save the initial values */ staticint follower_init(struct link_follower *follower)
{ struct snd_ctl_elem_info *uinfo __free(kfree) = NULL; int err;
if (follower->info.count) { /* already initialized */ if (follower->flags & SND_CTL_FOLLOWER_NEED_UPDATE) return follower_update(follower); return 0;
}
err = follower_init(follower); if (err < 0) return err; for (ch = 0; ch < follower->info.count; ch++) { if (ucontrol->value.integer.value[ch] < follower->info.min_val ||
ucontrol->value.integer.value[ch] > follower->info.max_val) return -EINVAL;
}
for (ch = 0; ch < follower->info.count; ch++) { if (follower->vals[ch] != ucontrol->value.integer.value[ch]) {
changed = 1;
follower->vals[ch] = ucontrol->value.integer.value[ch];
}
} if (!changed) return 0;
err = follower_put_val(follower, ucontrol); if (err < 0) return err; return 1;
}
staticint follower_tlv_cmd(struct snd_kcontrol *kcontrol, int op_flag, unsignedint size, unsignedint __user *tlv)
{ struct link_follower *follower = snd_kcontrol_chip(kcontrol); /* FIXME: this assumes that the max volume is 0 dB */ return follower->follower.tlv.c(&follower->follower, op_flag, size, tlv);
}
staticvoid follower_free(struct snd_kcontrol *kcontrol)
{ struct link_follower *follower = snd_kcontrol_chip(kcontrol); if (follower->follower.private_free)
follower->follower.private_free(&follower->follower); if (follower->master)
list_del(&follower->list);
kfree(follower);
}
/* * Add a follower control to the group with the given master control * * All followers must be the same type (returning the same information * via info callback). The function doesn't check it, so it's your * responsibility. * * Also, some additional limitations: * - at most two channels * - logarithmic volume control (dB level), no linear volume * - master can only attenuate the volume, no gain
*/ int _snd_ctl_add_follower(struct snd_kcontrol *master, struct snd_kcontrol *follower, unsignedint flags)
{ struct link_master *master_link = snd_kcontrol_chip(master); struct link_follower *srec;
/** * snd_ctl_add_followers - add multiple followers to vmaster * @card: card instance * @master: the target vmaster kcontrol object * @list: NULL-terminated list of name strings of followers to be added * * Adds the multiple follower kcontrols with the given names. * Returns 0 for success or a negative error code.
*/ int snd_ctl_add_followers(struct snd_card *card, struct snd_kcontrol *master, constchar * const *list)
{ struct snd_kcontrol *follower; int err;
for (; *list; list++) {
follower = snd_ctl_find_id_mixer(card, *list); if (follower) {
err = snd_ctl_add_follower(master, follower); if (err < 0) return err;
}
}
/* free all follower links and retore the original follower kctls */
list_for_each_entry_safe(follower, n, &master->followers, list) { struct snd_kcontrol *sctl = follower->kctl; struct list_head olist = sctl->list;
memcpy(sctl, &follower->follower, sizeof(*sctl));
memcpy(sctl->vd, follower->follower.vd,
sctl->count * sizeof(*sctl->vd));
sctl->list = olist; /* keep the current linked-list */
kfree(follower);
}
kfree(master);
}
/** * snd_ctl_make_virtual_master - Create a virtual master control * @name: name string of the control element to create * @tlv: optional TLV int array for dB information * * Creates a virtual master control with the given name string. * * After creating a vmaster element, you can add the follower controls * via snd_ctl_add_follower() or snd_ctl_add_follower_uncached(). * * The optional argument @tlv can be used to specify the TLV information * for dB scale of the master control. It should be a single element * with #SNDRV_CTL_TLVT_DB_SCALE, #SNDRV_CTL_TLV_DB_MINMAX or * #SNDRV_CTL_TLVT_DB_MINMAX_MUTE type, and should be the max 0dB. * * Return: The created control element, or %NULL for errors (ENOMEM).
*/ struct snd_kcontrol *snd_ctl_make_virtual_master(char *name, constunsignedint *tlv)
{ struct link_master *master; struct snd_kcontrol *kctl; struct snd_kcontrol_new knew;
/** * snd_ctl_add_vmaster_hook - Add a hook to a vmaster control * @kcontrol: vmaster kctl element * @hook: the hook function * @private_data: the private_data pointer to be saved * * Adds the given hook to the vmaster control element so that it's called * at each time when the value is changed. * * Return: Zero.
*/ 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 - Sync the vmaster followers and hook * @kcontrol: vmaster kctl element * @hook_only: sync only the hook * * Forcibly call the put callback of each follower and call the hook function * to synchronize with the current value of the given vmaster element. * NOP when NULL is passed to @kcontrol.
*/ void snd_ctl_sync_vmaster(struct snd_kcontrol *kcontrol, bool hook_only)
{ struct link_master *master; bool first_init = false;
if (!kcontrol) return;
master = snd_kcontrol_chip(kcontrol); if (!hook_only) { int err = master_init(master); if (err < 0) return;
first_init = err;
err = sync_followers(master, master->val, master->val); if (err < 0) return;
}
if (master->hook && !first_init)
master->hook(master->hook_private_data, master->val);
}
EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster);
/** * snd_ctl_apply_vmaster_followers - Apply function to each vmaster follower * @kctl: vmaster kctl element * @func: function to apply * @arg: optional function argument * * Apply the function @func to each follower kctl of the given vmaster kctl. * * Return: 0 if successful, or a negative error code
*/ int snd_ctl_apply_vmaster_followers(struct snd_kcontrol *kctl, int (*func)(struct snd_kcontrol *vfollower, struct snd_kcontrol *follower, void *arg), void *arg)
{ struct link_master *master; struct link_follower *follower; int err;
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.