about summary refs log blame commit diff stats
path: root/kernel.soso/ttydriver.c
blob: 755cda158302754b24098cb5406c68e1cf634ec6 (plain) (tree)























                                               
                          





                      
      













































                                                                
                             


































                                                         
                                  












































                                                                           
                                       



                                
                                  
                        
                          

                                                                  
              






















                                                         
                     


                      
                                        














                                                                  
                       





                        
                                                                             



                        
                      






                                                   
                        






                                                   
                    





                                                   
                   





                                                   
                      






                                                   
                      






                                                   
                  





                                                   
                    





                                                   
                     





                                                   
                    











                                                          
                                                  







                                                                      
                                           



                                                           
                                                              


                                     

                                           

                                                               
                                                         



                                              
              

                                                                
                                                     





                                          
                                  

                             

                                                         








                                                       
                                                
                                                        









                                                 
                                   


                                                  
                                                                

                                                 

                      







                                                     
             





                                                                                                  
             









                                                                                                  
                  











                                                                           
                   















                                                                           
                                                               

                       
                   

                                                     


                                                                

                                                  
                                      

                                                                          
                                                                  












                                                                         

                          


                                                                      
                                              














                                                                                                    
                                                          



                                          

                                      






                                                
                                                                


                                                            
                                    



                         
                           





                                                                                                 


                               






                        
                                                                            
                                       
                                                                                                   





                                      

                                                                     


                                               
                                                   
                      

                                       








                                          
                                             



                                    
                  

                     
                           













                                           
                                                             
     
          

                     
                           













                                          
                                                            



                                                  
#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);
    }
}