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

📄 cdu31a.c

📁 cdrom device drive for linux.
💻 C
📖 第 1 页 / 共 5 页
字号:

		write_params(params, num_params);
		write_cmd(cmd);

		get_result(result_buffer, result_size);
	}

	if (((result_buffer[0] & 0xf0) == 0x20)
	    && (num_retries < MAX_CDU31A_RETRIES)) {
		num_retries++;
		current->state = TASK_INTERRUPTIBLE;
		schedule_timeout(HZ / 10);	/* Wait .1 seconds on retries */
		goto retry_cd_operation;
	}

	if (!recursive_call) {
		has_cd_task = NULL;
		sony_inuse = 0;
		wake_up_interruptible(&sony_wait);
	}

	restore_flags(flags);
}


/*
 * Handle an attention from the drive.  This will return 1 if it found one
 * or 0 if not (if one is found, the caller might want to call again).
 *
 * This routine counts the number of consecutive times it is called
 * (since this is always called from a while loop until it returns
 * a 0), and returns a 0 if it happens too many times.  This will help
 * prevent a lockup.
 */
static int handle_sony_cd_attention(void)
{
	unsigned char atten_code;
	static int num_consecutive_attentions = 0;
	volatile int val;


#if 0*DEBUG
	printk("Entering handle_sony_cd_attention\n");
#endif
	if (is_attention()) {
		if (num_consecutive_attentions >
		    CDU31A_MAX_CONSECUTIVE_ATTENTIONS) {
			printk
			    ("cdu31a: Too many consecutive attentions: %d\n",
			     num_consecutive_attentions);
			num_consecutive_attentions = 0;
#if DEBUG
			printk("Leaving handle_sony_cd_attention at %d\n",
			       __LINE__);
#endif
			return (0);
		}

		clear_attention();
		atten_code = read_result_register();

		switch (atten_code) {
			/* Someone changed the CD.  Mark it as changed */
		case SONY_MECH_LOADED_ATTN:
			disk_changed = 1;
			sony_toc_read = 0;
			sony_audio_status = CDROM_AUDIO_NO_STATUS;
			sony_blocks_left = 0;
			break;

		case SONY_SPIN_DOWN_COMPLETE_ATTN:
			/* Mark the disk as spun down. */
			sony_spun_up = 0;
			break;

		case SONY_AUDIO_PLAY_DONE_ATTN:
			sony_audio_status = CDROM_AUDIO_COMPLETED;
			read_subcode();
			break;

		case SONY_EJECT_PUSHED_ATTN:
			if (is_auto_eject) {
				sony_audio_status = CDROM_AUDIO_INVALID;
			}
			break;

		case SONY_LEAD_IN_ERR_ATTN:
		case SONY_LEAD_OUT_ERR_ATTN:
		case SONY_DATA_TRACK_ERR_ATTN:
		case SONY_AUDIO_PLAYBACK_ERR_ATTN:
			sony_audio_status = CDROM_AUDIO_ERROR;
			break;
		}

		num_consecutive_attentions++;
#if DEBUG
		printk("Leaving handle_sony_cd_attention at %d\n",
		       __LINE__);
#endif
		return (1);
	} else if (abort_read_started) {
		while (is_result_reg_not_empty()) {
			val = read_result_register();
		}
		clear_data_ready();
		clear_result_ready();
		/* Clear out the data */
		while (is_data_requested()) {
			val = read_data_register();
		}
		abort_read_started = 0;
#if DEBUG
		printk("Leaving handle_sony_cd_attention at %d\n",
		       __LINE__);
#endif
		return (1);
	}

	num_consecutive_attentions = 0;
#if 0*DEBUG
	printk("Leaving handle_sony_cd_attention at %d\n", __LINE__);
#endif
	return (0);
}


/* Convert from an integer 0-99 to BCD */
static inline unsigned int int_to_bcd(unsigned int val)
{
	int retval;


	retval = (val / 10) << 4;
	retval = retval | val % 10;
	return (retval);
}


/* Convert from BCD to an integer from 0-99 */
static unsigned int bcd_to_int(unsigned int bcd)
{
	return ((((bcd >> 4) & 0x0f) * 10) + (bcd & 0x0f));
}


/*
 * Convert a logical sector value (like the OS would want to use for
 * a block device) to an MSF format.
 */
static void log_to_msf(unsigned int log, unsigned char *msf)
{
	log = log + LOG_START_OFFSET;
	msf[0] = int_to_bcd(log / 4500);
	log = log % 4500;
	msf[1] = int_to_bcd(log / 75);
	msf[2] = int_to_bcd(log % 75);
}


/*
 * Convert an MSF format to a logical sector.
 */
static unsigned int msf_to_log(unsigned char *msf)
{
	unsigned int log;


	log = msf[2];
	log += msf[1] * 75;
	log += msf[0] * 4500;
	log = log - LOG_START_OFFSET;

	return log;
}


/*
 * Take in integer size value and put it into a buffer like
 * the drive would want to see a number-of-sector value.
 */
static void size_to_buf(unsigned int size, unsigned char *buf)
{
	buf[0] = size / 65536;
	size = size % 65536;
	buf[1] = size / 256;
	buf[2] = size % 256;
}

/* Starts a read operation. Returns 0 on success and 1 on failure. 
   The read operation used here allows multiple sequential sectors 
   to be read and status returned for each sector.  The driver will
   read the output one at a time as the requests come and abort the
   operation if the requested sector is not the next one from the
   drive. */
static int
start_request(unsigned int sector, unsigned int nsect, int read_nsect_only)
{
	unsigned char params[6];
	unsigned int read_size;
	unsigned int retry_count;


#if DEBUG
	printk("Entering start_request\n");
#endif
	log_to_msf(sector, params);
	/* If requested, read exactly what was asked. */
	if (read_nsect_only) {
		read_size = nsect;
	}
	/*
	 * If the full read-ahead would go beyond the end of the media, trim
	 * it back to read just till the end of the media.
	 */
	else if ((sector + nsect) >= sony_toc.lead_out_start_lba) {
		read_size = sony_toc.lead_out_start_lba - sector;
	}
	/* Read the full readahead amount. */
	else {
		read_size = CDU31A_READAHEAD / 4;
	}
	size_to_buf(read_size, &params[3]);

	/*
	 * Clear any outstanding attentions and wait for the drive to
	 * complete any pending operations.
	 */
	while (handle_sony_cd_attention());

	retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
	while (time_before(jiffies, retry_count) && (is_busy())) {
		sony_sleep();

		while (handle_sony_cd_attention());
	}

	if (is_busy()) {
		printk("CDU31A: Timeout while waiting to issue command\n");
#if DEBUG
		printk("Leaving start_request at %d\n", __LINE__);
#endif
		return (1);
	} else {
		/* Issue the command */
		clear_result_ready();
		clear_param_reg();

		write_params(params, 6);
		write_cmd(SONY_READ_BLKERR_STAT_CMD);

		sony_blocks_left = read_size * 4;
		sony_next_block = sector * 4;
		readahead_dataleft = 0;
		readahead_bad = 0;
#if DEBUG
		printk("Leaving start_request at %d\n", __LINE__);
#endif
		return (0);
	}
#if DEBUG
	printk("Leaving start_request at %d\n", __LINE__);
#endif
}

/* Abort a pending read operation.  Clear all the drive status and
   readahead variables. */
static void abort_read(void)
{
	unsigned char result_reg[2];
	int result_size;
	volatile int val;


	do_sony_cd_cmd(SONY_ABORT_CMD, NULL, 0, result_reg, &result_size);
	if ((result_reg[0] & 0xf0) == 0x20) {
		printk("CDU31A: Error aborting read, %s error\n",
		       translate_error(result_reg[1]));
	}

	while (is_result_reg_not_empty()) {
		val = read_result_register();
	}
	clear_data_ready();
	clear_result_ready();
	/* Clear out the data */
	while (is_data_requested()) {
		val = read_data_register();
	}

	sony_blocks_left = 0;
	readahead_dataleft = 0;
	readahead_bad = 0;
}

/* Called when the timer times out.  This will abort the
   pending read operation. */
static void handle_abort_timeout(unsigned long data)
{
	unsigned long flags;

#if DEBUG
	printk("Entering handle_abort_timeout\n");
#endif
	save_flags(flags);
	cli();
	/* If it is in use, ignore it. */
	if (!sony_inuse) {
		/* We can't use abort_read(), because it will sleep
		   or schedule in the timer interrupt.  Just start
		   the operation, finish it on the next access to
		   the drive. */
		clear_result_ready();
		clear_param_reg();
		write_cmd(SONY_ABORT_CMD);

		sony_blocks_left = 0;
		readahead_dataleft = 0;
		readahead_bad = 0;
		abort_read_started = 1;
	}
	restore_flags(flags);
#if DEBUG
	printk("Leaving handle_abort_timeout\n");
#endif
}

/* Actually get data and status from the drive. */
static void
input_data(char *buffer,
	   unsigned int bytesleft,
	   unsigned int nblocks, unsigned int offset, unsigned int skip)
{
	int i;
	volatile unsigned char val;


#if DEBUG
	printk("Entering input_data\n");
#endif
	/* If an XA disk on a CDU31A, skip the first 12 bytes of data from
	   the disk.  The real data is after that. */
	if (sony_xa_mode) {
		for (i = 0; i < CD_XA_HEAD; i++) {
			val = read_data_register();
		}
	}

	clear_data_ready();

	if (bytesleft == 2048) {	/* 2048 byte direct buffer transfer */
		insb(sony_cd_read_reg, buffer, 2048);
		readahead_dataleft = 0;
	} else {
		/* If the input read did not align with the beginning of the block,
		   skip the necessary bytes. */
		if (skip != 0) {
			insb(sony_cd_read_reg, readahead_buffer, skip);
		}

		/* Get the data into the buffer. */
		insb(sony_cd_read_reg, &buffer[offset], bytesleft);

		/* Get the rest of the data into the readahead buffer at the
		   proper location. */
		readahead_dataleft = (2048 - skip) - bytesleft;
		insb(sony_cd_read_reg,
		     readahead_buffer + bytesleft, readahead_dataleft);
	}
	sony_blocks_left -= nblocks;
	sony_next_block += nblocks;

	/* If an XA disk, we have to clear out the rest of the unused
	   error correction data. */
	if (sony_xa_mode) {
		for (i = 0; i < CD_XA_TAIL; i++) {
			val = read_data_register();
		}
	}
#if DEBUG
	printk("Leaving input_data at %d\n", __LINE__);
#endif
}

/* read data from the drive.  Note the nsect must be <= 4. */
static void
read_data_block(char *buffer,
		unsigned int block,
		unsigned int nblocks,
		unsigned char res_reg[], int *res_size)
{
	unsigned int retry_count;
	unsigned int bytesleft;
	unsigned int offset;
	unsigned int skip;


#if DEBUG
	printk("Entering read_data_block\n");
#endif

	res_reg[0] = 0;
	res_reg[1] = 0;
	*res_size = 0;
	bytesleft = nblocks * 512;
	offset = 0;

	/* If the data in the read-ahead does not match the block offset,
	   then fix things up. */
	if (((block % 4) * 512) != ((2048 - readahead_dataleft) % 2048)) {
		sony_next_block += block % 4;
		sony_blocks_left -= block % 4;
		skip = (block % 4) * 512;
	} else {
		skip = 0;
	}

	/* We have readahead data in the buffer, get that first before we
	   decide if a read is necessary. */
	if (readahead_dataleft != 0) {
		if (bytesleft > readahead_dataleft) {
			/* The readahead will not fill the requested buffer, but
			   get the data out of the readahead into the buffer. */
			memcpy(buffer,
			       readahead_buffer + (2048 -
						   readahead_dataleft),
			       readahead_dataleft);
			readahead_dataleft = 0;
			bytesleft -= readahead_dataleft;
			offset += readahead_dataleft;
		} else {
			/* The readahead will fill the whole buffer, get the data
			   and return. */
			memcpy(buffer,
			       readahead_buffer + (2048 -
						   readahead_dataleft),
			       bytesleft);
			readahead_dataleft -= bytesleft;
			bytesleft = 0;
			sony_blocks_left -= nblocks;
			sony_next_block += nblocks;

			/* If the data in the readahead is bad, return an error so the
			   driver will abort the buffer. */
			if (readahead_bad) {
				res_reg[0] = 0x20;
				res_reg[1] = SONY_BAD_DATA_ERR;
				*res_size = 2;
			}

			if (readahead_dataleft == 0) {
				readahead_bad = 0;
			}

			/* Final transfer is done for read command, get final result. */
			if (sony_blocks_left == 0) {
				get_result(res_reg, res_size);
			}
#if DEBUG
			printk("Leaving read_data_block at %d\n",
			       __LINE__);
#endif
			return;
		}
	}

	/* Wait for the drive to tell us we have something */
	retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
	while (time_before(jiffies, retry_count) && !(is_data_ready())) {
		while (handle_sony_cd_attention());

		sony_sleep();
	}
	if (!(is_data_ready())) {
		if (is_result_ready()) {
			get_result(res_reg, res_size);
			if ((res_reg[0] & 0xf0) != 0x20) {
				printk
				    ("CDU31A: Got result that should have been error: %d\n",
				     res_reg[0]);
				res_reg[0] = 0x20;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -