Compare commits
19 Commits
5c20d5e291
...
f77aa65dc5
| Author | SHA1 | Date | |
|---|---|---|---|
| f77aa65dc5 | |||
| 9589b5984d | |||
| 32806a5af3 | |||
| 876fbe3d7c | |||
| c1b8f5e475 | |||
| cf31ea9cbe | |||
| 7e6b8c93b4 | |||
| dd2bbe4588 | |||
| e01e35713b | |||
| 82d5d9ba58 | |||
| d168492462 | |||
| 6f2e8320a9 | |||
| bf4831f468 | |||
| 5647cf24d2 | |||
| 85f61aded5 | |||
| 21639071c2 | |||
| 68506a789a | |||
| d9ca25b796 | |||
| e9c81477d7 |
@@ -6,19 +6,6 @@
|
||||
|
||||
#include <float.h>
|
||||
|
||||
// This is ugly but my clangd does not like including
|
||||
// intrinsic headers at all
|
||||
#if !defined(__SSE__) || !defined(__SSE2__)
|
||||
#pragma GCC push_options
|
||||
#ifndef __SSE__
|
||||
#pragma GCC target("sse")
|
||||
#endif
|
||||
#ifndef __SSE2__
|
||||
#pragma GCC target("sse2")
|
||||
#endif
|
||||
#define BAN_MATH_POP_OPTIONS
|
||||
#endif
|
||||
|
||||
namespace BAN::Math
|
||||
{
|
||||
|
||||
@@ -49,12 +36,11 @@ namespace BAN::Math
|
||||
template<integral T>
|
||||
inline constexpr T gcd(T a, T b)
|
||||
{
|
||||
T t;
|
||||
while (b)
|
||||
{
|
||||
t = b;
|
||||
T temp = b;
|
||||
b = a % b;
|
||||
a = t;
|
||||
a = temp;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
@@ -79,25 +65,20 @@ namespace BAN::Math
|
||||
return (x & (x - 1)) == 0;
|
||||
}
|
||||
|
||||
template<BAN::integral T>
|
||||
static constexpr bool will_multiplication_overflow(T a, T b)
|
||||
template<integral T>
|
||||
__attribute__((always_inline))
|
||||
inline constexpr bool will_multiplication_overflow(T a, T b)
|
||||
{
|
||||
if (a == 0 || b == 0)
|
||||
return false;
|
||||
if ((a > 0) == (b > 0))
|
||||
return a > BAN::numeric_limits<T>::max() / b;
|
||||
else
|
||||
return a < BAN::numeric_limits<T>::min() / b;
|
||||
T dummy;
|
||||
return __builtin_mul_overflow(a, b, &dummy);
|
||||
}
|
||||
|
||||
template<BAN::integral T>
|
||||
static constexpr bool will_addition_overflow(T a, T b)
|
||||
template<integral T>
|
||||
__attribute__((always_inline))
|
||||
inline constexpr bool will_addition_overflow(T a, T b)
|
||||
{
|
||||
if (a > 0 && b > 0)
|
||||
return a > BAN::numeric_limits<T>::max() - b;
|
||||
if (a < 0 && b < 0)
|
||||
return a < BAN::numeric_limits<T>::min() - b;
|
||||
return false;
|
||||
T dummy;
|
||||
return __builtin_add_overflow(a, b, &dummy);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -111,6 +92,19 @@ namespace BAN::Math
|
||||
return sizeof(T) * 8 - __builtin_clzll(x) - 1;
|
||||
}
|
||||
|
||||
// This is ugly but my clangd does not like including
|
||||
// intrinsic headers at all
|
||||
#if !defined(__SSE__) || !defined(__SSE2__)
|
||||
#pragma GCC push_options
|
||||
#ifndef __SSE__
|
||||
#pragma GCC target("sse")
|
||||
#endif
|
||||
#ifndef __SSE2__
|
||||
#pragma GCC target("sse2")
|
||||
#endif
|
||||
#define BAN_MATH_POP_OPTIONS
|
||||
#endif
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T floor(T x)
|
||||
{
|
||||
@@ -172,7 +166,23 @@ namespace BAN::Math
|
||||
"jne 1b;"
|
||||
: "+t"(a)
|
||||
: "u"(b)
|
||||
: "ax"
|
||||
: "ax", "cc"
|
||||
);
|
||||
return a;
|
||||
}
|
||||
|
||||
template<floating_point T>
|
||||
inline constexpr T remainder(T a, T b)
|
||||
{
|
||||
asm(
|
||||
"1:"
|
||||
"fprem1;"
|
||||
"fnstsw %%ax;"
|
||||
"testb $4, %%ah;"
|
||||
"jne 1b;"
|
||||
: "+t"(a)
|
||||
: "u"(b)
|
||||
: "ax", "cc"
|
||||
);
|
||||
return a;
|
||||
}
|
||||
@@ -447,9 +457,9 @@ namespace BAN::Math
|
||||
return sqrt<T>(x * x + y * y);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifdef BAN_MATH_POP_OPTIONS
|
||||
#undef BAN_MATH_POP_OPTIONS
|
||||
#pragma GCC pop_options
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -139,6 +139,7 @@ if("${BANAN_ARCH}" STREQUAL "x86_64")
|
||||
arch/x86_64/Signal.S
|
||||
arch/x86_64/Syscall.S
|
||||
arch/x86_64/Thread.S
|
||||
arch/x86_64/User.S
|
||||
arch/x86_64/Yield.S
|
||||
)
|
||||
elseif("${BANAN_ARCH}" STREQUAL "i686")
|
||||
@@ -150,6 +151,7 @@ elseif("${BANAN_ARCH}" STREQUAL "i686")
|
||||
arch/i686/Signal.S
|
||||
arch/i686/Syscall.S
|
||||
arch/i686/Thread.S
|
||||
arch/i686/User.S
|
||||
arch/i686/Yield.S
|
||||
)
|
||||
else()
|
||||
|
||||
54
kernel/arch/i686/User.S
Normal file
54
kernel/arch/i686/User.S
Normal file
@@ -0,0 +1,54 @@
|
||||
# bool safe_user_memcpy(void*, const void*, size_t)
|
||||
.global safe_user_memcpy
|
||||
.global safe_user_memcpy_end
|
||||
.global safe_user_memcpy_fault
|
||||
safe_user_memcpy:
|
||||
xorl %eax, %eax
|
||||
xchgl 4(%esp), %edi
|
||||
xchgl 8(%esp), %esi
|
||||
movl 12(%esp), %ecx
|
||||
movl %edi, %edx
|
||||
rep movsb
|
||||
movl 4(%esp), %edi
|
||||
movl 8(%esp), %esi
|
||||
incl %eax
|
||||
safe_user_memcpy_fault:
|
||||
ret
|
||||
safe_user_memcpy_end:
|
||||
|
||||
# bool safe_user_strncpy(void*, const void*, size_t)
|
||||
.global safe_user_strncpy
|
||||
.global safe_user_strncpy_end
|
||||
.global safe_user_strncpy_fault
|
||||
safe_user_strncpy:
|
||||
xchgl 4(%esp), %edi
|
||||
xchgl 8(%esp), %esi
|
||||
movl 12(%esp), %ecx
|
||||
|
||||
testl %ecx, %ecx
|
||||
jz safe_user_strncpy_fault
|
||||
|
||||
.safe_user_strncpy_loop:
|
||||
movb (%esi), %al
|
||||
movb %al, (%edi)
|
||||
testb %al, %al
|
||||
jz .safe_user_strncpy_done
|
||||
|
||||
incl %edi
|
||||
incl %esi
|
||||
decl %ecx
|
||||
jnz .safe_user_strncpy_loop
|
||||
|
||||
safe_user_strncpy_fault:
|
||||
xorl %eax, %eax
|
||||
jmp .safe_user_strncpy_return
|
||||
|
||||
.safe_user_strncpy_done:
|
||||
movl $1, %eax
|
||||
|
||||
.safe_user_strncpy_return:
|
||||
movl 4(%esp), %edi
|
||||
movl 8(%esp), %esi
|
||||
ret
|
||||
|
||||
safe_user_strncpy_end:
|
||||
@@ -866,7 +866,7 @@ namespace Kernel
|
||||
last_address -= rem;
|
||||
|
||||
ASSERT(is_canonical(first_address));
|
||||
ASSERT(is_canonical(last_address));
|
||||
ASSERT(is_canonical(last_address - 1));
|
||||
const vaddr_t uc_vaddr_start = uncanonicalize(first_address);
|
||||
const vaddr_t uc_vaddr_end = uncanonicalize(last_address);
|
||||
|
||||
@@ -948,7 +948,7 @@ namespace Kernel
|
||||
last_address -= rem;
|
||||
|
||||
ASSERT(is_canonical(first_address));
|
||||
ASSERT(is_canonical(last_address));
|
||||
ASSERT(is_canonical(last_address - 1));
|
||||
|
||||
SpinLockGuard _(m_lock);
|
||||
|
||||
|
||||
87
kernel/arch/x86_64/User.S
Normal file
87
kernel/arch/x86_64/User.S
Normal file
@@ -0,0 +1,87 @@
|
||||
# bool safe_user_memcpy(void*, const void*, size_t)
|
||||
.global safe_user_memcpy
|
||||
.global safe_user_memcpy_end
|
||||
.global safe_user_memcpy_fault
|
||||
safe_user_memcpy:
|
||||
xorq %rax, %rax
|
||||
movq %rdx, %rcx
|
||||
rep movsb
|
||||
incq %rax
|
||||
safe_user_memcpy_fault:
|
||||
ret
|
||||
safe_user_memcpy_end:
|
||||
|
||||
# bool safe_user_strncpy(void*, const void*, size_t)
|
||||
.global safe_user_strncpy
|
||||
.global safe_user_strncpy_end
|
||||
.global safe_user_strncpy_fault
|
||||
safe_user_strncpy:
|
||||
movq %rdx, %rcx
|
||||
testq %rcx, %rcx
|
||||
jz safe_user_strncpy_fault
|
||||
|
||||
.safe_user_strncpy_align_loop:
|
||||
testb $0x7, %sil
|
||||
jz .safe_user_strncpy_align_done
|
||||
|
||||
movb (%rsi), %al
|
||||
movb %al, (%rdi)
|
||||
testb %al, %al
|
||||
jz .safe_user_strncpy_done
|
||||
|
||||
incq %rdi
|
||||
incq %rsi
|
||||
decq %rcx
|
||||
jnz .safe_user_strncpy_align_loop
|
||||
jmp safe_user_strncpy_fault
|
||||
|
||||
.safe_user_strncpy_align_done:
|
||||
movq $0x0101010101010101, %r8
|
||||
movq $0x8080808080808080, %r9
|
||||
|
||||
.safe_user_strncpy_qword_loop:
|
||||
cmpq $8, %rcx
|
||||
jb .safe_user_strncpy_qword_done
|
||||
|
||||
movq (%rsi), %rax
|
||||
movq %rax, %r10
|
||||
movq %rax, %r11
|
||||
|
||||
# https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
|
||||
subq %r8, %r10
|
||||
notq %r11
|
||||
andq %r11, %r10
|
||||
andq %r9, %r10
|
||||
jnz .safe_user_strncpy_byte_loop
|
||||
|
||||
movq %rax, (%rdi)
|
||||
|
||||
addq $8, %rdi
|
||||
addq $8, %rsi
|
||||
subq $8, %rcx
|
||||
jnz .safe_user_strncpy_qword_loop
|
||||
jmp safe_user_strncpy_fault
|
||||
|
||||
.safe_user_strncpy_qword_done:
|
||||
testq %rcx, %rcx
|
||||
jz safe_user_strncpy_fault
|
||||
|
||||
.safe_user_strncpy_byte_loop:
|
||||
movb (%rsi), %al
|
||||
movb %al, (%rdi)
|
||||
testb %al, %al
|
||||
jz .safe_user_strncpy_done
|
||||
|
||||
incq %rdi
|
||||
incq %rsi
|
||||
decq %rcx
|
||||
jnz .safe_user_strncpy_byte_loop
|
||||
|
||||
safe_user_strncpy_fault:
|
||||
xorq %rax, %rax
|
||||
ret
|
||||
|
||||
.safe_user_strncpy_done:
|
||||
movb $1, %al
|
||||
ret
|
||||
safe_user_strncpy_end:
|
||||
@@ -6,7 +6,7 @@
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
class AC97AudioController : public AudioController, public Interruptable
|
||||
class AC97AudioController final : public AudioController, public Interruptable
|
||||
{
|
||||
public:
|
||||
static BAN::ErrorOr<void> create(PCI::Device& pci_device);
|
||||
@@ -23,11 +23,15 @@ namespace Kernel
|
||||
uint32_t get_current_pin() const override { return 0; }
|
||||
BAN::ErrorOr<void> set_current_pin(uint32_t pin) override { if (pin != 0) return BAN::Error::from_errno(EINVAL); return {}; }
|
||||
|
||||
BAN::ErrorOr<void> set_volume_mdB(int32_t) override;
|
||||
|
||||
private:
|
||||
AC97AudioController(PCI::Device& pci_device)
|
||||
: m_pci_device(pci_device)
|
||||
{ }
|
||||
|
||||
uint32_t get_volume_data() const;
|
||||
|
||||
BAN::ErrorOr<void> initialize();
|
||||
BAN::ErrorOr<void> initialize_bld();
|
||||
BAN::ErrorOr<void> initialize_interrupts();
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <kernel/Memory/ByteRingBuffer.h>
|
||||
#include <kernel/PCI.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
namespace Kernel
|
||||
{
|
||||
|
||||
@@ -28,6 +30,8 @@ namespace Kernel
|
||||
virtual uint32_t get_current_pin() const = 0;
|
||||
virtual BAN::ErrorOr<void> set_current_pin(uint32_t) = 0;
|
||||
|
||||
virtual BAN::ErrorOr<void> set_volume_mdB(int32_t) = 0;
|
||||
|
||||
bool can_read_impl() const override { return false; }
|
||||
bool can_write_impl() const override { SpinLockGuard _(m_spinlock); return !m_sample_data->full(); }
|
||||
bool has_error_impl() const override { return false; }
|
||||
@@ -44,6 +48,8 @@ namespace Kernel
|
||||
static constexpr size_t m_sample_data_capacity = 1 << 20;
|
||||
BAN::UniqPtr<ByteRingBuffer> m_sample_data;
|
||||
|
||||
snd_volume_info m_volume_info {};
|
||||
|
||||
private:
|
||||
const dev_t m_rdev;
|
||||
char m_name[10] {};
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Kernel
|
||||
|
||||
class HDAudioController;
|
||||
|
||||
class HDAudioFunctionGroup : public AudioController
|
||||
class HDAudioFunctionGroup final : public AudioController
|
||||
{
|
||||
public:
|
||||
static BAN::ErrorOr<BAN::RefPtr<HDAudioFunctionGroup>> create(BAN::RefPtr<HDAudioController>, uint8_t cid, HDAudio::AFGNode&&);
|
||||
@@ -24,6 +24,8 @@ namespace Kernel
|
||||
uint32_t get_current_pin() const override;
|
||||
BAN::ErrorOr<void> set_current_pin(uint32_t) override;
|
||||
|
||||
BAN::ErrorOr<void> set_volume_mdB(int32_t) override;
|
||||
|
||||
void handle_new_data() override;
|
||||
|
||||
private:
|
||||
@@ -46,7 +48,6 @@ namespace Kernel
|
||||
BAN::ErrorOr<void> recurse_output_paths(const HDAudio::AFGWidget& widget, BAN::Vector<const HDAudio::AFGWidget*>& path);
|
||||
|
||||
uint16_t get_format_data() const;
|
||||
uint16_t get_volume_data() const;
|
||||
|
||||
size_t bdl_offset() const;
|
||||
|
||||
|
||||
@@ -55,6 +55,16 @@ namespace Kernel::HDAudio
|
||||
} pin_complex;
|
||||
};
|
||||
|
||||
struct Amplifier
|
||||
{
|
||||
uint8_t offset;
|
||||
uint8_t num_steps;
|
||||
uint8_t step_size;
|
||||
bool mute;
|
||||
};
|
||||
|
||||
BAN::Optional<Amplifier> output_amplifier;
|
||||
|
||||
BAN::Vector<uint16_t> connections;
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#if ARCH(x86_64)
|
||||
#define KERNEL_OFFSET 0xFFFFFFFF80000000
|
||||
#define USERSPACE_END 0xFFFF800000000000
|
||||
#define USERSPACE_END 0x800000000000
|
||||
#elif ARCH(i686)
|
||||
#define KERNEL_OFFSET 0xC0000000
|
||||
#define USERSPACE_END 0xC0000000
|
||||
|
||||
@@ -137,8 +137,27 @@ namespace Kernel
|
||||
// Reset mixer to default values
|
||||
m_mixer->write16(AudioMixerRegister::Reset, 0);
|
||||
|
||||
// Master volume 100%, no mute
|
||||
m_mixer->write16(AudioMixerRegister::MasterVolume, 0x0000);
|
||||
// Master volumes
|
||||
m_mixer->write16(AudioMixerRegister::MasterVolume, 0x2020);
|
||||
if (m_mixer->read16(AudioMixerRegister::MasterVolume) == 0x2020)
|
||||
{
|
||||
m_volume_info = {
|
||||
.min_mdB = -94500,
|
||||
.max_mdB = 0,
|
||||
.step_mdB = 1500,
|
||||
.mdB = 0,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
m_volume_info = {
|
||||
.min_mdB = -46500,
|
||||
.max_mdB = 0,
|
||||
.step_mdB = 1500,
|
||||
.mdB = 0,
|
||||
};
|
||||
}
|
||||
m_mixer->write16(AudioMixerRegister::MasterVolume, get_volume_data());
|
||||
|
||||
// PCM output volume left/right +0 db, no mute
|
||||
m_mixer->write16(AudioMixerRegister::PCMOutVolume, 0x0808);
|
||||
@@ -187,6 +206,19 @@ namespace Kernel
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t AC97AudioController::get_volume_data() const
|
||||
{
|
||||
const uint32_t steps = (-m_volume_info.mdB + m_volume_info.step_mdB / 2) / m_volume_info.step_mdB;
|
||||
return (steps << 8) | steps;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> AC97AudioController::set_volume_mdB(int32_t mdB)
|
||||
{
|
||||
m_volume_info.mdB = BAN::Math::clamp(mdB, m_volume_info.min_mdB, m_volume_info.max_mdB);
|
||||
m_mixer->write16(AudioMixerRegister::MasterVolume, get_volume_data());
|
||||
return {};
|
||||
}
|
||||
|
||||
void AC97AudioController::handle_new_data()
|
||||
{
|
||||
ASSERT(m_spinlock.current_processor_has_lock());
|
||||
|
||||
@@ -105,6 +105,12 @@ namespace Kernel
|
||||
case SND_SET_PIN:
|
||||
TRY(set_current_pin(*static_cast<uint32_t*>(arg)));
|
||||
return 0;
|
||||
case SND_GET_VOLUME_INFO:
|
||||
*static_cast<snd_volume_info*>(arg) = m_volume_info;
|
||||
return 0;
|
||||
case SND_SET_VOLUME_MDB:
|
||||
TRY(set_volume_mdB(*static_cast<int32_t*>(arg)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return CharacterDevice::ioctl_impl(cmd, arg);
|
||||
|
||||
@@ -136,6 +136,37 @@ namespace Kernel
|
||||
return BAN::Error::from_errno(ENOTSUP);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> HDAudioFunctionGroup::set_volume_mdB(int32_t mdB)
|
||||
{
|
||||
mdB = BAN::Math::clamp(mdB, m_volume_info.min_mdB, m_volume_info.max_mdB);
|
||||
|
||||
const auto& path = m_output_paths[m_output_path_index];
|
||||
for (size_t i = 0; i < path.size(); i++)
|
||||
{
|
||||
if (!path[i]->output_amplifier.has_value())
|
||||
continue;
|
||||
|
||||
const int32_t step_round = (mdB >= 0)
|
||||
? +m_volume_info.step_mdB / 2
|
||||
: -m_volume_info.step_mdB / 2;
|
||||
const uint32_t step = (mdB + step_round) / m_volume_info.step_mdB + path[i]->output_amplifier->offset;
|
||||
const uint32_t volume = 0b1'0'1'1'0000'0'0000000 | step;
|
||||
|
||||
TRY(m_controller->send_command({
|
||||
.data = static_cast<uint8_t>(volume & 0xFF),
|
||||
.command = static_cast<uint16_t>(0x300 | (volume >> 8)),
|
||||
.node_index = path[i]->id,
|
||||
.codec_address = m_cid,
|
||||
}));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
m_volume_info.mdB = mdB;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t HDAudioFunctionGroup::bdl_offset() const
|
||||
{
|
||||
const size_t bdl_entry_bytes = m_bdl_entry_sample_frames * get_channels() * sizeof(uint16_t);
|
||||
@@ -291,13 +322,6 @@ namespace Kernel
|
||||
return 0b0'0'000'000'0'001'0001;
|
||||
}
|
||||
|
||||
uint16_t HDAudioFunctionGroup::get_volume_data() const
|
||||
{
|
||||
// TODO: don't hardcode this
|
||||
// left and right output, no mute, max gain
|
||||
return 0b1'0'1'1'0000'0'1111111;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> HDAudioFunctionGroup::enable_output_path(uint8_t index)
|
||||
{
|
||||
ASSERT(index < m_output_paths.size());
|
||||
@@ -318,7 +342,6 @@ namespace Kernel
|
||||
}
|
||||
|
||||
const auto format = get_format_data();
|
||||
const auto volume = get_volume_data();
|
||||
|
||||
for (size_t i = 0; i < path.size(); i++)
|
||||
{
|
||||
@@ -347,13 +370,17 @@ namespace Kernel
|
||||
}));
|
||||
}
|
||||
|
||||
// set volume
|
||||
// set volume to 0 dB, no mute
|
||||
if (path[i]->output_amplifier.has_value())
|
||||
{
|
||||
const uint32_t volume = 0b1'0'1'1'0000'0'0000000 | path[i]->output_amplifier->offset;
|
||||
TRY(m_controller->send_command({
|
||||
.data = static_cast<uint8_t>(volume & 0xFF),
|
||||
.command = static_cast<uint16_t>(0x300 | (volume >> 8)),
|
||||
.node_index = path[i]->id,
|
||||
.codec_address = m_cid,
|
||||
}));
|
||||
}
|
||||
|
||||
switch (path[i]->type)
|
||||
{
|
||||
@@ -398,6 +425,41 @@ namespace Kernel
|
||||
}
|
||||
}
|
||||
|
||||
// update volume info to this path
|
||||
m_volume_info.min_mdB = 0;
|
||||
m_volume_info.max_mdB = 0;
|
||||
m_volume_info.step_mdB = 0;
|
||||
for (size_t i = 0; i < path.size(); i++)
|
||||
{
|
||||
if (!path[i]->output_amplifier.has_value())
|
||||
continue;
|
||||
const auto& amp = path[i]->output_amplifier.value();
|
||||
|
||||
const int32_t step_mdB = amp.step_size * 250;
|
||||
m_volume_info.step_mdB = step_mdB;
|
||||
m_volume_info.min_mdB = -amp.offset * step_mdB;
|
||||
m_volume_info.max_mdB = (amp.num_steps - amp.offset) * step_mdB;
|
||||
m_volume_info.mdB = BAN::Math::clamp(m_volume_info.mdB, m_volume_info.min_mdB, m_volume_info.max_mdB);
|
||||
|
||||
const int32_t step_round = (m_volume_info.mdB >= 0)
|
||||
? +step_mdB / 2
|
||||
: -step_mdB / 2;
|
||||
const uint32_t step = (m_volume_info.mdB + step_round) / step_mdB + amp.offset;
|
||||
const uint32_t volume = 0b1'0'1'1'0000'0'0000000 | step;
|
||||
|
||||
TRY(m_controller->send_command({
|
||||
.data = static_cast<uint8_t>(volume & 0xFF),
|
||||
.command = static_cast<uint16_t>(0x300 | (volume >> 8)),
|
||||
.node_index = path[i]->id,
|
||||
.codec_address = m_cid,
|
||||
}));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_volume_info.min_mdB == 0 && m_volume_info.max_mdB == 0)
|
||||
m_volume_info.mdB = 0;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -319,6 +319,20 @@ namespace Kernel
|
||||
};
|
||||
}
|
||||
|
||||
if (const uint32_t out_amp_cap = send_command_or_zero(0xF00, 0x12))
|
||||
{
|
||||
const uint8_t offset = (out_amp_cap >> 0) & 0x7F;
|
||||
const uint8_t num_steps = (out_amp_cap >> 8) & 0x7F;
|
||||
const uint8_t step_size = (out_amp_cap >> 16) & 0x7F;
|
||||
const bool mute = (out_amp_cap >> 31);
|
||||
result.output_amplifier = HDAudio::AFGWidget::Amplifier {
|
||||
.offset = offset,
|
||||
.num_steps = num_steps,
|
||||
.step_size = step_size,
|
||||
.mute = mute,
|
||||
};
|
||||
}
|
||||
|
||||
const uint8_t connection_info = send_command_or_zero(0xF00, 0x0E);
|
||||
const uint8_t conn_width = (connection_info & 0x80) ? 2 : 1;
|
||||
const uint8_t conn_count = connection_info & 0x3F;
|
||||
|
||||
@@ -164,6 +164,33 @@ namespace Kernel
|
||||
"Unkown Exception 0x1F",
|
||||
};
|
||||
|
||||
extern "C" uint8_t safe_user_memcpy[];
|
||||
extern "C" uint8_t safe_user_memcpy_end[];
|
||||
extern "C" uint8_t safe_user_memcpy_fault[];
|
||||
|
||||
extern "C" uint8_t safe_user_strncpy[];
|
||||
extern "C" uint8_t safe_user_strncpy_end[];
|
||||
extern "C" uint8_t safe_user_strncpy_fault[];
|
||||
|
||||
struct safe_user_page_fault
|
||||
{
|
||||
const uint8_t* ip_start;
|
||||
const uint8_t* ip_end;
|
||||
const uint8_t* ip_fault;
|
||||
};
|
||||
static constexpr safe_user_page_fault s_safe_user_page_faults[] {
|
||||
{
|
||||
.ip_start = safe_user_memcpy,
|
||||
.ip_end = safe_user_memcpy_end,
|
||||
.ip_fault = safe_user_memcpy_fault,
|
||||
},
|
||||
{
|
||||
.ip_start = safe_user_strncpy,
|
||||
.ip_end = safe_user_strncpy_end,
|
||||
.ip_fault = safe_user_strncpy_fault,
|
||||
},
|
||||
};
|
||||
|
||||
extern "C" void cpp_isr_handler(uint32_t isr, uint32_t error, InterruptStack* interrupt_stack, const Registers* regs)
|
||||
{
|
||||
if (g_paniced)
|
||||
@@ -201,6 +228,15 @@ namespace Kernel
|
||||
if (result.value())
|
||||
return;
|
||||
|
||||
const uint8_t* ip = reinterpret_cast<const uint8_t*>(interrupt_stack->ip);
|
||||
for (const auto& safe_user : s_safe_user_page_faults)
|
||||
{
|
||||
if (ip < safe_user.ip_start || ip >= safe_user.ip_end)
|
||||
continue;
|
||||
interrupt_stack->ip = reinterpret_cast<vaddr_t>(safe_user.ip_fault);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ISR::DeviceNotAvailable:
|
||||
|
||||
@@ -136,6 +136,9 @@ namespace Kernel::Input
|
||||
m_scancode_to_keycode_extended[0x49] = keycode_function(18);
|
||||
m_scancode_to_keycode_extended[0x51] = keycode_function(19);
|
||||
m_scancode_to_keycode_normal[0x46] = keycode_function(20);
|
||||
m_scancode_to_keycode_extended[0x20] = keycode_function(21);
|
||||
m_scancode_to_keycode_extended[0x2E] = keycode_function(22);
|
||||
m_scancode_to_keycode_extended[0x30] = keycode_function(23);
|
||||
|
||||
// Arrow keys
|
||||
m_scancode_to_keycode_extended[0x48] = keycode_normal(5, 0);
|
||||
@@ -246,6 +249,9 @@ namespace Kernel::Input
|
||||
m_scancode_to_keycode_extended[0x7D] = keycode_function(18);
|
||||
m_scancode_to_keycode_extended[0x7A] = keycode_function(19);
|
||||
m_scancode_to_keycode_normal[0x7E] = keycode_function(20);
|
||||
m_scancode_to_keycode_extended[0x23] = keycode_function(21);
|
||||
m_scancode_to_keycode_extended[0x21] = keycode_function(22);
|
||||
m_scancode_to_keycode_extended[0x32] = keycode_function(23);
|
||||
|
||||
// Arrow keys
|
||||
m_scancode_to_keycode_extended[0x75] = keycode_normal(5, 0);
|
||||
@@ -356,6 +362,9 @@ namespace Kernel::Input
|
||||
m_scancode_to_keycode_normal[0x6F] = keycode_function(18);
|
||||
m_scancode_to_keycode_normal[0x6D] = keycode_function(19);
|
||||
m_scancode_to_keycode_normal[0x5F] = keycode_function(20);
|
||||
m_scancode_to_keycode_normal[0x9C] = keycode_function(21);
|
||||
m_scancode_to_keycode_normal[0x9D] = keycode_function(22);
|
||||
m_scancode_to_keycode_normal[0x95] = keycode_function(23);
|
||||
|
||||
// Arrow keys
|
||||
m_scancode_to_keycode_normal[0x63] = keycode_normal(5, 0);
|
||||
|
||||
@@ -3887,237 +3887,45 @@ namespace Kernel
|
||||
return region->allocate_page_containing(address, wants_write);
|
||||
}
|
||||
|
||||
// TODO: The following 3 functions could be simplified into one generic helper function
|
||||
extern "C" bool safe_user_memcpy(void*, const void*, size_t);
|
||||
extern "C" bool safe_user_strncpy(void*, const void*, size_t);
|
||||
|
||||
static inline bool is_valid_user_address(const void* user_addr, size_t size)
|
||||
{
|
||||
const vaddr_t user_vaddr = reinterpret_cast<vaddr_t>(user_addr);
|
||||
if (BAN::Math::will_addition_overflow<vaddr_t>(user_vaddr, size))
|
||||
return false;
|
||||
if (user_vaddr + size > USERSPACE_END)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Process::read_from_user(const void* user_addr, void* out, size_t size)
|
||||
{
|
||||
const vaddr_t user_vaddr = reinterpret_cast<vaddr_t>(user_addr);
|
||||
|
||||
auto* out_u8 = static_cast<uint8_t*>(out);
|
||||
size_t ncopied = 0;
|
||||
|
||||
{
|
||||
RWLockRDGuard _(m_memory_region_lock);
|
||||
|
||||
const size_t first_index = find_mapped_region(user_vaddr);
|
||||
for (size_t i = first_index; ncopied < size && i < m_mapped_regions.size(); i++)
|
||||
{
|
||||
auto& region = m_mapped_regions[i];
|
||||
if (!region->contains(user_vaddr + ncopied))
|
||||
if (!is_valid_user_address(user_addr, size))
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
if (!safe_user_memcpy(out, user_addr, size))
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
|
||||
const size_t ncopy = BAN::Math::min<size_t>(
|
||||
(region->vaddr() + region->size()) - (user_vaddr + ncopied),
|
||||
size - ncopied
|
||||
);
|
||||
|
||||
const size_t page_count = range_page_count(user_vaddr + ncopied, ncopy);
|
||||
const vaddr_t page_base = (user_vaddr + ncopied) & PAGE_ADDR_MASK;
|
||||
for (size_t p = 0; p < page_count; p++)
|
||||
{
|
||||
const auto flags = PageTable::UserSupervisor | PageTable::Present;
|
||||
if ((m_page_table->get_page_flags(page_base + p * PAGE_SIZE) & flags) != flags)
|
||||
goto read_from_user_with_allocation;
|
||||
}
|
||||
|
||||
memcpy(out_u8 + ncopied, reinterpret_cast<void*>(user_vaddr + ncopied), ncopy);
|
||||
ncopied += ncopy;
|
||||
}
|
||||
|
||||
if (ncopied >= size)
|
||||
return {};
|
||||
if (ncopied > 0)
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
read_from_user_with_allocation:
|
||||
RWLockWRGuard _(m_memory_region_lock);
|
||||
|
||||
const size_t first_index = find_mapped_region(user_vaddr + ncopied);
|
||||
for (size_t i = first_index; ncopied < size && i < m_mapped_regions.size(); i++)
|
||||
{
|
||||
auto& region = m_mapped_regions[i];
|
||||
if (!region->contains(user_vaddr + ncopied))
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
|
||||
const size_t ncopy = BAN::Math::min<size_t>(
|
||||
(region->vaddr() + region->size()) - (user_vaddr + ncopied),
|
||||
size - ncopied
|
||||
);
|
||||
|
||||
const size_t page_count = range_page_count(user_vaddr + ncopied, ncopy);
|
||||
const vaddr_t page_base = (user_vaddr + ncopied) & PAGE_ADDR_MASK;
|
||||
for (size_t p = 0; p < page_count; p++)
|
||||
{
|
||||
const auto flags = PageTable::UserSupervisor | PageTable::Present;
|
||||
if ((m_page_table->get_page_flags(page_base + p * PAGE_SIZE) & flags) == flags)
|
||||
continue;
|
||||
if (!TRY(region->allocate_page_containing(page_base + p * PAGE_SIZE, false)))
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
memcpy(out_u8 + ncopied, reinterpret_cast<void*>(user_vaddr + ncopied), ncopy);
|
||||
ncopied += ncopy;
|
||||
}
|
||||
|
||||
if (ncopied >= size)
|
||||
return {};
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Process::read_string_from_user(const char* user_addr, char* out, size_t max_size)
|
||||
{
|
||||
const vaddr_t user_vaddr = reinterpret_cast<vaddr_t>(user_addr);
|
||||
|
||||
size_t ncopied = 0;
|
||||
|
||||
{
|
||||
RWLockRDGuard _(m_memory_region_lock);
|
||||
|
||||
const size_t first_index = find_mapped_region(user_vaddr);
|
||||
for (size_t i = first_index; ncopied < max_size && i < m_mapped_regions.size(); i++)
|
||||
{
|
||||
auto& region = m_mapped_regions[i];
|
||||
if (!region->contains(user_vaddr + ncopied))
|
||||
max_size = BAN::Math::min<size_t>(max_size, USERSPACE_END - reinterpret_cast<vaddr_t>(user_addr));
|
||||
if (!is_valid_user_address(user_addr, max_size))
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
if (!safe_user_strncpy(out, user_addr, max_size))
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
|
||||
vaddr_t last_page = 0;
|
||||
|
||||
for (; ncopied < max_size; ncopied++)
|
||||
{
|
||||
const vaddr_t curr_page = (user_vaddr + ncopied) & PAGE_ADDR_MASK;
|
||||
if (curr_page != last_page)
|
||||
{
|
||||
const auto flags = PageTable::UserSupervisor | PageTable::Present;
|
||||
if ((m_page_table->get_page_flags(curr_page) & flags) != flags)
|
||||
goto read_string_from_user_with_allocation;
|
||||
}
|
||||
|
||||
out[ncopied] = user_addr[ncopied];
|
||||
if (out[ncopied] == '\0')
|
||||
return {};
|
||||
|
||||
last_page = curr_page;
|
||||
}
|
||||
}
|
||||
|
||||
if (ncopied >= max_size)
|
||||
return BAN::Error::from_errno(ENAMETOOLONG);
|
||||
if (ncopied > 0)
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
read_string_from_user_with_allocation:
|
||||
RWLockWRGuard _(m_memory_region_lock);
|
||||
|
||||
const size_t first_index = find_mapped_region(user_vaddr + ncopied);
|
||||
for (size_t i = first_index; ncopied < max_size && i < m_mapped_regions.size(); i++)
|
||||
{
|
||||
auto& region = m_mapped_regions[i];
|
||||
if (!region->contains(user_vaddr + ncopied))
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
|
||||
vaddr_t last_page = 0;
|
||||
|
||||
for (; ncopied < max_size; ncopied++)
|
||||
{
|
||||
const vaddr_t curr_page = (user_vaddr + ncopied) & PAGE_ADDR_MASK;
|
||||
if (curr_page != last_page)
|
||||
{
|
||||
const auto flags = PageTable::UserSupervisor | PageTable::Present;
|
||||
if ((m_page_table->get_page_flags(curr_page) & flags) == flags)
|
||||
;
|
||||
else if (!TRY(region->allocate_page_containing(curr_page, false)))
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
out[ncopied] = user_addr[ncopied];
|
||||
if (out[ncopied] == '\0')
|
||||
return {};
|
||||
|
||||
last_page = curr_page;
|
||||
}
|
||||
}
|
||||
|
||||
if (ncopied >= max_size)
|
||||
return BAN::Error::from_errno(ENAMETOOLONG);
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<void> Process::write_to_user(void* user_addr, const void* in, size_t size)
|
||||
{
|
||||
const vaddr_t user_vaddr = reinterpret_cast<vaddr_t>(user_addr);
|
||||
|
||||
const auto* in_u8 = static_cast<const uint8_t*>(in);
|
||||
size_t ncopied = 0;
|
||||
|
||||
{
|
||||
RWLockRDGuard _(m_memory_region_lock);
|
||||
|
||||
const size_t first_index = find_mapped_region(user_vaddr);
|
||||
for (size_t i = first_index; ncopied < size && i < m_mapped_regions.size(); i++)
|
||||
{
|
||||
auto& region = m_mapped_regions[i];
|
||||
if (!region->contains(user_vaddr + ncopied))
|
||||
if (!is_valid_user_address(user_addr, size))
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
if (!safe_user_memcpy(user_addr, in, size))
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
|
||||
const size_t ncopy = BAN::Math::min<size_t>(
|
||||
(region->vaddr() + region->size()) - (user_vaddr + ncopied),
|
||||
size - ncopied
|
||||
);
|
||||
|
||||
const size_t page_count = range_page_count(user_vaddr + ncopied, ncopy);
|
||||
const vaddr_t page_base = (user_vaddr + ncopied) & PAGE_ADDR_MASK;
|
||||
for (size_t i = 0; i < page_count; i++)
|
||||
{
|
||||
const auto flags = PageTable::UserSupervisor | PageTable::ReadWrite | PageTable::Present;
|
||||
if ((m_page_table->get_page_flags(page_base + i * PAGE_SIZE) & flags) != flags)
|
||||
goto write_to_user_with_allocation;
|
||||
}
|
||||
|
||||
memcpy(reinterpret_cast<void*>(user_vaddr + ncopied), in_u8 + ncopied, ncopy);
|
||||
ncopied += ncopy;
|
||||
}
|
||||
|
||||
if (ncopied >= size)
|
||||
return {};
|
||||
if (ncopied > 0)
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
write_to_user_with_allocation:
|
||||
RWLockWRGuard _(m_memory_region_lock);
|
||||
|
||||
const size_t first_index = find_mapped_region(user_vaddr + ncopied);
|
||||
for (size_t i = first_index; ncopied < size && i < m_mapped_regions.size(); i++)
|
||||
{
|
||||
auto& region = m_mapped_regions[i];
|
||||
if (!region->contains(user_vaddr + ncopied))
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
|
||||
const size_t ncopy = BAN::Math::min<size_t>(
|
||||
(region->vaddr() + region->size()) - (user_vaddr + ncopied),
|
||||
size - ncopied
|
||||
);
|
||||
|
||||
const size_t page_count = range_page_count(user_vaddr + ncopied, ncopy);
|
||||
const vaddr_t page_base = (user_vaddr + ncopied) & PAGE_ADDR_MASK;
|
||||
for (size_t p = 0; p < page_count; p++)
|
||||
{
|
||||
const auto flags = PageTable::UserSupervisor | PageTable::ReadWrite | PageTable::Present;
|
||||
if ((m_page_table->get_page_flags(page_base + p * PAGE_SIZE) & flags) == flags)
|
||||
continue;
|
||||
if (!TRY(region->allocate_page_containing(page_base + p * PAGE_SIZE, true)))
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
memcpy(reinterpret_cast<void*>(user_vaddr + ncopied), in_u8 + ncopied, ncopy);
|
||||
ncopied += ncopy;
|
||||
}
|
||||
|
||||
if (ncopied >= size)
|
||||
return {};
|
||||
return BAN::Error::from_errno(EFAULT);
|
||||
}
|
||||
|
||||
BAN::ErrorOr<MemoryRegion*> Process::validate_and_pin_pointer_access(const void* ptr, size_t size, bool needs_write)
|
||||
|
||||
@@ -342,6 +342,9 @@ namespace Kernel
|
||||
s_scancode_to_keycode[0x4B] = keycode_function(18);
|
||||
s_scancode_to_keycode[0x4E] = keycode_function(19);
|
||||
s_scancode_to_keycode[0x47] = keycode_function(20);
|
||||
s_scancode_to_keycode[0x7F] = keycode_function(21);
|
||||
s_scancode_to_keycode[0x81] = keycode_function(22);
|
||||
s_scancode_to_keycode[0x80] = keycode_function(23);
|
||||
|
||||
s_scancode_to_keycode[0x53] = keycode_numpad(0, 0);
|
||||
s_scancode_to_keycode[0x54] = keycode_numpad(0, 1);
|
||||
|
||||
@@ -145,7 +145,7 @@ namespace LibAudio
|
||||
|
||||
LibAudio::Packet packet {
|
||||
.type = LibAudio::Packet::Notify,
|
||||
.parameter = 0,
|
||||
.parameter = {},
|
||||
};
|
||||
|
||||
send(m_server_fd, &packet, sizeof(packet), 0);
|
||||
|
||||
@@ -40,6 +40,14 @@ namespace LibAudio
|
||||
SetPin, // parameter: pin number
|
||||
// response: nothing
|
||||
// set the active pin of the current device
|
||||
|
||||
GetVolume, // parameter: ignored
|
||||
// response: 10 * volume percentage (uint32_t)
|
||||
// get the volume of the current device
|
||||
|
||||
SetVolume, // parameter: 10 * volume percentage (uint32_t)
|
||||
// response: nothing
|
||||
// set the volume of the current device
|
||||
} type;
|
||||
|
||||
uint64_t parameter;
|
||||
|
||||
@@ -1,35 +1,3 @@
|
||||
.global memchr
|
||||
memchr:
|
||||
xchgl 4(%esp), %edi
|
||||
movl 8(%esp), %eax
|
||||
movl 12(%esp), %ecx
|
||||
movl $1, %edx
|
||||
cmpl $1, %ecx # clear ZF if count is zero
|
||||
repne scasb
|
||||
cmovel %edi, %edx
|
||||
leal -1(%edx), %eax
|
||||
movl 4(%esp), %edi
|
||||
ret
|
||||
|
||||
.global memcmp
|
||||
memcmp:
|
||||
xchgl 4(%esp), %edi
|
||||
xchgl 8(%esp), %esi
|
||||
movl 12(%esp), %ecx
|
||||
testl %ecx, %ecx # set ZF if count is zero
|
||||
repe cmpsb
|
||||
jne .memcmp_not_equal
|
||||
xorl %eax, %eax
|
||||
jmp .memcmp_done
|
||||
.memcmp_not_equal:
|
||||
movzbl -1(%edi), %eax
|
||||
movzbl -1(%esi), %ecx
|
||||
subl %ecx, %eax
|
||||
.memcmp_done:
|
||||
movl 4(%esp), %edi
|
||||
movl 8(%esp), %esi
|
||||
ret
|
||||
|
||||
.global memcpy
|
||||
memcpy:
|
||||
xchgl 4(%esp), %edi
|
||||
@@ -74,14 +42,3 @@ memset:
|
||||
movl 4(%esp), %edi
|
||||
movl %edx, %eax
|
||||
ret
|
||||
|
||||
.global strlen
|
||||
strlen:
|
||||
xchgl 4(%esp), %edi
|
||||
xorb %al, %al
|
||||
movl $-1, %ecx
|
||||
repne scasb
|
||||
movl 4(%esp), %edi
|
||||
movl $-2, %eax
|
||||
subl %ecx, %eax
|
||||
ret
|
||||
|
||||
@@ -1,28 +1,3 @@
|
||||
.global memchr
|
||||
memchr:
|
||||
movb %sil, %al
|
||||
movq %rdx, %rcx
|
||||
movq $1, %rdx
|
||||
cmpq $1, %rcx # clear ZF if count is zero
|
||||
repne scasb
|
||||
cmoveq %rdi, %rdx
|
||||
leaq -1(%rdx), %rax
|
||||
ret
|
||||
|
||||
.global memcmp
|
||||
memcmp:
|
||||
movq %rdx, %rcx
|
||||
testq %rcx, %rcx # set ZF if count is zero
|
||||
repe cmpsb
|
||||
jne .memcmp_not_equal
|
||||
xorq %rax, %rax
|
||||
ret
|
||||
.memcmp_not_equal:
|
||||
movzbl -1(%rdi), %eax
|
||||
movzbl -1(%rsi), %ecx
|
||||
subq %rcx, %rax
|
||||
ret
|
||||
|
||||
.global memcpy
|
||||
memcpy:
|
||||
movq %rdi, %rax
|
||||
@@ -52,11 +27,154 @@ memset:
|
||||
movq %r8, %rax
|
||||
ret
|
||||
|
||||
|
||||
#if defined(__SSE2__)
|
||||
|
||||
.global memchr
|
||||
memchr:
|
||||
testq %rdx, %rdx
|
||||
jz .memchr_no_match
|
||||
|
||||
movd %esi, %xmm0
|
||||
punpcklbw %xmm0, %xmm0
|
||||
punpcklwd %xmm0, %xmm0
|
||||
pshufd $0, %xmm0, %xmm0
|
||||
|
||||
movq %rdi, %rcx
|
||||
andq $15, %rcx
|
||||
jz .memchr_loop
|
||||
|
||||
movq %rdi, %rsi
|
||||
subq %rcx, %rsi
|
||||
movdqa (%rsi), %xmm1
|
||||
pcmpeqb %xmm0, %xmm1
|
||||
pmovmskb %xmm1, %eax
|
||||
shrl %cl, %eax
|
||||
jnz .memchr_match
|
||||
|
||||
leaq 16(%rsi), %rdi
|
||||
|
||||
addq %rcx, %rdx
|
||||
subq $16, %rdx
|
||||
jbe .memchr_no_match
|
||||
|
||||
.memchr_loop:
|
||||
movdqa (%rdi), %xmm1
|
||||
pcmpeqb %xmm0, %xmm1
|
||||
pmovmskb %xmm1, %eax
|
||||
testl %eax, %eax
|
||||
jnz .memchr_match
|
||||
|
||||
addq $16, %rdi
|
||||
subq $16, %rdx
|
||||
ja .memchr_loop
|
||||
|
||||
.memchr_no_match:
|
||||
xorq %rax, %rax
|
||||
ret
|
||||
|
||||
.memchr_match:
|
||||
bsfl %eax, %eax
|
||||
cmpq %rdx, %rax
|
||||
jae .memchr_no_match
|
||||
addq %rdi, %rax
|
||||
ret
|
||||
|
||||
.global memcmp
|
||||
memcmp:
|
||||
testq %rdx, %rdx
|
||||
jz .memcmp_equal
|
||||
|
||||
movq %rdi, %rax
|
||||
movq %rsi, %rcx
|
||||
andq $15, %rax
|
||||
andq $15, %rcx
|
||||
cmpq %rax, %rcx
|
||||
cmovaq %rcx, %rax
|
||||
|
||||
testq %rax, %rax
|
||||
jz .memcmp_loop
|
||||
|
||||
movq $16, %rcx
|
||||
subq %rax, %rcx
|
||||
|
||||
cmpq %rcx, %rdx
|
||||
cmovbq %rdx, %rcx
|
||||
|
||||
subq %rcx, %rdx
|
||||
|
||||
.memcmp_align_loop:
|
||||
movzbl (%rdi), %eax
|
||||
movzbl (%rsi), %r8d
|
||||
subl %r8d, %eax
|
||||
jnz .memcmp_return
|
||||
|
||||
incq %rdi
|
||||
incq %rsi
|
||||
decq %rcx
|
||||
jnz .memcmp_align_loop
|
||||
|
||||
.memcmp_loop:
|
||||
movdqu (%rdi), %xmm0
|
||||
movdqu (%rsi), %xmm1
|
||||
pcmpeqb %xmm0, %xmm1
|
||||
pmovmskb %xmm1, %eax
|
||||
xorl $0xFFFF, %eax
|
||||
jnz .memcmp_differ
|
||||
|
||||
addq $16, %rdi
|
||||
addq $16, %rsi
|
||||
subq $16, %rdx
|
||||
ja .memcmp_loop
|
||||
|
||||
.memcmp_equal:
|
||||
xorl %eax, %eax
|
||||
.memcmp_return:
|
||||
ret
|
||||
|
||||
.memcmp_differ:
|
||||
bsfl %eax, %ecx
|
||||
cmpq %rdx, %rcx
|
||||
jae .memcmp_equal
|
||||
movzbl (%rdi, %rcx), %eax
|
||||
movzbl (%rsi, %rcx), %edx
|
||||
subl %edx, %eax
|
||||
ret
|
||||
|
||||
.global strlen
|
||||
strlen:
|
||||
xorb %al, %al
|
||||
movq $-1, %rcx
|
||||
repne scasb
|
||||
movq $-2, %rax
|
||||
subq %rcx, %rax
|
||||
movq %rdi, %rsi
|
||||
|
||||
pxor %xmm0, %xmm0
|
||||
|
||||
movq %rsi, %rcx
|
||||
andq $15, %rcx
|
||||
jz .strlen_loop
|
||||
|
||||
movq %rsi, %rdx
|
||||
subq %rcx, %rdx
|
||||
movdqa (%rdx), %xmm1
|
||||
pcmpeqb %xmm0, %xmm1
|
||||
pmovmskb %xmm1, %eax
|
||||
shrl %cl, %eax
|
||||
jnz .strlen_null_found
|
||||
|
||||
leaq 16(%rdx), %rsi
|
||||
|
||||
.strlen_loop:
|
||||
movdqa (%rsi), %xmm1
|
||||
pcmpeqb %xmm0, %xmm1
|
||||
pmovmskb %xmm1, %eax
|
||||
testl %eax, %eax
|
||||
jnz .strlen_null_found
|
||||
|
||||
addq $16, %rsi
|
||||
jmp .strlen_loop
|
||||
|
||||
.strlen_null_found:
|
||||
bsfl %eax, %eax
|
||||
addq %rsi, %rax
|
||||
subq %rdi, %rax
|
||||
ret
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include <ctype.h>
|
||||
#include <fnmatch.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -5,6 +6,8 @@
|
||||
|
||||
static int fnmatch_impl(const char* pattern, const char* string, int flags, bool leading)
|
||||
{
|
||||
const bool ignore_case = !!(flags & FNM_IGNORECASE);
|
||||
|
||||
while (*pattern)
|
||||
{
|
||||
if ((flags & FNM_PERIOD) && leading && *string == '.' && *pattern != '.')
|
||||
@@ -34,9 +37,13 @@ static int fnmatch_impl(const char* pattern, const char* string, int flags, bool
|
||||
uint8_t ch;
|
||||
uint32_t bitmap[0x100 / 8] {};
|
||||
while ((ch = *pattern++) != ']')
|
||||
{
|
||||
if (ignore_case)
|
||||
ch = tolower(ch);
|
||||
bitmap[ch / 32] |= 1 << (ch % 32);
|
||||
}
|
||||
|
||||
ch = *string++;
|
||||
ch = ignore_case ? tolower(*string++) : *string++;
|
||||
if (!!(bitmap[ch / 32] & (1 << (ch % 32))) == negate)
|
||||
return FNM_NOMATCH;
|
||||
|
||||
@@ -63,7 +70,10 @@ static int fnmatch_impl(const char* pattern, const char* string, int flags, bool
|
||||
if (*pattern == '\0')
|
||||
break;
|
||||
|
||||
if (*pattern != *string)
|
||||
const char lhs = ignore_case ? tolower(*pattern) : *pattern;
|
||||
const char rhs = ignore_case ? tolower(*string) : *string;
|
||||
|
||||
if (lhs != rhs)
|
||||
return FNM_NOMATCH;
|
||||
if ((flags & FNM_PATHNAME) && *string == '/')
|
||||
leading = true;
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
#ifndef assert
|
||||
#ifdef NDEBUG
|
||||
#define assert(ignore) ((void)0)
|
||||
#else
|
||||
#define assert(expr) ((expr) ? (void)0 : __assert_fail(#expr, __FILE__, __LINE__, __func__))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef _ASSERT_H
|
||||
#define _ASSERT_H 1
|
||||
|
||||
@@ -5,12 +13,6 @@
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define assert(ignore) ((void)0)
|
||||
#else
|
||||
#define assert(expr) ((expr) ? (void)0 : __assert_fail(#expr, __FILE__, __LINE__, __func__))
|
||||
#endif
|
||||
|
||||
#if !defined(__cplusplus) && __STDC_VERSION__ >= 201112L && __STDC_VERSION__ < 202311L
|
||||
#define static_assert _Static_assert
|
||||
#endif
|
||||
|
||||
@@ -11,6 +11,8 @@ __BEGIN_DECLS
|
||||
#define FNM_PATHNAME 0x01
|
||||
#define FNM_PERIOD 0x02
|
||||
#define FNM_NOESCAPE 0x04
|
||||
#define FNM_CASEFOLD 0x08
|
||||
#define FNM_IGNORECASE FNM_CASEFOLD
|
||||
|
||||
int fnmatch(const char* pattern, const char* string, int flags);
|
||||
|
||||
|
||||
@@ -53,6 +53,13 @@ struct ifreq
|
||||
#define SIOCGIFFLAGS 9
|
||||
#define SIOCGIFMTU 10
|
||||
|
||||
#define IFF_UP 0x01
|
||||
#define IFF_BROADCAST 0x02
|
||||
#define IFF_LOOPBACK 0x04
|
||||
#define IFF_POINTOPOINT 0x08
|
||||
#define IFF_RUNNING 0x10
|
||||
#define IFF_MULTICAST 0x20
|
||||
|
||||
void if_freenameindex(struct if_nameindex* ptr);
|
||||
char* if_indextoname(unsigned ifindex, char* ifname);
|
||||
struct if_nameindex* if_nameindex(void);
|
||||
|
||||
@@ -18,49 +18,26 @@ __BEGIN_DECLS
|
||||
#define IPPROTO_TCP 6
|
||||
#define IPPROTO_UDP 7
|
||||
|
||||
enum
|
||||
{
|
||||
IP_ADD_MEMBERSHIP,
|
||||
#define IP_ADD_MEMBERSHIP IP_ADD_MEMBERSHIP
|
||||
IP_ADD_SOURCE_MEMBERSHIP,
|
||||
#define IP_ADD_SOURCE_MEMBERSHIP IP_ADD_SOURCE_MEMBERSHIP
|
||||
IP_DROP_MEMBERSHIP,
|
||||
#define IP_DROP_MEMBERSHIP IP_DROP_MEMBERSHIP
|
||||
IP_DROP_SOURCE_MEMBERSHIP,
|
||||
#define IP_DROP_SOURCE_MEMBERSHIP IP_DROP_SOURCE_MEMBERSHIP
|
||||
IP_MULTICAST_IF,
|
||||
#define IP_MULTICAST_IF IP_MULTICAST_IF
|
||||
IP_MULTICAST_LOOP,
|
||||
#define IP_MULTICAST_LOOP IP_MULTICAST_LOOP
|
||||
IP_MULTICAST_TTL,
|
||||
#define IP_MULTICAST_TTL IP_MULTICAST_TTL
|
||||
IP_TTL,
|
||||
#define IP_TTL IP_TTL
|
||||
IP_TOS,
|
||||
#define IP_TOS IP_TOS
|
||||
};
|
||||
#define IP_ADD_MEMBERSHIP 0
|
||||
#define IP_ADD_SOURCE_MEMBERSHIP 1
|
||||
#define IP_DROP_MEMBERSHIP 2
|
||||
#define IP_DROP_SOURCE_MEMBERSHIP 3
|
||||
#define IP_MULTICAST_IF 4
|
||||
#define IP_MULTICAST_LOOP 5
|
||||
#define IP_MULTICAST_TTL 6
|
||||
#define IP_TTL 7
|
||||
#define IP_TOS 8
|
||||
|
||||
enum
|
||||
{
|
||||
IPV6_ADD_MEMBERSHIP,
|
||||
#define IPV6_ADD_MEMBERSHIP IPV6_ADD_MEMBERSHIP
|
||||
IPV6_DROP_MEMBERSHIP,
|
||||
#define IPV6_DROP_MEMBERSHIP IPV6_DROP_MEMBERSHIP
|
||||
IPV6_JOIN_GROUP,
|
||||
#define IPV6_JOIN_GROUP IPV6_JOIN_GROUP
|
||||
IPV6_LEAVE_GROUP,
|
||||
#define IPV6_LEAVE_GROUP IPV6_LEAVE_GROUP
|
||||
IPV6_MULTICAST_HOPS,
|
||||
#define IPV6_MULTICAST_HOPS IPV6_MULTICAST_HOPS
|
||||
IPV6_MULTICAST_IF,
|
||||
#define IPV6_MULTICAST_IF IPV6_MULTICAST_IF
|
||||
IPV6_MULTICAST_LOOP,
|
||||
#define IPV6_MULTICAST_LOOP IPV6_MULTICAST_LOOP
|
||||
IPV6_UNICAST_HOPS,
|
||||
#define IPV6_UNICAST_HOPS IPV6_UNICAST_HOPS
|
||||
IPV6_V6ONLY,
|
||||
#define IPV6_V6ONLY IPV6_V6ONLY
|
||||
};
|
||||
#define IPV6_ADD_MEMBERSHIP 0
|
||||
#define IPV6_DROP_MEMBERSHIP 1
|
||||
#define IPV6_JOIN_GROUP 2
|
||||
#define IPV6_LEAVE_GROUP 3
|
||||
#define IPV6_MULTICAST_HOPS 4
|
||||
#define IPV6_MULTICAST_IF 5
|
||||
#define IPV6_MULTICAST_LOOP 6
|
||||
#define IPV6_UNICAST_HOPS 7
|
||||
#define IPV6_V6ONLY 8
|
||||
#define IPV6_TCLASS 9
|
||||
|
||||
#define IN_CLASSA(a) ((((in_addr_t)(a)) & 0x80000000) == 0)
|
||||
#define IN_CLASSA_NET 0xFF000000
|
||||
|
||||
@@ -28,6 +28,8 @@ int sched_setparam(pid_t pid, const struct sched_param* param);
|
||||
int sched_setscheduler(pid_t pid, int, const struct sched_param* param);
|
||||
int sched_yield(void);
|
||||
|
||||
int sched_getcpu(void);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
||||
|
||||
@@ -42,6 +42,7 @@ char* strndup(const char* s, size_t n);
|
||||
size_t strnlen(const char* s, size_t maxlen);
|
||||
char* strpbrk(const char* s1, const char* s2);
|
||||
char* strrchr(const char* s, int c);
|
||||
char* strsep(char** __restrict stringp, const char* __restrict delim);
|
||||
char* strsignal(int signum);
|
||||
size_t strspn(const char* s1, const char* s2);
|
||||
char* strstr(const char* s1, const char* s2);
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define I_ATMARK 1
|
||||
#define I_CANPUT 2
|
||||
#define I_CKBAND 3
|
||||
@@ -50,6 +52,13 @@ struct winsize
|
||||
#define TIOCGWINSZ 50
|
||||
#define TIOCSWINSZ 51
|
||||
|
||||
struct snd_volume_info
|
||||
{
|
||||
int32_t min_mdB;
|
||||
int32_t max_mdB;
|
||||
int32_t step_mdB;
|
||||
int32_t mdB;
|
||||
};
|
||||
#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 */
|
||||
@@ -57,6 +66,8 @@ struct winsize
|
||||
#define SND_GET_TOTAL_PINS 64 /* gets the number of pins on the current device as uint32_t */
|
||||
#define SND_GET_PIN 65 /* gets the currently active pin as uint32_t */
|
||||
#define SND_SET_PIN 66 /* sets the currently active pin to uint32_t */
|
||||
#define SND_GET_VOLUME_INFO 67 /* gets the current volume as snd_volume_info */
|
||||
#define SND_SET_VOLUME_MDB 68 /* sets the current volume to int32_t dB */
|
||||
|
||||
#define JOYSTICK_GET_LEDS 80 /* get controller led bitmap to uint8_t argument */
|
||||
#define JOYSTICK_SET_LEDS 81 /* set controller leds to uint8_t bitmap */
|
||||
|
||||
@@ -494,6 +494,10 @@ enum
|
||||
#define _SC_XOPEN_UUCP _SC_XOPEN_UUCP
|
||||
_SC_XOPEN_VERSION,
|
||||
#define _SC_XOPEN_VERSION _SC_XOPEN_VERSION
|
||||
_SC_PHYS_PAGES,
|
||||
#define _SC_PHYS_PAGES _SC_PHYS_PAGES
|
||||
_SC_AVPHYS_PAGES,
|
||||
#define _SC_AVPHYS_PAGES _SC_AVPHYS_PAGES
|
||||
};
|
||||
|
||||
#define F_OK 0x01
|
||||
|
||||
@@ -256,7 +256,7 @@ FUNC_EXPR1(nearbyint, BAN::Math::rint(a))
|
||||
FUNC_EXPR2(nextafter, nextafter_impl(a, b))
|
||||
FUNC_EXPR2_TYPE(nexttoward, long double, nextafter_impl(a, b))
|
||||
FUNC_EXPR2(pow, ({ if (isnan(a)) return a; if (isnan(b)) return b; BAN::Math::pow(a, b); }))
|
||||
// remainder
|
||||
BAN_FUNC2(remainder)
|
||||
// remquo
|
||||
BAN_FUNC1(rint)
|
||||
FUNC_EXPR1(round, ({ if (!isfinite(a)) return a; BAN::Math::round(a); }))
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <kernel/API/SharedPage.h>
|
||||
|
||||
extern volatile Kernel::API::SharedPage* g_shared_page;
|
||||
|
||||
int sched_get_priority_max(int policy)
|
||||
{
|
||||
(void)policy;
|
||||
@@ -18,3 +22,17 @@ int sched_yield(void)
|
||||
{
|
||||
return syscall(SYS_YIELD);
|
||||
}
|
||||
|
||||
int sched_getcpu(void)
|
||||
{
|
||||
if (g_shared_page == nullptr)
|
||||
return -1;
|
||||
|
||||
uint8_t cpu;
|
||||
#if defined(__x86_64__)
|
||||
asm volatile("movb %%gs:0, %0" : "=r"(cpu));
|
||||
#elif defined(__i686__)
|
||||
asm volatile("movb %%fs:0, %0" : "=q"(cpu));
|
||||
#endif
|
||||
return cpu;
|
||||
}
|
||||
|
||||
@@ -49,8 +49,12 @@ int sem_timedwait(sem_t* __restrict sem, const struct timespec* __restrict absti
|
||||
for (;;)
|
||||
{
|
||||
uint32_t expected = BAN::atomic_load(sem->value);
|
||||
if (expected > 0 && BAN::atomic_compare_exchange(sem->value, expected, expected - 1))
|
||||
if (expected > 0)
|
||||
{
|
||||
if (!BAN::atomic_compare_exchange(sem->value, expected, expected - 1))
|
||||
continue;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int op = FUTEX_WAIT | (sem->shared ? 0 : FUTEX_PRIVATE) | FUTEX_REALTIME;
|
||||
if (futex(op, &sem->value, expected, abstime) == -1 && (errno == EINTR || errno == ETIMEDOUT))
|
||||
|
||||
@@ -169,7 +169,7 @@ static mode_t parse_mode_string(const char* mode_str)
|
||||
return 0;
|
||||
if (len == 3 && mode_str[1] == mode_str[2])
|
||||
return 0;
|
||||
if (strspn(mode_str + 1, "b+") != len - 1)
|
||||
if (strspn(mode_str + 1, "tb+") != len - 1)
|
||||
return 0;
|
||||
bool plus = (mode_str[1] == '+' || mode_str[2] == '+');
|
||||
switch (mode_str[0])
|
||||
|
||||
@@ -274,6 +274,25 @@ char* strrchr(const char* str, int c)
|
||||
return (*str == c) ? (char*)str : nullptr;
|
||||
}
|
||||
|
||||
char* strsep(char** __restrict stringp, const char* __restrict delim)
|
||||
{
|
||||
if (*stringp == nullptr)
|
||||
return nullptr;
|
||||
|
||||
char* original = *stringp;
|
||||
|
||||
char* match = strpbrk(*stringp, delim);
|
||||
if (match == nullptr)
|
||||
*stringp = nullptr;
|
||||
else
|
||||
{
|
||||
*stringp = match + 1;
|
||||
*match = '\0';
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
char* strstr(const char* haystack, const char* needle)
|
||||
{
|
||||
const size_t needle_len = strlen(needle);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/banan-os.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/time.h>
|
||||
@@ -1180,6 +1181,25 @@ long sysconf(int name)
|
||||
case _SC_GETGR_R_SIZE_MAX: return 1024;
|
||||
case _SC_GETPW_R_SIZE_MAX: return 1024;
|
||||
case _SC_THREAD_STACK_MIN: return PTHREAD_STACK_MIN;
|
||||
|
||||
case _SC_PHYS_PAGES:
|
||||
case _SC_AVPHYS_PAGES:
|
||||
{
|
||||
int fd = open("/proc/meminfo", O_RDONLY);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
full_meminfo_t meminfo;
|
||||
const size_t nread = read(fd, &meminfo, sizeof(meminfo));
|
||||
close(fd);
|
||||
|
||||
if (nread != sizeof(meminfo))
|
||||
return -1;
|
||||
if (name == _SC_PHYS_PAGES)
|
||||
return meminfo.free_pages + meminfo.used_pages;
|
||||
if (name == _SC_AVPHYS_PAGES)
|
||||
return meminfo.free_pages;
|
||||
}
|
||||
}
|
||||
|
||||
errno = EINVAL;
|
||||
|
||||
@@ -98,12 +98,53 @@ bool AudioServer::on_client_packet(int fd, LibAudio::Packet packet)
|
||||
return false;
|
||||
}
|
||||
reset_kernel_buffer();
|
||||
if (uint32_t pin = packet.parameter; ioctl(device().fd, SND_SET_PIN, &pin) != 0)
|
||||
if (ioctl(device().fd, SND_SET_PIN, &packet.parameter) != 0)
|
||||
dwarnln("Failed to set pin {}: {}", packet.parameter, strerror(errno));
|
||||
else
|
||||
{
|
||||
device().current_pin = packet.parameter;
|
||||
|
||||
// NOTE: different pins have different volume controls
|
||||
if (ioctl(device().fd, SND_GET_VOLUME_INFO, &device().volume) != 0)
|
||||
dwarnln("Failed to query new volume: {}", strerror(errno));
|
||||
}
|
||||
update();
|
||||
break;
|
||||
case LibAudio::Packet::GetVolume:
|
||||
{
|
||||
// NOTE: maybe we should map [0->100%] -> [min, 0] instead?
|
||||
|
||||
const auto& volume = device().volume;
|
||||
if (volume.min_mdB == volume.max_mdB)
|
||||
response = 1000;
|
||||
else
|
||||
{
|
||||
const double min_dB = volume.min_mdB / 1000.0;
|
||||
const double max_dB = volume.max_mdB / 1000.0;
|
||||
const double dB = volume.mdB / 1000.0;
|
||||
const double percent = (BAN::Math::pow(10.0, (dB - min_dB) / (max_dB - min_dB)) - 1.0) / 9.0;
|
||||
response = BAN::Math::round(percent * 1000.0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LibAudio::Packet::SetVolume:
|
||||
{
|
||||
// NOTE: maybe we should map [0->100%] -> [min, 0] instead?
|
||||
|
||||
const auto& volume = device().volume;
|
||||
const double min_dB = volume.min_mdB / 1000.0;
|
||||
const double max_dB = volume.max_mdB / 1000.0;
|
||||
const double percent = packet.parameter / 1000.0;
|
||||
const double dB = min_dB + (max_dB - min_dB) * BAN::Math::log10(1.0 + 9.0 * percent);
|
||||
|
||||
const int32_t mdB = BAN::Math::round(dB * 1000.0);
|
||||
if (ioctl(device().fd, SND_SET_VOLUME_MDB, &mdB) != 0)
|
||||
dwarnln("Failed to set volume: {}", strerror(errno));
|
||||
else if (ioctl(device().fd, SND_GET_VOLUME_INFO, &device().volume) != 0)
|
||||
dwarnln("Failed to query new volume: {}", strerror(errno));
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
dwarnln("unknown packet type {}", static_cast<uint8_t>(packet.type));
|
||||
return false;
|
||||
@@ -278,7 +319,7 @@ void AudioServer::send_samples()
|
||||
for (size_t i = 0; i < samples_to_send; i++)
|
||||
{
|
||||
sample_buffer[i] = BAN::Math::clamp<sample_t>(
|
||||
0.2 * m_samples[m_samples_sent + i] * BAN::numeric_limits<kernel_sample_t>::max(),
|
||||
m_samples[m_samples_sent + i] * BAN::numeric_limits<kernel_sample_t>::max(),
|
||||
BAN::numeric_limits<kernel_sample_t>::min(),
|
||||
BAN::numeric_limits<kernel_sample_t>::max()
|
||||
);
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include <LibAudio/Audio.h>
|
||||
#include <LibAudio/Protocol.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
struct AudioDevice
|
||||
{
|
||||
int fd;
|
||||
@@ -15,6 +17,7 @@ struct AudioDevice
|
||||
uint32_t sample_rate;
|
||||
uint32_t total_pins;
|
||||
uint32_t current_pin;
|
||||
snd_volume_info volume;
|
||||
};
|
||||
|
||||
class AudioServer
|
||||
|
||||
@@ -68,6 +68,8 @@ static BAN::Optional<AudioDevice> initialize_audio_device(int fd)
|
||||
return {};
|
||||
if (ioctl(fd, SND_GET_PIN, &result.current_pin) != 0)
|
||||
return {};
|
||||
if (ioctl(fd, SND_GET_VOLUME_INFO, &result.volume) != 0)
|
||||
return {};
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ banan_link_library(TaskBar ban)
|
||||
banan_link_library(TaskBar libc)
|
||||
banan_link_library(TaskBar libfont)
|
||||
banan_link_library(TaskBar libgui)
|
||||
banan_include_headers(TaskBar libaudio)
|
||||
banan_include_headers(TaskBar libinput)
|
||||
|
||||
install(TARGETS TaskBar OPTIONAL)
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
#include <LibAudio/Protocol.h>
|
||||
#include <LibFont/Font.h>
|
||||
#include <LibGUI/Window.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
static BAN::ErrorOr<long long> read_integer_from_file(const char* file)
|
||||
{
|
||||
@@ -60,12 +63,57 @@ static BAN::String get_battery_percentage()
|
||||
return result;
|
||||
}
|
||||
|
||||
static int open_audio_server_fd()
|
||||
{
|
||||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
sockaddr_un addr;
|
||||
addr.sun_family = AF_UNIX;
|
||||
strcpy(addr.sun_path, LibAudio::s_audio_server_socket.data());
|
||||
if (connect(fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == -1)
|
||||
return -1;
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static BAN::String get_audio_volume()
|
||||
{
|
||||
static int fd = -1;
|
||||
if (fd == -1 && (fd = open_audio_server_fd()) == -1)
|
||||
return {};
|
||||
|
||||
const LibAudio::Packet request {
|
||||
.type = LibAudio::Packet::GetVolume,
|
||||
.parameter = {},
|
||||
};
|
||||
if (send(fd, &request, sizeof(request), 0) != sizeof(request))
|
||||
{
|
||||
close(fd);
|
||||
fd = -1;
|
||||
return {};
|
||||
}
|
||||
|
||||
uint32_t response;
|
||||
if (recv(fd, &response, sizeof(response), 0) != sizeof(response))
|
||||
{
|
||||
close(fd);
|
||||
fd = -1;
|
||||
return {};
|
||||
}
|
||||
|
||||
return MUST(BAN::String::formatted("vol {}% | ", response / 10));
|
||||
}
|
||||
|
||||
static BAN::ErrorOr<BAN::String> get_task_bar_string()
|
||||
{
|
||||
BAN::String result;
|
||||
|
||||
TRY(result.append(get_battery_percentage()));
|
||||
|
||||
TRY(result.append(get_audio_volume()));
|
||||
|
||||
const time_t current_time = time(nullptr);
|
||||
TRY(result.append(ctime(¤t_time)));
|
||||
result.pop_back();
|
||||
@@ -79,6 +127,8 @@ int main()
|
||||
constexpr uint32_t bg_color = 0xFF202020;
|
||||
constexpr uint32_t fg_color = 0xFFFFFFFF;
|
||||
|
||||
signal(SIGUSR1, [](int) {});
|
||||
|
||||
auto font = MUST(LibFont::Font::load("/usr/share/fonts/lat0-16.psfu"_sv));
|
||||
|
||||
auto attributes = LibGUI::Window::default_attributes;
|
||||
@@ -99,7 +149,8 @@ int main()
|
||||
|
||||
bool is_running = true;
|
||||
|
||||
const auto update_time_string =
|
||||
uint32_t old_text_w = 0;
|
||||
const auto update_string =
|
||||
[&]()
|
||||
{
|
||||
auto text_or_error = get_task_bar_string();
|
||||
@@ -112,15 +163,20 @@ int main()
|
||||
const uint32_t text_x = window->width() - text_w - padding;
|
||||
const uint32_t text_y = padding;
|
||||
|
||||
const uint32_t inval_w = BAN::Math::max(text_w, old_text_w);
|
||||
const uint32_t inval_x = window->width() - inval_w - padding;
|
||||
|
||||
auto& texture = window->texture();
|
||||
texture.fill_rect(text_x, text_y, text_w, text_h, bg_color);
|
||||
texture.fill_rect(inval_x, text_y, inval_w, text_h, bg_color);
|
||||
texture.draw_text(text, font, text_x, text_y, fg_color);
|
||||
window->invalidate(text_x, text_y, text_w, text_h);
|
||||
window->invalidate(inval_x, text_y, inval_w, text_h);
|
||||
|
||||
old_text_w = text_w;
|
||||
};
|
||||
|
||||
while (is_running)
|
||||
{
|
||||
update_time_string();
|
||||
update_string();
|
||||
|
||||
constexpr uint64_t ns_per_s = 1'000'000'000;
|
||||
|
||||
|
||||
@@ -418,11 +418,23 @@ void WindowServer::on_window_set_cursor(int fd, const LibGUI::WindowPacket::Wind
|
||||
invalidate(cursor_area().get_bounding_box(old_cursor));
|
||||
}
|
||||
|
||||
static void update_volume(const char* new_volume)
|
||||
{
|
||||
char command[128];
|
||||
sprintf(command, "audioctl --volume %s && kill -USR1 TaskBar", new_volume);
|
||||
system(command);
|
||||
}
|
||||
|
||||
void WindowServer::on_key_event(LibInput::KeyEvent event)
|
||||
{
|
||||
if (event.key == LibInput::Key::Super)
|
||||
m_is_mod_key_held = event.pressed();
|
||||
|
||||
if (event.pressed() && event.key == LibInput::Key::VolumeDown)
|
||||
update_volume("-5");
|
||||
if (event.pressed() && event.key == LibInput::Key::VolumeUp)
|
||||
update_volume("+5");
|
||||
|
||||
// Stop WindowServer with mod+shift+E
|
||||
if (m_is_mod_key_held && event.pressed() && event.shift() && event.key == LibInput::Key::E)
|
||||
{
|
||||
|
||||
@@ -74,17 +74,20 @@ static uint32_t send_request(int fd, LibAudio::Packet packet, bool wait_response
|
||||
|
||||
static void list_devices(int fd)
|
||||
{
|
||||
const uint32_t current_device = send_request(fd, { .type = LibAudio::Packet::GetDevice, .parameter = 0 }, true);
|
||||
const uint32_t current_pin = send_request(fd, { .type = LibAudio::Packet::GetPin, .parameter = 0 }, true);
|
||||
const uint32_t current_device = send_request(fd, { .type = LibAudio::Packet::GetDevice, .parameter = {} }, true);
|
||||
const uint32_t current_pin = send_request(fd, { .type = LibAudio::Packet::GetPin, .parameter = {} }, true);
|
||||
|
||||
const uint32_t total_devices = send_request(fd, { .type = LibAudio::Packet::QueryDevices, .parameter = 0 }, true);
|
||||
const uint32_t total_devices = send_request(fd, { .type = LibAudio::Packet::QueryDevices, .parameter = {} }, true);
|
||||
for (uint32_t dev = 0; dev < total_devices; dev++)
|
||||
{
|
||||
const uint32_t total_pins = send_request(fd, { .type = LibAudio::Packet::QueryPins, .parameter = dev }, true);
|
||||
|
||||
printf("Device %" PRIu32 "", dev);
|
||||
if (dev == current_device)
|
||||
printf(" (current)");
|
||||
{
|
||||
const uint32_t volume = send_request(fd, { .type = LibAudio::Packet::GetVolume, .parameter = {} }, true);
|
||||
printf(" (current) %" PRIu32 "%%", volume / 10);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
for (uint32_t pin = 0; pin < total_pins; pin++)
|
||||
@@ -103,16 +106,20 @@ int main(int argc, char** argv)
|
||||
BAN::Optional<uint32_t> device;
|
||||
BAN::Optional<uint32_t> pin;
|
||||
|
||||
BAN::Optional<uint32_t> volume;
|
||||
BAN::Optional<bool> volume_rel; // no value: absolute, false positive, true negative
|
||||
|
||||
for (;;)
|
||||
{
|
||||
static option long_options[] {
|
||||
{ "list", no_argument, nullptr, 'l' },
|
||||
{ "device", required_argument, nullptr, 'd' },
|
||||
{ "pin", required_argument, nullptr, 'p' },
|
||||
{ "volume", required_argument, nullptr, 'v' },
|
||||
{ "help", no_argument, nullptr, 'h' },
|
||||
};
|
||||
|
||||
int ch = getopt_long(argc, argv, "ld:p:h", long_options, nullptr);
|
||||
int ch = getopt_long(argc, argv, "ld:p:v:h", long_options, nullptr);
|
||||
if (ch == -1)
|
||||
break;
|
||||
|
||||
@@ -125,6 +132,7 @@ int main(int argc, char** argv)
|
||||
fprintf(stderr, " -l, --list list devices and their pins\n");
|
||||
fprintf(stderr, " -d, --device N set device index N as the current one\n");
|
||||
fprintf(stderr, " -p, --pin N set pin N as the current one\n");
|
||||
fprintf(stderr, " -v, --volume N set volume to N%%. if + or - is given, volume is relative to the current volume\n");
|
||||
fprintf(stderr, " -h, --help show this message and exit\n");
|
||||
return 0;
|
||||
case 'l':
|
||||
@@ -136,6 +144,11 @@ int main(int argc, char** argv)
|
||||
case 'p':
|
||||
pin = parse_u32_or_exit(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
if (optarg[0] == '-' || optarg[0] == '+')
|
||||
volume_rel = (optarg[0] == '-');
|
||||
volume = parse_u32_or_exit(optarg + volume_rel.has_value());
|
||||
break;
|
||||
case '?':
|
||||
fprintf(stderr, "invalid option %c\n", optopt);
|
||||
fprintf(stderr, "see '%s --help' for usage\n", argv[0]);
|
||||
@@ -143,7 +156,7 @@ int main(int argc, char** argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (!device.has_value() && !pin.has_value())
|
||||
if (!device.has_value() && !pin.has_value() && !volume.has_value())
|
||||
list = true;
|
||||
|
||||
const int fd = get_server_fd();
|
||||
@@ -156,6 +169,30 @@ int main(int argc, char** argv)
|
||||
if (pin.has_value())
|
||||
send_request(fd, { .type = LibAudio::Packet::SetPin, .parameter = pin.value() }, false);
|
||||
|
||||
if (volume.has_value())
|
||||
{
|
||||
const uint32_t volume10 = volume.value() * 10;
|
||||
|
||||
uint32_t param;
|
||||
if (!volume_rel.has_value())
|
||||
param = volume10;
|
||||
else
|
||||
{
|
||||
const uint32_t current = send_request(fd, { .type = LibAudio::Packet::GetVolume, .parameter = {} }, true);
|
||||
if (!volume_rel.value())
|
||||
param = current + volume10;
|
||||
else
|
||||
{
|
||||
if (current > volume10)
|
||||
param = current - volume10;
|
||||
else
|
||||
param = 0;
|
||||
}
|
||||
}
|
||||
|
||||
send_request(fd, { .type = LibAudio::Packet::SetVolume, .parameter = param }, false);
|
||||
}
|
||||
|
||||
if (list)
|
||||
list_devices(fd);
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
@@ -65,12 +67,18 @@ static int signal_name_to_number(const char* name)
|
||||
exit_with_error("unknown signal '%s'\n", name);
|
||||
}
|
||||
|
||||
static int parse_int(const char* string)
|
||||
static bool parse_int_safe(const char* string, int& out)
|
||||
{
|
||||
errno = 0;
|
||||
char* endptr;
|
||||
const int result = strtol(string, &endptr, 0);
|
||||
if (*endptr != '\0' || errno)
|
||||
out = strtol(string, &endptr, 0);
|
||||
return *endptr == '\0' && errno == 0;
|
||||
}
|
||||
|
||||
static int parse_int(const char* string)
|
||||
{
|
||||
int result;
|
||||
if (!parse_int_safe(string, result))
|
||||
exit_with_error("invalid integer '%s'\n", string);
|
||||
return result;
|
||||
}
|
||||
@@ -166,9 +174,61 @@ int main(int argc, char** argv)
|
||||
if (i >= argc)
|
||||
exit_with_error("missing pids\n");
|
||||
|
||||
DIR* proc_dirp = opendir("/proc");
|
||||
if (proc_dirp == NULL)
|
||||
perror("opendir");
|
||||
|
||||
for (; i < argc; i++)
|
||||
if (kill(parse_int(argv[i]), sig) == -1)
|
||||
{
|
||||
pid_t pid;
|
||||
if (parse_int_safe(argv[i], pid))
|
||||
{
|
||||
if (kill(pid, sig) == -1)
|
||||
perror("kill");
|
||||
continue;
|
||||
}
|
||||
|
||||
const size_t arg_len = strlen(argv[i]);
|
||||
if (proc_dirp == NULL || arg_len >= NAME_MAX)
|
||||
{
|
||||
fprintf(stderr, "cannot find process \"%s\"\n", argv[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
|
||||
dirent* dirent;
|
||||
rewinddir(proc_dirp);
|
||||
while ((dirent = readdir(proc_dirp)))
|
||||
{
|
||||
if (dirent->d_type != DT_DIR || !isdigit(dirent->d_name[0]))
|
||||
continue;
|
||||
|
||||
char path_buffer[PATH_MAX + 8];
|
||||
sprintf(path_buffer, "%s/cmdline", dirent->d_name);
|
||||
|
||||
int fd = openat(dirfd(proc_dirp), path_buffer, O_RDONLY);
|
||||
if (fd == -1)
|
||||
continue;
|
||||
|
||||
char buffer[NAME_MAX + 1];
|
||||
const ssize_t nread = read(fd, buffer, arg_len + 1);
|
||||
close(fd);
|
||||
|
||||
if (nread != static_cast<ssize_t>(arg_len + 1) || strcmp(argv[i], buffer) != 0)
|
||||
continue;
|
||||
|
||||
if (kill(parse_int(dirent->d_name), sig) == -1)
|
||||
perror("kill");
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
fprintf(stderr, "cannot find process \"%s\"\n", argv[i]);
|
||||
}
|
||||
|
||||
if (proc_dirp)
|
||||
closedir(proc_dirp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user