/* * Copyright 1985 by MIPS Computer Systems, Inc. */ /* * protocol.c -- serial line protocol */ #ident "$Revision: 1.21 $" #include #include "protocol.h" #include "protoio.h" #include "dbgmon.h" /* for prototypes */ #include #include #include #include #include #include #ifdef NULL #undef NULL #endif #define NULL 0 #define CONTROL(x) (x&0x1f) #define min(a, b) (a > b ? b : a) /* Defined in libsc as _min & _max, but */ #define max(a, b) (a < b ? b : a) /* since symmon doesn't link with libsc */ /* we need these here in case PROTO_PKTSIZE */ /* is defined, which it is not so far. */ char *str_buf(); static void csum_putc(unsigned c, ULONG fd); static int csum_getc(ULONG fd); static void __dprintf(char *, long, long); /* * Serial line protocol description: * This protocol is a ack/nack, byte-stuffing, half-duplex protocol. * The protocol may be used for bidirectional communication, but the * hand-shaking necessary to turn the line around is the responsibility * of the user of the protocol. * * SYN characters are used purely to synchonize the protocol and * always resync the protocol when received. SYN as part of the * data must be represented as DLE S. DLE as part of the data * is represented as DLE D. CONTROL('C') is not a protocol special * character, but it is the prom monitor "interrupt" character, * so it is escaped as DLE C. CONTROL('S') and CONTROL('Q') are also * escaped (DLE s and DLE q) to avoid conflict with common tty * flow control conventions. * * The protocol supports 8 bit data, but does not use 8 bit characters * in the packet header so the protocol is usable with 7 bit data paths * if the client is prepared to deal with 7 bit data. * * serial line protocol format: * ... * * ::= Ascii sync character * * ::= Packet type and high order bits of data length * Length is data length BEFORE DLE escapes are * inserted. * Bit 6 is set to 1 to avoid inadvertent SYN character * Bit 5 == 1 => Data packet (DATA_PKTTYPE) * Bit 5 == 0 => Acknowledgment packet (ACK_PKTTYPE) * Bits 4 -- 0 => Bits 10 -- 6 of data length * * ::= Low order data length bits. * Bit 6 is set to 1 to avoid inadvertent SYN character * Bits 5 -- 0 => Bits 5 -- 0 of data length * * ::= 6 bit sequence number, encoded as * low 6 bits of 1 character. * Character is OR'ed with 0x40 to * avoid conflict with SYN character. * ACK_PKTTYPE packets carry sequence number * of NEXT expected DATA_PKTTYPE packet. * * ::= Body of message * * ::= 18 bit checksum of all bytes of packet * excluding SYN character and checksum * itself, encoded as 3 characters with * most significant 6 bits in first character * middle significant 6 bits in second * character and least significant 6 bits * in third character. * Each character is OR'ed with 0x40 to * avoid conflict with SYN character. * */ static unsigned next_getseq; /* next expected receive seq number */ static unsigned current_putseq; /* seq number for current xmit packet */ static unsigned acked_putseq; /* last acknowledged xmit packet seq */ static unsigned get_csum; /* receive csum accumulator */ static unsigned put_csum; /* xmit csum accumulator */ static int n_acked; /* number of ACK pkts received */ static int n_xmited; /* number of transmitted pkts */ #if PROTO_PKTSIZE static int packet_size; /* current packet size */ #endif /* * init_proto -- reset protocol state info */ /* ARGSUSED */ void init_proto(ULONG fd) { PINIT(fd); next_getseq = current_putseq = 0; acked_putseq = (unsigned int)-1; n_acked = n_xmited = 0; #if PROTO_PKTSIZE /* * initial packet size is 1/2 of MAXPACKET/2 (256 bytes roughly) * (MAXPACKET/2 to handle worst case byte stuffing) */ packet_size = (MAXPACKET / 2) / 2; #endif } #ifdef PROTO_PKTSIZE /* * proto_pktsize -- line quality adaptive transmission routine */ int proto_pktsize(void) { int len; int ack_ratio; if (n_xmited > MIN_XMIT_PKTS) { ack_ratio = n_acked * 100 / n_xmited; if ( ack_ratio < MIN_ACK_THRESH) { /* * If we're having trouble, try halving * packet size. */ packet_size = max(MINPACKET, packet_size / 2); n_acked = n_xmited = 0; } else if (ack_ratio > MAX_ACK_THRESH) { /* * Max packet size is limited to MAXPACKET/2 * to allow for worst case DLE expansion */ packet_size = min(MAXPACKET/2, (packet_size * 8) / 7); n_acked = n_xmited = 0; } } return(packet_size); } #endif /* PROTO_PKTSIZE */ static jmp_buf resync_buf; /* * getpkt -- unwrap incoming packet, check sequence number and checksum * and send appropriate acknowledgment. * Returns data length. * * fd - device to receive from * buf - buf for received packet * cnt - max data len * pkt_typep - in: pointer to desired packet type * out: pointer to received packet type */ int getpkt(ULONG fd, char *buf, unsigned cnt, int *pkt_typep) { register char *cp; register int i; unsigned csum, pkt_csum, seq; int pkt_type; int new_packet, type_len, len; unsigned nseq; extern int Debug; if (*pkt_typep != DATA_PKTTYPE && *pkt_typep != ACK_PKTTYPE && *pkt_typep != ANY_PKTTYPE) { printf("*** getpkt: BAD PKTTYPE\n"); return(0); } new_packet = 0; while (!new_packet) { while ((i = GETC(fd)) != SYN) { /* sync to start of packet */ #ifdef DEBUG putchar(i); #endif /* DEBUG */ continue; } setjmp(resync_buf); /* longjmp here on SYN */ get_csum = 0; type_len = csum_getc(fd); pkt_type = type_len & MASK_PKTTYPE; if (pkt_type != *pkt_typep && *pkt_typep != ANY_PKTTYPE) { __dprintf("bad type, got %s\n", (long)(pkt_type == DATA_PKTTYPE ? "data" : "ack"),0); goto sendack; } len = (type_len & 0x1f) << 6; len |= csum_getc(fd) & 0x3f; if (len > cnt) { /* don't accept long packets */ __dprintf("bad len\n",0,0); goto sendack; } seq = (pkt_type == DATA_PKTTYPE) ? next_getseq /* next expected data seq */ : (current_putseq + 1) & 0x3f; /* next expected ack seq */ if ((nseq = (csum_getc(fd) & 0x3f)) != seq) { __dprintf("bad seq, got 0x%x wanted 0x%x\n", nseq, seq); goto sendack; } cp = buf; i = len; while (i-- > 0) *cp++ = csum_getc(fd); pkt_csum = get_csum; csum = (csum_getc(fd) & 0x3f) << 12; csum |= (csum_getc(fd) & 0x3f) << 6; csum |= csum_getc(fd) & 0x3f; if ((pkt_csum & 0x3ffff) != csum) { __dprintf("bad csum\n",0,0); goto sendack; } new_packet = 1; /* got a good packet */ if (pkt_type == DATA_PKTTYPE) next_getseq = (next_getseq + 1) & 0x3f; else acked_putseq = current_putseq; sendack: #ifdef DEBUG printf("getpkt len=%d buf=%s seq=0x%x type %s\r\n", len, str_buf(buf, len), nseq, pkt_type == ACK_PKTTYPE ? "ACK" : "DATA"); #endif /* DEBUG */ /* * Don't send ACKs to ACKs */ if (pkt_type != ACK_PKTTYPE) putpkt(fd, NULL, 0, ACK_PKTTYPE); } *pkt_typep = pkt_type; return(len); } /* * csum_getc -- get next character, handling checksum calculation * and DLE escapes */ static int csum_getc(ULONG fd) { unsigned c; c = GETC(fd) & 0xff; get_csum += c; if (c == SYN) { __dprintf("got unexpected sync\n",0,0); longjmp(resync_buf, 1); } if (c == DLE) { c = csum_getc(fd); switch (c) { case 'S': c = SYN; break; case 'D': c = DLE; break; case 'C': c = CONTROL('C'); break; case 's': c = CONTROL('S'); break; case 'q': c = CONTROL('Q'); break; default: printf("unknown DLE escape, 0x%x\n", c); break; } } return (c); } static jmp_buf rexmit_buf; static void rexmit_handler(void) { longjmp (rexmit_buf, 1); } /* * putpkt -- wrap data in packet and transmit. * Waits for acknowledgment and retransmits as necessary */ void putpkt(ULONG fd, char *buf, int cnt, int pkt_type) { register char *cp; register int i; int ack_type; unsigned seq; char type_len; extern int Debug; if (pkt_type != DATA_PKTTYPE && pkt_type != ACK_PKTTYPE) { printf("*** putpkt: ILLEGAL PACKET TYPE\n"); return; } if (cnt > MAXPACKET/2) { printf("*** putpkt: ILLEGAL PACKET SIZE\n"); return; } /* restart here if timeout */ if (setjmp(rexmit_buf)) __dprintf("retransmitting packet 0x%x\n", current_putseq,0); while (current_putseq != acked_putseq) { PUTC(SYN, fd); put_csum = 0; type_len = pkt_type | ((cnt >> 6) & 0x1f) | 0x40; csum_putc(type_len, fd); /* TYPE_LEN */ csum_putc((cnt & 0x3f) | 0x40, fd); /* LEN1 */ seq = (pkt_type == DATA_PKTTYPE) ? current_putseq /* sequence for data packets */ : next_getseq; /* sequence for ack packets */ csum_putc(seq | 0x40, fd); /* SEQ */ cp = buf; for (i = cnt; i > 0; i--) /* DATA */ csum_putc(*cp++, fd); PUTC(((put_csum >> 12) & 0x3f) | 0x40, fd); /* CSUM */ PUTC(((put_csum >> 6) & 0x3f) | 0x40, fd); PUTC((put_csum & 0x3f) | 0x40, fd); PUTFLUSH(fd); #ifdef DEBUG printf("putpkt len=%d buf=%s seq=0x%x type %s\r\n", cnt, str_buf(buf, cnt), seq, pkt_type == ACK_PKTTYPE ? "ACK" : "DATA"); #endif /* DEBUG */ if (pkt_type == ACK_PKTTYPE) /* don't send ACKs to ACKs */ return; n_xmited++; Signal (SIGALRM, rexmit_handler); alarm(REXMIT_TIME); ack_type = ACK_PKTTYPE; getpkt(fd, NULL, 0, &ack_type); alarm(0); Signal (SIGALRM, SIGDefault); } if (pkt_type == DATA_PKTTYPE) { current_putseq = (current_putseq + 1) & 0x3f; n_acked++; } } /* * csum_putc -- put character handling checksum calculation and doing * DLE stuffing for characters that must be escaped */ static void csum_putc(unsigned c, ULONG fd) { switch (c) { case SYN: put_csum += DLE; PUTC(DLE, fd); c = 'S'; break; case DLE: put_csum += DLE; PUTC(DLE, fd); c = 'D'; break; case CONTROL('C'): put_csum += DLE; PUTC(DLE, fd); c = 'C'; break; case CONTROL('S'): put_csum += DLE; PUTC(DLE, fd); c = 's'; break; case CONTROL('Q'): put_csum += DLE; PUTC(DLE, fd); c = 'q'; break; } put_csum += (c & 0xff); PUTC(c, fd); } /* * __dprintf -- print error messages if $verbose set */ static void __dprintf(char *fmt, long arg1, long arg2) { extern int Verbose; if (Verbose) printf(fmt, arg1, arg2); } #ifdef DEBUG char * str_buf(char *buf, int len) { static char tmpbuf[64]; int i; if (len == 0) strcpy(tmpbuf, "\"\""); else { tmpbuf[0] = '"'; for (i = 0; i < len && i < 20; i++) tmpbuf[i+1] = buf[i]; tmpbuf[++i] = '"'; tmpbuf[++i] = 0; } return(tmpbuf); } #endif #ifdef _STANDALONE /* * proto_enable -- enable a device for protocol use */ void proto_enable(ULONG fd) { if (!isatty(fd)) { printf("not a character device\n"); return; } ioctl(fd, TIOCRAW, 1); /* disable special chars */ ioctl(fd, TIOCFLUSH, 0);/* flush input */ } /* * proto_disable -- disable a getc device for protocol use */ void proto_disable(ULONG fd) { ioctl(fd, TIOCRAW, 0); /* enable special characters */ ioctl(fd, TIOCFLUSH, 0);/* flush input */ } int putc(int c, int fd) { char buf[1]; ULONG cnt; buf[0] = c; if (Write(fd, buf, 1, &cnt) || cnt != 1) return -1; /* EOF */ return c; } #endif