📄 aic79xx.seq
字号:
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 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|NO_CDB_SENT jz . + 3; mvi SEQINTCODE, PROTO_VIOLATION; jmp mesgin_done; /* * If the target never gave us status information, have * the host abort the command. */ test SCB_CONTROL, STATUS_RCVD jz . - 2; /* * See if we attempted to deliver a message but the target ingnored us. */ test SCB_CONTROL, MK_MESSAGE jz . + 2; mvi SEQINTCODE, MKMSG_FAILED; call queue_scb_completion; jmp await_busfree;freeze_queue: /* Cancel any pending select-out. */ test SSTAT0, SELDO 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: 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 jz disconnect_allowed; mvi SEQINTCODE, PROTO_VIOLATION; jmp mesgin_done;disconnect_allowed: or SCB_CONTROL,DISCONNECTED; test SCB_CONTROL, TAG_ENB jz queue_disc_scb; mov A, SCB_NONPACKET_TAG; cmp SCBPTR, A je await_busfree;queue_disc_scb: bmov REG0, SCBPTR, 2; shr SINDEX, 4, SCB_SCSIID; INDEX_DISC_LIST(SINDEX, SCB_LUN); bmov DINDEX, SINDEX, 2; bmov REG1, SINDIR, 2; bmov DINDIR, REG0, 2; bmov SCBPTR, REG0, 2; bmov SCB_NEXT, REG1, 2; /* FALLTHROUGH */await_busfree: and SIMODE1, ~ENBUSFREE; mov NONE, SCSIDAT; /* Ack the last byte */ call clear_target_state; 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: test SSTAT1,REQINIT|BUSFREE jz .; test SSTAT1, BUSFREE jnz idle_loop; mvi 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. For chips without S/G pipelining, * we can only ack the message after SHADDR has been saved. On these * chips, SHADDR increments with every bus transaction, even PIO. */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 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;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 .;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 .;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 return; clr SG_STATE;disable_ccsgen_fetch_done: clr CCSGCTL ret;toggle_dff_mode: mvi SEQINTCTL, INTVEC1DSL; xor MODE_PTR, MK_MODE(M_DFF1, M_DFF1); clr SEQINTCTL ret;data_group_idle_loop: mov SAVED_MODE, MODE_PTR; test SG_STATE, LOADING_NEEDED jz . + 2; call service_fifo; call toggle_dff_mode; test SG_STATE, LOADING_NEEDED jz . + 2; call service_fifo; call idle_loop_cchan; mov SAVED_MODE jmp set_mode_work_around;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? */ cmp CCSGCTL, CCSGEN|SG_CACHE_AVAIL|CCSGDONE je idle_sgfetch_complete; /* Are we actively fetching segments? */ test CCSGCTL, CCSGEN 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. */ mvi SGHCNT, SG_PREFETCH_CNT; and SGHADDR[0], SG_PREFETCH_ALIGN_MASK, SCB_RESIDUAL_SGPTR; bmov SGHADDR[1], SCB_RESIDUAL_SGPTR[1], 3; 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; call disable_ccsgen_fetch_done; 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. */ 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 REG0, ~SG_FULL_RESID, SCB_SGPTR[0]; test SCB_DATACNT[3], SG_LAST_SEG jz . + 2; or REG0, LAST_SEG; test DATA_COUNT_ODD, 0x1 jz . + 2; or REG0, ODD_SEG; mov SG_CACHE_PRE, REG0; 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: test SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT jz p_data_allowed; mvi 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; mvi SEQINTCODE, PDATA_REINIT;p_data_bitbucket: /* * Turn on `Bit Bucket' mode, wait until the target takes * us to another phase, and then notify the host. */ 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; test SCSIPHASE, DATA_PHASE_MASK jnz .; and SXFRCTL1, ~BITBUCKET; SET_MODE(M_DFF1, M_DFF1); mvi 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;data_group_dma_loop: /* * The transfer is complete if either the last segment * completes or the target changes phase. Both conditions * will clear SCSIEN. We test SCSIEN twice during our * "idle loop" to avoid long delays before we notice the * SCSIEN transition. */ call data_group_idle_loop; 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 * as possible while the data fifo drains on a read * and respond as quickly as possible to the standard
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -