aic7xxx.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,383 行 · 第 1/5 页
C
2,383 行
/* * Generic driver for the aic7xxx based adaptec SCSI controllers * Product specific probe and attach routines can be found in: * i386/eisa/ahc_eisa.c 27/284X and aic7770 motherboard controllers * pci/ahc_pci.c 3985, 3980, 3940, 2940, aic7895, aic7890, * aic7880, aic7870, aic7860, and aic7850 controllers * * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999 Justin T. 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. * * Where this Software is combined with software released under the terms of * the GNU Public License ("GPL") and the terms of the GPL would require the * combined work to also be released under the terms of the GPL, the terms * and conditions of this License will apply in addition to those of the * GPL with the exception of any terms or conditions of this License that * conflict with, or are expressly prohibited by, the GPL. * * 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: aic7xxx.c,v 1.16.2.8 1999/05/16 00:08:45 gibbs Exp $ *//* * A few notes on features of the driver. * * SCB paging takes advantage of the fact that devices stay disconnected * from the bus a relatively long time and that while they're disconnected, * having the SCBs for these transactions down on the host adapter is of * little use. Instead of leaving this idle SCB down on the card we copy * it back up into kernel memory and reuse the SCB slot on the card to * schedule another transaction. This can be a real payoff when doing random * I/O to tagged queueing devices since there are more transactions active at * once for the device to sort for optimal seek reduction. The algorithm goes * like this... * * The sequencer maintains two lists of its hardware SCBs. The first is the * singly linked free list which tracks all SCBs that are not currently in * use. The second is the doubly linked disconnected list which holds the * SCBs of transactions that are in the disconnected state sorted most * recently disconnected first. When the kernel queues a transaction to * the card, a hardware SCB to "house" this transaction is retrieved from * either of these two lists. If the SCB came from the disconnected list, * a check is made to see if any data transfer or SCB linking (more on linking * in a bit) information has been changed since it was copied from the host * and if so, DMAs the SCB back up before it can be used. Once a hardware * SCB has been obtained, the SCB is DMAed from the host. Before any work * can begin on this SCB, the sequencer must ensure that either the SCB is * for a tagged transaction or the target is not already working on another * non-tagged transaction. If a conflict arises in the non-tagged case, the * sequencer finds the SCB for the active transactions and sets the SCB_LINKED * field in that SCB to this next SCB to execute. To facilitate finding * active non-tagged SCBs, the last four bytes of up to the first four hardware * SCBs serve as a storage area for the currently active SCB ID for each * target. * * When a device reconnects, a search is made of the hardware SCBs to find * the SCB for this transaction. If the search fails, a hardware SCB is * pulled from either the free or disconnected SCB list and the proper * SCB is DMAed from the host. If the MK_MESSAGE control bit is set * in the control byte of the SCB while it was disconnected, the sequencer * will assert ATN and attempt to issue a message to the host. * * When a command completes, a check for non-zero status and residuals is * made. If either of these conditions exists, the SCB is DMAed back up to * the host so that it can interpret this information. Additionally, in the * case of bad status, the sequencer generates a special interrupt and pauses * itself. This allows the host to setup a request sense command if it * chooses for this target synchronously with the error so that sense * information isn't lost. * */#include <opt_aic7xxx.h>#include <pci.h>#include <stddef.h> /* For offsetof */#include <sys/param.h>#include <sys/systm.h>#include <sys/malloc.h>#include <sys/buf.h>#include <sys/proc.h>#include <cam/cam.h>#include <cam/cam_ccb.h>#include <cam/cam_sim.h>#include <cam/cam_xpt_sim.h>#include <cam/cam_debug.h>#include <cam/scsi/scsi_all.h>#include <cam/scsi/scsi_message.h>#if NPCI > 0#include <machine/bus_memio.h>#endif#include <machine/bus_pio.h>#include <machine/bus.h>#include <machine/clock.h>#include <vm/vm.h>#include <vm/vm_param.h>#include <vm/pmap.h>#include <dev/aic7xxx/aic7xxx.h>#include <dev/aic7xxx/sequencer.h>#include <aic7xxx_reg.h>#include <aic7xxx_seq.h>#include <sys/kernel.h>#ifndef AHC_TMODE_ENABLE#define AHC_TMODE_ENABLE 0#endif#define MAX(a,b) (((a) > (b)) ? (a) : (b))#define MIN(a,b) (((a) < (b)) ? (a) : (b))#define ALL_CHANNELS '\0'#define ALL_TARGETS_MASK 0xFFFF#define INITIATOR_WILDCARD (~0)#define SIM_IS_SCSIBUS_B(ahc, sim) \ ((sim) == ahc->sim_b)#define SIM_CHANNEL(ahc, sim) \ (((sim) == ahc->sim_b) ? 'B' : 'A')#define SIM_SCSI_ID(ahc, sim) \ (((sim) == ahc->sim_b) ? ahc->our_id_b : ahc->our_id)#define SIM_PATH(ahc, sim) \ (((sim) == ahc->sim_b) ? ahc->path_b : ahc->path)#define SCB_IS_SCSIBUS_B(scb) \ (((scb)->hscb->tcl & SELBUSB) != 0)#define SCB_TARGET(scb) \ (((scb)->hscb->tcl & TID) >> 4)#define SCB_CHANNEL(scb) \ (SCB_IS_SCSIBUS_B(scb) ? 'B' : 'A')#define SCB_LUN(scb) \ ((scb)->hscb->tcl & LID)#define SCB_TARGET_OFFSET(scb) \ (SCB_TARGET(scb) + (SCB_IS_SCSIBUS_B(scb) ? 8 : 0))#define SCB_TARGET_MASK(scb) \ (0x01 << (SCB_TARGET_OFFSET(scb)))#define TCL_CHANNEL(ahc, tcl) \ ((((ahc)->features & AHC_TWIN) && ((tcl) & SELBUSB)) ? 'B' : 'A')#define TCL_SCSI_ID(ahc, tcl) \ (TCL_CHANNEL((ahc), (tcl)) == 'B' ? (ahc)->our_id_b : (ahc)->our_id)#define TCL_TARGET(tcl) (((tcl) & TID) >> TCL_TARGET_SHIFT)#define TCL_LUN(tcl) ((tcl) & LID)#define ccb_scb_ptr spriv_ptr0#define ccb_ahc_ptr spriv_ptr1typedef enum { ROLE_UNKNOWN, ROLE_INITIATOR, ROLE_TARGET,} role_t;struct ahc_devinfo { int our_scsiid; int target_offset; u_int16_t target_mask; u_int8_t target; u_int8_t lun; char channel; role_t role; /* * Only guaranteed to be correct if not * in the busfree state. */};typedef enum { SEARCH_COMPLETE, SEARCH_COUNT, SEARCH_REMOVE} ahc_search_action;u_long ahc_unit;#ifdef AHC_DEBUGstatic int ahc_debug = AHC_DEBUG;#endif#if NPCI > 0void ahc_pci_intr(struct ahc_softc *ahc);#endifstatic int ahcinitscbdata(struct ahc_softc *ahc);static void ahcfiniscbdata(struct ahc_softc *ahc);static bus_dmamap_callback_t ahcdmamapcb; #if UNUSEDstatic void ahc_dump_targcmd(struct target_cmd *cmd);#endifstatic void ahc_shutdown(int howto, void *arg);static cam_status ahc_find_tmode_devs(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb, struct tmode_tstate **tstate, struct tmode_lstate **lstate, int notfound_failure);static void ahc_action(struct cam_sim *sim, union ccb *ccb);static void ahc_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg);static void ahc_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments, int error);static void ahc_poll(struct cam_sim *sim);static void ahc_setup_data(struct ahc_softc *ahc, struct ccb_scsiio *csio, struct scb *scb);static void ahc_freeze_devq(struct ahc_softc *ahc, struct cam_path *path);static void ahcallocscbs(struct ahc_softc *ahc);static void ahc_scb_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, struct scb *scb);static void ahc_fetch_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo);static void ahc_compile_devinfo(struct ahc_devinfo *devinfo, u_int our_id, u_int target, u_int lun, char channel, role_t role);static u_int ahc_abort_wscb(struct ahc_softc *ahc, u_int scbpos, u_int prev);static void ahc_done(struct ahc_softc *ahc, struct scb *scbp);static struct tmode_tstate * ahc_alloc_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel);static void ahc_free_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel, int force);static void ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb);static int ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd);static void ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat);static void ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat);static void ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo);static void ahc_setup_initiator_msgout(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, struct scb *scb);static void ahc_setup_target_msgin(struct ahc_softc *ahc, struct ahc_devinfo *devinfo);static int ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo);static void ahc_clear_msg_state(struct ahc_softc *ahc);static void ahc_handle_message_phase(struct ahc_softc *ahc, struct cam_path *path);static int ahc_sent_msg(struct ahc_softc *ahc, u_int msgtype, int full);static int ahc_parse_msg(struct ahc_softc *ahc, struct cam_path *path, struct ahc_devinfo *devinfo);static void ahc_handle_ign_wide_residue(struct ahc_softc *ahc, struct ahc_devinfo *devinfo);static void ahc_handle_devreset(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, cam_status status, ac_code acode, char *message, int verbose_only);static void ahc_loadseq(struct ahc_softc *ahc);static int ahc_check_patch(struct ahc_softc *ahc, struct patch **start_patch, int start_instr, int *skip_addr);static void ahc_download_instr(struct ahc_softc *ahc, int instrptr, u_int8_t *dconsts);static int ahc_match_scb(struct scb *scb, int target, char channel, int lun, u_int tag);#ifdef AHC_DEBUGstatic void ahc_print_scb(struct scb *scb);#endifstatic int ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel, int lun, u_int tag, u_int32_t status, ahc_search_action action);static void ahc_abort_ccb(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb);static int ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset);static int ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel, int lun, u_int tag, u_int32_t status);static int ahc_search_disc_list(struct ahc_softc *ahc, int target, char channel, int lun, u_int tag);static u_int ahc_rem_scb_from_disc_list(struct ahc_softc *ahc, u_int prev, u_int scbptr);static void ahc_add_curscb_to_free_list(struct ahc_softc *ahc);static void ahc_clear_intstat(struct ahc_softc *ahc);static void ahc_reset_current_bus(struct ahc_softc *ahc);static struct ahc_syncrate * ahc_devlimited_syncrate(struct ahc_softc *ahc, u_int *period);static struct ahc_syncrate * ahc_find_syncrate(struct ahc_softc *ahc, u_int *period, u_int maxsync);static u_int ahc_find_period(struct ahc_softc *ahc, u_int scsirate, u_int maxsync);static void ahc_validate_offset(struct ahc_softc *ahc, struct ahc_syncrate *syncrate, u_int *offset, int wide); static void ahc_update_target_msg_request(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, struct ahc_initiator_tinfo *tinfo, int force, int paused);static int ahc_create_path(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, struct cam_path **path);static void ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, struct cam_path *path, struct ahc_syncrate *syncrate, u_int period, u_int offset, u_int type);static void ahc_set_width(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, struct cam_path *path, u_int width, u_int type);static void ahc_set_tags(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, int enable);static void ahc_construct_sdtr(struct ahc_softc *ahc, u_int period, u_int offset); static void ahc_construct_wdtr(struct ahc_softc *ahc, u_int bus_width);static void ahc_calc_residual(struct scb *scb);static void ahc_update_pending_syncrates(struct ahc_softc *ahc);static void ahc_set_recoveryscb(struct ahc_softc *ahc, struct scb *scb);static timeout_t ahc_timeout;static __inline int sequencer_paused(struct ahc_softc *ahc);static __inline void pause_sequencer(struct ahc_softc *ahc);static __inline void unpause_sequencer(struct ahc_softc *ahc, int unpause_always);static __inline void restart_sequencer(struct ahc_softc *ahc);static __inline u_int ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl, int unbusy); static __inline void ahc_busy_tcl(struct ahc_softc *ahc, struct scb *scb);static __inline void ahc_freeze_ccb(union ccb* ccb);static __inline cam_status ahc_ccb_status(union ccb* ccb);static __inline void ahcsetccbstatus(union ccb* ccb, cam_status status);static __inline void ahc_run_tqinfifo(struct ahc_softc *ahc);static __inline struct ahc_initiator_tinfo * ahc_fetch_transinfo(struct ahc_softc *ahc, char channel, u_int our_id, u_int target, struct tmode_tstate **tstate);static __inline void ahcfreescb(struct ahc_softc *ahc, struct scb *scb);static __inline struct scb * ahcgetscb(struct ahc_softc *ahc);static __inline u_int32_tahc_hscb_busaddr(struct ahc_softc *ahc, u_int index){ return (ahc->scb_data->hscb_busaddr + (sizeof(struct hardware_scb) * index));}#define AHC_BUSRESET_DELAY 25 /* Reset delay in us */static __inline intsequencer_paused(struct ahc_softc *ahc){ return ((ahc_inb(ahc, HCNTRL) & PAUSE) != 0);}static __inline voidpause_sequencer(struct ahc_softc *ahc){ ahc_outb(ahc, HCNTRL, ahc->pause); /* * Since the sequencer can disable pausing in a critical section, we * must loop until it actually stops. */ while (sequencer_paused(ahc) == 0) ;}static __inline voidunpause_sequencer(struct ahc_softc *ahc, int unpause_always){ if (unpause_always || (ahc_inb(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0) ahc_outb(ahc, HCNTRL, ahc->unpause);}/* * Restart the sequencer program from address zero */static __inline voidrestart_sequencer(struct ahc_softc *ahc){ pause_sequencer(ahc); ahc_outb(ahc, SEQCTL, FASTMODE|SEQRESET); unpause_sequencer(ahc, /*unpause_always*/TRUE);}static __inline u_intahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl, int unbusy){ u_int scbid; scbid = ahc->untagged_scbs[tcl]; if (unbusy) ahc->untagged_scbs[tcl] = SCB_LIST_NULL; return (scbid);}static __inline voidahc_busy_tcl(struct ahc_softc *ahc, struct scb *scb){ ahc->untagged_scbs[scb->hscb->tcl] = scb->hscb->tag;}static __inline voidahc_freeze_ccb(union ccb* ccb){ if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { ccb->ccb_h.status |= CAM_DEV_QFRZN; xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); }}static __inline cam_statusahc_ccb_status(union ccb* ccb){ return (ccb->ccb_h.status & CAM_STATUS_MASK);}static __inline voidahcsetccbstatus(union ccb* ccb, cam_status status){ ccb->ccb_h.status &= ~CAM_STATUS_MASK; ccb->ccb_h.status |= status;}static __inline struct ahc_initiator_tinfo *ahc_fetch_transinfo(struct ahc_softc *ahc, char channel, u_int our_id, u_int remote_id, struct tmode_tstate **tstate){ /* * Transfer data structures are stored from the perspective * of the target role. Since the parameters for a connection * in the initiator role to a given target are the same as * when the roles are reversed, we pretend we are the target. */ if (channel == 'B') our_id += 8; *tstate = ahc->enabled_targets[our_id]; return (&(*tstate)->transinfo[remote_id]);}static __inline voidahc_run_tqinfifo(struct ahc_softc *ahc){ struct target_cmd *cmd; while ((cmd = &ahc->targetcmds[ahc->tqinfifonext])->cmd_valid != 0) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?