📄 cdu31a.c
字号:
write_params(params, num_params);
write_cmd(cmd);
get_result(result_buffer, result_size);
}
if (((result_buffer[0] & 0xf0) == 0x20)
&& (num_retries < MAX_CDU31A_RETRIES)) {
num_retries++;
current->state = TASK_INTERRUPTIBLE;
schedule_timeout(HZ / 10); /* Wait .1 seconds on retries */
goto retry_cd_operation;
}
if (!recursive_call) {
has_cd_task = NULL;
sony_inuse = 0;
wake_up_interruptible(&sony_wait);
}
restore_flags(flags);
}
/*
* 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;
volatile int val;
#if 0*DEBUG
printk("Entering handle_sony_cd_attention\n");
#endif
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;
#if DEBUG
printk("Leaving handle_sony_cd_attention at %d\n",
__LINE__);
#endif
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:
disk_changed = 1;
sony_toc_read = 0;
sony_audio_status = CDROM_AUDIO_NO_STATUS;
sony_blocks_left = 0;
break;
case SONY_SPIN_DOWN_COMPLETE_ATTN:
/* Mark the disk as spun down. */
sony_spun_up = 0;
break;
case SONY_AUDIO_PLAY_DONE_ATTN:
sony_audio_status = CDROM_AUDIO_COMPLETED;
read_subcode();
break;
case SONY_EJECT_PUSHED_ATTN:
if (is_auto_eject) {
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++;
#if DEBUG
printk("Leaving handle_sony_cd_attention at %d\n",
__LINE__);
#endif
return (1);
} else if (abort_read_started) {
while (is_result_reg_not_empty()) {
val = read_result_register();
}
clear_data_ready();
clear_result_ready();
/* Clear out the data */
while (is_data_requested()) {
val = read_data_register();
}
abort_read_started = 0;
#if DEBUG
printk("Leaving handle_sony_cd_attention at %d\n",
__LINE__);
#endif
return (1);
}
num_consecutive_attentions = 0;
#if 0*DEBUG
printk("Leaving handle_sony_cd_attention at %d\n", __LINE__);
#endif
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)
{
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(unsigned char *msf)
{
unsigned int log;
log = msf[2];
log += msf[1] * 75;
log += 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, unsigned char *buf)
{
buf[0] = size / 65536;
size = size % 65536;
buf[1] = size / 256;
buf[2] = size % 256;
}
/* Starts a read operation. Returns 0 on success and 1 on failure.
The read operation used here allows multiple sequential sectors
to be read and status returned for each sector. The driver will
read the output one at a time as the requests come and abort the
operation if the requested sector is not the next one from the
drive. */
static int
start_request(unsigned int sector, unsigned int nsect, int read_nsect_only)
{
unsigned char params[6];
unsigned int read_size;
unsigned int retry_count;
#if DEBUG
printk("Entering start_request\n");
#endif
log_to_msf(sector, params);
/* If requested, read exactly what was asked. */
if (read_nsect_only) {
read_size = nsect;
}
/*
* 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.
*/
else if ((sector + nsect) >= sony_toc.lead_out_start_lba) {
read_size = sony_toc.lead_out_start_lba - sector;
}
/* Read the full readahead amount. */
else {
read_size = CDU31A_READAHEAD / 4;
}
size_to_buf(read_size, ¶ms[3]);
/*
* 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 (time_before(jiffies, retry_count) && (is_busy())) {
sony_sleep();
while (handle_sony_cd_attention());
}
if (is_busy()) {
printk("CDU31A: Timeout while waiting to issue command\n");
#if DEBUG
printk("Leaving start_request at %d\n", __LINE__);
#endif
return (1);
} else {
/* Issue the command */
clear_result_ready();
clear_param_reg();
write_params(params, 6);
write_cmd(SONY_READ_BLKERR_STAT_CMD);
sony_blocks_left = read_size * 4;
sony_next_block = sector * 4;
readahead_dataleft = 0;
readahead_bad = 0;
#if DEBUG
printk("Leaving start_request at %d\n", __LINE__);
#endif
return (0);
}
#if DEBUG
printk("Leaving start_request at %d\n", __LINE__);
#endif
}
/* Abort a pending read operation. Clear all the drive status and
readahead variables. */
static void abort_read(void)
{
unsigned char result_reg[2];
int result_size;
volatile int val;
do_sony_cd_cmd(SONY_ABORT_CMD, NULL, 0, result_reg, &result_size);
if ((result_reg[0] & 0xf0) == 0x20) {
printk("CDU31A: Error aborting read, %s error\n",
translate_error(result_reg[1]));
}
while (is_result_reg_not_empty()) {
val = read_result_register();
}
clear_data_ready();
clear_result_ready();
/* Clear out the data */
while (is_data_requested()) {
val = read_data_register();
}
sony_blocks_left = 0;
readahead_dataleft = 0;
readahead_bad = 0;
}
/* Called when the timer times out. This will abort the
pending read operation. */
static void handle_abort_timeout(unsigned long data)
{
unsigned long flags;
#if DEBUG
printk("Entering handle_abort_timeout\n");
#endif
save_flags(flags);
cli();
/* If it is in use, ignore it. */
if (!sony_inuse) {
/* We can't use abort_read(), because it will sleep
or schedule in the timer interrupt. Just start
the operation, finish it on the next access to
the drive. */
clear_result_ready();
clear_param_reg();
write_cmd(SONY_ABORT_CMD);
sony_blocks_left = 0;
readahead_dataleft = 0;
readahead_bad = 0;
abort_read_started = 1;
}
restore_flags(flags);
#if DEBUG
printk("Leaving handle_abort_timeout\n");
#endif
}
/* Actually get data and status from the drive. */
static void
input_data(char *buffer,
unsigned int bytesleft,
unsigned int nblocks, unsigned int offset, unsigned int skip)
{
int i;
volatile unsigned char val;
#if DEBUG
printk("Entering input_data\n");
#endif
/* If an XA disk on a CDU31A, skip the first 12 bytes of data from
the disk. The real data is after that. */
if (sony_xa_mode) {
for (i = 0; i < CD_XA_HEAD; i++) {
val = read_data_register();
}
}
clear_data_ready();
if (bytesleft == 2048) { /* 2048 byte direct buffer transfer */
insb(sony_cd_read_reg, buffer, 2048);
readahead_dataleft = 0;
} else {
/* If the input read did not align with the beginning of the block,
skip the necessary bytes. */
if (skip != 0) {
insb(sony_cd_read_reg, readahead_buffer, skip);
}
/* Get the data into the buffer. */
insb(sony_cd_read_reg, &buffer[offset], bytesleft);
/* Get the rest of the data into the readahead buffer at the
proper location. */
readahead_dataleft = (2048 - skip) - bytesleft;
insb(sony_cd_read_reg,
readahead_buffer + bytesleft, readahead_dataleft);
}
sony_blocks_left -= nblocks;
sony_next_block += nblocks;
/* If an XA disk, we have to clear out the rest of the unused
error correction data. */
if (sony_xa_mode) {
for (i = 0; i < CD_XA_TAIL; i++) {
val = read_data_register();
}
}
#if DEBUG
printk("Leaving input_data at %d\n", __LINE__);
#endif
}
/* read data from the drive. Note the nsect must be <= 4. */
static void
read_data_block(char *buffer,
unsigned int block,
unsigned int nblocks,
unsigned char res_reg[], int *res_size)
{
unsigned int retry_count;
unsigned int bytesleft;
unsigned int offset;
unsigned int skip;
#if DEBUG
printk("Entering read_data_block\n");
#endif
res_reg[0] = 0;
res_reg[1] = 0;
*res_size = 0;
bytesleft = nblocks * 512;
offset = 0;
/* If the data in the read-ahead does not match the block offset,
then fix things up. */
if (((block % 4) * 512) != ((2048 - readahead_dataleft) % 2048)) {
sony_next_block += block % 4;
sony_blocks_left -= block % 4;
skip = (block % 4) * 512;
} else {
skip = 0;
}
/* We have readahead data in the buffer, get that first before we
decide if a read is necessary. */
if (readahead_dataleft != 0) {
if (bytesleft > readahead_dataleft) {
/* The readahead will not fill the requested buffer, but
get the data out of the readahead into the buffer. */
memcpy(buffer,
readahead_buffer + (2048 -
readahead_dataleft),
readahead_dataleft);
readahead_dataleft = 0;
bytesleft -= readahead_dataleft;
offset += readahead_dataleft;
} else {
/* The readahead will fill the whole buffer, get the data
and return. */
memcpy(buffer,
readahead_buffer + (2048 -
readahead_dataleft),
bytesleft);
readahead_dataleft -= bytesleft;
bytesleft = 0;
sony_blocks_left -= nblocks;
sony_next_block += nblocks;
/* If the data in the readahead is bad, return an error so the
driver will abort the buffer. */
if (readahead_bad) {
res_reg[0] = 0x20;
res_reg[1] = SONY_BAD_DATA_ERR;
*res_size = 2;
}
if (readahead_dataleft == 0) {
readahead_bad = 0;
}
/* Final transfer is done for read command, get final result. */
if (sony_blocks_left == 0) {
get_result(res_reg, res_size);
}
#if DEBUG
printk("Leaving read_data_block at %d\n",
__LINE__);
#endif
return;
}
}
/* Wait for the drive to tell us we have something */
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while (time_before(jiffies, retry_count) && !(is_data_ready())) {
while (handle_sony_cd_attention());
sony_sleep();
}
if (!(is_data_ready())) {
if (is_result_ready()) {
get_result(res_reg, res_size);
if ((res_reg[0] & 0xf0) != 0x20) {
printk
("CDU31A: Got result that should have been error: %d\n",
res_reg[0]);
res_reg[0] = 0x20;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -