跳转到内容

Aros/Developer/AHIDriversDev

来自Wikibooks,开放世界中的开放书籍
用于Aros维基百科的导航栏
Aros用户
Aros用户文档
Aros用户常见问题解答
Aros用户应用程序
Aros用户DOS Shell
Aros/User/AmigaLegacy
Aros开发文档
Aros开发者文档
从AmigaOS/SDL移植软件
针对Zune初学者
Zune .MUI类
针对SDL初学者
Aros开发者构建系统
特定平台
Aros x86完整系统HCL
Aros x86音频/视频支持
Aros x86网络支持
Aros Intel AMD x86安装
Aros存储支持 IDE SATA等
Aros Poseidon USB支持
x86-64支持
Motorola 68k Amiga支持
Linux和FreeBSD支持
Windows Mingw和MacOSX支持
Android支持
Arm Raspberry Pi支持
PPC Power Architecture
其他
Aros公共许可证

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驱动程序概述

[编辑 | 编辑源代码]

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

name-init.c

[编辑 | 编辑源代码]
#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)
{

}

name-main.c

[编辑 | 编辑源代码]

阅读更多 这里

#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)
{

}

name-accel.c

[编辑 | 编辑源代码]
#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;
}

name-interrupt.c

[编辑 | 编辑源代码]
#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);
}

DriverData.h

[编辑 | 编辑源代码]
#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);
    }
}

OpenSound OSS 驱动程序的结构

[编辑 | 编辑源代码]

OSS API 设计为使用传统的 Unix 框架 open()、read()、write() 和 ioctl(),通过 特殊设备。例如,声音输入和输出的默认设备是 /dev/dsp。

PDF手册页源代码.

主页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 触发(数量有限)或通过一个具有更多数量的额外芯片触发。

  • 异步 - 复位或电源故障
  • 同步 - 系统调用、非法指令

如何将 OSS 驱动程序改编为 AHI 驱动程序

[编辑 | 编辑源代码]

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 的模块就会出现问题,并导致音频出现循环。

华夏公益教科书