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