home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 6
/
AACD06.ISO
/
AACD
/
Emulation
/
BasiliskII
/
src
/
BeOS
/
sys_beos.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1999-10-03
|
17KB
|
832 lines
/*
* sys_beos.cpp - System dependent routines, BeOS implementation
*
* Basilisk II (C) 1997-1999 Christian Bauer
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <StorageKit.h>
#include <InterfaceKit.h>
#include <kernel/fs_info.h>
#include <drivers/Drivers.h>
#include <device/scsi.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include "sysdeps.h"
#include "main.h"
#include "macos_util.h"
#include "prefs.h"
#include "user_strings.h"
#include "sys.h"
#define DEBUG 0
#include "debug.h"
// File handles are pointers to these structures
struct file_handle {
file_handle *next; // Pointer to next file handle (must be first in struct!)
const char *name; // File/device name (copied, for mount menu)
int fd; // fd of file/device
bool is_file; // Flag: plain file or /dev/something?
bool read_only; // Copy of Sys_open() flag
loff_t start_byte; // Size of file header (if any)
loff_t file_size; // Size of file data (only valid if is_file is true)
};
// Linked list of file handles
static file_handle *first_file_handle;
// Temporary buffer for transfers from/to kernel space
const int TMP_BUF_SIZE = 0x10000;
static uint8 *tmp_buf;
// For B_SCSI_PREVENT_ALLOW
static const int32 PREVENT = 1;
static const int32 ALLOW = 0;
/*
* Check if device is a mounted HFS volume, get mount name
*/
static bool is_drive_mounted(const char *dev_name, char *mount_name)
{
int32 i = 0;
dev_t d;
fs_info info;
while ((d = next_dev(&i)) >= 0) {
fs_stat_dev(d, &info);
if (strcmp(dev_name, info.device_name) == 0) {
status_t err = -1;
BPath mount;
BDirectory dir;
BEntry entry;
node_ref node;
node.device = info.dev;
node.node = info.root;
err = dir.SetTo(&node);
if (!err)
err = dir.GetEntry(&entry);
if (!err)
err = entry.GetPath(&mount);
if (!err) {
strcpy(mount_name, mount.Path());
return true;
}
}
}
return false;
}
/*
* Initialization
*/
void SysInit(void)
{
first_file_handle = NULL;
// Allocate temporary buffer
tmp_buf = new uint8[TMP_BUF_SIZE];
}
/*
* Deinitialization
*/
void SysExit(void)
{
delete[] tmp_buf;
}
/*
* Create menu of used volumes (for "mount" menu)
*/
void SysCreateVolumeMenu(BMenu *menu, uint32 msg)
{
for (file_handle *fh=first_file_handle; fh; fh=fh->next)
if (!SysIsFixedDisk(fh))
menu->AddItem(new BMenuItem(fh->name, new BMessage(msg)));
}
/*
* Mount volume given name from mount menu
*/
void SysMountVolume(const char *name)
{
file_handle *fh;
for (fh=first_file_handle; fh && strcmp(fh->name, name); fh=fh->next) ;
if (fh)
MountVolume(fh);
}
/*
* This gets called when no "floppy" prefs items are found
* It scans for available floppy drives and adds appropriate prefs items
*/
void SysAddFloppyPrefs(void)
{
// Only one floppy drive under BeOS
PrefsAddString("floppy", "/dev/disk/floppy/raw");
}
/*
* This gets called when no "disk" prefs items are found
* It scans for available HFS volumes and adds appropriate prefs items
*/
void SysAddDiskPrefs(void)
{
// Let BeOS scan for HFS drives
D(bug("Looking for Mac volumes...\n"));
system("mountvolume -allhfs");
// Add all HFS volumes
int32 i = 0;
dev_t d;
fs_info info;
while ((d = next_dev(&i)) >= 0) {
fs_stat_dev(d, &info);
status_t err = -1;
BPath mount;
if (!strcmp(info.fsh_name, "hfs")) {
BDirectory dir;
BEntry entry;
node_ref node;
node.device = info.dev;
node.node = info.root;
err = dir.SetTo(&node);
if (!err)
err = dir.GetEntry(&entry);
if (!err)
err = entry.GetPath(&mount);
}
if (!err)
err = unmount(mount.Path());
if (!err) {
char dev_name[B_FILE_NAME_LENGTH];
if (info.flags & B_FS_IS_READONLY) {
dev_name[0] = '*';
dev_name[1] = 0;
} else
dev_name[0] = 0;
strcat(dev_name, info.device_name);
PrefsAddString("disk", dev_name);
}
}
}
/*
* This gets called when no "cdrom" prefs items are found
* It scans for available CD-ROM drives and adds appropriate prefs items
*/
// Scan directory for CD-ROM drives, add them to prefs
static void scan_for_cdrom_drives(const char *directory)
{
// Set directory
BDirectory dir;
dir.SetTo(directory);
if (dir.InitCheck() != B_NO_ERROR)
return;
dir.Rewind();
// Scan each entry
BEntry entry;
while (dir.GetNextEntry(&entry) >= 0) {
// Get path and ref for entry
BPath path;
if (entry.GetPath(&path) != B_NO_ERROR)
continue;
const char *name = path.Path();
entry_ref e;
if (entry.GetRef(&e) != B_NO_ERROR)
continue;
// Recursively enter subdirectories (except for floppy)
if (entry.IsDirectory()) {
if (!strcmp(e.name, "floppy"))
continue;
scan_for_cdrom_drives(name);
} else {
D(bug(" checking '%s'\n", name));
// Ignore partitions
if (strcmp(e.name, "raw"))
continue;
// Open device
int fd = open(name, O_RDONLY);
if (fd < 0)
continue;
// Get geometry and device type
device_geometry g;
if (ioctl(fd, B_GET_GEOMETRY, &g, sizeof(g)) < 0) {
close(fd);
continue;
}
// Insert to list if it is a CD drive
if (g.device_type == B_CD)
PrefsAddString("cdrom", name);
close(fd);
}
}
}
void SysAddCDROMPrefs(void)
{
// Don't scan for drives if nocdrom option given
if (PrefsFindBool("nocdrom"))
return;
// Look for CD-ROM drives and add prefs items
D(bug("Looking for CD-ROM drives...\n"));
scan_for_cdrom_drives("/dev/disk");
}
/*
* Add default serial prefs (must be added, even if no ports present)
*/
void SysAddSerialPrefs(void)
{
system_info info;
get_system_info(&info);
switch (info.platform_type) {
case B_BEBOX_PLATFORM:
case B_AT_CLONE_PLATFORM:
PrefsAddString("seriala", "serial1");
PrefsAddString("serialb", "serial2");
break;
case B_MAC_PLATFORM:
PrefsAddString("seriala", "modem");
PrefsAddString("serialb", "printer");
break;
default:
PrefsAddString("seriala", "none");
PrefsAddString("serialb", "none");
break;
}
}
/*
* Open file/device, create new file handle (returns NULL on error)
*/
void *Sys_open(const char *name, bool read_only)
{
static bool published_all = false;
bool is_file = (strstr(name, "/dev/") != name);
D(bug("Sys_open(%s, %s)\n", name, read_only ? "read-only" : "read/write"));
// Print warning message and eventually unmount drive when this is an HFS volume mounted under BeOS (double mounting will corrupt the volume)
char mount_name[B_FILE_NAME_LENGTH];
if (!is_file && !read_only && is_drive_mounted(name, mount_name)) {
char str[256 + B_FILE_NAME_LENGTH];
sprintf(str, GetString(STR_VOLUME_IS_MOUNTED_WARN), mount_name);
WarningAlert(str);
if (unmount(mount_name) != 0) {
sprintf(str, GetString(STR_CANNOT_UNMOUNT_WARN), mount_name);
WarningAlert(str);
return NULL;
}
}
int fd = open(name, read_only ? O_RDONLY : O_RDWR);
if (fd < 0 && !published_all) {
// Open failed, create all device nodes and try again, but only the first time
system("mountvolume -publishall");
published_all = true;
fd = open(name, read_only ? O_RDONLY : O_RDWR);
}
if (fd >= 0) {
file_handle *fh = new file_handle;
fh->name = strdup(name);
fh->fd = fd;
fh->is_file = is_file;
fh->read_only = read_only;
fh->start_byte = 0;
if (fh->is_file) {
// Detect disk image file layout
loff_t size = lseek(fd, 0, SEEK_END);
uint8 data[256];
lseek(fd, 0, SEEK_SET);
read(fd, data, 256);
FileDiskLayout(size, data, fh->start_byte, fh->file_size);
}
// Enqueue file handle
fh->next = NULL;
file_handle *q = first_file_handle;
if (q) {
while (q->next)
q = q->next;
q->next = fh;
} else
first_file_handle = fh;
return fh;
} else
return NULL;
}
/*
* Close file/device, delete file handle
*/
void Sys_close(void *arg)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return;
// Free device name and close file/device
free((void *)fh->name);
close(fh->fd);
// Dequeue file handle
file_handle *q = first_file_handle;
if (q == fh) {
first_file_handle = NULL;
delete fh;
return;
}
while (q) {
if (q->next == fh) {
q->next = fh->next;
delete fh;
return;
}
q = q->next;
}
}
/*
* Read "length" bytes from file/device, starting at "offset", to "buffer",
* returns number of bytes read (or 0)
*/
static inline ssize_t sread(int fd, void *buf, size_t count)
{
ssize_t res;
while ((res = read(fd, buf, count)) == B_INTERRUPTED) ;
return res;
}
size_t Sys_read(void *arg, void *buffer, loff_t offset, size_t length)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return 0;
// D(bug("Sys_read(%08lx, %08lx, %Ld, %d)\n", fh, buffer, offset, length));
// Seek to position
if (lseek(fh->fd, offset + fh->start_byte, SEEK_SET) < 0)
return 0;
// Buffer in kernel space?
size_t actual = 0;
if ((uint32)buffer < 0x80000000) {
// Yes, transfer via buffer
while (length) {
size_t transfer_size = (length > TMP_BUF_SIZE) ? TMP_BUF_SIZE : length;
if (sread(fh->fd, tmp_buf, transfer_size) != transfer_size)
return actual;
memcpy(buffer, tmp_buf, transfer_size);
buffer = (void *)((uint8 *)buffer + transfer_size);
length -= transfer_size;
actual += transfer_size;
}
} else {
// No, transfer directly
actual = sread(fh->fd, buffer, length);
if (actual < 0)
actual = 0;
}
return actual;
}
/*
* Write "length" bytes from "buffer" to file/device, starting at "offset",
* returns number of bytes written (or 0)
*/
static inline ssize_t swrite(int fd, void *buf, size_t count)
{
ssize_t res;
while ((res = write(fd, buf, count)) == B_INTERRUPTED) ;
return res;
}
size_t Sys_write(void *arg, void *buffer, loff_t offset, size_t length)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return 0;
// D(bug("Sys_write(%08lx, %08lx, %Ld, %d)\n", fh, buffer, offset, length));
// Seek to position
if (lseek(fh->fd, offset + fh->start_byte, SEEK_SET) < 0)
return 0;
// Buffer in kernel space?
size_t actual = 0;
if ((uint32)buffer < 0x80000000) {
// Yes, transfer via buffer
while (length) {
size_t transfer_size = (length > TMP_BUF_SIZE) ? TMP_BUF_SIZE : length;
memcpy(tmp_buf, buffer, transfer_size);
if (swrite(fh->fd, tmp_buf, transfer_size) != transfer_size)
return actual;
buffer = (void *)((uint8 *)buffer + transfer_size);
length -= transfer_size;
actual += transfer_size;
}
} else {
// No, transfer directly
actual = swrite(fh->fd, buffer, length);
if (actual < 0)
actual = 0;
}
return actual;
}
/*
* Return size of file/device (minus header)
*/
loff_t SysGetFileSize(void *arg)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return true;
if (fh->is_file)
return fh->file_size;
else {
device_geometry g;
if (ioctl(fh->fd, B_GET_GEOMETRY, &g, sizeof(g)) >= 0)
return (loff_t)g.bytes_per_sector * g.sectors_per_track * g.cylinder_count * g.head_count;
else
return 0;
}
}
/*
* Eject volume (if applicable)
*/
void SysEject(void *arg)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return;
if (!fh->is_file)
ioctl(fh->fd, B_EJECT_DEVICE);
}
/*
* Format volume (if applicable)
*/
bool SysFormat(void *arg)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return false;
if (!fh->is_file)
return ioctl(fh->fd, B_FORMAT_DEVICE) >= 0;
else
return false;
}
/*
* Check if file/device is read-only (this includes the read-only flag on Sys_open())
*/
bool SysIsReadOnly(void *arg)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return true;
if (fh->is_file) {
// File, return flag given to Sys_open
return fh->read_only;
} else {
// Device, check write protection
device_geometry g;
if (ioctl(fh->fd, B_GET_GEOMETRY, &g, sizeof(g)) >= 0)
return g.read_only | fh->read_only;
else
return fh->read_only; // Removable but not inserted
}
}
/*
* Check if the given file handle refers to a fixed or a removable disk
*/
bool SysIsFixedDisk(void *arg)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return true;
if (fh->is_file)
return true;
else {
device_geometry g;
if (ioctl(fh->fd, B_GET_GEOMETRY, &g, sizeof(g)) >= 0)
return !g.removable;
else
return false; // Removable but not inserted
}
}
/*
* Check if a disk is inserted in the drive (always true for files)
*/
bool SysIsDiskInserted(void *arg)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return false;
if (fh->is_file)
return true;
else {
status_t l;
if (ioctl(fh->fd, B_GET_MEDIA_STATUS, &l, sizeof(l)) >= 0 && l == B_NO_ERROR)
return true;
else
return false;
}
}
/*
* Prevent medium removal (if applicable)
*/
void SysPreventRemoval(void *arg)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return;
if (!fh->is_file)
ioctl(fh->fd, B_SCSI_PREVENT_ALLOW, &PREVENT, sizeof(PREVENT));
}
/*
* Allow medium removal (if applicable)
*/
void SysAllowRemoval(void *arg)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return;
if (!fh->is_file)
ioctl(fh->fd, B_SCSI_PREVENT_ALLOW, &ALLOW, sizeof(ALLOW));
}
/*
* Read CD-ROM TOC (binary MSF format, 804 bytes max.)
*/
bool SysCDReadTOC(void *arg, uint8 *toc)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return false;
if (!fh->is_file) {
memset(tmp_buf, 0, 804);
if (ioctl(fh->fd, B_SCSI_GET_TOC, tmp_buf, 804) < 0)
return false;
memcpy(toc, tmp_buf, 804);
return true;
} else
return false;
}
/*
* Read CD-ROM position data (Sub-Q Channel, 16 bytes, see SCSI standard)
*/
bool SysCDGetPosition(void *arg, uint8 *pos)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return false;
if (!fh->is_file) {
if (ioctl(fh->fd, B_SCSI_GET_POSITION, tmp_buf, 16) < 0)
return false;
memcpy(pos, tmp_buf, 16);
return true;
} else
return false;
}
/*
* Play CD audio
*/
bool SysCDPlay(void *arg, uint8 start_m, uint8 start_s, uint8 start_f, uint8 end_m, uint8 end_s, uint8 end_f)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return false;
if (!fh->is_file) {
scsi_play_position *p = (scsi_play_position *)tmp_buf;
p->start_m = start_m;
p->start_s = start_s;
p->start_f = start_f;
p->end_m = end_m;
p->end_s = end_s;
p->end_f = end_f;
return ioctl(fh->fd, B_SCSI_PLAY_POSITION, p, sizeof(scsi_play_position)) == 0;
} else
return false;
}
/*
* Pause CD audio
*/
bool SysCDPause(void *arg)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return true;
if (!fh->is_file)
return ioctl(fh->fd, B_SCSI_PAUSE_AUDIO) == 0;
else
return false;
}
/*
* Resume paused CD audio
*/
bool SysCDResume(void *arg)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return false;
if (!fh->is_file)
return ioctl(fh->fd, B_SCSI_RESUME_AUDIO) == 0;
else
return false;
}
/*
* Stop CD audio
*/
bool SysCDStop(void *arg, uint8 lead_out_m, uint8 lead_out_s, uint8 lead_out_f)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return false;
if (!fh->is_file)
return ioctl(fh->fd, B_SCSI_STOP_AUDIO) == 0;
else
return false;
}
/*
* Perform CD audio fast-forward/fast-reverse operation starting from specified address
*/
bool SysCDScan(void *arg, uint8 start_m, uint8 start_s, uint8 start_f, bool reverse)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return false;
if (!fh->is_file) {
scsi_scan *p = (scsi_scan *)tmp_buf;
p->speed = 0;
p->direction = reverse ? -1 : 1;
return ioctl(fh->fd, B_SCSI_SCAN, p, sizeof(scsi_scan)) == 0;
} else
return false;
}
/*
* Set CD audio volume (0..255 each channel)
*/
void SysCDSetVolume(void *arg, uint8 left, uint8 right)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return;
if (!fh->is_file) {
scsi_volume *p = (scsi_volume *)tmp_buf;
p->flags = B_SCSI_PORT0_VOLUME | B_SCSI_PORT1_VOLUME;
p->port0_volume = left;
p->port1_volume = right;
ioctl(fh->fd, B_SCSI_SET_VOLUME, p, sizeof(scsi_volume));
}
}
/*
* Get CD audio volume (0..255 each channel)
*/
void SysCDGetVolume(void *arg, uint8 &left, uint8 &right)
{
file_handle *fh = (file_handle *)arg;
if (!fh)
return;
left = right = 0;
if (!fh->is_file) {
scsi_volume *p = (scsi_volume *)tmp_buf;
p->flags = B_SCSI_PORT0_VOLUME | B_SCSI_PORT1_VOLUME;
if (ioctl(fh->fd, B_SCSI_GET_VOLUME, p, sizeof(scsi_volume)) == 0) {
left = p->port0_volume;
right = p->port1_volume;
}
}
}