Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions sources/kernel/include/drivers/bridges/uart_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,16 @@ enum class NUART_Baud_Rate
BR_115200 = 115200,
};

enum class NUART_Blocking_Read
{
BLOCKING = 0,
NONBLOCKING = 1,
};

// parametry UARTu pro prenos skrz IOCTL rozhrani
struct TUART_IOCtl_Params
{
NUART_Char_Length char_length;
NUART_Baud_Rate baud_rate;
NUART_Blocking_Read blocking_read;
};
28 changes: 26 additions & 2 deletions sources/kernel/include/drivers/uart.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
#pragma once

#include <hal/peripherals.h>
#include <drivers/bcm_aux.h>
#include <drivers/bridges/uart_defs.h>

#include "fs/filesystem.h"
#include "process/spinlock.h"

constexpr int CUART_BUF_SIZE = 8192;

class CUART
{
private:
Expand All @@ -12,10 +16,20 @@ class CUART

// byl UART kanal otevreny?
bool mOpened;
spinlock_t mOpenLock;

// nastavena baud rate, ukladame ji proto, ze do registru se uklada (potencialne ztratovy) prepocet
NUART_Baud_Rate mBaud_Rate;

NUART_Blocking_Read mBlocking_Read;

// cyklicky buffer pro cteni
char mRx_Buf[CUART_BUF_SIZE];
uint32_t mRx_head = 0;
uint32_t mRx_tail = 0;
spinlock_t mRx_Lock;
IFile* mWaiting_File = nullptr;

public:
CUART(CAUX& aux);

Expand All @@ -32,6 +46,12 @@ class CUART
NUART_Baud_Rate Get_Baud_Rate();
void Set_Baud_Rate(NUART_Baud_Rate rate);

NUART_Blocking_Read Get_Blocking_Read();
void Set_Blocking_Read(NUART_Blocking_Read r);

// IRQ handler vola tuto rutinu po signalizaci IRQ
void IRQ_Callback();

// miniUART na RPi0 nepodporuje nic moc jineho uzitecneho, napr. paritni bity, vice stop-bitu nez 1, atd.

void Write(char c);
Expand All @@ -40,7 +60,11 @@ class CUART
void Write(unsigned int num);
void Write_Hex(unsigned int num);

// TODO: read (budeme to pak nejspis propojovat s prerusenim)
// precist nebo se zablokovat (pokud je nastaveno jako blokujici)
int ReadOrWait(char* str, unsigned int len, IFile* file);

// pocka na udalost (zablokuje proces)
void Wait_For_Event(IFile* file);
};

extern CUART sUART0;
25 changes: 24 additions & 1 deletion sources/kernel/include/fs/drivers/uart_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,32 @@ class CUART_File final : public IFile

virtual uint32_t Read(char* buffer, uint32_t num) override
{
// NYI, prijde nejspis az s kernel buffery a prerusenimi z UARTu
if (num > 0 && buffer != nullptr)
{
if (mChannel == 0)
{
return sUART0.ReadOrWait(buffer, num, this);
}
}

return 0;
}

virtual bool Wait(uint32_t count) override
{
if (mChannel == 0)
{
Wait_Enqueue_Current();
sUART0.Wait_For_Event(this);

// zablokujeme, probudi nas az notify
sProcessMgr.Block_Current_Process();
return true;
}

return false;
}

virtual uint32_t Write(const char* buffer, uint32_t num) override
{
if (num > 0 && buffer != nullptr)
Expand Down Expand Up @@ -68,6 +89,7 @@ class CUART_File final : public IFile
{
params->baud_rate = sUART0.Get_Baud_Rate();
params->char_length = sUART0.Get_Char_Length();
params->blocking_read = sUART0.Get_Blocking_Read();
return true;
}
}
Expand All @@ -79,6 +101,7 @@ class CUART_File final : public IFile
{
sUART0.Set_Baud_Rate(params->baud_rate);
sUART0.Set_Char_Length(params->char_length);
sUART0.Set_Blocking_Read(params->blocking_read);
return true;
}
}
Expand Down
124 changes: 119 additions & 5 deletions sources/kernel/src/drivers/uart.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,63 @@

#include <stdstring.h>

#include "interrupt_controller.h"

CUART sUART0(sAUX);

CUART::CUART(CAUX& aux)
: mAUX(aux), mOpened(false)
{

spinlock_init(&mOpenLock);
spinlock_init(&mRx_Lock);
}

bool CUART::Open()
{
// TODO: zamek, aby se neco neseslo
// zamek, kdyby se nahodou dva procesy pokouseli otevrit UART
spinlock_lock(&mOpenLock);

if (mOpened)
{
spinlock_unlock(&mOpenLock);
return false;
}

// rezervujeme si TX a RX piny, exkluzivne pro nas (R i W, ackoliv je jeden jen vstupni a jeden jen vystupni)
if (!sGPIO.Reserve_Pin(14, true, true))
{
spinlock_unlock(&mOpenLock);
return false;
}

if (!sGPIO.Reserve_Pin(15, true, true))
{
sGPIO.Free_Pin(14, true, true);
spinlock_unlock(&mOpenLock);
return false;
}

mAUX.Enable(hal::AUX_Peripherals::MiniUART);
mAUX.Set_Register(hal::AUX_Reg::MU_IIR, 0);
mAUX.Set_Register(hal::AUX_Reg::MU_IER, 0);
mAUX.Set_Register(hal::AUX_Reg::MU_IER, 1);
mAUX.Set_Register(hal::AUX_Reg::MU_MCR, 0);
mAUX.Set_Register(hal::AUX_Reg::MU_CNTL, 3); // RX and TX enabled

// nastavime GPIO 14 a 15 na jejich alt funkci 5, coz je UART kanal 1
sGPIO.Set_GPIO_Function(14, NGPIO_Function::Alt_5);
sGPIO.Set_GPIO_Function(15, NGPIO_Function::Alt_5);

// povolime preruseni UARTu
sInterruptCtl.Enable_IRQ(hal::IRQ_Source::UART);

mOpened = true;

// nastavime vychozi rychlost a velikost znaku
Set_Char_Length(NUART_Char_Length::Char_8);
Set_Baud_Rate(NUART_Baud_Rate::BR_9600);
Set_Blocking_Read(NUART_Blocking_Read::BLOCKING);

spinlock_unlock(&mOpenLock);

return true;
}
Expand All @@ -53,8 +70,10 @@ void CUART::Close()
if (!mOpened)
return;

// zakazeme AUX periferii
// zakazeme AUX periferii a preruseni
sInterruptCtl.Disable_IRQ(hal::IRQ_Source::UART);
mAUX.Disable(hal::AUX_Peripherals::MiniUART);
mAUX.Set_Register(hal::AUX_Reg::MU_IER, 0);

// piny 14 a 15 prepneme na Input (tak zerou nejmin proudu)
sGPIO.Set_GPIO_Function(14, NGPIO_Function::Input);
Expand Down Expand Up @@ -88,6 +107,16 @@ void CUART::Set_Char_Length(NUART_Char_Length len)
mAUX.Set_Register(hal::AUX_Reg::MU_LCR, (mAUX.Get_Register(hal::AUX_Reg::MU_LCR) & 0xFFFFFFFE) | static_cast<unsigned int>(len));
}

NUART_Blocking_Read CUART::Get_Blocking_Read()
{
return mBlocking_Read;
}

void CUART::Set_Blocking_Read(NUART_Blocking_Read r)
{
mBlocking_Read = r;
}

NUART_Baud_Rate CUART::Get_Baud_Rate()
{
if (!mOpened)
Expand Down Expand Up @@ -166,4 +195,89 @@ void CUART::Write_Hex(unsigned int num)

itoa(num, buf, 16);
Write(buf);
}
}

void CUART::IRQ_Callback()
{
// 0 == pending interrupt
if (mAUX.Get_Register(hal::AUX_Reg::MU_IIR) & 0b1)
return;

spinlock_lock(&mRx_Lock);

while (mAUX.Get_Register(hal::AUX_Reg::MU_LSR) & 0x01) // RX data ready
{
// Precteni znaku
const auto c = static_cast<char>(mAUX.Get_Register(hal::AUX_Reg::MU_IO) & 0xFF);

uint32_t next = (mRx_head + 1) % CUART_BUF_SIZE;

if (next == mRx_tail)
{
// Buffer je plny -> zahodi se nejstarsi byte
mRx_tail = (mRx_tail + 1) % CUART_BUF_SIZE;
}

mRx_Buf[mRx_head] = c;
mRx_head = next;
}

// Pokud je cekajici soubor a buffer neni prazdny (head a tail jsou ruzne hodnoty), probudime proces
if (mWaiting_File && mRx_head != mRx_tail)
{
mWaiting_File->Notify(1);
mWaiting_File = nullptr;
}

spinlock_unlock(&mRx_Lock);
}

int CUART::ReadOrWait(char *str, unsigned int len, IFile* file)
{
spinlock_lock(&mRx_Lock);

while (mBlocking_Read == NUART_Blocking_Read::BLOCKING && mRx_head == mRx_tail)
{
if (!file)
{
spinlock_unlock(&mRx_Lock);
return 0;
}

spinlock_unlock(&mRx_Lock);
// Wait nad souborem pred zablokovanim zvola Wait_For_Event
file->Wait(1);
spinlock_lock(&mRx_Lock);
}

// zjisteni delky zpravy cekajici v bufferu
int msg_len = (mRx_head + CUART_BUF_SIZE - mRx_tail) % CUART_BUF_SIZE;
if (msg_len > len)
{
// omezeni cteni na velikost userspace bufferu
msg_len = len;
}

// zkopirovani znaku do userspace bufferu
for (uint32_t i = 0; i < msg_len; i++)
{
str[i] = mRx_Buf[(mRx_tail + i) % CUART_BUF_SIZE];
}

// posunuti ukazatele pro cteni z bufferu
mRx_tail = (mRx_tail + msg_len) % CUART_BUF_SIZE;

spinlock_unlock(&mRx_Lock);

return msg_len;
}

// implementace Wait() nad souborem
void CUART::Wait_For_Event(IFile* file)
{
spinlock_lock(&mRx_Lock);

mWaiting_File = file;

spinlock_unlock(&mRx_Lock);
}
5 changes: 5 additions & 0 deletions sources/kernel/src/interrupt_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <process/process_manager.h>
#include <process/swi.h>

#include "drivers/uart.h"

extern "C"
{
void enable_irq();
Expand Down Expand Up @@ -50,6 +52,9 @@ extern "C" void _internal_irq_handler()
// casovac
if (sTimer.Is_Timer_IRQ_Pending())
sTimer.IRQ_Callback();

// UART terminal (sam si overi, zda k nejakemu doslo)
sUART0.IRQ_Callback();
}

extern "C" void _internal_fiq_handler()
Expand Down