date and time created 88/12/14 15:30:22 by sklower
authorKeith Sklower <sklower@ucbvax.Berkeley.EDU>
Thu, 15 Dec 1988 07:30:22 +0000 (23:30 -0800)
committerKeith Sklower <sklower@ucbvax.Berkeley.EDU>
Thu, 15 Dec 1988 07:30:22 +0000 (23:30 -0800)
SCCS-vsn: sys/netiso/tp_usrreq.c 7.1

usr/src/sys/netiso/tp_usrreq.c [new file with mode: 0644]

diff --git a/usr/src/sys/netiso/tp_usrreq.c b/usr/src/sys/netiso/tp_usrreq.c
new file mode 100644 (file)
index 0000000..6781a47
--- /dev/null
@@ -0,0 +1,784 @@
+/***********************************************************
+               Copyright IBM Corporation 1987
+
+                      All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its 
+documentation for any purpose and without fee is hereby granted, 
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in 
+supporting documentation, and that the name of IBM not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.  
+
+IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+
+******************************************************************/
+
+/*
+ * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
+ */
+/* 
+ * ARGO TP
+ *
+ * $Header: tp_usrreq.c,v 5.4 88/11/18 17:29:18 nhall Exp $
+ * $Source: /usr/argo/sys/netiso/RCS/tp_usrreq.c,v $
+ *
+ * tp_usrreq(), the fellow that gets called from most of the socket code.
+ * Pretty straighforward.
+ * THe only really awful stuff here is the OOB processing, which is done
+ * wholly here.
+ * tp_rcvoob() and tp_sendoob() are contained here and called by tp_usrreq().
+ */
+
+#ifndef lint
+static char *rcsid = "$Header: tp_usrreq.c,v 5.4 88/11/18 17:29:18 nhall Exp $";
+#endif lint
+
+#include "param.h"
+#include "systm.h"
+#include "dir.h"
+#include "user.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "socketvar.h"
+#include "domain.h"
+#include "protosw.h"
+#include "errno.h"
+
+#include "../netiso/tp_param.h"
+#include "../netiso/tp_timer.h"
+#include "../netiso/tp_stat.h"
+#include "../netiso/tp_seq.h"
+#include "../netiso/tp_ip.h"
+#include "../netiso/tp_pcb.h"
+#include "../netiso/argo_debug.h"
+#include "../netiso/tp_trace.h"
+#include "../netiso/tp_meas.h"
+#include "../netiso/iso.h"
+#include "../netiso/iso_errno.h"
+
+int tp_attach(), tp_driver();
+
+#ifdef ARGO_DEBUG
+/*
+ * CALLED FROM:
+ *  anywhere you want to debug...
+ * FUNCTION and ARGUMENTS:
+ *  print (str) followed by the control info in the mbufs of an mbuf chain (n)
+ */
+void
+dump_mbuf(n, str)
+       struct mbuf *n;
+       char *str;
+{
+       struct mbuf *nextrecord;
+
+       printf("dump %s\n", str);
+
+       if( n == MNULL)  {
+               printf("EMPTY:\n");
+               return;
+       }
+               
+       for(;n;) {
+               nextrecord = n->m_act;
+               printf("RECORD:\n");
+               while (n) {
+                       printf("%x : Len %x Of %x A %x Nx %x Tp %x\n",
+                               n, n->m_len, n->m_off, n->m_act, n->m_next, n->m_type);
+#ifdef notdef
+                       {
+                               register char *p = mtod(n, char *);
+                               register int i;
+
+                               printf("data: ");
+                               for(i=0; i < n->m_len; i++ ) {
+                                       if(i%8 == 0)
+                                               printf("\n");
+                                       printf("0x%x ", *(p+i));
+                               }
+                               printf("\n");
+                       }
+#endif notdef
+                       if( n->m_next == n ) {
+                               printf("LOOP!\n");
+                               return;
+                       }
+                       n = n->m_next;
+               }
+               n = nextrecord;
+       }
+       printf("\n");
+}
+
+#endif ARGO_DEBUG
+
+/*
+ * CALLED FROM:
+ *  tp_usrreq(), PRU_RCVOOB
+ * FUNCTION and ARGUMENTS:
+ *     Copy data from the expedited data socket buffer into
+ *     the pre-allocated mbuf m.
+ *     There is an isomorphism between XPD TPDUs and expedited data TSDUs.
+ *     XPD tpdus are limited to 16 bytes of data so they fit in one mbuf.
+ * RETURN VALUE:
+ *  EINVAL if debugging is on and a disaster has occurred
+ *  ENOTCONN if the socket isn't connected
+ *  EWOULDBLOCK if the socket is in non-blocking mode and there's no
+ *             xpd data in the buffer
+ *  E* whatever is returned from the fsm.
+ */
+static int 
+tp_rcvoob(tpcb, so, m, outflags, inflags)
+       struct tp_pcb   *tpcb;
+       register struct socket  *so;
+       register struct mbuf    *m;
+       int                     *outflags;
+       int                     inflags;
+{
+       register struct mbuf *n;
+       register struct sockbuf *sb = &tpcb->tp_Xrcv;
+       struct tp_event E;
+       int error = 0;
+
+       IFDEBUG(D_XPD)
+               printf("PRU_RCVOOB, sostate 0x%x\n", so->so_state);
+       ENDDEBUG
+
+       /* if you use soreceive */
+       if (m==MNULL)
+               return ENOBUFS;
+
+restart:
+       sblock(sb);
+
+       if ((((so->so_state & SS_ISCONNECTED) == 0)
+                || (so->so_state & SS_ISDISCONNECTING) != 0) &&
+               (so->so_proto->pr_flags & PR_CONNREQUIRED)) {
+                       return ENOTCONN;
+       }
+
+       if ( sb->sb_cc == 0) {
+               ASSERT( (tpcb->tp_flags & TPF_DISC_DATA_IN)  == 0 );
+               IFDEBUG(D_XPD)
+                       printf("RCVOOB: empty queue!\n");
+               ENDDEBUG
+               if (so->so_state & SS_NBIO) {
+                       return  EWOULDBLOCK;
+               }
+               sbunlock(sb);
+               sbwait(sb);
+               goto restart;
+       }
+       /* Take the first mbuf off the chain.
+        * Each XPD TPDU gives you a complete TSDU so the chains don't get 
+        * coalesced, but one TSDU may span several mbufs.
+        * Nevertheless, since n should have a most 16 bytes, it
+        * will fit into m.  (size was checked in tp_input() )
+        */
+
+       n = sb->sb_mb;
+       ASSERT((n->m_type == TPMT_DATA) ||
+               n->m_type == TPMT_IPHDR || n->m_type == TPMT_TPHDR);
+
+       /* 
+        * mtod doesn't work for cluster-type mbufs, hence this disaster check: 
+        */
+       if( n->m_off > MMAXOFF )
+               panic("tp_rcvoob: unexpected cluster ");
+
+       m->m_next = MNULL;
+       m->m_act = MNULL;
+       m->m_off = MMINOFF;
+       m->m_len = 0;
+
+       /* Assuming at most one xpd tpdu is in the buffer at once */
+       while ( n != MNULL ) {
+               m->m_len += n->m_len;
+               bcopy(mtod(n, caddr_t), mtod(m, caddr_t), n->m_len);
+               m->m_off += n->m_len; /* so mtod() in bcopy() above gives right addr */
+               n = n->m_next;
+       }
+       m->m_off = MMINOFF; /* restore it to its proper value */
+
+       IFDEBUG(D_XPD)
+               printf("tp_rcvoob: xpdlen 0x%x\n", m->m_len);
+               dump_mbuf(so->so_rcv.sb_mb, "RCVOOB: Rcv socketbuf");
+               dump_mbuf(sb->sb_mb, "RCVOOB: Xrcv socketbuf");
+       ENDDEBUG
+
+       if( (inflags & MSG_PEEK) == 0 )
+               sbdrop(sb, m->m_len);
+
+release:
+       sbunlock(sb);
+
+       IFTRACE(D_XPD)
+               tptraceTPCB(TPPTmisc, "PRU_RCVOOB @ release sb_cc m_len",
+                       tpcb->tp_Xrcv.sb_cc, m->m_len,0,0 );
+       ENDTRACE
+       if(outflags)
+               *outflags = MSG_OOB | MSG_EOTSDU | inflags; /* always on xpd recv */
+       if (error == 0)
+               error = DoEvent(T_USR_Xrcvd); 
+       return error;
+}
+
+/*
+ * CALLED FROM:
+ *  tp_usrreq(), PRU_SENDOOB
+ * FUNCTION and ARGUMENTS:
+ *     Send what's in the mbuf chain (m) as an XPD TPDU.
+ *     The mbuf may not contain more then 16 bytes of data.
+ *     XPD TSDUs aren't segmented, so they translate into
+ *     exactly one XPD TPDU, with EOT bit set.
+ * RETURN VALUE:
+ *  EWOULDBLOCK if socket is in non-blocking mode and the previous
+ *   xpd data haven't been acked yet.
+ *  EMSGSIZE if trying to send > max-xpd bytes (16)
+ *  ENOBUFS if ran out of mbufs
+ */
+static int
+tp_sendoob(tpcb, so, xdata, outflags)
+       struct tp_pcb   *tpcb;
+       register struct socket  *so;
+       register struct mbuf    *xdata;
+       int                     *outflags; /* not used */
+{
+       /*
+        * Each mbuf chain represents a sequence # in the XPD seq space.
+        * The first one in the queue has sequence # tp_Xuna.
+        * When we add to the XPD queue, we stuff a zero-length
+        * mbuf (mark) into the DATA queue, with its sequence number in m_next
+        * to be assigned to this XPD tpdu, so data xfer can stop
+        * when it reaches the zero-length mbuf if this XPD TPDU hasn't
+        * yet been acknowledged.  
+        */
+       register struct sockbuf *sb = &(tpcb->tp_Xsnd);
+       register struct mbuf    *xmark;
+       register int                    len=0;
+       struct tp_event E;
+
+       IFDEBUG(D_XPD)
+               printf("tp_sendoob:");
+               if(xdata)
+                       printf("xdata len 0x%x\n", xdata->m_len);
+       ENDDEBUG
+oob_again:
+       /* DO NOT LOCK the Xsnd buffer!!!! You can have at MOST one 
+        * socket buf locked at any time!!! (otherwise you might
+        * sleep() in sblock() w/ a signal pending and cause the
+        * system call to be aborted w/ a locked socketbuf, which
+        * is a problem.  So the so_snd buffer lock
+        * (done in sosend()) serves as the lock for Xpd.
+        */
+       if (sb->sb_mb) { /* anything already in this sockbuf? */
+               if (so->so_state & SS_NBIO) {
+                       return EWOULDBLOCK;
+               }
+               sbunlock(&so->so_snd);
+               sbwait(&so->so_snd);
+               sblock(&so->so_snd);
+               goto oob_again;
+       }
+
+       if (xdata == (struct mbuf *)0) {
+               /* empty xpd packet */
+               MGET(xdata, M_WAIT, TPMT_DATA);
+               if (xdata == NULL) {
+                       return ENOBUFS;
+               }
+               xdata->m_len = 0;
+               xdata->m_act = MNULL;
+       }
+       IFDEBUG(D_XPD)
+               printf("tp_sendoob 1:");
+               if(xdata)
+                       printf("xdata len 0x%x\n", xdata->m_len);
+       ENDDEBUG
+       xmark = xdata; /* temporary use of variable xmark */
+       while (xmark) {
+               len += xmark->m_len;
+               xmark = xmark->m_next;
+       }
+       if (len > TP_MAX_XPD_DATA) {
+               return EMSGSIZE;
+       }
+       IFDEBUG(D_XPD)
+               printf("tp_sendoob 2:");
+               if(xdata)
+                       printf("xdata len 0x%x\n", len);
+       ENDDEBUG
+
+       /* stick an xpd mark in the normal data queue
+        * make sure we have enough mbufs before we stick anything into any queues
+        */
+       MGET(xmark,M_WAIT, TPMT_XPD);
+       if (xmark == MNULL) {
+               return ENOBUFS;
+       }
+       xmark->m_len = 0;
+       xmark->m_act = MNULL;
+       
+       {       /* stash the xpd sequence number in the mark */ 
+               SeqNum *Xuna = mtod(xmark, SeqNum *);
+               *Xuna = tpcb->tp_Xuna;
+       }
+
+       IFTRACE(D_XPD)
+               tptraceTPCB(TPPTmisc, "XPD mark m_next ", xmark->m_next, 0, 0, 0);
+       ENDTRACE
+
+       sbappendrecord(&so->so_snd, xmark); /* the mark */
+       sbappendrecord(sb, xdata);      
+
+       IFDEBUG(D_XPD)
+               printf("tp_sendoob len 0x%x\n", len);
+               dump_mbuf(so->so_snd.sb_mb, "XPD request Regular sndbuf:");
+               dump_mbuf(tpcb->tp_Xsnd.sb_mb, "XPD request Xsndbuf:");
+       ENDDEBUG
+       u.u_r.r_val1  += len; 
+       return DoEvent(T_XPD_req); 
+
+}
+
+/*
+ * CALLED FROM:
+ *  the socket routines
+ * FUNCTION and ARGUMENTS:
+ *     Handles all "user requests" except the [gs]ockopts() requests.
+ *     The argument (req) is the request type (PRU*), 
+ *     (m) is an mbuf chain, generally used for send and
+ *     receive type requests only.
+ *     (nam) is used for addresses usually, in particular for the bind request.
+ * 
+ *     The last argument (rights in most usrreq()s) has been stolen for 
+ *     returning flags values.  Since rights can't be passed around w/ tp,
+ *     this field is used only for RCVOOB user requests, and is assumed
+ *     to be either 0 (as soreceive() would have it) or a ptr to the int flags
+ *     (as recvv()'s version of soreceive() would have it
+ */
+/*ARGSUSED*/
+ProtoHook
+tp_usrreq(so, req, m, nam, rightsp, outflags)
+       struct socket *so;
+       u_int req;
+       struct mbuf *m, *nam, *rightsp /* not used */;
+       int *outflags; 
+{      
+       register struct tp_pcb *tpcb =  sototpcb(so);
+       int s = splnet();
+       int error = 0;
+       u_long eotsdu = 0;
+       struct tp_event E;
+
+       IFDEBUG(D_REQUEST)
+               printf("usrreq(0x%x,%d,0x%x,0x%x,0x%x)\n",so,req,m,nam,outflags);
+               if(so->so_error)
+                       printf("WARNING!!! so->so_error is 0x%x\n", so->so_error);
+       ENDDEBUG
+       IFTRACE(D_REQUEST)
+               tptraceTPCB(TPPTusrreq, "req so m state [", req, so, m, 
+                       tpcb?tpcb->tp_state:0);
+       ENDTRACE
+
+       if ((u_int)tpcb == 0 && req != PRU_ATTACH) {
+               IFTRACE(D_REQUEST)
+                       tptraceTPCB(TPPTusrreq, "req failed NO TPCB[", 0, 0, 0, 0);
+               ENDTRACE
+               splx(s);
+               return ENOTCONN;
+       }
+
+
+       IFDEBUG(D_XPD)
+               extern struct mbuf *mfree;
+               struct mbuf *m = mfree, *n=MNULL;
+
+               if ( (u_int) tpcb != 0 )  {
+                       n = tpcb->tp_Xrcv.sb_mb;
+                       if(n) while(m) {
+                               if(m == n) {
+                               printf("enter usrreq %d Xrcv sb_mb 0x%x is on freelist!\n",
+                                       req, n);
+                               }
+                               m = m->m_next;
+                       }
+               }
+       ENDDEBUG
+
+       switch (req) {
+
+       case PRU_ATTACH:
+               if (tpcb) {
+                       error = EISCONN;
+                       break;
+               }
+               if( error = tp_attach(so, so->so_proto->pr_domain->dom_family ) )
+                       break;
+               tpcb = sototpcb(so);
+               break;
+
+       case PRU_ABORT:         /* called from close() */
+               /* called for each incoming connect queued on the 
+                *      parent (accepting) socket 
+                */
+               if( tpcb->tp_state == TP_OPEN ) {
+                       E.ATTR(T_DISC_req).e_reason = E_TP_NO_SESSION;
+                       error = DoEvent(T_DISC_req); /* pretend it was a close() */
+                       break;
+               } /* else DROP THROUGH */
+
+       case PRU_DETACH:        /* called from close() */
+               /* called only after disconnect was called */
+               error = DoEvent(T_DETACH);
+               break;
+
+       case PRU_SHUTDOWN:
+               /* recv end may have been released; local credit might be zero  */
+       case PRU_DISCONNECT:
+               E.ATTR(T_DISC_req).e_reason = E_TP_NORMAL_DISC;
+               error = DoEvent(T_DISC_req);
+               break;
+
+       case PRU_BIND:
+               error =  (tpcb->tp_nlproto->nlp_pcbbind)( so->so_pcb, nam );
+               if (error == 0) {
+                       tpcb->tp_lsuffixlen = sizeof(short); /* default */ 
+                       *(u_short *)(tpcb->tp_lsuffix) = (u_short) 
+                               (tpcb->tp_nlproto->nlp_getsufx)( so->so_pcb, TP_LOCAL );
+               }
+               break;
+
+       case PRU_LISTEN:
+               if ( *SHORT_LSUFXP(tpcb) == (short)0 ) {
+                       /* note: this suffix is independent of the extended suffix */
+                       if( error = (tpcb->tp_nlproto->nlp_pcbbind)(so->so_pcb, MNULL) )
+                               break;
+               }
+               if( tpcb->tp_lsuffixlen ==  0) {
+                       tpcb->tp_lsuffixlen = sizeof(short); /* default */ 
+                       *SHORT_LSUFXP(tpcb)  = (short) 
+                               (tpcb->tp_nlproto->nlp_getsufx)( so->so_pcb, TP_LOCAL );
+               }
+               IFDEBUG(D_TPISO)
+                       if(tpcb->tp_state != TP_CLOSED)
+                               printf("LISTEN ERROR: state 0x%x\n", tpcb->tp_state);
+               ENDDEBUG
+               error = DoEvent(T_LISTEN_req);
+               break;
+
+       case PRU_CONNECT2:
+               error = EOPNOTSUPP; /* for unix domain sockets */
+               break;
+
+       case PRU_CONNECT:
+               IFTRACE(D_CONN)
+                       tptraceTPCB(TPPTmisc, 
+                       "PRU_CONNECT: so *SHORT_LSUFXP(tpcb) 0x%x lsuflen 0x%x, class 0x%x",
+                       tpcb->tp_sock, *SHORT_LSUFXP(tpcb), tpcb->tp_lsuffixlen,
+                               tpcb->tp_class);
+               ENDTRACE
+               IFDEBUG(D_CONN)
+                       printf("PRU_CONNECT: so *SHORT_LSUFXP(tpcb) 0x%x lsuflen 0x%x, class 0x%x",
+                       tpcb->tp_sock, *SHORT_LSUFXP(tpcb), tpcb->tp_lsuffixlen,
+                               tpcb->tp_class);
+               ENDDEBUG
+               if (*SHORT_LSUFXP(tpcb) == (short)0) {
+                       /* no bind was done */
+                       /* note: this suffix is independent of the extended suffix */
+                       if( error = (tpcb->tp_nlproto->nlp_pcbbind)(so->so_pcb, MNULL) ) {
+                               IFDEBUG(D_CONN)
+                                       printf("pcbbind returns error 0x%x\n", error );
+                               ENDDEBUG
+                               break;
+                       }
+               }
+               if (tpcb->tp_lsuffixlen == 0) { 
+                       /* didn't set an extended suffix */
+                       tpcb->tp_lsuffixlen = sizeof(short);
+                       *SHORT_LSUFXP(tpcb) = (short)
+                                       (tpcb->tp_nlproto->nlp_getsufx)( so->so_pcb, TP_LOCAL );
+               } 
+
+               IFDEBUG(D_CONN)
+                       printf("isop 0x%x isop->isop_socket offset 12 :\n", tpcb->tp_npcb);
+                       dump_buf( tpcb->tp_npcb, 16);
+               ENDDEBUG
+               if( error = tp_route_to( nam, tpcb, /* channel */0) )
+                       break;
+               IFDEBUG(D_CONN)
+                       printf(
+                               "PRU_CONNECT after tpcb 0x%x so 0x%x npcb 0x%x flags 0x%x\n", 
+                               tpcb, so, tpcb->tp_npcb, tpcb->tp_flags);
+                       printf("isop 0x%x isop->isop_socket offset 12 :\n", tpcb->tp_npcb);
+                       dump_buf( tpcb->tp_npcb, 16);
+               ENDDEBUG
+               if( tpcb->tp_fsuffixlen == 0 ) {
+                       /* didn't set peer extended suffix */
+                       tpcb->tp_fsuffixlen = sizeof(short);
+                       *SHORT_FSUFXP(tpcb) = (short)
+                                       (tpcb->tp_nlproto->nlp_getsufx)(so->so_pcb, TP_FOREIGN);
+               }
+               (void) (tpcb->tp_nlproto->nlp_mtu)(so, so->so_pcb,
+                                       &tpcb->tp_l_tpdusize, &tpcb->tp_tpdusize, 0);
+               if( tpcb->tp_state == TP_CLOSED) {
+                       soisconnecting(so);  
+                       error = DoEvent(T_CONN_req);
+               } else {
+                       (tpcb->tp_nlproto->nlp_pcbdisc)(so->so_pcb);
+                       error = EISCONN;
+               }
+               IFPERF(tpcb)
+                       u_int lsufx, fsufx;
+                       lsufx = *(u_int *)(tpcb->tp_lsuffix);
+                       fsufx = *(u_int *)(tpcb->tp_fsuffix);
+
+                       tpmeas( tpcb->tp_lref, 
+                               TPtime_open | (tpcb->tp_xtd_format <<4 ), 
+                               &time, lsufx, fsufx, tpcb->tp_fref);
+               ENDPERF
+               break;
+
+       case PRU_ACCEPT: 
+               /* all this garbage is to keep accept from returning
+                * before the 3-way handshake is done in class 4.
+                * it'll have to be modified for other classes 
+                */
+               IFDEBUG(D_REQUEST)
+                       printf("PRU_ACCEPT so_error 0x%x\n", so->so_error);
+               ENDDEBUG
+               so->so_error = 0;
+               if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTED)== 0) {
+                       error = EWOULDBLOCK;
+                       break;
+               }
+               while ((so->so_state & SS_ISCONNECTED) == 0 && so->so_error == 0) {
+                       sleep((caddr_t)&so->so_timeo, PZERO+1);
+               }
+               if (so->so_error) {
+                       error = so->so_error;
+               } else {
+                       struct sockaddr *sa = mtod(nam, struct sockaddr *);
+
+                       nam->m_len = sizeof (struct sockaddr);
+                       (tpcb->tp_nlproto->nlp_getnetaddr)(so->so_pcb, sa, TP_FOREIGN);
+
+                       switch(sa->sa_family = sototpcb(so)->tp_domain) {
+                       case AF_INET:
+                               satosin(sa)->sin_port =
+                                       (tpcb->tp_nlproto->nlp_getsufx)(so->so_pcb, TP_FOREIGN);
+                               break;
+                       case AF_ISO:
+                               satosiso(sa)->siso_tsuffix =
+                                       (tpcb->tp_nlproto->nlp_getsufx)(so->so_pcb, TP_FOREIGN);
+                               /* doesn't cover the case where the suffix is extended -
+                                * grotesque - the user *has* to do the getsockopt */
+                               break;
+                       }
+                       IFDEBUG(D_REQUEST)
+                               printf("ACCEPT PEERADDDR:");
+                               dump_buf(sa, sizeof(struct sockaddr));
+                       ENDDEBUG
+               }
+               IFPERF(tpcb)
+                       u_int lsufx, fsufx;
+                       lsufx = *(u_int *)(tpcb->tp_lsuffix);
+                       fsufx = *(u_int *)(tpcb->tp_fsuffix);
+
+                       tpmeas( tpcb->tp_lref, TPtime_open, 
+                               &time, lsufx, fsufx, tpcb->tp_fref);
+               ENDPERF
+               break;
+
+       case PRU_RCVD:
+               IFTRACE(D_DATA)
+                       tptraceTPCB(TPPTmisc,
+                       "RCVD BF: lcredit sent_lcdt cc hiwat \n",
+                               tpcb->tp_lcredit, tpcb->tp_sent_lcdt,
+                               so->so_rcv.sb_cc, so->so_rcv.sb_hiwat);
+                       LOCAL_CREDIT(tpcb);
+                       tptraceTPCB(TPPTmisc, 
+                               "PRU_RCVD AF sbspace lcredit hiwat cc",
+                               sbspace(&so->so_rcv), tpcb->tp_lcredit,
+                               so->so_rcv.sb_cc, so->so_rcv.sb_hiwat);
+               ENDTRACE
+               error = DoEvent(T_USR_rcvd); 
+               break;
+
+       case PRU_RCVOOB:
+               if ((so->so_state & SS_ISCONNECTED) == 0) {
+                       error = ENOTCONN;
+                       break;
+               }
+               if( ! tpcb->tp_xpd_service ) {
+                       error = EOPNOTSUPP;
+                       break;
+               }
+               /* kludge - nam is really flags here */
+               error = tp_rcvoob(tpcb, so, m, outflags, (int)nam);
+               break;
+
+       case PRU_SENDOOB:
+               if ((so->so_state & SS_ISCONNECTED) == 0) {
+                       error = ENOTCONN;
+                       break;
+               }
+               if( ! tpcb->tp_xpd_service ) {
+                       error = EOPNOTSUPP;
+                       break;
+               }
+               error = tp_sendoob(tpcb, so, m, outflags);
+               break;
+
+       case PRU_SENDEOT:
+               eotsdu = 1;
+               /* fall through */
+       case PRU_SEND:
+               /*
+                * The protocol machine copies mbuf chains,
+                * prepends headers, assigns seq numbers, and
+                * puts the packets on the device.
+                * When they are acked they are removed from the socket buf.
+                *
+                * sosend calls this up until sbspace goes negative.
+                * Sbspace may be made negative by appending this mbuf chain,
+                * possibly by a whole cluster.
+                */
+               if ((so->so_state & SS_ISCONNECTED) == 0) {
+                       error = ENOTCONN;
+                       break;
+               }
+               {
+                       register struct mbuf *n;
+                       register int len=0;
+                       register struct sockbuf *sb = &so->so_snd;
+
+                       n = m;
+                       while (n) { /* Could have eotsdu and no data.(presently MUST have
+                                                *      an mbuf though, even if its length == 0) 
+                                                */
+                               len += n->m_len;
+                               if( n->m_next == MNULL && eotsdu )  {
+                                       CHANGE_MTYPE(n, TPMT_EOT);
+                               }
+                               n = n->m_next;
+                       }
+                       IFPERF(tpcb)
+                          PStat(tpcb, Nb_from_sess) += len;
+                          tpmeas(tpcb->tp_lref, TPtime_from_session, 0, 0, 
+                                       PStat(tpcb, Nb_from_sess), len);
+                       ENDPERF
+                       IFDEBUG(D_SYSCALL)
+                               printf(
+                               "PRU_SEND: eot %d before sbappend 0x%x len 0x%x to sb @ 0x%x\n",
+                                       eotsdu, m,len, &sb->sb_mb);
+                               dump_mbuf(sb->sb_mb, "so_snd.sb_mb");
+                               dump_mbuf(m, "m : to be added");
+                       ENDDEBUG
+                       /* The last mbuf has type TPMT_EOT so it will never be compressed
+                        * with TPMT_DATA mbufs, but if this was an EOTSDU request w/o
+                        * any data, the only way to keep this mbuf from being thrown
+                        * away is to link it through the m_act field
+                        * We are ASSUMING that if there are any data at all with this
+                        * request, the last mbuf will be non-empty!!!
+                        */
+                       if( m->m_type == TPMT_EOT ) /* first mbuf in chain is EOT? */
+                               sbappendrecord(sb, m);  /* to keep 2 TPMT_EOTs from being
+                                                                                               compressed */
+                       else
+                               sbappend(sb, m);
+                       IFDEBUG(D_SYSCALL)
+                               printf("PRU_SEND: eot %d after sbappend 0x%x len 0x%x\n",
+                                       eotsdu, m,len);
+                               dump_mbuf(sb->sb_mb, "so_snd.sb_mb");
+                       ENDDEBUG
+                       u.u_r.r_val1  += len; 
+                       error = DoEvent(T_DATA_req); 
+                       IFDEBUG(D_SYSCALL)
+                               printf("PRU_SEND: after driver error 0x%x \n",error);
+                       ENDDEBUG
+               }
+               break;
+
+       case PRU_SOCKADDR: {
+                       struct sockaddr *sa = mtod(nam, struct sockaddr *);
+
+                       nam->m_len = sizeof (struct sockaddr);
+                       (tpcb->tp_nlproto->nlp_getnetaddr)(so->so_pcb, sa, TP_LOCAL);
+                       switch ( sa->sa_family = sototpcb(so)->tp_domain ) {
+                       case AF_INET:
+                               satosin(sa)->sin_port =
+                                       (tpcb->tp_nlproto->nlp_getsufx)(so->so_pcb, TP_LOCAL);
+                               break;
+                       case AF_ISO:
+                               satosiso(sa)->siso_tsuffix =
+                                       (tpcb->tp_nlproto->nlp_getsufx)(so->so_pcb, TP_LOCAL);
+                               break;
+                       }
+               }
+               break;
+
+       case PRU_PEERADDR:
+               if( (so->so_state & SS_ISCONNECTED) && 
+                       (so->so_state & SS_ISDISCONNECTING) == 0) {
+                               struct sockaddr *sa = mtod(nam, struct sockaddr *);
+
+                       nam->m_len = sizeof (struct sockaddr);
+
+                       (tpcb->tp_nlproto->nlp_getnetaddr)(so->so_pcb, sa, TP_FOREIGN);
+
+                       switch ( sa->sa_family = sototpcb(so)->tp_domain ) {
+                       case AF_INET:
+                               satosin(sa)->sin_port =
+                                       (tpcb->tp_nlproto->nlp_getsufx)(so->so_pcb, TP_FOREIGN);
+                               break;
+                       case AF_ISO:
+                               satosiso(sa)->siso_tsuffix =
+                                       (tpcb->tp_nlproto->nlp_getsufx)(so->so_pcb, TP_FOREIGN);
+                               break;
+                       }
+                       IFDEBUG(D_REQUEST)
+                               printf("PEERADDDR:");
+                               dump_buf(sa, sizeof(struct sockaddr));
+                       ENDDEBUG
+               } else 
+                       error = ENOTCONN;
+               break;
+
+       case PRU_CONTROL:
+               error = EOPNOTSUPP;
+               break;
+
+       case PRU_PROTOSEND:
+       case PRU_PROTORCV:
+       case PRU_SENSE:
+       case PRU_SLOWTIMO:
+       case PRU_FASTTIMO:
+               error = EOPNOTSUPP;
+               break;
+
+       default:
+#ifdef ARGO_DEBUG
+               printf("tp_usrreq UNKNOWN PRU %d\n", req);
+#endif ARGO_DEBUG
+               error = EOPNOTSUPP;
+       }
+
+       IFDEBUG(D_REQUEST)
+               printf("returning from tp_usrreq(so 0x%x) error 0x%x\n", so, error);
+       ENDDEBUG
+       IFTRACE(D_REQUEST)
+               tptraceTPCB(TPPTusrreq, "END req so m state [", req, so, m, 
+                       tpcb?0:tpcb->tp_state);
+       ENDTRACE
+       splx(s);
+       return error;
+}