1 /* @(#)scsi-mac-iokit.c	1.17 10/05/24 Copyright 1997,2001-2010 J. Schilling */
2 #ifndef lint
3 static	char __sccsid[] =
4 	"@(#)scsi-mac-iokit.c	1.17 10/05/24 Copyright 1997,2001-2010 J. Schilling";
5 #endif
6 /*
7  *	Interface to the Darwin IOKit SCSI drivers
8  *
9  *	Notes: Uses the IOKit/scsi-commands/SCSITaskLib interface
10  *
11  *	As of October 2001, this interface does not support SCSI parallel bus
12  *	(old-fashioned SCSI). It does support ATAPI, Firewire, and USB.
13  *
14  *	First version done by Constantine Sapuntzakis <csapuntz@Stanford.EDU>
15  *
16  *	Warning: you may change this source, but if you do that
17  *	you need to change the _scg_version and _scg_auth* string below.
18  *	You may not return "schily" for an SCG_AUTHOR request anymore.
19  *	Choose your name instead of "schily" and make clear that the version
20  *	string is related to a modified source.
21  *
22  *	Copyright (c) 1997,2001-2010 J. Schilling
23  */
24 /*
25  * The contents of this file are subject to the terms of the
26  * Common Development and Distribution License, Version 1.0 only
27  * (the "License").  You may not use this file except in compliance
28  * with the License.
29  *
30  * See the file CDDL.Schily.txt in this distribution for details.
31  * A copy of the CDDL is also available via the Internet at
32  * http://www.opensource.org/licenses/cddl1.txt
33  *
34  * The following exceptions apply:
35  * CDDL �3.6 needs to be replaced by: "You may create a Larger Work by
36  * combining Covered Software with other code if all other code is governed by
37  * the terms of a license that is OSI approved (see www.opensource.org) and
38  * you may distribute the Larger Work as a single product. In such a case,
39  * You must make sure the requirements of this License are fulfilled for
40  * the Covered Software."
41  *
42  * When distributing Covered Code, include this CDDL HEADER in each
43  * file and include the License file CDDL.Schily.txt from this distribution.
44  */
45 
46 /*
47  *	Warning: you may change this source, but if you do that
48  *	you need to change the _scg_version and _scg_auth* string below.
49  *	You may not return "schily" for an SCG_AUTHOR request anymore.
50  *	Choose your name instead of "schily" and make clear that the version
51  *	string is related to a modified source.
52  */
53 LOCAL	char	_scg_trans_version[] = "scsi-mac-iokit.c-1.17";	/* The version for this transport */
54 
55 #define	MAX_SCG		16	/* Max # of SCSI controllers */
56 #define	MAX_TGT		16
57 #define	MAX_LUN		8
58 
59 #include <schily/stat.h>
60 #include <mach/mach.h>
61 #include <Carbon/Carbon.h>
62 #include <IOKit/IOKitLib.h>
63 #include <IOKit/IOCFPlugIn.h>
64 /*
65  * IOKit/scsi-commands/ (being a symlink at least between Panther and Leopard)
66  * did disappear on "Snow Leopard" but we do not know whether IOKit/scsi/
67  * exist before. I do not like to create an autoconf test for the file,
68  * please report if you have problems on older Mac OS X releases.
69  */
70 /*#include <IOKit/scsi-commands/SCSITaskLib.h>*/
71 #include <IOKit/scsi/SCSITaskLib.h>
72 #include <mach/mach_error.h>
73 
74 struct scg_if {
75 	MMCDeviceInterface	**mmcDeviceInterface;
76 	SCSITaskDeviceInterface	**scsiTaskDeviceInterface;
77 	int			flags;
78 };
79 
80 /*
81  * Defines for flags
82  */
83 #define	NO_ACCESS	0x01
84 
85 struct scg_local {
86 	struct scg_if		scg_if[MAX_SCG][MAX_TGT];
87 	mach_port_t		masterPort;
88 };
89 #define	scglocal(p)	((struct scg_local *)((p)->local))
90 
91 #define	MAX_DMA_NEXT	(32*1024)
92 #if 0
93 #define	MAX_DMA_NEXT	(64*1024)	/* Check if this is not too big */
94 #endif
95 
96 LOCAL	int	iokit_open	__PR((SCSI *scgp, BOOL bydev, char *device, int busno, int tgt, int tlun));
97 LOCAL	void	iokit_warn	__PR((SCSI *scgp));
98 
99 
100 /*
101  * Return version information for the low level SCSI transport code.
102  * This has been introduced to make it easier to trace down problems
103  * in applications.
104  */
105 LOCAL char *
scgo_version(scgp,what)106 scgo_version(scgp, what)
107 	SCSI	*scgp;
108 	int	what;
109 {
110 	if (scgp != (SCSI *)0) {
111 		switch (what) {
112 
113 		case SCG_VERSION:
114 			return (_scg_trans_version);
115 		/*
116 		 * If you changed this source, you are not allowed to
117 		 * return "schily" for the SCG_AUTHOR request.
118 		 */
119 		case SCG_AUTHOR:
120 			return (_scg_auth_schily);
121 		case SCG_SCCS_ID:
122 			return (__sccsid);
123 		}
124 	}
125 	return ((char *)0);
126 }
127 
128 LOCAL int
scgo_help(scgp,f)129 scgo_help(scgp, f)
130 	SCSI	*scgp;
131 	FILE	*f;
132 {
133 	__scg_help(f, "SCSITaskDeviceInterface", "Apple SCSI",
134 		"", "Mac Prom device name", "IOCompactDiscServices/0 or IODVDServices/0 or IOBDServices/0",
135 								FALSE, FALSE);
136 	return (0);
137 }
138 
139 LOCAL char *devnames[] = {
140 		"IOCompactDiscServices",
141 		"IODVDServices",
142 		"IOBDServices",
143 		0
144 };
145 #define	NDEVS	((int)(sizeof (devnames) / sizeof (devnames[0]) - 1))
146 
147 /*
148  * Valid Device names:
149  *    IOCompactDiscServices
150  *    IODVDServices
151  *    IOSCSIPeripheralDeviceNub
152  *
153  * Also a / and a number can be appended to refer to something
154  * more than the first device (e.g. IOCompactDiscServices/5 for the 5th
155  * compact disc attached)
156  */
157 LOCAL int
scgo_open(scgp,device)158 scgo_open(scgp, device)
159 	SCSI	*scgp;
160 	char	*device;
161 {
162 		int	busno	= scg_scsibus(scgp);
163 		int	tgt	= scg_target(scgp);
164 		int	tlun	= scg_lun(scgp);
165 		int	b;
166 		int	t;
167 		int	nopen = 0;
168 		int	ret;
169 
170 	if (busno >= MAX_SCG || busno >= NDEVS || tgt >= MAX_TGT || tlun >= MAX_LUN) {
171 		errno = EINVAL;
172 		if (scgp->errstr) {
173 			js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
174 				"Illegal value for busno, target or lun '%d,%d,%d'",
175 					busno, tgt, tlun);
176 		}
177 		return (-1);
178 	}
179 
180 	if (scgp->local == NULL) {
181 		scgp->local = malloc(sizeof (struct scg_local));
182 		if (scgp->local == NULL) {
183 			if (scgp->errstr)
184 				js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "No memory for scg_local");
185 			return (0);
186 		}
187 		for (b = 0; b < MAX_SCG; b++) {
188 			for (t = 0; t < MAX_TGT; t++) {
189 				scglocal(scgp)->scg_if[b][t].mmcDeviceInterface = NULL;
190 				scglocal(scgp)->scg_if[b][t].scsiTaskDeviceInterface = NULL;
191 				scglocal(scgp)->scg_if[b][t].flags = 0;
192 			}
193 		}
194 	}
195 
196 	if ((device != NULL && *device != '\0') || (busno == -2 && tgt == -2))
197 		goto openbydev;
198 
199 	if (busno >= 0 && tgt >= 0 && tlun >= 0) {
200 		ret = iokit_open(scgp, FALSE, devnames[busno], busno, tgt, tlun);
201 		if (ret == 1)
202 			scg_settarget(scgp, busno, tgt, tlun);
203 		else
204 			scgo_close(scgp);
205 		if (scgp->fd == -2)
206 			iokit_warn(scgp);
207 		return (ret);
208 	} else {
209 		int	errsav = 0;
210 		int	exwarn = 0;
211 
212 		for (b = 0; b < NDEVS; b++) {
213 			for (t = 0; t < MAX_TGT; t++) {
214 				ret = iokit_open(scgp, FALSE, devnames[b], b, t, 0);
215 				if ((scgp->debug > 0 && ret > 0) || scgp->debug > 2) {
216 					js_fprintf((FILE *)scgp->errfile,
217 							"b %d t %d ret %d fd %d\n",
218 							b, t, ret, scgp->fd);
219 				}
220 				if (ret == 1)
221 					nopen++;
222 				if (exwarn == 0 && scgp->fd == -2) {
223 					iokit_warn(scgp);
224 					exwarn++;
225 				}
226 			}
227 		}
228 		seterrno(errsav);
229 	}
230 openbydev:
231 	if (nopen == 0) {
232 		if (device == NULL || device[0] == '\0')
233 			return (0);
234 
235 		ret = iokit_open(scgp, TRUE, device, 0, 0, 0);
236 		if (ret == 1)
237 			nopen++;
238 		if (scgp->fd == -2)
239 			iokit_warn(scgp);
240 	}
241 	if (nopen <= 0)
242 		scgo_close(scgp);
243 	return (nopen);
244 }
245 
246 LOCAL void
iokit_warn(scgp)247 iokit_warn(scgp)
248 	SCSI	*scgp;
249 {
250 	js_fprintf((FILE *)scgp->errfile, "\nWarning, 'diskarbitrationd' is running and does not allow us to\n");
251 	js_fprintf((FILE *)scgp->errfile, "send SCSI commands to the drive.\n");
252 	js_fprintf((FILE *)scgp->errfile, "To allow us to send SCSI commands, do the following:\n");
253 	js_fprintf((FILE *)scgp->errfile, "Eject all removable media, then call as root:\n");
254 	js_fprintf((FILE *)scgp->errfile, "	kill -STOP `(ps -ef | grep diskarbitrationd | awk '{ print $2 }')`\n");
255 	js_fprintf((FILE *)scgp->errfile, "then re-run the failed command. To continue 'diskarbitrationd' call as root:\n");
256 	js_fprintf((FILE *)scgp->errfile, "	kill -CONT `(ps -ef | grep diskarbitrationd | awk '{ print $2 }')`\n\n");
257 }
258 
259 LOCAL int
iokit_open(scgp,bydev,device,busno,tgt,tlun)260 iokit_open(scgp, bydev, device, busno, tgt, tlun)
261 	SCSI	*scgp;
262 	BOOL	bydev;
263 	char	*device;
264 	int	busno;
265 	int	tgt;
266 	int	tlun;
267 {
268 	mach_port_t masterPort = 0;
269 	io_iterator_t scsiObjectIterator = 0;
270 	IOReturn ioReturnValue = kIOReturnSuccess;
271 	CFMutableDictionaryRef dict = NULL;
272 	io_object_t scsiDevice = 0;
273 	HRESULT plugInResult;
274 	IOCFPlugInInterface **plugInInterface = NULL;
275 	MMCDeviceInterface **mmcDeviceInterface = NULL;
276 	SCSITaskDeviceInterface **scsiTaskDeviceInterface = NULL;
277 	SInt32 score = 0;
278 	int err = -1;
279 	char *realdevice = device;
280 	char *tmp;
281 	int idx;
282 
283 	if (scgp->local == NULL)
284 		return (err);
285 
286 	if (bydev) {
287 		realdevice = tmp = strdup(device);
288 		if (realdevice == NULL)
289 			return (err);
290 		tmp = strchr(tmp, '/');
291 		if (tmp != NULL) {
292 			*tmp++ = '\0';
293 			tgt = atoi(tmp);
294 		}
295 	}
296 
297 	/*
298 	 * Get master port handle
299 	 * The master port handle is deallocated in scgo_close()
300 	 */
301 	masterPort = scglocal(scgp)->masterPort;
302 	if (!masterPort) {
303 		ioReturnValue = IOMasterPort(bootstrap_port, &masterPort);
304 
305 		if (ioReturnValue != kIOReturnSuccess) {
306 			js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
307 				    "Couldn't get a master IOKit port. Error %d",
308 				    ioReturnValue);
309 			if (bydev)
310 				free(realdevice);
311 			return (err);
312 		}
313 		scglocal(scgp)->masterPort = masterPort;
314 	}
315 
316 	/*
317 	 * Get Service dict for "IOCompactDiscServices" or "IODVDServices"
318 	 * or "IODBDServices"
319 	 */
320 	dict = IOServiceMatching(realdevice);
321 	if (dict == NULL) {
322 		js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
323 			    "Couldn't create dictionary for searching '%s'.",
324 			    realdevice);
325 		goto out;
326 	}
327 
328 	ioReturnValue = IOServiceGetMatchingServices(masterPort, dict,
329 						    &scsiObjectIterator);
330 	dict = NULL;
331 
332 	if (scsiObjectIterator == 0 ||
333 	    (ioReturnValue != kIOReturnSuccess)) {
334 		js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
335 			    "No matching device %s/%d found.",
336 					realdevice, tgt);
337 		goto out;
338 	}
339 
340 	for (idx = 0; (scsiDevice = IOIteratorNext(scsiObjectIterator)) != 0; idx++) {
341 		if (idx == tgt)
342 			break;
343 		IOObjectRelease(scsiDevice);
344 		scsiDevice = 0;
345 		idx++;
346 	}
347 
348 	if (scsiDevice == 0) {
349 		js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
350 			    "No matching device %s/%d found. Iterator failed.",
351 					realdevice, tgt);
352 		goto out;
353 	}
354 
355 	ioReturnValue = IOCreatePlugInInterfaceForService(scsiDevice,
356 			kIOMMCDeviceUserClientTypeID,
357 			kIOCFPlugInInterfaceID,
358 			&plugInInterface, &score);
359 	if (ioReturnValue != kIOReturnSuccess) {
360 		goto try_generic;
361 	}
362 
363 	plugInResult = (*plugInInterface)->QueryInterface(plugInInterface,
364 				CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID),
365 				(LPVOID)&mmcDeviceInterface);
366 
367 	if (plugInResult != KERN_SUCCESS) {
368 		js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
369 			    "Unable to get MMC Interface: 0x%lX",
370 			    (long)plugInResult);
371 
372 		goto out;
373 	}
374 
375 	scsiTaskDeviceInterface =
376 		(*mmcDeviceInterface)->GetSCSITaskDeviceInterface(mmcDeviceInterface);
377 
378 	if (scsiTaskDeviceInterface == NULL) {
379 		js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
380 			    "Failed to get taskDeviceInterface");
381 		goto out;
382 	}
383 
384 	goto init;
385 
386 try_generic:
387 	ioReturnValue = IOCreatePlugInInterfaceForService(scsiDevice,
388 					kIOSCSITaskDeviceUserClientTypeID,
389 					kIOCFPlugInInterfaceID,
390 					&plugInInterface, &score);
391 	if (ioReturnValue != kIOReturnSuccess) {
392 		js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
393 			    "Unable to get plugin Interface: %x",
394 			    ioReturnValue);
395 		goto out;
396 	}
397 
398 	plugInResult = (*plugInInterface)->QueryInterface(plugInInterface,
399 			    CFUUIDGetUUIDBytes(kIOSCSITaskDeviceInterfaceID),
400 					(LPVOID)&scsiTaskDeviceInterface);
401 
402 	if (plugInResult != KERN_SUCCESS) {
403 		js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
404 			    "Unable to get generic Interface: 0x%lX",
405 			    (long)plugInResult);
406 
407 		goto out;
408 	}
409 
410 init:
411 	ioReturnValue =
412 		(*scsiTaskDeviceInterface)->ObtainExclusiveAccess(scsiTaskDeviceInterface);
413 
414 	if (ioReturnValue != kIOReturnSuccess) {
415 		js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
416 			    "Unable to get exclusive access to device");
417 		scglocal(scgp)->scg_if[busno][tgt].flags |= NO_ACCESS;
418 		scg_settarget(scgp, busno, tgt, tlun);
419 		goto out;
420 	}
421 
422 	if (mmcDeviceInterface) {
423 		(*mmcDeviceInterface)->AddRef(mmcDeviceInterface);
424 	}
425 	(*scsiTaskDeviceInterface)->AddRef(scsiTaskDeviceInterface);
426 	scglocal(scgp)->masterPort = masterPort;
427 	scglocal(scgp)->scg_if[busno][tgt].mmcDeviceInterface = mmcDeviceInterface;
428 	scglocal(scgp)->scg_if[busno][tgt].scsiTaskDeviceInterface = scsiTaskDeviceInterface;
429 	scg_settarget(scgp, busno, tgt, tlun);
430 	err = 1;
431 
432 out:
433 	if (scsiTaskDeviceInterface != NULL) {
434 		(*scsiTaskDeviceInterface)->Release(scsiTaskDeviceInterface);
435 	}
436 
437 	if (plugInInterface != NULL) {
438 		(*plugInInterface)->Release(plugInInterface);
439 	}
440 
441 	if (scsiDevice != 0) {
442 		IOObjectRelease(scsiDevice);
443 	}
444 
445 	if (scsiObjectIterator != 0) {
446 		IOObjectRelease(scsiObjectIterator);
447 	}
448 
449 	if (dict != NULL) {
450 		CFRelease(dict);
451 	}
452 
453 	if (bydev) {
454 		free(realdevice);
455 	}
456 	return (err);
457 }
458 
459 LOCAL int
scgo_close(scgp)460 scgo_close(scgp)
461 	SCSI	*scgp;
462 {
463 	int			b;
464 	int			t;
465 	SCSITaskDeviceInterface	**sc;
466 	MMCDeviceInterface	**mmc;
467 
468 	if (scgp->local == NULL)
469 		return (-1);
470 
471 	for (b = 0; b < MAX_SCG; b++) {
472 		for (t = 0; t < MAX_TGT; t++) {
473 			sc = scglocal(scgp)->scg_if[b][t].scsiTaskDeviceInterface;
474 			if (sc) {
475 				(*sc)->ReleaseExclusiveAccess(sc);
476 				(*sc)->Release(sc);
477 			}
478 			scglocal(scgp)->scg_if[b][t].scsiTaskDeviceInterface = NULL;
479 			mmc = scglocal(scgp)->scg_if[b][t].mmcDeviceInterface;
480 			if (mmc != NULL)
481 				(*mmc)->Release(mmc);
482 			scglocal(scgp)->scg_if[b][t].mmcDeviceInterface = NULL;
483 		}
484 	}
485 
486 	mach_port_deallocate(mach_task_self(), scglocal(scgp)->masterPort);
487 
488 	free(scgp->local);
489 	scgp->local = NULL;
490 
491 	return (0);
492 }
493 
494 LOCAL long
scgo_maxdma(scgp,amt)495 scgo_maxdma(scgp, amt)
496 	SCSI	*scgp;
497 	long	amt;
498 {
499 	long maxdma = MAX_DMA_NEXT;
500 #ifdef	SGIOCMAXDMA
501 	int  m;
502 
503 	if (ioctl(scglocal(scgp)->scgfile, SGIOCMAXDMA, &m) >= 0) {
504 		maxdma = m;
505 		if (scgp->debug > 0) {
506 			js_fprintf((FILE *)scgp->errfile,
507 				"maxdma: %d\n", maxdma);
508 		}
509 	}
510 #endif
511 	return (maxdma);
512 }
513 
514 LOCAL void *
scgo_getbuf(scgp,amt)515 scgo_getbuf(scgp, amt)
516 	SCSI	*scgp;
517 	long	amt;
518 {
519 	if (scgp->debug > 0) {
520 		js_fprintf((FILE *)scgp->errfile,
521 			"scgo_getbuf: %ld bytes\n", amt);
522 	}
523 	scgp->bufbase = malloc((size_t)(amt));
524 	return (scgp->bufbase);
525 }
526 
527 LOCAL void
scgo_freebuf(scgp)528 scgo_freebuf(scgp)
529 	SCSI	*scgp;
530 {
531 	if (scgp->bufbase)
532 		free(scgp->bufbase);
533 	scgp->bufbase = NULL;
534 }
535 
536 LOCAL int
scgo_numbus(scgp)537 scgo_numbus(scgp)
538 	SCSI	*scgp;
539 {
540 	return (MAX_SCG);
541 }
542 
543 LOCAL BOOL
scgo_havebus(scgp,busno)544 scgo_havebus(scgp, busno)
545 	SCSI	*scgp;
546 	int	busno;
547 {
548 	register int	t;
549 
550 	if (scgp->local == NULL || busno < 0 || busno >= MAX_SCG)
551 		return (FALSE);
552 
553 	for (t = 0; t < MAX_TGT; t++) {
554 		if (scglocal(scgp)->scg_if[busno][t].scsiTaskDeviceInterface != NULL)
555 			return (TRUE);
556 	}
557 	return (FALSE);
558 }
559 
560 LOCAL int
scgo_fileno(scgp,busno,tgt,tlun)561 scgo_fileno(scgp, busno, tgt, tlun)
562 	SCSI	*scgp;
563 	int	busno;
564 	int	tgt;
565 	int	tlun;
566 {
567 	if (scglocal(scgp) == NULL)
568 		return (-1);
569 
570 	if (scglocal(scgp)->scg_if[busno][tgt].flags & NO_ACCESS)
571 		return (-2);
572 	if (scglocal(scgp)->scg_if[busno][tgt].scsiTaskDeviceInterface != NULL)
573 		return (0);
574 	return (-1);
575 }
576 
577 LOCAL int
scgo_initiator_id(scgp)578 scgo_initiator_id(scgp)
579 	SCSI	*scgp;
580 {
581 	return (-1);
582 }
583 
584 LOCAL int
scgo_isatapi(scgp)585 scgo_isatapi(scgp)
586 	SCSI	*scgp;
587 
588 {
589 	return (FALSE);
590 }
591 
592 LOCAL int
scgo_reset(scgp,what)593 scgo_reset(scgp, what)
594 	SCSI	*scgp;
595 	int	what;
596 {
597 	if (what == SCG_RESET_NOP)
598 		return (0);
599 	if (what != SCG_RESET_BUS) {
600 		errno = EINVAL;
601 		return (-1);
602 	}
603 
604 	errno = 0;
605 	return (-1);
606 }
607 
608 LOCAL int
scgo_send(scgp)609 scgo_send(scgp)
610 	SCSI		*scgp;
611 {
612 	struct scg_cmd		*sp = scgp->scmd;
613 	SCSITaskDeviceInterface	**sc = NULL;
614 	SCSITaskInterface	**cmd = NULL;
615 #if	defined(__LP64__)			/* Ugly differences for LP64 */
616 	IOAddressRange		iov;
617 #else
618 	IOVirtualRange		iov;
619 #endif
620 	SCSI_Sense_Data		senseData;
621 	SCSITaskStatus		status;
622 	UInt64			bytesTransferred;
623 	IOReturn		ioReturnValue;
624 	int			ret = 0;
625 
626 	if (scgp->local == NULL) {
627 		sp->error = SCG_FATAL;
628 		return (0);
629 	}
630 	sc = scglocal(scgp)->scg_if[scg_scsibus(scgp)][scg_target(scgp)].scsiTaskDeviceInterface;
631 	if (sc == NULL) {
632 		sp->error = SCG_FATAL;
633 		return (0);
634 	}
635 
636 	cmd = (*sc)->CreateSCSITask(sc);
637 	if (cmd == NULL) {
638 		js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
639 			    "Failed to create SCSI task");
640 		ret = -1;
641 
642 		sp->error = SCG_FATAL;
643 		sp->ux_errno = EIO;
644 		goto out;
645 	}
646 
647 
648 #if	defined(__LP64__)			/* Ugly differences for LP64 */
649 	iov.address = (mach_vm_address_t) sp->addr;
650 #else
651 	iov.address = (IOVirtualAddress) sp->addr;
652 #endif
653 	iov.length = sp->size;
654 
655 	ioReturnValue = (*cmd)->SetCommandDescriptorBlock(cmd,
656 						sp->cdb.cmd_cdb, sp->cdb_len);
657 
658 	if (ioReturnValue != kIOReturnSuccess) {
659 		js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
660 			    "SetCommandDescriptorBlock failed with status %x",
661 			    ioReturnValue);
662 		ret = -1;
663 		goto out;
664 	}
665 
666 	ioReturnValue = (*cmd)->SetScatterGatherEntries(cmd, &iov, 1, sp->size,
667 				(sp->flags & SCG_RECV_DATA) ?
668 				kSCSIDataTransfer_FromTargetToInitiator :
669 				kSCSIDataTransfer_FromInitiatorToTarget);
670 	if (ioReturnValue != kIOReturnSuccess) {
671 		js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
672 			    "SetScatterGatherEntries failed with status %x",
673 			    ioReturnValue);
674 		ret = -1;
675 		goto out;
676 	}
677 
678 	ioReturnValue = (*cmd)->SetTimeoutDuration(cmd, sp->timeout * 1000);
679 	if (ioReturnValue != kIOReturnSuccess) {
680 		js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
681 			    "SetTimeoutDuration failed with status %x",
682 			    ioReturnValue);
683 		ret = -1;
684 		goto out;
685 	}
686 
687 	memset(&senseData, 0, sizeof (senseData));
688 
689 	seterrno(0);
690 	ioReturnValue = (*cmd)->ExecuteTaskSync(cmd,
691 				&senseData, &status, &bytesTransferred);
692 
693 	sp->resid = sp->size - bytesTransferred;
694 	sp->error = SCG_NO_ERROR;
695 	sp->ux_errno = geterrno();
696 
697 	if (ioReturnValue != kIOReturnSuccess) {
698 		js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
699 			    "Command execution failed with status %x",
700 			    ioReturnValue);
701 		sp->error = SCG_RETRYABLE;
702 		ret = -1;
703 		goto out;
704 	}
705 
706 	memset(&sp->scb, 0, sizeof (sp->scb));
707 	memset(&sp->u_sense.cmd_sense, 0, sizeof (sp->u_sense.cmd_sense));
708 	if (senseData.VALID_RESPONSE_CODE != 0 || status == 0x02) {
709 		/*
710 		 * There is no sense length - we need to asume that
711 		 * we always get 18 bytes.
712 		 */
713 		sp->sense_count = kSenseDefaultSize;
714 		memmove(&sp->u_sense.cmd_sense, &senseData, kSenseDefaultSize);
715 		if (sp->ux_errno == 0)
716 			sp->ux_errno = EIO;
717 	}
718 
719 	sp->u_scb.cmd_scb[0] = status;
720 
721 	/* ??? */
722 	if (status == kSCSITaskStatus_No_Status) {
723 		sp->error = SCG_RETRYABLE;
724 		ret = -1;
725 		goto out;
726 	}
727 	/*
728 	 * XXX Is it possible to have other senseful SCSI transport error codes?
729 	 */
730 
731 out:
732 	if (cmd != NULL) {
733 		(*cmd)->Release(cmd);
734 	}
735 
736 	return (ret);
737 }
738