dpt_scsi.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,444 行 · 第 1/5 页

C
2,444
字号
/* *       Copyright (c) 1997 by Simon Shapiro *       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. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. 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. *//* * dpt_scsi.c: SCSI dependant code for the DPT driver * * credits:	Assisted by Mike Neuffer in the early low level DPT code *		Thanx to Mark Salyzyn of DPT for his assistance. *		Special thanx to Justin Gibbs for invaluable help in *		making this driver look and work like a FreeBSD component. *		Last but not least, many thanx to UCB and the FreeBSD *		team for creating and maintaining such a wonderful O/S. * * TODO:     * Add ISA probe code. *	     * Add driver-level RAID-0. This will allow interoperability with *	       NiceTry, M$-Doze, Win-Dog, Slowlaris, etc., in recognizing RAID *	       arrays that span controllers (Wow!). */#ident "$Id: dpt_scsi.c,v 1.22.2.1 1999/05/07 00:43:33 ken Exp $"#define _DPT_C_#include "opt_dpt.h"#include <sys/param.h>#include <sys/systm.h>#include <sys/malloc.h>#include <sys/buf.h>#include <sys/kernel.h>#include <stddef.h>	/* For offsetof */#include <machine/bus_memio.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_debug.h>#include <cam/scsi/scsi_all.h>#include <cam/scsi/scsi_message.h>#include <vm/vm.h>#include <vm/pmap.h>#include <dev/dpt/dpt.h>/* dpt_isa.c, dpt_eisa.c, and dpt_pci.c need this in a central place */int dpt_controllers_present;u_long	dpt_unit;	/* Next unit number to use *//* The linked list of softc structures */struct dpt_softc_list dpt_softcs = TAILQ_HEAD_INITIALIZER(dpt_softcs);#define microtime_now dpt_time_now()#define dpt_inl(dpt, port)				\	bus_space_read_4((dpt)->tag, (dpt)->bsh, port)#define dpt_inb(dpt, port)				\	bus_space_read_1((dpt)->tag, (dpt)->bsh, port)#define dpt_outl(dpt, port, value)			\	bus_space_write_4((dpt)->tag, (dpt)->bsh, port, value)#define dpt_outb(dpt, port, value)			\	bus_space_write_1((dpt)->tag, (dpt)->bsh, port, value)/* * These will have to be setup by parameters passed at boot/load time. For * perfromance reasons, we make them constants for the time being. */#define	dpt_min_segs	DPT_MAX_SEGS#define	dpt_max_segs	DPT_MAX_SEGS/* Definitions for our use of the SIM private CCB area */#define ccb_dccb_ptr spriv_ptr0#define ccb_dpt_ptr spriv_ptr1/* ================= Private Inline Function declarations ===================*/static __inline int		dpt_just_reset(dpt_softc_t * dpt);static __inline int		dpt_raid_busy(dpt_softc_t * dpt);static __inline int		dpt_wait(dpt_softc_t *dpt, u_int bits,					 u_int state);static __inline struct dpt_ccb* dptgetccb(struct dpt_softc *dpt);static __inline void		dptfreeccb(struct dpt_softc *dpt,					   struct dpt_ccb *dccb);static __inline u_int32_t	dptccbvtop(struct dpt_softc *dpt,					   struct dpt_ccb *dccb);static __inline int		dpt_send_immediate(dpt_softc_t *dpt,						   eata_ccb_t *cmd_block,						   u_int32_t cmd_busaddr,  						   u_int retries,						   u_int ifc, u_int code,						   u_int code2);/* ==================== Private Function declarations =======================*/static void		dptmapmem(void *arg, bus_dma_segment_t *segs,				  int nseg, int error);static struct sg_map_node*			dptallocsgmap(struct dpt_softc *dpt);static int		dptallocccbs(dpt_softc_t *dpt);static int		dpt_get_conf(dpt_softc_t *dpt, dpt_ccb_t *dccb,				     u_int32_t dccb_busaddr, u_int size,				     u_int page, u_int target, int extent);static void		dpt_detect_cache(dpt_softc_t *dpt, dpt_ccb_t *dccb,					 u_int32_t dccb_busaddr,					 u_int8_t *buff);static void		dpt_poll(struct cam_sim *sim);static void		dptexecuteccb(void *arg, bus_dma_segment_t *dm_segs,				      int nseg, int error);static void		dpt_action(struct cam_sim *sim, union ccb *ccb);static int		dpt_send_eata_command(dpt_softc_t *dpt, eata_ccb_t *cmd,					      u_int32_t cmd_busaddr,					      u_int command, u_int retries,					      u_int ifc, u_int code,					      u_int code2);static void		dptprocesserror(dpt_softc_t *dpt, dpt_ccb_t *dccb,					union ccb *ccb, u_int hba_stat,					u_int scsi_stat, u_int32_t resid);static void		dpttimeout(void *arg);static void		dptshutdown(int howto, void *arg);/* ================= Private Inline Function definitions ====================*/static __inline intdpt_just_reset(dpt_softc_t * dpt){	if ((dpt_inb(dpt, 2) == 'D')	 && (dpt_inb(dpt, 3) == 'P')	 && (dpt_inb(dpt, 4) == 'T')	 && (dpt_inb(dpt, 5) == 'H'))		return (1);	else		return (0);}static __inline intdpt_raid_busy(dpt_softc_t * dpt){	if ((dpt_inb(dpt, 0) == 'D')	 && (dpt_inb(dpt, 1) == 'P')	 && (dpt_inb(dpt, 2) == 'T'))		return (1);	else		return (0);}static __inline intdpt_wait(dpt_softc_t *dpt, u_int bits, u_int state){	int   i;	u_int c;	for (i = 0; i < 20000; i++) {	/* wait 20ms for not busy */		c = dpt_inb(dpt, HA_RSTATUS) & bits;		if (c == state)			return (0);		else			DELAY(50);	}	return (-1);}static __inline struct dpt_ccb*dptgetccb(struct dpt_softc *dpt){	struct	dpt_ccb* dccb;	int	s;	s = splcam();	if ((dccb = SLIST_FIRST(&dpt->free_dccb_list)) != NULL) {		SLIST_REMOVE_HEAD(&dpt->free_dccb_list, links);		dpt->free_dccbs--;	} else if (dpt->total_dccbs < dpt->max_dccbs) {		dptallocccbs(dpt);		dccb = SLIST_FIRST(&dpt->free_dccb_list);		if (dccb == NULL)			printf("dpt%d: Can't malloc DCCB\n", dpt->unit);		else {			SLIST_REMOVE_HEAD(&dpt->free_dccb_list, links);			dpt->free_dccbs--;		}	}	splx(s);	return (dccb);}static __inline voiddptfreeccb(struct dpt_softc *dpt, struct dpt_ccb *dccb){	int s;	s = splcam();	if ((dccb->state & DCCB_ACTIVE) != 0)		LIST_REMOVE(&dccb->ccb->ccb_h, sim_links.le);	if ((dccb->state & DCCB_RELEASE_SIMQ) != 0)		dccb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ;	else if (dpt->resource_shortage != 0	 && (dccb->ccb->ccb_h.status & CAM_RELEASE_SIMQ) == 0) {		dccb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ;		dpt->resource_shortage = FALSE;	}	dccb->state = DCCB_FREE;	SLIST_INSERT_HEAD(&dpt->free_dccb_list, dccb, links);	++dpt->free_dccbs;	splx(s);}static __inline u_int32_tdptccbvtop(struct dpt_softc *dpt, struct dpt_ccb *dccb){	return (dpt->dpt_ccb_busbase	      + (u_int32_t)((caddr_t)dccb - (caddr_t)dpt->dpt_dccbs));}static __inline struct dpt_ccb *dptccbptov(struct dpt_softc *dpt, u_int32_t busaddr){	return (dpt->dpt_dccbs	     +  ((struct dpt_ccb *)busaddr	       - (struct dpt_ccb *)dpt->dpt_ccb_busbase));}/* * Send a command for immediate execution by the DPT * See above function for IMPORTANT notes. */static __inline intdpt_send_immediate(dpt_softc_t *dpt, eata_ccb_t *cmd_block,		   u_int32_t cmd_busaddr, u_int retries,		   u_int ifc, u_int code, u_int code2){	return (dpt_send_eata_command(dpt, cmd_block, cmd_busaddr,				      EATA_CMD_IMMEDIATE, retries, ifc,				      code, code2));}/* ===================== Private Function definitions =======================*/static voiddptmapmem(void *arg, bus_dma_segment_t *segs, int nseg, int error){	bus_addr_t *busaddrp;	busaddrp = (bus_addr_t *)arg;	*busaddrp = segs->ds_addr;}static struct sg_map_node *dptallocsgmap(struct dpt_softc *dpt){	struct sg_map_node *sg_map;	sg_map = malloc(sizeof(*sg_map), M_DEVBUF, M_NOWAIT);	if (sg_map == NULL)		return (NULL);	/* Allocate S/G space for the next batch of CCBS */	if (bus_dmamem_alloc(dpt->sg_dmat, (void **)&sg_map->sg_vaddr,			     BUS_DMA_NOWAIT, &sg_map->sg_dmamap) != 0) {		free(sg_map, M_DEVBUF);		return (NULL);	}	(void)bus_dmamap_load(dpt->sg_dmat, sg_map->sg_dmamap, sg_map->sg_vaddr,			      PAGE_SIZE, dptmapmem, &sg_map->sg_physaddr,			      /*flags*/0);	SLIST_INSERT_HEAD(&dpt->sg_maps, sg_map, links);	return (sg_map);}/* * Allocate another chunk of CCB's. Return count of entries added. * Assumed to be called at splcam(). */static intdptallocccbs(dpt_softc_t *dpt){	struct dpt_ccb *next_ccb;	struct sg_map_node *sg_map;	bus_addr_t physaddr;	dpt_sg_t *segs;	int newcount;	int i;	next_ccb = &dpt->dpt_dccbs[dpt->total_dccbs];	if (next_ccb == dpt->dpt_dccbs) {		/*		 * First time through.  Re-use the S/G		 * space we allocated for initialization		 * CCBS.		 */		sg_map = SLIST_FIRST(&dpt->sg_maps);	} else {		sg_map = dptallocsgmap(dpt);	}	if (sg_map == NULL)		return (0);	segs = sg_map->sg_vaddr;	physaddr = sg_map->sg_physaddr;	newcount = (PAGE_SIZE / (dpt->sgsize * sizeof(dpt_sg_t)));	for (i = 0; dpt->total_dccbs < dpt->max_dccbs && i < newcount; i++) {		int error;		error = bus_dmamap_create(dpt->buffer_dmat, /*flags*/0,					  &next_ccb->dmamap);		if (error != 0)			break;		next_ccb->sg_list = segs;		next_ccb->sg_busaddr = htonl(physaddr);		next_ccb->eata_ccb.cp_dataDMA = htonl(physaddr);		next_ccb->eata_ccb.cp_statDMA = htonl(dpt->sp_physaddr);		next_ccb->eata_ccb.cp_reqDMA =		    htonl(dptccbvtop(dpt, next_ccb)			+ offsetof(struct dpt_ccb, sense_data));		next_ccb->eata_ccb.cp_busaddr = dpt->dpt_ccb_busend;		next_ccb->state = DCCB_FREE;		next_ccb->tag = dpt->total_dccbs;		SLIST_INSERT_HEAD(&dpt->free_dccb_list, next_ccb, links);		segs += dpt->sgsize;		physaddr += (dpt->sgsize * sizeof(dpt_sg_t));		dpt->dpt_ccb_busend += sizeof(*next_ccb);		next_ccb++;		dpt->total_dccbs++;	}	return (i);}/* * Read a configuration page into the supplied dpt_cont_t buffer. */static intdpt_get_conf(dpt_softc_t *dpt, dpt_ccb_t *dccb, u_int32_t dccb_busaddr,	     u_int size, u_int page, u_int target, int extent){	eata_ccb_t *cp;	u_int8_t   status;	int	   ndx;	int	   ospl;	int	   result;	cp = &dccb->eata_ccb;	bzero((void *)dpt->sp, sizeof(*dpt->sp));	cp->Interpret = 1;	cp->DataIn = 1;	cp->Auto_Req_Sen = 1;	cp->reqlen = sizeof(struct scsi_sense_data);	cp->cp_id = target;	cp->cp_LUN = 0;		/* In the EATA packet */	cp->cp_lun = 0;		/* In the SCSI command */	cp->cp_scsi_cmd = INQUIRY;	cp->cp_len = size;	cp->cp_extent = extent;	cp->cp_page = page;	cp->cp_channel = 0;	/* DNC, Interpret mode is set */	cp->cp_identify = 1;	cp->cp_datalen = htonl(size);	ospl = splcam();	/*	 * This could be a simple for loop, but we suspected the compiler To	 * have optimized it a bit too much. Wait for the controller to	 * become ready	 */	while (((status = dpt_inb(dpt, HA_RSTATUS)) != (HA_SREADY | HA_SSC)	     && (status != (HA_SREADY | HA_SSC | HA_SERROR))	     && (status != (HA_SDRDY | HA_SERROR | HA_SDRQ)))	    || (dpt_wait(dpt, HA_SBUSY, 0))) {		/*		 * RAID Drives still Spinning up? (This should only occur if		 * the DPT controller is in a NON PC (PCI?) platform).		 */		if (dpt_raid_busy(dpt)) {			printf("dpt%d WARNING: Get_conf() RSUS failed.\n",			       dpt->unit);			splx(ospl);			return (0);		}	}	DptStat_Reset_BUSY(dpt->sp);	/*	 * XXXX We might want to do something more clever than aborting at	 * this point, like resetting (rebooting) the controller and trying	 * again.	 */	if ((result = dpt_send_eata_command(dpt, cp, dccb_busaddr,					    EATA_CMD_DMA_SEND_CP,					    10000, 0, 0, 0)) != 0) {		printf("dpt%d WARNING: Get_conf() failed (%d) to send "		       "EATA_CMD_DMA_READ_CONFIG\n",		       dpt->unit, result);		splx(ospl);		return (0);	}	/* Wait for two seconds for a response.  This can be slow  */	for (ndx = 0;	     (ndx < 20000)	     && !((status = dpt_inb(dpt, HA_RAUXSTAT)) & HA_AIRQ);	     ndx++) {		DELAY(50);	}	/* Grab the status and clear interrupts */	status = dpt_inb(dpt, HA_RSTATUS);	splx(ospl);	/*	 * Check the status carefully.  Return only if the	 * command was successful.	 */	if (((status & HA_SERROR) == 0)	 && (dpt->sp->hba_stat == 0)	 && (dpt->sp->scsi_stat == 0)	 && (dpt->sp->residue_len == 0))		return (0);	return (1);}/* Detect Cache parameters and size */static voiddpt_detect_cache(dpt_softc_t *dpt, dpt_ccb_t *dccb, u_int32_t dccb_busaddr,		 u_int8_t *buff){	eata_ccb_t *cp;	u_int8_t   *param;	int	    bytes;	int	    result;	int	    ospl;	int	    ndx;	u_int8_t    status;	/*	 * Default setting, for best perfromance..

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?