/* DANG_BEGIN_MODULE * * REMARK * ser_init.c: Serial ports initialization for DOSEMU * Please read the README.serial file in this directory for more info! * * Lock file stuff was derived from Taylor UUCP with these copyrights: * Copyright (C) 1991, 1992 Ian Lance Taylor * Uri Blumenthal <uri@watson.ibm.com> (C) 1994 * Paul Cadach, <paul@paul.east.alma-ata.su> (C) 1994 * * Rest of serial code Copyright (C) 1995 by Mark Rejhon * * The code in this module 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 module is maintained by Mark Rejhon at these Email addresses: * marky@magmacom.com * ag115@freenet.carleton.ca * /REMARK * * maintainer: * Mark Rejhon <marky@ottawa.com> * * DANG_END_MODULE */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/time.h> #include <errno.h> #include <assert.h> #include "emu.h" #include "sig.h" #include "port.h" #include "bios.h" #include "pic.h" #include "serial.h" #include "ser_defs.h" #include "tty_io.h" #include "sermouse.h" #include "utilities.h" /* due to getpwnam */ #include "iodev.h" int no_local_video = 0; serial_t com_cfg[MAX_SER]; com_t com[MAX_SER]; static u_char irq_source_num[255]; /* Index to map from IRQ no. to serial port */ struct ser_dmx { ioport_t port; Bit8u def_val; int use_cnt; char name[16]; }; #define DMX_MAX 4 static struct ser_dmx dmxs[DMX_MAX]; static int num_dmxs; static void add_dmx(ioport_t port, int val) { int i; Bit8u dval = val - 1; for (i = 0; i < num_dmxs; i++) { if (dmxs[i].port == port) { if (dmxs[i].def_val != dval) { error("SER: inconsistent config for demux on port %#x\n", port); return; } dmxs[i].use_cnt++; return; } } num_dmxs++; assert(num_dmxs <= DMX_MAX); dmxs[i].port = port; dmxs[i].def_val = dval; dmxs[i].use_cnt = 1; sprintf(dmxs[i].name, "ser_dmx_%hhi", (uint8_t)i); } static Bit8u dmx_readb(ioport_t port) { int num, i; Bit8u val; for (i = 0; i < num_dmxs; i++) { if (dmxs[i].port == port) break; } assert(i < num_dmxs); val = dmxs[i].def_val; for (num = 0; num < config.num_ser; num++) { if (com_cfg[num].dmx_port == port && (com[num].int_condition & com_cfg[num].dmx_mask)) { if (com_cfg[num].dmx_val) val |= 1 << com_cfg[num].dmx_shift; else val &= ~(1 << com_cfg[num].dmx_shift); } } s_printf("SER: read demux at port %#x=%#x\n", dmxs[i].port, val); return val; } static void dmx_writeb(ioport_t port, Bit8u value) { s_printf("SER: write to readonly port %#x, val=%#x\n", port, value); } static int init_dmxs(void) { emu_iodev_t io_device; int i; for (i = 0; i < num_dmxs; i++) { io_device.read_portb = dmx_readb; io_device.write_portb = dmx_writeb; io_device.read_portw = NULL; io_device.write_portw = NULL; io_device.read_portd = NULL; io_device.write_portd = NULL; io_device.start_addr = dmxs[i].port; io_device.end_addr = dmxs[i].port; io_device.irq = EMU_NO_IRQ; io_device.fd = -1; io_device.handler_name = dmxs[i].name; port_register_handler(io_device, 0); s_printf("SER: added demux at port %#x\n", dmxs[i].port); } return i; } static void ser_reset_dev(int num) { com[num].dll = 0x30; /* Baudrate divisor LSB: 2400bps */ com[num].dlm = 0; /* Baudrate divisor MSB: 2400bps */ com[num].IER = 0; /* Interrupt Enable Register */ com[num].IIR.mask = 0; /* Interrupt I.D. Register */ com[num].LCR = UART_LCR_WLEN8; /* Line Control Register: 5N1 */ com[num].FCReg = 0; /* FIFO Control Register */ com[num].rx_fifo_trigger = 1; /* Receive FIFO trigger level */ com[num].MCR = 0; /* Modem Control Register */ com[num].LSR = UART_LSR_TEMT | UART_LSR_THRE; /* Txmit Hold Reg Empty */ com[num].MSR = 0; /* Modem Status Register */ com[num].SCR = 0; /* Scratch Register */ com[num].int_condition = TX_INTR; /* FLAG: Pending xmit intr */ com[num].IIR.flags = 0; /* FLAG: FIFO disabled */ com[num].ms_timer = 0; /* Modem Status check timer */ com[num].rx_timer = 0; /* Receive read() polling timer */ com[num].rx_timeout = 0; /* FLAG: No Receive timeout */ com[num].rx_fifo_size = 16; /* Size of receive FIFO to emulate */ com[num].tx_cnt = 0; uart_clear_fifo(num,UART_FCR_CLEAR_CMD); /* Initialize FIFOs */ } static void ser_setup_custom(int num) { int i, cnt; switch (com_cfg[num].custom) { case SER_CUSTOM_NONE: break; case SER_CUSTOM_PCCOM: s_printf("SER%d: setting up PCCOM config\n", num); cnt = 0; for (i = 0; i < num; i++) { if (com_cfg[i].custom == SER_CUSTOM_PCCOM) cnt++; } com_cfg[num].dmx_port = (cnt < 4 ? 0x2bf : 0x1bf); com_cfg[num].dmx_mask = RX_INTR; com_cfg[num].dmx_shift = cnt; com_cfg[num].dmx_val = 0; if (!com_cfg[num].base_port) com_cfg[num].base_port = (cnt < 4 ? 0x2a0 : 0x1a0) + cnt * 8; com_cfg[num].end_port = com_cfg[num].base_port + 6; if (!com_cfg[num].irq) com_cfg[num].irq = (cnt < 4 ? 5 : 7); break; } } static Bit8u com_readb(ioport_t port) { int tmp; for (tmp = 0; tmp < config.num_ser; tmp++) { if (((u_short)(port & ~7)) == com_cfg[tmp].base_port) { return(do_serial_in(tmp, (int)port)); } } return 0; } static void com_writeb(ioport_t port, Bit8u value) { int tmp; for (tmp = 0; tmp < config.num_ser; tmp++) { if (((u_short)(port & ~7)) == com_cfg[tmp].base_port) { do_serial_out(tmp, (int)port, (int)value); } } } /* The following function is the main initialization routine that * initializes the UART for ONE serial port. This includes setting up * the environment, define default variables, the emulated UART's init * stat, and open/initialize the serial line. [num = port] */ static void do_ser_init(int num) { emu_iodev_t io_device; /* The following section sets up default com port, interrupt, base ** port address, and device path if they are undefined. The defaults are: ** ** COM1: irq = 4 base_port = 0x3F8 device = /dev/ttyS0 ** COM2: irq = 3 base_port = 0x2F8 device = /dev/ttyS1 ** COM3: irq = 4 base_port = 0x3E8 device = /dev/ttyS2 ** COM4: irq = 3 base_port = 0x2E8 device = /dev/ttyS3 */ static struct { int irq; ioport_t base_port; const char *dev; const char *handler_name; } default_com[MAX_SER] = { { 4, 0x3F8, "/dev/ttyS0", "COM1" }, { 3, 0x2F8, "/dev/ttyS1", "COM2" }, { 4, 0x3E8, "/dev/ttyS2", "COM3" }, { 3, 0x2E8, "/dev/ttyS3", "COM4" }, { 3, 0x4220, "/dev/ttyS4", "COM5" }, { 3, 0x4228, "/dev/ttyS5", "COM6" }, { 3, 0x5220, "/dev/ttyS6", "COM7" }, { 3, 0x5228, "/dev/ttyS7", "COM8" }, { 4, 0x6220, "/dev/ttyS8", "COM9" }, { 4, 0x6228, "/dev/ttyS9", "COM10" }, { 4, 0x7220, "/dev/ttyS10", "COM11" }, { 4, 0x7228, "/dev/ttyS11", "COM12" }, { 4, 0x8220, "/dev/ttyS12", "COM13" }, { 4, 0x8228, "/dev/ttyS13", "COM14" }, { 4, 0x9220, "/dev/ttyS14", "COM15" }, { 4, 0x9228, "/dev/ttyS15", "COM16" }, }; ser_setup_custom(num); if (com_cfg[num].real_comport == 0) { /* Is comport number undef? */ error("SER%d: No COMx port number given\n", num); config.exitearly = 1; return; } if (com_cfg[num].irq == 0) { /* Is interrupt undefined? */ /* Define it depending on using standard irqs */ com_cfg[num].irq = default_com[com_cfg[num].real_comport-1].irq; } com[num].interrupt = pic_irq_list[com_cfg[num].irq]; if (com_cfg[num].base_port == 0) { /* Is base port undefined? */ /* Define it depending on using standard addrs */ com_cfg[num].base_port = default_com[com_cfg[num].real_comport-1].base_port; } if (com_cfg[num].end_port == 0) com_cfg[num].end_port = com_cfg[num].base_port + 7; #ifdef USE_MODEMU if (com_cfg[num].vmodem) com_cfg[num].dev = modemu_init(num); #endif /* Is the device file undef? */ if ((!com_cfg[num].dev || !com_cfg[num].dev[0]) && !com_cfg[num].mouse) { /* Define it using std devs */ com_cfg[num].dev = strdup(default_com[com_cfg[num].real_comport-1].dev); } if (com_cfg[num].dev && com_cfg[num].dev[0]) iodev_add_device(com_cfg[num].dev); /* FOSSIL emulation is inactive at startup. */ com[num].fossil_active = FALSE; /* convert irq number to pic_ilevel number and set up interrupt * if irq is invalid, no interrupt will be assigned */ if(!irq_source_num[com_cfg[num].irq]) { s_printf("SER%d: enabling interrupt %d\n", num, com[num].interrupt); pic_seti(com[num].interrupt, pic_serial_run, 0, NULL); } irq_source_num[com_cfg[num].irq]++; /*** The following is where the real initialization begins ***/ /* Tell the port manager that we exist and that we're alive */ io_device.read_portb = com_readb; io_device.write_portb = com_writeb; io_device.read_portw = NULL; io_device.write_portw = NULL; io_device.read_portd = NULL; io_device.write_portd = NULL; io_device.start_addr = com_cfg[num].base_port; io_device.end_addr = com_cfg[num].end_port; io_device.irq = (irq_source_num[com_cfg[num].irq] == 1 ? com_cfg[num].irq : EMU_NO_IRQ); io_device.fd = -1; io_device.handler_name = default_com[num].handler_name; port_register_handler(io_device, 0); /* Information about serial port added to debug file */ s_printf("SER%d: COM%d, intlevel=%d, base=0x%x, device=%s\n", num, com_cfg[num].real_comport, com[num].interrupt, com_cfg[num].base_port, com_cfg[num].dev); #if 0 /* first call to serial timer update func to initialize the timer */ /* value, before the com[num] structure is initialized */ serial_timer_update(); #endif /* Set file descriptor as unused, then attempt to open serial port */ com[num].fd = -1; if (com_cfg[num].dmx_port) add_dmx(com_cfg[num].dmx_port, com_cfg[num].dmx_val); } void serial_reset(void) { int num; for (num = 0; num < config.num_ser; num++) ser_reset_dev(num); fossil_initialised = FALSE; } /* DANG_BEGIN_FUNCTION serial_run * * This is the main housekeeping function, which should be called about * 20 to 100 times per second. The more frequent, the better, up to * a certain point. However, it should be self-compensating if it * executes 10 times or even 1000 times per second. Serial performance * increases with frequency of execution of serial_run. * * Serial mouse performance becomes more smooth if the time between * calls to serial_run are smaller. * * DANG_END_FUNCTION */ static void serial_run(void) { int i; #if 0 /* Update the internal serial timers */ serial_timer_update(); #endif /* Do the necessary interrupt checksing in a logically efficient manner. * All the engines have built-in code to prevent loading the * system if they are called 100x's per second. */ for (i = 0; i < config.num_ser; i++) { if (com[i].opened <= 0) continue; serial_update(i); } } /* DANG_BEGIN_FUNCTION serial_init * * This is the master serial initialization function that is called * upon startup of DOSEMU to initialize ALL the emulated UARTs for * all configured serial ports. The UART is initialized via the * initialize_uart function, which opens the serial ports and defines * variables for the specific UART. * * If the port is a mouse, the port is only initialized when i * * DANG_END_FUNCTION */ void serial_init(void) { int i; warn("SERIAL $Id$\n"); s_printf("SER: Running serial_init, %d serial ports\n", config.num_ser); /* Do UART init here - Need to set up registers and init the lines. */ for (i = 0; i < config.num_ser; i++) { com[i].num = i; com[i].cfg = &com_cfg[i]; com[i].fd = -1; com[i].opened = 0; com[i].dev_locked = FALSE; com[i].drv = com_cfg[i].mouse ? &serm_drv : &tty_drv; /* Serial port init is skipped if the port is used for a mouse, and * dosemu is running in Xwindows, or not at the console. This is due * to the fact the mouse is in use by Xwindows (internal driver is used) * Direct access to the mouse by dosemu is useful mainly at the console. */ do_ser_init(i); } init_dmxs(); sigalrm_register_handler(serial_run); } /* Like serial_init, this is the master function that is called externally, * but at the end, when the user quits DOSEMU. It deinitializes all the * configured serial ports. */ void serial_close(void) { int i; s_printf("SER: Running serial_close\n"); for (i = 0; i < config.num_ser; i++) { if (com[i].opened <= 0) continue; #ifdef USE_MODEMU if (com_cfg[i].vmodem) modemu_done(i); #endif ser_close(i); } } #define SER_FN0(rt, f) \ rt f(int num) \ { \ return com[num].drv->f(&com[num]); \ } #define SER_FN1(rt, f, p, c) \ rt f(int num, p c) \ { \ return com[num].drv->f(&com[num], c); \ } #define SER_FN2(rt, f, p1, c1, p2, c2) \ rt f(int num, p1 c1, p2 c2) \ { \ return com[num].drv->f(&com[num], c1, c2); \ } SER_FN0(void, rx_buffer_dump) SER_FN0(void, tx_buffer_dump) SER_FN0(int, serial_get_tx_queued) SER_FN0(void, ser_termios) SER_FN1(int, serial_brkctl, int, brkflg) SER_FN2(ssize_t, serial_write, char*, buf, size_t, len) SER_FN1(int, serial_dtr, int, flag) SER_FN1(int, serial_rts, int, flag) SER_FN0(int, ser_open) SER_FN0(int, ser_close) SER_FN0(int, uart_fill) SER_FN0(int, serial_get_msr)