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 FCP module 31 */ 32 #include <sys/stat.h> 33 #include <sys/ddi.h> 34 #include <sys/sunddi.h> 35 #include <sys/1394/targets/av1394/av1394_impl.h> 36 37 /* configuration routines */ 38 static int av1394_fcp_ctl_register(av1394_inst_t *); 39 static int av1394_fcp_tgt_register(av1394_inst_t *); 40 static int av1394_fcp_ctl_alloc_cmd(av1394_inst_t *); 41 static void av1394_fcp_ctl_free_cmd(av1394_inst_t *); 42 static int av1394_fcp_tgt_alloc_cmd(av1394_inst_t *); 43 static void av1394_fcp_tgt_free_cmd(av1394_inst_t *); 44 static void av1394_fcp_cleanup(av1394_inst_t *, int); 45 46 /* FCP write and completion handling */ 47 static int av1394_fcp_cmd_write_sync(av1394_inst_t *, av1394_fcp_cmd_t *); 48 static void av1394_fcp_cmd_completion_cb(struct cmd1394_cmd *); 49 static int av1394_fcp_cmd_write_request_cb(cmd1394_cmd_t *); 50 static int av1394_fcp_resp_write_request_cb(cmd1394_cmd_t *); 51 static void av1394_fcp_common_write_request_cb(cmd1394_cmd_t *, int); 52 53 /* misc routines */ 54 static int av1394_fcp_copyin_block(iec61883_arq_t *, mblk_t *, 55 struct uio *); 56 57 #define AV1394_TNF_ENTER(func) \ 58 TNF_PROBE_0_DEBUG(func##_enter, AV1394_TNF_FCP_STACK, ""); 59 60 #define AV1394_TNF_EXIT(func) \ 61 TNF_PROBE_0_DEBUG(func##_exit, AV1394_TNF_FCP_STACK, ""); 62 63 /* 64 * 65 * --- configuration entry points 66 * 67 */ 68 int 69 av1394_fcp_attach(av1394_inst_t *avp) 70 { 71 av1394_fcp_t *fcp = &avp->av_a.a_fcp; 72 int ret; 73 74 AV1394_TNF_ENTER(av1394_fcp_attach); 75 76 /* register FCP controller */ 77 if ((ret = av1394_fcp_ctl_register(avp)) != DDI_SUCCESS) { 78 AV1394_TNF_EXIT(av1394_fcp_attach); 79 return (ret); 80 } 81 82 /* allocate FCP controller command */ 83 if ((ret = av1394_fcp_ctl_alloc_cmd(avp)) != DDI_SUCCESS) { 84 av1394_fcp_cleanup(avp, 1); 85 AV1394_TNF_EXIT(av1394_fcp_attach); 86 return (ret); 87 } 88 89 /* register FCP target */ 90 if ((ret = av1394_fcp_tgt_register(avp)) != DDI_SUCCESS) { 91 av1394_fcp_cleanup(avp, 2); 92 AV1394_TNF_EXIT(av1394_fcp_attach); 93 return (ret); 94 } 95 96 /* allocate FCP target command */ 97 if ((ret = av1394_fcp_tgt_alloc_cmd(avp)) != DDI_SUCCESS) { 98 av1394_fcp_cleanup(avp, 3); 99 AV1394_TNF_EXIT(av1394_fcp_attach); 100 return (ret); 101 } 102 103 cv_init(&fcp->fcp_cmd.fc_xmit_cv, NULL, CV_DRIVER, NULL); 104 cv_init(&fcp->fcp_cmd.fc_busy_cv, NULL, CV_DRIVER, NULL); 105 cv_init(&fcp->fcp_resp.fc_xmit_cv, NULL, CV_DRIVER, NULL); 106 cv_init(&fcp->fcp_resp.fc_busy_cv, NULL, CV_DRIVER, NULL); 107 108 AV1394_TNF_EXIT(av1394_fcp_attach); 109 return (ret); 110 } 111 112 void 113 av1394_fcp_detach(av1394_inst_t *avp) 114 { 115 AV1394_TNF_ENTER(av1394_fcp_detach); 116 117 av1394_fcp_cleanup(avp, AV1394_CLEANUP_LEVEL_MAX); 118 119 AV1394_TNF_EXIT(av1394_fcp_detach); 120 } 121 122 int 123 av1394_fcp_write(av1394_inst_t *avp, iec61883_arq_t *arq, struct uio *uiop) 124 { 125 av1394_async_t *ap = &avp->av_a; 126 av1394_fcp_t *fcp = &ap->a_fcp; 127 int len = arq->arq_len; 128 av1394_fcp_cmd_t *fc; 129 cmd1394_cmd_t *cmd; 130 mblk_t *mp = NULL; 131 int ret; 132 133 AV1394_TNF_ENTER(av1394_fcp_write); 134 135 ASSERT((arq->arq_type == IEC61883_ARQ_FCP_CMD) || 136 (arq->arq_type == IEC61883_ARQ_FCP_RESP)); 137 138 /* check arguments */ 139 if ((len == 0) || (len > AV1394_FCP_ARQ_LEN_MAX) || 140 (len % IEEE1394_QUADLET != 0)) { 141 TNF_PROBE_1(av1394_fcp_write_error, 142 AV1394_TNF_FCP_ERROR, "", tnf_int, len, len); 143 AV1394_TNF_EXIT(av1394_fcp_write); 144 return (EINVAL); 145 } 146 147 /* block write requires an mblk */ 148 if (len > IEEE1394_QUADLET) { 149 if ((mp = allocb(len, BPRI_HI)) == NULL) { 150 AV1394_TNF_EXIT(av1394_fcp_write); 151 return (ENOMEM); 152 } 153 if ((ret = av1394_fcp_copyin_block(arq, mp, uiop)) != 0) { 154 freemsg(mp); 155 AV1394_TNF_EXIT(av1394_fcp_write); 156 return (ret); 157 } 158 } 159 160 /* either FCP command or response */ 161 fc = (arq->arq_type == IEC61883_ARQ_FCP_CMD) ? 162 &fcp->fcp_cmd : &fcp->fcp_resp; 163 164 /* one command at a time */ 165 mutex_enter(&ap->a_mutex); 166 while (fc->fc_busy) { 167 if (cv_wait_sig(&fc->fc_busy_cv, &ap->a_mutex) == 0) { 168 mutex_exit(&ap->a_mutex); 169 freemsg(mp); 170 AV1394_TNF_EXIT(av1394_fcp_write); 171 return (EINTR); 172 } 173 } 174 fc->fc_busy = B_TRUE; 175 176 /* prepare command */ 177 cmd = fc->fc_cmd; 178 if (len == IEEE1394_QUADLET) { 179 cmd->cmd_type = CMD1394_ASYNCH_WR_QUAD; 180 cmd->cmd_u.q.quadlet_data = arq->arq_data.quadlet; 181 } else { 182 cmd->cmd_type = CMD1394_ASYNCH_WR_BLOCK; 183 cmd->cmd_u.b.data_block = mp; 184 cmd->cmd_u.b.blk_length = len; 185 } 186 187 /* do the write and wait for completion */ 188 ret = av1394_fcp_cmd_write_sync(avp, fc); 189 190 /* not busy anymore */ 191 fc->fc_busy = B_FALSE; 192 cv_broadcast(&fc->fc_busy_cv); 193 mutex_exit(&ap->a_mutex); 194 195 freemsg(mp); 196 197 AV1394_TNF_EXIT(av1394_fcp_write); 198 return (ret); 199 } 200 201 /* 202 * 203 * --- configuration routines 204 * 205 */ 206 static int 207 av1394_fcp_ctl_register(av1394_inst_t *avp) 208 { 209 t1394_fcp_evts_t evts; 210 int ret; 211 212 evts.fcp_write_request = av1394_fcp_resp_write_request_cb; 213 evts.fcp_arg = avp; 214 215 ret = t1394_fcp_register_controller(avp->av_t1394_hdl, &evts, 0); 216 if (ret != DDI_SUCCESS) { 217 TNF_PROBE_1(av1394_fcp_ctl_register_error, 218 AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret); 219 } 220 return (ret); 221 } 222 223 static int 224 av1394_fcp_tgt_register(av1394_inst_t *avp) 225 { 226 t1394_fcp_evts_t evts; 227 int ret; 228 229 evts.fcp_write_request = av1394_fcp_cmd_write_request_cb; 230 evts.fcp_arg = avp; 231 232 ret = t1394_fcp_register_target(avp->av_t1394_hdl, &evts, 0); 233 if (ret != DDI_SUCCESS) { 234 TNF_PROBE_1(av1394_fcp_tgt_register_error, 235 AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret); 236 } 237 return (ret); 238 } 239 240 static int 241 av1394_fcp_ctl_alloc_cmd(av1394_inst_t *avp) 242 { 243 av1394_fcp_cmd_t *fc = &avp->av_a.a_fcp.fcp_cmd; 244 int ret; 245 246 ret = t1394_alloc_cmd(avp->av_t1394_hdl, T1394_ALLOC_CMD_FCP_COMMAND, 247 &fc->fc_cmd); 248 if (ret != DDI_SUCCESS) { 249 TNF_PROBE_1(av1394_fcp_ctl_alloc_cmd_error, 250 AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret); 251 } 252 return (ret); 253 } 254 255 static void 256 av1394_fcp_ctl_free_cmd(av1394_inst_t *avp) 257 { 258 av1394_fcp_cmd_t *fc = &avp->av_a.a_fcp.fcp_cmd; 259 int ret; 260 261 ret = t1394_free_cmd(avp->av_t1394_hdl, 0, &fc->fc_cmd); 262 if (ret != DDI_SUCCESS) { 263 TNF_PROBE_1(av1394_fcp_ctl_free_cmd_error, 264 AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret); 265 } 266 } 267 268 static int 269 av1394_fcp_tgt_alloc_cmd(av1394_inst_t *avp) 270 { 271 av1394_fcp_cmd_t *fc = &avp->av_a.a_fcp.fcp_resp; 272 int ret; 273 274 ret = t1394_alloc_cmd(avp->av_t1394_hdl, T1394_ALLOC_CMD_FCP_RESPONSE, 275 &fc->fc_cmd); 276 if (ret != DDI_SUCCESS) { 277 TNF_PROBE_1(av1394_fcp_tgt_alloc_cmd_error, 278 AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret); 279 } 280 return (ret); 281 } 282 283 static void 284 av1394_fcp_tgt_free_cmd(av1394_inst_t *avp) 285 { 286 av1394_fcp_cmd_t *fc = &avp->av_a.a_fcp.fcp_resp; 287 int ret; 288 289 ret = t1394_free_cmd(avp->av_t1394_hdl, 0, &fc->fc_cmd); 290 if (ret != DDI_SUCCESS) { 291 TNF_PROBE_1(av1394_fcp_tgt_free_cmd_error, 292 AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret); 293 } 294 } 295 296 static void 297 av1394_fcp_cleanup(av1394_inst_t *avp, int level) 298 { 299 av1394_fcp_t *fcp = &avp->av_a.a_fcp; 300 301 ASSERT((level > 0) && (level <= AV1394_CLEANUP_LEVEL_MAX)); 302 303 switch (level) { 304 default: 305 cv_destroy(&fcp->fcp_cmd.fc_xmit_cv); 306 cv_destroy(&fcp->fcp_cmd.fc_busy_cv); 307 cv_destroy(&fcp->fcp_resp.fc_xmit_cv); 308 cv_destroy(&fcp->fcp_resp.fc_busy_cv); 309 310 av1394_fcp_tgt_free_cmd(avp); 311 /* FALLTHRU */ 312 case 3: 313 (void) t1394_fcp_unregister_target(avp->av_t1394_hdl); 314 /* FALLTHRU */ 315 case 2: 316 av1394_fcp_ctl_free_cmd(avp); 317 /* FALLTHRU */ 318 case 1: 319 (void) t1394_fcp_unregister_controller(avp->av_t1394_hdl); 320 } 321 } 322 323 /* 324 * 325 * --- FCP write and completion handling 326 * 327 */ 328 static int 329 av1394_fcp_cmd_write_sync(av1394_inst_t *avp, av1394_fcp_cmd_t *fc) 330 { 331 av1394_async_t *ap = &avp->av_a; 332 cmd1394_cmd_t *cmd = fc->fc_cmd; 333 int ret = 0; 334 335 cmd->completion_callback = av1394_fcp_cmd_completion_cb; 336 cmd->cmd_callback_arg = avp; 337 338 /* go */ 339 ASSERT(!fc->fc_xmit); 340 fc->fc_xmit = B_TRUE; 341 342 mutex_exit(&ap->a_mutex); 343 ret = t1394_write(avp->av_t1394_hdl, cmd); 344 mutex_enter(&ap->a_mutex); 345 346 /* immediate error? */ 347 if (ret != DDI_SUCCESS) { 348 fc->fc_xmit = B_FALSE; 349 TNF_PROBE_2(av1394_fcp_cmd_write_sync_error, 350 AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret, 351 tnf_int, cmd_result, cmd->cmd_result); 352 return (EIO); 353 } 354 355 /* wait for completion */ 356 while (fc->fc_xmit) { 357 if (cv_wait_sig(&fc->fc_xmit_cv, &ap->a_mutex) == 0) { 358 return (EINTR); 359 } 360 } 361 362 if (cmd->cmd_result != CMD1394_CMDSUCCESS) { 363 TNF_PROBE_1(av1394_fcp_cmd_write_sync_error, 364 AV1394_TNF_FCP_ERROR, "", 365 tnf_int, cmd_result, cmd->cmd_result); 366 if (cmd->cmd_result == CMD1394_EDEVICE_BUSY) { 367 return (EBUSY); 368 } else { 369 return (EIO); 370 } 371 } else { 372 return (0); 373 } 374 } 375 376 static void 377 av1394_fcp_cmd_completion_cb(struct cmd1394_cmd *cmd) 378 { 379 av1394_inst_t *avp = cmd->cmd_callback_arg; 380 av1394_async_t *ap = &avp->av_a; 381 av1394_fcp_t *fcp = &ap->a_fcp; 382 av1394_fcp_cmd_t *fc; 383 384 AV1394_TNF_ENTER(av1394_fcp_cmd_completion_cb); 385 386 mutex_enter(&ap->a_mutex); 387 /* is this FCP command or response */ 388 if (cmd == fcp->fcp_cmd.fc_cmd) { 389 fc = &fcp->fcp_cmd; 390 } else { 391 ASSERT(cmd == fcp->fcp_resp.fc_cmd); 392 fc = &fcp->fcp_resp; 393 } 394 395 /* wake the waiter */ 396 fc->fc_xmit = B_FALSE; 397 cv_signal(&fc->fc_xmit_cv); 398 mutex_exit(&ap->a_mutex); 399 400 AV1394_TNF_EXIT(av1394_fcp_cmd_completion_cb); 401 } 402 403 /* 404 * av1394_fcp_cmd_write_request_cb() 405 * Incoming response request from an FCP target 406 */ 407 static int 408 av1394_fcp_resp_write_request_cb(cmd1394_cmd_t *req) 409 { 410 av1394_inst_t *avp = req->cmd_callback_arg; 411 av1394_async_t *ap = &avp->av_a; 412 413 AV1394_TNF_ENTER(av1394_fcp_resp_write_request_cb); 414 415 mutex_enter(&ap->a_mutex); 416 if ((ap->a_nopen == 0) || 417 (req->bus_generation != ap->a_bus_generation) || 418 (req->nodeID != ap->a_targetinfo.target_nodeID)) { 419 mutex_exit(&ap->a_mutex); 420 421 AV1394_TNF_EXIT(av1394_fcp_resp_write_request_cb); 422 return (T1394_REQ_UNCLAIMED); 423 } 424 mutex_exit(&ap->a_mutex); 425 426 av1394_fcp_common_write_request_cb(req, AV1394_M_FCP_RESP); 427 428 AV1394_TNF_EXIT(av1394_fcp_resp_write_request_cb); 429 return (T1394_REQ_CLAIMED); 430 } 431 432 /* 433 * av1394_fcp_cmd_write_request_cb() 434 * Incoming command request from an FCP controller 435 */ 436 static int 437 av1394_fcp_cmd_write_request_cb(cmd1394_cmd_t *req) 438 { 439 av1394_inst_t *avp = req->cmd_callback_arg; 440 av1394_async_t *ap = &avp->av_a; 441 442 AV1394_TNF_ENTER(av1394_fcp_cmd_write_request_cb); 443 444 mutex_enter(&ap->a_mutex); 445 if (ap->a_nopen == 0) { 446 mutex_exit(&ap->a_mutex); 447 448 AV1394_TNF_EXIT(av1394_fcp_cmd_write_request_cb); 449 return (T1394_REQ_UNCLAIMED); 450 } 451 mutex_exit(&ap->a_mutex); 452 453 av1394_fcp_common_write_request_cb(req, AV1394_M_FCP_CMD); 454 455 AV1394_TNF_EXIT(av1394_fcp_cmd_write_request_cb); 456 return (T1394_REQ_CLAIMED); 457 } 458 459 static void 460 av1394_fcp_common_write_request_cb(cmd1394_cmd_t *req, int mtype) 461 { 462 av1394_inst_t *avp = req->cmd_callback_arg; 463 mblk_t *mp; 464 uint32_t quadlet_data; 465 int err; 466 467 AV1394_TNF_ENTER(av1394_fcp_common_write_request_cb); 468 469 ASSERT((req->cmd_type == CMD1394_ASYNCH_WR_QUAD) || 470 (req->cmd_type == CMD1394_ASYNCH_WR_BLOCK)); 471 472 /* get the data */ 473 if (req->cmd_type == CMD1394_ASYNCH_WR_QUAD) { 474 quadlet_data = req->cmd_u.q.quadlet_data; 475 } else { 476 mp = req->cmd_u.b.data_block; 477 req->cmd_u.b.data_block = NULL; 478 } 479 480 /* complete request */ 481 req->cmd_result = IEEE1394_RESP_COMPLETE; 482 483 err = t1394_recv_request_done(avp->av_t1394_hdl, req, 0); 484 if (err != DDI_SUCCESS) { 485 TNF_PROBE_2(av1394_fcp_common_write_request_cb_done_error, 486 AV1394_TNF_FCP_ERROR, "", tnf_int, err, err, 487 tnf_int, result, req->cmd_result); 488 } 489 490 /* allocate mblk and copy quadlet into it */ 491 if (req->cmd_type == CMD1394_ASYNCH_WR_QUAD) { 492 mp = allocb(IEEE1394_QUADLET, BPRI_HI); 493 if (mp == NULL) { 494 TNF_PROBE_0( 495 av1394_fcp_common_write_request_cb_allocb_error, 496 AV1394_TNF_FCP_ERROR, ""); 497 AV1394_TNF_EXIT(av1394_fcp_common_write_request_cb); 498 return; 499 } 500 *(uint32_t *)mp->b_rptr = quadlet_data; 501 mp->b_wptr += IEEE1394_QUADLET; 502 } 503 504 /* queue up the data */ 505 DB_TYPE(mp) = mtype; 506 av1394_async_putq_rq(avp, mp); 507 508 AV1394_TNF_EXIT(av1394_fcp_common_write_request_cb); 509 } 510 511 /* 512 * 513 * --- misc routines 514 * 515 */ 516 static int 517 av1394_fcp_copyin_block(iec61883_arq_t *arq, mblk_t *mp, struct uio *uiop) 518 { 519 int len = arq->arq_len; 520 int copylen; 521 int ret = 0; 522 523 ASSERT((len > 0) && (len % IEEE1394_QUADLET == 0)); 524 525 /* first copy ARQ-embedded data */ 526 copylen = min(len, sizeof (arq->arq_data)); 527 bcopy(&arq->arq_data.buf[0], mp->b_wptr, copylen); 528 mp->b_wptr += copylen; 529 530 /* now copyin the rest of the data, if any */ 531 copylen = len - copylen; 532 if (copylen > 0) { 533 ret = uiomove(mp->b_wptr, copylen, UIO_WRITE, uiop); 534 if (ret != 0) { 535 TNF_PROBE_1(av1394_fcp_copyin_block_error, 536 AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret); 537 return (ret); 538 } 539 mp->b_wptr += copylen; 540 } 541 return (ret); 542 } 543