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

📄 cm206.c

📁 还有没有人研究过cdrom 的驱动源码啊
💻 C
📖 第 1 页 / 共 3 页
字号:
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 100int 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;			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 */

⌨️ 快捷键说明

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