1 /////////////////////////////////////////////////////////////////////////
2 // $Id: cdrom.cc,v 1.92 2008/10/01 07:47:02 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
5 // Copyright (C) 2002 MandrakeSoft S.A.
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 /////////////////////////////////////////////////////////////////////////
28 // These are the low-level CDROM functions which are called
29 // from 'harddrv.cc'. They effect the OS specific functionality
30 // needed by the CDROM emulation in 'harddrv.cc'. Mostly, just
31 // ioctl() calls and such. Should be fairly easy to add support
32 // for your OS if it is not supported yet.
34 #ifndef BX_IODEV_CDROM_H
35 #define BX_IODEV_CDROM_H
37 // Define BX_PLUGGABLE in files that can be compiled into plugins. For
38 // platforms that require a special tag on exported symbols, BX_PLUGGABLE
39 // is used to know when we are exporting symbols and when we are importing.
47 #define LOG_THIS /* no SMF tricks here, not needed */
55 #include <sys/ioctl.h>
56 #include <linux/cdrom.h>
57 // I use the framesize in non OS specific code too
58 #define BX_CD_FRAMESIZE CD_FRAMESIZE
61 #elif defined(__GNU__) || (defined(__CYGWIN32__) && !defined(WIN32))
63 #include <sys/ioctl.h>
64 #define BX_CD_FRAMESIZE 2048
65 #define CD_FRAMESIZE 2048
69 #define BX_CD_FRAMESIZE 2048
70 #define CD_FRAMESIZE 2048
74 #include <sys/types.h>
76 #include <sys/ioctl.h>
78 #define BX_CD_FRAMESIZE CDROM_BLK_2048
81 #elif defined(__DJGPP__)
83 #include <sys/ioctl.h>
84 #define BX_CD_FRAMESIZE 2048
85 #define CD_FRAMESIZE 2048
88 #elif defined(__BEOS__)
89 #include "cdrom_beos.h"
90 #define BX_CD_FRAMESIZE 2048
92 #elif (defined(__NetBSD__) || defined(__NetBSD_kernel__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__))
93 // OpenBSD pre version 2.7 may require extern "C" { } structure around
94 // all the includes, because the i386 sys/disklabel.h contains code which
95 // c++ considers invalid.
96 #include <sys/types.h>
97 #include <sys/param.h>
100 #include <sys/ioctl.h>
101 #include <sys/disklabel.h>
102 // ntohl(x) et al have been moved out of sys/param.h in FreeBSD 5
103 #include <netinet/in.h>
106 #define BX_CD_FRAMESIZE 2048
107 #define CD_FRAMESIZE 2048
109 #elif defined(__APPLE__)
113 #include <sys/ioctl.h>
114 #if defined (__GNUC__) && (__GNUC__ >= 4)
115 #include <sys/disk.h>
117 #include <dev/disk.h>
121 #include <sys/param.h>
123 #define Float32 KLUDGE_Float32
124 #define Float64 KLUDGE_Float64
125 #include <IOKit/IOKitLib.h>
126 #include <IOKit/IOBSD.h>
127 #include <IOKit/storage/IOCDMedia.h>
128 #include <IOKit/storage/IOMedia.h>
129 #include <IOKit/storage/IOCDTypes.h>
130 #include <CoreFoundation/CoreFoundation.h>
134 // These definitions were taken from mount_cd9660.c
135 // There are some similar definitions in IOCDTypes.h
136 // however there seems to be some dissagreement in
137 // the definition of CDTOC.length
144 #define MSF_TO_LBA(msf) \
145 (((((msf).minute * 60UL) + (msf).second) * 75UL) + (msf).frame - 150)
149 u_char ctrl_adr
; /* typed to be machine and compiler independent */
152 struct _CDMSF address
;
158 u_short length
; /* in native cpu endian */
159 u_char first_session
;
161 struct _CDTOC_Desc trackdesc
[1];
164 static kern_return_t
FindEjectableCDMedia(io_iterator_t
*mediaIterator
, mach_port_t
*masterPort
);
165 static kern_return_t
GetDeviceFilePath(io_iterator_t mediaIterator
, char *deviceFilePath
, CFIndex maxPathSize
);
166 //int OpenDrive(const char *deviceFilePath);
167 static struct _CDTOC
*ReadTOC(const char *devpath
);
169 static char CDDevicePath
[MAXPATHLEN
];
171 #define BX_CD_FRAMESIZE 2048
172 #define CD_FRAMESIZE 2048
175 // windows.h included by bochs.h
176 #include <winioctl.h>
177 #include "aspi-win32.h"
178 #include "scsidefs.h"
180 DWORD (*GetASPI32SupportInfo
)(void);
181 DWORD (*SendASPI32Command
)(LPSRB
);
182 BOOL (*GetASPI32Buffer
)(PASPI32BUFF
);
183 BOOL (*FreeASPI32Buffer
)(PASPI32BUFF
);
184 BOOL (*TranslateASPI32Address
)(PDWORD
,PDWORD
);
185 DWORD (*GetASPI32DLLVersion
)(void);
188 static OSVERSIONINFO osinfo
;
189 static BOOL isWindowsXP
;
190 static BOOL bHaveDev
;
191 static UINT cdromCount
= 0;
192 static HINSTANCE hASPI
= NULL
;
194 #define BX_CD_FRAMESIZE 2048
195 #define CD_FRAMESIZE 2048
197 // READ_TOC_EX structure(s) and #defines
199 #define CDROM_READ_TOC_EX_FORMAT_TOC 0x00
200 #define CDROM_READ_TOC_EX_FORMAT_SESSION 0x01
201 #define CDROM_READ_TOC_EX_FORMAT_FULL_TOC 0x02
202 #define CDROM_READ_TOC_EX_FORMAT_PMA 0x03
203 #define CDROM_READ_TOC_EX_FORMAT_ATIP 0x04
204 #define CDROM_READ_TOC_EX_FORMAT_CDTEXT 0x05
206 #define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM
207 #define IOCTL_CDROM_READ_TOC_EX CTL_CODE(IOCTL_CDROM_BASE, 0x0015, METHOD_BUFFERED, FILE_READ_ACCESS)
208 #ifndef IOCTL_DISK_GET_LENGTH_INFO
209 #define IOCTL_DISK_GET_LENGTH_INFO CTL_CODE(IOCTL_DISK_BASE, 0x0017, METHOD_BUFFERED, FILE_READ_ACCESS)
212 typedef struct _CDROM_READ_TOC_EX
{
214 UCHAR Reserved1
: 3; // future expansion
217 UCHAR Reserved2
; // future expansion
218 UCHAR Reserved3
; // future expansion
219 } CDROM_READ_TOC_EX
, *PCDROM_READ_TOC_EX
;
221 typedef struct _TRACK_DATA
{
228 } TRACK_DATA
, *PTRACK_DATA
;
230 typedef struct _CDROM_TOC_SESSION_DATA
{
232 UCHAR Length
[2]; // add two bytes for this field
233 UCHAR FirstCompleteSession
;
234 UCHAR LastCompleteSession
;
235 // One track, representing the first track
236 // of the last finished session
237 TRACK_DATA TrackData
[1];
238 } CDROM_TOC_SESSION_DATA
, *PCDROM_TOC_SESSION_DATA
;
240 // End READ_TOC_EX structure(s) and #defines
242 #else // all others (Irix, Tru64)
243 #include <sys/types.h>
244 #include <sys/stat.h>
245 #include <sys/ioctl.h>
246 #define BX_CD_FRAMESIZE 2048
247 #define CD_FRAMESIZE 2048
253 static kern_return_t
FindEjectableCDMedia(io_iterator_t
*mediaIterator
,
254 mach_port_t
*masterPort
)
256 kern_return_t kernResult
;
257 CFMutableDictionaryRef classesToMatch
;
258 kernResult
= IOMasterPort(bootstrap_port
, masterPort
);
259 if (kernResult
!= KERN_SUCCESS
)
261 fprintf (stderr
, "IOMasterPort returned %d\n", kernResult
);
264 // CD media are instances of class kIOCDMediaClass.
265 classesToMatch
= IOServiceMatching(kIOCDMediaClass
);
266 if (classesToMatch
== NULL
)
267 fprintf (stderr
, "IOServiceMatching returned a NULL dictionary.\n");
270 // Each IOMedia object has a property with key kIOMediaEjectableKey
271 // which is true if the media is indeed ejectable. So add property
272 // to CFDictionary for matching.
273 CFDictionarySetValue(classesToMatch
,
274 CFSTR(kIOMediaEjectableKey
), kCFBooleanTrue
);
276 kernResult
= IOServiceGetMatchingServices(*masterPort
,
277 classesToMatch
, mediaIterator
);
278 if ((kernResult
!= KERN_SUCCESS
) || (*mediaIterator
== NULL
))
279 fprintf(stderr
, "No ejectable CD media found.\n kernResult = %d\n", kernResult
);
284 static kern_return_t
GetDeviceFilePath(io_iterator_t mediaIterator
,
285 char *deviceFilePath
, CFIndex maxPathSize
)
287 io_object_t nextMedia
;
288 kern_return_t kernResult
= KERN_FAILURE
;
289 nextMedia
= IOIteratorNext(mediaIterator
);
290 if (nextMedia
== NULL
)
292 *deviceFilePath
= '\0';
296 CFTypeRef deviceFilePathAsCFString
;
297 deviceFilePathAsCFString
= IORegistryEntryCreateCFProperty(nextMedia
, CFSTR(kIOBSDNameKey
),
298 kCFAllocatorDefault
, 0);
299 *deviceFilePath
= '\0';
300 if (deviceFilePathAsCFString
)
302 size_t devPathLength
= strlen(_PATH_DEV
);
303 strcpy(deviceFilePath
, _PATH_DEV
);
304 if (CFStringGetCString((const __CFString
*) deviceFilePathAsCFString
,
305 deviceFilePath
+ devPathLength
,
306 maxPathSize
- devPathLength
,
307 kCFStringEncodingASCII
))
309 // fprintf(stderr, "BSD path: %s\n", deviceFilePath);
310 kernResult
= KERN_SUCCESS
;
312 CFRelease(deviceFilePathAsCFString
);
316 IOObjectRelease(nextMedia
);
320 static int OpenDrive(const char *deviceFilePath
)
322 int fileDescriptor
= open(deviceFilePath
, O_RDONLY
);
323 if (fileDescriptor
== -1)
324 fprintf(stderr
, "Error %d opening device %s.\n", errno
, deviceFilePath
);
325 return fileDescriptor
;
328 static struct _CDTOC
* ReadTOC(const char *devpath
)
330 struct _CDTOC
* toc_p
= NULL
;
331 io_iterator_t iterator
= 0;
332 io_registry_entry_t service
= 0;
333 CFDictionaryRef properties
= 0;
335 mach_port_t port
= 0;
338 if ((devname
= strrchr(devpath
, '/')) != NULL
) {
342 devname
= (char *) devpath
;
345 if (IOMasterPort(bootstrap_port
, &port
) != KERN_SUCCESS
) {
346 fprintf(stderr
, "IOMasterPort failed\n");
350 if (IOServiceGetMatchingServices(port
, IOBSDNameMatching(port
, 0, devname
),
351 &iterator
) != KERN_SUCCESS
) {
352 fprintf(stderr
, "IOServiceGetMatchingServices failed\n");
356 service
= IOIteratorNext(iterator
);
358 IOObjectRelease(iterator
);
362 while (service
&& !IOObjectConformsTo(service
, "IOCDMedia")) {
363 if (IORegistryEntryGetParentIterator(service
, kIOServicePlane
,
364 &iterator
) != KERN_SUCCESS
)
366 fprintf(stderr
, "IORegistryEntryGetParentIterator failed\n");
370 IOObjectRelease(service
);
371 service
= IOIteratorNext(iterator
);
372 IOObjectRelease(iterator
);
375 if (service
== NULL
) {
376 fprintf(stderr
, "CD media not found\n");
380 if (IORegistryEntryCreateCFProperties(service
, (__CFDictionary
**) &properties
,
382 kNilOptions
) != KERN_SUCCESS
)
384 fprintf(stderr
, "IORegistryEntryGetParentIterator failed\n");
388 data
= (CFDataRef
) CFDictionaryGetValue(properties
, CFSTR(kIOCDMediaTOCKey
));
390 fprintf(stderr
, "CFDictionaryGetValue failed\n");
397 buflen
= CFDataGetLength(data
) + 1;
398 range
= CFRangeMake(0, buflen
);
399 toc_p
= (struct _CDTOC
*) malloc(buflen
);
401 fprintf(stderr
, "Out of memory\n");
405 CFDataGetBytes(data
, range
, (unsigned char *) toc_p
);
409 fprintf(stderr, "Table of contents\n length %d first %d last %d\n",
410 toc_p->length, toc_p->first_session, toc_p->last_session);
413 CFRelease(properties
);
419 IOObjectRelease(service
);
428 bool ReadCDSector(unsigned int hid
, unsigned int tid
, unsigned int lun
, unsigned long frame
, unsigned char *buf
, int bufsize
)
434 hEventSRB
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
436 memset(&srb
,0,sizeof(SRB_ExecSCSICmd
));
437 srb
.SRB_Cmd
= SC_EXEC_SCSI_CMD
;
439 srb
.SRB_Target
= tid
;
441 srb
.SRB_Flags
= SRB_DIR_IN
| SRB_EVENT_NOTIFY
;
442 srb
.SRB_SenseLen
= SENSE_LEN
;
443 srb
.SRB_PostProc
= hEventSRB
;
444 srb
.SRB_BufPointer
= buf
;
445 srb
.SRB_BufLen
= bufsize
;
447 srb
.CDBByte
[0] = SCSI_READ10
;
448 srb
.CDBByte
[2] = (unsigned char) (frame
>>24);
449 srb
.CDBByte
[3] = (unsigned char) (frame
>>16);
450 srb
.CDBByte
[4] = (unsigned char) (frame
>>8);
451 srb
.CDBByte
[5] = (unsigned char) (frame
);
453 srb
.CDBByte
[8] = 1; /* read 1 frames */
455 ResetEvent(hEventSRB
);
456 dwStatus
= SendASPI32Command((SRB
*)&srb
);
457 if(dwStatus
== SS_PENDING
) {
458 WaitForSingleObject(hEventSRB
, 100000);
460 CloseHandle(hEventSRB
);
461 return (srb
.SRB_TargStat
== STATUS_GOOD
);
464 int GetCDCapacity(unsigned int hid
, unsigned int tid
, unsigned int lun
)
469 unsigned char buf
[8];
471 hEventSRB
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
473 memset(&buf
, 0, sizeof(buf
));
474 memset(&srb
,0,sizeof(SRB_ExecSCSICmd
));
475 srb
.SRB_Cmd
= SC_EXEC_SCSI_CMD
;
477 srb
.SRB_Target
= tid
;
479 srb
.SRB_Flags
= SRB_DIR_IN
| SRB_EVENT_NOTIFY
;
480 srb
.SRB_SenseLen
= SENSE_LEN
;
481 srb
.SRB_PostProc
= hEventSRB
;
482 srb
.SRB_BufPointer
= (unsigned char *)buf
;
485 srb
.CDBByte
[0] = SCSI_READCDCAP
;
492 ResetEvent(hEventSRB
);
493 dwStatus
= SendASPI32Command((SRB
*)&srb
);
494 if(dwStatus
== SS_PENDING
) {
495 WaitForSingleObject(hEventSRB
, 100000);
498 CloseHandle(hEventSRB
);
499 return ((buf
[0] << 24) + (buf
[1] << 16) + (buf
[2] << 8) + buf
[3]) * ((buf
[4] << 24) + (buf
[5] << 16) + (buf
[6] << 8) + buf
[7]);
504 cdrom_interface::cdrom_interface(char *dev
)
508 fd
= -1; // File descriptor not yet allocated
518 osinfo
.dwOSVersionInfoSize
= sizeof(osinfo
);
519 GetVersionEx(&osinfo
);
520 isWindowsXP
= (osinfo
.dwMajorVersion
>= 5) && (osinfo
.dwMinorVersion
>= 1);
525 cdrom_interface::init(void) {
526 BX_DEBUG(("Init $Id: cdrom.cc,v 1.92 2008/10/01 07:47:02 sshwarts Exp $"));
527 BX_INFO(("file = '%s'",path
));
530 cdrom_interface::~cdrom_interface(void)
543 cdrom_interface::insert_cdrom(char *dev
)
545 unsigned char buffer
[BX_CD_FRAMESIZE
];
550 // Load CD-ROM. Returns 0 if CD is not ready.
551 if (dev
!= NULL
) path
= strdup(dev
);
552 BX_INFO (("load cdrom with path=%s", path
));
555 if ((path
[1] == ':') && (strlen(path
) == 2))
557 if(osinfo
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) {
558 // Use direct device access under windows NT/2k/XP
560 // With all the backslashes it's hard to see, but to open D: drive
561 // the name would be: \\.\d:
562 sprintf(drive
, "\\\\.\\%s", path
);
563 BX_INFO (("Using direct access for cdrom."));
564 // This trick only works for Win2k and WinNT, so warn the user of that.
566 BX_INFO(("Using ASPI for cdrom. Drive letters are unused yet."));
575 BX_INFO (("Opening image file as a cd"));
584 hASPI
= LoadLibrary("WNASPI32.DLL");
586 SendASPI32Command
= (DWORD(*)(LPSRB
))GetProcAddress(hASPI
, "SendASPI32Command");
587 GetASPI32DLLVersion
= (DWORD(*)(void))GetProcAddress(hASPI
, "GetASPI32DLLVersion");
588 GetASPI32SupportInfo
= (DWORD(*)(void))GetProcAddress(hASPI
, "GetASPI32SupportInfo");
589 d
= GetASPI32DLLVersion();
590 BX_INFO(("WNASPI32.DLL version %d.%02d initialized", d
& 0xff, (d
>> 8) & 0xff));
592 BX_PANIC(("Could not load ASPI drivers, so cdrom access will fail"));
598 d
= GetASPI32SupportInfo();
599 cnt
= LOBYTE(LOWORD(d
));
600 for(i
= 0; i
< cnt
; i
++) {
601 memset(&sh
, 0, sizeof(sh
));
602 sh
.SRB_Cmd
= SC_HA_INQUIRY
;
604 SendASPI32Command((LPSRB
)&sh
);
605 if(sh
.SRB_Status
!= SS_COMP
)
608 max
= (int)sh
.HA_Unique
[3];
609 for(j
= 0; j
< max
; j
++) {
610 for(k
= 0; k
< 8; k
++) {
611 memset(&sd
, 0, sizeof(sd
));
612 sd
.SRB_Cmd
= SC_GET_DEV_TYPE
;
616 SendASPI32Command((LPSRB
)&sd
);
617 if(sd
.SRB_Status
== SS_COMP
) {
618 if(sd
.SRB_DeviceType
== DTYPE_CDROM
) {
620 if(cdr
> cdromCount
) {
636 hFile
=CreateFile((char *)&drive
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_FLAG_RANDOM_ACCESS
, NULL
);
637 if (hFile
!=(void *)0xFFFFFFFF)
640 DWORD lpBytesReturned
;
641 DeviceIoControl(hFile
, IOCTL_STORAGE_LOAD_MEDIA
, NULL
, 0, NULL
, 0, &lpBytesReturned
, NULL
);
644 #elif defined(__APPLE__)
645 if(strcmp(path
, "drive") == 0)
647 mach_port_t masterPort
= NULL
;
648 io_iterator_t mediaIterator
;
649 kern_return_t kernResult
;
651 BX_INFO(("Insert CDROM"));
653 kernResult
= FindEjectableCDMedia(&mediaIterator
, &masterPort
);
654 if (kernResult
!= KERN_SUCCESS
) {
655 BX_INFO(("Unable to find CDROM"));
659 kernResult
= GetDeviceFilePath(mediaIterator
, CDDevicePath
, sizeof(CDDevicePath
));
660 if (kernResult
!= KERN_SUCCESS
) {
661 BX_INFO(("Unable to get CDROM device file path"));
665 // Here a cdrom was found so see if we can read from it.
666 // At this point a failure will result in panic.
667 if (strlen(CDDevicePath
)) {
668 fd
= open(CDDevicePath
, O_RDONLY
);
672 fd
= open(path
, O_RDONLY
);
675 // all platforms except win32
676 fd
= open(path
, O_RDONLY
);
679 BX_ERROR(("open cd failed for %s: %s", path
, strerror(errno
)));
683 // do fstat to determine if it's a file or a device, then set using_file.
684 struct stat stat_buf
;
685 ret
= fstat (fd
, &stat_buf
);
687 BX_PANIC (("fstat cdrom file returned error: %s", strerror (errno
)));
689 if (S_ISREG (stat_buf
.st_mode
)) {
691 BX_INFO (("Opening image file %s as a cd.", path
));
694 BX_INFO (("Using direct access for cdrom."));
698 // I just see if I can read a sector to verify that a
699 // CD is in the drive and readable.
700 return read_block(buffer
, 0, 2048);
704 cdrom_interface::start_cdrom()
706 // Spin up the cdrom drive.
709 #if defined(__NetBSD__) || defined(__NetBSD_kernel__)
710 if (ioctl (fd
, CDIOCSTART
) < 0)
711 BX_DEBUG(("start_cdrom: start returns error: %s", strerror (errno
)));
714 BX_INFO(("start_cdrom: your OS is not supported yet"));
715 return 0; // OS not supported yet, return 0 always
722 cdrom_interface::eject_cdrom()
724 // Logically eject the CD. I suppose we could stick in
725 // some ioctl() calls to really eject the CD as well.
728 #if (defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__))
729 (void) ioctl (fd
, CDIOCALLOW
);
730 if (ioctl (fd
, CDIOCEJECT
) < 0)
731 BX_DEBUG(("eject_cdrom: eject returns error"));
739 DWORD lpBytesReturned
;
740 DeviceIoControl(hFile
, IOCTL_STORAGE_EJECT_MEDIA
, NULL
, 0, NULL
, 0, &lpBytesReturned
, NULL
);
747 ioctl (fd
, CDROMEJECT
, NULL
);
758 cdrom_interface::read_toc(Bit8u
* buf
, int* length
, bx_bool msf
, int start_track
, int format
)
761 // Read CD TOC. Returns 0 if start track is out of bounds.
764 BX_PANIC(("cdrom: read_toc: file not open."));
768 // This is a hack and works okay if there's one rom track only
770 if (!isWindowsXP
|| using_file
) {
772 if (using_file
|| (format
!= 0)) {
779 // From atapi specs : start track can be 0-63, AA
780 if ((start_track
> 1) && (start_track
!= 0xaa))
786 if (start_track
<= 1) {
787 buf
[len
++] = 0; // Reserved
788 buf
[len
++] = 0x14; // ADR, control
789 buf
[len
++] = 1; // Track number
790 buf
[len
++] = 0; // Reserved
794 buf
[len
++] = 0; // reserved
795 buf
[len
++] = 0; // minute
796 buf
[len
++] = 2; // second
797 buf
[len
++] = 0; // frame
802 buf
[len
++] = 0; // logical sector 0
807 buf
[len
++] = 0; // Reserved
808 buf
[len
++] = 0x16; // ADR, control
809 buf
[len
++] = 0xaa; // Track number
810 buf
[len
++] = 0; // Reserved
816 buf
[len
++] = 0; // reserved
817 buf
[len
++] = (Bit8u
)(((blocks
+ 150) / 75) / 60); // minute
818 buf
[len
++] = (Bit8u
)(((blocks
+ 150) / 75) % 60); // second
819 buf
[len
++] = (Bit8u
)((blocks
+ 150) % 75); // frame;
821 buf
[len
++] = (blocks
>> 24) & 0xff;
822 buf
[len
++] = (blocks
>> 16) & 0xff;
823 buf
[len
++] = (blocks
>> 8) & 0xff;
824 buf
[len
++] = (blocks
>> 0) & 0xff;
826 buf
[0] = ((len
-2) >> 8) & 0xff;
827 buf
[1] = (len
-2) & 0xff;
831 // multi session stuff - emulate a single session only
836 for (i
= 0; i
< 8; i
++)
842 // raw toc - emulate a single session only (ported from qemu)
846 for (i
= 0; i
< 4; i
++) {
851 buf
[len
++] = 0xa0 + i
;
866 buf
[len
++] = 0; // reserved
867 buf
[len
++] = (Bit8u
)(((blocks
+ 150) / 75) / 60); // minute
868 buf
[len
++] = (Bit8u
)(((blocks
+ 150) / 75) % 60); // second
869 buf
[len
++] = (Bit8u
)((blocks
+ 150) % 75); // frame;
871 buf
[len
++] = (blocks
>> 24) & 0xff;
872 buf
[len
++] = (blocks
>> 16) & 0xff;
873 buf
[len
++] = (blocks
>> 8) & 0xff;
874 buf
[len
++] = (blocks
>> 0) & 0xff;
883 buf
[0] = ((len
-2) >> 8) & 0xff;
884 buf
[1] = (len
-2) & 0xff;
888 BX_PANIC(("cdrom: read_toc: unknown format"));
896 // all these implementations below are the platform-dependent code required
897 // to read the TOC from a physical cdrom.
901 // This only works with WinXP
902 CDROM_READ_TOC_EX input
;
903 memset(&input
, 0, sizeof(input
));
904 input
.Format
= format
;
906 input
.SessionTrack
= start_track
;
908 // We have to allocate a chunk of memory to make sure it is aligned on a sector base.
909 UCHAR
*data
= (UCHAR
*) VirtualAlloc(NULL
, 2048*2, MEM_COMMIT
|MEM_RESERVE
, PAGE_READWRITE
);
910 unsigned long iBytesReturned
;
911 DeviceIoControl(hFile
, IOCTL_CDROM_READ_TOC_EX
, &input
, sizeof(input
), data
, 804, &iBytesReturned
, NULL
);
912 // now copy it to the users buffer and free our buffer
913 *length
= data
[1] + (data
[0] << 8) + 2;
914 memcpy(buf
, data
, *length
);
915 VirtualFree(data
, 0, MEM_RELEASE
);
921 #elif __linux__ || defined(__sun)
923 struct cdrom_tochdr tochdr
;
924 if (ioctl(fd
, CDROMREADTOCHDR
, &tochdr
))
925 BX_PANIC(("cdrom: read_toc: READTOCHDR failed."));
927 if ((start_track
> tochdr
.cdth_trk1
) && (start_track
!= 0xaa))
930 buf
[2] = tochdr
.cdth_trk0
;
931 buf
[3] = tochdr
.cdth_trk1
;
933 if (start_track
< tochdr
.cdth_trk0
)
934 start_track
= tochdr
.cdth_trk0
;
937 for (int i
= start_track
; i
<= tochdr
.cdth_trk1
; i
++) {
938 struct cdrom_tocentry tocentry
;
939 tocentry
.cdte_format
= (msf
) ? CDROM_MSF
: CDROM_LBA
;
940 tocentry
.cdte_track
= i
;
941 if (ioctl(fd
, CDROMREADTOCENTRY
, &tocentry
))
942 BX_PANIC(("cdrom: read_toc: READTOCENTRY failed."));
943 buf
[len
++] = 0; // Reserved
944 buf
[len
++] = (tocentry
.cdte_adr
<< 4) | tocentry
.cdte_ctrl
; // ADR, control
945 buf
[len
++] = i
; // Track number
946 buf
[len
++] = 0; // Reserved
950 buf
[len
++] = 0; // reserved
951 buf
[len
++] = tocentry
.cdte_addr
.msf
.minute
;
952 buf
[len
++] = tocentry
.cdte_addr
.msf
.second
;
953 buf
[len
++] = tocentry
.cdte_addr
.msf
.frame
;
955 buf
[len
++] = (((unsigned)tocentry
.cdte_addr
.lba
) >> 24) & 0xff;
956 buf
[len
++] = (((unsigned)tocentry
.cdte_addr
.lba
) >> 16) & 0xff;
957 buf
[len
++] = (((unsigned)tocentry
.cdte_addr
.lba
) >> 8) & 0xff;
958 buf
[len
++] = (((unsigned)tocentry
.cdte_addr
.lba
) >> 0) & 0xff;
963 struct cdrom_tocentry tocentry
;
964 tocentry
.cdte_format
= (msf
) ? CDROM_MSF
: CDROM_LBA
;
966 tocentry
.cdte_track
= CDROM_LEADOUT
;
968 tocentry
.cdte_track
= 0xaa;
970 if (ioctl(fd
, CDROMREADTOCENTRY
, &tocentry
))
971 BX_PANIC(("cdrom: read_toc: READTOCENTRY lead-out failed."));
972 buf
[len
++] = 0; // Reserved
973 buf
[len
++] = (tocentry
.cdte_adr
<< 4) | tocentry
.cdte_ctrl
; // ADR, control
974 buf
[len
++] = 0xaa; // Track number
975 buf
[len
++] = 0; // Reserved
979 buf
[len
++] = 0; // reserved
980 buf
[len
++] = tocentry
.cdte_addr
.msf
.minute
;
981 buf
[len
++] = tocentry
.cdte_addr
.msf
.second
;
982 buf
[len
++] = tocentry
.cdte_addr
.msf
.frame
;
984 buf
[len
++] = (((unsigned)tocentry
.cdte_addr
.lba
) >> 24) & 0xff;
985 buf
[len
++] = (((unsigned)tocentry
.cdte_addr
.lba
) >> 16) & 0xff;
986 buf
[len
++] = (((unsigned)tocentry
.cdte_addr
.lba
) >> 8) & 0xff;
987 buf
[len
++] = (((unsigned)tocentry
.cdte_addr
.lba
) >> 0) & 0xff;
990 buf
[0] = ((len
-2) >> 8) & 0xff;
991 buf
[1] = (len
-2) & 0xff;
997 #elif (defined(__NetBSD__) || defined(__NetBSD_kernel__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__))
999 struct ioc_toc_header h
;
1000 struct ioc_read_toc_entry t
;
1002 if (ioctl (fd
, CDIOREADTOCHEADER
, &h
) < 0)
1003 BX_PANIC(("cdrom: read_toc: READTOCHDR failed."));
1005 if ((start_track
> h
.ending_track
) && (start_track
!= 0xaa))
1008 buf
[2] = h
.starting_track
;
1009 buf
[3] = h
.ending_track
;
1011 if (start_track
< h
.starting_track
)
1012 start_track
= h
.starting_track
;
1015 for (int i
= start_track
; i
<= h
.ending_track
; i
++) {
1016 struct cd_toc_entry tocentry
;
1017 t
.address_format
= (msf
) ? CD_MSF_FORMAT
: CD_LBA_FORMAT
;
1018 t
.starting_track
= i
;
1019 t
.data_len
= sizeof(tocentry
);
1022 if (ioctl (fd
, CDIOREADTOCENTRYS
, &t
) < 0)
1023 BX_PANIC(("cdrom: read_toc: READTOCENTRY failed."));
1025 buf
[len
++] = 0; // Reserved
1026 buf
[len
++] = (tocentry
.addr_type
<< 4) | tocentry
.control
; // ADR, control
1027 buf
[len
++] = i
; // Track number
1028 buf
[len
++] = 0; // Reserved
1032 buf
[len
++] = 0; // reserved
1033 buf
[len
++] = tocentry
.addr
.msf
.minute
;
1034 buf
[len
++] = tocentry
.addr
.msf
.second
;
1035 buf
[len
++] = tocentry
.addr
.msf
.frame
;
1037 buf
[len
++] = (((unsigned)tocentry
.addr
.lba
) >> 24) & 0xff;
1038 buf
[len
++] = (((unsigned)tocentry
.addr
.lba
) >> 16) & 0xff;
1039 buf
[len
++] = (((unsigned)tocentry
.addr
.lba
) >> 8) & 0xff;
1040 buf
[len
++] = (((unsigned)tocentry
.addr
.lba
) >> 0) & 0xff;
1045 struct cd_toc_entry tocentry
;
1046 t
.address_format
= (msf
) ? CD_MSF_FORMAT
: CD_LBA_FORMAT
;
1047 t
.starting_track
= 0xaa;
1048 t
.data_len
= sizeof(tocentry
);
1051 if (ioctl (fd
, CDIOREADTOCENTRYS
, &t
) < 0)
1052 BX_PANIC(("cdrom: read_toc: READTOCENTRY lead-out failed."));
1054 buf
[len
++] = 0; // Reserved
1055 buf
[len
++] = (tocentry
.addr_type
<< 4) | tocentry
.control
; // ADR, control
1056 buf
[len
++] = 0xaa; // Track number
1057 buf
[len
++] = 0; // Reserved
1061 buf
[len
++] = 0; // reserved
1062 buf
[len
++] = tocentry
.addr
.msf
.minute
;
1063 buf
[len
++] = tocentry
.addr
.msf
.second
;
1064 buf
[len
++] = tocentry
.addr
.msf
.frame
;
1066 buf
[len
++] = (((unsigned)tocentry
.addr
.lba
) >> 24) & 0xff;
1067 buf
[len
++] = (((unsigned)tocentry
.addr
.lba
) >> 16) & 0xff;
1068 buf
[len
++] = (((unsigned)tocentry
.addr
.lba
) >> 8) & 0xff;
1069 buf
[len
++] = (((unsigned)tocentry
.addr
.lba
) >> 0) & 0xff;
1072 buf
[0] = ((len
-2) >> 8) & 0xff;
1073 buf
[1] = (len
-2) & 0xff;
1079 #elif defined(__APPLE__)
1080 // Read CD TOC. Returns 0 if start track is out of bounds.
1084 struct _CDTOC
*toc
= ReadTOC(CDDevicePath
);
1086 if ((start_track
> toc
->last_session
) && (start_track
!= 0xaa))
1089 buf
[2] = toc
->first_session
;
1090 buf
[3] = toc
->last_session
;
1092 if (start_track
< toc
->first_session
)
1093 start_track
= toc
->first_session
;
1096 for (int i
= start_track
; i
<= toc
->last_session
; i
++) {
1097 buf
[len
++] = 0; // Reserved
1098 buf
[len
++] = toc
->trackdesc
[i
].ctrl_adr
; // ADR, control
1099 buf
[len
++] = i
; // Track number
1100 buf
[len
++] = 0; // Reserved
1104 buf
[len
++] = 0; // reserved
1105 buf
[len
++] = toc
->trackdesc
[i
].address
.minute
;
1106 buf
[len
++] = toc
->trackdesc
[i
].address
.second
;
1107 buf
[len
++] = toc
->trackdesc
[i
].address
.frame
;
1109 unsigned lba
= (unsigned)(MSF_TO_LBA(toc
->trackdesc
[i
].address
));
1110 buf
[len
++] = (lba
>> 24) & 0xff;
1111 buf
[len
++] = (lba
>> 16) & 0xff;
1112 buf
[len
++] = (lba
>> 8) & 0xff;
1113 buf
[len
++] = (lba
>> 0) & 0xff;
1118 buf
[len
++] = 0; // Reserved
1119 buf
[len
++] = 0x16; // ADR, control
1120 buf
[len
++] = 0xaa; // Track number
1121 buf
[len
++] = 0; // Reserved
1123 Bit32u blocks
= capacity();
1127 buf
[len
++] = 0; // reserved
1128 buf
[len
++] = (Bit8u
)(((blocks
+ 150) / 75) / 60); // minute
1129 buf
[len
++] = (Bit8u
)(((blocks
+ 150) / 75) % 60); // second
1130 buf
[len
++] = (Bit8u
)((blocks
+ 150) % 75); // frame;
1132 buf
[len
++] = (blocks
>> 24) & 0xff;
1133 buf
[len
++] = (blocks
>> 16) & 0xff;
1134 buf
[len
++] = (blocks
>> 8) & 0xff;
1135 buf
[len
++] = (blocks
>> 0) & 0xff;
1138 buf
[0] = ((len
-2) >> 8) & 0xff;
1139 buf
[1] = (len
-2) & 0xff;
1146 BX_INFO(("Read TOC - Not Implemented"));
1150 BX_INFO(("read_toc: your OS is not supported yet"));
1151 return 0; // OS not supported yet, return 0 always.
1155 Bit32u
cdrom_interface::capacity()
1157 // Return CD-ROM capacity. I believe you want to return
1158 // the number of blocks of capacity the actual media has.
1161 // win32 has its own way of doing this
1163 // return length of the image file
1164 struct stat stat_buf
;
1165 int ret
= fstat (fd
, &stat_buf
);
1167 BX_PANIC (("fstat on cdrom image returned err: %s", strerror(errno
)));
1169 if ((stat_buf
.st_size
% 2048) != 0) {
1170 BX_ERROR (("expected cdrom image to be a multiple of 2048 bytes"));
1172 return (stat_buf
.st_size
/ 2048);
1177 return GetNumDeviceBlocks(fd
, BX_CD_FRAMESIZE
);
1178 #elif defined(__sun)
1180 struct stat buf
= {0};
1183 BX_PANIC(("cdrom: capacity: file not open."));
1186 if(fstat(fd
, &buf
) != 0)
1187 BX_PANIC(("cdrom: capacity: stat() failed."));
1189 return(buf
.st_size
);
1191 #elif (defined(__NetBSD__) || defined(__NetBSD_kernel__) || defined(__OpenBSD__))
1193 // We just read the disklabel, imagine that...
1194 struct disklabel lp
;
1197 BX_PANIC(("cdrom: capacity: file not open."));
1199 if (ioctl(fd
, DIOCGDINFO
, &lp
) < 0)
1200 BX_PANIC(("cdrom: ioctl(DIOCGDINFO) failed"));
1202 BX_DEBUG(("capacity: %u", lp
.d_secperunit
));
1203 return(lp
.d_secperunit
);
1205 #elif defined(__linux__)
1207 // Read the TOC to get the data size, since BLKGETSIZE doesn't work on
1208 // non-ATAPI drives. This is based on Keith Jones code below.
1209 // <splite@purdue.edu> 21 June 2001
1211 int i
, dtrk_lba
, num_sectors
;
1213 struct cdrom_tochdr td
;
1214 struct cdrom_tocentry te
;
1217 BX_PANIC(("cdrom: capacity: file not open."));
1219 if (ioctl(fd
, CDROMREADTOCHDR
, &td
) < 0)
1220 BX_PANIC(("cdrom: ioctl(CDROMREADTOCHDR) failed"));
1225 for (i
= td
.cdth_trk0
; i
<= td
.cdth_trk1
; i
++) {
1227 te
.cdte_format
= CDROM_LBA
;
1228 if (ioctl(fd
, CDROMREADTOCENTRY
, &te
) < 0)
1229 BX_PANIC(("cdrom: ioctl(CDROMREADTOCENTRY) failed"));
1231 if (dtrk_lba
!= -1) {
1232 num_sectors
= te
.cdte_addr
.lba
- dtrk_lba
;
1235 if (te
.cdte_ctrl
& CDROM_DATA_TRACK
) {
1237 dtrk_lba
= te
.cdte_addr
.lba
;
1241 if (num_sectors
< 0) {
1242 if (dtrk_lba
!= -1) {
1243 te
.cdte_track
= CDROM_LEADOUT
;
1244 te
.cdte_format
= CDROM_LBA
;
1245 if (ioctl(fd
, CDROMREADTOCENTRY
, &te
) < 0)
1246 BX_PANIC(("cdrom: ioctl(CDROMREADTOCENTRY) failed"));
1247 num_sectors
= te
.cdte_addr
.lba
- dtrk_lba
;
1249 BX_PANIC(("cdrom: no data track found"));
1252 BX_INFO(("cdrom: Data track %d, length %d", dtrk
, num_sectors
));
1254 return(num_sectors
);
1257 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1259 // Read the TOC to get the size of the data track.
1260 // Keith Jones <freebsd.dev@blueyonder.co.uk>, 16 January 2000
1262 #define MAX_TRACKS 100
1264 int i
, num_tracks
, num_sectors
;
1265 struct ioc_toc_header td
;
1266 struct ioc_read_toc_entry rte
;
1267 struct cd_toc_entry toc_buffer
[MAX_TRACKS
+ 1];
1270 BX_PANIC(("cdrom: capacity: file not open."));
1272 if (ioctl(fd
, CDIOREADTOCHEADER
, &td
) < 0)
1273 BX_PANIC(("cdrom: ioctl(CDIOREADTOCHEADER) failed"));
1275 num_tracks
= (td
.ending_track
- td
.starting_track
) + 1;
1276 if (num_tracks
> MAX_TRACKS
)
1277 BX_PANIC(("cdrom: TOC is too large"));
1279 rte
.address_format
= CD_LBA_FORMAT
;
1280 rte
.starting_track
= td
.starting_track
;
1281 rte
.data_len
= (num_tracks
+ 1) * sizeof(struct cd_toc_entry
);
1282 rte
.data
= toc_buffer
;
1283 if (ioctl(fd
, CDIOREADTOCENTRYS
, &rte
) < 0)
1284 BX_PANIC(("cdrom: ioctl(CDIOREADTOCENTRYS) failed"));
1287 for (i
= 0; i
< num_tracks
; i
++) {
1288 if (rte
.data
[i
].control
& 4) { /* data track */
1289 num_sectors
= ntohl(rte
.data
[i
+ 1].addr
.lba
)
1290 - ntohl(rte
.data
[i
].addr
.lba
);
1291 BX_INFO(("cdrom: Data track %d, length %d",
1292 rte
.data
[i
].track
, num_sectors
));
1297 if (num_sectors
< 0)
1298 BX_PANIC(("cdrom: no data track found"));
1300 return(num_sectors
);
1306 return ((GetCDCapacity(hid
, tid
, lun
) / 2352) + 1);
1307 } else if (using_file
) {
1308 ULARGE_INTEGER FileSize
;
1309 FileSize
.LowPart
= GetFileSize(hFile
, &FileSize
.HighPart
);
1310 return (Bit32u
)(FileSize
.QuadPart
/ 2048);
1311 } else { /* direct device access */
1313 LARGE_INTEGER length
;
1314 DWORD iBytesReturned
;
1315 DeviceIoControl(hFile
, IOCTL_DISK_GET_LENGTH_INFO
, NULL
, 0, &length
, sizeof(length
), &iBytesReturned
, NULL
);
1316 return (Bit32u
)(length
.QuadPart
/ 2048);
1318 ULARGE_INTEGER FreeBytesForCaller
;
1319 ULARGE_INTEGER TotalNumOfBytes
;
1320 ULARGE_INTEGER TotalFreeBytes
;
1321 GetDiskFreeSpaceEx(path
, &FreeBytesForCaller
, &TotalNumOfBytes
, &TotalFreeBytes
);
1322 return (Bit32u
)(TotalNumOfBytes
.QuadPart
/ 2048);
1326 #elif defined __APPLE__
1327 // Find the size of the first data track on the cd. This has produced
1328 // the same results as the linux version on every cd I have tried, about
1329 // 5. The differences here seem to be that the entries in the TOC when
1330 // retrieved from the IOKit interface appear in a reversed order when
1331 // compared with the linux READTOCENTRY ioctl.
1333 // Return CD-ROM capacity. I believe you want to return
1334 // the number of bytes of capacity the actual media has.
1336 BX_INFO(("Capacity"));
1338 struct _CDTOC
*toc
= ReadTOC(CDDevicePath
);
1341 BX_PANIC(("capacity: Failed to read toc"));
1344 size_t toc_entries
= (toc
->length
- 2) / sizeof(struct _CDTOC_Desc
);
1346 BX_DEBUG(("reading %d toc entries\n", toc_entries
));
1348 int start_sector
= -1;
1349 int data_track
= -1;
1351 // Iterate through the list backward. Pick the first data track and
1352 // get the address of the immediately previous (or following depending
1353 // on how you look at it). The difference in the sector numbers
1354 // is returned as the sized of the data track.
1355 for (int i
=toc_entries
- 1; i
>=0; i
--) {
1356 BX_DEBUG(("session %d ctl_adr %d tno %d point %d lba %d z %d p lba %d\n",
1357 (int)toc
->trackdesc
[i
].session
,
1358 (int)toc
->trackdesc
[i
].ctrl_adr
,
1359 (int)toc
->trackdesc
[i
].tno
,
1360 (int)toc
->trackdesc
[i
].point
,
1361 MSF_TO_LBA(toc
->trackdesc
[i
].address
),
1362 (int)toc
->trackdesc
[i
].zero
,
1363 MSF_TO_LBA(toc
->trackdesc
[i
].p
)));
1365 if (start_sector
!= -1) {
1366 start_sector
= MSF_TO_LBA(toc
->trackdesc
[i
].p
) - start_sector
;
1370 if ((toc
->trackdesc
[i
].ctrl_adr
>> 4) != 1) continue;
1372 if (toc
->trackdesc
[i
].ctrl_adr
& 0x04) {
1373 data_track
= toc
->trackdesc
[i
].point
;
1374 start_sector
= MSF_TO_LBA(toc
->trackdesc
[i
].p
);
1380 if (start_sector
== -1) {
1384 BX_INFO(("first data track %d data size is %d", data_track
, start_sector
));
1386 return start_sector
;
1389 BX_ERROR(("capacity: your OS is not supported yet"));
1394 bx_bool
BX_CPP_AttrRegparmN(3)
1395 cdrom_interface::read_block(Bit8u
* buf
, int lba
, int blocksize
)
1397 // Read a single block from the CD
1405 Bit8u try_count
= 3;
1408 if (blocksize
== 2352) {
1409 memset(buf
, 0, 2352);
1410 memset(buf
+1, 0xff, 10);
1411 int raw_block
= lba
+ 150;
1412 buf
[12] = (raw_block
/ 75) / 60;
1413 buf
[13] = (raw_block
/ 75) % 60;
1414 buf
[14] = (raw_block
% 75);
1423 ReadCDSector(hid
, tid
, lun
, lba
, buf1
, BX_CD_FRAMESIZE
);
1424 n
= BX_CD_FRAMESIZE
;
1426 pos
.QuadPart
= (LONGLONG
)lba
*BX_CD_FRAMESIZE
;
1427 pos
.LowPart
= SetFilePointer(hFile
, pos
.LowPart
, &pos
.HighPart
, SEEK_SET
);
1428 if ((pos
.LowPart
== 0xffffffff) && (GetLastError() != NO_ERROR
)) {
1429 BX_PANIC(("cdrom: read_block: SetFilePointer returned error."));
1431 ReadFile(hFile
, (void *) buf1
, BX_CD_FRAMESIZE
, (unsigned long *) &n
, NULL
);
1434 #elif defined(__APPLE__)
1435 #define CD_SEEK_DISTANCE kCDSectorSizeWhole
1438 pos
= lseek(fd
, (off_t
) lba
* BX_CD_FRAMESIZE
, SEEK_SET
);
1440 BX_PANIC(("cdrom: read_block: lseek returned error."));
1442 n
= read(fd
, buf1
, BX_CD_FRAMESIZE
);
1447 // This seek will leave us 16 bytes from the start of the data
1448 // hence the magic number.
1449 pos
= lseek(fd
, (off_t
) lba
*CD_SEEK_DISTANCE
+ 16, SEEK_SET
);
1451 BX_PANIC(("cdrom: read_block: lseek returned error."));
1453 n
= read(fd
, buf1
, CD_FRAMESIZE
);
1457 pos
= lseek(fd
, (off_t
) lba
* BX_CD_FRAMESIZE
, SEEK_SET
);
1459 BX_PANIC(("cdrom: read_block: lseek returned error."));
1461 n
= read(fd
, (char*) buf1
, BX_CD_FRAMESIZE
);
1464 } while ((n
!= BX_CD_FRAMESIZE
) && (--try_count
> 0));
1466 return (n
== BX_CD_FRAMESIZE
);
1469 void cdrom_interface::seek(int lba
)
1471 unsigned char buffer
[BX_CD_FRAMESIZE
];
1473 read_block(buffer
, lba
, BX_CD_FRAMESIZE
);
1476 #endif /* if BX_SUPPORT_CDROM */