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
av1394_fcp_attach(av1394_inst_t * avp)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
av1394_fcp_detach(av1394_inst_t * avp)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
av1394_fcp_write(av1394_inst_t * avp,iec61883_arq_t * arq,struct uio * uiop)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
av1394_fcp_ctl_register(av1394_inst_t * avp)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
av1394_fcp_tgt_register(av1394_inst_t * avp)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
av1394_fcp_ctl_alloc_cmd(av1394_inst_t * avp)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
av1394_fcp_ctl_free_cmd(av1394_inst_t * avp)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
av1394_fcp_tgt_alloc_cmd(av1394_inst_t * avp)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
av1394_fcp_tgt_free_cmd(av1394_inst_t * avp)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
av1394_fcp_cleanup(av1394_inst_t * avp,int level)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
av1394_fcp_cmd_write_sync(av1394_inst_t * avp,av1394_fcp_cmd_t * fc)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
av1394_fcp_cmd_completion_cb(struct cmd1394_cmd * cmd)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
av1394_fcp_resp_write_request_cb(cmd1394_cmd_t * req)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
av1394_fcp_cmd_write_request_cb(cmd1394_cmd_t * req)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
av1394_fcp_common_write_request_cb(cmd1394_cmd_t * req,int mtype)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
av1394_fcp_copyin_block(iec61883_arq_t * arq,mblk_t * mp,struct uio * uiop)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