URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [branches/] [stable_0_2_x/] [or1ksim/] [peripheral/] [ps2kbd.c] - Rev 1359
Go to most recent revision | Compare with Previous | Blame | View Log
/* ps2kbd.c -- Very simple (and limited) PS/2 keyboard simulation Copyright (C) 2002 Marko Mlinar, markom@opencores.org This file is part of OpenRISC 1000 Architectural Simulator. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <stdlib.h> #include <stdio.h> #include <string.h> #include "config.h" #ifdef HAVE_INTTYPES_H #include <inttypes.h> #endif #include "port.h" #include "arch.h" #include "ps2kbd.h" #include "sim-config.h" #include "abstract.h" #include "sched.h" #include "pic.h" /* ASCII to scan code conversion table */ const static struct { /* Whether shift must be pressed */ unsigned char shift; /* Scan code to be generated */ unsigned char code; } scan_table [128] = { /* 0 - 15 */ {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x0E}, {0, 0x0F}, {0, 0x1C}, {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x00}, /* 16 - 31 */ {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x01}, {0, 0x00}, {0, 0x00}, {0, 0x00}, {0, 0x00}, /* 32 - 47 */ {0, 0x39}, {1, 0x02}, {1, 0x28}, {1, 0x04}, {1, 0x05}, {1, 0x06}, {1, 0x08}, {0, 0x28}, {1, 0x0A}, {1, 0x0B}, {1, 0x09}, {1, 0x0D}, {0, 0x33}, {0, 0x0C}, {0, 0x34}, {0, 0x35}, /* 48 - 63 */ {0, 0x0B}, {0, 0x02}, {0, 0x03}, {0, 0x04}, {0, 0x05}, {0, 0x06}, {0, 0x07}, {0, 0x08}, {0, 0x09}, {0, 0x0A}, {1, 0x27}, {0, 0x27}, {1, 0x33}, {0, 0x0D}, {1, 0x34}, {1, 0x35}, /* 64 - 79 */ {1, 0x03}, {1, 0x1E}, {1, 0x30}, {1, 0x2E}, {1, 0x20}, {1, 0x12}, {1, 0x21}, {1, 0x22}, {1, 0x23}, {1, 0x17}, {1, 0x24}, {1, 0x25}, {1, 0x26}, {1, 0x32}, {1, 0x31}, {1, 0x18}, /* 80 - 95 */ {1, 0x19}, {1, 0x10}, {1, 0x13}, {1, 0x1F}, {1, 0x14}, {1, 0x16}, {1, 0x2F}, {1, 0x11}, {1, 0x2D}, {1, 0x15}, {1, 0x2C}, {0, 0x1A}, {0, 0x2B}, {0, 0x1B}, {1, 0x07}, {1, 0x0C}, /* 96 - 111 */ {0, 0x29}, {0, 0x1E}, {0, 0x30}, {0, 0x2E}, {0, 0x20}, {0, 0x12}, {0, 0x21}, {0, 0x22}, {0, 0x23}, {0, 0x17}, {0, 0x24}, {0, 0x25}, {0, 0x26}, {0, 0x32}, {0, 0x31}, {0, 0x18}, /* 112 - 127 */ {0, 0x19}, {0, 0x10}, {0, 0x13}, {0, 0x1F}, {0, 0x14}, {0, 0x16}, {0, 0x2F}, {0, 0x11}, {0, 0x2D}, {0, 0x15}, {0, 0x2C}, {1, 0x1A}, {1, 0x2B}, {1, 0x1B}, {1, 0x29}, {0, 0x00} }; /* Temporary buffer to store incoming scan codes */ static unsigned char kbd_buf[KBD_MAX_BUF] = {0}; /* Number of scan codes in buffer */ static unsigned long kbd_buf_count = 0; static unsigned long kbd_buf_head = 0; static unsigned long kbd_buf_tail = 0; /* Input stream */ static FILE *kbd_rxfs = NULL; /* Controller Command (write into 0x64) */ static int kbd_ccmd; /* Keyboard Command (write into 0x60) */ static unsigned char kbd_kcmd; /* Controller Command Byte */ static unsigned char kbd_ccmdbyte; /* Keyboard response pending */ static unsigned long kbd_kresp; /* Keyboard slowdown factor */ static long kbd_slowdown; static void kbd_put (unsigned char c) { if (kbd_buf_count >= KBD_MAX_BUF) { fprintf (stderr, "WARNING: Keyboard buffer overflow.\n"); } else { kbd_buf[kbd_buf_head] = c; kbd_buf_head = (kbd_buf_head + 1) % KBD_MAX_BUF; kbd_buf_count++; } } /* Decodes ascii code c into multiple scan codes, placed into buf, length is returned */ static void scan_decode (unsigned char c) { /* Do not handle special characters and extended ascii */ if (c >= 128 || !scan_table[c].code) return; /* Make shift? */ if (scan_table[c].shift) kbd_put (0x2a); /* Make char */ kbd_put (scan_table[c].code); /* Break char */ kbd_put (scan_table[c].code | 0x80); /* Break shift? */ if (scan_table[c].shift) kbd_put (0xaa); } /* Write a register */ void kbd_write8 (oraddr_t addr, uint32_t value, void *dat) { int a = (addr - config.kbd.baseaddr); switch (a) { case KBD_CTRL: kbd_ccmd = value & 0xff; if (kbd_ccmd == KBD_CCMD_RCB) kbd_kresp = 0x1; if (kbd_ccmd == KBD_CCMD_ST1) kbd_kresp = 0x1; if (kbd_ccmd == KBD_CCMD_ST2) kbd_kresp = 0x1; if (kbd_ccmd == KBD_CCMD_DKI) kbd_ccmdbyte |= KBD_CCMDBYTE_EN; if (kbd_ccmd == KBD_CCMD_EKI) kbd_ccmdbyte &= ~KBD_CCMDBYTE_EN; if (config.sim.verbose) PRINTF("kbd_write8(%"PRIxADDR") %"PRIx32"\n", addr, value); break; case KBD_DATA: if (kbd_ccmd == KBD_CCMD_WCB) { kbd_ccmdbyte = value & 0xff; kbd_ccmd = 0x00; } else kbd_kcmd = value & 0xff; if (kbd_kcmd == KBD_KCMD_DK) kbd_ccmdbyte |= KBD_CCMDBYTE_EN; if (kbd_kcmd == KBD_KCMD_EK) kbd_ccmdbyte &= ~KBD_CCMDBYTE_EN; kbd_kresp = 0x1; kbd_ccmd = 0x00; if (config.sim.verbose) PRINTF("kbd_write8(%"PRIxADDR") %"PRIx32"\n", addr, value); break; default: fprintf (stderr, "Write out of keyboard space (0x%"PRIxADDR")!\n", addr); runtime.sim.cont_run = 0; break; } } /* Read a register */ uint32_t kbd_read8 (oraddr_t addr, void *dat) { int a = (addr - config.kbd.baseaddr); switch (a) { case KBD_CTRL: { unsigned long c = 0x0; if (kbd_kresp || kbd_buf_count) c |= KBD_STATUS_OBF; c |= kbd_ccmdbyte & KBD_CCMDBYTE_SYS; c |= KBD_STATUS_INH; if (config.sim.verbose) PRINTF("kbd_read8(%"PRIxADDR") %lx\n", addr, c); return c; } case KBD_DATA: if (kbd_ccmd) { unsigned long rc; if (kbd_ccmd == KBD_CCMD_RCB) rc = kbd_ccmdbyte; if (kbd_ccmd == KBD_CCMD_ST1) rc = 0x55; if (kbd_ccmd == KBD_CCMD_ST2) rc = 0x00; kbd_ccmd = 0x00; kbd_kresp = 0x0; if (config.sim.verbose) PRINTF("kbd_read8(%"PRIxADDR") %lx\n", addr, rc); return rc; } else if (kbd_kresp) { unsigned long rc; if (kbd_kresp == 0x2) { kbd_kresp = 0x0; rc = KBD_KRESP_RSTOK; } else if (kbd_kcmd == KBD_KCMD_RST) { kbd_kresp = 0x2; rc = KBD_KRESP_ACK; } else if (kbd_kcmd == KBD_KCMD_ECHO) { kbd_kresp = 0x0; rc = KBD_KRESP_ECHO; } else { kbd_kresp = 0x0; rc = KBD_KRESP_ACK; } kbd_kcmd = 0x00; if (config.sim.verbose) PRINTF("kbd_read8(%"PRIxADDR") %lx\n", addr, rc); return rc; } else if (kbd_buf_count) { unsigned long c = kbd_buf[kbd_buf_tail]; kbd_buf_tail = (kbd_buf_tail + 1) % KBD_MAX_BUF; kbd_buf_count--; kbd_kresp = 0x0; if (config.sim.verbose) PRINTF("kbd_read8(%"PRIxADDR") %lx\n", addr, c); return c; } kbd_kresp = 0x0; if (config.sim.verbose) PRINTF("kbd_read8(%"PRIxADDR") fifo empty\n", addr); return 0; default: fprintf (stderr, "Read out of keyboard space (0x%"PRIxADDR")!\n", addr); runtime.sim.cont_run = 0; return 0; } } /* Simulation hook. Must be called every couple of clock cycles to simulate incomming data. */ void kbd_job(int param) { int c; int kbd_int = 0; /* Check if there is something waiting, and decode it into kdb_buf */ if((c = fgetc(kbd_rxfs)) != EOF) { scan_decode (c); } kbd_int = kbd_kresp || kbd_buf_count; kbd_int = kbd_kresp || kbd_buf_count ? kbd_ccmdbyte & KBD_CCMDBYTE_INT : 0; if (config.sim.verbose && kbd_int) PRINTF("Keyboard Interrupt.... kbd_kresp %lx kbd_buf_count %lx \n", kbd_kresp, kbd_buf_count); if (kbd_int) report_interrupt(config.kbd.irq); SCHED_ADD(kbd_job, 0, runtime.sim.cycles + kbd_slowdown); } /* Reset all (simulated) ps2 controlers/keyboards */ void kbd_reset () { if (config.kbd.enabled) { kbd_buf_count = 0; kbd_buf_head = 0; kbd_buf_tail = 0; kbd_kresp = 0x0; kbd_ccmdbyte = 0x65; /* We reset into default normal operation. */ register_memoryarea(config.kbd.baseaddr, KBD_SPACE, 1, 0, kbd_read8, kbd_write8, NULL); if (!(kbd_rxfs = fopen(config.kbd.rxfile, "r")) && !(kbd_rxfs = fopen(config.kbd.rxfile, "r+"))) { fprintf (stderr, "WARNING: Keyboard has problems with RX file stream.\n"); config.kbd.enabled = 0; } kbd_slowdown = (long) ((config.sim.system_kfreq * 1000.) / KBD_BAUD_RATE); if (kbd_slowdown <= 0) kbd_slowdown = 1; if (config.kbd.enabled) SCHED_ADD(kbd_job, 0, runtime.sim.cycles + kbd_slowdown); } } void kbd_info() { PRINTF("kbd_kcmd: %x\n", kbd_kcmd); PRINTF("kbd_ccmd: %x\n", kbd_ccmd); PRINTF("kbd_ccmdbyte: %x\n", kbd_ccmdbyte); PRINTF("kbd_kresp: %lx\n", kbd_kresp); PRINTF("kbd_buf_count: %lx\n", kbd_buf_count); } /*----------------------------------------------------[ KBD Configuration ]---*/ void kbd_enabled(union param_val val, void *dat) { config.kbd.enabled = val.int_val; } void kbd_baseaddr(union param_val val, void *dat) { config.kbd.baseaddr = val.addr_val; } void kbd_irq(union param_val val, void *dat) { config.kbd.irq = val.int_val; } void kbd_rxfile(union param_val val, void *dat) { strcpy(config.kbd.rxfile, val.str_val); } void reg_kbd_sec(void) { struct config_section *sec = reg_config_sec("kbd", NULL, NULL); reg_config_param(sec, "enabled", paramt_int, kbd_enabled); reg_config_param(sec, "baseaddr", paramt_int, kbd_baseaddr); reg_config_param(sec, "irq", paramt_int, kbd_irq); reg_config_param(sec, "rxfile", paramt_str, kbd_rxfile); }
Go to most recent revision | Compare with Previous | Blame | View Log