summaryrefslogtreecommitdiffstats
path: root/rbutil/ipodpatcher/ipodio-posix.c
diff options
context:
space:
mode:
authorDominik Riebeling <Dominik.Riebeling@gmail.com>2010-01-30 17:02:37 +0000
committerDominik Riebeling <Dominik.Riebeling@gmail.com>2010-01-30 17:02:37 +0000
commitc8d13b6a7dc76e80663ca71338fed4020e2be132 (patch)
tree01136e99b5b874322f8af6838d2f704dfc4a40e8 /rbutil/ipodpatcher/ipodio-posix.c
parent25236232c00ed3ead85a6918fa501ba76b4a892a (diff)
downloadrockbox-c8d13b6a7dc76e80663ca71338fed4020e2be132.tar.gz
rockbox-c8d13b6a7dc76e80663ca71338fed4020e2be132.tar.bz2
rockbox-c8d13b6a7dc76e80663ca71338fed4020e2be132.zip
Implement ipod_scsi_inquiry() on OS X.
This implements the basic functionality for sending inquiries on OS X. The current implementation has some limitations: - it will not respect the selected device but pick the first Ipod found. - it is inefficient due to the way ipodpatcher expects this which doesn't really match how it works on OS X. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24382 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'rbutil/ipodpatcher/ipodio-posix.c')
-rw-r--r--rbutil/ipodpatcher/ipodio-posix.c154
1 files changed, 146 insertions, 8 deletions
diff --git a/rbutil/ipodpatcher/ipodio-posix.c b/rbutil/ipodpatcher/ipodio-posix.c
index 065aae6303..881c09b54f 100644
--- a/rbutil/ipodpatcher/ipodio-posix.c
+++ b/rbutil/ipodpatcher/ipodio-posix.c
@@ -19,6 +19,7 @@
*
****************************************************************************/
+
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
@@ -53,7 +54,7 @@ static void get_geometry(struct ipod_t* ipod)
}
}
-/* Linux SCSI Inquiry code based on the documentation and example code from
+/* Linux SCSI Inquiry code based on the documentation and example code from
http://www.ibm.com/developerworks/linux/library/l-scsi-api/index.html
*/
@@ -84,7 +85,7 @@ int ipod_scsi_inquiry(struct ipod_t* ipod, int page_code,
cdb[3] = 0;
cdb[4] = 0xff;
cdb[5] = 0; /* For control filed, just use 0 */
-
+
hdr.dxfer_direction = SG_DXFER_FROM_DEV;
hdr.cmdp = cdb;
hdr.cmd_len = 6;
@@ -123,7 +124,13 @@ int ipod_scsi_inquiry(struct ipod_t* ipod, int page_code,
}
#elif defined(__APPLE__) && defined(__MACH__)
+/* OS X IOKit includes don't like VERSION being defined! */
+#undef VERSION
#include <sys/disk.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/scsi-commands/SCSITaskLib.h>
+#include <IOKit/scsi-commands/SCSICommandOperationCodes.h>
#define IPOD_SECTORSIZE_IOCTL DKIOCGETBLOCKSIZE
/* TODO: Implement this function for Mac OS X */
@@ -137,12 +144,143 @@ static void get_geometry(struct ipod_t* ipod)
int ipod_scsi_inquiry(struct ipod_t* ipod, int page_code,
unsigned char* buf, int bufsize)
{
- /* TODO: Implement for OS X */
+ /* OS X doesn't allow to simply send out a SCSI inquiry request but
+ * requires registering an interface handler first.
+ * Currently this is done on each inquiry request which is somewhat
+ * inefficient but the current ipodpatcher API doesn't really fit here.
+ * Based on the documentation in Apple's document
+ * "SCSI Architecture Model Device Interface Guide".
+ *
+ * WARNING: this code currently doesn't take the selected device into
+ * account. It simply looks for an Ipod on the system and uses
+ * the first match.
+ */
(void)ipod;
- (void)page_code;
- (void)buf;
- (void)bufsize;
- return -1;
+ int result = 0;
+ /* first, create a dictionary to match the device. This is needed to get the
+ * service. */
+ CFMutableDictionaryRef match_dict;
+ match_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL);
+ if(match_dict == NULL)
+ return -1;
+
+ /* set value to match. In case of the Ipod this is "iPodUserClientDevice". */
+ CFMutableDictionaryRef sub_dict;
+ sub_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL);
+ CFDictionarySetValue(sub_dict, CFSTR(kIOPropertySCSITaskDeviceCategory),
+ CFSTR("iPodUserClientDevice"));
+ CFDictionarySetValue(match_dict, CFSTR(kIOPropertyMatchKey), sub_dict);
+
+ if(sub_dict == NULL)
+ return -1;
+
+ /* get an iterator for searching for the service. */
+ kern_return_t kr;
+ io_iterator_t iterator = IO_OBJECT_NULL;
+ /* get matching services from IO registry. Consumes one reference to
+ * the dictionary, so no need to release that. */
+ kr = IOServiceGetMatchingServices(kIOMasterPortDefault, match_dict, &iterator);
+
+ if(!iterator | (kr != kIOReturnSuccess))
+ return -1;
+
+ /* get interface and obtain exclusive access */
+ SInt32 score;
+ HRESULT herr;
+ kern_return_t err;
+ IOCFPlugInInterface **plugin_interface = NULL;
+ SCSITaskDeviceInterface **interface = NULL;
+ io_service_t device = IO_OBJECT_NULL;
+ device = IOIteratorNext(iterator);
+
+ err = IOCreatePlugInInterfaceForService(device, kIOSCSITaskDeviceUserClientTypeID,
+ kIOCFPlugInInterfaceID, &plugin_interface,
+ &score);
+
+ if(err != noErr) {
+ return -1;
+ }
+ /* query the plugin interface for task interface */
+ herr = (*plugin_interface)->QueryInterface(plugin_interface,
+ CFUUIDGetUUIDBytes(kIOSCSITaskDeviceInterfaceID), (LPVOID*)&interface);
+ if(herr != S_OK) {
+ IODestroyPlugInInterface(plugin_interface);
+ return -1;
+ }
+
+ err = (*interface)->ObtainExclusiveAccess(interface);
+ if(err != noErr) {
+ (*interface)->Release(interface);
+ IODestroyPlugInInterface(plugin_interface);
+ return -1;
+ }
+
+ /* do the inquiry */
+ SCSITaskInterface **task = NULL;
+
+ task = (*interface)->CreateSCSITask(interface);
+ if(task != NULL) {
+ kern_return_t err;
+ SCSITaskStatus task_status;
+ IOVirtualRange* range;
+ SCSI_Sense_Data sense_data;
+ SCSICommandDescriptorBlock cdb;
+ UInt64 transfer_count = 0;
+ memset(buf, 0, bufsize);
+ /* allocate virtual range for buffer. */
+ range = (IOVirtualRange*) malloc(sizeof(IOVirtualRange));
+ memset(&sense_data, 0, sizeof(sense_data));
+ memset(cdb, 0, sizeof(cdb));
+ /* set up range. address is buffer address, length is request size. */
+ range->address = (IOVirtualAddress)buf;
+ range->length = bufsize;
+ /* setup CDB */
+ cdb[0] = 0x12; /* inquiry */
+ cdb[1] = 1;
+ cdb[2] = page_code;
+ cdb[4] = bufsize;
+
+ /* set cdb in task */
+ err = (*task)->SetCommandDescriptorBlock(task, cdb, kSCSICDBSize_6Byte);
+ if(err != kIOReturnSuccess) {
+ result = -1;
+ goto cleanup;
+ }
+ err = (*task)->SetScatterGatherEntries(task, range, 1, bufsize,
+ kSCSIDataTransfer_FromTargetToInitiator);
+ if(err != kIOReturnSuccess) {
+ result = -1;
+ goto cleanup;
+ }
+ /* set timeout */
+ err = (*task)->SetTimeoutDuration(task, 10000);
+ if(err != kIOReturnSuccess) {
+ result = -1;
+ goto cleanup;
+ }
+
+ /* request data */
+ err = (*task)->ExecuteTaskSync(task, &sense_data, &task_status, &transfer_count);
+ if(err != kIOReturnSuccess) {
+ result = -1;
+ goto cleanup;
+ }
+ /* cleanup */
+ free(range);
+
+ /* release task interface */
+ (*task)->Release(task);
+ }
+ else {
+ result = -1;
+ }
+cleanup:
+ /* cleanup interface */
+ (*interface)->ReleaseExclusiveAccess(interface);
+ (*interface)->Release(interface);
+ IODestroyPlugInInterface(plugin_interface);
+
+ return result;
}
#else
@@ -189,7 +327,7 @@ int ipod_open(struct ipod_t* ipod, int silent)
if (!silent) {
fprintf(stderr,"[ERR] ioctl() call to get sector size failed, defaulting to %d\n"
,ipod->sector_size);
- }
+ }
}
get_geometry(ipod);