diff --git a/kernel/com.cpp b/kernel/com.cpp index ba7424b1..9d5745d6 100644 --- a/kernel/com.cpp +++ b/kernel/com.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2012, 2014, 2015, 2016 Jonas 'Sortie' Termansen. + * Copyright (c) 2011, 2012, 2014, 2015, 2016, 2023 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -17,6 +17,8 @@ * Handles communication to COM serial ports. */ +#include + #include #include #include @@ -38,6 +40,7 @@ #include #include "com.h" +#include "tty.h" extern "C" unsigned char nullpage[4096]; @@ -136,17 +139,30 @@ static inline bool CanWriteByte(uint16_t port) return inport8(port + LSR) & LSR_THRE; } -class DevCOMPort : public AbstractInode +class DevCOMPort : public TTY { public: - DevCOMPort(dev_t dev, uid_t owner, gid_t group, mode_t mode, uint16_t port); + DevCOMPort(dev_t dev, uid_t owner, gid_t group, mode_t mode, uint16_t port, + const char* name); virtual ~DevCOMPort(); + virtual int ioctl(ioctx_t* ctx, int cmd, uintptr_t arg); virtual int sync(ioctx_t* ctx); - virtual ssize_t read(ioctx_t* ctx, uint8_t* buf, size_t count); - virtual ssize_t write(ioctx_t* ctx, const uint8_t* buf, size_t count); + virtual void tty_output(const unsigned char* buffer, size_t length); + +public: + bool Initialize(int interrupt); + +private: + static void InterruptHandler(struct interrupt_context*, void*); + static void InterruptWorkHandler(void* context); + void OnInterrupt(); + void InterruptWork(); private: kthread_mutex_t port_lock; + struct interrupt_handler irq_registration; + struct interrupt_work interrupt_work; + struct winsize ws; uint16_t port; uint8_t pending_input_byte; bool has_pending_input_byte; @@ -154,24 +170,92 @@ private: }; DevCOMPort::DevCOMPort(dev_t dev, uid_t owner, gid_t group, mode_t mode, - uint16_t port) + uint16_t port, const char* name) : TTY(dev, ino, mode, + owner, group, name) { - inode_type = INODE_TYPE_STREAM; this->port = port; this->port_lock = KTHREAD_MUTEX_INITIALIZER; - this->stat_uid = owner; - this->stat_gid = group; - this->type = S_IFCHR; - this->stat_mode = (mode & S_SETABLE) | this->type; - this->dev = dev; - this->ino = (ino_t) this; this->has_pending_input_byte = false; + interrupt_work.handler = InterruptWorkHandler; + interrupt_work.context = this; } DevCOMPort::~DevCOMPort() { } +bool DevCOMPort::Initialize(int interrupt) +{ + uint8_t interrupts = 1; + // TODO: This was 9600. + tio.c_ispeed = B19200; + tio.c_ospeed = B19200; + uint16_t divisor = 115200 / tio.c_ispeed; + outport8(port + FCR, 0); + outport8(port + LCR, LCR_DLAB); + outport8(port + DLL, divisor & 0xFF); + outport8(port + DLM, divisor >> 8); + outport8(port + LCR, LCR_WLEN8); // 8n1 + outport8(port + MCR, 0x1 /* DTR */ | 0x2 /* RTS */); + outport8(port + IER, interrupts); + irq_registration.handler = DevCOMPort::InterruptHandler; + irq_registration.context = this; + Interrupt::RegisterHandler(interrupt, &irq_registration); + return true; +} + +void DevCOMPort::InterruptHandler(struct interrupt_context*, void* user) +{ + ((DevCOMPort*) user)->OnInterrupt(); +} + +void DevCOMPort::OnInterrupt() +{ + if ( !IsLineReady(port) ) + return; + Interrupt::ScheduleWork(&interrupt_work); +} + +void DevCOMPort::InterruptWorkHandler(void* context) +{ + ((DevCOMPort*) context)->InterruptWork(); +} + +void DevCOMPort::InterruptWork() +{ + ScopedLock lock1(&termlock); + ScopedLock lock2(&port_lock); + while ( IsLineReady(port) ) + { + unsigned char byte = inport8(port + RXR); + if ( tio.c_cflag & CREAD ) + ProcessByte(byte); + } +} + +int DevCOMPort::ioctl(ioctx_t* ctx, int cmd, uintptr_t arg) +{ + ScopedLock lock(&termlock); + if ( hungup ) + return errno = EIO, -1; + if ( cmd == TIOCGWINSZ ) + { + struct winsize* user_ws = (struct winsize*) arg; + if ( !ctx->copy_to_dest(user_ws, &ws, sizeof(ws)) ) + return -1; + return 0; + } + else if ( cmd == TIOCSWINSZ ) + { + const struct winsize* user_ws = (const struct winsize*) arg; + if ( !ctx->copy_from_src(&ws, user_ws, sizeof(ws)) ) + return -1; + return 0; + } + lock.Reset(); + return TTY::ioctl(ctx, cmd, arg); +} + int DevCOMPort::sync(ioctx_t* /*ctx*/) { ScopedLock lock(&port_lock); @@ -179,57 +263,9 @@ int DevCOMPort::sync(ioctx_t* /*ctx*/) return 0; } -ssize_t DevCOMPort::read(ioctx_t* ctx, uint8_t* dest, size_t count) +void DevCOMPort::tty_output(const unsigned char* buffer, size_t length) { - ScopedLock lock(&port_lock); - - for ( size_t i = 0; i < count; i++ ) - { - unsigned long attempt = 0; - while ( !has_pending_input_byte && !IsLineReady(port) ) - { - attempt++; - if ( attempt <= 10 ) - continue; - if ( attempt <= 15 && !(ctx->dflags & O_NONBLOCK) ) - { - kthread_mutex_unlock(&port_lock); - kthread_yield(); - kthread_mutex_lock(&port_lock); - continue; - } - if ( i ) - return (ssize_t) i; - if ( ctx->dflags & O_NONBLOCK ) - return errno = EWOULDBLOCK, -1; - if ( Signal::IsPending() ) - return errno = EINTR, -1; - kthread_mutex_unlock(&port_lock); - kthread_yield(); - kthread_mutex_lock(&port_lock); - } - - uint8_t value = has_pending_input_byte ? - pending_input_byte : - inport8(port + RXR); - if ( !ctx->copy_to_dest(dest + i, &value, sizeof(value)) ) - { - has_pending_input_byte = true; - pending_input_byte = value; - return i ? (ssize_t) i : -1; - } - - has_pending_input_byte = false; - } - - return (ssize_t) count; -} - -ssize_t DevCOMPort::write(ioctx_t* ctx, const uint8_t* src, size_t count) -{ - ScopedLock lock(&port_lock); - - for ( size_t i = 0; i < count; i++ ) + for ( size_t i = 0; i < length; i++ ) { unsigned long attempt = 0; while ( !CanWriteByte(port) ) @@ -237,7 +273,7 @@ ssize_t DevCOMPort::write(ioctx_t* ctx, const uint8_t* src, size_t count) attempt++; if ( attempt <= 10 ) continue; - if ( attempt <= 15 && !(ctx->dflags & O_NONBLOCK) ) + if ( attempt <= 15 ) { kthread_mutex_unlock(&port_lock); kthread_yield(); @@ -245,20 +281,16 @@ ssize_t DevCOMPort::write(ioctx_t* ctx, const uint8_t* src, size_t count) continue; } if ( i ) - return (ssize_t) i; - if ( ctx->dflags & O_NONBLOCK ) - return errno = EWOULDBLOCK, -1; + return; + // TODO: This is problematic. if ( Signal::IsPending() ) - return errno = EINTR, -1; + { + errno = EINTR; + return; + } } - - uint8_t value; - if ( !ctx->copy_from_src(&value, src + i, sizeof(value)) ) - return i ? (ssize_t) i : -1; - outport8(port + TXR, value); + outport8(port + TXR, buffer[i]); } - - return (ssize_t) count; } static Ref com_devices[1 + NUM_COM_PORTS]; @@ -282,21 +314,6 @@ void Init(const char* devpath, Ref slashdev) ioctx_t ctx; SetupKernelIOCtx(&ctx); - for ( size_t i = 1; i <= NUM_COM_PORTS; i++ ) - { - uint16_t port = com_ports[i]; - if ( !port ) - continue; - uint8_t interrupts = 0; - outport8(port + FCR, 0); - outport8(port + LCR, 0x80); - outport8(port + DLL, 0xC); - outport8(port + DLM, 0x0); - outport8(port + LCR, 0x3); // 8n1 - outport8(port + MCR, 0x3); // DTR + RTS - outport8(port + IER, interrupts); - } - for ( size_t i = 1; i <= NUM_COM_PORTS; i++ ) { if ( !com_ports[i] ) @@ -304,13 +321,17 @@ void Init(const char* devpath, Ref slashdev) com_devices[i] = Ref(); continue; } - com_devices[i] = Ref(new DevCOMPort(slashdev->dev, 0, 0, 0660, com_ports[i])); - if ( !com_devices[i] ) + char ttyname[TTY_NAME_MAX+1]; + snprintf(ttyname, sizeof(ttyname), "com%zu", i); + Ref com( + new DevCOMPort(slashdev->dev, 0, 0, 0660, com_ports[i], ttyname)); + if ( !com ) PanicF("Unable to allocate device for COM port %zu", i); - char name[3 + sizeof(size_t) * 3]; - snprintf(name, sizeof(name), "com%zu", i); - if ( LinkInodeInDir(&ctx, slashdev, name, com_devices[i]) != 0 ) - PanicF("Unable to link %s/%s to COM port driver.", devpath, name); + com_devices[i] = com; + int interrupt = i == 1 || i == 3 ? Interrupt::IRQ4 : Interrupt::IRQ3; + com->Initialize(interrupt); + if ( LinkInodeInDir(&ctx, slashdev, ttyname, com) != 0 ) + PanicF("Unable to link %s/%s to COM port driver.", devpath, ttyname); } }