xref: /illumos-gate/usr/src/uts/common/io/tirdwr.c (revision a0955b86cd77e22e80846428a5065e871b6d8eb8)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI" /* from S5R4 1.4 */
32 
33 /*
34  * Transport Interface Library read/write module - issue 1
35  */
36 
37 #include	<sys/types.h>
38 #include	<sys/param.h>
39 #include	<sys/stream.h>
40 #include	<sys/stropts.h>
41 #include	<sys/tihdr.h>
42 #include	<sys/debug.h>
43 #include	<sys/errno.h>
44 #include	<sys/kmem.h>
45 #include	<sys/tirdwr.h>
46 #include	<sys/conf.h>
47 #include	<sys/modctl.h>
48 #include	<sys/ddi.h>
49 #include	<sys/sunddi.h>
50 
51 #define	ORDREL		002
52 #define	DISCON		004
53 #define	FATAL		010
54 #define	WAITACK		020
55 #define	TIRDWR_ID	4
56 
57 /*
58  * Per-Stream private data structure.
59  */
60 struct trw_trw {
61 	queue_t	*trw_rdq;
62 	uint_t	trw_flags;
63 };
64 
65 /*
66  * stream data structure definitions
67  */
68 static	int tirdwropen(queue_t *q, dev_t *dev,
69     int flag, int sflag, cred_t	*cr);
70 
71 static	int tirdwrclose(queue_t *q, int flag, cred_t *cr);
72 
73 static	int check_strhead(queue_t *q);
74 
75 /*
76  * To save instructions, since STREAMS ignores the return value
77  * from these functions, they are defined as void here. Kind of icky, but...
78  */
79 static void tirdwrrput(queue_t *q, mblk_t *mp);
80 static void tirdwrwput(queue_t *q, mblk_t *mp);
81 
82 static struct module_info tirdwr_info = {
83 	TIRDWR_ID,
84 	"tirdwr",
85 	0,
86 	INFPSZ,
87 	4096,
88 	1024
89 };
90 
91 static struct qinit tirdwrrinit = {
92 	(int (*)())tirdwrrput,
93 	(int (*)())NULL,
94 	tirdwropen,
95 	tirdwrclose,
96 	nulldev,
97 	&tirdwr_info,
98 	NULL
99 };
100 
101 static struct qinit tirdwrwinit = {
102 	(int (*)())tirdwrwput,
103 	(int (*)())NULL,
104 	tirdwropen,
105 	tirdwrclose,
106 	nulldev,
107 	&tirdwr_info,
108 	NULL
109 };
110 
111 static struct streamtab trwinfo = {
112 	&tirdwrrinit,
113 	&tirdwrwinit,
114 	NULL,
115 	NULL
116 };
117 
118 static struct fmodsw fsw = {
119 	"tirdwr",
120 	&trwinfo,
121 	D_NEW|D_MTQPAIR|D_MP
122 };
123 
124 static struct modlstrmod modlstrmod = {
125 	&mod_strmodops, "xport interface rd/wr str mod", &fsw
126 };
127 
128 static struct modlinkage modlinkage = {
129 	MODREV_1, &modlstrmod, NULL
130 };
131 
132 int
133 _init(void)
134 {
135 	return (mod_install(&modlinkage));
136 }
137 
138 int
139 _fini(void)
140 {
141 	return (mod_remove(&modlinkage));
142 }
143 
144 int
145 _info(struct modinfo *modinfop)
146 {
147 	return (mod_info(&modlinkage, modinfop));
148 }
149 
150 static void send_fatal(queue_t *q, mblk_t *mp);
151 static void strip_strhead(queue_t *q);
152 
153 
154 /*
155  * tirdwropen - open routine gets called when the
156  *		module gets pushed onto the stream.
157  */
158 /*ARGSUSED*/
159 static int
160 tirdwropen(
161 	queue_t *q,
162 	dev_t	*dev,
163 	int flag,
164 	int sflag,
165 	cred_t	*cr)
166 {
167 	struct trw_trw *trwptr;
168 
169 	/* check if already open */
170 	if (q->q_ptr) {
171 		return (0);
172 	}
173 
174 	/*
175 	 * Allocate a new trw_trw struct.
176 	 */
177 	trwptr = kmem_alloc(sizeof (struct trw_trw), KM_SLEEP);
178 
179 	/* initialize data structure */
180 	trwptr->trw_flags = 0;
181 	trwptr->trw_rdq = q;
182 	q->q_ptr = (caddr_t)trwptr;
183 	WR(q)->q_ptr = (caddr_t)trwptr;
184 	qprocson(q);
185 
186 	freezestr(q);
187 
188 	(void) strqset(WR(q), QMAXPSZ, 0, (uintptr_t)WR(q)->q_next->q_maxpsz);
189 	(void) strqset(q, QMAXPSZ, 0, (uintptr_t)q->q_next->q_maxpsz);
190 
191 	if (!check_strhead(q)) {
192 		unfreezestr(q);
193 		qprocsoff(q);
194 		kmem_free(trwptr, sizeof (struct trw_trw));
195 		return (EPROTO);
196 	}
197 	strip_strhead(q);
198 	unfreezestr(q);
199 
200 	return (0);
201 }
202 
203 /*
204  * tirdwrclose - This routine gets called when the module
205  *		gets popped off of the stream.
206  */
207 
208 /*ARGSUSED1*/
209 static int
210 tirdwrclose(queue_t *q, int flag, cred_t *cr)
211 {
212 	struct trw_trw *trwptr;
213 	mblk_t *mp;
214 	union T_primitives *pptr;
215 
216 	qprocsoff(q);
217 	trwptr = (struct trw_trw *)q->q_ptr;
218 
219 	ASSERT(trwptr != NULL);
220 
221 	/*
222 	 * Send up a T_DISCON_IND if necessary.
223 	 */
224 	if ((trwptr->trw_flags & ORDREL) && !(trwptr->trw_flags & FATAL))
225 		if (mp = allocb(sizeof (struct T_discon_req), BPRI_LO)) {
226 			pptr = (union T_primitives *)mp->b_rptr;
227 			mp->b_wptr = mp->b_rptr + sizeof (struct T_ordrel_req);
228 			pptr->type = T_ORDREL_REQ;
229 			mp->b_datap->db_type = M_PROTO;
230 			putnext(WR(q), mp);
231 		}
232 
233 	kmem_free(trwptr, sizeof (struct trw_trw));
234 
235 	return (0);
236 }
237 
238 /*
239  * tirdwrrput - Module read queue put procedure.
240  *		This is called from the module or
241  *		driver downstream.
242  */
243 
244 static void
245 tirdwrrput(queue_t *q, mblk_t *mp)
246 {
247 	union T_primitives *pptr;
248 	struct trw_trw *trwptr;
249 	mblk_t *tmp;
250 
251 	trwptr = (struct trw_trw *)q->q_ptr;
252 
253 	ASSERT(trwptr != NULL);
254 
255 	if ((trwptr->trw_flags & FATAL) && !(trwptr->trw_flags & WAITACK)) {
256 		freemsg(mp);
257 		return;
258 	}
259 
260 	switch (mp->b_datap->db_type) {
261 
262 	default:
263 		putnext(q, mp);
264 		break;
265 
266 	case M_DATA:
267 		putnext(q, mp);
268 		break;
269 
270 	case M_PCPROTO:
271 	case M_PROTO:
272 		/* is there enough data to check type */
273 		if ((mp->b_wptr - mp->b_rptr) < sizeof (t_scalar_t)) {
274 			/* malformed message */
275 			freemsg(mp);
276 			break;
277 		}
278 		pptr = (union T_primitives *)mp->b_rptr;
279 
280 		switch (pptr->type) {
281 
282 		case T_EXDATA_IND:
283 			send_fatal(q, mp);
284 			break;
285 		case T_DATA_IND:
286 			if (msgdsize(mp) == 0) {
287 				freemsg(mp);
288 				break;
289 			}
290 
291 			tmp = (mblk_t *)unlinkb(mp);
292 			freemsg(mp);
293 			putnext(q, tmp);
294 			break;
295 
296 		case T_ORDREL_IND:
297 			trwptr->trw_flags |= ORDREL;
298 			mp->b_datap->db_type = M_DATA;
299 			mp->b_wptr = mp->b_rptr;
300 			putnext(q, mp);
301 			break;
302 
303 		case T_DISCON_IND:
304 			trwptr->trw_flags |= DISCON;
305 			trwptr->trw_flags &= ~ORDREL;
306 			if (msgdsize(mp) != 0) {
307 				tmp = (mblk_t *)unlinkb(mp);
308 				putnext(q, tmp);
309 			}
310 			mp->b_datap->db_type = M_HANGUP;
311 			mp->b_wptr = mp->b_rptr;
312 			putnext(q, mp);
313 			break;
314 
315 		default:
316 			send_fatal(q, mp);
317 			break;
318 		}
319 	}
320 }
321 
322 
323 /*
324  * tirdwrwput - Module write queue put procedure.
325  *		This is called from the module or
326  *		stream head upstream.
327  */
328 static void
329 tirdwrwput(queue_t *q, mblk_t *mp)
330 {
331 	struct trw_trw *trwptr;
332 
333 	trwptr = (struct trw_trw *)q->q_ptr;
334 
335 	ASSERT(trwptr != NULL);
336 
337 	if (trwptr->trw_flags & FATAL) {
338 		freemsg(mp);
339 		return;
340 	}
341 
342 	switch (mp->b_datap->db_type) {
343 	default:
344 		putnext(q, mp);
345 		break;
346 
347 	case M_DATA:
348 		putnext(q, mp);
349 		break;
350 
351 	case M_PROTO:
352 	case M_PCPROTO:
353 		send_fatal(q, mp);
354 		break;
355 	}
356 }
357 
358 
359 static void
360 send_fatal(queue_t *q, mblk_t *mp)
361 {
362 	struct trw_trw *trwptr;
363 
364 	trwptr = (struct trw_trw *)q->q_ptr;
365 
366 	trwptr->trw_flags |= FATAL;
367 	mp->b_datap->db_type = M_ERROR;
368 	*mp->b_datap->db_base = EPROTO;
369 	mp->b_rptr = mp->b_datap->db_base;
370 	mp->b_wptr = mp->b_datap->db_base + sizeof (char);
371 	freemsg(unlinkb(mp));
372 	if (q->q_flag&QREADR)
373 		putnext(q, mp);
374 	else
375 		qreply(q, mp);
376 }
377 
378 static int
379 check_strhead(queue_t *q)
380 {
381 	mblk_t *mp;
382 	union T_primitives *pptr;
383 
384 	for (mp = q->q_next->q_first; mp != NULL; mp = mp->b_next) {
385 
386 		switch (mp->b_datap->db_type) {
387 		case M_PROTO:
388 			pptr = (union T_primitives *)mp->b_rptr;
389 			if ((mp->b_wptr - mp->b_rptr) < sizeof (t_scalar_t))
390 				return (0);
391 			switch (pptr->type) {
392 
393 			case T_EXDATA_IND:
394 				return (0);
395 			case T_DATA_IND:
396 				if (mp->b_cont &&
397 				    (mp->b_cont->b_datap->db_type != M_DATA))
398 					return (0);
399 				break;
400 			default:
401 				return (0);
402 			}
403 			break;
404 
405 		case M_PCPROTO:
406 			return (0);
407 
408 		case M_DATA:
409 		case M_SIG:
410 			break;
411 		default:
412 			return (0);
413 		}
414 	}
415 	return (1);
416 }
417 
418 static void
419 strip_strhead(queue_t *q)
420 {
421 	mblk_t *mp;
422 	mblk_t *emp;
423 	mblk_t *tmp;
424 	union T_primitives *pptr;
425 
426 	q = q->q_next;
427 	/*CSTYLED*/
428 	for (mp = q->q_first; mp != NULL; ) {
429 
430 		switch (mp->b_datap->db_type) {
431 		case M_PROTO:
432 			pptr = (union T_primitives *)mp->b_rptr;
433 			switch (pptr->type) {
434 
435 			case T_DATA_IND:
436 				if (msgdsize(mp) == 0) {
437 strip0:
438 					tmp = mp->b_next;
439 					rmvq(q, mp);
440 					freemsg(mp);
441 					mp = tmp;
442 					break;
443 				}
444 				emp = mp->b_next;
445 				rmvq(q, mp);
446 				tmp = (mblk_t *)unlinkb(mp);
447 				freeb(mp);
448 				(void) insq(q, emp, tmp);
449 				mp = emp;
450 				break;
451 			}
452 			break;
453 
454 		case M_DATA:
455 			if (msgdsize(mp) == 0)
456 				goto strip0;
457 			mp = mp->b_next;
458 			break;
459 
460 		case M_SIG:
461 			mp = mp->b_next;
462 			break;
463 		}
464 	}
465 }
466