1. 从文件开始

Linux设计哲学之,一切皆是文件。感觉上这个论点更多的是针对驱动来说的,从使用者的角度来说,大多的驱动程序的使用与操作文件的方式类似,也就是使用open,read,write,ioctl这些通用的文件操作接口来操作驱动。而对于驱动开发者来说,就需要按照文件的方式来组织驱动接口,以文件的形式来呈现驱动程序。以下我们来具体看看ALSA所呈现的形式。

在ubuntu上,显示/dev/snd下的内容:

/dev/snd$ ls -lh

total 0

drwxr-xr-x  2 root root       60 May  3 02:08 by-path

crw-rw----+ 1 root audio 116,  6 May  3 02:08 controlC0

crw-rw----+ 1 root audio 116,  5 May  3 02:08 midiC0D0

crw-rw----+ 1 root audio 116,  3 May  3 02:08 pcmC0D0c

crw-rw----+ 1 root audio 116,  2 May  3 02:10 pcmC0D0p

crw-rw----+ 1 root audio 116,  4 May  3 02:08 pcmC0D1p

crw-rw----+ 1 root audio 116,  1 May  3 02:08 seq

crw-rw----+ 1 root audio 116, 33 May  3 02:08 timer

以上是在我的虚拟机上显示的内容,如果你查看Android上的内容,内容会更多一些,因为会有一些支持专门用途的声卡,但原理是一致的,这里按照ubuntu的内容来展开相关的内容。

可以看到文件名有一定的格式,其实也就代表了具体的用途。比如pcmC0D1p,这里的pcm表示该文件用来处理音频;C可以理解成card的缩写;一个系统可以有多个card,所以C后的数字表示的是顺序,这里的0表示是第一个card;D可以理解为device的缩写;D后的1表示是第二个设备;p 表示的播放(如果是c表示的就是录制)。pcm设备的用途主要是播放音频,而control的作用就是对音频做必要的控制,比如设定音量等。

学习音频开发,个人感觉一个比较好的起始点是学习tinyalsa,代码量不大,但足够我们熟悉一些基本概念。下面也通过tinyalsa来熟悉一下音频的用法,可以在github上找到tinyalsa的源码。

snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,

             flags & PCM_IN ? 'c' : 'p');

fd = open(fn, O_RDWR | O_NONBLOCK);

以上是打开文件的方法,和普通的文件没什么差别,都是通过open来打开文件。

static int pcm_rw_transfer(struct pcm *pcm, void *data, unsigned int frames)
{
    int is_playback;

    struct snd_xferi transfer;
    int res;

    is_playback = !(pcm->flags & PCM_IN);

    transfer.buf = data;
    transfer.frames = frames;
    transfer.result = 0;

    res = pcm->ops->ioctl(pcm->data, is_playback
                          ? SNDRV_PCM_IOCTL_WRITEI_FRAMES
                          : SNDRV_PCM_IOCTL_READI_FRAMES, &transfer);

    return res == 0 ? (int) transfer.result : -1;
}

以上是写文件的方式,可以看到传入的数据data,将被保存到snd_xferi 结构中,然后再通过ioctl传到内核。

static int pcm_hw_ioctl(void *data, unsigned int cmd, ...)

{

    struct pcm_hw_data *hw_data = data;

    va_list ap;

    void *arg;



    va_start(ap, cmd);

    arg = va_arg(ap, void *);

    va_end(ap);



    return ioctl(hw_data->fd, cmd, arg);

}

再看看pcm->ops->ioctl的具体实现,其实就是函数pcm_hw_ioctl。最终还是通过系统函数ioctl将命令(写数据使用SNDRV_PCM_IOCTL_WRITEI_FRAMES,读使用 SNDRV_PCM_IOCTL_READI_FRAMES)和数据传入到内核。

顺便提一下,其实默认情况下,写数据基本是使用mmap的方式来完成的。mmap的使用要稍微复杂一些,在讲到内核数据同步的时候,会专门讲解这部分的内容。

从以上的内容可以看到,音频设备是以文件的形式存在于系统中,使用方式也同文件类似。所以后续的内容会集中于如何生成这样的文件,以及这些文件是如何响应客户端的请求来展开。

你可能感兴趣的:(alsa驱动解析,linux内核,音频,驱动开发)