📄 mcd.c
字号:
/* This file contains the driver for a Mitsumi cdrom controller.
*
* The file contains one entry point:
*
* mcd_task: main entry when system is brought up
*
* Mar 30 1995 Author: Michel R. Prevenier
*/
#include "kernel.h"
#include "driver.h"
#include "drvlib.h"
#include <minix/cdrom.h>
#include <sys/ioctl.h>
#if ENABLE_MITSUMI_CDROM
#define MCD_DEBUG 0 /* debug level */
/* Default IRQ. */
#define MCD_IRQ 10
/* Default I/O ports (offset from base address */
#define MCD_IO_BASE_ADDRESS 0x300
#define MCD_DATA_PORT (mcd_io_base+0)
#define MCD_FLAG_PORT (mcd_io_base+1)
#define MCD_CONTROL_PORT (mcd_io_base+2)
/* Miscellaneous constants. */
#define MCD_SKIP 150 /* Skip first 150 blocks on cdrom */
#define MCD_BLOCK_SIZE 2048 /* Block size in cooked mode */
#define MCD_BLOCK_SHIFT 11 /* for division */
#define MCD_BLOCK_MASK 2047 /* and remainder */
#define BYTES_PER_SECTOR 2048 /* Nr. of bytes in a sector */
#define SECTORS_PER_SECOND 75 /* Nr. of sectors in a second */
#define SECONDS_PER_MINUTE 60 /* You never know, things change :-) */
#define MCD_RETRIES 2 /* Number of retries for a command */
#define REPLY_DELAY 5000 /* Count to wait for a reply */
#define MAX_TRACKS 104 /* Maximum nr. of tracks */
#define LEAD_OUT 0xAA /* Lead out track is always 0xAA */
#define SUB_PER_DRIVE (NR_PARTITIONS * NR_PARTITIONS)
/* Drive commands */
#define MCD_GET_VOL_INFO 0x10 /* Read volume information */
#define MCD_GET_Q_CHANNEL 0x20 /* Read q-channel information */
#define MCD_GET_STATUS 0x40 /* Read status of drive */
#define MCD_SET_MODE 0x50 /* Set transmission mode */
#define MCD_RESET 0x60 /* Reset controller */
#define MCD_STOP_AUDIO 0x70 /* Stop audio playing */
#define MCD_SET_DRIVE_MODE 0xA0 /* Set drive mode */
#define MCD_READ_FROM_TO 0xC0 /* Read from .. to .. */
#define MCD_GET_VERSION 0xDC /* Get version number */
#define MCD_STOP 0xF0 /* Stop everything */
#define MCD_EJECT 0xF6 /* Eject cd */
#define MCD_PICKLE 0x04 /* Needed for newer drive models */
/* Command bits for MCD_SET_MODE command */
#define MCD_MUTE_DATA 0x01 /* 1 = Don't play back data as audio */
#define MCD_GET_TOC 0x04 /* 0 = Get toc on next GET_Q_CHANNEL */
#define MCD_ECC_MODE 0x20 /* 0 = Use secondary ecc */
#define MCD_DATALENGTH 0x40 /* 0 = Read user data only */
#define MCD_COOKED (MCD_MUTE_DATA)
#define MCD_TOC (MCD_MUTE_DATA | MCD_GET_TOC)
/* Status bits */
#define MCD_CMD_ERROR 0x01 /* Command error */
#define MCD_AUDIO_BUSY 0x02 /* Audio disk is playing */
#define MCD_READ_ERROR 0x04 /* Read error */
#define MCD_AUDIO_DISK 0x08 /* Audio disk is in */
#define MCD_SPINNING 0x10 /* Motor is spinning */
#define MCD_DISK_CHANGED 0x20 /* Disk has been removed or changed */
#define MCD_DISK_IN 0x40 /* Disk is in */
#define MCD_DOOR_OPEN 0x80 /* Door is open */
/* Flag bits */
#define MCD_DATA_AVAILABLE 0x02 /* Data available */
#define MCD_BUSY 0x04 /* Drive is busy */
/* Function prototypes */
FORWARD _PROTOTYPE ( int mcd_init, (void));
FORWARD _PROTOTYPE ( int c_handler, (int irq));
FORWARD _PROTOTYPE ( int mcd_play_mss, (struct cd_play_mss));
FORWARD _PROTOTYPE ( int mcd_play_tracks, (struct cd_play_track tracks));
FORWARD _PROTOTYPE ( int mcd_stop, (void));
FORWARD _PROTOTYPE ( int mcd_eject, (void));
FORWARD _PROTOTYPE ( int mcd_pause, (void));
FORWARD _PROTOTYPE ( int mcd_resume, (void));
FORWARD _PROTOTYPE ( u8_t bin2bcd, (u8_t b));
FORWARD _PROTOTYPE ( void bcd2bin, (u8_t *bcd));
FORWARD _PROTOTYPE ( long mss2block, (u8_t *mss));
FORWARD _PROTOTYPE ( void block2mss, (long block, u8_t *mss));
FORWARD _PROTOTYPE ( int mcd_get_reply, (u8_t *reply, int delay));
FORWARD _PROTOTYPE ( int mcd_get_status, (int f));
FORWARD _PROTOTYPE ( int mcd_ready, (int delay));
FORWARD _PROTOTYPE ( int mcd_data_ready, (int delay));
FORWARD _PROTOTYPE ( int mcd_set_mode, (int mode));
FORWARD _PROTOTYPE ( int mcd_send_command, (int command));
FORWARD _PROTOTYPE ( int mcd_get_disk_info, (void));
FORWARD _PROTOTYPE ( int mcd_read_q_channel, (struct cd_toc_entry *qc));
FORWARD _PROTOTYPE ( int mcd_read_toc, (void));
FORWARD _PROTOTYPE ( int ioctl_read_toc, (message *m_ptr));
FORWARD _PROTOTYPE ( int ioctl_disk_info, (message *m_ptr));
FORWARD _PROTOTYPE ( int ioctl_read_sub, (message *m_ptr));
FORWARD _PROTOTYPE ( int ioctl_disk_info, (message *m_ptr));
FORWARD _PROTOTYPE ( int ioctl_play_mss, (message *m_ptr));
FORWARD _PROTOTYPE ( int ioctl_play_ti, (message *m_ptr));
FORWARD _PROTOTYPE ( int mcd_open, (struct driver *dp, message *m_ptr));
FORWARD _PROTOTYPE ( int mcd_close, (struct driver *dp, message *m_ptr));
FORWARD _PROTOTYPE ( int mcd_ioctl, (struct driver *dp, message *m_ptr));
FORWARD _PROTOTYPE ( char *mcd_name, (void));
FORWARD _PROTOTYPE ( struct device *mcd_prepare, (int dev));
FORWARD _PROTOTYPE ( int mcd_schedule, (int proc_nr, struct iorequest_s *iop));
FORWARD _PROTOTYPE ( int mcd_finish, (void));
FORWARD _PROTOTYPE ( void mcd_geometry, (struct partition *entry));
/* Flags displaying current status of cdrom, used with the McdStatus variable */
#define TOC_UPTODATE 0x001 /* Table of contents is up to date */
#define INFO_UPTODATE 0x002 /* Disk info is up to date */
#define DISK_CHANGED 0x004 /* Disk has changed */
#define AUDIO_PLAYING 0x008 /* Cdrom is playing audio */
#define AUDIO_PAUSED 0x010 /* Cdrom is paused (only audio) */
#define AUDIO_DISK 0x020 /* Disk contains audio */
#define DISK_ERROR 0x040 /* An error occured */
#define NO_DISK 0x080 /* No disk in device */
/* Entry points to this driver. */
PRIVATE struct driver mcd_dtab =
{
#if __minix_vmd
NULL, /* No private request buffer */
#endif
mcd_name, /* Current device's name */
mcd_open, /* Open request read table of contents */
mcd_close, /* Release device */
mcd_ioctl, /* Do cdrom ioctls */
mcd_prepare, /* Prepare for I/O */
mcd_schedule, /* Precompute blocks */
mcd_finish, /* Do the I/O */
nop_cleanup, /* No cleanup to do */
mcd_geometry /* Tell geometry */
};
PRIVATE struct trans
{
struct iorequest_s *tr_iop; /* Belongs to this I/O request */
unsigned long tr_pos; /* Byte position to transfer from */
int tr_count; /* Byte count */
phys_bytes tr_phys; /* User physical address */
} mcd_trans[NR_IOREQS];
/* Globals */
#if __minix_vmd
PRIVATE int mcd_tasknr = ANY;
#endif
PRIVATE int mcd_avail; /* Set if Mitsumi device exists */
PRIVATE int mcd_irq; /* Interrupt request line */
PRIVATE int mcd_io_base; /* I/O base register */
PRIVATE struct device *mcd_dv; /* Active partition */
PRIVATE struct trans *mcd_tp; /* Pointer to add transfer requests */
PRIVATE unsigned mcd_count; /* Number of bytes to transfer */
PRIVATE unsigned long mcd_nextpos; /* Next consecutive position on disk */
PRIVATE struct device mcd_part[DEV_PER_DRIVE];
/* Primary partitions: cd[0-4] */
PRIVATE struct device mcd_subpart[SUB_PER_DRIVE];
/* Subpartitions: cd[1-4][a-d] */
PRIVATE int mcd_open_ct; /* in-use count */
PRIVATE int McdStatus = NO_DISK; /* A new (or no) disk is inserted */
PRIVATE struct cd_play_mss PlayMss; /* Keep track of where we are if we
pause, used by resume */
PRIVATE struct cd_disk_info DiskInfo; /* Contains toc header */
PRIVATE struct cd_toc_entry Toc[MAX_TRACKS]; /* Buffer for toc */
/*=========================================================================*
* mcd_task *
*=========================================================================*/
PUBLIC void mcd_task()
{
long v;
static char var[] = "MCD";
static char fmt[] = "x:d";
#if __minix_vmd
mcd_tasknr = proc_number(proc_ptr);
#endif
/* Configure I/O base and IRQ. */
v = MCD_IO_BASE_ADDRESS;
(void) env_parse(var, fmt, 0, &v, 0x000L, 0x3FFL);
mcd_io_base = v;
v = MCD_IRQ;
(void) env_parse(var, fmt, 0, &v, 0L, (long) NR_IRQ_VECTORS - 1);
mcd_irq = v;
driver_task(&mcd_dtab); /* Start driver task for cdrom */
}
/*=========================================================================*
* mcd_open *
*=========================================================================*/
PRIVATE int mcd_open(dp, m_ptr)
struct driver *dp; /* pointer to this drive */
message *m_ptr; /* OPEN */
{
int i, status;
if (!mcd_avail && mcd_init() != OK) return EIO;
if (mcd_prepare(m_ptr->DEVICE) == NIL_DEV) return ENXIO;
/* A CD-ROM is read-only by definition. */
if (m_ptr->COUNT & W_BIT) return EACCES;
if (mcd_open_ct == 0)
{
i = 20;
for (;;) {
if (mcd_get_status(1) == -1) return EIO; /* set McdStatus flags */
if (!(McdStatus & NO_DISK)) break;
if (--i == 0) return EIO;
milli_delay(100);
}
/* Try to read the table of contents of the CD currently inserted */
if ((status = mcd_read_toc()) != OK)
return status;
mcd_open_ct++;
/* fill in size of device (= nr. of bytes on the disk) */
mcd_part[0].dv_base = 0;
mcd_part[0].dv_size =
((((unsigned long)DiskInfo.disk_length_mss[MINUTES] * SECONDS_PER_MINUTE
+ (unsigned long)DiskInfo.disk_length_mss[SECONDS]) * SECTORS_PER_SECOND)
+ (unsigned long)DiskInfo.disk_length_mss[SECTOR]) * BYTES_PER_SECTOR;
#if MCD_DEBUG >= 1
printf("cd size: %lu\n", mcd_part[0].dv_size);
#endif
/* Partition the disk. */
partition(&mcd_dtab, 0, P_PRIMARY);
}
return OK;
}
/*=========================================================================*
* mcd_close *
*=========================================================================*/
PRIVATE int mcd_close(dp, m_ptr)
struct driver *dp; /* pointer to this drive */
message *m_ptr; /* CLOSE */
{
/* One less reference to the device */
mcd_open_ct--;
return OK;
}
/*=========================================================================*
* mcd_name *
*=========================================================================*/
PRIVATE char *mcd_name()
{
/* Return a name for the device */
return "cd0";
}
/*=========================================================================*
* mcd_ioctl *
*=========================================================================*/
PRIVATE int mcd_ioctl(dp, m_ptr)
struct driver *dp; /* pointer to the drive */
message *m_ptr; /* contains ioctl command */
{
/* Perform the ioctl request */
int status;
if (mcd_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);
mcd_get_status(1); /* Update the status flags */
if ((McdStatus & NO_DISK) && m_ptr->REQUEST != CDIOEJECT)
return EIO;
switch(m_ptr->REQUEST)
{
case CDIOPLAYMSS: status = ioctl_play_mss(m_ptr);break;
case CDIOPLAYTI: status = ioctl_play_ti(m_ptr);break;
case CDIOREADTOCHDR: status = ioctl_disk_info(m_ptr);break;
case CDIOREADTOC: status = ioctl_read_toc(m_ptr);break;
case CDIOREADSUBCH: status = ioctl_read_sub(m_ptr);break;
case CDIOSTOP: status = mcd_stop();break;
case CDIOPAUSE: status = mcd_pause();break;
case CDIORESUME: status = mcd_resume();break;
case CDIOEJECT: status = mcd_eject();break;
default: status = do_diocntl(dp, m_ptr);
}
return status;
}
/*=========================================================================*
* mcd_get_reply *
*=========================================================================*/
PRIVATE int mcd_get_reply(reply, delay)
u8_t *reply; /* variable to put reply in */
int delay; /* count to wait for the reply */
{
/* Get a reply from the drive */
if (mcd_ready(delay) != OK) return EIO; /* wait for drive to
become available */
*reply = in_byte(MCD_DATA_PORT); /* get the reply */
return OK;
}
/*=========================================================================*
* mcd_ready *
*=========================================================================*/
PRIVATE int mcd_ready(delay)
int delay; /* count to wait for drive to become available again */
{
/* Wait for drive to become available */
struct milli_state ms;
milli_start(&ms);
do
{
if (!(in_byte(MCD_FLAG_PORT) & MCD_BUSY)) return OK; /* OK, drive ready */
} while(milli_elapsed(&ms) < delay);
return EIO; /* Timeout */
}
/*=========================================================================*
* mcd_data_ready *
*=========================================================================*/
PRIVATE int mcd_data_ready(delay)
int delay; /* count to wait for the data */
{
/* Wait for the drive to get the data */
struct milli_state ms;
milli_start(&ms);
do
{
if (!(in_byte(MCD_FLAG_PORT) & 2)) return OK; /* OK, data is there */
} while(milli_elapsed(&ms) < delay);
return EIO; /* Timeout */
}
/*=========================================================================*
* mcd_get_status *
*=========================================================================*/
PRIVATE int mcd_get_status(f)
int f; /* flag */
{
/* Return status info from the drive and update the global McdStatus */
u8_t status;
/* If f = 1, we first send a get_status command, otherwise we just get
the status info from the drive */
if (f) out_byte(MCD_DATA_PORT, MCD_GET_STATUS); /* Try to get status */
if (mcd_get_reply(&status,REPLY_DELAY) != OK) return -1;
McdStatus &= ~(NO_DISK | DISK_CHANGED | DISK_ERROR);
/* Fill in the McdStatus variable */
if (status & MCD_DOOR_OPEN ||
!(status & MCD_DISK_IN)) McdStatus = NO_DISK;
else if (status & MCD_DISK_CHANGED) McdStatus = DISK_CHANGED;
else if (status & MCD_READ_ERROR ||
status & MCD_CMD_ERROR) McdStatus = DISK_ERROR;
else
{
if (status & MCD_AUDIO_DISK)
{
McdStatus |= AUDIO_DISK;
if (!(status & MCD_AUDIO_BUSY)) McdStatus &= ~(AUDIO_PLAYING);
else McdStatus |= AUDIO_PLAYING;
}
}
#if MCD_DEBUG >= 3
printf("mcd_get_status(%d) = %02x, McdStatus = %02x\n",
f, status, McdStatus);
#endif
return status; /* Return status */
}
/*=========================================================================*
* mcd_set_mode *
*=========================================================================*/
PRIVATE int mcd_set_mode(mode)
int mode; /* new drive mode */
{
/* Set drive mode */
int i;
for (i = 0; i < MCD_RETRIES; i++)
{
out_byte(MCD_DATA_PORT, MCD_SET_MODE); /* Send set mode command */
out_byte(MCD_DATA_PORT, mode); /* Send which mode */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -