diff --git a/configure.ac b/configure.ac
index 62b052c..2a30513 100644
--- a/configure.ac
+++ b/configure.ac
@@ -27,6 +27,8 @@ AM_INIT_AUTOMAKE([foreign])
 AM_CONFIG_HEADER(config.h)
 AM_MAINTAINER_MODE
 
+SET_FEATURES="-D_ISOC99_SOURCE -D_POSIX_C_SOURCE=200112L"
+
 case "${host_os}" in
   "")
     SYS=unknown
@@ -43,6 +45,11 @@ case "${host_os}" in
         ;;
     esac
     ;;
+  *darwin*)
+    AC_DEFINE([USE_IOKIT], 1, [Use IOKit for MMC access])
+    LDFLAGS="${LDFLAGS} -lobjc -Wl,-framework,IOKit,-framework,Cocoa,-framework,DiskArbitration"
+    SET_FEATURES="${SET_FEATURES} -D_DARWIN_C_SOURCE"
+    ;;
   *)
     SYS="${host_os}"
     ;;
@@ -98,7 +105,8 @@ AC_TYPE_SIGNAL
 # required headers
 AC_CHECK_HEADERS([stdarg.h sys/types.h dirent.h errno.h libgen.h malloc.h])
 AC_CHECK_HEADERS([stdlib.h mntent.h linux/cdrom.h inttypes.h])
-AC_CHECK_HEADERS([sys/time.h time.h sys/select.h limits.h])
+AC_CHECK_HEADERS([sys/time.h time.h sys/select.h limits.h sys/param.h])
+AC_CHECK_HEADERS([sys/mount.h])
 
 # required structures
 AC_STRUCT_DIRENT_D_TYPE
@@ -224,5 +232,6 @@ AC_SUBST(LT_VERSION_INFO)
 AC_SUBST(SET_WARNINGS)
 AC_SUBST(SET_OPTIMIZATIONS)
 AC_SUBST(SET_DEBUG_OPTS)
+AC_SUBST(SET_FEATURES)
 AC_CONFIG_FILES([Makefile src/Makefile src/examples/Makefile src/libaacs.pc src/libaacs/aacs-version.h])
 AC_OUTPUT
diff --git a/src/Makefile.am b/src/Makefile.am
index c8e66e9..471bb8b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2,7 +2,7 @@ LIB_VERSION_INFO="@LT_VERSION_INFO@"
 
 SUBDIRS= . examples
 
-SET_FEATURES = -D_ISOC99_SOURCE -D_POSIX_C_SOURCE=200112L -D_REENTRANT
+SET_FEATURES = @SET_FEATURES@
 SET_WARNINGS = @SET_WARNINGS@
 SET_OPTIMIZATIONS = @SET_OPTIMIZATIONS@
 SET_DEBUG_OPTS = @SET_DEBUG_OPTS@
diff --git a/src/libaacs/mmc.c b/src/libaacs/mmc.c
index 4d41eeb..862669e 100644
--- a/src/libaacs/mmc.c
+++ b/src/libaacs/mmc.c
@@ -33,6 +33,42 @@
 #include <unistd.h>
 #include <string.h>
 
+#include <stdio.h>
+
+#define _IOKIT_SCSI_CMDS_INQUIRY_H_
+
+typedef void SCSICmd_INQUIRY_StandardData;
+
+#if USE_IOKIT
+
+#include <Carbon/Carbon.h>
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/scsi/SCSITaskLib.h>
+
+#include <DiskArbitration/DiskArbitration.h>
+
+#include <IOKit/storage/IOBDMediaBSDClient.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+
+#ifdef HAVE_LIBGEN_H
+#include <libgen.h>
+#endif
+
+#endif
+
 #ifdef HAVE_MNTENT_H
 #include <mntent.h>
 #endif
@@ -88,9 +124,19 @@ typedef struct _SCSI_PASS_THROUGH_DIRECT {
 
 #endif // defined(_WIN32)
 
-
 struct mmc {
-#if defined(_WIN32)
+#if defined(USE_IOKIT)
+    MMCDeviceInterface **mmcInterface;
+    SCSITaskDeviceInterface **taskInterface;
+
+    /* device short name (ie disk1) */
+    char bsd_name[128];
+
+    /* for mounting/unmounting the disc */
+    DADiskRef disk;
+    DASessionRef session;
+    bool is_mounted;
+#elif defined(_WIN32)
     HANDLE fd;
 #else
     int    fd;
@@ -205,6 +251,83 @@ static int _mmc_send_cmd(MMC *mmc, const uint8_t *cmd, uint8_t *buf, size_t tx,
 
     DEBUG(DBG_MMC, "  Send failed! (%p)\n", mmc);
 
+#elif defined(USE_IOKIT)
+    SCSITaskInterface **task = NULL;
+    SCSI_Sense_Data sense;
+    SCSITaskStatus status;
+    SCSITaskSGElement iov;
+    UInt8 direction;
+    UInt64 sent;
+    int rc;
+
+    if (NULL == mmc->taskInterface) {
+	return 0;
+    }
+
+    do {
+	task = (*mmc->taskInterface)->CreateSCSITask (mmc->taskInterface);
+	if (NULL == task) {
+	    DEBUG(DBG_MMC, "  Could not create SCSI Task\n");
+	    break;
+	}
+
+	iov.address = (uintptr_t) buf;
+	iov.length  = tx ? tx : rx;
+
+	if (buf) {
+	    direction = tx ? kSCSIDataTransfer_FromInitiatorToTarget :
+		kSCSIDataTransfer_FromTargetToInitiator;
+	} else {
+	    direction = kSCSIDataTransfer_NoDataTransfer;
+	}
+
+	rc = (*task)->SetCommandDescriptorBlock (task, cmd, 16);
+	if (kIOReturnSuccess != rc) {
+	    DEBUG(DBG_MMC, "  Error setting SCSI command\n");
+	    break;
+	}
+
+	rc = (*task)->SetScatterGatherEntries (task, &iov, 1, iov.length, direction);
+	if (kIOReturnSuccess != rc) {
+	    DEBUG(DBG_MMC, "  Error setting SCSI scatter gather entries\n");
+	    break;
+	}
+
+	rc = (*task)->SetTimeoutDuration (task, 5000000);
+	if (kIOReturnSuccess != rc) {
+	    DEBUG(DBG_MMC, "  Error setting SCSI command timeout\n");
+	    break;
+	}
+
+	memset (&sense, 0, sizeof (sense));
+
+	rc = (*task)->ExecuteTaskSync (task, &sense, &status, &sent);
+
+        char str[512];
+        DEBUG(DBG_MMC, "Send SDSI MMC cmd %s: (%p)\n",
+              print_hex(str, cmd, 16), mmc);
+        if (tx) {
+            DEBUG(DBG_MMC, "  Buffer: %s -> (%p)\n", print_hex(str, buf, tx>255?255:tx), mmc);
+        } else {
+            DEBUG(DBG_MMC, "  Buffer: %s <- (%p)\n", print_hex(str, buf, rx>255?255:rx), mmc);
+        }
+
+	if (kIOReturnSuccess != rc || status != 0) {
+	    DEBUG(DBG_MMC, "  Send failed! (%p)\n", mmc);
+	    break;
+	} else {
+            DEBUG(DBG_MMC, "  Send succeeded! (%p) sent = %lld status = %u. response = %x\n", mmc,
+		  (unsigned long long) sent, status, sense.VALID_RESPONSE_CODE);
+        }
+
+	(*task)->Release (task);
+
+	return 1;
+    } while (0);
+
+    if (task) {
+	(*task)->Release (task);
+    }
 #endif
 
     return 0;
@@ -460,9 +583,226 @@ static int _mmc_read_vid(MMC *mmc, uint8_t agid, uint8_t *volume_id,
     return 0;
 }
 
+static int get_mounted_device_from_path (MMC *mmc, const char *path) {
+  struct statfs stat_info;
+  int rc;
+
+  rc = statfs (path, &stat_info);
+  if (0 != rc) {
+    return rc;
+  }
+
+  strncpy (mmc->bsd_name, basename (stat_info.f_mntfromname), 256);
+
+  return 0;
+}
+
+static void iokit_unmount_complete (DADiskRef disk, DADissenterRef dissenter,
+                                    void *context) {
+    (void)disk; /* suppress warning */
+
+    if (dissenter) {
+        DEBUG(DBG_MMC, "Could not unmount the disc\n");
+    } else {
+        DEBUG(DBG_MMC, "Disc unmounted\n");
+        ((MMC *)context)->is_mounted = 0;
+    }
+}
+
+static void iokit_mount_complete (DADiskRef disk, DADissenterRef dissenter,
+                                  void *context) {
+    (void) disk; /* suppress warning */
+    (void) dissenter; /* suppress warning */
+    
+    /* the disc mounts despite whether there is a dessenter */
+    DEBUG(DBG_MMC, "Disc mounted\n");
+    ((MMC *)context)->is_mounted = 1;
+}
+
+static int iokit_unmount (MMC *mmc) {
+    if (0 == mmc->is_mounted) {
+        return 0; /* nothing to do */
+    }
+
+    DEBUG(DBG_MMC, "Unmounting disk\n");
+
+    mmc->session = DASessionCreate (kCFAllocatorDefault);
+    if (NULL == mmc->session) {
+        DEBUG(DBG_MMC, "Could not create a disc arbitration session\n");
+        return -1;
+    }
+
+    mmc->disk = DADiskCreateFromBSDName (kCFAllocatorDefault, mmc->session, mmc->bsd_name);
+    if (NULL == mmc->disk) {
+        DEBUG(DBG_MMC, "Could not create a disc arbitration disc for the device\n");
+        CFRelease (mmc->session);
+        mmc->session = NULL;
+        return -1;
+    }
+
+    DAApprovalSessionScheduleWithRunLoop (mmc->session, CFRunLoopGetCurrent (),
+                                          kCFRunLoopDefaultMode);
+
+    DADiskUnmount (mmc->disk, kDADiskUnmountOptionForce, iokit_unmount_complete, mmc);
+
+    CFRunLoopRunInMode (kCFRunLoopDefaultMode, 10, true);
+
+    return mmc->is_mounted ? -1 : 0;
+}
+
+static int iokit_mount (MMC *mmc) {
+    if (0 == mmc->is_mounted) {
+        if (mmc->disk && mmc->session) {
+            DADiskMount (mmc->disk, NULL, kDADiskMountOptionDefault, iokit_mount_complete, mmc);
+
+            CFRunLoopRunInMode (kCFRunLoopDefaultMode, 10, false);
+
+            DAApprovalSessionUnscheduleFromRunLoop (mmc->session, CFRunLoopGetCurrent (),
+                                                    kCFRunLoopDefaultMode);
+        }
+
+        if (mmc->disk) {
+            CFRelease (mmc->disk);
+            mmc->disk = NULL;
+        }
+
+        if (mmc->session) {
+            CFRelease (mmc->session);
+            mmc->session = NULL;
+        }
+    }
+
+    return mmc->is_mounted ? 0 : -1;
+}
+
+static int iokit_find_service_matching (MMC *mmc, io_service_t *servp) {
+    CFMutableDictionaryRef matchingDict = IOServiceMatching("IOBDServices");
+    io_iterator_t deviceIterator;
+    io_service_t service;
+    int rc;
+
+    assert (NULL != servp);
+        
+    *servp = 0;
+
+    if (!matchingDict) {
+        DEBUG(DBG_MMC, "Could not create a matching dictionary for IOBDServices\n");
+        return -1;
+    }
+
+    /* this call consumes the reference to the matchingDict. we do not need to release it */
+    rc = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &deviceIterator);
+    if (kIOReturnSuccess != rc) {
+        DEBUG(DBG_MMC, "Could not create device iterator\n");
+        return -1;
+    }
+
+    while (0 != (service = IOIteratorNext (deviceIterator))) {
+        CFStringRef data;
+        char name[256] = "";
+
+        data = IORegistryEntrySearchCFProperty (service, kIOServicePlane, CFSTR("BSD Name"),
+                                                kCFAllocatorDefault, kIORegistryIterateRecursively);
+
+        if (NULL != data) {
+            rc = CFStringGetCString (data, name, sizeof (name), kCFStringEncodingASCII);
+
+            if (0 == strcmp (name, mmc->bsd_name)) {
+                break;
+            }
+        }
+
+        (void) IOObjectRelease (service);
+    }
+
+    IOObjectRelease (deviceIterator);
+
+    *servp = service;
+
+    return (service) ? 0 : -1;
+}
+
+static int iokit_find_interfaces (MMC *mmc, io_service_t service) {
+    IOCFPlugInInterface **plugInInterface = NULL;
+    SInt32 score;
+    int rc;
+
+    rc = IOCreatePlugInInterfaceForService (service, kIOMMCDeviceUserClientTypeID,
+                                            kIOCFPlugInInterfaceID, &plugInInterface,
+                                            &score);
+    if (kIOReturnSuccess != rc || NULL == plugInInterface) {
+        return -1;
+    }
+
+    DEBUG(DBG_MMC, "Getting MMC interface\n");
+
+    rc = (*plugInInterface)->QueryInterface(plugInInterface,
+                                            CFUUIDGetUUIDBytes(kIOMMCDeviceInterfaceID),
+                                            (LPVOID)&mmc->mmcInterface);
+    if (kIOReturnSuccess != rc) {
+        DEBUG(DBG_MMC, "Could not get multimedia commands (MMC) interface\n");
+        return -1;
+    }
+
+    DEBUG(DBG_MMC, "Have an MMC interface (%p). Getting a SCSI task interface...\n", mmc->mmcInterface);
+
+    mmc->taskInterface = (*mmc->mmcInterface)->GetSCSITaskDeviceInterface (mmc->mmcInterface);
+    if (NULL == mmc->taskInterface) {
+        DEBUG(DBG_MMC, "Could not get SCSI task device interface\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+int mmc_open_iokit (const char *path, MMC *mmc) {
+    io_service_t service;
+    int rc;
+
+    mmc->mmcInterface = NULL;
+    mmc->taskInterface = NULL;
+    mmc->disk = NULL;
+    mmc->session = NULL;
+
+    rc = get_mounted_device_from_path (mmc, path);
+    if (0 != rc) {
+        DEBUG(DBG_MMC, "Could not locate mounted device associated with %s\n", path);
+        return rc;
+    }
+
+    rc = iokit_find_service_matching (mmc, &service);
+    if (0 != rc) {
+        DEBUG(DBG_MMC, "Could not find matching IOBDServices mounted @ %s\n", path);
+        return rc;
+    }
+
+    rc = iokit_find_interfaces (mmc, service);
+    IOObjectRelease (service);
+    if (0 != rc) {
+        return rc;
+    }
+
+    mmc->is_mounted = true;
+    rc = iokit_unmount (mmc);
+    if (0 != rc) {
+        return rc;
+    }
+
+    rc = (*mmc->taskInterface)->ObtainExclusiveAccess (mmc->taskInterface);
+    if (kIOReturnSuccess != rc) {
+        DEBUG(DBG_MMC, "Failed to obtain exclusive access. rc = %x\n", rc);
+        return -1;
+    }
+
+    DEBUG(DBG_MMC, "MMC Open complete\n");
+
+    return 0;
+}
+
 MMC *mmc_open(const char *path)
 {
     MMC *mmc = calloc(1, sizeof(MMC));
+    int rc;
 
     crypto_create_nonce(mmc->host_nonce, sizeof(mmc->host_nonce));
 
@@ -482,7 +822,14 @@ MMC *mmc_open(const char *path)
               print_hex(str, mmc->host_key_point, sizeof(mmc->host_key_point)));
     }
 
-#if defined(HAVE_MNTENT_H)
+#if defined(USE_IOKIT)
+    rc = mmc_open_iokit (path, mmc);
+    if (0 != rc) {
+        mmc_close (mmc);
+        return NULL;
+    }
+
+#elif defined(HAVE_MNTENT_H)
 
 #ifdef HAVE_REALPATH
     char *file_path = malloc(PATH_MAX);
@@ -577,7 +924,8 @@ MMC *mmc_open(const char *path)
 
     if (mmc && !_mmc_check_aacs(mmc)) {
         DEBUG(DBG_MMC | DBG_CRIT, "AACS not active or supported by the drive\n");
-        X_FREE(mmc);
+        mmc_close (mmc);
+        return NULL;
     }
 
     return mmc;
@@ -596,6 +944,19 @@ void mmc_close(MMC *mmc)
         if (mmc->fd != INVALID_HANDLE_VALUE) {
             CloseHandle(mmc->fd);
         }
+#elif defined(USE_IOKIT)
+        if (mmc->taskInterface) {
+            (*mmc->taskInterface)->ReleaseExclusiveAccess (mmc->taskInterface);
+            (*mmc->taskInterface)->Release (mmc->taskInterface);
+            mmc->taskInterface = NULL;
+        }
+
+        if (mmc->mmcInterface) {
+            (*mmc->mmcInterface)->Release (mmc->mmcInterface);
+            mmc->mmcInterface = NULL;
+        }
+
+        (void) iokit_mount (mmc);
 #endif
 
         DEBUG(DBG_MMC, "Closed MMC drive (%p)\n", mmc);
