📄 seagate.c
字号:
#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) return retcode (st0x_aborted);#endif DPRINTK (PHASE_SELECTION, "scsi%d : phase = SELECTION\n", hostno); 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 */#ifdef ARBITRATE save_flags (flags); cli (); WRITE_CONTROL (0); WRITE_DATA ((controller_type == SEAGATE) ? 0x80 : 0x40); WRITE_CONTROL (CMD_START_ARB); restore_flags (flags); ULOOP( ST0X_SELECTION_DELAY * 10000 ) { status_read = STATUS; if (status_read & STAT_ARB_CMPL) break; if (st0x_aborted) /* FIXME: What? We are going to do something even after abort? */ break; if (TIMEOUT || (status_read & STAT_SEL)) { printk( "scsi%d : arbitration lost or timeout.\n", hostno ); WRITE_CONTROL (BASE_CMD); return retcode (DID_NO_CONNECT); } } DPRINTK (PHASE_SELECTION, "scsi%d : arbitration complete\n", hostno);#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 around. * * 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. */ tmp_data = (unsigned char) ((1 << target) | (controller_type == SEAGATE ? 0x80 : 0x40)); tmp_control = BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL | (reselect ? CMD_ATTN : 0); save_flags(flags); cli();#ifdef OLDCNTDATASCEME#ifdef SWAPCNTDATA WRITE_CONTROL (tmp_control); WRITE_DATA (tmp_data);#else WRITE_DATA (tmp_data); WRITE_CONTROL (tmp_control);#endif#else tmp_control ^= CMD_BSY; /* This is guesswork. What used to be in driver */ WRITE_CONTROL (tmp_control); /* could never work: it sent data into control */ WRITE_DATA (tmp_data); /* register and control info into data. Hopefully */ tmp_control ^= CMD_BSY; /* fixed, but order of first two may be wrong. */ WRITE_CONTROL (tmp_control); /* -- pavel@ucw.cz */#endif restore_flags (flags); ULOOP( 250*1000 ) { if (st0x_aborted) {/* * 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. */ WRITE_CONTROL (BASE_CMD); if (STATUS & STAT_BSY) { printk ("scsi%d : BST asserted after we've been aborted.\n", hostno); seagate_st0x_reset (NULL, 0); return retcode (DID_RESET); } return retcode (st0x_aborted); } if (STATUS & STAT_BSY) break; if (TIMEOUT) { DPRINTK (PHASE_SELECTION, "scsi%d : NO CONNECT with target %d, stat = %x \n", hostno, target, STATUS); return retcode (DID_NO_CONNECT); } }/* 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 { DPRINTK (DEBUG_SG, "scsi%d : scatter gather not requested.\n", hostno); buffer = NULL; len = SCint->request_bufflen; data = (unsigned char *) SCint->request_buffer; } DPRINTK (PHASE_DATAIN | PHASE_DATAOUT, "scsi%d : len = %d\n", hostno, len); break;#ifdef LINKED case LINKED_RIGHT: break; case LINKED_WRONG: break;#endif } /* end of switch(reselect) *//* * 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. *//* GCC does not like an ifdef inside a macro, so do it the hard way. */#ifdef LINKED WRITE_CONTROL (BASE_CMD | CMD_DRVR_ENABLE | (((reselect == CAN_RECONNECT) || (reselect == LINKED_WRONG) )? CMD_ATTN : 0));#else WRITE_CONTROL (BASE_CMD | CMD_DRVR_ENABLE | (((reselect == CAN_RECONNECT) )? CMD_ATTN : 0));#endif/* * 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. * * SJT: The nasty-looking assembler is gone, so it's slower. * */ DPRINTK (PHASE_ETC, "scsi%d : phase = INFORMATION TRANSFER\n", hostno); 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. */ 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 ) { DPRINTK (DEBUG_FAST, "scsi%d : FAST transfer, underflow = %d, transfersize = %d\n" " len = %d, data = %08x\n", hostno, SCint->underflow, SCint->transfersize, len, data);/* SJT: Start. Fast Write */#ifdef SEAGATE_USE_ASM __asm__( "cld\n\t"#ifdef FAST32 "shr $2, %%ecx\n\t" "1:\t" "lodsl\n\t" "movl %%eax, (%%edi)\n\t"#else "1:\t" "lodsb\n\t" "movb %%al, (%%edi)\n\t"#endif "loop 1b;"/* output */ :/* input */ : "D" (phys_to_virt(st0x_dr)), "S" (data), "c" (SCint->transfersize) /* clobbered */ : "eax", "ecx", "esi" );#else /* SEAGATE_USE_ASM */ {#ifdef FAST32 unsigned int *iop = phys_to_virt (st0x_dr); const unsigned int *dp = (unsigned int *) data; int xferlen = transfersize >> 2;#else unsigned char *iop = phys_to_virt (st0x_dr); const unsigned char *dp = data; int xferlen = transfersize;#endif for (; xferlen; --xferlen) *iop = *dp++; }#endif /* SEAGATE_USE_ASM *//* SJT: End */ len -= transfersize; data += transfersize; DPRINTK (DEBUG_FAST, "scsi%d : FAST transfer complete len = %d data = %08x\n", hostno, len, data); } else {/* * We loop as long as we are in a data out phase, there is data to send, * and BSY is still active. *//* SJT: Start. Slow Write. */#ifdef SEAGATE_USE_ASMint __dummy_1,__dummy_2;/* * We loop as long as we are in a data out phase, there is data to send, * and BSY is still active. *//* Local variables : len = ecx , data = esi, st0x_cr_sr = ebx, st0x_dr = edi*/ __asm__ ( /* Test for any data here at all. */ "orl %%ecx, %%ecx\n\t" "jz 2f\n\t" "cld\n\t"/* "movl " SYMBOL_NAME_STR(st0x_cr_sr) ", %%ebx\n\t" *//* "movl " SYMBOL_NAME_STR(st0x_dr) ", %%edi\n\t" */ "1:\t" "movb (%%ebx), %%al\n\t" /* Test for BSY */ "test $1, %%al\n\t" "jz 2f\n\t" /* Test for data out phase - STATUS & REQ_MASK should be REQ_DATAOUT, which is 0. */ "test $0xe, %%al\n\t" "jnz 2f\n\t" /* Test for REQ */ "test $0x10, %%al\n\t" "jz 1b\n\t" "lodsb\n\t" "movb %%al, (%%edi)\n\t" "loop 1b\n\t" "2:\n"/* output */ : "=S" (data), "=c" (len) ,"=b" (__dummy_1) ,"=D" (__dummy_2)/* input */ : "0" (data), "1" (len), "2" (phys_to_virt(st0x_cr_sr)), "3" (phys_to_virt(st0x_dr)) /* clobbered */ : "eax"); #else /* SEAGATE_USE_ASM */ while (len) { unsigned char stat; stat = STATUS; if (!(stat & STAT_BSY) || ((stat & REQ_MASK) != REQ_DATAOUT)) break; if (stat & STAT_REQ) { WRITE_DATA (*data++); --len; } }#endif /* SEAGATE_USE_ASM *//* SJT: End. */ } if (!len && nobuffs) { --nobuffs; ++buffer; len = buffer->length; data = (unsigned char *) buffer->address; DPRINTK (DEBUG_SG, "scsi%d : next scatter-gather buffer len = %d address = %08x\n",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -