In this architecture, a PCM device is a device capable of supporting either a PCM capture channel, or a PCM playback channel, or both. A PCM capture channel converts an analog signal to a digital PCM stream, whereas a PCM playback channel takes a digital PCM stream and converts it to analog. A PCM device may also support converting multiple PCM streams simultaneously; each of these streams is called a PCM subchannel.
As described earlier, your Audio HW DLL must provide an entry point called ctrl_init(). The Organization of a Driver chapter describes the initialization that this function must do no matter what features your DLL supports.
In much the same way that we created a mixer device in the previous chapter, we now create a PCM device using the ado_pcm_create() function. This informs the upper levels of software that this card now supports a PCM device. If you call this function again, it creates additional devices.
The prototype of the ado_pcm_create() function is:
int32_t ado_pcm_create( ado_card_t *card, char *name, uint32_t flags, char *id, uint32_t play_subchns, ado_pcm_cap_t *play_cap, ado_pcm_hw_t *play_hw, uint32_t cap_subchns, ado_pcm_cap_t *cap_cap, ado_pcm_hw_t *cap_hw, ado_pcm_t **rpcm );
The arguments are:
In order to make the PCM device work, you need to define the callback functions in the ado_pcm_hw_t structures for the capture and playback portions of the PCM device.
Before we look at them in detail, let's first review how a PCM stream operates in hardware. The model used in this architecture is a DMA buffer in memory that's divided into two or more buffer fragments. When instructed to do so, the hardware acts on a fragment using DMA, and then generates an interrupt on completing the fragment.
So, if we consider the simplified case of playback with a 50K buffer, composed of two fragments, here's what happens when the client application sends data:
From a programming perspective, if the hardware can be set up to do a looping DMA buffer playback with an interrupt every x bytes, implementing this model is very straightforward. A variation on this theme is to reprogram the DMA engine after every fragment in the interrupt routine. In the general case, the client suggests the fragment size and number of fragments, but the driver has the ultimate authority on these parameters.