📄 rs232.c
字号:
#include <minix/config.h>/*---------------------------------------------------------------------------* * rs232.c - serial driver for 8250 and 16450 UARTs * * Added support for Atari ST M68901 and YM-2149 --kub * *---------------------------------------------------------------------------*/#include "../drivers.h"#include <termios.h>#include <signal.h>#include "tty.h"#if NR_RS_LINES > 0#if (MACHINE != IBM_PC) && (MACHINE != ATARI)#error /* rs232.c only supports PC and Atari ST */#endif#if (MACHINE == ATARI)#include "staddr.h"#include "stsound.h"#include "stmfp.h"#if (NR_RS_LINES > 1)#error /* Only one physical RS232 line available */#endif#endif#if (MACHINE == IBM_PC) /* PC/AT 8250/16450 chip combination *//* 8250 constants. */#define UART_FREQ 115200L /* timer frequency *//* Interrupt enable bits. */#define IE_RECEIVER_READY 1#define IE_TRANSMITTER_READY 2#define IE_LINE_STATUS_CHANGE 4#define IE_MODEM_STATUS_CHANGE 8/* Interrupt status bits. */#define IS_MODEM_STATUS_CHANGE 0#define IS_TRANSMITTER_READY 2#define IS_RECEIVER_READY 4#define IS_LINE_STATUS_CHANGE 6/* Line control bits. */#define LC_2STOP_BITS 0x04#define LC_PARITY 0x08#define LC_PAREVEN 0x10#define LC_BREAK 0x40#define LC_ADDRESS_DIVISOR 0x80/* Line status bits. */#define LS_OVERRUN_ERR 2#define LS_PARITY_ERR 4#define LS_FRAMING_ERR 8#define LS_BREAK_INTERRUPT 0x10#define LS_TRANSMITTER_READY 0x20/* Modem control bits. */#define MC_DTR 1#define MC_RTS 2#define MC_OUT2 8 /* required for PC & AT interrupts *//* Modem status bits. */#define MS_CTS 0x10#define MS_RLSD 0x80 /* Received Line Signal Detect */#define MS_DRLSD 0x08 /* RLSD Delta */#else /* MACHINE == ATARI */ /* Atari ST 68901 USART *//* Most of the USART constants are already defined in stmfp.h . The local * definitions made here are for keeping C code changes smaller. --kub */#define UART_FREQ 19200L /* timer frequency *//* Line status bits. */#define LS_OVERRUN_ERR R_OE#define LS_PARITY_ERR R_PE#define LS_FRAMING_ERR R_FE#define LS_BREAK_INTERRUPT R_BREAK/* Modem status bits. */#define MS_CTS IO_SCTS /* 0x04 */#endif /* MACHINE == ATARI */#define DATA_BITS_SHIFT 8 /* amount data bits shifted in mode */#define DEF_BAUD 1200 /* default baud rate */#define RS_IBUFSIZE 1024 /* RS232 input buffer size */#define RS_OBUFSIZE 1024 /* RS232 output buffer size *//* Input buffer watermarks. * The external device is asked to stop sending when the buffer * exactly reaches high water, or when TTY requests it. Sending restarts * when the input buffer empties below the low watermark. */#define RS_ILOWWATER (1 * RS_IBUFSIZE / 4)#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)/* Output buffer low watermark. * TTY is notified when the output buffer empties below the low watermark, so * it may continue filling the buffer if doing a large write. */#define RS_OLOWWATER (1 * RS_OBUFSIZE / 4)#if (MACHINE == IBM_PC)/* Macros to handle flow control. * Interrupts must be off when they are used. * Time is critical - already the function call for outb() is annoying. * If outb() can be done in-line, tests to avoid it can be dropped. * istart() tells external device we are ready by raising RTS. * istop() tells external device we are not ready by dropping RTS. * DTR is kept high all the time (it probably should be raised by open and * dropped by close of the device). * OUT2 is also kept high all the time. */#define istart(rs) \ (sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_RTS | MC_DTR), \ (rs)->idevready = TRUE)#define istop(rs) \ (sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_DTR), \ (rs)->idevready = FALSE)/* Macro to tell if device is ready. The rs->cts field is set to MS_CTS if * CLOCAL is in effect for a line without a CTS wire. */#define devready(rs) ((my_inb(rs->modem_status_port) | rs->cts) & MS_CTS)/* Macro to tell if transmitter is ready. */#define txready(rs) (my_inb(rs->line_status_port) & LS_TRANSMITTER_READY)/* Macro to tell if carrier has dropped. * The RS232 Carrier Detect (CD) line is usually connected to the 8250 * Received Line Signal Detect pin, reflected by bit MS_RLSD in the Modem * Status Register. The MS_DRLSD bit tells if MS_RLSD has just changed state. * So if MS_DRLSD is set and MS_RLSD cleared, we know that carrier has just * dropped. */#define devhup(rs) \ ((my_inb(rs->modem_status_port) & (MS_RLSD|MS_DRLSD)) == MS_DRLSD)#else /* MACHINE == ATARI *//* Macros to handle flow control. * Time is critical - already the function call for lock()/restore() is * annoying. * istart() tells external device we are ready by raising RTS. * istop() tells external device we are not ready by dropping RTS. * DTR is kept high all the time (it probably should be raised by open and * dropped by close of the device). NOTE: The modem lines are active low. */#define set_porta(msk,val) { register int s = lock(); \ SOUND->sd_selr = YM_IOA; \ SOUND->sd_wdat = \ SOUND->sd_rdat & (msk) | (val); \ restore(s); }#define istart(rs) { set_porta( ~(PA_SRTS|PA_SDTR),0 ); \ (rs)->idevready = TRUE; }#define istop(rs) { set_porta( ~PA_SDTR, PA_SRTS ); \ (rs)->idevready = FALSE; }/* Macro to tell if device is ready. The rs->cts field is set to MS_CTS if * CLOCAL is in effect for a line without a CTS wire. */#define devready(rs) ((~MFP->mf_gpip | rs->cts) & MS_CTS)/* Transmitter ready test */#define txready(rs) (MFP->mf_tsr & (T_EMPTY | T_UE))#endif /* MACHINE == ATARI *//* Types. */typedef unsigned char bool_t; /* boolean *//* RS232 device structure, one per device. */typedef struct rs232 { tty_t *tty; /* associated TTY structure */ int icount; /* number of bytes in the input buffer */ char *ihead; /* next free spot in input buffer */ char *itail; /* first byte to give to TTY */ bool_t idevready; /* nonzero if we are ready to receive (RTS) */ char cts; /* normally 0, but MS_CTS if CLOCAL is set */ unsigned char ostate; /* combination of flags: */#define ODONE 1 /* output completed (< output enable bits) */#define ORAW 2 /* raw mode for xoff disable (< enab. bits) */#define OWAKEUP 4 /* tty_wakeup() pending (asm code only) */#define ODEVREADY MS_CTS /* external device hardware ready (CTS) */#define OQUEUED 0x20 /* output buffer not empty */#define OSWREADY 0x40 /* external device software ready (no xoff) */#define ODEVHUP MS_RLSD /* external device has dropped carrier */#define OSOFTBITS (ODONE | ORAW | OWAKEUP | OQUEUED | OSWREADY) /* user-defined bits */#if (OSOFTBITS | ODEVREADY | ODEVHUP) == OSOFTBITS /* a weak sanity check */#error /* bits are not unique */#endif unsigned char oxoff; /* char to stop output */ bool_t inhibited; /* output inhibited? (follows tty_inhibited) */ bool_t drain; /* if set drain output and reconfigure line */ int ocount; /* number of bytes in the output buffer */ char *ohead; /* next free spot in output buffer */ char *otail; /* next char to output */#if (MACHINE == IBM_PC) port_t xmit_port; /* i/o ports */ port_t recv_port; port_t div_low_port; port_t div_hi_port; port_t int_enab_port; port_t int_id_port; port_t line_ctl_port; port_t modem_ctl_port; port_t line_status_port; port_t modem_status_port;#endif unsigned char lstatus; /* last line status */ unsigned char pad; /* ensure alignment for 16-bit ints */ unsigned framing_errors; /* error counts (no reporting yet) */ unsigned overrun_errors; unsigned parity_errors; unsigned break_interrupts; int irq; /* irq for this line */ int irq_hook_id; /* interrupt hook */ char ibuf[RS_IBUFSIZE]; /* input buffer */ char obuf[RS_OBUFSIZE]; /* output buffer */} rs232_t;PUBLIC rs232_t rs_lines[NR_RS_LINES];/* Table and macro to translate an RS232 line number to its rs_lines entry. */PRIVATE rs232_t *p_rs_addr[NR_RS_LINES];#define rs_addr(line) (p_rs_addr[line])#if (MACHINE == IBM_PC)/* 8250 base addresses. */PRIVATE port_t addr_8250[] = { 0x3F8, /* COM1 */ 0x2F8, /* COM2 */ 0x3E8, /* COM3 */ 0x2E8, /* COM4 */};#endifFORWARD _PROTOTYPE( void in_int, (rs232_t *rs) );FORWARD _PROTOTYPE( void line_int, (rs232_t *rs) );FORWARD _PROTOTYPE( void modem_int, (rs232_t *rs) );FORWARD _PROTOTYPE( int rs_write, (tty_t *tp, int try) );FORWARD _PROTOTYPE( void rs_echo, (tty_t *tp, int c) );FORWARD _PROTOTYPE( int rs_ioctl, (tty_t *tp, int try) );FORWARD _PROTOTYPE( void rs_config, (rs232_t *rs) );FORWARD _PROTOTYPE( int rs_read, (tty_t *tp, int try) );FORWARD _PROTOTYPE( int rs_icancel, (tty_t *tp, int try) );FORWARD _PROTOTYPE( int rs_ocancel, (tty_t *tp, int try) );FORWARD _PROTOTYPE( void rs_ostart, (rs232_t *rs) );FORWARD _PROTOTYPE( int rs_break, (tty_t *tp, int try) );FORWARD _PROTOTYPE( int rs_close, (tty_t *tp, int try) );FORWARD _PROTOTYPE( void out_int, (rs232_t *rs) );FORWARD _PROTOTYPE( void rs232_handler, (rs232_t *rs) );/* XXX */PRIVATE void lock(void) {}PRIVATE void unlock(void) {}PRIVATE int my_inb(port_t port){ int r, v = 0; r = sys_inb(port, &v); if (r != OK) printf("RS232 warning: failed inb 0x%x\n", port); return v;}/*===========================================================================* * rs_write * *===========================================================================*/PRIVATE int rs_write(tp, try)register tty_t *tp;int try;{/* (*devwrite)() routine for RS232. */ rs232_t *rs = tp->tty_priv; int count, ocount; if (rs->inhibited != tp->tty_inhibited) { /* Inhibition state has changed. */ lock(); rs->ostate |= OSWREADY; if (tp->tty_inhibited) rs->ostate &= ~OSWREADY; unlock(); rs->inhibited = tp->tty_inhibited; } if (rs->drain) { /* Wait for the line to drain then reconfigure and continue output. */ if (rs->ocount > 0) return 0; rs->drain = FALSE; rs_config(rs); } /* While there is something to do. */ for (;;) { ocount = buflen(rs->obuf) - rs->ocount; count = bufend(rs->obuf) - rs->ohead; if (count > ocount) count = ocount; if (count > tp->tty_outleft) count = tp->tty_outleft; if (count == 0 || tp->tty_inhibited) { if (try) return 0; break; } if (try) return 1; /* Copy from user space to the RS232 output buffer. */ sys_vircopy(tp->tty_outproc, D, (vir_bytes) tp->tty_out_vir, SELF, D, (vir_bytes) rs->ohead, (phys_bytes) count); /* Perform output processing on the output buffer. */ out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount); if (count == 0) break; /* Assume echoing messed up by output. */ tp->tty_reprint = TRUE; /* Bookkeeping. */ lock(); /* protect interrupt sensitive rs->ocount */ rs->ocount += ocount; rs_ostart(rs); unlock(); if ((rs->ohead += ocount) >= bufend(rs->obuf)) rs->ohead -= buflen(rs->obuf); tp->tty_out_vir += count; tp->tty_outcum += count; if ((tp->tty_outleft -= count) == 0) { /* Output is finished, reply to the writer. */ tty_reply(tp->tty_outrepcode, tp->tty_outcaller, tp->tty_outproc, tp->tty_outcum); tp->tty_outcum = 0; } } if (tp->tty_outleft > 0 && tp->tty_termios.c_ospeed == B0) { /* Oops, the line has hung up. */ tty_reply(tp->tty_outrepcode, tp->tty_outcaller, tp->tty_outproc, EIO); tp->tty_outleft = tp->tty_outcum = 0; } return 1;}/*===========================================================================* * rs_echo * *===========================================================================*/PRIVATE void rs_echo(tp, c)tty_t *tp; /* which TTY */int c; /* character to echo */{/* Echo one character. (Like rs_write, but only one character, optionally.) */ rs232_t *rs = tp->tty_priv; int count, ocount; ocount = buflen(rs->obuf) - rs->ocount; if (ocount == 0) return; /* output buffer full */ count = 1; *rs->ohead = c; /* add one character */ out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount); if (count == 0) return; lock(); rs->ocount += ocount; rs_ostart(rs); unlock(); if ((rs->ohead += ocount) >= bufend(rs->obuf)) rs->ohead -= buflen(rs->obuf);}/*===========================================================================* * rs_ioctl * *===========================================================================*/PRIVATE int rs_ioctl(tp, dummy)tty_t *tp; /* which TTY */int dummy;{/* Reconfigure the line as soon as the output has drained. */ rs232_t *rs = tp->tty_priv; rs->drain = TRUE; return 0; /* dummy */}/*===========================================================================* * rs_config * *===========================================================================*/PRIVATE void rs_config(rs)rs232_t *rs; /* which line */{/* Set various line control parameters for RS232 I/O. * If DataBits == 5 and StopBits == 2, 8250 will generate 1.5 stop bits. * The 8250 can't handle split speed, so we use the input speed. */ tty_t *tp = rs->tty; int divisor; int line_controls; static struct speed2divisor { speed_t speed; int divisor; } s2d[] = {#if (MACHINE == IBM_PC) { B50, UART_FREQ / 50 },#endif { B75, UART_FREQ / 75 }, { B110, UART_FREQ / 110 }, { B134, UART_FREQ * 10 / 1345 }, { B150, UART_FREQ / 150 }, { B200, UART_FREQ / 200 }, { B300, UART_FREQ / 300 }, { B600, UART_FREQ / 600 }, { B1200, UART_FREQ / 1200 },#if (MACHINE == IBM_PC) { B1800, UART_FREQ / 1800 },#endif { B2400, UART_FREQ / 2400 }, { B4800, UART_FREQ / 4800 }, { B9600, UART_FREQ / 9600 }, { B19200, UART_FREQ / 19200 },#if (MACHINE == IBM_PC) { B38400, UART_FREQ / 38400 }, { B57600, UART_FREQ / 57600 }, { B115200, UART_FREQ / 115200L },#endif }; struct speed2divisor *s2dp; /* RS232 needs to know the xoff character, and if CTS works. */ rs->oxoff = tp->tty_termios.c_cc[VSTOP]; rs->cts = (tp->tty_termios.c_cflag & CLOCAL) ? MS_CTS : 0; /* Look up the 8250 rate divisor from the output speed. */ divisor = 0; for (s2dp = s2d; s2dp < s2d + sizeof(s2d)/sizeof(s2d[0]); s2dp++) { if (s2dp->speed == tp->tty_termios.c_ospeed) divisor = s2dp->divisor; } if (divisor == 0) return; /* B0? */#if (MACHINE == IBM_PC) /* Compute line control flag bits. */ line_controls = 0; if (tp->tty_termios.c_cflag & PARENB) { line_controls |= LC_PARITY; if (!(tp->tty_termios.c_cflag & PARODD)) line_controls |= LC_PAREVEN; } if (divisor >= (UART_FREQ / 110)) line_controls |= LC_2STOP_BITS; line_controls |= (tp->tty_termios.c_cflag & CSIZE) >> 2; /* Lock out interrupts while setting the speed. The receiver register is * going to be hidden by the div_low register, but the input interrupt * handler relies on reading it to clear the interrupt and avoid looping * forever. */ lock(); /* Select the baud rate divisor registers and change the rate. */ sys_outb(rs->line_ctl_port, LC_ADDRESS_DIVISOR); sys_outb(rs->div_low_port, divisor); sys_outb(rs->div_hi_port, divisor >> 8); /* Change the line controls and reselect the usual registers. */ sys_outb(rs->line_ctl_port, line_controls); rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */ if ((tp->tty_termios.c_lflag & IXON) && rs->oxoff != _POSIX_VDISABLE) rs->ostate &= ~ORAW; unlock();#else /* MACHINE == ATARI */ line_controls = U_Q16; if (tp->tty_termios.c_cflag & PARENB) { line_controls |= U_PAR; if (!(tp->tty_termios.c_cflag & PARODD)) line_controls |= U_EVEN; } line_controls |= (divisor >= (UART_FREQ / 110)) ? U_ST2 : U_ST1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -