📄 cdu31a.c
字号:
res_reg[1] = SONY_BAD_DATA_ERR;
*res_size = 2;
}
abort_read();
} else {
#if DEBUG
printk("CDU31A timeout out %d\n", __LINE__);
#endif
res_reg[0] = 0x20;
res_reg[1] = SONY_TIMEOUT_OP_ERR;
*res_size = 2;
abort_read();
}
} else {
input_data(buffer, bytesleft, nblocks, offset, skip);
/* Wait for the status from the drive. */
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while (time_before(jiffies, retry_count)
&& !(is_result_ready())) {
while (handle_sony_cd_attention());
sony_sleep();
}
if (!is_result_ready()) {
#if DEBUG
printk("CDU31A timeout out %d\n", __LINE__);
#endif
res_reg[0] = 0x20;
res_reg[1] = SONY_TIMEOUT_OP_ERR;
*res_size = 2;
abort_read();
} else {
get_result(res_reg, res_size);
/* If we got a buffer status, handle that. */
if ((res_reg[0] & 0xf0) == 0x50) {
if ((res_reg[0] ==
SONY_NO_CIRC_ERR_BLK_STAT)
|| (res_reg[0] ==
SONY_NO_LECC_ERR_BLK_STAT)
|| (res_reg[0] ==
SONY_RECOV_LECC_ERR_BLK_STAT)) {
/* The data was successful, but if data was read from
the readahead and it was bad, set the whole
buffer as bad. */
if (readahead_bad) {
readahead_bad = 0;
res_reg[0] = 0x20;
res_reg[1] =
SONY_BAD_DATA_ERR;
*res_size = 2;
}
} else {
printk
("CDU31A: Data block error: 0x%x\n",
res_reg[0]);
res_reg[0] = 0x20;
res_reg[1] = SONY_BAD_DATA_ERR;
*res_size = 2;
/* Data is in the readahead buffer but an error was returned.
Make sure future requests don't use the data. */
if (bytesleft != 2048) {
readahead_bad = 1;
}
}
/* Final transfer is done for read command, get final result. */
if (sony_blocks_left == 0) {
get_result(res_reg, res_size);
}
} else if ((res_reg[0] & 0xf0) != 0x20) {
/* The drive gave me bad status, I don't know what to do.
Reset the driver and return an error. */
printk
("CDU31A: Invalid block status: 0x%x\n",
res_reg[0]);
restart_on_error();
res_reg[0] = 0x20;
res_reg[1] = SONY_BAD_DATA_ERR;
*res_size = 2;
}
}
}
#if DEBUG
printk("Leaving read_data_block at %d\n", __LINE__);
#endif
}
/*
* 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
* uses 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_cdu31a_request(request_queue_t * q)
{
int block;
int nblock;
unsigned char res_reg[12];
unsigned int res_size;
int num_retries;
unsigned long flags;
#if DEBUG
printk("Entering do_cdu31a_request\n");
#endif
/*
* Make sure no one else is using the driver; wait for them
* to finish if it is so.
*/
save_flags(flags);
cli();
while (sony_inuse) {
interruptible_sleep_on(&sony_wait);
if (signal_pending(current)) {
restore_flags(flags);
if (!QUEUE_EMPTY
&& CURRENT->rq_status != RQ_INACTIVE) {
end_request(0);
}
restore_flags(flags);
#if DEBUG
printk("Leaving do_cdu31a_request at %d\n",
__LINE__);
#endif
return;
}
}
sony_inuse = 1;
has_cd_task = current;
/* Get drive status before doing anything. */
while (handle_sony_cd_attention());
/* Make sure we have a valid TOC. */
sony_get_toc();
spin_unlock_irq(&io_request_lock);
/* Make sure the timer is cancelled. */
del_timer(&cdu31a_abort_timer);
while (1) {
cdu31a_request_startover:
/*
* The beginning here is stolen from the hard disk driver. I hope
* it's right.
*/
if (QUEUE_EMPTY || CURRENT->rq_status == RQ_INACTIVE) {
goto end_do_cdu31a_request;
}
if (!sony_spun_up) {
scd_spinup();
}
/* I don't use INIT_REQUEST because it calls return, which would
return without unlocking the device. It shouldn't matter,
but just to be safe... */
if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) {
panic(DEVICE_NAME ": request list destroyed");
}
if (CURRENT->bh) {
if (!buffer_locked(CURRENT->bh)) {
panic(DEVICE_NAME ": block not locked");
}
}
block = CURRENT->sector;
nblock = CURRENT->nr_sectors;
if (!sony_toc_read) {
printk("CDU31A: TOC not read\n");
end_request(0);
goto cdu31a_request_startover;
}
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 0
if ((block / 4) < sony_toc.start_track_lba) {
printk
("CDU31A: Request before beginning of media\n");
end_request(0);
goto cdu31a_request_startover;
}
#endif
if ((block / 4) >= sony_toc.lead_out_start_lba) {
printk
("CDU31A: Request past end of media\n");
end_request(0);
goto cdu31a_request_startover;
}
if (((block + nblock) / 4) >=
sony_toc.lead_out_start_lba) {
printk
("CDU31A: Request past end of media\n");
end_request(0);
goto cdu31a_request_startover;
}
num_retries = 0;
try_read_again:
while (handle_sony_cd_attention());
if (!sony_toc_read) {
printk("CDU31A: TOC not read\n");
end_request(0);
goto cdu31a_request_startover;
}
/* If no data is left to be read from the drive, start the
next request. */
if (sony_blocks_left == 0) {
if (start_request
(block / 4, CDU31A_READAHEAD / 4, 0)) {
end_request(0);
goto cdu31a_request_startover;
}
}
/* If the requested block is not the next one waiting in
the driver, abort the current operation and start a
new one. */
else if (block != sony_next_block) {
#if DEBUG
printk
("CDU31A Warning: Read for block %d, expected %d\n",
block, sony_next_block);
#endif
abort_read();
if (!sony_toc_read) {
printk("CDU31A: TOC not read\n");
end_request(0);
goto cdu31a_request_startover;
}
if (start_request
(block / 4, CDU31A_READAHEAD / 4, 0)) {
printk
("CDU31a: start request failed\n");
end_request(0);
goto cdu31a_request_startover;
}
}
read_data_block(CURRENT->buffer, block, nblock,
res_reg, &res_size);
if (res_reg[0] == 0x20) {
if (num_retries > MAX_CDU31A_RETRIES) {
end_request(0);
goto cdu31a_request_startover;
}
num_retries++;
if (res_reg[1] == SONY_NOT_SPIN_ERR) {
do_sony_cd_cmd(SONY_SPIN_UP_CMD,
NULL, 0, res_reg,
&res_size);
} else {
printk
("CDU31A: %s error for block %d, nblock %d\n",
translate_error(res_reg[1]),
block, nblock);
}
goto try_read_again;
} else {
end_request(1);
}
break;
case WRITE:
end_request(0);
break;
default:
panic("CDU31A: Unknown cmd");
}
}
end_do_cdu31a_request:
spin_lock_irq(&io_request_lock);
#if 0
/* After finished, cancel any pending operations. */
abort_read();
#else
/* Start a timer to time out after a while to disable
the read. */
cdu31a_abort_timer.expires = jiffies + 2 * HZ; /* Wait 2 seconds */
add_timer(&cdu31a_abort_timer);
#endif
has_cd_task = NULL;
sony_inuse = 0;
wake_up_interruptible(&sony_wait);
restore_flags(flags);
#if DEBUG
printk("Leaving do_cdu31a_request at %d\n", __LINE__);
#endif
}
/*
* Read the table of contents from the drive and set up TOC if
* successful.
*/
static void sony_get_toc(void)
{
unsigned char res_reg[2];
unsigned int res_size;
unsigned char parms[1];
int session;
int num_spin_ups;
int totaltracks = 0;
int mint = 99;
int maxt = 0;
#if DEBUG
printk("Entering sony_get_toc\n");
#endif
num_spin_ups = 0;
if (!sony_toc_read) {
respinup_on_gettoc:
/* Ignore the result, since it might error if spinning already. */
do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg,
&res_size);
do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg,
&res_size);
/* The drive sometimes returns error 0. I don't know why, but ignore
it. It seems to mean the drive has already done the operation. */
if ((res_size < 2)
|| ((res_reg[0] != 0) && (res_reg[1] != 0))) {
/* If the drive is already playing, it's ok. */
if ((res_reg[1] == SONY_AUDIO_PLAYING_ERR)
|| (res_reg[1] == 0)) {
goto gettoc_drive_spinning;
}
/* If the drive says it is not spun up (even though we just did it!)
then retry the operation at least a few times. */
if ((res_reg[1] == SONY_NOT_SPIN_ERR)
&& (num_spin_ups < MAX_CDU31A_RETRIES)) {
num_spin_ups++;
goto respinup_on_gettoc;
}
printk("cdu31a: Error reading TOC: %x %s\n",
res_reg[0], translate_error(res_reg[1]));
return;
}
gettoc_drive_spinning:
/* The idea here is we keep asking for sessions until the command
fails. Then we know what the last valid session on the disk is.
No need to check session 0, since session 0 is the same as session
1; the command returns different information if you give it 0.
*/
#if DEBUG
memset(&sony_toc, 0x0e, sizeof(sony_toc));
memset(&single_toc, 0x0f, sizeof(single_toc));
#endif
session = 1;
while (1) {
/* This seems to slow things down enough to make it work. This
* appears to be a problem in do_sony_cd_cmd. This printk seems
* to address the symptoms... -Erik */
#if 1
printk("cdu31a: Trying session %d\n", session);
#endif
parms[0] = session;
do_sony_cd_cmd(SONY_READ_TOC_SPEC_CMD,
parms, 1, res_reg, &res_size);
#if DEBUG
printk("%2.2x %2.2x\n", res_reg[0], res_reg[1]);
#endif
if ((res_size < 2)
|| ((res_reg[0] & 0xf0) == 0x20)) {
/* An error reading the TOC, this must be past the last session. */
if (session == 1)
printk
("Yikes! Couldn't read any sessions!");
break;
}
#if DEBUG
printk("Reading session %d\n", session);
#endif
parms[0] = session;
do_sony_cd_cmd(SONY_REQ_TOC_DATA_SPEC_CMD,
parms,
1,
(unsigned char *) &single_toc,
&res_size);
if ((res_size < 2)
|| ((single_toc.exec_status[0] & 0xf0) ==
0x20)) {
printk
("cdu31a: Error reading session %d: %x %s\n",
session, single_toc.exec_status[0],
translate_error(single_toc.
exec_status[1]));
/* An error reading the TOC. Return without sony_toc_read
set. */
return;
}
#if DEBUG
printk
("add0 %01x, con0 %01x, poi0 %02x, 1st trk %d, dsktyp %x, dum0 %x\n",
single_toc.address0, single_toc.control0,
single_toc.point0,
bcd_to_int(single_toc.first_track_num),
single_toc.disk_type, single_toc.dummy0);
printk
("add1 %01x, con1 %01x, poi1 %02x, lst trk %d, dummy1 %x, dum2 %x\n",
single_toc.address1, single_toc.control1,
single_toc.point1,
bcd_to_int(single_toc.last_track_num),
single_toc.dummy1, single_toc.dummy2);
printk
("add2 %01x, con2 %01x, poi2 %02x leadout start min %d, sec %d, frame %d\n",
single_toc.address2, single_toc.control2,
single_toc.point2,
bcd_to_int(single_toc.lead_out_start_msf[0]),
bcd_to_int(single_toc.lead_out_start_msf[1]),
bcd_to_int(single_toc.lead_out_start_msf[2]));
if (res_size > 18 && single_toc.pointb0 > 0xaf)
printk
("addb0 %01x, conb0 %01x, poib0 %02x, nextsession min %d, sec %d, frame %d\n"
"#mode5_ptrs %02d, max_start_outer_leadout_msf min %d, sec %d, frame %d\n",
single_toc.addressb0,
single_toc.controlb0,
single_toc.pointb0,
bcd_to_int(single_toc.
next_poss_prog_area_msf
[0]),
bcd_to_int(single_toc.
next_poss_prog_area_msf
[1]),
bcd_to_int(single_toc.
next_poss_prog_area_msf
[2]),
single_toc.num_mode_5_pointers,
bcd_to_int(single_toc.
max_start_outer_leadout_msf
[0]),
bcd_to_int(single_toc.
max_start_outer_leadout_msf
[1]),
bcd_to_int(single_toc.
max_start_outer_leadout_msf
[2]));
if (res_size > 27 && single_toc.pointb1 > 0xaf)
printk
("addb1 %01x, conb1 %01x, poib1 %02x, %x %x %x %x #skipint_ptrs %d, #skiptrkassign %d %x\n",
single_toc.addressb1,
single_toc.controlb1,
single_toc.pointb1,
single_toc.dummyb0_1[0],
single_toc.dummyb0_1[1],
single_toc.dummyb0_1[2],
single_toc.dummyb0_1[3],
single_toc.num_skip_interval_pointers,
single_toc.num_skip_track_assignments,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -