/** @file console.c * * @brief Console driver implementation. * * @author Hanjie Wu (hanjiew) * @bug No functional bugs */ #include #include #include #include #include #include #include #include #include #include #include #include #include /** index of the register to start displaying cursor */ #define CRTC_CURSOR_START 10 /** bit to start displaying cursor */ #define CURSOR_ENABLE_BIT 0x20 /** pointer to the viedo memory at B800h */ a_char_on_screen_t (*console_mem)[CONSOLE_WIDTH] = (a_char_on_screen_t(*)[CONSOLE_WIDTH])CONSOLE_MEM_BASE; /** the char used to produce blank */ #define BLANK_CH ' ' /** initial color of the cursor */ #define DEFAULT_COLOR (FGND_WHITE | BGND_BLACK) pts_t* active_pts = NULL; spl_t pts_lock = SPL_INIT; /** all pts structures */ queue_t* all_pts = NULL; pts_t kernel_pts; void setup_pts() { pts_init(&kernel_pts); kernel_pts.refcount++; /* kernel's refcount for all kths */ active_pts = &kernel_pts; } void pts_init(pts_t* pts) { pts->cur_x = pts->cur_y = 0; pts->cur_color = DEFAULT_COLOR; pts->cur_shown = 1; pts->reqs = NULL; pts->kbd_request_lock = MUTEX_INIT; pts->kbd_request_cv = CV_INIT; pts->input_lock = MUTEX_INIT; pts->input_cv = CV_INIT; pts->kh_r_pos = pts->kh_w_pos = 0; pts->chr_r_pos = pts->chr_w_pos = 0; pts->forward_tab = 0; pts->lock = MUTEX_INIT; pts->pvs = NULL; pts->refcount = 0; queue_insert_tail(&all_pts, &pts->pts_link); } void switch_pts(pts_t* new_pts) { int old_if = spl_lock(&pts_lock); pts_t* pts = active_pts; active_pts = new_pts; if (pts->refcount == 0) { queue_detach(&all_pts, &pts->pts_link); sfree(pts, sizeof(pts_t)); } memcpy(console_mem, new_pts->mem, sizeof(new_pts->mem)); int pos = new_pts->cur_y * CONSOLE_WIDTH + new_pts->cur_x; outb(CRTC_IDX_REG, CRTC_CURSOR_LSB_IDX); outb(CRTC_DATA_REG, pos & 0xff); outb(CRTC_IDX_REG, CRTC_CURSOR_MSB_IDX); outb(CRTC_DATA_REG, pos >> 8); spl_unlock(&pts_lock, old_if); } /** * @brief Move the cursor to a position. Position is checked before calling this * function * @param pts pts * @param x x * @param y y */ static inline void move_cursor(pts_t* pts, int x, int y) { pts->cur_x = x; pts->cur_y = y; int old_if = spl_lock(&pts_lock); if (active_pts == pts) { int pos = y * CONSOLE_WIDTH + x; outb(CRTC_IDX_REG, CRTC_CURSOR_LSB_IDX); outb(CRTC_DATA_REG, pos & 0xff); outb(CRTC_IDX_REG, CRTC_CURSOR_MSB_IDX); outb(CRTC_DATA_REG, pos >> 8); } spl_unlock(&pts_lock, old_if); } /** * @brief Scroll the console by one line and clean the last line */ static inline void scroll_page(pts_t* pts) { /* move all but last line forward */ memmove(pts->mem[0], pts->mem[1], sizeof(a_char_on_screen_t) * CONSOLE_WIDTH * (CONSOLE_HEIGHT - 1)); int i; for (i = 0; i < CONSOLE_WIDTH; i++) { pts->mem[CONSOLE_HEIGHT - 1][i] = (a_char_on_screen_t){BLANK_CH, pts->cur_color}; } int old_if = spl_lock(&pts_lock); if (active_pts == pts) { memmove( console_mem[0], console_mem[1], sizeof(a_char_on_screen_t) * CONSOLE_WIDTH * (CONSOLE_HEIGHT - 1)); int i; for (i = 0; i < CONSOLE_WIDTH; i++) { console_mem[CONSOLE_HEIGHT - 1][i] = (a_char_on_screen_t){BLANK_CH, pts->cur_color}; } } spl_unlock(&pts_lock, old_if); } static inline void draw_char(pts_t* pts, int row, int col, a_char_on_screen_t ch) { pts->mem[row][col] = ch; int old_if = spl_lock(&pts_lock); if (active_pts == pts) { console_mem[row][col] = ch; } spl_unlock(&pts_lock, old_if); } int putbyte(char ch) { if (active_pts == NULL) { /* this only happens when panicking before setup_pts */ active_pts = &kernel_pts; pts_putbyte(&kernel_pts, ch); active_pts = NULL; return 0; } return pts_putbyte(get_current()->pts, ch); } int pts_putbyte(pts_t* pts, char ch) { if (ch == '\n') { if (pts->cur_y < CONSOLE_HEIGHT - 1) { move_cursor(pts, 0, pts->cur_y + 1); } else { scroll_page(pts); move_cursor(pts, 0, pts->cur_y); } return ch; } if (ch == '\r') { move_cursor(pts, 0, pts->cur_y); return ch; } if (ch == '\b') { if (pts->cur_x > 0) { move_cursor(pts, pts->cur_x - 1, pts->cur_y); } else if (pts->cur_y > 0) { move_cursor(pts, CONSOLE_WIDTH - 1, pts->cur_y - 1); } a_char_on_screen_t c = {BLANK_CH, pts->cur_color}; draw_char(pts, pts->cur_y, pts->cur_x, c); return ch; } /* save the position where we should show the char */ int prev_x = pts->cur_x, prev_y = pts->cur_y; if (pts->cur_x < CONSOLE_WIDTH - 1) { /* move forward */ move_cursor(pts, pts->cur_x + 1, pts->cur_y); } else if (pts->cur_y < CONSOLE_HEIGHT - 1) { /* go to next line */ move_cursor(pts, 0, pts->cur_y + 1); } else { /* scroll and go to beginning of the line */ scroll_page(pts); move_cursor(pts, 0, pts->cur_y); prev_y -= 1; } a_char_on_screen_t c = {ch, pts->cur_color}; draw_char(pts, prev_y, prev_x, c); return ch; } void pts_putbytes(pts_t* pts, const char* s, int len) { if (s == NULL || len <= 0) { return; } int i; for (i = 0; i < len; i++) { pts_putbyte(pts, s[i]); } } int pts_set_term_color(pts_t* pts, int color) { if (color > 0xff || color < 0) { return -1; } pts->cur_color = (char)color; return 0; } void pts_get_term_color(pts_t* pts, int* color) { *color = pts->cur_color; } int pts_set_cursor(pts_t* pts, int row, int col) { if (row >= CONSOLE_HEIGHT || row < 0) { return -1; } if (col >= CONSOLE_WIDTH || col < 0) { return -1; } move_cursor(pts, col, row); return 0; } void pts_get_cursor(pts_t* pts, int* row, int* col) { *row = pts->cur_y; *col = pts->cur_x; } int pts_print_at(pts_t* pts, int len, va_t buf, int row, int col, int color) { int old_row, old_col; int old_color; pts_get_cursor(pts, &old_row, &old_col); if (pts_set_cursor(pts, row, col) != 0) { goto bad_pos; } pts_get_term_color(pts, &old_color); if (pts_set_term_color(pts, color) != 0) { goto bad_color; } if (print_buf_from_user(pts, buf, len) != 0) { goto bad_print; } pts_set_term_color(pts, old_color); pts_set_cursor(pts, old_row, old_col); return 0; bad_print: pts_set_term_color(pts, old_color); bad_color: pts_set_cursor(pts, old_row, old_col); bad_pos: return -1; } /** * @brief keyboard inturrupt handler */ void kbd_handler_real(stack_frame_t* f) { unsigned char sc = inb(KEYBOARD_PORT); pic_acknowledge(KBD_IRQ); pts_t* pts = active_pts; int should_insert = 0; kh_type kh = process_scancode(sc); if (KH_HASDATA(kh) && KH_ISMAKE(kh)) { should_insert = 1; if (KH_GETCHAR(kh) == KHE_TAB) { if (KH_SHIFT(kh) != 0) { if (pts->forward_tab == 0) { pts->forward_tab = 1; return; } /* else forward shift+tab to client */ } else if (KH_CTL(kh) != 0) { if (pts->forward_tab != 0) { pts->forward_tab = 0; return; } } else if (pts->forward_tab == 0) { pts_t* new_pts = queue_data(pts->pts_link.next, pts_t, pts_link); if (new_pts != pts) { switch_pts(new_pts); } return; } } } if (pts->pvs != NULL) { pv_t* pv = queue_data(pts->pvs, pv_t, pts_link); pv_pend_irq(pv, KEY_IDT_ENTRY, kh); return; } if (should_insert != 0) { int next_w_pos = (pts->kh_w_pos + 1) % KH_RING_SIZE; if (next_w_pos != pts->kh_r_pos) { pts->kh_ring[pts->kh_w_pos] = kh; pts->kh_w_pos = next_w_pos; } } cv_signal(&pts->input_cv); } /** * @brief process scancode until a char is produced * @return char produced */ static int sc_process(pts_t* pts); /** * @brief flush a line to user buffer * @param len length of buffer * @param buf buffer * @return size of chars flushed, -1: buf invalid */ static int flush_line(pts_t* pts, int len, va_t buf); int do_readline(int len, va_t buf) { if (len < 0 || len > MAX_READLINE) { return -1; } if (len == 0) { return 0; } pts_t* pts = get_current()->pts; kbd_request_t req; req.t = get_current(); /* wait until our turn */ mutex_lock(&pts->kbd_request_lock); queue_insert_tail(&pts->reqs, &req.node); while (pts->reqs != &req.node) { cv_wait(&pts->kbd_request_cv, &pts->kbd_request_lock); } mutex_unlock(&pts->kbd_request_lock); int should_flush = 0; if (pts->chr_r_pos != pts->chr_w_pos) { int i; for (i = pts->chr_r_pos; i != pts->chr_w_pos; i = (i + 1) % CHR_RING_SIZE) { if (pts->chr_ring[i] == '\n') { should_flush = 1; break; } } /* check if ring is full */ if (pts->chr_r_pos == (pts->chr_w_pos + 1) % CHR_RING_SIZE) { should_flush = 1; } } while (should_flush == 0) { /* wait for more chars */ char c = sc_process(pts); if (c == '\b') { if (pts->chr_w_pos != pts->chr_r_pos) { pts_putbyte(pts, c); pts->chr_w_pos = (pts->chr_w_pos + CHR_RING_SIZE - 1) % CHR_RING_SIZE; } } else { pts->chr_ring[pts->chr_w_pos] = c; pts->chr_w_pos = (pts->chr_w_pos + 1) % CHR_RING_SIZE; pts_putbyte(pts, c); should_flush = (c == '\n') || (pts->chr_r_pos == (pts->chr_w_pos + 1) % CHR_RING_SIZE); } } int result = flush_line(pts, len, buf); mutex_lock(&pts->kbd_request_lock); queue_detach(&pts->reqs, &req.node); cv_signal(&pts->kbd_request_cv); mutex_unlock(&pts->kbd_request_lock); return result; } int do_getchar() { pts_t* pts = get_current()->pts; kbd_request_t req; req.t = get_current(); mutex_lock(&pts->kbd_request_lock); queue_insert_tail(&pts->reqs, &req.node); while (pts->reqs != &req.node) { cv_wait(&pts->kbd_request_cv, &pts->kbd_request_lock); } mutex_unlock(&pts->kbd_request_lock); int result; if (pts->chr_r_pos != pts->chr_w_pos) { result = pts->chr_ring[pts->chr_r_pos]; pts->chr_r_pos = (pts->chr_r_pos + 1) % CHR_RING_SIZE; } else { result = sc_process(pts); } mutex_lock(&pts->kbd_request_lock); queue_detach(&pts->reqs, &req.node); cv_signal(&pts->kbd_request_cv); mutex_unlock(&pts->kbd_request_lock); return result; } static int flush_line(pts_t* pts, int len, va_t buf) { int size = 0, i; for (i = pts->chr_r_pos; i != pts->chr_w_pos; i = (i + 1) % CHR_RING_SIZE) { size++; if (pts->chr_ring[i] == '\n') { break; } } if (size > len) { size = len; } int end_pos = (pts->chr_r_pos + size) % CHR_RING_SIZE; if (end_pos > pts->chr_r_pos) { if (copy_to_user(buf, size, &pts->chr_ring[pts->chr_r_pos]) != 0) { return -1; } } else { int size_1 = (CHR_RING_SIZE - pts->chr_r_pos); if (copy_to_user(buf, size_1, &pts->chr_ring[pts->chr_r_pos]) != 0) { return -1; } if (copy_to_user(buf + size_1, end_pos, &pts->chr_ring[0]) != 0) { return -1; } } pts->chr_r_pos = end_pos; return size; } static int sc_process(pts_t* pts) { while (1) { /* wait for more scancode */ mutex_lock(&pts->input_lock); while (pts->kh_r_pos == pts->kh_w_pos) { cv_wait(&pts->input_cv, &pts->input_lock); } kh_type kh = pts->kh_ring[pts->kh_r_pos]; pts->kh_r_pos = (pts->kh_r_pos + 1) % KH_RING_SIZE; mutex_unlock(&pts->input_lock); return KH_GETCHAR(kh); } }