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 ****************************************************************************/
28 #include <sys/types.h>
30 #include <sys/ioctl.h>
35 #if defined(linux) || defined (__linux)
36 #include <sys/mount.h>
37 #include <linux/hdreg.h>
38 #include <scsi/scsi_ioctl.h>
41 #define IPOD_SECTORSIZE_IOCTL BLKSSZGET
43 static void get_geometry(struct ipod_t
* ipod
)
45 struct hd_geometry geometry
;
47 if (!ioctl(ipod
->dh
, HDIO_GETGEO
, &geometry
)) {
48 /* never use geometry.cylinders - it is truncated */
49 ipod
->num_heads
= geometry
.heads
;
50 ipod
->sectors_per_track
= geometry
.sectors
;
53 ipod
->sectors_per_track
= 0;
57 /* Linux SCSI Inquiry code based on the documentation and example code from
58 http://www.ibm.com/developerworks/linux/library/l-scsi-api/index.html
61 int ipod_scsi_inquiry(struct ipod_t
* ipod
, int page_code
,
62 unsigned char* buf
, int bufsize
)
66 unsigned char sense_buffer
[255];
68 memset(&hdr
, 0, sizeof(hdr
));
70 hdr
.interface_id
= 'S'; /* this is the only choice we have! */
71 hdr
.flags
= SG_FLAG_LUN_INHIBIT
; /* this would put the LUN to 2nd byte of cdb*/
75 hdr
.dxfer_len
= bufsize
;
78 hdr
.sbp
= sense_buffer
;
79 hdr
.mx_sb_len
= sizeof(sense_buffer
);
81 /* Set the cdb format */
83 cdb
[1] = 1; /* Enable Vital Product Data (EVPD) */
84 cdb
[2] = page_code
& 0xff;
87 cdb
[5] = 0; /* For control filed, just use 0 */
89 hdr
.dxfer_direction
= SG_DXFER_FROM_DEV
;
93 int ret
= ioctl(ipod
->dh
, SG_IO
, &hdr
);
102 #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \
103 || defined(__bsdi__) || defined(__DragonFly__)
104 #include <sys/disk.h>
105 #define IPOD_SECTORSIZE_IOCTL DIOCGSECTORSIZE
107 /* TODO: Implement this function for BSD */
108 static void get_geometry(struct ipod_t
* ipod
)
110 /* Are these universal for all ipods? */
111 ipod
->num_heads
= 255;
112 ipod
->sectors_per_track
= 63;
115 int ipod_scsi_inquiry(struct ipod_t
* ipod
, int page_code
,
116 unsigned char* buf
, int bufsize
)
118 /* TODO: Implement for BSD */
126 #elif defined(__APPLE__) && defined(__MACH__)
127 /* OS X IOKit includes don't like VERSION being defined! */
129 #include <sys/disk.h>
130 #include <CoreFoundation/CoreFoundation.h>
131 #include <IOKit/IOKitLib.h>
132 #include <IOKit/scsi-commands/SCSITaskLib.h>
133 #include <IOKit/scsi-commands/SCSICommandOperationCodes.h>
134 #define IPOD_SECTORSIZE_IOCTL DKIOCGETBLOCKSIZE
136 /* TODO: Implement this function for Mac OS X */
137 static void get_geometry(struct ipod_t
* ipod
)
139 /* Are these universal for all ipods? */
140 ipod
->num_heads
= 255;
141 ipod
->sectors_per_track
= 63;
144 int ipod_scsi_inquiry(struct ipod_t
* ipod
, int page_code
,
145 unsigned char* buf
, int bufsize
)
147 /* OS X doesn't allow to simply send out a SCSI inquiry request but
148 * requires registering an interface handler first.
149 * Currently this is done on each inquiry request which is somewhat
150 * inefficient but the current ipodpatcher API doesn't really fit here.
151 * Based on the documentation in Apple's document
152 * "SCSI Architecture Model Device Interface Guide".
154 * WARNING: this code currently doesn't take the selected device into
155 * account. It simply looks for an Ipod on the system and uses
160 /* first, create a dictionary to match the device. This is needed to get the
162 CFMutableDictionaryRef match_dict
;
163 match_dict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, NULL
, NULL
);
164 if(match_dict
== NULL
)
167 /* set value to match. In case of the Ipod this is "iPodUserClientDevice". */
168 CFMutableDictionaryRef sub_dict
;
169 sub_dict
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, NULL
, NULL
);
170 CFDictionarySetValue(sub_dict
, CFSTR(kIOPropertySCSITaskDeviceCategory
),
171 CFSTR("iPodUserClientDevice"));
172 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(unsigned char** sectorbuf
, int bufsize
)
363 *sectorbuf
=malloc(bufsize
);
364 if (*sectorbuf
== NULL
) {
370 int ipod_seek(struct ipod_t
* ipod
, unsigned long pos
)
374 res
= lseek(ipod
->dh
, pos
, SEEK_SET
);
382 ssize_t
ipod_read(struct ipod_t
* ipod
, unsigned char* buf
, int nbytes
)
384 return read(ipod
->dh
, buf
, nbytes
);
387 ssize_t
ipod_write(struct ipod_t
* ipod
, unsigned char* buf
, int nbytes
)
389 return write(ipod
->dh
, buf
, nbytes
);