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