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