#include <sys/1394/targets/av1394/av1394_impl.h>
static int av1394_it_start_common(av1394_ic_t *);
static void av1394_it_cleanup(av1394_ic_t *, int);
static int av1394_it_bld_ixl(av1394_ic_t *);
static void av1394_it_destroy_ixl(av1394_ic_t *);
static int av1394_it_ixl_bld_data(av1394_ic_t *);
static void av1394_it_ixl_destroy_data(av1394_ic_t *);
static av1394_it_ixl_buf_t *av1394_it_ixl_bld_buf(av1394_ic_t *, int, int,
off_t, int, int);
static void av1394_it_ixl_complete_buf(av1394_it_ixl_buf_t *,
av1394_it_ixl_empty_cip_t *);
static void av1394_it_ixl_complete_buf2(av1394_it_ixl_buf_t *,
av1394_it_ixl_buf_t *);
static av1394_it_ixl_empty_cip_t *av1394_it_ixl_bld_empty_cip(av1394_ic_t *,
int);
static void av1394_it_ixl_complete_empty_cip(av1394_it_ixl_empty_cip_t *,
av1394_it_ixl_buf_t *);
static void av1394_it_ixl_bld_begin(av1394_ic_t *);
static void av1394_it_ixl_begin_update_pkts(av1394_ic_t *,
av1394_it_ixl_buf_t *);
static int av1394_it_alloc_isoch_dma(av1394_ic_t *);
static void av1394_it_free_isoch_dma(av1394_ic_t *);
static void av1394_it_dma_sync_frames(av1394_ic_t *, int, int);
static void av1394_it_ixl_begin_cb(opaque_t, struct ixl1394_callback *);
static void av1394_it_ixl_buf_cb(opaque_t, struct ixl1394_callback *);
static void av1394_it_ixl_eof_cb(av1394_it_ixl_buf_t *bp);
static int av1394_it_underrun_resume(av1394_ic_t *);
static void av1394_it_dma_stopped_cb(t1394_isoch_dma_handle_t,
opaque_t, id1394_isoch_dma_stopped_t);
static int av1394_it_add_frames(av1394_ic_t *, int, int);
static int av1394_it_wait_frames(av1394_ic_t *, int *, int *, int *);
static void av1394_it_update_frame_syt(av1394_ic_t *, int, int, uint16_t);
static uint16_t av1394_it_ts_cyc2syt(uint16_t);
static uint16_t av1394_it_ts_syt_inc(uint16_t, uint16_t);
static void av1394_it_kcopyin(av1394_ic_t *, void *, size_t);
static int av1394_it_copyin(av1394_ic_t *, struct uio *, int *, int);
static boolean_t av1394_it_is_dv_frame_start(caddr_t);
static void av1394_it_reset_frame_syt(av1394_ic_t *, int);
int av1394_it_hiwat_sub = 2;
int av1394_it_lowat = 3;
int av1394_it_start_thre = 3;
int av1394_it_syt_off = 3;
int av1394_it_dump_ixl = 0;
int
av1394_it_init(av1394_ic_t *icp, int *error)
{
av1394_it_t *itp = &icp->ic_it;
av1394_isoch_pool_t *pool = &itp->it_data_pool;
int nframes;
nframes = av1394_ic_alloc_pool(pool, icp->ic_framesz, icp->ic_nframes,
AV1394_IT_NFRAMES_MIN);
if (nframes == 0) {
*error = IEC61883_ERR_NOMEM;
return (EINVAL);
}
mutex_enter(&icp->ic_mutex);
icp->ic_nframes = nframes;
itp->it_hiwat = nframes - av1394_it_hiwat_sub;
itp->it_lowat = av1394_it_lowat;
itp->it_start_thre = av1394_it_start_thre;
itp->it_nempty = icp->ic_nframes;
itp->it_last_full = icp->ic_nframes - 1;
if (av1394_ic_dma_setup(icp, pool) != DDI_SUCCESS) {
mutex_exit(&icp->ic_mutex);
*error = IEC61883_ERR_NOMEM;
av1394_it_cleanup(icp, 1);
return (EINVAL);
}
if (av1394_it_bld_ixl(icp) != DDI_SUCCESS) {
mutex_exit(&icp->ic_mutex);
*error = IEC61883_ERR_NOMEM;
av1394_it_cleanup(icp, 2);
return (EINVAL);
}
mutex_exit(&icp->ic_mutex);
if (av1394_it_alloc_isoch_dma(icp) != DDI_SUCCESS) {
*error = IEC61883_ERR_NOMEM;
av1394_it_cleanup(icp, 3);
return (EINVAL);
}
return (0);
}
void
av1394_it_fini(av1394_ic_t *icp)
{
av1394_it_cleanup(icp, AV1394_CLEANUP_LEVEL_MAX);
}
int
av1394_it_start(av1394_ic_t *icp)
{
av1394_it_t *itp = &icp->ic_it;
int ret = 0;
mutex_enter(&icp->ic_mutex);
ASSERT(icp->ic_state == AV1394_IC_IDLE);
if (itp->it_nfull >= itp->it_start_thre) {
ret = av1394_it_start_common(icp);
}
mutex_exit(&icp->ic_mutex);
return (ret);
}
static int
av1394_it_start_common(av1394_ic_t *icp)
{
av1394_inst_t *avp = icp->ic_avp;
id1394_isoch_dma_ctrlinfo_t idma_ctrlinfo = { 0 };
int result;
int err;
int ret = 0;
ASSERT(icp->ic_state == AV1394_IC_IDLE);
err = t1394_start_isoch_dma(avp->av_t1394_hdl, icp->ic_isoch_hdl,
&idma_ctrlinfo, 0, &result);
if (err == DDI_SUCCESS) {
icp->ic_state = AV1394_IC_DMA;
} else {
ret = EIO;
}
return (ret);
}
int
av1394_it_stop(av1394_ic_t *icp)
{
av1394_inst_t *avp = icp->ic_avp;
av1394_it_t *itp = &icp->ic_it;
mutex_enter(&icp->ic_mutex);
if (icp->ic_state != AV1394_IC_IDLE) {
mutex_exit(&icp->ic_mutex);
t1394_stop_isoch_dma(avp->av_t1394_hdl, icp->ic_isoch_hdl, 0);
mutex_enter(&icp->ic_mutex);
icp->ic_state = AV1394_IC_IDLE;
itp->it_nempty = icp->ic_nframes;
itp->it_first_empty = 0;
itp->it_last_full = icp->ic_nframes - 1;
itp->it_nfull = 0;
}
mutex_exit(&icp->ic_mutex);
return (0);
}
int
av1394_it_xmit(av1394_ic_t *icp, iec61883_xmit_t *xmit)
{
av1394_it_t *itp = &icp->ic_it;
int ret = 0;
int idx, cnt;
idx = xmit->tx_xfer.xf_full_idx;
cnt = xmit->tx_xfer.xf_full_cnt;
mutex_enter(&icp->ic_mutex);
if ((idx < 0) || (cnt < 0) || (cnt > itp->it_nempty)) {
mutex_exit(&icp->ic_mutex);
return (EINVAL);
}
if (cnt > 0) {
if ((ret = av1394_it_add_frames(icp, idx, cnt)) != 0) {
mutex_exit(&icp->ic_mutex);
return (ret);
}
}
if ((icp->ic_state == AV1394_IC_IDLE) &&
(itp->it_nfull >= itp->it_start_thre)) {
if ((ret = av1394_it_start_common(icp)) != 0) {
mutex_exit(&icp->ic_mutex);
return (ret);
}
}
ret = av1394_it_wait_frames(icp, &xmit->tx_xfer.xf_empty_idx,
&xmit->tx_xfer.xf_empty_cnt, &xmit->tx_miss_cnt);
mutex_exit(&icp->ic_mutex);
return (ret);
}
int
av1394_it_write(av1394_ic_t *icp, struct uio *uiop)
{
av1394_inst_t *avp = icp->ic_avp;
av1394_it_t *itp = &icp->ic_it;
av1394_isoch_autoxmit_t *axp = &avp->av_i.i_autoxmit;
int dv;
int ret = 0;
int full_cnt;
int miss_cnt;
mutex_enter(&icp->ic_mutex);
dv = (axp->ax_fmt & AV1394_ISOCH_AUTOXMIT_DV);
while (uiop->uio_resid > 0) {
if (itp->it_write_cnt == 0) {
ret = av1394_it_wait_frames(icp, &itp->it_write_idx,
&itp->it_write_cnt, &miss_cnt);
if (ret != 0) {
break;
}
}
if (axp->ax_copy_ciph) {
ASSERT(itp->it_write_off == 0);
av1394_it_kcopyin(icp, axp->ax_ciph, AV1394_CIPSZ);
axp->ax_copy_ciph = B_FALSE;
}
if ((ret = av1394_it_copyin(icp, uiop, &full_cnt, dv)) != 0) {
break;
}
if (full_cnt > 0) {
ret = av1394_it_add_frames(icp,
itp->it_write_idx, full_cnt);
if (ret != 0) {
break;
}
itp->it_write_idx += full_cnt;
itp->it_write_idx %= icp->ic_nframes;
}
if ((icp->ic_state == AV1394_IC_IDLE) &&
(itp->it_nfull >= itp->it_start_thre)) {
if ((ret = av1394_it_start_common(icp)) != 0) {
mutex_exit(&icp->ic_mutex);
return (ret);
}
}
}
mutex_exit(&icp->ic_mutex);
return (ret);
}
static void
av1394_it_cleanup(av1394_ic_t *icp, int level)
{
av1394_isoch_pool_t *pool = &icp->ic_it.it_data_pool;
ASSERT((level > 0) && (level <= AV1394_CLEANUP_LEVEL_MAX));
switch (level) {
default:
av1394_it_free_isoch_dma(icp);
case 3:
av1394_it_destroy_ixl(icp);
case 2:
av1394_ic_dma_cleanup(icp, pool);
case 1:
av1394_ic_free_pool(pool);
}
}
static int
av1394_it_bld_ixl(av1394_ic_t *icp)
{
av1394_it_t *itp = &icp->ic_it;
int ret;
if ((ret = av1394_it_ixl_bld_data(icp)) != DDI_SUCCESS) {
return (ret);
}
if (icp->ic_param.cp_ts_mode != IEC61883_TS_NONE) {
av1394_it_ixl_bld_begin(icp);
itp->it_ixlp = (ixl1394_command_t *)&itp->it_ixl_begin;
} else {
itp->it_ixlp = (ixl1394_command_t *)
&((av1394_it_ixl_buf_t *)itp->it_ixl_data)->tb_label;
}
if (av1394_it_dump_ixl) {
av1394_ic_ixl_dump(itp->it_ixlp);
}
return (ret);
}
static void
av1394_it_destroy_ixl(av1394_ic_t *icp)
{
av1394_it_t *itp = &icp->ic_it;
av1394_it_ixl_destroy_data(icp);
itp->it_ixlp = NULL;
}
static int
av1394_it_ixl_bld_data(av1394_ic_t *icp)
{
av1394_it_t *itp = &icp->ic_it;
av1394_isoch_pool_t *pool = &itp->it_data_pool;
int total = 0;
int nfull = 0;
int framenum = -1;
int bufsz_max;
int segnum = 0;
int segsz;
off_t segoff = 0;
av1394_it_ixl_empty_cip_t *ep = NULL;
av1394_it_ixl_buf_t *bp = NULL;
av1394_it_ixl_buf_t *prevbp = NULL;
int a, n, d;
int type, ptype;
int tb_flags;
itp->it_frame_info = kmem_zalloc(icp->ic_nframes *
sizeof (av1394_it_frame_info_t), KM_SLEEP);
bufsz_max = AV1394_IXL_BUFSZ_MAX / icp->ic_pktsz;
n = icp->ic_param.cp_n;
d = icp->ic_param.cp_d;
ASSERT((n == 0) || (d / n > 1));
ptype = AV1394_CIP_EMPTY;
segsz = pool->ip_seg[0].is_size / icp->ic_pktsz;
a = n;
while (total < icp->ic_nframes * icp->ic_npkts) {
a += n;
if (a > d) {
a -= d;
type = AV1394_CIP_EMPTY;
} else {
type = AV1394_CIP_FULL;
nfull++;
}
if (((type == AV1394_CIP_EMPTY) || (segoff + nfull == segsz) ||
(nfull == bufsz_max)) && (nfull > 0)) {
prevbp = bp;
tb_flags = 0;
if (type == AV1394_CIP_EMPTY) {
tb_flags |= AV1394_IT_IXL_BUF_NEXT_EMPTY;
}
if (total % icp->ic_npkts == 0) {
tb_flags |= AV1394_IT_IXL_BUF_SOF;
framenum++;
}
if ((total + nfull) % icp->ic_npkts == 0) {
tb_flags |= AV1394_IT_IXL_BUF_EOF;
}
bp = av1394_it_ixl_bld_buf(icp, nfull, segnum, segoff,
tb_flags, framenum);
if (itp->it_ixl_data == NULL) {
itp->it_ixl_data = &bp->tb_common;
}
if (ep) {
av1394_it_ixl_complete_empty_cip(ep, bp);
ep = NULL;
} else if (prevbp) {
av1394_it_ixl_complete_buf2(prevbp, bp);
}
segoff += nfull;
if (segoff == segsz) {
if (++segnum < pool->ip_nsegs) {
segsz = pool->ip_seg[segnum].is_size /
icp->ic_pktsz;
}
segoff = 0;
}
total += nfull;
nfull = 0;
}
if (type == AV1394_CIP_EMPTY) {
ep = av1394_it_ixl_bld_empty_cip(icp, framenum);
av1394_it_ixl_complete_buf(bp, ep);
}
ptype = type;
}
ASSERT(nfull == 0);
if (n != 0) {
if (ptype != AV1394_CIP_EMPTY) {
ep = av1394_it_ixl_bld_empty_cip(icp, framenum);
av1394_it_ixl_complete_buf(bp, ep);
}
av1394_it_ixl_complete_empty_cip(ep,
(av1394_it_ixl_buf_t *)itp->it_ixl_data);
ep->te_jump.next_ixlp = NULL;
ep->te_common.tc_next = NULL;
} else {
bp->tb_jump.label = (ixl1394_command_t *)
&(((av1394_it_ixl_buf_t *)itp->it_ixl_data)->tb_label);
}
return (DDI_SUCCESS);
}
static void
av1394_it_ixl_destroy_data(av1394_ic_t *icp)
{
av1394_it_t *itp = &icp->ic_it;
av1394_it_ixl_common_t *cmd, *cmd_next;
for (cmd = itp->it_ixl_data; cmd != NULL; cmd = cmd_next) {
cmd_next = cmd->tc_next;
kmem_free(cmd, cmd->tc_size);
}
itp->it_ixl_data = NULL;
kmem_free(itp->it_frame_info,
icp->ic_nframes * sizeof (av1394_it_frame_info_t));
}
static av1394_it_ixl_buf_t *
av1394_it_ixl_bld_buf(av1394_ic_t *icp, int cnt, int segnum, off_t off,
int flags, int framenum)
{
av1394_it_t *itp = &icp->ic_it;
av1394_isoch_seg_t *isp = &itp->it_data_pool.ip_seg[segnum];
av1394_it_ixl_buf_t *bp;
int pktsz = icp->ic_pktsz;
bp = kmem_zalloc(sizeof (av1394_it_ixl_buf_t), KM_SLEEP);
bp->tb_common.tc_size = sizeof (av1394_it_ixl_buf_t);
bp->tb_flags = flags;
bp->tb_framenum = framenum;
bp->tb_icp = icp;
bp->tb_label.ixl_opcode = IXL1394_OP_LABEL;
bp->tb_label.next_ixlp = (ixl1394_command_t *)&bp->tb_buf;
bp->tb_buf.ixl_opcode = IXL1394_OP_SEND_BUF;
bp->tb_buf.pkt_size = pktsz;
bp->tb_buf.size = cnt * pktsz;
bp->tb_buf.ixl_buf._dmac_ll =
isp->is_dma_cookie[0].dmac_laddress + off * pktsz;
bp->tb_buf.mem_bufp = isp->is_kaddr + off * pktsz;
if (flags & AV1394_IT_IXL_BUF_EOF) {
bp->tb_buf.next_ixlp = (ixl1394_command_t *)&bp->tb_store_ts;
bp->tb_store_ts.ixl_opcode = IXL1394_OP_STORE_TIMESTAMP;
bp->tb_store_ts.next_ixlp = (ixl1394_command_t *)&bp->tb_cb;
bp->tb_cb.ixl_opcode = IXL1394_OP_CALLBACK;
bp->tb_cb.callback = av1394_it_ixl_buf_cb;
bp->tb_cb.callback_arg = bp;
bp->tb_cb.next_ixlp = (ixl1394_command_t *)&bp->tb_jump;
bp->tb_jump.ixl_opcode = IXL1394_OP_JUMP_U;
} else {
bp->tb_buf.next_ixlp = (ixl1394_command_t *)&bp->tb_jump;
bp->tb_jump.ixl_opcode = IXL1394_OP_JUMP;
}
if (flags & AV1394_IT_IXL_BUF_SOF) {
itp->it_frame_info[framenum].fi_first_buf = bp;
itp->it_frame_info[framenum].fi_ts_off = bp->tb_buf.mem_bufp +
AV1394_TS_MODE_GET_OFF(icp->ic_param.cp_ts_mode);
} else if (flags & AV1394_IT_IXL_BUF_EOF) {
itp->it_frame_info[framenum].fi_last_buf = bp;
}
itp->it_frame_info[framenum].fi_ncycs += cnt;
return (bp);
}
static void
av1394_it_ixl_complete_buf(av1394_it_ixl_buf_t *bp,
av1394_it_ixl_empty_cip_t *ep)
{
bp->tb_common.tc_next = &ep->te_common;
bp->tb_jump.label = bp->tb_jump.next_ixlp =
(ixl1394_command_t *)&ep->te_label;
}
static void
av1394_it_ixl_complete_buf2(av1394_it_ixl_buf_t *bp,
av1394_it_ixl_buf_t *nextbp)
{
bp->tb_common.tc_next = &nextbp->tb_common;
bp->tb_jump.label = bp->tb_jump.next_ixlp =
(ixl1394_command_t *)&nextbp->tb_label;
}
static av1394_it_ixl_empty_cip_t *
av1394_it_ixl_bld_empty_cip(av1394_ic_t *icp, int framenum)
{
av1394_it_t *itp = &icp->ic_it;
av1394_it_ixl_empty_cip_t *ep;
ep = kmem_zalloc(sizeof (av1394_it_ixl_empty_cip_t), KM_SLEEP);
ep->te_common.tc_size = sizeof (av1394_it_ixl_empty_cip_t);
ep->te_label.ixl_opcode = IXL1394_OP_LABEL;
ep->te_label.next_ixlp = (ixl1394_command_t *)&ep->te_pkt;
ep->te_pkt.ixl_opcode = IXL1394_OP_SEND_PKT_ST;
ep->te_pkt.size = AV1394_CIPSZ;
ep->te_pkt.next_ixlp = (ixl1394_command_t *)&ep->te_jump;
ep->te_jump.ixl_opcode = IXL1394_OP_JUMP;
itp->it_frame_info[framenum].fi_ncycs++;
return (ep);
}
static void
av1394_it_ixl_complete_empty_cip(av1394_it_ixl_empty_cip_t *ep,
av1394_it_ixl_buf_t *bp)
{
ep->te_common.tc_next = &bp->tb_common;
ep->te_pkt.ixl_buf._dmac_ll = bp->tb_buf.ixl_buf._dmac_ll;
ep->te_pkt.mem_bufp = bp->tb_buf.mem_bufp;
ep->te_jump.label = ep->te_jump.next_ixlp =
(ixl1394_command_t *)&bp->tb_label;
}
static void
av1394_it_ixl_bld_begin(av1394_ic_t *icp)
{
av1394_it_t *itp = &icp->ic_it;
av1394_it_ixl_buf_t *bp = (av1394_it_ixl_buf_t *)itp->it_ixl_data;
av1394_it_ixl_begin_t *bep = &itp->it_ixl_begin;
int i;
bep->be_label.ixl_opcode = IXL1394_OP_LABEL;
bep->be_label.next_ixlp = (ixl1394_command_t *)&bep->be_empty_pre;
bep->be_empty_pre.ixl_opcode = IXL1394_OP_SEND_PKT_ST;
bep->be_empty_pre.size = AV1394_CIPSZ;
bep->be_empty_pre.ixl_buf._dmac_ll = bp->tb_buf.ixl_buf._dmac_ll;
bep->be_empty_pre.mem_bufp = bp->tb_buf.mem_bufp;
bep->be_empty_pre.next_ixlp = (ixl1394_command_t *)&bep->be_store_ts;
bep->be_store_ts.ixl_opcode = IXL1394_OP_STORE_TIMESTAMP;
bep->be_store_ts.next_ixlp = (ixl1394_command_t *)&bep->be_cb;
bep->be_cb.ixl_opcode = IXL1394_OP_CALLBACK;
bep->be_cb.callback = av1394_it_ixl_begin_cb;
bep->be_cb.callback_arg = &bep->be_store_ts.timestamp;
bep->be_cb.next_ixlp = (ixl1394_command_t *)&bep->be_empty_post[0];
for (i = 0; i < AV1394_IT_IXL_BEGIN_NPOST; i++) {
bep->be_empty_post[i].ixl_opcode = IXL1394_OP_SEND_PKT_ST;
bep->be_empty_post[i].size = AV1394_CIPSZ;
bep->be_empty_post[i].ixl_buf._dmac_ll =
bp->tb_buf.ixl_buf._dmac_ll;
bep->be_empty_post[i].mem_bufp = bp->tb_buf.mem_bufp;
bep->be_empty_post[i].next_ixlp =
(ixl1394_command_t *)&bep->be_empty_post[i + 1];
}
bep->be_empty_post[AV1394_IT_IXL_BEGIN_NPOST - 1].next_ixlp =
(ixl1394_command_t *)&bep->be_jump;
bep->be_jump.ixl_opcode = IXL1394_OP_JUMP_U;
bep->be_jump.label = (ixl1394_command_t *)&bp->tb_label;
bep->be_jump.next_ixlp = (ixl1394_command_t *)&bp->tb_label;
}
static void
av1394_it_ixl_begin_update_pkts(av1394_ic_t *icp, av1394_it_ixl_buf_t *bp)
{
av1394_it_t *itp = &icp->ic_it;
av1394_it_ixl_begin_t *bep = &itp->it_ixl_begin;
int i;
for (i = 0; i < AV1394_IT_IXL_BEGIN_NPOST; i++) {
bep->be_empty_post[i].ixl_buf._dmac_ll =
bp->tb_buf.ixl_buf._dmac_ll;
bep->be_empty_post[i].mem_bufp = bp->tb_buf.mem_bufp;
}
}
static int
av1394_it_alloc_isoch_dma(av1394_ic_t *icp)
{
av1394_inst_t *avp = icp->ic_avp;
av1394_it_t *itp = &icp->ic_it;
id1394_isoch_dmainfo_t di;
int result;
int ret;
di.ixlp = itp->it_ixlp;
di.channel_num = icp->ic_num;
di.idma_options = ID1394_TALK;
di.it_speed = icp->ic_param.cp_bus_speed;
di.it_default_skip = IXL1394_SKIP_TO_SELF;
di.default_tag = 1;
di.default_sync = 0;
di.global_callback_arg = icp;
di.isoch_dma_stopped = av1394_it_dma_stopped_cb;
di.idma_evt_arg = icp;
ret = t1394_alloc_isoch_dma(avp->av_t1394_hdl, &di, 0,
&icp->ic_isoch_hdl, &result);
return (ret);
}
static void
av1394_it_free_isoch_dma(av1394_ic_t *icp)
{
av1394_inst_t *avp = icp->ic_avp;
t1394_free_isoch_dma(avp->av_t1394_hdl, 0, &icp->ic_isoch_hdl);
}
static void
av1394_it_dma_sync_frames(av1394_ic_t *icp, int idx, int cnt)
{
av1394_ic_dma_sync_frames(icp, idx, cnt,
&icp->ic_it.it_data_pool, DDI_DMA_SYNC_FORDEV);
}
static void
av1394_it_ixl_begin_cb(opaque_t arg, struct ixl1394_callback *cb)
{
av1394_ic_t *icp = arg;
av1394_it_t *itp = &icp->ic_it;
uint16_t *cycp = cb->callback_arg;
uint16_t syt;
int first;
mutex_enter(&icp->ic_mutex);
itp->it_ts_init.ts_syt = av1394_it_ts_cyc2syt(*cycp);
if ((itp->it_nfull <= 0) ||
(AV1394_TS_MODE_GET_SIZE(icp->ic_param.cp_ts_mode) == 0)) {
mutex_exit(&icp->ic_mutex);
return;
}
ASSERT(itp->it_nfull <= icp->ic_nframes);
syt = av1394_it_ts_syt_inc(itp->it_ts_init.ts_syt,
AV1394_IT_IXL_BEGIN_NPOST + av1394_it_syt_off);
first = (itp->it_last_full + icp->ic_nframes - itp->it_nfull + 1) %
icp->ic_nframes;
av1394_it_update_frame_syt(icp, first, itp->it_nfull, syt);
mutex_exit(&icp->ic_mutex);
}
static void
av1394_it_ixl_buf_cb(opaque_t arg, struct ixl1394_callback *cb)
{
av1394_it_ixl_buf_t *bp = cb->callback_arg;
if (bp->tb_flags & AV1394_IT_IXL_BUF_EOF) {
av1394_it_ixl_eof_cb(bp);
}
}
static void
av1394_it_ixl_eof_cb(av1394_it_ixl_buf_t *bp)
{
av1394_ic_t *icp = bp->tb_icp;
av1394_isoch_t *ip = &icp->ic_avp->av_i;
av1394_it_t *itp = &icp->ic_it;
mutex_enter(&ip->i_mutex);
mutex_enter(&icp->ic_mutex);
if (itp->it_nempty < icp->ic_nframes) {
itp->it_nempty++;
itp->it_nfull--;
cv_signal(&icp->ic_xfer_cv);
}
if ((itp->it_nempty >= itp->it_hiwat) &&
(icp->ic_state == AV1394_IC_DMA)) {
av1394_ic_trigger_softintr(icp, icp->ic_num,
AV1394_PREQ_IT_UNDERRUN);
}
mutex_exit(&icp->ic_mutex);
mutex_exit(&ip->i_mutex);
}
void
av1394_it_underrun(av1394_ic_t *icp)
{
av1394_it_t *itp = &icp->ic_it;
av1394_inst_t *avp = icp->ic_avp;
int idx;
ixl1394_jump_t *old_jmp;
ixl1394_jump_t new_jmp;
id1394_isoch_dma_updateinfo_t update_info;
int err;
int result;
idx = (itp->it_first_empty + icp->ic_nframes - 1) % icp->ic_nframes;
old_jmp = &itp->it_frame_info[idx].fi_last_buf->tb_jump;
itp->it_saved_label = old_jmp->label;
new_jmp.ixl_opcode = IXL1394_OP_JUMP_U;
new_jmp.label = NULL;
new_jmp.next_ixlp = NULL;
update_info.orig_ixlp = (ixl1394_command_t *)old_jmp;
update_info.temp_ixlp = (ixl1394_command_t *)&new_jmp;
update_info.ixl_count = 1;
mutex_exit(&icp->ic_mutex);
err = t1394_update_isoch_dma(avp->av_t1394_hdl,
icp->ic_isoch_hdl, &update_info, 0, &result);
mutex_enter(&icp->ic_mutex);
if (err == DDI_SUCCESS) {
itp->it_underrun_idx = idx;
icp->ic_state = AV1394_IC_SUSPENDED;
cv_signal(&icp->ic_xfer_cv);
}
}
static int
av1394_it_underrun_resume(av1394_ic_t *icp)
{
av1394_it_t *itp = &icp->ic_it;
av1394_inst_t *avp = icp->ic_avp;
av1394_it_ixl_buf_t *bp;
int idx;
ixl1394_jump_t *old_jmp;
ixl1394_jump_t new_jmp;
id1394_isoch_dma_updateinfo_t update_info;
int err;
int result;
idx = (itp->it_underrun_idx + 1) % icp->ic_nframes;
bp = itp->it_frame_info[idx].fi_first_buf;
av1394_it_ixl_begin_update_pkts(icp, bp);
old_jmp = &itp->it_ixl_begin.be_jump;
new_jmp.ixl_opcode = IXL1394_OP_JUMP_U;
new_jmp.label = (ixl1394_command_t *)&bp->tb_label;
new_jmp.next_ixlp = NULL;
update_info.orig_ixlp = (ixl1394_command_t *)old_jmp;
update_info.temp_ixlp = (ixl1394_command_t *)&new_jmp;
update_info.ixl_count = 1;
mutex_exit(&icp->ic_mutex);
err = t1394_update_isoch_dma(avp->av_t1394_hdl,
icp->ic_isoch_hdl, &update_info, 0, &result);
mutex_enter(&icp->ic_mutex);
if (err != DDI_SUCCESS) {
return (EIO);
}
bp = itp->it_frame_info[itp->it_underrun_idx].fi_last_buf;
old_jmp = &bp->tb_jump;
new_jmp.ixl_opcode = IXL1394_OP_JUMP_U;
new_jmp.label = itp->it_saved_label;
new_jmp.next_ixlp = NULL;
update_info.orig_ixlp = (ixl1394_command_t *)old_jmp;
update_info.temp_ixlp = (ixl1394_command_t *)&new_jmp;
update_info.ixl_count = 1;
mutex_exit(&icp->ic_mutex);
err = t1394_update_isoch_dma(avp->av_t1394_hdl,
icp->ic_isoch_hdl, &update_info, 0, &result);
mutex_enter(&icp->ic_mutex);
if (err != DDI_SUCCESS) {
return (EIO);
}
icp->ic_state = AV1394_IC_DMA;
return (0);
}
static void
av1394_it_dma_stopped_cb(t1394_isoch_dma_handle_t t1394_idma_hdl,
opaque_t idma_evt_arg, id1394_isoch_dma_stopped_t status)
{
av1394_ic_t *icp = idma_evt_arg;
mutex_enter(&icp->ic_mutex);
icp->ic_state = AV1394_IC_IDLE;
mutex_exit(&icp->ic_mutex);
}
static int
av1394_it_add_frames(av1394_ic_t *icp, int idx, int cnt)
{
av1394_it_t *itp = &icp->ic_it;
av1394_it_frame_info_t *fip;
int prev_full = itp->it_last_full;
uint16_t syt;
int ret = 0;
if (idx != ((itp->it_last_full + 1) % icp->ic_nframes)) {
return (EINVAL);
}
itp->it_nempty -= cnt;
itp->it_first_empty = (itp->it_first_empty + cnt) % icp->ic_nframes;
itp->it_nfull += cnt;
itp->it_last_full = (itp->it_last_full + cnt) % icp->ic_nframes;
ASSERT((itp->it_nempty >= 0) && (itp->it_nfull <= icp->ic_nframes));
if (AV1394_TS_MODE_GET_SIZE(icp->ic_param.cp_ts_mode) > 0) {
ASSERT(prev_full >= 0);
fip = &itp->it_frame_info[prev_full];
syt = *(uint16_t *)fip->fi_ts_off;
syt = av1394_it_ts_syt_inc(syt, fip->fi_ncycs);
av1394_it_update_frame_syt(icp, idx, cnt, syt);
}
av1394_it_dma_sync_frames(icp, idx, cnt);
if ((icp->ic_state == AV1394_IC_SUSPENDED) &&
(itp->it_nempty >= itp->it_lowat)) {
ret = av1394_it_underrun_resume(icp);
}
return (ret);
}
static int
av1394_it_wait_frames(av1394_ic_t *icp, int *idx, int *cnt, int *nlost)
{
av1394_it_t *itp = &icp->ic_it;
int ret = 0;
while ((itp->it_nempty == 0) && (icp->ic_state == AV1394_IC_DMA)) {
if (cv_wait_sig(&icp->ic_xfer_cv, &icp->ic_mutex) <= 0) {
ret = EINTR;
break;
}
}
if (itp->it_nempty > 0) {
*idx = itp->it_first_empty;
*cnt = itp->it_nempty;
*nlost = 0;
ret = 0;
}
return (ret);
}
static void
av1394_it_update_frame_syt(av1394_ic_t *icp, int first, int cnt, uint16_t syt)
{
av1394_it_t *itp = &icp->ic_it;
int i;
int j = first;
for (i = cnt; i > 0; i--) {
*(uint16_t *)itp->it_frame_info[j].fi_ts_off = syt;
syt = av1394_it_ts_syt_inc(syt, itp->it_frame_info[j].fi_ncycs);
j = (j + 1) % icp->ic_nframes;
}
}
static uint16_t
av1394_it_ts_cyc2syt(uint16_t cyc)
{
return (((cyc & 0xF) << 12) + 0x800);
}
static uint16_t
av1394_it_ts_syt_inc(uint16_t syt, uint16_t ncycs)
{
return (syt + (ncycs << 12));
}
static void
av1394_it_kcopyin(av1394_ic_t *icp, void *buf, size_t len)
{
av1394_it_t *itp = &icp->ic_it;
av1394_isoch_seg_t *seg = itp->it_data_pool.ip_seg;
ASSERT(itp->it_write_off + len < icp->ic_framesz);
bcopy(buf, seg[itp->it_write_idx].is_kaddr + itp->it_write_off, len);
itp->it_write_off += len;
}
static int
av1394_it_copyin(av1394_ic_t *icp, struct uio *uiop, int *full_cnt, int dv)
{
av1394_it_t *itp = &icp->ic_it;
av1394_isoch_seg_t *seg = itp->it_data_pool.ip_seg;
int idx = itp->it_write_idx;
int framesz = icp->ic_framesz;
size_t len, frame_resid, start_resid;
caddr_t kaddr, kaddr_end;
int ret = 0;
*full_cnt = 0;
while ((uiop->uio_resid > 0) && (itp->it_write_cnt > 0)) {
kaddr = seg[idx].is_kaddr + itp->it_write_off;
frame_resid = framesz - itp->it_write_off;
len = min(uiop->uio_resid, frame_resid);
mutex_exit(&icp->ic_mutex);
ret = uiomove(kaddr, len, UIO_WRITE, uiop);
mutex_enter(&icp->ic_mutex);
if (ret != 0) {
break;
}
itp->it_write_off += len;
if ((itp->it_write_off == framesz) && dv) {
kaddr = seg[idx].is_kaddr;
kaddr_end = kaddr + framesz;
while (!av1394_it_is_dv_frame_start(kaddr)) {
kaddr += icp->ic_pktsz;
if (kaddr == kaddr_end) {
break;
}
}
start_resid = kaddr_end - kaddr;
if (start_resid != framesz) {
bcopy(kaddr, seg[idx].is_kaddr, start_resid);
itp->it_write_off = start_resid;
}
}
if (itp->it_write_off == framesz) {
if (dv) {
av1394_it_reset_frame_syt(icp, idx);
}
itp->it_write_off = 0;
itp->it_write_cnt--;
idx = (idx + 1) % icp->ic_nframes;
(*full_cnt)++;
}
}
return (ret);
}
static boolean_t
av1394_it_is_dv_frame_start(caddr_t kaddr)
{
uint8_t *p = (uint8_t *)kaddr + 8;
return (((p[0] & 0xE0) == 0) && ((p[1] & 0xF0) == 0) && (p[2] == 0));
}
static void
av1394_it_reset_frame_syt(av1394_ic_t *icp, int idx)
{
caddr_t kaddr = icp->ic_it.it_data_pool.ip_seg[idx].is_kaddr;
caddr_t kaddr_end = kaddr + icp->ic_framesz;
kaddr += 6;
while (kaddr < kaddr_end) {
*(uint16_t *)kaddr = 0xFFFF;
kaddr += icp->ic_pktsz;
}
}