xref: /illumos-gate/usr/src/uts/common/io/1394/targets/av1394/av1394_isoch.c (revision d3b5f56344d8bfcdd6cfb82446af0e5e55ad9ebe)
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  * av1394 isochronous module
28  */
29 #include <sys/stat.h>
30 #include <sys/file.h>
31 #include <sys/ddi.h>
32 #include <sys/sunddi.h>
33 #include <sys/av/iec61883.h>
34 #include <sys/1394/targets/av1394/av1394_impl.h>
35 
36 /* configuration routines */
37 static int	av1394_isoch_create_minor_node(av1394_inst_t *);
38 static void	av1394_isoch_remove_minor_node(av1394_inst_t *);
39 static void	av1394_isoch_cleanup(av1394_inst_t *, int);
40 av1394_isoch_seg_t *av1394_isoch_find_seg(av1394_inst_t *, offset_t, size_t);
41 static int	av1394_isoch_autorecv_init(av1394_inst_t *, av1394_ic_t **);
42 static int	av1394_isoch_autoxmit_init(av1394_inst_t *, av1394_ic_t **,
43 		struct uio *);
44 
45 /* ioctls */
46 static int	av1394_ioctl_isoch_init(av1394_inst_t *, void *, int);
47 static av1394_ic_t *av1394_ioctl_isoch_handle2ic(av1394_inst_t *, void *);
48 static int	av1394_ioctl_isoch_fini(av1394_inst_t *, void *, int);
49 static int	av1394_ioctl_start(av1394_inst_t *, void *, int);
50 static int	av1394_ioctl_stop(av1394_inst_t *, void *, int);
51 static int	av1394_ioctl_recv(av1394_inst_t *, void *, int);
52 static int	av1394_ioctl_xmit(av1394_inst_t *, void *, int);
53 
54 static uint_t av1394_isoch_softintr(caddr_t);
55 
56 static struct devmap_callback_ctl av1394_isoch_devmap_ops = {
57 	DEVMAP_OPS_REV,		/* rev */
58 	NULL,			/* map */
59 	NULL,			/* access */
60 	NULL,			/* dup */
61 	NULL,			/* unmap */
62 };
63 
64 /* tunables */
65 int av1394_rate_n_dv_ntsc = 246;
66 int av1394_rate_d_dv_ntsc = 3840;
67 int av1394_rate_n_dv_pal = 1;
68 int av1394_rate_d_dv_pal = 16;
69 
70 int av1394_isoch_autorecv_nframes = 50;
71 int av1394_isoch_autorecv_framesz = 250;
72 int av1394_isoch_autoxmit_nframes = 50;
73 int av1394_isoch_autoxmit_framesz = 250;
74 
75 #define	AV1394_TNF_ENTER(func)	\
76 	TNF_PROBE_0_DEBUG(func##_enter, AV1394_TNF_ISOCH_STACK, "");
77 
78 #define	AV1394_TNF_EXIT(func)	\
79 	TNF_PROBE_0_DEBUG(func##_exit, AV1394_TNF_ISOCH_STACK, "");
80 
81 int
82 av1394_isoch_attach(av1394_inst_t *avp)
83 {
84 	av1394_isoch_t	*ip = &avp->av_i;
85 	ddi_iblock_cookie_t ibc = avp->av_attachinfo.iblock_cookie;
86 
87 	AV1394_TNF_ENTER(av1394_isoch_attach);
88 
89 	mutex_init(&ip->i_mutex, NULL, MUTEX_DRIVER, ibc);
90 
91 	mutex_enter(&ip->i_mutex);
92 	if (av1394_isoch_create_minor_node(avp) != DDI_SUCCESS) {
93 		mutex_exit(&ip->i_mutex);
94 		av1394_isoch_cleanup(avp, 1);
95 		AV1394_TNF_EXIT(av1394_isoch_attach);
96 		return (DDI_FAILURE);
97 	}
98 
99 	if (ddi_add_softintr(avp->av_dip, DDI_SOFTINT_LOW, &ip->i_softintr_id,
100 	    0, 0, av1394_isoch_softintr, (caddr_t)avp) != DDI_SUCCESS) {
101 		mutex_exit(&ip->i_mutex);
102 		av1394_isoch_cleanup(avp, 2);
103 		AV1394_TNF_EXIT(av1394_isoch_attach);
104 		return (DDI_FAILURE);
105 	}
106 
107 	if (av1394_cmp_init(avp) != DDI_SUCCESS) {
108 		mutex_exit(&ip->i_mutex);
109 		av1394_isoch_cleanup(avp, 3);
110 		AV1394_TNF_EXIT(av1394_isoch_attach);
111 		return (DDI_FAILURE);
112 	}
113 
114 	av1394_as_init(&ip->i_mmap_as);
115 	mutex_exit(&ip->i_mutex);
116 
117 	AV1394_TNF_EXIT(av1394_isoch_attach);
118 	return (DDI_SUCCESS);
119 }
120 
121 void
122 av1394_isoch_detach(av1394_inst_t *avp)
123 {
124 	AV1394_TNF_ENTER(av1394_isoch_detach);
125 
126 	av1394_isoch_cleanup(avp, AV1394_CLEANUP_LEVEL_MAX);
127 
128 	AV1394_TNF_EXIT(av1394_isoch_detach);
129 }
130 
131 int
132 av1394_isoch_cpr_suspend(av1394_inst_t *avp)
133 {
134 	av1394_isoch_t	*ip = &avp->av_i;
135 	av1394_ic_t	*icp;
136 	int		i;
137 	int		ret = DDI_SUCCESS;
138 
139 	AV1394_TNF_ENTER(av1394_isoch_cpr_suspend);
140 
141 	/*
142 	 * suspend only if there are no active channels
143 	 */
144 	mutex_enter(&ip->i_mutex);
145 	for (i = 0; (i < NELEM(ip->i_ic)) && (ret == DDI_SUCCESS); i++) {
146 		icp = ip->i_ic[i];
147 		if (icp) {
148 			mutex_enter(&icp->ic_mutex);
149 			if (icp->ic_state != AV1394_IC_IDLE) {
150 				ret = DDI_FAILURE;
151 			}
152 			mutex_exit(&icp->ic_mutex);
153 		}
154 	}
155 	mutex_exit(&ip->i_mutex);
156 
157 	AV1394_TNF_EXIT(av1394_isoch_cpr_suspend);
158 	return (ret);
159 }
160 
161 /*ARGSUSED*/
162 int
163 av1394_isoch_close(av1394_inst_t *avp, int flag)
164 {
165 	int	ret;
166 
167 	AV1394_TNF_ENTER(av1394_isoch_close);
168 
169 	ret = av1394_ic_close(avp, flag);
170 	av1394_cmp_close(avp);
171 
172 	AV1394_TNF_EXIT(av1394_isoch_close);
173 	return (ret);
174 }
175 
176 int
177 av1394_isoch_read(av1394_inst_t *avp, struct uio *uiop)
178 {
179 	av1394_ic_t	*icp;
180 	int		ret;
181 
182 	AV1394_TNF_ENTER(av1394_isoch_read);
183 
184 	/* use broadcast channel */
185 	icp = avp->av_i.i_ic[63];
186 	if (icp == NULL) {
187 		if ((ret = av1394_isoch_autorecv_init(avp, &icp)) != 0) {
188 			AV1394_TNF_EXIT(av1394_isoch_read);
189 			return (ret);
190 		}
191 	} else if (icp->ic_dir != AV1394_IR) {
192 		/* channel already used for xmit */
193 		return (EBUSY);
194 	}
195 
196 	if ((ret = av1394_ir_start(icp)) == 0) {
197 		ret = av1394_ir_read(icp, uiop);
198 	}
199 
200 	AV1394_TNF_EXIT(av1394_isoch_read);
201 	return (ret);
202 }
203 
204 int
205 av1394_isoch_write(av1394_inst_t *avp, struct uio *uiop)
206 {
207 	av1394_ic_t	*icp;
208 	int		ret;
209 
210 	AV1394_TNF_ENTER(av1394_isoch_write);
211 
212 	/* use broadcast channel */
213 	icp = avp->av_i.i_ic[63];
214 	if (icp == NULL) {
215 		if ((ret = av1394_isoch_autoxmit_init(avp, &icp, uiop)) != 0) {
216 			AV1394_TNF_EXIT(av1394_isoch_write);
217 			return (ret);
218 		}
219 	} else if (icp->ic_dir != AV1394_IT) {
220 		/* channel already used for recv */
221 		AV1394_TNF_EXIT(av1394_isoch_write);
222 		return (EBUSY);
223 	}
224 
225 	ret = av1394_it_write(icp, uiop);
226 
227 	AV1394_TNF_EXIT(av1394_isoch_write);
228 	return (ret);
229 }
230 
231 /*ARGSUSED*/
232 int
233 av1394_isoch_ioctl(av1394_inst_t *avp, int cmd, intptr_t arg, int mode,
234     int *rvalp)
235 {
236 	int		ret = EINVAL;
237 
238 	switch (cmd) {
239 	case IEC61883_ISOCH_INIT:
240 		ret = av1394_ioctl_isoch_init(avp, (void *)arg, mode);
241 		break;
242 	case IEC61883_ISOCH_FINI:
243 		ret = av1394_ioctl_isoch_fini(avp, (void *)arg, mode);
244 		break;
245 	case IEC61883_START:
246 		ret = av1394_ioctl_start(avp, (void *)arg, mode);
247 		break;
248 	case IEC61883_STOP:
249 		ret = av1394_ioctl_stop(avp, (void *)arg, mode);
250 		break;
251 	case IEC61883_RECV:
252 		ret = av1394_ioctl_recv(avp, (void *)arg, mode);
253 		break;
254 	case IEC61883_XMIT:
255 		ret = av1394_ioctl_xmit(avp, (void *)arg, mode);
256 		break;
257 	case IEC61883_PLUG_INIT:
258 		ret = av1394_ioctl_plug_init(avp, (void *)arg, mode);
259 		break;
260 	case IEC61883_PLUG_FINI:
261 		ret = av1394_ioctl_plug_fini(avp, (void *)arg, mode);
262 		break;
263 	case IEC61883_PLUG_REG_READ:
264 		ret = av1394_ioctl_plug_reg_read(avp, (void *)arg, mode);
265 		break;
266 	case IEC61883_PLUG_REG_CAS:
267 		ret = av1394_ioctl_plug_reg_cas(avp, (void *)arg, mode);
268 		break;
269 	}
270 
271 	return (ret);
272 }
273 
274 /*ARGSUSED*/
275 int
276 av1394_isoch_devmap(av1394_inst_t *avp, devmap_cookie_t dhp, offset_t off,
277     size_t len, size_t *maplen, uint_t model)
278 {
279 	av1394_isoch_seg_t *isp;
280 
281 	AV1394_TNF_ENTER(av1394_isoch_devmap);
282 
283 	*maplen = 0;
284 
285 	/* find segment */
286 	isp = av1394_isoch_find_seg(avp, off, ptob(btopr(len)));
287 	if (isp == NULL) {
288 		AV1394_TNF_EXIT(av1394_isoch_devmap);
289 		return (EINVAL);
290 	}
291 
292 	/* map segment */
293 	if (devmap_umem_setup(dhp, avp->av_dip, &av1394_isoch_devmap_ops,
294 	    isp->is_umem_cookie, 0, isp->is_umem_size, PROT_ALL, 0,
295 	    &avp->av_attachinfo.acc_attr) != 0) {
296 		TNF_PROBE_0(av1394_isoch_devmap_error_umem_setup,
297 		    AV1394_TNF_ISOCH_ERROR, "");
298 		AV1394_TNF_EXIT(av1394_isoch_devmap);
299 		return (EINVAL);
300 	}
301 	*maplen = isp->is_umem_size;
302 
303 	AV1394_TNF_EXIT(av1394_isoch_devmap);
304 	return (0);
305 }
306 
307 /*
308  *
309  * --- configuration routines
310  *
311  * av1394_isoch_create_minor_node()
312  *    Create isoch minor node
313  */
314 static int
315 av1394_isoch_create_minor_node(av1394_inst_t *avp)
316 {
317 	int	ret;
318 
319 	ret = ddi_create_minor_node(avp->av_dip, "isoch",
320 	    S_IFCHR, AV1394_ISOCH_INST2MINOR(avp->av_instance),
321 	    DDI_NT_AV_ISOCH, 0);
322 	if (ret != DDI_SUCCESS) {
323 		TNF_PROBE_0(av1394_isoch_create_minor_node_error,
324 		    AV1394_TNF_ISOCH_ERROR, "");
325 	}
326 	return (ret);
327 }
328 
329 /*
330  * av1394_isoch_remove_minor_node()
331  *    Remove isoch minor node
332  */
333 static void
334 av1394_isoch_remove_minor_node(av1394_inst_t *avp)
335 {
336 	ddi_remove_minor_node(avp->av_dip, "isoch");
337 }
338 
339 /*
340  * av1394_isoch_cleanup()
341  *    Cleanup after attach
342  */
343 static void
344 av1394_isoch_cleanup(av1394_inst_t *avp, int level)
345 {
346 	av1394_isoch_t	*ip = &avp->av_i;
347 
348 	ASSERT((level > 0) && (level <= AV1394_CLEANUP_LEVEL_MAX));
349 
350 	switch (level) {
351 	default:
352 		mutex_enter(&ip->i_mutex);
353 		av1394_as_fini(&ip->i_mmap_as);
354 		av1394_cmp_fini(avp);
355 		mutex_exit(&ip->i_mutex);
356 		/* FALLTHRU */
357 	case 3:
358 		ddi_remove_softintr(ip->i_softintr_id);
359 		/* FALLTHRU */
360 	case 2:
361 		av1394_isoch_remove_minor_node(avp);
362 		/* FALLTHRU */
363 	case 1:
364 		mutex_destroy(&ip->i_mutex);
365 	}
366 }
367 
368 /*
369  * av1394_isoch_find_seg()
370  *    Given an offset and size, find a matching av1394_isoch_seg_t structure.
371  */
372 av1394_isoch_seg_t *
373 av1394_isoch_find_seg(av1394_inst_t *avp, offset_t off, size_t len)
374 {
375 	av1394_isoch_t	*ip = &avp->av_i;
376 	av1394_ic_t	*icp;
377 	av1394_isoch_pool_t *pool;
378 	av1394_isoch_seg_t *isp;
379 	offset_t	segoff;
380 	int		i;
381 
382 	/* find channel from within this range */
383 	for (i = 0; i < NELEM(ip->i_ic); i++) {
384 		icp = ip->i_ic[i];
385 		if (icp == NULL) {
386 			continue;
387 		}
388 		if ((off >= icp->ic_mmap_off) &&
389 		    (off + len <= icp->ic_mmap_off + icp->ic_mmap_sz)) {
390 			off -= icp->ic_mmap_off;	/* convert to base */
391 			break;
392 		}
393 		icp = NULL;
394 	}
395 	if (icp == NULL) {
396 		TNF_PROBE_0(av1394_isoch_find_seg_error_nochan,
397 		    AV1394_TNF_ISOCH_ERROR, "");
398 		return (NULL);
399 	}
400 
401 	/* find a segment */
402 	pool = (icp->ic_dir == AV1394_IR) ?
403 	    &icp->ic_ir.ir_data_pool : &icp->ic_it.it_data_pool;
404 	for (segoff = 0, i = 0; i < pool->ip_nsegs; i++) {
405 		isp = &pool->ip_seg[i];
406 		if (off == segoff) {
407 			break;
408 		}
409 		segoff += isp->is_umem_size;
410 		isp = NULL;
411 	}
412 	if (isp == NULL) {
413 		TNF_PROBE_0(av1394_isoch_find_seg_error_noseg,
414 		    AV1394_TNF_ISOCH_ERROR, "");
415 		return (NULL);
416 	}
417 
418 	/* only whole segments can be mapped */
419 	if (len != isp->is_umem_size) {
420 		TNF_PROBE_0(av1394_isoch_devmap_error_whole,
421 		    AV1394_TNF_ISOCH_ERROR, "");
422 		return (NULL);
423 	}
424 	return (isp);
425 }
426 
427 /*
428  * initialize default channel for data receipt
429  */
430 static int
431 av1394_isoch_autorecv_init(av1394_inst_t *avp, av1394_ic_t **icpp)
432 {
433 	iec61883_isoch_init_t ii;
434 	int		ret = 0;
435 
436 	AV1394_TNF_ENTER(av1394_isoch_autorecv_init);
437 
438 	bzero(&ii, sizeof (ii));
439 	ii.ii_version = IEC61883_V1_0;
440 	ii.ii_pkt_size = 512;
441 	ii.ii_frame_size = av1394_isoch_autorecv_framesz;
442 	ii.ii_frame_cnt = av1394_isoch_autorecv_nframes;
443 	ii.ii_direction = IEC61883_DIR_RECV;
444 	ii.ii_bus_speed = IEC61883_S100;
445 	ii.ii_channel = (1ULL << 63);
446 
447 	ret = av1394_ic_init(avp, &ii, icpp);
448 
449 	AV1394_TNF_EXIT(av1394_isoch_autorecv_init);
450 	return (ret);
451 }
452 
453 /*
454  * initialize default channel for data xmit
455  */
456 static int
457 av1394_isoch_autoxmit_init(av1394_inst_t *avp, av1394_ic_t **icpp,
458     struct uio *uiop)
459 {
460 	av1394_isoch_autoxmit_t *axp = &avp->av_i.i_autoxmit;
461 	iec61883_isoch_init_t ii;
462 	uint_t		fmt, dbs, fn, f5060, stype;	/* CIP fields */
463 	int		ret = 0;
464 
465 	AV1394_TNF_ENTER(av1394_isoch_autoxmit_init);
466 
467 	/* copyin the first CIP header */
468 	axp->ax_copy_ciph = B_FALSE;
469 	if (uiop->uio_resid < AV1394_CIPSZ) {
470 		TNF_PROBE_0_DEBUG(av1394_isoch_autoxmit_init_error_cipsz,
471 		    AV1394_TNF_ISOCH_ERROR, "");
472 		return (EINVAL);
473 	}
474 	ret = uiomove(axp->ax_ciph, AV1394_CIPSZ, UIO_WRITE, uiop);
475 	if (ret != 0) {
476 		return (ret);
477 	}
478 	axp->ax_copy_ciph = B_TRUE;
479 
480 	/* parse CIP header */
481 	dbs = axp->ax_ciph[1];
482 	fn = (axp->ax_ciph[2] >> 6) & 0x3;
483 	fmt = axp->ax_ciph[4] & 0x3F;
484 	stype = (axp->ax_ciph[5] >> 2) & 0x1F;
485 
486 	/* fill out the init structure */
487 	bzero(&ii, sizeof (ii));
488 	ii.ii_version = IEC61883_V1_0;
489 	ii.ii_frame_cnt = av1394_isoch_autoxmit_nframes;
490 	ii.ii_direction = IEC61883_DIR_XMIT;
491 	ii.ii_bus_speed = IEC61883_S100;
492 	ii.ii_channel = (1ULL << 63);
493 	ii.ii_dbs = dbs;
494 	ii.ii_fn = fn;
495 
496 	if ((fmt == 0) && (dbs == 0x78) && (fn == 0) && (stype == 0)) {
497 		/* either DV-NTSC or DV-PAL */
498 		ii.ii_pkt_size = 488;
499 		ii.ii_ts_mode = IEC61883_TS_SYT;
500 		f5060 = axp->ax_ciph[5] & 0x80;
501 		if (f5060 == 0) {
502 			axp->ax_fmt = AV1394_ISOCH_AUTOXMIT_DV_NTSC;
503 			ii.ii_frame_size = AV1394_DV_NTSC_FRAMESZ;
504 			ii.ii_rate_n = av1394_rate_n_dv_ntsc;
505 			ii.ii_rate_d = av1394_rate_d_dv_ntsc;
506 		} else {
507 			axp->ax_fmt = AV1394_ISOCH_AUTOXMIT_DV_PAL;
508 			ii.ii_frame_size = AV1394_DV_PAL_FRAMESZ;
509 			ii.ii_rate_n = av1394_rate_n_dv_pal;
510 			ii.ii_rate_d = av1394_rate_d_dv_pal;
511 		}
512 	} else {
513 		/* raw stream */
514 		axp->ax_fmt = AV1394_ISOCH_AUTOXMIT_UNKNOWN;
515 		ii.ii_pkt_size = 512;
516 		ii.ii_frame_size = av1394_isoch_autoxmit_framesz;
517 		ii.ii_ts_mode = IEC61883_TS_NONE;
518 	}
519 
520 	ret = av1394_ic_init(avp, &ii, icpp);
521 
522 	AV1394_TNF_EXIT(av1394_isoch_autoxmit_init);
523 	return (ret);
524 }
525 
526 
527 /*
528  *
529  * --- ioctls
530  *	these routines are generally responsible for copyin/out of arguments
531  *	and passing control to the actual implementation.
532  *
533  */
534 static int
535 av1394_ioctl_isoch_init(av1394_inst_t *avp, void *arg, int mode)
536 {
537 	iec61883_isoch_init_t	ii;
538 #ifdef _MULTI_DATAMODEL
539 	iec61883_isoch_init32_t	ii32;
540 #endif
541 	av1394_ic_t		*icp;
542 	int			ret;
543 
544 	AV1394_TNF_ENTER(av1394_ioctl_isoch_init);
545 
546 	if (ddi_copyin(arg, &ii, sizeof (ii), mode) != 0) {
547 		AV1394_TNF_EXIT(av1394_ioctl_isoch_init);
548 		return (EFAULT);
549 	}
550 
551 	ret = av1394_ic_init(avp, &ii, &icp);
552 
553 	if (ret != 0) {
554 		AV1394_TNF_EXIT(av1394_ioctl_isoch_init);
555 #ifdef _MULTI_DATAMODEL
556 		if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
557 			bcopy(&ii, &ii32, sizeof (ii32));
558 			ii32.ii_error = ii.ii_error;
559 			(void) ddi_copyout(&ii32, arg, sizeof (ii32), mode);
560 		} else
561 #endif
562 		(void) ddi_copyout(&ii, arg, sizeof (ii), mode);
563 		return (ret);
564 	}
565 
566 #ifdef _MULTI_DATAMODEL
567 	/* fixup 32-bit deviations */
568 	if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) {
569 		bcopy(&ii, &ii32, sizeof (ii32));
570 		ii32.ii_mmap_off = ii.ii_mmap_off;
571 		ii32.ii_rchannel = ii.ii_rchannel;
572 		ii32.ii_error = ii.ii_error;
573 		ret = ddi_copyout(&ii32, arg, sizeof (ii32), mode);
574 	} else
575 #endif
576 	ret = ddi_copyout(&ii, arg, sizeof (ii), mode);
577 	if (ret != 0) {
578 		AV1394_TNF_EXIT(av1394_ioctl_isoch_init);
579 		return (ENOMEM);
580 	}
581 
582 	AV1394_TNF_EXIT(av1394_ioctl_isoch_init);
583 	return (ret);
584 }
585 
586 static av1394_ic_t *
587 av1394_ioctl_isoch_handle2ic(av1394_inst_t *avp, void *arg)
588 {
589 	int		num = (int)(intptr_t)arg;
590 	av1394_isoch_t	*ip = &avp->av_i;
591 
592 	if (num >= (sizeof (ip->i_ic) / sizeof (av1394_ic_t))) {
593 		TNF_PROBE_0(av1394_ioctl_isoch_handle2ic_error_range,
594 		    AV1394_TNF_ISOCH_ERROR, "");
595 		return (NULL);
596 	}
597 	if (ip->i_ic[num] == NULL) {
598 		TNF_PROBE_0(av1394_ioctl_isoch_handle2ic_error_null,
599 		    AV1394_TNF_ISOCH_ERROR, "");
600 	}
601 	return (ip->i_ic[num]);
602 }
603 
604 /*ARGSUSED*/
605 static int
606 av1394_ioctl_isoch_fini(av1394_inst_t *avp, void *arg, int mode)
607 {
608 	av1394_ic_t	*icp;
609 
610 	AV1394_TNF_ENTER(av1394_ioctl_isoch_fini);
611 
612 	if ((icp = av1394_ioctl_isoch_handle2ic(avp, arg)) != NULL) {
613 		av1394_ic_fini(icp);
614 	}
615 
616 	AV1394_TNF_EXIT(av1394_ioctl_isoch_fini);
617 	return (0);
618 }
619 
620 /*ARGSUSED*/
621 static int
622 av1394_ioctl_start(av1394_inst_t *avp, void *arg, int mode)
623 {
624 	av1394_ic_t	*icp;
625 	int		ret = EINVAL;
626 
627 	AV1394_TNF_ENTER(av1394_ioctl_start);
628 
629 	if ((icp = av1394_ioctl_isoch_handle2ic(avp, arg)) != NULL) {
630 		ret = av1394_ic_start(icp);
631 	}
632 
633 	AV1394_TNF_EXIT(av1394_ioctl_start);
634 	return (ret);
635 }
636 
637 /*ARGSUSED*/
638 static int
639 av1394_ioctl_stop(av1394_inst_t *avp, void *arg, int mode)
640 {
641 	av1394_ic_t	*icp;
642 	int		ret = EINVAL;
643 
644 	AV1394_TNF_ENTER(av1394_ioctl_stop);
645 
646 	if ((icp = av1394_ioctl_isoch_handle2ic(avp, arg)) != NULL) {
647 		ret = av1394_ic_stop(icp);
648 	}
649 
650 	AV1394_TNF_EXIT(av1394_ioctl_stop);
651 	return (ret);
652 }
653 
654 static int
655 av1394_ioctl_recv(av1394_inst_t *avp, void *arg, int mode)
656 {
657 	av1394_isoch_t	*ip = &avp->av_i;
658 	av1394_ic_t	*icp;
659 	iec61883_recv_t	recv;
660 	int		num;
661 	int		ret = EINVAL;
662 
663 	/* copyin the structure and get channel pointer */
664 	if (ddi_copyin(arg, &recv, sizeof (recv), mode) != 0) {
665 		return (EFAULT);
666 	}
667 	num = recv.rx_handle;
668 	if (num >= (sizeof (ip->i_ic) / sizeof (av1394_ic_t))) {
669 		TNF_PROBE_0(av1394_ioctl_recv_error_range,
670 		    AV1394_TNF_ISOCH_ERROR, "");
671 		return (EINVAL);
672 	}
673 	icp = ip->i_ic[num];
674 	if (icp == NULL) {
675 		TNF_PROBE_0(av1394_ioctl_recv_error_null,
676 		    AV1394_TNF_ISOCH_ERROR, "");
677 	}
678 
679 	/* now call the actual handler */
680 	if (icp->ic_dir != AV1394_IR) {
681 		ret = EINVAL;
682 	} else {
683 		ret = av1394_ir_recv(icp, &recv);
684 	}
685 
686 	/* copyout the result */
687 	if (ret == 0) {
688 		if (ddi_copyout(&recv, arg, sizeof (recv), mode) != 0) {
689 			return (EFAULT);
690 		}
691 	}
692 
693 	return (ret);
694 }
695 
696 static int
697 av1394_ioctl_xmit(av1394_inst_t *avp, void *arg, int mode)
698 {
699 	av1394_isoch_t	*ip = &avp->av_i;
700 	av1394_ic_t	*icp;
701 	iec61883_xmit_t	xmit;
702 	int		num;
703 	int		ret = EINVAL;
704 
705 	/* copyin the structure and get channel pointer */
706 	if (ddi_copyin(arg, &xmit, sizeof (xmit), mode) != 0) {
707 		return (EFAULT);
708 	}
709 	num = xmit.tx_handle;
710 	if (num >= (sizeof (ip->i_ic) / sizeof (av1394_ic_t))) {
711 		TNF_PROBE_0(av1394_ioctl_xmit_error_range,
712 		    AV1394_TNF_ISOCH_ERROR, "");
713 		return (EINVAL);
714 	}
715 	icp = ip->i_ic[num];
716 	if (icp == NULL) {
717 		TNF_PROBE_0(av1394_ioctl_xmit_error_null,
718 		    AV1394_TNF_ISOCH_ERROR, "");
719 	}
720 
721 	/* now call the actual handler */
722 	if (icp->ic_dir != AV1394_IT) {
723 		ret = EINVAL;
724 	} else {
725 		ret = av1394_it_xmit(icp, &xmit);
726 	}
727 
728 	/* copyout the result */
729 	if (ret == 0) {
730 		if (ddi_copyout(&xmit, arg, sizeof (xmit), mode) != 0) {
731 			return (EFAULT);
732 		}
733 	}
734 
735 	return (ret);
736 }
737 
738 static uint_t
739 av1394_isoch_softintr(caddr_t arg)
740 {
741 	av1394_inst_t	*avp = (av1394_inst_t *)arg;
742 	av1394_isoch_t	*ip = &avp->av_i;
743 	int		i;
744 	uint64_t	ch;
745 	av1394_ic_t	*icp;
746 
747 	mutex_enter(&ip->i_mutex);
748 	do {
749 		for (i = 63, ch = (1ULL << 63);
750 		    (i > 0) && (ip->i_softintr_ch != 0);
751 		    i--, ch >>= 1) {
752 			if ((ip->i_softintr_ch & ch) == 0) {
753 				continue;
754 			}
755 			ip->i_softintr_ch &= ~ch;
756 			icp = ip->i_ic[i];
757 			if (icp == NULL) {
758 				continue;
759 			}
760 
761 			mutex_exit(&ip->i_mutex);
762 			mutex_enter(&icp->ic_mutex);
763 			if (icp->ic_preq & AV1394_PREQ_IR_OVERFLOW) {
764 				icp->ic_preq &= ~AV1394_PREQ_IR_OVERFLOW;
765 				av1394_ir_overflow(icp);
766 			}
767 			if (icp->ic_preq & AV1394_PREQ_IT_UNDERRUN) {
768 				icp->ic_preq &= ~AV1394_PREQ_IT_UNDERRUN;
769 				av1394_it_underrun(icp);
770 			}
771 			mutex_exit(&icp->ic_mutex);
772 			mutex_enter(&ip->i_mutex);
773 		}
774 	} while (ip->i_softintr_ch != 0);
775 	mutex_exit(&ip->i_mutex);
776 
777 	return (DDI_INTR_CLAIMED);
778 }
779