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