📄 aic79xx.seq
字号:
bmov ALLOCFIFO_SCBPTR, SCBPTR, 2; call allocate_fifo; /* See if the host wants to send a message upon reconnection */ test SCB_CONTROL, MK_MESSAGE jz mesgin_done; mvi HOST_MSG call mk_mesg; jmp mesgin_done;not_found: SET_SEQINTCODE(NO_MATCH) jmp mesgin_done;not_found_ITloop: SET_SEQINTCODE(NO_MATCH) jmp ITloop;/* * We received a "command complete" message. Put the SCB on the complete * queue and trigger a completion interrupt via the idle loop. Before doing * so, check to see if there * is a residual or the status byte is something other than STATUS_GOOD (0). * In either of these conditions, we upload the SCB back to the host so it can * process this information. In the case of a non zero status byte, we * additionally interrupt the kernel driver synchronously, allowing it to * decide if sense should be retrieved. If the kernel driver wishes to request * sense, it will fill the kernel SCB with a request sense command, requeue * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting * RETURN_1 to SEND_SENSE. */mesgin_complete: /* * If ATN is raised, we still want to give the target a message. * Perhaps there was a parity error on this last message byte. * Either way, the target should take us to message out phase * and then attempt to complete the command again. We should use a * critical section here to guard against a timeout triggering * for this command and setting ATN while we are still processing * the completion. test SCSISIGI, ATNI jnz mesgin_done; */ /* * If we are identified and have successfully sent the CDB, * any status will do. Optimize this fast path. */ test SCB_CONTROL, STATUS_RCVD jz mesgin_proto_violation; test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz complete_accepted; /* * If the target never sent an identify message but instead went * to mesgin to give an invalid message, let the host abort us. */ test SEQ_FLAGS, NOT_IDENTIFIED jnz mesgin_proto_violation; /* * If we recevied good status but never successfully sent the * cdb, abort the command. */ test SCB_SCSI_STATUS,0xff jnz complete_accepted; test SEQ_FLAGS, NO_CDB_SENT jnz mesgin_proto_violation;complete_accepted: /* * See if we attempted to deliver a message but the target ingnored us. */ test SCB_CONTROL, MK_MESSAGE jz complete_nomsg; SET_SEQINTCODE(MKMSG_FAILED)complete_nomsg: call queue_scb_completion; jmp await_busfree;freeze_queue: /* Cancel any pending select-out. */ test SSTAT0, SELDO|SELINGO jnz . + 2; and SCSISEQ0, ~ENSELO; mov ACCUM_SAVE, A; clr A; add QFREEZE_COUNT, 1; adc QFREEZE_COUNT[1], A; or SEQ_FLAGS2, SELECTOUT_QFROZEN; mov A, ACCUM_SAVE ret;queue_arg1_scb_completion: SET_MODE(M_SCSI, M_SCSI) bmov SCBPTR, ARG_1, 2;queue_scb_completion: if ((ahd->bugs & AHD_ABORT_LQI_BUG) == 0) { /* * Set MK_MESSAGE to trigger an abort should this SCB * be referenced by a target even though it is not currently * active. */ or SCB_CONTROL, MK_MESSAGE; } test SCB_SCSI_STATUS,0xff jnz bad_status; /* * Check for residuals */ test SCB_SGPTR, SG_LIST_NULL jnz complete; /* No xfer */ test SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */ test SCB_RESIDUAL_SGPTR, SG_LIST_NULL jz upload_scb;complete: bmov SCB_NEXT_COMPLETE, COMPLETE_SCB_HEAD, 2; bmov COMPLETE_SCB_HEAD, SCBPTR, 2 ret;bad_status: cmp SCB_SCSI_STATUS, STATUS_PKT_SENSE je upload_scb; call freeze_queue;upload_scb: bmov SCB_NEXT_COMPLETE, COMPLETE_DMA_SCB_HEAD, 2; bmov COMPLETE_DMA_SCB_HEAD, SCBPTR, 2; or SCB_SGPTR, SG_STATUS_VALID ret;/* * Is it a disconnect message? Set a flag in the SCB to remind us * and await the bus going free. If this is an untagged transaction * store the SCB id for it in our untagged target table for lookup on * a reselction. */mesgin_disconnect: /* * If ATN is raised, we still want to give the target a message. * Perhaps there was a parity error on this last message byte * or we want to abort this command. Either way, the target * should take us to message out phase and then attempt to * disconnect again. * XXX - Wait for more testing. test SCSISIGI, ATNI jnz mesgin_done; */ test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jnz mesgin_proto_violation; or SCB_CONTROL,DISCONNECTED; test SCB_CONTROL, TAG_ENB jnz await_busfree;queue_disc_scb: bmov REG0, SCBPTR, 2; INDEX_DISC_LIST(SAVED_SCSIID, SAVED_LUN); bmov DINDEX, SINDEX, 2; bmov DINDIR, REG0, 2; bmov SCBPTR, REG0, 2; /* FALLTHROUGH */await_busfree: and SIMODE1, ~ENBUSFREE; if ((ahd->bugs & AHD_BUSFREEREV_BUG) == 0) { /* * In the BUSFREEREV_BUG case, the * busfree status was cleared at the * beginning of the connection. */ mvi CLRSINT1,CLRBUSFREE; } mov NONE, SCSIDAT; /* Ack the last byte */ test MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1)) jnz await_busfree_not_m_dff;SET_SRC_MODE M_DFF1;SET_DST_MODE M_DFF1;await_busfree_clrchn: mvi DFFSXFRCTL, CLRCHN;await_busfree_not_m_dff: call clear_target_state; test SSTAT1,REQINIT|BUSFREE jz .; test SSTAT1, BUSFREE jnz idle_loop; SET_SEQINTCODE(MISSED_BUSFREE)/* * Save data pointers message: * Copying RAM values back to SCB, for Save Data Pointers message, but * only if we've actually been into a data phase to change them. This * protects against bogus data in scratch ram and the residual counts * since they are only initialized when we go into data_in or data_out. * Ack the message as soon as possible. */SET_SRC_MODE M_DFF1;SET_DST_MODE M_DFF1;mesgin_sdptrs: mov NONE,SCSIDAT; /*dummy read from latch to ACK*/ test SEQ_FLAGS, DPHASE jz ITloop; call save_pointers; jmp ITloop;save_pointers: /* * If we are asked to save our position at the end of the * transfer, just mark us at the end rather than perform a * full save. */ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz save_pointers_full; or SCB_SGPTR, SG_LIST_NULL ret;save_pointers_full: /* * The SCB_DATAPTR becomes the current SHADDR. * All other information comes directly from our residual * state. */ bmov SCB_DATAPTR, SHADDR, 8; bmov SCB_DATACNT, SCB_RESIDUAL_DATACNT, 8 ret;/* * Restore pointers message? Data pointers are recopied from the * SCB anytime we enter a data phase for the first time, so all * we need to do is clear the DPHASE flag and let the data phase * code do the rest. We also reset/reallocate the FIFO to make * sure we have a clean start for the next data or command phase. */mesgin_rdptrs: and SEQ_FLAGS, ~DPHASE; test MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1)) jnz msgin_rdptrs_get_fifo; mvi DFFSXFRCTL, RSTCHN|CLRSHCNT; SET_MODE(M_SCSI, M_SCSI)msgin_rdptrs_get_fifo: call allocate_fifo; jmp mesgin_done;clear_target_state: mvi LASTPHASE, P_BUSFREE; /* clear target specific flags */ mvi SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT ret;phase_lock: test SCSIPHASE, 0xFF jz .; test SSTAT1, SCSIPERR jnz phase_lock;phase_lock_latch_phase: and LASTPHASE, PHASE_MASK, SCSISIGI ret;/* * Functions to read data in Automatic PIO mode. * * An ACK is not sent on input from the target until SCSIDATL is read from. * So we wait until SCSIDATL is latched (the usual way), then read the data * byte directly off the bus using SCSIBUSL. When we have pulled the ATN * line, or we just want to acknowledge the byte, then we do a dummy read * from SCISDATL. The SCSI spec guarantees that the target will hold the * data byte on the bus until we send our ACK. * * The assumption here is that these are called in a particular sequence, * and that REQ is already set when inb_first is called. inb_{first,next} * use the same calling convention as inb. */inb_next: mov NONE,SCSIDAT; /*dummy read from latch to ACK*/inb_next_wait: /* * If there is a parity error, wait for the kernel to * see the interrupt and prepare our message response * before continuing. */ test SCSIPHASE, 0xFF jz .; test SSTAT1, SCSIPERR jnz inb_next_wait;inb_next_check_phase: and LASTPHASE, PHASE_MASK, SCSISIGI; cmp LASTPHASE, P_MESGIN jne mesgin_phasemis;inb_first: clr DINDEX[1]; mov DINDEX,SINDEX; mov DINDIR,SCSIBUS ret; /*read byte directly from bus*/inb_last: mov NONE,SCSIDAT ret; /*dummy read from latch to ACK*/mk_mesg: mvi SCSISIGO, ATNO; mov MSG_OUT,SINDEX ret;SET_SRC_MODE M_DFF1;SET_DST_MODE M_DFF1;disable_ccsgen: test SG_STATE, FETCH_INPROG jz disable_ccsgen_fetch_done; clr CCSGCTL;disable_ccsgen_fetch_done: clr SG_STATE ret;service_fifo: /* * Do we have any prefetch left??? */ test SG_STATE, SEGS_AVAIL jnz idle_sg_avail; /* * Can this FIFO have access to the S/G cache yet? */ test CCSGCTL, SG_CACHE_AVAIL jz return; /* Did we just finish fetching segs? */ test CCSGCTL, CCSGDONE jnz idle_sgfetch_complete; /* Are we actively fetching segments? */ test CCSGCTL, CCSGENACK jnz return; /* * We fetch a "cacheline aligned" and sized amount of data * so we don't end up referencing a non-existant page. * Cacheline aligned is in quotes because the kernel will * set the prefetch amount to a reasonable level if the * cacheline size is unknown. */ bmov SGHADDR, SCB_RESIDUAL_SGPTR, 4; mvi SGHCNT, SG_PREFETCH_CNT; if ((ahd->bugs & AHD_REG_SLOW_SETTLE_BUG) != 0) { /* * Need two instruction between "touches" of SGHADDR. */ nop; } and SGHADDR[0], SG_PREFETCH_ALIGN_MASK, SCB_RESIDUAL_SGPTR; mvi CCSGCTL, CCSGEN|SG_CACHE_AVAIL|CCSGRESET; or SG_STATE, FETCH_INPROG ret;idle_sgfetch_complete: /* * Guard against SG_CACHE_AVAIL activating during sg fetch * request in the other FIFO. */ test SG_STATE, FETCH_INPROG jz return; clr CCSGCTL; and CCSGADDR, SG_PREFETCH_ADDR_MASK, SCB_RESIDUAL_SGPTR; mvi SG_STATE, SEGS_AVAIL|LOADING_NEEDED;idle_sg_avail: /* Does the hardware have space for another SG entry? */ test DFSTATUS, PRELOAD_AVAIL jz return; if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) { bmov HADDR, CCSGRAM, 8; } else { bmov HADDR, CCSGRAM, 4; } bmov HCNT, CCSGRAM, 3; test HCNT[0], 0x1 jz . + 2; xor DATA_COUNT_ODD, 0x1; bmov SCB_RESIDUAL_DATACNT[3], CCSGRAM, 1; if ((ahd->flags & AHD_39BIT_ADDRESSING) != 0) { and HADDR[4], SG_HIGH_ADDR_BITS, SCB_RESIDUAL_DATACNT[3]; } if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) { /* Skip 4 bytes of pad. */ add CCSGADDR, 4; }sg_advance: clr A; /* add sizeof(struct scatter) */ add SCB_RESIDUAL_SGPTR[0],SG_SIZEOF; adc SCB_RESIDUAL_SGPTR[1],A; adc SCB_RESIDUAL_SGPTR[2],A; adc SCB_RESIDUAL_SGPTR[3],A; mov SINDEX, SCB_RESIDUAL_SGPTR[0]; test DATA_COUNT_ODD, 0x1 jz . + 2; or SINDEX, ODD_SEG; test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 3; or SINDEX, LAST_SEG; clr SG_STATE; mov SG_CACHE_PRE, SINDEX; /* * Load the segment. Or in HDMAEN here too * just in case HDMAENACK has not come true * by the time this segment is loaded. If * HDMAENACK is not true, this or will disable * HDMAEN mid-transfer. We do not want to simply * mvi our original settings as SCSIEN automatically * de-asserts and we don't want to accidentally * re-enable it. */ if ((ahd->features & AHD_NEW_DFCNTRL_OPTS) != 0) { /* * Use SCSIENWRDIS so that SCSIEN is never * modified by this operation. */ or DFCNTRL, PRELOADEN|SCSIENWRDIS|HDMAEN; } else { or DFCNTRL, PRELOADEN|HDMAEN; } /* * Do we have another segment in the cache? */ add NONE, SG_PREFETCH_CNT_LIMIT, CCSGADDR; jnc return; and SG_STATE, ~SEGS_AVAIL ret;/* * Initialize the DMA address and counter from the SCB. */load_first_seg: bmov HADDR, SCB_DATAPTR, 11; and DATA_COUNT_ODD, 0x1, SCB_DATACNT[0]; and REG_ISR, ~SG_FULL_RESID, SCB_SGPTR[0]; test SCB_DATACNT[3], SG_LAST_SEG jz . + 2; or REG_ISR, LAST_SEG; test DATA_COUNT_ODD, 0x1 jz . + 2; or REG_ISR, ODD_SEG; mov SG_CACHE_PRE, REG_ISR; mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN); /* * Since we've are entering a data phase, we will * rely on the SCB_RESID* fields. Initialize the * residual and clear the full residual flag. */ and SCB_SGPTR[0], ~SG_FULL_RESID; bmov SCB_RESIDUAL_DATACNT[3], SCB_DATACNT[3], 5; /* If we need more S/G elements, tell the idle loop */ test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jnz . + 2; mvi SG_STATE, LOADING_NEEDED ret; clr SG_STATE ret;p_data_handle_xfer: call setjmp_setscb; test SG_STATE, LOADING_NEEDED jnz service_fifo;p_data_clear_handler: or LONGJMP_ADDR[1], INVALID_ADDR ret;p_data: test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz p_data_allowed; SET_SEQINTCODE(PROTO_VIOLATION)p_data_allowed: test SEQ_FLAGS, DPHASE jz data_phase_initialize; /* * If we re-enter the data phase after going through another * phase, our transfer location has almost certainly been * corrupted by the interveining, non-data, transfers. Ask * the host driver to fix us up based on the transfer residual * unless we already know that we should be bitbucketing. */ test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jnz p_data_bitbucket; SET_SEQINTCODE(PDATA_REINIT) jmp data_phase_inbounds;p_data_bitbucket: /* * Turn on `Bit Bucket' mode, wait until the target takes * us to another phase, and then notify the host. */ mov SAVED_MODE, MODE_PTR; test MODE_PTR, ~(MK_MODE(M_DFF1, M_DFF1)) jnz bitbucket_not_m_dff; /* * Ensure that any FIFO contents are cleared out and the * FIFO free'd prior to starting the BITBUCKET. BITBUCKET * doesn't discard data already in the FIFO. */ mvi DFFSXFRCTL, RSTCHN|CLRSHCNT; SET_MODE(M_SCSI, M_SCSI)bitbucket_not_m_dff: or SXFRCTL1,BITBUCKET; /* Wait for non-data phase. */ test SCSIPHASE, ~DATA_PHASE_MASK jz .; and SXFRCTL1, ~BITBUCKET; RESTORE_MODE(SAVED_MODE)SET_SRC_MODE M_DFF1;SET_DST_MODE M_DFF1; SET_SEQINTCODE(DATA_OVERRUN) jmp ITloop;data_phase_initialize: test SCB_SGPTR[0], SG_LIST_NULL jnz p_data_bitbucket; call load_first_seg;data_phase_inbounds: /* We have seen a data phase at least once. */ or SEQ_FLAGS, DPHASE; mov SAVED_MODE, MODE_PTR; test SG_STATE, LOADING_NEEDED jz data_group_dma_loop; call p_data_handle_xfer;data_group_dma_loop: /* * The transfer is complete if either the last segment * completes or the target changes phase. Both conditions * will clear SCSIEN. */ call idle_loop_service_fifos; call idle_loop_cchan; call idle_loop_gsfifo; RESTORE_MODE(SAVED_MODE) test DFCNTRL, SCSIEN jnz data_group_dma_loop;data_group_dmafinish: /* * The transfer has terminated either due to a phase * change, and/or the completion of the last segment. * We have two goals here. Do as much other work
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -