Files
MentOS/mentos/src/io/proc_video.c
T
2022-01-19 22:50:45 -05:00

243 lines
6.7 KiB
C

/// @file proc_video.c
/// @brief Contains callbacks for procv system files.
/// @copyright (c) 2014-2021 This file is distributed under the MIT License.
/// See LICENSE.md for details.
// Include the kernel log levels.
#include "sys/kernel_levels.h"
/// Change the header.
#define __DEBUG_HEADER__ "[PROCV ]"
/// Set the log level.
#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG
#include "bits/termios-struct.h"
#include "drivers/keyboard/keyboard.h"
#include "drivers/keyboard/keymap.h"
#include "fs/procfs.h"
#include "bits/ioctls.h"
#include "sys/bitops.h"
#include "io/video.h"
#include "io/debug.h"
#include "sys/errno.h"
#include "fcntl.h"
#include "fs/vfs.h"
#include "ctype.h"
#include "process/scheduler.h"
void print_rb(fs_rb_scancode_t *rb)
{
if (!fs_rb_scancode_empty(rb)) {
for (unsigned i = rb->read; (i < rb->write) && (i < rb->size); ++i) {
pr_debug("%c", rb->buffer[i]);
}
pr_debug("\n");
}
}
static ssize_t procv_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyte)
{
// Stop if the buffer is invalid.
if (buf == NULL)
return -1;
// Get the currently running process.
task_struct *process = scheduler_get_current_process();
// Get a pointer to its ketboard ring-buffer.
fs_rb_scancode_t *rb = &process->keyboard_rb;
// Pre-check the flags.
bool_t flg_icanon = bitmask_check(process->termios.c_lflag, ICANON) == ICANON;
bool_t flg_echoe = bitmask_check(process->termios.c_lflag, ECHOE) == ECHOE;
bool_t flg_echo = bitmask_check(process->termios.c_lflag, ECHO) == ECHO;
bool_t flg_isig = bitmask_check(process->termios.c_lflag, ISIG) == ISIG;
// If we are in canonical mode, and the last inserted element is a newline,
// we pop the buffer until it's empty.
if (!fs_rb_scancode_empty(rb) && (!flg_icanon || (flg_icanon && (fs_rb_scancode_front(rb) == '\n')))) {
*((char *)buf) = fs_rb_scancode_pop_back(rb) & 0x00FF;
return 1;
}
// Once we have dealt with the canonical mode, get the character.
int c = keyboard_pop_back();
// Check that it's a valid caracter.
if (c < 0)
return 0;
// Keep only the character not the scancode.
c &= 0x00FF;
// We just received backspace.
if (c == '\b') {
// If !ECHOE and ECHO, We need to show the the `^?` string.
if (!flg_echoe && flg_echo) {
video_puts("^?");
}
// If we are in canonical mode, we pop the previous character.
if (flg_icanon) {
// Pop the previous character in buffer.
fs_rb_scancode_pop_front(rb);
// Delete the previous character on video.
if (flg_echoe)
video_putc(c);
} else {
// Add the character to the buffer.
fs_rb_scancode_push_front(rb, 0x7F);
// Return the character.
*((char *)buf) = fs_rb_scancode_pop_back(rb) & 0x00FF;
return 1;
}
return 0;
} else if (c == 0x7f) {
if (flg_echo) {
video_puts("^[[3~");
}
// Add the character to the buffer.
fs_rb_scancode_push_front(rb, '\033');
fs_rb_scancode_push_front(rb, '[');
fs_rb_scancode_push_front(rb, '3');
fs_rb_scancode_push_front(rb, '~');
return 0;
} else {
// Add the character to the buffer.
fs_rb_scancode_push_front(rb, c);
}
// If echo is activated, output the character to video.
if (flg_echo) {
if (iscntrl(c) && (isalpha('A' + (c - 1)) && (c != '\n') && (c != '\b'))) {
video_putc('^');
video_putc('A' + (c - 1));
} else {
video_putc(c);
}
}
if (flg_isig) {
if (iscntrl(c)) {
if (c == 0x03) {
sys_kill(process->pid, SIGTERM);
} else if (c == 0x1A) {
sys_kill(process->pid, SIGSTOP);
}
}
}
// If we are NOT in canonical mode, we can send the character back to user
// right away.
if (!flg_icanon) {
*((char *)buf) = fs_rb_scancode_pop_back(rb) & 0x00FF;
return 1;
}
#if 0
// The last inserted character.
int back_c = keyboard_back();
if (back_c < 0)
return 0;
// The first inserted character.
int front_c = keyboard_front();
pr_debug("'%c' (%3d %04x) [F: '%c' (%04x)]\n", back_c, back_c, back_c, front_c, front_c);
// Echo the character to video.
if (flg_echo) {
video_putc(back_c & 0x00FF);
}
// If we have the canonical input active, we should not return characters,
// until we receive a newline.
if ((flg_icanon && (front_c == '\n')) ||
!flg_icanon) {
*((char *)buf) = keyboard_pop_back() & 0x00FF;
return 1;
}
#endif
#if 0
// Read the character from the keyboard.
int c = keyboard_getc(false) & 0x00FF;
if (c < 0)
return 0;
if (c == KEY_PAGE_UP) {
video_shift_one_page_down();
return 0;
} else if (c == KEY_PAGE_DOWN) {
video_shift_one_page_up();
return 0;
} else {
// Echo the character to video.
if (flg_echo) {
video_putc(c & 0x00FF);
}
}
#endif
return 0;
}
static ssize_t procv_write(vfs_file_t *file, const void *buf, off_t offset, size_t nbyte)
{
for (size_t i = 0; i < nbyte; ++i) {
video_putc(((char *)buf)[i]);
}
return nbyte;
}
static int procv_fstat(vfs_file_t *file, stat_t *stat)
{
return -ENOSYS;
}
static int procv_ioctl(vfs_file_t *file, int request, void *data)
{
task_struct *process = scheduler_get_current_process();
switch (request) {
case TCGETS:
*((termios_t *)data) = process->termios;
break;
case TCSETS:
process->termios = *((termios_t *)data);
break;
default:
break;
}
return 0;
}
/// Filesystem general operations.
static vfs_sys_operations_t procv_sys_operations = {
.mkdir_f = NULL,
.rmdir_f = NULL,
.stat_f = NULL
};
/// Filesystem file operations.
static vfs_file_operations_t procv_fs_operations = {
.open_f = NULL,
.unlink_f = NULL,
.close_f = NULL,
.read_f = procv_read,
.write_f = procv_write,
.lseek_f = NULL,
.stat_f = procv_fstat,
.ioctl_f = procv_ioctl,
.getdents_f = NULL
};
int procv_module_init()
{
proc_dir_entry_t *video = proc_create_entry("video", NULL);
if (video == NULL) {
pr_err("Cannot create `/proc/video`.\n");
return 1;
}
pr_debug("Created `/proc/video` (%p)\n", video);
// Set the specific operations.
video->sys_operations = &procv_sys_operations;
video->fs_operations = &procv_fs_operations;
return 0;
}