1/* 2 * Copyright 2017, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(DATA61_BSD) 11 */ 12/* 13 * Implementation of an 80x25 EGA text mode. All the functions are named with 'serial' since 14 * that is what platsupport expects. This was a quick hack for a project and includes lots of 15 * logic that is rather inspecific to the underlying frame buffer. If someone adds another text 16 * mode frambuffer (or a linear frame buffer with a font renderer) then this should be abstracted. 17 * ideally some kind of virtual terminal code could be ported over to properly deal with escape characters 18 * and command codes for moving cursors etc around. But this is a basic hack for 'The machine I 19 * want to test on has a screen and no serial port' 20 */ 21 22#include <autoconf.h> 23#include <platsupport/gen_config.h> 24#include <assert.h> 25#include <string.h> 26#include <platsupport/plat/serial.h> 27#include "../../chardev.h" 28#include <string.h> 29 30/* Assumptions on the graphics mode and frame buffer location */ 31#define EGA_TEXT_FB_BASE 0xB8000 32#define MODE_WIDTH 80 33#define MODE_HEIGHT 25 34 35/* How many lines to scroll by */ 36#define SCROLL_LINES 1 37 38/* Hacky global state */ 39static volatile short *base_ptr = NULL; 40static int cursor_x = 0; 41static int cursor_y = 0; 42 43static void 44scroll(void) 45{ 46 /* number of chars we are dropping when we do the scroll */ 47 int clear_chars = SCROLL_LINES * MODE_WIDTH; 48 /* number of chars we need to move to perform the scroll. This all the lines 49 * minus however many we drop */ 50 int scroll_chars = MODE_WIDTH * MODE_HEIGHT - clear_chars; 51 /* copy the lines up. we skip the same number of characters that we will clear, and move the 52 * rest to the top. cannot use memcpy as the regions almost certainly overlap */ 53 memmove((void*)base_ptr, (void*)&base_ptr[clear_chars], scroll_chars * sizeof(*base_ptr)); 54 /* now zero out the bottom lines that we got rid of */ 55 memset((void*)&base_ptr[scroll_chars], 0, clear_chars * sizeof(*base_ptr)); 56 /* move the virtual cursor up */ 57 cursor_y -= SCROLL_LINES; 58} 59 60static int 61text_ega_getchar(ps_chardevice_t* d UNUSED) 62{ 63 assert(!"EGA framebuffer does not implement getchar"); 64 return 0; 65} 66 67static int 68text_ega_putchar(ps_chardevice_t* d, int c) 69{ 70 /* emulate various control characters */ 71 if (c == '\t') { 72 text_ega_putchar(d, ' '); 73 while (cursor_x % 4 != 0) { 74 text_ega_putchar(d, ' '); 75 } 76 } else if (c == '\n') { 77 cursor_y ++; 78 /* assume a \r with a \n */ 79 cursor_x = 0; 80 } else if (c == '\r') { 81 cursor_x = 0; 82 } else { 83 /* 7<<8 constructs a nice neutral grey color. */ 84 base_ptr[cursor_y * MODE_WIDTH + cursor_x] = ((char)c) | (7 << 8); 85 cursor_x++; 86 } 87 if (cursor_x >= MODE_WIDTH) { 88 cursor_x = 0; 89 cursor_y++; 90 } 91 while (cursor_y >= MODE_HEIGHT) { 92 scroll(); 93 } 94 return 0; 95} 96 97static ssize_t 98text_ega_write(ps_chardevice_t* d, const void* vdata, size_t count, chardev_callback_t rcb UNUSED, void* token UNUSED) 99{ 100 const char* data = (const char*)vdata; 101 int i; 102 for (i = 0; i < count; i++) { 103 if (text_ega_putchar(d, *data++) < 0) { 104 return i; 105 } 106 } 107 return count; 108} 109 110static ssize_t 111text_ega_read(ps_chardevice_t* d, void* vdata, size_t count, chardev_callback_t rcb UNUSED, void* token UNUSED) 112{ 113 char* data; 114 int ret; 115 int i; 116 data = (char*)vdata; 117 for (i = 0; i < count; i++) { 118 ret = text_ega_getchar(d); 119 if (ret != EOF) { 120 *data++ = ret; 121 } else { 122 return i; 123 } 124 } 125 return count; 126} 127 128static void 129text_ega_handle_irq(ps_chardevice_t* d) 130{ 131 /* TODO */ 132} 133 134int 135text_ega_init(const struct dev_defn* defn, const ps_io_ops_t* ops, ps_chardevice_t* dev) 136{ 137 /* handle an insane case where the serial might get repeatedly initialized. and 138 * avoid clearing the entire screen if this is the case. This comes about due 139 * to implementing device 'sharing' by just giving every process the device */ 140 int clear = !base_ptr; 141 memset(dev, 0, sizeof(*dev)); 142 base_ptr = chardev_map(defn, ops); 143 assert(base_ptr); 144 /* clear the screen */ 145 if (clear) { 146 memset((void*)base_ptr, 0, MODE_WIDTH * MODE_HEIGHT * sizeof(*base_ptr)); 147 cursor_x = 0; 148 cursor_y = 0; 149 } 150 151 dev->id = defn->id; 152 dev->vaddr = (void*)base_ptr; 153 dev->read = &text_ega_read; 154 dev->write = &text_ega_write; 155 dev->handle_irq = &text_ega_handle_irq; 156 dev->irqs = defn->irqs; 157 dev->ioops = *ops; 158 159 return 0; 160} 161