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
av1394_isoch_attach(av1394_inst_t * avp)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
av1394_isoch_detach(av1394_inst_t * avp)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
av1394_isoch_cpr_suspend(av1394_inst_t * avp)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
av1394_isoch_close(av1394_inst_t * avp,int flag)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
av1394_isoch_read(av1394_inst_t * avp,struct uio * uiop)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
av1394_isoch_write(av1394_inst_t * avp,struct uio * uiop)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
av1394_isoch_ioctl(av1394_inst_t * avp,int cmd,intptr_t arg,int mode,int * rvalp)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
av1394_isoch_devmap(av1394_inst_t * avp,devmap_cookie_t dhp,offset_t off,size_t len,size_t * maplen,uint_t model)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
av1394_isoch_create_minor_node(av1394_inst_t * avp)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, NULL);
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
av1394_isoch_remove_minor_node(av1394_inst_t * avp)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
av1394_isoch_cleanup(av1394_inst_t * avp,int level)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 *
av1394_isoch_find_seg(av1394_inst_t * avp,offset_t off,size_t len)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
av1394_isoch_autorecv_init(av1394_inst_t * avp,av1394_ic_t ** icpp)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
av1394_isoch_autoxmit_init(av1394_inst_t * avp,av1394_ic_t ** icpp,struct uio * uiop)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
av1394_ioctl_isoch_init(av1394_inst_t * avp,void * arg,int mode)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 *
av1394_ioctl_isoch_handle2ic(av1394_inst_t * avp,void * arg)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
av1394_ioctl_isoch_fini(av1394_inst_t * avp,void * arg,int mode)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
av1394_ioctl_start(av1394_inst_t * avp,void * arg,int mode)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
av1394_ioctl_stop(av1394_inst_t * avp,void * arg,int mode)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
av1394_ioctl_recv(av1394_inst_t * avp,void * arg,int mode)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
av1394_ioctl_xmit(av1394_inst_t * avp,void * arg,int mode)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
av1394_isoch_softintr(caddr_t arg)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