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