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

📄 cm206.c

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