📄 cm206.c
字号:
else if (cd->command && cd->intr_ls & ls_transmitter_buffer_empty) {
outw(dc_normal | (inw(r_data_status) & 0x7f),
r_data_control);
outw(cd->command, r_uart_transmit);
cd->command = 0;
if (!cd->background)
wake_up_interruptible(&cd->uart);
}
/* now treat errors (at least, identify them for debugging) */
else if (cd->intr_ds & ds_fifo_overflow) {
debug(("Fifo overflow at sectors 0x%x\n",
cd->sector_first));
fool = inw(r_fifo_output_buffer); /* de-assert the interrupt */
cd->fifo_overflowed = 1; /* signal one word less should be read */
stats(fifo_overflow);
} else if (cd->intr_ds & ds_data_error) {
debug(("Data error at sector 0x%x\n", cd->sector_first));
stats(data_error);
} else if (cd->intr_ds & ds_crc_error) {
debug(("CRC error at sector 0x%x\n", cd->sector_first));
stats(crc_error);
} else if (cd->intr_ds & ds_sync_error) {
debug(("Sync at sector 0x%x\n", cd->sector_first));
stats(sync_error);
} else if (cd->intr_ds & ds_toc_ready) {
/* do something appropriate */
}
/* couldn't see why this interrupt, maybe due to init */
else {
outw(dc_normal | READ_AHEAD, r_data_control);
stats(lost_intr);
}
if (cd->background
&& (cd->adapter_last - cd->adapter_first == cd->max_sectors
|| cd->fifo_overflowed))
mark_bh(CM206_BH); /* issue a stop read command */
stats(interrupt);
}
/* we have put the address of the wait queue in who */
void cm206_timeout(unsigned long who)
{
cd->timed_out = 1;
debug(("Timing out\n"));
wake_up_interruptible((wait_queue_head_t *) who);
}
/* This function returns 1 if a timeout occurred, 0 if an interrupt
happened */
int sleep_or_timeout(wait_queue_head_t * wait, int timeout)
{
cd->timed_out = 0;
cd->timer.data = (unsigned long) wait;
cd->timer.expires = jiffies + timeout;
add_timer(&cd->timer);
debug(("going to sleep\n"));
interruptible_sleep_on(wait);
del_timer(&cd->timer);
if (cd->timed_out) {
cd->timed_out = 0;
return 1;
} else
return 0;
}
void cm206_delay(int nr_jiffies)
{
DECLARE_WAIT_QUEUE_HEAD(wait);
sleep_or_timeout(&wait, nr_jiffies);
}
void send_command(int command)
{
debug(("Sending 0x%x\n", command));
if (!(inw(r_line_status) & ls_transmitter_buffer_empty)) {
cd->command = command;
cli(); /* don't interrupt before sleep */
outw(dc_mask_sync_error | dc_no_stop_on_error |
(inw(r_data_status) & 0x7f), r_data_control);
/* interrupt routine sends command */
if (sleep_or_timeout(&cd->uart, UART_TIMEOUT)) {
debug(("Time out on write-buffer\n"));
stats(write_timeout);
outw(command, r_uart_transmit);
}
debug(("Write commmand delayed\n"));
} else
outw(command, r_uart_transmit);
}
uch receive_byte(int timeout)
{
uch ret;
cli();
debug(("cli\n"));
ret = cd->ur[cd->ur_r];
if (cd->ur_r != cd->ur_w) {
sti();
debug(("returning #%d: 0x%x\n", cd->ur_r,
cd->ur[cd->ur_r]));
cd->ur_r++;
cd->ur_r %= UR_SIZE;
return ret;
} else if (sleep_or_timeout(&cd->uart, timeout)) { /* does sti() */
debug(("Time out on receive-buffer\n"));
#ifdef STATISTICS
if (timeout == UART_TIMEOUT)
stats(receive_timeout) /* no `;'! */
else
stats(dsb_timeout);
#endif
return 0xda;
}
ret = cd->ur[cd->ur_r];
debug(("slept; returning #%d: 0x%x\n", cd->ur_r,
cd->ur[cd->ur_r]));
cd->ur_r++;
cd->ur_r %= UR_SIZE;
return ret;
}
inline uch receive_echo(void)
{
return receive_byte(UART_TIMEOUT);
}
inline uch send_receive(int command)
{
send_command(command);
return receive_echo();
}
inline uch wait_dsb(void)
{
return receive_byte(DSB_TIMEOUT);
}
int type_0_command(int command, int expect_dsb)
{
int e;
clear_ur();
if (command != (e = send_receive(command))) {
debug(("command 0x%x echoed as 0x%x\n", command, e));
stats(echo);
return -1;
}
if (expect_dsb) {
cd->dsb = wait_dsb(); /* wait for command to finish */
}
return 0;
}
int type_1_command(int command, int bytes, uch * status)
{ /* returns info */
int i;
if (type_0_command(command, 0))
return -1;
for (i = 0; i < bytes; i++)
status[i] = send_receive(c_gimme);
return 0;
}
/* This function resets the adapter card. We'd better not do this too
* often, because it tends to generate `lost interrupts.' */
void reset_cm260(void)
{
outw(dc_normal | dc_initialize | READ_AHEAD, r_data_control);
udelay(10); /* 3.3 mu sec minimum */
outw(dc_normal | READ_AHEAD, r_data_control);
}
/* fsm: frame-sec-min from linear address; one of many */
void fsm(int lba, uch * fsm)
{
fsm[0] = lba % 75;
lba /= 75;
lba += 2;
fsm[1] = lba % 60;
fsm[2] = lba / 60;
}
inline int fsm2lba(uch * fsm)
{
return fsm[0] + 75 * (fsm[1] - 2 + 60 * fsm[2]);
}
inline int f_s_m2lba(uch f, uch s, uch m)
{
return f + 75 * (s - 2 + 60 * m);
}
int start_read(int start)
{
uch read_sector[4] = { c_read_data, };
int i, e;
fsm(start, &read_sector[1]);
clear_ur();
for (i = 0; i < 4; i++)
if (read_sector[i] != (e = send_receive(read_sector[i]))) {
debug(("read_sector: %x echoes %x\n",
read_sector[i], e));
stats(echo);
if (e == 0xff) { /* this seems to happen often */
e = receive_echo();
debug(("Second try %x\n", e));
if (e != read_sector[i])
return -1;
}
}
return 0;
}
int stop_read(void)
{
int e;
type_0_command(c_stop, 0);
if ((e = receive_echo()) != 0xff) {
debug(("c_stop didn't send 0xff, but 0x%x\n", e));
stats(stop_0xff);
return -1;
}
return 0;
}
/* This function starts to read sectors in adapter memory, the
interrupt routine should stop the read. In fact, the bottom_half
routine takes care of this. Set a flag `background' in the cd
struct to indicate the process. */
int read_background(int start, int reading)
{
if (cd->background)
return -1; /* can't do twice */
outw(dc_normal | BACK_AHEAD, r_data_control);
if (!reading && start_read(start))
return -2;
cd->adapter_first = cd->adapter_last = start;
cd->background = 1; /* flag a read is going on */
return 0;
}
#ifdef USE_INSW
#define transport_data insw
#else
/* this routine implements insw(,,). There was a time i had the
impression that there would be any difference in error-behaviour. */
void transport_data(int port, ush * dest, int count)
{
int i;
ush *d;
for (i = 0, d = dest; i < count; i++, d++)
*d = inw(port);
}
#endif
#define MAX_TRIES 100
int read_sector(int start)
{
int tries = 0;
if (cd->background) {
cd->background = 0;
cd->adapter_last = -1; /* invalidate adapter memory */
stop_read();
}
cd->fifo_overflowed = 0;
reset_cm260(); /* empty fifo etc. */
if (start_read(start))
return -1;
do {
if (sleep_or_timeout(&cd->data, DATA_TIMEOUT)) {
debug(("Read timed out sector 0x%x\n", start));
stats(read_timeout);
stop_read();
return -3;
}
tries++;
} while (cd->intr_ds & ds_fifo_empty && tries < MAX_TRIES);
if (tries > 1)
debug(("Took me some tries\n"))
else
if (tries == MAX_TRIES)
debug(("MAX_TRIES tries for read sector\n"));
transport_data(r_fifo_output_buffer, cd->sector,
READ_AHEAD * RAW_SECTOR_SIZE / 2);
if (read_background(start + READ_AHEAD, 1))
stats(read_background);
cd->sector_first = start;
cd->sector_last = start + READ_AHEAD;
stats(read_restarted);
return 0;
}
/* The function of bottom-half is to send a stop command to the drive
This isn't easy because the routine is not `owned' by any process;
we can't go to sleep! The variable cd->background gives the status:
0 no read pending
1 a read is pending
2 c_stop waits for write_buffer_empty
3 c_stop waits for receive_buffer_full: echo
4 c_stop waits for receive_buffer_full: 0xff
*/
void cm206_bh(void)
{
debug(("bh: %d\n", cd->background));
switch (cd->background) {
case 1:
stats(bh);
if (!(cd->intr_ls & ls_transmitter_buffer_empty)) {
cd->command = c_stop;
outw(dc_mask_sync_error | dc_no_stop_on_error |
(inw(r_data_status) & 0x7f), r_data_control);
cd->background = 2;
break; /* we'd better not time-out here! */
} else
outw(c_stop, r_uart_transmit);
/* fall into case 2: */
case 2:
/* the write has been satisfied by interrupt routine */
cd->background = 3;
break;
case 3:
if (cd->ur_r != cd->ur_w) {
if (cd->ur[cd->ur_r] != c_stop) {
debug(("cm206_bh: c_stop echoed 0x%x\n",
cd->ur[cd->ur_r]));
stats(echo);
}
cd->ur_r++;
cd->ur_r %= UR_SIZE;
}
cd->background++;
break;
case 4:
if (cd->ur_r != cd->ur_w) {
if (cd->ur[cd->ur_r] != 0xff) {
debug(("cm206_bh: c_stop reacted with 0x%x\n", cd->ur[cd->ur_r]));
stats(stop_0xff);
}
cd->ur_r++;
cd->ur_r %= UR_SIZE;
}
cd->background = 0;
}
}
/* This command clears the dsb_possible_media_change flag, so we must
* retain it.
*/
void get_drive_status(void)
{
uch status[2];
type_1_command(c_drive_status, 2, status); /* this might be done faster */
cd->dsb = status[0];
cd->cc = status[1];
cd->media_changed |=
!!(cd->dsb & (dsb_possible_media_change |
dsb_drive_not_ready | dsb_tray_not_closed));
}
void get_disc_status(void)
{
if (type_1_command(c_disc_status, 7, cd->disc_status)) {
debug(("get_disc_status: error\n"));
}
}
struct block_device_operations cm206_bdops =
{
owner: THIS_MODULE,
open: cdrom_open,
release: cdrom_release,
ioctl: cdrom_ioctl,
check_media_change: cdrom_media_changed,
};
/* The new open. The real opening strategy is defined in cdrom.c. */
static int cm206_open(struct cdrom_device_info *cdi, int purpose)
{
if (!cd->openfiles) { /* reset only first time */
cd->background = 0;
reset_cm260();
cd->adapter_last = -1; /* invalidate adapter memory */
cd->sector_last = -1;
}
++cd->openfiles;
stats(open);
return 0;
}
static void cm206_release(struct cdrom_device_info *cdi)
{
if (cd->openfiles == 1) {
if (cd->background) {
cd->background = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -