?? seagate.c
字號:
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) { int len = 0; unsigned char *data = NULL; struct scatterlist *buffer = NULL; int nobuffs = 0; int clock; int temp;#ifdef SLOW_HANDSHAKE int borken; /* Does the current target require Very Slow I/O ? */#endif#if (DEBUG & PHASE_DATAIN) || (DEBUG & PHASE_DATOUT) int transfered = 0;#endif#if (((DEBUG & PHASE_ETC) == PHASE_ETC) || (DEBUG & PRINT_COMMAND) || \ (DEBUG & PHASE_EXIT)) int i;#endif#if ((DEBUG & PHASE_ETC) == PHASE_ETC) int phase=0, newphase;#endif int done = 0; unsigned char status = 0; unsigned char message = 0; register unsigned char status_read; unsigned transfersize = 0, underflow = 0; incommand = 0; st0x_aborted = 0;#ifdef SLOW_HANDSHAKE borken = (int) scsi_devices[SCint->index].borken;#endif#if (DEBUG & PRINT_COMMAND) printk ("scsi%d : target = %d, command = ", hostno, target); print_command((unsigned char *) cmnd); printk("\n");#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 reselct phase, then SEL will * be asserted, and we must skip selection / arbitration phases. */ switch (reselect) { case RECONNECT_NOW:#if (DEBUG & PHASE_RESELECT) printk("scsi%d : phase RESELECT \n", hostno);#endif/* * 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 initator ID is not on the bus * until IO is asserted, so we must wait for that. */ for (clock = jiffies + 10, temp = 0; (jiffies < clock) && !(STATUS & STAT_IO);); if (jiffies >= clock) {#if (DEBUG & PHASE_RESELECT) printk("scsi%d : RESELECT timed out while waiting for IO .\n", hostno);#endif 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))) {#if (DEBUG & PHASE_RESELECT) printk("scsi%d : detected reconnect request to different target.\n" "\tData bus = %d\n", hostno, temp);#endif 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 CONTROL = (BASE_CMD | CMD_DRVR_ENABLE | CMD_BSY);#else CONTROL = (BASE_CMD | CMD_BSY);#endif/* * The target will drop SEL, and raise BSY, at which time we must drop * BSY. */ for (clock = jiffies + 10; (jiffies < clock) && (STATUS & STAT_SEL);); if (jiffies >= clock) { CONTROL = (BASE_CMD | CMD_INTR);#if (DEBUG & PHASE_RESELECT) printk("scsi%d : RESELECT timed out while waiting for SEL.\n", hostno);#endif return (DID_BAD_INTR << 16); } 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#if (DEBUG & PHASE_BUS_FREE) printk ("scsi%d : phase = BUS FREE \n", hostno);#endif/* * 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. */ clock = jiffies + ST0X_BUS_FREE_DELAY; #if !defined (ARBITRATE) while (((STATUS | STATUS | STATUS) & (STAT_BSY | STAT_SEL)) && (!st0x_aborted) && (jiffies < clock)); if (jiffies > clock) return retcode(DID_BUS_BUSY); else if (st0x_aborted) return retcode(st0x_aborted);#endif#if (DEBUG & PHASE_SELECTION) printk("scsi%d : phase = SELECTION\n", hostno);#endif clock = jiffies + ST0X_SELECTION_DELAY;/* * Arbitration/selection procedure : * 1. Disable drivers * 2. Write HOST adapter address bit * 3. Set start arbitration. * 4. We get either ARBITRATION COMPLETE or SELECT at this * point. * 5. OR our ID and targets on bus. * 6. Enable SCSI drivers and asserted SEL and ATTN */ #if defined(ARBITRATE) cli(); CONTROL = 0; DATA = (controller_type == SEAGATE) ? 0x80 : 0x40; CONTROL = CMD_START_ARB; sti(); while (!((status_read = STATUS) & (STAT_ARB_CMPL | STAT_SEL)) && (jiffies < clock) && !st0x_aborted); if (!(status_read & STAT_ARB_CMPL)) {#if (DEBUG & PHASE_SELECTION) if (status_read & STAT_SEL) printk("scsi%d : arbitration lost\n", hostno); else printk("scsi%d : arbitration timeout.\n", hostno);#endif CONTROL = BASE_CMD; return retcode(DID_NO_CONNECT); };#if (DEBUG & PHASE_SELECTION) printk("scsi%d : arbitration complete\n", hostno);#endif#endif/* * When the SCSI device decides that we're gawking at it, it will * respond by asserting BUSY on the bus. * * Note : the Seagate ST-01/02 product manual says that we should * twiddle the DATA register before the control register. However, * this does not work reliably so we do it the other way arround. * * Probably could be a problem with arbitration too, we really should * try this with a SCSI protocol or logic analyzer to see what is * going on. */ cli(); DATA = (unsigned char) ((1 << target) | (controller_type == SEAGATE ? 0x80 : 0x40)); CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL | (reselect ? CMD_ATTN : 0); sti(); while (!((status_read = STATUS) & STAT_BSY) && (jiffies < clock) && !st0x_aborted)#if 0 && (DEBUG & PHASE_SELECTION) { temp = clock - jiffies; if (!(jiffies % 5)) printk("seagate_st0x_timeout : %d \r",temp); } printk("Done. \n"); printk("scsi%d : status = %02x, seagate_st0x_timeout = %d, aborted = %02x \n", hostno, status_read, temp, st0x_aborted);#else ;#endif if ((jiffies >= clock) && !(status_read & STAT_BSY)) {#if (DEBUG & PHASE_SELECTION) printk ("scsi%d : NO CONNECT with target %d, status = %x \n", hostno, target, STATUS);#endif return retcode(DID_NO_CONNECT); }/* * If we have been aborted, and we have a command in progress, IE the * target still has BSY asserted, then we will reset the bus, and * notify the midlevel driver to expect sense. */ if (st0x_aborted) { CONTROL = BASE_CMD; if (STATUS & STAT_BSY) { printk("scsi%d : BST asserted after we've been aborted.\n", hostno); seagate_st0x_reset(NULL); return retcode(DID_RESET); } return retcode(st0x_aborted); } /* Establish current pointers. Take into account scatter / gather */ if ((nobuffs = SCint->use_sg)) {#if (DEBUG & DEBUG_SG) { int i; printk("scsi%d : scatter gather requested, using %d buffers.\n", hostno, nobuffs); for (i = 0; i < nobuffs; ++i) printk("scsi%d : buffer %d address = %08x length = %d\n", hostno, i, buffer[i].address, buffer[i].length); }#endif buffer = (struct scatterlist *) SCint->buffer; len = buffer->length; data = (unsigned char *) buffer->address; } else {#if (DEBUG & DEBUG_SG) printk("scsi%d : scatter gather not requested.\n", hostno);#endif buffer = NULL; len = SCint->request_bufflen; data = (unsigned char *) SCint->request_buffer; }#if (DEBUG & (PHASE_DATAIN | PHASE_DATAOUT)) printk("scsi%d : len = %d\n", hostno, len);#endif break;#ifdef LINKED case LINKED_RIGHT: break; case LINKED_WRONG: break;#endif }/* * There are several conditions under which we wish to send a message : * 1. When we are allowing disconnect / reconnect, and need to establish * the I_T_L nexus via an IDENTIFY with the DiscPriv bit set. * * 2. When we are doing linked commands, are have the wrong I_T_L nexus * established and want to send an ABORT message. */ CONTROL = BASE_CMD | CMD_DRVR_ENABLE | (((reselect == CAN_RECONNECT)#ifdef LINKED || (reselect == LINKED_WRONG)#endif ) ? CMD_ATTN : 0) ; /* * INFORMATION TRANSFER PHASE * * The nasty looking read / write inline assembler loops we use for * DATAIN and DATAOUT phases are approximately 4-5 times as fast as * the 'C' versions - since we're moving 1024 bytes of data, this * really adds up. */#if ((DEBUG & PHASE_ETC) == PHASE_ETC) printk("scsi%d : phase = INFORMATION TRANSFER\n", hostno);#endif incommand = 1; transfersize = SCint->transfersize; underflow = SCint->underflow;/* * Now, we poll the device for status information, * and handle any requests it makes. Note that since we are unsure of * how much data will be flowing across the system, etc and cannot * make reasonable timeouts, that we will instead have the midlevel * driver handle any timeouts that occur in this phase. */ while (((status_read = STATUS) & STAT_BSY) && !st0x_aborted && !done) {#ifdef PARITY if (status_read & STAT_PARITY) { printk("scsi%d : got parity error\n", hostno); st0x_aborted = DID_PARITY; } #endif if (status_read & STAT_REQ) {#if ((DEBUG & PHASE_ETC) == PHASE_ETC) if ((newphase = (status_read & REQ_MASK)) != phase) { phase = newphase; switch (phase) { case REQ_DATAOUT: printk("scsi%d : phase = DATA OUT\n", hostno); break; case REQ_DATAIN : printk("scsi%d : phase = DATA IN\n", hostno); break; case REQ_CMDOUT : printk("scsi%d : phase = COMMAND OUT\n", hostno); break; case REQ_STATIN : printk("scsi%d : phase = STATUS IN\n", hostno); break; case REQ_MSGOUT : printk("scsi%d : phase = MESSAGE OUT\n", hostno); break; case REQ_MSGIN : printk("scsi%d : phase = MESSAGE IN\n", hostno); break; default : printk("scsi%d : phase = UNKNOWN\n", hostno); st0x_aborted = DID_ERROR; } }#endif switch (status_read & REQ_MASK) { case REQ_DATAOUT : /* * If we are in fast mode, then we simply splat the data out * in word-sized chunks as fast as we can. */#ifdef FAST if (!len) {#if 0 printk("scsi%d: underflow to target %d lun %d \n", hostno, target, lun); st0x_aborted = DID_ERROR; fast = 0;#endif break;}if (fast && transfersize && !(len % transfersize) && (len >= transfersize)#ifdef FAST32 && !(transfersize % 4)#endif ) {#if (DEBUG & DEBUG_FAST) printk("scsi%d : FAST transfer, underflow = %d, transfersize = %d\n" " len = %d, data = %08x\n", hostno, SCint->underflow, SCint->transfersize, len, data);#endif __asm__(" cld;"#ifdef FAST32" shr $2, %%ecx;1: lodsl; movl %%eax, (%%edi);"#else"1: lodsb; movb %%al, (%%edi);"#endif" loop 1b;" : : /* input */ "D" (st0x_dr), "S" (data), "c" (SCint->transfersize) : /* clobbered */ "eax", "ecx", "esi" ); len -= transfersize; data += transfersize;#if (DEBUG & DEBUG_FAST) printk("scsi%d : FAST transfer complete len = %d data = %08x\n", hostno, len, data);#endif} else #endif{/* * We loop as long as we are in a data out phase, there is data to send, * and BSY is still active. */ __asm__ (/* Local variables : len = ecx data = esi st0x_cr_sr = ebx st0x_dr = edi Test for any data here at all.*/ "\torl %%ecx, %%ecx jz 2f cld movl _st0x_cr_sr, %%ebx movl _st0x_dr, %%edi 1: movb (%%ebx), %%al\n"/* Test for BSY*/ "\ttest $1, %%al jz 2f\n"/* Test for data out phase - STATUS & REQ_MASK should be REQ_DATAOUT, which is 0.*/ "\ttest $0xe, %%al jnz 2f \n"/* Test for REQ*/ "\ttest $0x10, %%al jz 1b lodsb movb %%al, (%%edi) loop 1b2: ":/* output */"=S" (data), "=c" (len) :/* input */"0" (data), "1" (len) :/* clobbered */"eax", "ebx", "edi"); } if (!len && nobuffs) { --nobuffs;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -