BSD 4_3_Net_2 development
authorCSRG <csrg@ucbvax.Berkeley.EDU>
Tue, 2 Apr 1991 07:19:43 +0000 (23:19 -0800)
committerCSRG <csrg@ucbvax.Berkeley.EDU>
Tue, 2 Apr 1991 07:19:43 +0000 (23:19 -0800)
Work on file usr/src/contrib/isode/tsap/ts2bsd.c

Synthesized-from: CSRG/cd2/net.2

usr/src/contrib/isode/tsap/ts2bsd.c [new file with mode: 0644]

diff --git a/usr/src/contrib/isode/tsap/ts2bsd.c b/usr/src/contrib/isode/tsap/ts2bsd.c
new file mode 100644 (file)
index 0000000..5bfd9f5
--- /dev/null
@@ -0,0 +1,1232 @@
+/* ts2bsd.c - TPM: 4.4BSD OSI TP4 interface */
+#define STATIC /**/
+
+#ifndef        lint
+static char *rcsid = "$Header: /f/osi/tsap/RCS/ts2bsd.c,v 7.12 91/03/09 11:58:13 mrose Exp $";
+#endif
+
+/* 
+ * $Header: /f/osi/tsap/RCS/ts2bsd.c,v 7.12 91/03/09 11:58:13 mrose Exp $
+ *
+ *
+ * $Log:       ts2bsd.c,v $
+ * Revision 7.12  91/03/09  11:58:13  mrose
+ * update
+ * 
+ * Revision 7.11  91/02/22  09:47:17  mrose
+ * Interim 6.8
+ * 
+ * Revision 7.10  91/01/14  13:34:23  mrose
+ * loader
+ * 
+ * Revision 7.9  90/07/09  14:51:13  mrose
+ * sync
+ * 
+ * Revision 7.8  90/03/23  17:31:16  mrose
+ * 8
+ * 
+ * Revision 7.7  90/03/22  08:38:02  mrose
+ * touch-up
+ * 
+ * Revision 7.6  89/12/19  10:18:28  mrose
+ * DLOG
+ * 
+ * Revision 7.5  89/12/18  17:49:48  mrose
+ * touch-up
+ * 
+ * Revision 7.4  89/12/08  16:26:47  mrose
+ * keith
+ * 
+ * Revision 7.1  89/12/01  10:46:26  mrose
+ * touch-up
+ * 
+ * Revision 7.0  89/11/23  22:30:36  mrose
+ * Release 6.0
+ * 
+ */
+
+/*
+ *                               NOTICE
+ *
+ *    Acquisition, use, and distribution of this module and related
+ *    materials are subject to the restrictions of a license agreement.
+ *    Consult the Preface in the User's Manual for the full terms of
+ *    this agreement.
+ *
+ */
+
+
+/* LINTLIBRARY */
+
+#include <stdio.h>
+#include <signal.h>
+#include "tpkt.h"
+
+#ifdef TP4
+#include "tp4.h"
+#endif
+
+#ifdef BSD_TP4
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include "tailor.h"
+
+/*#define      MAXTP4          8192    /* until we have a dynamic estimate... */
+#define        MAXTP4          1024    /* until we have a dynamic estimate... */
+#define        TP4SLOP           12    /* estimate of largest DT PCI */
+
+/* \f   DATA */
+
+STATIC struct msghdr msgs;
+STATIC union osi_control_msg ocm;
+
+
+extern int  errno;
+int tp4_disconnect_reason;
+
+/* Ancillary routines */
+STATIC int sendCmsg(fd, cc, type, data)
+int    fd;
+int    cc;
+int    type;
+char   *data;
+{
+    int            result;
+    register struct msghdr *msg = &msgs;
+    register union osi_control_msg *oc = &ocm;
+
+    bzero ((char *) msg, sizeof *msg);
+    msg -> msg_control = oc -> ocm_data;
+
+    bzero ((char *) oc, sizeof *oc);
+    oc -> ocm_control.ocm_cmhdr.cmsg_level = SOL_TRANSPORT;
+    oc -> ocm_control.ocm_cmhdr.cmsg_type = type;
+    oc -> ocm_control.ocm_cmhdr.cmsg_len = sizeof oc -> ocm_control.ocm_cmhdr;
+
+    if (cc) {
+       bcopy (data, oc -> ocm_control.ocm_cmdata, cc);
+       oc -> ocm_control.ocm_cmhdr.cmsg_len += cc;
+    }
+    msg -> msg_controllen = oc -> ocm_control.ocm_cmhdr.cmsg_len;
+
+    return sendmsg (fd, msg, 0);
+}
+
+int tp4getCmsg(fd, cc, type, data)
+int    fd;
+int    *cc;
+int    *type;
+char   *data;
+{
+    int            result;
+    register struct msghdr *msg = &msgs;
+    register union osi_control_msg *oc = &ocm;
+
+    bzero ((char *) msg, sizeof *msg);
+    msg -> msg_control = oc -> ocm_data;
+    msg -> msg_controllen = sizeof oc -> ocm_data;
+
+    bzero ((char *) oc, sizeof *oc);
+
+#ifndef TPOPT_DISC_REASON
+    /* this is sleazy; we assume that tp4getCmsg is being called to find out
+       what went wrong if errno is set to something */
+    if (errno && (errno & TP_ERROR_MASK) != 0 )
+       tp4_disconnect_reason = errno;
+#endif
+    result = recvmsg (fd, msg, 0);
+    if (result >= 0) {
+       register int n =
+               (oc -> ocm_control.ocm_cmhdr.cmsg_len
+                               - sizeof (oc -> ocm_control.ocm_cmhdr));
+#ifdef TPOPT_DISC_REASON
+       if ((n > 0) &&
+            oc -> ocm_control.ocm_cmhdr.cmsg_type == TPOPT_DISC_REASON) {
+           tp4_disconnect_reason = ((struct tp_disc_reason *)oc) -> dr_reason;
+           oc = (union osi_control_msg *)(1 + (struct tp_disc_reason *)oc);
+           n = oc -> ocm_control.ocm_cmhdr.cmsg_len
+                               - sizeof (oc -> ocm_control.ocm_cmhdr);
+       }
+#endif
+       if (cc && type && data && n > 0)  {
+               *type = oc -> ocm_control.ocm_cmhdr.cmsg_type;
+               if (n > *cc)
+                       n = *cc;
+               bcopy(oc -> ocm_control.ocm_cmdata, data, n);
+               *cc = n;
+       }
+    }
+    return (result);
+}
+
+/* \f   UPPER HALF */
+
+STATIC int  TConnect (tb, expedited, data, cc, td)
+register struct tsapblk *tb;
+char    *data;
+int    expedited,
+       cc;
+struct TSAPdisconnect *td;
+{
+    int            len;
+    union sockaddr_osi sock;
+    struct sockaddr_iso        *ifaddr = &sock.osi_sockaddr;
+    struct tp_conn_param tcp;
+    register struct tp_conn_param *p = &tcp;
+
+    if (gen2tp4X (&tb -> tb_responding, &sock) == NOTOK)
+       return tsaplose (td, DR_ADDRESS, NULLCP,
+                        "unable to parse remote address");
+
+    len = sizeof *p;
+    if (getsockopt (tb -> tb_fd, SOL_TRANSPORT, TPOPT_PARAMS, (char *) p, &len)
+           == NOTOK)
+       SLOG (tsap_log, LLOG_EXCEPTIONS, "TPOPT_PARAMS", ("unable to get"));
+    else {
+       if (p -> p_xpd_service ? !expedited : expedited) {
+           p -> p_xpd_service = expedited ? 1 : 0;
+           if (setsockopt (tb -> tb_fd, SOL_TRANSPORT, TPOPT_PARAMS,
+                           (char *) p, sizeof *p) == NOTOK)
+               SLOG (tsap_log, LLOG_EXCEPTIONS, "TPOPT_PARAMS",
+                     ("unable to set"));
+       }
+
+       if (expedited)
+           tb -> tb_flags |= TB_EXPD;
+       if (p -> p_tpdusize > 0) {
+           if (p -> p_tpdusize > 10)
+               p -> p_tpdusize = 10;
+           tb -> tb_tpduslop = TP4SLOP;
+           tb -> tb_tsdusize = (1 << p -> p_tpdusize) - tb -> tb_tpduslop;
+       }
+    }
+
+    if (data &&
+               sendCmsg(tb->tb_fd, data, cc, TPOPT_CONN_DATA) == NOTOK)
+       return tsaplose (td, DR_CONGEST, "TPOPT_CONN_DATA", "unable to send");
+
+/*
+   this is a real hack to pass information between TConnect and TRetry:
+       if tb_srcref is 0xffff:    this indicates that the connect is still
+                                  in progress
+
+       if tb_srcref is 0xfffe:    this indicates the connect is done
+
+       otherwise:                 this indicates a TS error
+ */
+
+    if (connect (tb -> tb_fd, (struct sockaddr *) ifaddr, ifaddr -> siso_len)
+               == NOTOK) {
+       if (errno == EINPROGRESS) {
+           tb -> tb_srcref = 0xffff;
+           return CONNECTING_1;
+       }
+       tp4_disconnect_reason = 0;
+       tp4getCmsg(tb->tb_fd, (int *)0, (int *)0, (char *)0);
+
+       if (!tp4_disconnect_reason)
+           return tsaplose (td, DR_REFUSED, "connection",
+                            "unable to establish");
+
+       tb -> tb_srcref = tp4_disconnect_reason;
+    }
+    else
+       tb -> tb_srcref = 0xfffe;
+
+    return DONE;
+}
+
+/* \f */
+
+STATIC int  TRetry (tb, async, tc, td)
+register struct tsapblk *tb;
+int    async;
+struct TSAPconnect *tc;
+struct TSAPdisconnect *td;
+{
+    int            len,
+           onoff,
+           flags,
+           reason;
+    union sockaddr_osi sock;
+    struct sockaddr_iso        *ifaddr = &sock.osi_sockaddr;
+    struct tp_conn_param tcp;
+    int cmsgtype = 0;
+    register struct tp_conn_param *p = &tcp;
+
+    switch (tb -> tb_srcref) {
+       case 0xfffe:
+           reason = NOTOK;
+           break;
+       
+       default:
+           reason = tb -> tb_srcref;
+
+       case 0xffff:
+           if (async)
+               switch ((*tb -> tb_retryfnx) (tb, td)) {
+                   case NOTOK:
+                       goto out;
+
+                   case OK:
+                       return CONNECTING_1;
+
+                   case DONE:
+                       break;
+               }
+
+           (void) gen2tp4X (&tb -> tb_responding, &sock);
+           if (connect (tb -> tb_fd, (struct sockaddr *) ifaddr,
+                        ifaddr -> siso_len) == NOTOK)
+               switch (errno) {
+                   case EINPROGRESS:
+                       return CONNECTING_1;
+
+                   case EISCONN:
+                       reason = NOTOK;
+                       break;
+
+                   default:
+                       tp4_disconnect_reason = 0;
+                       tp4getCmsg(tb->tb_fd, (int *)0, (int *)0, (char *)0);
+
+                       if (!tp4_disconnect_reason) {
+                           (void) tsaplose (td, DR_REFUSED, "connection",
+                                            "unable to establish");
+                           goto out;
+                       }
+                       reason = tp4_disconnect_reason;
+                       break;
+               }
+           break;
+    }
+
+
+    if (async)
+       (void) ioctl (tb -> tb_fd, FIONBIO, (onoff = 0, (char *) &onoff));
+
+    if (reason == NOTOK) {
+       tc -> tc_sd = tb -> tb_fd;
+       tc -> tc_tsdusize = tb -> tb_tsdusize = MAXTP4;
+
+       len = sizeof sock;
+       if (getsockname (tb -> tb_fd, (struct sockaddr *) ifaddr, &len)
+               != NOTOK) {
+           ifaddr -> siso_len = len;
+           (void) tp42genX (&tb -> tb_responding, &sock);
+       }
+       else
+           SLOG (tsap_log, LLOG_EXCEPTIONS, "failed", ("getpeername"));
+       copyTSAPaddrX (&tb -> tb_responding, &tc -> tc_responding);
+
+       len = sizeof *p;
+       if (getsockopt (tb -> tb_fd, SOL_TRANSPORT, TPOPT_PARAMS, (char *) p,
+                       &len)== NOTOK)
+           SLOG (tsap_log, LLOG_EXCEPTIONS, "TPOPT_PARAMS",
+                 ("unable to get"));
+       else {
+           if (!p -> p_xpd_service)
+               tb -> tb_flags &= ~TB_EXPD;
+
+           if (p -> p_tpdusize > 0) {
+               if (p -> p_tpdusize > 10)
+                   p -> p_tpdusize = 10;
+               tb -> tb_tpduslop = TP4SLOP;
+               tb -> tb_tsdusize = (1 << p -> p_tpdusize) - tb -> tb_tpduslop;
+           }
+       }
+       tc -> tc_expedited = (tb -> tb_flags & TB_EXPD) ? 1 : 0;
+       tc -> tc_cc = sizeof tc -> tc_data;
+       if (tp4getCmsg(tb->tb_fd, &cmsgtype, &tc->tc_cc, tc->tc_data) < 0) {
+           tc -> tc_cc = 0;
+       } else if (cmsgtype != TPOPT_CFRM_DATA)
+           tc -> tc_cc = 0;
+
+       tb -> tb_flags |= TB_CONN;
+#ifdef  MGMT
+       if (tb -> tb_manfnx)
+           (*tb -> tb_manfnx) (OPREQOUT, tb);
+#endif
+       if (tb -> tb_calling)
+           free ((char *) tb -> tb_calling), tb -> tb_calling = NULL;
+       if (tb -> tb_called)
+           free ((char *) tb -> tb_called), tb -> tb_called = NULL;
+
+       return DONE;
+    }
+
+    td -> td_reason = reason;
+    td -> td_cc = sizeof td -> td_data;
+    if (tp4getCmsg(tb->tb_fd, &cmsgtype, &td->td_cc, td->td_data) < 0)
+       td -> td_cc = 0;
+    else if (cmsgtype != TPOPT_DISC_DATA)
+       td -> td_cc = 0;
+
+out: ;
+    freetblk (tb);
+
+    return NOTOK;
+}
+
+/* \f */
+
+STATIC int  TStart (tb, cp, ts, td)
+register struct tsapblk *tb;
+char   *cp;
+struct TSAPstart *ts;
+struct TSAPdisconnect *td;
+{
+    int            i,
+           len;
+    struct tp_conn_param tcp;
+    register struct tp_conn_param *p = &tcp;
+
+    len = sizeof *p;
+    if (getsockopt (tb -> tb_fd, SOL_TRANSPORT, TPOPT_PARAMS, (char *) p, &len)
+           == NOTOK)
+       SLOG (tsap_log, LLOG_EXCEPTIONS, "TPOPT_PARAMS", ("unable to get"));
+    else {
+       if (p -> p_xpd_service)
+           tb -> tb_flags |= TB_EXPD;
+       if (p -> p_tpdusize > 0) {
+           if (p -> p_tpdusize > 10)
+               p -> p_tpdusize = 10;
+           tb -> tb_tpduslop = TP4SLOP;
+           tb -> tb_tsdusize = (1 << p -> p_tpdusize) - tb -> tb_tpduslop;
+       }
+    }
+
+    ts -> ts_sd = tb -> tb_fd;
+    copyTSAPaddrX (&tb -> tb_initiating, &ts -> ts_calling);
+    copyTSAPaddrX (&tb -> tb_responding, &ts -> ts_called);
+    ts -> ts_expedited = (tb -> tb_flags & TB_EXPD) ? 1 : 0;
+    ts -> ts_tsdusize = tb -> tb_tsdusize;
+
+    if ((i = strlen (cp)) > 0) {
+       if (i > 2 * TS_SIZE)
+           return tsaplose (td, DR_CONNECT, NULLCP,
+                            "too much initial user data");
+
+       ts -> ts_cc = implode ((u_char *) ts -> ts_data, cp, i);
+    }
+    else
+       ts -> ts_cc = 0;
+
+    return OK;
+}
+
+/* \f */
+
+/* ARGSUSED */
+
+STATIC int  TAccept (tb, responding, data, cc, qos, td)
+register struct tsapblk *tb;
+char   *data;
+int    responding,
+       cc;
+struct QOStype *qos;
+struct TSAPdisconnect *td;
+{
+    int            len;
+    struct tp_conn_param tcp;
+    register struct tp_conn_param *p = &tcp;
+
+    len = sizeof *p;
+    if (getsockopt (tb -> tb_fd, SOL_TRANSPORT, TPOPT_PARAMS, (char *) p, &len)
+           == NOTOK)
+       SLOG (tsap_log, LLOG_EXCEPTIONS, "TPOPT_PARAMS", ("unable to get"));
+    else {
+       if (!p -> p_xpd_service)
+           tb -> tb_flags &= ~TB_EXPD;
+       else
+           if (!(tb -> tb_flags & TB_EXPD)) {
+               p -> p_xpd_service = 0;
+               if (setsockopt (tb -> tb_fd, SOL_TRANSPORT, TPOPT_PARAMS,
+                               (char *) p, sizeof *p) == NOTOK)
+                   SLOG (tsap_log, LLOG_EXCEPTIONS, "TPOPT_PARAMS",
+                         ("unable to set"));
+           }
+
+       if (p -> p_tpdusize > 0) {
+           if (p -> p_tpdusize > 10)
+               p -> p_tpdusize = 10;
+           tb -> tb_tpduslop = TP4SLOP;
+           tb -> tb_tsdusize = (1 << p -> p_tpdusize) - tb -> tb_tpduslop;
+       }
+    }
+
+    if (sendCmsg (tb -> tb_fd, cc, TPOPT_CFRM_DATA, data) == NOTOK)
+       return tsaplose (td, DR_CONGEST, "TPOPT_CFRM_DATA", "unable to send");
+
+    tb -> tb_flags |= TB_CONN;
+#ifdef  MGMT
+    if (tb -> tb_manfnx)
+       (*tb -> tb_manfnx) (OPREQIN, tb);
+#endif
+
+    return OK;
+}
+
+/* \f */
+
+/* life would be nice if we didn't have to worry about the maximum number of
+   bytes that can be written in a single syscall() */
+
+#ifndef        MSG_MAXIOVLEN
+#define        MSG_MAXIOVLEN   NTPUV
+#endif
+
+
+STATIC int  TWrite (tb, uv, expedited, td)
+register struct tsapblk *tb;
+register struct udvec *uv;
+int    expedited;
+struct TSAPdisconnect *td;
+{
+    register int cc;
+    int            flags,
+           j,
+           len;
+#ifdef MGMT
+    int            dlen;
+#endif
+    register char *bp,
+                 *ep;
+    register struct qbuf *qb;
+    register struct msghdr *msg = &msgs;
+    struct iovec iovs[MSG_MAXIOVLEN];
+    register struct iovec *vv,
+                         *wv;
+    SFP            pstat;
+
+    bzero ((char *) msg, sizeof *msg);
+
+    flags = expedited ? MSG_OOB : 0;
+    
+#ifdef MGMT
+    dlen = 0;
+#endif
+
+    if (!expedited && (tb -> tb_flags & TB_QWRITES)) {
+       int     onoff,
+               nc;
+       struct udvec *xv;
+
+       cc = 0;
+       for (xv = uv; xv -> uv_base; xv++)
+           cc += xv -> uv_len;
+#ifdef MGMT
+       dlen = cc;
+#endif
+
+       if ((qb = (struct qbuf *) malloc (sizeof *qb + (unsigned) cc))
+               == NULL) {
+           (void) tsaplose (td, DR_CONGEST, NULLCP,
+                            "unable to malloc %d octets for pseudo-writev, failing...",
+                            cc);
+           freetblk (tb);
+
+           return NOTOK;
+       }
+       qb -> qb_forw = qb -> qb_back = qb;
+       qb -> qb_data = qb -> qb_base, qb -> qb_len = cc;
+
+       bp = qb -> qb_data;
+       for (xv = uv; xv -> uv_base; xv++) {
+           bcopy (xv -> uv_base, bp, xv -> uv_len);
+           bp += xv -> uv_len;
+       }
+
+       if (tb -> tb_qwrites.qb_forw != &tb -> tb_qwrites) {
+           nc = 0;
+           goto insert;
+       }
+
+       vv = iovs;
+       vv -> iov_base = qb -> qb_data, vv -> iov_len = qb -> qb_len;
+       vv++;
+
+       msg -> msg_iov = iovs;
+       msg -> msg_iovlen = vv - iovs;
+
+       pstat = signal (SIGPIPE, SIG_IGN);
+       (void) ioctl (tb -> tb_fd, FIONBIO, (onoff = 1, (char *) &onoff));
+
+       nc = sendmsg (tb -> tb_fd, msg, MSG_EOR);
+
+       (void) ioctl (tb -> tb_fd, FIONBIO, (onoff = 0, (char *) &onoff));
+       (void) signal (SIGPIPE, pstat);
+
+       if (nc != cc) {
+           if (nc == NOTOK) {
+               if (errno != EWOULDBLOCK) {
+                   (void) tsaplose (td, DR_CONGEST, "failed", "sendmsg");
+                   goto losing;
+               }
+
+               nc = 0;
+           }
+           if ((*tb -> tb_queuePfnx) (tb, 1, td) == NOTOK)
+               goto losing;
+
+           qb -> qb_data += nc, qb -> qb_len -= nc;
+insert: ;
+           insque (qb, tb -> tb_qwrites.qb_back);
+           DLOG (tsap_log, LLOG_TRACE,
+                 ("queueing blocked write of %d of %d octets", nc, cc));
+       }
+       else
+           free ((char *) qb);
+       goto done;
+
+losing: ;
+       free ((char *) qb);
+       freetblk (tb);
+
+       return NOTOK;
+    }
+
+    pstat = signal (SIGPIPE, SIG_IGN);
+
+    ep = (bp = uv -> uv_base) + (cc = uv -> uv_len);
+    while (uv -> uv_base) {
+       wv = (vv = iovs) + MSG_MAXIOVLEN;
+       for (len = tb -> tb_tsdusize; len > 0 && vv < wv; len -= j) {
+           j = min (cc, len);
+#ifdef MGMT
+           dlen += j;
+#endif
+           vv -> iov_base = bp, vv -> iov_len = j, vv++;
+           bp += j, cc -= j;
+
+           if (bp >= ep) {
+               if ((bp = (++uv) -> uv_base) == NULL)
+                   break;
+               ep = bp + (cc = uv -> uv_len);
+           }
+       }
+
+       if (expedited || uv -> uv_base == NULL)
+           flags |= MSG_EOR;
+
+       msg -> msg_iov = iovs;
+       msg -> msg_iovlen = vv - iovs;
+
+       if (sendmsg (tb -> tb_fd, msg, flags) == NOTOK) {
+           (void) tsaplose (td, DR_CONGEST, "failed", "sendmsg");
+           freetblk (tb);
+
+           (void) signal (SIGPIPE, pstat);
+           return NOTOK;
+       }
+    }
+
+    (void) signal (SIGPIPE, pstat);
+
+done: ;
+#ifdef  MGMT
+    if (tb -> tb_manfnx)
+       (*tb -> tb_manfnx) (USERDT, tb, dlen);
+#endif
+
+    return OK;
+}
+
+/* \f */
+
+STATIC int  TDrain (tb, td)
+register struct tsapblk *tb;
+struct TSAPdisconnect *td;
+{
+    int            nc,
+           onoff,
+           result;
+    register struct qbuf *qb;
+    register struct msghdr *msg = &msgs;
+    struct iovec vvs;
+    register struct iovec *vv = &vvs;
+    SFP            pstat;
+    SBV            smask;
+
+    bzero ((char *) msg, sizeof *msg);
+    msg -> msg_iov = vv, msg -> msg_iovlen = 1;
+
+    pstat = signal (SIGPIPE, SIG_IGN);
+    smask = sigioblock ();
+
+    (void) ioctl (tb -> tb_fd, FIONBIO, (onoff = 1, (char *) &onoff));
+
+    while ((qb = tb -> tb_qwrites.qb_forw) != &tb -> tb_qwrites) {
+       vv -> iov_base = qb -> qb_data, vv -> iov_len = qb -> qb_len;
+
+       if (nc = sendmsg (tb -> tb_fd, msg, MSG_EOR) != qb -> qb_len) {
+           if (nc == NOTOK) {
+               if (errno != EWOULDBLOCK) {
+                   result = tsaplose (td, DR_NETWORK, "failed",
+                                     "write to network");
+                   goto out;
+               }
+
+               nc = 0;
+           }
+
+           qb -> qb_data += nc, qb -> qb_len -= nc;
+           DLOG (tsap_log, LLOG_TRACE,
+                 ("wrote %d of %d octets from blocked write", nc,
+                  qb -> qb_len));
+
+           result = OK;
+           goto out;
+       }
+
+       DLOG (tsap_log, LLOG_TRACE,
+             ("finished blocked write of %d octets", qb -> qb_len));
+       remque (qb);
+       free ((char *) qb);
+    }
+    result = DONE;
+
+out: ;
+    (void) ioctl (tb -> tb_fd, FIONBIO, (onoff = 0, (char *) &onoff));
+
+    (void) sigiomask (smask);
+    (void) signal (SIGPIPE, pstat);
+
+    return result;
+}
+
+/* \f */
+
+/* ARGSUSED */
+
+STATIC int  TRead (tb, tx, td, async, oob)
+register struct tsapblk *tb;
+register struct TSAPdata *tx;
+struct TSAPdisconnect *td;
+int    async,
+       oob;
+{
+    int            cc;
+    register struct qbuf *qb;
+    register struct msghdr *msg = &msgs;
+    register union osi_control_msg *oc = &ocm;
+    struct iovec iovs[1];
+    static struct qbuf *spare_qb = 0;
+
+    bzero ((char *) tx, sizeof *tx);
+    tx -> tx_qbuf.qb_forw = tx -> tx_qbuf.qb_back = &tx -> tx_qbuf;
+
+    for (;;) {
+       qb = NULL;
+       if (spare_qb) {
+               if (spare_qb -> qb_len >= tb -> tb_tsdusize)
+                       qb = spare_qb;
+               else
+                       free ((char *)spare_qb);
+               spare_qb = NULL;
+       }
+       if (qb == NULL && (qb = (struct qbuf *) malloc ((unsigned) (sizeof *qb
+                                                    + tb -> tb_tsdusize)))
+               == NULL) {
+           (void) tsaplose (td, DR_CONGEST, NULLCP, NULLCP);
+           break;
+       }
+       qb -> qb_data = qb -> qb_base;
+
+       bzero ((char *) msg, sizeof *msg);
+       msg -> msg_iov = iovs;
+       msg -> msg_iovlen = 1;
+       msg -> msg_control = oc -> ocm_data;
+       msg -> msg_controllen = sizeof oc -> ocm_data;
+
+       iovs[0].iov_base = qb -> qb_data;
+       iovs[0].iov_len = tb -> tb_tsdusize;
+
+       bzero ((char *) oc, sizeof *oc);
+
+       if ((cc = recvmsg(tb -> tb_fd, msg, 0)) == NOTOK) {
+       /*if ((cc = recvmsg(tb -> tb_fd, msg, oob ? MSG_OOB : 0)) == NOTOK) */
+#ifndef TPOPT_DISC_REASON
+           if (!(errno & TP_ERROR_MASK)) {
+               (void) tsaplose (td, DR_CONGEST, "failed", "recvfrom");
+               break;
+           }
+
+           if ((td -> td_reason = errno & ~TP_ERROR_MASK) != DR_NORMAL)
+               SLOG (tsap_log, LLOG_EXCEPTIONS, NULLCP,
+                     ("TP error %d", td -> td_reason));
+#else
+           /* This should never happen */
+           SLOG (tsap_log, LLOG_EXCEPTIONS, NULLCP, ("TRead after close"));
+#endif
+           td -> td_cc = 0;
+           break;
+       }
+
+       if (msg -> msg_controllen) {
+           if (msg -> msg_controllen < sizeof oc -> ocm_control.ocm_cmhdr) {
+               (void) tsaplose (td, DR_CONGEST, NULLCP,
+                   "truncated control message, got %d expecting at least %d",
+                                msg -> msg_controllen,
+                                sizeof oc -> ocm_control.ocm_cmhdr);
+               break;
+           }
+
+           if (oc -> ocm_control.ocm_cmhdr.cmsg_level != SOL_TRANSPORT) {
+               (void) tsaplose (td, DR_CONGEST, NULLCP,
+                                "unexpected message (level 0x%x, type 0x%x)",
+                                oc -> ocm_control.ocm_cmhdr.cmsg_level,
+                                oc -> ocm_control.ocm_cmhdr.cmsg_type);
+               break;
+           }
+           td -> td_reason = 0;
+#ifdef TPOPT_DISC_REASON
+           if (oc -> ocm_control.ocm_cmhdr.cmsg_type == TPOPT_DISC_REASON) {
+               td -> td_reason =
+                           ((struct tp_disc_reason *)oc) -> dr_reason;
+               oc = (union osi_control_msg *)
+                           (1 + ((struct tp_disc_reason *)oc));
+           }
+#endif
+           if (oc -> ocm_control.ocm_cmhdr.cmsg_type == TPOPT_DISC_DATA) {
+               if (td -> td_reason == 0)
+                       td -> td_reason = DR_NORMAL;
+               if ((td -> td_cc = oc -> ocm_control.ocm_cmhdr.cmsg_len
+                                   - sizeof oc -> ocm_control.ocm_cmhdr) > 0)
+                   bcopy (oc -> ocm_control.ocm_cmdata, td -> td_data, cc);
+               break;
+           }
+       }
+
+       if (msg -> msg_flags & MSG_OOB) {
+           if (cc > 0) {
+               insque (qb, tx -> tx_qbuf.qb_back);
+               tx -> tx_cc = (qb -> qb_len = cc);
+           }
+           else
+               free ((char *) qb);
+           tx -> tx_expedited = 1;
+
+           return OK;
+       }
+
+       tb -> tb_len += (qb -> qb_len = cc);
+       if (cc > 0) {
+           register struct qbuf *qb2 = tb -> tb_qbuf.qb_back;
+
+           if (qb2 != &tb->tb_qbuf && qb2->qb_len + cc <= tb->tb_tsdusize) {
+               bcopy(qb -> qb_data, qb2 -> qb_len + qb2 -> qb_data, cc);
+               qb2 -> qb_len += cc;
+               (spare_qb = qb) -> qb_len = tb -> tb_tsdusize;
+           } else
+               insque (qb, qb2);
+       } else
+           free ((char *) qb);
+
+#ifdef MGMT
+       if (tb -> tb_manfnx)
+           (*tb -> tb_manfnx) (USERDR, tb, tb -> tb_len);
+#endif
+       if (!(msg -> msg_flags & MSG_EOR)) {
+           if (async) {
+               register struct qbuf *qb2 = tb -> tb_qbuf.qb_back;
+               if (qb2 != &tb->tb_qbuf && qb2->qb_len <= tb->tb_tsdusize)
+                       printf("short return %d\n", qb2->qb_len);
+               return DONE;
+           }
+           continue;
+       }
+       tx -> tx_expedited = 0;
+       if (tb -> tb_qbuf.qb_forw != &tb -> tb_qbuf) {
+           tx -> tx_qbuf = tb -> tb_qbuf;      /* struct copy */
+           tx -> tx_qbuf.qb_forw -> qb_back =
+               tx -> tx_qbuf.qb_back -> qb_forw = &tx -> tx_qbuf;
+           tx -> tx_cc = tb -> tb_len;
+           tb -> tb_qbuf.qb_forw =
+               tb -> tb_qbuf.qb_back = &tb -> tb_qbuf;
+           tb -> tb_len = 0;
+       }
+
+       {
+           register struct qbuf *qb2 = tb -> tb_qbuf.qb_back;
+           if (qb2 != &tb->tb_qbuf && qb2->qb_len <= tb->tb_tsdusize)
+                   printf("short return %d\n", qb2->qb_len);
+       }
+       return OK;
+    }
+    if (qb)
+       free ((char *) qb);
+
+    freetblk (tb);
+
+    return NOTOK;
+}
+
+/* \f */
+
+STATIC int  TDisconnect (tb, data, cc, td)
+register struct tsapblk *tb;
+char   *data;
+int    cc;
+struct TSAPdisconnect *td;
+{
+    int            result;
+
+    if (sendCmsg(tb -> tb_fd, cc, TPOPT_DISC_DATA, data) == NOTOK)
+       result = tsaplose (td, DR_CONGEST, "TPOPT_DISC_DATA",
+                          "unable to send");
+    else
+       result = OK;
+
+    freetblk (tb);
+
+    return result;
+}
+/* \f */
+
+/* ARGSUSED */
+
+STATIC int  TLose (tb, reason, td)
+register struct tsapblk *tb;
+int    reason;
+struct TSAPdisconnect *td;
+{
+    register struct msghdr *msg = &msgs;
+    register union osi_control_msg *oc = &ocm;
+
+    SLOG (tsap_log, LLOG_EXCEPTIONS, NULLCP, ("TPM error %d", reason));
+
+    bzero ((char *) msg, sizeof *msg);
+    msg -> msg_control = oc -> ocm_data;
+    msg -> msg_controllen = sizeof oc -> ocm_data;
+
+    bzero ((char *) oc, sizeof *oc);
+    oc -> ocm_control.ocm_cmhdr.cmsg_level = SOL_TRANSPORT;
+    oc -> ocm_control.ocm_cmhdr.cmsg_type = TPOPT_DISC_DATA;
+    oc -> ocm_control.ocm_cmhdr.cmsg_len = sizeof oc -> ocm_control.ocm_cmhdr;
+
+    if (sendmsg (tb -> tb_fd, msg, 0) == NOTOK)
+       SLOG (tsap_log, LLOG_EXCEPTIONS, "TPOPT_DISC_DATA",
+             ("unable to send"));
+}
+
+/* \f   LOWER HALF */
+
+/* ARGSUSED */
+
+int    tp4open (tb, local_ta, local_na, remote_ta, remote_na, td, async)
+register struct tsapblk *tb;
+struct TSAPaddr *local_ta,
+               *remote_ta;
+struct NSAPaddr *local_na,
+               *remote_na;
+struct TSAPdisconnect *td;
+int    async;
+{
+    int            fd,
+           onoff;
+    struct TSAPaddr tzs;
+    register struct TSAPaddr *tz = &tzs;
+    register struct NSAPaddr *nz = tz -> ta_addrs;
+    union sockaddr_osi sock;
+    struct sockaddr_iso        *ifaddr = &sock.osi_sockaddr;
+
+    bzero ((char *) tz, sizeof *tz);
+    if (local_ta)
+       *tz = *local_ta;        /* struct copy */
+    if (local_na) {
+       *nz = *local_na;        /* struct copy */
+       tz -> ta_naddr = 1;
+    }
+
+    (void) gen2tp4 (tz, &sock);
+
+    if ((fd = socket (AF_ISO, SOCK_SEQPACKET, 0)) == NOTOK)
+       return tsaplose (td, DR_CONGEST, "socket", "unable to start");
+
+    if (ifaddr->siso_nlen || ifaddr->siso_tlen)
+       if (bind (fd, (struct sockaddr *) ifaddr, ifaddr -> siso_len) == NOTOK) {
+       (void) tsaplose (td, DR_ADDRESS, "socket", "unable to bind");
+       (void) close (fd);
+       return NOTOK;
+    }
+
+    tb -> tb_fd = fd;
+    (void) tp4init (tb);
+
+    if (async)
+       (void) ioctl (fd, FIONBIO, (onoff = 1, (char *) &onoff));
+
+    return (async ? OK : DONE);
+}
+
+/* \f */
+
+/* ARGSUSED */
+
+STATIC int  retry_tp4_socket (tb, td)
+register struct tsapblk *tb;
+struct TSAPdisconnect *td;
+{
+    fd_set  mask;
+
+    FD_ZERO (&mask);
+    FD_SET (tb -> tb_fd, &mask);
+    if (xselect (tb -> tb_fd + 1, NULLFD, &mask, NULLFD, 0) < 1)
+       return OK;
+
+    return DONE;
+}
+
+/* \f */
+
+/* ARGSUSED */
+
+char   *tp4save (fd, td)
+int fd;
+struct TSAPdisconnect *td;
+{
+    static char buffer[BUFSIZ];
+
+    (void) sprintf (buffer, "%c%d", NT_BSD, fd);
+    return buffer;
+}
+
+/* \f */
+
+int    tp4restore (tb, buffer, td)
+register struct tsapblk *tb;
+char   *buffer;
+struct TSAPdisconnect *td;
+{
+    int            fd, len, ucdlen;
+    union sockaddr_osi sock;
+    struct sockaddr_iso        *ifaddr = &sock.osi_sockaddr;
+
+    if (sscanf (buffer, "%d", &fd) != 1 || fd < 0)
+       return tsaplose (td, DR_PARAMETER, NULLCP,
+                       "bad initialization vector \"%s\"", buffer);
+
+    tb -> tb_fd = fd;
+    (void) tp4init (tb);
+    len = sizeof sock;
+    if (getsockname (fd, (struct sockaddr *) ifaddr, &len) == NOTOK) {
+       (void) tsaplose (td, DR_CONGEST, "listen",
+                        "getsockname failed for tp4restore");
+       (void) close (fd);
+       return NOTOK;
+    }
+    ifaddr -> siso_len = len;
+    (void) tp42genX (&tb -> tb_responding, &sock);
+    len = sizeof sock;
+    if (getpeername (fd, (struct sockaddr *) ifaddr, &len) == NOTOK) {
+       (void) tsaplose (td, DR_CONGEST, "listen",
+                        "getpeername failed for tp4restore");
+       (void) close (fd);
+       return NOTOK;
+    }
+    ifaddr -> siso_len = len;
+    (void) tp42genX (&tb -> tb_initiating, &sock);
+    return OK;
+}
+
+/* \f */
+
+int    tp4init (tb)
+register struct tsapblk *tb;
+{
+    tb -> tb_connPfnx = TConnect;
+    tb -> tb_retryPfnx = TRetry;
+
+    tb -> tb_startPfnx = TStart;
+    tb -> tb_acceptPfnx = TAccept;
+
+    tb -> tb_writePfnx = TWrite;
+    tb -> tb_readPfnx = TRead;
+    tb -> tb_discPfnx = TDisconnect;
+    tb -> tb_losePfnx = TLose;
+
+    tb -> tb_drainPfnx = TDrain;
+
+#ifdef  MGMT
+    tb -> tb_manfnx = TManGen;
+#endif
+
+    tb -> tb_flags |= TB_TP4;
+
+    tb -> tb_tsdusize = MAXTP4 - (tb -> tb_tpduslop = 0);
+
+    tb -> tb_retryfnx = retry_tp4_socket;
+
+    tb -> tb_closefnx = close_tp4_socket;
+    tb -> tb_selectfnx = select_tp4_socket;
+}
+
+/* \f */
+
+/* ARGSUSED */
+
+int    start_tp4_server (local_ta, backlog, opt1, opt2, td)
+struct TSAPaddr *local_ta;
+int    backlog,
+       opt1,
+       opt2;
+struct TSAPdisconnect *td;
+{
+    int            sd,
+           onoff;
+    register struct NSAPaddr *na = local_ta -> ta_addrs;
+    union sockaddr_osi sock;
+    struct sockaddr_iso        *ifaddr = &sock.osi_sockaddr;
+
+    (void) gen2tp4 (local_ta, &sock);
+
+    if ((sd = socket (AF_ISO, SOCK_SEQPACKET, 0)) == NOTOK)
+       return tsaplose (td, DR_CONGEST, "socket", "unable to start");
+
+    if (bind (sd, (struct sockaddr *) ifaddr, ifaddr -> siso_len) == NOTOK) {
+       (void) tsaplose (td, DR_ADDRESS, "socket", "unable to bind");
+       (void) close (sd);
+       return NOTOK;
+    }
+
+    if (na -> na_addrlen == 0) {       /* unique listen */
+       int     len;
+
+       len = sizeof sock;
+       if (getsockname (sd, (struct sockaddr *) ifaddr, &len) == NOTOK) {
+           (void) tsaplose (td, DR_CONGEST, "listen",
+                            "getsockname failed for unique");
+           (void) close (sd);
+           return NOTOK;
+       }
+       ifaddr -> siso_len = len;
+
+       (void) tp42gen (local_ta, &sock);
+    }
+
+    if (opt1)
+       (void) setsockopt (sd, SOL_SOCKET, opt1, NULLCP, 0);
+    if (opt2)
+       (void) setsockopt (sd, SOL_SOCKET, opt2, NULLCP, 0);
+    
+    onoff = 1;
+
+    (void) listen (sd, backlog);
+
+    return sd;
+}
+
+/* \f */
+
+int    join_tp4_client (fd, remote_ta, td)
+int    fd;
+struct TSAPaddr *remote_ta;
+struct TSAPdisconnect *td;
+{
+    int            len,
+           sd;
+    union sockaddr_osi sock;
+    struct sockaddr_iso        *ifaddr = &sock.osi_sockaddr;
+
+    len = sizeof sock;
+    if ((sd = accept (fd, (struct sockaddr *) ifaddr, &len)) == NOTOK)
+       return tsaplose (td, DR_NETWORK, "socket", "unable to accept");
+    ifaddr -> siso_len = len;
+       
+    (void) tp42gen (remote_ta, &sock);
+    
+    return sd;
+}
+
+/* \f */
+
+STATIC int  gen2tp4 (generic, specific)
+struct TSAPaddr *generic;
+union sockaddr_osi *specific;
+{
+    register char *cp;
+    struct sockaddr_iso        *ifaddr = &specific -> osi_sockaddr;
+
+    bzero ((char *) specific, sizeof *specific);
+
+    cp = ifaddr -> siso_data;
+
+    if (generic -> ta_naddr > 0) {
+       register struct NSAPaddr *na = generic -> ta_addrs;
+
+       if (ifaddr -> siso_nlen = na -> na_addrlen) {
+           bcopy (na -> na_address, cp, na -> na_addrlen);
+           cp += na -> na_addrlen;
+       }
+    }
+
+    if (ifaddr -> siso_tlen = generic -> ta_selectlen) {
+       bcopy (generic -> ta_selector, cp, generic -> ta_selectlen);
+       cp += generic -> ta_selectlen;
+    }
+
+    if ((ifaddr -> siso_len = cp - (char *) ifaddr) < sizeof *ifaddr)
+       ifaddr -> siso_len = sizeof *ifaddr;
+    ifaddr -> siso_family = AF_ISO;
+
+    return OK;
+}
+
+
+
+STATIC int  gen2tp4X (generic, specific)
+struct tsapADDR *generic;
+union sockaddr_osi *specific;
+{
+    struct TSAPaddr tas;
+
+    copyTSAPaddrX (generic, &tas);
+    return gen2tp4 (&tas, specific);
+}
+
+/* \f */
+
+int    tp42gen (generic, specific)
+struct TSAPaddr *generic;
+union sockaddr_osi *specific;
+{
+    register char *cp;
+    register struct NSAPaddr *na = generic -> ta_addrs;
+    struct sockaddr_iso        *ifaddr = &specific -> osi_sockaddr;
+
+    bzero ((char *) generic, sizeof *generic);
+
+    cp = ifaddr -> siso_data;
+    if (na -> na_addrlen = ifaddr -> siso_nlen) {
+       na -> na_stack = NA_NSAP;
+       na -> na_community = ts_comm_nsap_default;
+       bcopy (cp, na -> na_address, na -> na_addrlen);
+       cp += na -> na_addrlen;
+
+       generic -> ta_naddr++;
+    }
+
+    if (generic -> ta_selectlen = ifaddr -> siso_tlen)
+       bcopy (cp, generic -> ta_selector, generic -> ta_selectlen);
+
+    return OK;
+}
+
+
+int    tp42genX (generic, specific)
+struct tsapADDR *generic;
+union sockaddr_osi *specific;
+{
+    int            result;
+    struct TSAPaddr tas;
+
+    if ((result = tp42gen (&tas, specific)) == OK)
+       copyTSAPaddrY (&tas, generic);
+
+    return result;
+}
+#else
+int    _ts2bsd_stub () {};
+#endif