⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ncr5380.c

📁 LINUX 1.0 内核c源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
      } else {
        printk("No overrun??\n");
        cnt = toPIO = 2;
      }
#if (NDEBUG & NDEBUG_DMA)
      printk("Doing %d-byte PIO to 0x%X\n", cnt, *data);
#endif
      NCR5380_transfer_pio(instance, phase, &cnt, data);
      *count -= toPIO - cnt;
    }
#endif        

#if (NDEBUG & NDEBUG_DMA)
     printk("Return with data ptr = 0x%X, count %d, last 0x%X, next 0x%X\n",
	    *data, *count, *(*data+*count-1), *(*data+*count));
#endif
     return 0;
     
#elif defined(REAL_DMA)
    return 0;
#else /* defined(REAL_DMA_POLL) */
    if (p & SR_IO) {
	if (!(foo = NCR5380_pread(instance, d, c - 1))) {
	    /*
	     * We can't disable DMA mode after successfully transfering 
	     * what we plan to be the last byte, since that would open up
	     * a race condition where if the target asserted REQ before 
	     * we got the DMA mode reset, the NCR5380 would have latched
	     * an additional byte into the INPUT DATA register and we'd
	     * have dropped it.
	     * 
	     * The workarround was to transfer one fewer bytes than we 
	     * intended to with the pseudo-DMA read function, wait for 
	     * the chip to latch the last byte, read it, and then disable
	     * pseudo-DMA mode.
	     * 
	     * After REQ is asserted, the NCR5380 asserts DRQ and ACK.
	     * REQ is deasserted when ACK is asserted, and not reasserted
	     * until ACK goes false.  Since the NCR5380 won't lower ACK
	     * until DACK is asserted, which won't happen unless we twiddle
	     * the DMA port or we take the NCR5380 out of DMA mode, we 
	     * can gurantee that we won't handshake another extra 
	     * byte.
    	     */

	    while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ));
	    /* Wait for clean handshake */
	    while (NCR5380_read(STATUS_REG) & SR_REQ);
	    d[c - 1] = NCR5380_read(INPUT_DATA_REG);
	}
    } else {
	int timeout;
	if (!(foo = NCR5380_pwrite(instance, d, c))) {
	    /*
	     * Wait for the last byte to be sent.  If REQ is being asserted for 
	     * the byte we're interested, we'll ACK it and it will go false.  
	     */
	    if (!(hostdata->flags & FLAG_HAS_LAST_BYTE_SENT)) {
		timeout = 20000;
#if 1
#if 1
		while (!(NCR5380_read(BUS_AND_STATUS_REG) & 
			BASR_DRQ) && (NCR5380_read(BUS_AND_STATUS_REG) &
		        BASR_PHASE_MATCH));
#else
		if (NCR5380_read(STATUS_REG) & SR_REQ) {
		    for (; timeout && 
			!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK); 
			--timeout);
		    for (; timeout && (NCR5380_read(STATUS_REG) & SR_REQ);
			--timeout);
		} 
#endif
	

#if (NDEBUG & NDEBUG_LAST_BYTE_SENT)
		if (!timeout) 
		    printk("scsi%d : timed out on last byte\n",
			    instance->host_no);
#endif


		if (hostdata->flags & FLAG_CHECK_LAST_BYTE_SENT) {
		    hostdata->flags &= ~FLAG_CHECK_LAST_BYTE_SENT;
		    if (NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT) {
			hostdata->flags |= FLAG_HAS_LAST_BYTE_SENT;
#if (NDEBUG & NDEBUG_LAST_BYTE_SENT)
			printk("scsi%d : last bit sent works\n", 
			    instance->host_no);
#endif
		    }
		}
	    } else 
		while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT));
#else
	    udelay (5);
#endif
	}
    }

    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
    NCR5380_write(MODE_REG, MR_BASE);

    *data = d + c;
    *count = 0;
    *phase = (NCR5380_read(STATUS_REG & PHASE_MASK));
#if defined(PSEUDO_DMA) && !defined(UNSAFE)
    sti();
#endif /* defined(REAL_DMA_POLL) */
    return foo;
#endif /* def REAL_DMA */
}
#endif /* defined(REAL_DMA) | defined(PSEUDO_DMA) */

/*
 * Function : NCR5380_information_transfer (struct Scsi_Host *instance)
 *
 * Purpose : run through the various SCSI phases and do as the target 
 * 	directs us to.  Operates on the currently connected command, 
 *	instance->connected.
 *
 * Inputs : instance, instance for which we are doing commands
 *
 * Side effects : SCSI things happen, the disconnected queue will be 
 *	modified if a command disconnects, *instance->connected will
 *	change.
 *
 * XXX Note : we need to watch for bus free or a reset condition here 
 * 	to recover from an unexpected bus free condition.
 */
 
static void NCR5380_information_transfer (struct Scsi_Host *instance) {
    NCR5380_local_declare();
    struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) 
	instance->hostdata;
    unsigned char msgout = NOP;
    int len, transfersize;
    unsigned char *data;
    unsigned char phase, tmp, old_phase=0xff;
    Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
    NCR5380_setup(instance);

    while (1) {
	tmp = NCR5380_read(STATUS_REG);
	/* We only have a valid SCSI phase when REQ is asserted */
	if (tmp & SR_REQ) {
	    phase = (tmp & PHASE_MASK); 
	    if (phase != old_phase) {
		old_phase = phase;
#if (NDEBUG & NDEBUG_INFORMATION)
		NCR5380_print_phase(instance);
#endif
	    }
	    switch (phase) {
	    case PHASE_DATAIN:
	    case PHASE_DATAOUT:
#if (NDEBUG & NDEBUG_NO_DATAOUT)
		printk("scsi%d : NDEBUG_NO_DATAOUT set, attempted DATAOUT aborted\n",
		    instance->host_no);
		msgout = ABORT;
		NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
		break;
#endif
		/* 
		 * If there is no room left in the current buffer in the
		 * scatter-gather list, move onto the next one.
		 */

		if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
		    ++cmd->SCp.buffer;
		    --cmd->SCp.buffers_residual;
		    cmd->SCp.this_residual = cmd->SCp.buffer->length;
		    cmd->SCp.ptr = cmd->SCp.buffer->address;
#if (NDEBUG & NDEBUG_INFORMATION)
		    printk("scsi%d : %d bytes and %d buffers left\n",
			instance->host_no, cmd->SCp.this_residual,
			cmd->SCp.buffers_residual);
#endif
		}

		/*
		 * The preffered transfer method is going to be 
		 * PSEUDO-DMA for systems that are strictly PIO,
		 * since we can let the hardware do the handshaking.
		 *
		 * For this to work, we need to know the transfersize
		 * ahead of time, since the pseudo-DMA code will sit
		 * in an unconditional loop.
		 */

#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
#ifdef NCR5380_dma_xfer_len
		if (!scsi_devices[cmd->index].borken &&
		    (transfersize = NCR5380_dma_xfer_len(instance, cmd)) != 0) {
#else
		if (!scsi_devices[cmd->index].borken && 
		    (transfersize = cmd->transfersize) && 
		    cmd->SCp.this_residual && !(cmd->SCp.this_residual % 
		    transfersize)) {
#endif
		    len = transfersize;
		    if (NCR5380_transfer_dma(instance, &phase,
			&len, (unsigned char **) &cmd->SCp.ptr)) {
			/*
			 * If the watchdog timer fires, all future accesses to this
			 * device will use the polled-IO.
			 */ 
			printk("scsi%d : switching target %d lun %d to slow handshake\n",
			    instance->host_no, cmd->target, cmd->lun);
			scsi_devices[cmd->index].borken = 1;
			NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | 
			    ICR_ASSERT_ATN);
			msgout = ABORT;
		    } else
			cmd->SCp.this_residual -= transfersize - len;
		} else
#endif /* defined(REAL_DMA) || defined(REAL_DMA_POLL) */
		  NCR5380_transfer_pio(instance, &phase, 
		    (int *) &cmd->SCp.this_residual, (unsigned char **)
		    &cmd->SCp.ptr);
		break;
	    case PHASE_MSGIN:
		/* 
		 * XXX - we don't handle multi-byte messages here, since we 
		 * shouldn't get them after the I_T_L_Q nexus is established
		 * for tagged queuing, and the host should initiate any 
		 * negotiations for sync. SCSI, etc.
		 */
		len = 1;
		data = &tmp;
		NCR5380_transfer_pio(instance, &phase, &len, &data);
		cmd->SCp.Message = tmp;

		switch (tmp) {
		/*
		 * Linking lets us reduce the time required to get the 
		 * next command out to the device, hopefully this will
		 * mean we don't waste another revolution due to the delays
		 * required by ARBITRATION and another SELECTION.
		 *
		 * In the current implementation proposal, low level drivers
		 * merely have to start the next command, pointed to by 
		 * next_link, done() is called as with unlinked commands.
		 */
#ifdef LINKED
		case LINKED_CMD_COMPLETE:
		case LINKED_FLG_CMD_COMPLETE:
#if (NDEBUG & NDEBUG_LINKED) 
		    printk("scsi%d : target %d lun %d linked command complete.\n",
			instance->host_no, cmd->target, cmd->lun);
#endif
		    /* 
		     * Sanity check : A linked command should only terminate with
		     * one of these messages if there are more linked commands
		     * available.
		     */

		    if (!cmd->next_link) {
			 printk("scsi%d : target %d lun %d linked command complete, no next_link\n"
			    instance->host_no, cmd->target, cmd->lun);
			    msgout = ABORT;
			    NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
				ICR_ASSERT_ATN);
			    break;
		    }

		    initialize_SCp(cmd->next_link);
		    /* The next command is still part of this process */
		    cmd->next_link->tag = cmd->tag;
		    cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); 
#if (NDEBUG & NDEBUG_LINKED) 
		    printk("scsi%d : target %d lun %d linked request done, calling scsi_done().\n",
			instance->host_no, cmd->target, cmd->lun);
#endif
		    cmd->scsi_done(cmd);
		    cmd = hostdata->connected;
		    break;
#endif /* def LINKED */
		case ABORT:
		case COMMAND_COMPLETE: 
		    hostdata->connected = NULL;
#if (NDEBUG & NDEBUG_QUEUES)
		    printk("scsi%d : command for target %d, lun %d completed\n",
			instance->host_no, cmd->target, cmd->lun);
#endif
		    hostdata->busy[cmd->target] &= ~(1 << cmd->lun);

		    /* 
		     * I'm not sure what the correct thing to do here is : 
		     * 
		     * If the command that just executed is NOT a request 
		     * sense, the obvious thing to do is to set the result
		     * code to the values of the stored parameters.
		     * 
		     * If it was a REQUEST SENSE command, we need some way 
		     * to differentiate between the failure code of the original
		     * and the failure code of the REQUEST sense - the obvious
		     * case is success, where we fall through and leave the result
		     * code unchanged.
		     * 
		     * The non-obvious place is where the REQUEST SENSE failed 
		     */

		    if (cmd->cmnd[0] != REQUEST_SENSE) 
			cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); 
		    else if (cmd->SCp.Status != GOOD)
			cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
		    
#ifdef AUTOSENSE
		    if ((cmd->cmnd[0] != REQUEST_SENSE) && 
			(cmd->SCp.Status == CHECK_CONDITION)) {
#if (NDEBUG & NDEBUG_AUTOSENSE) 
			printk("scsi%d : performing request sense\n", 
			    instance->host_no);
#endif
			cmd->cmnd[0] = REQUEST_SENSE;
			cmd->cmnd[1] &= 0xe0;
			cmd->cmnd[2] = 0;
			cmd->cmnd[3] = 0;
			cmd->cmnd[4] = sizeof(cmd->sense_buffer);
			cmd->cmnd[5] = 0;

			cmd->SCp.buffer = NULL;
			cmd->SCp.buffers_residual = 0;
			cmd->SCp.ptr = (char *) cmd->sense_buffer;
			cmd->SCp.this_residual = sizeof(cmd->sense_buffer);

			cli();
			cmd->host_scribble = (unsigned char *) 
			    hostdata->issue_queue;
		        hostdata->issue_queue = (Scsi_Cmnd *) cmd;
		        sti();
#if (NDEBUG & NDEBUG_QUEUES)
			printk("scsi%d : REQUEST SENSE added to head of issue queue\n");
#endif
		   } else
#endif /* def AUTOSENSE */
			cmd->scsi_done(cmd);

		    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
		    
		    while ((NCR5380_read(STATUS_REG) & SR_BSY) && 
			!hostdata->connected);
		    return;
		case MESSAGE_REJECT:
		    switch (hostdata->last_message) {
		    case HEAD_OF_QUEUE_TAG:
		    case ORDERED_QUEUE_TAG:
		    case SIMPLE_QUEUE_TAG:
			scsi_devices[cmd->index].tagged_queue = 0;
			hostdata->busy[cmd->target] |= (1 << cmd->lun);
			break;
		    default:
			break;
		    }
		case DISCONNECT:
		    scsi_devices[cmd->index].disconnect = 1;
		    cli();
		    cmd->host_scribble = (unsigned char *) 
			hostdata->disconnected_queue;
		    hostdata->connected = NULL;
		    hostdata->disconnected_queue = cmd;
		    sti();
#if (NDEBUG & NDEBUG_QUEUES)
		    printk("scsi%d : command for target %d lun %d was moved from connected to"
		           "  the disconnected_queue\n", instance->host_no, 
			    cmd->target, cmd->lun);
#endif

		    /* Enable reselect interupts */
		    NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
		    /* Wait for bus free to avoid nasty timeouts */
		    while ((NCR5380_read(STATUS_REG) & SR_BSY) && 
			!hostdata->connected);
		    return;
		/* 
		 * The SCSI data pointer is *IMPLICITLY* saved on a disconnect
		 * operation, in violation of the SCSI spec so we can safely 
		 * ignore SAVE/RESTORE pointers calls.
		 *
		 * Unfortunately, some disks violate the SCSI spec and 
		 * don't issue the required SAVE_POINTERS message before
		 * disconnecting, and we have to break spec to remain 
		 * compatable.
		 */
		case SAVE_POINTERS:
		case RESTORE_POINTERS:
		    break;
		default:
/* 
 * XXX rejected messages should be handled in the pio data transfer phase,
 * since ATN should be raised before ACK goes false when we reject a message
 */

		    printk("Unknown message!\n");

#ifdef notyet
		/*

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -