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 (c) 2014, Joyent, Inc. All rights reserved.
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
av1394_async_attach(av1394_inst_t * avp)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
av1394_async_detach(av1394_inst_t * avp)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
av1394_async_bus_reset(av1394_inst_t * avp)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
av1394_async_cpr_resume(av1394_inst_t * avp)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
av1394_async_reconnect(av1394_inst_t * avp)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
av1394_async_open(av1394_inst_t * avp,int flag)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
av1394_async_close(av1394_inst_t * avp,int flag)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
av1394_async_read(av1394_inst_t * avp,struct uio * uiop)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
av1394_async_write(av1394_inst_t * avp,struct uio * uiop)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
av1394_async_ioctl(av1394_inst_t * avp,int cmd,intptr_t arg,int mode,int * rvalp)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 /*ARGSUSED*/
354 int
av1394_async_poll(av1394_inst_t * avp,short events,int anyyet,short * reventsp,struct pollhead ** phpp)355 av1394_async_poll(av1394_inst_t *avp, short events, int anyyet, short *reventsp,
356 struct pollhead **phpp)
357 {
358 av1394_async_t *ap = &avp->av_a;
359 av1394_queue_t *rq = &ap->a_rq;
360
361 AV1394_TNF_ENTER(av1394_async_poll);
362
363 if (events & POLLIN) {
364 if (av1394_peekq(rq))
365 *reventsp |= POLLIN;
366
367 if ((!*reventsp && !anyyet) || (events & POLLET)) {
368 mutex_enter(&ap->a_mutex);
369 ap->a_pollevents |= POLLIN;
370 *phpp = &ap->a_pollhead;
371 mutex_exit(&ap->a_mutex);
372 }
373 }
374
375 AV1394_TNF_EXIT(av1394_async_poll);
376 return (0);
377 }
378
379
380 /*
381 * put a message on the read queue, take care of polling
382 */
383 void
av1394_async_putq_rq(av1394_inst_t * avp,mblk_t * mp)384 av1394_async_putq_rq(av1394_inst_t *avp, mblk_t *mp)
385 {
386 av1394_async_t *ap = &avp->av_a;
387
388 if (!av1394_putq(&ap->a_rq, mp)) {
389 freemsg(mp);
390 TNF_PROBE_0(av1394_async_putq_rq_error_putq,
391 AV1394_TNF_ASYNC_ERROR, "");
392 } else {
393 mutex_enter(&ap->a_mutex);
394 if (ap->a_pollevents & POLLIN) {
395 ap->a_pollevents &= ~POLLIN;
396 mutex_exit(&ap->a_mutex);
397 pollwakeup(&ap->a_pollhead, POLLIN);
398 } else {
399 mutex_exit(&ap->a_mutex);
400 }
401 }
402 }
403
404 /*
405 *
406 * --- configuration routines
407 *
408 * av1394_async_cleanup()
409 * Cleanup after attach
410 */
411 static void
av1394_async_cleanup(av1394_inst_t * avp,int level)412 av1394_async_cleanup(av1394_inst_t *avp, int level)
413 {
414 av1394_async_t *ap = &avp->av_a;
415
416 ASSERT((level > 0) && (level <= AV1394_CLEANUP_LEVEL_MAX));
417
418 switch (level) {
419 default:
420 av1394_async_remove_minor_node(avp);
421 /* FALLTHRU */
422 case 3:
423 av1394_cfgrom_fini(avp);
424 /* FALLTHRU */
425 case 2:
426 av1394_fcp_detach(avp);
427 /* FALLTHRU */
428 case 1:
429 av1394_destroyq(&ap->a_rq);
430 mutex_destroy(&ap->a_mutex);
431 }
432 }
433
434 /*
435 * av1394_async_create_minor_node()
436 * Create async minor node
437 */
438 static int
av1394_async_create_minor_node(av1394_inst_t * avp)439 av1394_async_create_minor_node(av1394_inst_t *avp)
440 {
441 int ret;
442
443 ret = ddi_create_minor_node(avp->av_dip, "async",
444 S_IFCHR, AV1394_ASYNC_INST2MINOR(avp->av_instance),
445 DDI_NT_AV_ASYNC, NULL);
446 if (ret != DDI_SUCCESS) {
447 TNF_PROBE_0(av1394_async_create_minor_node_error,
448 AV1394_TNF_ASYNC_ERROR, "");
449 }
450 return (ret);
451 }
452
453 /*
454 * av1394_async_remove_minor_node()
455 * Remove async minor node
456 */
457 static void
av1394_async_remove_minor_node(av1394_inst_t * avp)458 av1394_async_remove_minor_node(av1394_inst_t *avp)
459 {
460 ddi_remove_minor_node(avp->av_dip, "async");
461 }
462
463 /*
464 * av1394_async_update_targetinfo()
465 * Retrieve target info and bus generation
466 */
467 static int
av1394_async_update_targetinfo(av1394_inst_t * avp)468 av1394_async_update_targetinfo(av1394_inst_t *avp)
469 {
470 av1394_async_t *ap = &avp->av_a;
471 uint_t bg;
472 int ret;
473
474 mutex_enter(&avp->av_mutex);
475 bg = avp->av_attachinfo.localinfo.bus_generation;
476 mutex_exit(&avp->av_mutex);
477
478 mutex_enter(&ap->a_mutex);
479 ret = t1394_get_targetinfo(avp->av_t1394_hdl, bg, 0, &ap->a_targetinfo);
480 ap->a_bus_generation = bg;
481 mutex_exit(&ap->a_mutex);
482
483 return (ret);
484 }
485
486 static int
av1394_async_db2arq_type(int dbtype)487 av1394_async_db2arq_type(int dbtype)
488 {
489 int arq_type;
490
491 switch (dbtype) {
492 case AV1394_M_FCP_RESP:
493 arq_type = IEC61883_ARQ_FCP_RESP;
494 break;
495 case AV1394_M_FCP_CMD:
496 arq_type = IEC61883_ARQ_FCP_CMD;
497 break;
498 case AV1394_M_BUS_RESET:
499 arq_type = IEC61883_ARQ_BUS_RESET;
500 break;
501 default:
502 ASSERT(0); /* cannot happen */
503 }
504 return (arq_type);
505 }
506
507 static void
av1394_async_putbq(av1394_queue_t * q,mblk_t * mp)508 av1394_async_putbq(av1394_queue_t *q, mblk_t *mp)
509 {
510 if (!av1394_putbq(q, mp)) {
511 freemsg(mp);
512 TNF_PROBE_0(av1394_async_putbq_error,
513 AV1394_TNF_ASYNC_ERROR, "");
514 }
515 }
516
517 /*ARGSUSED*/
518 static int
av1394_ioctl_arq_get_ibuf_size(av1394_inst_t * avp,void * arg,int mode)519 av1394_ioctl_arq_get_ibuf_size(av1394_inst_t *avp, void *arg, int mode)
520 {
521 av1394_async_t *ap = &avp->av_a;
522 int sz;
523 int ret = 0;
524
525 AV1394_TNF_ENTER(av1394_ioctl_arq_get_ibuf_size);
526
527 sz = av1394_getmaxq(&ap->a_rq);
528
529 if (ddi_copyout(&sz, arg, sizeof (sz), mode) != 0) {
530 ret = EFAULT;
531 }
532
533 AV1394_TNF_EXIT(av1394_ioctl_arq_get_ibuf_size);
534 return (ret);
535 }
536
537 /*ARGSUSED*/
538 static int
av1394_ioctl_arq_set_ibuf_size(av1394_inst_t * avp,void * arg,int mode)539 av1394_ioctl_arq_set_ibuf_size(av1394_inst_t *avp, void *arg, int mode)
540 {
541 av1394_async_t *ap = &avp->av_a;
542 int sz;
543 int ret = 0;
544
545 AV1394_TNF_ENTER(av1394_ioctl_arq_set_ibuf_size);
546
547 sz = (int)(intptr_t)arg;
548
549 if ((sz < 0) || (sz > av1394_ibuf_size_max)) {
550 ret = EINVAL;
551 } else {
552 av1394_setmaxq(&ap->a_rq, sz);
553 }
554
555 AV1394_TNF_EXIT(av1394_ioctl_arq_set_ibuf_size);
556 return (ret);
557 }
558