mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 23:47:45 +00:00
AK: Rename the common integer typedefs to make it obvious what they are.
These types can be picked up by including <AK/Types.h>: * u8, u16, u32, u64 (unsigned) * i8, i16, i32, i64 (signed)
This commit is contained in:
parent
c4c4bbc5ba
commit
27f699ef0c
208 changed files with 1603 additions and 1621 deletions
|
@ -43,7 +43,7 @@ BXVGADevice::BXVGADevice()
|
|||
m_framebuffer_address = PhysicalAddress(find_framebuffer_address());
|
||||
}
|
||||
|
||||
void BXVGADevice::set_register(word index, word data)
|
||||
void BXVGADevice::set_register(u16 index, u16 data)
|
||||
{
|
||||
IO::out16(VBE_DISPI_IOPORT_INDEX, index);
|
||||
IO::out16(VBE_DISPI_IOPORT_DATA, data);
|
||||
|
@ -54,10 +54,10 @@ void BXVGADevice::set_resolution(int width, int height)
|
|||
m_framebuffer_size = { width, height };
|
||||
|
||||
set_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
|
||||
set_register(VBE_DISPI_INDEX_XRES, (word)width);
|
||||
set_register(VBE_DISPI_INDEX_YRES, (word)height);
|
||||
set_register(VBE_DISPI_INDEX_VIRT_WIDTH, (word)width);
|
||||
set_register(VBE_DISPI_INDEX_VIRT_HEIGHT, (word)height * 2);
|
||||
set_register(VBE_DISPI_INDEX_XRES, (u16)width);
|
||||
set_register(VBE_DISPI_INDEX_YRES, (u16)height);
|
||||
set_register(VBE_DISPI_INDEX_VIRT_WIDTH, (u16)width);
|
||||
set_register(VBE_DISPI_INDEX_VIRT_HEIGHT, (u16)height * 2);
|
||||
set_register(VBE_DISPI_INDEX_BPP, 32);
|
||||
set_register(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED);
|
||||
set_register(VBE_DISPI_INDEX_BANK, 0);
|
||||
|
@ -66,15 +66,15 @@ void BXVGADevice::set_resolution(int width, int height)
|
|||
void BXVGADevice::set_y_offset(int offset)
|
||||
{
|
||||
ASSERT(offset <= m_framebuffer_size.height());
|
||||
set_register(VBE_DISPI_INDEX_Y_OFFSET, (word)offset);
|
||||
set_register(VBE_DISPI_INDEX_Y_OFFSET, (u16)offset);
|
||||
}
|
||||
|
||||
dword BXVGADevice::find_framebuffer_address()
|
||||
u32 BXVGADevice::find_framebuffer_address()
|
||||
{
|
||||
// NOTE: The QEMU card has the same PCI ID as the Bochs one.
|
||||
static const PCI::ID bochs_vga_id = { 0x1234, 0x1111 };
|
||||
static const PCI::ID virtualbox_vga_id = { 0x80ee, 0xbeef };
|
||||
dword framebuffer_address = 0;
|
||||
u32 framebuffer_address = 0;
|
||||
PCI::enumerate_all([&framebuffer_address](const PCI::Address& address, PCI::ID id) {
|
||||
if (id == bochs_vga_id || id == virtualbox_vga_id) {
|
||||
framebuffer_address = PCI::get_BAR0(address) & 0xfffffff0;
|
||||
|
@ -133,12 +133,12 @@ bool BXVGADevice::can_write(FileDescription&) const
|
|||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
ssize_t BXVGADevice::read(FileDescription&, byte*, ssize_t)
|
||||
ssize_t BXVGADevice::read(FileDescription&, u8*, ssize_t)
|
||||
{
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
ssize_t BXVGADevice::write(FileDescription&, const byte*, ssize_t)
|
||||
ssize_t BXVGADevice::write(FileDescription&, const u8*, ssize_t)
|
||||
{
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
|
|
@ -20,18 +20,18 @@ public:
|
|||
virtual int ioctl(FileDescription&, unsigned request, unsigned arg) override;
|
||||
virtual KResultOr<Region*> mmap(Process&, FileDescription&, VirtualAddress preferred_vaddr, size_t offset, size_t, int prot) override;
|
||||
|
||||
size_t framebuffer_size_in_bytes() const { return m_framebuffer_size.area() * sizeof(dword) * 2; }
|
||||
size_t framebuffer_size_in_bytes() const { return m_framebuffer_size.area() * sizeof(u32) * 2; }
|
||||
Size framebuffer_size() const { return m_framebuffer_size; }
|
||||
|
||||
private:
|
||||
virtual const char* class_name() const override { return "BXVGA"; }
|
||||
virtual bool can_read(FileDescription&) const override;
|
||||
virtual bool can_write(FileDescription&) const override;
|
||||
virtual ssize_t read(FileDescription&, byte*, ssize_t) override;
|
||||
virtual ssize_t write(FileDescription&, const byte*, ssize_t) override;
|
||||
virtual ssize_t read(FileDescription&, u8*, ssize_t) override;
|
||||
virtual ssize_t write(FileDescription&, const u8*, ssize_t) override;
|
||||
|
||||
void set_register(word index, word value);
|
||||
dword find_framebuffer_address();
|
||||
void set_register(u16 index, u16 value);
|
||||
u32 find_framebuffer_address();
|
||||
|
||||
PhysicalAddress m_framebuffer_address;
|
||||
Size m_framebuffer_size;
|
||||
|
|
|
@ -19,7 +19,7 @@ DebugLogDevice::~DebugLogDevice()
|
|||
{
|
||||
}
|
||||
|
||||
ssize_t DebugLogDevice::write(FileDescription&, const byte* data, ssize_t data_size)
|
||||
ssize_t DebugLogDevice::write(FileDescription&, const u8* data, ssize_t data_size)
|
||||
{
|
||||
for (int i = 0; i < data_size; ++i)
|
||||
IO::out8(0xe9, data[i]);
|
||||
|
|
|
@ -9,8 +9,8 @@ public:
|
|||
|
||||
private:
|
||||
// ^CharacterDevice
|
||||
virtual ssize_t read(FileDescription&, byte*, ssize_t) override { return 0; }
|
||||
virtual ssize_t write(FileDescription&, const byte*, ssize_t) override;
|
||||
virtual ssize_t read(FileDescription&, u8*, ssize_t) override { return 0; }
|
||||
virtual ssize_t write(FileDescription&, const u8*, ssize_t) override;
|
||||
virtual bool can_write(FileDescription&) const override { return true; }
|
||||
virtual bool can_read(FileDescription&) const override { return true; }
|
||||
virtual const char* class_name() const override { return "DebugLogDevice"; }
|
||||
|
|
|
@ -8,21 +8,21 @@ DiskDevice::~DiskDevice()
|
|||
{
|
||||
}
|
||||
|
||||
bool DiskDevice::read(DiskOffset offset, unsigned length, byte* out) const
|
||||
bool DiskDevice::read(DiskOffset offset, unsigned length, u8* out) const
|
||||
{
|
||||
ASSERT((offset % block_size()) == 0);
|
||||
ASSERT((length % block_size()) == 0);
|
||||
dword first_block = offset / block_size();
|
||||
dword end_block = (offset + length) / block_size();
|
||||
u32 first_block = offset / block_size();
|
||||
u32 end_block = (offset + length) / block_size();
|
||||
return const_cast<DiskDevice*>(this)->read_blocks(first_block, end_block - first_block, out);
|
||||
}
|
||||
|
||||
bool DiskDevice::write(DiskOffset offset, unsigned length, const byte* in)
|
||||
bool DiskDevice::write(DiskOffset offset, unsigned length, const u8* in)
|
||||
{
|
||||
ASSERT((offset % block_size()) == 0);
|
||||
ASSERT((length % block_size()) == 0);
|
||||
dword first_block = offset / block_size();
|
||||
dword end_block = (offset + length) / block_size();
|
||||
u32 first_block = offset / block_size();
|
||||
u32 end_block = (offset + length) / block_size();
|
||||
ASSERT(first_block <= 0xffffffff);
|
||||
ASSERT(end_block <= 0xffffffff);
|
||||
return write_blocks(first_block, end_block - first_block, in);
|
||||
|
|
|
@ -4,21 +4,21 @@
|
|||
#include <AK/Types.h>
|
||||
|
||||
// FIXME: Support 64-bit DiskOffset
|
||||
typedef dword DiskOffset;
|
||||
typedef u32 DiskOffset;
|
||||
|
||||
class DiskDevice : public RefCounted<DiskDevice> {
|
||||
public:
|
||||
virtual ~DiskDevice();
|
||||
|
||||
virtual unsigned block_size() const = 0;
|
||||
virtual bool read_block(unsigned index, byte*) const = 0;
|
||||
virtual bool write_block(unsigned index, const byte*) = 0;
|
||||
virtual bool read_block(unsigned index, u8*) const = 0;
|
||||
virtual bool write_block(unsigned index, const u8*) = 0;
|
||||
virtual const char* class_name() const = 0;
|
||||
bool read(DiskOffset, unsigned length, byte*) const;
|
||||
bool write(DiskOffset, unsigned length, const byte*);
|
||||
bool read(DiskOffset, unsigned length, u8*) const;
|
||||
bool write(DiskOffset, unsigned length, const u8*);
|
||||
|
||||
virtual bool read_blocks(unsigned index, word count, byte*) = 0;
|
||||
virtual bool write_blocks(unsigned index, word count, const byte*) = 0;
|
||||
virtual bool read_blocks(unsigned index, u16 count, u8*) = 0;
|
||||
virtual bool write_blocks(unsigned index, u16 count, const u8*) = 0;
|
||||
|
||||
protected:
|
||||
DiskDevice();
|
||||
|
|
|
@ -22,7 +22,7 @@ unsigned DiskPartition::block_size() const
|
|||
return m_device->block_size();
|
||||
}
|
||||
|
||||
bool DiskPartition::read_block(unsigned index, byte* out) const
|
||||
bool DiskPartition::read_block(unsigned index, u8* out) const
|
||||
{
|
||||
#ifdef OFFD_DEBUG
|
||||
kprintf("DiskPartition::read_block %u (really: %u)\n", index, m_block_offset + index);
|
||||
|
@ -31,7 +31,7 @@ bool DiskPartition::read_block(unsigned index, byte* out) const
|
|||
return m_device->read_block(m_block_offset + index, out);
|
||||
}
|
||||
|
||||
bool DiskPartition::write_block(unsigned index, const byte* data)
|
||||
bool DiskPartition::write_block(unsigned index, const u8* data)
|
||||
{
|
||||
#ifdef OFFD_DEBUG
|
||||
kprintf("DiskPartition::write_block %u (really: %u)\n", index, m_block_offset + index);
|
||||
|
@ -40,7 +40,7 @@ bool DiskPartition::write_block(unsigned index, const byte* data)
|
|||
return m_device->write_block(m_block_offset + index, data);
|
||||
}
|
||||
|
||||
bool DiskPartition::read_blocks(unsigned index, word count, byte* out)
|
||||
bool DiskPartition::read_blocks(unsigned index, u16 count, u8* out)
|
||||
{
|
||||
#ifdef OFFD_DEBUG
|
||||
kprintf("DiskPartition::read_blocks %u (really: %u) count=%u\n", index, m_block_offset + index, count);
|
||||
|
@ -49,7 +49,7 @@ bool DiskPartition::read_blocks(unsigned index, word count, byte* out)
|
|||
return m_device->read_blocks(m_block_offset + index, count, out);
|
||||
}
|
||||
|
||||
bool DiskPartition::write_blocks(unsigned index, word count, const byte* data)
|
||||
bool DiskPartition::write_blocks(unsigned index, u16 count, const u8* data)
|
||||
{
|
||||
#ifdef OFFD_DEBUG
|
||||
kprintf("DiskPartition::write_blocks %u (really: %u) count=%u\n", index, m_block_offset + index, count);
|
||||
|
|
|
@ -9,10 +9,10 @@ public:
|
|||
virtual ~DiskPartition();
|
||||
|
||||
virtual unsigned block_size() const override;
|
||||
virtual bool read_block(unsigned index, byte* out) const override;
|
||||
virtual bool write_block(unsigned index, const byte*) override;
|
||||
virtual bool read_blocks(unsigned index, word count, byte*) override;
|
||||
virtual bool write_blocks(unsigned index, word count, const byte*) override;
|
||||
virtual bool read_block(unsigned index, u8* out) const override;
|
||||
virtual bool write_block(unsigned index, const u8*) override;
|
||||
virtual bool read_blocks(unsigned index, u16 count, u8*) override;
|
||||
virtual bool write_blocks(unsigned index, u16 count, const u8*) override;
|
||||
|
||||
private:
|
||||
virtual const char* class_name() const override;
|
||||
|
|
|
@ -32,19 +32,19 @@ unsigned FileBackedDiskDevice::block_size() const
|
|||
return m_block_size;
|
||||
}
|
||||
|
||||
bool FileBackedDiskDevice::read_block(unsigned index, byte* out) const
|
||||
bool FileBackedDiskDevice::read_block(unsigned index, u8* out) const
|
||||
{
|
||||
DiskOffset offset = index * m_block_size;
|
||||
return read_internal(offset, block_size(), out);
|
||||
}
|
||||
|
||||
bool FileBackedDiskDevice::write_block(unsigned index, const byte* data)
|
||||
bool FileBackedDiskDevice::write_block(unsigned index, const u8* data)
|
||||
{
|
||||
DiskOffset offset = index * m_block_size;
|
||||
return write_internal(offset, block_size(), data);
|
||||
}
|
||||
|
||||
bool FileBackedDiskDevice::read_internal(DiskOffset offset, unsigned length, byte* out) const
|
||||
bool FileBackedDiskDevice::read_internal(DiskOffset offset, unsigned length, u8* out) const
|
||||
{
|
||||
#ifndef IGNORE_FILE_LENGTH
|
||||
if (offset + length >= m_file_length)
|
||||
|
@ -54,12 +54,12 @@ bool FileBackedDiskDevice::read_internal(DiskOffset offset, unsigned length, byt
|
|||
printf("[FileBackedDiskDevice] Read device @ offset %llx, length %u\n", offset, length);
|
||||
#endif
|
||||
fseeko(m_file, offset, SEEK_SET);
|
||||
unsigned nread = fread(out, sizeof(byte), length, m_file);
|
||||
unsigned nread = fread(out, sizeof(u8), length, m_file);
|
||||
ASSERT(nread == length);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileBackedDiskDevice::write_internal(DiskOffset offset, unsigned length, const byte* data)
|
||||
bool FileBackedDiskDevice::write_internal(DiskOffset offset, unsigned length, const u8* data)
|
||||
{
|
||||
#ifndef IGNORE_FILE_LENGTH
|
||||
if (offset + length >= m_file_length)
|
||||
|
@ -70,7 +70,7 @@ bool FileBackedDiskDevice::write_internal(DiskOffset offset, unsigned length, co
|
|||
#endif
|
||||
fseeko(m_file, offset, SEEK_SET);
|
||||
// size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
|
||||
unsigned nwritten = fwrite(data, sizeof(byte), length, m_file);
|
||||
unsigned nwritten = fwrite(data, sizeof(u8), length, m_file);
|
||||
ASSERT(nwritten == length);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -14,14 +14,14 @@ public:
|
|||
bool is_valid() const { return m_file; }
|
||||
|
||||
virtual unsigned block_size() const override;
|
||||
virtual bool read_block(unsigned index, byte* out) const override;
|
||||
virtual bool write_block(unsigned index, const byte*) override;
|
||||
virtual bool read_block(unsigned index, u8* out) const override;
|
||||
virtual bool write_block(unsigned index, const u8*) override;
|
||||
|
||||
private:
|
||||
virtual const char* class_name() const override;
|
||||
|
||||
bool read_internal(DiskOffset, unsigned length, byte* out) const;
|
||||
bool write_internal(DiskOffset, unsigned length, const byte* data);
|
||||
bool read_internal(DiskOffset, unsigned length, u8* out) const;
|
||||
bool write_internal(DiskOffset, unsigned length, const u8* data);
|
||||
|
||||
FileBackedDiskDevice(String&& imagePath, unsigned block_size);
|
||||
|
||||
|
|
|
@ -17,14 +17,14 @@ bool FullDevice::can_read(FileDescription&) const
|
|||
return true;
|
||||
}
|
||||
|
||||
ssize_t FullDevice::read(FileDescription&, byte* buffer, ssize_t size)
|
||||
ssize_t FullDevice::read(FileDescription&, u8* buffer, ssize_t size)
|
||||
{
|
||||
ssize_t count = min(PAGE_SIZE, size);
|
||||
memset(buffer, 0, (size_t)count);
|
||||
return count;
|
||||
}
|
||||
|
||||
ssize_t FullDevice::write(FileDescription&, const byte*, ssize_t size)
|
||||
ssize_t FullDevice::write(FileDescription&, const u8*, ssize_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
|
|
@ -10,8 +10,8 @@ public:
|
|||
|
||||
private:
|
||||
// ^CharacterDevice
|
||||
virtual ssize_t read(FileDescription&, byte*, ssize_t) override;
|
||||
virtual ssize_t write(FileDescription&, const byte*, ssize_t) override;
|
||||
virtual ssize_t read(FileDescription&, u8*, ssize_t) override;
|
||||
virtual ssize_t write(FileDescription&, const u8*, ssize_t) override;
|
||||
virtual bool can_read(FileDescription&) const override;
|
||||
virtual bool can_write(FileDescription&) const override { return true; }
|
||||
virtual const char* class_name() const override { return "FullDevice"; }
|
||||
|
|
|
@ -106,19 +106,19 @@ unsigned IDEDiskDevice::block_size() const
|
|||
return 512;
|
||||
}
|
||||
|
||||
bool IDEDiskDevice::read_blocks(unsigned index, word count, byte* out)
|
||||
bool IDEDiskDevice::read_blocks(unsigned index, u16 count, u8* out)
|
||||
{
|
||||
if (m_bus_master_base && m_dma_enabled.resource())
|
||||
return read_sectors_with_dma(index, count, out);
|
||||
return read_sectors(index, count, out);
|
||||
}
|
||||
|
||||
bool IDEDiskDevice::read_block(unsigned index, byte* out) const
|
||||
bool IDEDiskDevice::read_block(unsigned index, u8* out) const
|
||||
{
|
||||
return const_cast<IDEDiskDevice*>(this)->read_blocks(index, 1, out);
|
||||
}
|
||||
|
||||
bool IDEDiskDevice::write_blocks(unsigned index, word count, const byte* data)
|
||||
bool IDEDiskDevice::write_blocks(unsigned index, u16 count, const u8* data)
|
||||
{
|
||||
if (m_bus_master_base && m_dma_enabled.resource())
|
||||
return write_sectors_with_dma(index, count, data);
|
||||
|
@ -129,12 +129,12 @@ bool IDEDiskDevice::write_blocks(unsigned index, word count, const byte* data)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool IDEDiskDevice::write_block(unsigned index, const byte* data)
|
||||
bool IDEDiskDevice::write_block(unsigned index, const u8* data)
|
||||
{
|
||||
return write_blocks(index, 1, data);
|
||||
}
|
||||
|
||||
static void print_ide_status(byte status)
|
||||
static void print_ide_status(u8 status)
|
||||
{
|
||||
kprintf("DRQ=%u BSY=%u DRDY=%u DSC=%u DF=%u CORR=%u IDX=%u ERR=%u\n",
|
||||
(status & ATA_SR_DRQ) != 0,
|
||||
|
@ -166,7 +166,7 @@ bool IDEDiskDevice::wait_for_irq()
|
|||
|
||||
void IDEDiskDevice::handle_irq()
|
||||
{
|
||||
byte status = IO::in8(m_io_base + ATA_REG_STATUS);
|
||||
u8 status = IO::in8(m_io_base + ATA_REG_STATUS);
|
||||
if (status & ATA_SR_ERR) {
|
||||
print_ide_status(status);
|
||||
m_device_error = IO::in8(m_io_base + ATA_REG_ERROR);
|
||||
|
@ -192,7 +192,7 @@ void IDEDiskDevice::initialize()
|
|||
});
|
||||
|
||||
#ifdef DISK_DEBUG
|
||||
byte status = IO::in8(m_io_base + ATA_REG_STATUS);
|
||||
u8 status = IO::in8(m_io_base + ATA_REG_STATUS);
|
||||
kprintf("initial status: ");
|
||||
print_ide_status(status);
|
||||
#endif
|
||||
|
@ -213,19 +213,19 @@ void IDEDiskDevice::initialize()
|
|||
|
||||
ByteBuffer wbuf = ByteBuffer::create_uninitialized(512);
|
||||
ByteBuffer bbuf = ByteBuffer::create_uninitialized(512);
|
||||
byte* b = bbuf.pointer();
|
||||
word* w = (word*)wbuf.pointer();
|
||||
const word* wbufbase = (word*)wbuf.pointer();
|
||||
u8* b = bbuf.pointer();
|
||||
u16* w = (u16*)wbuf.pointer();
|
||||
const u16* wbufbase = (u16*)wbuf.pointer();
|
||||
|
||||
for (dword i = 0; i < 256; ++i) {
|
||||
word data = IO::in16(m_io_base + ATA_REG_DATA);
|
||||
for (u32 i = 0; i < 256; ++i) {
|
||||
u16 data = IO::in16(m_io_base + ATA_REG_DATA);
|
||||
*(w++) = data;
|
||||
*(b++) = MSB(data);
|
||||
*(b++) = LSB(data);
|
||||
}
|
||||
|
||||
// "Unpad" the device name string.
|
||||
for (dword i = 93; i > 54 && bbuf[i] == ' '; --i)
|
||||
for (u32 i = 93; i > 54 && bbuf[i] == ' '; --i)
|
||||
bbuf[i] = 0;
|
||||
|
||||
m_cylinders = wbufbase[1];
|
||||
|
@ -249,13 +249,13 @@ void IDEDiskDevice::initialize()
|
|||
}
|
||||
}
|
||||
|
||||
static void wait_400ns(word io_base)
|
||||
static void wait_400ns(u16 io_base)
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
IO::in8(io_base + ATA_REG_ALTSTATUS);
|
||||
}
|
||||
|
||||
bool IDEDiskDevice::read_sectors_with_dma(dword lba, word count, byte* outbuf)
|
||||
bool IDEDiskDevice::read_sectors_with_dma(u32 lba, u16 count, u8* outbuf)
|
||||
{
|
||||
LOCKER(m_lock);
|
||||
#ifdef DISK_DEBUG
|
||||
|
@ -275,7 +275,7 @@ bool IDEDiskDevice::read_sectors_with_dma(dword lba, word count, byte* outbuf)
|
|||
IO::out8(m_bus_master_base, 0);
|
||||
|
||||
// Write the PRDT location
|
||||
IO::out32(m_bus_master_base + 4, (dword)&m_prdt);
|
||||
IO::out32(m_bus_master_base + 4, (u32)&m_prdt);
|
||||
|
||||
// Turn on "Interrupt" and "Error" flag. The error flag should be cleared by hardware.
|
||||
IO::out8(m_bus_master_base + 2, IO::in8(m_bus_master_base + 2) | 0x6);
|
||||
|
@ -332,7 +332,7 @@ bool IDEDiskDevice::read_sectors_with_dma(dword lba, word count, byte* outbuf)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool IDEDiskDevice::read_sectors(dword start_sector, word count, byte* outbuf)
|
||||
bool IDEDiskDevice::read_sectors(u32 start_sector, u16 count, u8* outbuf)
|
||||
{
|
||||
ASSERT(count <= 256);
|
||||
LOCKER(m_lock);
|
||||
|
@ -369,7 +369,7 @@ bool IDEDiskDevice::read_sectors(dword start_sector, word count, byte* outbuf)
|
|||
if (m_device_error)
|
||||
return false;
|
||||
|
||||
byte status = IO::in8(m_io_base + ATA_REG_STATUS);
|
||||
u8 status = IO::in8(m_io_base + ATA_REG_STATUS);
|
||||
ASSERT(status & ATA_SR_DRQ);
|
||||
#ifdef DISK_DEBUG
|
||||
kprintf("Retrieving %u bytes (status=%b), outbuf=%p...\n", count * 512, status, outbuf);
|
||||
|
@ -379,7 +379,7 @@ bool IDEDiskDevice::read_sectors(dword start_sector, word count, byte* outbuf)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool IDEDiskDevice::write_sectors_with_dma(dword lba, word count, const byte* inbuf)
|
||||
bool IDEDiskDevice::write_sectors_with_dma(u32 lba, u16 count, const u8* inbuf)
|
||||
{
|
||||
LOCKER(m_lock);
|
||||
#ifdef DISK_DEBUG
|
||||
|
@ -401,7 +401,7 @@ bool IDEDiskDevice::write_sectors_with_dma(dword lba, word count, const byte* in
|
|||
IO::out8(m_bus_master_base, 0);
|
||||
|
||||
// Write the PRDT location
|
||||
IO::out32(m_bus_master_base + 4, (dword)&m_prdt);
|
||||
IO::out32(m_bus_master_base + 4, (u32)&m_prdt);
|
||||
|
||||
// Turn on "Interrupt" and "Error" flag. The error flag should be cleared by hardware.
|
||||
IO::out8(m_bus_master_base + 2, IO::in8(m_bus_master_base + 2) | 0x6);
|
||||
|
@ -453,7 +453,7 @@ bool IDEDiskDevice::write_sectors_with_dma(dword lba, word count, const byte* in
|
|||
return true;
|
||||
}
|
||||
|
||||
bool IDEDiskDevice::write_sectors(dword start_sector, word count, const byte* data)
|
||||
bool IDEDiskDevice::write_sectors(u32 start_sector, u16 count, const u8* data)
|
||||
{
|
||||
ASSERT(count <= 256);
|
||||
LOCKER(m_lock);
|
||||
|
@ -484,7 +484,7 @@ bool IDEDiskDevice::write_sectors(dword start_sector, word count, const byte* da
|
|||
while (!(IO::in8(m_io_base + ATA_REG_STATUS) & ATA_SR_DRQ))
|
||||
;
|
||||
|
||||
byte status = IO::in8(m_io_base + ATA_REG_STATUS);
|
||||
u8 status = IO::in8(m_io_base + ATA_REG_STATUS);
|
||||
ASSERT(status & ATA_SR_DRQ);
|
||||
IO::repeated_out16(m_io_base + ATA_REG_DATA, data, count * 256);
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
|
||||
struct PhysicalRegionDescriptor {
|
||||
PhysicalAddress offset;
|
||||
word size { 0 };
|
||||
word end_of_table { 0 };
|
||||
u16 size { 0 };
|
||||
u16 end_of_table { 0 };
|
||||
};
|
||||
|
||||
class IDEDiskDevice final : public IRQHandler
|
||||
|
@ -23,10 +23,10 @@ public:
|
|||
|
||||
// ^DiskDevice
|
||||
virtual unsigned block_size() const override;
|
||||
virtual bool read_block(unsigned index, byte*) const override;
|
||||
virtual bool write_block(unsigned index, const byte*) override;
|
||||
virtual bool read_blocks(unsigned index, word count, byte*) override;
|
||||
virtual bool write_blocks(unsigned index, word count, const byte*) override;
|
||||
virtual bool read_block(unsigned index, u8*) const override;
|
||||
virtual bool write_block(unsigned index, const u8*) override;
|
||||
virtual bool read_blocks(unsigned index, u16 count, u8*) override;
|
||||
virtual bool write_blocks(unsigned index, u16 count, const u8*) override;
|
||||
|
||||
protected:
|
||||
IDEDiskDevice();
|
||||
|
@ -40,22 +40,22 @@ private:
|
|||
|
||||
void initialize();
|
||||
bool wait_for_irq();
|
||||
bool read_sectors_with_dma(dword lba, word count, byte*);
|
||||
bool write_sectors_with_dma(dword lba, word count, const byte*);
|
||||
bool read_sectors(dword lba, word count, byte* buffer);
|
||||
bool write_sectors(dword lba, word count, const byte* data);
|
||||
bool read_sectors_with_dma(u32 lba, u16 count, u8*);
|
||||
bool write_sectors_with_dma(u32 lba, u16 count, const u8*);
|
||||
bool read_sectors(u32 lba, u16 count, u8* buffer);
|
||||
bool write_sectors(u32 lba, u16 count, const u8* data);
|
||||
|
||||
Lock m_lock { "IDEDiskDevice" };
|
||||
word m_cylinders { 0 };
|
||||
word m_heads { 0 };
|
||||
word m_sectors_per_track { 0 };
|
||||
word m_io_base { 0 };
|
||||
u16 m_cylinders { 0 };
|
||||
u16 m_heads { 0 };
|
||||
u16 m_sectors_per_track { 0 };
|
||||
u16 m_io_base { 0 };
|
||||
volatile bool m_interrupted { false };
|
||||
volatile byte m_device_error { 0 };
|
||||
volatile u8 m_device_error { 0 };
|
||||
|
||||
PCI::Address m_pci_address;
|
||||
PhysicalRegionDescriptor m_prdt;
|
||||
RefPtr<PhysicalPage> m_dma_buffer_page;
|
||||
word m_bus_master_base { 0 };
|
||||
u16 m_bus_master_base { 0 };
|
||||
Lockable<bool> m_dma_enabled;
|
||||
};
|
||||
|
|
|
@ -223,7 +223,7 @@ static KeyCode shifted_key_map[0x100] = {
|
|||
Key_Logo,
|
||||
};
|
||||
|
||||
void KeyboardDevice::key_state_changed(byte raw, bool pressed)
|
||||
void KeyboardDevice::key_state_changed(u8 raw, bool pressed)
|
||||
{
|
||||
Event event;
|
||||
event.key = (m_modifiers & Mod_Shift) ? shifted_key_map[raw] : unshifted_key_map[raw];
|
||||
|
@ -239,11 +239,11 @@ void KeyboardDevice::key_state_changed(byte raw, bool pressed)
|
|||
void KeyboardDevice::handle_irq()
|
||||
{
|
||||
for (;;) {
|
||||
byte status = IO::in8(I8042_STATUS);
|
||||
u8 status = IO::in8(I8042_STATUS);
|
||||
if (!(((status & I8042_WHICH_BUFFER) == I8042_KEYBOARD_BUFFER) && (status & I8042_BUFFER_FULL)))
|
||||
return;
|
||||
byte raw = IO::in8(I8042_BUFFER);
|
||||
byte ch = raw & 0x7f;
|
||||
u8 raw = IO::in8(I8042_BUFFER);
|
||||
u8 ch = raw & 0x7f;
|
||||
bool pressed = !(raw & 0x80);
|
||||
|
||||
#ifdef KEYBOARD_DEBUG
|
||||
|
@ -316,7 +316,7 @@ bool KeyboardDevice::can_read(FileDescription&) const
|
|||
return !m_queue.is_empty();
|
||||
}
|
||||
|
||||
ssize_t KeyboardDevice::read(FileDescription&, byte* buffer, ssize_t size)
|
||||
ssize_t KeyboardDevice::read(FileDescription&, u8* buffer, ssize_t size)
|
||||
{
|
||||
ssize_t nread = 0;
|
||||
while (nread < size) {
|
||||
|
@ -332,7 +332,7 @@ ssize_t KeyboardDevice::read(FileDescription&, byte* buffer, ssize_t size)
|
|||
return nread;
|
||||
}
|
||||
|
||||
ssize_t KeyboardDevice::write(FileDescription&, const byte*, ssize_t)
|
||||
ssize_t KeyboardDevice::write(FileDescription&, const u8*, ssize_t)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -23,9 +23,9 @@ public:
|
|||
void set_client(KeyboardClient* client) { m_client = client; }
|
||||
|
||||
// ^CharacterDevice
|
||||
virtual ssize_t read(FileDescription&, byte* buffer, ssize_t) override;
|
||||
virtual ssize_t read(FileDescription&, u8* buffer, ssize_t) override;
|
||||
virtual bool can_read(FileDescription&) const override;
|
||||
virtual ssize_t write(FileDescription&, const byte* buffer, ssize_t) override;
|
||||
virtual ssize_t write(FileDescription&, const u8* buffer, ssize_t) override;
|
||||
virtual bool can_write(FileDescription&) const override { return true; }
|
||||
|
||||
private:
|
||||
|
@ -35,8 +35,8 @@ private:
|
|||
// ^CharacterDevice
|
||||
virtual const char* class_name() const override { return "KeyboardDevice"; }
|
||||
|
||||
void key_state_changed(byte raw, bool pressed);
|
||||
void update_modifier(byte modifier, bool state)
|
||||
void key_state_changed(u8 raw, bool pressed);
|
||||
void update_modifier(u8 modifier, bool state)
|
||||
{
|
||||
if (state)
|
||||
m_modifiers |= modifier;
|
||||
|
@ -46,7 +46,7 @@ private:
|
|||
|
||||
KeyboardClient* m_client { nullptr };
|
||||
CircularQueue<Event, 16> m_queue;
|
||||
byte m_modifiers { 0 };
|
||||
u8 m_modifiers { 0 };
|
||||
};
|
||||
|
||||
class KeyboardClient {
|
||||
|
|
|
@ -8,23 +8,23 @@
|
|||
#define MBR_SIGNATURE 0xaa55
|
||||
|
||||
struct MBRPartitionEntry {
|
||||
byte status;
|
||||
byte chs1[3];
|
||||
byte type;
|
||||
byte chs2[3];
|
||||
dword offset;
|
||||
dword length;
|
||||
u8 status;
|
||||
u8 chs1[3];
|
||||
u8 type;
|
||||
u8 chs2[3];
|
||||
u32 offset;
|
||||
u32 length;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct MBRPartitionHeader {
|
||||
byte code1[218];
|
||||
word ts_zero;
|
||||
byte ts_drive, ts_seconds, ts_minutes, ts_hours;
|
||||
byte code2[216];
|
||||
dword disk_signature;
|
||||
word disk_signature_zero;
|
||||
u8 code1[218];
|
||||
u16 ts_zero;
|
||||
u8 ts_drive, ts_seconds, ts_minutes, ts_hours;
|
||||
u8 code2[216];
|
||||
u32 disk_signature;
|
||||
u16 disk_signature_zero;
|
||||
MBRPartitionEntry entry[4];
|
||||
word mbr_signature;
|
||||
u16 mbr_signature;
|
||||
} __attribute__((packed));
|
||||
|
||||
class MBRPartitionTable {
|
||||
|
@ -43,5 +43,5 @@ private:
|
|||
ByteBuffer read_header() const;
|
||||
const MBRPartitionHeader& header() const;
|
||||
|
||||
byte m_cached_header[512];
|
||||
u8 m_cached_header[512];
|
||||
};
|
||||
|
|
|
@ -25,12 +25,12 @@ bool NullDevice::can_read(FileDescription&) const
|
|||
return true;
|
||||
}
|
||||
|
||||
ssize_t NullDevice::read(FileDescription&, byte*, ssize_t)
|
||||
ssize_t NullDevice::read(FileDescription&, u8*, ssize_t)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t NullDevice::write(FileDescription&, const byte*, ssize_t buffer_size)
|
||||
ssize_t NullDevice::write(FileDescription&, const u8*, ssize_t buffer_size)
|
||||
{
|
||||
return min(PAGE_SIZE, buffer_size);
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ public:
|
|||
|
||||
private:
|
||||
// ^CharacterDevice
|
||||
virtual ssize_t read(FileDescription&, byte*, ssize_t) override;
|
||||
virtual ssize_t write(FileDescription&, const byte*, ssize_t) override;
|
||||
virtual ssize_t read(FileDescription&, u8*, ssize_t) override;
|
||||
virtual ssize_t write(FileDescription&, const u8*, ssize_t) override;
|
||||
virtual bool can_write(FileDescription&) const override { return true; }
|
||||
virtual bool can_read(FileDescription&) const override;
|
||||
virtual const char* class_name() const override { return "NullDevice"; }
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
void PCSpeaker::tone_on(int frequency)
|
||||
{
|
||||
IO::out8(PIT_CTL, TIMER2_SELECT | WRITE_WORD | MODE_SQUARE_WAVE);
|
||||
word timer_reload = BASE_FREQUENCY / frequency;
|
||||
u16 timer_reload = BASE_FREQUENCY / frequency;
|
||||
|
||||
IO::out8(TIMER2_CTL, LSB(timer_reload));
|
||||
IO::out8(TIMER2_CTL, MSB(timer_reload));
|
||||
|
|
|
@ -39,11 +39,11 @@ PS2MouseDevice& PS2MouseDevice::the()
|
|||
void PS2MouseDevice::handle_irq()
|
||||
{
|
||||
for (;;) {
|
||||
byte status = IO::in8(I8042_STATUS);
|
||||
u8 status = IO::in8(I8042_STATUS);
|
||||
if (!(((status & I8042_WHICH_BUFFER) == I8042_MOUSE_BUFFER) && (status & I8042_BUFFER_FULL)))
|
||||
return;
|
||||
|
||||
byte data = IO::in8(I8042_BUFFER);
|
||||
u8 data = IO::in8(I8042_BUFFER);
|
||||
m_data[m_data_state] = data;
|
||||
|
||||
auto commit_packet = [&] {
|
||||
|
@ -113,13 +113,13 @@ void PS2MouseDevice::parse_data_packet()
|
|||
m_queue.enqueue(packet);
|
||||
}
|
||||
|
||||
void PS2MouseDevice::wait_then_write(byte port, byte data)
|
||||
void PS2MouseDevice::wait_then_write(u8 port, u8 data)
|
||||
{
|
||||
prepare_for_output();
|
||||
IO::out8(port, data);
|
||||
}
|
||||
|
||||
byte PS2MouseDevice::wait_then_read(byte port)
|
||||
u8 PS2MouseDevice::wait_then_read(u8 port)
|
||||
{
|
||||
prepare_for_input();
|
||||
return IO::in8(port);
|
||||
|
@ -135,7 +135,7 @@ void PS2MouseDevice::initialize()
|
|||
|
||||
// Enable the PS/2 mouse IRQ (12).
|
||||
// NOTE: The keyboard uses IRQ 1 (and is enabled by bit 0 in this register).
|
||||
byte status = wait_then_read(0x60) | 2;
|
||||
u8 status = wait_then_read(0x60) | 2;
|
||||
wait_then_write(0x64, 0x60);
|
||||
wait_then_write(0x60, status);
|
||||
|
||||
|
@ -149,7 +149,7 @@ void PS2MouseDevice::initialize()
|
|||
|
||||
mouse_write(PS2MOUSE_GET_DEVICE_ID);
|
||||
expect_ack();
|
||||
byte device_id = mouse_read();
|
||||
u8 device_id = mouse_read();
|
||||
|
||||
if (device_id != PS2MOUSE_INTELLIMOUSE_ID) {
|
||||
// Send magical wheel initiation sequence.
|
||||
|
@ -183,7 +183,7 @@ void PS2MouseDevice::initialize()
|
|||
|
||||
void PS2MouseDevice::expect_ack()
|
||||
{
|
||||
byte data = mouse_read();
|
||||
u8 data = mouse_read();
|
||||
ASSERT(data == I8042_ACK);
|
||||
}
|
||||
|
||||
|
@ -203,7 +203,7 @@ void PS2MouseDevice::prepare_for_output()
|
|||
}
|
||||
}
|
||||
|
||||
void PS2MouseDevice::mouse_write(byte data)
|
||||
void PS2MouseDevice::mouse_write(u8 data)
|
||||
{
|
||||
prepare_for_output();
|
||||
IO::out8(0x64, 0xd4);
|
||||
|
@ -211,7 +211,7 @@ void PS2MouseDevice::mouse_write(byte data)
|
|||
IO::out8(0x60, data);
|
||||
}
|
||||
|
||||
byte PS2MouseDevice::mouse_read()
|
||||
u8 PS2MouseDevice::mouse_read()
|
||||
{
|
||||
prepare_for_input();
|
||||
return IO::in8(0x60);
|
||||
|
@ -222,7 +222,7 @@ bool PS2MouseDevice::can_read(FileDescription&) const
|
|||
return !m_queue.is_empty();
|
||||
}
|
||||
|
||||
ssize_t PS2MouseDevice::read(FileDescription&, byte* buffer, ssize_t size)
|
||||
ssize_t PS2MouseDevice::read(FileDescription&, u8* buffer, ssize_t size)
|
||||
{
|
||||
ssize_t nread = 0;
|
||||
while (nread < size) {
|
||||
|
@ -238,7 +238,7 @@ ssize_t PS2MouseDevice::read(FileDescription&, byte* buffer, ssize_t size)
|
|||
return nread;
|
||||
}
|
||||
|
||||
ssize_t PS2MouseDevice::write(FileDescription&, const byte*, ssize_t)
|
||||
ssize_t PS2MouseDevice::write(FileDescription&, const u8*, ssize_t)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ public:
|
|||
|
||||
// ^CharacterDevice
|
||||
virtual bool can_read(FileDescription&) const override;
|
||||
virtual ssize_t read(FileDescription&, byte*, ssize_t) override;
|
||||
virtual ssize_t write(FileDescription&, const byte*, ssize_t) override;
|
||||
virtual ssize_t read(FileDescription&, u8*, ssize_t) override;
|
||||
virtual ssize_t write(FileDescription&, const u8*, ssize_t) override;
|
||||
virtual bool can_write(FileDescription&) const override { return true; }
|
||||
|
||||
private:
|
||||
|
@ -29,15 +29,15 @@ private:
|
|||
void initialize();
|
||||
void prepare_for_input();
|
||||
void prepare_for_output();
|
||||
void mouse_write(byte);
|
||||
byte mouse_read();
|
||||
void wait_then_write(byte port, byte data);
|
||||
byte wait_then_read(byte port);
|
||||
void mouse_write(u8);
|
||||
u8 mouse_read();
|
||||
void wait_then_write(u8 port, u8 data);
|
||||
u8 wait_then_read(u8 port);
|
||||
void parse_data_packet();
|
||||
void expect_ack();
|
||||
|
||||
CircularQueue<MousePacket, 100> m_queue;
|
||||
byte m_data_state { 0 };
|
||||
byte m_data[4];
|
||||
u8 m_data_state { 0 };
|
||||
u8 m_data[4];
|
||||
bool m_has_wheel { false };
|
||||
};
|
||||
|
|
|
@ -10,10 +10,10 @@ RandomDevice::~RandomDevice()
|
|||
{
|
||||
}
|
||||
|
||||
static dword next = 1;
|
||||
static u32 next = 1;
|
||||
|
||||
#define MY_RAND_MAX 4294967295U
|
||||
dword RandomDevice::random_value()
|
||||
u32 RandomDevice::random_value()
|
||||
{
|
||||
next = next * 1103515245 + 12345;
|
||||
return next;
|
||||
|
@ -31,18 +31,18 @@ bool RandomDevice::can_read(FileDescription&) const
|
|||
return true;
|
||||
}
|
||||
|
||||
ssize_t RandomDevice::read(FileDescription&, byte* buffer, ssize_t size)
|
||||
ssize_t RandomDevice::read(FileDescription&, u8* buffer, ssize_t size)
|
||||
{
|
||||
const int range = 'z' - 'a';
|
||||
ssize_t nread = min(size, PAGE_SIZE);
|
||||
for (ssize_t i = 0; i < nread; ++i) {
|
||||
dword r = random_value() % range;
|
||||
buffer[i] = (byte)('a' + r);
|
||||
u32 r = random_value() % range;
|
||||
buffer[i] = (u8)('a' + r);
|
||||
}
|
||||
return nread;
|
||||
}
|
||||
|
||||
ssize_t RandomDevice::write(FileDescription&, const byte*, ssize_t size)
|
||||
ssize_t RandomDevice::write(FileDescription&, const u8*, ssize_t size)
|
||||
{
|
||||
// FIXME: Use input for entropy? I guess that could be a neat feature?
|
||||
return min(PAGE_SIZE, size);
|
||||
|
|
|
@ -8,12 +8,12 @@ public:
|
|||
RandomDevice();
|
||||
virtual ~RandomDevice() override;
|
||||
|
||||
static dword random_value();
|
||||
static u32 random_value();
|
||||
|
||||
private:
|
||||
// ^CharacterDevice
|
||||
virtual ssize_t read(FileDescription&, byte*, ssize_t) override;
|
||||
virtual ssize_t write(FileDescription&, const byte*, ssize_t) override;
|
||||
virtual ssize_t read(FileDescription&, u8*, ssize_t) override;
|
||||
virtual ssize_t write(FileDescription&, const u8*, ssize_t) override;
|
||||
virtual bool can_read(FileDescription&) const override;
|
||||
virtual bool can_write(FileDescription&) const override { return true; }
|
||||
virtual const char* class_name() const override { return "RandomDevice"; }
|
||||
|
|
|
@ -17,7 +17,7 @@ bool SerialDevice::can_read(FileDescription&) const
|
|||
return (get_line_status() & DataReady) != 0;
|
||||
}
|
||||
|
||||
ssize_t SerialDevice::read(FileDescription&, byte* buffer, ssize_t size)
|
||||
ssize_t SerialDevice::read(FileDescription&, u8* buffer, ssize_t size)
|
||||
{
|
||||
if (!size)
|
||||
return 0;
|
||||
|
@ -35,7 +35,7 @@ bool SerialDevice::can_write(FileDescription&) const
|
|||
return (get_line_status() & EmptyTransmitterHoldingRegister) != 0;
|
||||
}
|
||||
|
||||
ssize_t SerialDevice::write(FileDescription&, const byte* buffer, ssize_t size)
|
||||
ssize_t SerialDevice::write(FileDescription&, const u8* buffer, ssize_t size)
|
||||
{
|
||||
if (!size)
|
||||
return 0;
|
||||
|
|
|
@ -13,9 +13,9 @@ public:
|
|||
|
||||
// ^CharacterDevice
|
||||
virtual bool can_read(FileDescription&) const override;
|
||||
virtual ssize_t read(FileDescription&, byte*, ssize_t) override;
|
||||
virtual ssize_t read(FileDescription&, u8*, ssize_t) override;
|
||||
virtual bool can_write(FileDescription&) const override;
|
||||
virtual ssize_t write(FileDescription&, const byte*, ssize_t) override;
|
||||
virtual ssize_t write(FileDescription&, const u8*, ssize_t) override;
|
||||
|
||||
enum InterruptEnable {
|
||||
LowPowerMode = 0x01 << 5,
|
||||
|
|
|
@ -16,14 +16,14 @@ bool ZeroDevice::can_read(FileDescription&) const
|
|||
return true;
|
||||
}
|
||||
|
||||
ssize_t ZeroDevice::read(FileDescription&, byte* buffer, ssize_t size)
|
||||
ssize_t ZeroDevice::read(FileDescription&, u8* buffer, ssize_t size)
|
||||
{
|
||||
ssize_t count = min(PAGE_SIZE, size);
|
||||
memset(buffer, 0, (size_t)count);
|
||||
return count;
|
||||
}
|
||||
|
||||
ssize_t ZeroDevice::write(FileDescription&, const byte*, ssize_t size)
|
||||
ssize_t ZeroDevice::write(FileDescription&, const u8*, ssize_t size)
|
||||
{
|
||||
return min(PAGE_SIZE, size);
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ public:
|
|||
|
||||
private:
|
||||
// ^CharacterDevice
|
||||
virtual ssize_t read(FileDescription&, byte*, ssize_t) override;
|
||||
virtual ssize_t write(FileDescription&, const byte*, ssize_t) override;
|
||||
virtual ssize_t read(FileDescription&, u8*, ssize_t) override;
|
||||
virtual ssize_t write(FileDescription&, const u8*, ssize_t) override;
|
||||
virtual bool can_read(FileDescription&) const override;
|
||||
virtual bool can_write(FileDescription&) const override { return true; }
|
||||
virtual const char* class_name() const override { return "ZeroDevice"; }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue