📄 advansys.c
字号:
/* * Generic driver for the Advanced Systems Inc. SCSI controllers * Product specific probe and attach routines can be found in: * * i386/isa/adv_isa.c ABP5140, ABP542, ABP5150, ABP842, ABP852 * i386/eisa/adv_eisa.c ABP742, ABP752 * pci/adv_pci.c ABP920, ABP930, ABP930U, ABP930UA, ABP940, ABP940U, * ABP940UA, ABP950, ABP960, ABP960U, ABP960UA, * ABP970, ABP970U * * Copyright (c) 1996-1998 Justin Gibbs. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: advansys.c,v 1.7.2.3 1999/05/07 00:43:19 ken Exp $ *//* * Ported from: * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters * * Copyright (c) 1995-1997 Advanced System Products, Inc. * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that redistributions of source * code retain the above copyright notice and this comment without * modification. */ #include <sys/param.h>#include <sys/systm.h>#include <sys/malloc.h>#include <sys/buf.h>#include <sys/kernel.h>#include <machine/bus_pio.h>#include <machine/bus.h>#include <machine/clock.h>#include <cam/cam.h>#include <cam/cam_ccb.h>#include <cam/cam_sim.h>#include <cam/cam_xpt_sim.h>#include <cam/cam_xpt_periph.h>#include <cam/cam_debug.h>#include <cam/scsi/scsi_all.h>#include <cam/scsi/scsi_message.h>#include <vm/vm.h>#include <vm/vm_param.h>#include <vm/pmap.h>#include <dev/advansys/advansys.h>u_long adv_unit;static void adv_action(struct cam_sim *sim, union ccb *ccb);static void adv_execute_ccb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, int error);static void adv_poll(struct cam_sim *sim);static void adv_run_doneq(struct adv_softc *adv);static struct adv_ccb_info * adv_alloc_ccb_info(struct adv_softc *adv);static void adv_destroy_ccb_info(struct adv_softc *adv, struct adv_ccb_info *cinfo); static __inline struct adv_ccb_info * adv_get_ccb_info(struct adv_softc *adv);static __inline void adv_free_ccb_info(struct adv_softc *adv, struct adv_ccb_info *cinfo);struct adv_softc *advsoftcs[NADV]; /* XXX Config should handle this */static __inline struct adv_ccb_info *adv_get_ccb_info(struct adv_softc *adv){ struct adv_ccb_info *cinfo; int opri; opri = splcam(); if ((cinfo = SLIST_FIRST(&adv->free_ccb_infos)) != NULL) { SLIST_REMOVE_HEAD(&adv->free_ccb_infos, links); } else { cinfo = adv_alloc_ccb_info(adv); } splx(opri); return (cinfo);}static __inline voidadv_free_ccb_info(struct adv_softc *adv, struct adv_ccb_info *cinfo){ int opri; opri = splcam(); cinfo->state = ACCB_FREE; SLIST_INSERT_HEAD(&adv->free_ccb_infos, cinfo, links); splx(opri);}void adv_map(void *arg, bus_dma_segment_t *segs, int nseg, int error){ bus_addr_t* physaddr; physaddr = (bus_addr_t*)arg; *physaddr = segs->ds_addr;}char *adv_name(struct adv_softc *adv){ static char name[10]; snprintf(name, sizeof(name), "adv%d", adv->unit); return (name);}static voidadv_action(struct cam_sim *sim, union ccb *ccb){ struct adv_softc *adv; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("adv_action\n")); adv = (struct adv_softc *)cam_sim_softc(sim); switch (ccb->ccb_h.func_code) { /* Common cases first */ case XPT_SCSI_IO: /* Execute the requested I/O operation */ { struct ccb_hdr *ccb_h; struct ccb_scsiio *csio; struct adv_ccb_info *cinfo; ccb_h = &ccb->ccb_h; csio = &ccb->csio; cinfo = adv_get_ccb_info(adv); if (cinfo == NULL) panic("XXX Handle CCB info error!!!"); ccb_h->ccb_cinfo_ptr = cinfo; /* Only use S/G if there is a transfer */ if ((ccb_h->flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if ((ccb_h->flags & CAM_SCATTER_VALID) == 0) { /* * We've been given a pointer * to a single buffer */ if ((ccb_h->flags & CAM_DATA_PHYS) == 0) { int s; int error; s = splsoftvm(); error = bus_dmamap_load(adv->buffer_dmat, cinfo->dmamap, csio->data_ptr, csio->dxfer_len, adv_execute_ccb, csio, /*flags*/0); if (error == EINPROGRESS) { /* * So as to maintain ordering, * freeze the controller queue * until our mapping is * returned. */ xpt_freeze_simq(adv->sim, /*count*/1); cinfo->state |= ACCB_RELEASE_SIMQ; } splx(s); } else { struct bus_dma_segment seg; /* Pointer to physical buffer */ seg.ds_addr = (bus_addr_t)csio->data_ptr; seg.ds_len = csio->dxfer_len; adv_execute_ccb(csio, &seg, 1, 0); } } else { struct bus_dma_segment *segs; if ((ccb_h->flags & CAM_DATA_PHYS) != 0) panic("adv_setup_data - Physical " "segment pointers unsupported"); if ((ccb_h->flags & CAM_SG_LIST_PHYS) == 0) panic("adv_setup_data - Virtual " "segment addresses unsupported"); /* Just use the segments provided */ segs = (struct bus_dma_segment *)csio->data_ptr; adv_execute_ccb(ccb, segs, csio->sglist_cnt, 0); } } else { adv_execute_ccb(ccb, NULL, 0, 0); } break; } case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */ case XPT_TARGET_IO: /* Execute target I/O request */ case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ case XPT_EN_LUN: /* Enable LUN as a target */ case XPT_ABORT: /* Abort the specified CCB */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_SET_TRAN_SETTINGS: { struct ccb_trans_settings *cts; target_bit_vector targ_mask; struct adv_transinfo *tconf; u_int update_type; int s; cts = &ccb->cts; targ_mask = ADV_TID_TO_TARGET_MASK(cts->ccb_h.target_id); update_type = 0; /* * The user must specify which type of settings he wishes * to change. */ if (((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) && ((cts->flags & CCB_TRANS_USER_SETTINGS) == 0)) { tconf = &adv->tinfo[cts->ccb_h.target_id].current; update_type |= ADV_TRANS_GOAL; } else if (((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) && ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) == 0)) { tconf = &adv->tinfo[cts->ccb_h.target_id].user; update_type |= ADV_TRANS_USER; } else { ccb->ccb_h.status = CAM_REQ_INVALID; break; } s = splcam(); if ((update_type & ADV_TRANS_GOAL) != 0) { if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) adv->disc_enable |= targ_mask; else adv->disc_enable &= ~targ_mask; adv_write_lram_8(adv, ADVV_DISC_ENABLE_B, adv->disc_enable); } if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) adv->cmd_qng_enabled |= targ_mask; else adv->cmd_qng_enabled &= ~targ_mask; } } if ((update_type & ADV_TRANS_USER) != 0) { if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) adv->user_disc_enable |= targ_mask; else adv->user_disc_enable &= ~targ_mask; } if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) adv->user_cmd_qng_enabled |= targ_mask; else adv->user_cmd_qng_enabled &= ~targ_mask; } } /* * If the user specifies either the sync rate, or offset, * but not both, the unspecified parameter defaults to its * current value in transfer negotiations. */ if (((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) || ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0)) { /* * If the user provided a sync rate but no offset, * use the current offset. */ if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) == 0) cts->sync_offset = tconf->offset; /* * If the user provided an offset but no sync rate, * use the current sync rate. */ if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) == 0) cts->sync_period = tconf->period; adv_period_offset_to_sdtr(adv, &cts->sync_period, &cts->sync_offset, cts->ccb_h.target_id); adv_set_syncrate(adv, /*struct cam_path */NULL, cts->ccb_h.target_id, cts->sync_period, cts->sync_offset, update_type); } splx(s); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_GET_TRAN_SETTINGS: /* Get default/user set transfer settings for the target */ { struct ccb_trans_settings *cts; struct adv_transinfo *tconf; target_bit_vector target_mask; int s; cts = &ccb->cts; target_mask = ADV_TID_TO_TARGET_MASK(cts->ccb_h.target_id); cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB); s = splcam(); if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { tconf = &adv->tinfo[cts->ccb_h.target_id].current; if ((adv->disc_enable & target_mask) != 0) cts->flags |= CCB_TRANS_DISC_ENB; if ((adv->cmd_qng_enabled & target_mask) != 0) cts->flags |= CCB_TRANS_TAG_ENB; } else { tconf = &adv->tinfo[cts->ccb_h.target_id].user; if ((adv->user_disc_enable & target_mask) != 0) cts->flags |= CCB_TRANS_DISC_ENB; if ((adv->user_cmd_qng_enabled & target_mask) != 0) cts->flags |= CCB_TRANS_TAG_ENB; } cts->sync_period = tconf->period; cts->sync_offset = tconf->offset; splx(s); cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; cts->valid = CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID | CCB_TRANS_BUS_WIDTH_VALID | CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_CALC_GEOMETRY: { struct ccb_calc_geometry *ccg; u_int32_t size_mb; u_int32_t secs_per_cylinder; int extended; ccg = &ccb->ccg; size_mb = ccg->volume_size / ((1024L * 1024L) / ccg->block_size); extended = (adv->control & ADV_CNTL_BIOS_GT_1GB) != 0; if (size_mb > 1024 && extended) { ccg->heads = 255; ccg->secs_per_track = 63; } else { ccg->heads = 64; ccg->secs_per_track = 32; } secs_per_cylinder = ccg->heads * ccg->secs_per_track; ccg->cylinders = ccg->volume_size / secs_per_cylinder; ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_RESET_BUS: /* Reset the specified SCSI bus */ { int s; s = splcam(); adv_stop_execution(adv); adv_reset_bus(adv); adv_start_execution(adv); splx(s); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } case XPT_TERM_IO: /* Terminate the I/O process */ /* XXX Implement */ ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; case XPT_PATH_INQ: /* Path routing inquiry */ { struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; /* XXX??? */ cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE; cpi->target_sprt = 0; cpi->hba_misc = 0; cpi->hba_eng_cnt = 0; cpi->max_target = 7; cpi->max_lun = 7; cpi->initiator_id = adv->scsi_id; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 3300; strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); strncpy(cpi->hba_vid, "Advansys", HBA_IDLEN); strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); cpi->unit_number = cam_sim_unit(sim); cpi->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; } default: ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); break; }}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -