- added instructions how to update the online documentation
[bochs-mirror.git] / iodev / cdrom.cc
blobea935b9880f6b1c06400ce1ebaed55af2c227178
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: cdrom.cc,v 1.92 2008/10/01 07:47:02 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2002 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
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.
40 #define BX_PLUGGABLE
42 #include "bochs.h"
43 #if BX_SUPPORT_CDROM
45 #include "cdrom.h"
47 #define LOG_THIS /* no SMF tricks here, not needed */
49 extern "C" {
50 #include <errno.h>
53 #ifdef __linux__
54 extern "C" {
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))
62 extern "C" {
63 #include <sys/ioctl.h>
64 #define BX_CD_FRAMESIZE 2048
65 #define CD_FRAMESIZE 2048
68 #elif BX_WITH_MACOS
69 #define BX_CD_FRAMESIZE 2048
70 #define CD_FRAMESIZE 2048
72 #elif defined(__sun)
73 extern "C" {
74 #include <sys/types.h>
75 #include <sys/stat.h>
76 #include <sys/ioctl.h>
77 #include <sys/cdio.h>
78 #define BX_CD_FRAMESIZE CDROM_BLK_2048
81 #elif defined(__DJGPP__)
82 extern "C" {
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>
98 #include <sys/file.h>
99 #include <sys/cdio.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>
105 // XXX
106 #define BX_CD_FRAMESIZE 2048
107 #define CD_FRAMESIZE 2048
109 #elif defined(__APPLE__)
110 #include <string.h>
111 #include <unistd.h>
112 #include <fcntl.h>
113 #include <sys/ioctl.h>
114 #if defined (__GNUC__) && (__GNUC__ >= 4)
115 #include <sys/disk.h>
116 #else
117 #include <dev/disk.h>
118 #endif
119 #include <errno.h>
120 #include <paths.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>
131 #undef Float32
132 #undef Float64
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
138 struct _CDMSF {
139 u_char minute;
140 u_char second;
141 u_char frame;
144 #define MSF_TO_LBA(msf) \
145 (((((msf).minute * 60UL) + (msf).second) * 75UL) + (msf).frame - 150)
147 struct _CDTOC_Desc {
148 u_char session;
149 u_char ctrl_adr; /* typed to be machine and compiler independent */
150 u_char tno;
151 u_char point;
152 struct _CDMSF address;
153 u_char zero;
154 struct _CDMSF p;
157 struct _CDTOC {
158 u_short length; /* in native cpu endian */
159 u_char first_session;
160 u_char last_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
174 #elif defined(WIN32)
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)
210 #endif
212 typedef struct _CDROM_READ_TOC_EX {
213 UCHAR Format : 4;
214 UCHAR Reserved1 : 3; // future expansion
215 UCHAR Msf : 1;
216 UCHAR SessionTrack;
217 UCHAR Reserved2; // future expansion
218 UCHAR Reserved3; // future expansion
219 } CDROM_READ_TOC_EX, *PCDROM_READ_TOC_EX;
221 typedef struct _TRACK_DATA {
222 UCHAR Reserved;
223 UCHAR Control : 4;
224 UCHAR Adr : 4;
225 UCHAR TrackNumber;
226 UCHAR Reserved1;
227 UCHAR Address[4];
228 } TRACK_DATA, *PTRACK_DATA;
230 typedef struct _CDROM_TOC_SESSION_DATA {
231 // Header
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
248 #endif
250 #include <stdio.h>
252 #ifdef __APPLE__
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);
262 return 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");
268 else
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);
281 return 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';
294 else
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);
317 return kernResult;
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;
334 CFDataRef data = 0;
335 mach_port_t port = 0;
336 char *devname;
338 if ((devname = strrchr(devpath, '/')) != NULL) {
339 ++devname;
341 else {
342 devname = (char *) devpath;
345 if (IOMasterPort(bootstrap_port, &port) != KERN_SUCCESS) {
346 fprintf(stderr, "IOMasterPort failed\n");
347 goto Exit;
350 if (IOServiceGetMatchingServices(port, IOBSDNameMatching(port, 0, devname),
351 &iterator) != KERN_SUCCESS) {
352 fprintf(stderr, "IOServiceGetMatchingServices failed\n");
353 goto Exit;
356 service = IOIteratorNext(iterator);
358 IOObjectRelease(iterator);
360 iterator = 0;
362 while (service && !IOObjectConformsTo(service, "IOCDMedia")) {
363 if (IORegistryEntryGetParentIterator(service, kIOServicePlane,
364 &iterator) != KERN_SUCCESS)
366 fprintf(stderr, "IORegistryEntryGetParentIterator failed\n");
367 goto Exit;
370 IOObjectRelease(service);
371 service = IOIteratorNext(iterator);
372 IOObjectRelease(iterator);
375 if (service == NULL) {
376 fprintf(stderr, "CD media not found\n");
377 goto Exit;
380 if (IORegistryEntryCreateCFProperties(service, (__CFDictionary **) &properties,
381 kCFAllocatorDefault,
382 kNilOptions) != KERN_SUCCESS)
384 fprintf(stderr, "IORegistryEntryGetParentIterator failed\n");
385 goto Exit;
388 data = (CFDataRef) CFDictionaryGetValue(properties, CFSTR(kIOCDMediaTOCKey));
389 if (data == NULL) {
390 fprintf(stderr, "CFDictionaryGetValue failed\n");
391 goto Exit;
393 else {
394 CFRange range;
395 CFIndex buflen;
397 buflen = CFDataGetLength(data) + 1;
398 range = CFRangeMake(0, buflen);
399 toc_p = (struct _CDTOC *) malloc(buflen);
400 if (toc_p == NULL) {
401 fprintf(stderr, "Out of memory\n");
402 goto Exit;
404 else {
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);
416 Exit:
418 if (service) {
419 IOObjectRelease(service);
422 return toc_p;
424 #endif
426 #ifdef WIN32
428 bool ReadCDSector(unsigned int hid, unsigned int tid, unsigned int lun, unsigned long frame, unsigned char *buf, int bufsize)
430 HANDLE hEventSRB;
431 SRB_ExecSCSICmd srb;
432 DWORD dwStatus;
434 hEventSRB = CreateEvent(NULL, TRUE, FALSE, NULL);
436 memset(&srb,0,sizeof(SRB_ExecSCSICmd));
437 srb.SRB_Cmd = SC_EXEC_SCSI_CMD;
438 srb.SRB_HaId = hid;
439 srb.SRB_Target = tid;
440 srb.SRB_Lun = lun;
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;
446 srb.SRB_CDBLen = 10;
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);
452 srb.CDBByte[7] = 0;
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)
466 HANDLE hEventSRB;
467 SRB_ExecSCSICmd srb;
468 DWORD dwStatus;
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;
476 srb.SRB_HaId = hid;
477 srb.SRB_Target = tid;
478 srb.SRB_Lun = lun;
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;
483 srb.SRB_BufLen = 8;
484 srb.SRB_CDBLen = 10;
485 srb.CDBByte[0] = SCSI_READCDCAP;
486 srb.CDBByte[2] = 0;
487 srb.CDBByte[3] = 0;
488 srb.CDBByte[4] = 0;
489 srb.CDBByte[5] = 0;
490 srb.CDBByte[8] = 0;
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]);
502 #endif
504 cdrom_interface::cdrom_interface(char *dev)
506 put("CD");
507 settype(CDLOG);
508 fd = -1; // File descriptor not yet allocated
510 if (dev == NULL) {
511 path = NULL;
512 } else {
513 path = strdup(dev);
515 using_file=0;
516 #ifdef WIN32
517 bUseASPI = FALSE;
518 osinfo.dwOSVersionInfoSize = sizeof(osinfo);
519 GetVersionEx(&osinfo);
520 isWindowsXP = (osinfo.dwMajorVersion >= 5) && (osinfo.dwMinorVersion >= 1);
521 #endif
524 void
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)
532 #ifdef WIN32
533 #else
534 if (fd >= 0)
535 close(fd);
536 #endif
537 if (path)
538 free(path);
539 BX_DEBUG(("Exit"));
542 bx_bool
543 cdrom_interface::insert_cdrom(char *dev)
545 unsigned char buffer[BX_CD_FRAMESIZE];
546 #ifndef WIN32
547 ssize_t ret;
548 #endif
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));
553 #ifdef WIN32
554 char drive[256];
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.
565 } else {
566 BX_INFO(("Using ASPI for cdrom. Drive letters are unused yet."));
567 bUseASPI = TRUE;
569 using_file = 0;
571 else
573 strcpy(drive,path);
574 using_file = 1;
575 BX_INFO (("Opening image file as a cd"));
577 if(bUseASPI) {
578 DWORD d;
579 UINT cdr, cnt, max;
580 UINT i, j, k;
581 SRB_HAInquiry sh;
582 SRB_GDEVBlock sd;
583 if (!hASPI) {
584 hASPI = LoadLibrary("WNASPI32.DLL");
585 if (hASPI) {
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));
591 } else {
592 BX_PANIC(("Could not load ASPI drivers, so cdrom access will fail"));
593 return 0;
596 cdr = 0;
597 bHaveDev = FALSE;
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;
603 sh.SRB_HaId = i;
604 SendASPI32Command((LPSRB)&sh);
605 if(sh.SRB_Status != SS_COMP)
606 continue;
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;
613 sd.SRB_HaId = i;
614 sd.SRB_Target = j;
615 sd.SRB_Lun = k;
616 SendASPI32Command((LPSRB)&sd);
617 if(sd.SRB_Status == SS_COMP) {
618 if(sd.SRB_DeviceType == DTYPE_CDROM) {
619 cdr++;
620 if(cdr > cdromCount) {
621 hid = i;
622 tid = j;
623 lun = k;
624 cdromCount++;
625 bHaveDev = TRUE;
629 if(bHaveDev) break;
631 if(bHaveDev) break;
634 fd=1;
635 } else {
636 hFile=CreateFile((char *)&drive, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL);
637 if (hFile !=(void *)0xFFFFFFFF)
638 fd=1;
639 if (!using_file) {
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"));
656 return 0;
659 kernResult = GetDeviceFilePath(mediaIterator, CDDevicePath, sizeof(CDDevicePath));
660 if (kernResult != KERN_SUCCESS) {
661 BX_INFO(("Unable to get CDROM device file path"));
662 return 0;
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);
671 else {
672 fd = open(path, O_RDONLY);
674 #else
675 // all platforms except win32
676 fd = open(path, O_RDONLY);
677 #endif
678 if (fd < 0) {
679 BX_ERROR(("open cd failed for %s: %s", path, strerror(errno)));
680 return 0;
682 #ifndef WIN32
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);
686 if (ret) {
687 BX_PANIC (("fstat cdrom file returned error: %s", strerror (errno)));
689 if (S_ISREG (stat_buf.st_mode)) {
690 using_file = 1;
691 BX_INFO (("Opening image file %s as a cd.", path));
692 } else {
693 using_file = 0;
694 BX_INFO (("Using direct access for cdrom."));
696 #endif
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);
703 bx_bool
704 cdrom_interface::start_cdrom()
706 // Spin up the cdrom drive.
708 if (fd >= 0) {
709 #if defined(__NetBSD__) || defined(__NetBSD_kernel__)
710 if (ioctl (fd, CDIOCSTART) < 0)
711 BX_DEBUG(("start_cdrom: start returns error: %s", strerror (errno)));
712 return 1;
713 #else
714 BX_INFO(("start_cdrom: your OS is not supported yet"));
715 return 0; // OS not supported yet, return 0 always
716 #endif
718 return 0;
721 void
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.
727 if (fd >= 0) {
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"));
732 #endif
734 #ifdef WIN32
735 if (using_file == 0)
737 if(bUseASPI) {
738 } else {
739 DWORD lpBytesReturned;
740 DeviceIoControl(hFile, IOCTL_STORAGE_EJECT_MEDIA, NULL, 0, NULL, 0, &lpBytesReturned, NULL);
743 #else // WIN32
745 #if __linux__
746 if (!using_file)
747 ioctl (fd, CDROMEJECT, NULL);
748 #endif
750 close(fd);
751 #endif // WIN32
752 fd = -1;
757 bx_bool
758 cdrom_interface::read_toc(Bit8u* buf, int* length, bx_bool msf, int start_track, int format)
760 unsigned i;
761 // Read CD TOC. Returns 0 if start track is out of bounds.
763 if (fd < 0) {
764 BX_PANIC(("cdrom: read_toc: file not open."));
765 return 0;
768 // This is a hack and works okay if there's one rom track only
769 #if defined(WIN32)
770 if (!isWindowsXP || using_file) {
771 #else
772 if (using_file || (format != 0)) {
773 #endif
774 Bit32u blocks;
775 int len = 4;
777 switch (format) {
778 case 0:
779 // From atapi specs : start track can be 0-63, AA
780 if ((start_track > 1) && (start_track != 0xaa))
781 return 0;
783 buf[2] = 1;
784 buf[3] = 1;
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
792 // Start address
793 if (msf) {
794 buf[len++] = 0; // reserved
795 buf[len++] = 0; // minute
796 buf[len++] = 2; // second
797 buf[len++] = 0; // frame
798 } else {
799 buf[len++] = 0;
800 buf[len++] = 0;
801 buf[len++] = 0;
802 buf[len++] = 0; // logical sector 0
806 // Lead out track
807 buf[len++] = 0; // Reserved
808 buf[len++] = 0x16; // ADR, control
809 buf[len++] = 0xaa; // Track number
810 buf[len++] = 0; // Reserved
812 blocks = capacity();
814 // Start address
815 if (msf) {
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;
820 } else {
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;
828 break;
830 case 1:
831 // multi session stuff - emulate a single session only
832 buf[0] = 0;
833 buf[1] = 0x0a;
834 buf[2] = 1;
835 buf[3] = 1;
836 for (i = 0; i < 8; i++)
837 buf[4+i] = 0;
838 len = 12;
839 break;
841 case 2:
842 // raw toc - emulate a single session only (ported from qemu)
843 buf[2] = 1;
844 buf[3] = 1;
846 for (i = 0; i < 4; i++) {
847 buf[len++] = 1;
848 buf[len++] = 0x14;
849 buf[len++] = 0;
850 if (i < 3) {
851 buf[len++] = 0xa0 + i;
852 } else {
853 buf[len++] = 1;
855 buf[len++] = 0;
856 buf[len++] = 0;
857 buf[len++] = 0;
858 if (i < 2) {
859 buf[len++] = 0;
860 buf[len++] = 1;
861 buf[len++] = 0;
862 buf[len++] = 0;
863 } else if (i == 2) {
864 blocks = capacity();
865 if (msf) {
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;
870 } else {
871 buf[len++] = (blocks >> 24) & 0xff;
872 buf[len++] = (blocks >> 16) & 0xff;
873 buf[len++] = (blocks >> 8) & 0xff;
874 buf[len++] = (blocks >> 0) & 0xff;
876 } else {
877 buf[len++] = 0;
878 buf[len++] = 0;
879 buf[len++] = 0;
880 buf[len++] = 0;
883 buf[0] = ((len-2) >> 8) & 0xff;
884 buf[1] = (len-2) & 0xff;
885 break;
887 default:
888 BX_PANIC(("cdrom: read_toc: unknown format"));
889 return 0;
892 *length = len;
894 return 1;
896 // all these implementations below are the platform-dependent code required
897 // to read the TOC from a physical cdrom.
898 #ifdef WIN32
899 if (isWindowsXP)
901 // This only works with WinXP
902 CDROM_READ_TOC_EX input;
903 memset(&input, 0, sizeof(input));
904 input.Format = format;
905 input.Msf = msf;
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);
917 return 1;
918 } else {
919 return 0;
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))
928 return 0;
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;
936 int len = 4;
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
948 // Start address
949 if (msf) {
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;
954 } else {
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;
962 // Lead out track
963 struct cdrom_tocentry tocentry;
964 tocentry.cdte_format = (msf) ? CDROM_MSF : CDROM_LBA;
965 #ifdef CDROM_LEADOUT
966 tocentry.cdte_track = CDROM_LEADOUT;
967 #else
968 tocentry.cdte_track = 0xaa;
969 #endif
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
977 // Start address
978 if (msf) {
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;
983 } else {
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;
993 *length = len;
995 return 1;
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))
1006 return 0;
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;
1014 int len = 4;
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);
1020 t.data = &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
1030 // Start address
1031 if (msf) {
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;
1036 } else {
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;
1044 // Lead out track
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);
1049 t.data = &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
1059 // Start address
1060 if (msf) {
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;
1065 } else {
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;
1075 *length = len;
1077 return 1;
1079 #elif defined(__APPLE__)
1080 // Read CD TOC. Returns 0 if start track is out of bounds.
1082 #if 1
1084 struct _CDTOC *toc = ReadTOC(CDDevicePath);
1086 if ((start_track > toc->last_session) && (start_track != 0xaa))
1087 return 0;
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;
1095 int len = 4;
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
1102 // Start address
1103 if (msf) {
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;
1108 } else {
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;
1117 // Lead out track
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();
1125 // Start address
1126 if (msf) {
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;
1131 } else {
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;
1141 *length = len;
1143 return 1;
1145 #else
1146 BX_INFO(("Read TOC - Not Implemented"));
1147 return 0;
1148 #endif
1149 #else
1150 BX_INFO(("read_toc: your OS is not supported yet"));
1151 return 0; // OS not supported yet, return 0 always.
1152 #endif
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.
1160 #if !defined WIN32
1161 // win32 has its own way of doing this
1162 if (using_file) {
1163 // return length of the image file
1164 struct stat stat_buf;
1165 int ret = fstat (fd, &stat_buf);
1166 if (ret) {
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);
1174 #endif
1176 #ifdef __BEOS__
1177 return GetNumDeviceBlocks(fd, BX_CD_FRAMESIZE);
1178 #elif defined(__sun)
1180 struct stat buf = {0};
1182 if (fd < 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;
1196 if (fd < 0)
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;
1212 int dtrk = 0;
1213 struct cdrom_tochdr td;
1214 struct cdrom_tocentry te;
1216 if (fd < 0)
1217 BX_PANIC(("cdrom: capacity: file not open."));
1219 if (ioctl(fd, CDROMREADTOCHDR, &td) < 0)
1220 BX_PANIC(("cdrom: ioctl(CDROMREADTOCHDR) failed"));
1222 num_sectors = -1;
1223 dtrk_lba = -1;
1225 for (i = td.cdth_trk0; i <= td.cdth_trk1; i++) {
1226 te.cdte_track = 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;
1233 break;
1235 if (te.cdte_ctrl & CDROM_DATA_TRACK) {
1236 dtrk = i;
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;
1248 } else
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];
1269 if (fd < 0)
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"));
1286 num_sectors = -1;
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));
1293 break;
1297 if (num_sectors < 0)
1298 BX_PANIC(("cdrom: no data track found"));
1300 return(num_sectors);
1303 #elif defined WIN32
1305 if (bUseASPI) {
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 */
1312 if (isWindowsXP) {
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);
1317 } else {
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);
1340 if (toc == NULL) {
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;
1367 break;
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);
1378 free(toc);
1380 if (start_sector == -1) {
1381 start_sector = 0;
1384 BX_INFO(("first data track %d data size is %d", data_track, start_sector));
1386 return start_sector;
1388 #else
1389 BX_ERROR(("capacity: your OS is not supported yet"));
1390 return(0);
1391 #endif
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
1399 #ifdef WIN32
1400 LARGE_INTEGER pos;
1401 #else
1402 off_t pos;
1403 #endif
1404 ssize_t n = 0;
1405 Bit8u try_count = 3;
1406 Bit8u* buf1;
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);
1415 buf[15] = 0x01;
1416 buf1 = buf + 16;
1417 } else {
1418 buf1 = buf;
1420 do {
1421 #ifdef WIN32
1422 if(bUseASPI) {
1423 ReadCDSector(hid, tid, lun, lba, buf1, BX_CD_FRAMESIZE);
1424 n = BX_CD_FRAMESIZE;
1425 } else {
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."));
1430 } else {
1431 ReadFile(hFile, (void *) buf1, BX_CD_FRAMESIZE, (unsigned long *) &n, NULL);
1434 #elif defined(__APPLE__)
1435 #define CD_SEEK_DISTANCE kCDSectorSizeWhole
1436 if(using_file)
1438 pos = lseek(fd, (off_t) lba * BX_CD_FRAMESIZE, SEEK_SET);
1439 if (pos < 0) {
1440 BX_PANIC(("cdrom: read_block: lseek returned error."));
1441 } else {
1442 n = read(fd, buf1, BX_CD_FRAMESIZE);
1445 else
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);
1450 if (pos < 0) {
1451 BX_PANIC(("cdrom: read_block: lseek returned error."));
1452 } else {
1453 n = read(fd, buf1, CD_FRAMESIZE);
1456 #else
1457 pos = lseek(fd, (off_t) lba * BX_CD_FRAMESIZE, SEEK_SET);
1458 if (pos < 0) {
1459 BX_PANIC(("cdrom: read_block: lseek returned error."));
1460 } else {
1461 n = read(fd, (char*) buf1, BX_CD_FRAMESIZE);
1463 #endif
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 */
1478 #endif