Aros/Developer/AHIDriversDev
AHI是一个旨在利用硬件播放准备好的声音的系统。
开源驱动程序 - M Blom 和 G Steger (SBLive),M Schulz (ac97),D Wentzler (HDAudio, Envy24HT 和 CMI8738),R Vumbaca (SB128) 和 Krzysztof Smiechowicz (Alsa 驱动程序)
一个典型的设备驱动程序通常处于三种状态之一...
- 非活动 - 需要安装,电源启动或启用
- 忙碌 - 卸载,断电或禁用,释放设备
- 已完成 - 读取或写入请求,锁定设备
AHI 读取一个“modefile”,该文件描述了声音驱动程序的基本功能,例如单声道、立体声、7.1 环绕声等。驱动程序打开并开始寻找匹配的声卡。
对于音频驱动程序来说,是的 - 会探测 pci。但在大多数情况下,只会调用驻留结构定义的初始化例程。如果驱动程序无法找到合适的硬件,它会退出初始化并被卸载。在 AHI 配置中,你永远不会看到无法工作的驱动程序。
AHI 从来不会直接与音频芯片组对话,而是通过包含诸如需要多少通道、每个样本多少位以及样本如何到达等详细信息的缓冲区进行通信。它为与声卡通信建立一个缓冲区,并为获取声卡的回复建立另一个缓冲区。
持续馈送的典型方法称为双缓冲。当音频芯片正在播放第一个缓冲区时,AHI 会将下一个声音填充到第二个缓冲区中。一旦回放例程移动到第二个缓冲区,第一个缓冲区将被填充新的内容,依此类推。显然,如果音频芯片组也支持两个缓冲区,这将会有所帮助。
8 位、16 位或 32 位单声道或立体声,或 32 位 7.1 声道由硬件播放。AHI 会将来自应用程序的多个通道混合在一起。一个鲜为人知的细节:所有标注为“HiFi”的模式都在 AHI 和驱动程序之间使用 32 位样本,而不管进入 AHI 的样本的分辨率如何。
所有公开给用户(或驱动程序)的可变数量都为 Fixed 类型,这是一种有符号的 32 位定点值,其“定点”位于高 16 位和低 16 位之间。值 1 将存储为 0x00010000,值 0.5 将存储为 0x00008000,0.25 将存储为 0x00004000 等等。
与浮点数变量相比,这使得计算速度更快得多。
声卡在完成每个音频缓冲区时,预期会产生一个硬件中断。假设使用两个(或更多)缓冲区,这会触发刚完成的缓冲区的重新加载,以便声卡可以在两个缓冲区之间自由地来回运行,永远不会用完新的声音。(双缓冲)。
驱动程序的编写要符合 AHI 的预期,而不是反过来。AHI 已经很好地为我们服务了,并且已成为所有受 Amiga 启发的平台的标准音频设备,但随着我们转向更现代的硬件,我们可能会受到 AHI 设计限制的限制。
USB 音频设备在构建一个可以使用 DeviceIO 命令而不是由硬件中断驱动的 AHI 驱动程序方面遇到了一些麻烦。AHI 用于使访问声卡“容易”的一些技术现在使它变得比应该的更难。USB 音频设备的工作原理与任何其他设备一样。它处理一个写请求队列,每个请求都包含一些要发送到声卡硬件的数据。不涉及中断,只是 Exec 自 Workbench 1.0 以来一直在执行的相同的 DeviceIO 技术。更改为 USB 几乎就像更改 OpenDevice() 调用一样简单。。但我们没有那么幸运。AHI 将会有一个 USB 驱动程序,但“旧方法”可能会使事情比最初预期的更困难。
AHI 为每个程序提供音量控制。
声音以对数方式被耳朵识别,所以 10db 被认为大约是两倍响度,注意这与两倍功率不同,当然两倍功率是 6db。
但声音本身只是一个压力波,既不对数也不线性,它就是这样。
样本通常以 LPCM(线性脉冲编码调制)形式存储,因此绝对可以使用简单的加法将它们加在一起。
至于 AHI 在混合多个样本时降低音量,我认为有几种不同的模式,安全、安全动态、全音量和 -3 -6
假设你拥有 24 个通道要混合。在没有其他样本值的知识的情况下,为了防止削波(这对扬声器或你的耳朵来说非常糟糕,如果你戴着耳机的话),你必须将每个样本的音量降低到 1/24,这(我认为)类似于安全模式。
这通过降低样本分辨率来丢弃大量质量。可能不会有任何溢出或溢出很少,但它仍然会修改输出音量。当然,即使在现实世界中,许多声音一起也会导致饱和。诀窍是将它们组合在一起并对此进行补偿,但仍然保留原始样本音量。自然,较响亮的声音会压倒较柔和的声音,因此这可能可以利用。但我现在经常看到 A+B-(AB)。
因此,如果你有可变数量的样本,你可以仅根据当前使用的通道数量来降低音量。所以,只有 6 个通道在使用,音量降低到 1/6,清晰度损失更少,但每个子声音的表观音量随着更多声音而降低的虚假效果。(安全,动态)
或者,你可以让计算机退出循环,并假设人类(或人类运行的软件,例如进行自己混合的游戏,或 AudioEvolution 等)会做出明智的判断,不衰减,不损失质量,但如果出错,你会炸毁你的耳朵。(全音量模式)。因为 90% 的时间我只播放单一的立体声流,所以我总是使用这个选项。
-3 和 -6 db 模式只是在全音量方程中添加了少许余量。
只重新编译 HDAudio 驱动程序的简单方法?在 AROS 目录中运行 'make AHI-quick' 或 'make workbench-devs-AHI-quick' 应该可以做到。'make AHI' 还会重建 AHI 的依赖项
即使进行了更改,也只显示“MMAKE: Nothing to do for xxxx”。AHI 的构建是通过调用 configure 并调用 make (%build_with_configure mmake 宏) 完成的。因此,mmake 没有看到依赖项;它只使用一些时间戳文件。你可以删除 bin/__ARCH__-__CPU__/gen/workbench/devs/AHI,然后调用 'make AHI-quick'。我希望这应该可以解决问题。
重建 AHI 驱动程序很棘手。我使用类似这样的命令
rm bin/pc-i386/gen/workbench/devs/AHI/.installed ; make workbench-devs-AHI-quick && cp bin/pc-i386/AROS/Devs/AHI/hdaudio.audio ...
AHI 源驱动程序包含在 main source.bz2 中,而不是 contrib 中,每个驱动程序的源代码在 workbench/devs/AHI/Drivers 中都有自己的抽屉。
回放只是简单地复制到缓冲区,而录制根本不会对缓冲区进行任何操作。缓冲区大小由 AHI 根据采样率和大小设置,以便中断速率为 50 Hz。如果我记得正确,对于 44100/16 位,它们通常约为 8KB。大小不应更改。
如果硬件不支持 32 位样本,你应该在 AUDIOMODE 中重新启用 HiFi,以便 AHI 以“HiFi”质量进行混合。驱动程序已经在 HiFi 模式使用时处理跳过上部。
- MODEID
30 FM 801 Benjamin Vernoux <[email protected]> 31 ???/Mediator Pawel Stypula <> 32 FM 801/AOS4 Davy Wentzler <> +33 OSS/AROS Martin Blom <> 34 SB128/AOS4 Ross Vumbaca <> 35 CMI8738/AOS4 Davy Wentzler <> 36 ICE1724/AOS4 Davy Wentzler <> 37 ICE1712/AOS4 Davy Wentzler <> 38 Trid. 4DWAVE-DX Marcus Comstedt +39 ac97/AROS Michal Schulz <> +3E HDAudio/AROS Davy Wentzler <> +42 Alsa/AROS Krzysztof Smiechowicz <> 200 via-ac97/AROS Davy Wentzler <>
name.s name-init.c name-main.c name-accel.c name-interrupt.c DriverData.h pci.c
Void 抽屉包含一个虚拟的仅回放驱动程序
FORM_START AHIM CHUNK_START AUDN .asciz "ac97" CHUNK_END CHUNK_START AUDM 1: LONG2 AHIDB_AudioID, 0x00390004 LONG2 AHIDB_Volume, TRUE LONG2 AHIDB_Panning, TRUE LONG2 AHIDB_Stereo, TRUE LONG2 AHIDB_HiFi, FALSE LONG2 AHIDB_MultTable,FALSE LONG2 AHIDB_Name, 2f-1b LONG TAG_DONE 2: .asciz "ac97:16 bit stereo++" CHUNK_END FORM_END .balign 4,0 .END
#include <exec/memory.h> #include <proto/expansion.h> #include <proto/dos.h> #include <proto/exec.h> #include <clib/alib_protos.h> #ifdef __AROS__ #include <aros/debug.h> struct ExecBase* SysBase = NULL; struct DosLibrary* DOSBase; #endif #include <stdlib.h> #include "library.h" #include "version.h" #include "pci_wrapper.h" #include "misc.h" struct DriverBase* AHIsubBase; struct VendorDevice { UWORD vendor; UWORD device; }; struct VendorDevice *vendor_device_list = NULL; static int vendor_device_list_size = 0; static void parse_config_file(); static int hex_char_to_int(char c); #define MAX_DEVICE_VENDORS 512 /****************************************************************************** ** Custom driver init ********************************************************* ******************************************************************************/ BOOL DriverInit(struct DriverBase* ahisubbase) { bug("exit init\n"); return TRUE; } /****************************************************************************** ** Custom driver clean-up ***************************************************** ******************************************************************************/ VOID DriverCleanup(struct DriverBase* AHIsubBase) { }
阅读更多 这里
#include <config.h> #include <devices/ahi.h> #include <exec/memory.h> #include <libraries/ahi_sub.h> #include <math.h> #include <proto/ahi_sub.h> #include <proto/exec.h> #include <proto/dos.h> #include <proto/utility.h> #ifdef __AROS__ #include <aros/debug.h> #define DebugPrintF bug #endif #include <string.h> #include "library.h" #include "regs.h" #include "misc.h" #include "pci_wrapper.h" extern int z, timer; /****************************************************************************** ** Globals ******************************************************************** ******************************************************************************/ #define uint32 unsigned int /****************************************************************************** ** AHIsub_AllocAudio ********************************************************** ******************************************************************************/ ULONG _AHIsub_AllocAudio(struct TagItem* taglist, struct AHIAudioCtrlDrv* AudioCtrl, struct DriverBase* AHIsubBase) { } /****************************************************************************** ** AHIsub_FreeAudio *********************************************************** ******************************************************************************/ void _AHIsub_FreeAudio(struct AHIAudioCtrlDrv* AudioCtrl, struct DriverBase* AHIsubBase) { } /****************************************************************************** ** AHIsub_Disable ************************************************************* ******************************************************************************/ void _AHIsub_Disable(struct AHIAudioCtrlDrv* AudioCtrl, struct DriverBase* AHIsubBase) { // V6 drivers do not have to preserve all registers Disable(); } /****************************************************************************** ** AHIsub_Enable ************************************************************** ******************************************************************************/ void _AHIsub_Enable(struct AHIAudioCtrlDrv* AudioCtrl, struct DriverBase* AHIsubBase) { // V6 drivers do not have to preserve all registers Enable(); } /****************************************************************************** ** AHIsub_Start *************************************************************** ******************************************************************************/ ULONG _AHIsub_Start(ULONG flags, struct AHIAudioCtrlDrv* AudioCtrl, struct DriverBase* AHIsubBase) { return AHIE_OK; } /****************************************************************************** ** AHIsub_Update ************************************************************** ******************************************************************************/ void _AHIsub_Update(ULONG flags, struct AHIAudioCtrlDrv* AudioCtrl, struct DriverBase* AHIsubBase) { } /****************************************************************************** ** AHIsub_Stop **************************************************************** ******************************************************************************/ void _AHIsub_Stop(ULONG flags, struct AHIAudioCtrlDrv* AudioCtrl, struct DriverBase* AHIsubBase) { } /****************************************************************************** ** AHIsub_GetAttr ************************************************************* ******************************************************************************/ LONG _AHIsub_GetAttr(ULONG attribute, LONG argument, LONG def, struct TagItem* taglist, struct AHIAudioCtrlDrv* AudioCtrl, struct DriverBase* AHIsubBase) { } /****************************************************************************** ** AHIsub_HardwareControl ***************************************************** ******************************************************************************/ ULONG _AHIsub_HardwareControl(ULONG attribute, LONG argument, struct AHIAudioCtrlDrv* AudioCtrl, struct DriverBase* AHIsubBase) { }
#include <config.h> #include <devices/ahi.h> #include <libraries/ahi_sub.h> #include "library.h" /****************************************************************************** ** AHIsub_SetVol ************************************************************** ******************************************************************************/ ULONG _AHIsub_SetVol(UWORD channel, Fixed volume, sposition pan, struct AHIAudioCtrlDrv* AudioCtrl, ULONG flags, struct DriverBase* AHIsubBase) { return AHIS_UNKNOWN; } /****************************************************************************** ** AHIsub_SetFreq ************************************************************* ******************************************************************************/ ULONG _AHIsub_SetFreq(UWORD channel, ULONG freq, struct AHIAudioCtrlDrv* AudioCtrl, ULONG flags, struct DriverBase* AHIsubBase) { return AHIS_UNKNOWN; } /****************************************************************************** ** AHIsub_SetSound ************************************************************ ******************************************************************************/ ULONG _AHIsub_SetSound(UWORD channel, UWORD sound, ULONG offset, LONG length, struct AHIAudioCtrlDrv* AudioCtrl, ULONG flags, struct DriverBase* AHIsubBase) { return AHIS_UNKNOWN; } /****************************************************************************** ** AHIsub_SetEffect *********************************************************** ******************************************************************************/ ULONG _AHIsub_SetEffect(APTR effect, struct AHIAudioCtrlDrv* AudioCtrl, struct DriverBase* AHIsubBase) { return AHIS_UNKNOWN; } /****************************************************************************** ** AHIsub_LoadSound *********************************************************** ******************************************************************************/ ULONG _AHIsub_LoadSound(UWORD sound, ULONG type, APTR info, struct AHIAudioCtrlDrv* AudioCtrl, struct DriverBase* AHIsubBase) { return AHIS_UNKNOWN; } /****************************************************************************** ** AHIsub_UnloadSound ********************************************************* ******************************************************************************/ ULONG _AHIsub_UnloadSound(UWORD sound, struct AHIAudioCtrlDrv* AudioCtrl, struct DriverBase* AHIsubBase) { return AHIS_UNKNOWN; }
#include <config.h> #include <proto/expansion.h> #include <libraries/ahi_sub.h> #include <proto/exec.h> #include <stddef.h> #include "library.h" #include "regs.h" #include "interrupt.h" #include "misc.h" #include "pci_wrapper.h" #ifdef __AROS__ #include <aros/debug.h> #endif #define min(a,b) ((a)<(b)?(a):(b)) int z = 0; ULONG timer = 0; // for demo/test #define TIME_LIMIT 150 // 150 irq's /****************************************************************************** ** Hardware interrupt handler ************************************************* ******************************************************************************/ #ifdef __AMIGAOS4__ ULONG CardInterrupt( struct ExceptionContext *pContext, struct ExecBase *SysBase, struct HDAudioChip* card ) #else ULONG CardInterrupt( struct HDAudioChip* card ) #endif { struct AHIAudioCtrlDrv* AudioCtrl = card->audioctrl; struct DriverBase* AHIsubBase = (struct DriverBase*) card->ahisubbase; struct PCIDevice *dev = (struct PCIDevice *) card->pci_dev; ULONG intreq, status; LONG handled = 0; UBYTE rirb_status; int i; intreq = pci_inl(HD_INTSTS, card); if (intreq & HD_INTCTL_GLOBAL) { if (intreq & 0x3fffffff) // stream interrupt { ULONG position; BOOL playback = FALSE; BOOL recording = FALSE; //bug("Stream irq\n"); for (i = 0; i < card->nr_of_streams; i++) { if (intreq & (1 << card->streams[i].index)) { // acknowledge stream interrupt pci_outb(0x1C, card->streams[i].sd_reg_offset + HD_SD_OFFSET_STATUS, card); if (i < card->nr_of_input_streams) { recording = TRUE; } else { playback = TRUE; } } } pci_outb(0xFF, HD_INTSTS, card); z++; #ifdef TIME_LIMITED timer++; if (timer > TIME_LIMIT) // stop playback { outb_clearbits(HD_SD_CONTROL_STREAM_RUN, card->streams[card->nr_of_input_streams].sd_reg_offset + HD_SD_OFFSET_CONTROL, card); } #endif //bug("SIRQ\n"); if (playback) { // bug("PB\n"); position = pci_inl(card->streams[card->nr_of_input_streams].sd_reg_offset + HD_SD_OFFSET_LINKPOS, card); if (card->flip == 1) //position <= card->current_bytesize + 64) { if (card->flip == 0) { bug("Lost IRQ!\n"); } card->flip = 0; card->current_buffer = card->playback_buffer1; } else { if (card->flip == 1) { bug("Lost IRQ!\n"); } card->flip = 1; card->current_buffer = card->playback_buffer2; } Cause(&card->playback_interrupt); } if (recording) { position = pci_inl(card->streams[0].sd_reg_offset + HD_SD_OFFSET_LINKPOS, card); if (card->recflip == 1) //position <= card->current_record_bytesize + 64) { if (card->recflip == 0) { bug("Lost rec IRQ!\n"); } card->recflip = 0; card->current_record_buffer = card->record_buffer1; } else { if (card->recflip == 1) { bug("Lost rec IRQ!\n"); } card->recflip = 1; card->current_record_buffer = card->record_buffer2; } Cause(&card->record_interrupt); } } if (intreq & HD_INTCTL_CIE) { //bug("CIE\n"); pci_outb(0x4, HD_INTSTS + 3, card); // only byte access allowed // if (card->is_playing) // bug("CIE irq! rirb is %x, STATESTS = %x\n", pci_inb(HD_RIRBSTS, card), pci_inw(HD_STATESTS, card)); // check for RIRB status rirb_status = pci_inb(HD_RIRBSTS, card); if (rirb_status & 0x5) { if (rirb_status & 0x4) // RIRBOIS { // bug("RIRB overrun!\n"); } if (rirb_status & 0x1) // RINTFL { card->rirb_irq++; /*if (card->rirb_irq > 1) { bug("IRQ: rirb_irq = %d\n", card->rirb_irq); }*/ //bug("RIRB IRQ!\n"); } pci_outb(0x5, HD_RIRBSTS, card); } } handled = 1; } return handled; } /****************************************************************************** ** Playback interrupt handler ************************************************* ******************************************************************************/ #ifdef __AMIGAOS4__ void PlaybackInterrupt( struct ExceptionContext *pContext, struct ExecBase *SysBase, struct HDAudioChip* card ) #else void PlaybackInterrupt( struct HDAudioChip* card ) #endif { struct AHIAudioCtrlDrv* AudioCtrl = card->audioctrl; struct DriverBase* AHIsubBase = (struct DriverBase*) card->ahisubbase; if (card->mix_buffer != NULL && card->current_buffer != NULL && card->is_playing) { BOOL skip_mix; WORD* src; int i; LONG* srclong, *dstlong, left, right; int frames = card->current_frames; skip_mix = CallHookPkt(AudioCtrl->ahiac_PreTimerFunc, (Object*) AudioCtrl, 0); CallHookPkt(AudioCtrl->ahiac_PlayerFunc, (Object*) AudioCtrl, NULL); if (! skip_mix) { CallHookPkt(AudioCtrl->ahiac_MixerFunc, (Object*) AudioCtrl, card->mix_buffer); } /* Now translate and transfer to the DMA buffer */ srclong = (LONG*) card->mix_buffer; dstlong = (LONG*) card->current_buffer; i = frames; if (AudioCtrl->ahiac_Flags & AHIACF_HIFI) { while(i > 0) { *dstlong++ = *srclong++; *dstlong++ = *srclong++; --i; } } else { while(i > 0) { *dstlong++ = (*srclong & 0xFF00) >> 16; srclong++; // tbd *dstlong++ = (*srclong & 0xFF000000) >> 16; srclong++; --i; } } CallHookPkt(AudioCtrl->ahiac_PostTimerFunc, (Object*) AudioCtrl, 0); } } /****************************************************************************** ** Record interrupt handler *************************************************** ******************************************************************************/ #ifdef __AMIGAOS4__ void RecordInterrupt( struct ExceptionContext *pContext, struct ExecBase *SysBase, struct HDAudioChip* card ) #else void RecordInterrupt( struct HDAudioChip* card ) #endif { struct AHIAudioCtrlDrv* AudioCtrl = card->audioctrl; struct DriverBase* AHIsubBase = (struct DriverBase*) card->ahisubbase; int i = 0; int frames = card->current_record_bytesize / 2; struct AHIRecordMessage rm = { AHIST_S16S, card->current_record_buffer, RECORD_BUFFER_SAMPLES }; WORD *src = card->current_record_buffer; WORD* dst = card->current_record_buffer; #ifdef __AMIGAOS4__ while( i < frames ) { *dst = ( ( *src & 0x00FF ) << 8 ) | ( ( *src & 0xFF00 ) >> 8 ); ++i; ++src; ++dst; } #else /*while( i < frames ) { *dst = (*src); ++i; ++src; ++dst; }*/ #endif CallHookPkt(AudioCtrl->ahiac_SamplerFunc, (Object*) AudioCtrl, &rm); }
#ifndef AHI_Drivers_Card_DriverData_h #define AHI_Drivers_Card_DriverData_h #include <exec/types.h> #include <exec/interrupts.h> #include <devices/ahi.h> /** Make the common library code initialize a global SysBase for us. It's required for hwaccess.c */ #define DRIVER "hdaudio.audio" #define DRIVER_NEEDS_GLOBAL_EXECBASE #define INPUTS 5 #ifdef __AROS__ #define DRIVER_NEED_GLOBAL_EXECBASE #endif #ifdef __amigaos4__ #define DRIVER_NEED_GLOBAL_EXECBASE #endif #include "DriverBase.h" { }; #endif /* AHI_Drivers_Card_DriverData_h */
#define DEBUG 0
#include <aros/debug.h>
#include <asm/io.h>
#include <hidd/irq.h>
#include <config.h>
#include "library.h"
#include "DriverData.h"
OOP_AttrBase __IHidd_PCIDev;
static const struct {
UWORD VendorID;
UWORD ProductID;
STRPTR Model;
} support[] = {
{ 0x8086, 0x2415, "Intel 82801AA" },
{ 0x8086, 0x2425, "Intel 82801AB" },
{ 0x8086, 0x2445, "Intel 82801BA" },
{ 0x8086, 0x2485, "Intel ICH3" },
{ 0x8086, 0x24c5, "Intel ICH4" },
{ 0x8086, 0x24d5, "Intel ICH5" },
{ 0x8086, 0x25a6, "ESB" },
{ 0x8086, 0x266e, "Intel ICH6" },
{ 0x8086, 0x27de, "Intel ICH7" },
{ 0x8086, 0x2698, "ESB2" },
{ 0x8086, 0x7195, "Intel 440MX" },
{ 0x1039, 0x7012, "SIS 7012" },
{ 0x10de, 0x01b1, "NVIDIA nForce" },
{ 0x10de, 0x003a, "MCP04" },
{ 0x10de, 0x006a, "NVIDIA nForce2" },
{ 0x10de, 0x0059, "CK804" },
{ 0x10de, 0x008a, "MCP2S AC'97 Audio Controller" },
{ 0x10de, 0x00da, "NVIDIA nForce3" },
{ 0x10de, 0x00ea, "CK8S" },
{ 0x10de, 0x026b, "MCP51" },
{ 0x1022, 0x746d, "AMD 8111" },
{ 0x1022, 0x7445, "AMD 768" },
{ 0x10b9, 0x5455, "Ali 5455" },
{0,0,NULL},
};
static void i8x0_set_reg(struct ac97Base *ac97Base, ULONG reg, UWORD value)
{
int count=1000000;
while(count-- && (inb(ac97Base->dmabase + ACC_SEMA) & 1));
outw(value, reg+ac97Base->mixerbase);
}
static UWORD i8x0_get_reg(struct ac97Base *ac97Base, ULONG reg)
{
int count=1000000;
while(count-- && (inb(ac97Base->dmabase + ACC_SEMA) & 1));
return inw(reg+ac97Base->mixerbase);
}
/******************************************************************************
** Custom driver init *********************************************************
******************************************************************************/
#define ac97Base ((struct ac97Base *)hook->h_Data)
#define AHIsubBase ((struct DriverBase *)hook->h_Data)
static AROS_UFH3(void, Enumerator,
AROS_UFHA(struct Hook *, hook, A0),
AROS_UFHA(OOP_Object *, device, A2),
AROS_UFHA(APTR, msg, A1))
{
AROS_USERFUNC_INIT
ULONG VendorID,ProductID;
int i;
OOP_GetAttr(device, aHidd_PCIDevice_ProductID, &ProductID);
OOP_GetAttr(device, aHidd_PCIDevice_VendorID, &VendorID);
D(bug("[ac97] Found audio device %04x:%04x\n", VendorID, ProductID));
for (i=0; support[i].VendorID; i++)
{
if (VendorID == support[i].VendorID && ProductID == support[i].ProductID)
{
struct TagItem attrs[] = {
{ aHidd_PCIDevice_isIO, TRUE },
{ aHidd_PCIDevice_isMEM, FALSE },
{ aHidd_PCIDevice_isMaster, TRUE },
{ TAG_DONE, 0UL },
};
D(bug("[ac97] Found supported '%s' card\n", support[i].Model));
ac97Base->cardfound = TRUE;
ac97Base->mixer_set_reg = i8x0_set_reg;
ac97Base->mixer_get_reg = i8x0_get_reg;
OOP_SetAttrs(device, (struct TagItem *)&attrs);
OOP_GetAttr(device, aHidd_PCIDevice_Base0, &ac97Base->mixerbase);
OOP_GetAttr(device, aHidd_PCIDevice_Base1, &ac97Base->dmabase);
OOP_GetAttr(device, aHidd_PCIDevice_INTLine, &ac97Base->irq_num);
D(bug("[ac97] Mixer IO base %x\n", ac97Base->mixerbase));
D(bug("[ac97] DMA IO base %x\n", ac97Base->dmabase));
if (VendorID == 0x1039 && ProductID == 0x7012)
{
/* SIS 7012 */
ac97Base->off_po_sr = DEFAULT_PO_PICB; /* swap registers */
ac97Base->off_po_picb = DEFAULT_PO_SR;
ac97Base->size_shift = 1; /* chip requires size in bytes, not samples */
}
else
{
/* All other cards */
ac97Base->off_po_sr = DEFAULT_PO_SR; /* swap registers */
ac97Base->off_po_picb = DEFAULT_PO_PICB;
ac97Base->size_shift = 0;
}
outl(2, ac97Base->dmabase + GLOB_CNT);
ac97Base->mixer_set_reg(ac97Base, AC97_RESET, 0);
ac97Base->mixer_set_reg(ac97Base, AC97_POWERDOWN, 0);
/* Set master volume to no attenuation, mute off */
ac97Base->mixer_set_reg(ac97Base, AC97_MASTER_VOL, 0x0000);
ac97Base->mixer_set_reg(ac97Base, AC97_HEADPHONE_VOL, 0x0000);
ac97Base->mixer_set_reg(ac97Base, AC97_TONE, 0x0f0f);
ac97Base->mixer_set_reg(ac97Base, AC97_PCM_VOL, 0x0000);
D(bug("[ac97] Powerdown = %02x\n", ac97Base->mixer_get_reg(ac97Base, AC97_POWERDOWN)));
D(bug("[ac97] GLOB_CNT = %08x\n", inl(ac97Base->dmabase + GLOB_CNT)));
D(bug("[ac97] GLOB_STA = %08x\n", inl(ac97Base->dmabase + GLOB_STA)));
/*
int i;
for (i=0; i < 64; i+=2)
{
D(bug("[ac97] reg %02x = %04x\n", i, ac97Base->mixer_get_reg(ac97Base, i)));
}
*/
outl(ac97Base->PCM_out, ac97Base->dmabase + PO_BDBAR);
D(bug("[ac97] PO_BDBAR=%08x\n", inl(ac97Base->dmabase + PO_BDBAR)));
D(bug("[ac97] PO_REGS=%08x\n", inl(ac97Base->dmabase + PO_CIV)));
D(bug("[ac97] PO_PICB=%04x\n", inw(ac97Base->dmabase + ac97Base->off_po_picb)));
D(bug("[ac97] PO_PIV=%02x\n", inb(ac97Base->dmabase + PO_PIV)));
D(bug("[ac97] PO_CR=%02x\n", inb(ac97Base->dmabase + PO_CR)));
}
}
AROS_USERFUNC_EXIT
}
#undef ac97Base
#undef AHIsubBase
BOOL DriverInit( struct DriverBase* AHIsubBase )
{
struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
ac97Base->dosbase = OpenLibrary( DOSNAME, 37 );
ac97Base->sysbase = SysBase;
D(bug("[ac97] Init\n"));
if(DOSBase)
{
ac97Base->oopbase = OpenLibrary(AROSOOP_NAME, 0);
if (OOPBase)
{
__IHidd_PCIDev = OOP_ObtainAttrBase(IID_Hidd_PCIDevice);
D(bug("[ac97] Libraries opened\n"));
if (__IHidd_PCIDev)
{
OOP_Object *pci = OOP_NewObject(NULL, CLID_Hidd_PCI, NULL);
D(bug("[ac97] PCIDevice AttrBase = %x\n",__IHidd_PCIDev));
if (pci)
{
struct Hook FindHook = {
h_Entry: (IPTR(*)())Enumerator,
h_Data: ac97Base,
};
struct TagItem Reqs[] = {
{ tHidd_PCI_Class, 0x04 },
{ tHidd_PCI_SubClass, 0x01 },
{ TAG_DONE, 0UL },
};
struct pHidd_PCI_EnumDevices enummsg = {
mID: OOP_GetMethodID(CLID_Hidd_PCI, moHidd_PCI_EnumDevices),
callback: &FindHook,
requirements: (struct TagItem *)&Reqs,
}, *msg = &enummsg;
D(bug("[ac97] Got PCI object\n"));
ac97Base->cardfound = FALSE;
ac97Base->PCM_out = AllocMem(8*32, MEMF_PUBLIC | MEMF_CLEAR);
OOP_DoMethod(pci, (OOP_Msg)msg);
OOP_DisposeObject(pci);
D(bug("[ac97] PCM out base %08x\n", ac97Base->PCM_out));
return ac97Base->cardfound;
}
}
}
else
{
Req("Unable to open 'oop.library'\n");
}
}
else
{
Req( "Unable to open 'dos.library' version 37.\n" );
}
return FALSE;
}
/******************************************************************************
** Custom driver clean-up *****************************************************
******************************************************************************/
VOID DriverCleanup( struct DriverBase* AHIsubBase )
{
struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
OOP_ReleaseAttrBase(IID_Hidd_PCIDevice);
CloseLibrary( (struct Library*) DOSBase );
CloseLibrary( (struct Library*) ac97Base->oopbase);
}
#define DEBUG 0
#include <aros/debug.h>
#include <config.h>
#include <devices/ahi.h>
#include <dos/dostags.h>
#include <exec/memory.h>
#include <libraries/ahi_sub.h>
#include <utility/tagitem.h>
#include <proto/ahi_sub.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>
#include <stddef.h>
#include <hidd/irq.h>
#include <asm/io.h>
#include "library.h"
#include "DriverData.h"
void
SlaveEntry( void );
PROCGW( static, void, slaveentry, SlaveEntry );
/* There is probably no reason to support all these frequencies. If,
* for example, your hardware is locked at 48 kHz, it's ok to only
* present one single mixing/recording frequency to the user. If your
* hardware has internal resamples and accepts any frequency, select a
* few common ones.
*/
static const LONG frequencies[] =
{
48000, // DAT
};
#define FREQUENCIES (sizeof frequencies / sizeof frequencies[ 0 ])
static const ULONG table_5bit[] = {
0xb53c,
0x804e,
0x5ad5,
0x404e,
0x2d86,
0x203a,
0x16d1,
0x1027,
0x0b6f,
0x0818,
0x05bb,
0x040f,
0x02df,
0x0209,
0x0171,
0x0105,
0x00b9,
0x0083,
0x005d,
0x0042,
0x002e,
0x0021,
0x0017,
0x0010,
0x000c,
0x0008,
0x0006,
0x0004,
0x0003,
0x0002,
0x0001,
0x0000
};
static UWORD LinToLog(ULONG vol)
{
int i;
if (!vol) return 0x20;
for (i=0; i < 32; i++)
{
if (vol > table_5bit[i])
{
return i;
}
}
return 0x1f;
}
static void play_int(HIDDT_IRQ_Handler *irq, HIDDT_IRQ_HwInfo *hw);
/******************************************************************************
** AHIsub_AllocAudio **********************************************************
******************************************************************************/
ULONG
_AHIsub_AllocAudio( struct TagItem* taglist,
struct AHIAudioCtrlDrv* AudioCtrl,
struct DriverBase* AHIsubBase )
{
struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
OOP_Object *irq = OOP_NewObject(NULL, CLID_Hidd_IRQ, NULL);
AudioCtrl->ahiac_DriverData = AllocVec( sizeof( struct AC97Data ),
MEMF_CLEAR | MEMF_PUBLIC );
#define dd ((struct AC97Data *) AudioCtrl->ahiac_DriverData)
D(bug("AHI: AllocAudio: dd=%08x\n", dd));
if( dd != NULL )
{
dd->slavesignal = -1;
dd->mastersignal = AllocSignal( -1 );
dd->mastertask = (struct Process*) FindTask( NULL );
dd->ahisubbase = ac97Base;
dd->out_volume = 0x10000;
}
else
{
return AHISF_ERROR;
}
dd->irq = AllocVec(sizeof (HIDDT_IRQ_Handler), MEMF_CLEAR | MEMF_PUBLIC);
if (dd->irq)
{
struct pHidd_IRQ_AddHandler __msg__ = {
mID: OOP_GetMethodID(CLID_Hidd_IRQ, moHidd_IRQ_AddHandler),
handlerinfo: dd->irq,
id: ac97Base->irq_num,
}, *msg = &__msg__;
dd->irq->h_Node.ln_Pri = 0;
dd->irq->h_Node.ln_Name = "AHI Int";
dd->irq->h_Code = play_int;
dd->irq->h_Data = AudioCtrl;
OOP_DoMethod(irq, (OOP_Msg)msg);
OOP_DisposeObject(irq);
}
D(bug("AHI: AllocAudio: Everything OK\n"));
if( dd->mastersignal == -1 )
{
return AHISF_ERROR;
}
/* Setting the only working frequency for AC97 */
AudioCtrl->ahiac_MixFreq = 48000;
return ( AHISF_KNOWSTEREO |
AHISF_MIXING |
AHISF_TIMING );
}
/******************************************************************************
** AHIsub_FreeAudio ***********************************************************
******************************************************************************/
void
_AHIsub_FreeAudio( struct AHIAudioCtrlDrv* AudioCtrl,
struct DriverBase* AHIsubBase )
{
struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
OOP_Object *irq = OOP_NewObject(NULL, CLID_Hidd_IRQ, NULL);
D(bug("AHI: FreeAudio\n"));
if (dd->irq)
{
struct pHidd_IRQ_RemHandler __msg__ = {
mID: OOP_GetMethodID(CLID_Hidd_IRQ, moHidd_IRQ_RemHandler),
handlerinfo: dd->irq,
}, *msg = &__msg__;
OOP_DoMethod(irq, (OOP_Msg)msg);
FreeVec(dd->irq);
}
D(bug("AHI: FreeAudio: IRQ removed\n"));
if( AudioCtrl->ahiac_DriverData != NULL )
{
FreeSignal( dd->mastersignal );
D(bug("AHI: FreeAudio: Signal freed\n"));
FreeVec( AudioCtrl->ahiac_DriverData );
D(bug("AHI: FreeAudio: DriverData freed\n"));
AudioCtrl->ahiac_DriverData = NULL;
}
OOP_DisposeObject(irq);
D(bug("AHI: FreeAudio: IRQ object freed\n"));
}
/******************************************************************************
** AHIsub_Disable *************************************************************
******************************************************************************/
void
_AHIsub_Disable( struct AHIAudioCtrlDrv* AudioCtrl,
struct DriverBase* AHIsubBase )
{
struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
// V6 drivers do not have to preserve all registers
Disable();
}
/******************************************************************************
** AHIsub_Enable **************************************************************
******************************************************************************/
void
_AHIsub_Enable( struct AHIAudioCtrlDrv* AudioCtrl,
struct DriverBase* AHIsubBase )
{
struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
// V6 drivers do not have to preserve all registers
Enable();
}
/******************************************************************************
** AHIsub_Start ***************************************************************
******************************************************************************/
ULONG
_AHIsub_Start( ULONG flags,
struct AHIAudioCtrlDrv* AudioCtrl,
struct DriverBase* AHIsubBase )
{
struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
D(bug("AHI: Start\n"));
AHIsub_Stop( flags, AudioCtrl );
D(bug("AHI: Start: Stop called\n"));
if(flags & AHISF_PLAY)
{
struct TagItem proctags[] =
{
{ NP_Entry, (IPTR) &slaveentry },
{ NP_Name, (IPTR) LibName },
{ NP_Priority, -1 },
{ TAG_DONE, 0 }
};
dd->mixbuffer = AllocVec( AudioCtrl->ahiac_BuffSize,
MEMF_ANY | MEMF_PUBLIC );
D(bug("AHI: Start: Mixing buffer = %08x\n",dd->mixbuffer));
if( dd->mixbuffer == NULL ) return AHIE_NOMEM;
Forbid();
dd->slavetask = CreateNewProc( proctags );
D(bug("AHI: Start: Slave task = %08x\n",dd->slavetask));
if( dd->slavetask != NULL )
{
dd->slavetask->pr_Task.tc_UserData = AudioCtrl;
}
D(bug("AHI: Start: Slave task UserData set\n"));
Permit();
if( dd->slavetask != NULL )
{
Wait( 1L << dd->mastersignal ); // Wait for slave to come alive
D(bug("AHI: Start: Slave task UP and running\n"));
if( dd->slavetask == NULL ) // Is slave alive or dead?
{
return AHIE_UNKNOWN;
}
}
else
{
return AHIE_NOMEM; // Well, out of memory or whatever...
}
}
if( flags & AHISF_RECORD )
{
return AHIE_UNKNOWN;
}
D(bug("AHI: Start: Everything OK\n"));
return AHIE_OK;
}
/******************************************************************************
** AHIsub_Update **************************************************************
******************************************************************************/
void
_AHIsub_Update( ULONG flags,
struct AHIAudioCtrlDrv* AudioCtrl,
struct DriverBase* AHIsubBase )
{
struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
// Empty function
}
/******************************************************************************
** AHIsub_Stop ****************************************************************
******************************************************************************/
void
_AHIsub_Stop( ULONG flags,
struct AHIAudioCtrlDrv* AudioCtrl,
struct DriverBase* AHIsubBase )
{
struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
if( flags & AHISF_PLAY )
{
if( dd->slavetask != NULL )
{
if( dd->slavesignal != -1 )
{
Signal( (struct Task*) dd->slavetask,
1L << dd->slavesignal ); // Kill him!
}
Wait( 1L << dd->mastersignal ); // Wait for slave to die
}
FreeVec( dd->mixbuffer );
dd->mixbuffer = NULL;
}
if(flags & AHISF_RECORD)
{
// Do nothing
}
}
/******************************************************************************
** AHIsub_GetAttr *************************************************************
******************************************************************************/
IPTR
_AHIsub_GetAttr( ULONG attribute,
LONG argument,
IPTR def,
struct TagItem* taglist,
struct AHIAudioCtrlDrv* AudioCtrl,
struct DriverBase* AHIsubBase )
{
struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
size_t i;
switch( attribute )
{
case AHIDB_Bits:
return 16;
case AHIDB_Frequencies:
return FREQUENCIES;
case AHIDB_Frequency: // Index->Frequency
return (LONG) frequencies[ argument ];
case AHIDB_Index: // Frequency->Index
if( argument <= frequencies[ 0 ] )
{
return 0;
}
if( argument >= frequencies[ FREQUENCIES - 1 ] )
{
return FREQUENCIES - 1;
}
for( i = 1; i < FREQUENCIES; i++ )
{
if( frequencies[ i ] > argument )
{
if( ( argument - frequencies[ i - 1 ] ) <
( frequencies[ i ] - argument ) )
{
return i-1;
}
else
{
return i;
}
}
}
return 0; // Will not happen
case AHIDB_Author:
return (IPTR) "Michal Schulz";
case AHIDB_Copyright:
return (IPTR) "APL";
case AHIDB_Version:
return (IPTR) LibIDString;
case AHIDB_Record:
return FALSE;
case AHIDB_Realtime:
return TRUE;
case AHIDB_Outputs:
return 1;
/*
case AHIDB_MinMonitorVolume:
return 0x00000;
case AHIDB_MaxMonitorVolume:
return 0x10000;
*/
case AHIDB_MinOutputVolume:
return 0x00000;
case AHIDB_MaxOutputVolume:
return 0x10000;
case AHIDB_Output:
return (IPTR) "Default"; // We have only one "output"!
default:
return def;
}
}
/******************************************************************************
** AHIsub_HardwareControl *****************************************************
******************************************************************************/
ULONG
_AHIsub_HardwareControl( ULONG attribute,
LONG argument,
struct AHIAudioCtrlDrv* AudioCtrl,
struct DriverBase* AHIsubBase )
{
struct ac97Base* ac97Base = (struct ac97Base*) AHIsubBase;
UWORD vol;
switch(attribute)
{
case AHIC_OutputVolume:
vol = LinToLog(argument);
if (vol == 0x20) vol = 0x8000;
else vol = vol | vol << 8;
D(bug("SetVol %05x translated to %04x\n", argument, vol));
dd->out_volume = argument;
if (ac97Base->mixer_set_reg)
ac97Base->mixer_set_reg(ac97Base, AC97_PCM_VOL, vol);
return TRUE;
case AHIC_OutputVolume_Query:
return dd->out_volume;
}
return 0;
}
#undef SysBase
static void play_int(HIDDT_IRQ_Handler *irq, HIDDT_IRQ_HwInfo *hw)
{
struct AHIAudioCtrlDrv* AudioCtrl;
struct DriverBase* AHIsubBase;
struct ac97Base* ac97Base;
struct ExecBase *SysBase;
AudioCtrl = (struct AHIAudioCtrlDrv*) irq->h_Data;
AHIsubBase = (struct DriverBase*) dd->ahisubbase;
ac97Base = (struct ac97Base*) AHIsubBase;
SysBase = (struct SysBase*) ac97Base->sysbase;
dd->old_SR = inw(ac97Base->dmabase + ac97Base->off_po_sr);
outw(dd->old_SR & 0x1c, ac97Base->dmabase + ac97Base->off_po_sr);
if ((dd->old_SR & 0x1c) && dd->slavetask)
{
/* Signaling the slave task */
Signal((struct Task *)dd->slavetask, SIGBREAKF_CTRL_E);
}
}
OSS API 设计为使用传统的 Unix 框架 open()、read()、write() 和 ioctl(),通过 特殊设备。例如,声音输入和输出的默认设备是 /dev/dsp。
主页 和 OSS BSD 源代码 有时会关闭。
/drv/ 源代码中的 AC97 编解码器/混合器支持
通过 AC97 具有混合器、读取、写入、中断、音频设置速率、音频设置声道、音频 IOctl、音频复位、音频复位输入和输出、音频打开、音频关闭、音频启动输入、音频触发、音频准备输入、音频准备输出、分配缓冲区、获取缓冲区指针、SGD 缓冲区、控制。
SBpci 具有 SRC 初始化、SRC 注册读取、SRC 注册写入、SRC 获取速率、SRC 设置速率、写入内存、读取内存、中断、音频设置速率、音频设置声道、音频设置格式、音频 IOctl、音频复位、音频复位输入和输出、音频打开、音频关闭、音频输出块、音频启动输入、音频触发、音频准备输入、音频准备输出、获取缓冲区指针、控制、混合初始化、SBPCI 连接、SBPCI 断开连接。
cmpci 具有定义、设置 SPDIF 速率、设置 AC3、设置混合器、获取混合器、更改位、混合器设置、Outsw、混合器 IOCtl、设置 Recmask、Outsw、混合初始化、混合器复位、中断、音频设置速率、音频设置声道、音频设置格式、音频 IOctl、音频复位、音频复位输入和输出、音频打开、音频关闭、音频输出块、音频启动输入、音频触发、设置 DAC 速率、设置 DAC 格式、设置 ADC 速率、设置 ADC 格式、设置录制、音频准备输入、设置播放、音频准备输出、获取缓冲区指针、控制、混合初始化、cmpci 连接、cmpci 断开连接。
中断可以由 CPU 触发(数量有限)或通过一个具有更多数量的额外芯片触发。
- 异步 - 复位或电源故障
- 同步 - 系统调用、非法指令
INTB_TIMERTICK 技巧已被移除,不再使用。VERTB 中断现在以什么速率发生?
它以前是
VERTB:始终为 50 Hz TIMERTICK:100 Hz(默认值,可以在启动 AROS 时使用“-t”参数更改)
某些东西,比如 AHI 驱动程序,如果它只以 50 Hz 的速率发生,则无法正常工作。而且 50 Hz 的通用计时器精度也很差。这就是 TIMERTICK 技巧修复的内容。
这是由于声卡未正确设置中断号造成的。我设法通过修改 BIOS 设置来获得正确的中断号。
如果你使用 PCITool 查看你的声卡信息,如果显示中断号为 255 或 0,则说明它未正确设置。
我之前已经发布过关于这个中断号的内容,目前 PCI.hidd 只从 pci 配置空间获取中断号,不检查其完整性。Bios 可能已经或可能没有设置中断号,这取决于你的 Bios 设置。
通用的 AC97 代码被分离到一个 OO 类中,AHI 驱动程序可以利用它来提供必要的功
/* SIS7012 以字节处理 pcm 数据,其他以采样处理 */
Ac97 驱动程序只支持 48 kHz 音频(这是 ac97 编解码器的默认值)。混合工作(尝试同时运行 3 个 madahi 副本,播放不同的 mp3 文件)。驱动程序的任务以优先级 30 工作,以覆盖 input.device(嗯,当用户移动/调整窗口大小时应该很好地播放声音,不是吗?)并充分利用中断:)
每个人都喜欢使用设备接口来流式传输音频吗?在这种情况下,你能缓存声音,直到你有足够的音频数据发送到声卡上的硬中断吗?
即使使用底层库接口,该接口实际上是为模块播放而设计的,驱动程序也会为每个模块中断(如果你愿意,可以称为软中断)缓存音频流,然后在硬件中断时将其发送到声卡。
当然,如果模块数据播放速度快于声卡处理速度,就会出现问题,例如 OS4 EMU10K 驱动程序。它被设置为 80 Hz 硬件中断,因为 A1 有一个问题,据我所知,它与 1000 Hz 的正常值存在问题,该值在所有其他 Amiga 相关驱动程序(包括真实设备)上都能正常工作。一个声音中断频率超过 80 Hz 的模块就会出现问题,并导致音频出现循环。