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