/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * av1394 FCP module */ #include #include #include #include /* configuration routines */ static int av1394_fcp_ctl_register(av1394_inst_t *); static int av1394_fcp_tgt_register(av1394_inst_t *); static int av1394_fcp_ctl_alloc_cmd(av1394_inst_t *); static void av1394_fcp_ctl_free_cmd(av1394_inst_t *); static int av1394_fcp_tgt_alloc_cmd(av1394_inst_t *); static void av1394_fcp_tgt_free_cmd(av1394_inst_t *); static void av1394_fcp_cleanup(av1394_inst_t *, int); /* FCP write and completion handling */ static int av1394_fcp_cmd_write_sync(av1394_inst_t *, av1394_fcp_cmd_t *); static void av1394_fcp_cmd_completion_cb(struct cmd1394_cmd *); static int av1394_fcp_cmd_write_request_cb(cmd1394_cmd_t *); static int av1394_fcp_resp_write_request_cb(cmd1394_cmd_t *); static void av1394_fcp_common_write_request_cb(cmd1394_cmd_t *, int); /* misc routines */ static int av1394_fcp_copyin_block(iec61883_arq_t *, mblk_t *, struct uio *); #define AV1394_TNF_ENTER(func) \ TNF_PROBE_0_DEBUG(func##_enter, AV1394_TNF_FCP_STACK, ""); #define AV1394_TNF_EXIT(func) \ TNF_PROBE_0_DEBUG(func##_exit, AV1394_TNF_FCP_STACK, ""); /* * * --- configuration entry points * */ int av1394_fcp_attach(av1394_inst_t *avp) { av1394_fcp_t *fcp = &avp->av_a.a_fcp; int ret; AV1394_TNF_ENTER(av1394_fcp_attach); /* register FCP controller */ if ((ret = av1394_fcp_ctl_register(avp)) != DDI_SUCCESS) { AV1394_TNF_EXIT(av1394_fcp_attach); return (ret); } /* allocate FCP controller command */ if ((ret = av1394_fcp_ctl_alloc_cmd(avp)) != DDI_SUCCESS) { av1394_fcp_cleanup(avp, 1); AV1394_TNF_EXIT(av1394_fcp_attach); return (ret); } /* register FCP target */ if ((ret = av1394_fcp_tgt_register(avp)) != DDI_SUCCESS) { av1394_fcp_cleanup(avp, 2); AV1394_TNF_EXIT(av1394_fcp_attach); return (ret); } /* allocate FCP target command */ if ((ret = av1394_fcp_tgt_alloc_cmd(avp)) != DDI_SUCCESS) { av1394_fcp_cleanup(avp, 3); AV1394_TNF_EXIT(av1394_fcp_attach); return (ret); } cv_init(&fcp->fcp_cmd.fc_xmit_cv, NULL, CV_DRIVER, NULL); cv_init(&fcp->fcp_cmd.fc_busy_cv, NULL, CV_DRIVER, NULL); cv_init(&fcp->fcp_resp.fc_xmit_cv, NULL, CV_DRIVER, NULL); cv_init(&fcp->fcp_resp.fc_busy_cv, NULL, CV_DRIVER, NULL); AV1394_TNF_EXIT(av1394_fcp_attach); return (ret); } void av1394_fcp_detach(av1394_inst_t *avp) { AV1394_TNF_ENTER(av1394_fcp_detach); av1394_fcp_cleanup(avp, AV1394_CLEANUP_LEVEL_MAX); AV1394_TNF_EXIT(av1394_fcp_detach); } int av1394_fcp_write(av1394_inst_t *avp, iec61883_arq_t *arq, struct uio *uiop) { av1394_async_t *ap = &avp->av_a; av1394_fcp_t *fcp = &ap->a_fcp; int len = arq->arq_len; av1394_fcp_cmd_t *fc; cmd1394_cmd_t *cmd; mblk_t *mp = NULL; int ret; AV1394_TNF_ENTER(av1394_fcp_write); ASSERT((arq->arq_type == IEC61883_ARQ_FCP_CMD) || (arq->arq_type == IEC61883_ARQ_FCP_RESP)); /* check arguments */ if ((len == 0) || (len > AV1394_FCP_ARQ_LEN_MAX) || (len % IEEE1394_QUADLET != 0)) { TNF_PROBE_1(av1394_fcp_write_error, AV1394_TNF_FCP_ERROR, "", tnf_int, len, len); AV1394_TNF_EXIT(av1394_fcp_write); return (EINVAL); } /* block write requires an mblk */ if (len > IEEE1394_QUADLET) { if ((mp = allocb(len, BPRI_HI)) == NULL) { AV1394_TNF_EXIT(av1394_fcp_write); return (ENOMEM); } if ((ret = av1394_fcp_copyin_block(arq, mp, uiop)) != 0) { freemsg(mp); AV1394_TNF_EXIT(av1394_fcp_write); return (ret); } } /* either FCP command or response */ fc = (arq->arq_type == IEC61883_ARQ_FCP_CMD) ? &fcp->fcp_cmd : &fcp->fcp_resp; /* one command at a time */ mutex_enter(&ap->a_mutex); while (fc->fc_busy) { if (cv_wait_sig(&fc->fc_busy_cv, &ap->a_mutex) == 0) { mutex_exit(&ap->a_mutex); freemsg(mp); AV1394_TNF_EXIT(av1394_fcp_write); return (EINTR); } } fc->fc_busy = B_TRUE; /* prepare command */ cmd = fc->fc_cmd; if (len == IEEE1394_QUADLET) { cmd->cmd_type = CMD1394_ASYNCH_WR_QUAD; cmd->cmd_u.q.quadlet_data = arq->arq_data.quadlet; } else { cmd->cmd_type = CMD1394_ASYNCH_WR_BLOCK; cmd->cmd_u.b.data_block = mp; cmd->cmd_u.b.blk_length = len; } /* do the write and wait for completion */ ret = av1394_fcp_cmd_write_sync(avp, fc); /* not busy anymore */ fc->fc_busy = B_FALSE; cv_broadcast(&fc->fc_busy_cv); mutex_exit(&ap->a_mutex); freemsg(mp); AV1394_TNF_EXIT(av1394_fcp_write); return (ret); } /* * * --- configuration routines * */ static int av1394_fcp_ctl_register(av1394_inst_t *avp) { t1394_fcp_evts_t evts; int ret; evts.fcp_write_request = av1394_fcp_resp_write_request_cb; evts.fcp_arg = avp; ret = t1394_fcp_register_controller(avp->av_t1394_hdl, &evts, 0); if (ret != DDI_SUCCESS) { TNF_PROBE_1(av1394_fcp_ctl_register_error, AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret); } return (ret); } static int av1394_fcp_tgt_register(av1394_inst_t *avp) { t1394_fcp_evts_t evts; int ret; evts.fcp_write_request = av1394_fcp_cmd_write_request_cb; evts.fcp_arg = avp; ret = t1394_fcp_register_target(avp->av_t1394_hdl, &evts, 0); if (ret != DDI_SUCCESS) { TNF_PROBE_1(av1394_fcp_tgt_register_error, AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret); } return (ret); } static int av1394_fcp_ctl_alloc_cmd(av1394_inst_t *avp) { av1394_fcp_cmd_t *fc = &avp->av_a.a_fcp.fcp_cmd; int ret; ret = t1394_alloc_cmd(avp->av_t1394_hdl, T1394_ALLOC_CMD_FCP_COMMAND, &fc->fc_cmd); if (ret != DDI_SUCCESS) { TNF_PROBE_1(av1394_fcp_ctl_alloc_cmd_error, AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret); } return (ret); } static void av1394_fcp_ctl_free_cmd(av1394_inst_t *avp) { av1394_fcp_cmd_t *fc = &avp->av_a.a_fcp.fcp_cmd; int ret; ret = t1394_free_cmd(avp->av_t1394_hdl, 0, &fc->fc_cmd); if (ret != DDI_SUCCESS) { TNF_PROBE_1(av1394_fcp_ctl_free_cmd_error, AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret); } } static int av1394_fcp_tgt_alloc_cmd(av1394_inst_t *avp) { av1394_fcp_cmd_t *fc = &avp->av_a.a_fcp.fcp_resp; int ret; ret = t1394_alloc_cmd(avp->av_t1394_hdl, T1394_ALLOC_CMD_FCP_RESPONSE, &fc->fc_cmd); if (ret != DDI_SUCCESS) { TNF_PROBE_1(av1394_fcp_tgt_alloc_cmd_error, AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret); } return (ret); } static void av1394_fcp_tgt_free_cmd(av1394_inst_t *avp) { av1394_fcp_cmd_t *fc = &avp->av_a.a_fcp.fcp_resp; int ret; ret = t1394_free_cmd(avp->av_t1394_hdl, 0, &fc->fc_cmd); if (ret != DDI_SUCCESS) { TNF_PROBE_1(av1394_fcp_tgt_free_cmd_error, AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret); } } static void av1394_fcp_cleanup(av1394_inst_t *avp, int level) { av1394_fcp_t *fcp = &avp->av_a.a_fcp; ASSERT((level > 0) && (level <= AV1394_CLEANUP_LEVEL_MAX)); switch (level) { default: cv_destroy(&fcp->fcp_cmd.fc_xmit_cv); cv_destroy(&fcp->fcp_cmd.fc_busy_cv); cv_destroy(&fcp->fcp_resp.fc_xmit_cv); cv_destroy(&fcp->fcp_resp.fc_busy_cv); av1394_fcp_tgt_free_cmd(avp); /* FALLTHRU */ case 3: (void) t1394_fcp_unregister_target(avp->av_t1394_hdl); /* FALLTHRU */ case 2: av1394_fcp_ctl_free_cmd(avp); /* FALLTHRU */ case 1: (void) t1394_fcp_unregister_controller(avp->av_t1394_hdl); } } /* * * --- FCP write and completion handling * */ static int av1394_fcp_cmd_write_sync(av1394_inst_t *avp, av1394_fcp_cmd_t *fc) { av1394_async_t *ap = &avp->av_a; cmd1394_cmd_t *cmd = fc->fc_cmd; int ret = 0; cmd->completion_callback = av1394_fcp_cmd_completion_cb; cmd->cmd_callback_arg = avp; /* go */ ASSERT(!fc->fc_xmit); fc->fc_xmit = B_TRUE; mutex_exit(&ap->a_mutex); ret = t1394_write(avp->av_t1394_hdl, cmd); mutex_enter(&ap->a_mutex); /* immediate error? */ if (ret != DDI_SUCCESS) { fc->fc_xmit = B_FALSE; TNF_PROBE_2(av1394_fcp_cmd_write_sync_error, AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret, tnf_int, cmd_result, cmd->cmd_result); return (EIO); } /* wait for completion */ while (fc->fc_xmit) { if (cv_wait_sig(&fc->fc_xmit_cv, &ap->a_mutex) == 0) { return (EINTR); } } if (cmd->cmd_result != CMD1394_CMDSUCCESS) { TNF_PROBE_1(av1394_fcp_cmd_write_sync_error, AV1394_TNF_FCP_ERROR, "", tnf_int, cmd_result, cmd->cmd_result); if (cmd->cmd_result == CMD1394_EDEVICE_BUSY) { return (EBUSY); } else { return (EIO); } } else { return (0); } } static void av1394_fcp_cmd_completion_cb(struct cmd1394_cmd *cmd) { av1394_inst_t *avp = cmd->cmd_callback_arg; av1394_async_t *ap = &avp->av_a; av1394_fcp_t *fcp = &ap->a_fcp; av1394_fcp_cmd_t *fc; AV1394_TNF_ENTER(av1394_fcp_cmd_completion_cb); mutex_enter(&ap->a_mutex); /* is this FCP command or response */ if (cmd == fcp->fcp_cmd.fc_cmd) { fc = &fcp->fcp_cmd; } else { ASSERT(cmd == fcp->fcp_resp.fc_cmd); fc = &fcp->fcp_resp; } /* wake the waiter */ fc->fc_xmit = B_FALSE; cv_signal(&fc->fc_xmit_cv); mutex_exit(&ap->a_mutex); AV1394_TNF_EXIT(av1394_fcp_cmd_completion_cb); } /* * av1394_fcp_cmd_write_request_cb() * Incoming response request from an FCP target */ static int av1394_fcp_resp_write_request_cb(cmd1394_cmd_t *req) { av1394_inst_t *avp = req->cmd_callback_arg; av1394_async_t *ap = &avp->av_a; AV1394_TNF_ENTER(av1394_fcp_resp_write_request_cb); mutex_enter(&ap->a_mutex); if ((ap->a_nopen == 0) || (req->bus_generation != ap->a_bus_generation) || (req->nodeID != ap->a_targetinfo.target_nodeID)) { mutex_exit(&ap->a_mutex); AV1394_TNF_EXIT(av1394_fcp_resp_write_request_cb); return (T1394_REQ_UNCLAIMED); } mutex_exit(&ap->a_mutex); av1394_fcp_common_write_request_cb(req, AV1394_M_FCP_RESP); AV1394_TNF_EXIT(av1394_fcp_resp_write_request_cb); return (T1394_REQ_CLAIMED); } /* * av1394_fcp_cmd_write_request_cb() * Incoming command request from an FCP controller */ static int av1394_fcp_cmd_write_request_cb(cmd1394_cmd_t *req) { av1394_inst_t *avp = req->cmd_callback_arg; av1394_async_t *ap = &avp->av_a; AV1394_TNF_ENTER(av1394_fcp_cmd_write_request_cb); mutex_enter(&ap->a_mutex); if (ap->a_nopen == 0) { mutex_exit(&ap->a_mutex); AV1394_TNF_EXIT(av1394_fcp_cmd_write_request_cb); return (T1394_REQ_UNCLAIMED); } mutex_exit(&ap->a_mutex); av1394_fcp_common_write_request_cb(req, AV1394_M_FCP_CMD); AV1394_TNF_EXIT(av1394_fcp_cmd_write_request_cb); return (T1394_REQ_CLAIMED); } static void av1394_fcp_common_write_request_cb(cmd1394_cmd_t *req, int mtype) { av1394_inst_t *avp = req->cmd_callback_arg; mblk_t *mp; uint32_t quadlet_data; int err; AV1394_TNF_ENTER(av1394_fcp_common_write_request_cb); ASSERT((req->cmd_type == CMD1394_ASYNCH_WR_QUAD) || (req->cmd_type == CMD1394_ASYNCH_WR_BLOCK)); /* get the data */ if (req->cmd_type == CMD1394_ASYNCH_WR_QUAD) { quadlet_data = req->cmd_u.q.quadlet_data; } else { mp = req->cmd_u.b.data_block; req->cmd_u.b.data_block = NULL; } /* complete request */ req->cmd_result = IEEE1394_RESP_COMPLETE; err = t1394_recv_request_done(avp->av_t1394_hdl, req, 0); if (err != DDI_SUCCESS) { TNF_PROBE_2(av1394_fcp_common_write_request_cb_done_error, AV1394_TNF_FCP_ERROR, "", tnf_int, err, err, tnf_int, result, req->cmd_result); } /* allocate mblk and copy quadlet into it */ if (req->cmd_type == CMD1394_ASYNCH_WR_QUAD) { mp = allocb(IEEE1394_QUADLET, BPRI_HI); if (mp == NULL) { TNF_PROBE_0( av1394_fcp_common_write_request_cb_allocb_error, AV1394_TNF_FCP_ERROR, ""); AV1394_TNF_EXIT(av1394_fcp_common_write_request_cb); return; } *(uint32_t *)mp->b_rptr = quadlet_data; mp->b_wptr += IEEE1394_QUADLET; } /* queue up the data */ DB_TYPE(mp) = mtype; av1394_async_putq_rq(avp, mp); AV1394_TNF_EXIT(av1394_fcp_common_write_request_cb); } /* * * --- misc routines * */ static int av1394_fcp_copyin_block(iec61883_arq_t *arq, mblk_t *mp, struct uio *uiop) { int len = arq->arq_len; int copylen; int ret = 0; ASSERT((len > 0) && (len % IEEE1394_QUADLET == 0)); /* first copy ARQ-embedded data */ copylen = min(len, sizeof (arq->arq_data)); bcopy(&arq->arq_data.buf[0], mp->b_wptr, copylen); mp->b_wptr += copylen; /* now copyin the rest of the data, if any */ copylen = len - copylen; if (copylen > 0) { ret = uiomove(mp->b_wptr, copylen, UIO_WRITE, uiop); if (ret != 0) { TNF_PROBE_1(av1394_fcp_copyin_block_error, AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret); return (ret); } mp->b_wptr += copylen; } return (ret); }