forked from Bananymous/banan-os
				
			Kernel: Implement basic AC97 driver
This commit is contained in:
		
							parent
							
								
									674e194a91
								
							
						
					
					
						commit
						8a663cb94f
					
				|  | @ -6,6 +6,8 @@ set(KERNEL_SOURCES | ||||||
| 	kernel/ACPI/AML/OpRegion.cpp | 	kernel/ACPI/AML/OpRegion.cpp | ||||||
| 	kernel/ACPI/BatterySystem.cpp | 	kernel/ACPI/BatterySystem.cpp | ||||||
| 	kernel/APIC.cpp | 	kernel/APIC.cpp | ||||||
|  | 	kernel/Audio/AC97/Controller.cpp | ||||||
|  | 	kernel/Audio/Controller.cpp | ||||||
| 	kernel/BootInfo.cpp | 	kernel/BootInfo.cpp | ||||||
| 	kernel/CPUID.cpp | 	kernel/CPUID.cpp | ||||||
| 	kernel/Credentials.cpp | 	kernel/Credentials.cpp | ||||||
|  |  | ||||||
|  | @ -0,0 +1,51 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <kernel/Audio/Controller.h> | ||||||
|  | #include <kernel/Memory/DMARegion.h> | ||||||
|  | 
 | ||||||
|  | namespace Kernel | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | 	class AC97AudioController : public AudioController, public Interruptable | ||||||
|  | 	{ | ||||||
|  | 	public: | ||||||
|  | 		static BAN::ErrorOr<BAN::RefPtr<AC97AudioController>> create(PCI::Device& pci_device); | ||||||
|  | 
 | ||||||
|  | 		void handle_irq() override; | ||||||
|  | 
 | ||||||
|  | 	protected: | ||||||
|  | 		void handle_new_data() override; | ||||||
|  | 
 | ||||||
|  | 		uint32_t get_channels() const override { return 2; } | ||||||
|  | 		uint32_t get_sample_rate() const override { return 48000; } | ||||||
|  | 
 | ||||||
|  | 	private: | ||||||
|  | 		AC97AudioController(PCI::Device& pci_device) | ||||||
|  | 			: m_pci_device(pci_device) | ||||||
|  | 		{ } | ||||||
|  | 
 | ||||||
|  | 		BAN::ErrorOr<void> initialize(); | ||||||
|  | 		BAN::ErrorOr<void> initialize_bld(); | ||||||
|  | 		BAN::ErrorOr<void> initialize_interrupts(); | ||||||
|  | 
 | ||||||
|  | 		void queue_samples_to_bld(); | ||||||
|  | 
 | ||||||
|  | 	private: | ||||||
|  | 		static constexpr size_t m_bdl_entries = 32; | ||||||
|  | 		static constexpr size_t m_samples_per_entry = 0x1000; | ||||||
|  | 
 | ||||||
|  | 		// We only store samples in 2 BDL entries at a time to reduce the amount of samples queued.
 | ||||||
|  | 		// This is to reduce latency as you cannot remove data already passed to the BDLs
 | ||||||
|  | 		static constexpr size_t m_used_bdl_entries = 2; | ||||||
|  | 
 | ||||||
|  | 		PCI::Device& m_pci_device; | ||||||
|  | 		BAN::UniqPtr<PCI::BarRegion> m_mixer; | ||||||
|  | 		BAN::UniqPtr<PCI::BarRegion> m_bus_master; | ||||||
|  | 
 | ||||||
|  | 		BAN::UniqPtr<DMARegion> m_bdl_region; | ||||||
|  | 
 | ||||||
|  | 		uint32_t m_bdl_tail { 0 }; | ||||||
|  | 		uint32_t m_bdl_head { 0 }; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,15 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | namespace Kernel::AC97 | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | 	struct BufferDescriptorListEntry | ||||||
|  | 	{ | ||||||
|  | 		uint32_t address; | ||||||
|  | 		uint16_t samples; | ||||||
|  | 		uint16_t flags; // bit 14: last entry, bit 15: IOC
 | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,48 @@ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <kernel/Device/Device.h> | ||||||
|  | #include <kernel/PCI.h> | ||||||
|  | 
 | ||||||
|  | namespace Kernel | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | 	class AudioController : public CharacterDevice | ||||||
|  | 	{ | ||||||
|  | 	public: | ||||||
|  | 		static BAN::ErrorOr<BAN::RefPtr<AudioController>> create(PCI::Device& pci_device); | ||||||
|  | 
 | ||||||
|  | 		dev_t rdev() const override { return m_rdev; } | ||||||
|  | 		BAN::StringView name() const override { return m_name; } | ||||||
|  | 
 | ||||||
|  | 	protected: | ||||||
|  | 		AudioController(); | ||||||
|  | 
 | ||||||
|  | 		virtual void handle_new_data() = 0; | ||||||
|  | 
 | ||||||
|  | 		virtual uint32_t get_channels() const = 0; | ||||||
|  | 		virtual uint32_t get_sample_rate() const = 0; | ||||||
|  | 
 | ||||||
|  | 		bool can_read_impl() const override { return false; } | ||||||
|  | 		bool can_write_impl() const override { SpinLockGuard _(m_spinlock); return m_sample_data_size < m_sample_data_capacity; } | ||||||
|  | 		bool has_error_impl() const override { return false; } | ||||||
|  | 		bool has_hungup_impl() const override { return false; } | ||||||
|  | 
 | ||||||
|  | 		BAN::ErrorOr<size_t> write_impl(off_t, BAN::ConstByteSpan) override; | ||||||
|  | 
 | ||||||
|  | 		BAN::ErrorOr<long> ioctl_impl(int cmd, void* arg) override; | ||||||
|  | 
 | ||||||
|  | 	protected: | ||||||
|  | 		ThreadBlocker m_sample_data_blocker; | ||||||
|  | 		mutable SpinLock m_spinlock; | ||||||
|  | 
 | ||||||
|  | 		static constexpr size_t m_sample_data_capacity = 1 << 20; | ||||||
|  | 		uint8_t m_sample_data[m_sample_data_capacity]; | ||||||
|  | 		size_t m_sample_data_head { 0 }; | ||||||
|  | 		size_t m_sample_data_size { 0 }; | ||||||
|  | 
 | ||||||
|  | 	private: | ||||||
|  | 		const dev_t m_rdev; | ||||||
|  | 		char m_name[10] {}; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -22,6 +22,7 @@ namespace Kernel | ||||||
| 		Ethernet, | 		Ethernet, | ||||||
| 		Loopback, | 		Loopback, | ||||||
| 		TmpFS, | 		TmpFS, | ||||||
|  | 		AudioController, | ||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,265 @@ | ||||||
|  | #include <kernel/Audio/AC97/Controller.h> | ||||||
|  | #include <kernel/Audio/AC97/Definitions.h> | ||||||
|  | 
 | ||||||
|  | namespace Kernel | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | 	enum AudioMixerRegister : uint8_t | ||||||
|  | 	{ | ||||||
|  | 		Reset = 0x00, | ||||||
|  | 		MasterVolume = 0x02, | ||||||
|  | 		AuxOutVolume = 0x04, | ||||||
|  | 		MonoVolume = 0x06, | ||||||
|  | 		MasterToneRL = 0x08, | ||||||
|  | 		PC_BEEPVolume = 0x0A, | ||||||
|  | 		PhoneVolume = 0x0C, | ||||||
|  | 		MicVolume = 0x0E, | ||||||
|  | 		LineInVolume = 0x10, | ||||||
|  | 		CDVolume = 0x12, | ||||||
|  | 		VideoVolume = 0x14, | ||||||
|  | 		AuxInVolume = 0x16, | ||||||
|  | 		PCMOutVolume = 0x18, | ||||||
|  | 		RecordSelect = 0x1A, | ||||||
|  | 		RecordGain = 0x1C, | ||||||
|  | 		RecordGainMic = 0x1E, | ||||||
|  | 		GeneralPurpose = 0x20, | ||||||
|  | 		_3DControl = 0x22, | ||||||
|  | 		PowerdownCtrlStat = 0x26, | ||||||
|  | 		ExtendedAudio = 0x28, | ||||||
|  | 		ExtendedAudioCtrlStat = 0x2A, | ||||||
|  | 		PCMFrontDACRate = 0x2C, | ||||||
|  | 		PCMSurroundDACRate = 0x2E, | ||||||
|  | 		PCMLFEDACRate = 0x30, | ||||||
|  | 		PCMLRADCRate = 0x32, | ||||||
|  | 		MICADCRate = 0x34, | ||||||
|  | 		_6ChVolC_LFE = 0x36, | ||||||
|  | 		_6ChVolL_R_Surround = 0x38, | ||||||
|  | 		S_PDIFControl = 0x3A | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	enum BusMasterRegister : uint8_t | ||||||
|  | 	{ | ||||||
|  | 		PI_BDBAR = 0x00, | ||||||
|  | 		PI_CIV = 0x04, | ||||||
|  | 		PI_LVI = 0x05, | ||||||
|  | 		PI_SR = 0x06, | ||||||
|  | 		PI_PICB = 0x08, | ||||||
|  | 		PI_PIV = 0x0A, | ||||||
|  | 		PI_CR = 0x0B, | ||||||
|  | 		PO_BDBAR = 0x10, | ||||||
|  | 		PO_CIV = 0x14, | ||||||
|  | 		PO_LVI = 0x15, | ||||||
|  | 		PO_SR = 0x16, | ||||||
|  | 		PO_PICB = 0x18, | ||||||
|  | 		PO_PIV = 0x1A, | ||||||
|  | 		PO_CR = 0x1B, | ||||||
|  | 		MC_BDBAR = 0x20, | ||||||
|  | 		MC_CIV = 0x24, | ||||||
|  | 		MC_LVI = 0x25, | ||||||
|  | 		MC_SR = 0x26, | ||||||
|  | 		MC_PICB = 0x28, | ||||||
|  | 		MC_PIV = 0x2A, | ||||||
|  | 		MC_CR = 0x2B, | ||||||
|  | 		GLOB_CNT = 0x2C, | ||||||
|  | 		GLOB_STA = 0x30, | ||||||
|  | 		CAS = 0x34, | ||||||
|  | 		MC2_BDBAR = 0x40, | ||||||
|  | 		MC2_CIV = 0x44, | ||||||
|  | 		MC2_LVI = 0x45, | ||||||
|  | 		MC2_SR = 0x46, | ||||||
|  | 		MC2_PICB = 0x48, | ||||||
|  | 		MC2_PIV = 0x4A, | ||||||
|  | 		MC2_CR = 0x4B, | ||||||
|  | 		PI2_BDBAR = 0x50, | ||||||
|  | 		PI2_CIV = 0x54, | ||||||
|  | 		PI2_LVI = 0x55, | ||||||
|  | 		PI2_SR = 0x56, | ||||||
|  | 		PI2_PICB = 0x58, | ||||||
|  | 		PI2_PIV = 0x5A, | ||||||
|  | 		PI2_CR = 0x5B, | ||||||
|  | 		SPBAR = 0x60, | ||||||
|  | 		SPCIV = 0x64, | ||||||
|  | 		SPLVI = 0x65, | ||||||
|  | 		SPSR = 0x66, | ||||||
|  | 		SPPICB = 0x68, | ||||||
|  | 		SPPIV = 0x6A, | ||||||
|  | 		SPCR = 0x6B, | ||||||
|  | 		SDM = 0x80, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	enum BusMasterStatus : uint16_t | ||||||
|  | 	{ | ||||||
|  | 		DMAHalted = 1 << 0, | ||||||
|  | 		CELV      = 1 << 1, | ||||||
|  | 		LVBCI     = 1 << 2, | ||||||
|  | 		BCIS      = 1 << 3, | ||||||
|  | 		FIFOE     = 1 << 4, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	enum BusMasterControl : uint8_t | ||||||
|  | 	{ | ||||||
|  | 		RDBM  = 1 << 0, | ||||||
|  | 		RR    = 1 << 1, | ||||||
|  | 		LVBIE = 1 << 2, | ||||||
|  | 		FEIE  = 1 << 3, | ||||||
|  | 		IOCE  = 1 << 4, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	BAN::ErrorOr<BAN::RefPtr<AC97AudioController>> AC97AudioController::create(PCI::Device& pci_device) | ||||||
|  | 	{ | ||||||
|  | 		auto* ac97_ptr = new AC97AudioController(pci_device); | ||||||
|  | 		if (ac97_ptr == nullptr) | ||||||
|  | 			return BAN::Error::from_errno(ENOMEM); | ||||||
|  | 		auto ac97 = BAN::RefPtr<AC97AudioController>::adopt(ac97_ptr); | ||||||
|  | 		TRY(ac97->initialize()); | ||||||
|  | 		return ac97; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	BAN::ErrorOr<void> AC97AudioController::initialize() | ||||||
|  | 	{ | ||||||
|  | 		m_pci_device.enable_bus_mastering(); | ||||||
|  | 
 | ||||||
|  | 		m_mixer = TRY(m_pci_device.allocate_bar_region(0)); | ||||||
|  | 		m_bus_master = TRY(m_pci_device.allocate_bar_region(1)); | ||||||
|  | 
 | ||||||
|  | 		if (m_mixer->size() < 0x34 || m_bus_master->size() < 0x34) | ||||||
|  | 			return BAN::Error::from_errno(ENODEV); | ||||||
|  | 
 | ||||||
|  | 		// Reset bus master
 | ||||||
|  | 		m_bus_master->write32(BusMasterRegister::GLOB_CNT, 0x00); | ||||||
|  | 
 | ||||||
|  | 		// no interrupts, 2 channels, 16 bit samples
 | ||||||
|  | 		m_bus_master->write32(BusMasterRegister::GLOB_CNT, 0x02); | ||||||
|  | 
 | ||||||
|  | 		// Reset mixer to default values
 | ||||||
|  | 		m_mixer->write16(AudioMixerRegister::Reset, 0); | ||||||
|  | 
 | ||||||
|  | 		// Master volume 100%, no mute
 | ||||||
|  | 		m_mixer->write16(AudioMixerRegister::MasterVolume, 0x0000); | ||||||
|  | 
 | ||||||
|  | 		// PCM output volume left/right +0 db, no mute
 | ||||||
|  | 		m_mixer->write16(AudioMixerRegister::PCMOutVolume, 0x0808); | ||||||
|  | 
 | ||||||
|  | 		TRY(initialize_bld()); | ||||||
|  | 
 | ||||||
|  | 		TRY(initialize_interrupts()); | ||||||
|  | 
 | ||||||
|  | 		// disable transfer, enable all interrupts
 | ||||||
|  | 		m_bus_master->write8(BusMasterRegister::PO_CR, IOCE | FEIE | LVBIE); | ||||||
|  | 
 | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	BAN::ErrorOr<void> AC97AudioController::initialize_bld() | ||||||
|  | 	{ | ||||||
|  | 		const size_t bdl_size = sizeof(AC97::BufferDescriptorListEntry) * m_bdl_entries; | ||||||
|  | 		const size_t buffer_size = m_samples_per_entry * sizeof(int16_t); | ||||||
|  | 
 | ||||||
|  | 		m_bdl_region = TRY(DMARegion::create(bdl_size + buffer_size * m_used_bdl_entries)); | ||||||
|  | 		memset(reinterpret_cast<void*>(m_bdl_region->vaddr()), 0x00, m_bdl_region->size()); | ||||||
|  | 
 | ||||||
|  | 		for (size_t i = 0; i < m_bdl_entries; i++) | ||||||
|  | 		{ | ||||||
|  | 			auto& entry = reinterpret_cast<AC97::BufferDescriptorListEntry*>(m_bdl_region->vaddr())[i]; | ||||||
|  | 			entry.address = m_bdl_region->paddr() + bdl_size + (i % m_used_bdl_entries) * buffer_size; | ||||||
|  | 			entry.samples = 0; | ||||||
|  | 			entry.flags = 0; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		m_bus_master->write32(BusMasterRegister::PO_BDBAR, m_bdl_region->paddr()); | ||||||
|  | 
 | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	BAN::ErrorOr<void> AC97AudioController::initialize_interrupts() | ||||||
|  | 	{ | ||||||
|  | 		TRY(m_pci_device.reserve_interrupts(1)); | ||||||
|  | 		m_pci_device.enable_interrupt(0, *this); | ||||||
|  | 
 | ||||||
|  | 		// enable interrupts
 | ||||||
|  | 		m_bus_master->write32(BusMasterRegister::GLOB_CNT, m_bus_master->read32(BusMasterRegister::GLOB_CNT) | 0x01); | ||||||
|  | 
 | ||||||
|  | 		return {}; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void AC97AudioController::handle_new_data() | ||||||
|  | 	{ | ||||||
|  | 		ASSERT(m_spinlock.current_processor_has_lock()); | ||||||
|  | 
 | ||||||
|  | 		if (m_bdl_head != m_bdl_tail) | ||||||
|  | 			return; | ||||||
|  | 
 | ||||||
|  | 		queue_samples_to_bld(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void AC97AudioController::queue_samples_to_bld() | ||||||
|  | 	{ | ||||||
|  | 		ASSERT(m_spinlock.current_processor_has_lock()); | ||||||
|  | 
 | ||||||
|  | 		uint32_t lvi = m_bdl_head; | ||||||
|  | 
 | ||||||
|  | 		while (m_bdl_head != (m_bdl_tail + m_used_bdl_entries) % m_bdl_entries) | ||||||
|  | 		{ | ||||||
|  | 			const uint32_t next_bld_head = (m_bdl_head + 1) % m_bdl_entries; | ||||||
|  | 			if (next_bld_head == m_bdl_tail) | ||||||
|  | 				break; | ||||||
|  | 
 | ||||||
|  | 			const size_t sample_data_tail = (m_sample_data_head + m_sample_data_capacity - m_sample_data_size) % m_sample_data_capacity; | ||||||
|  | 
 | ||||||
|  | 			const size_t max_memcpy = BAN::Math::min(m_sample_data_size, m_sample_data_capacity - sample_data_tail); | ||||||
|  | 			const size_t samples = BAN::Math::min(max_memcpy / 2, m_samples_per_entry); | ||||||
|  | 			if (samples == 0) | ||||||
|  | 				break; | ||||||
|  | 
 | ||||||
|  | 			auto& entry = reinterpret_cast<AC97::BufferDescriptorListEntry*>(m_bdl_region->vaddr())[m_bdl_head]; | ||||||
|  | 			entry.samples = samples; | ||||||
|  | 			entry.flags = (1 << 15); | ||||||
|  | 			memcpy( | ||||||
|  | 				reinterpret_cast<void*>(m_bdl_region->paddr_to_vaddr(entry.address)), | ||||||
|  | 				&m_sample_data[sample_data_tail], | ||||||
|  | 				samples * 2 | ||||||
|  | 			); | ||||||
|  | 
 | ||||||
|  | 			m_sample_data_size -= samples * 2; | ||||||
|  | 
 | ||||||
|  | 			lvi = m_bdl_head; | ||||||
|  | 			m_bdl_head = next_bld_head; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// if head was not updated, no data was queued
 | ||||||
|  | 		if (lvi == m_bdl_head) | ||||||
|  | 			return; | ||||||
|  | 
 | ||||||
|  | 		m_sample_data_blocker.unblock(); | ||||||
|  | 
 | ||||||
|  | 		m_bus_master->write8(BusMasterRegister::PO_LVI, lvi); | ||||||
|  | 
 | ||||||
|  | 		// start playing if we are not already
 | ||||||
|  | 		const uint8_t control = m_bus_master->read8(BusMasterRegister::PO_CR); | ||||||
|  | 		if (!(control & RDBM)) | ||||||
|  | 			m_bus_master->write8(BusMasterRegister::PO_CR, control | RDBM); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void AC97AudioController::handle_irq() | ||||||
|  | 	{ | ||||||
|  | 		const uint16_t status = m_bus_master->read16(BusMasterRegister::PO_SR); | ||||||
|  | 		if (!(status & (LVBCI | BCIS | FIFOE))) | ||||||
|  | 			return; | ||||||
|  | 		m_bus_master->write16(BusMasterRegister::PO_SR, LVBCI | BCIS | FIFOE); | ||||||
|  | 
 | ||||||
|  | 		SpinLockGuard _(m_spinlock); | ||||||
|  | 
 | ||||||
|  | 		if (status & LVBCI) | ||||||
|  | 		{ | ||||||
|  | 			const uint8_t control = m_bus_master->read8(BusMasterRegister::PO_CR); | ||||||
|  | 			m_bus_master->write8(BusMasterRegister::PO_CR, control & ~RDBM); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (status & BCIS) | ||||||
|  | 		{ | ||||||
|  | 			m_bdl_tail = (m_bdl_tail + 1) % m_bdl_entries; | ||||||
|  | 			queue_samples_to_bld(); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,105 @@ | ||||||
|  | #include <kernel/Audio/AC97/Controller.h> | ||||||
|  | #include <kernel/Audio/Controller.h> | ||||||
|  | #include <kernel/Device/DeviceNumbers.h> | ||||||
|  | #include <kernel/FS/DevFS/FileSystem.h> | ||||||
|  | #include <kernel/Lock/SpinLockAsMutex.h> | ||||||
|  | 
 | ||||||
|  | #include <sys/ioctl.h> | ||||||
|  | #include <sys/sysmacros.h> | ||||||
|  | 
 | ||||||
|  | namespace Kernel | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | 	static BAN::Atomic<dev_t> s_next_audio_minor = 0; | ||||||
|  | 
 | ||||||
|  | 	AudioController::AudioController() | ||||||
|  | 		: CharacterDevice(0644, 0, 0) | ||||||
|  | 		, m_rdev(makedev(DeviceNumber::AudioController, s_next_audio_minor++)) | ||||||
|  | 	{ | ||||||
|  | 		char* ptr = m_name; | ||||||
|  | 		BAN::Formatter::print([&ptr](char c) { *ptr++ = c; }, "audio{}", minor(m_rdev)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	BAN::ErrorOr<BAN::RefPtr<AudioController>> AudioController::create(PCI::Device& pci_device) | ||||||
|  | 	{ | ||||||
|  | 		switch (pci_device.subclass()) | ||||||
|  | 		{ | ||||||
|  | 			case 0x01: | ||||||
|  | 				// We should confirm that the card is actually AC97 but I'm trusting osdev wiki on this one
 | ||||||
|  | 				// > you can probably expect that every sound card with subclass 0x01 is sound card compatibile with AC97
 | ||||||
|  | 				if (auto ret = AC97AudioController::create(pci_device); !ret.is_error()) | ||||||
|  | 				{ | ||||||
|  | 					DevFileSystem::get().add_device(ret.value()); | ||||||
|  | 					return BAN::RefPtr<AudioController>(ret.release_value()); | ||||||
|  | 				} | ||||||
|  | 				else | ||||||
|  | 				{ | ||||||
|  | 					dwarnln("Failed to initialize AC97: {}", ret.error()); | ||||||
|  | 					return ret.release_error(); | ||||||
|  | 				} | ||||||
|  | 			default: | ||||||
|  | 				dprintln("Unsupported Sound card (PCI {2H}:{2H}:{2H})", | ||||||
|  | 					pci_device.class_code(), | ||||||
|  | 					pci_device.subclass(), | ||||||
|  | 					pci_device.prog_if() | ||||||
|  | 				); | ||||||
|  | 				return BAN::Error::from_errno(ENOTSUP); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	BAN::ErrorOr<size_t> AudioController::write_impl(off_t, BAN::ConstByteSpan buffer) | ||||||
|  | 	{ | ||||||
|  | 		SpinLockGuard lock_guard(m_spinlock); | ||||||
|  | 
 | ||||||
|  | 		while (m_sample_data_size >= m_sample_data_capacity) | ||||||
|  | 		{ | ||||||
|  | 			SpinLockGuardAsMutex smutex(lock_guard); | ||||||
|  | 			TRY(Thread::current().block_or_eintr_indefinite(m_sample_data_blocker, &smutex)); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		size_t nwritten = 0; | ||||||
|  | 		while (nwritten < buffer.size()) | ||||||
|  | 		{ | ||||||
|  | 			if (m_sample_data_size >= m_sample_data_capacity) | ||||||
|  | 				break; | ||||||
|  | 
 | ||||||
|  | 			const size_t max_memcpy = BAN::Math::min(m_sample_data_capacity - m_sample_data_size, m_sample_data_capacity - m_sample_data_head); | ||||||
|  | 			const size_t to_copy = BAN::Math::min(buffer.size() - nwritten, max_memcpy); | ||||||
|  | 			memcpy(m_sample_data + m_sample_data_head, buffer.data() + nwritten, to_copy); | ||||||
|  | 
 | ||||||
|  | 			nwritten += to_copy; | ||||||
|  | 			m_sample_data_head = (m_sample_data_head + to_copy) % m_sample_data_capacity; | ||||||
|  | 			m_sample_data_size += to_copy; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		handle_new_data(); | ||||||
|  | 
 | ||||||
|  | 		return nwritten; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	BAN::ErrorOr<long> AudioController::ioctl_impl(int cmd, void* arg) | ||||||
|  | 	{ | ||||||
|  | 		switch (cmd) | ||||||
|  | 		{ | ||||||
|  | 			case SND_GET_CHANNELS: | ||||||
|  | 				*static_cast<uint32_t*>(arg) = get_channels(); | ||||||
|  | 				return 0; | ||||||
|  | 			case SND_GET_SAMPLE_RATE: | ||||||
|  | 				*static_cast<uint32_t*>(arg) = get_sample_rate(); | ||||||
|  | 				return 0; | ||||||
|  | 			case SND_RESET_BUFFER: | ||||||
|  | 			case SND_GET_BUFFERSZ: | ||||||
|  | 			{ | ||||||
|  | 				SpinLockGuard _(m_spinlock); | ||||||
|  | 				*static_cast<uint32_t*>(arg) = m_sample_data_size; | ||||||
|  | 				if (cmd == SND_RESET_BUFFER) | ||||||
|  | 					m_sample_data_size = 0; | ||||||
|  | 				return 0; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return CharacterDevice::ioctl_impl(cmd, arg); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -1,7 +1,8 @@ | ||||||
| #include <BAN/ScopeGuard.h> | #include <BAN/ScopeGuard.h> | ||||||
| 
 | 
 | ||||||
| #include <kernel/APIC.h> |  | ||||||
| #include <kernel/ACPI/ACPI.h> | #include <kernel/ACPI/ACPI.h> | ||||||
|  | #include <kernel/APIC.h> | ||||||
|  | #include <kernel/Audio/Controller.h> | ||||||
| #include <kernel/IDT.h> | #include <kernel/IDT.h> | ||||||
| #include <kernel/IO.h> | #include <kernel/IO.h> | ||||||
| #include <kernel/Memory/PageTable.h> | #include <kernel/Memory/PageTable.h> | ||||||
|  | @ -228,6 +229,18 @@ namespace Kernel::PCI | ||||||
| 							dprintln("{}", res.error()); | 							dprintln("{}", res.error()); | ||||||
| 						break; | 						break; | ||||||
| 					} | 					} | ||||||
|  | 					case 0x04: | ||||||
|  | 					{ | ||||||
|  | 						switch (pci_device.subclass()) | ||||||
|  | 						{ | ||||||
|  | 							case 0x01: | ||||||
|  | 							case 0x03: | ||||||
|  | 								if (auto res = AudioController::create(pci_device); res.is_error()) | ||||||
|  | 									dprintln("Sound Card: {}", res.error()); | ||||||
|  | 								break; | ||||||
|  | 						} | ||||||
|  | 						break; | ||||||
|  | 					} | ||||||
| 					case 0x0C: | 					case 0x0C: | ||||||
| 					{ | 					{ | ||||||
| 						switch (pci_device.subclass()) | 						switch (pci_device.subclass()) | ||||||
|  |  | ||||||
|  | @ -47,6 +47,11 @@ struct winsize | ||||||
| #define TIOCGWINSZ	50 | #define TIOCGWINSZ	50 | ||||||
| #define TIOCSWINSZ	51 | #define TIOCSWINSZ	51 | ||||||
| 
 | 
 | ||||||
|  | #define SND_GET_CHANNELS    60 /* stores number of channels to uint32_t argument */ | ||||||
|  | #define SND_GET_SAMPLE_RATE 61 /* stores sample rate to uint32_t argument */ | ||||||
|  | #define SND_RESET_BUFFER    62 /* stores the size of internal buffer to uint32_t argument and clears the buffer */ | ||||||
|  | #define SND_GET_BUFFERSZ    63 /* stores the size of internal buffer to uint32_t argument */ | ||||||
|  | 
 | ||||||
| int ioctl(int, int, ...); | int ioctl(int, int, ...); | ||||||
| 
 | 
 | ||||||
| __END_DECLS | __END_DECLS | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue