home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AMIGA PD 1
/
AMIGA-PD-1.iso
/
Aminet
/
Tools
/
Useful
/
AmiCDROM
/
src
/
cdrom.c
next >
Wrap
C/C++ Source or Header
|
1994-10-03
|
17KB
|
684 lines
/* cdrom.c:
*
* Basic interaction with the CDROM drive.
*
* Modified by Nicola Salmoria.
*
* ----------------------------------------------------------------------
* This code is (C) Copyright 1993,1994 by Frank Munkert.
* All rights reserved.
* This software may be freely distributed and redistributed for
* non-commercial purposes, provided this notice is included.
* ----------------------------------------------------------------------
* [History removed]
*/
#include <string.h>
#include <exec/interrupts.h>
#include <exec/errors.h>
#include <clib/alib_protos.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <devices/trackdisk.h>
#include <limits.h>
#include "cdrom.h"
void Determine_Drive_Type (CDROM *p_cd)
{
t_inquiry_data data;
char buf[33];
p_cd->scsi_compliance = 1;
p_cd->model = MODEL_ANY;
if (!Inquire (p_cd, &data))
return;
if ((data.version & 0x7) >= 2)
p_cd->scsi_compliance = 2;
if (strncmp (data.vendor, "TOSHIBA", 7) == 0) {
memcpy (buf, data.product, 32);
buf[32] = 0;
if (strstr (buf, "3401") || strstr (buf, "4101"))
p_cd->model = MODEL_TOSHIBA_3401;
} else if (strncmp (data.vendor, "SONY", 4) == 0) {
memcpy (buf, data.product, 32);
buf[32] = 0;
if (strstr (buf, "CDU-8002"))
p_cd->model = MODEL_CDU_8002;
}
}
/*
* This is the diskchange interrupt which will be added with TD_ADDCHANGEINT
* to the device.
*/
LONG __saveds __asm DiskChangeInterrupt(register __a1 struct Task *task)
{
Signal(task,SIGBREAKF_CTRL_F);
return(0);
}
CDROM *Open_CDROM (char *p_device, int p_scsi_id, int p_flags,
long p_startoffset,int p_use_trackdisk,
unsigned long p_memory_type,
int p_std_buffers, int p_file_buffers,
BOOL addchangeint)
{
CDROM *cd;
int i;
int bufs;
struct IOExtTD *changereq;
bufs = p_std_buffers + p_file_buffers + 1;
cd = AllocMem (sizeof (CDROM),
MEMF_PUBLIC | MEMF_CLEAR | p_memory_type);
if (!cd)
return NULL;
cd->std_buffers = p_std_buffers;
cd->file_buffers = p_file_buffers;
cd->buffer_data = AllocMem (SCSI_BUFSIZE * bufs + 15,
MEMF_PUBLIC | p_memory_type);
if (!cd->buffer_data) {
Cleanup_CDROM (cd);
return NULL;
}
cd->buffers = AllocMem (sizeof (unsigned char *) * bufs, MEMF_PUBLIC);
if (!cd->buffers) {
Cleanup_CDROM (cd);
return NULL;
}
cd->current_sectors = AllocMem (sizeof (long) * bufs, MEMF_PUBLIC);
if (!cd->current_sectors) {
Cleanup_CDROM (cd);
return NULL;
}
cd->last_used = AllocMem (sizeof (unsigned long) * p_std_buffers,
MEMF_PUBLIC | MEMF_CLEAR);
if (!cd->last_used) {
Cleanup_CDROM (cd);
return NULL;
}
/* make the buffer quad-word aligned. This greatly helps
* performance on '040-powered systems with DMA SCSI
* controllers.
*/
cd->buffers[0] = (UBYTE *)(((ULONG)cd->buffer_data + 15) & ~15);
for (i=1; i<bufs; i++)
cd->buffers[i] = cd->buffers[0] + i * SCSI_BUFSIZE;
cd->port = CreateMsgPort ();
if (!cd->port) {
Cleanup_CDROM (cd);
return NULL;
}
cd->scsireq = CreateIORequest (cd->port, sizeof (struct IOExtTD));
if (!cd->scsireq) {
Cleanup_CDROM (cd);
return NULL;
}
if (OpenDevice ((UBYTE *) p_device, p_scsi_id,
(struct IORequest *) cd->scsireq, p_flags)) {
Cleanup_CDROM (cd);
return NULL;
}
cd->device_open = TRUE;
for (i=0; i<bufs; i++)
cd->current_sectors[i] = -1;
cd->startoffset = p_startoffset;
/* to avoid problems when booting from RDB, don't use SCSI commands if */
/* the start block is not block 0 */
if (p_startoffset) p_use_trackdisk = TRUE;
cd->use_trackdisk = p_use_trackdisk;
cd->t_changeint = (unsigned long)-1;
cd->t_changeint2 = (unsigned long)-2;
/* The LUN is the 2nd digit of the SCSI id number: */
cd->lun = (p_scsi_id / 10) % 10;
/* 'tick' is incremented every time a sector is accessed. */
cd->tick = 0;
if (p_use_trackdisk) {
cd->scsireq->io_Command = CMD_CLEAR;
DoIO ((struct IORequest *) cd->scsireq);
}
else Determine_Drive_Type (cd);
cd->block_length = Block_Length (cd);
switch (cd->block_length) {
case 2048:
cd->blocking_factor = 0;
break;
case 1024:
cd->blocking_factor = 1;
break;
case 512:
cd->blocking_factor = 2;
break;
case 0:
cd->blocking_factor = 0;
break;
default:
Cleanup_CDROM (cd);
return NULL;
}
/* now (try to) add the DiskChangeInt */
if (addchangeint && (changereq = CreateIORequest(cd->port,sizeof(struct IOExtTD) + sizeof(struct Interrupt))))
{
struct Interrupt *ir;
ir = (struct Interrupt *)(changereq + 1);
ir->is_Node.ln_Type = NT_INTERRUPT;
ir->is_Node.ln_Pri = 32;
ir->is_Data = FindTask(NULL);
ir->is_Code = (VOID (*)())DiskChangeInterrupt;
changereq->iotd_Req.io_Device = cd->scsireq->io_Device;
changereq->iotd_Req.io_Unit = cd->scsireq->io_Unit;
changereq->iotd_Req.io_Data = ir;
changereq->iotd_Req.io_Length = sizeof(struct Interrupt);
changereq->iotd_Req.io_Command = TD_ADDCHANGEINT;
SendIO(changereq);
}
return cd;
}
int Do_SCSI_Command (CDROM *p_cd, unsigned char *p_buf, long p_buf_length,
unsigned char *p_command, int p_length, int p_direction)
{
int bufs = p_cd->std_buffers + p_cd->file_buffers + 1;
p_cd->scsireq->io_Length = sizeof (struct SCSICmd);
p_cd->scsireq->io_Data = (APTR) &p_cd->cmd;
p_cd->scsireq->io_Command = HD_SCSICMD;
p_cd->cmd.scsi_Data = (UWORD *) p_buf;
p_cd->cmd.scsi_Length = p_buf_length;
p_cd->cmd.scsi_Flags = SCSIF_AUTOSENSE | p_direction;
p_cd->cmd.scsi_SenseData = (UBYTE *) p_cd->sense;
p_cd->cmd.scsi_SenseLength = 20;
p_cd->cmd.scsi_SenseActual = 0;
p_cd->cmd.scsi_Command = (UBYTE *) p_command;
p_cd->cmd.scsi_CmdLength = p_length;
p_command[1] |= p_cd->lun << 5;
if (DoIO((struct IORequest *) p_cd->scsireq) || p_cd->cmd.scsi_Status)
{
int i;
D(kprintf("SCSI command failed error %ld status %ld\n",p_cd->scsireq->io_Error,p_cd->cmd.scsi_Status));
for (i=0; i<bufs; i++)
p_cd->current_sectors[i] = -1;
return 0;
} else
return 1;
}
int Read_From_Drive (CDROM *p_cd, unsigned char *p_buf, long p_buf_length,
long p_sector, int p_number_of_sectors)
{
int bufs = p_cd->std_buffers + p_cd->file_buffers + 1;
#ifdef DEBUG_SECTORS
D(
if (p_number_of_sectors == 1)
kprintf("[%ld]\n",p_sector);
else
kprintf("[%ld-%ld]\n",p_sector, p_sector + p_number_of_sectors - 1);
)
#endif
p_cd->scsireq->io_Length = 2048 * p_number_of_sectors;
p_cd->scsireq->io_Data = (APTR) p_buf;
p_cd->scsireq->io_Offset = p_cd->startoffset + p_sector * 2048;
p_cd->scsireq->io_Command = CMD_READ;
/* remember that the motor is on */
p_cd->motor = 2;
if (DoIO ((struct IORequest *) p_cd->scsireq))
{
int i;
for (i=0; i<bufs; i++)
p_cd->current_sectors[i] = -1;
SetIoErr(p_cd->scsireq->io_Error);
return 0;
} else return 1;
}
/* Read one sector from the CDROM drive.
*/
int Read_Sector (CDROM *p_cd, long p_sector)
{
int status;
int i;
int maxbuf = p_cd->std_buffers;
int loc;
p_cd->tick++;
for (i=0; i<maxbuf; i++)
if (p_cd->current_sectors[i] == p_sector) {
p_cd->buffer = p_cd->buffers[i];
p_cd->last_used[i] = p_cd->tick;
return 1;
}
/* find an empty buffer position: */
for (loc=0; loc<maxbuf; loc++)
if (p_cd->current_sectors[loc] == -1)
break;
if (loc==maxbuf) {
/* no free buffer position; remove the buffer that is unused
for the longest time: */
unsigned long oldest_tick = ULONG_MAX;
unsigned long tick;
for (loc=0, i=0; i<maxbuf; i++) {
tick = p_cd->last_used[i];
if (tick < oldest_tick)
loc = i, oldest_tick = tick;
}
}
status = Read_From_Drive (p_cd, p_cd->buffers[loc], SCSI_BUFSIZE,
p_sector, 1);
if (status) {
p_cd->current_sectors[loc] = p_sector;
p_cd->buffer = p_cd->buffers[loc];
p_cd->last_used[loc] = p_cd->tick;
}
return status;
}
/* Read_Contiguous_Sectors uses the 'file buffers' instead of the
* 'standard buffers'. Additionaly, more than one sector may be read
* with a single SCSI command. This may cause a substantial increase
* in speed when reading large files.
*/
int Read_Contiguous_Sectors (CDROM *p_cd, long p_sector, long p_last_sector)
{
int status;
int i;
int maxbuf = p_cd->std_buffers + p_cd->file_buffers;
int len;
for (i=p_cd->std_buffers; i<maxbuf; i++)
if (p_cd->current_sectors[i] == p_sector) {
p_cd->buffer = p_cd->buffers[i];
return 1;
}
if (p_last_sector <= p_sector)
len = 1;
else {
len = p_last_sector - p_sector + 1;
if (len > p_cd->file_buffers)
len = p_cd->file_buffers;
if (len > 255)
len = 255;
}
status = Read_From_Drive (p_cd, p_cd->buffers[p_cd->std_buffers],
SCSI_BUFSIZE * len, p_sector, len);
if (status) {
long sector = p_sector;
for (i=p_cd->std_buffers; len; i++, len--)
p_cd->current_sectors[i] = sector++;
p_cd->buffer = p_cd->buffers[p_cd->std_buffers];
}
return status;
}
int Test_Unit_Ready (CDROM *p_cd)
{
if (p_cd->use_trackdisk)
{
p_cd->scsireq->io_Command = TD_CHANGENUM;
if (!DoIO((struct IORequest *)p_cd->scsireq))
p_cd->t_changeint = p_cd->scsireq->io_Actual;
p_cd->scsireq->io_Command = TD_CHANGESTATE;
if (!DoIO((struct IORequest *)p_cd->scsireq))
{
if (!p_cd->scsireq->io_Actual) return TRUE;
}
return FALSE;
}
else
{
int dummy_buf = p_cd->std_buffers + p_cd->file_buffers;
static unsigned char cmd[6] = { 0, 0, 0, 0, 0, 0 };
return Do_SCSI_Command (p_cd, p_cd->buffers[dummy_buf], SCSI_BUFSIZE,
cmd, 6, SCSIF_READ);
}
}
/*
* if the drive motor is on, turn it off. This is needed otherwise some
* controllers will not allow to eject the disc.
*/
VOID Motor_Off(CDROM *p_cd)
{
if (p_cd->motor && !--p_cd->motor)
{
D(kprintf ("motor off\n"));
p_cd->scsireq->io_Length = 0;
p_cd->scsireq->io_Command = TD_MOTOR;
DoIO((struct IORequest *)p_cd->scsireq);
}
}
int Is_XA_Mode_Disk (CDROM *p_cd)
{
static unsigned char cmd[10] = { 0xC7, 3, 0, 0, 0, 0, 0, 0, 0, 0 };
int dummy_buf = p_cd->std_buffers + p_cd->file_buffers;
if (!Do_SCSI_Command (p_cd, p_cd->buffers[dummy_buf], 4,
cmd, 10, SCSIF_READ))
return FALSE;
return *(p_cd->buffers[dummy_buf]) == 0x20;
}
int Mode_Select (CDROM *p_cd, int p_mode, int p_block_length)
{
static unsigned char cmd[6] = { 0x15, 0x10, 0, 0, 12, 0 };
static unsigned char mode[12] = { 0, 0, 0, 8,
0, 0, 0, 0, 0, 0, 0, 0 };
int dummy_buf = p_cd->std_buffers + p_cd->file_buffers;
if (p_cd->use_trackdisk)
return FALSE;
p_cd->block_length = p_block_length;
switch (p_cd->block_length) {
case 2048:
p_cd->blocking_factor = 0;
break;
case 1024:
p_cd->blocking_factor = 1;
break;
case 512:
p_cd->blocking_factor = 2;
break;
}
mode[4] = p_mode;
mode[9] = p_block_length >> 16;
mode[10] = (p_block_length >> 8) & 0xff;
mode[11] = p_block_length & 0xff;
memcpy (p_cd->buffers[dummy_buf], mode, sizeof (mode));
return Do_SCSI_Command (p_cd, p_cd->buffers[dummy_buf], sizeof (mode),
cmd, 6, SCSIF_WRITE);
}
int Inquire (CDROM *p_cd, t_inquiry_data *p_data)
{
static unsigned char cmd[6] = { 0x12, 0, 0, 0, 96, 0 };
int dummy_buf = p_cd->std_buffers + p_cd->file_buffers;
if (p_cd->use_trackdisk)
return FALSE;
if (!Do_SCSI_Command (p_cd, p_cd->buffers[dummy_buf], 96,
cmd, 6, SCSIF_READ))
{
/* if the device doesn't support SCSI commands, turn to trackdisk operations */
if (p_cd->scsireq->io_Error == IOERR_NOCMD)
p_cd->use_trackdisk = TRUE;
return FALSE;
}
memcpy (p_data, p_cd->buffers[dummy_buf], sizeof (*p_data));
return 1;
}
#define TOC_SIZE 804
t_toc_data *Read_TOC (CDROM *p_cd, t_toc_header *p_toc_header)
{
static unsigned char cmd[10] = { 0x43, 0, 0, 0, 0, 0, 0,
TOC_SIZE >> 8, TOC_SIZE & 0xff, 0 };
int dummy_buf = p_cd->std_buffers + p_cd->file_buffers;
if (p_cd->use_trackdisk)
return NULL;
if (p_cd->model == MODEL_CDU_8002) /* READ TOC not supported by this drive */
return NULL;
if (!Do_SCSI_Command (p_cd, p_cd->buffers[dummy_buf], TOC_SIZE,
cmd, 10, SCSIF_READ))
return NULL;
memcpy (p_toc_header, p_cd->buffers[dummy_buf], sizeof (*p_toc_header));
return (t_toc_data *) (p_cd->buffers[dummy_buf] + 4);
}
int Has_Audio_Tracks (CDROM *p_cd)
{
t_toc_header hdr;
t_toc_data *toc;
int i, len;
if (p_cd->use_trackdisk) return(FALSE);
if (!(toc = Read_TOC (p_cd, &hdr)))
return FALSE;
len = hdr.length / 8;
for (i=0; i<len; i++) {
if (toc[i].track_number <= 99 &&
!(toc[i].flags & 4))
return toc[i].track_number;
}
return FALSE;
}
/*
* Create a buffer containing the start addresses of all data tracks
* on the disk.
*
* Returns:
* number of tracks or -1 on error.
*/
int Data_Tracks (CDROM *p_cd, unsigned long** p_buf)
{
int cnt=0;
t_toc_header hdr;
t_toc_data *toc;
int i, j, len;
if (!(toc = Read_TOC (p_cd, &hdr)))
return -1;
len = hdr.length / 8;
/* count number of data tracks: */
for (i=0; i<len; i++)
if (toc[i].track_number <= 99 && (toc[i].flags & 4))
cnt++;
if (cnt == 0)
return 0;
/* allocate memory for output buffer: */
*p_buf = (unsigned long*) AllocVec (cnt * sizeof (unsigned long*),
MEMF_PUBLIC);
if (!*p_buf)
return -1;
/* fill output buffer: */
for (i=0, j=0; i<len; i++)
if (toc[i].track_number <= 99 && (toc[i].flags & 4))
(*p_buf)[j++] = toc[i].address;
return cnt;
}
int Start_Play_Audio (CDROM *p_cd)
{
static unsigned char cmd[10] = { 0x48, 0, 0, 0, 0, 1, 0, 99, 1, 0 };
int dummy_buf = p_cd->std_buffers + p_cd->file_buffers;
if (p_cd->use_trackdisk ||
(p_cd->scsi_compliance == 1 && p_cd->model == MODEL_ANY))
return FALSE;
if (p_cd->model == MODEL_CDU_8002)
cmd[0] = 0xC9;
cmd[4] = Has_Audio_Tracks (p_cd);
return Do_SCSI_Command (p_cd, p_cd->buffers[dummy_buf], 0,
cmd, 10, SCSIF_WRITE);
}
int Stop_Play_Audio (CDROM *p_cd)
{
static unsigned char cmd[6] = { 0x1B, 0, 0, 0, 0, 0 };
int dummy_buf = p_cd->std_buffers + p_cd->file_buffers;
if (p_cd->use_trackdisk)
return FALSE;
return Do_SCSI_Command (p_cd, p_cd->buffers[dummy_buf], 0,
cmd, 6, SCSIF_WRITE);
}
int Block_Length (CDROM *p_cd)
{
static unsigned char cmd[6] = { 0x1A, 0, 1, 0, 100, 0 };
int dummy_buf = p_cd->std_buffers + p_cd->file_buffers;
unsigned char *buf = p_cd->buffers[dummy_buf];
if (p_cd->use_trackdisk)
return 0;
if (!Do_SCSI_Command (p_cd, buf, 100, cmd, 6, SCSIF_READ))
return 0;
if (buf[3] == 0)
return 0;
return (buf[9]<<16) + (buf[10]<<8) + buf[11];
}
void Cleanup_CDROM (CDROM *p_cd)
{
int bufs = p_cd->std_buffers + p_cd->file_buffers + 1;
if (p_cd->device_open)
CloseDevice ((struct IORequest *) p_cd->scsireq);
if (p_cd->scsireq)
DeleteIORequest (p_cd->scsireq);
if (p_cd->port)
DeleteMsgPort (p_cd->port);
if (p_cd->last_used)
FreeMem (p_cd->last_used, sizeof (unsigned long) * p_cd->std_buffers);
if (p_cd->current_sectors)
FreeMem (p_cd->current_sectors, sizeof (long) * bufs);
if (p_cd->buffers)
FreeMem (p_cd->buffers, sizeof (unsigned char *) * bufs);
if (p_cd->buffer_data)
FreeMem (p_cd->buffer_data, SCSI_BUFSIZE * bufs + 15);
FreeMem (p_cd, sizeof (CDROM));
}
void Clear_Sector_Buffers (CDROM *p_cd)
{
int i;
int bufs = p_cd->std_buffers + p_cd->file_buffers + 1;
for (i=0; i<bufs; i++)
p_cd->current_sectors[i] = -1;
}
/* Finds offset of last session. (Not supported by all CDROM drives)
*
* Returns: - FALSE if there is no special SCSI command to determine the
* offset of the last session.
* - TRUE if the offset of the last session has been determined.
*/
int Find_Last_Session (CDROM *p_cd, unsigned long *p_result)
{
static unsigned char cmd[10] = { 0xC7, 3, 0, 0, 0, 0, 0, 0, 0, 0 };
int dummy_buf = p_cd->std_buffers + p_cd->file_buffers;
unsigned char *buf = p_cd->buffers[dummy_buf];
int min, sec, frame;
if (p_cd->use_trackdisk)
return FALSE;
if (p_cd->model != MODEL_TOSHIBA_3401)
return FALSE;
if (!Do_SCSI_Command (p_cd, p_cd->buffers[dummy_buf], SCSI_BUFSIZE,
cmd, sizeof (cmd), SCSIF_READ))
return FALSE;
min = (buf[1] & 0xF) + ((buf[1] & 0xF0) >> 4) * 10;
sec = (buf[2] & 0xF) + ((buf[2] & 0xF0) >> 4) * 10;
frame = (buf[3] & 0xF) + ((buf[3] & 0xF0) >> 4) * 10;
*p_result = frame + sec * 75 + min * (75 * 60) - 150;
return TRUE;
}