📄 advansys.c
字号:
* Currently, the output of bus_dmammap_load suits our needs just * fine, but should it change, we'd need to do something here. */#define adv_fixup_dmasegs(adv, dm_segs) (struct adv_sg_entry *)(dm_segs)static voidadv_execute_ccb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, int error){ struct ccb_scsiio *csio; struct ccb_hdr *ccb_h; struct cam_sim *sim; struct adv_softc *adv; struct adv_ccb_info *cinfo; struct adv_scsi_q scsiq; struct adv_sg_head sghead; int s; csio = (struct ccb_scsiio *)arg; ccb_h = &csio->ccb_h; sim = xpt_path_sim(ccb_h->path); adv = (struct adv_softc *)cam_sim_softc(sim); cinfo = (struct adv_ccb_info *)csio->ccb_h.ccb_cinfo_ptr; if ((ccb_h->flags & CAM_CDB_POINTER) != 0) { if ((ccb_h->flags & CAM_CDB_PHYS) == 0) { /* XXX Need phystovirt!!!! */ /* How about pmap_kenter??? */ scsiq.cdbptr = csio->cdb_io.cdb_ptr; } else { scsiq.cdbptr = csio->cdb_io.cdb_ptr; } } else { scsiq.cdbptr = csio->cdb_io.cdb_bytes; } /* * Build up the request */ scsiq.q1.status = 0; scsiq.q1.q_no = 0; scsiq.q1.cntl = 0; scsiq.q1.sg_queue_cnt = 0; scsiq.q1.target_id = ADV_TID_TO_TARGET_MASK(ccb_h->target_id); scsiq.q1.target_lun = ccb_h->target_lun; scsiq.q1.sense_len = csio->sense_len; scsiq.q1.extra_bytes = 0; scsiq.q2.ccb_ptr = (u_int32_t)csio; scsiq.q2.target_ix = ADV_TIDLUN_TO_IX(ccb_h->target_id, ccb_h->target_lun); scsiq.q2.flag = 0; scsiq.q2.cdb_len = csio->cdb_len; if ((ccb_h->flags & CAM_TAG_ACTION_VALID) != 0) scsiq.q2.tag_code = csio->tag_action; else scsiq.q2.tag_code = 0; scsiq.q2.vm_id = 0; if (nsegments != 0) { bus_dmasync_op_t op; scsiq.q1.data_addr = dm_segs->ds_addr; scsiq.q1.data_cnt = dm_segs->ds_len; if (nsegments > 1) { scsiq.q1.cntl |= QC_SG_HEAD; sghead.entry_cnt = sghead.entry_to_copy = nsegments; sghead.res = 0; sghead.sg_list = adv_fixup_dmasegs(adv, dm_segs); scsiq.sg_head = &sghead; } else { scsiq.sg_head = NULL; } if ((ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_PREREAD; else op = BUS_DMASYNC_PREWRITE; bus_dmamap_sync(adv->buffer_dmat, cinfo->dmamap, op); } else { scsiq.q1.data_addr = 0; scsiq.q1.data_cnt = 0; scsiq.sg_head = NULL; } s = splcam(); /* * Last time we need to check if this SCB needs to * be aborted. */ if (ccb_h->status != CAM_REQ_INPROG) { if (nsegments != 0) { bus_dmamap_unload(adv->buffer_dmat, cinfo->dmamap); } if ((cinfo->state & ACCB_RELEASE_SIMQ) != 0) { ccb_h->status |= CAM_RELEASE_SIMQ; } adv_free_ccb_info(adv, cinfo); xpt_done((union ccb *)csio); splx(s); return; } if (adv_execute_scsi_queue(adv, &scsiq, csio->dxfer_len) != 0) { /* Temporary resource shortage */ if (nsegments != 0) { bus_dmamap_unload(adv->buffer_dmat, cinfo->dmamap); } ccb_h->status = CAM_REQUEUE_REQ; if ((cinfo->state & ACCB_RELEASE_SIMQ) != 0) ccb_h->status |= CAM_RELEASE_SIMQ; /* Unfreeze when resources are available */ xpt_freeze_simq(adv->sim, /*count*/1); adv_free_ccb_info(adv, cinfo); xpt_done((union ccb *)csio); splx(s); return; } cinfo->state |= ACCB_ACTIVE; ccb_h->status |= CAM_SIM_QUEUED; LIST_INSERT_HEAD(&adv->pending_ccbs, ccb_h, sim_links.le); /* Schedule our timeout */ ccb_h->timeout_ch = timeout(adv_timeout, csio, (ccb_h->timeout * hz)/1000); splx(s);}static struct adv_ccb_info *adv_alloc_ccb_info(struct adv_softc *adv){ int error; struct adv_ccb_info *cinfo; cinfo = malloc(sizeof(*cinfo), M_DEVBUF, M_NOWAIT); if (cinfo == NULL) { printf("%s: Can't malloc CCB info\n", adv_name(adv)); return (NULL); } cinfo->state = ACCB_FREE; error = bus_dmamap_create(adv->buffer_dmat, /*flags*/0, &cinfo->dmamap); if (error != 0) { printf("%s: Unable to allocate CCB info " "dmamap - error %d\n", adv_name(adv), error); free(cinfo, M_DEVBUF); cinfo = NULL; } return (cinfo);}static voidadv_destroy_ccb_info(struct adv_softc *adv, struct adv_ccb_info *cinfo){ bus_dmamap_destroy(adv->buffer_dmat, cinfo->dmamap); free(cinfo, M_DEVBUF);}voidadv_timeout(void *arg){ int s; union ccb *ccb; struct adv_softc *adv; struct adv_ccb_info *cinfo; ccb = (union ccb *)arg; adv = (struct adv_softc *)xpt_path_sim(ccb->ccb_h.path)->softc; cinfo = (struct adv_ccb_info *)ccb->ccb_h.ccb_cinfo_ptr; xpt_print_path(ccb->ccb_h.path); printf("Timed out\n"); s = splcam(); /* Have we been taken care of already?? */ if (cinfo == NULL || cinfo->state == ACCB_FREE) { splx(s); return; } adv_stop_execution(adv); if ((cinfo->state & ACCB_ABORT_QUEUED) == 0) { struct ccb_hdr *ccb_h; /* * In order to simplify the recovery process, we ask the XPT * layer to halt the queue of new transactions and we traverse * the list of pending CCBs and remove their timeouts. This * means that the driver attempts to clear only one error * condition at a time. In general, timeouts that occur * close together are related anyway, so there is no benefit * in attempting to handle errors in parrallel. Timeouts will * be reinstated when the recovery process ends. */ if ((cinfo->state & ACCB_RELEASE_SIMQ) == 0) { xpt_freeze_simq(adv->sim, /*count*/1); cinfo->state |= ACCB_RELEASE_SIMQ; } /* This CCB is the CCB representing our recovery actions */ cinfo->state |= ACCB_RECOVERY_CCB|ACCB_ABORT_QUEUED; ccb_h = LIST_FIRST(&adv->pending_ccbs); while (ccb_h != NULL) { untimeout(adv_timeout, ccb_h, ccb_h->timeout_ch); ccb_h = LIST_NEXT(ccb_h, sim_links.le); } /* XXX Should send a BDR */ /* Attempt an abort as our first tact */ xpt_print_path(ccb->ccb_h.path); printf("Attempting abort\n"); adv_abort_ccb(adv, ccb->ccb_h.target_id, ccb->ccb_h.target_lun, ccb, CAM_CMD_TIMEOUT, /*queued_only*/FALSE); ccb->ccb_h.timeout_ch = timeout(adv_timeout, ccb, 2 * hz); } else { /* Our attempt to perform an abort failed, go for a reset */ xpt_print_path(ccb->ccb_h.path); printf("Resetting bus\n"); ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= CAM_CMD_TIMEOUT; adv_reset_bus(adv); } adv_start_execution(adv); splx(s);}struct adv_softc *adv_alloc(int unit, bus_space_tag_t tag, bus_space_handle_t bsh){ struct adv_softc *adv; if (unit >= NADV) { printf("adv: unit number (%d) too high\n", unit); return NULL; } /* * Allocate a storage area for us */ if (advsoftcs[unit]) { printf("adv%d: memory already allocated\n", unit); return NULL; } adv = malloc(sizeof(struct adv_softc), M_DEVBUF, M_NOWAIT); if (!adv) { printf("adv%d: cannot malloc!\n", unit); return NULL; } bzero(adv, sizeof(struct adv_softc)); LIST_INIT(&adv->pending_ccbs); SLIST_INIT(&adv->free_ccb_infos); advsoftcs[unit] = adv; adv->unit = unit; adv->tag = tag; adv->bsh = bsh; return(adv);}voidadv_free(struct adv_softc *adv){ switch (adv->init_level) { case 5: { struct adv_ccb_info *cinfo; while ((cinfo = SLIST_FIRST(&adv->free_ccb_infos)) != NULL) { SLIST_REMOVE_HEAD(&adv->free_ccb_infos, links); adv_destroy_ccb_info(adv, cinfo); } bus_dmamap_unload(adv->sense_dmat, adv->sense_dmamap); } case 4: bus_dmamem_free(adv->sense_dmat, adv->sense_buffers, adv->sense_dmamap); case 3: bus_dma_tag_destroy(adv->sense_dmat); case 2: bus_dma_tag_destroy(adv->buffer_dmat); case 1: bus_dma_tag_destroy(adv->parent_dmat); case 0: break; } free(adv, M_DEVBUF);}intadv_init(struct adv_softc *adv){ struct adv_eeprom_config eeprom_config; int checksum, i; u_int16_t config_lsw; u_int16_t config_msw; adv_reset_chip_and_scsi_bus(adv); adv_lib_init(adv); /* * Stop script execution. */ adv_write_lram_16(adv, ADV_HALTCODE_W, 0x00FE); adv_stop_execution(adv); if (adv_is_chip_halted(adv) == 0) { printf("adv%d: Unable to halt adapter. Initialization" "failed\n", adv->unit); return (1); } ADV_OUTW(adv, ADV_REG_PROG_COUNTER, ADV_MCODE_START_ADDR); if (ADV_INW(adv, ADV_REG_PROG_COUNTER) != ADV_MCODE_START_ADDR) { printf("adv%d: Unable to set program counter. Initialization" "failed\n", adv->unit); return (1); } config_msw = ADV_INW(adv, ADV_CONFIG_MSW); config_lsw = ADV_INW(adv, ADV_CONFIG_LSW); if ((config_msw & ADV_CFG_MSW_CLR_MASK) != 0) { config_msw &= (~(ADV_CFG_MSW_CLR_MASK)); /* * XXX The Linux code flags this as an error, * but what should we report to the user??? * It seems that clearing the config register * makes this error recoverable. */ ADV_OUTW(adv, ADV_CONFIG_MSW, config_msw); } /* Suck in the configuration from the EEProm */ checksum = adv_get_eeprom_config(adv, &eeprom_config); eeprom_config.cfg_msw &= (~(ADV_CFG_MSW_CLR_MASK)); if (ADV_INW(adv, ADV_CHIP_STATUS) & ADV_CSW_AUTO_CONFIG) { /* * XXX The Linux code sets a warning level for this * condition, yet nothing of meaning is printed to * the user. What does this mean??? */ if (adv->chip_version == 3) { if (eeprom_config.cfg_lsw != config_lsw) { eeprom_config.cfg_lsw = ADV_INW(adv, ADV_CONFIG_LSW); } if (eeprom_config.cfg_msw != config_msw) { eeprom_config.cfg_msw = ADV_INW(adv, ADV_CONFIG_MSW); } } } eeprom_config.cfg_lsw |= ADV_CFG_LSW_HOST_INT_ON; if (adv_test_external_lram(adv) == 0) { /* * XXX What about non PCI cards with no * external LRAM???? */ if ((adv->type & (ADV_PCI|ADV_ULTRA)) == (ADV_PCI|ADV_ULTRA)) { eeprom_config.max_total_qng = ADV_MAX_PCI_ULTRA_INRAM_TOTAL_QNG; eeprom_config.max_tag_qng = ADV_MAX_PCI_ULTRA_INRAM_TAG_QNG; } else { eeprom_config.cfg_msw |= 0x0800; config_msw |= 0x0800; ADV_OUTW(adv, ADV_CONFIG_MSW, config_msw); eeprom_config.max_total_qng = ADV_MAX_PCI_INRAM_TOTAL_QNG; eeprom_config.max_tag_qng = ADV_MAX_INRAM_TAG_QNG; } adv->max_openings = eeprom_config.max_total_qng; } if (checksum == eeprom_config.chksum) { /* Range/Sanity checking */ if (eeprom_config.max_total_qng < ADV_MIN_TOTAL_QNG) { eeprom_config.max_total_qng = ADV_MIN_TOTAL_QNG; } if (eeprom_config.max_total_qng > ADV_MAX_TOTAL_QNG) { eeprom_config.max_total_qng = ADV_MAX_TOTAL_QNG; } if (eeprom_config.max_tag_qng > eeprom_config.max_total_qng) { eeprom_config.max_tag_qng = eeprom_config.max_total_qng; } if (eeprom_config.max_tag_qng < ADV_MIN_TAG_Q_PER_DVC) { eeprom_config.max_tag_qng = ADV_MIN_TAG_Q_PER_DVC; } adv->max_openings = eeprom_config.max_total_qng; adv->user_disc_enable = eeprom_config.disc_enable; adv->user_cmd_qng_enabled = eeprom_config.use_cmd_qng; adv->isa_dma_speed = EEPROM_DMA_SPEED(eeprom_config); adv->scsi_id = EEPROM_SCSIID(eeprom_config) & ADV_MAX_TID; EEPROM_SET_SCSIID(eeprom_config, adv->scsi_id); adv->control = eeprom_config.cntl; for (i = 0; i <= ADV_MAX_TID; i++) adv_sdtr_to_period_offset(adv, eeprom_config.sdtr_data[i], &adv->tinfo[i].user.period, &adv->tinfo[i].user.offset, i); } else { u_int8_t sync_data; printf("adv%d: Warning EEPROM Checksum mismatch. " "Using default device parameters\n", adv->unit); /* Set reasonable defaults since we can't read the EEPROM */ adv->isa_dma_speed = /*ADV_DEF_ISA_DMA_SPEED*/1; adv->max_openings = ADV_DEF_MAX_TOTAL_QNG; adv->disc_enable = TARGET_BIT_VECTOR_SET; adv->user_disc_enable = TARGET_BIT_VECTOR_SET; adv->cmd_qng_enabled = TARGET_BIT_VECTOR_SET; adv->user_cmd_qng_enabled = TARGET_BIT_VECTOR_SET; adv->scsi_id = 7; sync_data = ADV_DEF_SDTR_OFFSET | (ADV_DEF_SDTR_INDEX << 4); for (i = 0; i <= ADV_MAX_TID; i++) adv_sdtr_to_period_offset(adv, sync_data, &adv->tinfo[i].user.period, &adv->tinfo[i].user.offset, i); } if (adv_set_eeprom_config(adv, &eeprom_config) != 0) printf("%s: WARNING! Failure writing to EEPROM.\n", adv_name(adv)); adv_set_chip_scsiid(adv, adv->scsi_id); if (adv_init_lram_and_mcode(adv)) return (1); adv->disc_enable = adv->user_disc_enable; adv_write_lram_8(adv, ADVV_DISC_ENABLE_B, adv->disc_enable); for (i = 0; i <= ADV_MAX_TID; i++) { /* * Start off in async mode. */ adv_set_syncrate(adv, /*struct cam_path */NULL, i, /*period*/0, /*offset*/0, ADV_TRANS_CUR); /* * Enable the use of tagged commands on all targets.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -