V8/usr/sys/chncp/chsend.c
#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);
}