コントロール インターフェイスは主に、ユーザー空間アプリケーション ( ) がオーディオコーデック チップ内のマルチウェイ スイッチとスライド コントロールalsa-lib
にアクセスして制御できるようにします。(ミキシング) では、Control インターフェイスが特に重要です. ALSA 0.9.x から、すべてのミキサー作業は、コントロール インターフェイスのAPIを通じて実装されます。Mixer
ALSA は AC97 用の完全な制御インターフェース モデルを定義しています。コーデック チップが AC97 インターフェースのみをサポートしている場合は、このセクションの内容を気にする必要はありません。
<sound/control.h>
すべてのコントロール API を定義します。コーデックに独自のコントロールを実装する場合は、このヘッダー ファイルをコードに含めてください。
1、snd_kcontrol_new
struct snd_kcontrol_new {
snd_ctl_elem_iface_t iface; /* interface identifier */
unsigned int device; /* device/client number */
unsigned int subdevice; /* subdevice (substream) number */
const char *name; /* ASCII name of item */
unsigned int index; /* index of item */
unsigned int access; /* access rights */
unsigned int count; /* count of same elements */
snd_kcontrol_info_t *info;
snd_kcontrol_get_t *get;
snd_kcontrol_put_t *put;
union {
snd_kcontrol_tlv_rw_t *c;
const unsigned int *p;
} tlv;
unsigned long private_value;
};
iface: SNDRV_CTL_ELEM_IFACE_XXX で定義されたコントロールのタイプを示します。通常は MIXER が使用され、グローバルな CARD タイプとして定義することもできます.HWDEP、PCMRAWMIDI、TIMER などの Morey デバイス タイプとして定義する場合は、カードのデバイス ロジック番号をデバイスおよびサブデバイス フィールド。
name:コントロールの名前を示します. ユーザー層はこの名前を介してコントロールにアクセスできます. 詳細は後述します.
index:このコントロールのインデックス番号を格納します。サウンド カードの下に複数のコーデックがある場合。各コーデックには、同じ名前のコントロールがあります。このとき、これらのコントロールをインデックスで区別する必要があります.インデックスが0の場合、この区別戦略は無視できます.
アクセス:アクセス制御、READ、WRITE、READWRITE など。各ビットはアクセス タイプを表し、これらのアクセス タイプは複数の OR 演算と組み合わせて使用できます。
private_value:個人の長整数値が含まれており、info、get、および put のコールバック関数を介してアクセスできます。
tlv:このフィールドは、コントロールのメタデータを提供します。
2. コントロールの名前
コントロールの名前は、いくつかの標準に従う必要があります。通常は、コントロールの名前を定義するために 3 つの部分に分けることができます源--方向--功能
。
ソース:コントロールの入力端子として理解することができます. Alsa はいくつかの一般的に使用されるソースを定義しています: マスター, PCM, CD, ラインなど.
方向:コントロールのデータ フローの方向を表します (再生、キャプチャ、バイパス、キャプチャのバイパスなど)。方向が定義されていない場合は、コントロールが双方向 (再生とキャプチャ) であることを意味します。
機能:コントロールの機能に応じて、次の文字列にすることができます: スイッチ、ボリューム、ルートなど。
命名の例外もいくつかあります。
1.グローバル キャプチャと再生:「キャプチャ ソース」、「キャプチャ ボリューム」、「キャプチャ スイッチ」は、グローバル キャプチャ ソース、スイッチ、およびボリュームに使用されます。同じ「再生ボリューム」、「再生スイッチ」、それらはグローバル出力スイッチとボリュームに使用されます。
2.トーン コントロール: トーン コントロールのスイッチとボリュームの名前は、 Tomw Control-XXX です。たとえば、「Tone-Control-Switch」、「Tone Control-Bass」、「Tone Control-Center」などです。
3、3D コントロール:3D控件的命名规则:“3D Control-Switch”,“3D Control-Center”,“3D Control-Space”。
4. MIC ブースト:マイク ボリューム ブースト スペースの名前は、「MIC Boost」または「MIC Bosst (6dB)」です。
3. アクセスフラグ(ACCESS Flags)
Access フィールドは、コントロールのアクセス タイプを格納するビットマスクです。デフォルトのアクセス タイプは SNDDRV_CTL_ELEM_ACCESS_READWRITE で、コントロールが読み取りおよび書き込み操作をサポートしていることを示します。アクセス フィールドが定義されていない場合 (.access==0) も、READWRITE タイプと見なされます。
読み取り専用コントロールの場合、アクセスは SNDDRV_CTL_ELEM_ACCESS_READ に設定する必要があります。現時点では、put コールバック関数を定義する必要はありません。同様に、書き込み専用コントロールの場合、アクセスは SNDDRV_CTL_ELEM_ACCESS_WRITE に設定する必要があります。現時点では、get コールバック関数を定義する必要はありません。
コントロールの値が頻繁に変更される場合 (レベル メーターなど)、VOLATILE タイプを使用できます。これは、コントロールが通知なしで変更されることを意味し、アプリケーションは定期的にコントロールの値をクエリする必要があります。
4. メタデータ (METADATA)
多くのミキサー コントロールは dB で情報を提供する必要があります. DECLARE_TLV_xxx マクロを使用して、この情報を含むいくつかの変数を定義し、コントロールの tlv.p フィールドをこれらの変数にポイントすることができます. 最後に、SNDRV_CTL_ELEM_ACCESS_TLV_READ フラグをアクセス フィールドに追加します.例えば:
static const DECLARE_TLV_DB_SCALE(snd_cx88_db_scale, -6300, 100, 0);
static const struct snd_kcontrol_new snd_cx88_volume = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
.name = "Analog-TV Volume",
.info = snd_cx88_volume_info,
.get = snd_cx88_volume_get,
.put = snd_cx88_volume_put,
.tlv.p = snd_cx88_db_scale,
};
5.機能詳細
5.1、snd_ctl_new1 関数
/**
* 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(const struct snd_kcontrol_new *ncontrol,
void *private_data)
{
struct snd_kcontrol *kctl;
unsigned int count;
unsigned int access;
int err;
if (snd_BUG_ON(!ncontrol || !ncontrol->info))
return NULL;
count = ncontrol->count;
if (count == 0)
count = 1;
access = ncontrol->access;
if (access == 0)
access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_VOLATILE |
SNDRV_CTL_ELEM_ACCESS_INACTIVE |
SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND |
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK |
SNDRV_CTL_ELEM_ACCESS_LED_MASK |
SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK);
/* 创建snd_kcontrol */
err = snd_ctl_new(&kctl, count, access, NULL);
if (err < 0)
return NULL;
/* 根据snd_kcontrol_new初始化snd_kcontrol */
/* 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;
kctl->info = ncontrol->info;
kctl->get = ncontrol->get;
kctl->put = ncontrol->put;
kctl->tlv.p = ncontrol->tlv.p;
kctl->private_value = ncontrol->private_value;
kctl->private_data = private_data;
return kctl;
}
snd_kcontrol の新しいインスタンスを割り当て、my_control の対応する値をこのインスタンスにコピーします。したがって、通常、my_control を定義するときに、__devinitdata のプレフィックスを追加できます。snd_ctl_add は、コントロールをサウンド カード オブジェクト カードにバインドします。
struct snd_kcontrol {
struct list_head list; /* list of controls */
struct snd_ctl_elem_id id;
unsigned int count; /* count of same elements */
snd_kcontrol_info_t *info;
snd_kcontrol_get_t *get;
snd_kcontrol_put_t *put;
union {
snd_kcontrol_tlv_rw_t *c;
const unsigned int *p;
} tlv;
unsigned long private_value;
void *private_data;
void (*private_free)(struct snd_kcontrol *kcontrol);
struct snd_kcontrol_volatile vd[]; /* volatile data */
};
#define snd_kcontrol(n) list_entry(n, struct snd_kcontrol, list)
5.2、snd_ctl_add 関数
/* add/replace a new kcontrol object; call with card->controls_rwsem locked */
static int __snd_ctl_add_replace(struct snd_card *card,
struct snd_kcontrol *kcontrol,
enum snd_ctl_add_mode mode)
{
struct snd_ctl_elem_id id;
unsigned int idx;
struct snd_kcontrol *old;
int err;
id = kcontrol->id;
if (id.index > UINT_MAX - kcontrol->count)
return -EINVAL;
old = snd_ctl_find_id(card, &id);
if (!old) {
if (mode == CTL_REPLACE)
return -EINVAL;
} else {
if (mode == CTL_ADD_EXCLUSIVE) {
dev_err(card->dev,
"control %i:%i:%i:%s:%i is already present\n",
id.iface, id.device, id.subdevice, id.name,
id.index);
return -EBUSY;
}
err = snd_ctl_remove(card, old);
if (err < 0)
return err;
}
if (snd_ctl_find_hole(card, kcontrol->count) < 0)
return -ENOMEM;
/* 把snd_kcontrol挂入snd_card的controls链表 */
list_add_tail(&kcontrol->list, &card->controls);
card->controls_count += kcontrol->count;
/* 设置元素ID */
kcontrol->id.numid = card->last_numid + 1;
card->last_numid += kcontrol->count;
for (idx = 0; idx < kcontrol->count; idx++)
snd_ctl_notify_one(card, SNDRV_CTL_EVENT_MASK_ADD, kcontrol, idx);
return 0;
}
static int snd_ctl_add_replace(struct snd_card *card,
struct snd_kcontrol *kcontrol,
enum snd_ctl_add_mode mode)
{
int err = -EINVAL;
if (! kcontrol)
return err;
if (snd_BUG_ON(!card || !kcontrol->info))
goto error;
down_write(&card->controls_rwsem);
err = __snd_ctl_add_replace(card, kcontrol, mode);
up_write(&card->controls_rwsem);
if (err < 0)
goto error;
return 0;
error:
snd_ctl_free_one(kcontrol);
return err;
}
/**
* 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);
}
5.3、情報コールバック関数
対応するコントロールの詳細情報を取得するには、情報を snd_ctl_elem_info オブジェクトに格納する必要があります。
struct snd_ctl_elem_info {
struct snd_ctl_elem_id id; /* W: element ID */
snd_ctl_elem_type_t type; /* R: value type - SNDRV_CTL_ELEM_TYPE_* */
unsigned int access; /* R: value access (bitmask) - SNDRV_CTL_ELEM_ACCESS_* */
unsigned int count; /* count of values */
__kernel_pid_t owner; /* owner's PID of this control */
union {
struct {
long min; /* R: minimum value */
long max; /* R: maximum value */
long step; /* R: step (0 variable) */
} integer;
struct {
long long min; /* R: minimum value */
long long max; /* R: maximum value */
long long step; /* R: step (0 variable) */
} integer64;
struct {
unsigned int items; /* R: number of items */
unsigned int item; /* W: item number */
char name[64]; /* R: value name */
__u64 names_ptr; /* W: names list (ELEM_ADD only) */
unsigned int names_length;
} enumerated;
unsigned char reserved[128];
} value;
unsigned char reserved[64];
};
その中の値は共用体であり、コントロールのタイプに応じて値のタイプを決定する必要があります。コントロール タイプには次のタイプがあります。
typedef int __bitwise snd_ctl_elem_type_t;
#define SNDRV_CTL_ELEM_TYPE_NONE ((__force snd_ctl_elem_type_t) 0) /* invalid */
#define SNDRV_CTL_ELEM_TYPE_BOOLEAN ((__force snd_ctl_elem_type_t) 1) /* boolean type */
#define SNDRV_CTL_ELEM_TYPE_INTEGER ((__force snd_ctl_elem_type_t) 2) /* integer type */
#define SNDRV_CTL_ELEM_TYPE_ENUMERATED ((__force snd_ctl_elem_type_t) 3) /* enumerated type */
#define SNDRV_CTL_ELEM_TYPE_BYTES ((__force snd_ctl_elem_type_t) 4) /* byte array */
#define SNDRV_CTL_ELEM_TYPE_IEC958 ((__force snd_ctl_elem_type_t) 5) /* IEC958 (S/PDIF) setup */
#define SNDRV_CTL_ELEM_TYPE_INTEGER64 ((__force snd_ctl_elem_type_t) 6) /* 64-bit integer type */
#define SNDRV_CTL_ELEM_TYPE_LAST SNDRV_CTL_ELEM_TYPE_INTEGER64
以下は、例として SNDRV_CTL_ELEM_TYPE_INTEGER および SNDRV_CTL_ELEM_TYPE_BOOLEAN によって定義された情報コールバック関数です。
static int snd_cx88_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *info)
{
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
info->count = 2;
info->value.integer.min = 0;
info->value.integer.max = 0x3f;
return 0;
}
static int snd_saa7134_capsrc_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;
}
5.4、コールバック関数を取得する
この関数は、現在のコントロールの値を読み取り、それをユーザー空間に返すために使用されます. 値は、info 構造に類似した snd_ctl_elem_value 構造に配置する必要があります. 値フィールドは共用体であり、タイプ。value の cont が 1 より大きい場合、すべての値を value[] 配列に入れる必要があります。
static int snd_cx88_volume_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *info)
{
info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
info->count = 2;
info->value.integer.min = 0;
info->value.integer.max = 0x3f;
return 0;
}
static int snd_cx88_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *value)
{
struct cx88_audio_dev *chip = snd_kcontrol_chip(kcontrol);
struct cx88_core *core = chip->core;
int vol = 0x3f - (cx_read(AUD_VOL_CTL) & 0x3f),
bal = cx_read(AUD_BAL_CTL);
value->value.integer.value[(bal & 0x40) ? 0 : 1] = vol;
vol -= (bal & 0x3f);
value->value.integer.value[(bal & 0x40) ? 1 : 0] = vol < 0 ? 0 : vol;
return 0;
}
5.5、コールバック関数を入れる
put コールバック関数は、アプリケーションのコントロール値をコントロールに設定するために使用されます。
static void snd_cx88_wm8775_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *value)
{
struct cx88_audio_dev *chip = snd_kcontrol_chip(kcontrol);
struct cx88_core *core = chip->core;
u16 left = value->value.integer.value[0];
u16 right = value->value.integer.value[1];
int v, b;
/* Pass volume & balance onto any WM8775 */
if (left >= right) {
v = left << 10;
b = left ? (0x8000 * right) / left : 0x8000;
} else {
v = right << 10;
b = right ? 0xffff - (0x8000 * left) / right : 0x8000;
}
wm8775_s_ctrl(core, V4L2_CID_AUDIO_VOLUME, v);
wm8775_s_ctrl(core, V4L2_CID_AUDIO_BALANCE, b);
}
/* OK - TODO: test it */
static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *value)
{
struct cx88_audio_dev *chip = snd_kcontrol_chip(kcontrol);
struct cx88_core *core = chip->core;
int left, right, v, b;
int changed = 0;
u32 old;
if (core->sd_wm8775)
snd_cx88_wm8775_volume_put(kcontrol, value);
left = value->value.integer.value[0] & 0x3f;
right = value->value.integer.value[1] & 0x3f;
b = right - left;
if (b < 0) {
v = 0x3f - left;
b = (-b) | 0x40;
} else {
v = 0x3f - right;
}
/* Do we really know this will always be called with IRQs on? */
spin_lock_irq(&chip->reg_lock);
old = cx_read(AUD_VOL_CTL);
if (v != (old & 0x3f)) {
cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, (old & ~0x3f) | v);
changed = 1;
}
if ((cx_read(AUD_BAL_CTL) & 0x7f) != b) {
cx_write(AUD_BAL_CTL, b);
changed = 1;
}
spin_unlock_irq(&chip->reg_lock);
return changed;
}
6. 制御装置の作成プロセス
コントロール デバイスは、PCM デバイスと同様に、サウンド カードの下にある論理デバイスです。ユーザー空間アプリケーションは、alsa-lib を介してコントロール デバイスにアクセスし、コントロールのコントロール状態を読み取りまたは設定して、オーディオ コーデックを制御し、さまざまなミキサーやその他のコントロール操作を実行します。
コントロールデバイスを作成するプロセスは、PCM デバイスのプロセスとほぼ同じです。詳細な作成プロセスについては、以下のシーケンス図を参照してください。
ドライバが初期化され、制御デバイスが snd_ctl_new1() で作成され、snd_ctl_new1() が snd_ctl_create() 関数を呼び出して制御デバイス ノードを作成するときに、snd_pcm_new() 関数をアクティブに呼び出して pcm デバイスを作成する必要があります。そのため、コントロール デバイスを明示的に作成する必要はありません。サウンド カードが作成されている限り、コントロール デバイスは自動的に作成されます。
pcm デバイスと同様に、制御デバイスの名前は特定の規則に従います: controlCxx。ここで、xx はサウンド カードの番号を表します。
snd_ctl_dev_register() 関数は、サウンド カードの登録フェーズである snd_card_register() で呼び出されます。登録が完了すると、制御デバイスの関連情報が snd_minors[] 配列に格納され、制御デバイスのマイナー デバイス番号をインデックスとして使用して、snd_minors[] 配列で関連情報を見つけることができます。登録後のデータ構造の関係は、次の図で表すことができます。