cm206.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,627 行 · 第 1/4 页

C
1,627
字号
	}	/* 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))		tasklet_schedule(&cm206_tasklet);	/* issue a stop read command */	stats(interrupt);	return IRQ_HANDLED;}/* 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;	init_timer(&cd->timer);	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 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*/static void cm206_tasklet_func(unsigned long ignore){	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;	}}static DECLARE_TASKLET(cm206_tasklet, cm206_tasklet_func, 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"));	}}/* 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);	}

⌨️ 快捷键说明

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