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