Statement: The content of this blog by Http://blog.csdn.net/droidphone Original, reproduced please indicate the source, thank you!
Control Interface
The control interface primarily allows user-space Applications (ALSA-LIB) to access and control multiple switches in the audio codec chip, slide controls, and so on. for Mixer (mix), the control interface is particularly important , starting with the Alsa 0.9.x version, all mixer work is done through the Control Interface API .
ALSA has defined a complete control interface model for AC97, and if your codec chip supports only AC97 interfaces, you can take care of the content of this section.
<sound/control.h> defines all the control APIs. If you want to implement your own controls for your codec, include the header file in your code.
Definition of controls
To customize a control, we first define the 3 callback functions:info,get and put. Then, define a SND_KCONTROL_NEW structure:
[C-sharp]View PlainCopy
- 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
- };
The IFACE field indicates the type of control, ALSA defines several types (snddrv_ctl_elem_iface_xxx), the common type is mixer, and of course you can define a card type that belongs to the global, or you can define a type that belongs to a class of devices. such as Hwdep,pcmrawmidi,timer, you need to indicate the device logic number of the card in the device and Subdevice fields.
The Name field is the name of the control, and starting with Alsa 0.9.x, control names are more important because control is categorized by name. ALSA has predefined some control names, and we'll discuss them in detail in the Control Name section.
The index field is used to hold the control's number in the card. If there is more than one codec in the sound card, and each codec has the same name control, then we can differentiate the controls by index. When index is 0 o'clock, this distinction policy can be ignored.
An Access field contains the type of access for the control. Each bit represents an access type that can be combined with multiple "or" operations.
The Private_value field contains an arbitrary long integer type value. This value can be accessed through several callback functions info,get,put. You can decide for yourself how to use the field, for example, you can split it into multiple bit fields or a pointer to a data structure.
The TLV field provides metadata for the control.
Control's name
The name of control needs to follow some criteria, usually divided into 3 parts to define the control's name: source-direction-function.
- source, which can be understood as the input of the control, ALSA has pre-defined some common sources, such asmaster,pcm,cd,line and so on .
- Direction, which represents the control's data flow, for example: Playback,capture,bypass,bypass Capture, and so on, can also not define the direction, this means that the control is bidirectional (Playback and capture).
- function, depending on the function of control, can be the following string: Switch,volume,route and so on.
There are some exceptions to the naming:
- global capture and playback "Capture Source", "Capture Volume", "Capture Switch", they are used for the global capture Source,switch and Volume. Similarly, "Playback Volume", "Playback switch", are used for global output switch and Volume.
- tone-controles Tone control of the switch and volume named: Tone control-xxx, for example, "Tone Control-switch", "Tone control-bass", "Tone Control -Center ".
- Naming rules for the 3D Controls 3D Control:, "3D control-switch", "3D control-center", "3D control-space".
- The mic boost Microphone Volume enhancement control is named "Mic Boost" or "mic boost (6dB)".
Access flags
The Access field is a bitmask that holds the access type of the control. The default access type is: Snddrv_ctl_elem_access_readwrite, which indicates that the control supports read and write operations. If the Access field does not have a definition (. access==0), it is also considered a readwrite type.
If a read-only control,access should be set to: Snddrv_ctl_elem_access_read, then we do not have to define a put callback function. Similarly, if the write-only control,access should be set to: Snddrv_ctl_elem_access_write, then we do not have to define a get callback function.
If the value of control is changed frequently (for example, a level meter), we can use the volatile type, which means that the control will change without notice, and the application should query the value of that control on a regular basis.
callback function Info callback function
The info callback function is used to get the control details . Its main work is to populate the Snd_ctl_elem_info object passed through the parameter, the following example is a Boolean control with a single element of the info callback:
[C-sharp]View PlainCopy
- 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;
- }
The Type field indicates the value type of the control, and the value type can be one of the Boolean, INTEGER, enumerated, bytes,iec958, and INTEGER64. The Count field indicates how many element elements are contained in the change control, for example, the volume control of the stereo to the left and right of the two channels, and its count field equals 2. The Value field is a union (union), and the content of value is related to the type of control. Where the Boolean and integer types are the same.
The enumerated type is somewhat special. Its value requires a string and a string to be indexed, see the following example:
[C-sharp]View PlainCopy
- 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;
- }
ALSA has implemented some common info callback functions for us, such as Snd_ctl_boolean_mono_info (), Snd_ctl_boolean_stereo_info (), and so on.
Get callback function
The callback function is used to read the current value of control and return the application to user space.
[C-sharp]View PlainCopy
- 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;
- }
The assignment of the value field depends on the type of control (as in the info callback). Many sound card drivers use it to store the address, Bit-shift, and bit-mask of the hardware registers, at which point the Private_value field can be set in the following example:
. Private_value = Reg | (Shift << 16) | (Mask << 24);
The get callback function can then be implemented like this:
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 >>) & 0xFF;
int mask = (kcontrol->private_value >>) & 0xFF;
....
Read the value of the corresponding register according to the above value and fill in value
}
If the Count field of control is greater than 1, which means that control has multiple element cells, the get callback function should also populate multiple numeric values for value.
Put callback function
The put callback function is used to set the control value of the application to the controls .
[C-sharp]View PlainCopy
- 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;
- }
As shown in the example above, when the value of control is changed, the put callback must return 1, and if the value is not changed, 0 is returned. If an error occurs, a negative number is returned.
As with the get callback, when count of control is greater than 1 o'clock, the put callback also handles the element values in multiple control.
Create controls
When the above discussion is ready, we can create our own control. Alsa-driver provides us with two APIs for creating control :
- Snd_ctl_new1 ()
- Snd_ctl_add ()
We can create the control in the simplest way:
[C-sharp]View PlainCopy
- Err = Snd_ctl_add (Card, Snd_ctl_new1 (&my_control, Chip));
- if (Err < 0)
- return err;
Here, My_control is a previously defined Snd_kcontrol_new object, and the chip object will be assigned a value in the Kcontrol->private_data field, which can be accessed in the callback function.
Snd_ctl_new1 () assigns a new Snd_kcontrol instance and copies the corresponding values in My_control to that instance, so when you define My_control, we usually add the __devinitdata prefix. Snd_ctl_add then binds the control to the sound card object card.
Meta data (Metadata)
Many mixer control needs to provide information in db, we can use the DECLARE_TLV_XXX macro to define some variables that contain this information, then point the control's TLV.P field to those variables, and finally, add sndrv in the Access field. _ctl_elem_access_tlv_read flag, just like this:
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,
};
The Declare_tlv_db_scale macro defines the mixer control, which represents a value that varies by the step size of a fixed DB value. The first parameter of the macro is the name of the variable to be defined, and the second parameter is the minimum value, in 0.01dB. The third parameter is the step size of the change, which is also measured in 0.01dB. If the control is in the minimum value when it makes a mute, the fourth parameter needs to be set to 1.
The Declare_tlv_db_linear macro defines the mixer control whose output changes linearly with the value. The first parameter of the macro is the name of the variable to be defined, and the second parameter is the minimum value, in 0.01dB. The second parameter is the maximum value, in 0.01dB. If the control is in the minimum value when it makes a MUTE, the second parameter needs to be set to Tlv_db_gain_mute.
These two macros actually define an array of shapes, the so-called TLV, which is the meaning of Type-lenght-value, the No. 0 element of the array represents the type of data, the 1th element represents the length of the data, the third element and the subsequent element hold the variable's data.
Establishment of control devices
The control device, like the PCM device, belongs to the logical device under the sound card. The application of user space accesses the control device through Alsa-lib, reads or controls the controlling state of control, thus achieves the control operation of controlling audio codec for various mixer and so on.
The control device is created in the same way that the PCM device was created. The detailed creation process can refer to another article in Benbow: Linux Audio Driver III: the creation of PCM devices. Let's just talk about the difference.
We need to invoke the Snd_pcm_new () function to create the PCM device when our driver initializes, and the control device is created within snd_card_create (), Snd_card_create () by calling Snd_ctl_ The Create () function creates a control device node. So we don't have to explicitly create a control device, as long as the sound card is established, the control device is created automatically.
As with PCM devices, the control device's name follows certain rules: Controlcxx, where xx represents the number of the sound card. We can also pass the code exactly this, the following is the code for the Snd_ctl_dev_register () function:
[C-sharp]View PlainCopy
- /*
- * Registration of the control device
- */
- static int Snd_ctl_dev_register (struct snd_device *device)
- {
- struct Snd_card *card = device->device_data;
- int err, cardnum;
- Char name[16];
- if (snd_bug_on (!card))
- Return-enxio;
- Cardnum = card->number;
- if (snd_bug_on (Cardnum < 0 | | cardnum >= sndrv_cards))
- Return-enxio;
- / * The name of the control device * /
- sprintf (name, "controlc%i", cardnum);
- if (err = Snd_register_device (Sndrv_device_type_control, Card,-1,
- &snd_ctl_f_ops, card, name)) < 0)
- return err;
- return 0;
- }
The Snd_ctl_dev_register () function is called in Snd_card_register (), which is the registration phase of the sound card. When the registration is complete, the control device information is saved in the snd_minors[] array, and the device number of the control device is indexed to find the relevant information in the snd_minors[] array. The relationship between the data structure after the registration is completed can be expressed as follows:
Operation function entry for control device
When a user program needs to open a control device, the driver passes the snd_minors[] global array and this device number, and can get the various callback functions in the SND_CTL_F_OPS structure. These callback functions then access the information and data in the control (which will eventually call the control's several callback functions, Get,put,info). Detailed code I will not post, we can read the code:/SOUND/CORE/CONTROL.C.
Linux ALSA sound card driver Four: Control device creation