?? ncr5380.c
字號:
*/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#endifstatic 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_commandstatic#endifint 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; /* * Insert the cmd into the issue queue. Note that REQUEST SENSE * commands are added to the head of the queue since any command will * clear the contingent allegience condition that exists and the * sense data is only guranteed to be valid while the condition exists. */ cli(); if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) { cmd->host_scribble = (unsigned char *) hostdata->issue_queue; hostdata->issue_queue = cmd; } else { for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->host_scribble; tmp = (Scsi_Cmnd *) tmp->host_scribble); tmp->host_scribble = (unsigned char *) cmd; }#if (NDEBUG & NDEBUG_QUEUES) printk("scsi%d : command added to %s of queue\n", instance->host_no, (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail");#endif/* Run the coroutine if it isn't allready running. */ run_main(); return 0;}/* * Function : NCR5380_main (void) *
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -