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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * av1394 isochronous module 28 */ 29 #include <sys/stat.h> 30 #include <sys/file.h> 31 #include <sys/ddi.h> 32 #include <sys/sunddi.h> 33 #include <sys/av/iec61883.h> 34 #include <sys/1394/targets/av1394/av1394_impl.h> 35 36 /* configuration routines */ 37 static int av1394_isoch_create_minor_node(av1394_inst_t *); 38 static void av1394_isoch_remove_minor_node(av1394_inst_t *); 39 static void av1394_isoch_cleanup(av1394_inst_t *, int); 40 av1394_isoch_seg_t *av1394_isoch_find_seg(av1394_inst_t *, offset_t, size_t); 41 static int av1394_isoch_autorecv_init(av1394_inst_t *, av1394_ic_t **); 42 static int av1394_isoch_autoxmit_init(av1394_inst_t *, av1394_ic_t **, 43 struct uio *); 44 45 /* ioctls */ 46 static int av1394_ioctl_isoch_init(av1394_inst_t *, void *, int); 47 static av1394_ic_t *av1394_ioctl_isoch_handle2ic(av1394_inst_t *, void *); 48 static int av1394_ioctl_isoch_fini(av1394_inst_t *, void *, int); 49 static int av1394_ioctl_start(av1394_inst_t *, void *, int); 50 static int av1394_ioctl_stop(av1394_inst_t *, void *, int); 51 static int av1394_ioctl_recv(av1394_inst_t *, void *, int); 52 static int av1394_ioctl_xmit(av1394_inst_t *, void *, int); 53 54 static uint_t av1394_isoch_softintr(caddr_t); 55 56 static struct devmap_callback_ctl av1394_isoch_devmap_ops = { 57 DEVMAP_OPS_REV, /* rev */ 58 NULL, /* map */ 59 NULL, /* access */ 60 NULL, /* dup */ 61 NULL, /* unmap */ 62 }; 63 64 /* tunables */ 65 int av1394_rate_n_dv_ntsc = 246; 66 int av1394_rate_d_dv_ntsc = 3840; 67 int av1394_rate_n_dv_pal = 1; 68 int av1394_rate_d_dv_pal = 16; 69 70 int av1394_isoch_autorecv_nframes = 50; 71 int av1394_isoch_autorecv_framesz = 250; 72 int av1394_isoch_autoxmit_nframes = 50; 73 int av1394_isoch_autoxmit_framesz = 250; 74 75 #define AV1394_TNF_ENTER(func) \ 76 TNF_PROBE_0_DEBUG(func##_enter, AV1394_TNF_ISOCH_STACK, ""); 77 78 #define AV1394_TNF_EXIT(func) \ 79 TNF_PROBE_0_DEBUG(func##_exit, AV1394_TNF_ISOCH_STACK, ""); 80 81 int 82 av1394_isoch_attach(av1394_inst_t *avp) 83 { 84 av1394_isoch_t *ip = &avp->av_i; 85 ddi_iblock_cookie_t ibc = avp->av_attachinfo.iblock_cookie; 86 87 AV1394_TNF_ENTER(av1394_isoch_attach); 88 89 mutex_init(&ip->i_mutex, NULL, MUTEX_DRIVER, ibc); 90 91 mutex_enter(&ip->i_mutex); 92 if (av1394_isoch_create_minor_node(avp) != DDI_SUCCESS) { 93 mutex_exit(&ip->i_mutex); 94 av1394_isoch_cleanup(avp, 1); 95 AV1394_TNF_EXIT(av1394_isoch_attach); 96 return (DDI_FAILURE); 97 } 98 99 if (ddi_add_softintr(avp->av_dip, DDI_SOFTINT_LOW, &ip->i_softintr_id, 100 0, 0, av1394_isoch_softintr, (caddr_t)avp) != DDI_SUCCESS) { 101 mutex_exit(&ip->i_mutex); 102 av1394_isoch_cleanup(avp, 2); 103 AV1394_TNF_EXIT(av1394_isoch_attach); 104 return (DDI_FAILURE); 105 } 106 107 if (av1394_cmp_init(avp) != DDI_SUCCESS) { 108 mutex_exit(&ip->i_mutex); 109 av1394_isoch_cleanup(avp, 3); 110 AV1394_TNF_EXIT(av1394_isoch_attach); 111 return (DDI_FAILURE); 112 } 113 114 av1394_as_init(&ip->i_mmap_as); 115 mutex_exit(&ip->i_mutex); 116 117 AV1394_TNF_EXIT(av1394_isoch_attach); 118 return (DDI_SUCCESS); 119 } 120 121 void 122 av1394_isoch_detach(av1394_inst_t *avp) 123 { 124 AV1394_TNF_ENTER(av1394_isoch_detach); 125 126 av1394_isoch_cleanup(avp, AV1394_CLEANUP_LEVEL_MAX); 127 128 AV1394_TNF_EXIT(av1394_isoch_detach); 129 } 130 131 int 132 av1394_isoch_cpr_suspend(av1394_inst_t *avp) 133 { 134 av1394_isoch_t *ip = &avp->av_i; 135 av1394_ic_t *icp; 136 int i; 137 int ret = DDI_SUCCESS; 138 139 AV1394_TNF_ENTER(av1394_isoch_cpr_suspend); 140 141 /* 142 * suspend only if there are no active channels 143 */ 144 mutex_enter(&ip->i_mutex); 145 for (i = 0; (i < NELEM(ip->i_ic)) && (ret == DDI_SUCCESS); i++) { 146 icp = ip->i_ic[i]; 147 if (icp) { 148 mutex_enter(&icp->ic_mutex); 149 if (icp->ic_state != AV1394_IC_IDLE) { 150 ret = DDI_FAILURE; 151 } 152 mutex_exit(&icp->ic_mutex); 153 } 154 } 155 mutex_exit(&ip->i_mutex); 156 157 AV1394_TNF_EXIT(av1394_isoch_cpr_suspend); 158 return (ret); 159 } 160 161 /*ARGSUSED*/ 162 int 163 av1394_isoch_close(av1394_inst_t *avp, int flag) 164 { 165 int ret; 166 167 AV1394_TNF_ENTER(av1394_isoch_close); 168 169 ret = av1394_ic_close(avp, flag); 170 av1394_cmp_close(avp); 171 172 AV1394_TNF_EXIT(av1394_isoch_close); 173 return (ret); 174 } 175 176 int 177 av1394_isoch_read(av1394_inst_t *avp, struct uio *uiop) 178 { 179 av1394_ic_t *icp; 180 int ret; 181 182 AV1394_TNF_ENTER(av1394_isoch_read); 183 184 /* use broadcast channel */ 185 icp = avp->av_i.i_ic[63]; 186 if (icp == NULL) { 187 if ((ret = av1394_isoch_autorecv_init(avp, &icp)) != 0) { 188 AV1394_TNF_EXIT(av1394_isoch_read); 189 return (ret); 190 } 191 } else if (icp->ic_dir != AV1394_IR) { 192 /* channel already used for xmit */ 193 return (EBUSY); 194 } 195 196 if ((ret = av1394_ir_start(icp)) == 0) { 197 ret = av1394_ir_read(icp, uiop); 198 } 199 200 AV1394_TNF_EXIT(av1394_isoch_read); 201 return (ret); 202 } 203 204 int 205 av1394_isoch_write(av1394_inst_t *avp, struct uio *uiop) 206 { 207 av1394_ic_t *icp; 208 int ret; 209 210 AV1394_TNF_ENTER(av1394_isoch_write); 211 212 /* use broadcast channel */ 213 icp = avp->av_i.i_ic[63]; 214 if (icp == NULL) { 215 if ((ret = av1394_isoch_autoxmit_init(avp, &icp, uiop)) != 0) { 216 AV1394_TNF_EXIT(av1394_isoch_write); 217 return (ret); 218 } 219 } else if (icp->ic_dir != AV1394_IT) { 220 /* channel already used for recv */ 221 AV1394_TNF_EXIT(av1394_isoch_write); 222 return (EBUSY); 223 } 224 225 ret = av1394_it_write(icp, uiop); 226 227 AV1394_TNF_EXIT(av1394_isoch_write); 228 return (ret); 229 } 230 231 /*ARGSUSED*/ 232 int 233 av1394_isoch_ioctl(av1394_inst_t *avp, int cmd, intptr_t arg, int mode, 234 int *rvalp) 235 { 236 int ret = EINVAL; 237 238 switch (cmd) { 239 case IEC61883_ISOCH_INIT: 240 ret = av1394_ioctl_isoch_init(avp, (void *)arg, mode); 241 break; 242 case IEC61883_ISOCH_FINI: 243 ret = av1394_ioctl_isoch_fini(avp, (void *)arg, mode); 244 break; 245 case IEC61883_START: 246 ret = av1394_ioctl_start(avp, (void *)arg, mode); 247 break; 248 case IEC61883_STOP: 249 ret = av1394_ioctl_stop(avp, (void *)arg, mode); 250 break; 251 case IEC61883_RECV: 252 ret = av1394_ioctl_recv(avp, (void *)arg, mode); 253 break; 254 case IEC61883_XMIT: 255 ret = av1394_ioctl_xmit(avp, (void *)arg, mode); 256 break; 257 case IEC61883_PLUG_INIT: 258 ret = av1394_ioctl_plug_init(avp, (void *)arg, mode); 259 break; 260 case IEC61883_PLUG_FINI: 261 ret = av1394_ioctl_plug_fini(avp, (void *)arg, mode); 262 break; 263 case IEC61883_PLUG_REG_READ: 264 ret = av1394_ioctl_plug_reg_read(avp, (void *)arg, mode); 265 break; 266 case IEC61883_PLUG_REG_CAS: 267 ret = av1394_ioctl_plug_reg_cas(avp, (void *)arg, mode); 268 break; 269 } 270 271 return (ret); 272 } 273 274 /*ARGSUSED*/ 275 int 276 av1394_isoch_devmap(av1394_inst_t *avp, devmap_cookie_t dhp, offset_t off, 277 size_t len, size_t *maplen, uint_t model) 278 { 279 av1394_isoch_seg_t *isp; 280 281 AV1394_TNF_ENTER(av1394_isoch_devmap); 282 283 *maplen = 0; 284 285 /* find segment */ 286 isp = av1394_isoch_find_seg(avp, off, ptob(btopr(len))); 287 if (isp == NULL) { 288 AV1394_TNF_EXIT(av1394_isoch_devmap); 289 return (EINVAL); 290 } 291 292 /* map segment */ 293 if (devmap_umem_setup(dhp, avp->av_dip, &av1394_isoch_devmap_ops, 294 isp->is_umem_cookie, 0, isp->is_umem_size, PROT_ALL, 0, 295 &avp->av_attachinfo.acc_attr) != 0) { 296 TNF_PROBE_0(av1394_isoch_devmap_error_umem_setup, 297 AV1394_TNF_ISOCH_ERROR, ""); 298 AV1394_TNF_EXIT(av1394_isoch_devmap); 299 return (EINVAL); 300 } 301 *maplen = isp->is_umem_size; 302 303 AV1394_TNF_EXIT(av1394_isoch_devmap); 304 return (0); 305 } 306 307 /* 308 * 309 * --- configuration routines 310 * 311 * av1394_isoch_create_minor_node() 312 * Create isoch minor node 313 */ 314 static int 315 av1394_isoch_create_minor_node(av1394_inst_t *avp) 316 { 317 int ret; 318 319 ret = ddi_create_minor_node(avp->av_dip, "isoch", 320 S_IFCHR, AV1394_ISOCH_INST2MINOR(avp->av_instance), 321 DDI_NT_AV_ISOCH, NULL); 322 if (ret != DDI_SUCCESS) { 323 TNF_PROBE_0(av1394_isoch_create_minor_node_error, 324 AV1394_TNF_ISOCH_ERROR, ""); 325 } 326 return (ret); 327 } 328 329 /* 330 * av1394_isoch_remove_minor_node() 331 * Remove isoch minor node 332 */ 333 static void 334 av1394_isoch_remove_minor_node(av1394_inst_t *avp) 335 { 336 ddi_remove_minor_node(avp->av_dip, "isoch"); 337 } 338 339 /* 340 * av1394_isoch_cleanup() 341 * Cleanup after attach 342 */ 343 static void 344 av1394_isoch_cleanup(av1394_inst_t *avp, int level) 345 { 346 av1394_isoch_t *ip = &avp->av_i; 347 348 ASSERT((level > 0) && (level <= AV1394_CLEANUP_LEVEL_MAX)); 349 350 switch (level) { 351 default: 352 mutex_enter(&ip->i_mutex); 353 av1394_as_fini(&ip->i_mmap_as); 354 av1394_cmp_fini(avp); 355 mutex_exit(&ip->i_mutex); 356 /* FALLTHRU */ 357 case 3: 358 ddi_remove_softintr(ip->i_softintr_id); 359 /* FALLTHRU */ 360 case 2: 361 av1394_isoch_remove_minor_node(avp); 362 /* FALLTHRU */ 363 case 1: 364 mutex_destroy(&ip->i_mutex); 365 } 366 } 367 368 /* 369 * av1394_isoch_find_seg() 370 * Given an offset and size, find a matching av1394_isoch_seg_t structure. 371 */ 372 av1394_isoch_seg_t * 373 av1394_isoch_find_seg(av1394_inst_t *avp, offset_t off, size_t len) 374 { 375 av1394_isoch_t *ip = &avp->av_i; 376 av1394_ic_t *icp; 377 av1394_isoch_pool_t *pool; 378 av1394_isoch_seg_t *isp; 379 offset_t segoff; 380 int i; 381 382 /* find channel from within this range */ 383 for (i = 0; i < NELEM(ip->i_ic); i++) { 384 icp = ip->i_ic[i]; 385 if (icp == NULL) { 386 continue; 387 } 388 if ((off >= icp->ic_mmap_off) && 389 (off + len <= icp->ic_mmap_off + icp->ic_mmap_sz)) { 390 off -= icp->ic_mmap_off; /* convert to base */ 391 break; 392 } 393 icp = NULL; 394 } 395 if (icp == NULL) { 396 TNF_PROBE_0(av1394_isoch_find_seg_error_nochan, 397 AV1394_TNF_ISOCH_ERROR, ""); 398 return (NULL); 399 } 400 401 /* find a segment */ 402 pool = (icp->ic_dir == AV1394_IR) ? 403 &icp->ic_ir.ir_data_pool : &icp->ic_it.it_data_pool; 404 for (segoff = 0, i = 0; i < pool->ip_nsegs; i++) { 405 isp = &pool->ip_seg[i]; 406 if (off == segoff) { 407 break; 408 } 409 segoff += isp->is_umem_size; 410 isp = NULL; 411 } 412 if (isp == NULL) { 413 TNF_PROBE_0(av1394_isoch_find_seg_error_noseg, 414 AV1394_TNF_ISOCH_ERROR, ""); 415 return (NULL); 416 } 417 418 /* only whole segments can be mapped */ 419 if (len != isp->is_umem_size) { 420 TNF_PROBE_0(av1394_isoch_devmap_error_whole, 421 AV1394_TNF_ISOCH_ERROR, ""); 422 return (NULL); 423 } 424 return (isp); 425 } 426 427 /* 428 * initialize default channel for data receipt 429 */ 430 static int 431 av1394_isoch_autorecv_init(av1394_inst_t *avp, av1394_ic_t **icpp) 432 { 433 iec61883_isoch_init_t ii; 434 int ret = 0; 435 436 AV1394_TNF_ENTER(av1394_isoch_autorecv_init); 437 438 bzero(&ii, sizeof (ii)); 439 ii.ii_version = IEC61883_V1_0; 440 ii.ii_pkt_size = 512; 441 ii.ii_frame_size = av1394_isoch_autorecv_framesz; 442 ii.ii_frame_cnt = av1394_isoch_autorecv_nframes; 443 ii.ii_direction = IEC61883_DIR_RECV; 444 ii.ii_bus_speed = IEC61883_S100; 445 ii.ii_channel = (1ULL << 63); 446 447 ret = av1394_ic_init(avp, &ii, icpp); 448 449 AV1394_TNF_EXIT(av1394_isoch_autorecv_init); 450 return (ret); 451 } 452 453 /* 454 * initialize default channel for data xmit 455 */ 456 static int 457 av1394_isoch_autoxmit_init(av1394_inst_t *avp, av1394_ic_t **icpp, 458 struct uio *uiop) 459 { 460 av1394_isoch_autoxmit_t *axp = &avp->av_i.i_autoxmit; 461 iec61883_isoch_init_t ii; 462 uint_t fmt, dbs, fn, f5060, stype; /* CIP fields */ 463 int ret = 0; 464 465 AV1394_TNF_ENTER(av1394_isoch_autoxmit_init); 466 467 /* copyin the first CIP header */ 468 axp->ax_copy_ciph = B_FALSE; 469 if (uiop->uio_resid < AV1394_CIPSZ) { 470 TNF_PROBE_0_DEBUG(av1394_isoch_autoxmit_init_error_cipsz, 471 AV1394_TNF_ISOCH_ERROR, ""); 472 return (EINVAL); 473 } 474 ret = uiomove(axp->ax_ciph, AV1394_CIPSZ, UIO_WRITE, uiop); 475 if (ret != 0) { 476 return (ret); 477 } 478 axp->ax_copy_ciph = B_TRUE; 479 480 /* parse CIP header */ 481 dbs = axp->ax_ciph[1]; 482 fn = (axp->ax_ciph[2] >> 6) & 0x3; 483 fmt = axp->ax_ciph[4] & 0x3F; 484 stype = (axp->ax_ciph[5] >> 2) & 0x1F; 485 486 /* fill out the init structure */ 487 bzero(&ii, sizeof (ii)); 488 ii.ii_version = IEC61883_V1_0; 489 ii.ii_frame_cnt = av1394_isoch_autoxmit_nframes; 490 ii.ii_direction = IEC61883_DIR_XMIT; 491 ii.ii_bus_speed = IEC61883_S100; 492 ii.ii_channel = (1ULL << 63); 493 ii.ii_dbs = dbs; 494 ii.ii_fn = fn; 495 496 if ((fmt == 0) && (dbs == 0x78) && (fn == 0) && (stype == 0)) { 497 /* either DV-NTSC or DV-PAL */ 498 ii.ii_pkt_size = 488; 499 ii.ii_ts_mode = IEC61883_TS_SYT; 500 f5060 = axp->ax_ciph[5] & 0x80; 501 if (f5060 == 0) { 502 axp->ax_fmt = AV1394_ISOCH_AUTOXMIT_DV_NTSC; 503 ii.ii_frame_size = AV1394_DV_NTSC_FRAMESZ; 504 ii.ii_rate_n = av1394_rate_n_dv_ntsc; 505 ii.ii_rate_d = av1394_rate_d_dv_ntsc; 506 } else { 507 axp->ax_fmt = AV1394_ISOCH_AUTOXMIT_DV_PAL; 508 ii.ii_frame_size = AV1394_DV_PAL_FRAMESZ; 509 ii.ii_rate_n = av1394_rate_n_dv_pal; 510 ii.ii_rate_d = av1394_rate_d_dv_pal; 511 } 512 } else { 513 /* raw stream */ 514 axp->ax_fmt = AV1394_ISOCH_AUTOXMIT_UNKNOWN; 515 ii.ii_pkt_size = 512; 516 ii.ii_frame_size = av1394_isoch_autoxmit_framesz; 517 ii.ii_ts_mode = IEC61883_TS_NONE; 518 } 519 520 ret = av1394_ic_init(avp, &ii, icpp); 521 522 AV1394_TNF_EXIT(av1394_isoch_autoxmit_init); 523 return (ret); 524 } 525 526 527 /* 528 * 529 * --- ioctls 530 * these routines are generally responsible for copyin/out of arguments 531 * and passing control to the actual implementation. 532 * 533 */ 534 static int 535 av1394_ioctl_isoch_init(av1394_inst_t *avp, void *arg, int mode) 536 { 537 iec61883_isoch_init_t ii; 538 #ifdef _MULTI_DATAMODEL 539 iec61883_isoch_init32_t ii32; 540 #endif 541 av1394_ic_t *icp; 542 int ret; 543 544 AV1394_TNF_ENTER(av1394_ioctl_isoch_init); 545 546 if (ddi_copyin(arg, &ii, sizeof (ii), mode) != 0) { 547 AV1394_TNF_EXIT(av1394_ioctl_isoch_init); 548 return (EFAULT); 549 } 550 551 ret = av1394_ic_init(avp, &ii, &icp); 552 553 if (ret != 0) { 554 AV1394_TNF_EXIT(av1394_ioctl_isoch_init); 555 #ifdef _MULTI_DATAMODEL 556 if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) { 557 bcopy(&ii, &ii32, sizeof (ii32)); 558 ii32.ii_error = ii.ii_error; 559 (void) ddi_copyout(&ii32, arg, sizeof (ii32), mode); 560 } else 561 #endif 562 (void) ddi_copyout(&ii, arg, sizeof (ii), mode); 563 return (ret); 564 } 565 566 #ifdef _MULTI_DATAMODEL 567 /* fixup 32-bit deviations */ 568 if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) { 569 bcopy(&ii, &ii32, sizeof (ii32)); 570 ii32.ii_mmap_off = ii.ii_mmap_off; 571 ii32.ii_rchannel = ii.ii_rchannel; 572 ii32.ii_error = ii.ii_error; 573 ret = ddi_copyout(&ii32, arg, sizeof (ii32), mode); 574 } else 575 #endif 576 ret = ddi_copyout(&ii, arg, sizeof (ii), mode); 577 if (ret != 0) { 578 AV1394_TNF_EXIT(av1394_ioctl_isoch_init); 579 return (ENOMEM); 580 } 581 582 AV1394_TNF_EXIT(av1394_ioctl_isoch_init); 583 return (ret); 584 } 585 586 static av1394_ic_t * 587 av1394_ioctl_isoch_handle2ic(av1394_inst_t *avp, void *arg) 588 { 589 int num = (int)(intptr_t)arg; 590 av1394_isoch_t *ip = &avp->av_i; 591 592 if (num >= (sizeof (ip->i_ic) / sizeof (av1394_ic_t))) { 593 TNF_PROBE_0(av1394_ioctl_isoch_handle2ic_error_range, 594 AV1394_TNF_ISOCH_ERROR, ""); 595 return (NULL); 596 } 597 if (ip->i_ic[num] == NULL) { 598 TNF_PROBE_0(av1394_ioctl_isoch_handle2ic_error_null, 599 AV1394_TNF_ISOCH_ERROR, ""); 600 } 601 return (ip->i_ic[num]); 602 } 603 604 /*ARGSUSED*/ 605 static int 606 av1394_ioctl_isoch_fini(av1394_inst_t *avp, void *arg, int mode) 607 { 608 av1394_ic_t *icp; 609 610 AV1394_TNF_ENTER(av1394_ioctl_isoch_fini); 611 612 if ((icp = av1394_ioctl_isoch_handle2ic(avp, arg)) != NULL) { 613 av1394_ic_fini(icp); 614 } 615 616 AV1394_TNF_EXIT(av1394_ioctl_isoch_fini); 617 return (0); 618 } 619 620 /*ARGSUSED*/ 621 static int 622 av1394_ioctl_start(av1394_inst_t *avp, void *arg, int mode) 623 { 624 av1394_ic_t *icp; 625 int ret = EINVAL; 626 627 AV1394_TNF_ENTER(av1394_ioctl_start); 628 629 if ((icp = av1394_ioctl_isoch_handle2ic(avp, arg)) != NULL) { 630 ret = av1394_ic_start(icp); 631 } 632 633 AV1394_TNF_EXIT(av1394_ioctl_start); 634 return (ret); 635 } 636 637 /*ARGSUSED*/ 638 static int 639 av1394_ioctl_stop(av1394_inst_t *avp, void *arg, int mode) 640 { 641 av1394_ic_t *icp; 642 int ret = EINVAL; 643 644 AV1394_TNF_ENTER(av1394_ioctl_stop); 645 646 if ((icp = av1394_ioctl_isoch_handle2ic(avp, arg)) != NULL) { 647 ret = av1394_ic_stop(icp); 648 } 649 650 AV1394_TNF_EXIT(av1394_ioctl_stop); 651 return (ret); 652 } 653 654 static int 655 av1394_ioctl_recv(av1394_inst_t *avp, void *arg, int mode) 656 { 657 av1394_isoch_t *ip = &avp->av_i; 658 av1394_ic_t *icp; 659 iec61883_recv_t recv; 660 int num; 661 int ret = EINVAL; 662 663 /* copyin the structure and get channel pointer */ 664 if (ddi_copyin(arg, &recv, sizeof (recv), mode) != 0) { 665 return (EFAULT); 666 } 667 num = recv.rx_handle; 668 if (num >= (sizeof (ip->i_ic) / sizeof (av1394_ic_t))) { 669 TNF_PROBE_0(av1394_ioctl_recv_error_range, 670 AV1394_TNF_ISOCH_ERROR, ""); 671 return (EINVAL); 672 } 673 icp = ip->i_ic[num]; 674 if (icp == NULL) { 675 TNF_PROBE_0(av1394_ioctl_recv_error_null, 676 AV1394_TNF_ISOCH_ERROR, ""); 677 } 678 679 /* now call the actual handler */ 680 if (icp->ic_dir != AV1394_IR) { 681 ret = EINVAL; 682 } else { 683 ret = av1394_ir_recv(icp, &recv); 684 } 685 686 /* copyout the result */ 687 if (ret == 0) { 688 if (ddi_copyout(&recv, arg, sizeof (recv), mode) != 0) { 689 return (EFAULT); 690 } 691 } 692 693 return (ret); 694 } 695 696 static int 697 av1394_ioctl_xmit(av1394_inst_t *avp, void *arg, int mode) 698 { 699 av1394_isoch_t *ip = &avp->av_i; 700 av1394_ic_t *icp; 701 iec61883_xmit_t xmit; 702 int num; 703 int ret = EINVAL; 704 705 /* copyin the structure and get channel pointer */ 706 if (ddi_copyin(arg, &xmit, sizeof (xmit), mode) != 0) { 707 return (EFAULT); 708 } 709 num = xmit.tx_handle; 710 if (num >= (sizeof (ip->i_ic) / sizeof (av1394_ic_t))) { 711 TNF_PROBE_0(av1394_ioctl_xmit_error_range, 712 AV1394_TNF_ISOCH_ERROR, ""); 713 return (EINVAL); 714 } 715 icp = ip->i_ic[num]; 716 if (icp == NULL) { 717 TNF_PROBE_0(av1394_ioctl_xmit_error_null, 718 AV1394_TNF_ISOCH_ERROR, ""); 719 } 720 721 /* now call the actual handler */ 722 if (icp->ic_dir != AV1394_IT) { 723 ret = EINVAL; 724 } else { 725 ret = av1394_it_xmit(icp, &xmit); 726 } 727 728 /* copyout the result */ 729 if (ret == 0) { 730 if (ddi_copyout(&xmit, arg, sizeof (xmit), mode) != 0) { 731 return (EFAULT); 732 } 733 } 734 735 return (ret); 736 } 737 738 static uint_t 739 av1394_isoch_softintr(caddr_t arg) 740 { 741 av1394_inst_t *avp = (av1394_inst_t *)arg; 742 av1394_isoch_t *ip = &avp->av_i; 743 int i; 744 uint64_t ch; 745 av1394_ic_t *icp; 746 747 mutex_enter(&ip->i_mutex); 748 do { 749 for (i = 63, ch = (1ULL << 63); 750 (i > 0) && (ip->i_softintr_ch != 0); 751 i--, ch >>= 1) { 752 if ((ip->i_softintr_ch & ch) == 0) { 753 continue; 754 } 755 ip->i_softintr_ch &= ~ch; 756 icp = ip->i_ic[i]; 757 if (icp == NULL) { 758 continue; 759 } 760 761 mutex_exit(&ip->i_mutex); 762 mutex_enter(&icp->ic_mutex); 763 if (icp->ic_preq & AV1394_PREQ_IR_OVERFLOW) { 764 icp->ic_preq &= ~AV1394_PREQ_IR_OVERFLOW; 765 av1394_ir_overflow(icp); 766 } 767 if (icp->ic_preq & AV1394_PREQ_IT_UNDERRUN) { 768 icp->ic_preq &= ~AV1394_PREQ_IT_UNDERRUN; 769 av1394_it_underrun(icp); 770 } 771 mutex_exit(&icp->ic_mutex); 772 mutex_enter(&ip->i_mutex); 773 } 774 } while (ip->i_softintr_ch != 0); 775 mutex_exit(&ip->i_mutex); 776 777 return (DDI_INTR_CLAIMED); 778 } 779