Linux ALSA声卡驱动之一:ALSA架构简介

一.  概述

    ALSA是Advanced Linux Sound Architecture 的缩写,目前已经成为了linux的主流音频体系结构,想了解更多的关于ALSA的这一开源项目的信息和知识,请查看以下网址:http://www.alsa-project.org/

    在内核设备驱动层,ALSA提供了alsa-driver,同时在应用层,ALSA为我们提供了alsa-lib,应用程序只要调用alsa-lib提供的API,即可以完成对底层音频硬件的控制。

 

 

                                                           图 1.1   alsa的软件体系结构

由图1.1可以看出,用户空间的alsa-lib对应用程序提供统一的API接口,这样可以隐藏了驱动层的实现细节,简化了应用程序的实现难度。内核空间中,alsa-soc其实是对alsa-driver的进一步封装,他针对嵌入式设备提供了一些列增强的功能。本系列博文仅对嵌入式系统中的alsa-driver和alsa-soc进行讨论。

下面对录音的过程做简要描述“

第一步骤:

audio_io_handle_t AudioFlinger::openInput(audio_module_handle_tmodule,
                                          audio_devices_t *pDevices,
                                          uint32_t *pSamplingRate,
                                          audio_format_t *pFormat,
                                          audio_channel_mask_t *pChannelMask)
{
    status_t status;
    RecordThread *thread = NULL;
    struct audio_config config = {
        sample_rate: pSamplingRate ? *pSamplingRate : 0,
        channel_mask: pChannelMask ? *pChannelMask : 0,
        format: pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT,
    };
    uint32_t reqSamplingRate = config.sample_rate;
    audio_format_t reqFormat = config.format;
    audio_channel_mask_t reqChannels = config.channel_mask;
    audio_stream_in_t *inStream = NULL;       //    1    audio_stream_in_t 结构体 audio_stream_in    
    AudioHwDevice *inHwDev;                        //     2     AudioHwDevice类在AudioFlinger.h文件中,处理设备的描述

    if (pDevices == NULL || *pDevices == 0) {
        return 0;
    }

    Mutex::Autolock _l(mLock);

    inHwDev = findSuitableHwDev_l(module, *pDevices);    //   3    根据线程号和设备号找到对应的AudioHwDevice
    if (inHwDev == NULL)
        return 0;

    audio_hw_device_t *inHwHal = inHwDev->hwDevice(); //   4    根据线程号和设备号找到对应的设备结构体audio_hw_device
    audio_io_handle_t id = nextUniqueId();

    status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config,&inStream);  //  5   根据inHwHal,pDevices,inStream等参数打开AudioHardwareALSA::openInputStream()方法,进而打开AudioStreamInALSA::set()方法。在执行inHwHal->open_input_stream()方法的过程中会有个转换的过程,见步骤二过程
    ALOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, status %d",
            inStream,
            config.sample_rate,
            config.format,
            config.channel_mask,
            status);

    // If the input could not be opened with the requested parameters and we can handle the conversion internally,
    // try to open again with the proposed parameters. The AudioFlinger can resample the input and do mono to stereo
    // or stereo to mono conversions on 16 bit PCM inputs.
    if (status == BAD_VALUE &&
        reqFormat == config.format && config.format == AUDIO_FORMAT_PCM_16_BIT &&
        (config.sample_rate <= 2 * reqSamplingRate) &&
        (getInputChannelCount(config.channel_mask) <= FCC_2) && (getInputChannelCount(reqChannels) <= FCC_2)) {
        ALOGV("openInput() reopening with proposed sampling rate and channel mask");
        inStream = NULL;
        status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config, &inStream);
    }

    if (status == NO_ERROR && inStream != NULL) {
        AudioStreamIn *input = new AudioStreamIn(inHwDev,inStream); //  6   把inHwDev(设备结构体audio_hw_device)和inStream(流结构体 audio_stream_in)放入input

        // Start record thread
        // RecorThread require both input and output device indication to forward to audio
        // pre processing modules
        audio_devices_t device = (*pDevices) | primaryOutputDevice_l();
        thread = new RecordThread(this,           //    7   新建一个录音线程,参数包括input,id,device等
                                  input,
                                  reqSamplingRate,
                                  reqChannels,
                                  id,
                                  device);
        mRecordThreads.add(id, thread);         //  8   把新建线程id和对应的线程加入到mRecordThreads数组中
        ALOGV("openInput() created record thread: ID %d thread %p", id, thread);
        if (pSamplingRate != NULL) *pSamplingRate = reqSamplingRate;
        if (pFormat != NULL) *pFormat = config.format;
        if (pChannelMask != NULL) *pChannelMask = reqChannels;

        // notify client processes of the new input creation
        thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED);    //  9   重新配置新建的线程信息,使之配置信息生效
        return id;
    }
    return 0;
}

第二步骤:

 //  5   根据inHwHal,pDevices,inStream等参数打开AudioHardwareALSA::openInputStream()方法,进而打开AudioStreamInALSA::set()方法。在执inHwHal->open_input_stream()方法的过程中会有个转换的过程,见步骤二过程。

2.1  hardware\libhardware\include\hardware\audio.h Audio.h中struct audio_hw_device {
    struct hw_device_t common;

    /** This method creates and opens the audio hardware output stream */
    int (*open_output_stream)(struct audio_hw_device *dev,
                              audio_io_handle_t handle,
                              audio_devices_t devices,
                              audio_output_flags_t flags,
                              struct audio_config *config,
                              struct audio_stream_out **stream_out);

    void (*close_output_stream)(struct audio_hw_device *dev,
                                struct audio_stream_out* stream_out);

    /** This method creates and opens the audio hardware input stream */
    int (*open_input_stream)(struct audio_hw_device *dev,
                             audio_io_handle_t handle,
                             audio_devices_t devices,
                             struct audio_config *config,
                             struct audio_stream_in **stream_in);

    void (*close_input_stream)(struct audio_hw_device *dev,
                               struct audio_stream_in *stream_in);
}

2.2 \hardware\qcom\audio\alsa_sound\audio_hw_hal.cpp中

static int qcom_adev_open(const hw_module_t* module, const char* name, hw_device_t** device)
{
    struct qcom_audio_device *qadev;
    int ret;

    if (strcmp(name, AUDIO_HARDWARE_INTERFACE) != 0)
        return -EINVAL;

    qadev = (struct qcom_audio_device *)calloc(1, sizeof(*qadev));
    if (!qadev)
        return -ENOMEM;

    qadev->device.common.tag = HARDWARE_DEVICE_TAG;
    qadev->device.common.version = AUDIO_DEVICE_API_VERSION_2_0;
    qadev->device.common.module = const_cast<hw_module_t*>(module);
    qadev->device.common.close = qcom_adev_close;

    qadev->device.get_supported_devices = adev_get_supported_devices;
    qadev->device.init_check = adev_init_check;
    qadev->device.set_voice_volume = adev_set_voice_volume;
    qadev->device.set_master_volume = adev_set_master_volume;
    qadev->device.get_master_volume = adev_get_master_volume;
#ifdef QCOM_FM_ENABLED
    qadev->device.set_fm_volume = adev_set_fm_volume;
#endif
    qadev->device.set_mode = adev_set_mode;
    qadev->device.set_mic_mute = adev_set_mic_mute;
    qadev->device.get_mic_mute = adev_get_mic_mute;
    qadev->device.set_parameters = adev_set_parameters;
    qadev->device.get_parameters = adev_get_parameters;
    qadev->device.get_input_buffer_size = adev_get_input_buffer_size;
    qadev->device.open_output_stream = adev_open_output_stream;
    qadev->device.close_output_stream = adev_close_output_stream;
    qadev->device.open_input_stream = adev_open_input_stream;  //qadev->device 代表的就是hardware\libhardware\include\hardware\audio.h中struct audio_hw_device的方法open_input_stream,见说下说明1
    qadev->device.close_input_stream = adev_close_input_stream;
    qadev->device.dump = adev_dump;

    qadev->hwif = createAudioHardware();  // qadev->hwif 代表的是结构体中struct qcom_audio_device的结构体struct AudioHardwareInterface *hwif;也即是AudioHardwareALSA
    if (!qadev->hwif) {
        ret = -EIO;
        goto err_create_audio_hw;
    }

    *device = &qadev->device.common;

    return 0;

err_create_audio_hw:
    free(qadev);
    return ret;
}

 2.3 \hardware\qcom\audio\alsa_sound\audio_hw_hal.cpp中

/** This method creates and opens the audio hardware input stream */
static int adev_open_input_stream(struct audio_hw_device *dev,
                                  audio_io_handle_t handle,
                                  audio_devices_t devices,
                                  audio_config *config,
                                  audio_stream_in **stream_in)
{
    struct qcom_audio_device *qadev = to_ladev(dev);
    status_t status;
    struct qcom_stream_in *in;
    int ret;

    in = (struct qcom_stream_in *)calloc(1, sizeof(*in));
    if (!in)
        return -ENOMEM;

    devices = convert_audio_device(devices, HAL_API_REV_2_0, HAL_API_REV_1_0);

    in->qcom_in = qadev->hwif->openInputStream(devices, (int *)&config->format,
                                    &config->channel_mask,
                                    &config->sample_rate,
                                    &status,
                                    (AudioSystem::audio_in_acoustics)0);

    if (!in->qcom_in) {
        ret = status;
        goto err_open;
    }

    in->stream.common.get_sample_rate = in_get_sample_rate;
    in->stream.common.set_sample_rate = in_set_sample_rate;
    in->stream.common.get_buffer_size = in_get_buffer_size;
    in->stream.common.get_channels = in_get_channels;
    in->stream.common.get_format = in_get_format;
    in->stream.common.set_format = in_set_format;
    in->stream.common.standby = in_standby;
    in->stream.common.dump = in_dump;
    in->stream.common.set_parameters = in_set_parameters;
    in->stream.common.get_parameters = in_get_parameters;
    in->stream.common.add_audio_effect = in_add_audio_effect;
    in->stream.common.remove_audio_effect = in_remove_audio_effect;
    in->stream.set_gain = in_set_gain;
    in->stream.read = in_read;
    in->stream.get_input_frames_lost = in_get_input_frames_lost;

    *stream_in = &in->stream;
    return 0;

err_open:
    free(in);
    *stream_in = NULL;
    return ret;
}

2.4 \hardware\qcom\audio\alsa_sound\audio_hw_hal.cpp中

static void adev_close_input_stream(struct audio_hw_device *dev,
                               struct audio_stream_in *stream)
{
    struct qcom_audio_device *qadev = to_ladev(dev);
    struct qcom_stream_in *in =reinterpret_cast<struct qcom_stream_in *>(stream);

    qadev->hwif->closeInputStream(in->qcom_in);
    free(in);
}

2.5 \hardware\qcom\audio\alsa_sound\audio_hw_hal.cpp中

/** audio_hw_device implementation **/
static inline struct qcom_audio_device * to_ladev(struct audio_hw_device *dev)
{
    return reinterpret_cast<struct qcom_audio_device *>(dev);
}

第三步骤:ALSA-lib中录音的open的过程如下:

3.1 audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,audio_devices_t *pDevices,uint32_t *pSamplingRate,audio_format_t *pFormat,  audio_channel_mask_t *pChannelMask){ status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config, &inStream);}

3.2 \hardware\qcom\audio\alsa_sound\audio_hw_hal.cpp中static int qcom_adev_open(const hw_module_t* module, const char* name, hw_device_t** device){

qadev->device.open_input_stream = adev_open_input_stream;

qadev->hwif = createAudioHardware();

*device = &qadev->device.common;  }

3.3 \hardware\qcom\audio\alsa_sound\audio_hw_hal.cpp中static int adev_open_input_stream(struct audio_hw_device *dev, audio_io_handle_t handle, audio_devices_t devices, audio_config *config,audio_stream_in **stream_in){

in->qcom_in = qadev->hwif->openInputStream(devices, (int *)&config->format,  &config->channel_mask,&config->sample_rate, &status,(AudioSystem::audio_in_acoustics)0);

in->stream.read = in_read;

*stream_in = &in->stream;  }

3.4 \hardware\libhardware_legacy\include\hardware_legacy\AudioHardwareInterface.h 中virtual AudioStreamIn* openInputStream(){}

3.5 \hardware\qcom\audio\alsa_sound\AudioHardwareALSA.cpp  AudioStreamIn *AudioHardwareALSA::openInputStream(){

AudioStreamInALSA *in = 0;

 in = new AudioStreamInALSA(this, &(*it), acoustics);
 err = in->set(format, channels, sampleRate, devices);
}

 

说明1

extern "C" {

struct qcom_audio_module {
    struct audio_module module;
};

struct qcom_audio_device {
    struct audio_hw_device device;

    struct AudioHardwareInterface *hwif;
};

struct qcom_stream_out {
    struct audio_stream_out stream;

    AudioStreamOut *qcom_out;
};

struct qcom_stream_in {
    struct audio_stream_in stream;

    AudioStreamIn *qcom_in;
};
说明2:

status_t ALSAStreamOps::set(int      *format,                             uint32_t *channels,                             uint32_t *rate,                             uint32_t device){}

相关文章
相关标签/搜索