diff options
Diffstat (limited to 'qemu/roms/openhackware/src/char.c')
-rw-r--r-- | qemu/roms/openhackware/src/char.c | 780 |
1 files changed, 780 insertions, 0 deletions
diff --git a/qemu/roms/openhackware/src/char.c b/qemu/roms/openhackware/src/char.c new file mode 100644 index 000000000..4e74ac6af --- /dev/null +++ b/qemu/roms/openhackware/src/char.c @@ -0,0 +1,780 @@ +/* + * <char.c> + * + * Open Hack'Ware BIOS character devices drivers. + * + * Copyright (c) 2004-2005 Jocelyn Mayer + * + * cuda driver: Copyright (c) 2004-2005 Fabrice Bellard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License V2 + * as published by the Free Software Foundation + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdlib.h> +#include <stdio.h> +#include "bios.h" +#include "adb.h" + +//#define DEBUG_CHARDEV +//#define DEBUG_CUDA +//#define DEBUG_ADB + +#ifdef DEBUG_CHARDEV +#define CHARDEV_DPRINTF(fmt, args...) \ +do { dprintf("CHARDEV - %s: " fmt, __func__ , ##args); } while (0) +#else +#define CHARDEV_DPRINTF(fmt, args...) do { } while (0) +#endif + +/* Generic character device API */ +struct chardev_t { + chardev_t *next; + int type; + cops_t *ops; + void *private; +}; + +static chardev_t *char_devices; + +int chardev_register (int type, cops_t *ops, void *private) +{ + chardev_t *dev, **cur; + + CHARDEV_DPRINTF("Register char device of type %d\n", type); + if (type > CHARDEV_LAST) + return -1; + dev = malloc(sizeof(chardev_t)); + if (dev == NULL) + return -1; + dev->type = type; + dev->ops = ops; + dev->private = private; + for (cur = &char_devices; *cur != NULL; cur = &((*cur)->next)) + continue; + *cur = dev; + + return 0; +} + +int chardev_open (chardev_t *dev) +{ + if (dev->ops == NULL) + return -1; + if (dev->ops->open == NULL) + return 0; + + return (*dev->ops->open)(dev->private); +} + +int chardev_close (chardev_t *dev) +{ + if (dev->ops == NULL) + return -1; + if (dev->ops->close == NULL) + return 0; + + return (*dev->ops->close)(dev->private); +} + +int chardev_read (chardev_t *dev, void *buffer, int maxlen) +{ + unsigned char *p; + int len; + int c; + + if (dev->ops == NULL || dev->ops->read == NULL) + return -1; + + p = buffer; + for (len = 0; len < maxlen; len++) { + c = (*dev->ops->read)(dev->private); + if (c < 0) + break; + *p++ = c; + } + + return len; +} + +int chardev_write (chardev_t *dev, const void *buffer, int maxlen) +{ + const unsigned char *p; + int len; + int c; + + if (dev->ops == NULL || dev->ops->write == NULL) + return -1; + + p = buffer; + for (len = 0; len < maxlen; len++) { + c = *p++; + if ((*dev->ops->write)(dev->private, c) < 0) + break; + } + + return len; +} + +int chardev_type (chardev_t *dev) +{ + return dev->type; +} + +/* Console driver */ +static chardev_t *console_in_devs[17], *console_out_devs[17]; +static int console_last_in; + +int console_open (void) +{ + chardev_t *cur; + int i, j, n, register_outd; + + i = 0; + j = 0; + n = 0; + /* Check all character devices and register those which are usable + * as IO for the console + */ + CHARDEV_DPRINTF("enter\n"); + for (cur = char_devices; cur != NULL; cur = cur->next, n++) { + register_outd = 0; + switch (cur->type) { + case CHARDEV_SERIAL: + CHARDEV_DPRINTF("one serial port\n"); + register_outd = 1; + /* No break here */ + case CHARDEV_KBD: + CHARDEV_DPRINTF("one input port %d %d\n", n, i); + if (i < 16 && chardev_open(cur) == 0) { + console_in_devs[i++] = cur; + } + if (!register_outd) + break; + /* No break here */ + case CHARDEV_DISPLAY: + CHARDEV_DPRINTF("one output port %d %d\n", n, j); + if (j < 16 && chardev_open(cur) == 0) { + console_out_devs[j++] = cur; + } + break; + default: + CHARDEV_DPRINTF("Skip device %d\n", n); + break; + } + } + + return 0; +} + +int console_read (void *buffer, int maxlen) +{ + chardev_t *cur; + int i, in; + + CHARDEV_DPRINTF("enter\n"); + /* Get data from the first in device responding to us */ + cur = console_in_devs[console_last_in]; + for (i = console_last_in;;) { + CHARDEV_DPRINTF("read from device %d\n", i); + in = chardev_read(cur, buffer, maxlen); + if (in > 0) { + console_last_in = i; +#if 0 + printf("Read %d chars '%c'...\n", in, *((char *)buffer)); +#endif + return in; + } + cur = console_in_devs[++i]; + if (cur == NULL) { + i = 0; + cur = console_in_devs[0]; + } + if (i == console_last_in || cur == NULL) + break; + } + console_last_in = i; + CHARDEV_DPRINTF("out\n"); + + return 0; +} + +int console_write (const void *buffer, int len) +{ + chardev_t *cur; + int i, out, max; + + /* Write data to all devices */ + max = 0; + for (i = 0; i < 16; i++) { + cur = console_out_devs[i]; + if (cur == NULL) + break; + out = chardev_write(cur, buffer, len); + if (out > max) + max = out; + } + + return max; +} + +void console_close (void) +{ + chardev_t *cur; + int i; + + for (i = 0; i < 16; i++) { + cur = console_out_devs[i]; + if (cur == NULL) + break; + chardev_close(cur); + console_out_devs[i] = NULL; + } +} + +/* PC serial port "driver" */ +#define PC_SERIAL_LSR_OFFSET (5) +typedef struct pc_serial_t { + uint16_t base; +} pc_serial_t; + +static int pc_serial_open (unused void *private) +{ + return 0; +} + +static int pc_serial_writeb (void *private, int data) +{ + pc_serial_t *serial = private; + + /* Wait for the FIFO to be ready to accept more chars. + * Note: this is completely buggy and would never work on real hardware, + * as the serial port (especialy the FIFO) has not been programmed + * anywhere before ! + */ + if (!(inb(serial->base + PC_SERIAL_LSR_OFFSET) & 0x20)) + usleep(100); + outb(serial->base, data); + + return 0; +} + +static int pc_serial_readb (void *private) +{ + pc_serial_t *serial = private; + + if (!(inb(serial->base + PC_SERIAL_LSR_OFFSET) & 0x01)) + return -1; + + return inb(serial->base); +} + +static int pc_serial_close (unused void *private) +{ + return 0; +} + +static cops_t pc_serial_ops = { + .open = &pc_serial_open, + .read = &pc_serial_readb, + .write = &pc_serial_writeb, + .close = &pc_serial_close, +}; + +/* XXX: debug stuff only ! (TOFIX with a generic debug console) */ +int serial_write (const void *buffer, int len) +{ + const char *p; + + for (p = buffer; len > 0; len--) { + if (!(inb(0x3F8 + PC_SERIAL_LSR_OFFSET) & 0x20)) + usleep(100); + outb(0x3F8, *p++); + } + + return 0; +} + +int pc_serial_register (uint16_t base) +{ + pc_serial_t *serial; + + serial = malloc(sizeof(pc_serial_t)); + if (serial == NULL) + return -1; + serial->base = base; + /* XXX: TODO: initialize the serial port (FIFO, speed, ...) */ + + return chardev_register(CHARDEV_SERIAL, &pc_serial_ops, serial); +} + +/* VGA console device */ +static int vga_cons_open (unused void *private) +{ + return 0; +} + +static int vga_cons_writeb (unused void *private, int data) +{ + vga_putchar(data); + + return 0; +} + +static int vga_cons_close (unused void *private) +{ + return 0; +} + +static cops_t vga_cons_ops = { + .open = &vga_cons_open, + .read = NULL, + .write = &vga_cons_writeb, + .close = &vga_cons_close, +}; + +int vga_console_register (void) +{ + return chardev_register(CHARDEV_DISPLAY, &vga_cons_ops, NULL); +} + +/* Macintosh via-cuda driver */ +#ifdef DEBUG_CUDA +#define CUDA_DPRINTF(fmt, args...) \ +do { dprintf("CUDA - %s: " fmt, __func__ , ##args); } while (0) +#else +#define CUDA_DPRINTF(fmt, args...) do { } while (0) +#endif + +/* VIA registers - spaced 0x200 bytes apart */ +#define RS 0x200 /* skip between registers */ +#define B 0 /* B-side data */ +#define A RS /* A-side data */ +#define DIRB (2*RS) /* B-side direction (1=output) */ +#define DIRA (3*RS) /* A-side direction (1=output) */ +#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */ +#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */ +#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */ +#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */ +#define T2CL (8*RS) /* Timer 2 ctr/latch (low 8 bits) */ +#define T2CH (9*RS) /* Timer 2 counter (high 8 bits) */ +#define SR (10*RS) /* Shift register */ +#define ACR (11*RS) /* Auxiliary control register */ +#define PCR (12*RS) /* Peripheral control register */ +#define IFR (13*RS) /* Interrupt flag register */ +#define IER (14*RS) /* Interrupt enable register */ +#define ANH (15*RS) /* A-side data, no handshake */ + +/* Bits in B data register: all active low */ +#define TREQ 0x08 /* Transfer request (input) */ +#define TACK 0x10 /* Transfer acknowledge (output) */ +#define TIP 0x20 /* Transfer in progress (output) */ + +/* Bits in ACR */ +#define SR_CTRL 0x1c /* Shift register control bits */ +#define SR_EXT 0x0c /* Shift on external clock */ +#define SR_OUT 0x10 /* Shift out if 1 */ + +/* Bits in IFR and IER */ +#define IER_SET 0x80 /* set bits in IER */ +#define IER_CLR 0 /* clear bits in IER */ +#define SR_INT 0x04 /* Shift register full/empty */ + +#define CUDA_BUF_SIZE 16 + +#define ADB_PACKET 0 +#define CUDA_PACKET 1 + +struct cuda_t { + uint32_t base; + adb_bus_t *adb_bus; +}; + +static uint8_t cuda_readb (cuda_t *dev, int reg) +{ + return *(volatile uint8_t *)(dev->base + reg); +} + +static void cuda_writeb (cuda_t *dev, int reg, uint8_t val) +{ + *(volatile uint8_t *)(dev->base + reg) = val; +} + +static void cuda_wait_irq (cuda_t *dev) +{ + int val; + + CUDA_DPRINTF("\n"); + for(;;) { + val = cuda_readb(dev, IFR); + cuda_writeb(dev, IFR, val & 0x7f); + if (val & SR_INT) + break; + } +} + +static int cuda_request (cuda_t *dev, uint8_t pkt_type, const uint8_t *buf, + int buf_len, uint8_t *obuf) +{ + int i, obuf_len, val; + + cuda_writeb(dev, ACR, cuda_readb(dev, ACR) | SR_OUT); + cuda_writeb(dev, SR, pkt_type); + cuda_writeb(dev, B, cuda_readb(dev, B) & ~TIP); + if (buf) { + CUDA_DPRINTF("Send buf len: %d\n", buf_len); + /* send 'buf' */ + for(i = 0; i < buf_len; i++) { + cuda_wait_irq(dev); + cuda_writeb(dev, SR, buf[i]); + cuda_writeb(dev, B, cuda_readb(dev, B) ^ TACK); + } + } + cuda_wait_irq(dev); + cuda_writeb(dev, ACR, cuda_readb(dev, ACR) & ~SR_OUT); + cuda_readb(dev, SR); + cuda_writeb(dev, B, cuda_readb(dev, B) | TIP | TACK); + + obuf_len = 0; + if (obuf) { + cuda_wait_irq(dev); + cuda_readb(dev, SR); + cuda_writeb(dev, B, cuda_readb(dev, B) & ~TIP); + for(;;) { + cuda_wait_irq(dev); + val = cuda_readb(dev, SR); + if (obuf_len < CUDA_BUF_SIZE) + obuf[obuf_len++] = val; + if (cuda_readb(dev, B) & TREQ) + break; + cuda_writeb(dev, B, cuda_readb(dev, B) ^ TACK); + } + cuda_writeb(dev, B, cuda_readb(dev, B) | TIP | TACK); + + cuda_wait_irq(dev); + cuda_readb(dev, SR); + } + CUDA_DPRINTF("Got len: %d\n", obuf_len); + + return obuf_len; +} + +#if 0 +void cuda_test(void) +{ + int keycode; + printf("cuda test:\n"); + cuda_init(0x80400000 + 0x16000); + for(;;) { + keycode = adb_read_key(); + if (keycode >= 0) + printf("keycode=%x\n", keycode); + } +} +#endif + +/* Cuda ADB glue */ +static int cuda_adb_req (void *host, const uint8_t *snd_buf, int len, + uint8_t *rcv_buf) +{ + uint8_t buffer[CUDA_BUF_SIZE], *pos; + + CUDA_DPRINTF("len: %d %02x\n", len, snd_buf[0]); + len = cuda_request(host, ADB_PACKET, snd_buf, len, buffer); + if (len > 1 && buffer[0] == ADB_PACKET) { + pos = buffer + 2; + len -= 2; + } else { + pos = buffer + 1; + len = -1; + } + memcpy(rcv_buf, pos, len); + + return len; +} + +cuda_t *cuda_init (uint32_t base) +{ + cuda_t *cuda; + + CUDA_DPRINTF(" base=%08x\n", base); + cuda = malloc(sizeof(cuda_t)); + if (cuda == NULL) + return NULL; + cuda->base = base; + cuda_writeb(cuda, B, cuda_readb(cuda, B) | TREQ | TIP); +#if 0 + { + int len; + + /* enable auto poll */ + buf[0] = 0x01; + buf[1] = 1; + len = cuda_request(cuda, CUDA_PACKET, buf, 2, obuf); + if (len != 2 || obuf[0] != CUDA_PACKET || obuf[1] != 1) { + printf("cuda: invalid reply for auto poll request"); + free(cuda); + return NULL; + } + } +#endif + cuda->adb_bus = adb_bus_new(cuda, &cuda_adb_req); + if (cuda->adb_bus == NULL) { + free(cuda); + return NULL; + } + adb_bus_init(cuda->adb_bus); + + return cuda; +} + +void cuda_reset (cuda_t *cuda) +{ + adb_bus_reset(cuda->adb_bus); +} + +/* ADB generic driver */ +#ifdef DEBUG_ADB +#define ADB_DPRINTF(fmt, args...) \ +do { dprintf("ADB - %s: " fmt, __func__ , ##args); } while (0) +#else +#define ADB_DPRINTF(fmt, args...) do { } while (0) +#endif + +int adb_cmd (adb_dev_t *dev, uint8_t cmd, uint8_t reg, + uint8_t *buf, int len) +{ + uint8_t adb_send[ADB_BUF_SIZE], adb_rcv[ADB_BUF_SIZE]; + + ADB_DPRINTF("cmd: %d reg: %d len: %d\n", cmd, reg, len); + if (dev->bus == NULL || dev->bus->req == NULL) { + ADB_DPRINTF("ERROR: invalid bus !\n"); + bug(); + } + /* Sanity checks */ + if (cmd != ADB_LISTEN && len != 0) { + /* No buffer transmitted but for LISTEN command */ + ADB_DPRINTF("in buffer for cmd %d\n", cmd); + return -1; + } + if (cmd == ADB_LISTEN && ((len < 2 || len > 8) || buf == NULL)) { + /* Need a buffer with a regular register size for LISTEN command */ + ADB_DPRINTF("no/invalid buffer for ADB_LISTEN (%d)\n", len); + return -1; + } + if ((cmd == ADB_TALK || cmd == ADB_LISTEN) && reg > 3) { + /* Need a valid register number for LISTEN and TALK commands */ + ADB_DPRINTF("invalid reg for TALK/LISTEN command (%d %d)\n", cmd, reg); + return -1; + } + switch (cmd) { + case ADB_SEND_RESET: + adb_send[0] = ADB_SEND_RESET; + break; + case ADB_FLUSH: + adb_send[0] = (dev->addr << 4) | ADB_FLUSH; + break; + case ADB_LISTEN: + memcpy(adb_send + 1, buf, len); + /* No break here */ + case ADB_TALK: + adb_send[0] = (dev->addr << 4) | cmd | reg; + break; + } + memset(adb_rcv, 0, ADB_BUF_SIZE); + len = (*dev->bus->req)(dev->bus->host, adb_send, len + 1, adb_rcv); +#ifdef DEBUG_ADB + printf("%x %x %x %x\n", adb_rcv[0], adb_rcv[1], adb_rcv[2], adb_rcv[3]); +#endif + switch (len) { + case 0: + /* No data */ + break; + case 2 ... 8: + /* Register transmitted */ + if (buf != NULL) + memcpy(buf, adb_rcv, len); + break; + default: + /* Should never happen */ + ADB_DPRINTF("Cmd %d returned %d bytes !\n", cmd, len); + return -1; + } + ADB_DPRINTF("retlen: %d\n", len); + + return len; +} + +void adb_bus_reset (adb_bus_t *bus) +{ + adb_reset(bus); +} + +adb_bus_t *adb_bus_new (void *host, + int (*req)(void *host, const uint8_t *snd_buf, + int len, uint8_t *rcv_buf)) +{ + adb_bus_t *new; + + new = malloc(sizeof(adb_bus_t)); + if (new == NULL) + return NULL; + new->host = host; + new->req = req; + + return new; +} + +/* ADB */ +void *adb_kbd_new (void *private); + +static int adb_mouse_open (void *private); +static int adb_mouse_close (void *private); +static int adb_mouse_read (void *private); + +static cops_t adb_mouse_ops = { + &adb_mouse_open, + &adb_mouse_close, + &adb_mouse_read, + NULL, +}; + +/* Check and relocate all ADB devices as suggested in + * ADB_manager Apple documentation + */ +int adb_bus_init (adb_bus_t *bus) +{ + uint8_t buffer[ADB_BUF_SIZE]; + uint8_t adb_addresses[16] = + { 8, 9, 10, 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, -1, 0, }; + adb_dev_t tmp_device, **cur; + int address; + int reloc = 0, next_free = 7; + int keep; + + /* Reset the bus */ + ADB_DPRINTF("\n"); + adb_reset(bus); + cur = &bus->devices; + memset(&tmp_device, 0, sizeof(adb_dev_t)); + tmp_device.bus = bus; + for (address = 1; address < 8 && adb_addresses[reloc] > 0;) { + if (address == ADB_RES) { + /* Reserved */ + address++; + continue; + } + ADB_DPRINTF("Check device on ADB address %d\n", address); + tmp_device.addr = address; + switch (adb_reg_get(&tmp_device, 3, buffer)) { + case 0: + ADB_DPRINTF("No device on ADB address %d\n", address); + /* Register this address as free */ + if (adb_addresses[next_free] != 0) + adb_addresses[next_free++] = address; + /* Check next ADB address */ + address++; + break; + case 2: + /* One device answered : + * make it available and relocate it to a free address + */ + if (buffer[0] == ADB_CHADDR) { + /* device self test failed */ + ADB_DPRINTF("device on ADB address %d self-test failed " + "%02x %02x %02x\n", address, + buffer[0], buffer[1], buffer[2]); + keep = 0; + } else { + ADB_DPRINTF("device on ADB address %d self-test OK\n", + address); + keep = 1; + } + ADB_DPRINTF("Relocate device on ADB address %d to %d (%d)\n", + address, adb_addresses[reloc], reloc); + buffer[0] = ((buffer[0] & 0x40) & ~0x90) | adb_addresses[reloc]; + if (keep == 1) + buffer[0] |= 0x20; + buffer[1] = ADB_CHADDR_NOCOLL; + if (adb_reg_set(&tmp_device, 3, buffer, 2) < 0) { + ADB_DPRINTF("ADB device relocation failed\n"); + return -1; + } + if (keep == 1) { + *cur = malloc(sizeof(adb_dev_t)); + if (*cur == NULL) { + return -1; + } + (*cur)->type = address; + (*cur)->bus = bus; + (*cur)->addr = adb_addresses[reloc++]; + /* Flush buffers */ + adb_flush(*cur); + switch ((*cur)->type) { + case ADB_PROTECT: + ADB_DPRINTF("Found one protected device\n"); + break; + case ADB_KEYBD: + ADB_DPRINTF("Found one keyboard\n"); + adb_kbd_new(*cur); + break; + case ADB_MOUSE: + ADB_DPRINTF("Found one mouse\n"); + chardev_register(CHARDEV_MOUSE, &adb_mouse_ops, *cur); + break; + case ADB_ABS: + ADB_DPRINTF("Found one absolute positioning device\n"); + break; + case ADB_MODEM: + ADB_DPRINTF("Found one modem\n"); + break; + case ADB_RES: + ADB_DPRINTF("Found one ADB res device\n"); + break; + case ADB_MISC: + ADB_DPRINTF("Found one ADB misc device\n"); + break; + } + cur = &((*cur)->next); + } + break; + case 1: + case 3 ... 7: + /* SHOULD NOT HAPPEN : register 3 is always two bytes long */ + ADB_DPRINTF("Invalid returned len for ADB register 3\n"); + return -1; + case -1: + /* ADB ERROR */ + ADB_DPRINTF("error gettting ADB register 3\n"); + return -1; + } + } + + return 0; +} + +/* ADB mouse chardev interface (TODO) */ +static int adb_mouse_open (unused void *private) +{ + return 0; +} + +static int adb_mouse_close (unused void *private) +{ + return 0; +} + +static int adb_mouse_read (unused void *private) +{ + return -1; +} |