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 + -
显示快捷键?