home *** CD-ROM | disk | FTP | other *** search
- /* cdrom.c:
- *
- * Basic interaction with the CDROM drive.
- *
- * ----------------------------------------------------------------------
- * 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:
- *
- * 17-Feb-94 fmu Added support for Toshiba 4101.
- * 06-Feb-94 dmb - Fixed bug in Test_Unit_Ready() trackdisk support.
- * - Fixed bug in Open_CDROM (size of request)
- * - Added function Clear_Sector_Buffers().
- * 01-Jan-94 fmu Added function Data_Tracks() for multisession support.
- * 11-Dec-93 fmu - Memory type can now be chosen by the user.
- * - Addional parameter p_direction for Do_SCSI_Command().
- * - Start_Play_Audio() now plays all tracks.
- * - Mode_Select() instead of Select_XA_Mode().
- * - Support for CDROM drives with 512, 1024 or 2048 bytes
- * per block.
- * 06-Dec-93 fmu New drive type DRIVE_SCSI_2.
- * 09-Nov-93 fmu Added Select_XA_Mode.
- * 23-Oct-93 fmu Open_CDROM now returns an error code that tell what
- * went wrong.
- * 09-Oct-93 fmu SAS/C support added.
- * 03-Oct-93 fmu New buffering algorithm.
- * 27-Sep-93 fmu Added support for multi-LUN devices.
- * 24-Sep-93 fmu - SCSI buffers may now reside in fast or chip memory.
- * - TD_CHANGESTATE instead of CMD_READ in Test_Unit_Ready
- */
-
- #include <string.h>
-
- #include <clib/exec_protos.h>
- #include <clib/alib_protos.h>
-
- #ifdef AZTEC_C
- #include <pragmas/exec_lib.h>
- #endif
- #ifdef LATTICE
- #include <pragmas/exec_pragmas.h>
- #endif
- #if defined (_DCC) && defined (REGISTERED)
- #include <pragmas/exec_pragmas.h>
- extern struct Library *SysBase;
- #endif
-
- #include <devices/trackdisk.h>
-
- #include <limits.h>
-
- #include "cdrom.h"
-
- int g_cdrom_errno;
- int g_ignore_blocklength = FALSE;
-
- void Determine_Drive_Type (CDROM *p_cd)
- {
- t_inquiry_data data;
- char buf[33];
-
- p_cd->drive_type = DRIVE_ANY;
-
- if (!Inquire (p_cd, &data))
- return;
-
- if ((data.version & 0x7) >= 2)
- p_cd->drive_type = DRIVE_SCSI_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->drive_type = DRIVE_TOSHIBA_3401;
- }
- }
-
- CDROM *Open_CDROM (char *p_device, int p_scsi_id, int p_use_trackdisk,
- unsigned long p_memory_type,
- int p_std_buffers, int p_file_buffers)
- {
- CDROM *cd;
- int i;
- int bufs = p_std_buffers + p_file_buffers + 1;
-
- g_cdrom_errno = CDROMERR_NO_MEMORY;
-
- 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) {
- g_cdrom_errno = CDROMERR_MSGPORT;
- Cleanup_CDROM (cd);
- return NULL;
- }
-
- cd->scsireq = CreateIORequest (cd->port, sizeof (struct IOExtTD));
- if (!cd->scsireq) {
- g_cdrom_errno = CDROMERR_IOREQ;
- Cleanup_CDROM (cd);
- return NULL;
- }
-
- if (OpenDevice ((UBYTE *) p_device, p_scsi_id,
- (struct IORequest *) cd->scsireq, 0)) {
- g_cdrom_errno = CDROMERR_DEVICE;
- Cleanup_CDROM (cd);
- return NULL;
- }
-
- cd->device_open = TRUE;
-
- for (i=0; i<bufs; i++)
- cd->current_sectors[i] = -1;
-
- cd->use_trackdisk = p_use_trackdisk;
- cd->t_changeint = -1;
- cd->t_changeint2 = -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;
-
- g_cdrom_errno = 0;
-
- Determine_Drive_Type (cd);
-
- if (g_ignore_blocklength) {
- cd->block_length = 0;
- cd->blocking_factor = 0;
- } else {
- 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:
- g_cdrom_errno = CDROMERR_BLOCKSIZE;
- Cleanup_CDROM (cd);
- return NULL;
- }
- }
-
- 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 = 18;
- 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;
-
- DoIO ((struct IORequest *) p_cd->scsireq);
- if (p_cd->cmd.scsi_Status) {
- int i;
- 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)
- {
- static unsigned char cmd[10] = { 0x28, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- int bufs = p_cd->std_buffers + p_cd->file_buffers + 1;
-
- #ifdef DEBUG_SECTORS
- extern void *dbprintf (char *, ...);
-
- if (p_number_of_sectors == 1)
- dbprintf ("[%ld]", p_sector);
- else
- dbprintf ("[%ld-%ld]", p_sector, p_sector + p_number_of_sectors - 1);
- #endif
-
- if (p_cd->use_trackdisk) {
- p_cd->scsireq->io_Length = 2048 * p_number_of_sectors;
- p_cd->scsireq->io_Data = (APTR) p_buf;
- p_cd->scsireq->io_Offset = (ULONG) p_sector * 2048;
- p_cd->scsireq->io_Command = CMD_READ;
-
- DoIO ((struct IORequest *) p_cd->scsireq);
- if (p_cd->scsireq->io_Error) {
- int i;
- for (i=0; i<bufs; i++)
- p_cd->current_sectors[i] = -1;
- return 0;
- } else
- return 1;
- } else {
- long sector = p_sector << p_cd->blocking_factor;
- cmd[5] = sector & 0xff;
- cmd[4] = (sector >> 8) & 0xff;
- cmd[3] = (sector >> 16) & 0x1f;
-
- cmd[8] = p_number_of_sectors << p_cd->blocking_factor;
-
- return Do_SCSI_Command (p_cd, p_buf, p_buf_length, cmd, sizeof (cmd),
- SCSIF_READ);
- }
- }
-
- /* 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)) {
- if (p_cd->scsireq->io_Error==0)
- 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_Error==0 &&
- p_cd->scsireq->io_Actual==0)
- 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);
- }
- }
-
- 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))
- 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 (!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 (!(toc = Read_TOC (p_cd, &hdr)))
- return FALSE;
-
- len = hdr.length / 8;
- for (i=0; i<len; i++) {
- if (toc[i].track_number != 0xAA &&
- !(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 != 0xAA && (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 != 0xAA && (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, 99, 0 };
- int dummy_buf = p_cd->std_buffers + p_cd->file_buffers;
-
- if (p_cd->use_trackdisk || p_cd->drive_type == DRIVE_ANY)
- return FALSE;
-
- 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 || p_cd->drive_type == DRIVE_ANY)
- 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;
- }
-