📄 sonycd535.c
字号:
check_drive_status(void)
{
Byte status, e_status[2];
int CDD, ATN;
Byte cmd;
select_unit(0);
if (sony_audio_status == CDROM_AUDIO_PLAY) { /* check status */
outb(SONY535_REQUEST_AUDIO_STATUS, command_reg);
if (read_result_reg(&status) == 0) {
switch (status) {
case 0x0:
break; /* play in progress */
case 0x1:
break; /* paused */
case 0x3: /* audio play completed */
case 0x5: /* play not requested */
sony_audio_status = CDROM_AUDIO_COMPLETED;
read_subcode();
break;
case 0x4: /* error during play */
sony_audio_status = CDROM_AUDIO_ERROR;
break;
}
}
}
/* now check drive status */
outb(SONY535_REQUEST_DRIVE_STATUS_2, command_reg);
if (read_result_reg(&status) != 0)
return TIME_OUT;
#if DEBUG > 1
printk(CDU535_MESSAGE_NAME ": check_drive_status() got 0x%x\n", status);
#endif
if (status == 0)
return 0;
ATN = status & 0xf;
CDD = (status >> 4) & 0xf;
switch (ATN) {
case 0x0:
break; /* go on to CDD stuff */
case SONY535_ATN_BUSY:
if (initialized)
printk(CDU535_MESSAGE_NAME " error: drive busy\n");
return CD_BUSY;
case SONY535_ATN_EJECT_IN_PROGRESS:
printk(CDU535_MESSAGE_NAME " error: eject in progress\n");
sony_audio_status = CDROM_AUDIO_INVALID;
return CD_BUSY;
case SONY535_ATN_RESET_OCCURRED:
case SONY535_ATN_DISC_CHANGED:
case SONY535_ATN_RESET_AND_DISC_CHANGED:
#if DEBUG > 0
printk(CDU535_MESSAGE_NAME " notice: reset occurred or disc changed\n");
#endif
sony_disc_changed = 1;
sony_toc_read = 0;
sony_audio_status = CDROM_AUDIO_NO_STATUS;
sony_first_block = -1;
sony_last_block = -1;
if (initialized) {
cmd = SONY535_SPIN_UP;
do_sony_cmd(&cmd, 1, e_status, NULL, 0, 0);
sony_get_toc();
}
return 0;
default:
printk(CDU535_MESSAGE_NAME " error: drive busy (ATN=0x%x)\n", ATN);
return CD_BUSY;
}
switch (CDD) { /* the 531 docs are not helpful in decoding this */
case 0x0: /* just use the values from the DOS driver */
case 0x2:
case 0xa:
break; /* no error */
case 0xc:
printk(CDU535_MESSAGE_NAME
": check_drive_status(): CDD = 0xc! Not properly handled!\n");
return CD_BUSY; /* ? */
default:
return CD_BUSY;
}
return 0;
} /* check_drive_status() */
/*****************************************************************************
* int do_sony_cmd( Byte *cmd, int n_cmd, Byte status[2],
* Byte *response, int n_response, int ignore_status_bit7 )
*
* Generic routine for executing commands. The command and its parameters
* should be placed in the cmd[] array, number of bytes in the command is
* stored in nCmd. The response from the command will be stored in the
* response array. The number of bytes you expect back (excluding status)
* should be passed in n_response. Finally, some
* commands set bit 7 of the return status even when there is no second
* status byte, on these commands set ignoreStatusBit7 TRUE.
* If the command was sent and data received back, then we return 0,
* else we return TIME_OUT. You still have to check the status yourself.
* You should call check_drive_status() before calling this routine
* so that you do not lose notifications of disk changes, etc.
****************************************************************************/
static int
do_sony_cmd(Byte * cmd, int n_cmd, Byte status[2],
Byte * response, int n_response, int ignore_status_bit7)
{
int i;
/* write out the command */
for (i = 0; i < n_cmd; i++)
outb(cmd[i], command_reg);
/* read back the status */
if (read_result_reg(status) != 0)
return TIME_OUT;
if (!ignore_status_bit7 && ((status[0] & 0x80) != 0)) {
/* get second status byte */
if (read_result_reg(status + 1) != 0)
return TIME_OUT;
} else {
status[1] = 0;
}
#if DEBUG > 2
printk(CDU535_MESSAGE_NAME ": do_sony_cmd %x: %x %x\n",
*cmd, status[0], status[1]);
#endif
/* do not know about when I should read set of data and when not to */
if ((status[0] & ((ignore_status_bit7 ? 0x7f : 0xff) & 0x8f)) != 0)
return 0;
/* else, read in rest of data */
for (i = 0; 0 < n_response; n_response--, i++)
if (read_result_reg(response + i) != 0)
return TIME_OUT;
return 0;
} /* do_sony_cmd() */
/**************************************************************************
* int set_drive_mode( int mode, Byte status[2] )
*
* Set the drive mode to the specified value (mode=0 is audio, mode=e0
* is mode-1 CDROM
**************************************************************************/
static int
set_drive_mode(int mode, Byte status[2])
{
Byte cmd_buff[2];
Byte ret_buff[1];
cmd_buff[0] = SONY535_SET_DRIVE_MODE;
cmd_buff[1] = mode;
return do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1);
}
/***************************************************************************
* int seek_and_read_N_blocks( Byte params[], int n_blocks, Byte status[2],
* Byte *data_buff, int buff_size )
*
* Read n_blocks of data from the CDROM starting at position params[0:2],
* number of blocks in stored in params[3:5] -- both these are already
* int bcd format.
* Transfer the data into the buffer pointed at by data_buff. buff_size
* gives the number of bytes available in the buffer.
* The routine returns number of bytes read in if successful, otherwise
* it returns one of the standard error returns.
***************************************************************************/
static int
seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2],
Byte **buff, int buf_size)
{
Byte cmd_buff[7];
int i;
int read_status;
unsigned long snap;
Byte *data_buff;
int sector_count = 0;
if (buf_size < CDU535_BLOCK_SIZE * n_blocks)
return NO_ROOM;
set_drive_mode(SONY535_CDROM_DRIVE_MODE, status);
/* send command to read the data */
cmd_buff[0] = SONY535_SEEK_AND_READ_N_BLOCKS_1;
for (i = 0; i < 6; i++)
cmd_buff[i + 1] = params[i];
for (i = 0; i < 7; i++)
outb(cmd_buff[i], command_reg);
/* read back the data one block at a time */
while (0 < n_blocks--) {
/* wait for data to be ready */
int data_valid = 0;
snap = jiffies;
while (jiffies-snap < SONY_JIFFIES_TIMEOUT) {
read_status = inb(read_status_reg);
if ((read_status & SONY535_RESULT_NOT_READY_BIT) == 0) {
read_exec_status(status);
return BAD_STATUS;
}
if ((read_status & SONY535_DATA_NOT_READY_BIT) == 0) {
/* data is ready, read it */
data_buff = buff[sector_count++];
for (i = 0; i < CDU535_BLOCK_SIZE; i++)
*data_buff++ = inb(data_reg); /* unrolling this loop does not seem to help */
data_valid = 1;
break; /* exit the timeout loop */
}
sony_sleep(); /* data not ready, sleep a while */
}
if (!data_valid)
return TIME_OUT; /* if we reach this stage */
}
/* read all the data, now read the status */
if ((i = read_exec_status(status)) != 0)
return i;
return CDU535_BLOCK_SIZE * sector_count;
} /* seek_and_read_N_blocks() */
/****************************************************************************
* int request_toc_data( Byte status[2], struct s535_sony_toc *toc )
*
* Read in the table of contents data. Converts all the bcd data
* into integers in the toc structure.
****************************************************************************/
static int
request_toc_data(Byte status[2], struct s535_sony_toc *toc)
{
int to_status;
int i, j, n_tracks, track_no;
int first_track_num, last_track_num;
Byte cmd_no = 0xb2;
Byte track_address_buffer[5];
/* read the fixed portion of the table of contents */
if ((to_status = do_sony_cmd(&cmd_no, 1, status, (Byte *) toc, 15, 1)) != 0)
return to_status;
/* convert the data into integers so we can use them */
first_track_num = bcd_to_int(toc->first_track_num);
last_track_num = bcd_to_int(toc->last_track_num);
n_tracks = last_track_num - first_track_num + 1;
/* read each of the track address descriptors */
for (i = 0; i < n_tracks; i++) {
/* read the descriptor into a temporary buffer */
for (j = 0; j < 5; j++) {
if (read_result_reg(track_address_buffer + j) != 0)
return TIME_OUT;
if (j == 1) /* need to convert from bcd */
track_no = bcd_to_int(track_address_buffer[j]);
}
/* copy the descriptor to proper location - sonycd.c just fills */
memcpy(toc->tracks + i, track_address_buffer, 5);
}
return 0;
} /* request_toc_data() */
/***************************************************************************
* int spin_up_drive( Byte status[2] )
*
* Spin up the drive (unless it is already spinning).
***************************************************************************/
static int
spin_up_drive(Byte status[2])
{
Byte cmd;
/* first see if the drive is already spinning */
cmd = SONY535_REQUEST_DRIVE_STATUS_1;
if (do_sony_cmd(&cmd, 1, status, NULL, 0, 0) != 0)
return TIME_OUT;
if ((status[0] & SONY535_STATUS1_NOT_SPINNING) == 0)
return 0; /* it's already spinning */
/* otherwise, give the spin-up command */
cmd = SONY535_SPIN_UP;
return do_sony_cmd(&cmd, 1, status, NULL, 0, 0);
}
/*--------------------end of SONY CDU535 very specific ---------------------*/
/* Convert from an integer 0-99 to BCD */
static inline unsigned int
int_to_bcd(unsigned int val)
{
int retval;
retval = (val / 10) << 4;
retval = retval | val % 10;
return retval;
}
/* Convert from BCD to an integer from 0-99 */
static unsigned int
bcd_to_int(unsigned int bcd)
{
return (((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f);
}
/*
* Convert a logical sector value (like the OS would want to use for
* a block device) to an MSF format.
*/
static void
log_to_msf(unsigned int log, Byte *msf)
{
log = log + LOG_START_OFFSET;
msf[0] = int_to_bcd(log / 4500);
log = log % 4500;
msf[1] = int_to_bcd(log / 75);
msf[2] = int_to_bcd(log % 75);
}
/*
* Convert an MSF format to a logical sector.
*/
static unsigned int
msf_to_log(Byte *msf)
{
unsigned int log;
log = bcd_to_int(msf[2]);
log += bcd_to_int(msf[1]) * 75;
log += bcd_to_int(msf[0]) * 4500;
log = log - LOG_START_OFFSET;
return log;
}
/*
* Take in integer size value and put it into a buffer like
* the drive would want to see a number-of-sector value.
*/
static void
size_to_buf(unsigned int size, Byte *buf)
{
buf[0] = size / 65536;
size = size % 65536;
buf[1] = size / 256;
buf[2] = size % 256;
}
/*
* The OS calls this to perform a read or write operation to the drive.
* Write obviously fail. Reads to a read ahead of sony_buffer_size
* bytes to help speed operations. This especially helps since the OS
* may use 1024 byte blocks and the drive uses 2048 byte blocks. Since most
* data access on a CD is done sequentially, this saves a lot of operations.
*/
static void
do_cdu535_request(request_queue_t * q)
{
unsigned int dev;
unsigned int read_size;
int block;
int nsect;
int copyoff;
int spin_up_retry;
Byte params[10];
Byte status[2];
Byte cmd[2];
while (1) {
/*
* The beginning here is stolen from the hard disk driver. I hope
* it's right.
*/
if (QUEUE_EMPTY || CURRENT->rq_status == RQ_INACTIVE) {
return;
}
INIT_REQUEST;
dev = MINOR(CURRENT->rq_dev);
block = CURRENT->sector;
nsect = CURRENT->nr_sectors;
if (dev != 0) {
end_request(0);
continue;
}
switch (CURRENT->cmd) {
case READ:
/*
* If the block address is invalid or the request goes beyond the end of
* the media, return an error.
*/
if (sony_toc->lead_out_start_lba <= (block / 4)) {
end_request(0);
return;
}
if (sony_toc->lead_out_start_lba <= ((block + nsect) / 4)) {
end_request(0);
return;
}
while (0 < nsect) {
/*
* If the requested sector is not currently in the read-ahead buffer,
* it must be read in.
*/
if ((block < sony_first_block) || (sony_last_block < block)) {
sony_first_block = (block / 4) * 4;
log_to_msf(block / 4, params);
/*
* If the full read-ahead would go beyond the end of the media, trim
* it back to read just till the end of the media.
*/
if (sony_toc->lead_out_start_lba <= ((block / 4) + sony_buffer_sectors)) {
sony_last_block = (sony_toc->lead_out_start_lba * 4) - 1;
read_size = sony_toc->lead_out_start_lba - (block / 4);
} else {
sony_last_block = sony_first_block + (sony_buffer_sectors * 4) - 1;
read_size = sony_buffer_sectors;
}
size_to_buf(read_size, ¶ms[3]);
/*
* Read the data. If the drive was not spinning,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -