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 }