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
);
172 CFDictionarySetValue(sub_dict
, CFSTR(kIOPropertySCSITaskDeviceCategory
),
173 CFSTR("iPodUserClientDevice"));
174 CFDictionarySetValue(match_dict
, CFSTR(kIOPropertyMatchKey
), sub_dict
);
176 /* get an iterator for searching for the service. */
178 io_iterator_t iterator
= IO_OBJECT_NULL
;
179 /* get matching services from IO registry. Consumes one reference to
180 * the dictionary, so no need to release that. */
181 kr
= IOServiceGetMatchingServices(kIOMasterPortDefault
, match_dict
, &iterator
);
183 if(!iterator
| (kr
!= kIOReturnSuccess
))
186 /* get interface and obtain exclusive access */
190 IOCFPlugInInterface
**plugin_interface
= NULL
;
191 SCSITaskDeviceInterface
**interface
= NULL
;
192 io_service_t device
= IO_OBJECT_NULL
;
193 device
= IOIteratorNext(iterator
);
195 err
= IOCreatePlugInInterfaceForService(device
, kIOSCSITaskDeviceUserClientTypeID
,
196 kIOCFPlugInInterfaceID
, &plugin_interface
,
202 /* query the plugin interface for task interface */
203 herr
= (*plugin_interface
)->QueryInterface(plugin_interface
,
204 CFUUIDGetUUIDBytes(kIOSCSITaskDeviceInterfaceID
), (LPVOID
*)&interface
);
206 IODestroyPlugInInterface(plugin_interface
);
210 err
= (*interface
)->ObtainExclusiveAccess(interface
);
212 (*interface
)->Release(interface
);
213 IODestroyPlugInInterface(plugin_interface
);
218 SCSITaskInterface
**task
= NULL
;
220 task
= (*interface
)->CreateSCSITask(interface
);
223 SCSITaskStatus task_status
;
224 IOVirtualRange
* range
;
225 SCSI_Sense_Data sense_data
;
226 SCSICommandDescriptorBlock cdb
;
227 UInt64 transfer_count
= 0;
228 memset(buf
, 0, bufsize
);
229 /* allocate virtual range for buffer. */
230 range
= (IOVirtualRange
*) malloc(sizeof(IOVirtualRange
));
231 memset(&sense_data
, 0, sizeof(sense_data
));
232 memset(cdb
, 0, sizeof(cdb
));
233 /* set up range. address is buffer address, length is request size. */
234 range
->address
= (IOVirtualAddress
)buf
;
235 range
->length
= bufsize
;
237 cdb
[0] = 0x12; /* inquiry */
242 /* set cdb in task */
243 err
= (*task
)->SetCommandDescriptorBlock(task
, cdb
, kSCSICDBSize_6Byte
);
244 if(err
!= kIOReturnSuccess
) {
248 err
= (*task
)->SetScatterGatherEntries(task
, range
, 1, bufsize
,
249 kSCSIDataTransfer_FromTargetToInitiator
);
250 if(err
!= kIOReturnSuccess
) {
255 err
= (*task
)->SetTimeoutDuration(task
, 10000);
256 if(err
!= kIOReturnSuccess
) {
262 err
= (*task
)->ExecuteTaskSync(task
, &sense_data
, &task_status
, &transfer_count
);
263 if(err
!= kIOReturnSuccess
) {
270 /* release task interface */
271 (*task
)->Release(task
);
277 /* cleanup interface */
278 (*interface
)->ReleaseExclusiveAccess(interface
);
279 (*interface
)->Release(interface
);
280 IODestroyPlugInInterface(plugin_interface
);
286 #error No sector-size detection implemented for this platform
289 #if defined(__APPLE__) && defined(__MACH__)
290 static int ipod_unmount(struct ipod_t
* ipod
)
295 sprintf(cmd
, "/usr/sbin/diskutil unmount \"%ss2\"",ipod
->diskname
);
296 fprintf(stderr
,"[INFO] ");
302 perror("Unmount failed");
308 void ipod_print_error(char* msg
)
313 int ipod_open(struct ipod_t
* ipod
, int silent
)
315 ipod
->dh
=open(ipod
->diskname
,O_RDONLY
);
317 if (!silent
) perror(ipod
->diskname
);
318 if(errno
== EACCES
) return -2;
322 /* Read information about the disk */
324 if(ioctl(ipod
->dh
,IPOD_SECTORSIZE_IOCTL
,&ipod
->sector_size
) < 0) {
325 ipod
->sector_size
=512;
327 fprintf(stderr
,"[ERR] ioctl() call to get sector size failed, defaulting to %d\n"
338 int ipod_reopen_rw(struct ipod_t
* ipod
)
340 #if defined(__APPLE__) && defined(__MACH__)
341 if (ipod_unmount(ipod
) < 0)
346 ipod
->dh
=open(ipod
->diskname
,O_RDWR
);
348 perror(ipod
->diskname
);
354 int ipod_close(struct ipod_t
* ipod
)
360 int ipod_alloc_buffer(unsigned char** sectorbuf
, int bufsize
)
362 *sectorbuf
=malloc(bufsize
);
363 if (*sectorbuf
== NULL
) {
369 int ipod_seek(struct ipod_t
* ipod
, unsigned long pos
)
373 res
= lseek(ipod
->dh
, pos
, SEEK_SET
);
381 ssize_t
ipod_read(struct ipod_t
* ipod
, unsigned char* buf
, int nbytes
)
383 return read(ipod
->dh
, buf
, nbytes
);
386 ssize_t
ipod_write(struct ipod_t
* ipod
, unsigned char* buf
, int nbytes
)
388 return write(ipod
->dh
, buf
, nbytes
);