xref: /csrg-svn/sys/netiso/tp_usrreq.c (revision 37469)
1 /***********************************************************
2 		Copyright IBM Corporation 1987
3 
4                       All Rights Reserved
5 
6 Permission to use, copy, modify, and distribute this software and its
7 documentation for any purpose and without fee is hereby granted,
8 provided that the above copyright notice appear in all copies and that
9 both that copyright notice and this permission notice appear in
10 supporting documentation, and that the name of IBM not be
11 used in advertising or publicity pertaining to distribution of the
12 software without specific, written prior permission.
13 
14 IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16 IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20 SOFTWARE.
21 
22 ******************************************************************/
23 
24 /*
25  * ARGO Project, Computer Sciences Dept., University of Wisconsin - Madison
26  */
27 /*
28  * ARGO TP
29  *
30  * $Header: tp_usrreq.c,v 5.4 88/11/18 17:29:18 nhall Exp $
31  * $Source: /usr/argo/sys/netiso/RCS/tp_usrreq.c,v $
32  *
33  * tp_usrreq(), the fellow that gets called from most of the socket code.
34  * Pretty straighforward.
35  * THe only really awful stuff here is the OOB processing, which is done
36  * wholly here.
37  * tp_rcvoob() and tp_sendoob() are contained here and called by tp_usrreq().
38  */
39 
40 #ifndef lint
41 static char *rcsid = "$Header: tp_usrreq.c,v 5.4 88/11/18 17:29:18 nhall Exp $";
42 #endif lint
43 
44 #include "param.h"
45 #include "systm.h"
46 #include "dir.h"
47 #include "user.h"
48 #include "mbuf.h"
49 #include "socket.h"
50 #include "socketvar.h"
51 #include "domain.h"
52 #include "protosw.h"
53 #include "errno.h"
54 
55 #include "tp_param.h"
56 #include "tp_timer.h"
57 #include "tp_stat.h"
58 #include "tp_seq.h"
59 #include "tp_ip.h"
60 #include "tp_pcb.h"
61 #include "argo_debug.h"
62 #include "tp_trace.h"
63 #include "tp_meas.h"
64 #include "iso.h"
65 #include "iso_errno.h"
66 
67 int tp_attach(), tp_driver();
68 
69 #ifdef ARGO_DEBUG
70 /*
71  * CALLED FROM:
72  *  anywhere you want to debug...
73  * FUNCTION and ARGUMENTS:
74  *  print (str) followed by the control info in the mbufs of an mbuf chain (n)
75  */
76 void
77 dump_mbuf(n, str)
78 	struct mbuf *n;
79 	char *str;
80 {
81 	struct mbuf *nextrecord;
82 
83 	printf("dump %s\n", str);
84 
85 	if( n == MNULL)  {
86 		printf("EMPTY:\n");
87 		return;
88 	}
89 
90 	for(;n;) {
91 		nextrecord = n->m_act;
92 		printf("RECORD:\n");
93 		while (n) {
94 			printf("%x : Len %x Data %x A %x Nx %x Tp %x\n",
95 				n, n->m_len, n->m_data, n->m_act, n->m_next, n->m_type);
96 #ifdef notdef
97 			{
98 				register char *p = mtod(n, char *);
99 				register int i;
100 
101 				printf("data: ");
102 				for(i=0; i < n->m_len; i++ ) {
103 					if(i%8 == 0)
104 						printf("\n");
105 					printf("0x%x ", *(p+i));
106 				}
107 				printf("\n");
108 			}
109 #endif notdef
110 			if( n->m_next == n ) {
111 				printf("LOOP!\n");
112 				return;
113 			}
114 			n = n->m_next;
115 		}
116 		n = nextrecord;
117 	}
118 	printf("\n");
119 }
120 
121 #endif ARGO_DEBUG
122 
123 /*
124  * CALLED FROM:
125  *  tp_usrreq(), PRU_RCVOOB
126  * FUNCTION and ARGUMENTS:
127  * 	Copy data from the expedited data socket buffer into
128  * 	the pre-allocated mbuf m.
129  * 	There is an isomorphism between XPD TPDUs and expedited data TSDUs.
130  * 	XPD tpdus are limited to 16 bytes of data so they fit in one mbuf.
131  * RETURN VALUE:
132  *  EINVAL if debugging is on and a disaster has occurred
133  *  ENOTCONN if the socket isn't connected
134  *  EWOULDBLOCK if the socket is in non-blocking mode and there's no
135  *		xpd data in the buffer
136  *  E* whatever is returned from the fsm.
137  */
138 tp_rcvoob(tpcb, so, m, outflags, inflags)
139 	struct tp_pcb	*tpcb;
140 	register struct socket	*so;
141 	register struct mbuf 	*m;
142 	int 		 	*outflags;
143 	int 		 	inflags;
144 {
145 	register struct mbuf *n;
146 	register struct sockbuf *sb = &so->so_rcv;
147 	struct tp_event E;
148 	int error = 0;
149 	register struct mbuf **nn;
150 
151 	IFDEBUG(D_XPD)
152 		printf("PRU_RCVOOB, sostate 0x%x\n", so->so_state);
153 	ENDDEBUG
154 
155 	/* if you use soreceive */
156 	if (m==MNULL)
157 		return ENOBUFS;
158 
159 restart:
160 	sblock(sb);
161 
162 	if ((((so->so_state & SS_ISCONNECTED) == 0)
163 		 || (so->so_state & SS_ISDISCONNECTING) != 0) &&
164 		(so->so_proto->pr_flags & PR_CONNREQUIRED)) {
165 			return ENOTCONN;
166 	}
167 
168 	/* Take the first mbuf off the chain.
169 	 * Each XPD TPDU gives you a complete TSDU so the chains don't get
170 	 * coalesced, but one TSDU may span several mbufs.
171 	 * Nevertheless, since n should have a most 16 bytes, it
172 	 * will fit into m.  (size was checked in tp_input() )
173 	 */
174 
175 	/*
176 	 * Code for excision of OOB data should be added to
177 	 * uipc_socket2.c (like sbappend).
178 	 */
179 
180 	for (nn = &sb->sb_mb; n = *nn; nn = &n->m_act)
181 		if (n->m_type == MT_OOBDATA)
182 			break;
183 
184 	if (n == 0) {
185 		ASSERT( (tpcb->tp_flags & TPF_DISC_DATA_IN)  == 0 );
186 		IFDEBUG(D_XPD)
187 			printf("RCVOOB: empty queue!\n");
188 		ENDDEBUG
189 		if (so->so_state & SS_NBIO) {
190 			return  EWOULDBLOCK;
191 		}
192 		sbunlock(sb);
193 		sbwait(sb);
194 		goto restart;
195 	}
196 	m->m_len = 0;
197 
198 	/* Assuming at most one xpd tpdu is in the buffer at once */
199 	while ( n != MNULL ) {
200 		m->m_len += n->m_len;
201 		bcopy(mtod(n, caddr_t), mtod(m, caddr_t), (unsigned)n->m_len);
202 		m->m_data += n->m_len; /* so mtod() in bcopy() above gives right addr */
203 		n = n->m_next;
204 	}
205 	m->m_data = m->m_dat;
206 	m->m_flags |= M_EOR;
207 
208 	IFDEBUG(D_XPD)
209 		printf("tp_rcvoob: xpdlen 0x%x\n", m->m_len);
210 		dump_mbuf(so->so_rcv.sb_mb, "RCVOOB: Rcv socketbuf");
211 		dump_mbuf(sb->sb_mb, "RCVOOB: Xrcv socketbuf");
212 	ENDDEBUG
213 
214 	if( (inflags & MSG_PEEK) == 0 ) {
215 		n = *nn;
216 		*nn = n->m_act;
217 		sb->sb_cc -= m->m_len;
218 	}
219 
220 release:
221 	sbunlock(sb);
222 
223 	IFTRACE(D_XPD)
224 		tptraceTPCB(TPPTmisc, "PRU_RCVOOB @ release sb_cc m_len",
225 			tpcb->tp_Xrcv.sb_cc, m->m_len,0,0 );
226 	ENDTRACE
227 	if (error == 0)
228 		error = DoEvent(T_USR_Xrcvd);
229 	return error;
230 }
231 
232 /*
233  * CALLED FROM:
234  *  tp_usrreq(), PRU_SENDOOB
235  * FUNCTION and ARGUMENTS:
236  * 	Send what's in the mbuf chain (m) as an XPD TPDU.
237  * 	The mbuf may not contain more then 16 bytes of data.
238  * 	XPD TSDUs aren't segmented, so they translate into
239  * 	exactly one XPD TPDU, with EOT bit set.
240  * RETURN VALUE:
241  *  EWOULDBLOCK if socket is in non-blocking mode and the previous
242  *   xpd data haven't been acked yet.
243  *  EMSGSIZE if trying to send > max-xpd bytes (16)
244  *  ENOBUFS if ran out of mbufs
245  */
246 tp_sendoob(tpcb, so, xdata, outflags)
247 	struct tp_pcb	*tpcb;
248 	register struct socket	*so;
249 	register struct mbuf 	*xdata;
250 	int 		 	*outflags; /* not used */
251 {
252 	/*
253 	 * Each mbuf chain represents a sequence # in the XPD seq space.
254 	 * The first one in the queue has sequence # tp_Xuna.
255 	 * When we add to the XPD queue, we stuff a zero-length
256 	 * mbuf (mark) into the DATA queue, with its sequence number in m_next
257 	 * to be assigned to this XPD tpdu, so data xfer can stop
258 	 * when it reaches the zero-length mbuf if this XPD TPDU hasn't
259 	 * yet been acknowledged.
260 	 */
261 	register struct sockbuf *sb = &(tpcb->tp_Xsnd);
262 	register struct mbuf 	*xmark;
263 	register int 			len=0;
264 	struct tp_event E;
265 
266 	IFDEBUG(D_XPD)
267 		printf("tp_sendoob:");
268 		if(xdata)
269 			printf("xdata len 0x%x\n", xdata->m_len);
270 	ENDDEBUG
271 oob_again:
272 	/* DO NOT LOCK the Xsnd buffer!!!! You can have at MOST one
273 	 * socket buf locked at any time!!! (otherwise you might
274 	 * sleep() in sblock() w/ a signal pending and cause the
275 	 * system call to be aborted w/ a locked socketbuf, which
276 	 * is a problem.  So the so_snd buffer lock
277 	 * (done in sosend()) serves as the lock for Xpd.
278 	 */
279 	if (sb->sb_mb) { /* anything already in this sockbuf? */
280 		if (so->so_state & SS_NBIO) {
281 			return EWOULDBLOCK;
282 		}
283 		sbunlock(&so->so_snd);
284 		sbwait(&so->so_snd);
285 		sblock(&so->so_snd);
286 		goto oob_again;
287 	}
288 
289 	if (xdata == (struct mbuf *)0) {
290 		/* empty xpd packet */
291 		MGETHDR(xdata, M_WAIT, MT_OOBDATA);
292 		if (xdata == NULL) {
293 			return ENOBUFS;
294 		}
295 		xdata->m_len = 0;
296 		xdata->m_pkthdr.len = 0;
297 	}
298 	IFDEBUG(D_XPD)
299 		printf("tp_sendoob 1:");
300 		if(xdata)
301 			printf("xdata len 0x%x\n", xdata->m_len);
302 	ENDDEBUG
303 	xmark = xdata; /* temporary use of variable xmark */
304 	while (xmark) {
305 		len += xmark->m_len;
306 		xmark = xmark->m_next;
307 	}
308 	if (len > TP_MAX_XPD_DATA) {
309 		return EMSGSIZE;
310 	}
311 	IFDEBUG(D_XPD)
312 		printf("tp_sendoob 2:");
313 		if(xdata)
314 			printf("xdata len 0x%x\n", len);
315 	ENDDEBUG
316 
317 
318 	IFTRACE(D_XPD)
319 		tptraceTPCB(TPPTmisc, "XPD mark m_next ", xmark->m_next, 0, 0, 0);
320 	ENDTRACE
321 
322 	sbappendrecord(sb, xdata);
323 
324 	IFDEBUG(D_XPD)
325 		printf("tp_sendoob len 0x%x\n", len);
326 		dump_mbuf(so->so_snd.sb_mb, "XPD request Regular sndbuf:");
327 		dump_mbuf(tpcb->tp_Xsnd.sb_mb, "XPD request Xsndbuf:");
328 	ENDDEBUG
329 	return DoEvent(T_XPD_req);
330 
331 }
332 
333 /*
334  * CALLED FROM:
335  *  the socket routines
336  * FUNCTION and ARGUMENTS:
337  * 	Handles all "user requests" except the [gs]ockopts() requests.
338  * 	The argument (req) is the request type (PRU*),
339  * 	(m) is an mbuf chain, generally used for send and
340  * 	receive type requests only.
341  * 	(nam) is used for addresses usually, in particular for the bind request.
342  *
343  * 	The last argument (rights in most usrreq()s) has been stolen for
344  * 	returning flags values.  Since rights can't be passed around w/ tp,
345  * 	this field is used only for RCVOOB user requests, and is assumed
346  * 	to be either 0 (as soreceive() would have it) or a ptr to the int flags
347  * 	(as recvv()'s version of soreceive() would have it
348  */
349 /*ARGSUSED*/
350 ProtoHook
351 tp_usrreq(so, req, m, nam, rightsp, controlp)
352 	struct socket *so;
353 	u_int req;
354 	struct mbuf *m, *nam, *rightsp, *controlp;
355 {
356 	register struct tp_pcb *tpcb =  sototpcb(so);
357 	int s = splnet();
358 	int error = 0;
359 	int flags, *outflags = &flags;
360 	u_long eotsdu = 0;
361 	struct tp_event E;
362 
363 	IFDEBUG(D_REQUEST)
364 		printf("usrreq(0x%x,%d,0x%x,0x%x,0x%x)\n",so,req,m,nam,outflags);
365 		if(so->so_error)
366 			printf("WARNING!!! so->so_error is 0x%x\n", so->so_error);
367 	ENDDEBUG
368 	IFTRACE(D_REQUEST)
369 		tptraceTPCB(TPPTusrreq, "req so m state [", req, so, m,
370 			tpcb?tpcb->tp_state:0);
371 	ENDTRACE
372 
373 	if ((u_int)tpcb == 0 && req != PRU_ATTACH) {
374 		IFTRACE(D_REQUEST)
375 			tptraceTPCB(TPPTusrreq, "req failed NO TPCB[", 0, 0, 0, 0);
376 		ENDTRACE
377 		splx(s);
378 		return ENOTCONN;
379 	}
380 
381 	switch (req) {
382 
383 	case PRU_ATTACH:
384 		if (tpcb) {
385 			error = EISCONN;
386 			break;
387 		}
388 		if( error = tp_attach(so, so->so_proto->pr_domain->dom_family ) )
389 			break;
390 		tpcb = sototpcb(so);
391 		break;
392 
393 	case PRU_ABORT: 	/* called from close() */
394 		/* called for each incoming connect queued on the
395 		 *	parent (accepting) socket
396 		 */
397 		if( tpcb->tp_state == TP_OPEN ) {
398 			E.ATTR(T_DISC_req).e_reason = E_TP_NO_SESSION;
399 			error = DoEvent(T_DISC_req); /* pretend it was a close() */
400 			break;
401 		} /* else DROP THROUGH */
402 
403 	case PRU_DETACH: 	/* called from close() */
404 		/* called only after disconnect was called */
405 		error = DoEvent(T_DETACH);
406 		if (tpcb->tp_state == TP_CLOSED) {
407 			free((caddr_t)tpcb, M_PCB);
408 			tpcb = 0;
409 		}
410 		break;
411 
412 	case PRU_SHUTDOWN:
413 		/* recv end may have been released; local credit might be zero  */
414 	case PRU_DISCONNECT:
415 		E.ATTR(T_DISC_req).e_reason = E_TP_NORMAL_DISC;
416 		error = DoEvent(T_DISC_req);
417 		break;
418 
419 	case PRU_BIND:
420 		error =  (tpcb->tp_nlproto->nlp_pcbbind)( so->so_pcb, nam );
421 		if (error == 0) {
422 			(tpcb->tp_nlproto->nlp_getsufx)(so->so_pcb, &tpcb->tp_lsuffixlen,
423 				tpcb->tp_lsuffix, TP_LOCAL );
424 		}
425 		break;
426 
427 	case PRU_LISTEN:
428 		if ( *SHORT_LSUFXP(tpcb) == (short)0 ) {
429 			/* note: this suffix is independent of the extended suffix */
430 			if( error = (tpcb->tp_nlproto->nlp_pcbbind)(so->so_pcb, MNULL) )
431 				break;
432 		}
433 		if( tpcb->tp_lsuffixlen ==  0) {
434 			(tpcb->tp_nlproto->nlp_getsufx)(so->so_pcb, &tpcb->tp_lsuffixlen,
435 				tpcb->tp_lsuffix, TP_LOCAL );
436 		}
437 		IFDEBUG(D_TPISO)
438 			if(tpcb->tp_state != TP_CLOSED)
439 				printf("LISTEN ERROR: state 0x%x\n", tpcb->tp_state);
440 		ENDDEBUG
441 		error = DoEvent(T_LISTEN_req);
442 		break;
443 
444 	case PRU_CONNECT2:
445 		error = EOPNOTSUPP; /* for unix domain sockets */
446 		break;
447 
448 	case PRU_CONNECT:
449 		IFTRACE(D_CONN)
450 			tptraceTPCB(TPPTmisc,
451 			"PRU_CONNECT: so 0x%x *SHORT_LSUFXP(tpcb) 0x%x lsuflen 0x%x, class 0x%x",
452 			tpcb->tp_sock, *SHORT_LSUFXP(tpcb), tpcb->tp_lsuffixlen,
453 				tpcb->tp_class);
454 		ENDTRACE
455 		IFDEBUG(D_CONN)
456 			printf("PRU_CONNECT: so *SHORT_LSUFXP(tpcb) 0x%x lsuflen 0x%x, class 0x%x",
457 			tpcb->tp_sock, *SHORT_LSUFXP(tpcb), tpcb->tp_lsuffixlen,
458 				tpcb->tp_class);
459 		ENDDEBUG
460 		if (*SHORT_LSUFXP(tpcb) == (short)0) {
461 			/* no bind was done */
462 			/* note: this suffix is independent of the extended suffix */
463 			if( error = (tpcb->tp_nlproto->nlp_pcbbind)(so->so_pcb, MNULL) ) {
464 				IFDEBUG(D_CONN)
465 					printf("pcbbind returns error 0x%x\n", error );
466 				ENDDEBUG
467 				break;
468 			}
469 		}
470 		if( tpcb->tp_lsuffixlen ==  0) {
471 			(tpcb->tp_nlproto->nlp_getsufx)(so->so_pcb, &tpcb->tp_lsuffixlen,
472 				tpcb->tp_lsuffix, TP_LOCAL );
473 		}
474 
475 		IFDEBUG(D_CONN)
476 			printf("isop 0x%x isop->isop_socket offset 12 :\n", tpcb->tp_npcb);
477 			dump_buf( tpcb->tp_npcb, 16);
478 		ENDDEBUG
479 		if( error = tp_route_to( nam, tpcb, /* channel */0) )
480 			break;
481 		IFDEBUG(D_CONN)
482 			printf(
483 				"PRU_CONNECT after tpcb 0x%x so 0x%x npcb 0x%x flags 0x%x\n",
484 				tpcb, so, tpcb->tp_npcb, tpcb->tp_flags);
485 			printf("isop 0x%x isop->isop_socket offset 12 :\n", tpcb->tp_npcb);
486 			dump_buf( tpcb->tp_npcb, 16);
487 		ENDDEBUG
488 		if( tpcb->tp_fsuffixlen ==  0) {
489 			/* didn't set peer extended suffix */
490 			(tpcb->tp_nlproto->nlp_getsufx)(so->so_pcb, &tpcb->tp_fsuffixlen,
491 				tpcb->tp_fsuffix, TP_FOREIGN );
492 		}
493 		(void) (tpcb->tp_nlproto->nlp_mtu)(so, so->so_pcb,
494 					&tpcb->tp_l_tpdusize, &tpcb->tp_tpdusize, 0);
495 		if( tpcb->tp_state == TP_CLOSED) {
496 			soisconnecting(so);
497 			error = DoEvent(T_CONN_req);
498 		} else {
499 			(tpcb->tp_nlproto->nlp_pcbdisc)(so->so_pcb);
500 			error = EISCONN;
501 		}
502 		IFPERF(tpcb)
503 			u_int lsufx, fsufx;
504 			lsufx = *(u_int *)(tpcb->tp_lsuffix);
505 			fsufx = *(u_int *)(tpcb->tp_fsuffix);
506 
507 			tpmeas( tpcb->tp_lref,
508 				TPtime_open | (tpcb->tp_xtd_format <<4 ),
509 				&time, lsufx, fsufx, tpcb->tp_fref);
510 		ENDPERF
511 		break;
512 
513 	case PRU_ACCEPT:
514 		/* all this garbage is to keep accept from returning
515 		 * before the 3-way handshake is done in class 4.
516 		 * it'll have to be modified for other classes
517 		 */
518 		IFDEBUG(D_REQUEST)
519 			printf("PRU_ACCEPT so_error 0x%x\n", so->so_error);
520 		ENDDEBUG
521 		so->so_error = 0;
522 		if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTED)== 0) {
523 			error = EWOULDBLOCK;
524 			break;
525 		}
526 		while ((so->so_state & SS_ISCONNECTED) == 0 && so->so_error == 0) {
527 			sleep((caddr_t)&so->so_timeo, PZERO+1);
528 		}
529 		if (so->so_error) {
530 			error = so->so_error;
531 		} else {
532 			(tpcb->tp_nlproto->nlp_getnetaddr)(so->so_pcb, nam, TP_FOREIGN);
533 			IFDEBUG(D_REQUEST)
534 				printf("ACCEPT PEERADDDR:");
535 				dump_buf(mtod(nam, char *), nam->m_len);
536 			ENDDEBUG
537 		}
538 		IFPERF(tpcb)
539 			u_int lsufx, fsufx;
540 			lsufx = *(u_int *)(tpcb->tp_lsuffix);
541 			fsufx = *(u_int *)(tpcb->tp_fsuffix);
542 
543 			tpmeas( tpcb->tp_lref, TPtime_open,
544 				&time, lsufx, fsufx, tpcb->tp_fref);
545 		ENDPERF
546 		break;
547 
548 	case PRU_RCVD:
549 		IFTRACE(D_DATA)
550 			tptraceTPCB(TPPTmisc,
551 			"RCVD BF: lcredit sent_lcdt cc hiwat \n",
552 				tpcb->tp_lcredit, tpcb->tp_sent_lcdt,
553 				so->so_rcv.sb_cc, so->so_rcv.sb_hiwat);
554 			LOCAL_CREDIT(tpcb);
555 			tptraceTPCB(TPPTmisc,
556 				"PRU_RCVD AF sbspace lcredit hiwat cc",
557 				sbspace(&so->so_rcv), tpcb->tp_lcredit,
558 				so->so_rcv.sb_cc, so->so_rcv.sb_hiwat);
559 		ENDTRACE
560 		IFDEBUG(D_REQUEST)
561 			printf("RCVD: cc %d space %d hiwat %d\n",
562 				so->so_rcv.sb_cc, sbspace(&so->so_rcv),
563 				so->so_rcv.sb_hiwat);
564 		ENDDEBUG
565 		if (((int)nam) & MSG_OOB)
566 			error = DoEvent(T_USR_Xrcvd);
567 		else
568 			error = DoEvent(T_USR_rcvd);
569 		break;
570 
571 	case PRU_RCVOOB:
572 		if ((so->so_state & SS_ISCONNECTED) == 0) {
573 			error = ENOTCONN;
574 			break;
575 		}
576 		if( ! tpcb->tp_xpd_service ) {
577 			error = EOPNOTSUPP;
578 			break;
579 		}
580 		/* kludge - nam is really flags here */
581 		error = tp_rcvoob(tpcb, so, m, outflags, (int)nam);
582 		break;
583 
584 	case PRU_SENDOOB:
585 		if (controlp && (error = tp_snd_control(controlp, so, &m)))
586 			break;
587 		if (m == 0)
588 			break;
589 		if (so->so_state & SS_ISCONFIRMING)
590 			tp_confirm();
591 		if( ! tpcb->tp_xpd_service ) {
592 			error = EOPNOTSUPP;
593 			break;
594 		}
595 		error = tp_sendoob(tpcb, so, m, outflags);
596 		break;
597 
598 	case PRU_SEND:
599 		/*
600 		 * The protocol machine copies mbuf chains,
601 		 * prepends headers, assigns seq numbers, and
602 		 * puts the packets on the device.
603 		 * When they are acked they are removed from the socket buf.
604 		 *
605 		 * sosend calls this up until sbspace goes negative.
606 		 * Sbspace may be made negative by appending this mbuf chain,
607 		 * possibly by a whole cluster.
608 		 */
609 		if (controlp && (error = tp_snd_control(controlp, so, &m)))
610 			break;
611 		if (m == 0)
612 			break;
613 		if (so->so_state & SS_ISCONFIRMING)
614 			tp_confirm();
615 		{
616 			register struct mbuf *n = m;
617 			register int len=0;
618 			register struct sockbuf *sb = &so->so_snd;
619 			int	maxsize = tpcb->tp_l_tpdusize
620 				    - tp_headersize(DT_TPDU_type, tpcb)
621 				    - (tpcb->tp_use_checksum?4:0) ;
622 			int totlen = n->m_pkthdr.len;
623 
624 			/*
625 			 * Could have eotsdu and no data.(presently MUST have
626 			 * an mbuf though, even if its length == 0)
627 			 */
628 			if (n->m_flags & M_EOR)
629 				eotsdu = 1;
630 			IFPERF(tpcb)
631 			   PStat(tpcb, Nb_from_sess) += totlen;
632 			   tpmeas(tpcb->tp_lref, TPtime_from_session, 0, 0,
633 					PStat(tpcb, Nb_from_sess), totlen);
634 			ENDPERF
635 			IFDEBUG(D_SYSCALL)
636 				printf(
637 				"PRU_SEND: eot %d before sbappend 0x%x len 0x%x to sb @ 0x%x\n",
638 					eotsdu, m,len, sb);
639 				dump_mbuf(sb->sb_mb, "so_snd.sb_mb");
640 				dump_mbuf(m, "m : to be added");
641 			ENDDEBUG
642 			/*
643 			 * Pre-packetize the data in the sockbuf
644 			 * according to negotiated mtu.  Do it here
645 			 * where we can safely wait for mbufs.
646 			 */
647 			while (n->m_pkthdr.len > maxsize) {
648 				struct mbuf *nn
649 					    = m_copym(n, 0, maxsize, M_WAIT);
650 				if (eotsdu)
651 					n->m_flags &= ~M_EOR;
652 				sbappendrecord(sb, nn);
653 				m_adj(n, maxsize);
654 			}
655 			sbappendrecord(sb, n);
656 			IFDEBUG(D_SYSCALL)
657 				printf("PRU_SEND: eot %d after sbappend 0x%x len 0x%x\n",
658 					eotsdu, n, len);
659 				dump_mbuf(sb->sb_mb, "so_snd.sb_mb");
660 			ENDDEBUG
661 			error = DoEvent(T_DATA_req);
662 			IFDEBUG(D_SYSCALL)
663 				printf("PRU_SEND: after driver error 0x%x \n",error);
664 				printf("so_snd 0x%x cc 0t%d mbcnt 0t%d\n",
665 						sb, sb->sb_cc, sb->sb_mbcnt);
666 				dump_mbuf(sb->sb_mb, "so_snd.sb_mb after driver");
667 			ENDDEBUG
668 		}
669 		break;
670 
671 	case PRU_SOCKADDR:
672 		(tpcb->tp_nlproto->nlp_getnetaddr)(so->so_pcb, nam, TP_LOCAL);
673 		break;
674 
675 	case PRU_PEERADDR:
676 		if ((so->so_state & SS_ISCONNECTED) &&
677 		    (so->so_state & SS_ISDISCONNECTING) == 0) {
678 				(tpcb->tp_nlproto->nlp_getnetaddr)(so->so_pcb, nam, TP_FOREIGN);
679 				IFDEBUG(D_REQUEST)
680 					printf("PEERADDDR:");
681 					dump_buf(mtod(nam, char *), nam->m_len);
682 				ENDDEBUG
683 		} else
684 			error = ENOTCONN;
685 		break;
686 
687 	case PRU_CONTROL:
688 		error = EOPNOTSUPP;
689 		break;
690 
691 	case PRU_PROTOSEND:
692 	case PRU_PROTORCV:
693 	case PRU_SENSE:
694 	case PRU_SLOWTIMO:
695 	case PRU_FASTTIMO:
696 		error = EOPNOTSUPP;
697 		break;
698 
699 	default:
700 #ifdef ARGO_DEBUG
701 		printf("tp_usrreq UNKNOWN PRU %d\n", req);
702 #endif ARGO_DEBUG
703 		error = EOPNOTSUPP;
704 	}
705 
706 	IFDEBUG(D_REQUEST)
707 		printf("returning from tp_usrreq(so 0x%x) error 0x%x\n", so, error);
708 	ENDDEBUG
709 	IFTRACE(D_REQUEST)
710 		tptraceTPCB(TPPTusrreq, "END req so m state [", req, so, m,
711 			tpcb?0:tpcb->tp_state);
712 	ENDTRACE
713 	splx(s);
714 	return error;
715 }
716 
717 /*
718  * Stub for future negotiated confirmation of connections.
719  */
720 tp_confirm()
721 {
722 }
723 
724 /*
725  * Process control data sent with sendmsg()
726  */
727 tp_snd_control(m0, so, data)
728 	register struct mbuf *m0;
729 	struct socket *so;
730 	register struct mbuf **data;
731 {
732 	register struct tp_control_hdr *ch;
733 	struct mbuf *m;
734 	int error = 0;
735 
736 	if (m0 && m0->m_len) {
737 		ch = mtod(m0, struct tp_control_hdr *);
738 		m0->m_len -= sizeof (*ch);
739 		m0->m_data += sizeof (*ch);
740 		m = m_copym(m0, 0, M_COPYALL, M_WAIT);
741 		error = tp_ctloutput(PRCO_SETOPT,
742 							 so, ch->cmsg_level, ch->cmsg_type, &m);
743 		if (m)
744 			m_freem(m);
745 		if (ch->cmsg_type == TPOPT_DISC_DATA) {
746 			if (data && *data) {
747 				m_freem(*data);
748 				*data = 0;
749 			}
750 			m0 = 0;
751 			error = tp_usrreq(so, PRU_DISCONNECT, m0, (caddr_t)0, m0, m0);
752 		}
753 	}
754 	return error;
755 }
756