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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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