about summary refs log tree commit diff stats
path: root/tools/iso/kernel.soso/ttydriver.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/iso/kernel.soso/ttydriver.c')
-rw-r--r--tools/iso/kernel.soso/ttydriver.c598
1 files changed, 598 insertions, 0 deletions
diff --git a/tools/iso/kernel.soso/ttydriver.c b/tools/iso/kernel.soso/ttydriver.c
new file mode 100644
index 00000000..755cda15
--- /dev/null
+++ b/tools/iso/kernel.soso/ttydriver.c
@@ -0,0 +1,598 @@
+#include "ttydriver.h"
+#include "device.h"
+#include "screen.h"
+#include "serial.h"
+#include "devfs.h"
+#include "alloc.h"
+#include "common.h"
+#include "list.h"
+#include "fifobuffer.h"
+#include "gfx.h"
+#include "debugprint.h"
+#include "commonuser.h"
+#include "termios.h"
+
+static List* gTtyList = NULL;
+
+static List* gReaderList = NULL;
+
+static Tty* gActiveTty = NULL;
+
+static uint8 gKeyModifier = 0;
+
+static uint32 gPseudoTerminalNameGenerator = 0;
+
+typedef enum KeyModifier {
+    KM_LeftShift = 1,
+    KM_RightShift = 2,
+    KM_Ctrl = 4,
+    KM_Alt = 8
+} KeyModifier;
+
+enum {
+    KEY_LEFTSHIFT = 0x2A,
+    KEY_RIGHTSHIFT = 0x36,
+    KEY_CTRL = 0x1D,
+    KEY_ALT = 0x38,
+    KEY_CAPSLOCK = 0x3A,
+    KEY_F1 = 0x3B,
+    KEY_F2 = 0x3C,
+    KEY_F3 = 0x3D
+};
+
+// PC keyboard interface constants
+
+#define KBSTATP         0x64    // kbd controller status port(I)
+#define KBS_DIB         0x01    // kbd data in buffer
+#define KBDATAP         0x60    // kbd data port(I)
+
+#define NO              0
+
+#define SHIFT           (1<<0)
+#define CTL             (1<<1)
+#define ALT             (1<<2)
+
+#define CAPSLOCK        (1<<3)
+#define NUMLOCK         (1<<4)
+#define SCROLLLOCK      (1<<5)
+
+#define E0ESC           (1<<6)
+
+// Special keycodes
+#define KEY_HOME        0xE0
+#define KEY_END         0xE1
+#define KEY_UP          0xE2
+#define KEY_DOWN        0xE3
+#define KEY_LEFT        0xE4
+#define KEY_RIGHT       0xE5
+#define KEY_PAGEUP      0xE6
+#define KEY_PAGEDOWN    0xE7
+#define KEY_INSERT      0xE8
+#define KEY_DELETE      0xE9
+
+// C('A') == Control-A
+#define C(x) (x - '@')
+
+
+
+
+static uint8 gKeyMap[256] = {
+  NO,   0x1B, '1',  '2',  '3',  '4',  '5',  '6',  // 0x00
+  '7',  '8',  '9',  '0',  '-',  '=',  '\b', '\t',
+  'q',  'w',  'e',  'r',  't',  'y',  'u',  'i',  // 0x10
+  'o',  'p',  '[',  ']',  '\n', NO,   'a',  's',
+  'd',  'f',  'g',  'h',  'j',  'k',  'l',  ';',  // 0x20
+  '\'', '`',  NO,   '\\', 'z',  'x',  'c',  'v',
+  'b',  'n',  'm',  ',',  '.',  '/',  NO,   '*',  // 0x30
+  NO,   ' ',  NO,   NO,   NO,   NO,   NO,   NO,
+  NO,   NO,   NO,   NO,   NO,   NO,   NO,   '7',  // 0x40
+  '8',  '9',  '-',  '4',  '5',  '6',  '+',  '1',
+  '2',  '3',  '0',  '.',  NO,   NO,   NO,   NO,   // 0x50
+  [0x49] = KEY_PAGEUP,
+  [0x51] = KEY_PAGEDOWN,
+  [0x47] = KEY_HOME,
+  [0x4F] = KEY_END,
+  [0x52] = KEY_INSERT,
+  [0x53] = KEY_DELETE,
+  [0x48] = KEY_UP,
+  [0x50] = KEY_DOWN,
+  [0x4B] = KEY_LEFT,
+  [0x4D] = KEY_RIGHT,
+  [0x9C] = '\n',      // KP_Enter
+  [0xB5] = '/',       // KP_Div
+  [0xC8] = KEY_UP,
+  [0xD0] = KEY_DOWN,
+  [0xC9] = KEY_PAGEUP,
+  [0xD1] = KEY_PAGEDOWN,
+  [0xCB] = KEY_LEFT,
+  [0xCD] = KEY_RIGHT,
+  [0x97] = KEY_HOME,
+  [0xCF] = KEY_END,
+  [0xD2] = KEY_INSERT,
+  [0xD3] = KEY_DELETE
+};
+
+static uint8 gKeyShiftMap[256] = {
+  NO,   033,  '!',  '@',  '#',  '$',  '%',  '^',  // 0x00
+  '&',  '*',  '(',  ')',  '_',  '+',  '\b', '\t',
+  'Q',  'W',  'E',  'R',  'T',  'Y',  'U',  'I',  // 0x10
+  'O',  'P',  '{',  '}',  '\n', NO,   'A',  'S',
+  'D',  'F',  'G',  'H',  'J',  'K',  'L',  ':',  // 0x20
+  '"',  '~',  NO,   '|',  'Z',  'X',  'C',  'V',
+  'B',  'N',  'M',  '<',  '>',  '?',  NO,   '*',  // 0x30
+  NO,   ' ',  NO,   NO,   NO,   NO,   NO,   NO,
+  NO,   NO,   NO,   NO,   NO,   NO,   NO,   '7',  // 0x40
+  '8',  '9',  '-',  '4',  '5',  '6',  '+',  '1',
+  '2',  '3',  '0',  '.',  NO,   NO,   NO,   NO,   // 0x50
+  [0x49] = KEY_PAGEUP,
+  [0x51] = KEY_PAGEDOWN,
+  [0x47] = KEY_HOME,
+  [0x4F] = KEY_END,
+  [0x52] = KEY_INSERT,
+  [0x53] = KEY_DELETE,
+  [0x48] = KEY_UP,
+  [0x50] = KEY_DOWN,
+  [0x4B] = KEY_LEFT,
+  [0x4D] = KEY_RIGHT,
+  [0x9C] = '\n',      // KP_Enter
+  [0xB5] = '/',       // KP_Div
+  [0xC8] = KEY_UP,
+  [0xD0] = KEY_DOWN,
+  [0xC9] = KEY_PAGEUP,
+  [0xD1] = KEY_PAGEDOWN,
+  [0xCB] = KEY_LEFT,
+  [0xCD] = KEY_RIGHT,
+  [0x97] = KEY_HOME,
+  [0xCF] = KEY_END,
+  [0xD2] = KEY_INSERT,
+  [0xD3] = KEY_DELETE
+};
+
+static BOOL tty_open(File *file, uint32 flags);
+static void tty_close(File *file);
+static int32 tty_ioctl(File *file, int32 request, void * argp);
+static int32 tty_read(File *file, uint32 size, uint8 *buffer);
+static int32 tty_write(File *file, uint32 size, uint8 *buffer);
+static int32 write(Tty* tty, uint32 size, uint8 *buffer);
+
+static uint8 getCharacterForScancode(KeyModifier modifier, uint8 scancode);
+static void processScancode(uint8 scancode);
+
+void initializeTTYs(BOOL graphicMode) {
+    gTtyList = List_Create();
+
+    gReaderList = List_Create();
+
+    for (int i = 1; i <= 9; ++i) {
+        Tty* tty = NULL;
+        if (graphicMode) {
+            tty = createTty(768 / 16, 1024 / 9, Gfx_FlushFromTty);
+        }
+        else {
+            tty = createTty(25, 80, Screen_FlushFromTty);
+        }
+
+        tty->color = 0x0A;
+
+        List_Append(gTtyList, tty);
+
+        Device device;
+        memset((uint8*)&device, 0, sizeof(Device));
+        sprintf(device.name, "tty%d", i);
+        device.deviceType = FT_CharacterDevice;
+        device.open = tty_open;
+        device.close = tty_close;
+        device.ioctl = tty_ioctl;
+        device.read = tty_read;
+        device.write = tty_write;
+        device.privateData = tty;
+        registerDevice(&device);
+    }
+
+    gActiveTty = List_GetFirstNode(gTtyList)->data;
+}
+
+Tty* getActiveTTY() {
+    return gActiveTty;
+}
+
+FileSystemNode* createPseudoTerminal() {
+    Tty* tty = createTty(768 / 16, 1024 / 9, Gfx_FlushFromTty);
+
+    tty->color = 0x0A;
+
+    Device device;
+    memset((uint8*)&device, 0, sizeof(Device));
+    sprintf(device.name, "pts%d", gPseudoTerminalNameGenerator++);
+    device.deviceType = FT_CharacterDevice;
+    device.open = tty_open;
+    device.close = tty_close;
+    device.ioctl = tty_ioctl;
+    device.read = tty_read;
+    device.write = tty_write;
+    device.privateData = tty;
+    FileSystemNode* node = registerDevice(&device);
+    if (NULL == node) {
+        destroyTty(tty);
+    }
+
+    return node;
+}
+
+static void sendInputToKeyBuffer(Tty* tty, uint8 scancode, uint8 character) {
+    char seq[8];
+    memset(seq, 0, 8);
+
+    switch (character) {
+    case KEY_PAGEUP: {
+        seq[0] = 27;
+        seq[1] = 91;
+        seq[2] = 53;
+        seq[3] = 126;
+        FifoBuffer_enqueue(tty->keyBuffer, seq, 4);
+    }
+        break;
+    case KEY_PAGEDOWN: {
+        seq[0] = 27;
+        seq[1] = 91;
+        seq[2] = 54;
+        seq[3] = 126;
+        FifoBuffer_enqueue(tty->keyBuffer, seq, 4);
+    }
+        break;
+    case KEY_HOME: {
+        seq[0] = 27;
+        seq[1] = 91;
+        seq[2] = 72;
+        FifoBuffer_enqueue(tty->keyBuffer, seq, 3);
+    }
+        break;
+    case KEY_END: {
+        seq[0] = 27;
+        seq[1] = 91;
+        seq[2] = 70;
+        FifoBuffer_enqueue(tty->keyBuffer, seq, 3);
+    }
+        break;
+    case KEY_INSERT: {
+        seq[0] = 27;
+        seq[1] = 91;
+        seq[2] = 50;
+        seq[3] = 126;
+        FifoBuffer_enqueue(tty->keyBuffer, seq, 4);
+    }
+        break;
+    case KEY_DELETE: {
+        seq[0] = 27;
+        seq[1] = 91;
+        seq[2] = 51;
+        seq[3] = 126;
+        FifoBuffer_enqueue(tty->keyBuffer, seq, 4);
+    }
+        break;
+    case KEY_UP: {
+        seq[0] = 27;
+        seq[1] = 91;
+        seq[2] = 65;
+        FifoBuffer_enqueue(tty->keyBuffer, seq, 3);
+    }
+        break;
+    case KEY_DOWN: {
+        seq[0] = 27;
+        seq[1] = 91;
+        seq[2] = 66;
+        FifoBuffer_enqueue(tty->keyBuffer, seq, 3);
+    }
+        break;
+    case KEY_RIGHT: {
+        seq[0] = 27;
+        seq[1] = 91;
+        seq[2] = 67;
+        FifoBuffer_enqueue(tty->keyBuffer, seq, 3);
+    }
+        break;
+    case KEY_LEFT: {
+        seq[0] = 27;
+        seq[1] = 91;
+        seq[2] = 68;
+        FifoBuffer_enqueue(tty->keyBuffer, seq, 3);
+    }
+        break;
+    default:
+        FifoBuffer_enqueue(tty->keyBuffer, &character, 1);
+        break;
+    }
+}
+
+void sendKeyInputToTTY(Tty* tty, uint8 scancode) {
+    beginCriticalSection();
+
+    processScancode(scancode);
+
+    uint8 character = getCharacterForScancode(gKeyModifier, scancode);
+
+    uint8 keyRelease = (0x80 & scancode); //ignore release event
+
+    if (character > 0 && keyRelease == 0) {
+        //enqueue for non-canonical readers
+        sendInputToKeyBuffer(tty, scancode, character);
+        //FifoBuffer_enqueue(tty->keyBuffer, &scancode, 1);
+
+        if (tty->lineBufferIndex >= TTY_LINEBUFFER_SIZE - 1) {
+            tty->lineBufferIndex = 0;
+        }
+
+        if (character == '\b') {
+            if (tty->lineBufferIndex > 0) {
+                tty->lineBuffer[--tty->lineBufferIndex] = '\0';
+
+                if ((tty->term.c_lflag & ECHO) == ECHO) {
+                    write(tty, 1, &character);
+                }
+            }
+        }
+        else {
+            tty->lineBuffer[tty->lineBufferIndex++] = character;
+
+            if ((tty->term.c_lflag & ECHO) == ECHO) {
+                write(tty, 1, &character);
+            }
+        }
+    }
+
+    //Wake readers
+    List_Foreach(n, gReaderList) {
+        File* file = n->data;
+
+        if (file->thread->state == TS_WAITIO) {
+            if (file->thread->state_privateData == tty) {
+                file->thread->state = TS_RUN;
+                file->thread->state_privateData = NULL;
+            }
+        }
+    }
+
+    endCriticalSection();
+}
+
+static BOOL tty_open(File *file, uint32 flags) {
+    //printkf("tty_open: pid:%d\n", file->process->pid);
+
+    Tty* tty = (Tty*)file->node->privateNodeData;
+
+    FifoBuffer_clear(tty->keyBuffer);
+
+    List_Append(gReaderList, file);
+
+    return TRUE;
+}
+
+static void tty_close(File *file) {
+    List_RemoveFirstOccurrence(gReaderList, file);
+}
+
+static int32 tty_ioctl(File *file, int32 request, void * argp) {
+    Tty* tty = (Tty*)file->node->privateNodeData;
+
+    switch (request) {
+    case 0: {
+        sendKeyInputToTTY(tty, (uint8)(uint32)argp);
+
+        return 0;
+    }
+        break;
+    case 1:
+        return tty->columnCount * tty->lineCount * 2;
+        break;
+    case 2: {
+        //set
+        TtyUserBuffer* userTtyBuffer = (TtyUserBuffer*)argp;
+        memcpy(tty->buffer, (uint8*)userTtyBuffer->buffer, tty->columnCount * tty->lineCount * 2);
+        return 0;
+    }
+        break;
+    case 3: {
+        //get
+        TtyUserBuffer* userTtyBuffer = (TtyUserBuffer*)argp;
+        userTtyBuffer->columnCount = tty->columnCount;
+        userTtyBuffer->lineCount = tty->lineCount;
+        userTtyBuffer->currentColumn = tty->currentColumn;
+        userTtyBuffer->currentLine = tty->currentLine;
+        memcpy((uint8*)userTtyBuffer->buffer, tty->buffer, tty->columnCount * tty->lineCount * 2);
+        return 0;
+    }
+        break;
+    case TCGETS: {
+        struct termios* term = (struct termios*)argp;
+
+        //Debug_PrintF("TCGETS\n");
+
+        memcpy((uint8*)term, (uint8*)&(tty->term), sizeof(struct termios));
+
+        return 0;//success
+    }
+        break;
+    case TCSETS:
+    case TCSETSW:
+        break;
+    case TCSETSF: {
+        struct termios* term = (struct termios*)argp;
+
+        //Debug_PrintF("TCSETSF\n");
+
+        memcpy((uint8*)&(tty->term), (uint8*)term, sizeof(struct termios));
+
+        return 0;//success
+    }
+        break;
+    default:
+        break;
+    }
+
+    return -1;
+}
+
+static int32 tty_read(File *file, uint32 size, uint8 *buffer) {
+    enableInterrupts();
+
+    if (size > 0) {
+        Tty* tty = (Tty*)file->node->privateNodeData;
+
+        if ((tty->term.c_lflag & ICANON) == ICANON) {
+            while (TRUE) {
+                for (int i = 0; i < tty->lineBufferIndex; ++i) {
+                    char chr = tty->lineBuffer[i];
+
+                    if (chr == '\n') {
+                        int bytesToCopy = MIN(tty->lineBufferIndex, size);
+
+                        if (bytesToCopy >= tty->term.c_cc[VMIN]) {
+                            tty->lineBufferIndex = 0;
+                            memcpy(buffer, tty->lineBuffer, bytesToCopy);
+
+                            return bytesToCopy;
+                        }
+                    }
+                }
+
+                file->thread->state = TS_WAITIO;
+                file->thread->state_privateData = tty;
+                halt();
+            }
+        }
+        else {
+            while (TRUE) {
+                uint32 neededSize = tty->term.c_cc[VMIN];
+                uint32 bufferLen = FifoBuffer_getSize(tty->keyBuffer);
+
+                if (bufferLen >= neededSize) {
+                    int readSize = FifoBuffer_dequeue(tty->keyBuffer, buffer, MIN(bufferLen, size));
+
+                    return readSize;
+                }
+
+                file->thread->state = TS_WAITIO;
+                file->thread->state_privateData = tty;
+                halt();
+            }
+        }
+    }
+
+    return -1;
+}
+
+static int32 write(Tty* tty, uint32 size, uint8 *buffer) {
+    buffer[size] = '\0';
+
+    Tty_PutText(tty, (const char*)buffer);
+
+    if (gActiveTty == tty) {
+        if (gActiveTty->flushScreen) {
+            gActiveTty->flushScreen(gActiveTty);
+        }
+    }
+
+    return size;
+}
+
+static int32 tty_write(File *file, uint32 size, uint8 *buffer) {
+    return write(file->node->privateNodeData, size, buffer);
+}
+
+static void setActiveTty(Tty* tty) {
+    gActiveTty = tty;
+
+    Gfx_Fill(0xFFFFFFFF);
+
+    if (tty->flushScreen) {
+        tty->flushScreen(tty);
+    }
+
+    //Serial_PrintF("line:%d column:%d\r\n", gActiveTty->currentLine, gActiveTty->currentColumn);
+}
+
+BOOL isValidTTY(Tty* tty) {
+    List_Foreach(n, gTtyList) {
+        if (n->data == tty) {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+static uint8 getCharacterForScancode(KeyModifier modifier, uint8 scancode) {
+    //return gKeyboardLayout[scancode];
+    if ((modifier & KM_LeftShift) == KM_LeftShift || (modifier & KM_RightShift) == KM_RightShift) {
+        return gKeyShiftMap[scancode];
+    }
+
+    return gKeyMap[scancode];
+}
+
+static void applyModifierKeys(KeyModifier modifier, uint8 scancode) {
+    if ((modifier & KM_Ctrl) == KM_Ctrl) {
+        int ttyIndex = scancode - KEY_F1;
+        //printkf("TTY:%d\n", ttyIndex);
+        int ttyCount = List_GetCount(gTtyList);
+        if (ttyIndex >= 0 && ttyIndex < ttyCount) {
+            int i = 0;
+            List_Foreach(n, gTtyList) {
+                if (ttyIndex == i) {
+                    setActiveTty(n->data);
+                    break;
+                }
+                ++i;
+            }
+        }
+    }
+}
+
+static void processScancode(uint8 scancode) {
+    uint8 lastBit = scancode & 0x80;
+
+    scancode &= 0x7F;
+
+    if (lastBit) {
+        //key release
+
+        switch (scancode) {
+        case KEY_LEFTSHIFT:
+            gKeyModifier &= ~KM_LeftShift;
+            break;
+        case KEY_RIGHTSHIFT:
+            gKeyModifier &= ~KM_RightShift;
+            break;
+        case KEY_CTRL:
+            gKeyModifier &= ~KM_Ctrl;
+            break;
+        case KEY_ALT:
+            gKeyModifier &= ~KM_Alt;
+            break;
+        }
+
+        //printkf("released: %x (%d)\n", scancode, scancode);
+    }
+    else {
+        //key pressed
+
+        switch (scancode) {
+        case KEY_LEFTSHIFT:
+            gKeyModifier |= KM_LeftShift;
+            break;
+        case KEY_RIGHTSHIFT:
+            gKeyModifier |= KM_RightShift;
+            break;
+        case KEY_CTRL:
+            gKeyModifier |= KM_Ctrl;
+            break;
+        case KEY_ALT:
+            gKeyModifier |= KM_Alt;
+            break;
+        }
+
+        //printkf("pressed: %x (%d)\n", scancode, scancode);
+
+        applyModifierKeys(gKeyModifier, scancode);
+    }
+}