Audio Development on OpenWRT: A Comprehensive Guide

Audio Development on OpenWRT: A Comprehensive Guide

Audio Development on OpenWRT: A Comprehensive Guide

1. Linux Audio Architecture Diagram

The quality of audio and video directly affects product experience.

Audio Development on OpenWRT: A Comprehensive Guide

2. Explanation of Audio Architecture Layers

openWRT adopts the ALSA layer architecture as follows:

Application: The upper layer application mainly calls the interfaces in alsa-lib to implement business logic. Use aplay, arecord, amixer, speaker-test in alsa-util for related testing. HAL Layer: Porting alsa-lib and alsa-utils. In openwrt, feeds come with alsa-lib 1.1.01 [Introduction] kernel: Adaptation according to the ALSA driver layer. [Introduction]

3. Application Layer

Applications in openwrt require custom startup scripts placed in the /etc/init.d directory.

4. Definition of Startup Script Order

05 defconfig // Load default parameters 10 boot // Start 39 usb // Load usbfs 40 network // Set network card parameters 45 firewall // Firewall 50 dropbear // sshd server 50 cron // … 50 telnet // If the root password is not changed, start telnet server 60 dnsmasq // DHCP and DNS server 95 done // … 96 led // Indicator 97 watchdog // … 99 sysctl // Finally, make necessary kernel parameter adjustments

5. Writing Startup Scripts

Customize according to the official Wiki script.

6. HAL Layer

This layer does not require too many changes, just configure feeds to select the version you need. For specific interface queries, you can refer to alsa-project.

7. Kernel

Analyze according to the ALSA driver.

machine
/* SoC machine */
struct snd_soc_card {
      char *name;
      ...
      int (*probe)(struct platform_device *pdev);
      int (*remove)(struct platform_device *pdev);

      /* the pre and post PM functions are used to do any PM work before and
       * after the codec and DAIs do any PM work. */
      int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
      int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
      int (*resume_pre)(struct platform_device *pdev);
      int (*resume_post)(struct platform_device *pdev);
      ...

      /* CPU <--> Codec DAI links  */
      struct snd_soc_dai_link *dai_link;
      int num_links;
      ...
};

probe/remove are optional, mainly to detect the machine. suspend/resume will be triggered correspondingly on codec, DAIs, and DMA suspend resume, which is also optional for machine DAI port configuration, configuring the corresponding structure.

/*  digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link corgi_dai = {
    .name = "WM8731",
    .stream_name = "WM8731",
    .cpu_dai_name = "pxa-is2-dai",
    .codec_dai_name = "wm8731-hifi",
    .platform_name = "pxa-pcm-audio",
    .codec_name = "wm8713-codec.0-001a",
    .init = corgi_wm8731_init,
    .ops = &corgi_ops,
};

/* audio machine driver */
static struct snd_soc_card snd_soc_corgi = {
      .name = "Corgi",
      .dai_link = &corgi_dai,
      .num_links = 1,
};

platform DMA driver, SoC DAI driver

/* SoC audio ops */ 
struct snd_soc_ops { 
        int (*startup)(struct snd_pcm_substream *);
        void (*shutdown)(struct snd_pcm_substream *);
        int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *); int (*hw_free)(struct snd_pcm_substream *);
        int (*prepare)(struct snd_pcm_substream *);
        int (*trigger)(struct snd_pcm_substream *, int); 
};
// The platform driver is associated through the DMA interface
struct snd_soc_platform_driver { 
        char *name;
        int (*probe)(struct platform_device *pdev);
        int (*remove)(struct platform_device *pdev);
        int (*suspend)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai); 
        int (*resume)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai);
    /* pcm creation and destruction */
        int (*pcm_new)(struct snd_card *, struct snd_soc_codec_dai *, struct snd_pcm *); 
        void (*pcm_free)(struct snd_pcm *);
    /*
     * For platform caused delay reporting.
     * Optional.
     */ 
     snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *, struct snd_soc_dai *);
    /* platform stream ops */
        struct snd_pcm_ops *pcm_ops;
};

Each codec driver must have the following functions.

Codec DAI and PCM configuration using I2C or SPI control IO mixer audio control codec audio operations DAPM description DAPM event handling optional DAC mute handling // DAI PCM configuration

static struct snd_soc_dai_ops wm8731_dai_ops = {
    .prepare    = wm8731_pcm_prepare,
    .hw_params  = wm8731_hw_params,
    .shutdown   = wm8731_shutdown,
    .digital_mute   = wm8731_mute,
    .set_sysclk = wm8731_set_dai_sysclk,
    .set_fmt    = wm8731_set_dai_fmt, 
};
struct snd_soc_dai_driver wm8731_dai = { .name = "wm8731-hifi",
    .playback = { .stream_name = "Playback",
        .channels_min = 1,
        .channels_max = 2,
        .rates = WM8731_RATES,
        .formats = WM8731_FORMATS,
     },
    .capture = { .stream_name = "Capture",
        .channels_min = 1,
        .channels_max = 2,
        .rates = WM8731_RATES,
        .formats = WM8731_FORMATS,}, .ops = &wm8731_dai_ops,
    .symmetric_rates = 1, 
}; 

// i2c control read/write

i2c_write
i2c_read
// mixer audio control

All codec mixer and control are defined in soc.h

#define SOC_SINGLE(xname, reg, shift, mask, invert) defines a single controller: – xname control name e.g. “Playback Volume” reg = codec register shift = control bit(s) offset in register mask = control bit size(s) e.g. mask of 7 = 3 bits invert = whether the control is inverted

Other macros include: – #define SOC_DOUBLE (xname ,reg ,shift_left ,shift_right ,mask ,invert ) stereo control #define SOC_DOUBLE_R (xname ,reg_left ,reg_right ,shift ,mask ,invert ) stereo control across 2 registers #define SOC_ENUM_SINGLE (xreg ,xshift ,xmask ,xtexts ) xreg = register XSHIFT = control bit (small number) offset in register xmask = control bit (small number) size xtexts = pointer to the string array describing each setting

//audio control operation

/* SoC audio ops */ 
struct snd_soc_ops { 
    int (*startup)(struct snd_pcm_substream *);
    void (*shutdown)(struct snd_pcm_substream *);
    int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *); int (*hw_free)(struct snd_pcm_substream *);
    int (*prepare)(struct snd_pcm_substream *); 
};

//DAPM//DAPM handler reference

8. Audio Applications on OpenWRT

The previous section introduced the audio framework. In openWRT, the main audio service is VOIP through the following modules:

Audio Development on OpenWRT: A Comprehensive Guide

9. Debugging Techniques

Application layer UDP captures files under UDP G711A corresponding buffer.

// Application layer writes buffer to file code block
static struct file *file_g711a= NULL;
int writelen = 0;
if (NULL == fp)
{
    file_g711a= file_open("/test1", O_RDWR | O_CREAT, 0777);
     if(file_g711a == NULL){
          printf("g711a file  = NULL");
           }else{
           printf("g711a file open ok");
         }   
}
if(file_g711a != NULL){
     writelen = fwrite(g711a_buffer, 1, sizeof(g711a_buffer), file_g711a);
}
printk("writelen:%d\n",writelen);

10. Capture Files on the Link with ALSA

After converting to PCM, write the corresponding buffer to the file.

// Application layer writes buffer to file code block
static struct file *file_pcm= NULL;
int writelen = 0;
if (NULL == fp)
{
    file_pcm= file_open("/test.pcm", O_RDWR | O_CREAT, 0777);
     if(file_pcm == NULL){
          printf("pcm file  = NULL");
           }else{
           printf("pcm file open ok");
         }   
}
if(file_pcm != NULL){
     writelen = fwrite(pcm_buffer, 1, sizeof(pcm_buffer), file_pcm);
}
printk("writelen:%d\n",writelen);

On the link, the kernel captures files in the function of PCM and DMA communication, writing the buffer to the file.

// kernel writes buffer to file code block
static struct file *fp = NULL;
mm_segment_t fs;
static loff_t pos = 0;
printk("hello enter\n");
if (NULL == fp)
{
    fp = filp_open("/test.pcm", O_RDWR | O_CREAT, 0777);
    if (IS_ERR(fp))
    {
        printk("create file error\n");
        return -1;
    }
}
fs = get_fs();
set_fs(KERNEL_DS);
int writelen = vfs_write(fp, buf, size*4, &pos);
pos += size*4;
printk("writelen:%d pos:%d\n",writelen, pos);
Audio Development on OpenWRT: A Comprehensive Guide

Welcome to follow our WeChat public account – Embedded Linux

If you find it helpful, please help to share and like. Every support you give, I will remember in my heart.

Audio Development on OpenWRT: A Comprehensive Guide

Leave a Comment