#include <sys/stat.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/1394/targets/av1394/av1394_impl.h>
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);
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);
static int av1394_fcp_copyin_block(iec61883_arq_t *, mblk_t *,
struct uio *);
int
av1394_fcp_attach(av1394_inst_t *avp)
{
av1394_fcp_t *fcp = &avp->av_a.a_fcp;
int ret;
if ((ret = av1394_fcp_ctl_register(avp)) != DDI_SUCCESS) {
return (ret);
}
if ((ret = av1394_fcp_ctl_alloc_cmd(avp)) != DDI_SUCCESS) {
av1394_fcp_cleanup(avp, 1);
return (ret);
}
if ((ret = av1394_fcp_tgt_register(avp)) != DDI_SUCCESS) {
av1394_fcp_cleanup(avp, 2);
return (ret);
}
if ((ret = av1394_fcp_tgt_alloc_cmd(avp)) != DDI_SUCCESS) {
av1394_fcp_cleanup(avp, 3);
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);
return (ret);
}
void
av1394_fcp_detach(av1394_inst_t *avp)
{
av1394_fcp_cleanup(avp, AV1394_CLEANUP_LEVEL_MAX);
}
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;
ASSERT((arq->arq_type == IEC61883_ARQ_FCP_CMD) ||
(arq->arq_type == IEC61883_ARQ_FCP_RESP));
if ((len == 0) || (len > AV1394_FCP_ARQ_LEN_MAX) ||
(len % IEEE1394_QUADLET != 0)) {
return (EINVAL);
}
if (len > IEEE1394_QUADLET) {
if ((mp = allocb(len, BPRI_HI)) == NULL) {
return (ENOMEM);
}
if ((ret = av1394_fcp_copyin_block(arq, mp, uiop)) != 0) {
freemsg(mp);
return (ret);
}
}
fc = (arq->arq_type == IEC61883_ARQ_FCP_CMD) ?
&fcp->fcp_cmd : &fcp->fcp_resp;
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);
return (EINTR);
}
}
fc->fc_busy = B_TRUE;
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;
}
ret = av1394_fcp_cmd_write_sync(avp, fc);
fc->fc_busy = B_FALSE;
cv_broadcast(&fc->fc_busy_cv);
mutex_exit(&ap->a_mutex);
freemsg(mp);
return (ret);
}
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);
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);
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);
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;
(void) t1394_free_cmd(avp->av_t1394_hdl, 0, &fc->fc_cmd);
}
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);
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;
(void) t1394_free_cmd(avp->av_t1394_hdl, 0, &fc->fc_cmd);
}
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);
case 3:
(void) t1394_fcp_unregister_target(avp->av_t1394_hdl);
case 2:
av1394_fcp_ctl_free_cmd(avp);
case 1:
(void) t1394_fcp_unregister_controller(avp->av_t1394_hdl);
}
}
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;
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);
if (ret != DDI_SUCCESS) {
fc->fc_xmit = B_FALSE;
return (EIO);
}
while (fc->fc_xmit) {
if (cv_wait_sig(&fc->fc_xmit_cv, &ap->a_mutex) == 0) {
return (EINTR);
}
}
if (cmd->cmd_result != CMD1394_CMDSUCCESS) {
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;
mutex_enter(&ap->a_mutex);
if (cmd == fcp->fcp_cmd.fc_cmd) {
fc = &fcp->fcp_cmd;
} else {
ASSERT(cmd == fcp->fcp_resp.fc_cmd);
fc = &fcp->fcp_resp;
}
fc->fc_xmit = B_FALSE;
cv_signal(&fc->fc_xmit_cv);
mutex_exit(&ap->a_mutex);
}
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;
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);
return (T1394_REQ_UNCLAIMED);
}
mutex_exit(&ap->a_mutex);
av1394_fcp_common_write_request_cb(req, AV1394_M_FCP_RESP);
return (T1394_REQ_CLAIMED);
}
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;
mutex_enter(&ap->a_mutex);
if (ap->a_nopen == 0) {
mutex_exit(&ap->a_mutex);
return (T1394_REQ_UNCLAIMED);
}
mutex_exit(&ap->a_mutex);
av1394_fcp_common_write_request_cb(req, AV1394_M_FCP_CMD);
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;
ASSERT((req->cmd_type == CMD1394_ASYNCH_WR_QUAD) ||
(req->cmd_type == CMD1394_ASYNCH_WR_BLOCK));
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;
}
req->cmd_result = IEEE1394_RESP_COMPLETE;
(void) t1394_recv_request_done(avp->av_t1394_hdl, req, 0);
if (req->cmd_type == CMD1394_ASYNCH_WR_QUAD) {
mp = allocb(IEEE1394_QUADLET, BPRI_HI);
if (mp == NULL) {
return;
}
*(uint32_t *)mp->b_rptr = quadlet_data;
mp->b_wptr += IEEE1394_QUADLET;
}
DB_TYPE(mp) = mtype;
av1394_async_putq_rq(avp, mp);
}
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));
copylen = min(len, sizeof (arq->arq_data));
bcopy(&arq->arq_data.buf[0], mp->b_wptr, copylen);
mp->b_wptr += copylen;
copylen = len - copylen;
if (copylen > 0) {
ret = uiomove(mp->b_wptr, copylen, UIO_WRITE, uiop);
if (ret != 0) {
return (ret);
}
mp->b_wptr += copylen;
}
return (ret);
}