306 lines
7.7 KiB
C++
306 lines
7.7 KiB
C++
/////////////////////////////////////////////////////////////////////////
|
|
// $Id$
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2002-2021 The Bochs Project
|
|
//
|
|
// This library is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU Lesser General Public
|
|
// License as published by the Free Software Foundation; either
|
|
// version 2 of the License, or (at your option) any later version.
|
|
//
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
// Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public
|
|
// License along with this library; if not, write to the Free Software
|
|
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
// shared code for the low-level cdrom support
|
|
|
|
#include "bochs.h"
|
|
#include "cdrom.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#define LOG_THIS /* no SMF tricks here, not needed */
|
|
|
|
#define BX_CD_FRAMESIZE 2048
|
|
|
|
unsigned int bx_cdrom_count = 0;
|
|
|
|
cdrom_base_c::cdrom_base_c(const char *dev)
|
|
{
|
|
char prefix[6];
|
|
|
|
sprintf(prefix, "CD%d", ++bx_cdrom_count);
|
|
put(prefix);
|
|
fd = -1; // File descriptor not yet allocated
|
|
|
|
if (dev == NULL) {
|
|
path = NULL;
|
|
} else {
|
|
path = strdup(dev);
|
|
}
|
|
using_file = 0;
|
|
}
|
|
|
|
cdrom_base_c::~cdrom_base_c(void)
|
|
{
|
|
if (fd >= 0)
|
|
close(fd);
|
|
if (path)
|
|
free(path);
|
|
BX_DEBUG(("Exit"));
|
|
}
|
|
|
|
bool cdrom_base_c::insert_cdrom(const char *dev)
|
|
{
|
|
unsigned char buffer[BX_CD_FRAMESIZE];
|
|
ssize_t ret;
|
|
|
|
// Load CD-ROM. Returns 0 if CD is not ready.
|
|
if (dev != NULL) path = strdup(dev);
|
|
BX_INFO(("load cdrom with path='%s'", path));
|
|
|
|
// all platforms except win32
|
|
fd = open(path, O_RDONLY
|
|
#ifdef O_BINARY
|
|
| O_BINARY
|
|
#endif
|
|
);
|
|
if (fd < 0) {
|
|
BX_ERROR(("open cd failed for '%s': %s", path, strerror(errno)));
|
|
return 0;
|
|
}
|
|
// do fstat to determine if it's a file or a device, then set using_file.
|
|
struct stat stat_buf;
|
|
ret = fstat(fd, &stat_buf);
|
|
if (ret) {
|
|
BX_PANIC(("fstat cdrom file returned error: %s", strerror (errno)));
|
|
}
|
|
if (S_ISREG(stat_buf.st_mode)) {
|
|
using_file = 1;
|
|
BX_INFO(("Opening image file as a cd."));
|
|
} else {
|
|
using_file = 0;
|
|
BX_INFO(("Using direct access for cdrom."));
|
|
}
|
|
// I just see if I can read a sector to verify that a
|
|
// CD is in the drive and readable.
|
|
return read_block(buffer, 0, 2048);
|
|
}
|
|
|
|
void cdrom_base_c::eject_cdrom()
|
|
{
|
|
// Logically eject the CD. I suppose we could stick in
|
|
// some ioctl() calls to really eject the CD as well.
|
|
|
|
if (fd >= 0) {
|
|
close(fd);
|
|
fd = -1;
|
|
}
|
|
}
|
|
|
|
bool cdrom_base_c::read_toc(Bit8u* buf, int* length, bool msf, int start_track, int format)
|
|
{
|
|
unsigned i;
|
|
Bit32u blocks;
|
|
int len = 4;
|
|
|
|
switch (format) {
|
|
case 0:
|
|
// From atapi specs : start track can be 0-63, AA
|
|
if ((start_track > 1) && (start_track != 0xaa))
|
|
return 0;
|
|
|
|
buf[2] = 1;
|
|
buf[3] = 1;
|
|
|
|
if (start_track <= 1) {
|
|
buf[len++] = 0; // Reserved
|
|
buf[len++] = 0x14; // ADR, control
|
|
buf[len++] = 1; // Track number
|
|
buf[len++] = 0; // Reserved
|
|
|
|
// Start address
|
|
if (msf) {
|
|
buf[len++] = 0; // reserved
|
|
buf[len++] = 0; // minute
|
|
buf[len++] = 2; // second
|
|
buf[len++] = 0; // frame
|
|
} else {
|
|
buf[len++] = 0;
|
|
buf[len++] = 0;
|
|
buf[len++] = 0;
|
|
buf[len++] = 0; // logical sector 0
|
|
}
|
|
}
|
|
|
|
// Lead out track
|
|
buf[len++] = 0; // Reserved
|
|
buf[len++] = 0x16; // ADR, control
|
|
buf[len++] = 0xaa; // Track number
|
|
buf[len++] = 0; // Reserved
|
|
|
|
blocks = capacity();
|
|
|
|
// Start address
|
|
if (msf) {
|
|
buf[len++] = 0; // reserved
|
|
buf[len++] = (Bit8u)(((blocks + 150) / 75) / 60); // minute
|
|
buf[len++] = (Bit8u)(((blocks + 150) / 75) % 60); // second
|
|
buf[len++] = (Bit8u)((blocks + 150) % 75); // frame;
|
|
} else {
|
|
buf[len++] = (blocks >> 24) & 0xff;
|
|
buf[len++] = (blocks >> 16) & 0xff;
|
|
buf[len++] = (blocks >> 8) & 0xff;
|
|
buf[len++] = (blocks >> 0) & 0xff;
|
|
}
|
|
buf[0] = ((len-2) >> 8) & 0xff;
|
|
buf[1] = (len-2) & 0xff;
|
|
break;
|
|
|
|
case 1:
|
|
// multi session stuff - emulate a single session only
|
|
buf[0] = 0;
|
|
buf[1] = 0x0a;
|
|
buf[2] = 1;
|
|
buf[3] = 1;
|
|
for (i = 0; i < 8; i++)
|
|
buf[4+i] = 0;
|
|
len = 12;
|
|
break;
|
|
|
|
case 2:
|
|
// raw toc - emulate a single session only (ported from qemu)
|
|
buf[2] = 1;
|
|
buf[3] = 1;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
buf[len++] = 1;
|
|
buf[len++] = 0x14;
|
|
buf[len++] = 0;
|
|
if (i < 3) {
|
|
buf[len++] = 0xa0 + i;
|
|
} else {
|
|
buf[len++] = 1;
|
|
}
|
|
buf[len++] = 0;
|
|
buf[len++] = 0;
|
|
buf[len++] = 0;
|
|
if (i < 2) {
|
|
buf[len++] = 0;
|
|
buf[len++] = 1;
|
|
buf[len++] = 0;
|
|
buf[len++] = 0;
|
|
} else if (i == 2) {
|
|
blocks = capacity();
|
|
if (msf) {
|
|
buf[len++] = 0; // reserved
|
|
buf[len++] = (Bit8u)(((blocks + 150) / 75) / 60); // minute
|
|
buf[len++] = (Bit8u)(((blocks + 150) / 75) % 60); // second
|
|
buf[len++] = (Bit8u)((blocks + 150) % 75); // frame;
|
|
} else {
|
|
buf[len++] = (blocks >> 24) & 0xff;
|
|
buf[len++] = (blocks >> 16) & 0xff;
|
|
buf[len++] = (blocks >> 8) & 0xff;
|
|
buf[len++] = (blocks >> 0) & 0xff;
|
|
}
|
|
} else {
|
|
buf[len++] = 0;
|
|
buf[len++] = 0;
|
|
buf[len++] = 0;
|
|
buf[len++] = 0;
|
|
}
|
|
}
|
|
buf[0] = ((len-2) >> 8) & 0xff;
|
|
buf[1] = (len-2) & 0xff;
|
|
break;
|
|
|
|
default:
|
|
BX_PANIC(("cdrom: read_toc(): unknown format"));
|
|
return 0;
|
|
}
|
|
|
|
*length = len;
|
|
|
|
return 1;
|
|
}
|
|
|
|
bool BX_CPP_AttrRegparmN(3) cdrom_base_c::read_block(Bit8u* buf, Bit32u lba, int blocksize)
|
|
{
|
|
// Read a single block from the CD
|
|
|
|
off_t pos;
|
|
ssize_t n = 0;
|
|
Bit8u try_count = 3;
|
|
Bit8u* buf1;
|
|
|
|
if (blocksize == 2352) {
|
|
memset(buf, 0, 2352);
|
|
memset(buf+1, 0xff, 10);
|
|
Bit32u raw_block = lba + 150;
|
|
buf[12] = (raw_block / 75) / 60;
|
|
buf[13] = (raw_block / 75) % 60;
|
|
buf[14] = (raw_block % 75);
|
|
buf[15] = 0x01;
|
|
buf1 = buf + 16;
|
|
} else {
|
|
buf1 = buf;
|
|
}
|
|
do {
|
|
pos = lseek(fd, (off_t) lba * BX_CD_FRAMESIZE, SEEK_SET);
|
|
if (pos < 0) {
|
|
BX_PANIC(("cdrom: read_block: lseek returned error."));
|
|
} else {
|
|
n = read(fd, (char*) buf1, BX_CD_FRAMESIZE);
|
|
}
|
|
} while ((n != BX_CD_FRAMESIZE) && (--try_count > 0));
|
|
|
|
return (n == BX_CD_FRAMESIZE);
|
|
}
|
|
|
|
Bit32u cdrom_base_c::capacity()
|
|
{
|
|
// Return CD-ROM capacity. I believe you want to return
|
|
// the number of blocks of capacity the actual media has.
|
|
|
|
if (using_file) {
|
|
// return length of the image file
|
|
struct stat stat_buf;
|
|
int ret = fstat(fd, &stat_buf);
|
|
if (ret) {
|
|
BX_PANIC(("fstat on cdrom image returned err: %s", strerror(errno)));
|
|
}
|
|
if ((stat_buf.st_size % 2048) != 0) {
|
|
BX_ERROR(("expected cdrom image to be a multiple of 2048 bytes"));
|
|
}
|
|
return (Bit32u)(stat_buf.st_size / 2048);
|
|
} else {
|
|
BX_ERROR(("capacity: your OS is not supported yet"));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
bool cdrom_base_c::start_cdrom()
|
|
{
|
|
// Spin up the cdrom drive.
|
|
|
|
if (fd >= 0) {
|
|
BX_INFO(("start_cdrom: your OS is not supported yet"));
|
|
return 0; // OS not supported yet, return 0 always
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool cdrom_base_c::seek(Bit32u lba)
|
|
{
|
|
unsigned char buffer[BX_CD_FRAMESIZE];
|
|
|
|
return read_block(buffer, lba, BX_CD_FRAMESIZE);
|
|
}
|