Linux ALSA sound card driver 4: creation of control devices

Source: Internet
Author: User

Statement: the content of this blog is created at http://blog.csdn.net/droidphone. please refer to it for help. Thank you!

Control Interface

The control interface allows the user space application (ALSA-Lib) to access and control multiple switches and sliding controls in the Audio Codec Chip. For mixer (sound mixing), the control interface is particularly important. Starting from ALSA 0.9.x, all mixer operations are implemented through the control interface API.

 

ALSA has defined a complete control interface model for ac97. If your Codec Chip only supports the ac97 interface, you do not need to care about the content in this section.

 

<Sound/control. h> defines all control APIs. If you want to implement your own controls for your CODEC, please include this header file in the code.

Definition of controls

To customize a control, we must first define three callback functions: info, get, and put. Then, define a snd_kcontrol_new structure:

Static struct snd_kcontrol_new my_control _ devinitdata ={< br/>. iface = sndrv_ctl_elem_iface_mixer, <br/>. name = "PCM playback switch", <br/>. index = 0, <br/>. access = sndrv_ctl_elem_access_readwrite, <br/>. private_value = 0 xFFFF, <br/>. info = my_control_info, <br/>. get = my_control_get, <br/>. put = my_control_put <br/> };

 

The iface field specifies the control type. ALSA defines several types (snddrv_ctl_elem_iface_xxx). Common types are mixer. Of course, you can also define a global card type, you can also define the device type, such as hwdep, pcmrawmidi, and timer. In this case, you need to specify the device logical number of the card in the device and subdevice fields.

 

The name field is the name of the Control. Since ALSA 0.9.x, the name of the control becomes more important, because the role of control is to classify by name. ALSA has predefined some control names. We will discuss them in detail in the control name section.

 

The index field is used to save the number of the control in the card. If there is more than one Codec in The Sound Card, and each codec has a control with the same name, we can identify these controls through index. When the index is 0, you can ignore this differentiation policy.

 

The access field contains the access type of the control. Each bit represents an access type, which can be combined by multiple "or" operations.

 

The private_value field contains any long integer value. This value can be accessed through the callback functions info, get, and put. You can decide how to use this field. For example, you can split it into multiple single-bit fields or a pointer pointing to a data structure.

 

The TLV field provides metadata for the control.

Control name

The name of a control must follow some standards. Generally, it can be divided into three parts to define the name of a control: Source-direction-function.

 

  • It can be understood as the input of the Control. ALSA has predefined some common sources, such as master, PCM, CD, line, and so on.
  • Direction: indicates the data flow direction of the control. For example, playback, capture, bypass, and bypass capture can also be left unspecified. In this case, the control is bidirectional (playback and capture ).
  • Function, according to the control function, can be the following string: Switch, volume, route, and so on.

There are also some naming exceptions:

  • Global Capture and Playback"Capture source", "Capture volume", "Capture switch", which are used for global capture source, switch, and volume. Similarly, "playback volume" and "playback switch" are used for global output switches and volume.
  • Tone-controlesThe switch and volume of Tone Control are named Tone Control-xxx, for example, "Tone Control-switch", "Tone Control-Bass", and "Tone Control-center ".
  • 3D controlsNaming rules for 3D controls: "3D control-switch", "3D control-center", and "3D control-space ".
  • Mic boostThe microphone volume enhancement control is named "mic boost" or "mic boost (6db )".
Access flags)

The access field is a bitmask that saves the access type changed to control. The default access type is snddrv_ctl_elem_access_readwrite, indicating that the control supports read and write operations. If the access field is not defined (. Access = 0), it is also considered as the readwrite type.

 

For a read-only control, access should be set to snddrv_ctl_elem_access_read. In this case, we do not need to define the put callback function. Similarly, if you write only control, access should be set to snddrv_ctl_elem_access_write. In this case, we do not need to define the get callback function.

 

If the control value changes frequently (for example, the meter), we can use the volatile type, which means the control will change without notice, the application should regularly query the value of this control.

 

Callback Function

Info callback function

The info callback function is used to obtain detailed control information. The main task is to fill in the snd_ctl_elem_info object passed in through the parameter. The following example shows the info callback of A boolean control with a single element:

Static int snd_myctl_mono_info (struct snd_kcontrol * kcontrol, <br/> struct snd_ctl_elem_info * uinfo) <br/>{< br/> uinfo-> type = sndrv_ctl_elem_type_boolean; <br/> uinfo-> COUNT = 1; <br/> uinfo-> value. integer. min = 0; <br/> uinfo-> value. integer. max = 1; <br/> return 0; <br/>}

 

The Type field specifies the value type of the control. The value type can be boolean, integer, enumerated, bytes, iec958, or integer64. The Count field specifies the number of element units in the change control. For example, the volume of two sound channels is controlled by the volume of the stereo sound, and its count field is equal to 2. The value field is a union. The value content is related to the control type. Boolean and integer are of the same type.

 

The enumerated type is special. Its value needs to be indexed by a string and a string. See the following example:

Static int snd_myctl_enum_info (struct snd_kcontrol * kcontrol, <br/> struct snd_ctl_elem_info * uinfo) <br/>{< br/> static char * Texts [4] ={< br/> "first", "second", "third ", "Fourth" <br/>}; <br/> uinfo-> type = sndrv_ctl_elem_type_enumerated; <br/> uinfo-> COUNT = 1; <br/> uinfo-> value. enumerated. items = 4; <br/> If (uinfo-> value. enumerated. item> 3) <br/> uinfo-> value. enumerated. item = 3; <br/> strcpy (uinfo-> value. enumerated. name, <br/> texts [uinfo-> value. enumerated. item]); <br/> return 0; <br/>}

 

ALSA has implemented some general info callback functions for us, such as snd_ctl_boolean_mono_info (), snd_ctl_boolean_stereo_info (), and so on.

Get callback function

This callback function is used to read the current value of the control and return it to the application of the user space.

Static int snd_myctl_get (struct snd_kcontrol * kcontrol, <br/> struct snd_ctl_elem_value * ucontrol) <br/>{< br/> struct mychip * chip = snd_kcontrol_chip (kcontrol ); <br/> ucontrol-> value. integer. value [0] = get_some_value (CHIP); <br/> return 0; <br/>}

 

The value field value is dependent on the control type (like the info callback ). Many sound card drivers use it to store the address, bit-shift, and bit-mask of hardware registers. In this case, the private_value field can be set as follows:

 

. Private_value = reg | (shift <16) | (mask <24 );

 

Then, the get callback function can be implemented as follows:

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;
....

// Read the value of the corresponding register based on the above values and fill in the value
}

 

If the Count field of control is greater than 1, it indicates that control has multiple element units. The get callback function should also fill multiple values with values.

 

Put callback function

The put callback function is used to set the application control value to control.

Static int snd_myctl_put (struct snd_kcontrol * kcontrol, <br/> struct snd_ctl_elem_value * ucontrol) <br/>{< br/> struct mychip * chip = snd_kcontrol_chip (kcontrol ); <br/> int changed = 0; <br/> If (chip-> current_value! = <Br/> ucontrol-> value. integer. value [0]) {<br/> change_current_value (chip, <br/> ucontrol-> value. integer. value [0]); <br/> changed = 1; <br/>}< br/> return changed; <br/>}

 

As shown in the preceding example, when the control value is changed, the put callback must return 1. If the value is not changed, the system returns 0. If an error occurs, a negative error number is returned.

 

Like the get callback, when the count of the control is greater than 1, The put callback must also process the element values in multiple controls.

Create controls

After we have prepared all the content discussed above, we can create our own control. ALSA-Driver provides two APIs for creating control:

  • Snd_ctl_new1 ()
  • Snd_ctl_add ()

You can create a control using the following simple methods:

Err = snd_ctl_add (card, snd_ctl_new1 (& my_control, Chip); <br/> If (ERR <0) <br/> return err;

 

Here, my_control is a previously defined snd_kcontrol_new object. 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 () allocates a new snd_kcontrol instance and copies the corresponding values in my_control to the instance. Therefore, when defining my_control, we can usually add the prefix _ devinitdata. Snd_ctl_add binds the control to the card of the Sound Card object.

Metadata (metadata)

Many mixer control systems need to provide DB-based information. We can use the declare_tlv_xxx macro to define some variables that contain this information, and then set the TLV of the control. the P field points to these variables. Finally, add the sndrv_ctl_elem_access_tlv_read flag to the access field, as shown in the following figure:

 

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 mixer control defined by the declare_tlv_db_scale macro changes the value represented by a fixed dB value step. The first parameter of the macro is the name of the variable to be defined, and the second parameter is the minimum value, in the unit of 0.01db. The third parameter is the variable step, also in the unit of 0.01db. If the control is at the minimum value, the fourth parameter must be set to 1.

 

The mixer control defined by the declare_tlv_db_linear macro. Its output varies 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 the unit of 0.01db. The second parameter is the maximum value, in the unit of 0.01db. If the control is at the minimum value, the second parameter must be set to tlv_db_gain_mute.

 

These two macros define an integer array. The so-called TLV indicates the type-lenght-value. Each element of the array 0th represents the data type, and the other 1st represents the Data Length, the third and subsequent elements Save the data of the variable.

Control Device Creation

The control device, like the PCM device, belongs to the logical device under the sound card. Applications in the user space access the control device through ALSA-lib, read or control the control status of the control, so as to control the audio codec for various control operations such as mixer.

 

The creation process of the control device is basically the same as that of the PCM device. For detailed creation process, refer to another article in this blog: Linux audio driver 3: Creating PCM devices. Here we will only discuss the differences.

 

We need to call the snd_pcm_new () function to create the PCM device during driver initialization, while the control device is created in snd_card_create (). snd_card_create () is called by snd_ctl_create () function to create a control device node. Therefore, you do not need to create a control device explicitly. If you create a sound card, the control device is automatically created.

 

Like PCM devices, the name of a control device follows certain rules: controlcxx, where xx represents the number of the sound card. We can also use the code below for this purpose. The code of the snd_ctl_dev_register () function is as follows:

/* <Br/> * Registration of the control device <br/> */<br/> static int snd_ctl_dev_register (struct snd_device * device) <br/>{< br/> struct snd_card * card = device-> device_data; <br/> int err, cardnum; <br/> char name [16]; </P> <p> If (snd_bug_on (! Card) <br/> return-enxio; <br/> cardnum = card-> number; <br/> If (snd_bug_on (cardnum <0 | cardnum> = sndrv_cards) <br/> return-enxio; <br/>/* control device name */<br/> sprintf (name, "controlc % I", cardnum ); <br/> If (ERR = snd_register_device (sndrv_device_type_control, card,-1, <br/> & snd_ctl_f_ops, card, name) <0) <br/> return err; <br/> return 0; <br/>}< br/>

 

The snd_ctl_dev_register () function is called in snd_card_register (), that is, the registration phase of the sound card. After registration, the information about the control device is saved in the snd_minors [] array, and the device Number of the control device is used as an index to locate the information in the snd_minors [] array. The data structure relationship after registration can be expressed as follows:

Operation Function entry of the control device

 

When a user program needs to enable the control device, the driver can use the snd_minors [] global array and the device number to obtain various callback functions in the snd_ctl_f_ops structure, then, use these callback functions to access the information and data in the control (several callback functions get, put, info will be called in the end ). I will not post the detailed code. You can read the code:/sound/CORE/control. C.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.