📄 cm206.c
字号:
stop_read();
}
cd->sector_last = -1; /* Make our internal buffer invalid */
FIRST_TRACK = 0; /* No valid disc status */
}
--cd->openfiles;
}
/* Empty buffer empties $sectors$ sectors of the adapter card buffer,
* and then reads a sector in kernel memory. */
void empty_buffer(int sectors)
{
while (sectors >= 0) {
transport_data(r_fifo_output_buffer,
cd->sector + cd->fifo_overflowed,
RAW_SECTOR_SIZE / 2 - cd->fifo_overflowed);
--sectors;
++cd->adapter_first; /* update the current adapter sector */
cd->fifo_overflowed = 0; /* reset overflow bit */
stats(sector_transferred);
}
cd->sector_first = cd->adapter_first - 1;
cd->sector_last = cd->adapter_first; /* update the buffer sector */
}
/* try_adapter. This function determines if the requested sector is
in adapter memory, or will appear there soon. Returns 0 upon
success */
int try_adapter(int sector)
{
if (cd->adapter_first <= sector && sector < cd->adapter_last) {
/* sector is in adapter memory */
empty_buffer(sector - cd->adapter_first);
return 0;
} else if (cd->background == 1 && cd->adapter_first <= sector
&& sector < cd->adapter_first + cd->max_sectors) {
/* a read is going on, we can wait for it */
cd->wait_back = 1;
while (sector >= cd->adapter_last) {
if (sleep_or_timeout(&cd->data, DATA_TIMEOUT)) {
debug(("Timed out during background wait: %d %d %d %d\n", sector, cd->adapter_last, cd->adapter_first, cd->background));
stats(back_read_timeout);
cd->wait_back = 0;
return -1;
}
}
cd->wait_back = 0;
empty_buffer(sector - cd->adapter_first);
return 0;
} else
return -2;
}
/* This is not a very smart implementation. We could optimize for
consecutive block numbers. I'm not convinced this would really
bring down the processor load. */
static void do_cm206_request(request_queue_t * q)
{
long int i, cd_sec_no;
int quarter, error;
uch *source, *dest;
while (1) { /* repeat until all requests have been satisfied */
INIT_REQUEST;
if (QUEUE_EMPTY || CURRENT->rq_status == RQ_INACTIVE)
return;
if (CURRENT->cmd != READ) {
debug(("Non-read command %d on cdrom\n",
CURRENT->cmd));
end_request(0);
continue;
}
spin_unlock_irq(&io_request_lock);
error = 0;
for (i = 0; i < CURRENT->nr_sectors; i++) {
int e1, e2;
cd_sec_no = (CURRENT->sector + i) / BLOCKS_ISO; /* 4 times 512 bytes */
quarter = (CURRENT->sector + i) % BLOCKS_ISO;
dest = CURRENT->buffer + i * LINUX_BLOCK_SIZE;
/* is already in buffer memory? */
if (cd->sector_first <= cd_sec_no
&& cd_sec_no < cd->sector_last) {
source =
((uch *) cd->sector) + 16 +
quarter * LINUX_BLOCK_SIZE +
(cd_sec_no -
cd->sector_first) * RAW_SECTOR_SIZE;
memcpy(dest, source, LINUX_BLOCK_SIZE);
} else if (!(e1 = try_adapter(cd_sec_no)) ||
!(e2 = read_sector(cd_sec_no))) {
source =
((uch *) cd->sector) + 16 +
quarter * LINUX_BLOCK_SIZE;
memcpy(dest, source, LINUX_BLOCK_SIZE);
} else {
error = 1;
debug(("cm206_request: %d %d\n", e1, e2));
}
}
spin_lock_irq(&io_request_lock);
end_request(!error);
}
}
/* Audio support. I've tried very hard, but the cm206 drive doesn't
seem to have a get_toc (table-of-contents) function, while i'm
pretty sure it must read the toc upon disc insertion. Therefore
this function has been implemented through a binary search
strategy. All track starts that happen to be found are stored in
cd->toc[], for future use.
I've spent a whole day on a bug that only shows under Workman---
I don't get it. Tried everything, nothing works. If workman asks
for track# 0xaa, it'll get the wrong time back. Any other program
receives the correct value. I'm stymied.
*/
/* seek seeks to address lba. It does wait to arrive there. */
void seek(int lba)
{
int i;
uch seek_command[4] = { c_seek, };
fsm(lba, &seek_command[1]);
for (i = 0; i < 4; i++)
type_0_command(seek_command[i], 0);
cd->dsb = wait_dsb();
}
uch bcdbin(unsigned char bcd)
{ /* stolen from mcd.c! */
return (bcd >> 4) * 10 + (bcd & 0xf);
}
inline uch normalize_track(uch track)
{
if (track < 1)
return 1;
if (track > LAST_TRACK)
return LAST_TRACK + 1;
return track;
}
/* This function does a binary search for track start. It records all
* tracks seen in the process. Input $track$ must be between 1 and
* #-of-tracks+1. Note that the start of the disc must be in toc[1].fsm.
*/
int get_toc_lba(uch track)
{
int max = 74 * 60 * 75 - 150, min = fsm2lba(cd->toc[1].fsm);
int i, lba, l, old_lba = 0;
uch *q = cd->q;
uch ct; /* current track */
int binary = 0;
const int skip = 3 * 60 * 75; /* 3 minutes */
for (i = track; i > 0; i--)
if (cd->toc[i].track) {
min = fsm2lba(cd->toc[i].fsm);
break;
}
lba = min + skip;
do {
seek(lba);
type_1_command(c_read_current_q, 10, q);
ct = normalize_track(q[1]);
if (!cd->toc[ct].track) {
l = q[9] - bcdbin(q[5]) + 75 * (q[8] -
bcdbin(q[4]) - 2 +
60 * (q[7] -
bcdbin(q
[3])));
cd->toc[ct].track = q[1]; /* lead out still 0xaa */
fsm(l, cd->toc[ct].fsm);
cd->toc[ct].q0 = q[0]; /* contains adr and ctrl info */
if (ct == track)
return l;
}
old_lba = lba;
if (binary) {
if (ct < track)
min = lba;
else
max = lba;
lba = (min + max) / 2;
} else {
if (ct < track)
lba += skip;
else {
binary = 1;
max = lba;
min = lba - skip;
lba = (min + max) / 2;
}
}
} while (lba != old_lba);
return lba;
}
void update_toc_entry(uch track)
{
track = normalize_track(track);
if (!cd->toc[track].track)
get_toc_lba(track);
}
/* return 0 upon success */
int read_toc_header(struct cdrom_tochdr *hp)
{
if (!FIRST_TRACK)
get_disc_status();
if (hp) {
int i;
hp->cdth_trk0 = FIRST_TRACK;
hp->cdth_trk1 = LAST_TRACK;
/* fill in first track position */
for (i = 0; i < 3; i++)
cd->toc[1].fsm[i] = cd->disc_status[3 + i];
update_toc_entry(LAST_TRACK + 1); /* find most entries */
return 0;
}
return -1;
}
void play_from_to_msf(struct cdrom_msf *msfp)
{
uch play_command[] = { c_play,
msfp->cdmsf_frame0, msfp->cdmsf_sec0, msfp->cdmsf_min0,
msfp->cdmsf_frame1, msfp->cdmsf_sec1, msfp->cdmsf_min1, 2,
2
};
int i;
for (i = 0; i < 9; i++)
type_0_command(play_command[i], 0);
for (i = 0; i < 3; i++)
PLAY_TO.fsm[i] = play_command[i + 4];
PLAY_TO.track = 0; /* say no track end */
cd->dsb = wait_dsb();
}
void play_from_to_track(int from, int to)
{
uch play_command[8] = { c_play, };
int i;
if (from == 0) { /* continue paused play */
for (i = 0; i < 3; i++) {
play_command[i + 1] = cd->audio_status[i + 2];
play_command[i + 4] = PLAY_TO.fsm[i];
}
} else {
update_toc_entry(from);
update_toc_entry(to + 1);
for (i = 0; i < 3; i++) {
play_command[i + 1] = cd->toc[from].fsm[i];
PLAY_TO.fsm[i] = play_command[i + 4] =
cd->toc[to + 1].fsm[i];
}
PLAY_TO.track = to;
}
for (i = 0; i < 7; i++)
type_0_command(play_command[i], 0);
for (i = 0; i < 2; i++)
type_0_command(0x2, 0); /* volume */
cd->dsb = wait_dsb();
}
int get_current_q(struct cdrom_subchnl *qp)
{
int i;
uch *q = cd->q;
if (type_1_command(c_read_current_q, 10, q))
return 0;
/* q[0] = bcdbin(q[0]); Don't think so! */
for (i = 2; i < 6; i++)
q[i] = bcdbin(q[i]);
qp->cdsc_adr = q[0] & 0xf;
qp->cdsc_ctrl = q[0] >> 4; /* from mcd.c */
qp->cdsc_trk = q[1];
qp->cdsc_ind = q[2];
if (qp->cdsc_format == CDROM_MSF) {
qp->cdsc_reladdr.msf.minute = q[3];
qp->cdsc_reladdr.msf.second = q[4];
qp->cdsc_reladdr.msf.frame = q[5];
qp->cdsc_absaddr.msf.minute = q[7];
qp->cdsc_absaddr.msf.second = q[8];
qp->cdsc_absaddr.msf.frame = q[9];
} else {
qp->cdsc_reladdr.lba = f_s_m2lba(q[5], q[4], q[3]);
qp->cdsc_absaddr.lba = f_s_m2lba(q[9], q[8], q[7]);
}
get_drive_status();
if (cd->dsb & dsb_play_in_progress)
qp->cdsc_audiostatus = CDROM_AUDIO_PLAY;
else if (PAUSED)
qp->cdsc_audiostatus = CDROM_AUDIO_PAUSED;
else
qp->cdsc_audiostatus = CDROM_AUDIO_NO_STATUS;
return 0;
}
void invalidate_toc(void)
{
memset(cd->toc, 0, sizeof(cd->toc));
memset(cd->disc_status, 0, sizeof(cd->disc_status));
}
/* cdrom.c guarantees that cdte_format == CDROM_MSF */
void get_toc_entry(struct cdrom_tocentry *ep)
{
uch track = normalize_track(ep->cdte_track);
update_toc_entry(track);
ep->cdte_addr.msf.frame = cd->toc[track].fsm[0];
ep->cdte_addr.msf.second = cd->toc[track].fsm[1];
ep->cdte_addr.msf.minute = cd->toc[track].fsm[2];
ep->cdte_adr = cd->toc[track].q0 & 0xf;
ep->cdte_ctrl = cd->toc[track].q0 >> 4;
ep->cdte_datamode = 0;
}
/* Audio ioctl. Ioctl commands connected to audio are in such an
* idiosyncratic i/o format, that we leave these untouched. Return 0
* upon success. Memory checking has been done by cdrom_ioctl(), the
* calling function, as well as LBA/MSF sanitization.
*/
int cm206_audio_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
void *arg)
{
switch (cmd) {
case CDROMREADTOCHDR:
return read_toc_header((struct cdrom_tochdr *) arg);
case CDROMREADTOCENTRY:
get_toc_entry((struct cdrom_tocentry *) arg);
return 0;
case CDROMPLAYMSF:
play_from_to_msf((struct cdrom_msf *) arg);
return 0;
case CDROMPLAYTRKIND: /* admittedly, not particularly beautiful */
play_from_to_track(((struct cdrom_ti *) arg)->cdti_trk0,
((struct cdrom_ti *) arg)->cdti_trk1);
return 0;
case CDROMSTOP:
PAUSED = 0;
if (cd->dsb & dsb_play_in_progress)
return type_0_command(c_stop, 1);
else
return 0;
case CDROMPAUSE:
get_drive_status();
if (cd->dsb & dsb_play_in_progress) {
type_0_command(c_stop, 1);
type_1_command(c_audio_status, 5,
cd->audio_status);
PAUSED = 1; /* say we're paused */
}
return 0;
case CDROMRESUME:
if (PAUSED)
play_from_to_track(0, 0);
PAUSED = 0;
return 0;
case CDROMSTART:
case CDROMVOLCTRL:
return 0;
case CDROMSUBCHNL:
return get_current_q((struct cdrom_subchnl *) arg);
default:
return -EINVAL;
}
}
/* Ioctl. These ioctls are specific to the cm206 driver. I have made
some driver statistics accessible through ioctl calls.
*/
static int cm206_ioctl(struct cdrom_device_info *cdi, unsigned int cmd,
unsigned long arg)
{
switch (cmd) {
#ifdef STATISTICS
case CM206CTL_GET_STAT:
if (arg >= NR_STATS)
return -EINVAL;
else
return cd->stats[arg];
case CM206CTL_GET_LAST_STAT:
if (arg >= NR_STATS)
return -EINVAL;
else
return cd->last_stat[arg];
#endif
default:
debug(("Unknown ioctl call 0x%x\n", cmd));
return -EINVAL;
}
}
int cm206_media_changed(struct cdrom_device_info *cdi, int disc_nr)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -