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 asynchronous 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/1394/targets/av1394/av1394_impl.h>
37
38 /* configuration routines */
39 static void av1394_async_cleanup(av1394_inst_t *, int);
40 static int av1394_async_create_minor_node(av1394_inst_t *);
41 static void av1394_async_remove_minor_node(av1394_inst_t *);
42 static int av1394_async_update_targetinfo(av1394_inst_t *);
43 static int av1394_async_db2arq_type(int);
44 static void av1394_async_putbq(av1394_queue_t *, mblk_t *);
45
46 static int av1394_ioctl_arq_get_ibuf_size(av1394_inst_t *, void *, int);
47 static int av1394_ioctl_arq_set_ibuf_size(av1394_inst_t *, void *, int);
48
49 #define AV1394_TNF_ENTER(func) \
50 TNF_PROBE_0_DEBUG(func##_enter, AV1394_TNF_ASYNC_STACK, "");
51
52 #define AV1394_TNF_EXIT(func) \
53 TNF_PROBE_0_DEBUG(func##_exit, AV1394_TNF_ASYNC_STACK, "");
54
55 /* tunables */
56 int av1394_ibuf_size_default = 64 * 1024; /* default ibuf size */
57 int av1394_ibuf_size_max = 1024 * 1024; /* max ibuf size */
58
59 /*
60 *
61 * --- configuration entry points
62 *
63 */
64 int
av1394_async_attach(av1394_inst_t * avp)65 av1394_async_attach(av1394_inst_t *avp)
66 {
67 av1394_async_t *ap = &avp->av_a;
68 ddi_iblock_cookie_t ibc = avp->av_attachinfo.iblock_cookie;
69
70 AV1394_TNF_ENTER(av1394_async_attach);
71
72 mutex_init(&ap->a_mutex, NULL, MUTEX_DRIVER, ibc);
73 av1394_initq(&ap->a_rq, ibc, av1394_ibuf_size_default);
74
75 if (av1394_fcp_attach(avp) != DDI_SUCCESS) {
76 av1394_async_cleanup(avp, 1);
77 AV1394_TNF_EXIT(av1394_async_attach);
78 return (DDI_FAILURE);
79 }
80
81 if (av1394_cfgrom_init(avp) != DDI_SUCCESS) {
82 av1394_async_cleanup(avp, 2);
83 AV1394_TNF_EXIT(av1394_async_attach);
84 return (DDI_FAILURE);
85 }
86
87 if (av1394_async_create_minor_node(avp) != DDI_SUCCESS) {
88 av1394_async_cleanup(avp, 3);
89 AV1394_TNF_EXIT(av1394_async_attach);
90 return (DDI_FAILURE);
91 }
92
93 if (av1394_async_update_targetinfo(avp) != DDI_SUCCESS) {
94 av1394_async_cleanup(avp, 4);
95 AV1394_TNF_EXIT(av1394_async_attach);
96 return (DDI_FAILURE);
97 }
98
99 AV1394_TNF_EXIT(av1394_async_attach);
100 return (DDI_SUCCESS);
101 }
102
103 void
av1394_async_detach(av1394_inst_t * avp)104 av1394_async_detach(av1394_inst_t *avp)
105 {
106 AV1394_TNF_ENTER(av1394_async_detach);
107
108 av1394_async_cleanup(avp, AV1394_CLEANUP_LEVEL_MAX);
109
110 AV1394_TNF_EXIT(av1394_async_detach);
111 }
112
113 void
av1394_async_bus_reset(av1394_inst_t * avp)114 av1394_async_bus_reset(av1394_inst_t *avp)
115 {
116 av1394_async_t *ap = &avp->av_a;
117 mblk_t *bp;
118
119 AV1394_TNF_ENTER(av1394_async_bus_reset);
120
121 (void) av1394_async_update_targetinfo(avp);
122
123 mutex_enter(&ap->a_mutex);
124 if (ap->a_nopen > 0) {
125 mutex_exit(&ap->a_mutex);
126 return;
127 }
128 mutex_exit(&ap->a_mutex);
129
130 /* queue up a bus reset message */
131 if ((bp = allocb(1, BPRI_HI)) == NULL) {
132 TNF_PROBE_0(av1394_async_bus_reset_error_allocb,
133 AV1394_TNF_ASYNC_ERROR, "");
134 } else {
135 DB_TYPE(bp) = AV1394_M_BUS_RESET;
136 av1394_async_putq_rq(avp, bp);
137 }
138
139 AV1394_TNF_EXIT(av1394_async_bus_reset);
140 }
141
142 int
av1394_async_cpr_resume(av1394_inst_t * avp)143 av1394_async_cpr_resume(av1394_inst_t *avp)
144 {
145 int ret;
146
147 AV1394_TNF_ENTER(av1394_async_cpr_resume);
148
149 ret = av1394_async_update_targetinfo(avp);
150
151 AV1394_TNF_EXIT(av1394_async_cpr_resume);
152 return (ret);
153 }
154
155 void
av1394_async_reconnect(av1394_inst_t * avp)156 av1394_async_reconnect(av1394_inst_t *avp)
157 {
158 AV1394_TNF_ENTER(av1394_async_reconnect);
159
160 (void) av1394_async_update_targetinfo(avp);
161
162 AV1394_TNF_EXIT(av1394_async_reconnect);
163 }
164
165 int
av1394_async_open(av1394_inst_t * avp,int flag)166 av1394_async_open(av1394_inst_t *avp, int flag)
167 {
168 av1394_async_t *ap = &avp->av_a;
169
170 AV1394_TNF_ENTER(av1394_async_open);
171
172 mutex_enter(&ap->a_mutex);
173 if (ap->a_nopen == 0) {
174 ap->a_pollevents = 0;
175 }
176 ap->a_nopen++;
177 ap->a_oflag = flag;
178 mutex_exit(&ap->a_mutex);
179
180 AV1394_TNF_EXIT(av1394_async_open);
181 return (0);
182 }
183
184 /*ARGSUSED*/
185 int
av1394_async_close(av1394_inst_t * avp,int flag)186 av1394_async_close(av1394_inst_t *avp, int flag)
187 {
188 av1394_async_t *ap = &avp->av_a;
189
190 AV1394_TNF_ENTER(av1394_async_close);
191
192 av1394_cfgrom_close(avp);
193
194 av1394_flushq(&ap->a_rq);
195
196 mutex_enter(&ap->a_mutex);
197 ap->a_nopen = 0;
198 ap->a_pollevents = 0;
199 mutex_exit(&ap->a_mutex);
200
201 AV1394_TNF_EXIT(av1394_async_close);
202 return (0);
203 }
204
205 int
av1394_async_read(av1394_inst_t * avp,struct uio * uiop)206 av1394_async_read(av1394_inst_t *avp, struct uio *uiop)
207 {
208 av1394_async_t *ap = &avp->av_a;
209 av1394_queue_t *q = &ap->a_rq;
210 iec61883_arq_t arq;
211 int ret = 0;
212 mblk_t *mp;
213 int dbtype;
214 int len;
215
216 AV1394_TNF_ENTER(av1394_async_read);
217
218 /* copyout as much as we can */
219 while ((uiop->uio_resid > 0) && (ret == 0)) {
220 /*
221 * if data is available, copy it out. otherwise wait until
222 * data arrives, unless opened with non-blocking flag
223 */
224 if ((mp = av1394_getq(q)) == NULL) {
225 if (ap->a_oflag & FNDELAY) {
226 AV1394_TNF_EXIT(av1394_async_read);
227 return (EAGAIN);
228 }
229 if (av1394_qwait_sig(q) <= 0) {
230 ret = EINTR;
231 }
232 continue;
233 }
234 dbtype = AV1394_DBTYPE(mp);
235
236 /* generate and copyout ARQ header, if not already */
237 if (!AV1394_IS_NOHDR(mp)) {
238 /* headers cannot be partially read */
239 if (uiop->uio_resid < sizeof (arq)) {
240 av1394_async_putbq(q, mp);
241 ret = EINVAL;
242 break;
243 }
244
245 arq.arq_type = av1394_async_db2arq_type(dbtype);
246 arq.arq_len = MBLKL(mp);
247 arq.arq_data.octlet = 0;
248
249 /* copy ARQ-embedded data */
250 len = min(arq.arq_len, sizeof (arq.arq_data));
251 bcopy(mp->b_rptr, &arq.arq_data.buf[0], len);
252
253 /* copyout the ARQ */
254 ret = uiomove(&arq, sizeof (arq), UIO_READ, uiop);
255 if (ret != 0) {
256 av1394_async_putbq(q, mp);
257 break;
258 }
259 mp->b_rptr += len;
260 AV1394_MARK_NOHDR(mp);
261 }
262
263 /* any data left? */
264 if (MBLKL(mp) == 0) {
265 freemsg(mp);
266 continue;
267 }
268
269 /* now we have some data and some user buffer space to fill */
270 len = min(uiop->uio_resid, MBLKL(mp));
271 if (len > 0) {
272 ret = uiomove(mp->b_rptr, len, UIO_READ, uiop);
273 if (ret != 0) {
274 av1394_async_putbq(q, mp);
275 break;
276 }
277 mp->b_rptr += len;
278 }
279
280 /* save the rest of the data for later */
281 if (MBLKL(mp) > 0) {
282 av1394_async_putbq(q, mp);
283 }
284 }
285
286 AV1394_TNF_EXIT(av1394_async_read);
287 return (0);
288 }
289
290 int
av1394_async_write(av1394_inst_t * avp,struct uio * uiop)291 av1394_async_write(av1394_inst_t *avp, struct uio *uiop)
292 {
293 iec61883_arq_t arq;
294 int ret;
295
296 AV1394_TNF_ENTER(av1394_async_write);
297
298 /* all data should arrive in ARQ format */
299 while (uiop->uio_resid >= sizeof (arq)) {
300 if ((ret = uiomove(&arq, sizeof (arq), UIO_WRITE, uiop)) != 0) {
301 break;
302 }
303
304 switch (arq.arq_type) {
305 case IEC61883_ARQ_FCP_CMD:
306 case IEC61883_ARQ_FCP_RESP:
307 ret = av1394_fcp_write(avp, &arq, uiop);
308 break;
309 default:
310 ret = EINVAL;
311 }
312 if (ret != 0) {
313 break;
314 }
315 }
316
317 AV1394_TNF_EXIT(av1394_async_write);
318 return (ret);
319 }
320
321 /*ARGSUSED*/
322 int
av1394_async_ioctl(av1394_inst_t * avp,int cmd,intptr_t arg,int mode,int * rvalp)323 av1394_async_ioctl(av1394_inst_t *avp, int cmd, intptr_t arg, int mode,
324 int *rvalp)
325 {
326 int ret = EINVAL;
327
328 AV1394_TNF_ENTER(av1394_async_ioctl);
329
330 switch (cmd) {
331 case IEC61883_ARQ_GET_IBUF_SIZE:
332 ret = av1394_ioctl_arq_get_ibuf_size(avp, (void *)arg, mode);
333 break;
334 case IEC61883_ARQ_SET_IBUF_SIZE:
335 ret = av1394_ioctl_arq_set_ibuf_size(avp, (void *)arg, mode);
336 break;
337 case IEC61883_NODE_GET_BUS_NAME:
338 ret = av1394_ioctl_node_get_bus_name(avp, (void *)arg, mode);
339 break;
340 case IEC61883_NODE_GET_UID:
341 ret = av1394_ioctl_node_get_uid(avp, (void *)arg, mode);
342 break;
343 case IEC61883_NODE_GET_TEXT_LEAF:
344 ret = av1394_ioctl_node_get_text_leaf(avp, (void *)arg, mode);
345 }
346
347 AV1394_TNF_EXIT(av1394_async_ioctl);
348 return (ret);
349 }
350
351 /*ARGSUSED*/
352 int
av1394_async_poll(av1394_inst_t * avp,short events,int anyyet,short * reventsp,struct pollhead ** phpp)353 av1394_async_poll(av1394_inst_t *avp, short events, int anyyet, short *reventsp,
354 struct pollhead **phpp)
355 {
356 av1394_async_t *ap = &avp->av_a;
357 av1394_queue_t *rq = &ap->a_rq;
358
359 AV1394_TNF_ENTER(av1394_async_poll);
360
361 if (events & POLLIN) {
362 if (av1394_peekq(rq)) {
363 *reventsp |= POLLIN;
364 } else if (!anyyet) {
365 mutex_enter(&ap->a_mutex);
366 ap->a_pollevents |= POLLIN;
367 *phpp = &ap->a_pollhead;
368 mutex_exit(&ap->a_mutex);
369 }
370 }
371
372 AV1394_TNF_EXIT(av1394_async_poll);
373 return (0);
374 }
375
376
377 /*
378 * put a message on the read queue, take care of polling
379 */
380 void
av1394_async_putq_rq(av1394_inst_t * avp,mblk_t * mp)381 av1394_async_putq_rq(av1394_inst_t *avp, mblk_t *mp)
382 {
383 av1394_async_t *ap = &avp->av_a;
384
385 if (!av1394_putq(&ap->a_rq, mp)) {
386 freemsg(mp);
387 TNF_PROBE_0(av1394_async_putq_rq_error_putq,
388 AV1394_TNF_ASYNC_ERROR, "");
389 } else {
390 mutex_enter(&ap->a_mutex);
391 if (ap->a_pollevents & POLLIN) {
392 ap->a_pollevents &= ~POLLIN;
393 mutex_exit(&ap->a_mutex);
394 pollwakeup(&ap->a_pollhead, POLLIN);
395 } else {
396 mutex_exit(&ap->a_mutex);
397 }
398 }
399 }
400
401 /*
402 *
403 * --- configuration routines
404 *
405 * av1394_async_cleanup()
406 * Cleanup after attach
407 */
408 static void
av1394_async_cleanup(av1394_inst_t * avp,int level)409 av1394_async_cleanup(av1394_inst_t *avp, int level)
410 {
411 av1394_async_t *ap = &avp->av_a;
412
413 ASSERT((level > 0) && (level <= AV1394_CLEANUP_LEVEL_MAX));
414
415 switch (level) {
416 default:
417 av1394_async_remove_minor_node(avp);
418 /* FALLTHRU */
419 case 3:
420 av1394_cfgrom_fini(avp);
421 /* FALLTHRU */
422 case 2:
423 av1394_fcp_detach(avp);
424 /* FALLTHRU */
425 case 1:
426 av1394_destroyq(&ap->a_rq);
427 mutex_destroy(&ap->a_mutex);
428 }
429 }
430
431 /*
432 * av1394_async_create_minor_node()
433 * Create async minor node
434 */
435 static int
av1394_async_create_minor_node(av1394_inst_t * avp)436 av1394_async_create_minor_node(av1394_inst_t *avp)
437 {
438 int ret;
439
440 ret = ddi_create_minor_node(avp->av_dip, "async",
441 S_IFCHR, AV1394_ASYNC_INST2MINOR(avp->av_instance),
442 DDI_NT_AV_ASYNC, NULL);
443 if (ret != DDI_SUCCESS) {
444 TNF_PROBE_0(av1394_async_create_minor_node_error,
445 AV1394_TNF_ASYNC_ERROR, "");
446 }
447 return (ret);
448 }
449
450 /*
451 * av1394_async_remove_minor_node()
452 * Remove async minor node
453 */
454 static void
av1394_async_remove_minor_node(av1394_inst_t * avp)455 av1394_async_remove_minor_node(av1394_inst_t *avp)
456 {
457 ddi_remove_minor_node(avp->av_dip, "async");
458 }
459
460 /*
461 * av1394_async_update_targetinfo()
462 * Retrieve target info and bus generation
463 */
464 static int
av1394_async_update_targetinfo(av1394_inst_t * avp)465 av1394_async_update_targetinfo(av1394_inst_t *avp)
466 {
467 av1394_async_t *ap = &avp->av_a;
468 uint_t bg;
469 int ret;
470
471 mutex_enter(&avp->av_mutex);
472 bg = avp->av_attachinfo.localinfo.bus_generation;
473 mutex_exit(&avp->av_mutex);
474
475 mutex_enter(&ap->a_mutex);
476 ret = t1394_get_targetinfo(avp->av_t1394_hdl, bg, 0, &ap->a_targetinfo);
477 ap->a_bus_generation = bg;
478 mutex_exit(&ap->a_mutex);
479
480 return (ret);
481 }
482
483 static int
av1394_async_db2arq_type(int dbtype)484 av1394_async_db2arq_type(int dbtype)
485 {
486 int arq_type;
487
488 switch (dbtype) {
489 case AV1394_M_FCP_RESP:
490 arq_type = IEC61883_ARQ_FCP_RESP;
491 break;
492 case AV1394_M_FCP_CMD:
493 arq_type = IEC61883_ARQ_FCP_CMD;
494 break;
495 case AV1394_M_BUS_RESET:
496 arq_type = IEC61883_ARQ_BUS_RESET;
497 break;
498 default:
499 ASSERT(0); /* cannot happen */
500 }
501 return (arq_type);
502 }
503
504 static void
av1394_async_putbq(av1394_queue_t * q,mblk_t * mp)505 av1394_async_putbq(av1394_queue_t *q, mblk_t *mp)
506 {
507 if (!av1394_putbq(q, mp)) {
508 freemsg(mp);
509 TNF_PROBE_0(av1394_async_putbq_error,
510 AV1394_TNF_ASYNC_ERROR, "");
511 }
512 }
513
514 /*ARGSUSED*/
515 static int
av1394_ioctl_arq_get_ibuf_size(av1394_inst_t * avp,void * arg,int mode)516 av1394_ioctl_arq_get_ibuf_size(av1394_inst_t *avp, void *arg, int mode)
517 {
518 av1394_async_t *ap = &avp->av_a;
519 int sz;
520 int ret = 0;
521
522 AV1394_TNF_ENTER(av1394_ioctl_arq_get_ibuf_size);
523
524 sz = av1394_getmaxq(&ap->a_rq);
525
526 if (ddi_copyout(&sz, arg, sizeof (sz), mode) != 0) {
527 ret = EFAULT;
528 }
529
530 AV1394_TNF_EXIT(av1394_ioctl_arq_get_ibuf_size);
531 return (ret);
532 }
533
534 /*ARGSUSED*/
535 static int
av1394_ioctl_arq_set_ibuf_size(av1394_inst_t * avp,void * arg,int mode)536 av1394_ioctl_arq_set_ibuf_size(av1394_inst_t *avp, void *arg, int mode)
537 {
538 av1394_async_t *ap = &avp->av_a;
539 int sz;
540 int ret = 0;
541
542 AV1394_TNF_ENTER(av1394_ioctl_arq_set_ibuf_size);
543
544 sz = (int)(intptr_t)arg;
545
546 if ((sz < 0) || (sz > av1394_ibuf_size_max)) {
547 ret = EINVAL;
548 } else {
549 av1394_setmaxq(&ap->a_rq, sz);
550 }
551
552 AV1394_TNF_EXIT(av1394_ioctl_arq_set_ibuf_size);
553 return (ret);
554 }
555