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, 0);
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 }
560