📄 cdu31a.c
字号:
* how many more status bytes are coming.
*
* The result register can be read 10 bytes at a time, a wait for
* result ready to be asserted must be done between every 10 bytes.
*/
if ((a & 0xf0) != 0x20)
{
if (b > 8)
{
for (i=0; i<8; i++)
{
*result_buffer = read_result_register();
result_buffer++;
(*result_size)++;
}
b = b - 8;
while (b > 10)
{
retry_count = SONY_READY_RETRIES;
while ((retry_count > 0) && (!is_result_ready()))
{
retry_count--;
}
if (!is_result_ready())
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
return;
}
clear_result_ready();
for (i=0; i<10; i++)
{
*result_buffer = read_result_register();
result_buffer++;
(*result_size)++;
}
b = b - 10;
}
if (b > 0)
{
retry_count = SONY_READY_RETRIES;
while ((retry_count > 0) && (!is_result_ready()))
{
retry_count--;
}
if (!is_result_ready())
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
return;
}
}
}
while (b > 0)
{
*result_buffer = read_result_register();
result_buffer++;
(*result_size)++;
b--;
}
}
}
/*
* Read in a 2048 byte block of data.
*/
static void
read_data_block(unsigned char *data,
unsigned char *result_buffer,
unsigned int *result_size)
{
int i;
unsigned int retry_count;
for (i=0; i<2048; i++)
{
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while ((retry_count > jiffies) && (!is_data_requested()))
{
while (handle_sony_cd_attention())
;
sony_sleep();
}
if (!is_data_requested())
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
return;
}
*data = read_data_register();
data++;
}
}
/*
* This routine issues a read data command and gets the data. I don't
* really like the way this is done (I would prefer for do_sony_cmd() to
* handle it automatically) but I found that the drive returns status
* when it finishes reading (not when the host has read all the data)
* or after it gets an error. This means that the status can be
* received at any time and should be handled immediately (at least
* between every 2048 byte block) to check for errors, we can't wait
* until all the data is read.
*
* This routine returns the total number of sectors read. It will
* not return an error if it reads at least one sector successfully.
*/
static unsigned int
get_data(unsigned char *orig_data,
unsigned char *params, /* 6 bytes with the MSF start address
and number of sectors to read. */
unsigned int orig_data_size,
unsigned char *result_buffer,
unsigned int *result_size)
{
unsigned int cur_offset;
unsigned int retry_count;
int result_read;
int num_retries;
unsigned int num_sectors_read = 0;
unsigned char *data = orig_data;
unsigned int data_size = orig_data_size;
cli();
while (sony_inuse)
{
interruptible_sleep_on(&sony_wait);
if (current->signal & ~current->blocked)
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_SIGNAL_OP_ERR;
*result_size = 2;
return 0;
}
}
sony_inuse = 1;
has_cd_task = current;
sti();
num_retries = 0;
retry_data_operation:
result_buffer[0] = 0;
result_buffer[1] = 0;
/*
* Clear any outstanding attentions and wait for the drive to
* complete any pending operations.
*/
while (handle_sony_cd_attention())
;
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while ((retry_count > jiffies) && (is_busy()))
{
sony_sleep();
while (handle_sony_cd_attention())
;
}
if (is_busy())
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
}
else
{
/* Issue the command */
clear_result_ready();
clear_param_reg();
write_params(params, 6);
write_cmd(SONY_READ_CMD);
/*
* Read the data from the drive one 2048 byte sector at a time. Handle
* any results received between sectors, if an error result is returned
* terminate the operation immediately.
*/
cur_offset = 0;
result_read = 0;
while ((data_size > 0) && (result_buffer[0] == 0))
{
/* Wait for the drive to tell us we have something */
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while ((retry_count > jiffies) && (!(is_result_ready() || is_data_ready())))
{
while (handle_sony_cd_attention())
;
sony_sleep();
}
if (!(is_result_ready() || is_data_ready()))
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
}
/* Handle results first */
else if (is_result_ready())
{
result_read = 1;
get_result(result_buffer, result_size);
}
else /* Handle data next */
{
/*
* The drive has to be polled for status on a byte-by-byte basis
* to know if the data is ready. Yuck. I really wish I could use DMA.
*/
clear_data_ready();
read_data_block(data, result_buffer, result_size);
data += 2048;
data_size -= 2048;
cur_offset = cur_offset + 2048;
num_sectors_read++;
}
}
/* Make sure the result has been read */
if (!result_read)
{
get_result(result_buffer, result_size);
}
}
if ( ((result_buffer[0] & 0x20) == 0x20)
&& (result_buffer[1] != SONY_NOT_SPIN_ERR) /* No retry when not spin */
&& (num_retries < MAX_CDU31A_RETRIES))
{
/*
* If an error occurs, go back and only read one sector at the
* given location. Hopefully the error occurred on an unused
* sector after the first one. It is hard to say which sector
* the error occurred on because the drive returns status before
* the data transfer is finished and doesn't say which sector.
*/
data_size = 2048;
data = orig_data;
num_sectors_read = 0;
size_to_buf(1, ¶ms[3]);
num_retries++;
/* Issue a reset on an error (the second time), othersize just delay */
if (num_retries == 2)
{
restart_on_error();
}
else
{
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + 10;
schedule();
}
/* Restart the operation. */
goto retry_data_operation;
}
has_cd_task = NULL;
sony_inuse = 0;
wake_up_interruptible(&sony_wait);
return(num_sectors_read);
}
/*
* Do a command that does not involve data transfer. This routine must
* be re-entrant from the same task to support being called from the
* data operation code when an error occurs.
*/
static void
do_sony_cd_cmd(unsigned char cmd,
unsigned char *params,
unsigned int num_params,
unsigned char *result_buffer,
unsigned int *result_size)
{
unsigned int retry_count;
int num_retries;
int recursive_call;
cli();
if (current != has_cd_task) /* Allow recursive calls to this routine */
{
while (sony_inuse)
{
interruptible_sleep_on(&sony_wait);
if (current->signal & ~current->blocked)
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_SIGNAL_OP_ERR;
*result_size = 2;
return;
}
}
sony_inuse = 1;
has_cd_task = current;
recursive_call = 0;
}
else
{
recursive_call = 1;
}
sti();
num_retries = 0;
retry_cd_operation:
while (handle_sony_cd_attention())
;
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while ((retry_count > jiffies) && (is_busy()))
{
sony_sleep();
while (handle_sony_cd_attention())
;
}
if (is_busy())
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
}
else
{
clear_result_ready();
clear_param_reg();
write_params(params, num_params);
write_cmd(cmd);
get_result(result_buffer, result_size);
}
if ( ((result_buffer[0] & 0x20) == 0x20)
&& (num_retries < MAX_CDU31A_RETRIES))
{
num_retries++;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + 10; /* Wait .1 seconds on retries */
schedule();
goto retry_cd_operation;
}
if (!recursive_call)
{
has_cd_task = NULL;
sony_inuse = 0;
wake_up_interruptible(&sony_wait);
}
}
/*
* Handle an attention from the drive. This will return 1 if it found one
* or 0 if not (if one is found, the caller might want to call again).
*
* This routine counts the number of consecutive times it is called
* (since this is always called from a while loop until it returns
* a 0), and returns a 0 if it happens too many times. This will help
* prevent a lockup.
*/
static int
handle_sony_cd_attention(void)
{
unsigned char atten_code;
static int num_consecutive_attentions = 0;
if (is_attention())
{
if (num_consecutive_attentions > CDU31A_MAX_CONSECUTIVE_ATTENTIONS)
{
printk("cdu31a: Too many consecutive attentions: %d\n",
num_consecutive_attentions);
num_consecutive_attentions = 0;
return(0);
}
clear_attention();
atten_code = read_result_register();
switch (atten_code)
{
/* Someone changed the CD. Mark it as changed */
case SONY_MECH_LOADED_ATTN:
sony_disc_changed = 1;
sony_toc_read = 0;
sony_audio_status = CDROM_AUDIO_NO_STATUS;
sony_first_block = -1;
sony_last_block = -1;
break;
case SONY_AUDIO_PLAY_DONE_ATTN:
sony_audio_status = CDROM_AUDIO_COMPLETED;
read_subcode();
break;
case SONY_EJECT_PUSHED_ATTN:
sony_audio_status = CDROM_AUDIO_INVALID;
break;
case SONY_LEAD_IN_ERR_ATTN:
case SONY_LEAD_OUT_ERR_ATTN:
case SONY_DATA_TRACK_ERR_ATTN:
case SONY_AUDIO_PLAYBACK_ERR_ATTN:
sony_audio_status = CDROM_AUDIO_ERROR;
break;
}
num_consecutive_attentions++;
return(1);
}
num_consecutive_attentions = 0;
return(0);
}
/* 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, unsigned char *msf)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -