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 */
57struct trw_trw {
58	queue_t	*trw_rdq;
59	uint_t	trw_flags;
60};
61
62/*
63 * stream data structure definitions
64 */
65static	int tirdwropen(queue_t *q, dev_t *dev,
66    int flag, int sflag, cred_t	*cr);
67
68static	int tirdwrclose(queue_t *q, int flag, cred_t *cr);
69
70static	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 */
76static int tirdwrrput(queue_t *q, mblk_t *mp);
77static int tirdwrwput(queue_t *q, mblk_t *mp);
78
79static struct module_info tirdwr_info = {
80	TIRDWR_ID,
81	"tirdwr",
82	0,
83	INFPSZ,
84	4096,
85	1024
86};
87
88static struct qinit tirdwrrinit = {
89	tirdwrrput,
90	NULL,
91	tirdwropen,
92	tirdwrclose,
93	nulldev,
94	&tirdwr_info,
95	NULL
96};
97
98static struct qinit tirdwrwinit = {
99	tirdwrwput,
100	NULL,
101	tirdwropen,
102	tirdwrclose,
103	nulldev,
104	&tirdwr_info,
105	NULL
106};
107
108static struct streamtab trwinfo = {
109	&tirdwrrinit,
110	&tirdwrwinit,
111	NULL,
112	NULL
113};
114
115static struct fmodsw fsw = {
116	"tirdwr",
117	&trwinfo,
118	D_NEW|D_MTQPAIR|D_MP
119};
120
121static struct modlstrmod modlstrmod = {
122	&mod_strmodops, "xport interface rd/wr str mod", &fsw
123};
124
125static struct modlinkage modlinkage = {
126	MODREV_1, &modlstrmod, NULL
127};
128
129int
130_init(void)
131{
132	return (mod_install(&modlinkage));
133}
134
135int
136_fini(void)
137{
138	return (mod_remove(&modlinkage));
139}
140
141int
142_info(struct modinfo *modinfop)
143{
144	return (mod_info(&modlinkage, modinfop));
145}
146
147static void send_fatal(queue_t *q, mblk_t *mp);
148static 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*/
156static int
157tirdwropen(
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*/
206static int
207tirdwrclose(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
241static int
242tirdwrrput(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 */
326static int
327tirdwrwput(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
358static void
359send_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
377static int
378check_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
417static void
418strip_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) {
436strip0:
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