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