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 2005 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  * 1394 mass storage SBP-2 bus routines
31  */
32 
33 #include <sys/param.h>
34 #include <sys/errno.h>
35 #include <sys/cred.h>
36 #include <sys/conf.h>
37 #include <sys/modctl.h>
38 #include <sys/stat.h>
39 #include <sys/ddi.h>
40 #include <sys/sunddi.h>
41 
42 #include <sys/sbp2/bus.h>
43 #include <sys/1394/targets/scsa1394/impl.h>
44 
45 static ddi_iblock_cookie_t scsa1394_bus_get_iblock_cookie(void *);
46 static uint_t	scsa1394_bus_get_node_id(void *);
47 static int	scsa1394_bus_alloc_cmd(void *, void **, int);
48 static void	scsa1394_bus_free_cmd(void *, void *);
49 static int	scsa1394_bus_rq(void *, void *, uint64_t, uint32_t *, int *);
50 static int	scsa1394_bus_rb(void *, void *, uint64_t, mblk_t **, int,
51 		int *);
52 static int	scsa1394_bus_wq(void *, void *, uint64_t, uint32_t, int *);
53 static int	scsa1394_bus_wb(void *, void *, uint64_t, mblk_t *, int,
54 		int *);
55 static int	scsa1394_bus_alloc_buf(void *, sbp2_bus_buf_t *);
56 static int	scsa1394_bus_alloc_buf_phys(void *, sbp2_bus_buf_t *);
57 static void	scsa1394_bus_free_buf_phys(void *, sbp2_bus_buf_t *);
58 static int	scsa1394_bus_alloc_buf_normal(void *, sbp2_bus_buf_t *,
59 		boolean_t);
60 static void	scsa1394_bus_free_buf_normal(void *, sbp2_bus_buf_t *);
61 static void	scsa1394_bus_free_buf(void *, sbp2_bus_buf_t *);
62 static int	scsa1394_bus_sync_buf(void *, sbp2_bus_buf_t *, off_t, size_t,
63 		int);
64 static void	scsa1394_bus_buf_rw_done(void *, sbp2_bus_buf_t *, void *, int);
65 
66 /* callbacks */
67 static void	scsa1394_bus_recv_read_request(cmd1394_cmd_t *);
68 static void	scsa1394_bus_recv_write_request(cmd1394_cmd_t *);
69 
70 sbp2_bus_t scsa1394_sbp2_bus = {
71 	SBP2_BUS_REV,			/* rev */
72 	0xFFFFF0000000LL,		/* csr_base */
73 	IEEE1394_CONFIG_ROM_ADDR,	/* cfgrom_addr */
74 	scsa1394_bus_get_iblock_cookie,	/* get_iblock_cookie */
75 	scsa1394_bus_get_node_id,	/* get_node_id */
76 	scsa1394_bus_alloc_buf,		/* alloc_buf */
77 	scsa1394_bus_free_buf,		/* free_buf */
78 	scsa1394_bus_sync_buf,		/* sync_buf */
79 	scsa1394_bus_buf_rw_done,	/* buf_rd_done */
80 	scsa1394_bus_buf_rw_done,	/* buf_wr_done */
81 	scsa1394_bus_alloc_cmd,		/* alloc_cmd */
82 	scsa1394_bus_free_cmd,		/* free_cmd */
83 	scsa1394_bus_rq,		/* rq */
84 	scsa1394_bus_rb,		/* rb */
85 	scsa1394_bus_wq,		/* wq */
86 	scsa1394_bus_wb			/* wb */
87 };
88 
89 /*
90  * fault injector
91  *
92  * global on/off switch
93  */
94 int scsa1394_bus_fi_on = 0;
95 
96 /* fault probabilities per operation, in tenths of percent, i.e. 10 is 1% */
97 int scsa1394_bus_fi_prob_alloc_buf = 10;
98 int scsa1394_bus_fi_prob_alloc_cmd = 10;
99 int scsa1394_bus_fi_prob_rq = 10;
100 int scsa1394_bus_fi_prob_rb = 10;
101 int scsa1394_bus_fi_prob_wq = 10;
102 int scsa1394_bus_fi_prob_wb = 10;
103 
104 #define	SCSA1394_BUS_FI_POSITIVE(p) (scsa1394_bus_fi_on &&	\
105 	((p) > 0) && ((gethrtime() % (p)) == 0))
106 
107 /*
108  * translate command result to SBP2 error code
109  */
110 static int
scsa1394_bus_rw_result2code(int result)111 scsa1394_bus_rw_result2code(int result)
112 {
113 	int	code;
114 
115 	switch (result) {
116 	case CMD1394_EDEVICE_BUSY:
117 		code = SBP2_EBUSY;
118 		break;
119 	case CMD1394_EADDRESS_ERROR:
120 		code = SBP2_EADDR;
121 		break;
122 	case CMD1394_ETIMEOUT:
123 	case CMD1394_ERETRIES_EXCEEDED:
124 		code = SBP2_ETIMEOUT;
125 		break;
126 	case CMD1394_EDEVICE_REMOVED:
127 		code = SBP2_ENODEV;
128 		break;
129 	default:
130 		code = SBP2_EIO;
131 		break;
132 	}
133 	return (code);
134 }
135 
136 static ddi_iblock_cookie_t
scsa1394_bus_get_iblock_cookie(void * hdl)137 scsa1394_bus_get_iblock_cookie(void *hdl)
138 {
139 	scsa1394_state_t *sp = hdl;
140 
141 	return (sp->s_attachinfo.iblock_cookie);
142 }
143 
144 static uint_t
scsa1394_bus_get_node_id(void * hdl)145 scsa1394_bus_get_node_id(void *hdl)
146 {
147 	scsa1394_state_t *sp = hdl;
148 
149 	return (sp->s_attachinfo.localinfo.local_nodeID);
150 }
151 
152 
153 /*ARGSUSED*/
154 static int
scsa1394_bus_alloc_cmd(void * hdl,void ** cmdp,int flags)155 scsa1394_bus_alloc_cmd(void *hdl, void **cmdp, int flags)
156 {
157 	scsa1394_state_t *sp = hdl;
158 	cmd1394_cmd_t	*cmd;
159 
160 	if (SCSA1394_BUS_FI_POSITIVE(scsa1394_bus_fi_prob_alloc_cmd)) {
161 		return (SBP2_ENOMEM);
162 	}
163 
164 	if (t1394_alloc_cmd(sp->s_t1394_hdl, 0, &cmd) != DDI_SUCCESS) {
165 		return (SBP2_ENOMEM);
166 	}
167 	*cmdp = cmd;
168 	return (SBP2_SUCCESS);
169 }
170 
171 
172 static void
scsa1394_bus_free_cmd(void * hdl,void * argcmd)173 scsa1394_bus_free_cmd(void *hdl, void *argcmd)
174 {
175 	scsa1394_state_t *sp = hdl;
176 	cmd1394_cmd_t	*cmd = argcmd;
177 
178 	(void) t1394_free_cmd(sp->s_t1394_hdl, 0, &cmd);
179 }
180 
181 
182 /*ARGSUSED*/
183 static int
scsa1394_bus_rq(void * hdl,void * argcmd,uint64_t addr,uint32_t * q,int * berr)184 scsa1394_bus_rq(void *hdl, void *argcmd, uint64_t addr, uint32_t *q, int *berr)
185 {
186 	scsa1394_state_t *sp = hdl;
187 	cmd1394_cmd_t	*cmd = argcmd;
188 
189 	if (SCSA1394_BUS_FI_POSITIVE(scsa1394_bus_fi_prob_rq)) {
190 		return (SBP2_EIO);
191 	}
192 
193 	cmd->cmd_addr = addr;
194 	cmd->cmd_type = CMD1394_ASYNCH_RD_QUAD;
195 	cmd->cmd_options = CMD1394_BLOCKING;
196 
197 	if ((t1394_read(sp->s_t1394_hdl, cmd) != DDI_SUCCESS) ||
198 	    (cmd->cmd_result != CMD1394_CMDSUCCESS)) {
199 		*berr = cmd->cmd_result;
200 		return (scsa1394_bus_rw_result2code(cmd->cmd_result));
201 	}
202 
203 	*q = cmd->cmd_u.q.quadlet_data;
204 	return (SBP2_SUCCESS);
205 }
206 
207 
208 /*ARGSUSED*/
209 static int
scsa1394_bus_rb(void * hdl,void * argcmd,uint64_t addr,mblk_t ** bpp,int len,int * berr)210 scsa1394_bus_rb(void *hdl, void *argcmd, uint64_t addr, mblk_t **bpp, int len,
211     int *berr)
212 {
213 	scsa1394_state_t *sp = hdl;
214 	cmd1394_cmd_t	*cmd = argcmd;
215 	mblk_t		*bp = *bpp;
216 
217 	/* caller wants us to allocate memory */
218 	if ((bp == NULL) && ((bp = allocb(len, BPRI_HI)) == NULL)) {
219 		return (SBP2_ENOMEM);
220 	}
221 
222 	cmd->cmd_addr = addr;
223 	cmd->cmd_type = CMD1394_ASYNCH_RD_BLOCK;
224 	cmd->cmd_u.b.data_block = bp;
225 	cmd->cmd_u.b.blk_length = len;
226 	cmd->cmd_options = CMD1394_BLOCKING;
227 
228 	if ((t1394_read(sp->s_t1394_hdl, cmd) != DDI_SUCCESS) ||
229 	    (cmd->cmd_result != CMD1394_CMDSUCCESS)) {
230 		freeb(bp);
231 		*berr = cmd->cmd_result;
232 		return (scsa1394_bus_rw_result2code(cmd->cmd_result));
233 	}
234 
235 	*bpp = bp;
236 	return (SBP2_SUCCESS);
237 }
238 
239 
240 /*ARGSUSED*/
241 static int
scsa1394_bus_wq(void * hdl,void * argcmd,uint64_t addr,uint32_t q,int * berr)242 scsa1394_bus_wq(void *hdl, void *argcmd, uint64_t addr, uint32_t q, int *berr)
243 {
244 	scsa1394_state_t *sp = hdl;
245 	cmd1394_cmd_t	*cmd = argcmd;
246 
247 	cmd->cmd_addr = addr;
248 	cmd->cmd_type = CMD1394_ASYNCH_WR_QUAD;
249 	cmd->cmd_u.q.quadlet_data = q;
250 	cmd->cmd_options = CMD1394_BLOCKING;
251 
252 	if ((t1394_write(sp->s_t1394_hdl, cmd) != DDI_SUCCESS) ||
253 	    (cmd->cmd_result != CMD1394_CMDSUCCESS)) {
254 		*berr = cmd->cmd_result;
255 		return (scsa1394_bus_rw_result2code(cmd->cmd_result));
256 	}
257 
258 	return (SBP2_SUCCESS);
259 }
260 
261 
262 /*ARGSUSED*/
263 static int
scsa1394_bus_wb(void * hdl,void * argcmd,uint64_t addr,mblk_t * bp,int len,int * berr)264 scsa1394_bus_wb(void *hdl, void *argcmd, uint64_t addr, mblk_t *bp, int len,
265     int *berr)
266 {
267 	scsa1394_state_t *sp = hdl;
268 	cmd1394_cmd_t	*cmd = argcmd;
269 
270 	cmd->cmd_addr = addr;
271 	cmd->cmd_type = CMD1394_ASYNCH_WR_BLOCK;
272 	cmd->cmd_u.b.data_block = bp;
273 	cmd->cmd_u.b.blk_length = len;
274 	cmd->cmd_options = CMD1394_BLOCKING;
275 
276 	if ((t1394_write(sp->s_t1394_hdl, cmd) != DDI_SUCCESS) ||
277 	    (cmd->cmd_result != CMD1394_CMDSUCCESS)) {
278 		*berr = cmd->cmd_result;
279 		return (scsa1394_bus_rw_result2code(cmd->cmd_result));
280 	}
281 
282 	return (SBP2_SUCCESS);
283 }
284 
285 
286 /*ARGSUSED*/
287 static int
scsa1394_bus_alloc_buf(void * hdl,sbp2_bus_buf_t * buf)288 scsa1394_bus_alloc_buf(void *hdl, sbp2_bus_buf_t *buf)
289 {
290 	if (SCSA1394_BUS_FI_POSITIVE(scsa1394_bus_fi_prob_alloc_buf)) {
291 		return (SBP2_ENOMEM);
292 	}
293 
294 	if (buf->bb_flags & SBP2_BUS_BUF_DMA) {
295 		return (scsa1394_bus_alloc_buf_phys(hdl, buf));
296 	} else {
297 		return (scsa1394_bus_alloc_buf_normal(hdl, buf,
298 		    ((buf->bb_flags & SBP2_BUS_BUF_POSTED) != 0)));
299 	}
300 }
301 
302 
303 static void
scsa1394_bus_free_buf(void * hdl,sbp2_bus_buf_t * buf)304 scsa1394_bus_free_buf(void *hdl, sbp2_bus_buf_t *buf)
305 {
306 	if (buf->bb_flags & SBP2_BUS_BUF_DMA) {
307 		scsa1394_bus_free_buf_phys(hdl, buf);
308 	} else {
309 		scsa1394_bus_free_buf_normal(hdl, buf);
310 	}
311 }
312 
313 
314 static int
scsa1394_bus_alloc_buf_phys(void * hdl,sbp2_bus_buf_t * buf)315 scsa1394_bus_alloc_buf_phys(void *hdl, sbp2_bus_buf_t *buf)
316 {
317 	scsa1394_state_t	*sp = hdl;
318 	scsa1394_bus_buf_t	*sbb;		/* bus private structure */
319 	size_t			real_length;	/* real allocated length */
320 	ddi_dma_cookie_t	cookie;		/* cookies */
321 	uint_t			ccount;		/* cookie count */
322 	t1394_alloc_addr_t	aa;
323 	int			result;
324 
325 	/* allocate bus private structure */
326 	sbb = kmem_zalloc(sizeof (scsa1394_bus_buf_t), KM_SLEEP);
327 	sbb->sbb_state = sp;
328 
329 	/* allocate DMA resources */
330 	if (ddi_dma_alloc_handle(sp->s_dip, &sp->s_attachinfo.dma_attr,
331 	    DDI_DMA_SLEEP, NULL, &sbb->sbb_dma_hdl) != DDI_SUCCESS) {
332 		kmem_free(sbb, sizeof (scsa1394_bus_buf_t));
333 		return (SBP2_ENOMEM);
334 	}
335 
336 	if (ddi_dma_mem_alloc(sbb->sbb_dma_hdl, buf->bb_len,
337 	    &sp->s_attachinfo.acc_attr,
338 	    buf->bb_flags & (DDI_DMA_STREAMING | DDI_DMA_CONSISTENT),
339 	    DDI_DMA_SLEEP, NULL, &buf->bb_kaddr, &real_length,
340 	    &sbb->sbb_acc_hdl) != DDI_SUCCESS) {
341 		ddi_dma_free_handle(&sbb->sbb_dma_hdl);
342 		kmem_free(sbb, sizeof (scsa1394_bus_buf_t));
343 		return (SBP2_ENOMEM);
344 	}
345 
346 	buf->bb_flags &= ~DDI_DMA_PARTIAL;
347 	if (ddi_dma_addr_bind_handle(sbb->sbb_dma_hdl, NULL, buf->bb_kaddr,
348 	    buf->bb_len, buf->bb_flags, DDI_DMA_SLEEP, NULL,
349 	    &cookie, &ccount) != DDI_DMA_MAPPED) {
350 		ddi_dma_mem_free(&sbb->sbb_acc_hdl);
351 		ddi_dma_free_handle(&sbb->sbb_dma_hdl);
352 		kmem_free(sbb, sizeof (scsa1394_bus_buf_t));
353 		return (SBP2_ENOMEM);
354 	}
355 	ASSERT(ccount == 1);
356 	buf->bb_paddr = cookie.dmac_address;	/* 32-bit address */
357 
358 	/* allocate 1394 resources */
359 	bzero(&aa, sizeof (aa));
360 	aa.aa_type = T1394_ADDR_FIXED;
361 	aa.aa_length = buf->bb_len;
362 	if (buf->bb_flags & SBP2_BUS_BUF_RD) {
363 		aa.aa_enable |= T1394_ADDR_RDENBL;
364 	}
365 	if (buf->bb_flags & SBP2_BUS_BUF_WR) {
366 		aa.aa_enable |= T1394_ADDR_WRENBL;
367 	}
368 	aa.aa_address = buf->bb_paddr;		/* PCI-1394 mapping is 1-1 */
369 
370 	if (t1394_alloc_addr(sp->s_t1394_hdl, &aa, 0, &result) != DDI_SUCCESS) {
371 		(void) ddi_dma_unbind_handle(sbb->sbb_dma_hdl);
372 		ddi_dma_mem_free(&sbb->sbb_acc_hdl);
373 		ddi_dma_free_handle(&sbb->sbb_dma_hdl);
374 		kmem_free(sbb, sizeof (scsa1394_bus_buf_t));
375 		return (SBP2_ENOMEM);
376 	}
377 	sbb->sbb_addr_hdl = aa.aa_hdl;
378 	buf->bb_baddr = aa.aa_address;
379 
380 	buf->bb_hdl = sbb;
381 	return (SBP2_SUCCESS);
382 }
383 
384 
385 static void
scsa1394_bus_free_buf_phys(void * hdl,sbp2_bus_buf_t * buf)386 scsa1394_bus_free_buf_phys(void *hdl, sbp2_bus_buf_t *buf)
387 {
388 	scsa1394_state_t	*sp = hdl;
389 	scsa1394_bus_buf_t	*sbb = buf->bb_hdl;
390 
391 	(void) t1394_free_addr(sp->s_t1394_hdl, &sbb->sbb_addr_hdl, 0);
392 	(void) ddi_dma_unbind_handle(sbb->sbb_dma_hdl);
393 	ddi_dma_mem_free(&sbb->sbb_acc_hdl);
394 	ddi_dma_free_handle(&sbb->sbb_dma_hdl);
395 	kmem_free(sbb, sizeof (scsa1394_bus_buf_t));
396 	buf->bb_hdl = NULL;
397 }
398 
399 
400 static int
scsa1394_bus_alloc_buf_normal(void * hdl,sbp2_bus_buf_t * buf,boolean_t posted)401 scsa1394_bus_alloc_buf_normal(void *hdl, sbp2_bus_buf_t *buf, boolean_t posted)
402 {
403 	scsa1394_state_t 	*sp = hdl;
404 	scsa1394_bus_buf_t	*sbb;		/* bus private structure */
405 	t1394_alloc_addr_t	aa;
406 	int			result;
407 
408 	/* allocate bus private structure */
409 	sbb = kmem_zalloc(sizeof (scsa1394_bus_buf_t), KM_SLEEP);
410 	sbb->sbb_state = sp;
411 
412 	/* allocate 1394 resources */
413 	bzero(&aa, sizeof (aa));
414 	aa.aa_type = posted ? T1394_ADDR_POSTED_WRITE : T1394_ADDR_NORMAL;
415 	aa.aa_length = buf->bb_len;
416 	if (buf->bb_flags & SBP2_BUS_BUF_RD) {
417 		aa.aa_enable |= T1394_ADDR_RDENBL;
418 		aa.aa_evts.recv_read_request = scsa1394_bus_recv_read_request;
419 	}
420 	if (buf->bb_flags & SBP2_BUS_BUF_WR) {
421 		aa.aa_enable |= T1394_ADDR_WRENBL;
422 		aa.aa_evts.recv_write_request = scsa1394_bus_recv_write_request;
423 	}
424 	aa.aa_arg = buf;
425 
426 	if (t1394_alloc_addr(sp->s_t1394_hdl, &aa, 0, &result) != DDI_SUCCESS) {
427 		kmem_free(sbb, sizeof (scsa1394_bus_buf_t));
428 		return (SBP2_ENOMEM);
429 	}
430 	sbb->sbb_addr_hdl = aa.aa_hdl;
431 	buf->bb_baddr = aa.aa_address;
432 
433 	buf->bb_hdl = sbb;
434 	return (SBP2_SUCCESS);
435 }
436 
437 static void
scsa1394_bus_free_buf_normal(void * hdl,sbp2_bus_buf_t * buf)438 scsa1394_bus_free_buf_normal(void *hdl, sbp2_bus_buf_t *buf)
439 {
440 	scsa1394_state_t 	*sp = hdl;
441 	scsa1394_bus_buf_t	*sbb = buf->bb_hdl;
442 
443 	(void) t1394_free_addr(sp->s_t1394_hdl, &sbb->sbb_addr_hdl, 0);
444 	kmem_free(sbb, sizeof (scsa1394_bus_buf_t));
445 	buf->bb_hdl = NULL;
446 }
447 
448 /*ARGSUSED*/
449 static int
scsa1394_bus_sync_buf(void * hdl,sbp2_bus_buf_t * buf,off_t offset,size_t length,int type)450 scsa1394_bus_sync_buf(void *hdl, sbp2_bus_buf_t *buf, off_t offset,
451     size_t length, int type)
452 {
453 	scsa1394_bus_buf_t	*sbb = buf->bb_hdl;
454 
455 	if (buf->bb_flags & SBP2_BUS_BUF_DMA) {
456 		return (ddi_dma_sync(sbb->sbb_dma_hdl, offset, length, type));
457 	} else {
458 		return (SBP2_SUCCESS);
459 	}
460 }
461 
462 /*ARGSUSED*/
463 static void
scsa1394_bus_buf_rw_done(void * hdl,sbp2_bus_buf_t * buf,void * reqh,int error)464 scsa1394_bus_buf_rw_done(void *hdl, sbp2_bus_buf_t *buf, void *reqh, int error)
465 {
466 	scsa1394_state_t	*sp = hdl;
467 	cmd1394_cmd_t		*req = reqh;
468 
469 	/* complete request */
470 	switch (error) {
471 	case SBP2_BUS_BUF_SUCCESS:
472 		req->cmd_result = IEEE1394_RESP_COMPLETE;
473 		break;
474 	case SBP2_BUS_BUF_ELENGTH:
475 		req->cmd_result = IEEE1394_RESP_DATA_ERROR;
476 		break;
477 	case SBP2_BUS_BUF_EBUSY:
478 		req->cmd_result = IEEE1394_RESP_CONFLICT_ERROR;
479 		break;
480 	default:
481 		req->cmd_result = IEEE1394_RESP_TYPE_ERROR;
482 	}
483 	(void) t1394_recv_request_done(sp->s_t1394_hdl, req, 0);
484 }
485 
486 
487 /*
488  *
489  * --- callbacks
490  *
491  */
492 static void
scsa1394_bus_recv_read_request(cmd1394_cmd_t * req)493 scsa1394_bus_recv_read_request(cmd1394_cmd_t *req)
494 {
495 	sbp2_bus_buf_t		*buf = req->cmd_callback_arg;
496 	scsa1394_bus_buf_t	*sbb = buf->bb_hdl;
497 	scsa1394_state_t	*sp = sbb->sbb_state;
498 
499 	/* XXX sanity checks: addr, etc */
500 	if (req->cmd_type == CMD1394_ASYNCH_RD_QUAD) {
501 		if (buf->bb_rq_cb) {
502 			buf->bb_rq_cb(buf, req, &req->cmd_u.q.quadlet_data);
503 			return;
504 		}
505 	} else {
506 		if (buf->bb_rb_cb) {
507 			buf->bb_rb_cb(buf, req, &req->cmd_u.b.data_block,
508 			    req->cmd_u.b.blk_length);
509 			return;
510 		}
511 	}
512 	scsa1394_bus_buf_rw_done(sp, buf, req, SBP2_BUS_BUF_FAILURE);
513 }
514 
515 
516 static void
scsa1394_bus_recv_write_request(cmd1394_cmd_t * req)517 scsa1394_bus_recv_write_request(cmd1394_cmd_t *req)
518 {
519 	sbp2_bus_buf_t		*buf = req->cmd_callback_arg;
520 	scsa1394_bus_buf_t	*sbb = buf->bb_hdl;
521 	scsa1394_state_t	*sp = sbb->sbb_state;
522 
523 	/* XXX sanity checks: addr, etc */
524 	if (req->cmd_type == CMD1394_ASYNCH_WR_QUAD) {
525 		if (buf->bb_wq_cb) {
526 			buf->bb_wq_cb(buf, req, req->cmd_u.q.quadlet_data);
527 			return;
528 		}
529 	} else {
530 		if (buf->bb_wb_cb) {
531 			buf->bb_wb_cb(buf, req, &req->cmd_u.b.data_block);
532 			return;
533 		}
534 	}
535 	scsa1394_bus_buf_rw_done(sp, buf, req, SBP2_BUS_BUF_FAILURE);
536 }
537