Data structure of callbacks for PCM devices
struct ado_pcm_hw { int32_t (*capabilities) (HW_CONTEXT_T *hw_context, snd_pcm_channel_info_t *info ); int32_t (*aquire) (HW_CONTEXT_T *hw_context, PCM_SUBCHN_CONTEXT_T **PCM_SUBCHN_CONTEXT, ado_pcm_config_t *config, ado_pcm_subchn_t *subchn, uint32_t *why_failed ); int32_t (*release) (HW_CONTEXT_T *hw_context, PCM_SUBCHN_CONTEXT_T *PCM_SUBCHN_CONTEXT, ado_pcm_config_t *config); int32_t (*prepare) (HW_CONTEXT_T *hw_context, PCM_SUBCHN_CONTEXT_T *PCM_SUBCHN_CONTEXT, ado_pcm_config_t *config); int32_t (*trigger) (HW_CONTEXT_T *hw_context, PCM_SUBCHN_CONTEXT_T *PCM_SUBCHN_CONTEXT, uint32_t cmd); uint32_t (*position) (HW_CONTEXT_T *hw_context, PCM_SUBCHN_CONTEXT_T *PCM_SUBCHN_CONTEXT, ado_pcm_config_t *config); int32_t (*reconstitute) (ado_pcm_config_t * config, int8_t *dmaptr, size_t size); } ado_pcm_hw_t;
The ado_pcm_hw_t structure specifies the callbacks that you must provide for your PCM device's playback and/or capture portions.
The callback functions include:
The prototype is:
int32_t (*aquire)( HW_CONTEXT_T *hw_context, PCM_SUBCHN_CONTEXT_T **PCM_SUBCHN_CONTEXT, ado_pcm_config_t *config, ado_pcm_subchn_t *subchn, uint32_t *why_failed );
This is the function that the upper driver layers call when a client attempts to open a PCM stream.
The name of this member is spelt incorrectly; it should be “acquire” in the declaration of the structure. Unfortunately, it has to remain misspelt to be compatible with drivers that you and other developers may have already written. :-( |
The arguments are:
This function is called only when the client asks for a setup that's within the capabilities of the device. This is done by examining the capabilities structure that was passed in as part of the PCM device creation.
The main responsibility of this call is to verify that the hardware can accommodate this request, given its current state. Then the callback must allocate any hardware necessary to fulfill the request, and allocate the DMA buffer for the channel. The important idea here is that on a card that supports multiple subchannels, there may be a finite amount of resources to accommodate user requests. So if a request is received when all required resources are being used, the request has to fail even though fewer than the total subchannels are active.
A very good real-world example is a card with 8 sample rate converters that supports 24 streams simultaneously at a native sample rate of 48 kHz. If 9 clients attempt to play 22 kHz data, one fails, but up to 16 additional requests at 48 kHz pass.
This callback should return:
The prototype is:
int32_t (*release)( HW_CONTEXT_T *hw_context, PCM_SUBCHN_CONTEXT_T *PCM_SUBCHN_CONTEXT, ado_pcm_config_t *config);
The upper layers of the driver call this function when the client application closes its connection to the device. This function is the reciprocal of the aquire callback; it must free any acquired hardware and release the memory from the DMA buffer.
This function should return EOK.
The prototype is:
int32_t (*prepare)( HW_CONTEXT_T *hw_context, PCM_SUBCHN_CONTEXT_T *PCM_SUBCHN_CONTEXT, ado_pcm_config_t *config);
The upper layers of the driver call this function to prepare the hardware before it's started up. The primary reason this function is here is that the stream may stop and then restart at any time because of an underrun, so the hardware must be reset. Usually this function simply resets the DMA engine to start from the beginning of the DMA buffer.
This function should return EOK.
The prototype is:
int32_t (*trigger)( HW_CONTEXT_T *hw_context, PCM_SUBCHN_CONTEXT_T *PCM_SUBCHN_CONTEXT, uint32_t cmd);
This function is the hardware start and stop callback. The cmd parameter states the go or stop condition, and is one of:
This function should return EOK.
The prototype is:
uint32_t (*position)( HW_CONTEXT_T *hw_context, PCM_SUBCHN_CONTEXT_T *PCM_SUBCHN_CONTEXT, ado_pcm_config_t *config);
This function must return where the DMA engine is within the current fragment. It's used by the upper layer of the driver to return timing information to the client.
This function should return the number of bytes played from the beginning of the fragment.
The prototype is:
int32_t (*reconstitute)( ado_pcm_config_t *config, int8_t *dmaptr, size_t size);
This function is a catchall for hardware with very strange format support. It's used to reformat the data in the DMA buffer for the strange hardware requirements.
This callback has been used only once to date for hardware that supported 20 bits of resolution in a 32-bit sample. The upper layers of the driver filled the buffer with the MSB of the sample at bit position 31. The hardware wanted the MSB to be at bit position 19, so the reconstitute function performed a 12-bit shift.
This function should return EOK.
The prototype is:
int32_t (*capabilities)( HW_CONTEXT_T *hw_context, snd_pcm_channel_info_t *info );
This function is used to return to the client the capabilities of the device at this instant. When the device was created, its static capabilities were passed in as an argument; however, if a number of subchannels are already running, the device may no longer have the ability to support those capabilities.
The upper driver layers call this function after copying the static capabilities into the info structure. This function should simply remove some options from the info structure, based on what hardware is currently not allocated. In the most extreme case where the device only supports one subchannel, and it's already in use, the function should remove all capabilities from the info structure.
You can call ado_pcm_subchn_is_channel() to determine the type of subchannel. By doing this, you can use the same capabilities callback for capture and playback subchannels.
For details about the snd_pcm_channel_info_t structure, see snd_pcm_channel_info() in the QNX Audio Developer's Guide.
The capabilities function should return EOK.
QNX Neutrino
ado_pcm_config_t, ado_pcm_create()
snd_pcm_channel_info() in the QNX Neutrino Audio Developer's Guide