xref: /illumos-gate/usr/src/uts/common/io/1394/targets/av1394/av1394_fcp.c (revision 3ce5372277f4657ad0e52d36c979527c4ca22de2)
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 FCP module
31  */
32 #include <sys/stat.h>
33 #include <sys/ddi.h>
34 #include <sys/sunddi.h>
35 #include <sys/1394/targets/av1394/av1394_impl.h>
36 
37 /* configuration routines */
38 static int	av1394_fcp_ctl_register(av1394_inst_t *);
39 static int	av1394_fcp_tgt_register(av1394_inst_t *);
40 static int	av1394_fcp_ctl_alloc_cmd(av1394_inst_t *);
41 static void	av1394_fcp_ctl_free_cmd(av1394_inst_t *);
42 static int	av1394_fcp_tgt_alloc_cmd(av1394_inst_t *);
43 static void	av1394_fcp_tgt_free_cmd(av1394_inst_t *);
44 static void	av1394_fcp_cleanup(av1394_inst_t *, int);
45 
46 /* FCP write and completion handling */
47 static int	av1394_fcp_cmd_write_sync(av1394_inst_t *, av1394_fcp_cmd_t *);
48 static void	av1394_fcp_cmd_completion_cb(struct cmd1394_cmd *);
49 static int	av1394_fcp_cmd_write_request_cb(cmd1394_cmd_t *);
50 static int	av1394_fcp_resp_write_request_cb(cmd1394_cmd_t *);
51 static void	av1394_fcp_common_write_request_cb(cmd1394_cmd_t *, int);
52 
53 /* misc routines */
54 static int	av1394_fcp_copyin_block(iec61883_arq_t *, mblk_t *,
55 		struct uio *);
56 
57 #define	AV1394_TNF_ENTER(func)	\
58 	TNF_PROBE_0_DEBUG(func##_enter, AV1394_TNF_FCP_STACK, "");
59 
60 #define	AV1394_TNF_EXIT(func)	\
61 	TNF_PROBE_0_DEBUG(func##_exit, AV1394_TNF_FCP_STACK, "");
62 
63 /*
64  *
65  * --- configuration entry points
66  *
67  */
68 int
69 av1394_fcp_attach(av1394_inst_t *avp)
70 {
71 	av1394_fcp_t	*fcp = &avp->av_a.a_fcp;
72 	int		ret;
73 
74 	AV1394_TNF_ENTER(av1394_fcp_attach);
75 
76 	/* register FCP controller */
77 	if ((ret = av1394_fcp_ctl_register(avp)) != DDI_SUCCESS) {
78 		AV1394_TNF_EXIT(av1394_fcp_attach);
79 		return (ret);
80 	}
81 
82 	/* allocate FCP controller command */
83 	if ((ret = av1394_fcp_ctl_alloc_cmd(avp)) != DDI_SUCCESS) {
84 		av1394_fcp_cleanup(avp, 1);
85 		AV1394_TNF_EXIT(av1394_fcp_attach);
86 		return (ret);
87 	}
88 
89 	/* register FCP target */
90 	if ((ret = av1394_fcp_tgt_register(avp)) != DDI_SUCCESS) {
91 		av1394_fcp_cleanup(avp, 2);
92 		AV1394_TNF_EXIT(av1394_fcp_attach);
93 		return (ret);
94 	}
95 
96 	/* allocate FCP target command */
97 	if ((ret = av1394_fcp_tgt_alloc_cmd(avp)) != DDI_SUCCESS) {
98 		av1394_fcp_cleanup(avp, 3);
99 		AV1394_TNF_EXIT(av1394_fcp_attach);
100 		return (ret);
101 	}
102 
103 	cv_init(&fcp->fcp_cmd.fc_xmit_cv, NULL, CV_DRIVER, NULL);
104 	cv_init(&fcp->fcp_cmd.fc_busy_cv, NULL, CV_DRIVER, NULL);
105 	cv_init(&fcp->fcp_resp.fc_xmit_cv, NULL, CV_DRIVER, NULL);
106 	cv_init(&fcp->fcp_resp.fc_busy_cv, NULL, CV_DRIVER, NULL);
107 
108 	AV1394_TNF_EXIT(av1394_fcp_attach);
109 	return (ret);
110 }
111 
112 void
113 av1394_fcp_detach(av1394_inst_t *avp)
114 {
115 	AV1394_TNF_ENTER(av1394_fcp_detach);
116 
117 	av1394_fcp_cleanup(avp, AV1394_CLEANUP_LEVEL_MAX);
118 
119 	AV1394_TNF_EXIT(av1394_fcp_detach);
120 }
121 
122 int
123 av1394_fcp_write(av1394_inst_t *avp, iec61883_arq_t *arq, struct uio *uiop)
124 {
125 	av1394_async_t	*ap = &avp->av_a;
126 	av1394_fcp_t	*fcp = &ap->a_fcp;
127 	int		len = arq->arq_len;
128 	av1394_fcp_cmd_t *fc;
129 	cmd1394_cmd_t	*cmd;
130 	mblk_t		*mp = NULL;
131 	int		ret;
132 
133 	AV1394_TNF_ENTER(av1394_fcp_write);
134 
135 	ASSERT((arq->arq_type == IEC61883_ARQ_FCP_CMD) ||
136 		(arq->arq_type == IEC61883_ARQ_FCP_RESP));
137 
138 	/* check arguments */
139 	if ((len == 0) || (len > AV1394_FCP_ARQ_LEN_MAX) ||
140 	    (len % IEEE1394_QUADLET != 0)) {
141 		TNF_PROBE_1(av1394_fcp_write_error,
142 		    AV1394_TNF_FCP_ERROR, "", tnf_int, len, len);
143 		AV1394_TNF_EXIT(av1394_fcp_write);
144 		return (EINVAL);
145 	}
146 
147 	/* block write requires an mblk */
148 	if (len > IEEE1394_QUADLET) {
149 		if ((mp = allocb(len, BPRI_HI)) == NULL) {
150 			AV1394_TNF_EXIT(av1394_fcp_write);
151 			return (ENOMEM);
152 		}
153 		if ((ret = av1394_fcp_copyin_block(arq, mp, uiop)) != 0) {
154 			freemsg(mp);
155 			AV1394_TNF_EXIT(av1394_fcp_write);
156 			return (ret);
157 		}
158 	}
159 
160 	/* either FCP command or response */
161 	fc = (arq->arq_type == IEC61883_ARQ_FCP_CMD) ?
162 					&fcp->fcp_cmd : &fcp->fcp_resp;
163 
164 	/* one command at a time */
165 	mutex_enter(&ap->a_mutex);
166 	while (fc->fc_busy) {
167 		if (cv_wait_sig(&fc->fc_busy_cv, &ap->a_mutex) == 0) {
168 			mutex_exit(&ap->a_mutex);
169 			freemsg(mp);
170 			AV1394_TNF_EXIT(av1394_fcp_write);
171 			return (EINTR);
172 		}
173 	}
174 	fc->fc_busy = B_TRUE;
175 
176 	/* prepare command */
177 	cmd = fc->fc_cmd;
178 	if (len == IEEE1394_QUADLET) {
179 		cmd->cmd_type = CMD1394_ASYNCH_WR_QUAD;
180 		cmd->cmd_u.q.quadlet_data = arq->arq_data.quadlet;
181 	} else {
182 		cmd->cmd_type = CMD1394_ASYNCH_WR_BLOCK;
183 		cmd->cmd_u.b.data_block = mp;
184 		cmd->cmd_u.b.blk_length = len;
185 	}
186 
187 	/* do the write and wait for completion */
188 	ret = av1394_fcp_cmd_write_sync(avp, fc);
189 
190 	/* not busy anymore */
191 	fc->fc_busy = B_FALSE;
192 	cv_broadcast(&fc->fc_busy_cv);
193 	mutex_exit(&ap->a_mutex);
194 
195 	freemsg(mp);
196 
197 	AV1394_TNF_EXIT(av1394_fcp_write);
198 	return (ret);
199 }
200 
201 /*
202  *
203  * --- configuration routines
204  *
205  */
206 static int
207 av1394_fcp_ctl_register(av1394_inst_t *avp)
208 {
209 	t1394_fcp_evts_t evts;
210 	int		ret;
211 
212 	evts.fcp_write_request = av1394_fcp_resp_write_request_cb;
213 	evts.fcp_arg = avp;
214 
215 	ret = t1394_fcp_register_controller(avp->av_t1394_hdl, &evts, 0);
216 	if (ret != DDI_SUCCESS) {
217 		TNF_PROBE_1(av1394_fcp_ctl_register_error,
218 		    AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret);
219 	}
220 	return (ret);
221 }
222 
223 static int
224 av1394_fcp_tgt_register(av1394_inst_t *avp)
225 {
226 	t1394_fcp_evts_t evts;
227 	int		ret;
228 
229 	evts.fcp_write_request = av1394_fcp_cmd_write_request_cb;
230 	evts.fcp_arg = avp;
231 
232 	ret = t1394_fcp_register_target(avp->av_t1394_hdl, &evts, 0);
233 	if (ret != DDI_SUCCESS) {
234 		TNF_PROBE_1(av1394_fcp_tgt_register_error,
235 		    AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret);
236 	}
237 	return (ret);
238 }
239 
240 static int
241 av1394_fcp_ctl_alloc_cmd(av1394_inst_t *avp)
242 {
243 	av1394_fcp_cmd_t *fc = &avp->av_a.a_fcp.fcp_cmd;
244 	int		ret;
245 
246 	ret = t1394_alloc_cmd(avp->av_t1394_hdl, T1394_ALLOC_CMD_FCP_COMMAND,
247 				&fc->fc_cmd);
248 	if (ret != DDI_SUCCESS) {
249 		TNF_PROBE_1(av1394_fcp_ctl_alloc_cmd_error,
250 		    AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret);
251 	}
252 	return (ret);
253 }
254 
255 static void
256 av1394_fcp_ctl_free_cmd(av1394_inst_t *avp)
257 {
258 	av1394_fcp_cmd_t *fc = &avp->av_a.a_fcp.fcp_cmd;
259 	int		ret;
260 
261 	ret = t1394_free_cmd(avp->av_t1394_hdl, 0, &fc->fc_cmd);
262 	if (ret != DDI_SUCCESS) {
263 		TNF_PROBE_1(av1394_fcp_ctl_free_cmd_error,
264 		    AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret);
265 	}
266 }
267 
268 static int
269 av1394_fcp_tgt_alloc_cmd(av1394_inst_t *avp)
270 {
271 	av1394_fcp_cmd_t *fc = &avp->av_a.a_fcp.fcp_resp;
272 	int		ret;
273 
274 	ret = t1394_alloc_cmd(avp->av_t1394_hdl, T1394_ALLOC_CMD_FCP_RESPONSE,
275 				&fc->fc_cmd);
276 	if (ret != DDI_SUCCESS) {
277 		TNF_PROBE_1(av1394_fcp_tgt_alloc_cmd_error,
278 		    AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret);
279 	}
280 	return (ret);
281 }
282 
283 static void
284 av1394_fcp_tgt_free_cmd(av1394_inst_t *avp)
285 {
286 	av1394_fcp_cmd_t *fc = &avp->av_a.a_fcp.fcp_resp;
287 	int		ret;
288 
289 	ret = t1394_free_cmd(avp->av_t1394_hdl, 0, &fc->fc_cmd);
290 	if (ret != DDI_SUCCESS) {
291 		TNF_PROBE_1(av1394_fcp_tgt_free_cmd_error,
292 		    AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret);
293 	}
294 }
295 
296 static void
297 av1394_fcp_cleanup(av1394_inst_t *avp, int level)
298 {
299 	av1394_fcp_t	*fcp = &avp->av_a.a_fcp;
300 
301 	ASSERT((level > 0) && (level <= AV1394_CLEANUP_LEVEL_MAX));
302 
303 	switch (level) {
304 	default:
305 		cv_destroy(&fcp->fcp_cmd.fc_xmit_cv);
306 		cv_destroy(&fcp->fcp_cmd.fc_busy_cv);
307 		cv_destroy(&fcp->fcp_resp.fc_xmit_cv);
308 		cv_destroy(&fcp->fcp_resp.fc_busy_cv);
309 
310 		av1394_fcp_tgt_free_cmd(avp);
311 		/* FALLTHRU */
312 	case 3:
313 		(void) t1394_fcp_unregister_target(avp->av_t1394_hdl);
314 		/* FALLTHRU */
315 	case 2:
316 		av1394_fcp_ctl_free_cmd(avp);
317 		/* FALLTHRU */
318 	case 1:
319 		(void) t1394_fcp_unregister_controller(avp->av_t1394_hdl);
320 	}
321 }
322 
323 /*
324  *
325  * --- FCP write and completion handling
326  *
327  */
328 static int
329 av1394_fcp_cmd_write_sync(av1394_inst_t *avp, av1394_fcp_cmd_t *fc)
330 {
331 	av1394_async_t	*ap = &avp->av_a;
332 	cmd1394_cmd_t	*cmd = fc->fc_cmd;
333 	int		ret = 0;
334 
335 	cmd->completion_callback = av1394_fcp_cmd_completion_cb;
336 	cmd->cmd_callback_arg = avp;
337 
338 	/* go */
339 	ASSERT(!fc->fc_xmit);
340 	fc->fc_xmit = B_TRUE;
341 
342 	mutex_exit(&ap->a_mutex);
343 	ret = t1394_write(avp->av_t1394_hdl, cmd);
344 	mutex_enter(&ap->a_mutex);
345 
346 	/* immediate error? */
347 	if (ret != DDI_SUCCESS) {
348 		fc->fc_xmit = B_FALSE;
349 		TNF_PROBE_2(av1394_fcp_cmd_write_sync_error,
350 		    AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret,
351 		    tnf_int, cmd_result, cmd->cmd_result);
352 		return (EIO);
353 	}
354 
355 	/* wait for completion */
356 	while (fc->fc_xmit) {
357 		if (cv_wait_sig(&fc->fc_xmit_cv, &ap->a_mutex) == 0) {
358 			return (EINTR);
359 		}
360 	}
361 
362 	if (cmd->cmd_result != CMD1394_CMDSUCCESS) {
363 		TNF_PROBE_1(av1394_fcp_cmd_write_sync_error,
364 		    AV1394_TNF_FCP_ERROR, "",
365 		    tnf_int, cmd_result, cmd->cmd_result);
366 		if (cmd->cmd_result == CMD1394_EDEVICE_BUSY) {
367 			return (EBUSY);
368 		} else {
369 			return (EIO);
370 		}
371 	} else {
372 		return (0);
373 	}
374 }
375 
376 static void
377 av1394_fcp_cmd_completion_cb(struct cmd1394_cmd *cmd)
378 {
379 	av1394_inst_t	*avp = cmd->cmd_callback_arg;
380 	av1394_async_t	*ap = &avp->av_a;
381 	av1394_fcp_t	*fcp = &ap->a_fcp;
382 	av1394_fcp_cmd_t *fc;
383 
384 	AV1394_TNF_ENTER(av1394_fcp_cmd_completion_cb);
385 
386 	mutex_enter(&ap->a_mutex);
387 	/* is this FCP command or response */
388 	if (cmd == fcp->fcp_cmd.fc_cmd) {
389 		fc = &fcp->fcp_cmd;
390 	} else {
391 		ASSERT(cmd == fcp->fcp_resp.fc_cmd);
392 		fc = &fcp->fcp_resp;
393 	}
394 
395 	/* wake the waiter */
396 	fc->fc_xmit = B_FALSE;
397 	cv_signal(&fc->fc_xmit_cv);
398 	mutex_exit(&ap->a_mutex);
399 
400 	AV1394_TNF_EXIT(av1394_fcp_cmd_completion_cb);
401 }
402 
403 /*
404  * av1394_fcp_cmd_write_request_cb()
405  *    Incoming response request from an FCP target
406  */
407 static int
408 av1394_fcp_resp_write_request_cb(cmd1394_cmd_t *req)
409 {
410 	av1394_inst_t	*avp = req->cmd_callback_arg;
411 	av1394_async_t	*ap = &avp->av_a;
412 
413 	AV1394_TNF_ENTER(av1394_fcp_resp_write_request_cb);
414 
415 	mutex_enter(&ap->a_mutex);
416 	if ((ap->a_nopen == 0) ||
417 	    (req->bus_generation != ap->a_bus_generation) ||
418 	    (req->nodeID != ap->a_targetinfo.target_nodeID)) {
419 		mutex_exit(&ap->a_mutex);
420 
421 		AV1394_TNF_EXIT(av1394_fcp_resp_write_request_cb);
422 		return (T1394_REQ_UNCLAIMED);
423 	}
424 	mutex_exit(&ap->a_mutex);
425 
426 	av1394_fcp_common_write_request_cb(req, AV1394_M_FCP_RESP);
427 
428 	AV1394_TNF_EXIT(av1394_fcp_resp_write_request_cb);
429 	return (T1394_REQ_CLAIMED);
430 }
431 
432 /*
433  * av1394_fcp_cmd_write_request_cb()
434  *    Incoming command request from an FCP controller
435  */
436 static int
437 av1394_fcp_cmd_write_request_cb(cmd1394_cmd_t *req)
438 {
439 	av1394_inst_t	*avp = req->cmd_callback_arg;
440 	av1394_async_t	*ap = &avp->av_a;
441 
442 	AV1394_TNF_ENTER(av1394_fcp_cmd_write_request_cb);
443 
444 	mutex_enter(&ap->a_mutex);
445 	if (ap->a_nopen == 0) {
446 		mutex_exit(&ap->a_mutex);
447 
448 		AV1394_TNF_EXIT(av1394_fcp_cmd_write_request_cb);
449 		return (T1394_REQ_UNCLAIMED);
450 	}
451 	mutex_exit(&ap->a_mutex);
452 
453 	av1394_fcp_common_write_request_cb(req, AV1394_M_FCP_CMD);
454 
455 	AV1394_TNF_EXIT(av1394_fcp_cmd_write_request_cb);
456 	return (T1394_REQ_CLAIMED);
457 }
458 
459 static void
460 av1394_fcp_common_write_request_cb(cmd1394_cmd_t *req, int mtype)
461 {
462 	av1394_inst_t	*avp = req->cmd_callback_arg;
463 	mblk_t		*mp;
464 	uint32_t	quadlet_data;
465 	int		err;
466 
467 	AV1394_TNF_ENTER(av1394_fcp_common_write_request_cb);
468 
469 	ASSERT((req->cmd_type == CMD1394_ASYNCH_WR_QUAD) ||
470 		(req->cmd_type == CMD1394_ASYNCH_WR_BLOCK));
471 
472 	/* get the data */
473 	if (req->cmd_type == CMD1394_ASYNCH_WR_QUAD) {
474 		quadlet_data = req->cmd_u.q.quadlet_data;
475 	} else {
476 		mp = req->cmd_u.b.data_block;
477 		req->cmd_u.b.data_block = NULL;
478 	}
479 
480 	/* complete request */
481 	req->cmd_result = IEEE1394_RESP_COMPLETE;
482 
483 	err = t1394_recv_request_done(avp->av_t1394_hdl, req, 0);
484 	if (err != DDI_SUCCESS) {
485 		TNF_PROBE_2(av1394_fcp_common_write_request_cb_done_error,
486 		    AV1394_TNF_FCP_ERROR, "", tnf_int, err, err,
487 		    tnf_int, result, req->cmd_result);
488 	}
489 
490 	/* allocate mblk and copy quadlet into it */
491 	if (req->cmd_type == CMD1394_ASYNCH_WR_QUAD) {
492 		mp = allocb(IEEE1394_QUADLET, BPRI_HI);
493 		if (mp == NULL) {
494 			TNF_PROBE_0(
495 			    av1394_fcp_common_write_request_cb_allocb_error,
496 			    AV1394_TNF_FCP_ERROR, "");
497 			AV1394_TNF_EXIT(av1394_fcp_common_write_request_cb);
498 			return;
499 		}
500 		*(uint32_t *)mp->b_rptr = quadlet_data;
501 		mp->b_wptr += IEEE1394_QUADLET;
502 	}
503 
504 	/* queue up the data */
505 	DB_TYPE(mp) = mtype;
506 	av1394_async_putq_rq(avp, mp);
507 
508 	AV1394_TNF_EXIT(av1394_fcp_common_write_request_cb);
509 }
510 
511 /*
512  *
513  * --- misc routines
514  *
515  */
516 static int
517 av1394_fcp_copyin_block(iec61883_arq_t *arq, mblk_t *mp, struct uio *uiop)
518 {
519 	int	len = arq->arq_len;
520 	int	copylen;
521 	int	ret = 0;
522 
523 	ASSERT((len > 0) && (len % IEEE1394_QUADLET == 0));
524 
525 	/* first copy ARQ-embedded data */
526 	copylen = min(len, sizeof (arq->arq_data));
527 	bcopy(&arq->arq_data.buf[0], mp->b_wptr, copylen);
528 	mp->b_wptr += copylen;
529 
530 	/* now copyin the rest of the data, if any */
531 	copylen = len - copylen;
532 	if (copylen > 0) {
533 		ret = uiomove(mp->b_wptr, copylen, UIO_WRITE, uiop);
534 		if (ret != 0) {
535 			TNF_PROBE_1(av1394_fcp_copyin_block_error,
536 			    AV1394_TNF_FCP_ERROR, "", tnf_int, ret, ret);
537 			return (ret);
538 		}
539 		mp->b_wptr += copylen;
540 	}
541 	return (ret);
542 }
543