📄 adwcam.c
字号:
adw_eeprom_write(adw, &eep_config); } /* Pull eeprom information into our softc. */ adw->bios_ctrl = eep_config.bios_ctrl; adw->user_wdtr = eep_config.wdtr_able; adw->user_sdtr = eep_config.sdtr_able; adw->user_ultra = eep_config.ultra_able; adw->user_tagenb = eep_config.tagqng_able; adw->user_discenb = eep_config.disc_enable; adw->max_acbs = eep_config.max_host_qng; adw->initiator_id = (eep_config.adapter_scsi_id & ADW_MAX_TID); /* * Sanity check the number of host openings. */ if (adw->max_acbs > ADW_DEF_MAX_HOST_QNG) adw->max_acbs = ADW_DEF_MAX_HOST_QNG; else if (adw->max_acbs < ADW_DEF_MIN_HOST_QNG) { /* If the value is zero, assume it is uninitialized. */ if (adw->max_acbs == 0) adw->max_acbs = ADW_DEF_MAX_HOST_QNG; else adw->max_acbs = ADW_DEF_MIN_HOST_QNG; } scsicfg1 = 0; switch (eep_config.termination) { default: printf("%s: Invalid EEPROM Termination Settings.\n", adw_name(adw)); printf("%s: Reverting to Automatic Termination\n", adw_name(adw)); /* FALLTHROUGH */ case ADW_EEPROM_TERM_AUTO: break; case ADW_EEPROM_TERM_BOTH_ON: scsicfg1 |= ADW_SCSI_CFG1_TERM_CTL_L; /* FALLTHROUGH */ case ADW_EEPROM_TERM_HIGH_ON: scsicfg1 |= ADW_SCSI_CFG1_TERM_CTL_H; /* FALLTHROUGH */ case ADW_EEPROM_TERM_OFF: scsicfg1 |= ADW_SCSI_CFG1_TERM_CTL_MANUAL; break; } printf("%s: SCSI ID %d, ", adw_name(adw), adw->initiator_id); if (adw_init_chip(adw, scsicfg1) != 0) return (-1); printf("Queue Depth %d\n", adw->max_acbs); /* DMA tag for mapping buffers into device visible space. */ if (bus_dma_tag_create(adw->parent_dmat, /*alignment*/0, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/MAXBSIZE, /*nsegments*/ADW_SGSIZE, /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT, /*flags*/BUS_DMA_ALLOCNOW, &adw->buffer_dmat) != 0) { return (-1); } adw->init_level++; /* DMA tag for our ccb structures */ if (bus_dma_tag_create(adw->parent_dmat, /*alignment*/0, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, adw->max_acbs * sizeof(struct acb), /*nsegments*/1, /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT, /*flags*/0, &adw->acb_dmat) != 0) { return (-1); } adw->init_level++; /* Allocation for our ccbs */ if (bus_dmamem_alloc(adw->acb_dmat, (void **)&adw->acbs, BUS_DMA_NOWAIT, &adw->acb_dmamap) != 0) { return (-1); } adw->init_level++; /* And permanently map them */ bus_dmamap_load(adw->acb_dmat, adw->acb_dmamap, adw->acbs, adw->max_acbs * sizeof(struct acb), adwmapmem, &adw->acb_busbase, /*flags*/0); /* Clear them out. */ bzero(adw->acbs, adw->max_acbs * sizeof(struct acb)); /* DMA tag for our S/G structures. We allocate in page sized chunks */ if (bus_dma_tag_create(adw->parent_dmat, /*alignment*/0, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, PAGE_SIZE, /*nsegments*/1, /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT, /*flags*/0, &adw->sg_dmat) != 0) { return (-1); } adw->init_level++; /* Allocate our first batch of ccbs */ if (adwallocacbs(adw) == 0) return (-1); return (0);}/* * Attach all the sub-devices we can find */intadw_attach(struct adw_softc *adw){ struct ccb_setasync csa; struct cam_devq *devq; /* Start the Risc processor now that we are fully configured. */ adw_outw(adw, ADW_RISC_CSR, ADW_RISC_CSR_RUN); /* * Create the device queue for our SIM. */ devq = cam_simq_alloc(adw->max_acbs); if (devq == NULL) return (0); /* * Construct our SIM entry. */ adw->sim = cam_sim_alloc(adw_action, adw_poll, "adw", adw, adw->unit, 1, adw->max_acbs, devq); if (adw->sim == NULL) return (0); /* * Register the bus. */ if (xpt_bus_register(adw->sim, 0) != CAM_SUCCESS) { cam_sim_free(adw->sim, /*free devq*/TRUE); return (0); } if (xpt_create_path(&adw->path, /*periph*/NULL, cam_sim_path(adw->sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) == CAM_REQ_CMP) { xpt_setup_ccb(&csa.ccb_h, adw->path, /*priority*/5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = AC_LOST_DEVICE; csa.callback = adw_async; csa.callback_arg = adw; xpt_action((union ccb *)&csa); } return (0);}voidadw_intr(void *arg){ struct adw_softc *adw; u_int int_stat; u_int next_doneq; u_int next_completeq; u_int doneq_start; adw = (struct adw_softc *)arg; if ((adw_inw(adw, ADW_CTRL_REG) & ADW_CTRL_REG_HOST_INTR) == 0) return; /* Reading the register clears the interrupt. */ int_stat = adw_inb(adw, ADW_INTR_STATUS_REG); if ((int_stat & ADW_INTR_STATUS_INTRB) != 0) { /* Idle Command Complete */ adw->idle_command_cmp = 1; switch (adw->idle_cmd) { case ADW_IDLE_CMD_DEVICE_RESET: adw_handle_device_reset(adw, /*target*/adw->idle_cmd_param); break; case ADW_IDLE_CMD_SCSI_RESET: adw_handle_bus_reset(adw, /*initiated*/TRUE); break; default: break; } adw->idle_cmd = ADW_IDLE_CMD_COMPLETED; } if ((int_stat & ADW_INTR_STATUS_INTRC) != 0) { /* SCSI Bus Reset */ adw_handle_bus_reset(adw, /*initiated*/FALSE); } /* * ADW_MC_HOST_NEXT_DONE is actually the last completed RISC * Queue List request. Its forward pointer (RQL_FWD) points to the * current completed RISC Queue List request. */ next_doneq = adw_lram_read_8(adw, ADW_MC_HOST_NEXT_DONE); next_doneq = ADW_MC_RISC_Q_LIST_BASE + RQL_FWD + (next_doneq * ADW_MC_RISC_Q_LIST_SIZE); next_completeq = adw_lram_read_8(adw, next_doneq); doneq_start = ADW_MC_NULL_Q; /* Loop until all completed Q's are processed. */ while (next_completeq != ADW_MC_NULL_Q) { u_int32_t acb_busaddr; struct acb *acb; union ccb *ccb; doneq_start = next_completeq; next_doneq = ADW_MC_RISC_Q_LIST_BASE + (next_completeq * ADW_MC_RISC_Q_LIST_SIZE); /* * Read the ADW_SCSI_REQ_Q physical address pointer from * the RISC list entry. */ acb_busaddr = adw_lram_read_32(adw, next_doneq + RQL_PHYADDR); acb = acbptov(adw, acb_busaddr); /* Change the RISC Queue List state to free. */ adw_lram_write_8(adw, next_doneq + RQL_STATE, ADW_MC_QS_FREE); /* Get the RISC Queue List forward pointer. */ next_completeq = adw_lram_read_8(adw, next_doneq + RQL_FWD); /* Process CCB */ ccb = acb->ccb; untimeout(adwtimeout, acb, ccb->ccb_h.timeout_ch); if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) { bus_dmasync_op_t op; if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_POSTREAD; else op = BUS_DMASYNC_POSTWRITE; bus_dmamap_sync(adw->buffer_dmat, acb->dmamap, op); bus_dmamap_unload(adw->buffer_dmat, acb->dmamap); ccb->csio.resid = acb->queue.data_cnt; } else ccb->csio.resid = 0; /* Common Cases inline... */ if (acb->queue.host_status == QHSTA_NO_ERROR && (acb->queue.done_status == QD_NO_ERROR || acb->queue.done_status == QD_WITH_ERROR)) { ccb->csio.scsi_status = acb->queue.scsi_status; ccb->ccb_h.status = 0; switch (ccb->csio.scsi_status) { case SCSI_STATUS_OK: ccb->ccb_h.status |= CAM_REQ_CMP; break; case SCSI_STATUS_CHECK_COND: case SCSI_STATUS_CMD_TERMINATED: bcopy(&acb->sense_data, &ccb->csio.sense_data, ccb->csio.sense_len); ccb->ccb_h.status |= CAM_AUTOSNS_VALID; ccb->csio.sense_resid = acb->queue.sense_len; /* FALLTHROUGH */ default: ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR | CAM_DEV_QFRZN; xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); break; } adwfreeacb(adw, acb); xpt_done(ccb); } else { adwprocesserror(adw, acb); } } if (doneq_start != ADW_MC_NULL_Q) adw_lram_write_8(adw, ADW_MC_HOST_NEXT_DONE, doneq_start);}static voidadwprocesserror(struct adw_softc *adw, struct acb *acb){ union ccb *ccb; ccb = acb->ccb; if (acb->queue.done_status == QD_ABORTED_BY_HOST) { ccb->ccb_h.status = CAM_REQ_ABORTED; } else { switch (acb->queue.host_status) { case QHSTA_M_SEL_TIMEOUT: ccb->ccb_h.status = CAM_SEL_TIMEOUT; break; case QHSTA_M_SXFR_OFF_UFLW: case QHSTA_M_SXFR_OFF_OFLW: case QHSTA_M_DATA_OVER_RUN: ccb->ccb_h.status = CAM_DATA_RUN_ERR; break; case QHSTA_M_SXFR_DESELECTED: case QHSTA_M_UNEXPECTED_BUS_FREE: ccb->ccb_h.status = CAM_UNEXP_BUSFREE; break; case QHSTA_M_QUEUE_ABORTED: /* BDR or Bus Reset */ ccb->ccb_h.status = adw->last_reset; break; case QHSTA_M_SXFR_SDMA_ERR: case QHSTA_M_SXFR_SXFR_PERR: case QHSTA_M_RDMA_PERR: ccb->ccb_h.status = CAM_UNCOR_PARITY; break; case QHSTA_M_WTM_TIMEOUT: case QHSTA_M_SXFR_WD_TMO: /* The SCSI bus hung in a phase */ ccb->ccb_h.status = CAM_SEQUENCE_FAIL; adw_idle_cmd_send(adw, ADW_IDLE_CMD_SCSI_RESET, /*param*/0); break; case QHSTA_M_SXFR_XFR_PH_ERR: ccb->ccb_h.status = CAM_SEQUENCE_FAIL; break; case QHSTA_M_SXFR_UNKNOWN_ERROR: break; case QHSTA_M_BAD_CMPL_STATUS_IN: /* No command complete after a status message */ ccb->ccb_h.status = CAM_SEQUENCE_FAIL; break; case QHSTA_M_AUTO_REQ_SENSE_FAIL: ccb->ccb_h.status = CAM_AUTOSENSE_FAIL; break; case QHSTA_M_INVALID_DEVICE: ccb->ccb_h.status = CAM_PATH_INVALID; break; case QHSTA_M_NO_AUTO_REQ_SENSE: /* * User didn't request sense, but we got a * check condition. */ ccb->csio.scsi_status = acb->queue.scsi_status; ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR; break; default: panic("%s: Unhandled Host status error %x", adw_name(adw), acb->queue.host_status); /* NOTREACHED */ } } if (ccb->ccb_h.status != CAM_REQ_CMP) { xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); ccb->ccb_h.status |= CAM_DEV_QFRZN; } adwfreeacb(adw, acb); xpt_done(ccb);}static voidadwtimeout(void *arg){ struct acb *acb; union ccb *ccb; struct adw_softc *adw; adw_idle_cmd_status_t status; int s; acb = (struct acb *)arg; ccb = acb->ccb; adw = (struct adw_softc *)ccb->ccb_h.ccb_adw_ptr; xpt_print_path(ccb->ccb_h.path); printf("ACB %p - timed out\n", (void *)acb); s = splcam(); if ((acb->state & ACB_ACTIVE) == 0) { xpt_print_path(ccb->ccb_h.path); printf("ACB %p - timed out CCB already completed\n", (void *)acb); splx(s); return; } /* Attempt a BDR first */ adw_idle_cmd_send(adw, ADW_IDLE_CMD_DEVICE_RESET, ccb->ccb_h.target_id); splx(s); status = adw_idle_cmd_wait(adw); if (status == ADW_IDLE_CMD_SUCCESS) { printf("%s: BDR Delivered. No longer in timeout\n", adw_name(adw)); } else { adw_idle_cmd_send(adw, ADW_IDLE_CMD_SCSI_RESET, /*param*/0); status = adw_idle_cmd_wait(adw); if (status != ADW_IDLE_CMD_SUCCESS) panic("%s: Bus Reset during timeout failed", adw_name(adw)); }}static voidadw_handle_device_reset(struct adw_softc *adw, u_int target){ struct cam_path *path; cam_status error; error = xpt_create_path(&path, /*periph*/NULL, cam_sim_path(adw->sim), target, CAM_LUN_WILDCARD); if (error == CAM_REQ_CMP) { xpt_async(AC_SENT_BDR, path, NULL); xpt_free_path(path); } adw->last_reset = CAM_BDR_SENT;}static voidadw_handle_bus_reset(struct adw_softc *adw, int initiated){ if (initiated) { /* * The microcode currently sets the SCSI Bus Reset signal * while handling the AscSendIdleCmd() IDLE_CMD_SCSI_RESET * command above. But the SCSI Bus Reset Hold Time in the * microcode is not deterministic (it may in fact be for less * than the SCSI Spec. minimum of 25 us). Therefore on return * the Adv Library sets the SCSI Bus Reset signal for * ADW_SCSI_RESET_HOLD_TIME_US, which is defined to be greater * than 25 us. */ u_int scsi_ctrl; scsi_ctrl = adw_inw(adw, ADW_SCSI_CTRL) & ~ADW_SCSI_CTRL_RSTOUT; adw_outw(adw, ADW_SCSI_CTRL, scsi_ctrl | ADW_SCSI_CTRL_RSTOUT); DELAY(ADW_SCSI_RESET_HOLD_TIME_US); adw_outw(adw, ADW_SCSI_CTRL, scsi_ctrl); /* * We will perform the async notification when the * SCSI Reset interrupt occurs. */ } else xpt_async(AC_BUS_RESET, adw->path, NULL); adw->last_reset = CAM_SCSI_BUS_RESET;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -