1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2006-2007 Dave Chapman
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #if !defined(_WIN32) /* all non-Windows platforms are considered POSIX. */
29 #include <sys/types.h>
31 #include <sys/ioctl.h>
36 #if defined(linux) || defined (__linux)
37 #include <sys/mount.h>
38 #include <linux/hdreg.h>
39 #include <scsi/scsi_ioctl.h>
42 #define IPOD_SECTORSIZE_IOCTL BLKSSZGET
44 static void get_geometry(struct ipod_t
* ipod
)
46 struct hd_geometry geometry
;
48 if (!ioctl(ipod
->dh
, HDIO_GETGEO
, &geometry
)) {
49 /* never use geometry.cylinders - it is truncated */
50 ipod
->num_heads
= geometry
.heads
;
51 ipod
->sectors_per_track
= geometry
.sectors
;
54 ipod
->sectors_per_track
= 0;
58 /* Linux SCSI Inquiry code based on the documentation and example code from
59 http://www.ibm.com/developerworks/linux/library/l-scsi-api/index.html
62 int ipod_scsi_inquiry(struct ipod_t
* ipod
, int page_code
,
63 unsigned char* buf
, int bufsize
)
67 unsigned char sense_buffer
[255];
69 memset(&hdr
, 0, sizeof(hdr
));
71 hdr
.interface_id
= 'S'; /* this is the only choice we have! */
72 hdr
.flags
= SG_FLAG_LUN_INHIBIT
; /* this would put the LUN to 2nd byte of cdb*/
76 hdr
.dxfer_len
= bufsize
;
79 hdr
.sbp
= sense_buffer
;
80 hdr
.mx_sb_len
= sizeof(sense_buffer
);
82 /* Set the cdb format */
84 cdb
[1] = 1; /* Enable Vital Product Data (EVPD) */
85 cdb
[2] = page_code
& 0xff;
88 cdb
[5] = 0; /* For control filed, just use 0 */
90 hdr
.dxfer_direction
= SG_DXFER_FROM_DEV
;
94 int ret
= ioctl(ipod
->dh
, SG_IO
, &hdr
);
103 #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \
104 || defined(__bsdi__) || defined(__DragonFly__)
105 #include <sys/disk.h>
106 #define IPOD_SECTORSIZE_IOCTL DIOCGSECTORSIZE
108 /* TODO: Implement this function for BSD */
109 static void get_geometry(struct ipod_t
* ipod
)
111 /* Are these universal for all ipods? */
112 ipod
->num_heads
= 255;
113 ipod
->sectors_per_track
= 63;
116 int ipod_scsi_inquiry(struct ipod_t
* ipod
, int page_code
,
117 unsigned char* buf
, int bufsize
)
119 /* TODO: Implement for BSD */
127 #elif defined(__APPLE__) && defined(__MACH__)
128 /* OS X IOKit includes don't like VERSION being defined! */
130 #include <sys/disk.h>
131 #include <CoreFoundation/CoreFoundation.h>
132 #include <IOKit/IOKitLib.h>
133 #include <IOKit/scsi-commands/SCSITaskLib.h>
134 #include <IOKit/scsi-commands/SCSICommandOperationCodes.h>
135 #define IPOD_SECTORSIZE_IOCTL DKIOCGETBLOCKSIZE
137 /* TODO: Implement this function for Mac OS X */
138 static void get_geometry(struct ipod_t
* ipod
)
140 /* Are these universal for all ipods? */
141 ipod
->num_heads
= 255;
142 ipod
->sectors_per_track
= 63;
145 int ipod_scsi_inquiry(struct ipod_t
* ipod
, int page_code
,
146 unsigned char* buf
, int bufsize
)
148 /* OS X doesn't allow to simply send out a SCSI inquiry request but
149 * requires registering an interface handler first.
150 * Currently this is done on each inquiry request which is somewhat
151 * inefficient but the current ipodpatcher API doesn't really fit here.
152 * Based on the documentation in Apple's document
153 * "SCSI Architecture Model Device Interface Guide".
155 * WARNING: this code currently doesn't take the selected device into
156 * account. It simply looks for an Ipod on the system and uses
161 /* first, create a dictionary to match the device. This is needed to get the
163 CFMutableDictionaryRef match_dict
;
164 match_dict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, NULL
, NULL
);
165 if(match_dict
== NULL
)
168 /* set value to match. In case of the Ipod this is "iPodUserClientDevice". */
169 CFMutableDictionaryRef sub_dict
;
170 sub_dict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, NULL
, NULL
);
173 CFDictionarySetValue(sub_dict
, CFSTR(kIOPropertySCSITaskDeviceCategory
),
174 CFSTR("iPodUserClientDevice"));
175 CFDictionarySetValue(match_dict
, CFSTR(kIOPropertyMatchKey
), sub_dict
);
177 /* get an iterator for searching for the service. */
179 io_iterator_t iterator
= IO_OBJECT_NULL
;
180 /* get matching services from IO registry. Consumes one reference to
181 * the dictionary, so no need to release that. */
182 kr
= IOServiceGetMatchingServices(kIOMasterPortDefault
, match_dict
, &iterator
);
184 if(!iterator
| (kr
!= kIOReturnSuccess
))
187 /* get interface and obtain exclusive access */
191 IOCFPlugInInterface
**plugin_interface
= NULL
;
192 SCSITaskDeviceInterface
**interface
= NULL
;
193 io_service_t device
= IO_OBJECT_NULL
;
194 device
= IOIteratorNext(iterator
);
196 err
= IOCreatePlugInInterfaceForService(device
, kIOSCSITaskDeviceUserClientTypeID
,
197 kIOCFPlugInInterfaceID
, &plugin_interface
,
203 /* query the plugin interface for task interface */
204 herr
= (*plugin_interface
)->QueryInterface(plugin_interface
,
205 CFUUIDGetUUIDBytes(kIOSCSITaskDeviceInterfaceID
), (LPVOID
*)&interface
);
207 IODestroyPlugInInterface(plugin_interface
);
211 err
= (*interface
)->ObtainExclusiveAccess(interface
);
213 (*interface
)->Release(interface
);
214 IODestroyPlugInInterface(plugin_interface
);
219 SCSITaskInterface
**task
= NULL
;
221 task
= (*interface
)->CreateSCSITask(interface
);
224 SCSITaskStatus task_status
;
225 IOVirtualRange
* range
;
226 SCSI_Sense_Data sense_data
;
227 SCSICommandDescriptorBlock cdb
;
228 UInt64 transfer_count
= 0;
229 memset(buf
, 0, bufsize
);
230 /* allocate virtual range for buffer. */
231 range
= (IOVirtualRange
*) malloc(sizeof(IOVirtualRange
));
232 memset(&sense_data
, 0, sizeof(sense_data
));
233 memset(cdb
, 0, sizeof(cdb
));
234 /* set up range. address is buffer address, length is request size. */
235 range
->address
= (IOVirtualAddress
)buf
;
236 range
->length
= bufsize
;
238 cdb
[0] = 0x12; /* inquiry */
243 /* set cdb in task */
244 err
= (*task
)->SetCommandDescriptorBlock(task
, cdb
, kSCSICDBSize_6Byte
);
245 if(err
!= kIOReturnSuccess
) {
249 err
= (*task
)->SetScatterGatherEntries(task
, range
, 1, bufsize
,
250 kSCSIDataTransfer_FromTargetToInitiator
);
251 if(err
!= kIOReturnSuccess
) {
256 err
= (*task
)->SetTimeoutDuration(task
, 10000);
257 if(err
!= kIOReturnSuccess
) {
263 err
= (*task
)->ExecuteTaskSync(task
, &sense_data
, &task_status
, &transfer_count
);
264 if(err
!= kIOReturnSuccess
) {
271 /* release task interface */
272 (*task
)->Release(task
);
278 /* cleanup interface */
279 (*interface
)->ReleaseExclusiveAccess(interface
);
280 (*interface
)->Release(interface
);
281 IODestroyPlugInInterface(plugin_interface
);
287 #error No sector-size detection implemented for this platform
290 #if defined(__APPLE__) && defined(__MACH__)
291 static int ipod_unmount(struct ipod_t
* ipod
)
296 sprintf(cmd
, "/usr/sbin/diskutil unmount \"%ss2\"",ipod
->diskname
);
297 fprintf(stderr
,"[INFO] ");
303 perror("Unmount failed");
309 void ipod_print_error(char* msg
)
314 int ipod_open(struct ipod_t
* ipod
, int silent
)
316 ipod
->dh
=open(ipod
->diskname
,O_RDONLY
);
318 if (!silent
) perror(ipod
->diskname
);
319 if(errno
== EACCES
) return -2;
323 /* Read information about the disk */
325 if(ioctl(ipod
->dh
,IPOD_SECTORSIZE_IOCTL
,&ipod
->sector_size
) < 0) {
326 ipod
->sector_size
=512;
328 fprintf(stderr
,"[ERR] ioctl() call to get sector size failed, defaulting to %d\n"
339 int ipod_reopen_rw(struct ipod_t
* ipod
)
341 #if defined(__APPLE__) && defined(__MACH__)
342 if (ipod_unmount(ipod
) < 0)
347 ipod
->dh
=open(ipod
->diskname
,O_RDWR
);
349 perror(ipod
->diskname
);
355 int ipod_close(struct ipod_t
* ipod
)
361 int ipod_alloc_buffer(struct ipod_t
* ipod
, int bufsize
)
363 ipod
->sectorbuf
= malloc(bufsize
);
364 if (ipod
->sectorbuf
== NULL
) {
370 int ipod_dealloc_buffer(struct ipod_t
* ipod
)
372 if (ipod
->sectorbuf
== NULL
) {
375 free(ipod
->sectorbuf
);
376 ipod
->sectorbuf
= NULL
;
380 int ipod_seek(struct ipod_t
* ipod
, unsigned long pos
)
384 res
= lseek(ipod
->dh
, pos
, SEEK_SET
);
392 ssize_t
ipod_read(struct ipod_t
* ipod
, int nbytes
)
394 if(ipod
->sectorbuf
== NULL
) {
397 return read(ipod
->dh
, ipod
->sectorbuf
, nbytes
);
400 ssize_t
ipod_write(struct ipod_t
* ipod
, int nbytes
)
402 if(ipod
->sectorbuf
== NULL
) {
405 return write(ipod
->dh
, ipod
->sectorbuf
, nbytes
);