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