/* @(#)scsi-mac-iokit.c 1.9 04/01/15 Copyright 1997,2001-2004 J. Schilling */ #ifndef lint static char __sccsid[] = "@(#)scsi-mac-iokit.c 1.9 04/01/15 Copyright 1997,2001-2004 J. Schilling"; #endif /* * 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 * * Warning: you may change this source, but if you do that * you need to change the _scg_version and _scg_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 as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * 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 _scg_version and _scg_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. */ LOCAL char _scg_trans_version[] = "scsi-mac-iokit.c-1.9"; /* The version for this transport */ #define MAX_SCG 16 /* Max # of SCSI controllers */ #define MAX_TGT 16 #define MAX_LUN 8 #include #include #include #include #include #include #include struct scg_local { MMCDeviceInterface **mmcDeviceInterface; SCSITaskDeviceInterface **scsiTaskDeviceInterface; mach_port_t masterPort; }; #define scglocal(p) ((struct scg_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. */ LOCAL char * scgo_version(scgp, what) SCSI *scgp; int what; { if (scgp != (SCSI *)0) { switch (what) { case SCG_VERSION: return (_scg_trans_version); /* * If you changed this source, you are not allowed to * return "schily" for the SCG_AUTHOR request. */ case SCG_AUTHOR: return (_scg_auth_schily); case SCG_SCCS_ID: return (__sccsid); } } return ((char *)0); } LOCAL int scgo_help(scgp, f) SCSI *scgp; FILE *f; { __scg_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) */ LOCAL int scgo_open(scgp, device) SCSI *scgp; 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) { js_snprintf(scgp->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 (scgp->local == NULL) { scgp->local = malloc(sizeof (struct scg_local)); if (scgp->local == NULL) goto out; } ioReturnValue = IOMasterPort(bootstrap_port, &masterPort); if (ioReturnValue != kIOReturnSuccess) { js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Couldn't get a master IOKit port. Error %d", ioReturnValue); goto out; } dict = IOServiceMatching(realdevice); if (dict == NULL) { js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Couldn't create dictionary for searching"); goto out; } ioReturnValue = IOServiceGetMatchingServices(masterPort, dict, &scsiObjectIterator); dict = NULL; if (scsiObjectIterator == NULL || (ioReturnValue != kIOReturnSuccess)) { js_snprintf(scgp->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) { js_snprintf(scgp->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) { js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Unable to get MMC Interface: 0x%lX", (long)plugInResult); goto out; } scsiTaskDeviceInterface = (*mmcDeviceInterface)->GetSCSITaskDeviceInterface(mmcDeviceInterface); if (scsiTaskDeviceInterface == NULL) { js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Failed to get taskDeviceInterface"); goto out; } goto init; try_generic: ioReturnValue = IOCreatePlugInInterfaceForService(scsiDevice, kIOSCSITaskDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugInInterface, &score); if (ioReturnValue != kIOReturnSuccess) { js_snprintf(scgp->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) { js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Unable to get generic Interface: 0x%lX", (long)plugInResult); goto out; } init: ioReturnValue = (*scsiTaskDeviceInterface)->ObtainExclusiveAccess(scsiTaskDeviceInterface); if (ioReturnValue != kIOReturnSuccess) { js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Unable to get exclusive access to device"); goto out; } if (mmcDeviceInterface) { (*mmcDeviceInterface)->AddRef(mmcDeviceInterface); } (*scsiTaskDeviceInterface)->AddRef(scsiTaskDeviceInterface); scglocal(scgp)->mmcDeviceInterface = mmcDeviceInterface; scglocal(scgp)->scsiTaskDeviceInterface = scsiTaskDeviceInterface; scglocal(scgp)->masterPort = masterPort; scg_settarget(scgp, 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 (scgp->local) { free(scgp->local); scgp->local = NULL; } if (masterPort) { mach_port_deallocate(mach_task_self(), masterPort); } } if (dict != NULL) { CFRelease(dict); } if (realdevice != NULL) { free(realdevice); } return (err); } LOCAL int scgo_close(scgp) SCSI *scgp; { SCSITaskDeviceInterface **sc; MMCDeviceInterface **mmc; if (scgp->local == NULL) return (-1); sc = scglocal(scgp)->scsiTaskDeviceInterface; (*sc)->ReleaseExclusiveAccess(sc); (*sc)->Release(sc); scglocal(scgp)->scsiTaskDeviceInterface = NULL; mmc = scglocal(scgp)->mmcDeviceInterface; if (mmc != NULL) (*mmc)->Release(mmc); mach_port_deallocate(mach_task_self(), scglocal(scgp)->masterPort); free(scgp->local); scgp->local = NULL; return (0); } LOCAL long scgo_maxdma(scgp, amt) SCSI *scgp; long amt; { long maxdma = MAX_DMA_NEXT; #ifdef SGIOCMAXDMA int m; if (ioctl(scglocal(scgp)->scgfile, SGIOCMAXDMA, &m) >= 0) { maxdma = m; if (scgp->debug > 0) { js_fprintf((FILE *)scgp->errfile, "maxdma: %d\n", maxdma); } } #endif return (maxdma); } LOCAL void * scgo_getbuf(scgp, amt) SCSI *scgp; long amt; { if (scgp->debug > 0) { js_fprintf((FILE *)scgp->errfile, "scgo_getbuf: %ld bytes\n", amt); } scgp->bufbase = malloc((size_t)(amt)); return (scgp->bufbase); } LOCAL void scgo_freebuf(scgp) SCSI *scgp; { if (scgp->bufbase) free(scgp->bufbase); scgp->bufbase = NULL; } LOCAL BOOL scgo_havebus(scgp, busno) SCSI *scgp; int busno; { if (busno == 0) return (TRUE); return (FALSE); } LOCAL int scgo_fileno(scgp, busno, tgt, tlun) SCSI *scgp; int busno; int tgt; int tlun; { return (-1); } LOCAL int scgo_initiator_id(scgp) SCSI *scgp; { return (-1); } LOCAL int scgo_isatapi(scgp) SCSI *scgp; { return (FALSE); } LOCAL int scgo_reset(scgp, what) SCSI *scgp; int what; { if (what == SCG_RESET_NOP) return (0); if (what != SCG_RESET_BUS) { errno = EINVAL; return (-1); } errno = 0; return (-1); } LOCAL int scgo_send(scgp) SCSI *scgp; { struct scg_cmd *sp = scgp->scmd; SCSITaskDeviceInterface **sc = NULL; SCSITaskInterface **cmd = NULL; IOVirtualRange iov; SCSI_Sense_Data senseData; SCSITaskStatus status; UInt64 bytesTransferred; IOReturn ioReturnValue; int ret = 0; if (scgp->local == NULL) { return (-1); } sc = scglocal(scgp)->scsiTaskDeviceInterface; cmd = (*sc)->CreateSCSITask(sc); if (cmd == NULL) { js_snprintf(scgp->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) { js_snprintf(scgp->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) { js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "SetScatterGatherEntries failed with status %x", ioReturnValue); ret = -1; goto out; } ioReturnValue = (*cmd)->SetTimeoutDuration(cmd, sp->timeout * 1000); if (ioReturnValue != kIOReturnSuccess) { js_snprintf(scgp->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) { js_snprintf(scgp->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); }