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 driver 31 */ 32 33 #include <sys/param.h> 34 #include <sys/errno.h> 35 #include <sys/cred.h> 36 #include <sys/conf.h> 37 #include <sys/modctl.h> 38 #include <sys/stat.h> 39 #include <sys/ddi.h> 40 #include <sys/sunddi.h> 41 42 #include <sys/1394/targets/av1394/av1394_impl.h> 43 44 /* DDI/DKI entry points */ 45 static int av1394_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 46 static int av1394_attach(dev_info_t *, ddi_attach_cmd_t); 47 static int av1394_detach(dev_info_t *, ddi_detach_cmd_t); 48 static int av1394_open(dev_t *, int, int, cred_t *); 49 static int av1394_close(dev_t, int, int, cred_t *); 50 static int av1394_read(dev_t, struct uio *, cred_t *); 51 static int av1394_write(dev_t, struct uio *, cred_t *); 52 static int av1394_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 53 static int av1394_devmap(dev_t, devmap_cookie_t, offset_t, size_t, 54 size_t *, uint_t); 55 static int av1394_poll(dev_t, short, int, short *, struct pollhead **); 56 57 /* configuration routines */ 58 static void av1394_cleanup(av1394_inst_t *, int); 59 static int av1394_t1394_attach(av1394_inst_t *, dev_info_t *); 60 static void av1394_t1394_detach(av1394_inst_t *); 61 static int av1394_add_events(av1394_inst_t *); 62 static void av1394_remove_events(av1394_inst_t *); 63 64 /* CPR */ 65 static int av1394_cpr_suspend(av1394_inst_t *); 66 static int av1394_cpr_resume(av1394_inst_t *); 67 68 /* callbacks */ 69 static void av1394_bus_reset(dev_info_t *, ddi_eventcookie_t, void *, 70 void *); 71 static void av1394_disconnect(dev_info_t *, ddi_eventcookie_t, void *, 72 void *); 73 static void av1394_reconnect(dev_info_t *, ddi_eventcookie_t, void *, 74 void *); 75 76 extern struct mod_ops mod_driverops; 77 78 struct cb_ops av1394_cb_ops = { 79 av1394_open, /* open */ 80 av1394_close, /* close */ 81 nulldev, /* strategy */ 82 nulldev, /* print */ 83 nulldev, /* dump */ 84 av1394_read, /* read */ 85 av1394_write, /* write */ 86 av1394_ioctl, /* ioctl */ 87 av1394_devmap, /* devmap */ 88 nulldev, /* mmap */ 89 nulldev, /* segmap */ 90 av1394_poll, /* poll */ 91 ddi_prop_op, /* cb_prop_op */ 92 NULL, /* streamtab */ 93 D_MP | D_NEW | D_HOTPLUG | D_DEVMAP 94 }; 95 96 static struct dev_ops av1394_ops = { 97 DEVO_REV, /* devo_rev */ 98 0, /* refcnt */ 99 av1394_getinfo, /* getinfo */ 100 nulldev, /* identify */ 101 nulldev, /* probe */ 102 av1394_attach, /* attach */ 103 av1394_detach, /* detach */ 104 nodev, /* reset */ 105 &av1394_cb_ops, /* driver operations */ 106 NULL, /* bus operations */ 107 NULL /* power */ 108 }; 109 110 static struct modldrv av1394_modldrv = { 111 &mod_driverops, 112 "IEEE 1394 AV driver %I%", 113 &av1394_ops 114 }; 115 116 static struct modlinkage av1394_modlinkage = { 117 MODREV_1, 118 &av1394_modldrv, 119 NULL, 120 }; 121 122 static void *av1394_statep; 123 124 #ifndef NPROBE 125 extern int tnf_mod_load(void); 126 extern int tnf_mod_unload(struct modlinkage *mlp); 127 #endif 128 129 #define AV1394_INST2STATE(inst) (ddi_get_soft_state(av1394_statep, inst)) 130 #define AV1394_DEV2STATE(dev) \ 131 (ddi_get_soft_state(av1394_statep, AV1394_DEV2INST(dev))) 132 133 #define AV1394_TNF_ENTER(func) \ 134 TNF_PROBE_0_DEBUG(func##_enter, AV1394_TNF_INST_STACK, ""); 135 136 #define AV1394_TNF_EXIT(func) \ 137 TNF_PROBE_0_DEBUG(func##_exit, AV1394_TNF_INST_STACK, ""); 138 139 /* 140 * 141 * --- DDI/DKI entry points 142 * 143 */ 144 int 145 _init(void) 146 { 147 int error; 148 149 #ifndef NPROBE 150 (void) tnf_mod_load(); 151 #endif 152 error = ddi_soft_state_init(&av1394_statep, sizeof (av1394_inst_t), 1); 153 if (error != 0) { 154 #ifndef NPROBE 155 (void) tnf_mod_unload(&av1394_modlinkage); 156 #endif 157 return (error); 158 } 159 160 if ((error = mod_install(&av1394_modlinkage)) != 0) { 161 ddi_soft_state_fini(&av1394_statep); 162 #ifndef NPROBE 163 (void) tnf_mod_unload(&av1394_modlinkage); 164 #endif 165 } 166 167 return (error); 168 } 169 170 int 171 _fini(void) 172 { 173 int error; 174 175 if ((error = mod_remove(&av1394_modlinkage)) == 0) { 176 ddi_soft_state_fini(&av1394_statep); 177 #ifndef NPROBE 178 (void) tnf_mod_unload(&av1394_modlinkage); 179 #endif 180 } 181 182 return (error); 183 } 184 185 int 186 _info(struct modinfo *modinfop) 187 { 188 return (mod_info(&av1394_modlinkage, modinfop)); 189 } 190 191 /* 192 * attach 193 */ 194 static int 195 av1394_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 196 { 197 int instance = ddi_get_instance(dip); 198 av1394_inst_t *avp; 199 200 AV1394_TNF_ENTER(av1394_attach); 201 202 switch (cmd) { 203 case DDI_ATTACH: 204 break; 205 case DDI_RESUME: 206 if ((avp = AV1394_INST2STATE(instance)) == NULL) { 207 return (DDI_FAILURE); 208 } 209 return (av1394_cpr_resume(avp)); 210 default: 211 AV1394_TNF_EXIT(av1394_attach); 212 return (DDI_FAILURE); 213 } 214 215 if (ddi_soft_state_zalloc(av1394_statep, instance) != 0) { 216 TNF_PROBE_0(av1394_attach_error_soft_state_zalloc, 217 AV1394_TNF_INST_ERROR, ""); 218 AV1394_TNF_EXIT(av1394_attach); 219 return (DDI_FAILURE); 220 } 221 avp = AV1394_INST2STATE(instance); 222 223 if (av1394_t1394_attach(avp, dip) != DDI_SUCCESS) { 224 av1394_cleanup(avp, 1); 225 AV1394_TNF_EXIT(av1394_attach); 226 return (DDI_FAILURE); 227 } 228 229 mutex_init(&avp->av_mutex, NULL, MUTEX_DRIVER, 230 avp->av_attachinfo.iblock_cookie); 231 232 #ifndef __lock_lint 233 avp->av_dip = dip; 234 avp->av_instance = instance; 235 #endif 236 237 if (av1394_add_events(avp) != DDI_SUCCESS) { 238 av1394_cleanup(avp, 2); 239 AV1394_TNF_EXIT(av1394_attach); 240 return (DDI_FAILURE); 241 } 242 243 if (av1394_isoch_attach(avp) != DDI_SUCCESS) { 244 av1394_cleanup(avp, 3); 245 AV1394_TNF_EXIT(av1394_attach); 246 return (DDI_FAILURE); 247 } 248 249 if (av1394_async_attach(avp) != DDI_SUCCESS) { 250 av1394_cleanup(avp, 4); 251 AV1394_TNF_EXIT(av1394_attach); 252 return (DDI_FAILURE); 253 } 254 255 #ifndef __lock_lint 256 avp->av_dev_state = AV1394_DEV_ONLINE; 257 #endif 258 259 ddi_report_dev(dip); 260 261 AV1394_TNF_EXIT(av1394_attach); 262 return (DDI_SUCCESS); 263 } 264 265 static int 266 av1394_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 267 { 268 int instance = ddi_get_instance(dip); 269 av1394_inst_t *avp; 270 271 AV1394_TNF_ENTER(av1394_detach); 272 273 if ((avp = AV1394_INST2STATE(instance)) == NULL) { 274 TNF_PROBE_0(av1394_detach_error_instance, 275 AV1394_TNF_INST_ERROR, ""); 276 AV1394_TNF_EXIT(av1394_detach); 277 return (DDI_FAILURE); 278 } 279 280 switch (cmd) { 281 case DDI_DETACH: 282 av1394_cleanup(avp, AV1394_CLEANUP_LEVEL_MAX); 283 AV1394_TNF_EXIT(av1394_detach); 284 return (DDI_SUCCESS); 285 case DDI_SUSPEND: 286 return (av1394_cpr_suspend(avp)); 287 default: 288 AV1394_TNF_EXIT(av1394_detach); 289 return (DDI_FAILURE); 290 } 291 } 292 293 /*ARGSUSED*/ 294 static int 295 av1394_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 296 void **result) 297 { 298 dev_t dev = (dev_t)arg; 299 av1394_inst_t *avp; 300 int rval = DDI_FAILURE; 301 302 switch (infocmd) { 303 case DDI_INFO_DEVT2DEVINFO: 304 if ((avp = AV1394_DEV2STATE(dev)) != NULL) { 305 *result = avp->av_dip; 306 rval = DDI_SUCCESS; 307 } else { 308 *result = NULL; 309 } 310 break; 311 case DDI_INFO_DEVT2INSTANCE: 312 *result = (void *)(uintptr_t)AV1394_DEV2INST(dev); 313 rval = DDI_SUCCESS; 314 break; 315 } 316 317 return (rval); 318 } 319 320 /*ARGSUSED*/ 321 static int 322 av1394_open(dev_t *dev, int flag, int otyp, cred_t *cr) 323 { 324 av1394_inst_t *avp = AV1394_DEV2STATE(*dev); 325 int ret = ENXIO; 326 327 AV1394_TNF_ENTER(av1394_open); 328 if (avp != NULL) { 329 if (AV1394_DEV_IS_ISOCH(*dev)) { 330 ret = 0; 331 } else if (AV1394_DEV_IS_ASYNC(*dev)) { 332 ret = av1394_async_open(avp, flag); 333 } 334 } 335 AV1394_TNF_EXIT(av1394_open); 336 return (ret); 337 } 338 339 /*ARGSUSED*/ 340 static int 341 av1394_close(dev_t dev, int flag, int otyp, cred_t *cr) 342 { 343 av1394_inst_t *avp = AV1394_DEV2STATE(dev); 344 int ret = ENXIO; 345 346 AV1394_TNF_ENTER(av1394_close); 347 if (avp != NULL) { 348 if (AV1394_DEV_IS_ISOCH(dev)) { 349 ret = av1394_isoch_close(avp, flag); 350 } else if (AV1394_DEV_IS_ASYNC(dev)) { 351 ret = av1394_async_close(avp, flag); 352 } 353 } 354 AV1394_TNF_EXIT(av1394_close); 355 return (ret); 356 } 357 358 /*ARGSUSED*/ 359 static int 360 av1394_read(dev_t dev, struct uio *uiop, cred_t *cr) 361 { 362 av1394_inst_t *avp = AV1394_DEV2STATE(dev); 363 int ret = ENXIO; 364 365 AV1394_TNF_ENTER(av1394_read); 366 if (avp != NULL) { 367 if (AV1394_DEV_IS_ISOCH(dev)) { 368 ret = av1394_isoch_read(avp, uiop); 369 } else if (AV1394_DEV_IS_ASYNC(dev)) { 370 ret = av1394_async_read(avp, uiop); 371 } 372 } 373 AV1394_TNF_EXIT(av1394_read); 374 return (ret); 375 } 376 377 /*ARGSUSED*/ 378 static int 379 av1394_write(dev_t dev, struct uio *uiop, cred_t *cr) 380 { 381 av1394_inst_t *avp = AV1394_DEV2STATE(dev); 382 int ret = ENXIO; 383 384 AV1394_TNF_ENTER(av1394_write); 385 if (avp != NULL) { 386 if (AV1394_DEV_IS_ISOCH(dev)) { 387 ret = av1394_isoch_write(avp, uiop); 388 } else if (AV1394_DEV_IS_ASYNC(dev)) { 389 ret = av1394_async_write(avp, uiop); 390 } 391 } 392 AV1394_TNF_EXIT(av1394_write); 393 return (ret); 394 } 395 396 /*ARGSUSED*/ 397 static int 398 av1394_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rvalp) 399 { 400 av1394_inst_t *avp = AV1394_DEV2STATE(dev); 401 int ret = ENXIO; 402 403 AV1394_TNF_ENTER(av1394_ioctl); 404 if (avp != NULL) { 405 if (AV1394_DEV_IS_ISOCH(dev)) { 406 ret = av1394_isoch_ioctl(avp, cmd, arg, mode, rvalp); 407 } else if (AV1394_DEV_IS_ASYNC(dev)) { 408 ret = av1394_async_ioctl(avp, cmd, arg, mode, rvalp); 409 } 410 } 411 AV1394_TNF_EXIT(av1394_ioctl); 412 return (ret); 413 } 414 415 /*ARGSUSED*/ 416 static int 417 av1394_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len, 418 size_t *maplen, uint_t model) 419 { 420 av1394_inst_t *avp = AV1394_DEV2STATE(dev); 421 int ret = ENXIO; 422 423 AV1394_TNF_ENTER(av1394_devmap); 424 if ((avp != NULL) && (AV1394_DEV_IS_ISOCH(dev))) { 425 ret = av1394_isoch_devmap(avp, dhp, off, len, maplen, model); 426 } 427 AV1394_TNF_EXIT(av1394_devmap); 428 return (ret); 429 } 430 431 static int 432 av1394_poll(dev_t dev, short events, int anyyet, short *reventsp, 433 struct pollhead **phpp) 434 { 435 av1394_inst_t *avp = AV1394_DEV2STATE(dev); 436 int ret = ENXIO; 437 438 AV1394_TNF_ENTER(av1394_poll); 439 if ((avp != NULL) && AV1394_DEV_IS_ASYNC(dev)) { 440 ret = av1394_async_poll(avp, events, anyyet, reventsp, phpp); 441 } 442 AV1394_TNF_EXIT(av1394_poll); 443 return (ret); 444 } 445 446 447 /* 448 * 449 * --- configuration routines 450 * 451 * av1394_cleanup() 452 * Cleanup after attach 453 */ 454 static void 455 av1394_cleanup(av1394_inst_t *avp, int level) 456 { 457 ASSERT((level > 0) && (level <= AV1394_CLEANUP_LEVEL_MAX)); 458 459 AV1394_TNF_ENTER(av1394_cleanup); 460 switch (level) { 461 default: 462 av1394_async_detach(avp); 463 /* FALLTHRU */ 464 case 4: 465 av1394_isoch_detach(avp); 466 /* FALLTHRU */ 467 case 3: 468 av1394_remove_events(avp); 469 /* FALLTHRU */ 470 case 2: 471 av1394_t1394_detach(avp); 472 mutex_destroy(&avp->av_mutex); 473 /* FALLTHRU */ 474 case 1: 475 ddi_soft_state_free(av1394_statep, avp->av_instance); 476 } 477 AV1394_TNF_EXIT(av1394_cleanup); 478 } 479 480 static int 481 av1394_t1394_attach(av1394_inst_t *avp, dev_info_t *dip) 482 { 483 int ret; 484 485 AV1394_TNF_ENTER(av1394_t1394_attach); 486 487 ret = t1394_attach(dip, T1394_VERSION_V1, 0, &avp->av_attachinfo, 488 &avp->av_t1394_hdl); 489 490 if (ret != DDI_SUCCESS) { 491 TNF_PROBE_1(av1394_t1394_attach_error, AV1394_TNF_INST_ERROR, 492 "", tnf_int, ret, ret); 493 } 494 495 AV1394_TNF_EXIT(av1394_t1394_attach); 496 return (ret); 497 } 498 499 static void 500 av1394_t1394_detach(av1394_inst_t *avp) 501 { 502 AV1394_TNF_ENTER(av1394_t1394_detach); 503 504 (void) t1394_detach(&avp->av_t1394_hdl, 0); 505 506 AV1394_TNF_EXIT(av1394_t1394_detach); 507 } 508 509 static int 510 av1394_add_events(av1394_inst_t *avp) 511 { 512 ddi_eventcookie_t br_evc, rem_evc, ins_evc; 513 514 if (ddi_get_eventcookie(avp->av_dip, DDI_DEVI_BUS_RESET_EVENT, 515 &br_evc) != DDI_SUCCESS) { 516 TNF_PROBE_0(av1394_add_events_error_bus_reset_cookie, 517 AV1394_TNF_INST_ERROR, ""); 518 return (DDI_FAILURE); 519 } 520 if (ddi_add_event_handler(avp->av_dip, br_evc, av1394_bus_reset, 521 avp, &avp->av_reset_cb) != DDI_SUCCESS) { 522 TNF_PROBE_0(av1394_add_events_error_bus_reset_event, 523 AV1394_TNF_INST_ERROR, ""); 524 return (DDI_FAILURE); 525 } 526 527 if (ddi_get_eventcookie(avp->av_dip, DDI_DEVI_REMOVE_EVENT, 528 &rem_evc) != DDI_SUCCESS) { 529 (void) ddi_remove_event_handler(avp->av_reset_cb); 530 TNF_PROBE_0(av1394_add_events_error_remove_cookie, 531 AV1394_TNF_INST_ERROR, ""); 532 return (DDI_FAILURE); 533 } 534 if (ddi_add_event_handler(avp->av_dip, rem_evc, av1394_disconnect, 535 avp, &avp->av_remove_cb) != DDI_SUCCESS) { 536 (void) ddi_remove_event_handler(avp->av_reset_cb); 537 TNF_PROBE_0(av1394_add_events_error_remove_event, 538 AV1394_TNF_INST_ERROR, ""); 539 return (DDI_FAILURE); 540 } 541 542 if (ddi_get_eventcookie(avp->av_dip, DDI_DEVI_INSERT_EVENT, 543 &ins_evc) != DDI_SUCCESS) { 544 (void) ddi_remove_event_handler(avp->av_remove_cb); 545 (void) ddi_remove_event_handler(avp->av_reset_cb); 546 TNF_PROBE_0(av1394_add_events_error_insert_cookie, 547 AV1394_TNF_INST_ERROR, ""); 548 return (DDI_FAILURE); 549 } 550 if (ddi_add_event_handler(avp->av_dip, ins_evc, av1394_reconnect, 551 avp, &avp->av_insert_cb) != DDI_SUCCESS) { 552 (void) ddi_remove_event_handler(avp->av_remove_cb); 553 (void) ddi_remove_event_handler(avp->av_reset_cb); 554 TNF_PROBE_0(av1394_add_events_error_insert_event, 555 AV1394_TNF_INST_ERROR, ""); 556 return (DDI_FAILURE); 557 } 558 559 return (DDI_SUCCESS); 560 } 561 562 static void 563 av1394_remove_events(av1394_inst_t *avp) 564 { 565 ddi_eventcookie_t evc; 566 567 if (ddi_get_eventcookie(avp->av_dip, DDI_DEVI_INSERT_EVENT, 568 &evc) == DDI_SUCCESS) { 569 (void) ddi_remove_event_handler(avp->av_insert_cb); 570 } 571 572 if (ddi_get_eventcookie(avp->av_dip, DDI_DEVI_REMOVE_EVENT, 573 &evc) == DDI_SUCCESS) { 574 (void) ddi_remove_event_handler(avp->av_remove_cb); 575 } 576 577 if (ddi_get_eventcookie(avp->av_dip, DDI_DEVI_BUS_RESET_EVENT, 578 &evc) == DDI_SUCCESS) { 579 (void) ddi_remove_event_handler(avp->av_reset_cb); 580 } 581 } 582 583 /* 584 * 585 * --- CPR 586 * 587 */ 588 static int 589 av1394_cpr_suspend(av1394_inst_t *avp) 590 { 591 int ret; 592 593 AV1394_TNF_ENTER(av1394_cpr_suspend); 594 595 ret = av1394_isoch_cpr_suspend(avp); 596 597 if (ret == DDI_SUCCESS) { 598 mutex_enter(&avp->av_mutex); 599 avp->av_prev_dev_state = avp->av_dev_state; 600 avp->av_dev_state = AV1394_DEV_SUSPENDED; 601 mutex_exit(&avp->av_mutex); 602 } 603 604 AV1394_TNF_EXIT(av1394_cpr_suspend); 605 return (ret); 606 } 607 608 /* 609 * CPR resume should always succeed 610 */ 611 static int 612 av1394_cpr_resume(av1394_inst_t *avp) 613 { 614 AV1394_TNF_ENTER(av1394_cpr_resume); 615 616 mutex_enter(&avp->av_mutex); 617 avp->av_dev_state = avp->av_prev_dev_state; 618 mutex_exit(&avp->av_mutex); 619 620 (void) av1394_async_cpr_resume(avp); 621 622 AV1394_TNF_EXIT(av1394_cpr_resume); 623 return (DDI_SUCCESS); 624 } 625 626 /* 627 * 628 * --- callbacks 629 * 630 */ 631 /*ARGSUSED*/ 632 static void 633 av1394_bus_reset(dev_info_t *dip, ddi_eventcookie_t evc, void *arg, void *data) 634 { 635 av1394_inst_t *avp = arg; 636 637 AV1394_TNF_ENTER(av1394_bus_reset); 638 639 if (avp == NULL) { 640 AV1394_TNF_EXIT(av1394_bus_reset); 641 return; 642 } 643 644 mutex_enter(&avp->av_mutex); 645 avp->av_attachinfo.localinfo = *(t1394_localinfo_t *)data; 646 mutex_exit(&avp->av_mutex); 647 648 av1394_async_bus_reset(avp); 649 av1394_cmp_bus_reset(avp); 650 651 AV1394_TNF_EXIT(av1394_bus_reset); 652 } 653 654 /*ARGSUSED*/ 655 static void 656 av1394_disconnect(dev_info_t *dip, ddi_eventcookie_t evc, void *arg, void *data) 657 { 658 av1394_inst_t *avp = arg; 659 660 AV1394_TNF_ENTER(av1394_disconnect); 661 662 if (avp == NULL) { 663 AV1394_TNF_EXIT(av1394_disconnect); 664 return; 665 } 666 667 mutex_enter(&avp->av_mutex); 668 avp->av_dev_state = AV1394_DEV_DISCONNECTED; 669 mutex_exit(&avp->av_mutex); 670 671 AV1394_TNF_EXIT(av1394_disconnect); 672 } 673 674 /*ARGSUSED*/ 675 static void 676 av1394_reconnect(dev_info_t *dip, ddi_eventcookie_t evc, void *arg, void *data) 677 { 678 av1394_inst_t *avp = arg; 679 680 AV1394_TNF_ENTER(av1394_disconnect); 681 682 if (avp == NULL) { 683 AV1394_TNF_EXIT(av1394_disconnect); 684 return; 685 } 686 687 mutex_enter(&avp->av_mutex); 688 avp->av_dev_state = AV1394_DEV_ONLINE; 689 avp->av_attachinfo.localinfo = *(t1394_localinfo_t *)data; 690 mutex_exit(&avp->av_mutex); 691 692 av1394_async_reconnect(avp); 693 694 AV1394_TNF_EXIT(av1394_disconnect); 695 } 696