2023年7月3日星期一

[Linux Audio] 使用ALSA API 實作amixer command: amixer -c0 set i2s_clock on,Bug 修正

 之前有寫了一篇 ALSA API 實作amixer command:  amixer -c0 set i2s_clock on

 後來發現,有兩個 Bug 需要被修正,分別如下:

 1. 第一次如果使用 i2s_clock off,會造成 kernel 端出現 core_dump 錯誤,導致執行時間會變很長 (原本大概只需要 20ms,結果時間拉長到 200ms)

 2. 發現這個上層的API程式,呼叫Linux Kernel 層,去triger I2S,出現 non-blocking的現象(application 呼叫底層程式,但沒有等待底層做完,就直接繼續執行application層的事情)


因此加入底下一些程式,來解決這兩個問題 (用不同顏色標出)

#include <stdio.h>

#include <alsa/asoundlib.h>


int set_mixer_onoff(const char *control_name,
                            int option)
{
    const char *device = "hw:0";  // 音訊設備的名稱或識別符號
    //const char *control_name = "i2s_clock";  // 控制元素的名稱, 由API參數帶入

    snd_ctl_t *handle;
    snd_ctl_elem_id_t *elem_id;
    snd_ctl_elem_value_t *elem_value;
    int err;
    int ori_value;
    int i2s_is_set = 0;

    // 打開音訊設備的控制界面
    err = snd_ctl_open(&handle, device, 0);
    if (err < 0) {
        printf(":Cannot open device interface: %s\n", snd_strerror(err));  //無法打開音訊設備的控制界面
        return err;
    }


    // 分配並初始化 snd_ctl_elem_id_t 結構
    snd_ctl_elem_id_alloca(&elem_id);
    snd_ctl_elem_id_set_name(elem_id, control_name);


    // 獲取控制元素的索引
    snd_ctl_elem_id_set_interface(elem_id, SND_CTL_ELEM_IFACE_MIXER);

    // 獲取控制元素的數值
    snd_ctl_elem_value_alloca(&elem_value);
    snd_ctl_elem_value_set_id(elem_value, elem_id);
    snd_ctl_elem_value_set_boolean(elem_value, 0, option);  // 設置boolean值,由API參數option帶入

     

    // This is for fixing the core_dump issue
    // 先取得之前狀態,如果跟這次要設定的狀態相同,就不需要再做,直接 return.
    err = snd_ctl_elem_read(handle, elem_value);

    if (err < 0) {

            printf("Cannot read value: %s\n", snd_strerror(err));

            snd_ctl_close(handle);

            return err;

    }


    ori_value = snd_ctl_elem_value_get_boolean(elem_value, 0);

    if( option == ori_value )

    {

        printf("No need to Set, option equal to ori_value\n");

        snd_ctl_close(handle);

        return 0;

    }

    else

        snd_ctl_elem_value_set_boolean(elem_value, 0, option); //設定boolean值,由API參數option帶入

    /////////////////////////////////////////////


    // 設置控制元素的數值
    err = snd_ctl_elem_write(handle, elem_value);
    if (err < 0) {
        printf("Cannot set value: %s\n", snd_strerror(err));  //無法設置控制元素的數值
        snd_ctl_close(handle);
        return err;

    }

    // Poll the control value until it reaches the desired state

    // For fix the non-blocking issue

    while (!i2s_is_set) {

        // Sleep for a short period to avoid busy-waiting

        usleep(5000); // Sleep for 5 milliseconds


        // Read the control value

        err = snd_ctl_elem_read(handle, elem_value);

        if (err < 0) {

            printf("Cannot read value 2th: %s\n", snd_strerror(err));

            snd_ctl_close(handle);

            return err;

        }


        // Check if the control value is equal to the desired state

        int cur_value = snd_ctl_elem_value_get_boolean(elem_value, 0);

        if (cur_value == option) {

            i2s_is_set = 1;

        }

    }

    // 關閉音訊設備的控制界面
    snd_ctl_close(handle);
    printf("Setting %s %d success\n", control_name, option);  //已成功設置 i2s_clock 為 on
    return 0;

}


沒有留言:

發佈留言

使用 lsblk 印出 emmc 每個 partition的"名字"與"size"

使用以下的command可以印出 eMMC的 partition資訊 lsblk --bytes --output name,partlabel,size   參數說明 --bytes: partition的大小,以byte的方式輸出 --output: 後面可以指定要輸出的內容...