#include "../chunix/chsys.h" #include "../chunix/chconf.h" #include "../chaos/chaos.h" /* * Send a STS packet on this connection * if allocation fails it is not sent */ sendsts(conn) register struct connection *conn; { register struct packet *pkt; if ((pkt = pkalloc(sizeof(struct sts_data), 1)) != NOPKT) { setpkt(conn, pkt); makests(conn, pkt); sendctl(pkt); } } /* * Send a SNS packet on this connection * if allocation failed nothing is sent */ sendsns(conn) register struct connection *conn; { register struct packet *pkt; if ((pkt = pkalloc(0, 1)) != NOPKT) { setpkt(conn, pkt); pkt->pk_op = SNSOP; pkt->pk_lenword = 0; pkt->pk_ackn = conn->cn_racked = conn->cn_rread; sendctl(pkt); } } /* * Send an open in response to a RFC * return NOPKT is allocation fails else, open packet */ sendopn(conn) register struct connection *conn; { register struct packet *pkt; if ((pkt = pkalloc(sizeof(struct sts_data), 1)) != NOPKT) { conn->cn_rsts = conn->cn_rwsize >> 1; conn->cn_tlast = 0; pkt->pk_op = OPNOP; pkt->pk_len = sizeof(struct sts_data); pkt->pk_receipt = conn->cn_rlast; pkt->pk_rwsize = conn->cn_rwsize; debug(DCONN,printf("Conn #%x: open sent\n",conn->cn_lidx)); (void)ch_write(conn, pkt); } } /* * Following are functions and macros that deal with tranmitters directly * * Macro XMITHEAD (pkt, xcvr) queues pkt on xcvr for transmission, putting * it at the head of the queue for "quick" action. The transmitter is * started if no packet is currently being transmitted. * It is assumed that the pkt is a single one and not a list. * Macro XMITTAIL is similar except that is queues the packet at the tail * of the transmit list, treats the pkt argument as a list, and puts a time * stamp in each packet in the list. * Note that XMITTAIL modifies the variable pkt. */ #define XMITHEAD(pkt, axcvr) \ { register struct chxcvr *xcvr = axcvr; \ pkt->pk_next = xcvr->xc_list; \ xcvr->xc_list = pkt; \ if (xcvr->xc_tail == NOPKT) { \ xcvr->xc_tail = pkt; \ if (xcvr->xc_tpkt == NOPKT) \ (*xcvr->xc_start)(xcvr); \ } \ } /* * Sendctl - send a control packet that will not be acknowledged and * will not be retransmitted (this actual packet). Put the * given packet at the head of the transmit queue so it is transmitted * "quickly", i.e. before data packets queued at the tail of the queue. * If anything is wrong (no path, bad subnet) we just throw it * away. Remember, pk_next is assumed to be == NOPKT. */ sendctl(pkt) register struct packet *pkt; { register struct chroute *r; debug(DSEND, (printf("Sending: %d ", pkt->pk_op), prpkt(pkt, "ctl"), printf("\n"))); if (pkt->pk_dsubnet >= CHNSUBNET || (r = &Chroutetab[pkt->pk_dsubnet])->rt_type == CHNOPATH) ch_free((char *)pkt); else if (pkt->pk_daddr == Chmyaddr) sendtome(pkt); else { if (r->rt_type == CHFIXED || r->rt_type == CHBRIDGE) { pkt->pk_xdest = r->rt_addr; r = &Chroutetab[r->rt_subnet]; } else pkt->pk_xdest = pkt->pk_daddr; XMITHEAD(pkt, r->rt_xcvr); } } /* * Senddata - send a list of controlled packets, all assumed to be to the * same destination. Queue them on the end of the appropriate transmit * queue. * Similar to sendctl, but stuffs time, handles a list, and puts pkts * at the end of the queue instead of at the beginning, and fakes * transmission if it can't really send the packets (as opposed to * throwing the packets away) */ senddata(pkt) register struct packet *pkt; { register struct chroute *r; debug(DSEND, (printf("Sending: %d ", pkt->pk_op), prpkt(pkt, "data"), printf("\n"))); if (pkt->pk_dsubnet >= CHNSUBNET || (r = &Chroutetab[pkt->pk_dsubnet])->rt_type == CHNOPATH) { struct packet *npkt; do { npkt = pkt->pk_next; pkt->pk_next = NOPKT; xmitdone(pkt); } while ((pkt = npkt) != NOPKT); } else if (pkt->pk_daddr == Chmyaddr) sendtome(pkt); else { register struct chxcvr *xcvr; register unsigned short dest; if (r->rt_type == CHFIXED || r->rt_type == CHBRIDGE) { dest = r->rt_addr; r = &Chroutetab[r->rt_subnet]; } else dest = pkt->pk_daddr; xcvr = r->rt_xcvr; if (xcvr->xc_list == NOPKT) xcvr->xc_list = pkt; else xcvr->xc_tail->pk_next = pkt; for (;;) { pkt->pk_time = Chclock; pkt->pk_xdest = dest; if (pkt->pk_next == NOPKT) break; pkt = pkt->pk_next; } xcvr->xc_tail = pkt; if (xcvr->xc_tpkt == NOPKT) (*xcvr->xc_start)(xcvr); } } /* * Send the given RUT packet out on the given tranceiver, which has the given * cost. If the "copy" flag is true, make a copy of the packet. * Note that if copy is not set, the packet data gets modified. */ sendrut(pkt, axcvr, cost, copy) register struct packet *pkt; register struct chxcvr *axcvr; unsigned short cost; int copy; { register struct rut_data *rd; struct rut_data *rdend; if (copy) { struct packet *npkt; if ((npkt = pkalloc((int)pkt->pk_len, 1)) == NOPKT) return; movepkt(pkt, npkt); pkt = npkt; } rdend = (struct rut_data *)(pkt->pk_cdata + pkt->pk_len); for (rd = pkt->pk_rutdata; rd < rdend; rd++) rd->pk_cost += cost; pkt->pk_saddr = axcvr->xc_addr; pkt->pk_xdest = 0; XMITHEAD(pkt, axcvr); } /* * Send the (list of) packet(s) to myself - NOTE THIS CAN BE RECURSIVE! */ sendtome(pkt) register struct packet *pkt; { register struct packet *rpkt, *npkt; static struct chxcvr fakexcvr; /* * Static structure is used to economize on stack space. * We are careful to use it very locally so that recursion still * works. Cleaner solutions don't recurse very well. * Basically for each packet in the list, prepare it for * transmission by executing xmitnext, * complete the transmission by calling xmitdone, * then receive the packet on the other side, possibly * causing this routine to recurse (after we're done with the * static structure until next time around the loop) * When this routine is called with a list of data packets, things * can get pretty weird. */ while (pkt != NOPKT) { /* * Make the transmit list consist of one packet to send. * Save the rest of the list in npkt. */ npkt = pkt->pk_next; pkt->pk_next = NOPKT; fakexcvr.xc_tpkt = NOPKT; fakexcvr.xc_list = fakexcvr.xc_tail = pkt; /* * The xmitnext just dequeues pkt and sets ackn. * It will free the packet if its not worth sending. */ (void)xmitnext(&fakexcvr); if (fakexcvr.xc_tpkt) { /* * So it really should be sent. * First make a copy for the receiving side in rpkt. */ if ((rpkt = pkalloc((int)pkt->pk_len, 1)) != NOPKT) movepkt(pkt, rpkt); /* * This xmitdone just completes transmission. * Now pkt is out of our hands. */ xmitdone(pkt); /* * So transmission is complete. Now receive it. */ if (rpkt != NOPKT) rcvpkt(rpkt); } pkt = npkt; } } /* * Indicate that actual transmission of the current packet has been completed. * Called by the device dependent interrupt routine when transmission * of a packet has finished. */ xmitdone(pkt) register struct packet *pkt; { register struct connection *conn; register struct packet *npkt; if (pkt->pk_saddr == Chmyaddr && CONTPKT(pkt) && pkt->pk_stindex < CHNCONNS && (conn = Chconntab[pkt->pk_stindex]) != NOCONN && pkt->pk_sidx == conn->cn_lidx && (conn->cn_state == CSOPEN || conn->cn_state == CSRFCSENT) && cmp_gt(pkt->pk_pkn, conn->cn_trecvd)) { pkt->pk_time = Chclock; if ((npkt = conn->cn_thead) == NOPKT || cmp_lt(pkt->pk_pkn, npkt->pk_pkn)) { pkt->pk_next = npkt; conn->cn_thead = pkt; } else { for( ; npkt->pk_next != NOPKT; npkt = npkt->pk_next) if(cmp_lt(pkt->pk_pkn, npkt->pk_next->pk_pkn)) break; pkt->pk_next = npkt->pk_next; npkt->pk_next = pkt; } if(pkt->pk_next == NOPKT) conn->cn_ttail = pkt; } else ch_free((char *)pkt); } /* * Return the next packet on which to begin transmission (if none, NOPKT). */ struct packet * xmitnext(xcvr) register struct chxcvr *xcvr; { register struct packet *pkt; register struct connection *conn; while ((pkt = xcvr->xc_tpkt = xcvr->xc_list) != NOPKT) { if ((xcvr->xc_list = pkt->pk_next) == NOPKT) xcvr->xc_tail = NOPKT; if (pkt->pk_saddr == Chmyaddr && CONTPKT(pkt)) if (pkt->pk_stindex < CHNCONNS && (conn = Chconntab[pkt->pk_stindex]) != NOCONN && pkt->pk_sidx == conn->cn_lidx && (conn->cn_state == CSOPEN || conn->cn_state == CSRFCSENT) && cmp_gt(pkt->pk_pkn, conn->cn_trecvd)) pkt->pk_ackn = conn->cn_racked = conn->cn_rread; else { debug(DPKT, printf("invalid pkt to send: #%x\n", conn->cn_lidx)); ch_free((char *)pkt); continue; } xcvr->xc_ttime = Chclock; break; } return (pkt); }