mirror of
https://github.com/EnderIce2/Fennix.git
synced 2025-07-04 11:59:17 +00:00
Merge remote-tracking branch 'Drivers/master'
This commit is contained in:
22
Drivers/audio/ac97/Makefile
Normal file
22
Drivers/audio/ac97/Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
# Config files
|
||||
include ../../../Makefile.conf
|
||||
include ../../config.mk
|
||||
|
||||
S_SOURCES = $(shell find ./ -type f -name '*.S')
|
||||
C_SOURCES = $(shell find ./ -type f -name '*.c')
|
||||
CPP_SOURCES = $(shell find ./ -type f -name '*.cpp')
|
||||
HEADERS = $(sort $(dir $(wildcard ../../include/*)))
|
||||
OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(S_SOURCES:.S=.o)
|
||||
STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CPP_SOURCES:.cpp=.su)
|
||||
|
||||
FILENAME = ac97.drv
|
||||
|
||||
build: $(FILENAME)
|
||||
mv $(FILENAME) ../../out/$(FILENAME)
|
||||
|
||||
$(FILENAME): $(OBJ)
|
||||
$(info Linking $@)
|
||||
$(CC) $(DRIVER_LDFLAGS) $(OBJ) ../../out/dcrt0.o -L../../out -lkernel -o $@
|
||||
|
||||
clean:
|
||||
rm -f file.map $(OBJ) $(STACK_USAGE_OBJ)
|
881
Drivers/audio/ac97/ac97.cpp
Normal file
881
Drivers/audio/ac97/ac97.cpp
Normal file
@ -0,0 +1,881 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <audio.h>
|
||||
#include <regs.h>
|
||||
#include <base.h>
|
||||
#include <pci.h>
|
||||
#include <io.h>
|
||||
#include <fs.h>
|
||||
|
||||
#define DescriptorListLength 0x20
|
||||
|
||||
enum AudioVolumeValues
|
||||
{
|
||||
AV_Maximum = 0x0,
|
||||
AV_Minimum = 0x3F,
|
||||
};
|
||||
|
||||
enum AudioEncodingValues
|
||||
{
|
||||
AE_PCMs8,
|
||||
AE_PCMu8,
|
||||
|
||||
AE_PCMs16le,
|
||||
AE_PCMs20le,
|
||||
AE_PCMs24le,
|
||||
AE_PCMs32le,
|
||||
|
||||
AE_PCMu16le,
|
||||
AE_PCMu20le,
|
||||
AE_PCMu24le,
|
||||
AE_PCMu32le,
|
||||
|
||||
AE_PCMs16be,
|
||||
AE_PCMs20be,
|
||||
AE_PCMs24be,
|
||||
AE_PCMs32be,
|
||||
|
||||
AE_PCMu16be,
|
||||
AE_PCMu20be,
|
||||
AE_PCMu24be,
|
||||
AE_PCMu32be,
|
||||
};
|
||||
|
||||
enum NativeAudioMixerRegisters
|
||||
{
|
||||
/**
|
||||
* @brief Reset Register
|
||||
* @note Length: word
|
||||
*/
|
||||
NAM_Reset = 0x00,
|
||||
|
||||
/**
|
||||
* @brief Master Volume Register
|
||||
* @note Length: word
|
||||
*/
|
||||
NAM_MasterVolume = 0x02,
|
||||
|
||||
/**
|
||||
* @brief Microphone Volume Register
|
||||
* @note Length: word
|
||||
*/
|
||||
NAM_MicrophoneVolume = 0x0E,
|
||||
|
||||
/**
|
||||
* @brief PCM Out Volume Register
|
||||
* @note Length: word
|
||||
*/
|
||||
NAM_PCMOutVolume = 0x18,
|
||||
|
||||
/**
|
||||
* @brief Select Record Input Register
|
||||
* @note Length: word
|
||||
*/
|
||||
NAM_SelectRecordInput = 0x1A,
|
||||
|
||||
/**
|
||||
* @brief Record Gain Register
|
||||
* @note Length: word
|
||||
*/
|
||||
NAM_RecordGain = 0x1C,
|
||||
|
||||
/**
|
||||
* @brief Record Gain Microphone Register
|
||||
* @note Length: word
|
||||
*/
|
||||
NAM_RecordGainMicrophone = 0x1E,
|
||||
};
|
||||
|
||||
enum NativeAudioBusMasterRegisters
|
||||
{
|
||||
/**
|
||||
* @brief Register box for PCM IN
|
||||
* @note Length: below
|
||||
*/
|
||||
NABM_PCMInBox = 0x00,
|
||||
|
||||
/**
|
||||
* @brief Register box for PCM OUT
|
||||
* @note Length: below
|
||||
*/
|
||||
NABM_PCMOutBox = 0x10,
|
||||
|
||||
/**
|
||||
* @brief Register box for Microphone
|
||||
* @note Length: below
|
||||
*/
|
||||
NABM_MicrophoneBox = 0x20,
|
||||
|
||||
/**
|
||||
* @brief Global Control Register
|
||||
* @note Length: dword
|
||||
*/
|
||||
NABM_GlobalControl = 0x2C, /* 0x30 */
|
||||
|
||||
/**
|
||||
* @brief Global Status Register
|
||||
* @note Length: dword
|
||||
*/
|
||||
NABM_GlobalStatus = 0x30, /* 0x34 */
|
||||
};
|
||||
|
||||
enum NativeAudioBusMasterBoxOffsets
|
||||
{
|
||||
/**
|
||||
* @brief Physical Address of Buffer Descriptor List
|
||||
* @note Length: dword
|
||||
*/
|
||||
NABMBOFF_BufferDescriptorList = 0x00,
|
||||
|
||||
/**
|
||||
* @brief Number of Actual Processed Buffer Descriptor Entry
|
||||
* @note Length: byte
|
||||
*/
|
||||
NABMBOFF_BufferDescriptorEntry = 0x04,
|
||||
|
||||
/**
|
||||
* @brief Number of all Descriptor Entries
|
||||
* @note Length: byte
|
||||
*/
|
||||
NABMBOFF_DescriptorEntries = 0x05,
|
||||
|
||||
/**
|
||||
* @brief Status of transferring Data
|
||||
* @note Length: word
|
||||
*/
|
||||
NABMBOFF_Status = 0x06,
|
||||
|
||||
/**
|
||||
* @brief Number of transferred Samples in Actual Processed Entry
|
||||
* @note Length: word
|
||||
*/
|
||||
NABMBOFF_TransferredSamples = 0x08,
|
||||
|
||||
/**
|
||||
* @brief Number of next processed Buffer Entry
|
||||
* @note Length: byte
|
||||
*/
|
||||
NABMBOFF_NextProcessedBufferEntry = 0x0A,
|
||||
|
||||
/**
|
||||
* @brief Transfer Control
|
||||
* @note Length: byte
|
||||
*/
|
||||
NABMBOFF_TransferControl = 0x0B,
|
||||
};
|
||||
|
||||
enum OutputPulseCodeModulationRegisters
|
||||
{
|
||||
/**
|
||||
* @brief Physical Address of Buffer Descriptor List
|
||||
* @note Length: dword
|
||||
*/
|
||||
PCMOUT_BufferDescriptorList = (int)NABM_PCMOutBox + (int)NABMBOFF_BufferDescriptorList,
|
||||
|
||||
/**
|
||||
* @brief Number of Actual Processed Buffer Descriptor Entry
|
||||
* @note Length: byte
|
||||
*/
|
||||
PCMOUT_BufferDescriptorEntry = (int)NABM_PCMOutBox + (int)NABMBOFF_BufferDescriptorEntry,
|
||||
|
||||
/**
|
||||
* @brief Number of all Descriptor Entries
|
||||
* @note Length: byte
|
||||
*/
|
||||
PCMOUT_DescriptorEntries = (int)NABM_PCMOutBox + (int)NABMBOFF_DescriptorEntries,
|
||||
|
||||
/**
|
||||
* @brief Status of transferring Data
|
||||
* @note Length: word
|
||||
*/
|
||||
PCMOUT_Status = (int)NABM_PCMOutBox + (int)NABMBOFF_Status,
|
||||
|
||||
/**
|
||||
* @brief Number of transferred Samples in Actual Processed Entry
|
||||
* @note Length: word
|
||||
*/
|
||||
PCMOUT_TransferredSamples = (int)NABM_PCMOutBox + (int)NABMBOFF_TransferredSamples,
|
||||
|
||||
/**
|
||||
* @brief Number of next processed Buffer Entry
|
||||
* @note Length: byte
|
||||
*/
|
||||
PCMOUT_NextProcessedBufferEntry = (int)NABM_PCMOutBox + (int)NABMBOFF_NextProcessedBufferEntry,
|
||||
|
||||
/**
|
||||
* @brief Transfer Control
|
||||
* @note Length: byte
|
||||
*/
|
||||
PCMOUT_TransferControl = (int)NABM_PCMOutBox + (int)NABMBOFF_TransferControl,
|
||||
};
|
||||
|
||||
enum TransferControlRegisters
|
||||
{
|
||||
/**
|
||||
* @brief DMA controller control
|
||||
*
|
||||
* 0 = Pause transfer
|
||||
* 1 = Transfer sound data
|
||||
*/
|
||||
TC_DMAControllerControl = 0x01,
|
||||
|
||||
/**
|
||||
* @brief Reset
|
||||
*
|
||||
* 0 = Remove reset condition
|
||||
* 1 = Reset this NABM register box, this bit is cleared by card when is reset complete
|
||||
*/
|
||||
TC_TransferReset = 0x02,
|
||||
|
||||
/**
|
||||
* @brief Last Buffer Entry Interrupt enable
|
||||
*
|
||||
* 0 = Disable interrupt
|
||||
* 1 = Enable interrupt
|
||||
*/
|
||||
TC_LastBufferEntryInterruptEnable = 0x04,
|
||||
|
||||
/**
|
||||
* @brief IOC Interrupt enable
|
||||
*
|
||||
* 0 = Disable interrupt
|
||||
* 1 = Enable interrupt
|
||||
*/
|
||||
TC_IOCInterruptEnable = 0x08,
|
||||
|
||||
/**
|
||||
* @brief Fifo ERROR Interrupt enable
|
||||
*
|
||||
* 0 = Disable interrupt
|
||||
* 1 = Enable interrupt
|
||||
*/
|
||||
TC_FifoERRORInterruptEnable = 0x10,
|
||||
};
|
||||
|
||||
enum GlobalControlRegisters
|
||||
{
|
||||
/**
|
||||
* @brief Global Interrupt Enable
|
||||
*
|
||||
* 0 = Disable Interrupts
|
||||
* 1 = Enable Interrupts
|
||||
*/
|
||||
GC_GlobalInterruptEnable = 0x01,
|
||||
|
||||
/**
|
||||
* @brief Cold reset
|
||||
*
|
||||
* 0 = Device is in reset and can not be used
|
||||
* 1 = Resume to operational state
|
||||
*/
|
||||
GC_ColdReset = 0x02,
|
||||
|
||||
/**
|
||||
* @brief Warm reset
|
||||
*/
|
||||
GC_WarmReset = 0x04,
|
||||
|
||||
/**
|
||||
* @brief Shut down
|
||||
*
|
||||
* 0 = Device is powered
|
||||
* 1 = Shut down
|
||||
*/
|
||||
GC_ShutDown = 0x08,
|
||||
|
||||
/**
|
||||
* @brief Channels for PCM Output
|
||||
*
|
||||
* 00 = 2 channels
|
||||
* 01 = 4 channels
|
||||
* 10 = 6 channels
|
||||
* 11 = Reserved
|
||||
*/
|
||||
GC_ChannelsForPCMOutput = 0x30,
|
||||
|
||||
/**
|
||||
* @brief PCM Output mode
|
||||
*
|
||||
* 00 = 16 bit samples
|
||||
* 01 = 20 bit samples
|
||||
*/
|
||||
GC_PCMOutputMode = 0xC0,
|
||||
};
|
||||
|
||||
struct BufferDescriptorList
|
||||
{
|
||||
/**
|
||||
* @brief Physical Address to sound data in memory
|
||||
* @note Length: dword
|
||||
*/
|
||||
uint32_t Address;
|
||||
|
||||
/**
|
||||
* @brief Number of samples in this buffer
|
||||
* @note Length: word
|
||||
*/
|
||||
uint16_t SampleCount;
|
||||
|
||||
/**
|
||||
* @brief Flags
|
||||
* @note Length: word
|
||||
*
|
||||
* Bit 15 = Interrupt fired when data from this entry is transferred
|
||||
* Bit 14 = Last entry of buffer, stop playing
|
||||
* Other bits = Reserved
|
||||
*/
|
||||
uint16_t Flags;
|
||||
} __attribute__((packed));
|
||||
|
||||
uint16_t MixerVolume(uint8_t Left, uint8_t Right, bool Mute)
|
||||
{
|
||||
return ((uint16_t)((Right & 0x3F) |
|
||||
((Left & 0x3F) << 0x8) |
|
||||
(Mute & 1 << 0xF)));
|
||||
}
|
||||
|
||||
class AC97Device
|
||||
{
|
||||
private:
|
||||
PCIHeader0 *Header;
|
||||
BufferDescriptorList *DescriptorList = nullptr;
|
||||
|
||||
uint16_t MixerAddress;
|
||||
uint16_t BusMasterAddress;
|
||||
|
||||
AudioEncodingValues Encoding = AE_PCMs16le;
|
||||
char Channels = 2;
|
||||
uint8_t Volume = AV_Maximum;
|
||||
bool Mute = false;
|
||||
int SampleRate = 48000;
|
||||
char SampleSize = 2;
|
||||
|
||||
public:
|
||||
size_t write(uint8_t *Buffer, size_t Size)
|
||||
{
|
||||
if (Buffer == nullptr)
|
||||
{
|
||||
KernelLog("Invalid buffer.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((Size == 0) || (Size % (SampleSize * Channels)))
|
||||
{
|
||||
KernelLog("Invalid buffer length.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int TotalBDLToFill = (int)((Size + PAGE_SIZE - 1) >> 12);
|
||||
|
||||
while (Size > 0)
|
||||
{
|
||||
bool ActiveDMA = !(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl);
|
||||
|
||||
if (ActiveDMA)
|
||||
{
|
||||
int RemainingBDL = 0;
|
||||
|
||||
do
|
||||
{
|
||||
int CurrentBDL = inb(BusMasterAddress + PCMOUT_BufferDescriptorEntry);
|
||||
int LastBDL = inb(BusMasterAddress + PCMOUT_DescriptorEntries);
|
||||
|
||||
RemainingBDL = LastBDL - CurrentBDL;
|
||||
if (RemainingBDL < 0)
|
||||
RemainingBDL += DescriptorListLength;
|
||||
|
||||
RemainingBDL += 1;
|
||||
|
||||
if (RemainingBDL >= DescriptorListLength - 1)
|
||||
{
|
||||
long SampleCount = DescriptorList[(CurrentBDL + 1) % DescriptorListLength].SampleCount / Channels;
|
||||
if (SampleCount > 0)
|
||||
Sleep(SampleCount * 1000 / SampleRate);
|
||||
}
|
||||
|
||||
} while (RemainingBDL >= DescriptorListLength - 1 &&
|
||||
!(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl));
|
||||
}
|
||||
|
||||
uint8_t CurrentBDL = inb(BusMasterAddress + PCMOUT_BufferDescriptorEntry);
|
||||
uint8_t LastBDL = inb(BusMasterAddress + PCMOUT_DescriptorEntries);
|
||||
uint8_t NextBDL = LastBDL % DescriptorListLength;
|
||||
|
||||
ActiveDMA = !(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl);
|
||||
if (ActiveDMA)
|
||||
{
|
||||
NextBDL = (uint8_t)((LastBDL + 1) % DescriptorListLength);
|
||||
if (NextBDL == CurrentBDL)
|
||||
continue;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
size_t Wrote = (PAGE_SIZE > Size) ? size_t(Size)
|
||||
: size_t(PAGE_SIZE);
|
||||
|
||||
if (Wrote == 0)
|
||||
{
|
||||
KernelLog("Wrote 0 bytes.");
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy((void *)((uint64_t)DescriptorList[NextBDL].Address), Buffer, Wrote);
|
||||
DescriptorList[NextBDL].Flags = 0;
|
||||
|
||||
Buffer += Wrote;
|
||||
Size -= (unsigned int)Wrote;
|
||||
|
||||
DescriptorList[NextBDL].SampleCount = uint16_t(Wrote / SampleSize);
|
||||
TotalBDLToFill--;
|
||||
NextBDL = (uint8_t)((NextBDL + 1) % DescriptorListLength);
|
||||
} while (TotalBDLToFill-- && NextBDL != CurrentBDL);
|
||||
|
||||
outb(BusMasterAddress + PCMOUT_DescriptorEntries, NextBDL - 1);
|
||||
|
||||
ActiveDMA = !(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl);
|
||||
if (!ActiveDMA)
|
||||
{
|
||||
// Start DMA
|
||||
outb(BusMasterAddress + PCMOUT_TransferControl,
|
||||
inb(BusMasterAddress + PCMOUT_TransferControl) | TC_DMAControllerControl);
|
||||
}
|
||||
}
|
||||
return Size;
|
||||
}
|
||||
|
||||
int ioctl(AudioIoctl, void *)
|
||||
{
|
||||
// if (Data->AudioCallback.Adjust._Volume)
|
||||
// {
|
||||
// Volume = (uint8_t)(0x3F - (0x3F * Data->AudioCallback.Adjust.Volume / 100));
|
||||
// outw(BAR.MixerAddress + NAM_MasterVolume, MixerVolume(Volume, Volume, Mute));
|
||||
// // outw(BAR.MixerAddress + NAM_PCMOutVolume, MixerVolume(Volume, Volume, Mute));
|
||||
// }
|
||||
// else if (Data->AudioCallback.Adjust._Encoding)
|
||||
// {
|
||||
// fixme("Encoding changing not supported yet.");
|
||||
// }
|
||||
// else if (Data->AudioCallback.Adjust._SampleRate)
|
||||
// {
|
||||
// switch (Data->AudioCallback.Adjust.SampleRate)
|
||||
// {
|
||||
// case 0:
|
||||
// {
|
||||
// SampleRate = 8000;
|
||||
// break;
|
||||
// }
|
||||
// case 1:
|
||||
// {
|
||||
// SampleRate = 11025;
|
||||
// break;
|
||||
// }
|
||||
// case 2:
|
||||
// {
|
||||
// SampleRate = 16000;
|
||||
// break;
|
||||
// }
|
||||
// case 3:
|
||||
// {
|
||||
// SampleRate = 22050;
|
||||
// break;
|
||||
// }
|
||||
// case 4:
|
||||
// {
|
||||
// SampleRate = 32000;
|
||||
// break;
|
||||
// }
|
||||
// case 5:
|
||||
// {
|
||||
// SampleRate = 44100;
|
||||
// break;
|
||||
// }
|
||||
// case 6:
|
||||
// {
|
||||
// SampleRate = 48000;
|
||||
// break;
|
||||
// }
|
||||
// case 7:
|
||||
// {
|
||||
// SampleRate = 88200;
|
||||
// break;
|
||||
// }
|
||||
// case 8:
|
||||
// {
|
||||
// SampleRate = 96000;
|
||||
// break;
|
||||
// }
|
||||
// default:
|
||||
// {
|
||||
// SampleRate = 16000;
|
||||
// error("Invalid sample rate. Defaulting to 16000.");
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else if (Data->AudioCallback.Adjust._Channels)
|
||||
// {
|
||||
// switch (Data->AudioCallback.Adjust.Channels)
|
||||
// {
|
||||
// case 0:
|
||||
// {
|
||||
// Channels = 1; // Mono
|
||||
// break;
|
||||
// }
|
||||
// case 1:
|
||||
// {
|
||||
// Channels = 2; // Stereo
|
||||
// break;
|
||||
// }
|
||||
// default:
|
||||
// {
|
||||
// Channels = 2;
|
||||
// error("Invalid channel count. Defaulting to 2.");
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return 0;
|
||||
}
|
||||
|
||||
void OnInterruptReceived(TrapFrame *)
|
||||
{
|
||||
uint16_t Status = inw(MixerAddress + PCMOUT_Status);
|
||||
if (Status & TC_IOCInterruptEnable)
|
||||
{
|
||||
DebugLog("IOC");
|
||||
outw(MixerAddress + PCMOUT_Status, TC_IOCInterruptEnable);
|
||||
uint16_t CurrentBDL = inb(BusMasterAddress + PCMOUT_BufferDescriptorEntry);
|
||||
uint16_t LastBDL = (CurrentBDL + 2) & (DescriptorListLength - 1);
|
||||
outb(BusMasterAddress + PCMOUT_DescriptorEntries, LastBDL);
|
||||
KernelLog("FIXME: CurrentBDL: %d, LastBDL: %d", CurrentBDL, LastBDL);
|
||||
}
|
||||
else if (Status & TC_LastBufferEntryInterruptEnable)
|
||||
{
|
||||
DebugLog("Last buffer entry");
|
||||
// Stop DMA
|
||||
uint8_t TransferControl = inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl));
|
||||
TransferControl &= ~TC_DMAControllerControl;
|
||||
outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl), TransferControl);
|
||||
|
||||
outw(MixerAddress + PCMOUT_Status, TC_LastBufferEntryInterruptEnable);
|
||||
}
|
||||
else if (Status & TC_FifoERRORInterruptEnable)
|
||||
{
|
||||
KernelLog("FIFO error");
|
||||
outw(MixerAddress + PCMOUT_Status, TC_FifoERRORInterruptEnable);
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLog("Unknown interrupt status %#x", Status);
|
||||
outw(MixerAddress + PCMOUT_Status, 0xFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
void Panic()
|
||||
{
|
||||
uint8_t TransferControl = inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl));
|
||||
TransferControl &= ~(TC_LastBufferEntryInterruptEnable |
|
||||
TC_IOCInterruptEnable |
|
||||
TC_FifoERRORInterruptEnable);
|
||||
outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl), TransferControl);
|
||||
|
||||
uint32_t GlobalControl = inl((uint16_t)(BusMasterAddress + NABM_GlobalControl));
|
||||
GlobalControl &= ~GC_GlobalInterruptEnable;
|
||||
GlobalControl |= GC_ShutDown;
|
||||
outl((uint16_t)(BusMasterAddress + NABM_GlobalControl), GlobalControl);
|
||||
}
|
||||
|
||||
AC97Device(PCIHeader0 *_Header)
|
||||
: Header(_Header)
|
||||
{
|
||||
/* Native Audio Mixer Base Address */
|
||||
uint32_t PCIBAR0 = Header->BAR0;
|
||||
|
||||
/* Native Audio Bus Master Base Address */
|
||||
uint32_t PCIBAR1 = Header->BAR1;
|
||||
|
||||
// uint8_t Type = PCIBAR0 & 1;
|
||||
MixerAddress = (uint16_t)(PCIBAR0 & (~3));
|
||||
BusMasterAddress = PCIBAR1 & (~15);
|
||||
|
||||
uint16_t OutputPCMTransferControl = BusMasterAddress + PCMOUT_TransferControl;
|
||||
|
||||
/* DescriptorList address MUST be physical. */
|
||||
DescriptorList = (BufferDescriptorList *)AllocateMemory(TO_PAGES(sizeof(BufferDescriptorList) * DescriptorListLength));
|
||||
memset(DescriptorList, 0, sizeof(BufferDescriptorList) * DescriptorListLength);
|
||||
|
||||
uint16_t DLSampleCount = (uint16_t)(PAGE_SIZE / SampleSize);
|
||||
for (int i = 0; i < DescriptorListLength; i++)
|
||||
{
|
||||
DescriptorList[i].Address = (uint32_t)(uintptr_t)AllocateMemory(TO_PAGES(sizeof(uint16_t *)));
|
||||
DescriptorList[i].SampleCount = DLSampleCount;
|
||||
DescriptorList[i].Flags = 0;
|
||||
DebugLog("DescriptorList[%d] = { Address: %#lx, SampleCount: %d, Flags: %#lx }",
|
||||
i,
|
||||
DescriptorList[i].Address,
|
||||
DescriptorList[i].SampleCount,
|
||||
DescriptorList[i].Flags);
|
||||
}
|
||||
|
||||
outw(MixerAddress + NAM_MasterVolume, MixerVolume(Volume, Volume, Mute));
|
||||
outw(MixerAddress + NAM_PCMOutVolume, MixerVolume(Volume, Volume, Mute));
|
||||
|
||||
Volume = 0x3F - (0x3F * /* VOL 50% */ 50 / 100);
|
||||
outw(MixerAddress + NAM_MasterVolume, MixerVolume(Volume, Volume, Mute));
|
||||
|
||||
outb(OutputPCMTransferControl, inb(OutputPCMTransferControl) | TC_TransferReset);
|
||||
while (inb(OutputPCMTransferControl) & TC_TransferReset)
|
||||
;
|
||||
|
||||
uint32_t GlobalControl = inl(BusMasterAddress + NABM_GlobalControl);
|
||||
GlobalControl = (GlobalControl & ~((0x3U) << 0x16)); /* PCM 16-bit mode */
|
||||
GlobalControl = (GlobalControl & ~((0x3U) << 20)); /* 2 channels */
|
||||
GlobalControl |= GC_GlobalInterruptEnable;
|
||||
GlobalControl &= ~GC_ShutDown;
|
||||
|
||||
outl(BusMasterAddress + PCMOUT_BufferDescriptorList,
|
||||
(uint32_t)(uint64_t)DescriptorList);
|
||||
|
||||
outl(BusMasterAddress + NABM_GlobalControl, GlobalControl);
|
||||
|
||||
uint8_t TransferControl = inb(OutputPCMTransferControl);
|
||||
TransferControl |= TC_IOCInterruptEnable |
|
||||
TC_FifoERRORInterruptEnable;
|
||||
outb(OutputPCMTransferControl, TransferControl);
|
||||
|
||||
// Stop DMA
|
||||
outb(OutputPCMTransferControl, inb(OutputPCMTransferControl) & ~TC_DMAControllerControl);
|
||||
}
|
||||
|
||||
~AC97Device()
|
||||
{
|
||||
outw(MixerAddress + NAM_MasterVolume, MixerVolume(AV_Maximum, AV_Maximum, true));
|
||||
outw(MixerAddress + NAM_PCMOutVolume, MixerVolume(AV_Maximum, AV_Maximum, true));
|
||||
|
||||
// Stop DMA
|
||||
outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl),
|
||||
inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl)) & ~TC_DMAControllerControl);
|
||||
|
||||
// Disable interrupts
|
||||
uint8_t TransferControl = inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl));
|
||||
|
||||
TransferControl &= ~(TC_LastBufferEntryInterruptEnable |
|
||||
TC_IOCInterruptEnable |
|
||||
TC_FifoERRORInterruptEnable);
|
||||
outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl), TransferControl);
|
||||
|
||||
// Disable global control
|
||||
uint32_t GlobalControl = inl((uint16_t)(BusMasterAddress + NABM_GlobalControl));
|
||||
GlobalControl &= ~GC_GlobalInterruptEnable;
|
||||
GlobalControl |= GC_ShutDown;
|
||||
outl((uint16_t)(BusMasterAddress + NABM_GlobalControl), GlobalControl);
|
||||
}
|
||||
};
|
||||
|
||||
AC97Device *Drivers[4] = {nullptr};
|
||||
dev_t AudioID[4] = {(dev_t)-1};
|
||||
|
||||
#define OIR(x) OIR_##x
|
||||
#define CREATE_OIR(x) \
|
||||
void OIR_##x(TrapFrame *f) { Drivers[x]->OnInterruptReceived(f); }
|
||||
|
||||
CREATE_OIR(0);
|
||||
CREATE_OIR(1);
|
||||
CREATE_OIR(2);
|
||||
CREATE_OIR(3);
|
||||
|
||||
int __fs_Open(struct Inode *, int, mode_t) { return 0; }
|
||||
int __fs_Close(struct Inode *) { return 0; }
|
||||
ssize_t __fs_Read(struct Inode *, void *, size_t, off_t) { return 0; }
|
||||
|
||||
ssize_t __fs_Write(struct Inode *Node, const void *Buffer, size_t Size, off_t)
|
||||
{
|
||||
return Drivers[AudioID[Node->GetMinor()]]->write((uint8_t *)Buffer, Size);
|
||||
}
|
||||
|
||||
int __fs_Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
|
||||
{
|
||||
return Drivers[AudioID[Node->GetMinor()]]->ioctl((AudioIoctl)Request, Argp);
|
||||
}
|
||||
|
||||
const struct InodeOperations AudioOps = {
|
||||
.Lookup = nullptr,
|
||||
.Create = nullptr,
|
||||
.Remove = nullptr,
|
||||
.Rename = nullptr,
|
||||
.Read = __fs_Read,
|
||||
.Write = __fs_Write,
|
||||
.Truncate = nullptr,
|
||||
.Open = __fs_Open,
|
||||
.Close = __fs_Close,
|
||||
.Ioctl = __fs_Ioctl,
|
||||
.ReadDir = nullptr,
|
||||
.MkDir = nullptr,
|
||||
.RmDir = nullptr,
|
||||
.SymLink = nullptr,
|
||||
.ReadLink = nullptr,
|
||||
.Seek = nullptr,
|
||||
.Stat = nullptr,
|
||||
};
|
||||
|
||||
PCIArray *Devices;
|
||||
EXTERNC int cxx_Panic()
|
||||
{
|
||||
PCIArray *ctx = Devices;
|
||||
short Count = 0;
|
||||
while (ctx != nullptr)
|
||||
{
|
||||
if (Drivers[Count] != nullptr)
|
||||
Drivers[Count]->Panic();
|
||||
Count++;
|
||||
ctx = (PCIArray *)ctx->Next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXTERNC int cxx_Probe()
|
||||
{
|
||||
uint16_t VendorIDs[] = {0x8086, PCI_END};
|
||||
uint16_t DeviceIDs[] = {0x2415, PCI_END};
|
||||
Devices = GetPCIDevices(VendorIDs, DeviceIDs);
|
||||
if (Devices == nullptr)
|
||||
{
|
||||
KernelLog("No AC'97 device found.");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
PCIArray *ctx = Devices;
|
||||
bool Found = false;
|
||||
size_t Count = 0;
|
||||
while (ctx != nullptr)
|
||||
{
|
||||
if (Count++ > sizeof(Drivers) / sizeof(AC97Device *))
|
||||
break;
|
||||
|
||||
PCIHeader0 *PCIBaseAddress = (PCIHeader0 *)ctx->Device->Header;
|
||||
uint32_t PCIBAR0 = PCIBaseAddress->BAR0;
|
||||
uint8_t Type = PCIBAR0 & 1;
|
||||
if (Type != 1)
|
||||
{
|
||||
KernelLog("Device %x:%x.%d BAR0 is not I/O.",
|
||||
PCIBaseAddress->Header.VendorID,
|
||||
PCIBaseAddress->Header.DeviceID,
|
||||
PCIBaseAddress->Header.ProgIF);
|
||||
continue;
|
||||
}
|
||||
|
||||
Found = true;
|
||||
ctx = (PCIArray *)ctx->Next;
|
||||
}
|
||||
|
||||
if (!Found)
|
||||
{
|
||||
KernelLog("No valid AC'97 device found.");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXTERNC int cxx_Initialize()
|
||||
{
|
||||
PCIArray *ctx = Devices;
|
||||
size_t Count = 0;
|
||||
while (ctx != nullptr)
|
||||
{
|
||||
if (Count > sizeof(Drivers) / sizeof(AC97Device *))
|
||||
break;
|
||||
|
||||
PCIHeader0 *PCIBaseAddress = (PCIHeader0 *)ctx->Device->Header;
|
||||
uint32_t PCIBAR0 = PCIBaseAddress->BAR0;
|
||||
uint8_t Type = PCIBAR0 & 1;
|
||||
if (Type != 1)
|
||||
{
|
||||
KernelLog("Device %x:%x.%d BAR0 is not I/O.",
|
||||
PCIBaseAddress->Header.VendorID,
|
||||
PCIBaseAddress->Header.DeviceID,
|
||||
PCIBaseAddress->Header.ProgIF);
|
||||
continue;
|
||||
}
|
||||
|
||||
InitializePCI(ctx->Device);
|
||||
|
||||
Drivers[Count] = new AC97Device((PCIHeader0 *)ctx->Device->Header);
|
||||
/* FIXME: bad code */
|
||||
switch (Count)
|
||||
{
|
||||
case 0:
|
||||
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(0));
|
||||
break;
|
||||
case 1:
|
||||
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(1));
|
||||
break;
|
||||
case 2:
|
||||
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(2));
|
||||
break;
|
||||
case 3:
|
||||
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(3));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dev_t ret = RegisterDevice(AUDIO_TYPE_PCM, &AudioOps);
|
||||
AudioID[Count] = ret;
|
||||
Count++;
|
||||
ctx = (PCIArray *)ctx->Next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXTERNC int cxx_Finalize()
|
||||
{
|
||||
PCIArray *ctx = Devices;
|
||||
size_t Count = 0;
|
||||
while (ctx != nullptr)
|
||||
{
|
||||
if (Count++ > sizeof(Drivers) / sizeof(AC97Device *))
|
||||
break;
|
||||
|
||||
PCIHeader0 *PCIBaseAddress = (PCIHeader0 *)ctx->Device->Header;
|
||||
uint32_t PCIBAR0 = PCIBaseAddress->BAR0;
|
||||
uint8_t Type = PCIBAR0 & 1;
|
||||
if (Type != 1)
|
||||
{
|
||||
KernelLog("Device %x:%x.%d BAR0 is not I/O.",
|
||||
PCIBaseAddress->Header.VendorID,
|
||||
PCIBaseAddress->Header.DeviceID,
|
||||
PCIBaseAddress->Header.ProgIF);
|
||||
continue;
|
||||
}
|
||||
|
||||
delete Drivers[Count++];
|
||||
ctx->Device->Header->Command |= PCI_COMMAND_INTX_DISABLE;
|
||||
ctx = (PCIArray *)ctx->Next;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sizeof(AudioID) / sizeof(dev_t); i++)
|
||||
{
|
||||
if (AudioID[i] != (dev_t)-1)
|
||||
UnregisterDevice(AudioID[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
25
Drivers/audio/ac97/ac97.hpp
Normal file
25
Drivers/audio/ac97/ac97.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
This file is part of Fennix Drivers.
|
||||
|
||||
Fennix Drivers is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Drivers is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <types.h>
|
||||
|
||||
EXTERNC int cxx_Panic();
|
||||
EXTERNC int cxx_Probe();
|
||||
EXTERNC int cxx_Initialize();
|
||||
EXTERNC int cxx_Finalize();
|
31
Drivers/audio/ac97/main.c
Normal file
31
Drivers/audio/ac97/main.c
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
This file is part of Fennix Drivers.
|
||||
|
||||
Fennix Drivers is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Drivers is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <base.h>
|
||||
|
||||
#include "ac97.hpp"
|
||||
|
||||
int DriverEntry() { return cxx_Initialize(); }
|
||||
int DriverFinal() { return cxx_Finalize(); }
|
||||
int DriverPanic() { return cxx_Panic(); }
|
||||
int DriverProbe() { return cxx_Probe(); }
|
||||
|
||||
DriverInfo("ac97",
|
||||
"Audio Codec '97 Driver",
|
||||
"EnderIce2",
|
||||
0, 0, 1,
|
||||
"GPLv3");
|
Reference in New Issue
Block a user