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