xref: /illumos-gate/usr/src/uts/common/io/1394/targets/av1394/av1394_isoch_recv.c (revision 2570281cf351044b6936651ce26dbe1f801dcbd8)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
547cd5876SAlan Perry  * Common Development and Distribution License (the "License").
647cd5876SAlan Perry  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
2247cd5876SAlan Perry  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  * av1394 isochronous receive module
287c478bd9Sstevel@tonic-gate  */
297c478bd9Sstevel@tonic-gate #include <sys/1394/targets/av1394/av1394_impl.h>
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate /* configuration routines */
327c478bd9Sstevel@tonic-gate static void	av1394_ir_cleanup(av1394_ic_t *, int);
337c478bd9Sstevel@tonic-gate static int	av1394_ir_build_ixl(av1394_ic_t *);
347c478bd9Sstevel@tonic-gate static void	av1394_ir_ixl_label_init(av1394_ir_ixl_data_t *,
357c478bd9Sstevel@tonic-gate 		ixl1394_command_t *);
367c478bd9Sstevel@tonic-gate static void	av1394_ir_ixl_buf_init(av1394_ic_t *, ixl1394_xfer_buf_t *,
3747cd5876SAlan Perry 		av1394_isoch_seg_t *, off_t, uint64_t, uint16_t,
3847cd5876SAlan Perry 		ixl1394_command_t *);
397c478bd9Sstevel@tonic-gate static void	av1394_ir_ixl_cb_init(av1394_ic_t *, av1394_ir_ixl_data_t *,
407c478bd9Sstevel@tonic-gate 		int);
417c478bd9Sstevel@tonic-gate static void	av1394_ir_ixl_jump_init(av1394_ic_t *, av1394_ir_ixl_data_t *,
427c478bd9Sstevel@tonic-gate 		int);
437c478bd9Sstevel@tonic-gate static void	av1394_ir_destroy_ixl(av1394_ic_t *);
447c478bd9Sstevel@tonic-gate static int	av1394_ir_alloc_isoch_dma(av1394_ic_t *);
457c478bd9Sstevel@tonic-gate static void	av1394_ir_free_isoch_dma(av1394_ic_t *);
467c478bd9Sstevel@tonic-gate static void	av1394_ir_dma_sync_frames(av1394_ic_t *, int, int);
477c478bd9Sstevel@tonic-gate 
487c478bd9Sstevel@tonic-gate /* callbacks */
497c478bd9Sstevel@tonic-gate static void	av1394_ir_ixl_frame_cb(opaque_t, struct ixl1394_callback *);
507c478bd9Sstevel@tonic-gate static void	av1394_ir_overflow_resume(av1394_ic_t *icp);
517c478bd9Sstevel@tonic-gate static void	av1394_ir_dma_stopped_cb(t1394_isoch_dma_handle_t,
527c478bd9Sstevel@tonic-gate 		opaque_t, id1394_isoch_dma_stopped_t);
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate /* data transfer routines */
557c478bd9Sstevel@tonic-gate static int	av1394_ir_add_frames(av1394_ic_t *, int, int);
567c478bd9Sstevel@tonic-gate static int	av1394_ir_wait_frames(av1394_ic_t *, int *, int *);
577c478bd9Sstevel@tonic-gate static int	av1394_ir_copyout(av1394_ic_t *, struct uio *, int *);
587c478bd9Sstevel@tonic-gate static void	av1394_ir_zero_pkts(av1394_ic_t *, int, int);
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate /* value complementary to hi & lo watermarks (modulo number of frames) */
617c478bd9Sstevel@tonic-gate int av1394_ir_hiwat_sub = 2;
627c478bd9Sstevel@tonic-gate int av1394_ir_lowat_sub = 3;
637c478bd9Sstevel@tonic-gate int av1394_ir_dump_ixl = 0;
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate int
av1394_ir_init(av1394_ic_t * icp,int * error)667c478bd9Sstevel@tonic-gate av1394_ir_init(av1394_ic_t *icp, int *error)
677c478bd9Sstevel@tonic-gate {
687c478bd9Sstevel@tonic-gate 	av1394_ir_t	*irp = &icp->ic_ir;
697c478bd9Sstevel@tonic-gate 	av1394_isoch_pool_t *pool = &irp->ir_data_pool;
707c478bd9Sstevel@tonic-gate 	int		nframes;
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate 	nframes = av1394_ic_alloc_pool(pool, icp->ic_framesz, icp->ic_nframes,
737c478bd9Sstevel@tonic-gate 	    AV1394_IR_NFRAMES_MIN);
747c478bd9Sstevel@tonic-gate 	if (nframes == 0) {
757c478bd9Sstevel@tonic-gate 		*error = IEC61883_ERR_NOMEM;
767c478bd9Sstevel@tonic-gate 		return (EINVAL);
777c478bd9Sstevel@tonic-gate 	}
787c478bd9Sstevel@tonic-gate 	mutex_enter(&icp->ic_mutex);
797c478bd9Sstevel@tonic-gate 	icp->ic_nframes = nframes;
807c478bd9Sstevel@tonic-gate 	irp->ir_hiwat = nframes - av1394_ir_hiwat_sub;
817c478bd9Sstevel@tonic-gate 	irp->ir_lowat = nframes - av1394_ir_lowat_sub;
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate 	if (av1394_ic_dma_setup(icp, pool) != DDI_SUCCESS) {
847c478bd9Sstevel@tonic-gate 		mutex_exit(&icp->ic_mutex);
857c478bd9Sstevel@tonic-gate 		*error = IEC61883_ERR_NOMEM;
867c478bd9Sstevel@tonic-gate 		av1394_ir_cleanup(icp, 1);
877c478bd9Sstevel@tonic-gate 		return (EINVAL);
887c478bd9Sstevel@tonic-gate 	}
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate 	if (av1394_ir_build_ixl(icp) != DDI_SUCCESS) {
917c478bd9Sstevel@tonic-gate 		mutex_exit(&icp->ic_mutex);
927c478bd9Sstevel@tonic-gate 		*error = IEC61883_ERR_NOMEM;
937c478bd9Sstevel@tonic-gate 		av1394_ir_cleanup(icp, 2);
947c478bd9Sstevel@tonic-gate 		return (EINVAL);
957c478bd9Sstevel@tonic-gate 	}
967c478bd9Sstevel@tonic-gate 	mutex_exit(&icp->ic_mutex);
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate 	if (av1394_ir_alloc_isoch_dma(icp) != DDI_SUCCESS) {
997c478bd9Sstevel@tonic-gate 		*error = IEC61883_ERR_NOMEM;
1007c478bd9Sstevel@tonic-gate 		av1394_ir_cleanup(icp, 3);
1017c478bd9Sstevel@tonic-gate 		return (EINVAL);
1027c478bd9Sstevel@tonic-gate 	}
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate 	return (0);
1057c478bd9Sstevel@tonic-gate }
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate void
av1394_ir_fini(av1394_ic_t * icp)1087c478bd9Sstevel@tonic-gate av1394_ir_fini(av1394_ic_t *icp)
1097c478bd9Sstevel@tonic-gate {
1107c478bd9Sstevel@tonic-gate 	av1394_ir_cleanup(icp, AV1394_CLEANUP_LEVEL_MAX);
1117c478bd9Sstevel@tonic-gate }
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate int
av1394_ir_start(av1394_ic_t * icp)1147c478bd9Sstevel@tonic-gate av1394_ir_start(av1394_ic_t *icp)
1157c478bd9Sstevel@tonic-gate {
1167c478bd9Sstevel@tonic-gate 	av1394_inst_t	*avp = icp->ic_avp;
1177c478bd9Sstevel@tonic-gate 	av1394_ir_t	*irp = &icp->ic_ir;
1187c478bd9Sstevel@tonic-gate 	id1394_isoch_dma_ctrlinfo_t idma_ctrlinfo = { 0 };
1197c478bd9Sstevel@tonic-gate 	int		result;
1207c478bd9Sstevel@tonic-gate 	int		err;
1217c478bd9Sstevel@tonic-gate 	int		ret = 0;
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate 	mutex_enter(&icp->ic_mutex);
1247c478bd9Sstevel@tonic-gate 	if (icp->ic_state != AV1394_IC_IDLE) {
1257c478bd9Sstevel@tonic-gate 		mutex_exit(&icp->ic_mutex);
1267c478bd9Sstevel@tonic-gate 		return (0);
1277c478bd9Sstevel@tonic-gate 	}
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate 	irp->ir_first_full = 0;
1307c478bd9Sstevel@tonic-gate 	irp->ir_last_empty = icp->ic_nframes - 1;
1317c478bd9Sstevel@tonic-gate 	irp->ir_nfull = 0;
1327c478bd9Sstevel@tonic-gate 	irp->ir_nempty = icp->ic_nframes;
1337c478bd9Sstevel@tonic-gate 	irp->ir_read_cnt = 0;
1347c478bd9Sstevel@tonic-gate 	mutex_exit(&icp->ic_mutex);
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate 	err = t1394_start_isoch_dma(avp->av_t1394_hdl, icp->ic_isoch_hdl,
1377c478bd9Sstevel@tonic-gate 	    &idma_ctrlinfo, 0, &result);
1387c478bd9Sstevel@tonic-gate 	if (err == DDI_SUCCESS) {
1397c478bd9Sstevel@tonic-gate 		mutex_enter(&icp->ic_mutex);
1407c478bd9Sstevel@tonic-gate 		icp->ic_state = AV1394_IC_DMA;
1417c478bd9Sstevel@tonic-gate 		mutex_exit(&icp->ic_mutex);
1427c478bd9Sstevel@tonic-gate 	} else {
1437c478bd9Sstevel@tonic-gate 		ret = EIO;
1447c478bd9Sstevel@tonic-gate 	}
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate 	return (ret);
1477c478bd9Sstevel@tonic-gate }
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate int
av1394_ir_stop(av1394_ic_t * icp)1507c478bd9Sstevel@tonic-gate av1394_ir_stop(av1394_ic_t *icp)
1517c478bd9Sstevel@tonic-gate {
1527c478bd9Sstevel@tonic-gate 	av1394_inst_t	*avp = icp->ic_avp;
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate 	mutex_enter(&icp->ic_mutex);
1557c478bd9Sstevel@tonic-gate 	if (icp->ic_state != AV1394_IC_IDLE) {
1567c478bd9Sstevel@tonic-gate 		mutex_exit(&icp->ic_mutex);
1577c478bd9Sstevel@tonic-gate 		t1394_stop_isoch_dma(avp->av_t1394_hdl, icp->ic_isoch_hdl, 0);
1587c478bd9Sstevel@tonic-gate 		mutex_enter(&icp->ic_mutex);
1597c478bd9Sstevel@tonic-gate 		icp->ic_state = AV1394_IC_IDLE;
1607c478bd9Sstevel@tonic-gate 	}
1617c478bd9Sstevel@tonic-gate 	mutex_exit(&icp->ic_mutex);
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 	return (0);
1647c478bd9Sstevel@tonic-gate }
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate int
av1394_ir_recv(av1394_ic_t * icp,iec61883_recv_t * recv)1677c478bd9Sstevel@tonic-gate av1394_ir_recv(av1394_ic_t *icp, iec61883_recv_t *recv)
1687c478bd9Sstevel@tonic-gate {
1697c478bd9Sstevel@tonic-gate 	int		ret = 0;
1707c478bd9Sstevel@tonic-gate 	int		idx, cnt;
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate 	idx = recv->rx_xfer.xf_empty_idx;
1737c478bd9Sstevel@tonic-gate 	cnt = recv->rx_xfer.xf_empty_cnt;
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate 	/* check arguments */
1767c478bd9Sstevel@tonic-gate 	if ((idx < 0) || (idx >= icp->ic_nframes) ||
1777c478bd9Sstevel@tonic-gate 	    (cnt < 0) || (cnt > icp->ic_nframes)) {
1787c478bd9Sstevel@tonic-gate 		return (EINVAL);
1797c478bd9Sstevel@tonic-gate 	}
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate 	mutex_enter(&icp->ic_mutex);
1827c478bd9Sstevel@tonic-gate 	if (cnt > 0) {
1837c478bd9Sstevel@tonic-gate 		/* add empty frames to the pool */
1847c478bd9Sstevel@tonic-gate 		if ((ret = av1394_ir_add_frames(icp, idx, cnt)) != 0) {
1857c478bd9Sstevel@tonic-gate 			mutex_exit(&icp->ic_mutex);
1867c478bd9Sstevel@tonic-gate 			return (ret);
1877c478bd9Sstevel@tonic-gate 		}
1887c478bd9Sstevel@tonic-gate 	}
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 	/* wait for new frames to arrive */
1917c478bd9Sstevel@tonic-gate 	ret = av1394_ir_wait_frames(icp,
1927c478bd9Sstevel@tonic-gate 	    &recv->rx_xfer.xf_full_idx, &recv->rx_xfer.xf_full_cnt);
1937c478bd9Sstevel@tonic-gate 	mutex_exit(&icp->ic_mutex);
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate 	return (ret);
1967c478bd9Sstevel@tonic-gate }
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate int
av1394_ir_read(av1394_ic_t * icp,struct uio * uiop)1997c478bd9Sstevel@tonic-gate av1394_ir_read(av1394_ic_t *icp, struct uio *uiop)
2007c478bd9Sstevel@tonic-gate {
2017c478bd9Sstevel@tonic-gate 	av1394_ir_t	*irp = &icp->ic_ir;
2027c478bd9Sstevel@tonic-gate 	int		ret = 0;
2037c478bd9Sstevel@tonic-gate 	int		empty_cnt;
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 	mutex_enter(&icp->ic_mutex);
2067c478bd9Sstevel@tonic-gate 	while (uiop->uio_resid) {
2077c478bd9Sstevel@tonic-gate 		/* wait for full frames, if necessary */
2087c478bd9Sstevel@tonic-gate 		if (irp->ir_read_cnt == 0) {
2097c478bd9Sstevel@tonic-gate 			irp->ir_read_off = 0;
2107c478bd9Sstevel@tonic-gate 			ret = av1394_ir_wait_frames(icp,
2117c478bd9Sstevel@tonic-gate 			    &irp->ir_read_idx, &irp->ir_read_cnt);
2127c478bd9Sstevel@tonic-gate 			if (ret != 0) {
2137c478bd9Sstevel@tonic-gate 				mutex_exit(&icp->ic_mutex);
2147c478bd9Sstevel@tonic-gate 				return (ret);
2157c478bd9Sstevel@tonic-gate 			}
2167c478bd9Sstevel@tonic-gate 		}
2177c478bd9Sstevel@tonic-gate 
2187c478bd9Sstevel@tonic-gate 		/* copyout the data */
2197c478bd9Sstevel@tonic-gate 		ret = av1394_ir_copyout(icp, uiop, &empty_cnt);
2207c478bd9Sstevel@tonic-gate 
2217c478bd9Sstevel@tonic-gate 		/* return freed frames to the pool */
2227c478bd9Sstevel@tonic-gate 		if (empty_cnt > 0) {
2237c478bd9Sstevel@tonic-gate 			av1394_ir_zero_pkts(icp, irp->ir_read_idx, empty_cnt);
2247c478bd9Sstevel@tonic-gate 			ret = av1394_ir_add_frames(icp, irp->ir_read_idx,
2257c478bd9Sstevel@tonic-gate 			    empty_cnt);
2267c478bd9Sstevel@tonic-gate 			irp->ir_read_idx += empty_cnt;
2277c478bd9Sstevel@tonic-gate 			irp->ir_read_idx %= icp->ic_nframes;
2287c478bd9Sstevel@tonic-gate 			irp->ir_read_cnt -= empty_cnt;
2297c478bd9Sstevel@tonic-gate 		}
2307c478bd9Sstevel@tonic-gate 	}
2317c478bd9Sstevel@tonic-gate 	mutex_exit(&icp->ic_mutex);
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 	return (ret);
2347c478bd9Sstevel@tonic-gate }
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate /*
2377c478bd9Sstevel@tonic-gate  *
2387c478bd9Sstevel@tonic-gate  * --- configuration routines
2397c478bd9Sstevel@tonic-gate  *
2407c478bd9Sstevel@tonic-gate  */
2417c478bd9Sstevel@tonic-gate static void
av1394_ir_cleanup(av1394_ic_t * icp,int level)2427c478bd9Sstevel@tonic-gate av1394_ir_cleanup(av1394_ic_t *icp, int level)
2437c478bd9Sstevel@tonic-gate {
2447c478bd9Sstevel@tonic-gate 	av1394_isoch_pool_t *pool = &icp->ic_ir.ir_data_pool;
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 	ASSERT((level > 0) && (level <= AV1394_CLEANUP_LEVEL_MAX));
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 	switch (level) {
2497c478bd9Sstevel@tonic-gate 	default:
2507c478bd9Sstevel@tonic-gate 		av1394_ir_free_isoch_dma(icp);
2517c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
2527c478bd9Sstevel@tonic-gate 	case 3:
2537c478bd9Sstevel@tonic-gate 		av1394_ir_destroy_ixl(icp);
2547c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
2557c478bd9Sstevel@tonic-gate 	case 2:
2567c478bd9Sstevel@tonic-gate 		av1394_ic_dma_cleanup(icp, pool);
2577c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
2587c478bd9Sstevel@tonic-gate 	case 1:
2597c478bd9Sstevel@tonic-gate 		av1394_ic_free_pool(pool);
2607c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
2617c478bd9Sstevel@tonic-gate 	}
2627c478bd9Sstevel@tonic-gate }
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate /*
2657c478bd9Sstevel@tonic-gate  * av1394_ir_build_ixl()
2667c478bd9Sstevel@tonic-gate  *    Build an IXL chain to receive CIP data. The smallest instance of data
2677c478bd9Sstevel@tonic-gate  *    that can be received is a packet, typically 512 bytes. Frames consist
2687c478bd9Sstevel@tonic-gate  *    of a number of packets, typically 250-300. Packet size, frame size and
2697c478bd9Sstevel@tonic-gate  *    number of frames allocated are set by a user process. The received data
2707c478bd9Sstevel@tonic-gate  *    made available to the user process in full frames, hence there an IXL
2717c478bd9Sstevel@tonic-gate  *    callback at the end of each frame. A sequence of IXL commands that
2727c478bd9Sstevel@tonic-gate  *    receives one frame is further referred to as an IXL data block.
2737c478bd9Sstevel@tonic-gate  *
2747c478bd9Sstevel@tonic-gate  *    During normal operation, frames are in a circular list and IXL chain
2757c478bd9Sstevel@tonic-gate  *    does not change. When the user process does not keep up with the
2767c478bd9Sstevel@tonic-gate  *    data flow and there are too few empty frames left, the jump following
2777c478bd9Sstevel@tonic-gate  *    last empty frame is dynamically updated to point to NULL -- otherwise
2787c478bd9Sstevel@tonic-gate  *    the first full frame would be overwritten. When IXL execution reaches
2797c478bd9Sstevel@tonic-gate  *    the nulled jump, it just waits until the driver updates it again or
2807c478bd9Sstevel@tonic-gate  *    stops the transfer. Once a user process frees up enough frames, the
2817c478bd9Sstevel@tonic-gate  *    jump is restored and transfer continues. User process will be able to
2827c478bd9Sstevel@tonic-gate  *    detect dropped packets using continuity conters embedded in the data.
2837c478bd9Sstevel@tonic-gate  *
2847c478bd9Sstevel@tonic-gate  *    Because RECV_BUF buffer size is limited to AV1394_IXL_BUFSZ_MAX, and due
2857c478bd9Sstevel@tonic-gate  *    to isoch pool segmentaion, the number of RECV_BUF commands per IXL data
2867c478bd9Sstevel@tonic-gate  *    block depends on frame size. Also, to simplify calculations, we consider
2877c478bd9Sstevel@tonic-gate  *    a sequence of RECV_BUF commands to consist of two parts: zero or more
2887c478bd9Sstevel@tonic-gate  *    equal-sized RECV_BUF commands followed by one "tail" REC_BUF command,
2897c478bd9Sstevel@tonic-gate  *    whose size may not be equal to others.
2907c478bd9Sstevel@tonic-gate  *
2917c478bd9Sstevel@tonic-gate  *    Schematically the IXL chain looks like this:
2927c478bd9Sstevel@tonic-gate  *
2937c478bd9Sstevel@tonic-gate  *    ...
2947c478bd9Sstevel@tonic-gate  *    LABEL N;
2957c478bd9Sstevel@tonic-gate  *    RECV_BUF(buf)
2967c478bd9Sstevel@tonic-gate  *    ...
2977c478bd9Sstevel@tonic-gate  *    RECV_BUF(tail)
2987c478bd9Sstevel@tonic-gate  *    CALLBACK(frame done);
2997c478bd9Sstevel@tonic-gate  *    JUMP_U(LABEL (N+1)%nframes or NULL);
3007c478bd9Sstevel@tonic-gate  *    ...
3017c478bd9Sstevel@tonic-gate  */
3027c478bd9Sstevel@tonic-gate static int
av1394_ir_build_ixl(av1394_ic_t * icp)3037c478bd9Sstevel@tonic-gate av1394_ir_build_ixl(av1394_ic_t *icp)
3047c478bd9Sstevel@tonic-gate {
3057c478bd9Sstevel@tonic-gate 	av1394_ir_t		*irp = &icp->ic_ir;
3067c478bd9Sstevel@tonic-gate 	av1394_isoch_pool_t	*pool = &irp->ir_data_pool;
30747cd5876SAlan Perry 	int			i;	/* segment index */
30847cd5876SAlan Perry 	int			j;
30947cd5876SAlan Perry 	int			fi;	/* frame index */
31047cd5876SAlan Perry 	int			bi;	/* buffer index */
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 	/* allocate space for IXL data blocks */
3137c478bd9Sstevel@tonic-gate 	irp->ir_ixl_data = kmem_zalloc(icp->ic_nframes *
3147c478bd9Sstevel@tonic-gate 	    sizeof (av1394_ir_ixl_data_t), KM_SLEEP);
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate 	/*
31747cd5876SAlan Perry 	 * We have a bunch of segments, and each is divided into cookies.  We
31847cd5876SAlan Perry 	 * need to cover the segments with RECV_BUFs such that they
31947cd5876SAlan Perry 	 *   - don't span cookies
32047cd5876SAlan Perry 	 *   - don't span frames
32147cd5876SAlan Perry 	 *   - are at most AV1394_IXL_BUFSZ_MAX
32247cd5876SAlan Perry 	 *
32347cd5876SAlan Perry 	 * The straightforward algorithm is to start from the beginning, find
32447cd5876SAlan Perry 	 * the next lowest frame or cookie boundary, and either make a buf for
32547cd5876SAlan Perry 	 * it if it is smaller than AV1394_IXL_BUFSZ_MAX, or make multiple
32647cd5876SAlan Perry 	 * bufs for it as with av1394_ic_ixl_seg_decomp().  And repeat.
3277c478bd9Sstevel@tonic-gate 	 */
3287c478bd9Sstevel@tonic-gate 
32947cd5876SAlan Perry 	irp->ir_ixl_nbufs = 0;
33047cd5876SAlan Perry 	for (i = 0; i < pool->ip_nsegs; ++i) {
33147cd5876SAlan Perry 		av1394_isoch_seg_t *isp = &pool->ip_seg[i];
33247cd5876SAlan Perry 		size_t dummy1, dummy2;
33347cd5876SAlan Perry 
33447cd5876SAlan Perry 		uint_t off = 0;
33547cd5876SAlan Perry 		uint_t end;
33647cd5876SAlan Perry 
33747cd5876SAlan Perry 		uint_t frame_end = icp->ic_framesz;
33847cd5876SAlan Perry 		int ci = 0;
33947cd5876SAlan Perry 		uint_t cookie_end = isp->is_dma_cookie[ci].dmac_size;
34047cd5876SAlan Perry 
34147cd5876SAlan Perry 		for (;;) {
34247cd5876SAlan Perry 			end = min(frame_end, cookie_end);
34347cd5876SAlan Perry 
34447cd5876SAlan Perry 			if (end - off <= AV1394_IXL_BUFSZ_MAX) {
34547cd5876SAlan Perry 				++irp->ir_ixl_nbufs;
3467c478bd9Sstevel@tonic-gate 			} else {
34747cd5876SAlan Perry 				irp->ir_ixl_nbufs += av1394_ic_ixl_seg_decomp(
34847cd5876SAlan Perry 				    end - off, icp->ic_pktsz, &dummy1, &dummy2);
34947cd5876SAlan Perry 				/* count the tail buffer */
35047cd5876SAlan Perry 				++irp->ir_ixl_nbufs;
3517c478bd9Sstevel@tonic-gate 			}
3527c478bd9Sstevel@tonic-gate 
35347cd5876SAlan Perry 			off = end;
35447cd5876SAlan Perry 			if (off >= isp->is_size)
35547cd5876SAlan Perry 				break;
35647cd5876SAlan Perry 
35747cd5876SAlan Perry 			if (off == frame_end)
35847cd5876SAlan Perry 				frame_end += icp->ic_framesz;
35947cd5876SAlan Perry 			if (off == cookie_end) {
36047cd5876SAlan Perry 				++ci;
36147cd5876SAlan Perry 				cookie_end += isp->is_dma_cookie[ci].dmac_size;
36247cd5876SAlan Perry 			}
36347cd5876SAlan Perry 		}
36447cd5876SAlan Perry 	}
36547cd5876SAlan Perry 
3667c478bd9Sstevel@tonic-gate 	irp->ir_ixl_buf = kmem_zalloc(irp->ir_ixl_nbufs *
3677c478bd9Sstevel@tonic-gate 	    sizeof (ixl1394_xfer_buf_t), KM_SLEEP);
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 
37047cd5876SAlan Perry 	fi = 0;
37147cd5876SAlan Perry 	bi = 0;
3727c478bd9Sstevel@tonic-gate 
37347cd5876SAlan Perry 	for (i = 0; i < pool->ip_nsegs; ++i) {
37447cd5876SAlan Perry 		av1394_isoch_seg_t *isp = &pool->ip_seg[i];
3757c478bd9Sstevel@tonic-gate 
37647cd5876SAlan Perry 		uint_t off = 0;		/* offset into segment */
37747cd5876SAlan Perry 		uint_t end;
37847cd5876SAlan Perry 		uint_t coff = 0;	/* offset into cookie */
37947cd5876SAlan Perry 
38047cd5876SAlan Perry 
38147cd5876SAlan Perry 		uint_t frame_end = icp->ic_framesz;
38247cd5876SAlan Perry 		int ci = 0;
38347cd5876SAlan Perry 		uint_t cookie_end = isp->is_dma_cookie[ci].dmac_size;
38447cd5876SAlan Perry 
38547cd5876SAlan Perry 		ixl1394_command_t *nextp;
38647cd5876SAlan Perry 
38747cd5876SAlan Perry 		av1394_ir_ixl_label_init(&irp->ir_ixl_data[fi],
38847cd5876SAlan Perry 		    (ixl1394_command_t *)&irp->ir_ixl_buf[bi]);
38947cd5876SAlan Perry 
39047cd5876SAlan Perry 		for (;;) {
39147cd5876SAlan Perry 			end = min(frame_end, cookie_end);
39247cd5876SAlan Perry 
39347cd5876SAlan Perry 			if (end == frame_end)
39447cd5876SAlan Perry 				nextp = (ixl1394_command_t *)
39547cd5876SAlan Perry 				    &irp->ir_ixl_data[fi].rd_cb;
39647cd5876SAlan Perry 			else
39747cd5876SAlan Perry 				nextp = (ixl1394_command_t *)
39847cd5876SAlan Perry 				    &irp->ir_ixl_buf[bi + 1];
39947cd5876SAlan Perry 
40047cd5876SAlan Perry 			if (end - off <= AV1394_IXL_BUFSZ_MAX) {
40147cd5876SAlan Perry 				av1394_ir_ixl_buf_init(icp,
40247cd5876SAlan Perry 				    &irp->ir_ixl_buf[bi], isp, off,
40347cd5876SAlan Perry 				    isp->is_dma_cookie[ci].dmac_laddress + coff,
40447cd5876SAlan Perry 				    end - off, nextp);
40547cd5876SAlan Perry 				coff += end - off;
40647cd5876SAlan Perry 				off = end;
40747cd5876SAlan Perry 				++bi;
4087c478bd9Sstevel@tonic-gate 			} else {
40947cd5876SAlan Perry 				size_t reg, tail;
41047cd5876SAlan Perry 				uint_t nbufs;
41147cd5876SAlan Perry 
41247cd5876SAlan Perry 				nbufs = av1394_ic_ixl_seg_decomp(end - off,
41347cd5876SAlan Perry 				    icp->ic_pktsz, &reg, &tail);
41447cd5876SAlan Perry 
41547cd5876SAlan Perry 				for (j = 0; j < nbufs; ++j) {
41647cd5876SAlan Perry 					av1394_ir_ixl_buf_init(icp,
41747cd5876SAlan Perry 					    &irp->ir_ixl_buf[bi], isp, off,
41847cd5876SAlan Perry 					    isp->is_dma_cookie[ci].
41947cd5876SAlan Perry 					    dmac_laddress + coff, reg,
42047cd5876SAlan Perry 					    (ixl1394_command_t *)
42147cd5876SAlan Perry 					    &irp->ir_ixl_buf[bi + 1]);
42247cd5876SAlan Perry 					++bi;
42347cd5876SAlan Perry 					off += reg;
42447cd5876SAlan Perry 					coff += reg;
4257c478bd9Sstevel@tonic-gate 				}
4267c478bd9Sstevel@tonic-gate 
42747cd5876SAlan Perry 				av1394_ir_ixl_buf_init(icp,
42847cd5876SAlan Perry 				    &irp->ir_ixl_buf[bi], isp, off,
42947cd5876SAlan Perry 				    isp->is_dma_cookie[ci].dmac_laddress + coff,
43047cd5876SAlan Perry 				    tail, nextp);
43147cd5876SAlan Perry 				++bi;
43247cd5876SAlan Perry 				off += tail;
43347cd5876SAlan Perry 				coff += tail;
4347c478bd9Sstevel@tonic-gate 			}
4357c478bd9Sstevel@tonic-gate 
43647cd5876SAlan Perry 			ASSERT((off == frame_end) || (off == cookie_end));
43747cd5876SAlan Perry 
43847cd5876SAlan Perry 			if (off >= isp->is_size)
43947cd5876SAlan Perry 				break;
44047cd5876SAlan Perry 
44147cd5876SAlan Perry 			if (off == frame_end) {
44247cd5876SAlan Perry 				av1394_ir_ixl_cb_init(icp,
44347cd5876SAlan Perry 				    &irp->ir_ixl_data[fi], fi);
44447cd5876SAlan Perry 				av1394_ir_ixl_jump_init(icp,
44547cd5876SAlan Perry 				    &irp->ir_ixl_data[fi], fi);
44647cd5876SAlan Perry 				++fi;
44747cd5876SAlan Perry 				frame_end += icp->ic_framesz;
44847cd5876SAlan Perry 				av1394_ir_ixl_label_init(&irp->ir_ixl_data[fi],
44947cd5876SAlan Perry 				    (ixl1394_command_t *)&irp->ir_ixl_buf[bi]);
45047cd5876SAlan Perry 			}
45147cd5876SAlan Perry 
45247cd5876SAlan Perry 			if (off == cookie_end) {
45347cd5876SAlan Perry 				++ci;
45447cd5876SAlan Perry 				cookie_end += isp->is_dma_cookie[ci].dmac_size;
45547cd5876SAlan Perry 				coff = 0;
45647cd5876SAlan Perry 			}
45747cd5876SAlan Perry 		}
45847cd5876SAlan Perry 
45947cd5876SAlan Perry 		av1394_ir_ixl_cb_init(icp, &irp->ir_ixl_data[fi], fi);
46047cd5876SAlan Perry 		av1394_ir_ixl_jump_init(icp, &irp->ir_ixl_data[fi], fi);
46147cd5876SAlan Perry 		++fi;
46247cd5876SAlan Perry 	}
46347cd5876SAlan Perry 
46447cd5876SAlan Perry 	ASSERT(fi == icp->ic_nframes);
46547cd5876SAlan Perry 	ASSERT(bi == irp->ir_ixl_nbufs);
46647cd5876SAlan Perry 
4677c478bd9Sstevel@tonic-gate 	irp->ir_ixlp = (ixl1394_command_t *)irp->ir_ixl_data;
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 	if (av1394_ir_dump_ixl) {
4707c478bd9Sstevel@tonic-gate 		av1394_ic_ixl_dump(irp->ir_ixlp);
4717c478bd9Sstevel@tonic-gate 	}
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
4747c478bd9Sstevel@tonic-gate }
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate static void
av1394_ir_ixl_label_init(av1394_ir_ixl_data_t * dp,ixl1394_command_t * nextp)4777c478bd9Sstevel@tonic-gate av1394_ir_ixl_label_init(av1394_ir_ixl_data_t *dp, ixl1394_command_t *nextp)
4787c478bd9Sstevel@tonic-gate {
4797c478bd9Sstevel@tonic-gate 	dp->rd_label.ixl_opcode = IXL1394_OP_LABEL;
4807c478bd9Sstevel@tonic-gate 	dp->rd_label.next_ixlp	= nextp;
4817c478bd9Sstevel@tonic-gate }
4827c478bd9Sstevel@tonic-gate 
4837c478bd9Sstevel@tonic-gate static void
av1394_ir_ixl_buf_init(av1394_ic_t * icp,ixl1394_xfer_buf_t * buf,av1394_isoch_seg_t * isp,off_t offset,uint64_t addr,uint16_t size,ixl1394_command_t * nextp)4847c478bd9Sstevel@tonic-gate av1394_ir_ixl_buf_init(av1394_ic_t *icp, ixl1394_xfer_buf_t *buf,
48547cd5876SAlan Perry 	av1394_isoch_seg_t *isp, off_t offset, uint64_t addr, uint16_t size,
4867c478bd9Sstevel@tonic-gate 	ixl1394_command_t *nextp)
4877c478bd9Sstevel@tonic-gate {
4887c478bd9Sstevel@tonic-gate 	buf->ixl_opcode = IXL1394_OP_RECV_BUF;
4897c478bd9Sstevel@tonic-gate 	buf->size = size;
4907c478bd9Sstevel@tonic-gate 	buf->pkt_size = icp->ic_pktsz;
49147cd5876SAlan Perry 	buf->ixl_buf._dmac_ll = addr;
4927c478bd9Sstevel@tonic-gate 	buf->mem_bufp = isp->is_kaddr + offset;
4937c478bd9Sstevel@tonic-gate 	buf->next_ixlp = nextp;
4947c478bd9Sstevel@tonic-gate }
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4977c478bd9Sstevel@tonic-gate static void
av1394_ir_ixl_cb_init(av1394_ic_t * icp,av1394_ir_ixl_data_t * dp,int i)4987c478bd9Sstevel@tonic-gate av1394_ir_ixl_cb_init(av1394_ic_t *icp, av1394_ir_ixl_data_t *dp, int i)
4997c478bd9Sstevel@tonic-gate {
5007c478bd9Sstevel@tonic-gate 	dp->rd_cb.ixl_opcode = IXL1394_OP_CALLBACK;
5017c478bd9Sstevel@tonic-gate 	dp->rd_cb.callback = av1394_ir_ixl_frame_cb;
5027c478bd9Sstevel@tonic-gate 	dp->rd_cb.callback_arg = (void *)(intptr_t)i;
5037c478bd9Sstevel@tonic-gate 	dp->rd_cb.next_ixlp = (ixl1394_command_t *)&dp->rd_jump;
5047c478bd9Sstevel@tonic-gate }
5057c478bd9Sstevel@tonic-gate 
5067c478bd9Sstevel@tonic-gate static void
av1394_ir_ixl_jump_init(av1394_ic_t * icp,av1394_ir_ixl_data_t * dp,int i)5077c478bd9Sstevel@tonic-gate av1394_ir_ixl_jump_init(av1394_ic_t *icp, av1394_ir_ixl_data_t *dp, int i)
5087c478bd9Sstevel@tonic-gate {
5097c478bd9Sstevel@tonic-gate 	av1394_ir_t	*irp = &icp->ic_ir;
5107c478bd9Sstevel@tonic-gate 	int		next_idx;
5117c478bd9Sstevel@tonic-gate 	ixl1394_command_t *jump_cmd;
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 	next_idx = (i + 1) % icp->ic_nframes;
5147c478bd9Sstevel@tonic-gate 	jump_cmd = (ixl1394_command_t *)&irp->ir_ixl_data[next_idx];
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 	dp->rd_jump.ixl_opcode	= IXL1394_OP_JUMP_U;
5177c478bd9Sstevel@tonic-gate 	dp->rd_jump.label = jump_cmd;
5187c478bd9Sstevel@tonic-gate 	dp->rd_jump.next_ixlp = (next_idx != 0) ? jump_cmd : NULL;
5197c478bd9Sstevel@tonic-gate }
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate static void
av1394_ir_destroy_ixl(av1394_ic_t * icp)5227c478bd9Sstevel@tonic-gate av1394_ir_destroy_ixl(av1394_ic_t *icp)
5237c478bd9Sstevel@tonic-gate {
5247c478bd9Sstevel@tonic-gate 	av1394_ir_t		*irp = &icp->ic_ir;
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate 	mutex_enter(&icp->ic_mutex);
5277c478bd9Sstevel@tonic-gate 	kmem_free(irp->ir_ixl_buf,
5287c478bd9Sstevel@tonic-gate 	    irp->ir_ixl_nbufs * sizeof (ixl1394_xfer_buf_t));
5297c478bd9Sstevel@tonic-gate 	kmem_free(irp->ir_ixl_data,
5307c478bd9Sstevel@tonic-gate 	    icp->ic_nframes * sizeof (av1394_ir_ixl_data_t));
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate 	irp->ir_ixlp = NULL;
5337c478bd9Sstevel@tonic-gate 	irp->ir_ixl_buf = NULL;
5347c478bd9Sstevel@tonic-gate 	irp->ir_ixl_data = NULL;
5357c478bd9Sstevel@tonic-gate 	mutex_exit(&icp->ic_mutex);
5367c478bd9Sstevel@tonic-gate }
5377c478bd9Sstevel@tonic-gate 
5387c478bd9Sstevel@tonic-gate static int
av1394_ir_alloc_isoch_dma(av1394_ic_t * icp)5397c478bd9Sstevel@tonic-gate av1394_ir_alloc_isoch_dma(av1394_ic_t *icp)
5407c478bd9Sstevel@tonic-gate {
5417c478bd9Sstevel@tonic-gate 	av1394_inst_t		*avp = icp->ic_avp;
5427c478bd9Sstevel@tonic-gate 	av1394_ir_t		*irp = &icp->ic_ir;
5437c478bd9Sstevel@tonic-gate 	id1394_isoch_dmainfo_t	di;
5447c478bd9Sstevel@tonic-gate 	int			result;
5457c478bd9Sstevel@tonic-gate 	int			ret;
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate 	di.ixlp = irp->ir_ixlp;
5487c478bd9Sstevel@tonic-gate 	di.channel_num = icp->ic_num;
5497c478bd9Sstevel@tonic-gate 	di.global_callback_arg = icp;
5507c478bd9Sstevel@tonic-gate 	di.idma_options = ID1394_LISTEN_PKT_MODE;
5517c478bd9Sstevel@tonic-gate 	di.isoch_dma_stopped = av1394_ir_dma_stopped_cb;
5527c478bd9Sstevel@tonic-gate 	di.idma_evt_arg = icp;
5537c478bd9Sstevel@tonic-gate 
554*2570281cSToomas Soome 	ret = t1394_alloc_isoch_dma(avp->av_t1394_hdl, &di, 0,
555*2570281cSToomas Soome 	    &icp->ic_isoch_hdl, &result);
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 	return (ret);
5587c478bd9Sstevel@tonic-gate }
5597c478bd9Sstevel@tonic-gate 
5607c478bd9Sstevel@tonic-gate static void
av1394_ir_free_isoch_dma(av1394_ic_t * icp)5617c478bd9Sstevel@tonic-gate av1394_ir_free_isoch_dma(av1394_ic_t *icp)
5627c478bd9Sstevel@tonic-gate {
5637c478bd9Sstevel@tonic-gate 	av1394_inst_t		*avp = icp->ic_avp;
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 	t1394_free_isoch_dma(avp->av_t1394_hdl, 0, &icp->ic_isoch_hdl);
5667c478bd9Sstevel@tonic-gate }
5677c478bd9Sstevel@tonic-gate 
5687c478bd9Sstevel@tonic-gate static void
av1394_ir_dma_sync_frames(av1394_ic_t * icp,int idx,int cnt)5697c478bd9Sstevel@tonic-gate av1394_ir_dma_sync_frames(av1394_ic_t *icp, int idx, int cnt)
5707c478bd9Sstevel@tonic-gate {
5717c478bd9Sstevel@tonic-gate 	av1394_ic_dma_sync_frames(icp, idx, cnt,
5727c478bd9Sstevel@tonic-gate 	    &icp->ic_ir.ir_data_pool, DDI_DMA_SYNC_FORCPU);
5737c478bd9Sstevel@tonic-gate }
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate /*
5767c478bd9Sstevel@tonic-gate  *
5777c478bd9Sstevel@tonic-gate  * --- callbacks
5787c478bd9Sstevel@tonic-gate  *
5797c478bd9Sstevel@tonic-gate  */
5807c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5817c478bd9Sstevel@tonic-gate static void
av1394_ir_ixl_frame_cb(opaque_t arg,struct ixl1394_callback * cb)5827c478bd9Sstevel@tonic-gate av1394_ir_ixl_frame_cb(opaque_t arg, struct ixl1394_callback *cb)
5837c478bd9Sstevel@tonic-gate {
5847c478bd9Sstevel@tonic-gate 	av1394_ic_t	*icp = arg;
5857c478bd9Sstevel@tonic-gate 	av1394_isoch_t	*ip = &icp->ic_avp->av_i;
5867c478bd9Sstevel@tonic-gate 	av1394_ir_t	*irp = &icp->ic_ir;
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 	mutex_enter(&ip->i_mutex);
5897c478bd9Sstevel@tonic-gate 	mutex_enter(&icp->ic_mutex);
5907c478bd9Sstevel@tonic-gate 	if (irp->ir_nfull < icp->ic_nframes) {
5917c478bd9Sstevel@tonic-gate 		irp->ir_nfull++;
5927c478bd9Sstevel@tonic-gate 		irp->ir_nempty--;
5937c478bd9Sstevel@tonic-gate 		cv_broadcast(&icp->ic_xfer_cv);
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate 		/*
5967c478bd9Sstevel@tonic-gate 		 * signal the overflow condition early, so we get enough
5977c478bd9Sstevel@tonic-gate 		 * time to handle it before old data is overwritten
5987c478bd9Sstevel@tonic-gate 		 */
5997c478bd9Sstevel@tonic-gate 		if (irp->ir_nfull >= irp->ir_hiwat) {
6007c478bd9Sstevel@tonic-gate 			av1394_ic_trigger_softintr(icp, icp->ic_num,
6017c478bd9Sstevel@tonic-gate 			    AV1394_PREQ_IR_OVERFLOW);
6027c478bd9Sstevel@tonic-gate 		}
6037c478bd9Sstevel@tonic-gate 	}
6047c478bd9Sstevel@tonic-gate 	mutex_exit(&icp->ic_mutex);
6057c478bd9Sstevel@tonic-gate 	mutex_exit(&ip->i_mutex);
6067c478bd9Sstevel@tonic-gate }
6077c478bd9Sstevel@tonic-gate 
6087c478bd9Sstevel@tonic-gate /*
6097c478bd9Sstevel@tonic-gate  * received data overflow
6107c478bd9Sstevel@tonic-gate  */
6117c478bd9Sstevel@tonic-gate void
av1394_ir_overflow(av1394_ic_t * icp)6127c478bd9Sstevel@tonic-gate av1394_ir_overflow(av1394_ic_t *icp)
6137c478bd9Sstevel@tonic-gate {
6147c478bd9Sstevel@tonic-gate 	av1394_inst_t	*avp = icp->ic_avp;
6157c478bd9Sstevel@tonic-gate 	av1394_ir_t	*irp = &icp->ic_ir;
6167c478bd9Sstevel@tonic-gate 	int		idx;
6177c478bd9Sstevel@tonic-gate 	ixl1394_jump_t	*old_jmp;
6187c478bd9Sstevel@tonic-gate 	ixl1394_jump_t	new_jmp;
6197c478bd9Sstevel@tonic-gate 	id1394_isoch_dma_updateinfo_t update_info;
6207c478bd9Sstevel@tonic-gate 	int		err;
6217c478bd9Sstevel@tonic-gate 	int		result;
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	/*
6247c478bd9Sstevel@tonic-gate 	 * in the circular IXL chain overflow means overwriting the least
6257c478bd9Sstevel@tonic-gate 	 * recent data. to avoid that, we suspend the transfer by NULL'ing
6267c478bd9Sstevel@tonic-gate 	 * the last IXL block until the user process frees up some frames.
6277c478bd9Sstevel@tonic-gate 	 */
6287c478bd9Sstevel@tonic-gate 	idx = irp->ir_last_empty;
6297c478bd9Sstevel@tonic-gate 
6307c478bd9Sstevel@tonic-gate 	old_jmp = &irp->ir_ixl_data[idx].rd_jump;
6317c478bd9Sstevel@tonic-gate 
6327c478bd9Sstevel@tonic-gate 	new_jmp.ixl_opcode = IXL1394_OP_JUMP_U;
6337c478bd9Sstevel@tonic-gate 	new_jmp.label = NULL;
6347c478bd9Sstevel@tonic-gate 	new_jmp.next_ixlp = NULL;
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate 	update_info.orig_ixlp = (ixl1394_command_t *)old_jmp;
6377c478bd9Sstevel@tonic-gate 	update_info.temp_ixlp = (ixl1394_command_t *)&new_jmp;
6387c478bd9Sstevel@tonic-gate 	update_info.ixl_count = 1;
6397c478bd9Sstevel@tonic-gate 
6407c478bd9Sstevel@tonic-gate 	mutex_exit(&icp->ic_mutex);
6417c478bd9Sstevel@tonic-gate 	err = t1394_update_isoch_dma(avp->av_t1394_hdl, icp->ic_isoch_hdl,
6427c478bd9Sstevel@tonic-gate 	    &update_info, 0, &result);
6437c478bd9Sstevel@tonic-gate 	mutex_enter(&icp->ic_mutex);
6447c478bd9Sstevel@tonic-gate 
6457c478bd9Sstevel@tonic-gate 	if (err == DDI_SUCCESS) {
6467c478bd9Sstevel@tonic-gate 		irp->ir_overflow_idx = idx;
6477c478bd9Sstevel@tonic-gate 		icp->ic_state = AV1394_IC_SUSPENDED;
6487c478bd9Sstevel@tonic-gate 	}
6497c478bd9Sstevel@tonic-gate }
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate /*
6527c478bd9Sstevel@tonic-gate  * restore from overflow condition
6537c478bd9Sstevel@tonic-gate  */
6547c478bd9Sstevel@tonic-gate static void
av1394_ir_overflow_resume(av1394_ic_t * icp)6557c478bd9Sstevel@tonic-gate av1394_ir_overflow_resume(av1394_ic_t *icp)
6567c478bd9Sstevel@tonic-gate {
6577c478bd9Sstevel@tonic-gate 	av1394_inst_t	*avp = icp->ic_avp;
6587c478bd9Sstevel@tonic-gate 	av1394_ir_t	*irp = &icp->ic_ir;
6597c478bd9Sstevel@tonic-gate 	int		idx, next_idx;
6607c478bd9Sstevel@tonic-gate 	ixl1394_jump_t	*old_jmp;
6617c478bd9Sstevel@tonic-gate 	ixl1394_jump_t	new_jmp;
6627c478bd9Sstevel@tonic-gate 	id1394_isoch_dma_updateinfo_t update_info;
6637c478bd9Sstevel@tonic-gate 	int		err;
6647c478bd9Sstevel@tonic-gate 	int		result;
6657c478bd9Sstevel@tonic-gate 
6667c478bd9Sstevel@tonic-gate 	/*
6677c478bd9Sstevel@tonic-gate 	 * restore the jump command we NULL'ed in av1394_ir_overflow()
6687c478bd9Sstevel@tonic-gate 	 */
6697c478bd9Sstevel@tonic-gate 	idx = irp->ir_overflow_idx;
6707c478bd9Sstevel@tonic-gate 	next_idx = (idx + 1) % icp->ic_nframes;
6717c478bd9Sstevel@tonic-gate 
6727c478bd9Sstevel@tonic-gate 	old_jmp = &irp->ir_ixl_data[idx].rd_jump;
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate 	new_jmp.ixl_opcode = IXL1394_OP_JUMP_U;
6757c478bd9Sstevel@tonic-gate 	new_jmp.label = (ixl1394_command_t *)&irp->ir_ixl_data[next_idx];
6767c478bd9Sstevel@tonic-gate 	new_jmp.next_ixlp = NULL;
6777c478bd9Sstevel@tonic-gate 
6787c478bd9Sstevel@tonic-gate 	update_info.orig_ixlp = (ixl1394_command_t *)old_jmp;
6797c478bd9Sstevel@tonic-gate 	update_info.temp_ixlp = (ixl1394_command_t *)&new_jmp;
6807c478bd9Sstevel@tonic-gate 	update_info.ixl_count = 1;
6817c478bd9Sstevel@tonic-gate 
6827c478bd9Sstevel@tonic-gate 	mutex_exit(&icp->ic_mutex);
6837c478bd9Sstevel@tonic-gate 	err = t1394_update_isoch_dma(avp->av_t1394_hdl,
6847c478bd9Sstevel@tonic-gate 	    icp->ic_isoch_hdl, &update_info, 0, &result);
6857c478bd9Sstevel@tonic-gate 	mutex_enter(&icp->ic_mutex);
6867c478bd9Sstevel@tonic-gate 
6877c478bd9Sstevel@tonic-gate 	if (err == DDI_SUCCESS) {
6887c478bd9Sstevel@tonic-gate 		icp->ic_state = AV1394_IC_DMA;
6897c478bd9Sstevel@tonic-gate 	}
6907c478bd9Sstevel@tonic-gate }
6917c478bd9Sstevel@tonic-gate 
6927c478bd9Sstevel@tonic-gate /*ARGSUSED*/
6937c478bd9Sstevel@tonic-gate static void
av1394_ir_dma_stopped_cb(t1394_isoch_dma_handle_t t1394_idma_hdl,opaque_t idma_evt_arg,id1394_isoch_dma_stopped_t status)6947c478bd9Sstevel@tonic-gate av1394_ir_dma_stopped_cb(t1394_isoch_dma_handle_t t1394_idma_hdl,
6957c478bd9Sstevel@tonic-gate 	opaque_t idma_evt_arg, id1394_isoch_dma_stopped_t status)
6967c478bd9Sstevel@tonic-gate {
6977c478bd9Sstevel@tonic-gate 	av1394_ic_t	*icp = idma_evt_arg;
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate 	mutex_enter(&icp->ic_mutex);
7007c478bd9Sstevel@tonic-gate 	icp->ic_state = AV1394_IC_IDLE;
7017c478bd9Sstevel@tonic-gate 	mutex_exit(&icp->ic_mutex);
7027c478bd9Sstevel@tonic-gate }
7037c478bd9Sstevel@tonic-gate 
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate /*
7067c478bd9Sstevel@tonic-gate  *
7077c478bd9Sstevel@tonic-gate  * --- data transfer routines
7087c478bd9Sstevel@tonic-gate  *
7097c478bd9Sstevel@tonic-gate  * av1394_ir_add_frames()
7107c478bd9Sstevel@tonic-gate  *    Add empty frames to the pool.
7117c478bd9Sstevel@tonic-gate  */
7127c478bd9Sstevel@tonic-gate static int
av1394_ir_add_frames(av1394_ic_t * icp,int idx,int cnt)7137c478bd9Sstevel@tonic-gate av1394_ir_add_frames(av1394_ic_t *icp, int idx, int cnt)
7147c478bd9Sstevel@tonic-gate {
7157c478bd9Sstevel@tonic-gate 	av1394_ir_t	*irp = &icp->ic_ir;
7167c478bd9Sstevel@tonic-gate 
7177c478bd9Sstevel@tonic-gate 	/* can only add to the tail */
7187c478bd9Sstevel@tonic-gate 	if (idx != ((irp->ir_last_empty + 1) % icp->ic_nframes)) {
7197c478bd9Sstevel@tonic-gate 		return (EINVAL);
7207c478bd9Sstevel@tonic-gate 	}
7217c478bd9Sstevel@tonic-gate 
7227c478bd9Sstevel@tonic-gate 	/* turn full frames into empty ones */
7237c478bd9Sstevel@tonic-gate 	irp->ir_nfull -= cnt;
7247c478bd9Sstevel@tonic-gate 	irp->ir_first_full = (irp->ir_first_full + cnt) % icp->ic_nframes;
7257c478bd9Sstevel@tonic-gate 	irp->ir_nempty += cnt;
7267c478bd9Sstevel@tonic-gate 	irp->ir_last_empty = (irp->ir_last_empty + cnt) % icp->ic_nframes;
7277c478bd9Sstevel@tonic-gate 	ASSERT((irp->ir_nfull >= 0) && (irp->ir_nempty <= icp->ic_nframes));
7287c478bd9Sstevel@tonic-gate 
7297c478bd9Sstevel@tonic-gate 	/* if suspended due to overflow, check if iwe can resume */
7307c478bd9Sstevel@tonic-gate 	if ((icp->ic_state == AV1394_IC_SUSPENDED) &&
7317c478bd9Sstevel@tonic-gate 	    (irp->ir_nempty >= irp->ir_lowat)) {
7327c478bd9Sstevel@tonic-gate 		av1394_ir_overflow_resume(icp);
7337c478bd9Sstevel@tonic-gate 	}
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate 	return (0);
7367c478bd9Sstevel@tonic-gate }
7377c478bd9Sstevel@tonic-gate 
7387c478bd9Sstevel@tonic-gate static int
av1394_ir_wait_frames(av1394_ic_t * icp,int * idx,int * cnt)7397c478bd9Sstevel@tonic-gate av1394_ir_wait_frames(av1394_ic_t *icp, int *idx, int *cnt)
7407c478bd9Sstevel@tonic-gate {
7417c478bd9Sstevel@tonic-gate 	av1394_ir_t	*irp = &icp->ic_ir;
7427c478bd9Sstevel@tonic-gate 	int		ret = 0;
7437c478bd9Sstevel@tonic-gate 
7447c478bd9Sstevel@tonic-gate 	while (irp->ir_nfull == 0) {
7457c478bd9Sstevel@tonic-gate 		if (cv_wait_sig(&icp->ic_xfer_cv, &icp->ic_mutex) <= 0) {
7467c478bd9Sstevel@tonic-gate 			ret = EINTR;
7477c478bd9Sstevel@tonic-gate 			break;
7487c478bd9Sstevel@tonic-gate 		}
7497c478bd9Sstevel@tonic-gate 	}
7507c478bd9Sstevel@tonic-gate 	if (irp->ir_nfull > 0) {
7517c478bd9Sstevel@tonic-gate 		*idx = irp->ir_first_full;
7527c478bd9Sstevel@tonic-gate 		*cnt = irp->ir_nfull;
7537c478bd9Sstevel@tonic-gate 		av1394_ir_dma_sync_frames(icp, *idx, *cnt);
7547c478bd9Sstevel@tonic-gate 		ret = 0;
7557c478bd9Sstevel@tonic-gate 	}
7567c478bd9Sstevel@tonic-gate 	return (ret);
7577c478bd9Sstevel@tonic-gate }
7587c478bd9Sstevel@tonic-gate 
7597c478bd9Sstevel@tonic-gate /*
7607c478bd9Sstevel@tonic-gate  * copyout the data, adjust to data format and remove empty CIPs if possible
7617c478bd9Sstevel@tonic-gate  */
7627c478bd9Sstevel@tonic-gate static int
av1394_ir_copyout(av1394_ic_t * icp,struct uio * uiop,int * empty_cnt)7637c478bd9Sstevel@tonic-gate av1394_ir_copyout(av1394_ic_t *icp, struct uio *uiop, int *empty_cnt)
7647c478bd9Sstevel@tonic-gate {
7657c478bd9Sstevel@tonic-gate 	av1394_ir_t	*irp = &icp->ic_ir;
7667c478bd9Sstevel@tonic-gate 	av1394_isoch_seg_t *seg = irp->ir_data_pool.ip_seg;
7677c478bd9Sstevel@tonic-gate 	int		idx = irp->ir_read_idx;
7687c478bd9Sstevel@tonic-gate 	int		cnt = irp->ir_read_cnt;
7697c478bd9Sstevel@tonic-gate 	int		pktsz = icp->ic_pktsz;
7707c478bd9Sstevel@tonic-gate 	int		bs;		/* data block size */
7717c478bd9Sstevel@tonic-gate 	caddr_t		kaddr_begin, kaddr;
7727c478bd9Sstevel@tonic-gate 	int		pkt_off;	/* offset into current packet */
7737c478bd9Sstevel@tonic-gate 	int		len;
7747c478bd9Sstevel@tonic-gate 	int		frame_resid;	/* bytes left in the current frame */
7757c478bd9Sstevel@tonic-gate 	int		ret = 0;
7767c478bd9Sstevel@tonic-gate 
7777c478bd9Sstevel@tonic-gate 	*empty_cnt = 0;
7787c478bd9Sstevel@tonic-gate 
7797c478bd9Sstevel@tonic-gate 	/* DBS -> block size */
7807c478bd9Sstevel@tonic-gate 	bs = *(uchar_t *)(seg[idx].is_kaddr + 1) * 4 + AV1394_CIPSZ;
7817c478bd9Sstevel@tonic-gate 	if ((bs > pktsz) || (bs < AV1394_CIPSZ + 8)) {
7827c478bd9Sstevel@tonic-gate 		bs = pktsz;
7837c478bd9Sstevel@tonic-gate 	}
7847c478bd9Sstevel@tonic-gate 
7857c478bd9Sstevel@tonic-gate 	while ((cnt > 0) && (uiop->uio_resid > 0) && (ret == 0)) {
7867c478bd9Sstevel@tonic-gate 		kaddr = kaddr_begin = seg[idx].is_kaddr + irp->ir_read_off;
7877c478bd9Sstevel@tonic-gate 		frame_resid = icp->ic_framesz - irp->ir_read_off;
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate 		mutex_exit(&icp->ic_mutex);
7907c478bd9Sstevel@tonic-gate 		/* copyout data blocks, skipping empty CIPs */
7917c478bd9Sstevel@tonic-gate 		while ((uiop->uio_resid > 0) && (frame_resid > 0)) {
7927c478bd9Sstevel@tonic-gate 			pkt_off = (uintptr_t)kaddr % pktsz;
7937c478bd9Sstevel@tonic-gate 			/*
7947c478bd9Sstevel@tonic-gate 			 * a quadlet following CIP header can't be zero
7957c478bd9Sstevel@tonic-gate 			 * unless in an empty packet
7967c478bd9Sstevel@tonic-gate 			 */
7977c478bd9Sstevel@tonic-gate 			if ((pkt_off == 0) &&
7987c478bd9Sstevel@tonic-gate 			    (*(uint32_t *)(kaddr + AV1394_CIPSZ) == 0)) {
7997c478bd9Sstevel@tonic-gate 				kaddr += pktsz;
8007c478bd9Sstevel@tonic-gate 				frame_resid -= pktsz;
8017c478bd9Sstevel@tonic-gate 				continue;
8027c478bd9Sstevel@tonic-gate 			}
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate 			len = bs - pkt_off;
8057c478bd9Sstevel@tonic-gate 			if (len > uiop->uio_resid) {
8067c478bd9Sstevel@tonic-gate 				len = uiop->uio_resid;
8077c478bd9Sstevel@tonic-gate 			}
8087c478bd9Sstevel@tonic-gate 			if (len > frame_resid) {
8097c478bd9Sstevel@tonic-gate 				len = frame_resid;
8107c478bd9Sstevel@tonic-gate 			}
8117c478bd9Sstevel@tonic-gate 			if ((ret = uiomove(kaddr, len, UIO_READ, uiop)) != 0) {
8127c478bd9Sstevel@tonic-gate 				break;
8137c478bd9Sstevel@tonic-gate 			}
8147c478bd9Sstevel@tonic-gate 
8157c478bd9Sstevel@tonic-gate 			if (pkt_off + len == bs) {
8167c478bd9Sstevel@tonic-gate 				kaddr += pktsz - pkt_off;
8177c478bd9Sstevel@tonic-gate 				frame_resid -= pktsz - pkt_off;
8187c478bd9Sstevel@tonic-gate 			} else {
8197c478bd9Sstevel@tonic-gate 				kaddr += len;
8207c478bd9Sstevel@tonic-gate 				frame_resid -= len;
8217c478bd9Sstevel@tonic-gate 			}
8227c478bd9Sstevel@tonic-gate 		}
8237c478bd9Sstevel@tonic-gate 		mutex_enter(&icp->ic_mutex);
8247c478bd9Sstevel@tonic-gate 
8257c478bd9Sstevel@tonic-gate 		if (frame_resid > 0) {
8267c478bd9Sstevel@tonic-gate 			irp->ir_read_off = kaddr - kaddr_begin;
8277c478bd9Sstevel@tonic-gate 		} else {
8287c478bd9Sstevel@tonic-gate 			irp->ir_read_off = 0;
8297c478bd9Sstevel@tonic-gate 			idx = (idx + 1) % icp->ic_nframes;
8307c478bd9Sstevel@tonic-gate 			cnt--;
8317c478bd9Sstevel@tonic-gate 			(*empty_cnt)++;
8327c478bd9Sstevel@tonic-gate 		}
8337c478bd9Sstevel@tonic-gate 	}
8347c478bd9Sstevel@tonic-gate 
8357c478bd9Sstevel@tonic-gate 	return (ret);
8367c478bd9Sstevel@tonic-gate }
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate /*
8397c478bd9Sstevel@tonic-gate  * zero a quadlet in each packet so we can recognize empty CIPs
8407c478bd9Sstevel@tonic-gate  */
8417c478bd9Sstevel@tonic-gate static void
av1394_ir_zero_pkts(av1394_ic_t * icp,int idx,int cnt)8427c478bd9Sstevel@tonic-gate av1394_ir_zero_pkts(av1394_ic_t *icp, int idx, int cnt)
8437c478bd9Sstevel@tonic-gate {
8447c478bd9Sstevel@tonic-gate 	av1394_ir_t	*irp = &icp->ic_ir;
8457c478bd9Sstevel@tonic-gate 	av1394_isoch_seg_t *seg = irp->ir_data_pool.ip_seg;
8467c478bd9Sstevel@tonic-gate 	caddr_t		kaddr, kaddr_end;
8477c478bd9Sstevel@tonic-gate 	int		pktsz = icp->ic_pktsz;
8487c478bd9Sstevel@tonic-gate 	int		i;
8497c478bd9Sstevel@tonic-gate 
8507c478bd9Sstevel@tonic-gate 	for (i = cnt; i > 0; i--) {
8517c478bd9Sstevel@tonic-gate 		kaddr = seg[idx].is_kaddr + AV1394_CIPSZ;
8527c478bd9Sstevel@tonic-gate 		kaddr_end = seg[idx].is_kaddr + icp->ic_framesz;
8537c478bd9Sstevel@tonic-gate 		do {
8547c478bd9Sstevel@tonic-gate 			*(uint32_t *)kaddr = 0;
8557c478bd9Sstevel@tonic-gate 			kaddr += pktsz;
8567c478bd9Sstevel@tonic-gate 		} while (kaddr < kaddr_end);
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate 		idx = (idx + 1) % icp->ic_nframes;
8597c478bd9Sstevel@tonic-gate 	}
8607c478bd9Sstevel@tonic-gate }
861