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

📄 sonycd535.c

📁 cdrom device drive for linux.
💻 C
📖 第 1 页 / 共 4 页
字号:
check_drive_status(void)
{
	Byte status, e_status[2];
	int  CDD, ATN;
	Byte cmd;

	select_unit(0);
	if (sony_audio_status == CDROM_AUDIO_PLAY) {	/* check status */
		outb(SONY535_REQUEST_AUDIO_STATUS, command_reg);
		if (read_result_reg(&status) == 0) {
			switch (status) {
			case 0x0:
				break;		/* play in progress */
			case 0x1:
				break;		/* paused */
			case 0x3:		/* audio play completed */
			case 0x5:		/* play not requested */
				sony_audio_status = CDROM_AUDIO_COMPLETED;
				read_subcode();
				break;
			case 0x4:		/* error during play */
				sony_audio_status = CDROM_AUDIO_ERROR;
				break;
			}
		}
	}
	/* now check drive status */
	outb(SONY535_REQUEST_DRIVE_STATUS_2, command_reg);
	if (read_result_reg(&status) != 0)
		return TIME_OUT;

#if DEBUG > 1
	printk(CDU535_MESSAGE_NAME ": check_drive_status() got 0x%x\n", status);
#endif

	if (status == 0)
		return 0;

	ATN = status & 0xf;
	CDD = (status >> 4) & 0xf;

	switch (ATN) {
	case 0x0:
		break;					/* go on to CDD stuff */
	case SONY535_ATN_BUSY:
		if (initialized)
			printk(CDU535_MESSAGE_NAME " error: drive busy\n");
		return CD_BUSY;
	case SONY535_ATN_EJECT_IN_PROGRESS:
		printk(CDU535_MESSAGE_NAME " error: eject in progress\n");
		sony_audio_status = CDROM_AUDIO_INVALID;
		return CD_BUSY;
	case SONY535_ATN_RESET_OCCURRED:
	case SONY535_ATN_DISC_CHANGED:
	case SONY535_ATN_RESET_AND_DISC_CHANGED:
#if DEBUG > 0
		printk(CDU535_MESSAGE_NAME " notice: reset occurred or disc changed\n");
#endif
		sony_disc_changed = 1;
		sony_toc_read = 0;
		sony_audio_status = CDROM_AUDIO_NO_STATUS;
		sony_first_block = -1;
		sony_last_block = -1;
		if (initialized) {
			cmd = SONY535_SPIN_UP;
			do_sony_cmd(&cmd, 1, e_status, NULL, 0, 0);
			sony_get_toc();
		}
		return 0;
	default:
		printk(CDU535_MESSAGE_NAME " error: drive busy (ATN=0x%x)\n", ATN);
		return CD_BUSY;
	}
	switch (CDD) {			/* the 531 docs are not helpful in decoding this */
	case 0x0:				/* just use the values from the DOS driver */
	case 0x2:
	case 0xa:
		break;				/* no error */
	case 0xc:
		printk(CDU535_MESSAGE_NAME
				": check_drive_status(): CDD = 0xc! Not properly handled!\n");
		return CD_BUSY;		/* ? */
	default:
		return CD_BUSY;
	}
	return 0;
}	/* check_drive_status() */

/*****************************************************************************
 * int do_sony_cmd( Byte *cmd, int n_cmd, Byte status[2],
 *                Byte *response, int n_response, int ignore_status_bit7 )
 *
 *  Generic routine for executing commands.  The command and its parameters
 *  should be placed in the cmd[] array, number of bytes in the command is
 *  stored in nCmd.  The response from the command will be stored in the
 *  response array.  The number of bytes you expect back (excluding status)
 *  should be passed in n_response.  Finally, some
 *  commands set bit 7 of the return status even when there is no second
 *  status byte, on these commands set ignoreStatusBit7 TRUE.
 *    If the command was sent and data received back, then we return 0,
 *  else we return TIME_OUT.  You still have to check the status yourself.
 *    You should call check_drive_status() before calling this routine
 *  so that you do not lose notifications of disk changes, etc.
 ****************************************************************************/
static int
do_sony_cmd(Byte * cmd, int n_cmd, Byte status[2],
			Byte * response, int n_response, int ignore_status_bit7)
{
	int i;

	/* write out the command */
	for (i = 0; i < n_cmd; i++)
		outb(cmd[i], command_reg);

	/* read back the status */
	if (read_result_reg(status) != 0)
		return TIME_OUT;
	if (!ignore_status_bit7 && ((status[0] & 0x80) != 0)) {
		/* get second status byte */
		if (read_result_reg(status + 1) != 0)
			return TIME_OUT;
	} else {
		status[1] = 0;
	}
#if DEBUG > 2
	printk(CDU535_MESSAGE_NAME ": do_sony_cmd %x: %x %x\n",
			*cmd, status[0], status[1]);
#endif

	/* do not know about when I should read set of data and when not to */
	if ((status[0] & ((ignore_status_bit7 ? 0x7f : 0xff) & 0x8f)) != 0)
		return 0;

	/* else, read in rest of data */
	for (i = 0; 0 < n_response; n_response--, i++)
		if (read_result_reg(response + i) != 0)
			return TIME_OUT;
	return 0;
}	/* do_sony_cmd() */

/**************************************************************************
 * int set_drive_mode( int mode, Byte status[2] )
 *
 *  Set the drive mode to the specified value (mode=0 is audio, mode=e0
 * is mode-1 CDROM
 **************************************************************************/
static int
set_drive_mode(int mode, Byte status[2])
{
	Byte cmd_buff[2];
	Byte ret_buff[1];

	cmd_buff[0] = SONY535_SET_DRIVE_MODE;
	cmd_buff[1] = mode;
	return do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1);
}

/***************************************************************************
 * int seek_and_read_N_blocks( Byte params[], int n_blocks, Byte status[2],
 *                             Byte *data_buff, int buff_size )
 *
 *  Read n_blocks of data from the CDROM starting at position params[0:2],
 *  number of blocks in stored in params[3:5] -- both these are already
 *  int bcd format.
 *  Transfer the data into the buffer pointed at by data_buff.  buff_size
 *  gives the number of bytes available in the buffer.
 *    The routine returns number of bytes read in if successful, otherwise
 *  it returns one of the standard error returns.
 ***************************************************************************/
static int
seek_and_read_N_blocks(Byte params[], int n_blocks, Byte status[2],
					   Byte **buff, int buf_size)
{
	Byte cmd_buff[7];
	int  i;
	int  read_status;
	unsigned long snap;
	Byte *data_buff;
	int  sector_count = 0;

	if (buf_size < CDU535_BLOCK_SIZE * n_blocks)
		return NO_ROOM;

	set_drive_mode(SONY535_CDROM_DRIVE_MODE, status);

	/* send command to read the data */
	cmd_buff[0] = SONY535_SEEK_AND_READ_N_BLOCKS_1;
	for (i = 0; i < 6; i++)
		cmd_buff[i + 1] = params[i];
	for (i = 0; i < 7; i++)
		outb(cmd_buff[i], command_reg);

	/* read back the data one block at a time */
	while (0 < n_blocks--) {
		/* wait for data to be ready */
		int data_valid = 0;
		snap = jiffies;
		while (jiffies-snap < SONY_JIFFIES_TIMEOUT) {
			read_status = inb(read_status_reg);
			if ((read_status & SONY535_RESULT_NOT_READY_BIT) == 0) {
				read_exec_status(status);
				return BAD_STATUS;
			}
			if ((read_status & SONY535_DATA_NOT_READY_BIT) == 0) {
				/* data is ready, read it */
				data_buff = buff[sector_count++];
				for (i = 0; i < CDU535_BLOCK_SIZE; i++)
					*data_buff++ = inb(data_reg);	/* unrolling this loop does not seem to help */
				data_valid = 1;
				break;			/* exit the timeout loop */
			}
			sony_sleep();		/* data not ready, sleep a while */
		}
		if (!data_valid)
			return TIME_OUT;	/* if we reach this stage */
	}

	/* read all the data, now read the status */
	if ((i = read_exec_status(status)) != 0)
		return i;
	return CDU535_BLOCK_SIZE * sector_count;
}	/* seek_and_read_N_blocks() */

/****************************************************************************
 * int request_toc_data( Byte status[2], struct s535_sony_toc *toc )
 *
 *  Read in the table of contents data.  Converts all the bcd data
 * into integers in the toc structure.
 ****************************************************************************/
static int
request_toc_data(Byte status[2], struct s535_sony_toc *toc)
{
	int  to_status;
	int  i, j, n_tracks, track_no;
	int  first_track_num, last_track_num;
	Byte cmd_no = 0xb2;
	Byte track_address_buffer[5];

	/* read the fixed portion of the table of contents */
	if ((to_status = do_sony_cmd(&cmd_no, 1, status, (Byte *) toc, 15, 1)) != 0)
		return to_status;

	/* convert the data into integers so we can use them */
	first_track_num = bcd_to_int(toc->first_track_num);
	last_track_num = bcd_to_int(toc->last_track_num);
	n_tracks = last_track_num - first_track_num + 1;

	/* read each of the track address descriptors */
	for (i = 0; i < n_tracks; i++) {
		/* read the descriptor into a temporary buffer */
		for (j = 0; j < 5; j++) {
			if (read_result_reg(track_address_buffer + j) != 0)
				return TIME_OUT;
			if (j == 1)		/* need to convert from bcd */
				track_no = bcd_to_int(track_address_buffer[j]);
		}
		/* copy the descriptor to proper location - sonycd.c just fills */
		memcpy(toc->tracks + i, track_address_buffer, 5);
	}
	return 0;
}	/* request_toc_data() */

/***************************************************************************
 * int spin_up_drive( Byte status[2] )
 *
 *  Spin up the drive (unless it is already spinning).
 ***************************************************************************/
static int
spin_up_drive(Byte status[2])
{
	Byte cmd;

	/* first see if the drive is already spinning */
	cmd = SONY535_REQUEST_DRIVE_STATUS_1;
	if (do_sony_cmd(&cmd, 1, status, NULL, 0, 0) != 0)
		return TIME_OUT;
	if ((status[0] & SONY535_STATUS1_NOT_SPINNING) == 0)
		return 0;	/* it's already spinning */

	/* otherwise, give the spin-up command */
	cmd = SONY535_SPIN_UP;
	return do_sony_cmd(&cmd, 1, status, NULL, 0, 0);
}

/*--------------------end of SONY CDU535 very specific ---------------------*/

/* 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, Byte *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(Byte *msf)
{
	unsigned int log;


	log = bcd_to_int(msf[2]);
	log += bcd_to_int(msf[1]) * 75;
	log += bcd_to_int(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, Byte *buf)
{
	buf[0] = size / 65536;
	size = size % 65536;
	buf[1] = size / 256;
	buf[2] = size % 256;
}


/*
 * The OS calls this to perform a read or write operation to the drive.
 * Write obviously fail.  Reads to a read ahead of sony_buffer_size
 * bytes to help speed operations.  This especially helps since the OS
 * may use 1024 byte blocks and the drive uses 2048 byte blocks.  Since most
 * data access on a CD is done sequentially, this saves a lot of operations.
 */
static void
do_cdu535_request(request_queue_t * q)
{
	unsigned int dev;
	unsigned int read_size;
	int  block;
	int  nsect;
	int  copyoff;
	int  spin_up_retry;
	Byte params[10];
	Byte status[2];
	Byte cmd[2];

	while (1) {
		/*
		 * The beginning here is stolen from the hard disk driver.  I hope
		 * it's right.
		 */
		if (QUEUE_EMPTY || CURRENT->rq_status == RQ_INACTIVE) {
			return;
		}
		INIT_REQUEST;
		dev = MINOR(CURRENT->rq_dev);
		block = CURRENT->sector;
		nsect = CURRENT->nr_sectors;
		if (dev != 0) {
			end_request(0);
			continue;
		}
		switch (CURRENT->cmd) {
		case READ:
			/*
			 * If the block address is invalid or the request goes beyond the end of
			 * the media, return an error.
			 */

			if (sony_toc->lead_out_start_lba <= (block / 4)) {
				end_request(0);
				return;
			}
			if (sony_toc->lead_out_start_lba <= ((block + nsect) / 4)) {
				end_request(0);
				return;
			}
			while (0 < nsect) {
				/*
				 * If the requested sector is not currently in the read-ahead buffer,
				 * it must be read in.
				 */
				if ((block < sony_first_block) || (sony_last_block < block)) {
					sony_first_block = (block / 4) * 4;
					log_to_msf(block / 4, params);

					/*
					 * 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.
					 */
					if (sony_toc->lead_out_start_lba <= ((block / 4) + sony_buffer_sectors)) {
						sony_last_block = (sony_toc->lead_out_start_lba * 4) - 1;
						read_size = sony_toc->lead_out_start_lba - (block / 4);
					} else {
						sony_last_block = sony_first_block + (sony_buffer_sectors * 4) - 1;
						read_size = sony_buffer_sectors;
					}
					size_to_buf(read_size, &params[3]);

					/*
					 * Read the data.  If the drive was not spinning,

⌨️ 快捷键说明

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