/**************************************************************************** Copyright (C) 1987-2005 by Jeffery P. Hansen 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 #include #include #include "gsim.h" /* To transmit a character (tty receive): Unassert DSR Wait for DTR to be high Place data on RD Assert DSR (trigger on posedge) To receive a character (tty transmit): Unassert CTS Wait for RTS to be high Read data from TD Assert CTS (trigger on posedge) */ #define TTY_BUFSIZE 512 /* Number of characters to buffer */ #define TTY_TD 0 #define TTY_RD 1 #define TTY_RTS 2 #define TTY_CTS 3 #define TTY_DSR 4 #define TTY_DTR 5 #define TTY_EV_TRDONE 0 /* Transmit is complete */ #define TTY_EV_RECVDONE 1 /* Receive is complete */ #define TTY_DELAY_TRANSMIT 0 /* Time to put data on TD */ #define TTY_DELAY_RTS_UP 1 /* Time to transmit a character and assert RTS */ #define TTY_DELAY_RTS_DN 2 /* Time to transmit a character and assert RTS */ #define TTY_DELAY_RECEIVE 3 /* Time to receive a character */ #define TTY_DELAY_DTR_UP 4 /* */ #define TTY_DELAY_DTR_DN 5 /* */ struct tty_data { char buffer[TTY_BUFSIZE]; /* Characters typed at keyboard */ int first; /* First buffered character */ int last; /* One after last buffered character */ int tr_sendwait; /* Waiting for char to be transmitted */ int tr_ackwait; /* Waiting for acknowledge */ int recv_ok; /* Receiver is ready */ }; static void Tty_processEvent(SGate*,EvQueue*,SEvent*); static int Tty_checkGate(SGate*); static void Tty_initGate(EvQueue*,SGate*); static void Tty_command(EvQueue *,SGate*,const char*); static SGateInfo tty_info = { 0, "tty",0x0, 6,{{"TD",GIO_OUT,PF_CUT}, /* Transmitted data */ {"RD",GIO_IN,PF_CUT}, /* Receive data */ {"RTS",GIO_OUT,PF_CUT}, /* Request to send */ {"CTS",GIO_IN,PF_CUT}, /* Clear to send */ {"DSR",GIO_IN,PF_CUT}, /* Set set ready */ {"DTR",GIO_OUT,PF_CUT}}, /* Data terminal ready */ {{"TR",0,-1}, {"RTS_UP",0,-1}, {"RTS_DN",0,-1}, {"RD",0,-1}, {"DTR_UP",0,-1}, {"DTR_DN",0,-1}, {0}}, Generic_copyGate, Tty_processEvent, Tty_checkGate, Tty_initGate, 0, Tty_command }; static void tty_rts(EvQueue *Q,SGate *g,int value) { SState *S = alloc_SState(); SPort *RTS = g->g_ports.port[TTY_RTS]; int delay; SState_reinit(S,1); SState_convertFromInt(S,value); if (value) delay = g->g_delayParms[TTY_DELAY_RTS_UP]; else delay = g->g_delayParms[TTY_DELAY_RTS_DN]; EvQueue_setPort(Q,RTS,S,delay); free_SState(S); } static void tty_tryTransmit(EvQueue *Q,SGate *g) { struct tty_data *d = (struct tty_data*) g->g_data; SPort *TD = g->g_ports.port[TTY_TD]; SState *S; int c; if (d->first == d->last) return; /* Nothing in buffer */ if (d->tr_sendwait || d->tr_ackwait) return; /* Not ready to send yet */ S = alloc_SState(); c = d->buffer[d->first]; d->first = (d->first + 1) % TTY_BUFSIZE; d->tr_sendwait = 1; /* * Put character on output */ SState_reinit(S,8); SState_convertFromInt(S,c); EvQueue_setPort(Q,TD,S,g->g_delayParms[TTY_DELAY_TRANSMIT]); /* * Send wakeup to self */ EvQueue_qGateEv(Q,g,TTY_EV_TRDONE,0,g->g_delayParms[TTY_DELAY_TRANSMIT]); free_SState(S); } static void tty_receive(EvQueue *Q,SGate *g) { SState *RD = SGate_allocPortState(g,TTY_RD); SPort *DTR = g->g_ports.port[TTY_DTR]; SState *S = alloc_SState(); SState_reinit(S,1); SState_convertFromInt(S,0); EvQueue_setPort(Q,DTR,S,g->g_delayParms[TTY_DELAY_DTR_DN]); /* * Send wakeup to self */ EvQueue_qGateEv(Q,g,TTY_EV_RECVDONE,(void*)(RD->one[0] & 0xff), g->g_delayParms[TTY_DELAY_RECEIVE]); free_SState(RD); free_SState(S); } static void tty_processGateEvent(SGate *g,EvQueue *Q,SEvent *E) { struct tty_data *d = (struct tty_data*) g->g_data; switch (E->evgate.type) { case TTY_EV_TRDONE : { #if 0 SState *CTS = SGate_allocPortState(g,TTY_CTS); int cts = SState_getBitSym(CTS,0); free_SState(CTS); #endif tty_rts(Q,g,1); d->tr_sendwait = 0; d->tr_ackwait = 1; } break; case TTY_EV_RECVDONE : { SPort *DTR = g->g_ports.port[TTY_DTR]; SState *S = alloc_SState(); size_t c = (size_t) E->evgate.gdata; sendMsg("tty_char %s %d",g->g_name,c); SState_reinit(S,1); SState_convertFromInt(S,1); EvQueue_setPort(Q,DTR,S,g->g_delayParms[TTY_DELAY_DTR_UP]); d->recv_ok = 1; free_SState(S); } break; } } static void Tty_processEvent(SGate *g,EvQueue *Q,SEvent *E) { struct tty_data *d = (struct tty_data*) g->g_data; if (E->evclass == EV_GATE) { /* Internal state change */ tty_processGateEvent(g,Q,E); } else { /* An input changed */ if (!d->tr_sendwait) { if (IsChangeOn(E,g,TTY_CTS)) { SState *CTS = SGate_allocPortState(g,TTY_CTS); int cts = SState_getBitSym(CTS,0); free_SState(CTS); if (cts != SYM_ZERO) { d->tr_ackwait = 0; tty_tryTransmit(Q,g); tty_rts(Q,g,0); } } } if (d->recv_ok) { if (IsChangeOn(E,g,TTY_DSR)) { SState *DSR = SGate_allocPortState(g,TTY_DSR); int dsr = SState_getBitSym(DSR,0); free_SState(DSR); if (dsr != SYM_ZERO) tty_receive(Q,g); } } } } static int Tty_checkGate(SGate *g) { SPort *TD = g->g_ports.port[TTY_TD]; SPort *RD = g->g_ports.port[TTY_RD]; SPort *RTS = g->g_ports.port[TTY_RTS]; SPort *CTS = g->g_ports.port[TTY_CTS]; SPort *DSR = g->g_ports.port[TTY_DSR]; SPort *DTR = g->g_ports.port[TTY_DTR]; if (TD->p_state.nbits != 8 || RD->p_state.nbits != 8) { errorGate(g->g_name,"TD and RD ports on tty must be 8 bits."); return -1; } if (RTS->p_state.nbits != 1 || CTS->p_state.nbits != 1 || DSR->p_state.nbits != 1 || DTR->p_state.nbits != 1) { errorGate(g->g_name,"RTS, CTS, DSR and DTR ports on tty must be 1 bit."); return -1; } return 0; } static void Tty_initGate(EvQueue *Q,SGate *g) { struct tty_data *d = (struct tty_data*) malloc(sizeof(struct tty_data)); SState *S = alloc_SState(); SPort *RTS = g->g_ports.port[TTY_RTS]; SPort *DTR = g->g_ports.port[TTY_DTR]; g->g_data = d; d->first = d->last = 0; d->tr_sendwait = 0; d->tr_ackwait = 0; d->recv_ok = 1; SState_reinit(S,1); sendMsg("mktty %s",g->g_name); SState_zero(S); EvQueue_setPort(Q,RTS,S,0); SState_one(S); EvQueue_setPort(Q,DTR,S,0); free_SState(S); } static void Tty_command(EvQueue *Q,SGate *g,const char *cmd) { struct tty_data *d = (struct tty_data*) g->g_data; int c; if (sscanf(cmd," key %d",&c) == 1) { if ( ((d->last+1) % TTY_BUFSIZE) != d->first) { d->buffer[d->last] = c; d->last = (d->last + 1) % TTY_BUFSIZE; } } tty_tryTransmit(Q,g); } void init_tty() { SGateInfo_register(&tty_info,0); }