FreeBSD 系统结构手册
上一页 | 第15章 　声音子系统 | 下一页

# 15.4 接口

`pcm`核心与声音驱动程序之间的接口以术语 内核对象的叫法来定义。

声音驱动程序通常提供两种主要的接口： CHANNEL以及 MIXER或AC97。

AC97是一个很小的硬件访问（寄存器读/写） 接口，由驱动程序为带AC97编码解码器的硬件来实现。这种情况下，实际的 MIXER接口由`pcm`中共享的AC97代码提供。

## 15.4.1 CHANNEL接口

### 15.4.1.1 函数参数的通常注意事项

声音驱动程序通常用一个私有数据结构来描述他们的设备，驱动 程序所支持的播放和录音数据通道各有一个。

对于所有的CHANNEL接口函数，第一个参数是一个不透明的指针。

第二个参数是指向私有的通道数据结构的指针， `channel_init()`是个例外，它的指针指向私有 设备结构（并返回由`pcm`以后使用的通道指针）。

### 15.4.1.2 数据传输操作概览

对于声音数据传输，`pcm`核心与声音驱动 程序是通过一个由`struct snd_dbuf`描述的 共享内存区域进行通信的。

`struct snd_dbuf`是 `pcm`私有的，声音驱动程序通过调用访问者 函数（`sndbuf_getxxx()`）来获得感兴趣的值。

共享内存区域的大小等于 `sndbuf_getsize()`，并被分割为大小固定，且等于 `sndbuf_getblksz()`字节的很多块。

当播放时，常规的传输机制如下（将意思反过来就是录音）：

- `pcm`开始时填充缓冲区，然后以 参数PCMTRIG_START调用声音驱动程序的 `xxxchannel_trigger()` 。

- 声音驱动程序接着安排以 `sndbuf_getblksz()`字节大小为块，重复将 整个内存区域（`sndbuf_getbuf()`， `sndbuf_getsize()`）传输到设备。对于每个 传输块回调`pcm`函数 `chn_intr()`（这通常在中断时间发生）。

- `chn_intr()`安排将新数据拷贝到那些 数据已传输到设备（现在空闲）的区域，并对 `snd_dbuf`结构进行适当的更新。

### 15.4.1.3 channel_init

调用`xxxchannel_init()`来初始化每个播放 和录音通道。这个调用从声音驱动程序的连接例程中发起。（参看 探测和连接一节）。

              static void *
              xxxchannel_init(kobj_t obj, void *data,
                 struct snd_dbuf *b, struct pcm_channel *c, int dir)
              {
                  struct xxx_info *sc = data;
                  struct xxx_chinfo *ch;
                   ...
                  return ch;
               }

  `b`为通道 `struct snd_dbuf`的地址。它应当在 函数中通过调用`sndbuf_alloc()`来初始化。 所用的缓冲区大小通常是设备'典型'传输大小的一个较小的倍数。

  `c`为 `pcm`通道控制结构的指针。这是个不透明 指针。函数应当将它保存到局部通道结构中，在后面调用 `pcm`函数（例如： `chn_intr(c)`）时会使用它。

  `dir`指示通道方向 （`PCMDIR_PLAY`或 `PCMDIR_REC`）。

  函数应当返回一个指针，此指针指向用于控制此通道的私有 区域。它将作为参数被传递到对其他通道接口的调用。

### 15.4.1.4 channel_setformat

`xxxchannel_setformat()`应当按特定通道， 特定声音格式设置硬件。

              static int
              xxxchannel_setformat(kobj_t obj, void *data, u_int32_t format)
              {
                  struct xxx_chinfo *ch = data;
                   ...
                  return 0;
               }

  `format`为 `AFMT_XXX value`值之一 （`soundcard.h`）。

### 15.4.1.5 channel_setspeed

`xxxchannel_setspeed()`按指定的取样速度 设置通道硬件，并返回返回可能调整过的速度。

              static int
              xxxchannel_setspeed(kobj_t obj, void *data, u_int32_t speed)
              {
                  struct xxx_chinfo *ch = data;
                   ...
                  return speed;
               }

### 15.4.1.6 channel_setblocksize

`xxxchannel_setblocksize()`设置块大小， 这是`pcm`与声音驱动程序，以及声音驱动 程序与设备之间的传输单位的大小。传输期间，每次传输这样大小的 数据后，声音驱动程序都应当调用`pcm`的 `chn_intr()`。

大多数驱动程序只注意这儿的块大小，因为当实际传输开始时应该 使用这个值。

              static int
              xxxchannel_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
              {
                  struct xxx_chinfo *ch = data;
                    ...
                  return blocksize;
               }

  函数返回可能调整过的块大小。如果块大小真的变化了， 这种情况下应当调用`sndbuf_resize()`调整 缓冲区的大小。

### 15.4.1.7 channel_trigger

`xxxchannel_trigger()`由 `pcm`来控制驱动程序中的实际传输操作。

              static int
              xxxchannel_trigger(kobj_t obj, void *data, int go)
              {
                  struct xxx_chinfo *ch = data;
                   ...
                  return 0;
               }

  `go`定义当前调用的动作。可能的值为：

  - `PCMTRIG_START`：驱动程序应当 启动从/到通道缓冲区的数据传输。如果需要，应当通过 `sndbuf_getbuf()`和 `sndbuf_getsize()`检取缓冲区的 基地址和大小。

  - `PCMTRIG_EMLDMAWR` / `PCMTRIG_EMLDMARD`：告诉驱动程序， 输入或输出缓冲区可能已被更新过了。大多数驱动程序只是 忽略这些调用。

  - `PCMTRIG_STOP` / `PCMTRIG_ABORT`：驱动程序应当停止当前 的传输。

> 注意: 如果驱动程序使用ISA DMA，则应当在设备上执行动作前 调用`sndbuf_isadma()`，并处理DMA芯片一方的 事情。

### 15.4.1.8 channel_getptr

`xxxchannel_getptr()`返回传输缓冲区中 当前的缓冲。它通常由`chn_intr()`调用，而且 这也是为什么`pcm`知道它应当往哪儿传送 新数据。

### 15.4.1.9 channel_free

调用`xxxchannel_free()`来释放通道资源， 例如当驱动程序卸载时，并且如果通道数据结构是动态分配的，或者 如果不使用`sndbuf_alloc()`进行缓冲区分配， 则应当实现这个函数。

### 15.4.1.10 channel_getcaps

              struct pcmchan_caps *
              xxxchannel_getcaps(kobj_t obj, void *data)
              {
                  return &xxx_caps;
               }

  这个例程返回指向（通常静态定义的） `pcmchan_caps`结构的指针（在 `sound/pcm/channel.h`中定义）。这个结构 保存着最小和最大采样频率和被接受的声音格式。任何声音驱动 程序都可以作为一个范例。

### 15.4.1.11 更多函数

`channel_reset()`, `channel_resetdone()`和 `channel_notify()`用于特殊目的，未与权威人士 (Cameron Grant `<cg@FreeBSD.org>`)进行探讨之前不应当在驱动程序中实现它。

不赞成使用`channel_setdir()`.

## 15.4.2 MIXER接口

### 15.4.2.1 mixer_init

`xxxmixer_init()`初始化硬件，并告诉 `pcm`什么混音器设备可用来播放和录音。

              static int
              xxxmixer_init(struct snd_mixer *m)
              {
                  struct xxx_info   *sc = mix_getdevinfo(m);
                  u_int32_t v;

                  [初始化硬件]

                  [为播放混音器设置v中适当的位]
                  mix_setdevs(m, v);
                  [为录音混音器设置v中适当的位]
                  mix_setrecdevs(m, v)

                  return 0;
              }

  设置一个整数值中的位，并调用 `mix_setdevs()`和 `mix_setrecdevs()`来告诉 `pcm`存在什么设备。

混音器的位定义可以在`soundcard.h`中 找到。（`SOUND_MASK_XXX`值和 `SOUND_MIXER_XXX`移位）。

### 15.4.2.2 mixer_set

`xxxmixer_set()`为混音器设备设置音量级别 (level)。

              static int
              xxxmixer_set(struct snd_mixer *m, unsigned dev,
                               unsigned left, unsigned right)
              {
                  struct sc_info *sc = mix_getdevinfo(m);
                  [设置音量级别(level)]
                  return left | (right << 8);
              }

  设备被指定为 `SOUND_MIXER_XXX` 值

  在范围[0-100]之间指定音量值。零值应当让设备静音。

  由于硬件(音量)级别(level)可能不匹配输入比例，会出现 某些圆整，例程返回如上面所示的实际级别值（范围0-100内）。

### 15.4.2.3 mixer_setrecsrc

`xxxmixer_setrecsrc()`设定录音源设备。

              static int
              xxxmixer_setrecsrc(struct snd_mixer *m, u_int32_t src)
              {
                  struct xxx_info *sc = mix_getdevinfo(m);

                  [查看src中的非零位, 设置硬件]

                  [更新src反映实际动作]
                  return src;
               }

  期望的录音设备由一个位域指定.
  返回设置用来录音的实际设备。一些驱动程序只能设置一个 录音设备。如果出现错误，函数应当返回-1。

### 15.4.2.4 mixer_uninit, mixer_reinit

`xxxmixer_uninit()`应当确保不会发出任何 声音，并且如果可能则应当让混音器硬件断电。

`xxxmixer_reinit()`应当确保混音器硬件 加电，并且恢复所有不受`mixer_set()`或 `mixer_setrecsrc()`控制的设置。

## 15.4.3 AC97接口

AC97由带有AC97编码解码器的驱动程序实现。 它只有三个方法：

- `xxxac97_init()`返回找到的 ac97编码解码器的数目。

- `ac97_read()`与 `ac97_write()`读写指定的寄存器。

The AC97接口由 `pcm`中的AC97代码来执行高层操作。参看 `sound/pci/maestro3.c`或 `sound/pci/`下很多其他内容作为范例。

上一页 | 起始页 | 下一页
探测，连接等 | 上一级 | PC Card

本文档和其它文档可从这里下载：ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

如果对于FreeBSD有问题，请先阅读文档，如不能解决再联系<questions@FreeBSD.org>.
关于本文档的问题请发信联系 <doc@FreeBSD.org>.