Глава 6. Интерфейс Control

Предыдущая  Содержание  Следующая V*D*V

Общие сведения

Интерфейс управления control широко используется для многих переключателей, регуляторов и тому подобному, доступному из пользовательского пространства. Наиболее важным является использование интерфейса микшера. Другими словами, начиная с ALSA 0.9.x, всё необходимое для микшера реализовано как API управления ядра.

 

ALSA имеет отдельный модуль управления AC97. Если ваш чип поддерживает только AC97 и больше ничего, вы можете пропустить этот раздел.

 

API управления определён в <sound/control.h>. Подключите этот файл, если хотите добавить свои собственные элементы управления.

Создание элементов управления

Чтобы создать новый элемент управления, необходимо определить три следующих обратных вызова: info, get и put. Затем  создать объект struct snd_kcontrol_new, выглядящий следующим образом:

 

Пример 6.1. Создание элемента управления

 

static struct snd_kcontrol_new my_control __devinitdata = {

    .iface         = SNDRV_CTL_ELEM_IFACE_MIXER,

    .name          = "PCM Playback Switch",

    .index         = 0,

    .access        = SNDRV_CTL_ELEM_ACCESS_READWRITE,

    .private_value = 0xffff,

    .info          = my_control_info,

    .get           = my_control_get,

    .put           = my_control_put

};

 

Чаще всего элемент управления создаётся с помощью snd_ctl_new1(), и в таком случае, можно добавить к определению префикс __devinitdata, как показано выше.

 

Поле iface определяет тип элемента управления, SNDRV_CTL_ELEM_IFACE_XXX, который, как правило, MIXER. Для общих  элементов управления, которые не являются логическими частями микшера, используйте CARD. Если элемент управления тесно связан с некоторыми определёнными устройствами звуковой карты, используйте HWDEP, PCM, RAWMIDI, TIMER, или SEQUENCER, и укажите номер устройства с помощью полей device и subdevice.

 

Поле name - строка идентификатора. Начиная с ALSA 0.9.x имя элемента управления очень важно, потому что его роль  определяется из его названия. Есть предопределённые стандартные имена элементов управления. Подробности описаны в подразделе Имена элементов управления.

 

Поле index содержит порядковый номер этого элемента управления. Если существуют несколько разных элементов управления с одинаковым именем, их можно отличить по номеру индекса. Это случай, когда на карте имеется несколько кодеков. Если индекс равен нулю, вышеописанный параметр можно опустить.

 

Поле access содержит тип доступа данного элемента управления. Здесь указывается комбинация битовых масок, SNDRV_CTL_ELEM_ACCESS_XXX. Подробности будут объяснены в подразделе Флаги доступа.

 

Поле private_value содержит произвольное значение long integer для этого объекта. При использовании универсальных  обратных вызовов info, get и put, через это поле можно передать какое-либо значение. Если необходимы несколько небольших  чисел, их можно побитово объединить. Или в этом поле можно передать указатель (приведённый к типу unsigned long) на какой-то объект.

 

Поле tlv может быть использовано для предоставления метаданных об элементе управления; смотрите подраздел Метаданные.

 

Остальные три поля являются функциями обратного вызова.

Имена элементов управления

Для определения имён элементов управления есть некоторые стандарты. Имя элемента управления обычно состоит из трёх частей: "ИСТОЧНИК НАПРАВЛЕНИЕ ФУНКЦИЯ".

 

Первая, ИСТОЧНИК, указывает источник для элемента управления, и представляет собой такую строку, как "Master", "PCM", "CD" и "Line". Есть много предопределенных источников.

 

Вторая, НАПРАВЛЕНИЕ, является одной из следующих строк в зависимости от направления управления: "Playback" ("Воспроизведение"), "Capture" ("Захват"), "Bypass Playback" ("Обход Воспроизведение") и "Bypass Capture" ("Обход Захвата"). Она может быть опущена, что означает оба направления, воспроизведение и захват.

 

Третья, ФУНКЦИЯ, - одна из следующих строк в соответствии с функцией управления: "Switch" ("Переключатель"), "Volume" ("Громкость") и "Route" ("Маршрут").

 

Примерами имён элементов управления являются, таким образом, "Master Capture Switch" ("Главный переключатель захвата") или "PCM Playback Volume" ("Громкость воспроизведения PCM").

 

Есть некоторые исключения:

Основной захват и воспроизведение

"Capture Source", "Capture Switch" и "Capture Volume" используются для источника, переключателя и громкости основного захвата (входа). Аналогично, "Playback Switch" ("Переключатель воспроизведения") и "Playback Volume" ("Громкость воспроизведения") используются для переключателя уровня и громкости основного выхода.

Элементы управления тембром

Переключатель управления тембром и регуляторы указываются как "Tone Control - XXX", например, "Tone Control - Switch", "Tone Control - Bass", "Tone Control - Center".

Элементы управления 3D

Переключатели управлением 3D и регуляторы указываются как "3D Control - XXX", например, "3D Control - Switch", "3D Control - Center", "3D Control - Space".

Увеличение усиления микрофона

Переключатель увеличения усиления микрофона указывается как "Mic Boost" или "Mic Boost (6 дБ)".

 

Более точную информацию можно найти в Documentation/sound/alsa/ControlNames.txt.

Флаги доступа

Флаг access представляет собой битовую маску, которая определяет тип доступа данного элемента управления. Типом доступа по умолчанию является SNDRV_CTL_ELEM_ACCESS_READWRITE, что означает разрешение на чтение и запись для этого элемента управления. Когда флаг доступа опущен (то есть = 0), он рассматривается как и доступ по умолчанию на чтение и запись.

 

Когда элемент управление предназначен только для чтения, вместо этого указывается SNDRV_CTL_ELEM_ACCESS_READ. В этом случае вы не должны определять обратный вызов put. Аналогичным образом, когда элемент управления предназначен только для записи (хотя это редкий случай), можно использовать взамен флаг WRITE, и нет необходимости в обратном вызове get.

 

Если значение элемента управления происходит часто (например, измеритель уровня, VU-метр), должен быть указан флаг VOLATILE (нестабильный). Это означает, что элемент управления может быть изменён без уведомления. Приложения должны постоянно опрашивать такой элемент управления.

 

Когда элемент управления неактивен, устанавливается также флаг INACTIVE. Для изменения записи разрешений записи есть  флаги LOCK (блокировка) и OWNER (владелец).

Обратные вызовы

Обратный вызов info

Обратный вызов info используется для получения подробной информации о данном элементе управления. Он должен хранить  эти значения в объекте struct snd_ctl_elem_info. Например, для булева элемента управления из одного элемента:

 

Пример 6.2. Пример обратного вызова info

 

static int snd_myctl_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;

}

 

Поле type определяет тип управления. Используются BOOLEAN, INTEGER, ENUMERATED, BYTES, IEC958 и INTEGER64. Поле count определяет число элементов в этом элементе. Например, стереофонический регулятор громкости имел бы count = 2. Поле value представляет собой объединение и значения хранятся в зависимости от типа. Логический и целый типы являются идентичными.

 

Перечислимый тип немного отличается от других. Необходимо будет установить строку для типа, индекс которого используется в настоящее время.

 

static int snd_myctl_enum_info(struct snd_kcontrol *kcontrol,

                               struct snd_ctl_elem_info *uinfo)

{

    static char *texts[4] = {

        "First", "Second", "Third", "Fourth"

    };

    uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;

    uinfo->count = 1;

    uinfo->value.enumerated.items = 4;

    if (uinfo->value.enumerated.item > 3)

        uinfo->value.enumerated.item = 3;

    strcpy(uinfo->value.enumerated.name,

        texts[uinfo->value.enumerated.item]);

    return 0;

}

 

Для удобства доступны некоторые обычные обратные вызовы получения информации: snd_ctl_boolean_mono_info() и snd_ctl_boolean_stereo_info(). Очевидно, что первый является обратным вызовом info для одноканального булева элемента, так же, как показанный выше snd_myctl_mono_info, а последний - для двухканального булева элемента.

Обратный вызов get

Этот обратный вызов используется для чтения текущего значения элемента управления и передачи в пространство пользователя.

 

Пример 6.3. Пример обратного вызова get

 

static int snd_myctl_get(struct snd_kcontrol *kcontrol,

                         struct snd_ctl_elem_value *ucontrol)

{

    struct mychip *chip = snd_kcontrol_chip(kcontrol);

    ucontrol->value.integer.value[0] = get_some_value(chip);

    return 0;

}

 

Поле value зависит от типа элемента управления, а также от обратного вызова info. Например, драйвер sb использует это поле для хранения регистра смещения, битов сдвига и битовой маски. Поле private_value устанавливается следующим образом:

 

.private_value = reg | (shift << 16) | (mask << 24)

 

и извлекается в обратных вызовах следующим образом

 

static int snd_sbmixer_get_single(struct snd_kcontrol *kcontrol,

                                  struct snd_ctl_elem_value *ucontrol)

{

    int reg = kcontrol->private_value & 0xff;

    int shift = (kcontrol->private_value >> 16) & 0xff;

    int mask = (kcontrol->private_value >> 24) & 0xff;

    ....

}

 

В обратном вызове get вы должны заполнить все элементы, если имеется более одного элемента, то есть count > 1. В приведённом выше примере заполняется только один элемент (value.integer.value[0]), так как предполагается, что сount = 1.

Обратный вызов put

Этот обратный вызов используется для записи значения, полученного из пользовательского пространства.

 

Пример 6.4. Пример обратного вызова put

 

static int snd_myctl_put(struct snd_kcontrol *kcontrol,

                         struct snd_ctl_elem_value *ucontrol)

{

    struct mychip *chip = snd_kcontrol_chip(kcontrol);

    int changed = 0;

    if (chip->current_value !=

        ucontrol->value.integer.value[0]) {

            change_current_value(chip,

            ucontrol->value.integer.value[0]);

            changed = 1;

    }

    return changed;

}

 

Как видно из примера, необходимо вернуть 1, если значение изменилось. Если значение не изменилось, возвращается 0. Если произошла какая-либо фатальная ошибка, возвращается отрицательный код ошибки, как обычно.

 

Также как и в обратном вызове get, если элемент управления имеет более одного элемента, в этом обратном вызове должны быть установлены значения всех элементов.

Обратные вызовы не атомарны

Все эти три функции обратного вызова в своей основе являются не атомарными.

Конструктор

Наконец, когда всё будет готово, можно создать новый элемент управления. Чтобы создать элемент управления, могут быть вызваны две функции, snd_ctl_new1() и snd_ctl_add().

 

В простейшем случае это можно сделать следующим образом:

 

err = snd_ctl_add(card, snd_ctl_new1(&my_control, chip));

if (err < 0)

    return err;

 

где my_control является объектом struct snd_kcontrol_new, созданным выше, а chip - указатель на объект, передаваемый  в kcontrol->private_data, который может быть использован в обратных вызовах.

 

snd_ctl_new1() создаёт новый экземпляр snd_kcontrol (именно поэтому определение my_control может быть с префиксом __devinitdata), а snd_ctl_add связывает данный компонент управления с картой.

Уведомление об изменении

Если в процедуре обработки прерывания необходимо изменить и обновить какой-нибудь элемент управления, можно вызвать snd_ctl_notify().

Например,

 

snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, id_pointer);

 

Эта функция для уведомления принимает указатель на карту, маску события и идентификатор элемента управления. Маска события определяет типы уведомлений, например, в приведённом выше примере сообщается об изменении управляемых значений. Указатель идентификатора - это указатель на struct snd_ctl_elem_id, получающую уведомление. Вы можете найти некоторые примеры в es1938.c или es1968.c в обработке аппаратных прерываний громкости.

Метаданные

Чтобы предоставить информацию о значениях элемента управления микшером в дБ, используйте макрос DECLARE_TLV_xxx из <sound/tlv.h>, чтобы определить переменную, содержащую эту информацию, установите поле tlv.p, чтобы указать на эту переменную, и включите флаг SNDRV_CTL_ELEM_ACCESS_TLV_READ в поле access следующим образом:

 

static DECLARE_TLV_DB_SCALE(db_scale_my_control, -4050, 150, 0);

 

static struct snd_kcontrol_new my_control __devinitdata = {

    ...

    .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |

              SNDRV_CTL_ELEM_ACCESS_TLV_READ,

    ...

    .tlv.p = db_scale_my_control,

};

 

Макрос DECLARE_TLV_DB_SCALE указывает информацию об элементе управления микшером, каждый шаг значения которого изменяет своё значение в дБ на постоянную величину, указанную в дБ. Первым параметром является имя определяемой переменной. Вторым параметром является минимальное значение, в единицах 0.01 дБ. Третьим параметром является величина шага, в единицах 0.01 дБ. Установите четвёртый параметр в 1, если минимальное значение на самом деле отключает данный элемент управления (выключает звук).

 

Макрос DECLARE_TLV_DB_LINEAR указывает информацию об элементе управления микшером, значение которого влияет на выход линейно. Первым параметром является имя определяемой переменной. Вторым параметром является минимальное значение, в единицах 0.01 дБ. Третьим параметром является максимальное значение, в единицах 0.01 дБ. Если минимальное значение отключает данный элемент управления, установить второй параметр в TLV_DB_GAIN_MUTE.

 

Предыдущая  Содержание  Следующая