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