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

📄 ncr5380.c

📁 LINUX 1.0 内核c源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
 * 1.  The mutex flag, main_running, can only be set when the main 
 *     routine can actually process data, otherwise SCSI commands
 *     will never get issued.
 *
 * 2.  NCR5380_main() shouldn't be called before it has exited, because
 *     other drivers have had kernel stack overflows in similar
 *     situations.
 *
 * 3.  We don't want to inline NCR5380_main() because of space concerns,
 *     even though it is only called in two places.
 *
 * So, the solution is to set the mutex in an inline wrapper for the 
 * main corroutine, and have the main corroutine exit with interrupts 
 * disabled after the final search through the queues so that no race 
 * conditions are possible.
 */

static volatile int main_running = 0;

/* 
 * Function : run_main(void)
 * 
 * Purpose : insure that the coroutine is running and will process our 
 * 	request.  main_running is checked/set here (in an inline function)
 *	rather than in NCR5380_main itself to reduce the chances of stack
 *	overflow.
 *
 */

static __inline__ void run_main(void) {
    cli();
    if (!main_running) {
	main_running = 1;
        NCR5380_main();
	/* 
         * main_running is cleared in NCR5380_main once it can't do 
	 * more work, and NCR5380_main exits with interrupts disabled.
	 */
	sti();
    } else 
	sti();
}

#ifdef USLEEP
#ifndef NCR5380_TIMER
#error "NCR5380_TIMER must be defined so that this type of NCR5380 driver gets a unique timer."
#endif

/*
 * These need tweaking, and would probably work best as per-device 
 * flags initialized differently for disk, tape, cd, etc devices.
 * People with broken devices are free to experiment as to what gives
 * the best results for them.
 *
 * USLEEP_SLEEP should be a minimum seek time.
 *
 * USLEEP_POLL should be a maximum rotational latency.
 */
#ifndef USLEEP_SLEEP
/* 20 ms (reasonable hard disk speed) */
#define USLEEP_SLEEP 2
#endif
/* 300 RPM (floppy speed) */
#ifndef USLEEP_POLL
#define USLEEP_POLL 20
#endif

static struct Scsi_Host * expires_first = NULL;

/* 
 * Function : int should_disconnect (unsigned char cmd)
 *
 * Purpose : decide weather a commmand would normally disconnect or 
 *	not, since if it won't disconnect we should go to sleep.
 *
 * Input : cmd - opcode of SCSI command
 *
 * Returns : DISCONNECT_LONG if we should disconnect for a really long 
 * 	time (ie always, sleep, look for REQ active, sleep), 
 *	DISCONNECT_TIME_TO_DATA if we would only disconnect for a normal
 * 	time-to-data dealy, DISCONNECT_NONE if this command would return
 * 	immediately.
 *
 *      Future sleep algorithms based on time to data can exploit 
 *      something like this so they can differentiate between "normal" 
 *	(ie, read, write, seek) and unusual commands (ie, * format).
 *
 * Note : We don't deal with commands that handle an immediate disconnect,
 *        
 */

static int should_disconnect (unsigned char cmd) {
    switch (cmd) {
    case READ_6:
    case WRITE_6:
    case SEEK_6:
    case READ_10:
    case WRITE_10:
    case SEEK_10:
	return DISCONNECT_TIME_TO_DATA;
    case FORMAT_UNIT:
    case SEARCH_HIGH:
    case SEARCH_LOW:
    case SEARCH_EQUAL:
	return DISCONNECT_LONG;
    default:
	return DISCONNECT_NONE;
    }
}

/*
 * Assumes instance->time_expires has been set in higher level code.
 */

static int NCR5380_set_timer (struct Scsi_Host *instance) {
    struct Scsi_Host *tmp, **prev;
    
    cli();
    if (((struct NCR5380_hostdata *) (instance->host_data))->next_timer) {
	sti();
	return -1;
    }

    for (prev = &expires_first, tmp = expires_first; tmp; 
	prev = &(((struct NCR5380_hostdata *) tmp->host_data)->next_timer), 
	tmp = ((struct NCR5380_hostdata *) tmp->host_data)->next_timer)
	if (instance->time_expires < tmp->time_expires) 
	    break;
	   
    instance->next_timer = tmp;
    *prev = instance;
    timer_table[NCR5380_TIMER].expires = expires_first->time_expires;
    timer_active |= 1 << NCR5380_TIMER;
    sti;
    return 0;
}    

/* Doing something about unwanted rentrancy here might be useful */
void NCR5380_timer_fn(void) {
    struct Scsi_Host *instance;
    cli();
    for (; expires_first && expires_first->time_expires >= jiffies; ) {
	instance = ((NCR5380_hostdata *) expires_first->host_data)->
	    expires_next;
	((NCR5380_hostdata *) expires_first->host_data)->expires_next = 
	    NULL;
	((NCR5380_hostdata *) expires_first->host_data)->time_expires = 
	    0;
	expires_first = instance;
    }

    if (expires_first) {
	timer_table[NCR5380_TIMER].expires = ((NCR5380_hostdata *) 
	    expires_first->host_data)->time_expires;
	timer_active |= (1 << NCR5380_TIMER);
    } else {
	timer_table[NCR5380_TIMER].expires = 0;
	timer_active &= ~(1 << MCR5380_TIMER);
    }
    sti();

    run_main();
}
#endif /* def USLEEP */

static void NCR5380_all_init (void) {
    static int done = 0;
    if (!done) {
#if (NDEBUG & NDEBUG_INIT)
	printk("scsi : NCR5380_all_init()\n");
#endif
	done = 1;
#ifdef USLEEP
	timer_table[NCR5380_TIMER].expires = 0;
	timer_table[NCR5380_TIMER].fn = NCR5380_timer_fn;
#endif
    }
}

#ifdef AUTOPROBE_IRQ
/*
 * Function : int NCR5380_probe_irq (struct Scsi_Host *instance, int possible)
 * 
 * Purpose : autoprobe for the IRQ line used by the NCR5380.  
 *
 * Inputs : instance - pointer to this instance of the NCR5380 driver,
 *          possible - bitmask of permissable interrupts.
 *
 * Returns : number of the IRQ selected, IRQ_NONE if no interrupt fired.
 * 
 * XXX no effort is made to deal with spurious interrupts. 
 */


static int probe_irq;
static void probe_intr (int sig) {
    probe_irq = sig;
};
static struct sigaction probe_sigaction = { probe_intr, 0, SA_INTERRUPT,
    NULL};

static int NCR5380_probe_irq (struct Scsi_Host *instance, int possible) {
    NCR5380_local_declare();
    struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
	 instance->hostdata;
    unsigned long timeout;
    int trying_irqs, i, mask;
    NCR5380_setup(instance);

    for (trying_irqs = i = 0, mask = 1; i < 16; ++i, mask <<= 1) 
	if ((mask & possible) &&  (irqaction (i, &probe_sigaction) 
	    == 0)) 
	    trying_irqs |= mask;

    timeout = jiffies + 25;
    probe_irq = IRQ_NONE;

/*
 * A interrupt is triggered whenever BSY = false, SEL = true
 * and a bit set in the SELECT_ENABLE_REG is asserted on the 
 * SCSI bus.
 *
 * Note that the bus is only driven when the phase control signals
 * (I/O, C/D, and MSG) match those in the TCR, so we must reset that
 * to zero.
 */

    NCR5380_write(TARGET_COMMAND_REG, 0);
    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
    NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA | 
	ICR_ASSERT_SEL);

    while (probe_irq == IRQ_NONE && jiffies < timeout);

    NCR5380_write(SELECT_ENABLE_REG, 0);
    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);

    for (i = 0, mask = 1; i < 16; ++i, mask <<= 1)
	if (trying_irqs & mask) 
	    free_irq(i);

    return probe_irq;
}
#endif /* AUTOPROBE_IRQ */
 
/*
 * Function : void NCR58380_print_options (struct Scsi_Host *instance)
 *
 * Purpose : called by probe code indicating the NCR5380 driver
 *	     options that were selected.
 *
 * Inputs : instance, pointer to this instance.  Unused.
 */

static void NCR5380_print_options (struct Scsi_Host *instance) {
    printk(" generic options"
#ifdef AUTOPROBE_IRQ
    " AUTOPROBE_IRQ"
#endif
#ifdef AUTOSENSE 
    " AUTOSENSE"
#endif
#ifdef DIFFERENTIAL
    " DIFFERENTIAL"
#endif
#ifdef REAL_DMA
    " REAL DMA"
#endif
#ifdef REAL_DMA_POLL
    " REAL DMA POLL"
#endif
#ifdef PARITY
    " PARITY"
#endif
#ifdef PSEUDO_DMA
    " PSEUDO DMA"
#endif
#ifdef SCSI2
    " SCSI-2"
#endif
#ifdef UNSAFE
    " UNSAFE "
#endif
    );
#ifdef USLEEP
    printk(" USLEEP, USLEEP_POLL=%d USLEEP_SLEEP=%d", USLEEP_POLL, USLEEP_SLEEP);
#endif
    printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
}


/* 
 * Function : void NCR5380_init (struct Scsi_Host *instance)
 *
 * Purpose : initializies *instance and corresponding 5380 chip.
 *
 * Inputs : instance - instantiation of the 5380 driver.  
 *
 * Notes : I assume that the host, hostno, and id bits have been
 * 	set correctly.  I don't care about the irq and other fields. 
 * 
 */

static void NCR5380_init (struct Scsi_Host *instance) {
    NCR5380_local_declare();
    int i;
    struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) 
	instance->hostdata;
    NCR5380_setup(instance);

    NCR5380_all_init();

    hostdata->id_mask = 1 << instance->this_id;
    for (i = hostdata->id_mask; i <= 0x80; i <<= 1)
	if (i > hostdata->id_mask)
	    hostdata->id_higher_mask |= i;
    for (i = 0; i < 8; ++i)
	hostdata->busy[i] = 0;
#ifdef REAL_DMA
    hostdata->dmalen = 0;
#endif
    hostdata->connected = NULL;
    hostdata->issue_queue = NULL;
    hostdata->disconnected_queue = NULL;
    hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT;

    if (!the_template) {
	the_template = instance->hostt;
	first_instance = instance;
    }
	

#ifdef USLEEP
    hostdata->time_expires = 0;
    hostdata->next_timer = NULL;
#endif

#ifndef AUTOSENSE
    if ((instance->cmd_per_lun > 1) || instance->can_queue > 1)) 
	 printk("scsi%d : WARNING : support for multiple outstanding commands enabled\n"
	        "         without AUTOSENSE option, contigent alligence conditions may\n"
	        "         be incorrectly cleared.\n", instance->host_no);
#endif /* def AUTOSENSE */

    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
    NCR5380_write(MODE_REG, MR_BASE);
    NCR5380_write(TARGET_COMMAND_REG, 0);
    NCR5380_write(SELECT_ENABLE_REG, 0);
}

/* 
 * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd, 
 *	void (*done)(Scsi_Cmnd *)) 
 *
 * Purpose :  enqueues a SCSI command
 *
 * Inputs : cmd - SCSI command, done - function called on completion, with
 *	a pointer to the command descriptor.
 * 
 * Returns : 0
 *
 * Side effects : 
 *      cmd is added to the per instance issue_queue, with minor 
 *	twiddling done to the host specific fields of cmd.  If the 
 *	main coroutine is not running, it is restarted.
 *
 */

/* Only make static if a wrapper function is used */
#ifndef NCR5380_queue_command
static
#endif
int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) {
    struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
	cmd->host->hostdata;
    Scsi_Cmnd *tmp;

#if (NDEBUG & NDEBUG_NO_WRITE)
    switch (cmd->cmnd[0]) {
    case WRITE:
    case WRITE_10:
	printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n",
	    instance->host_no);
	cmd->result = (DID_ERROR << 16);
	done(cmd);
	return 0;
    }
#endif /* (NDEBUG & NDEBUG_NO_WRITE) */


    /* 
     * We use the host_scribble field as a pointer to the next command  
     * in a queue 
     */

    cmd->host_scribble = NULL;
    cmd->scsi_done = done;

    cmd->result = 0;

⌨️ 快捷键说明

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