?? ncr5380.c
字號:
* 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 + -