⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 serdos.c

📁 Libnet is a cross-platform library aimed at game developers. It has an abstract high level API, whic
💻 C
字号:
/*---------------------------------------------------------------- * serdos.c - low-level DOS serial routines *---------------------------------------------------------------- *  libnet is (c) Copyright Chad Catlett and George Foot 1997-1999 * *  Please look in `docs' for details, documentation and *  distribution conditions. */#include "platdefs.h"/* If we can use DOS serial ports, do so. */#ifdef __USE_REAL_SERIAL_DOS__#include <dos.h>#include <dpmi.h>#include <go32.h>#include <ctype.h>#include <stdlib.h>#include <string.h>#include <time.h>#include "serial.h"#include "serdos.h"/* We only support this many ports in this driver.  */#define MAX_PORT	4/* Port structure.  */struct port {    int baseaddr;     int irq, vector_num;    _go32_dpmi_seginfo old_vector;        _go32_dpmi_seginfo new_vector;    int pic_imr;                       /* interrupt mask register */    int pic_icr;                       /* interrupt control register */    int interrupt_enable_mask;         /* mask used to enable interrupt */    int fifo_enabled;        struct queue send;    struct queue recv;};/* Default configurations.  */static struct {    int baseaddr, irq, baudrate, bits, parity, stopbits;} config[MAX_PORT] = {    { -1, 4, BAUD_115200, BITS_8, PARITY_NONE, STOPBITS_1 },    { -1, 3, BAUD_115200, BITS_8, PARITY_NONE, STOPBITS_1 },    { -1, 4, BAUD_115200, BITS_8, PARITY_NONE, STOPBITS_1 },    { -1, 3, BAUD_115200, BITS_8, PARITY_NONE, STOPBITS_1 }};static int fallback_baseaddr[MAX_PORT] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };/* Port representations.  */static struct port port_0, port_1, port_2, port_3;static struct port *port_table[] = { &port_0, &port_1, &port_2, &port_3 };/* Wait for a bit.  */static void _delay(void){    unsigned int end = clock();    while (end == clock()) ;}/* Memory locking.  */static void _unlock_dpmi_data(void *addr, int size){    unsigned long baseaddr;    __dpmi_meminfo mem;    __dpmi_get_segment_base_address(_go32_my_ds(), &baseaddr);    mem.address = baseaddr + (unsigned long)addr;    mem.size = size;    __dpmi_unlock_linear_region(&mem);}#define END_OF_STATIC_FUNCTION(x)    	static void x##_end(void) { }#define LOCK_DATA(d,s)        		_go32_dpmi_lock_data((d), (s))#define LOCK_CODE(c,s)        		_go32_dpmi_lock_code((c), (s))#define LOCK_VARIABLE(x)      		LOCK_DATA((void *)&x, sizeof(x))#define LOCK_FUNCTION(x)      		LOCK_CODE(x, (long)x##_end - (long)x)/* * Enable and disable the THRE interrupt, which tells the ISR to send * characters to the serial port. */#define THREINT 0x02static inline void enable_thre_int(int baseaddr){    unsigned char ch = inportb(baseaddr + IER);    if (!(ch & THREINT))	outportb(baseaddr + IER, ch | THREINT);}END_OF_STATIC_FUNCTION(enable_thre_int);static inline void disable_thre_int(int baseaddr){    unsigned char ch = inportb(baseaddr + IER);    if (ch & THREINT)	outportb(baseaddr + IER, ch & ~THREINT);}END_OF_STATIC_FUNCTION(disable_thre_int);/* * Interrupt service routine (ISR). */static inline void isr(struct port *p){    int cause, len;    unsigned char ch;    disable();        /* Loop until all interrupts handled.  */    while (1) {	/* Only use lower 3 bits.  */	cause = inportb(p->baseaddr + IIR) & 0x07;	if (cause & 0x01)	    break;	switch (cause) {	    /* "OE, PE, FE or BI of the LSR set. 	     *  Serviced by reading the LSR." */	    case 0x06:		inportb(p->baseaddr + LSR);		break;	    /* "Receiver DR or trigger level reached. 	     *  Serviced by reading RBR until under level" */	    case 0x04:		ch = inportb(p->baseaddr + RBR);	    	if (!queue_full(p->recv))		    queue_put(p->recv, ch);	    	break;	    /* "THRE. Serviced by reading IIR (if source of int only!) 	     *  or writing to THR." */	    case 0x02:	    	/* If FIFO is enabled, we can blast up to 16 		 * bytes into THR at once. */	    	len = (p->fifo_enabled) ? 16 : 1;	    	    	while (!queue_empty(p->send) && (len--)) {		    queue_get(p->send, ch);		    outportb(p->baseaddr + THR, ch);		}	    	if (queue_empty(p->send))		    disable_thre_int(p->baseaddr);		break;	    /* "One of the delta flags in the MSR set. 	     *  Serviced by reading MSR." */	    case 0x00:		inportb(p->baseaddr + MSR);		break;	    default: 	}    }    /* End of interrupt.  */    outportb(0x20, 0x20);    if (p->irq > 7)	outportb(0xa0, 0x20);}END_OF_STATIC_FUNCTION(isr);/* * ISR wrappers. */static inline void isr(struct port *p);#define MAKE_ISR_WRAPPER(X)					\	static void isr_wrapper_##X(void) { isr(&port_##X); }   \     	END_OF_STATIC_FUNCTION(isr_wrapper_##X);MAKE_ISR_WRAPPER(0);MAKE_ISR_WRAPPER(1);MAKE_ISR_WRAPPER(2);MAKE_ISR_WRAPPER(3);static void (*isr_wrapper_table[])(void) = {    &isr_wrapper_0, &isr_wrapper_1,    &isr_wrapper_2, &isr_wrapper_3};/* * Write to port. */int __libnet_internal__serial_send(struct port *port,				   const unsigned char *buf, int size){    const unsigned char *p = buf;    while (!queue_full(port->send) && (size--))	queue_put(port->send, *p++);    enable_thre_int(port->baseaddr);    return p - buf;}static void flush_send_buffer(struct port *p){    while (!queue_empty(p->send)) {	enable_thre_int(p->baseaddr);	_delay();    }    disable_thre_int(p->baseaddr);}/* * Read from port. */int __libnet_internal__serial_read(struct port *port, unsigned char *buf, 				   int size){        unsigned char *p = buf;    while (!queue_empty(port->recv) && (size--))	queue_get(port->recv, *p++);    return p - buf;}/* * Init / shutdown driver. */int __libnet_internal__serial_init(void){    LOCK_FUNCTION(enable_thre_int);    LOCK_FUNCTION(disable_thre_int);    LOCK_FUNCTION(isr);    LOCK_FUNCTION(isr_wrapper_0);    LOCK_FUNCTION(isr_wrapper_1);    LOCK_FUNCTION(isr_wrapper_2);    LOCK_FUNCTION(isr_wrapper_3);    LOCK_VARIABLE(port_0);    LOCK_VARIABLE(port_1);    LOCK_VARIABLE(port_2);    LOCK_VARIABLE(port_3);    return 0;}int __libnet_internal__serial_exit(void) {    return 0; }/*  * Detect what type of UART exists at a particular address. * ONLY call this before the ISR is installed for that port. */static int detect_uart(unsigned int baseaddr){    int x, olddata, temp;    /* check if a UART is present */    olddata = inportb(baseaddr + MCR);    /* enable loopback mode, set RTS & DTR to 1 */    outportb(baseaddr + MCR, 0x1f);    _delay();    /* Read the state of RTS and DTR.      * Do this twice, so that lower 4 bits are clear.      * OS/2 returns 0xB0 after this, instead of 0xff if no port is there.     */    disable();    temp = inportb(baseaddr + MSR);    temp = inportb(baseaddr + MSR);    enable();    if ((temp & 0x3f) != 0x30)	return 0;    /* restore RTS & DTR */    outportb(baseaddr + MCR, olddata);    _delay();    /* next thing to do is look for the scratch register */    olddata = inportb(baseaddr + SCR);    outportb(baseaddr + SCR, 0x55);    _delay();    if (inportb(baseaddr + SCR) != 0x55)	return UART_8250;    outportb(baseaddr + SCR, 0xAA);    _delay();    if (inportb(baseaddr + SCR) != 0xAA)	return UART_8250;    /* restore it if it's there */    outportb(baseaddr + SCR, olddata);    _delay();    /* check if there's a FIFO */    outportb(baseaddr + IIR, 1);    _delay();    x = inportb(baseaddr + IIR);    /* some old-fashioned software relies on this! */    outportb(baseaddr + IIR, 0x0);    _delay();    if ((x & 0x80) == 0) return UART_16450;    if ((x & 0x40) == 0) return UART_16550;    return UART_16550A;}/* * Enable / disable FIFOs. */static void enable_fifo(struct port *p){    /* 0xC7: 14 byte trigger level */    /* 0x87: 08 byte trigger level */    outportb(p->baseaddr + FCR, 0x87);    p->fifo_enabled = 1;}static void disable_fifo(struct port *p){    outportb(p->baseaddr + FCR, 0);    p->fifo_enabled = 0;}/* * Install / uninstall ISRs. */static void install_isr(int portnum, struct port *p, int irq){    int x, baseaddr = p->baseaddr;        p->irq = irq;    /* Clear pending interrupts.  */    do {	inportb(baseaddr + RBR);	inportb(baseaddr + LSR);	inportb(baseaddr + MSR);	x = inportb(baseaddr + IIR);    } while (!(x & 0x01));      /* Calculate vector from IRQ.  */    if (p->irq <= 7) {	p->interrupt_enable_mask = ~(1 << p->irq);	/* IMR is inverted */	p->vector_num = 8 + p->irq;	p->pic_imr = 0x21;    }    else {	p->interrupt_enable_mask = ~(1 << (p->irq - 8));	p->vector_num = 0x70 + (p->irq - 8);	p->pic_imr = 0xa1;    }    /* Install ISR into vector.  */    p->new_vector.pm_selector = _go32_my_cs();    p->new_vector.pm_offset = (int)isr_wrapper_table[portnum];    _go32_dpmi_allocate_iret_wrapper(&p->new_vector);    disable();    _go32_dpmi_get_protected_mode_interrupt_vector(p->vector_num, &p->old_vector);    _go32_dpmi_set_protected_mode_interrupt_vector(p->vector_num, &p->new_vector);    /* Enable interrupts.  */    outportb(baseaddr + MCR, 0x0f);    outportb(baseaddr + IER, 0x0f);    outportb(p->pic_imr, inportb(p->pic_imr) & p->interrupt_enable_mask);        enable();}static void uninstall_isr(struct port *p){    /* Disable interrupts.  */    outportb(p->baseaddr + MCR, 0);    outportb(p->baseaddr + IER, 0);    outportb(p->pic_imr, inportb(p->pic_imr) | (~p->interrupt_enable_mask));    /* Restore old vector.  */    disable();    _go32_dpmi_set_protected_mode_interrupt_vector(p->vector_num, &p->old_vector);    _go32_dpmi_free_iret_wrapper(&p->new_vector);    enable();}static void set_config(int baseaddr, int baudrate, int config){    /* Turn on divisor latch registers.  */    outportb(baseaddr + LCR, DIV_LATCH_ON);    /* Send low and high bytes to divisor latches.  */    outportb(baseaddr + DLL, baudrate);    outportb(baseaddr + DLH, 0);    /* Set the configuration for the port.  */    outportb(baseaddr + LCR, config & 0x7F);}/* * Open and close port. */struct port *__libnet_internal__serial_open(int portnum){    struct port *p;    int uart, baudrate, cfg;        if ((portnum < 0) || (portnum >= MAX_PORT))	return NULL;    p = port_table[portnum];    memset(p, 0, sizeof *p);    if (config[portnum].baseaddr == -1) {	dosmemget(0x400 + portnum * 2, 2, &p->baseaddr);	if (p->baseaddr == 0)	    p->baseaddr = fallback_baseaddr[portnum];    }    else {	p->baseaddr = config[portnum].baseaddr;    }    uart = detect_uart(p->baseaddr);    if (!uart)	return NULL;    if (uart == UART_16550A)	enable_fifo(p);    else 	disable_fifo(p);    install_isr(portnum, p, config[portnum].irq);    set_config(p->baseaddr, config[portnum].baudrate, 	       (config[portnum].bits 		| config[portnum].parity 		| config[portnum].stopbits));    return p;}void __libnet_internal__serial_close(struct port *p){    flush_send_buffer(p);    if (p->fifo_enabled) 	disable_fifo(p);    uninstall_isr(p);}/* * Config stuff. */static int my_atoi(const char *s){    return (isdigit(*s)) ? atoi(s) : -1;}static int baudrate(int baud){    switch (baud) {	case 300:    return BAUD_300;	case 600:    return BAUD_600;	case 1200:   return BAUD_1200;	case 2400:   return BAUD_2400;	case 9600:   return BAUD_9600;	case 19200:  return BAUD_19200;	case 38400:  return BAUD_38400;	case 57600:  return BAUD_57600;	case 115200: return BAUD_115200;	default:     return -1;    }}static int bits(int b){    switch (b) {	case 5:  return BITS_5;	case 6:  return BITS_6;	case 7:  return BITS_7;	case 8:  return BITS_8;	default: return -1;    }}static int stopbits(int sb){    switch (sb) {	case 1:  return STOPBITS_1;	case 2:  return STOPBITS_2;	default: return -1;    }}static int parity(char *p){    if	    (!strcmp(p, "none")) return PARITY_NONE;    else if (!strcmp(p, "odd"))  return PARITY_ODD;    else if (!strcmp(p, "even")) return PARITY_EVEN;    else return -1;}static int baseaddr(int x) { return x; }static int irq(int x) { return x; }void __libnet_internal__serial_load_config(int portnum, char *option, char *value){    int x;        #define SET(type, value)				\	do {						\	    x = type(value);				\	    if (x != -1) config[portnum].type = x;      \	} while (0)     if      (strcmp(option, "baudrate") == 0)  SET(baudrate,	atoi(value));    else if (strcmp(option, "bits")	== 0)  SET(bits,	atoi(value));    else if (strcmp(option, "stopbits") == 0)  SET(stopbits,	atoi(value));    else if (strcmp(option, "parity")	== 0)  SET(parity,	value);    else if (strcmp(option, "baseaddr") == 0)  SET(baseaddr,	my_atoi(value));    else if (strcmp(option, "irq")	== 0)  SET(irq,		my_atoi(value));}#endif /* __USE_REAL_SERIAL_DOS__ *//* *  Originally based on SK 0.7d, which was based on Andre' LaMothe's *  routines from the book `Tricks of the Game Programming Gurus'. * *  A lot of code and ideas from DZComm, by Dim Zegebart. * *  Technical info and sample code from `The Serial Port' by Chris Blum. * *  Memory locking stuff from Allegro by Shawn Hargreaves. *  *  Modifications to detect_uart by Ralph Deane. * *  Bug causing a crash when receiving too many data fixed by Ben Davis. */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -