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