320 lines · c
1/* SPDX-License-Identifier: GPL-2.0-or-later */2#ifndef __SOUND_CONTROL_H3#define __SOUND_CONTROL_H4 5/*6 * Header file for control interface7 * Copyright (c) by Jaroslav Kysela <perex@perex.cz>8 */9 10#include <linux/wait.h>11#include <linux/nospec.h>12#include <sound/asound.h>13 14#define snd_kcontrol_chip(kcontrol) ((kcontrol)->private_data)15 16struct snd_kcontrol;17typedef int (snd_kcontrol_info_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_info * uinfo);18typedef int (snd_kcontrol_get_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol);19typedef int (snd_kcontrol_put_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol);20typedef int (snd_kcontrol_tlv_rw_t)(struct snd_kcontrol *kcontrol,21 int op_flag, /* SNDRV_CTL_TLV_OP_XXX */22 unsigned int size,23 unsigned int __user *tlv);24 25/* internal flag for skipping validations */26#ifdef CONFIG_SND_CTL_DEBUG27#define SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK (1 << 24)28#define snd_ctl_skip_validation(info) \29 ((info)->access & SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK)30#else31#define SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK 032#define snd_ctl_skip_validation(info) true33#endif34 35/* kernel only - LED bits */36#define SNDRV_CTL_ELEM_ACCESS_LED_SHIFT 2537#define SNDRV_CTL_ELEM_ACCESS_LED_MASK (7<<25) /* kernel three bits - LED group */38#define SNDRV_CTL_ELEM_ACCESS_SPK_LED (1<<25) /* kernel speaker (output) LED flag */39#define SNDRV_CTL_ELEM_ACCESS_MIC_LED (2<<25) /* kernel microphone (input) LED flag */40 41enum {42 SNDRV_CTL_TLV_OP_READ = 0,43 SNDRV_CTL_TLV_OP_WRITE = 1,44 SNDRV_CTL_TLV_OP_CMD = -1,45};46 47struct snd_kcontrol_new {48 snd_ctl_elem_iface_t iface; /* interface identifier */49 unsigned int device; /* device/client number */50 unsigned int subdevice; /* subdevice (substream) number */51 const char *name; /* ASCII name of item */52 unsigned int index; /* index of item */53 unsigned int access; /* access rights */54 unsigned int count; /* count of same elements */55 snd_kcontrol_info_t *info;56 snd_kcontrol_get_t *get;57 snd_kcontrol_put_t *put;58 union {59 snd_kcontrol_tlv_rw_t *c;60 const unsigned int *p;61 } tlv;62 unsigned long private_value;63};64 65struct snd_kcontrol_volatile {66 struct snd_ctl_file *owner; /* locked */67 unsigned int access; /* access rights */68};69 70struct snd_kcontrol {71 struct list_head list; /* list of controls */72 struct snd_ctl_elem_id id;73 unsigned int count; /* count of same elements */74 snd_kcontrol_info_t *info;75 snd_kcontrol_get_t *get;76 snd_kcontrol_put_t *put;77 union {78 snd_kcontrol_tlv_rw_t *c;79 const unsigned int *p;80 } tlv;81 unsigned long private_value;82 void *private_data;83 void (*private_free)(struct snd_kcontrol *kcontrol);84 struct snd_kcontrol_volatile vd[] __counted_by(count); /* volatile data */85};86 87#define snd_kcontrol(n) list_entry(n, struct snd_kcontrol, list)88 89struct snd_kctl_event {90 struct list_head list; /* list of events */91 struct snd_ctl_elem_id id;92 unsigned int mask;93};94 95#define snd_kctl_event(n) list_entry(n, struct snd_kctl_event, list)96 97struct pid;98 99enum {100 SND_CTL_SUBDEV_PCM,101 SND_CTL_SUBDEV_RAWMIDI,102 SND_CTL_SUBDEV_ITEMS,103};104 105struct snd_ctl_file {106 struct list_head list; /* list of all control files */107 struct snd_card *card;108 struct pid *pid;109 int preferred_subdevice[SND_CTL_SUBDEV_ITEMS];110 wait_queue_head_t change_sleep;111 spinlock_t read_lock;112 struct snd_fasync *fasync;113 int subscribed; /* read interface is activated */114 struct list_head events; /* waiting events for read */115};116 117struct snd_ctl_layer_ops {118 struct snd_ctl_layer_ops *next;119 const char *module_name;120 void (*lregister)(struct snd_card *card);121 void (*ldisconnect)(struct snd_card *card);122 void (*lnotify)(struct snd_card *card, unsigned int mask, struct snd_kcontrol *kctl, unsigned int ioff);123};124 125#define snd_ctl_file(n) list_entry(n, struct snd_ctl_file, list)126 127typedef int (*snd_kctl_ioctl_func_t) (struct snd_card * card,128 struct snd_ctl_file * control,129 unsigned int cmd, unsigned long arg);130 131void snd_ctl_notify(struct snd_card * card, unsigned int mask, struct snd_ctl_elem_id * id);132void snd_ctl_notify_one(struct snd_card * card, unsigned int mask, struct snd_kcontrol * kctl, unsigned int ioff);133 134struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new * kcontrolnew, void * private_data);135void snd_ctl_free_one(struct snd_kcontrol * kcontrol);136int snd_ctl_add(struct snd_card * card, struct snd_kcontrol * kcontrol);137int snd_ctl_remove(struct snd_card * card, struct snd_kcontrol * kcontrol);138int snd_ctl_replace(struct snd_card *card, struct snd_kcontrol *kcontrol, bool add_on_replace);139int snd_ctl_remove_id(struct snd_card * card, struct snd_ctl_elem_id *id);140int snd_ctl_rename_id(struct snd_card * card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id);141void snd_ctl_rename(struct snd_card *card, struct snd_kcontrol *kctl, const char *name);142int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, int active);143struct snd_kcontrol *snd_ctl_find_numid(struct snd_card *card, unsigned int numid);144struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card, const struct snd_ctl_elem_id *id);145 146/**147 * snd_ctl_find_id_mixer - find the control instance with the given name string148 * @card: the card instance149 * @name: the name string150 *151 * Finds the control instance with the given name and152 * @SNDRV_CTL_ELEM_IFACE_MIXER. Other fields are set to zero.153 *154 * This is merely a wrapper to snd_ctl_find_id().155 *156 * Return: The pointer of the instance if found, or %NULL if not.157 */158static inline struct snd_kcontrol *159snd_ctl_find_id_mixer(struct snd_card *card, const char *name)160{161 struct snd_ctl_elem_id id = {};162 163 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;164 strscpy(id.name, name, sizeof(id.name));165 return snd_ctl_find_id(card, &id);166}167 168int snd_ctl_create(struct snd_card *card);169 170int snd_ctl_register_ioctl(snd_kctl_ioctl_func_t fcn);171int snd_ctl_unregister_ioctl(snd_kctl_ioctl_func_t fcn);172#ifdef CONFIG_COMPAT173int snd_ctl_register_ioctl_compat(snd_kctl_ioctl_func_t fcn);174int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn);175#else176#define snd_ctl_register_ioctl_compat(fcn)177#define snd_ctl_unregister_ioctl_compat(fcn)178#endif179 180int snd_ctl_request_layer(const char *module_name);181void snd_ctl_register_layer(struct snd_ctl_layer_ops *lops);182void snd_ctl_disconnect_layer(struct snd_ctl_layer_ops *lops);183 184int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type);185 186static inline unsigned int snd_ctl_get_ioffnum(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id)187{188 unsigned int ioff = id->numid - kctl->id.numid;189 return array_index_nospec(ioff, kctl->count);190}191 192static inline unsigned int snd_ctl_get_ioffidx(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id)193{194 unsigned int ioff = id->index - kctl->id.index;195 return array_index_nospec(ioff, kctl->count);196}197 198static inline unsigned int snd_ctl_get_ioff(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id)199{200 if (id->numid) {201 return snd_ctl_get_ioffnum(kctl, id);202 } else {203 return snd_ctl_get_ioffidx(kctl, id);204 }205}206 207static inline struct snd_ctl_elem_id *snd_ctl_build_ioff(struct snd_ctl_elem_id *dst_id,208 struct snd_kcontrol *src_kctl,209 unsigned int offset)210{211 *dst_id = src_kctl->id;212 dst_id->index += offset;213 dst_id->numid += offset;214 return dst_id;215}216 217/*218 * Frequently used control callbacks/helpers219 */220int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol,221 struct snd_ctl_elem_info *uinfo);222int snd_ctl_boolean_stereo_info(struct snd_kcontrol *kcontrol,223 struct snd_ctl_elem_info *uinfo);224int snd_ctl_enum_info(struct snd_ctl_elem_info *info, unsigned int channels,225 unsigned int items, const char *const names[]);226 227/*228 * virtual master control229 */230struct snd_kcontrol *snd_ctl_make_virtual_master(char *name,231 const unsigned int *tlv);232int _snd_ctl_add_follower(struct snd_kcontrol *master,233 struct snd_kcontrol *follower,234 unsigned int flags);235/* optional flags for follower */236#define SND_CTL_FOLLOWER_NEED_UPDATE (1 << 0)237 238/**239 * snd_ctl_add_follower - Add a virtual follower control240 * @master: vmaster element241 * @follower: follower element to add242 *243 * Add a virtual follower control to the given master element created via244 * snd_ctl_create_virtual_master() beforehand.245 *246 * All followers must be the same type (returning the same information247 * via info callback). The function doesn't check it, so it's your248 * responsibility.249 *250 * Also, some additional limitations:251 * at most two channels,252 * logarithmic volume control (dB level) thus no linear volume,253 * master can only attenuate the volume without gain254 *255 * Return: Zero if successful or a negative error code.256 */257static inline int258snd_ctl_add_follower(struct snd_kcontrol *master, struct snd_kcontrol *follower)259{260 return _snd_ctl_add_follower(master, follower, 0);261}262 263int snd_ctl_add_followers(struct snd_card *card, struct snd_kcontrol *master,264 const char * const *list);265 266/**267 * snd_ctl_add_follower_uncached - Add a virtual follower control268 * @master: vmaster element269 * @follower: follower element to add270 *271 * Add a virtual follower control to the given master.272 * Unlike snd_ctl_add_follower(), the element added via this function273 * is supposed to have volatile values, and get callback is called274 * at each time queried from the master.275 *276 * When the control peeks the hardware values directly and the value277 * can be changed by other means than the put callback of the element,278 * this function should be used to keep the value always up-to-date.279 *280 * Return: Zero if successful or a negative error code.281 */282static inline int283snd_ctl_add_follower_uncached(struct snd_kcontrol *master,284 struct snd_kcontrol *follower)285{286 return _snd_ctl_add_follower(master, follower, SND_CTL_FOLLOWER_NEED_UPDATE);287}288 289int snd_ctl_add_vmaster_hook(struct snd_kcontrol *kctl,290 void (*hook)(void *private_data, int),291 void *private_data);292void snd_ctl_sync_vmaster(struct snd_kcontrol *kctl, bool hook_only);293#define snd_ctl_sync_vmaster_hook(kctl) snd_ctl_sync_vmaster(kctl, true)294int snd_ctl_apply_vmaster_followers(struct snd_kcontrol *kctl,295 int (*func)(struct snd_kcontrol *vfollower,296 struct snd_kcontrol *follower,297 void *arg),298 void *arg);299 300/*301 * Control LED trigger layer302 */303#define SND_CTL_LAYER_MODULE_LED "snd-ctl-led"304 305#if IS_MODULE(CONFIG_SND_CTL_LED)306static inline int snd_ctl_led_request(void) { return snd_ctl_request_layer(SND_CTL_LAYER_MODULE_LED); }307#else308static inline int snd_ctl_led_request(void) { return 0; }309#endif310 311/*312 * Helper functions for jack-detection controls313 */314struct snd_kcontrol *315snd_kctl_jack_new(const char *name, struct snd_card *card);316void snd_kctl_jack_report(struct snd_card *card,317 struct snd_kcontrol *kctl, bool status);318 319#endif /* __SOUND_CONTROL_H */320