xref: /illumos-gate/usr/src/uts/common/io/1394/targets/av1394/av1394_async.c (revision 2e67aa296fc3707ae8e2b532f67387daf0823499)
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
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 /*ARGSUSED*/
354 int
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
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
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
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
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
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
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
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
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
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