/* * sys_beos.cpp - System dependent routines, BeOS implementation * * Basilisk II (C) 1997-2005 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 #include #include #include #include #include #include #include #include #include #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; } } }