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 }