📄 optcd.c
字号:
S_READ, /* 2 */
S_DATA, /* 3 */
S_STOP, /* 4 */
S_STOPPING /* 5 */
};
static volatile enum state_e state = S_IDLE;
#if DEBUG_STATE
static volatile enum state_e state_old = S_STOP;
static volatile int flags_old = 0;
static volatile long state_n = 0;
#endif
/* Used as mutex to keep do_optcd_request (and other processes calling
ioctl) out while some process is inside a VFS call.
Reverse is accomplished by checking if state = S_IDLE upon entry
of opt_ioctl and opt_media_change. */
static int in_vfs = 0;
static volatile int transfer_is_active = 0;
static volatile int error = 0; /* %% do something with this?? */
static int tries; /* ibid?? */
static int timeout = 0;
static void poll(unsigned long data);
static struct timer_list req_timer = {function: poll};
static void poll(unsigned long data)
{
static volatile int read_count = 1;
int flags;
int loop_again = 1;
int status = 0;
int skip = 0;
if (error) {
printk(KERN_ERR "optcd: I/O error 0x%02x\n", error);
opt_invalidate_buffers();
if (!tries--) {
printk(KERN_ERR "optcd: read block %d failed;"
" Giving up\n", next_bn);
if (transfer_is_active)
loop_again = 0;
if (CURRENT_VALID)
end_request(0);
tries = 5;
}
error = 0;
state = S_STOP;
}
while (loop_again)
{
loop_again = 0; /* each case must flip this back to 1 if we want
to come back up here */
#if DEBUG_STATE
if (state == state_old)
state_n++;
else {
state_old = state;
if (++state_n > 1)
printk(KERN_DEBUG "optcd: %ld times "
"in previous state\n", state_n);
printk(KERN_DEBUG "optcd: state %d\n", state);
state_n = 0;
}
#endif
switch (state) {
case S_IDLE:
return;
case S_START:
if (in_vfs)
break;
if (send_cmd(COMDRVST)) {
state = S_IDLE;
while (CURRENT_VALID)
end_request(0);
return;
}
state = S_READ;
timeout = READ_TIMEOUT;
break;
case S_READ: {
struct cdrom_msf msf;
if (!skip) {
status = fetch_status();
if (status < 0)
break;
if (status & ST_DSK_CHG) {
toc_uptodate = 0;
opt_invalidate_buffers();
}
}
skip = 0;
if ((status & ST_DOOR_OPEN) || (status & ST_DRVERR)) {
toc_uptodate = 0;
opt_invalidate_buffers();
printk(KERN_WARNING "optcd: %s\n",
(status & ST_DOOR_OPEN)
? "door open"
: "disk removed");
state = S_IDLE;
while (CURRENT_VALID)
end_request(0);
return;
}
if (!CURRENT_VALID) {
state = S_STOP;
loop_again = 1;
break;
}
next_bn = CURRENT -> sector / 4;
lba2msf(next_bn, &msf);
read_count = N_BUFS;
msf.cdmsf_frame1 = read_count; /* Not BCD! */
DEBUG((DEBUG_REQUEST, "reading %x:%x.%x %x:%x.%x",
msf.cdmsf_min0,
msf.cdmsf_sec0,
msf.cdmsf_frame0,
msf.cdmsf_min1,
msf.cdmsf_sec1,
msf.cdmsf_frame1));
DEBUG((DEBUG_REQUEST, "next_bn:%d buf_in:%d"
" buf_out:%d buf_bn:%d",
next_bn,
buf_in,
buf_out,
buf_bn[buf_in]));
exec_read_cmd(COMREAD, &msf);
state = S_DATA;
timeout = READ_TIMEOUT;
break;
}
case S_DATA:
flags = stdt_flags() & (FL_STEN|FL_DTEN);
#if DEBUG_STATE
if (flags != flags_old) {
flags_old = flags;
printk(KERN_DEBUG "optcd: flags:%x\n", flags);
}
if (flags == FL_STEN)
printk(KERN_DEBUG "timeout cnt: %d\n", timeout);
#endif
switch (flags) {
case FL_DTEN: /* only STEN low */
if (!tries--) {
printk(KERN_ERR
"optcd: read block %d failed; "
"Giving up\n", next_bn);
if (transfer_is_active) {
tries = 0;
break;
}
if (CURRENT_VALID)
end_request(0);
tries = 5;
}
state = S_START;
timeout = READ_TIMEOUT;
loop_again = 1;
case (FL_STEN|FL_DTEN): /* both high */
break;
default: /* DTEN low */
tries = 5;
if (!CURRENT_VALID && buf_in == buf_out) {
state = S_STOP;
loop_again = 1;
break;
}
if (read_count<=0)
printk(KERN_WARNING
"optcd: warning - try to read"
" 0 frames\n");
while (read_count) {
buf_bn[buf_in] = NOBUF;
if (!flag_low(FL_DTEN, BUSY_TIMEOUT)) {
/* should be no waiting here!?? */
printk(KERN_ERR
"read_count:%d "
"CURRENT->nr_sectors:%ld "
"buf_in:%d\n",
read_count,
CURRENT->nr_sectors,
buf_in);
printk(KERN_ERR
"transfer active: %x\n",
transfer_is_active);
read_count = 0;
state = S_STOP;
loop_again = 1;
end_request(0);
break;
}
fetch_data(buf+
CD_FRAMESIZE*buf_in,
CD_FRAMESIZE);
read_count--;
DEBUG((DEBUG_REQUEST,
"S_DATA; ---I've read data- "
"read_count: %d",
read_count));
DEBUG((DEBUG_REQUEST,
"next_bn:%d buf_in:%d "
"buf_out:%d buf_bn:%d",
next_bn,
buf_in,
buf_out,
buf_bn[buf_in]));
buf_bn[buf_in] = next_bn++;
if (buf_out == NOBUF)
buf_out = buf_in;
buf_in = buf_in + 1 ==
N_BUFS ? 0 : buf_in + 1;
}
if (!transfer_is_active) {
while (CURRENT_VALID) {
transfer();
if (CURRENT -> nr_sectors == 0)
end_request(1);
else
break;
}
}
if (CURRENT_VALID
&& (CURRENT -> sector / 4 < next_bn ||
CURRENT -> sector / 4 >
next_bn + N_BUFS)) {
state = S_STOP;
loop_again = 1;
break;
}
timeout = READ_TIMEOUT;
if (read_count == 0) {
state = S_STOP;
loop_again = 1;
break;
}
}
break;
case S_STOP:
if (read_count != 0)
printk(KERN_ERR
"optcd: discard data=%x frames\n",
read_count);
flush_data();
if (send_cmd(COMDRVST)) {
state = S_IDLE;
while (CURRENT_VALID)
end_request(0);
return;
}
state = S_STOPPING;
timeout = STOP_TIMEOUT;
break;
case S_STOPPING:
status = fetch_status();
if (status < 0 && timeout)
break;
if ((status >= 0) && (status & ST_DSK_CHG)) {
toc_uptodate = 0;
opt_invalidate_buffers();
}
if (CURRENT_VALID) {
if (status >= 0) {
state = S_READ;
loop_again = 1;
skip = 1;
break;
} else {
state = S_START;
timeout = 1;
}
} else {
state = S_IDLE;
return;
}
break;
default:
printk(KERN_ERR "optcd: invalid state %d\n", state);
return;
} /* case */
} /* while */
if (!timeout--) {
printk(KERN_ERR "optcd: timeout in state %d\n", state);
state = S_STOP;
if (exec_cmd(COMSTOP) < 0) {
state = S_IDLE;
while (CURRENT_VALID)
end_request(0);
return;
}
}
mod_timer(&req_timer, jiffies + HZ/100);
}
static void do_optcd_request(request_queue_t * q)
{
DEBUG((DEBUG_REQUEST, "do_optcd_request(%ld+%ld)",
CURRENT -> sector, CURRENT -> nr_sectors));
if (disk_info.audio) {
printk(KERN_WARNING "optcd: tried to mount an Audio CD\n");
end_request(0);
return;
}
transfer_is_active = 1;
while (CURRENT_VALID) {
if (CURRENT->bh) {
if (!buffer_locked(CURRENT->bh))
panic(DEVICE_NAME ": block not locked");
}
transfer(); /* First try to transfer block from buffers */
if (CURRENT -> nr_sectors == 0) {
end_request(1);
} else { /* Want to read a block not in buffer */
buf_out = NOBUF;
if (state == S_IDLE) {
/* %% Should this block the request queue?? */
if (update_toc() < 0) {
while (CURRENT_VALID)
end_request(0);
break;
}
/* Start state machine */
state = S_START;
timeout = READ_TIMEOUT;
tries = 5;
/* %% why not start right away?? */
mod_timer(&req_timer, jiffies + HZ/100);
}
break;
}
}
transfer_is_active = 0;
DEBUG((DEBUG_REQUEST, "next_bn:%d buf_in:%d buf_out:%d buf_bn:%d",
next_bn, buf_in, buf_out, buf_bn[buf_in]));
DEBUG((DEBUG_REQUEST, "do_optcd_request ends"));
}
/* IOCTLs */
static char auto_eject = 0;
static int cdrompause(void)
{
int status;
if (audio_status != CDROM_AUDIO_PLAY)
return -EINVAL;
status = exec_cmd(COMPAUSEON);
if (status < 0) {
DEBUG((DEBUG_VFS, "exec_cmd COMPAUSEON: %02x", -status));
return -EIO;
}
audio_status = CDROM_AUDIO_PAUSED;
return 0;
}
static int cdromresume(void)
{
int status;
if (audio_status != CDROM_AUDIO_PAUSED)
return -EINVAL;
status = exec_cmd(COMPAUSEOFF);
if (status < 0) {
DEBUG((DEBUG_VFS, "exec_cmd COMPAUSEOFF: %02x", -status));
audio_status = CDROM_AUDIO_ERROR;
return -EIO;
}
audio_status = CDROM_AUDIO_PLAY;
return 0;
}
static int cdromplaymsf(unsigned long arg)
{
int status;
struct cdrom_msf msf;
status = verify_area(VERIFY_READ, (void *) arg, sizeof msf);
if (status)
return status;
copy_from_user(&msf, (void *) arg, sizeof msf);
bin2bcd(&msf);
status = exec_long_cmd(COMPLAY, &msf);
if (status < 0) {
DEBUG((DEBUG_VFS, "exec_long_cmd COMPLAY: %02x", -status));
audio_status = CDROM_AUDIO_ERROR;
return -EIO;
}
audio_status = CDROM_AUDIO_PLAY;
return 0;
}
static int cdromplaytrkind(unsigned long arg)
{
int status;
struct cdrom_ti ti;
struct cdrom_msf msf;
status = verify_area(VERIFY_READ, (void *) arg, sizeof ti);
if (status)
return status;
copy_from_user(&ti, (void *) arg, sizeof ti);
if (ti.cdti_trk0 < disk_info.first
|| ti.cdti_trk0 > disk_info.last
|| ti.cdti_trk1 < ti.cdti_trk0)
return -EINVAL;
if (ti.cdti_trk1 > disk_info.last)
ti.cdti_trk1 = disk_info.last;
msf.cdmsf_min0 = toc[ti.cdti_trk0].cdsc_absaddr.msf.minute;
msf.cdmsf_sec0 = toc[ti.cdti_trk0].cdsc_absaddr.msf.second;
msf.cdmsf_frame0 = toc[ti.cdti_trk0].cdsc_absaddr.msf.frame;
msf.cdmsf_min1 = toc[ti.cdti_trk1 + 1].cdsc_absaddr.msf.minute;
msf.cdmsf_sec1 = toc[ti.cdti_trk1 + 1].cdsc_absaddr.msf.second;
msf.cdmsf_frame1 = toc[ti.cdti_trk1 + 1].cdsc_absaddr.msf.frame;
DEBUG((DEBUG_VFS, "play %02d:%02d.%02d to %02d:%02d.%02d",
msf.cdmsf_min0,
msf.cdmsf_sec0,
msf.cdmsf_frame0,
msf.cdmsf_min1,
msf.cdmsf_sec1,
msf.cdmsf_frame1));
bin2bcd(&msf);
status = exec_long_cmd(COMPLAY, &msf);
if (status < 0) {
DEBUG((DEBUG_VFS, "exec_long_cmd COMPLAY: %02x", -status));
audio_status = CDROM_AUDIO_ERROR;
return -EIO;
}
audio_status = CDROM_AUDIO_PLAY;
return 0;
}
static int cdromreadtochdr(unsigned long arg)
{
int status;
struct cdrom_tochdr tochdr;
status = verify_area(VERIFY_WRITE, (void *) arg, sizeof tochdr);
if (status)
return status;
tochdr.cdth_trk0 = disk_info.first;
tochdr.cdth_trk1 = disk_info.last;
copy_to_user((void *) arg, &tochdr, sizeof tochdr);
return 0;
}
static int cdromreadtocentry(unsigned long arg)
{
int status;
struct cdrom_tocentry entry;
struct cdrom_subchnl *tocptr;
status = verify_area(VERIFY_WRITE, (void *) arg, sizeof entry);
if (status)
return status;
copy_from_user(&entry, (void *) arg, sizeof entry);
if (entry.cdte_track == CDROM_LEADOUT)
tocptr = &toc[disk_info.last + 1];
else if (entry.cdte_track > disk_info.last
|| entry.cdte_track < disk_info.first)
return -EINVAL;
else
tocptr = &toc[entry.cdte_track];
entry.cdte_adr = tocptr->cdsc_adr;
entry.cdte_ctrl = tocptr->cdsc_ctrl;
entry.cdte_addr.msf.minute = tocptr->cdsc_absaddr.msf.minute;
entry.cdte_addr.msf.second = tocptr->cdsc_absaddr.msf.second;
entry.cdte_addr.msf.frame = tocptr->cdsc_absaddr.msf.frame;
/* %% What should go into entry.cdte_datamode? */
if (entry.cdte_format == CDROM_LBA)
msf2lba(&entry.cdte_addr);
else if (entry.cdte_format != CDROM_MSF)
return -EINVAL;
copy_to_user((void *) arg, &entry, sizeof entry);
return 0;
}
static int cdromvolctrl(unsigned long arg)
{
int status;
struct cdrom_volctrl volctrl;
struct cdrom_msf msf;
status = verify_area(VERIFY_READ, (void *) arg, sizeof volctrl);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -