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