📄 adwcam.c
字号:
/* * CAM SCSI interface for the the Advanced Systems Inc. * Second Generation SCSI controllers. * * Product specific probe and attach routines can be found in: * * pci/adw_pci.c ABP940UW * * Copyright (c) 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: adwcam.c,v 1.2.2.1 1999/05/07 00:43:22 ken Exp $ *//* * Ported from: * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters * * Copyright (c) 1995-1998 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 <stddef.h> /* For offsetof */#include <sys/param.h>#include <sys/systm.h>#include <sys/kernel.h>#include <sys/malloc.h>#include <machine/bus_pio.h>#include <machine/bus_memio.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_message.h>#include <dev/advansys/adwvar.h>/* Definitions for our use of the SIM private CCB area */#define ccb_acb_ptr spriv_ptr0#define ccb_adw_ptr spriv_ptr1#define MIN(a, b) (((a) < (b)) ? (a) : (b))u_long adw_unit;static __inline u_int32_t acbvtop(struct adw_softc *adw, struct acb *acb);static __inline struct acb * acbptov(struct adw_softc *adw, u_int32_t busaddr);static __inline struct acb* adwgetacb(struct adw_softc *adw);static __inline void adwfreeacb(struct adw_softc *adw, struct acb *acb);static void adwmapmem(void *arg, bus_dma_segment_t *segs, int nseg, int error);static struct sg_map_node* adwallocsgmap(struct adw_softc *adw);static int adwallocacbs(struct adw_softc *adw);static void adwexecuteacb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error);static void adw_action(struct cam_sim *sim, union ccb *ccb);static void adw_poll(struct cam_sim *sim);static void adw_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg);static void adwprocesserror(struct adw_softc *adw, struct acb *acb);static void adwtimeout(void *arg);static void adw_handle_device_reset(struct adw_softc *adw, u_int target);static void adw_handle_bus_reset(struct adw_softc *adw, int initiated);static __inline u_int32_tacbvtop(struct adw_softc *adw, struct acb *acb){ return (adw->acb_busbase + (u_int32_t)((caddr_t)acb - (caddr_t)adw->acbs));}static __inline struct acb *acbptov(struct adw_softc *adw, u_int32_t busaddr){ return (adw->acbs + ((struct acb *)busaddr - (struct acb *)adw->acb_busbase));}static __inline struct acb*adwgetacb(struct adw_softc *adw){ struct acb* acb; int s; s = splcam(); if ((acb = SLIST_FIRST(&adw->free_acb_list)) != NULL) { SLIST_REMOVE_HEAD(&adw->free_acb_list, links); } else if (adw->num_acbs < adw->max_acbs) { adwallocacbs(adw); acb = SLIST_FIRST(&adw->free_acb_list); if (acb == NULL) printf("%s: Can't malloc ACB\n", adw_name(adw)); else { SLIST_REMOVE_HEAD(&adw->free_acb_list, links); } } splx(s); return (acb);}static __inline voidadwfreeacb(struct adw_softc *adw, struct acb *acb){ int s; s = splcam(); if ((acb->state & ACB_ACTIVE) != 0) LIST_REMOVE(&acb->ccb->ccb_h, sim_links.le); if ((acb->state & ACB_RELEASE_SIMQ) != 0) acb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ; else if ((adw->state & ADW_RESOURCE_SHORTAGE) != 0 && (acb->ccb->ccb_h.status & CAM_RELEASE_SIMQ) == 0) { acb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ; adw->state &= ~ADW_RESOURCE_SHORTAGE; } acb->state = ACB_FREE; SLIST_INSERT_HEAD(&adw->free_acb_list, acb, links); splx(s);}static voidadwmapmem(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 *adwallocsgmap(struct adw_softc *adw){ 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 ACBS */ if (bus_dmamem_alloc(adw->sg_dmat, (void **)&sg_map->sg_vaddr, BUS_DMA_NOWAIT, &sg_map->sg_dmamap) != 0) { free(sg_map, M_DEVBUF); return (NULL); } SLIST_INSERT_HEAD(&adw->sg_maps, sg_map, links); bus_dmamap_load(adw->sg_dmat, sg_map->sg_dmamap, sg_map->sg_vaddr, PAGE_SIZE, adwmapmem, &sg_map->sg_physaddr, /*flags*/0); bzero(sg_map->sg_vaddr, PAGE_SIZE); return (sg_map);}/* * Allocate another chunk of CCB's. Return count of entries added. * Assumed to be called at splcam(). */static intadwallocacbs(struct adw_softc *adw){ struct acb *next_acb; struct sg_map_node *sg_map; bus_addr_t busaddr; struct adw_sg_block *blocks; int newcount; int i; next_acb = &adw->acbs[adw->num_acbs]; sg_map = adwallocsgmap(adw); if (sg_map == NULL) return (0); blocks = sg_map->sg_vaddr; busaddr = sg_map->sg_physaddr; newcount = (PAGE_SIZE / (ADW_SG_BLOCKCNT * sizeof(*blocks))); for (i = 0; adw->num_acbs < adw->max_acbs && i < newcount; i++) { int error; int j; error = bus_dmamap_create(adw->buffer_dmat, /*flags*/0, &next_acb->dmamap); if (error != 0) break; next_acb->queue.scsi_req_baddr = acbvtop(adw, next_acb); next_acb->queue.sense_addr = acbvtop(adw, next_acb) + offsetof(struct acb, sense_data); next_acb->sg_blocks = blocks; next_acb->sg_busaddr = busaddr; /* Setup static data in the sg blocks */ for (j = 0; j < ADW_SG_BLOCKCNT; j++) { next_acb->sg_blocks[j].first_entry_no = j * ADW_NO_OF_SG_PER_BLOCK; } next_acb->state = ACB_FREE; SLIST_INSERT_HEAD(&adw->free_acb_list, next_acb, links); blocks += ADW_SG_BLOCKCNT; busaddr += ADW_SG_BLOCKCNT * sizeof(*blocks); next_acb++; adw->num_acbs++; } return (i);}static voidadwexecuteacb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error){ struct acb *acb; union ccb *ccb; struct adw_softc *adw; int s; acb = (struct acb *)arg; ccb = acb->ccb; adw = (struct adw_softc *)ccb->ccb_h.ccb_adw_ptr; if (error != 0) { if (error != EFBIG) printf("%s: Unexepected error 0x%x returned from " "bus_dmamap_load\n", adw_name(adw), error); if (ccb->ccb_h.status == CAM_REQ_INPROG) { xpt_freeze_devq(ccb->ccb_h.path, /*count*/1); ccb->ccb_h.status = CAM_REQ_TOO_BIG|CAM_DEV_QFRZN; } adwfreeacb(adw, acb); xpt_done(ccb); return; } if (nseg != 0) { bus_dmasync_op_t op; acb->queue.data_addr = dm_segs[0].ds_addr; acb->queue.data_cnt = ccb->csio.dxfer_len; if (nseg > 1) { struct adw_sg_block *sg_block; struct adw_sg_elm *sg; bus_addr_t sg_busaddr; u_int sg_index; bus_dma_segment_t *end_seg; end_seg = dm_segs + nseg; sg_busaddr = acb->sg_busaddr; sg_index = 0; /* Copy the segments into our SG list */ for (sg_block = acb->sg_blocks;; sg_block++) { u_int sg_left; sg_left = ADW_NO_OF_SG_PER_BLOCK; sg = sg_block->sg_list; while (dm_segs < end_seg && sg_left != 0) { sg->sg_addr = dm_segs->ds_addr; sg->sg_count = dm_segs->ds_len; sg++; dm_segs++; sg_left--; } sg_index += ADW_NO_OF_SG_PER_BLOCK - sg_left; sg_block->last_entry_no = sg_index - 1; if (dm_segs == end_seg) { sg_block->sg_busaddr_next = 0; break; } else { sg_busaddr += sizeof(struct adw_sg_block); sg_block->sg_busaddr_next = sg_busaddr; } } acb->queue.sg_entry_cnt = nseg; acb->queue.sg_real_addr = acb->sg_busaddr; } else { acb->queue.sg_entry_cnt = 0; acb->queue.sg_real_addr = 0; } if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) op = BUS_DMASYNC_PREREAD; else op = BUS_DMASYNC_PREWRITE; bus_dmamap_sync(adw->buffer_dmat, acb->dmamap, op); } else { acb->queue.sg_entry_cnt = 0; acb->queue.data_addr = 0; acb->queue.data_cnt = 0; acb->queue.sg_real_addr = 0; } acb->queue.free_scsiq_link = 0; acb->queue.ux_wk_data_cnt = 0; s = splcam(); /* * Last time we need to check if this CCB needs to * be aborted. */ if (ccb->ccb_h.status != CAM_REQ_INPROG) { if (nseg != 0) bus_dmamap_unload(adw->buffer_dmat, acb->dmamap); adwfreeacb(adw, acb); xpt_done(ccb); splx(s); return; } acb->state |= ACB_ACTIVE; ccb->ccb_h.status |= CAM_SIM_QUEUED; LIST_INSERT_HEAD(&adw->pending_ccbs, &ccb->ccb_h, sim_links.le); ccb->ccb_h.timeout_ch = timeout(adwtimeout, (caddr_t)acb, (ccb->ccb_h.timeout * hz) / 1000); adw_send_acb(adw, acb, acbvtop(adw, acb)); splx(s);}static voidadw_action(struct cam_sim *sim, union ccb *ccb){ struct adw_softc *adw; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("adw_action\n")); adw = (struct adw_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_scsiio *csio; struct ccb_hdr *ccbh; struct acb *acb; csio = &ccb->csio; ccbh = &ccb->ccb_h; /* Max supported CDB length is 12 bytes */ if (csio->cdb_len > 12) { ccb->ccb_h.status = CAM_REQ_INVALID; xpt_done(ccb); return; } if ((acb = adwgetacb(adw)) == NULL) { int s; s = splcam(); adw->state |= ADW_RESOURCE_SHORTAGE; splx(s); xpt_freeze_simq(sim, /*count*/1); ccb->ccb_h.status = CAM_REQUEUE_REQ; xpt_done(ccb); return; } /* Link dccb and ccb so we can find one from the other */ acb->ccb = ccb; ccb->ccb_h.ccb_acb_ptr = acb; ccb->ccb_h.ccb_adw_ptr = adw; acb->queue.cntl = 0; acb->queue.target_id = ccb->ccb_h.target_id; acb->queue.target_lun = ccb->ccb_h.target_lun; acb->queue.srb_ptr = 0; acb->queue.a_flag = 0; acb->queue.sense_len = MIN(csio->sense_len, sizeof(acb->sense_data)); acb->queue.cdb_len = csio->cdb_len; if ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0) acb->queue.tag_code = csio->tag_action; else acb->queue.tag_code = 0; acb->queue.done_status = 0; acb->queue.scsi_status = 0; acb->queue.host_status = 0; acb->queue.ux_sg_ix = 0; if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) { if ((ccb->ccb_h.flags & CAM_CDB_PHYS) == 0) { bcopy(csio->cdb_io.cdb_ptr, acb->queue.cdb, csio->cdb_len); } else { /* I guess I could map it in... */ ccb->ccb_h.status = CAM_REQ_INVALID; adwfreeacb(adw, acb); xpt_done(ccb); return; } } else { bcopy(csio->cdb_io.cdb_bytes, acb->queue.cdb, csio->cdb_len); } /* * If we have any data to send with this command, * map it into bus space. */ if ((ccbh->flags & CAM_DIR_MASK) != CAM_DIR_NONE) { if ((ccbh->flags & CAM_SCATTER_VALID) == 0) { /* * We've been given a pointer * to a single buffer. */ if ((ccbh->flags & CAM_DATA_PHYS) == 0) { int s; int error;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -