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 /*
  28  * Copyright 2017 Joyent, Inc.
  29  */
  30 
  31 /*
  32  * av1394 asynchronous module
  33  */
  34 #include <sys/stat.h>
  35 #include <sys/file.h>
  36 #include <sys/ddi.h>
  37 #include <sys/sunddi.h>
  38 #include <sys/1394/targets/av1394/av1394_impl.h>
  39 
  40 /* configuration routines */
  41 static void     av1394_async_cleanup(av1394_inst_t *, int);
  42 static int      av1394_async_create_minor_node(av1394_inst_t *);
  43 static void     av1394_async_remove_minor_node(av1394_inst_t *);
  44 static int      av1394_async_update_targetinfo(av1394_inst_t *);
  45 static int      av1394_async_db2arq_type(int);
  46 static void     av1394_async_putbq(av1394_queue_t *, mblk_t *);
  47 
  48 static int      av1394_ioctl_arq_get_ibuf_size(av1394_inst_t *, void *, int);
  49 static int      av1394_ioctl_arq_set_ibuf_size(av1394_inst_t *, void *, int);
  50 
  51 #define AV1394_TNF_ENTER(func)  \
  52         TNF_PROBE_0_DEBUG(func##_enter, AV1394_TNF_ASYNC_STACK, "");
  53 
  54 #define AV1394_TNF_EXIT(func)   \
  55         TNF_PROBE_0_DEBUG(func##_exit, AV1394_TNF_ASYNC_STACK, "");
  56 
  57 /* tunables */
  58 int av1394_ibuf_size_default = 64 * 1024;       /* default ibuf size */
  59 int av1394_ibuf_size_max = 1024 * 1024;         /* max ibuf size */
  60 
  61 /*
  62  *
  63  * --- configuration entry points
  64  *
  65  */
  66 int
  67 av1394_async_attach(av1394_inst_t *avp)
  68 {
  69         av1394_async_t  *ap = &avp->av_a;
  70         ddi_iblock_cookie_t ibc = avp->av_attachinfo.iblock_cookie;
  71 
  72         AV1394_TNF_ENTER(av1394_async_attach);
  73 
  74         mutex_init(&ap->a_mutex, NULL, MUTEX_DRIVER, ibc);
  75         av1394_initq(&ap->a_rq, ibc, av1394_ibuf_size_default);
  76 
  77         if (av1394_fcp_attach(avp) != DDI_SUCCESS) {
  78                 av1394_async_cleanup(avp, 1);
  79                 AV1394_TNF_EXIT(av1394_async_attach);
  80                 return (DDI_FAILURE);
  81         }
  82 
  83         if (av1394_cfgrom_init(avp) != DDI_SUCCESS) {
  84                 av1394_async_cleanup(avp, 2);
  85                 AV1394_TNF_EXIT(av1394_async_attach);
  86                 return (DDI_FAILURE);
  87         }
  88 
  89         if (av1394_async_create_minor_node(avp) != DDI_SUCCESS) {
  90                 av1394_async_cleanup(avp, 3);
  91                 AV1394_TNF_EXIT(av1394_async_attach);
  92                 return (DDI_FAILURE);
  93         }
  94 
  95         if (av1394_async_update_targetinfo(avp) != DDI_SUCCESS) {
  96                 av1394_async_cleanup(avp, 4);
  97                 AV1394_TNF_EXIT(av1394_async_attach);
  98                 return (DDI_FAILURE);
  99         }
 100 
 101         AV1394_TNF_EXIT(av1394_async_attach);
 102         return (DDI_SUCCESS);
 103 }
 104 
 105 void
 106 av1394_async_detach(av1394_inst_t *avp)
 107 {
 108         AV1394_TNF_ENTER(av1394_async_detach);
 109 
 110         av1394_async_cleanup(avp, AV1394_CLEANUP_LEVEL_MAX);
 111 
 112         AV1394_TNF_EXIT(av1394_async_detach);
 113 }
 114 
 115 void
 116 av1394_async_bus_reset(av1394_inst_t *avp)
 117 {
 118         av1394_async_t  *ap = &avp->av_a;
 119         mblk_t          *bp;
 120 
 121         AV1394_TNF_ENTER(av1394_async_bus_reset);
 122 
 123         (void) av1394_async_update_targetinfo(avp);
 124 
 125         mutex_enter(&ap->a_mutex);
 126         if (ap->a_nopen > 0) {
 127                 mutex_exit(&ap->a_mutex);
 128                 return;
 129         }
 130         mutex_exit(&ap->a_mutex);
 131 
 132         /* queue up a bus reset message */
 133         if ((bp = allocb(1, BPRI_HI)) == NULL) {
 134                 TNF_PROBE_0(av1394_async_bus_reset_error_allocb,
 135                     AV1394_TNF_ASYNC_ERROR, "");
 136         } else {
 137                 DB_TYPE(bp) = AV1394_M_BUS_RESET;
 138                 av1394_async_putq_rq(avp, bp);
 139         }
 140 
 141         AV1394_TNF_EXIT(av1394_async_bus_reset);
 142 }
 143 
 144 int
 145 av1394_async_cpr_resume(av1394_inst_t *avp)
 146 {
 147         int     ret;
 148 
 149         AV1394_TNF_ENTER(av1394_async_cpr_resume);
 150 
 151         ret = av1394_async_update_targetinfo(avp);
 152 
 153         AV1394_TNF_EXIT(av1394_async_cpr_resume);
 154         return (ret);
 155 }
 156 
 157 void
 158 av1394_async_reconnect(av1394_inst_t *avp)
 159 {
 160         AV1394_TNF_ENTER(av1394_async_reconnect);
 161 
 162         (void) av1394_async_update_targetinfo(avp);
 163 
 164         AV1394_TNF_EXIT(av1394_async_reconnect);
 165 }
 166 
 167 int
 168 av1394_async_open(av1394_inst_t *avp, int flag)
 169 {
 170         av1394_async_t  *ap = &avp->av_a;
 171 
 172         AV1394_TNF_ENTER(av1394_async_open);
 173 
 174         mutex_enter(&ap->a_mutex);
 175         if (ap->a_nopen == 0) {
 176                 ap->a_pollevents = 0;
 177         }
 178         ap->a_nopen++;
 179         ap->a_oflag = flag;
 180         mutex_exit(&ap->a_mutex);
 181 
 182         AV1394_TNF_EXIT(av1394_async_open);
 183         return (0);
 184 }
 185 
 186 /*ARGSUSED*/
 187 int
 188 av1394_async_close(av1394_inst_t *avp, int flag)
 189 {
 190         av1394_async_t  *ap = &avp->av_a;
 191 
 192         AV1394_TNF_ENTER(av1394_async_close);
 193 
 194         av1394_cfgrom_close(avp);
 195 
 196         av1394_flushq(&ap->a_rq);
 197 
 198         mutex_enter(&ap->a_mutex);
 199         ap->a_nopen = 0;
 200         ap->a_pollevents = 0;
 201         mutex_exit(&ap->a_mutex);
 202 
 203         AV1394_TNF_EXIT(av1394_async_close);
 204         return (0);
 205 }
 206 
 207 int
 208 av1394_async_read(av1394_inst_t *avp, struct uio *uiop)
 209 {
 210         av1394_async_t  *ap = &avp->av_a;
 211         av1394_queue_t  *q = &ap->a_rq;
 212         iec61883_arq_t  arq;
 213         int             ret = 0;
 214         mblk_t          *mp;
 215         int             dbtype;
 216         int             len;
 217 
 218         AV1394_TNF_ENTER(av1394_async_read);
 219 
 220         /* copyout as much as we can */
 221         while ((uiop->uio_resid > 0) && (ret == 0)) {
 222                 /*
 223                  * if data is available, copy it out. otherwise wait until
 224                  * data arrives, unless opened with non-blocking flag
 225                  */
 226                 if ((mp = av1394_getq(q)) == NULL) {
 227                         if (ap->a_oflag & FNDELAY) {
 228                                 AV1394_TNF_EXIT(av1394_async_read);
 229                                 return (EAGAIN);
 230                         }
 231                         if (av1394_qwait_sig(q) <= 0) {
 232                                 ret = EINTR;
 233                         }
 234                         continue;
 235                 }
 236                 dbtype = AV1394_DBTYPE(mp);
 237 
 238                 /* generate and copyout ARQ header, if not already */
 239                 if (!AV1394_IS_NOHDR(mp)) {
 240                         /* headers cannot be partially read */
 241                         if (uiop->uio_resid < sizeof (arq)) {
 242                                 av1394_async_putbq(q, mp);
 243                                 ret = EINVAL;
 244                                 break;
 245                         }
 246 
 247                         arq.arq_type = av1394_async_db2arq_type(dbtype);
 248                         arq.arq_len = MBLKL(mp);
 249                         arq.arq_data.octlet = 0;
 250 
 251                         /* copy ARQ-embedded data */
 252                         len = min(arq.arq_len, sizeof (arq.arq_data));
 253                         bcopy(mp->b_rptr, &arq.arq_data.buf[0], len);
 254 
 255                         /* copyout the ARQ */
 256                         ret = uiomove(&arq, sizeof (arq), UIO_READ, uiop);
 257                         if (ret != 0) {
 258                                 av1394_async_putbq(q, mp);
 259                                 break;
 260                         }
 261                         mp->b_rptr += len;
 262                         AV1394_MARK_NOHDR(mp);
 263                 }
 264 
 265                 /* any data left? */
 266                 if (MBLKL(mp) == 0) {
 267                         freemsg(mp);
 268                         continue;
 269                 }
 270 
 271                 /* now we have some data and some user buffer space to fill */
 272                 len = min(uiop->uio_resid, MBLKL(mp));
 273                 if (len > 0) {
 274                         ret = uiomove(mp->b_rptr, len, UIO_READ, uiop);
 275                         if (ret != 0) {
 276                                 av1394_async_putbq(q, mp);
 277                                 break;
 278                         }
 279                         mp->b_rptr += len;
 280                 }
 281 
 282                 /* save the rest of the data for later */
 283                 if (MBLKL(mp) > 0) {
 284                         av1394_async_putbq(q, mp);
 285                 }
 286         }
 287 
 288         AV1394_TNF_EXIT(av1394_async_read);
 289         return (0);
 290 }
 291 
 292 int
 293 av1394_async_write(av1394_inst_t *avp, struct uio *uiop)
 294 {
 295         iec61883_arq_t  arq;
 296         int             ret;
 297 
 298         AV1394_TNF_ENTER(av1394_async_write);
 299 
 300         /* all data should arrive in ARQ format */
 301         while (uiop->uio_resid >= sizeof (arq)) {
 302                 if ((ret = uiomove(&arq, sizeof (arq), UIO_WRITE, uiop)) != 0) {
 303                         break;
 304                 }
 305 
 306                 switch (arq.arq_type) {
 307                 case IEC61883_ARQ_FCP_CMD:
 308                 case IEC61883_ARQ_FCP_RESP:
 309                         ret = av1394_fcp_write(avp, &arq, uiop);
 310                         break;
 311                 default:
 312                         ret = EINVAL;
 313                 }
 314                 if (ret != 0) {
 315                         break;
 316                 }
 317         }
 318 
 319         AV1394_TNF_EXIT(av1394_async_write);
 320         return (ret);
 321 }
 322 
 323 /*ARGSUSED*/
 324 int
 325 av1394_async_ioctl(av1394_inst_t *avp, int cmd, intptr_t arg, int mode,
 326     int *rvalp)
 327 {
 328         int     ret = EINVAL;
 329 
 330         AV1394_TNF_ENTER(av1394_async_ioctl);
 331 
 332         switch (cmd) {
 333         case IEC61883_ARQ_GET_IBUF_SIZE:
 334                 ret = av1394_ioctl_arq_get_ibuf_size(avp, (void *)arg, mode);
 335                 break;
 336         case IEC61883_ARQ_SET_IBUF_SIZE:
 337                 ret = av1394_ioctl_arq_set_ibuf_size(avp, (void *)arg, mode);
 338                 break;
 339         case IEC61883_NODE_GET_BUS_NAME:
 340                 ret = av1394_ioctl_node_get_bus_name(avp, (void *)arg, mode);
 341                 break;
 342         case IEC61883_NODE_GET_UID:
 343                 ret = av1394_ioctl_node_get_uid(avp, (void *)arg, mode);
 344                 break;
 345         case IEC61883_NODE_GET_TEXT_LEAF:
 346                 ret = av1394_ioctl_node_get_text_leaf(avp, (void *)arg, mode);
 347         }
 348 
 349         AV1394_TNF_EXIT(av1394_async_ioctl);
 350         return (ret);
 351 }
 352 
 353 int
 354 av1394_async_poll(av1394_inst_t *avp, short events, int anyyet, short *reventsp,
 355     struct pollhead **phpp)
 356 {
 357         av1394_async_t  *ap = &avp->av_a;
 358         av1394_queue_t  *rq = &ap->a_rq;
 359 
 360         AV1394_TNF_ENTER(av1394_async_poll);
 361 
 362         if (events & (POLLIN | POLLET)) {
 363                 if ((events & POLLIN) && av1394_peekq(rq)) {
 364                         *reventsp |= POLLIN;
 365                 }
 366 
 367                 if ((!*reventsp && !anyyet) || (events & POLLET)) {
 368                         mutex_enter(&ap->a_mutex);
 369                         if (events & POLLIN) {
 370                                 ap->a_pollevents |= POLLIN;
 371                         }
 372                         *phpp = &ap->a_pollhead;
 373                         mutex_exit(&ap->a_mutex);
 374                 }
 375         }
 376 
 377         AV1394_TNF_EXIT(av1394_async_poll);
 378         return (0);
 379 }
 380 
 381 
 382 /*
 383  * put a message on the read queue, take care of polling
 384  */
 385 void
 386 av1394_async_putq_rq(av1394_inst_t *avp, mblk_t *mp)
 387 {
 388         av1394_async_t  *ap = &avp->av_a;
 389 
 390         if (!av1394_putq(&ap->a_rq, mp)) {
 391                 freemsg(mp);
 392                 TNF_PROBE_0(av1394_async_putq_rq_error_putq,
 393                     AV1394_TNF_ASYNC_ERROR, "");
 394         } else {
 395                 mutex_enter(&ap->a_mutex);
 396                 if (ap->a_pollevents & POLLIN) {
 397                         ap->a_pollevents &= ~POLLIN;
 398                         mutex_exit(&ap->a_mutex);
 399                         pollwakeup(&ap->a_pollhead, POLLIN);
 400                 } else {
 401                         mutex_exit(&ap->a_mutex);
 402                 }
 403         }
 404 }
 405 
 406 /*
 407  *
 408  * --- configuration routines
 409  *
 410  * av1394_async_cleanup()
 411  *    Cleanup after attach
 412  */
 413 static void
 414 av1394_async_cleanup(av1394_inst_t *avp, int level)
 415 {
 416         av1394_async_t  *ap = &avp->av_a;
 417 
 418         ASSERT((level > 0) && (level <= AV1394_CLEANUP_LEVEL_MAX));
 419 
 420         switch (level) {
 421         default:
 422                 av1394_async_remove_minor_node(avp);
 423                 /* FALLTHRU */
 424         case 3:
 425                 av1394_cfgrom_fini(avp);
 426                 /* FALLTHRU */
 427         case 2:
 428                 av1394_fcp_detach(avp);
 429                 /* FALLTHRU */
 430         case 1:
 431                 av1394_destroyq(&ap->a_rq);
 432                 mutex_destroy(&ap->a_mutex);
 433         }
 434 }
 435 
 436 /*
 437  * av1394_async_create_minor_node()
 438  *    Create async minor node
 439  */
 440 static int
 441 av1394_async_create_minor_node(av1394_inst_t *avp)
 442 {
 443         int     ret;
 444 
 445         ret = ddi_create_minor_node(avp->av_dip, "async",
 446             S_IFCHR, AV1394_ASYNC_INST2MINOR(avp->av_instance),
 447             DDI_NT_AV_ASYNC, NULL);
 448         if (ret != DDI_SUCCESS) {
 449                 TNF_PROBE_0(av1394_async_create_minor_node_error,
 450                     AV1394_TNF_ASYNC_ERROR, "");
 451         }
 452         return (ret);
 453 }
 454 
 455 /*
 456  * av1394_async_remove_minor_node()
 457  *    Remove async minor node
 458  */
 459 static void
 460 av1394_async_remove_minor_node(av1394_inst_t *avp)
 461 {
 462         ddi_remove_minor_node(avp->av_dip, "async");
 463 }
 464 
 465 /*
 466  * av1394_async_update_targetinfo()
 467  *    Retrieve target info and bus generation
 468  */
 469 static int
 470 av1394_async_update_targetinfo(av1394_inst_t *avp)
 471 {
 472         av1394_async_t  *ap = &avp->av_a;
 473         uint_t          bg;
 474         int             ret;
 475 
 476         mutex_enter(&avp->av_mutex);
 477         bg = avp->av_attachinfo.localinfo.bus_generation;
 478         mutex_exit(&avp->av_mutex);
 479 
 480         mutex_enter(&ap->a_mutex);
 481         ret = t1394_get_targetinfo(avp->av_t1394_hdl, bg, 0, &ap->a_targetinfo);
 482         ap->a_bus_generation = bg;
 483         mutex_exit(&ap->a_mutex);
 484 
 485         return (ret);
 486 }
 487 
 488 static int
 489 av1394_async_db2arq_type(int dbtype)
 490 {
 491         int     arq_type;
 492 
 493         switch (dbtype) {
 494         case AV1394_M_FCP_RESP:
 495                 arq_type = IEC61883_ARQ_FCP_RESP;
 496                 break;
 497         case AV1394_M_FCP_CMD:
 498                 arq_type = IEC61883_ARQ_FCP_CMD;
 499                 break;
 500         case AV1394_M_BUS_RESET:
 501                 arq_type = IEC61883_ARQ_BUS_RESET;
 502                 break;
 503         default:
 504                 ASSERT(0);      /* cannot happen */
 505         }
 506         return (arq_type);
 507 }
 508 
 509 static void
 510 av1394_async_putbq(av1394_queue_t *q, mblk_t *mp)
 511 {
 512         if (!av1394_putbq(q, mp)) {
 513                 freemsg(mp);
 514                 TNF_PROBE_0(av1394_async_putbq_error,
 515                     AV1394_TNF_ASYNC_ERROR, "");
 516         }
 517 }
 518 
 519 /*ARGSUSED*/
 520 static int
 521 av1394_ioctl_arq_get_ibuf_size(av1394_inst_t *avp, void *arg, int mode)
 522 {
 523         av1394_async_t  *ap = &avp->av_a;
 524         int             sz;
 525         int             ret = 0;
 526 
 527         AV1394_TNF_ENTER(av1394_ioctl_arq_get_ibuf_size);
 528 
 529         sz = av1394_getmaxq(&ap->a_rq);
 530 
 531         if (ddi_copyout(&sz, arg, sizeof (sz), mode) != 0) {
 532                 ret = EFAULT;
 533         }
 534 
 535         AV1394_TNF_EXIT(av1394_ioctl_arq_get_ibuf_size);
 536         return (ret);
 537 }
 538 
 539 /*ARGSUSED*/
 540 static int
 541 av1394_ioctl_arq_set_ibuf_size(av1394_inst_t *avp, void *arg, int mode)
 542 {
 543         av1394_async_t  *ap = &avp->av_a;
 544         int             sz;
 545         int             ret = 0;
 546 
 547         AV1394_TNF_ENTER(av1394_ioctl_arq_set_ibuf_size);
 548 
 549         sz = (int)(intptr_t)arg;
 550 
 551         if ((sz < 0) || (sz > av1394_ibuf_size_max)) {
 552                 ret = EINVAL;
 553         } else {
 554                 av1394_setmaxq(&ap->a_rq, sz);
 555         }
 556 
 557         AV1394_TNF_EXIT(av1394_ioctl_arq_set_ibuf_size);
 558         return (ret);
 559 }