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 (c) 2014, Joyent, Inc. All rights reserved.
  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 /*ARGSUSED*/
 354 int
 355 av1394_async_poll(av1394_inst_t *avp, short events, int anyyet, short *reventsp,
 356                 struct pollhead **phpp)
 357 {
 358         av1394_async_t  *ap = &avp->av_a;
 359         av1394_queue_t  *rq = &ap->a_rq;
 360 
 361         AV1394_TNF_ENTER(av1394_async_poll);
 362 
 363         if (events & POLLIN) {
 364                 if (av1394_peekq(rq))
 365                         *reventsp |= POLLIN;
 366 
 367                 if ((!*reventsp && !anyyet) || (events & POLLET)) {
 368                         mutex_enter(&ap->a_mutex);
 369                         ap->a_pollevents |= POLLIN;
 370                         *phpp = &ap->a_pollhead;
 371                         mutex_exit(&ap->a_mutex);
 372                 }
 373         }
 374 
 375         AV1394_TNF_EXIT(av1394_async_poll);
 376         return (0);
 377 }
 378 
 379 
 380 /*
 381  * put a message on the read queue, take care of polling
 382  */
 383 void
 384 av1394_async_putq_rq(av1394_inst_t *avp, mblk_t *mp)
 385 {
 386         av1394_async_t  *ap = &avp->av_a;
 387 
 388         if (!av1394_putq(&ap->a_rq, mp)) {
 389                 freemsg(mp);
 390                 TNF_PROBE_0(av1394_async_putq_rq_error_putq,
 391                     AV1394_TNF_ASYNC_ERROR, "");
 392         } else {
 393                 mutex_enter(&ap->a_mutex);
 394                 if (ap->a_pollevents & POLLIN) {
 395                         ap->a_pollevents &= ~POLLIN;
 396                         mutex_exit(&ap->a_mutex);
 397                         pollwakeup(&ap->a_pollhead, POLLIN);
 398                 } else {
 399                         mutex_exit(&ap->a_mutex);
 400                 }
 401         }
 402 }
 403 
 404 /*
 405  *
 406  * --- configuration routines
 407  *
 408  * av1394_async_cleanup()
 409  *    Cleanup after attach
 410  */
 411 static void
 412 av1394_async_cleanup(av1394_inst_t *avp, int level)
 413 {
 414         av1394_async_t  *ap = &avp->av_a;
 415 
 416         ASSERT((level > 0) && (level <= AV1394_CLEANUP_LEVEL_MAX));
 417 
 418         switch (level) {
 419         default:
 420                 av1394_async_remove_minor_node(avp);
 421                 /* FALLTHRU */
 422         case 3:
 423                 av1394_cfgrom_fini(avp);
 424                 /* FALLTHRU */
 425         case 2:
 426                 av1394_fcp_detach(avp);
 427                 /* FALLTHRU */
 428         case 1:
 429                 av1394_destroyq(&ap->a_rq);
 430                 mutex_destroy(&ap->a_mutex);
 431         }
 432 }
 433 
 434 /*
 435  * av1394_async_create_minor_node()
 436  *    Create async minor node
 437  */
 438 static int
 439 av1394_async_create_minor_node(av1394_inst_t *avp)
 440 {
 441         int     ret;
 442 
 443         ret = ddi_create_minor_node(avp->av_dip, "async",
 444             S_IFCHR, AV1394_ASYNC_INST2MINOR(avp->av_instance),
 445             DDI_NT_AV_ASYNC, NULL);
 446         if (ret != DDI_SUCCESS) {
 447                 TNF_PROBE_0(av1394_async_create_minor_node_error,
 448                     AV1394_TNF_ASYNC_ERROR, "");
 449         }
 450         return (ret);
 451 }
 452 
 453 /*
 454  * av1394_async_remove_minor_node()
 455  *    Remove async minor node
 456  */
 457 static void
 458 av1394_async_remove_minor_node(av1394_inst_t *avp)
 459 {
 460         ddi_remove_minor_node(avp->av_dip, "async");
 461 }
 462 
 463 /*
 464  * av1394_async_update_targetinfo()
 465  *    Retrieve target info and bus generation
 466  */
 467 static int
 468 av1394_async_update_targetinfo(av1394_inst_t *avp)
 469 {
 470         av1394_async_t  *ap = &avp->av_a;
 471         uint_t          bg;
 472         int             ret;
 473 
 474         mutex_enter(&avp->av_mutex);
 475         bg = avp->av_attachinfo.localinfo.bus_generation;
 476         mutex_exit(&avp->av_mutex);
 477 
 478         mutex_enter(&ap->a_mutex);
 479         ret = t1394_get_targetinfo(avp->av_t1394_hdl, bg, 0, &ap->a_targetinfo);
 480         ap->a_bus_generation = bg;
 481         mutex_exit(&ap->a_mutex);
 482 
 483         return (ret);
 484 }
 485 
 486 static int
 487 av1394_async_db2arq_type(int dbtype)
 488 {
 489         int     arq_type;
 490 
 491         switch (dbtype) {
 492         case AV1394_M_FCP_RESP:
 493                 arq_type = IEC61883_ARQ_FCP_RESP;
 494                 break;
 495         case AV1394_M_FCP_CMD:
 496                 arq_type = IEC61883_ARQ_FCP_CMD;
 497                 break;
 498         case AV1394_M_BUS_RESET:
 499                 arq_type = IEC61883_ARQ_BUS_RESET;
 500                 break;
 501         default:
 502                 ASSERT(0);      /* cannot happen */
 503         }
 504         return (arq_type);
 505 }
 506 
 507 static void
 508 av1394_async_putbq(av1394_queue_t *q, mblk_t *mp)
 509 {
 510         if (!av1394_putbq(q, mp)) {
 511                 freemsg(mp);
 512                 TNF_PROBE_0(av1394_async_putbq_error,
 513                     AV1394_TNF_ASYNC_ERROR, "");
 514         }
 515 }
 516 
 517 /*ARGSUSED*/
 518 static int
 519 av1394_ioctl_arq_get_ibuf_size(av1394_inst_t *avp, void *arg, int mode)
 520 {
 521         av1394_async_t  *ap = &avp->av_a;
 522         int             sz;
 523         int             ret = 0;
 524 
 525         AV1394_TNF_ENTER(av1394_ioctl_arq_get_ibuf_size);
 526 
 527         sz = av1394_getmaxq(&ap->a_rq);
 528 
 529         if (ddi_copyout(&sz, arg, sizeof (sz), mode) != 0) {
 530                 ret = EFAULT;
 531         }
 532 
 533         AV1394_TNF_EXIT(av1394_ioctl_arq_get_ibuf_size);
 534         return (ret);
 535 }
 536 
 537 /*ARGSUSED*/
 538 static int
 539 av1394_ioctl_arq_set_ibuf_size(av1394_inst_t *avp, void *arg, int mode)
 540 {
 541         av1394_async_t  *ap = &avp->av_a;
 542         int             sz;
 543         int             ret = 0;
 544 
 545         AV1394_TNF_ENTER(av1394_ioctl_arq_set_ibuf_size);
 546 
 547         sz = (int)(intptr_t)arg;
 548 
 549         if ((sz < 0) || (sz > av1394_ibuf_size_max)) {
 550                 ret = EINVAL;
 551         } else {
 552                 av1394_setmaxq(&ap->a_rq, sz);
 553         }
 554 
 555         AV1394_TNF_EXIT(av1394_ioctl_arq_set_ibuf_size);
 556         return (ret);
 557 }