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