ALSA Learning Notes Because the project uses a kernel of 2.6.17, the following analysis is based on the 2.6.17 version, in this version, there is no ASOC.1 overall architecture Application --------------- Alsa-lib User Space ------------------------------------- Alsa Kernel Space ------- Sound Driver ---------------------------------- Hardware
Application: For example, Aplay, it is not directly to invoke the interface provided by kernel, but to invoke the Alsa-lib interface. So the app just #include "asound.h" and link libasound. For the above schema, there can only be one program at a time to open the sound card and occupy it, when other programs open, will return to busy. If you want to support multiple applications to open the sound card at the same time, you need support Mixing features, some sound cards support hardware mix, but most sound cards do not support hardware mix, the need for software mix. This requires Esd,pulseaudio, and the architecture becomes: App1 APP2 --------------- ESD, PulseAudio -------------------- Alsa-lib User Space ------------------------------------- Alsa Kernel Space ------- Sound Driver ---------------------------------- Hardware At this point, the application will invoke the interface provided by the Esd,pulseaudio and other mixer. For ESD, a lot of program support, such as MPlayer. For PulseAudio, there are corresponding patch. Alsa itself also offers mixed-tone plugin,dmix. App1 APP2 --------------- Alsa-lib (dmix) User space ------------------------------------- Alsa Kernel Space ------- Sound Driver ---------------------------------- Hardware This schema and schema 1, the application does not need to make any changes, only need to modify the asound.conf Example of the asound.conf of Architecture 1: Pcm.! Default { Type HW Card 0 } Ctl.! Default { Type HW Card 0 } Example of the asound.conf of Architecture 3: pcm.card0 { Type HW Card 0 } Pcm.! Default { Type plug SLAVE.PCM "Dmixer" } Pcm.dmixer { Type Dmix Ipc_key 1025 Slave { PCM "hw:0,0" Period_time 0 Period_size 4096 Buffer_size 16384 Periods 128 Rate 44100 } Bindings { 0 0 1 1 } } For configuration, refer to this web site: Http://www.alsa-project.org/alsa-doc/alsa-lib/pcm_plugins.html For Period_size and Buffer_size, be aware that I modified them to 1024, 8192. On our platform with DMIX will appear underrun!!! Information. 2 ALSA Kernel 2.1 Directory Alsa-driver includes many drivers in development and support for some 2.2,2.4 Linux kernel versions. When these drivers are stabilized, they are moved into the Alsa-kernel and eventually in the Linux kernel In the sound directory. Sound /core /oss /seq /oss /instr /drivers /mpu401 /opl3 /i2c /l3 /synth /emux /pci /(Cards) /isa /(Cards) /arm /ppc /sparc /usb /pcmcia/(cards) /oss The core directory is the heart of Alsa, The Core/oss directory is primarily designed to support OSS simulations of PCM and mixer, and Rawmidi OSS simulations are placed in Alsa Rawmidi files mainly because it is fairly small. So many previously supported OSS Applications can also run on ALSA. CORE/SEQ, ALSA Sequencer, Core/seq/oss is ALSA simulation of OSS Sequencer. The driver directory contains a variety of chip drivers. I2C directory, Alsa I2C components. Although Linux has a standard I2C layer, ALSA has its own I2C code for some sound cards because the sound card requires only a few simple operations and a standard The I2C API is too complex for this purpose. PCI directory, mainly for PCI sound cards on PCI bus ISA directory, primarily for ISA sound cards on the ISA bus ARM, PPC, and SPARC directories, some sound card drivers under these architectures, such as the PXA2XX-PCM.C PXA processor USB Directory, Usb-audio driver Pcmcia OSS directory, OSS architecture files, not belonging to Alsa 2.2 Interface Alsa kernel mainly provides the following interfaces for the upper layer: The 1 Control interface provides a flexible way to manage registered sound cards and to query for existing sound cards. 2 PCM interface provides the management of digital audio capture and playback. 3 Original MIDI interface A standard electronic music instruction set. These APIs provide access to the MIDI bus on the sound card. These original excuses work directly on the MIDI Events, programmers only need to manage protocols and time. The 4 Timer interface provides access to the timer on the sound card for synchronization events that support sound. 5 Sequencer interface an advanced MIDI programming and voice synchronization higher level interface than the original MIDI interface. It can handle a lot of MIDI protocols and timers. The 6 mixer interface controls the device on the sound card that transmits the signal and controls the sound size. /dsp/mixer,oss in the present.
Our main concern is the 1,2 interface.
2.3 Management of sound card 2.3.1 Card For each sound card, a "card" record must be assigned. The "card" record is the head office of the sound card, which manages a list of all devices (or components) on the sound card, such as Pcm,mixer,midi and so on. Data structure is: Snd_card Number: The first few sound cards, the largest for Sndrv_cards 8, for our system, only one sound card, the second is 0 ID: string for sound card Devices: Device List Root of Proc_root:proc file Private_data: Private data for sound card CONTROLS: List of control interfaces for sound cards There are some power management and other
Call Snd_card_new to create a sound card entity. Snd_card_new (index, ID, module, extra_size); Where Extra_size is the size of the private_data memory space, allocated in snd_card_new. 2.3.2 Devices (components) After the card instance is created, we can attach a component (device) to an instance of a card. In the ALSA drive, a component is represented by a structure Snd_device object. A component can be a PCM instance, a control instance, a raw MIDI interface, and so on. It invokes snd_device_new creation. Snd_device_new (Card, snd_device_type_t, Device_data, &ops); In Control.c, PCM.C,INFO.C,RAWMIDI.C, and timer.c, there are snd_device_new calls that create control,pcm of type Deice, respectively. Data structure is: Snd_device struct List_head list; /* List of registered devices * * struct Snd_card *card; /* Card which holds this device * * snd_device_state_t State; /* State of the device * * snd_device_type_t type; /* Device Type * * void *device_data; /* Device Structure * * struct Snd_device_ops *ops; * Operations * * Snd_device_ops includes functions such as registration, Unregister,free, and so on. In Snd_card_new, we created a control device, and Snd_pcm_new created a PCM device
2.3.3 Registration and release After Snd_card_register calls it, the device file can be accessed by the outside world. Before, can not be safely outside the access. Snd_card_free is typically invoked when exiting, which will automatically release all components. 2.4 PCM Interface The ALSA PCM middle tier is very powerful, and drives only need to implement the underlying functions to access the hardware. Each card can have up to 4 PCM instances. A PCM instance contains playback (playback) and capture (recording) streams, and the data structure is: SND_PCM, where struct snd_pcm_str streams[2]; Stream[0] Represents Playback,stream[1] represents capture. Each PCM stream consists of one or more PCM streams, Some sound cards support multiple playback features. For example, EMU10K1 has a PCM playback 32-bit stereo stream (Substream), at which point, each time, an idle stream is automatically selected and Open, and if only one of the streams exists and is open, the next opening is either blocked or returned to Eagain, but these are not needed in your drive, and the PCM mesosphere Manage the work. Snd_pcm_new creates an entity, int snd_pcm_new (struct snd_card *card, char *id, int device, int playback_count, int capture_count, struct SND_PCM * * rpcm) Device: The first several PCM entities, starting from 0 Playback_count: Number of playback sub streams Capture_count: Number of recording streams
Data structure of a stream: Snd_pcm_str Where the stream represents the direction, is playback or capture Substream_count Number of sub streams A pointer to the struct Snd_pcm_substream *substream, pointing to the first stream, which, depending on the first, can be found next (substream->next)
Snd_pcm_new_stream creates a stream that is invoked in Snd_pcm_new.
Data structure of a substream (Snd_pcm_substream): Where the stream represents the direction, is playback or capture Buffer_bytes_max represents the largest annular buffer size Dma_buffer ..... Dma_max Max struct Snd_pcm_ops *ops; The operation function of the snd_pcm_set_ops that will set Ops struct Snd_pcm_runtime *runtime; Next next Substream, for a stream with only one playback, null struct Snd_timer *timer; ????
Snd_pcm_ops defines a series of hardware operating functions, such as open, Ioctrl, trigger, etc. 1 Open callback: static int Snd_xxx_open (struct snd_pcm_substream *substream); This function is called when a stream is opened (Snd_pcm_open_substream function call) The calling relationship is: In snd_pcm_f_ops. Open = Snd_pcm_playback_open Snd_pcm_playback_open---> Snd_pcm_open_file---> Snd_pcm_open_substream---> Snd_xxx_open Snd_pcm_f_ops Open, when you are called on the/dev/snd/pcmc0d0 open time Our main driver is to allocate substream->runtime and call REQUEST_IRQ to associate the interrupt handler function with the runtime.
2 Close Callback static int snd_xxx_close (struct snd_pcm_substream *substream); This function is called when a child stream is closed The calling relationship is: In snd_pcm_f_ops. Release = Snd_pcm_release Snd_pcm_release---> Snd_pcm_release_substream---> Snd_xxx_close Snd_pcm_f_ops release, when you are called on/dev/snd/pcmc0d0 close Our main driver is to call FREE_IRQ to release the interrupt handler function and runtime. (whether there is a memory leak, because in the open function to Runtimer with Kmalloc, in the close function should be Kfree (runtime)??? 3 IOCTL Callback General use Snd_pcm_lib_ioctl
4 Hw_params Callback static int snd_xxx_hw_params (struct snd_pcm_substream *substream, struct Snd_pcm_hw_params *hw_params); The calling relationship is: In the snd_pcm_f_ops. Unlocked_ioctl = Snd_pcm_playback_ioctl Snd_pcm_playback_ioctl--> SND_PCM_PLAYBACK_IOCTL1 -->SND_PCM_COMMON_IOCTL1---> Snd_pcm_hw_params_user---> Snd_pcm_hw_params---> Snd_xxx_hw_params
Snd_pcm_f_ops Unlocked_ioctl, when you invoke/DEV/SND/PCMC0D0 ioctl (int fd, int command, (char *) argstruct), the file system Do_ioctl You will first determine whether the Unlocked_ioctl function is empty, not empty, and call Filp->f_op->unlocked_ioctl, where command is sndrv_pcm_ioctl_hw_params. This function is invoked when the application sets hardware parameters, that is, when the PCM stream's buffer size, cycle size, and format are defined. Many hardware settings must be done in this callback function, including the allocation of buffer. Buffer allocation can call the Snd_pcm_lib_malloc_pages function. Our drive seems to have only buffer allocation, no other processing, there will be problems????
5 Hw_free Callback static int Snd_xxx_hw_free (struct snd_pcm_substream *substream); Releasing resources allocated in Hw_params
6 Prepare callback static int snd_xxx_prepare (struct snd_pcm_substream *substream); The calling relationship is: In the snd_pcm_f_ops. Unlocked_ioctl = Snd_pcm_playback_ioctl Snd_pcm_playback_ioctl--> SND_PCM_PLAYBACK_IOCTL1 -->SND_PCM_COMMON_IOCTL1---> Snd_pcm_prepare--> snd_pcm_do_prepare---> Snd_xxx_prepare IO command for Sndrv_pcm_ioctl_prepare. You can set the format type, the sampling rate, and so on in this function, which differs from hw_params in that the prepare callback function is invoked every Snd_pcm_prepare For example, after the underrun recovery, etc. In this function, you can get some values through the runtime record substream->runtime, such as the current sampling rate, format type, channel number, runtime->rate, Runtime->format or Runtime->channels The allocated memory is set at Runtime->dma_area, and the size and cycle are runtime->buffer_size and runtime->period_size respectively.
Our driver has a problem with this function.
7 Trigger Callback static int Snd_xxx_trigger (struct snd_pcm_substream *substream, int cmd); The calling relationship is: In the snd_pcm_f_ops. Unlocked_ioctl = Snd_pcm_playback_ioctl Snd_pcm_playback_ioctl--> SND_PCM_PLAYBACK_IOCTL1 -->SND_PCM_COMMON_IOCTL1---> Snd_pcm_drain--> snd_pcm_do_drain_init---> Snd_xxx_trigger IO command for Sndrv_pcm_ioctl_drain
The callback function is atomic, that is, the function that can not call sleep, trigger callback function should be as small as possible, just triggering DMA, other operations in Hw_params and Perpare inside do. Our drive implements Start,stop,suspend and RESUME, without realizing pause,unpause. Also called the Msleep???? It's not right. Mdelay is a let the CPU idling, wait until the time of approval before exiting Msleep is to let the current process hibernate, give up the CPU to other processes to use, wait until the time to wake up Msleep cannot be used in an interrupt context
8 Pointer callback Static snd_pcm_uframes_t snd_xxx_pointer (struct snd_pcm_substream *substream) The callback function is called at the current hardware position in the buffer in the PCM middle-tier, and the position is computed in frames, ranging from 0 to buffer_size-1. The calling relationship is: snd_pcm_period_elapsed--> Snd_pcm_update_hw_ptr_pos---> Snd_xxx_pointer In the interrupt handler function, the snd_pcm_period_elapsed is called, then the PCM middle tier updates the location, calculates the remaining space, and wakes the sleeping thread. Sndrv_pcm_pos_xrun????
9 Other callback are not mandatory, and we are not driven to achieve them.
After you set up the hardware operation function, you can allocate buffer beforehand, call Snd_pcm_lib_preallocate_pages_for_all The function updates the Dma_max and dma_buffer of the stream, where the area in Dma_buffer represents the memory region
The release of PCM instances, generally not required, the PCM intermediate layer will automatically release the memory, unless you initialize the time, the allocation of some special variables (with Kmalloc), At this point in the Exit function, with the kfree off.
2.4.2. Runtime When a PCM stream is opened, a runtime instance of PCM is assigned and assigned to Substream-〉runtime. Runtime keep Most of the information you need to control PCM, Hw_params and sw_params configuration copies, buffer pointers, etc. Data structure: Snd_pcm_runtime Include hw:hw_params information The hardware descriptor (struct snd_pcm_hardware) contains the definition of the basic hardware configuration. First, you will define it in the open callback function. What needs to be explained is that the runtime instance holds the copy of the descriptor, not the pointer to an already descriptor, that is, in the open callback function, you can modify the RUNTIME->HW In accordance with your needs.
2.4.3 Interrupt Handling The function of the interrupt handler in the sound card driver: Update the position of the buffer and tell the PCM middle layer when the buffer position crosses the previously set period size. Call snd_pcm_period_elapsed function Will tell the PCM mesosphere. The period_size is set in Snd_pcm_hw_params. There are several ways in which the following sound card types can be interrupted: 1 Interrupt at Cycle boundary The most common way 2 High Frequency Timer interrupt For chips that don't interrupt. Our implementation: For position Pcm_buf_pos, the prepare call is set to 0, at the time of interruption, each will pass 60 bytes, each time plus 60, loop. Then call the Snd_pcm_period_elapsed function to notify the upper level.
Call the Spin_lock???? in the interrupt For multiple processors???
File_operation in PCM: When Snd_pcm_new is invoked, Snd_pcm_new---> snd_pcm_dev_register--> snd_register_device This function to update the SND_MINOR structure,
And in Alsa_sound_init, Register_chrdev (Major, "Alsa", &snd_fops); When a ALSA primary device is turned on, the open function in Snd_fops is invoked, That is, Snd_open (in SOUND.C), and Snd_open will replace file_ops according to Snd_minor, if the minor is a PCM device, it will use the PCM file_operation (Updated in Snd_pcm_new) |