summaryrefslogtreecommitdiff
path: root/libusal/scsi-mac-iokit.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-12-31 05:04:42 +0400
committerIgor Pashev <pashev.igor@gmail.com>2012-12-31 05:04:42 +0400
commit71dc8760ff4de5f365330d1bc571d934deb54af9 (patch)
tree7346d42a282562a3937d82307012b5857d642ce6 /libusal/scsi-mac-iokit.c
downloadcdrkit-upstream.tar.gz
Imported Upstream version 1.1.11upstream/1.1.11upstream
Diffstat (limited to 'libusal/scsi-mac-iokit.c')
-rw-r--r--libusal/scsi-mac-iokit.c539
1 files changed, 539 insertions, 0 deletions
diff --git a/libusal/scsi-mac-iokit.c b/libusal/scsi-mac-iokit.c
new file mode 100644
index 0000000..7e5ee4f
--- /dev/null
+++ b/libusal/scsi-mac-iokit.c
@@ -0,0 +1,539 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)scsi-mac-iokit.c 1.10 05/05/15 Copyright 1997,2001-2004 J. Schilling */
+/*
+ * Interface to the Darwin IOKit SCSI drivers
+ *
+ * Notes: Uses the IOKit/scsi-commands/SCSITaskLib interface
+ *
+ * As of October 2001, this interface does not support SCSI parallel bus
+ * (old-fashioned SCSI). It does support ATAPI, Firewire, and USB.
+ *
+ * First version done by Constantine Sapuntzakis <csapuntz@Stanford.EDU>
+ *
+ * Warning: you may change this source, but if you do that
+ * you need to change the _usal_version and _usal_auth* string below.
+ * You may not return "schily" for an SCG_AUTHOR request anymore.
+ * Choose your name instead of "schily" and make clear that the version
+ * string is related to a modified source.
+ *
+ * Copyright (c) 1997,2001-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Warning: you may change this source, but if you do that
+ * you need to change the _usal_version and _usal_auth* string below.
+ * You may not return "schily" for an SCG_AUTHOR request anymore.
+ * Choose your name instead of "schily" and make clear that the version
+ * string is related to a modified source.
+ */
+static char _usal_trans_version[] = "scsi-mac-iokit.c-1.10"; /* The version for this transport */
+
+#define MAX_SCG 16 /* Max # of SCSI controllers */
+#define MAX_TGT 16
+#define MAX_LUN 8
+
+#include <statdefs.h>
+#include <mach/mach.h>
+#include <Carbon/Carbon.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/scsi-commands/SCSITaskLib.h>
+#include <mach/mach_error.h>
+
+struct usal_local {
+ MMCDeviceInterface **mmcDeviceInterface;
+ SCSITaskDeviceInterface **scsiTaskDeviceInterface;
+ mach_port_t masterPort;
+};
+#define usallocal(p) ((struct usal_local *)((p)->local))
+
+#define MAX_DMA_NEXT (32*1024)
+#if 0
+#define MAX_DMA_NEXT (64*1024) /* Check if this is not too big */
+#endif
+
+/*
+ * Return version information for the low level SCSI transport code.
+ * This has been introduced to make it easier to trace down problems
+ * in applications.
+ */
+static char *
+usalo_version(SCSI *usalp, int what)
+{
+ if (usalp != (SCSI *)0) {
+ switch (what) {
+
+ case SCG_VERSION:
+ return (_usal_trans_version);
+ /*
+ * If you changed this source, you are not allowed to
+ * return "schily" for the SCG_AUTHOR request.
+ */
+ case SCG_AUTHOR:
+ return (_usal_auth_cdrkit);
+ case SCG_SCCS_ID:
+ return (__sccsid);
+ }
+ }
+ return ((char *)0);
+}
+
+static int
+usalo_help(SCSI *usalp, FILE *f)
+{
+ __usal_help(f, "SCSITaskDeviceInterface", "Apple SCSI",
+ "", "Mac Prom device name", "IOCompactDiscServices/0",
+ FALSE, FALSE);
+ return (0);
+}
+
+
+/*
+ * Valid Device names:
+ * IOCompactDiscServices
+ * IODVDServices
+ * IOSCSIPeripheralDeviceNub
+ *
+ * Also a / and a number can be appended to refer to something
+ * more than the first device (e.g. IOCompactDiscServices/5 for the 5th
+ * compact disc attached)
+ */
+static int
+usalo_open(SCSI *usalp, char *device)
+{
+ mach_port_t masterPort = NULL;
+ io_iterator_t scsiObjectIterator = NULL;
+ IOReturn ioReturnValue = kIOReturnSuccess;
+ CFMutableDictionaryRef dict = NULL;
+ io_object_t scsiDevice = NULL;
+ HRESULT plugInResult;
+ IOCFPlugInInterface **plugInInterface = NULL;
+ MMCDeviceInterface **mmcDeviceInterface = NULL;
+ SCSITaskDeviceInterface **scsiTaskDeviceInterface = NULL;
+ SInt32 score = 0;
+ int err = -1;
+ char *realdevice = NULL, *tmp;
+ int driveidx = 1, idx = 1;
+
+ if (device == NULL) {
+ snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
+ "Please specify a device name (e.g. IOCompactDiscServices/0)");
+ goto out;
+ }
+
+ realdevice = tmp = strdup(device);
+ tmp = strchr(tmp, '/');
+ if (tmp != NULL) {
+ *tmp++ = '\0';
+ driveidx = atoi(tmp);
+ }
+
+ if (usalp->local == NULL) {
+ usalp->local = malloc(sizeof (struct usal_local));
+ if (usalp->local == NULL)
+ goto out;
+ }
+
+ ioReturnValue = IOMasterPort(bootstrap_port, &masterPort);
+
+ if (ioReturnValue != kIOReturnSuccess) {
+ snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
+ "Couldn't get a master IOKit port. Error %d",
+ ioReturnValue);
+ goto out;
+ }
+
+ dict = IOServiceMatching(realdevice);
+ if (dict == NULL) {
+ snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
+ "Couldn't create dictionary for searching");
+ goto out;
+ }
+
+ ioReturnValue = IOServiceGetMatchingServices(masterPort, dict,
+ &scsiObjectIterator);
+ dict = NULL;
+
+ if (scsiObjectIterator == NULL ||
+ (ioReturnValue != kIOReturnSuccess)) {
+ snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
+ "No matching device %s found.", device);
+ goto out;
+ }
+
+ if (driveidx <= 0)
+ driveidx = 1;
+
+ idx = 1;
+ while ((scsiDevice = IOIteratorNext(scsiObjectIterator)) != NULL) {
+ if (idx == driveidx)
+ break;
+ IOObjectRelease(scsiDevice);
+ scsiDevice = NULL;
+ idx++;
+ }
+
+ if (scsiDevice == NULL) {
+ snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
+ "No matching device found. Iterator failed.");
+ goto out;
+ }
+
+ ioReturnValue = IOCreatePlugInInterfaceForService(scsiDevice,
+ kIOMMCDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface, &score);
+ if (ioReturnValue != kIOReturnSuccess) {
+ goto try_generic;
+ }
+
+ plugInResult = (*plugInInterface)->QueryInterface(plugInInterface,
+ CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID),
+ (LPVOID)&mmcDeviceInterface);
+
+ if (plugInResult != KERN_SUCCESS) {
+ snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
+ "Unable to get MMC Interface: 0x%lX",
+ (long)plugInResult);
+
+ goto out;
+ }
+
+ scsiTaskDeviceInterface =
+ (*mmcDeviceInterface)->GetSCSITaskDeviceInterface(mmcDeviceInterface);
+
+ if (scsiTaskDeviceInterface == NULL) {
+ snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
+ "Failed to get taskDeviceInterface");
+ goto out;
+ }
+
+ goto init;
+
+try_generic:
+ ioReturnValue = IOCreatePlugInInterfaceForService(scsiDevice,
+ kIOSCSITaskDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID,
+ &plugInInterface, &score);
+ if (ioReturnValue != kIOReturnSuccess) {
+ snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
+ "Unable to get plugin Interface: %x",
+ ioReturnValue);
+ goto out;
+ }
+
+ plugInResult = (*plugInInterface)->QueryInterface(plugInInterface,
+ CFUUIDGetUUIDBytes(kIOSCSITaskDeviceInterfaceID),
+ (LPVOID)&scsiTaskDeviceInterface);
+
+ if (plugInResult != KERN_SUCCESS) {
+ snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
+ "Unable to get generic Interface: 0x%lX",
+ (long)plugInResult);
+
+ goto out;
+ }
+
+init:
+ ioReturnValue =
+ (*scsiTaskDeviceInterface)->ObtainExclusiveAccess(scsiTaskDeviceInterface);
+
+ if (ioReturnValue != kIOReturnSuccess) {
+ snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
+ "Unable to get exclusive access to device");
+ goto out;
+ }
+
+ if (mmcDeviceInterface) {
+ (*mmcDeviceInterface)->AddRef(mmcDeviceInterface);
+ }
+ (*scsiTaskDeviceInterface)->AddRef(scsiTaskDeviceInterface);
+ usallocal(usalp)->mmcDeviceInterface = mmcDeviceInterface;
+ usallocal(usalp)->scsiTaskDeviceInterface = scsiTaskDeviceInterface;
+ usallocal(usalp)->masterPort = masterPort;
+ usal_settarget(usalp, 0, 0, 0);
+ err = 1;
+
+out:
+ if (scsiTaskDeviceInterface != NULL) {
+ (*scsiTaskDeviceInterface)->Release(scsiTaskDeviceInterface);
+ }
+
+ if (plugInInterface != NULL) {
+ (*plugInInterface)->Release(plugInInterface);
+ }
+
+ if (scsiDevice != NULL) {
+ IOObjectRelease(scsiDevice);
+ }
+
+ if (scsiObjectIterator != NULL) {
+ IOObjectRelease(scsiObjectIterator);
+ }
+
+ if (err < 0) {
+ if (usalp->local) {
+ free(usalp->local);
+ usalp->local = NULL;
+ }
+
+ if (masterPort) {
+ mach_port_deallocate(mach_task_self(), masterPort);
+ }
+ }
+
+ if (dict != NULL) {
+ CFRelease(dict);
+ }
+
+ if (realdevice != NULL) {
+ free(realdevice);
+ }
+ return (err);
+}
+
+static int
+usalo_close(SCSI *usalp)
+{
+ SCSITaskDeviceInterface **sc;
+ MMCDeviceInterface **mmc;
+
+ if (usalp->local == NULL)
+ return (-1);
+
+ sc = usallocal(usalp)->scsiTaskDeviceInterface;
+ (*sc)->ReleaseExclusiveAccess(sc);
+ (*sc)->Release(sc);
+ usallocal(usalp)->scsiTaskDeviceInterface = NULL;
+
+ mmc = usallocal(usalp)->mmcDeviceInterface;
+ if (mmc != NULL)
+ (*mmc)->Release(mmc);
+
+ mach_port_deallocate(mach_task_self(), usallocal(usalp)->masterPort);
+
+ free(usalp->local);
+ usalp->local = NULL;
+
+ return (0);
+}
+
+static long
+usalo_maxdma(SCSI *usalp, long amt)
+{
+ long maxdma = MAX_DMA_NEXT;
+#ifdef SGIOCMAXDMA
+ int m;
+
+ if (ioctl(usallocal(usalp)->usalfile, SGIOCMAXDMA, &m) >= 0) {
+ maxdma = m;
+ if (usalp->debug > 0) {
+ fprintf((FILE *)usalp->errfile,
+ "maxdma: %d\n", maxdma);
+ }
+ }
+#endif
+ return (maxdma);
+}
+
+static void *
+usalo_getbuf(SCSI *usalp, long amt)
+{
+ if (usalp->debug > 0) {
+ fprintf((FILE *)usalp->errfile,
+ "usalo_getbuf: %ld bytes\n", amt);
+ }
+ usalp->bufbase = malloc((size_t)(amt));
+ return (usalp->bufbase);
+}
+
+static void
+usalo_freebuf(SCSI *usalp)
+{
+ if (usalp->bufbase)
+ free(usalp->bufbase);
+ usalp->bufbase = NULL;
+}
+
+static BOOL
+usalo_havebus(SCSI *usalp, int busno)
+{
+ if (busno == 0)
+ return (TRUE);
+ return (FALSE);
+}
+
+static int
+usalo_fileno(SCSI *usalp, int busno, int tgt, int tlun)
+{
+ return (-1);
+}
+
+static int
+usalo_initiator_id(SCSI *usalp)
+{
+ return (-1);
+}
+
+static int
+usalo_isatapi(SCSI *usalp)
+{
+ return (FALSE);
+}
+
+static int
+usalo_reset(SCSI *usalp, int what)
+{
+ if (what == SCG_RESET_NOP)
+ return (0);
+ if (what != SCG_RESET_BUS) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ errno = 0;
+ return (-1);
+}
+
+static int
+usalo_send(SCSI *usalp)
+{
+ struct usal_cmd *sp = usalp->scmd;
+ SCSITaskDeviceInterface **sc = NULL;
+ SCSITaskInterface **cmd = NULL;
+ IOVirtualRange iov;
+ SCSI_Sense_Data senseData;
+ SCSITaskStatus status;
+ UInt64 bytesTransferred;
+ IOReturn ioReturnValue;
+ int ret = 0;
+
+ if (usalp->local == NULL) {
+ return (-1);
+ }
+
+ sc = usallocal(usalp)->scsiTaskDeviceInterface;
+
+ cmd = (*sc)->CreateSCSITask(sc);
+ if (cmd == NULL) {
+ snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
+ "Failed to create SCSI task");
+ ret = -1;
+
+ sp->error = SCG_FATAL;
+ sp->ux_errno = EIO;
+ goto out;
+ }
+
+
+ iov.address = (IOVirtualAddress) sp->addr;
+ iov.length = sp->size;
+
+ ioReturnValue = (*cmd)->SetCommandDescriptorBlock(cmd,
+ sp->cdb.cmd_cdb, sp->cdb_len);
+
+ if (ioReturnValue != kIOReturnSuccess) {
+ snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
+ "SetCommandDescriptorBlock failed with status %x",
+ ioReturnValue);
+ ret = -1;
+ goto out;
+ }
+
+ ioReturnValue = (*cmd)->SetScatterGatherEntries(cmd, &iov, 1, sp->size,
+ (sp->flags & SCG_RECV_DATA) ?
+ kSCSIDataTransfer_FromTargetToInitiator :
+ kSCSIDataTransfer_FromInitiatorToTarget);
+ if (ioReturnValue != kIOReturnSuccess) {
+ snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
+ "SetScatterGatherEntries failed with status %x",
+ ioReturnValue);
+ ret = -1;
+ goto out;
+ }
+
+ ioReturnValue = (*cmd)->SetTimeoutDuration(cmd, sp->timeout * 1000);
+ if (ioReturnValue != kIOReturnSuccess) {
+ snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
+ "SetTimeoutDuration failed with status %x",
+ ioReturnValue);
+ ret = -1;
+ goto out;
+ }
+
+ memset(&senseData, 0, sizeof (senseData));
+
+ seterrno(0);
+ ioReturnValue = (*cmd)->ExecuteTaskSync(cmd,
+ &senseData, &status, &bytesTransferred);
+
+ sp->resid = sp->size - bytesTransferred;
+ sp->error = SCG_NO_ERROR;
+ sp->ux_errno = geterrno();
+
+ if (ioReturnValue != kIOReturnSuccess) {
+ snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
+ "Command execution failed with status %x",
+ ioReturnValue);
+ sp->error = SCG_RETRYABLE;
+ ret = -1;
+ goto out;
+ }
+
+ memset(&sp->scb, 0, sizeof (sp->scb));
+ memset(&sp->u_sense.cmd_sense, 0, sizeof (sp->u_sense.cmd_sense));
+ if (senseData.VALID_RESPONSE_CODE != 0 || status == 0x02) {
+ /*
+ * There is no sense length - we need to asume that
+ * we always get 18 bytes.
+ */
+ sp->sense_count = kSenseDefaultSize;
+ memmove(&sp->u_sense.cmd_sense, &senseData, kSenseDefaultSize);
+ if (sp->ux_errno == 0)
+ sp->ux_errno = EIO;
+ }
+
+ sp->u_scb.cmd_scb[0] = status;
+
+ /* ??? */
+ if (status == kSCSITaskStatus_No_Status) {
+ sp->error = SCG_RETRYABLE;
+ ret = -1;
+ goto out;
+ }
+ /*
+ * XXX Is it possible to have other senseful SCSI transport error codes?
+ */
+
+out:
+ if (cmd != NULL) {
+ (*cmd)->Release(cmd);
+ }
+
+ return (ret);
+}