1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * routines common to isoch receive and isoch transmit
28 */
29#include <sys/stat.h>
30#include <sys/systm.h>
31#include <sys/ddi.h>
32#include <sys/sunddi.h>
33#include <sys/bitmap.h>
34#include <sys/av/iec61883.h>
35#include <sys/1394/targets/av1394/av1394_impl.h>
36
37/* configuration routines */
38static void	av1394_ic_cleanup(av1394_ic_t *icp, int level);
39static int	av1394_ic_validate_init_params(iec61883_isoch_init_t *ii);
40static void	av1394_ic_set_params(av1394_inst_t *avp,
41		iec61883_isoch_init_t *ii, av1394_ic_t *icp, int num);
42static int	av1394_ic_alloc_channel(av1394_ic_t *icp, uint64_t mask, int *);
43static void	av1394_ic_free_channel(av1394_ic_t *icp);
44
45/* callbacks */
46static void	av1394_ic_rsrc_fail(t1394_isoch_single_handle_t t1394_sii_hdl,
47		opaque_t arg, t1394_isoch_rsrc_error_t fail_args);
48
49uint64_t	av1394_ic_bitreverse(uint64_t);
50boolean_t	av1394_ic_onebit(uint64_t);
51
52#define	AV1394_TNF_ENTER(func)	\
53	TNF_PROBE_0_DEBUG(func##_enter, AV1394_TNF_ISOCH_STACK, "");
54
55#define	AV1394_TNF_EXIT(func)	\
56	TNF_PROBE_0_DEBUG(func##_exit, AV1394_TNF_ISOCH_STACK, "");
57
58/* tunables */
59extern int av1394_rate_n_dv_ntsc;
60extern int av1394_rate_d_dv_ntsc;
61extern int av1394_rate_n_dv_pal;
62extern int av1394_rate_d_dv_pal;
63
64/*ARGSUSED*/
65int
66av1394_ic_close(av1394_inst_t *avp, int flags)
67{
68	av1394_isoch_t	*ip = &avp->av_i;
69	av1394_ic_t	*icp;
70	int		i;
71
72	AV1394_TNF_ENTER(av1394_ic_close);
73
74	/* cleanup channels in case application didn't */
75	for (i = 0; i < NELEM(ip->i_ic); i++) {
76		icp = ip->i_ic[i];
77		if (icp != NULL) {
78			(void) av1394_ic_stop(icp);
79			av1394_ic_fini(icp);
80		}
81	}
82
83	AV1394_TNF_EXIT(av1394_ic_close);
84	return (0);
85}
86
87/*
88 * av1394_ic_init()
89 *    Channel allocation and initialization.
90 */
91int
92av1394_ic_init(av1394_inst_t *avp, iec61883_isoch_init_t *ii,
93		av1394_ic_t **icpp)
94{
95	av1394_isoch_t		*ip = &avp->av_i;
96	av1394_ic_t		*icp = NULL;
97	int			num;
98	av1394_isoch_pool_t	*pool;
99	uint64_t		mask;	/* channel mask */
100	int			ret;
101	ddi_iblock_cookie_t	ibc = avp->av_attachinfo.iblock_cookie;
102
103	AV1394_TNF_ENTER(av1394_ic_init);
104
105	ii->ii_frame_rcnt = 0;
106	ii->ii_rchannel = 0;
107	ii->ii_error = 0;
108
109	if ((ret = av1394_ic_validate_init_params(ii)) != 0) {
110		AV1394_TNF_EXIT(av1394_ic_init);
111		return (ret);
112	}
113
114	/* allocate channel structure */
115	icp = kmem_zalloc(sizeof (av1394_ic_t), KM_SLEEP);
116
117	mutex_init(&icp->ic_mutex, NULL, MUTEX_DRIVER, ibc);
118	cv_init(&icp->ic_xfer_cv, NULL, CV_DRIVER, NULL);
119
120	av1394_ic_set_params(avp, ii, icp, -1);
121
122	/* allocate isoch channel and bandwidth, except for broadcast */
123	if (ii->ii_channel == (1ULL << 63)) {
124		num = 63;
125	} else if (ii->ii_flags & IEC61883_PRIV_ISOCH_NOALLOC) {
126		num = lowbit(ii->ii_channel) - 1;
127	} else {
128		mask = av1394_ic_bitreverse(ii->ii_channel);
129		ret = av1394_ic_alloc_channel(icp, mask, &num);
130		if (ret != DDI_SUCCESS) {
131			ii->ii_error = IEC61883_ERR_NOCHANNEL;
132			av1394_ic_cleanup(icp, 1);
133			AV1394_TNF_EXIT(av1394_ic_init);
134			return (EINVAL);
135		}
136	}
137	ASSERT((num >= 0) && (num < 64));
138
139	mutex_enter(&icp->ic_mutex);
140	icp->ic_num = num;
141	mutex_exit(&icp->ic_mutex);
142
143	mutex_enter(&ip->i_mutex);
144	if (ip->i_ic[num] != NULL) {
145		mutex_exit(&ip->i_mutex);
146		ii->ii_error = IEC61883_ERR_NOCHANNEL;
147		av1394_ic_cleanup(icp, 2);
148		TNF_PROBE_0(av1394_ic_init_error_chan_used,
149		    AV1394_TNF_ISOCH_ERROR, "");
150		AV1394_TNF_EXIT(av1394_ic_init);
151		return (EINVAL);
152	}
153	ip->i_ic[num] = icp;
154	mutex_exit(&ip->i_mutex);
155
156	/* do direction specific initialization */
157	if (icp->ic_dir == AV1394_IR) {
158		ret = av1394_ir_init(icp, &ii->ii_error);
159		pool = &icp->ic_ir.ir_data_pool;
160	} else {
161		ret = av1394_it_init(icp, &ii->ii_error);
162		pool = &icp->ic_it.it_data_pool;
163	}
164
165	if (ret != 0) {
166		av1394_ic_cleanup(icp, 3);
167		AV1394_TNF_EXIT(av1394_ic_init);
168		return (ret);
169	}
170
171	/* allocate mmap space */
172	mutex_enter(&ip->i_mutex);
173	mutex_enter(&icp->ic_mutex);
174	icp->ic_mmap_sz = pool->ip_umem_size;
175	icp->ic_mmap_off = av1394_as_alloc(&ip->i_mmap_as, icp->ic_mmap_sz);
176
177	icp->ic_state = AV1394_IC_IDLE;
178
179	*icpp = icp;
180	ii->ii_handle = icp->ic_num;
181	ii->ii_frame_rcnt = icp->ic_nframes;
182	ii->ii_mmap_off = icp->ic_mmap_off;
183	ii->ii_rchannel = icp->ic_num;
184	mutex_exit(&icp->ic_mutex);
185	mutex_exit(&ip->i_mutex);
186
187	TNF_PROBE_2_DEBUG(av1394_ic_init, AV1394_TNF_ISOCH, "",
188	    tnf_string, msg, "channel allocated", tnf_int, num, icp->ic_num);
189
190	AV1394_TNF_EXIT(av1394_ic_init);
191	return (0);
192}
193
194void
195av1394_ic_fini(av1394_ic_t *icp)
196{
197	AV1394_TNF_ENTER(av1394_ic_fini);
198
199	av1394_ic_cleanup(icp, AV1394_CLEANUP_LEVEL_MAX);
200
201	AV1394_TNF_EXIT(av1394_ic_fini);
202}
203
204/*
205 *
206 * --- configuration routines
207 *
208 */
209static void
210av1394_ic_cleanup(av1394_ic_t *icp, int level)
211{
212	av1394_inst_t	*avp = icp->ic_avp;
213	av1394_isoch_t	*ip = &avp->av_i;
214
215	ASSERT((level > 0) && (level <= AV1394_CLEANUP_LEVEL_MAX));
216
217	switch (level) {
218	default:
219		if (icp->ic_dir == AV1394_IR) {
220			av1394_ir_fini(icp);
221		} else {
222			av1394_it_fini(icp);
223		}
224		/* FALLTHRU */
225	case 3:
226		mutex_enter(&ip->i_mutex);
227		av1394_as_free(&ip->i_mmap_as, icp->ic_mmap_off);
228		ip->i_ic[icp->ic_num] = NULL;
229		mutex_exit(&ip->i_mutex);
230		/* FALLTHRU */
231	case 2:
232		av1394_ic_free_channel(icp);
233		/* FALLTHRU */
234	case 1:
235		cv_destroy(&icp->ic_xfer_cv);
236		mutex_destroy(&icp->ic_mutex);
237		kmem_free(icp, sizeof (av1394_ic_t));
238	}
239}
240
241static int
242av1394_ic_validate_init_params(iec61883_isoch_init_t *ii)
243{
244	int	framesz;
245
246	ii->ii_error = 0;
247	if ((IEC61883_IMPL_VER_MAJOR(ii->ii_version) !=
248	    IEC61883_IMPL_VER_MAJOR(AV1394_IEC61883_VER)) ||
249	    (IEC61883_IMPL_VER_MINOR(ii->ii_version) >
250	    IEC61883_IMPL_VER_MINOR(AV1394_IEC61883_VER))) {
251		TNF_PROBE_0(av1394_ic_validate_init_params_ver_error,
252		    AV1394_TNF_ISOCH_ERROR, "");
253		ii->ii_error = IEC61883_ERR_VERSION;
254		return (EINVAL);
255	}
256	if ((ii->ii_pkt_size % 4) || (ii->ii_pkt_size > 512)) {
257		TNF_PROBE_0(av1394_ic_validate_init_params_pktsz_error,
258		    AV1394_TNF_ISOCH_ERROR, "");
259		ii->ii_error = IEC61883_ERR_PKT_SIZE;
260		return (EINVAL);
261	}
262	framesz = ii->ii_frame_size * ii->ii_pkt_size;
263	if (framesz > AV1394_IC_FRAME_SIZE_MAX) {
264		TNF_PROBE_0(av1394_ic_validate_init_params_frsz_error,
265		    AV1394_TNF_ISOCH_ERROR, "");
266		ii->ii_error = IEC61883_ERR_NOMEM;
267		return (EINVAL);
268	}
269	if ((ii->ii_direction != IEC61883_DIR_RECV) &&
270	    (ii->ii_direction != IEC61883_DIR_XMIT)) {
271		TNF_PROBE_0(av1394_ic_validate_init_params_dir_error,
272		    AV1394_TNF_ISOCH_ERROR, "");
273		ii->ii_error = IEC61883_ERR_INVAL;
274		return (EINVAL);
275	}
276	if (((ii->ii_direction == IEC61883_DIR_RECV) &&
277	    (ii->ii_frame_cnt < AV1394_IR_NFRAMES_MIN)) ||
278	    ((ii->ii_direction == IEC61883_DIR_XMIT) &&
279	    (ii->ii_frame_cnt < AV1394_IT_NFRAMES_MIN))) {
280		TNF_PROBE_0(av1394_ic_validate_init_params_frcnt_error,
281		    AV1394_TNF_ISOCH_ERROR, "");
282		ii->ii_error = IEC61883_ERR_INVAL;
283		return (EINVAL);
284	}
285	if ((ii->ii_bus_speed != IEC61883_S100) &&
286	    (ii->ii_bus_speed != IEC61883_S200) &&
287	    (ii->ii_bus_speed != IEC61883_S400)) {
288		TNF_PROBE_0(av1394_ic_validate_init_params_speed_error,
289		    AV1394_TNF_ISOCH_ERROR, "");
290		ii->ii_error = IEC61883_ERR_INVAL;
291		return (EINVAL);
292	}
293	if (ii->ii_channel == 0) {
294		TNF_PROBE_0(av1394_ic_validate_init_params_chan_error,
295		    AV1394_TNF_ISOCH_ERROR, "");
296		ii->ii_error = IEC61883_ERR_INVAL;
297		return (EINVAL);
298	}
299	if ((ii->ii_flags & IEC61883_PRIV_ISOCH_NOALLOC) &&
300	    !av1394_ic_onebit(ii->ii_channel)) {
301		TNF_PROBE_0(av1394_ic_validate_init_params_chan_onebit_error,
302		    AV1394_TNF_ISOCH_ERROR, "");
303		ii->ii_error = IEC61883_ERR_INVAL;
304		return (EINVAL);
305	}
306	/* the rest are xmit only */
307	if (ii->ii_direction == IEC61883_DIR_RECV) {
308		return (0);
309	}
310	if (((ii->ii_rate_d != 0) ||
311	    (ii->ii_rate_n != IEC61883_RATE_N_DV_NTSC) &&
312	    (ii->ii_rate_n != IEC61883_RATE_N_DV_PAL)) &&
313	    ((ii->ii_rate_d <= 0) || (ii->ii_rate_n < 0) ||
314	    ((ii->ii_rate_n != 0) && (ii->ii_rate_d / ii->ii_rate_n < 2)))) {
315		TNF_PROBE_0(av1394_ic_validate_init_params_rate_error,
316		    AV1394_TNF_ISOCH_ERROR, "");
317		ii->ii_error = IEC61883_ERR_INVAL;
318		return (EINVAL);
319	}
320	if (AV1394_TS_MODE_GET_OFF(ii->ii_ts_mode) +
321	    AV1394_TS_MODE_GET_SIZE(ii->ii_ts_mode) > ii->ii_pkt_size) {
322		TNF_PROBE_0(av1394_ic_validate_init_params_ts_error,
323		    AV1394_TNF_ISOCH_ERROR, "");
324		ii->ii_error = IEC61883_ERR_INVAL;
325		return (EINVAL);
326	}
327	return (0);
328}
329
330static void
331av1394_ic_set_params(av1394_inst_t *avp, iec61883_isoch_init_t *ii,
332		av1394_ic_t *icp, int num)
333{
334	av1394_ic_param_t	*cp = &icp->ic_param;
335
336	mutex_enter(&icp->ic_mutex);
337	icp->ic_avp = avp;
338	icp->ic_num = num;
339	icp->ic_dir = (ii->ii_direction == IEC61883_DIR_RECV) ?
340	    AV1394_IR : AV1394_IT;
341	icp->ic_pktsz = ii->ii_pkt_size;
342	icp->ic_npkts = ii->ii_frame_size;
343	icp->ic_framesz = icp->ic_pktsz * icp->ic_npkts;
344	icp->ic_nframes = ii->ii_frame_cnt;
345	cp->cp_bus_speed = ii->ii_bus_speed;
346	cp->cp_dbs = ii->ii_dbs;
347	cp->cp_fn = ii->ii_fn;
348	if (icp->ic_dir == AV1394_IT) {
349		if (ii->ii_rate_d == 0) {
350			switch (ii->ii_rate_n) {
351			case IEC61883_RATE_N_DV_NTSC:
352				cp->cp_n = av1394_rate_n_dv_ntsc;
353				cp->cp_d = av1394_rate_d_dv_ntsc;
354				break;
355			case IEC61883_RATE_N_DV_PAL:
356				cp->cp_n = av1394_rate_n_dv_pal;
357				cp->cp_d = av1394_rate_d_dv_pal;
358				break;
359			default:
360				ASSERT(0);	/* can't happen */
361			}
362		} else {
363			cp->cp_n = ii->ii_rate_n;
364			cp->cp_d = ii->ii_rate_d;
365		}
366	}
367	cp->cp_ts_mode = ii->ii_ts_mode;
368	mutex_exit(&icp->ic_mutex);
369}
370
371static int
372av1394_ic_alloc_channel(av1394_ic_t *icp, uint64_t mask, int *num)
373{
374	av1394_inst_t	*avp = icp->ic_avp;
375	int		ret, result;
376	t1394_isoch_singleinfo_t sii;
377	t1394_isoch_single_out_t so;
378
379	/* allocate isoch channel */
380	sii.si_channel_mask	= mask;
381	sii.si_bandwidth	= icp->ic_pktsz;
382	sii.rsrc_fail_target	= av1394_ic_rsrc_fail;
383	sii.single_evt_arg	= icp;
384	sii.si_speed		= icp->ic_param.cp_bus_speed;
385
386	ret = t1394_alloc_isoch_single(avp->av_t1394_hdl, &sii, 0, &so,
387	    &icp->ic_sii_hdl, &result);
388	if (ret != DDI_SUCCESS) {
389		TNF_PROBE_1(av1394_ic_alloc_channel_error,
390		    AV1394_TNF_ISOCH_ERROR, "", tnf_int, result, result);
391	} else {
392		*num = so.channel_num;
393	}
394	return (ret);
395}
396
397static void
398av1394_ic_free_channel(av1394_ic_t *icp)
399{
400	av1394_inst_t	*avp = icp->ic_avp;
401
402	if (icp->ic_sii_hdl != NULL) {
403		t1394_free_isoch_single(avp->av_t1394_hdl, &icp->ic_sii_hdl, 0);
404	}
405}
406
407/*
408 *
409 * --- memory allocation and mapping routines
410 *
411 * av1394_ic_alloc_pool()
412 *    Allocate isoch pool for at least 'mincnt' and at most 'cnt' frames
413 *    'framesz' bytes each. The strategy is to allocate segments of reasonably
414 *    large size, to avoid fragmentation and use resources efficiently in case
415 *    of a large number of very small frames.
416 *
417 *    Another problem is that RECV/SEND_BUF IXL commands can address limited
418 *    amount of buffer space (AV1394_IXL_BUFSZ_MAX), and if segment size and
419 *    buffer size are not aligned, it can make much harder to build IXL chains.
420 *    To simplify things, segments shall always contain full frames.
421 *
422 *    Function returns number of frames the resulting pool can hold.
423 */
424int
425av1394_ic_alloc_pool(av1394_isoch_pool_t *pool, size_t framesz, int cnt,
426	int mincnt)
427{
428	av1394_isoch_seg_t *seg;
429	int		fps;		/* frames per segment */
430	int		nsegs;
431	size_t		totalsz, segsz;
432	int		i;
433	int		ret;
434
435	AV1394_TNF_ENTER(av1394_ic_alloc_pool);
436
437	totalsz = framesz * cnt;
438	ASSERT(totalsz > 0);
439
440	/* request should be reasonable */
441	if (btopr(totalsz) > physmem / AV1394_MEM_MAX_PERCENT) {
442		TNF_PROBE_0(av1394_ic_alloc_pool_error_physmem,
443		    AV1394_TNF_ISOCH_ERROR, "");
444		AV1394_TNF_EXIT(av1394_ic_alloc_pool);
445		return (0);
446	}
447
448	/* calculate segment size and number of segments */
449	segsz = framesz;
450	nsegs = cnt;
451	if (framesz < AV1394_IXL_BUFSZ_MAX / 2) {
452		fps = AV1394_IXL_BUFSZ_MAX / framesz;
453		segsz = framesz * fps;
454		nsegs = totalsz / segsz;
455		if ((totalsz % segsz) != 0)
456			nsegs++;	/* remainder in non-full segment */
457	}
458	ASSERT(segsz * nsegs >= totalsz);
459
460	/* allocate segment array */
461	pool->ip_alloc_size = nsegs * sizeof (av1394_isoch_seg_t);
462	pool->ip_seg = kmem_zalloc(pool->ip_alloc_size, KM_SLEEP);
463
464	/* allocate page-aligned user-mappable memory for each segment */
465	pool->ip_nsegs = 0;
466	pool->ip_size = 0;
467	pool->ip_umem_size = 0;
468	for (i = 0; i < nsegs; i++) {
469		seg = &pool->ip_seg[i];
470
471		seg->is_umem_size = ptob(btopr(segsz));
472		seg->is_kaddr = ddi_umem_alloc(seg->is_umem_size,
473		    DDI_UMEM_SLEEP, &seg->is_umem_cookie);
474		if (seg->is_kaddr == NULL) {
475			TNF_PROBE_0(av1394_ic_alloc_pool_error_umem_alloc,
476			    AV1394_TNF_ISOCH_ERROR, "");
477			break;
478		}
479		seg->is_size = segsz;
480
481		pool->ip_size += seg->is_size;
482		pool->ip_umem_size += seg->is_umem_size;
483		pool->ip_nsegs++;
484	}
485
486	/* number of frames the pool can hold */
487	ret = pool->ip_size / framesz;
488	if (ret < mincnt) {
489		TNF_PROBE_0(av1394_ic_alloc_pool_error_mincnt,
490		    AV1394_TNF_ISOCH_ERROR, "");
491		av1394_ic_free_pool(pool);
492		ret = 0;
493	}
494
495	AV1394_TNF_EXIT(av1394_ic_alloc_pool);
496	return (ret);
497}
498
499void
500av1394_ic_free_pool(av1394_isoch_pool_t *pool)
501{
502	int	i;
503
504	AV1394_TNF_ENTER(av1394_ic_free_pool);
505
506	if (pool->ip_seg != NULL) {
507		for (i = 0; i < pool->ip_nsegs; i++) {
508			ddi_umem_free(pool->ip_seg[i].is_umem_cookie);
509		}
510		kmem_free(pool->ip_seg, pool->ip_alloc_size);
511		pool->ip_seg = NULL;
512	}
513
514	AV1394_TNF_EXIT(av1394_ic_free_pool);
515}
516
517int
518av1394_ic_dma_setup(av1394_ic_t *icp, av1394_isoch_pool_t *pool)
519{
520	av1394_inst_t		*avp = icp->ic_avp;
521	av1394_isoch_seg_t	*isp;
522	uint_t			dma_dir;
523	int			ret;
524	int			i;
525	int			j;
526
527	AV1394_TNF_ENTER(av1394_ic_dma_setup);
528
529	dma_dir = (icp->ic_dir == AV1394_IR) ? DDI_DMA_READ : DDI_DMA_WRITE;
530	/*
531	 * Alloc and bind a DMA handle for each segment.
532	 * Note that we need packet size alignment, but since ddi_umem_alloc'ed
533	 * memory is page-aligned and our packets are less than page size (yet)
534	 * we don't need to do anything special here.
535	 */
536	for (i = 0; i < pool->ip_nsegs; i++) {
537		isp = &pool->ip_seg[i];
538
539		ret = ddi_dma_alloc_handle(avp->av_dip,
540		    &avp->av_attachinfo.dma_attr, DDI_DMA_DONTWAIT, NULL,
541		    &isp->is_dma_hdl);
542		if (ret != DDI_SUCCESS) {
543			TNF_PROBE_0(av1394_ic_dma_setup_error_alloc_hdl,
544			    AV1394_TNF_ISOCH_ERROR, "");
545			av1394_ic_dma_cleanup(icp, pool);
546			AV1394_TNF_EXIT(av1394_ic_dma_setup);
547			return (ret);
548		}
549
550		ret = ddi_dma_addr_bind_handle(isp->is_dma_hdl, NULL,
551		    isp->is_kaddr, isp->is_size,
552		    dma_dir | DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL,
553		    &isp->is_dma_cookie[0], &isp->is_dma_ncookies);
554
555		if (ret != DDI_DMA_MAPPED) {
556			TNF_PROBE_0(av1394_ic_dma_setup_error_bind_hdl,
557			    AV1394_TNF_ISOCH_ERROR, "");
558			av1394_ic_dma_cleanup(icp, pool);
559			AV1394_TNF_EXIT(av1394_ic_dma_setup);
560			return (DDI_FAILURE);
561		}
562
563		if (isp->is_dma_ncookies > COOKIES) {
564			TNF_PROBE_0(av1394_ic_dma_setup_error_ncookies,
565			    AV1394_TNF_ISOCH_ERROR, "");
566			av1394_ic_dma_cleanup(icp, pool);
567			AV1394_TNF_EXIT(av1394_ic_dma_setup);
568			return (DDI_FAILURE);
569		}
570
571		for (j = 1; j < isp->is_dma_ncookies; ++j)
572			ddi_dma_nextcookie(isp->is_dma_hdl,
573			    &isp->is_dma_cookie[j]);
574	}
575
576	AV1394_TNF_EXIT(av1394_ic_dma_setup);
577	return (DDI_SUCCESS);
578}
579
580/*ARGSUSED*/
581void
582av1394_ic_dma_cleanup(av1394_ic_t *icp, av1394_isoch_pool_t *pool)
583{
584	av1394_isoch_seg_t	*seg;
585	int			i;
586
587	AV1394_TNF_ENTER(av1394_ic_dma_cleanup);
588
589	for (i = 0; i < pool->ip_nsegs; i++) {
590		seg = &pool->ip_seg[i];
591		if (seg->is_dma_hdl != NULL) {
592			if (seg->is_dma_ncookies > 0) {
593				(void) ddi_dma_unbind_handle(seg->is_dma_hdl);
594			}
595			ddi_dma_free_handle(&seg->is_dma_hdl);
596		}
597	}
598
599	AV1394_TNF_EXIT(av1394_ic_dma_cleanup);
600}
601
602/*
603 * sync frames for CPU access
604 */
605void
606av1394_ic_dma_sync_frames(av1394_ic_t *icp, int idx, int cnt,
607		av1394_isoch_pool_t *pool, uint_t type)
608{
609	int	fps;		/* frames per segment */
610	int	nsegs;		/* number of segments for indicated frames */
611	int	seg;		/* index of segment to sync */
612
613	fps = icp->ic_nframes / pool->ip_nsegs;
614
615	nsegs = (cnt / fps) + 1;
616
617		seg = idx / fps;
618		for (;;) {
619			(void) ddi_dma_sync(pool->ip_seg[seg].is_dma_hdl, 0,
620			    icp->ic_framesz, type);
621
622		--nsegs;
623		if (nsegs == 0)
624			break;
625
626		++seg;
627		if (seg == pool->ip_nsegs)
628			seg = 0;	/* wrap segment index */
629	}
630}
631
632/*
633 *
634 * --- transfer
635 *
636 */
637int
638av1394_ic_start(av1394_ic_t *icp)
639{
640	if (icp->ic_dir == AV1394_IR) {
641		return (av1394_ir_start(icp));
642	} else {
643		return (av1394_it_start(icp));
644	}
645}
646
647int
648av1394_ic_stop(av1394_ic_t *icp)
649{
650	if (icp->ic_dir == AV1394_IR) {
651		return (av1394_ir_stop(icp));
652	} else {
653		return (av1394_it_stop(icp));
654	}
655}
656
657/*
658 *
659 * --- callbacks
660 *
661 */
662/*ARGSUSED*/
663static void
664av1394_ic_rsrc_fail(t1394_isoch_single_handle_t t1394_sii_hdl, opaque_t arg,
665		t1394_isoch_rsrc_error_t fail_args)
666{
667	AV1394_TNF_ENTER(av1394_ic_rsrc_fail);
668
669	/* XXX this could be handled more gracefully */
670	cmn_err(CE_CONT, "av1394: can't reallocate isochronous resources"
671	    " after bus reset\n");
672
673	AV1394_TNF_EXIT(av1394_ic_rsrc_fail);
674}
675
676/*
677 *
678 * --- misc
679 *
680 *
681 * av1394_ic_ixl_seg_decomp()
682 *    Calculate the best decomposition of a segment into buffers.
683 *    Return number of buffers, buffer and tail buffer sizes.
684 *
685 *    We are looking to divide a segment evenly into equally-sized or almost
686 *    equally-sized buffers. Maximum buffer size is AV1394_IXL_BUFSZ_MAX.
687 *    Algorithm:
688 *	1. If segment size divides evenly by maximum size, terminate.
689 *	2. n = number of maximum-size buffers than fits into the segment.
690 *	3. Divide the segment by n+1, calculate buffer size and tail
691 *	   (remainder) size.
692 *	4. If the tail can be appended to the last buffer and the resulting
693 *	   buffer is still less than maximum size, terminate.
694 *	5. Repeat steps 3-5 for n+2, n+3, ... until division is too small.
695 *
696 *    Since all sizes are packet-aligned, we scale them down (divide by
697 *    packet size) in the beginning, do all calculations and scale them up
698 *    in the end.
699 */
700int
701av1394_ic_ixl_seg_decomp(size_t segsz, size_t pktsz, size_t *bufszp,
702	size_t *tailszp)
703{
704	size_t	nbufs, bufsz, tailsz;
705	size_t	maxsz = AV1394_IXL_BUFSZ_MAX;
706
707	ASSERT(segsz >= maxsz);
708	ASSERT(segsz % pktsz == 0);
709
710	if (segsz % maxsz == 0) {
711		*tailszp = *bufszp = maxsz;
712		return (segsz / *bufszp - 1);
713	}
714
715	maxsz /= pktsz;
716	segsz /= pktsz;
717
718	nbufs = segsz / maxsz;
719	do {
720		nbufs++;
721		bufsz = segsz / nbufs;
722		tailsz = bufsz + (segsz - bufsz * nbufs);
723	} while ((tailsz > maxsz) && ((segsz / (nbufs + 1)) > 1));
724	nbufs--;
725
726	*bufszp = bufsz * pktsz;
727	*tailszp = tailsz * pktsz;
728	return (nbufs);
729}
730
731void
732av1394_ic_ixl_dump(ixl1394_command_t *cmd)
733{
734	ixl1394_callback_t	*cb;
735	ixl1394_jump_t		*jmp;
736	ixl1394_xfer_buf_t	*buf;
737	ixl1394_xfer_pkt_t	*pkt;
738
739	while (cmd) {
740		switch (cmd->ixl_opcode) {
741		case IXL1394_OP_LABEL:
742			cmn_err(CE_CONT, "%p: LABEL\n", (void *)cmd);
743			break;
744		case IXL1394_OP_RECV_BUF:
745		case IXL1394_OP_RECV_BUF_U:
746			buf = (ixl1394_xfer_buf_t *)cmd;
747			cmn_err(CE_CONT, "%p: RECV_BUF addr=%p size=%d "
748			    "pkt_size=%d\n", (void *)cmd, (void *)buf->mem_bufp,
749			    buf->size, buf->pkt_size);
750			break;
751		case IXL1394_OP_SEND_BUF:
752		case IXL1394_OP_SEND_BUF_U:
753			buf = (ixl1394_xfer_buf_t *)cmd;
754			cmn_err(CE_CONT, "%p: SEND_BUF addr=%p size=%d "
755			    "pkt_size=%d\n", (void *)cmd, (void *)buf->mem_bufp,
756			    buf->size, buf->pkt_size);
757			break;
758		case IXL1394_OP_SEND_PKT_ST:
759			pkt = (ixl1394_xfer_pkt_t *)cmd;
760			cmn_err(CE_CONT, "%p: SEND_PKT_ST addr=%p size=%d\n",
761			    (void *)cmd, (void *)pkt->mem_bufp, pkt->size);
762			break;
763		case IXL1394_OP_CALLBACK:
764		case IXL1394_OP_CALLBACK_U:
765			cb = (ixl1394_callback_t *)cmd;
766			cmn_err(CE_CONT, "%p: CALLBACK %p\n", (void *)cmd,
767			    (void *)cb->callback);
768			break;
769		case IXL1394_OP_JUMP:
770			jmp = (ixl1394_jump_t *)cmd;
771			cmn_err(CE_CONT, "%p: JUMP %p\n", (void *)cmd,
772			    (void *)jmp->label);
773			break;
774		case IXL1394_OP_JUMP_U:
775			jmp = (ixl1394_jump_t *)cmd;
776			cmn_err(CE_CONT, "%p: JUMP_U %p\n", (void *)cmd,
777			    (void *)jmp->label);
778			break;
779		case IXL1394_OP_STORE_TIMESTAMP:
780			cmn_err(CE_CONT, "%p: STORE_TIMESTAMP\n", (void *)cmd);
781			break;
782		default:
783			cmn_err(CE_CONT, "%p: other\n", (void *)cmd);
784		}
785		cmd = cmd->next_ixlp;
786	}
787}
788
789/*
790 * trigger a soft interrupt, if not already, for a given channel and type
791 */
792void
793av1394_ic_trigger_softintr(av1394_ic_t *icp, int num, int preq)
794{
795	av1394_isoch_t	*ip = &icp->ic_avp->av_i;
796	uint64_t	chmask = (1ULL << num);
797
798	if (((ip->i_softintr_ch & chmask) == 0) ||
799	    ((icp->ic_preq & preq) == 0)) {
800		ip->i_softintr_ch |= chmask;
801		icp->ic_preq |= preq;
802		ddi_trigger_softintr(ip->i_softintr_id);
803	}
804}
805
806/*
807 * reverse bits in a 64-bit word
808 */
809uint64_t
810av1394_ic_bitreverse(uint64_t x)
811{
812	x = (((x >> 1) & 0x5555555555555555) | ((x & 0x5555555555555555) << 1));
813	x = (((x >> 2) & 0x3333333333333333) | ((x & 0x3333333333333333) << 2));
814	x = (((x >> 4) & 0x0f0f0f0f0f0f0f0f) | ((x & 0x0f0f0f0f0f0f0f0f) << 4));
815	x = (((x >> 8) & 0x00ff00ff00ff00ff) | ((x & 0x00ff00ff00ff00ff) << 8));
816	x = (((x >> 16) & 0x0000ffff0000ffff) |
817	    ((x & 0x0000ffff0000ffff) << 16));
818
819	return ((x >> 32) | (x << 32));
820}
821
822/*
823 * return B_TRUE if a 64-bit value has only one bit set to 1
824 */
825boolean_t
826av1394_ic_onebit(uint64_t i)
827{
828	return (((~i + 1) | ~i) == 0xFFFFFFFFFFFFFFFF);
829}
830