📄 seagate.c
字号:
for (j = 0; !base_address && j < NUM_SIGNATURES; ++j) if (check_signature (seagate_bases[i] + signatures[j].offset, signatures[j].signature, signatures[j].length)) { base_address = seagate_bases[i]; controller_type = signatures[j].type; }#endif /* OVERRIDE */ } /* (! controller_type) */ tpnt->this_id = (controller_type == SEAGATE) ? 7 : 6; tpnt->name = (controller_type == SEAGATE) ? ST0X_ID_STR : FD_ID_STR; if (!base_address) { DANY ("ST0x / TMC-8xx not detected.\n"); return 0; } st0x_cr_sr = base_address + (controller_type == SEAGATE ? 0x1a00 : 0x1c00); st0x_dr = st0x_cr_sr + 0x200; DANY ("%s detected. Base address = %x, cr = %x, dr = %x\n", tpnt->name, base_address, st0x_cr_sr, st0x_dr);/* * At all times, we will use IRQ 5. Should also check for IRQ3 if we * loose our first interrupt. */ instance = scsi_register (tpnt, 0); hostno = instance->host_no; if (request_irq (irq, do_seagate_reconnect_intr, SA_INTERRUPT, (controller_type == SEAGATE) ? "seagate" : "tmc-8xx", NULL)) { printk ("scsi%d : unable to allocate IRQ%d\n", hostno, irq); return 0; } instance->irq = irq; instance->io_port = base_address;#ifdef SLOW_RATE printk( "Calibrating borken timer... " ); borken_init (); printk( " %d cycles per transfer\n", borken_calibration );#endif printk( "This is one second... " ); { int clock; ULOOP( 1*1000*1000 ) { volatile int x = STATUS; if (TIMEOUT) break; } } printk ("done, %s options:"#ifdef ARBITRATE " ARBITRATE"#endif#ifdef DEBUG " DEBUG"#endif#ifdef FAST " FAST"#ifdef FAST32 "32"#endif#endif#ifdef LINKED " LINKED"#endif#ifdef PARITY " PARITY"#endif#ifdef SEAGATE_USE_ASM " SEAGATE_USE_ASM"#endif#ifdef SLOW_RATE " SLOW_RATE"#endif#ifdef SWAPSTAT " SWAPSTAT"#endif#ifdef SWAPCNTDATA " SWAPCNTDATA"#endif "\n", tpnt->name); return 1;}const char *seagate_st0x_info (struct Scsi_Host *shpnt){ static char buffer[64]; sprintf (buffer, "%s at irq %d, address 0x%05X", (controller_type == SEAGATE) ? ST0X_ID_STR : FD_ID_STR, irq, base_address); return buffer;}/* * These are our saved pointers for the outstanding command that is * waiting for a reconnect */static unsigned char current_target, current_lun;static unsigned char *current_cmnd, *current_data;static int current_nobuffs;static struct scatterlist *current_buffer;static int current_bufflen;#ifdef LINKED/* * linked_connected indicates whether or not we are currently connected to * linked_target, linked_lun and in an INFORMATION TRANSFER phase, * using linked commands. */static int linked_connected = 0;static unsigned char linked_target, linked_lun;#endifstatic void (*done_fn) (Scsi_Cmnd *) = NULL;static Scsi_Cmnd *SCint = NULL;/* * These control whether or not disconnect / reconnect will be attempted, * or are being attempted. */#define NO_RECONNECT 0#define RECONNECT_NOW 1#define CAN_RECONNECT 2/* * LINKED_RIGHT indicates that we are currently connected to the correct target * for this command, LINKED_WRONG indicates that we are connected to the wrong * target. Note that these imply CAN_RECONNECT and require defined(LINKED). */#define LINKED_RIGHT 3#define LINKED_WRONG 4/* * This determines if we are expecting to reconnect or not. */static int should_reconnect = 0;/* * The seagate_reconnect_intr routine is called when a target reselects the * host adapter. This occurs on the interrupt triggered by the target * asserting SEL. */static void do_seagate_reconnect_intr (int irq, void *dev_id, struct pt_regs *regs){ unsigned long flags; spin_lock_irqsave(&io_request_lock, flags); seagate_reconnect_intr(irq, dev_id, regs); spin_unlock_irqrestore(&io_request_lock, flags);}static void seagate_reconnect_intr (int irq, void *dev_id, struct pt_regs *regs){ int temp; Scsi_Cmnd *SCtmp; DPRINTK (PHASE_RESELECT, "scsi%d : seagate_reconnect_intr() called\n", hostno); if (!should_reconnect) printk ("scsi%d: unexpected interrupt.\n", hostno); else { should_reconnect = 0; DPRINTK (PHASE_RESELECT, "scsi%d : internal_command(" "%d, %08x, %08x, RECONNECT_NOW\n", hostno, current_target, current_data, current_bufflen); temp = internal_command (current_target, current_lun, current_cmnd, current_data, current_bufflen, RECONNECT_NOW); if (msg_byte (temp) != DISCONNECT) { if (done_fn) { DPRINTK (PHASE_RESELECT, "scsi%d : done_fn(%d,%08x)", hostno, hostno, temp); if (!SCint) panic ("SCint == NULL in seagate"); SCtmp = SCint; SCint = NULL; SCtmp->result = temp; done_fn (SCtmp); } else printk ("done_fn() not defined.\n"); } }}/* * The seagate_st0x_queue_command() function provides a queued interface * to the seagate SCSI driver. Basically, it just passes control onto the * seagate_command() function, after fixing it so that the done_fn() * is set to the one passed to the function. We have to be very careful, * because there are some commands on some devices that do not disconnect, * and if we simply call the done_fn when the command is done then another * command is started and queue_command is called again... We end up * overflowing the kernel stack, and this tends not to be such a good idea. */static int recursion_depth = 0;int seagate_st0x_queue_command (Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)){ int result, reconnect; Scsi_Cmnd *SCtmp; DANY( "seagate: que_command" ); done_fn = done; current_target = SCpnt->target; current_lun = SCpnt->lun; (const void *) current_cmnd = SCpnt->cmnd; current_data = (unsigned char *) SCpnt->request_buffer; current_bufflen = SCpnt->request_bufflen; SCint = SCpnt; if (recursion_depth) return 0; recursion_depth++; do {#ifdef LINKED/* * Set linked command bit in control field of SCSI command. */ current_cmnd[SCpnt->cmd_len] |= 0x01; if (linked_connected) { DPRINTK (DEBUG_LINKED, "scsi%d : using linked commands, current I_T_L nexus is ", hostno); if ((linked_target == current_target) && (linked_lun == current_lun)) { DPRINTK (DEBUG_LINKED, "correct\n"); reconnect = LINKED_RIGHT; } else { DPRINTK (DEBUG_LINKED, "incorrect\n"); reconnect = LINKED_WRONG; } } else#endif /* LINKED */ reconnect = CAN_RECONNECT; result = internal_command (SCint->target, SCint->lun, SCint->cmnd, SCint->request_buffer, SCint->request_bufflen, reconnect); if (msg_byte (result) == DISCONNECT) break; SCtmp = SCint; SCint = NULL; SCtmp->result = result; done_fn (SCtmp); } while (SCint); recursion_depth--; return 0;}int seagate_st0x_command (Scsi_Cmnd * SCpnt){ return internal_command (SCpnt->target, SCpnt->lun, SCpnt->cmnd, SCpnt->request_buffer, SCpnt->request_bufflen, (int) NO_RECONNECT);}static int internal_command (unsigned char target, unsigned char lun, const void *cmnd, void *buff, int bufflen, int reselect){ unsigned char *data = NULL; struct scatterlist *buffer = NULL; int clock, temp, nobuffs = 0, done = 0, len = 0; unsigned long flags;#ifdef DEBUG int transfered = 0, phase = 0, newphase;#endif register unsigned char status_read; unsigned char tmp_data, tmp_control, status = 0, message = 0; unsigned transfersize = 0, underflow = 0;#ifdef SLOW_RATE int borken = (int) SCint->device->borken; /* Does the current target require Very Slow I/O ? */#endif incommand = 0; st0x_aborted = 0;#if (DEBUG & PRINT_COMMAND) printk ("scsi%d : target = %d, command = ", hostno, target); print_command ((unsigned char *) cmnd);#endif#if (DEBUG & PHASE_RESELECT) switch (reselect) { case RECONNECT_NOW: printk ("scsi%d : reconnecting\n", hostno); break;#ifdef LINKED case LINKED_RIGHT: printk ("scsi%d : connected, can reconnect\n", hostno); break; case LINKED_WRONG: printk ("scsi%d : connected to wrong target, can reconnect\n", hostno); break;#endif case CAN_RECONNECT: printk ("scsi%d : allowed to reconnect\n", hostno); break; default: printk ("scsi%d : not allowed to reconnect\n", hostno); }#endif if (target == (controller_type == SEAGATE ? 7 : 6)) return DID_BAD_TARGET;/* * We work it differently depending on if this is is "the first time," * or a reconnect. If this is a reselect phase, then SEL will * be asserted, and we must skip selection / arbitration phases. */ switch (reselect) { case RECONNECT_NOW: DPRINTK ( PHASE_RESELECT, "scsi%d : phase RESELECT \n", hostno);/* * At this point, we should find the logical or of our ID and the original * target's ID on the BUS, with BSY, SEL, and I/O signals asserted. * * After ARBITRATION phase is completed, only SEL, BSY, and the * target ID are asserted. A valid initiator ID is not on the bus * until IO is asserted, so we must wait for that. */ ULOOP( 100*1000 ) { temp = STATUS; if ((temp & STAT_IO) && !(temp & STAT_BSY)) break; if (TIMEOUT) { DPRINTK (PHASE_RESELECT, "scsi%d : RESELECT timed out while waiting for IO .\n", hostno); return (DID_BAD_INTR << 16); } }/* * After I/O is asserted by the target, we can read our ID and its * ID off of the BUS. */ if (!((temp = DATA) & (controller_type == SEAGATE ? 0x80 : 0x40))) { DPRINTK (PHASE_RESELECT, "scsi%d : detected reconnect request to different target.\n" "\tData bus = %d\n", hostno, temp); return (DID_BAD_INTR << 16); } if (!(temp & (1 << current_target))) { printk ("scsi%d : Unexpected reselect interrupt. Data bus = %d\n", hostno, temp); return (DID_BAD_INTR << 16); } buffer = current_buffer; cmnd = current_cmnd; /* WDE add */ data = current_data; /* WDE add */ len = current_bufflen; /* WDE add */ nobuffs = current_nobuffs;/* * We have determined that we have been selected. At this point, * we must respond to the reselection by asserting BSY ourselves */#if 1 WRITE_CONTROL (BASE_CMD | CMD_DRVR_ENABLE | CMD_BSY);#else WRITE_CONTROL (BASE_CMD | CMD_BSY);#endif/* * The target will drop SEL, and raise BSY, at which time we must drop * BSY. */ ULOOP( 100*1000 ) { if (!(STATUS & STAT_SEL)) break; if (TIMEOUT) { WRITE_CONTROL (BASE_CMD | CMD_INTR); DPRINTK (PHASE_RESELECT, "scsi%d : RESELECT timed out while waiting for SEL.\n", hostno); return (DID_BAD_INTR << 16); } } WRITE_CONTROL (BASE_CMD);/* * At this point, we have connected with the target and can get * on with our lives. */ break; case CAN_RECONNECT:#ifdef LINKED/* * This is a bletcherous hack, just as bad as the Unix #! interpreter stuff. * If it turns out we are using the wrong I_T_L nexus, the easiest way to deal * with it is to go into our INFORMATION TRANSFER PHASE code, send a ABORT * message on MESSAGE OUT phase, and then loop back to here. */ connect_loop:#endif DPRINTK (PHASE_BUS_FREE, "scsi%d : phase = BUS FREE \n", hostno);/* * BUS FREE PHASE * * On entry, we make sure that the BUS is in a BUS FREE * phase, by insuring that both BSY and SEL are low for * at least one bus settle delay. Several reads help * eliminate wire glitch. */#ifndef ARBITRATE#error FIXME: this is broken: we may not use jiffies here - we are under cli(). It will hardlock. clock = jiffies + ST0X_BUS_FREE_DELAY; while (((STATUS | STATUS | STATUS) & (STAT_BSY | STAT_SEL)) && (!st0x_aborted) && time_before(jiffies, clock)); if (time_after(jiffies, clock)) return retcode (DID_BUS_BUSY); else if (st0x_aborted)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -