Commit 6c612f2c authored by npzacs's avatar npzacs
Browse files

split mmc.c

parent d8ca7915
......@@ -27,6 +27,7 @@ libaacs_la_SOURCES=\
src/file/keydb.h \
src/file/keydbcfg-parser.y \
src/file/keydbcfg-lexer.l \
src/file/mmc_device.h \
src/file/path.h \
src/util/attributes.h \
src/util/macro.h \
......@@ -40,14 +41,17 @@ EXTRA_libaacs_la_SOURCES=\
if HAVE_DARWIN
libaacs_la_SOURCES+= \
src/file/dirs_darwin.c
src/file/dirs_darwin.c \
src/file/mmc_device_darwin.c
else
if HAVE_WIN32
libaacs_la_SOURCES+= \
src/file/dirs_win32.c
src/file/dirs_win32.c \
src/file/mmc_device_win32.c
else
libaacs_la_SOURCES+= \
src/file/dirs_xdg.c \
src/file/mmc_device_linux.c \
src/file/path.c
endif
endif
......
/*
* This file is part of libaacs
* Copyright (C) 2009-2010 Obliter0n
* Copyright (C) 2010-2015 npzacs
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef MMC_DEVICE_H_
#define MMC_DEVICE_H_
#include <util/attributes.h>
#include <stdint.h>
#include <stddef.h>
typedef struct mmcdev MMCDEV;
AACS_PRIVATE MMCDEV *device_open(const char *path);
AACS_PRIVATE void device_close(MMCDEV **mmc);
AACS_PRIVATE int device_send_cmd(MMCDEV *dev, const uint8_t *cmd, uint8_t *buf, size_t tx, size_t rx);
#endif /* MMC_DEVICE_H_ */
/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
/*
* This file is part of libaacs
* Copyright (C) 2009-2010 Obliter0n
* Copyright (C) 2010-2015 npzacs
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "mmc_device.h"
#include "util/macro.h"
#include "util/logging.h"
#include <stdlib.h>
#include <string.h>
#include <Carbon/Carbon.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <DiskArbitration/DiskArbitration.h>
/* need to undefine VERSION as one of the members of struct
SCSICmd_INQUIRY_StandardData is named VERSION (see
IOKit/scsi/SCSICmds_INQUIRY_Definitions.h) */
#undef VERSION
#include <IOKit/scsi/SCSITaskLib.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
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
/*
*
*/
struct mmcdev {
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;
};
int device_send_cmd(MMCDEV *mmc, const uint8_t *cmd, uint8_t *buf, size_t tx, size_t rx)
{
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 SCSI MMC cmd %s:\n", print_hex(str, cmd, 16));
if (tx) {
DEBUG(DBG_MMC, " Buffer: %s ->\n", print_hex(str, buf, tx>255?255:tx));
} else {
DEBUG(DBG_MMC, " Buffer: %s <-\n", print_hex(str, buf, rx>255?255:rx));
}
if (kIOReturnSuccess != rc || status != 0) {
DEBUG(DBG_MMC, " Send failed!\n");
break;
} else {
DEBUG(DBG_MMC, " Send succeeded! sent = %lld status = %u. response = %x\n",
(unsigned long long) sent, status, sense.VALID_RESPONSE_CODE);
}
(*task)->Release (task);
return 1;
} while (0);
if (task) {
(*task)->Release (task);
}
return 0;
}
static int get_mounted_device_from_path (MMCDEV *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), sizeof (mmc->bsd_name));
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");
((MMCDEV *)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");
((MMCDEV *)context)->is_mounted = 1;
}
static int iokit_unmount (MMCDEV *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 (MMCDEV *mmc) {
if (0 == mmc->is_mounted) {
if (mmc->disk && mmc->session) {
DADiskMount (mmc->disk, NULL, kDADiskMountOptionDefault, iokit_mount_complete, mmc);
CFRunLoopRunInMode (kCFRunLoopDefaultMode, 10, true);
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 (MMCDEV *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[128] = "";
data = IORegistryEntrySearchCFProperty (service, kIOServicePlane, CFSTR("BSD Name"),
kCFAllocatorDefault, kIORegistryIterateRecursively);
if (NULL != data) {
rc = CFStringGetCString (data, name, sizeof (name), kCFStringEncodingASCII);
CFRelease (data);
if (0 == strcmp (name, mmc->bsd_name)) {
break;
}
}
(void) IOObjectRelease (service);
}
IOObjectRelease (deviceIterator);
*servp = service;
return (service) ? 0 : -1;
}
static int iokit_find_interfaces (MMCDEV *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);
/* call release instead of IODestroyPlugInInterface to avoid stopping IOBDServices */
(*plugInInterface)->Release(plugInInterface);
if (kIOReturnSuccess != rc || NULL == mmc->mmcInterface) {
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", (void*)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;
}
static int mmc_open_iokit (const char *path, MMCDEV *mmc) {
io_service_t service;
int rc;
mmc->mmcInterface = NULL;
mmc->taskInterface = NULL;
mmc->disk = NULL;
mmc->session = NULL;
mmc->is_mounted = true;
/* get the bsd name associated with this mount */
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;
}
/* find a matching io service (IOBDServices) */
rc = iokit_find_service_matching (mmc, &service);
if (0 != rc) {
DEBUG(DBG_MMC, "Could not find matching IOBDServices mounted @ %s\n", path);
return rc;
}
/* find mmc and scsi task interfaces */
rc = iokit_find_interfaces (mmc, service);
/* done with the ioservice. release it */
(void) IOObjectRelease (service);
if (0 != rc) {
return rc;
}
/* unmount the disk so exclusive access can be obtained (this is required
to use the scsi task interface) */
rc = iokit_unmount (mmc);
if (0 != rc) {
return rc;
}
/* finally, obtain exclusive access */
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;
}
MMCDEV *device_open(const char *path)
{
MMCDEV *dev;
int rc;
dev = calloc(1, sizeof(MMCDEV));
rc = mmc_open_iokit (path, dev);
if (0 != rc) {
device_close (dev);
return NULL;
}
return dev;
}
void device_close(MMCDEV **pp)
{
if (pp && *pp) {
MMCDEV *mmc = *pp;
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);
X_FREE(*pp);
}
}
/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
/*
* This file is part of libaacs
* Copyright (C) 2009-2010 Obliter0n
* Copyright (C) 2010-2015 npzacs
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include "mmc_device.h"
#include "path.h"
#include "util/macro.h"
#include "util/logging.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#ifdef HAVE_MNTENT_H
#include <mntent.h>
#endif
#ifdef HAVE_LINUX_CDROM_H
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/cdrom.h>
#endif
/*
*
*/
struct mmcdev {
int fd;
};
int device_send_cmd(MMCDEV *dev, const uint8_t *cmd, uint8_t *buf, size_t tx, size_t rx)
{
#if defined(HAVE_LINUX_CDROM_H)
struct cdrom_generic_command cgc;
struct request_sense sense;
char str[512];
int result;
memset(&cgc, 0, sizeof(cgc));
memcpy(cgc.cmd, cmd, CDROM_PACKET_SIZE);
cgc.sense = &sense;
cgc.timeout = 5000;
if (buf) {
if (tx) {
cgc.data_direction = CGC_DATA_WRITE;
cgc.buflen = tx;
cgc.buffer = buf;
} else if (rx) {
cgc.data_direction = CGC_DATA_READ;
cgc.buflen = rx;
cgc.buffer = buf;
}
} else {
cgc.data_direction = CGC_DATA_NONE;
cgc.buflen = 0;
cgc.buffer = NULL;
}
result = ioctl(dev->fd, CDROM_SEND_PACKET, &cgc);
DEBUG(DBG_MMC, "Send LINUX MMC cmd %s:\n", print_hex(str, cmd, 16));
if (tx) {
DEBUG(DBG_MMC, " Buffer: %s ->\n", print_hex(str, buf, tx>255?255:tx));
} else {
DEBUG(DBG_MMC, " Buffer: %s <-\n", print_hex(str, buf, rx>255?255:rx));
}
if (result >= 0) {
DEBUG(DBG_MMC, " Send succeeded! [%d]\n", result);
return 1;
}
DEBUG(DBG_MMC, " Send failed! [%d] %s\n", result, strerror(errno));
#else
#warning no MMC drive support
DEBUG(DBG_MMC | DBG_CRIT, "No MMC drive support\n");
#endif
return 0;
}
MMCDEV *device_open(const char *path)
{
char resolved_path[AACS_PATH_MAX];
size_t path_len;
struct stat st;
int fd = -1;
MMCDEV *dev = NULL;
FILE *proc_mounts;
/* resolve path */
if (!aacs_resolve_path(path, resolved_path)) {
DEBUG(DBG_MMC | DBG_CRIT, "Failed resolving path %s\n", path);
return NULL;
}
/* strip trailing '/'s */
path_len = strlen(resolved_path);
while (path_len > 0 && resolved_path[--path_len] == '/') {
resolved_path[path_len] = '\0';
}