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