⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cm206.c

📁 cdrom device drive for linux.
💻 C
📖 第 1 页 / 共 4 页
字号:
	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 + -