#include <os/stream.h> #include <stdio.h> #include <os/serial.h> #include <string.h> #include <kernel/driver.h> #include <kernel/sys.h> /* ;; *********************************************************** ;; Register Indexes for the 8250 UART ;; NOTE: Indexes have been multiplied by 2 to get the ;; correct location in the register table. ;; *********************************************************** */ #define THR 0 // Transmit Holding Register (DLAB==0) #define RBR 0 // Recieve Buffer Register (DLAB==0) #define BAUDL 0 // Baud Rate Divisor, Low byte (DLAB==1) #define BAUDH 1 // Baud Rate Divisor, High Byte (DLAB==1) #define IER 1 // Interrupt Enable Register #define IIR 2 // Interrupt Identification Register #define FCR 2 // FIFO Control Register (UARTS 16550+) #define LCR 3 // Line Control Register #define MCR 4 // Modem Control Register #define LSR 5 // Line Status Register #define MSR 6 // Modem Status Register /* ;; *********************************************************** ;; Send and Receive Buffers ;; NOTE: The buffer sizes (SendSize & RecvSize) must be of a ;; size X such that log2(X+1)=Integer. In otherwords, ;; take a power of 2, subtract one, and that is a legal ;; buffer size. (1, 3, 7, 15, 31, 63, 127 ... 2^X-1) ;; *********************************************************** */ #define SendSize 4095 // 2^X-1 Send Buffer Size (4095) #define RecvSize 4095 // 2^X-1 Receive Buffer Size (4095) class CSerialPort : public IUnknown, public IDevice, public IStream { public: CSerialPort(word base, byte irq); virtual ~CSerialPort(); /* IUnknown methods */ STDMETHOD(QueryInterface)(REFIID iid, void** obj); IMPLEMENT_IUNKNOWN(CSerialPort); /* IDevice methods */ STDMETHOD(GetInfo)(device_t* buf); STDMETHOD(DeviceOpen)(); /* IStream methods */ STDMETHOD_(size_t, Read)(void* buffer, size_t length); STDMETHOD_(size_t, Write)(const void* buffer, size_t length); STDMETHOD(SetIoMode)(dword mode); STDMETHOD(IsReady)(); STDMETHOD(Stat)(folderitem_t* buf); STDMETHOD(Seek)(long offset, int origin); protected: word m_port; // Port Base Address word m_registers[7]; // Register Index Map byte m_irq; // Vector Number bool m_in_isr; byte m_send_buffer[SendSize]; // Send FIFO Buffer word m_send_head; // Send FIFO Buffer Head Index word m_send_tail; // Send FIFO Buffer Tail Index byte m_receive_buffer[RecvSize]; // Receive FIFO Buffer word m_receive_head; // Receive FIFO Buffer Head Index word m_receive_tail; // Receive FIFO Buffer Tail Index static void Isr(void* context, int irq); void Interrupt(); void ClosePort(); void ClearRead(); void ClearWrite(); char HwRead(); int HwWrite(char data); int ReadStatus(); int WriteStatus(); void SetBaud(int baud); int GetBaud(); void SetHandShake(int hand); int GetHandShake(); int GetStatus(); int GetControl(); void SetControl(int control); }; extern "C" IDevice* Serial_Create(word port, byte irq) { return new CSerialPort(port, irq); } void CSerialPort::Isr(void* context, int irq) { _cputws(L"Serial IRQ\n"); ((CSerialPort*) context)->Interrupt(); } void CSerialPort::Interrupt() { byte intr, state; m_in_isr = true; out(0x21, in(0x21) | ~0xef); do { intr = (in(m_registers[IIR]) & 6) >> 1; wprintf(L"serial_isr: %x\n", intr); switch (intr) { case 0: // nothing case 3: state = in(m_registers[IER]); //wprintf(L"state = %x ", state); out(m_registers[IER], 0); //_cputws(L"out(0) "); out(m_registers[IER], state); //_cputws(L"out(state)\n"); goto finished; case 1: // transmit if (m_send_head == m_send_tail) { out(m_registers[IER], in(m_registers[IER]) & 0xFD); goto finished; } else { wprintf(L"transmit: %c\n", m_send_buffer[m_send_tail]); out(m_registers[THR], m_send_buffer[m_send_tail]); m_send_tail = (m_send_tail + 1) & SendSize; } break; case 2: // receive m_receive_buffer[m_receive_head] = in(m_registers[RBR]); if (((m_receive_head + 1) & RecvSize) != m_receive_tail) m_receive_head = (m_receive_head + 1) & RecvSize; break; } } while ((in(m_registers[IIR]) & 1) == 0); finished: _cputws(L"finish: "); out(m_registers[MCR], in(m_registers[MCR]) | 8); out(m_registers[IER], 1); out(0x21, in(0x21) & 0xef); _cputws(L"done\n"); m_in_isr = false; } CSerialPort::CSerialPort(word base, byte irq) { int i; m_refs = 0; wprintf(L"CSerialPort::CSerialPort(%x, %d)\n", base, irq); /* ;; *********************************************************** ;; Set up COMM variables ;; *********************************************************** */ m_port = base; m_irq = irq; /* ;; *********************************************************** ;; Set up the Register Port Indexes ;; *********************************************************** */ for (i = 6; i > 0; i--) m_registers[i] = base + i; /* ;; *********************************************************** ;; Initialize the Read/Write Buffers ;; *********************************************************** */ ClearWrite(); ClearRead(); /* ;; *********************************************************** ;; Install the ISR ;; *********************************************************** */ sysRegisterIrq(m_irq, (void (*) (dword, int)) Isr, (dword) this); out(m_registers[MCR], in(m_registers[MCR]) | 8); out(m_registers[LCR], in(m_registers[LCR]) & 0x7F); out(m_registers[IER], 1); /* ;; *********************************************************** ;; Clear the buffers again, just in case . . . ;; *********************************************************** */ ClearWrite(); ClearRead(); } CSerialPort::~CSerialPort() { ClosePort(); } void CSerialPort::ClosePort() { /* ;; *********************************************************** ;; Disable the 8250 Interrupt (DLAB clear perhaps before this) ;; *********************************************************** */ out(m_registers[IER], 0); out(m_registers[MCR], in(m_registers[MCR]) & 0xF7); /* ;; *********************************************************** ;; ISR is disabled, so reset old ISR Vector ;; *********************************************************** */ sysRegisterIrq(m_irq, NULL, 0); } void CSerialPort::ClearRead() { disable(); m_receive_head = m_receive_tail = 0; enable(); } void CSerialPort::ClearWrite() { disable(); m_send_head = m_send_tail = 0; disable(); } char CSerialPort::HwRead() { byte data; if (m_receive_head == m_receive_tail) return 0; data = m_receive_buffer[m_receive_tail]; m_receive_tail = (m_receive_tail + 1) & RecvSize; return data; } int CSerialPort::HwWrite(char data) { /* ;; *********************************************************** ;; Check if buffer is FULL ;; *********************************************************** */ if (((m_send_head + 1) & SendSize) == m_send_tail || m_in_isr) return 0; m_send_buffer[m_send_head] = data; m_send_head = (m_send_head + 1) & SendSize; /* ;; *********************************************************** ;; Enable the THRE Interrupt ;; *********************************************************** */ out(m_registers[IER], in(m_registers[IER]) | 2); return -1; } int CSerialPort::ReadStatus() { if (m_receive_head >= m_receive_tail) return m_receive_head - m_receive_tail; else return m_receive_head - m_receive_tail + RecvSize; } int CSerialPort::WriteStatus() { if (m_send_head >= m_send_tail) return m_send_head - m_send_tail; else return m_send_head - m_send_tail + SendSize; } void CSerialPort::SetBaud(int baud) { byte divisor; if (baud == 0) return; divisor = 115200 / baud; disable(); out(m_registers[LCR], in(m_registers[LCR]) | 0x80); out(m_registers[BAUDL], divisor % 256); out(m_registers[BAUDH], divisor / 256); out(m_registers[LCR], in(m_registers[LCR]) & 0x7F); enable(); } int CSerialPort::GetBaud() { word divisor; disable(); out(m_registers[LCR], in(m_registers[LCR]) | 0x80); divisor = in(m_registers[BAUDL]) | in(m_registers[BAUDH]) << 8; out(m_registers[LCR], in(m_registers[LCR]) & 0x7F); enable(); if (divisor == 0) return 0; return 115200 / divisor; } void CSerialPort::SetHandShake(int hand) { out(m_registers[MCR], (byte) hand | 8); } int CSerialPort::GetHandShake() { return in(m_registers[MCR]); } int CSerialPort::GetStatus() { return in(m_registers[MSR]) << 8 | in(m_registers[LSR]); } int CSerialPort::GetControl() { return in(m_registers[LCR]) & 0xf; } void CSerialPort::SetControl(int control) { out(m_registers[LCR], (byte) control & 0x7F); } HRESULT CSerialPort::QueryInterface(REFIID iid, void** obj) { if (InlineIsEqualGUID(iid, IID_IUnknown) || InlineIsEqualGUID(iid, IID_IStream)) { AddRef(); *obj = this; return S_OK; } else return E_FAIL; } HRESULT CSerialPort::GetInfo(device_t* buf) { if (buf->size < sizeof(device_t)) return E_FAIL; wcsncpy(buf->name, L"Serial port", buf->name_max); return S_OK; } HRESULT CSerialPort::DeviceOpen() { _cputws(L"CSerialPort::DeviceOpen\n"); ClearRead(); ClearWrite(); SetBaud(9600); SetControl(BITS_8 | NO_PARITY | STOP_1); SetHandShake(0); return S_OK; } size_t CSerialPort::Read(void* buffer, size_t length) { size_t i; for (i = 0; i < length; i++) { while (ReadStatus() == 0) ; ((byte*) buffer)[i] = HwRead(); } return i; } size_t CSerialPort::Write(const void* buffer, size_t length) { size_t i; for (i = 0; i < length; i++) if (HwWrite(((const byte*) buffer)[i]) == 0) break; return i; } HRESULT CSerialPort::SetIoMode(dword mode) { return S_OK; } HRESULT CSerialPort::IsReady() { return S_OK; } HRESULT CSerialPort::Stat(folderitem_t* buf) { return E_FAIL; } HRESULT CSerialPort::Seek(long offset, int origin) { return E_FAIL; }