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
av1394_async_attach(av1394_inst_t * avp)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
av1394_async_detach(av1394_inst_t * avp)93 av1394_async_detach(av1394_inst_t *avp)
94 {
95 av1394_async_cleanup(avp, AV1394_CLEANUP_LEVEL_MAX);
96 }
97
98 void
av1394_async_bus_reset(av1394_inst_t * avp)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
av1394_async_cpr_resume(av1394_inst_t * avp)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
av1394_async_reconnect(av1394_inst_t * avp)131 av1394_async_reconnect(av1394_inst_t *avp)
132 {
133 (void) av1394_async_update_targetinfo(avp);
134 }
135
136 int
av1394_async_open(av1394_inst_t * avp,int flag)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
av1394_async_close(av1394_inst_t * avp,int flag)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
av1394_async_read(av1394_inst_t * avp,struct uio * uiop)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
av1394_async_write(av1394_inst_t * avp,struct uio * uiop)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
av1394_async_ioctl(av1394_inst_t * avp,int cmd,intptr_t arg,int mode,int * rvalp)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
av1394_async_poll(av1394_inst_t * avp,short events,int anyyet,short * reventsp,struct pollhead ** phpp)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
av1394_async_putq_rq(av1394_inst_t * avp,mblk_t * mp)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
av1394_async_cleanup(av1394_inst_t * avp,int level)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
av1394_async_create_minor_node(av1394_inst_t * avp)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
av1394_async_remove_minor_node(av1394_inst_t * avp)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
av1394_async_update_targetinfo(av1394_inst_t * avp)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
av1394_async_db2arq_type(int dbtype)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
av1394_async_putbq(av1394_queue_t * q,mblk_t * mp)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
av1394_ioctl_arq_get_ibuf_size(av1394_inst_t * avp,void * arg,int mode)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
av1394_ioctl_arq_set_ibuf_size(av1394_inst_t * avp,void * arg,int mode)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