Skip to content

Commit 6c4024c

Browse files
rburchellawesomekling
authored andcommitted
Kernel: First cut of a sb16 driver
Also add an AudioServer that (right now) doesn't do much. It tries to open, parse, and play a wav file. In the future, it can do more. My general thinking here here is that /dev/audio will be "owned" by AudioServer, and we'll do mixing in software before passing buffers off to the kernel to play, but we have to start somewhere.
1 parent 6e671f7 commit 6c4024c

File tree

11 files changed

+436
-4
lines changed

11 files changed

+436
-4
lines changed

Kernel/Devices/SB16.cpp

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
#include "SB16.h"
2+
#include "IO.h"
3+
#include <Kernel/VM/MemoryManager.h>
4+
5+
const u16 DSP_READ = 0x22A;
6+
const u16 DSP_WRITE = 0x22C;
7+
const u16 DSP_STATUS = 0x22E;
8+
const u16 DSP_R_ACK = 0x22F;
9+
10+
/* Write a value to the DSP write register */
11+
void SB16::dsp_write(u8 value)
12+
{
13+
while (IO::in8(DSP_WRITE) & 0x80) {
14+
;
15+
}
16+
IO::out8(DSP_WRITE, value);
17+
}
18+
19+
/* Reads the value of the DSP read register */
20+
u8 SB16::dsp_read()
21+
{
22+
while (!(IO::in8(DSP_STATUS) & 0x80)) {
23+
;
24+
}
25+
return IO::in8(DSP_READ);
26+
}
27+
28+
/* Changes the sample rate of sound output */
29+
void SB16::set_sample_rate(uint16_t hz)
30+
{
31+
dsp_write(0x41); // output
32+
dsp_write((u8)(hz >> 8));
33+
dsp_write((u8)hz);
34+
dsp_write(0x42); // input
35+
dsp_write((u8)(hz >> 8));
36+
dsp_write((u8)hz);
37+
}
38+
39+
static SB16* s_the;
40+
41+
SB16::SB16()
42+
: IRQHandler(5)
43+
, CharacterDevice(42, 42) // ### ?
44+
{
45+
s_the = this;
46+
initialize();
47+
}
48+
49+
SB16::~SB16()
50+
{
51+
}
52+
53+
SB16& SB16::the()
54+
{
55+
return *s_the;
56+
}
57+
58+
void SB16::handle_irq()
59+
{
60+
m_interrupted = true;
61+
62+
// Stop sound output ready for the next block.
63+
dsp_write(0xd0);
64+
65+
IO::in8(DSP_STATUS); // 8 bit interrupt
66+
if (m_major_version >= 4)
67+
IO::in8(DSP_R_ACK); // 16 bit interrupt
68+
}
69+
70+
void SB16::initialize()
71+
{
72+
IO::out8(0x226, 1);
73+
IO::delay();
74+
IO::out8(0x226, 0);
75+
76+
auto data = dsp_read();
77+
if (data != 0xaa) {
78+
kprintf("SB16: sb not ready");
79+
return;
80+
}
81+
82+
// Get the version info
83+
dsp_write(0xe1);
84+
m_major_version = dsp_read();
85+
auto vmin = dsp_read();
86+
87+
kprintf("SB16: found version %d.%d\n", m_major_version, vmin);
88+
enable_irq();
89+
}
90+
91+
bool SB16::can_read(FileDescription&) const
92+
{
93+
return false;
94+
}
95+
96+
ssize_t SB16::read(FileDescription&, u8*, ssize_t)
97+
{
98+
return 0;
99+
}
100+
101+
void SB16::dma_start(uint32_t length)
102+
{
103+
const auto addr = m_dma_buffer_page->paddr().get();
104+
const u8 channel = 1;
105+
const u8 mode = 0;
106+
107+
// Disable the DMA channel
108+
IO::out8(0x0a, 4 + (channel % 4));
109+
110+
// Clear the byte pointer flip-flop
111+
IO::out8(0x0c, 0);
112+
113+
// Write the DMA mode for the transfer
114+
IO::out8(0x0b, channel | mode);
115+
116+
// Write the offset of the buffer
117+
IO::out8(0x02, (u8)addr);
118+
IO::out8(0x02, (u8)(addr >> 8));
119+
120+
// Write the transfer length
121+
IO::out8(0x03, (u8)(length - 1));
122+
IO::out8(0x03, (u8)((length - 1) >> 8));
123+
124+
// Write the buffer
125+
IO::out8(0x83, addr >> 16);
126+
127+
// Enable the DMA channel
128+
IO::out8(0x0a, channel);
129+
}
130+
131+
void SB16::wait_for_irq()
132+
{
133+
m_interrupted = false;
134+
#ifdef SB16_DEBUG
135+
kprintf("SB16: waiting for interrupt...\n");
136+
#endif
137+
// FIXME: Add timeout.
138+
while (!m_interrupted) {
139+
// FIXME: Put this process into a Blocked state instead, it's stupid to wake up just to check a flag.
140+
Scheduler::yield();
141+
}
142+
#ifdef SB16_DEBUG
143+
kprintf("SB16: got interrupt!\n");
144+
#endif
145+
memory_barrier();
146+
}
147+
148+
ssize_t SB16::write(FileDescription&, const u8* data, ssize_t length)
149+
{
150+
if (!m_dma_buffer_page) {
151+
kprintf("SB16: Allocating page\n");
152+
m_dma_buffer_page = MM.allocate_supervisor_physical_page();
153+
}
154+
155+
kprintf("SB16: Writing buffer of %d bytes\n", length);
156+
const int BLOCK_SIZE = 32 * 1024;
157+
if (length > BLOCK_SIZE) {
158+
return -ENOSPC;
159+
}
160+
const u8 mode = 0;
161+
162+
disable_irq();
163+
const int sample_rate = 44100;
164+
set_sample_rate(sample_rate);
165+
dma_start(length);
166+
memcpy(m_dma_buffer_page->paddr().as_ptr(), data, length);
167+
168+
u8 command = 0x06;
169+
command |= 0xc0;
170+
171+
dsp_write(command);
172+
dsp_write(mode);
173+
dsp_write((u8)length);
174+
dsp_write((u8)(length >> 8));
175+
176+
enable_irq();
177+
wait_for_irq();
178+
return length;
179+
}

Kernel/Devices/SB16.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#pragma once
2+
3+
#include <AK/CircularQueue.h>
4+
#include <Kernel/Devices/CharacterDevice.h>
5+
#include <Kernel/IRQHandler.h>
6+
#include <Kernel/VM/PhysicalAddress.h>
7+
#include <Kernel/VM/PhysicalPage.h>
8+
9+
class SB16 final : public IRQHandler
10+
, public CharacterDevice {
11+
public:
12+
SB16();
13+
virtual ~SB16() override;
14+
15+
static SB16& the();
16+
17+
// ^CharacterDevice
18+
virtual bool can_read(FileDescription&) const override;
19+
virtual ssize_t read(FileDescription&, u8*, ssize_t) override;
20+
virtual ssize_t write(FileDescription&, const u8*, ssize_t) override;
21+
virtual bool can_write(FileDescription&) const override { return true; }
22+
23+
private:
24+
// ^IRQHandler
25+
virtual void handle_irq() override;
26+
27+
// ^CharacterDevice
28+
virtual const char* class_name() const override { return "SB16"; }
29+
30+
void initialize();
31+
void wait_for_irq();
32+
void dma_start(uint32_t length);
33+
void set_sample_rate(uint16_t hz);
34+
void dsp_write(u8 value);
35+
u8 dsp_read();
36+
37+
RefPtr<PhysicalPage> m_dma_buffer_page;
38+
bool m_interrupted { false };
39+
int m_major_version { 0 };
40+
};

Kernel/IO.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,12 @@ inline void repeated_out16(u16 port, const u8* data, int data_size)
6161
: "d"(port));
6262
}
6363

64+
inline void delay()
65+
{
66+
// ~3 microsecs
67+
for (auto i = 0; i < 32; i++) {
68+
IO::in8(0x80);
69+
}
70+
}
71+
6472
}

Kernel/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ VFS_OBJS = \
7575
FileSystem/Ext2FileSystem.o \
7676
FileSystem/VirtualFileSystem.o \
7777
FileSystem/FileDescription.o \
78-
FileSystem/SyntheticFileSystem.o
78+
FileSystem/SyntheticFileSystem.o \
79+
Devices/SB16.o
7980

8081
AK_OBJS = \
8182
../AK/String.o \

Kernel/build-root-filesystem.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ mknod -m 666 mnt/dev/full c 1 7
3838
mknod -m 666 mnt/dev/debuglog c 1 18
3939
mknod mnt/dev/keyboard c 85 1
4040
mknod mnt/dev/psaux c 10 1
41+
mknod -m 666 mnt/dev/audio c 42 42
4142
mknod -m 666 mnt/dev/ptmx c 5 2
4243
ln -s /proc/self/fd/0 mnt/dev/stdin
4344
ln -s /proc/self/fd/1 mnt/dev/stdout
@@ -87,6 +88,7 @@ cp ../Games/Snake/Snake mnt/bin/Snake
8788
cp ../Servers/LookupServer/LookupServer mnt/bin/LookupServer
8889
cp ../Servers/SystemServer/SystemServer mnt/bin/SystemServer
8990
cp ../Servers/WindowServer/WindowServer mnt/bin/WindowServer
91+
cp ../Servers/AudioServer/AudioServer mnt/bin/AudioServer
9092
cp ../Shell/Shell mnt/bin/Shell
9193
cp ../Libraries/LibHTML/tho mnt/bin/tho
9294
echo "done"

Kernel/init.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <Kernel/Devices/MBRPartitionTable.h>
1717
#include <Kernel/Devices/NullDevice.h>
1818
#include <Kernel/Devices/PS2MouseDevice.h>
19+
#include <Kernel/Devices/SB16.h>
1920
#include <Kernel/Devices/RandomDevice.h>
2021
#include <Kernel/Devices/SerialDevice.h>
2122
#include <Kernel/Devices/ZeroDevice.h>
@@ -39,6 +40,7 @@ VirtualConsole* tty2;
3940
VirtualConsole* tty3;
4041
KeyboardDevice* keyboard;
4142
PS2MouseDevice* ps2mouse;
43+
SB16* sb16;
4244
DebugLogDevice* dev_debuglog;
4345
NullDevice* dev_null;
4446
SerialDevice* ttyS0;
@@ -177,6 +179,7 @@ extern "C" [[noreturn]] void init()
177179

178180
keyboard = new KeyboardDevice;
179181
ps2mouse = new PS2MouseDevice;
182+
sb16 = new SB16;
180183
dev_null = new NullDevice;
181184
ttyS0 = new SerialDevice(SERIAL_COM1_ADDR, 64);
182185
ttyS1 = new SerialDevice(SERIAL_COM2_ADDR, 65);

Kernel/makeall.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ build_targets="$build_targets ../Libraries/LibCore"
2121
build_targets="$build_targets ../Servers/SystemServer"
2222
build_targets="$build_targets ../Servers/LookupServer"
2323
build_targets="$build_targets ../Servers/WindowServer"
24+
build_targets="$build_targets ../Servers/AudioServer"
2425
build_targets="$build_targets ../Libraries/LibGUI"
2526
build_targets="$build_targets ../Libraries/LibHTML"
2627
build_targets="$build_targets ../Userland"

Kernel/run

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ elif [ "$1" = "qn" ]; then
2020
-kernel kernel \
2121
-append "${SERENITY_KERNEL_CMDLINE}" \
2222
-hda _disk_image \
23-
-soundhw pcspk
23+
-soundhw pcspk \
24+
-soundhw sb16
2425
elif [ "$1" = "qtap" ]; then
2526
# ./run qtap: qemu with tap
2627
sudo $SERENITY_QEMU_BIN -s -m ${SERENITY_RAM_SIZE:-128} \
@@ -34,7 +35,8 @@ elif [ "$1" = "qtap" ]; then
3435
-kernel kernel \
3536
-append "${SERENITY_KERNEL_CMDLINE}" \
3637
-hda _disk_image \
37-
-soundhw pcspk
38+
-soundhw pcspk \
39+
-soundhw sb16
3840
elif [ "$1" = "qgrub" ]; then
3941
# ./run qgrub: qemu with grub
4042
$SERENITY_QEMU_BIN -s -m ${SERENITY_RAM_SIZE:-128} \
@@ -60,6 +62,7 @@ else
6062
-kernel kernel \
6163
-append "${SERENITY_KERNEL_CMDLINE}" \
6264
-hda _disk_image \
63-
-soundhw pcspk
65+
-soundhw pcspk \
66+
-soundhw sb16
6467
fi
6568

Servers/AudioServer/Makefile

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
include ../../Makefile.common
2+
3+
AUDIOSERVER_OBJS = \
4+
main.o
5+
6+
APP = AudioServer
7+
OBJS = $(AUDIOSERVER_OBJS)
8+
9+
DEFINES += -DUSERLAND
10+
11+
all: $(APP)
12+
13+
$(APP): $(OBJS)
14+
$(LD) -o $(APP) $(LDFLAGS) $(OBJS) -lc -lcore
15+
16+
.cpp.o:
17+
@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $<
18+
19+
-include $(OBJS:%.o=%.d)
20+
21+
clean:
22+
@echo "CLEAN"; rm -f $(APP) $(OBJS) *.d
23+

0 commit comments

Comments
 (0)