diff options
Diffstat (limited to 'sys/dev/ic/gdt_common.c')
-rw-r--r-- | sys/dev/ic/gdt_common.c | 1278 |
1 files changed, 1278 insertions, 0 deletions
diff --git a/sys/dev/ic/gdt_common.c b/sys/dev/ic/gdt_common.c new file mode 100644 index 00000000000..51a4d2ba48b --- /dev/null +++ b/sys/dev/ic/gdt_common.c @@ -0,0 +1,1278 @@ +/* $OpenBSD: gdt_common.c,v 1.1 2000/02/07 00:33:02 niklas Exp $ */ + +/* + * Copyright (c) 1999, 2000 Niklas Hallqvist. 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. + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Niklas Hallqvist. + * 4. 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 ``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 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. + */ + +/* + * This driver would not have written if it was not for the hardware donations + * from both ICP-Vortex and Öko.neT. I want to thank them for their support. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/malloc.h> + +#include <machine/bus.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsi_disk.h> +#include <scsi/scsiconf.h> + +#include <dev/ic/gdtreg.h> +#include <dev/ic/gdtvar.h> + +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +int gdt_async_event __P((struct gdt_softc *, int)); +void gdt_chain __P((struct gdt_softc *)); +void gdt_clear_events __P((struct gdt_softc *)); +void gdt_copy_internal_data __P((struct scsi_xfer *, u_int8_t *, size_t)); +struct scsi_xfer *gdt_dequeue __P((struct gdt_softc *)); +void gdt_enqueue __P((struct gdt_softc *, struct scsi_xfer *, int)); +void gdt_enqueue_ccb __P((struct gdt_softc *, struct gdt_ccb *)); +void gdt_eval_mapping __P((u_int32_t, int *, int *, int *)); +int gdt_exec_ccb __P((struct gdt_ccb *)); +void gdt_free_ccb __P((struct gdt_softc *, struct gdt_ccb *)); +struct gdt_ccb *gdt_get_ccb __P((struct gdt_softc *, int)); +int gdt_internal_cache_cmd __P((struct scsi_xfer *)); +int gdt_internal_cmd __P((struct gdt_softc *, u_int8_t, u_int16_t, + u_int32_t, u_int32_t, u_int32_t)); +int gdt_raw_scsi_cmd __P((struct scsi_xfer *)); +int gdt_scsi_cmd __P((struct scsi_xfer *)); +void gdt_start_ccbs __P((struct gdt_softc *)); +int gdt_sync_event __P((struct gdt_softc *, int, u_int8_t, + struct scsi_xfer *)); +void gdt_timeout __P((void *)); +int gdt_wait __P((struct gdt_softc *, struct gdt_ccb *, int)); +void gdt_watchdog __P((void *)); + +struct cfdriver gdt_cd = { + NULL, "gdt", DV_DULL +}; + +struct scsi_adapter gdt_switch = { + gdt_scsi_cmd, gdtminphys, 0, 0, +}; + +struct scsi_adapter gdt_raw_switch = { + gdt_raw_scsi_cmd, gdtminphys, 0, 0, +}; + +struct scsi_device gdt_dev = { + NULL, NULL, NULL, NULL +}; + +u_int8_t gdt_polling; +u_int8_t gdt_from_wait; +struct gdt_softc *gdt_wait_gdt; +int gdt_wait_index; +#ifdef GDT_DEBUG +int gdt_debug = GDT_DEBUG; +#endif + +int +gdt_attach(gdt) + struct gdt_softc *gdt; +{ + u_int16_t cdev_cnt; + int i, id, drv_cyls, drv_hds, drv_secs; + + gdt_polling = 1; + gdt_from_wait = 0; + gdt_clear_events(gdt); + + TAILQ_INIT(&gdt->sc_free_ccb); + TAILQ_INIT(&gdt->sc_ccbq); + LIST_INIT(&gdt->sc_queue); + + /* Initialize the ccbs */ + for (i = 0; i < GDT_MAXCMDS; i++) { + gdt->sc_ccbs[i].gc_cmd_index = i + 2; + (void)gdt_ccb_set_cmd(gdt->sc_ccbs + i, GDT_GCF_UNUSED); + TAILQ_INSERT_HEAD(&gdt->sc_free_ccb, &gdt->sc_ccbs[i], + gc_chain); + } + + /* Fill in the prototype scsi_link. */ + gdt->sc_link.adapter_softc = gdt; + gdt->sc_link.adapter = &gdt_switch; + gdt->sc_link.adapter_target = 7; + gdt->sc_link.device = &gdt_dev; + gdt->sc_link.openings = GDT_MAXCMDS; /* XXX what is optimal? */ + gdt->sc_link.adapter_buswidth = + (gdt->sc_class & GDT_FC) ? GDT_MAXID : GDT_MAX_HDRIVES; + + if (!gdt_internal_cmd(gdt, GDT_SCREENSERVICE, GDT_INIT, 0, 0, 0)) { + printf("screen service initialization error %d\n", + gdt->sc_status); + return (1); + } + + if (!gdt_internal_cmd(gdt, GDT_CACHESERVICE, GDT_INIT, GDT_LINUX_OS, 0, + 0)) { + printf("cache service initialization error %d\n", + gdt->sc_status); + return (1); + } + + if (!gdt_internal_cmd(gdt, GDT_CACHESERVICE, GDT_MOUNT, 0xffff, 1, + 0)) { + printf("cache service mount error %d\n", + gdt->sc_status); + return (1); + } + + if (!gdt_internal_cmd(gdt, GDT_CACHESERVICE, GDT_INIT, GDT_LINUX_OS, 0, + 0)) { + printf("cache service post-mount initialization error %d\n", + gdt->sc_status); + return (1); + } + cdev_cnt = (u_int16_t)gdt->sc_info; + + /* Detect number of busses */ + gdt_enc32(gdt->sc_scratch + GDT_IOC_VERSION, GDT_IOC_NEWEST); + gdt->sc_scratch[GDT_IOC_LIST_ENTRIES] = GDT_MAXBUS; + gdt->sc_scratch[GDT_IOC_FIRST_CHAN] = 0; + gdt->sc_scratch[GDT_IOC_LAST_CHAN] = GDT_MAXBUS - 1; + gdt_enc32(gdt->sc_scratch + GDT_IOC_LIST_OFFSET, GDT_IOC_HDR_SZ); + if (gdt_internal_cmd(gdt, GDT_CACHESERVICE, GDT_IOCTL, + GDT_IOCHAN_RAW_DESC, GDT_INVALID_CHANNEL, + GDT_IOC_HDR_SZ + GDT_RAWIOC_SZ)) { + gdt->sc_bus_cnt = gdt->sc_scratch[GDT_IOC_CHAN_COUNT]; + for (i = 0; i < gdt->sc_bus_cnt; i++) { + id = gdt->sc_scratch[GDT_IOC_HDR_SZ + + i * GDT_RAWIOC_SZ + GDT_RAWIOC_PROC_ID]; + gdt->sc_bus_id[id] = id < GDT_MAXID ? id : 0xff; + } + + } else { + /* New method failed, use fallback. */ + gdt_enc32(gdt->sc_scratch + GDT_GETCH_CHANNEL_NO, i); + for (i = 0; i < GDT_MAXBUS; i++) { + if (!gdt_internal_cmd(gdt, GDT_CACHESERVICE, GDT_IOCTL, + GDT_SCSI_CHAN_CNT | GDT_L_CTRL_PATTERN, + GDT_IO_CHANNEL | GDT_INVALID_CHANNEL, + GDT_GETCH_SZ)) { + if (i == 0) { + printf("cannot get channel count, " + "error %d\n", gdt->sc_status); + return (1); + } + break; + } + gdt->sc_bus_id[i] = + (gdt->sc_scratch[GDT_GETCH_SIOP_ID] < GDT_MAXID) ? + gdt->sc_scratch[GDT_GETCH_SIOP_ID] : 0xff; + } + gdt->sc_bus_cnt = i; + } + + /* Read cache confgiuration */ + if (!gdt_internal_cmd(gdt, GDT_CACHESERVICE, GDT_IOCTL, GDT_CACHE_INFO, + GDT_INVALID_CHANNEL, GDT_CINFO_SZ)) { + printf("cannot get cache info, error %d\n", gdt->sc_status); + return (1); + } + gdt->sc_cpar.cp_version = + gdt_dec32(gdt->sc_scratch + GDT_CPAR_VERSION); + gdt->sc_cpar.cp_state = gdt_dec16(gdt->sc_scratch + GDT_CPAR_STATE); + gdt->sc_cpar.cp_strategy = + gdt_dec16(gdt->sc_scratch + GDT_CPAR_STRATEGY); + gdt->sc_cpar.cp_write_back = + gdt_dec16(gdt->sc_scratch + GDT_CPAR_WRITE_BACK); + gdt->sc_cpar.cp_block_size = + gdt_dec16(gdt->sc_scratch + GDT_CPAR_BLOCK_SIZE); + + /* Read board information and features */ + gdt->sc_more_proc = 0; + if (gdt_internal_cmd(gdt, GDT_CACHESERVICE, GDT_IOCTL, GDT_BOARD_INFO, + GDT_INVALID_CHANNEL, GDT_BINFO_SZ)) { + /* XXX A lot of these assignments can probably go later */ + gdt->sc_binfo.bi_ser_no = + gdt_dec32(gdt->sc_scratch + GDT_BINFO_SER_NO); + bcopy(gdt->sc_scratch + GDT_BINFO_OEM_ID, + gdt->sc_binfo.bi_oem_id, sizeof gdt->sc_binfo.bi_oem_id); + gdt->sc_binfo.bi_ep_flags = + gdt_dec16(gdt->sc_scratch + GDT_BINFO_EP_FLAGS); + gdt->sc_binfo.bi_proc_id = + gdt_dec32(gdt->sc_scratch + GDT_BINFO_PROC_ID); + gdt->sc_binfo.bi_memsize = + gdt_dec32(gdt->sc_scratch + GDT_BINFO_MEMSIZE); + gdt->sc_binfo.bi_mem_banks = + gdt->sc_scratch[GDT_BINFO_MEM_BANKS]; + gdt->sc_binfo.bi_chan_type = + gdt->sc_scratch[GDT_BINFO_CHAN_TYPE]; + gdt->sc_binfo.bi_chan_count = + gdt->sc_scratch[GDT_BINFO_CHAN_COUNT]; + gdt->sc_binfo.bi_rdongle_pres = + gdt->sc_scratch[GDT_BINFO_RDONGLE_PRES]; + gdt->sc_binfo.bi_epr_fw_ver = + gdt_dec32(gdt->sc_scratch + GDT_BINFO_EPR_FW_VER); + gdt->sc_binfo.bi_upd_fw_ver = + gdt_dec32(gdt->sc_scratch + GDT_BINFO_UPD_FW_VER); + gdt->sc_binfo.bi_upd_revision = + gdt_dec32(gdt->sc_scratch + GDT_BINFO_UPD_REVISION); + bcopy(gdt->sc_scratch + GDT_BINFO_TYPE_STRING, + gdt->sc_binfo.bi_type_string, + sizeof gdt->sc_binfo.bi_type_string); + bcopy(gdt->sc_scratch + GDT_BINFO_RAID_STRING, + gdt->sc_binfo.bi_raid_string, + sizeof gdt->sc_binfo.bi_raid_string); + gdt->sc_binfo.bi_update_pres = + gdt->sc_scratch[GDT_BINFO_UPDATE_PRES]; + gdt->sc_binfo.bi_xor_pres = + gdt->sc_scratch[GDT_BINFO_XOR_PRES]; + gdt->sc_binfo.bi_prom_type = + gdt->sc_scratch[GDT_BINFO_PROM_TYPE]; + gdt->sc_binfo.bi_prom_count = + gdt->sc_scratch[GDT_BINFO_PROM_COUNT]; + gdt->sc_binfo.bi_dup_pres = + gdt_dec32(gdt->sc_scratch + GDT_BINFO_DUP_PRES); + gdt->sc_binfo.bi_chan_pres = + gdt_dec32(gdt->sc_scratch + GDT_BINFO_CHAN_PRES); + gdt->sc_binfo.bi_mem_pres = + gdt_dec32(gdt->sc_scratch + GDT_BINFO_MEM_PRES); + gdt->sc_binfo.bi_ft_bus_system = + gdt->sc_scratch[GDT_BINFO_FT_BUS_SYSTEM]; + gdt->sc_binfo.bi_subtype_valid = + gdt->sc_scratch[GDT_BINFO_SUBTYPE_VALID]; + gdt->sc_binfo.bi_board_subtype = + gdt->sc_scratch[GDT_BINFO_BOARD_SUBTYPE]; + gdt->sc_binfo.bi_rampar_pres = + gdt->sc_scratch[GDT_BINFO_RAMPAR_PRES]; + + if (gdt_internal_cmd(gdt, GDT_CACHESERVICE, GDT_IOCTL, + GDT_BOARD_FEATURES, GDT_INVALID_CHANNEL, GDT_BFEAT_SZ)) { + gdt->sc_bfeat.bf_chaining = + gdt->sc_scratch[GDT_BFEAT_CHAINING]; + gdt->sc_bfeat.bf_striping = + gdt->sc_scratch[GDT_BFEAT_STRIPING]; + gdt->sc_bfeat.bf_mirroring = + gdt->sc_scratch[GDT_BFEAT_MIRRORING]; + gdt->sc_bfeat.bf_raid = + gdt->sc_scratch[GDT_BFEAT_RAID]; + gdt->sc_more_proc = 1; + } + } else { + /* XXX Not implemented yet */ + } + + /* Read more information */ + if (gdt->sc_more_proc) { + /* XXX Not implemented yet */ + } + + if (!gdt_internal_cmd(gdt, GDT_SCSIRAWSERVICE, GDT_INIT, 0, 0, 0)) { + printf("raw service initialization error %d\n", + gdt->sc_status); + return (1); + } + + /* Set/get features raw service (scatter/gather) */ + gdt->sc_raw_feat = 0; + if (gdt_internal_cmd(gdt, GDT_SCSIRAWSERVICE, GDT_SET_FEAT, + GDT_SCATTER_GATHER, 0, 0)) + if (gdt_internal_cmd(gdt, GDT_SCSIRAWSERVICE, GDT_GET_FEAT, 0, + 0, 0)) + gdt->sc_raw_feat = gdt->sc_info; + + /* Set/get features cache service (scatter/gather) */ + gdt->sc_cache_feat = 0; + if (gdt_internal_cmd(gdt, GDT_CACHESERVICE, GDT_SET_FEAT, 0, + GDT_SCATTER_GATHER, 0)) + if (gdt_internal_cmd(gdt, GDT_CACHESERVICE, GDT_GET_FEAT, 0, 0, + 0)) + gdt->sc_cache_feat = gdt->sc_info; + + /* XXX Linux reserve drives here, potentially */ + + /* Scan for cache devices */ + for (i = 0; i < cdev_cnt && i < GDT_MAX_HDRIVES; i++) + if (gdt_internal_cmd(gdt, GDT_CACHESERVICE, GDT_INFO, i, 0, + 0)) { + gdt->sc_hdr[i].hd_present = 1; + gdt->sc_hdr[i].hd_size = gdt->sc_info; + + /* + * Evaluate mapping (sectors per head, heads per cyl) + */ + gdt->sc_hdr[i].hd_size &= ~GDT_SECS32; + if (gdt->sc_info2 == 0) + gdt_eval_mapping(gdt->sc_hdr[i].hd_size, + &drv_cyls, &drv_hds, &drv_secs); + else { + drv_hds = gdt->sc_info2 & 0xff; + drv_secs = (gdt->sc_info2 >> 8) & 0xff; + drv_cyls = gdt->sc_hdr[i].hd_size / drv_hds / + drv_secs; + } + gdt->sc_hdr[i].hd_heads = drv_hds; + gdt->sc_hdr[i].hd_secs = drv_secs; + /* Round the size */ + gdt->sc_hdr[i].hd_size = drv_cyls * drv_hds * drv_secs; + + if (gdt_internal_cmd(gdt, GDT_CACHESERVICE, + GDT_DEVTYPE, i, 0, 0)) + gdt->sc_hdr[i].hd_devtype = gdt->sc_info; + } + + printf("dpmem %x %d-bus %d cache device%s\n", gdt->sc_dpmembase, + gdt->sc_bus_cnt, cdev_cnt, cdev_cnt == 1 ? "" : "s"); + printf("%s: ver %x, cache %s, strategy %d, writeback %s, blksz %d\n", + gdt->sc_dev.dv_xname, gdt->sc_cpar.cp_version, + gdt->sc_cpar.cp_state ? "on" : "off", gdt->sc_cpar.cp_strategy, + gdt->sc_cpar.cp_write_back ? "on" : "off", + gdt->sc_cpar.cp_block_size); +#if 1 + printf("%s: raw feat %x cache feat %x\n", gdt->sc_dev.dv_xname, + gdt->sc_raw_feat, gdt->sc_cache_feat); +#endif + + config_found(&gdt->sc_dev, &gdt->sc_link, scsiprint); + + MALLOC(gdt->sc_raw_link, struct scsi_link *, + gdt->sc_bus_cnt * sizeof (struct scsi_link), M_DEVBUF, M_NOWAIT); + bzero(gdt->sc_raw_link, gdt->sc_bus_cnt * sizeof (struct scsi_link)); + + for (i = 0; i < gdt->sc_bus_cnt; i++) { + /* Fill in the prototype scsi_link. */ + gdt->sc_raw_link[i].adapter_softc = gdt; + gdt->sc_raw_link[i].adapter = &gdt_raw_switch; + gdt->sc_raw_link[i].adapter_target = 7; + gdt->sc_raw_link[i].device = &gdt_dev; + gdt->sc_raw_link[i].openings = 4; /* XXX a guess */ + gdt->sc_raw_link[i].adapter_buswidth = + (gdt->sc_class & GDT_FC) ? GDT_MAXID : 16; /* XXX */ + + config_found(&gdt->sc_dev, &gdt->sc_raw_link[i], scsiprint); + } + + gdt_polling = 0; + return (0); +} + +void +gdt_eval_mapping(size, cyls, heads, secs) + u_int32_t size; + int *cyls, *heads, *secs; +{ + *cyls = size / GDT_HEADS / GDT_SECS; + if (*cyls < GDT_MAXCYLS) { + *heads = GDT_HEADS; + *secs = GDT_SECS; + } else { + /* Too high for 64 * 32 */ + *cyls = size / GDT_MEDHEADS / GDT_MEDSECS; + if (*cyls < GDT_MAXCYLS) { + *heads = GDT_MEDHEADS; + *secs = GDT_MEDSECS; + } else { + /* Too high for 127 * 63 */ + *cyls = size / GDT_BIGHEADS / GDT_BIGSECS; + *heads = GDT_BIGHEADS; + *secs = GDT_BIGSECS; + } + } +} + +/* + * Insert a command into the driver queue, either at the front or at the tail. + * It's ok to overload the freelist link as these structures are never on + * the freelist at this time. + */ +void +gdt_enqueue(gdt, xs, infront) + struct gdt_softc *gdt; + struct scsi_xfer *xs; + int infront; +{ + if (infront || LIST_FIRST(&gdt->sc_queue) == NULL) { + if (LIST_FIRST(&gdt->sc_queue) == NULL) + gdt->sc_queuelast = xs; + LIST_INSERT_HEAD(&gdt->sc_queue, xs, free_list); + return; + } + LIST_INSERT_AFTER(gdt->sc_queuelast, xs, free_list); + gdt->sc_queuelast = xs; +} + +/* + * Pull a command off the front of the driver queue. + */ +struct scsi_xfer * +gdt_dequeue(gdt) + struct gdt_softc *gdt; +{ + struct scsi_xfer *xs; + + xs = LIST_FIRST(&gdt->sc_queue); + LIST_REMOVE(xs, free_list); + + if (LIST_FIRST(&gdt->sc_queue) == NULL) + gdt->sc_queuelast = NULL; + + return (xs); +} + +/* Start a SCSI operation on a cache device */ +int +gdt_scsi_cmd(xs) + struct scsi_xfer *xs; +{ + struct scsi_link *link = xs->sc_link; + struct gdt_softc *gdt = link->adapter_softc; + u_int8_t target = link->target; + struct gdt_ccb *ccb; + int dontqueue = 0; + u_int32_t blockno, blockcnt; + struct scsi_rw *rw; + struct scsi_rw_big *rwb; + + GDT_DPRINTF(GDT_D_CMD, ("gdt_scsi_cmd ")); + + if (target >= GDT_MAX_HDRIVES || !gdt->sc_hdr[target].hd_present || + link->lun != 0) { + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); + } + + xs->error = XS_NOERROR; + ccb = NULL; + + GDT_LOCK_GDT(gdt); + if (!gdt_polling && gdt->sc_test_busy(gdt)) { + /* Don't double enqueue if we came from gdt_chain. */ + if (xs != LIST_FIRST(&gdt->sc_queue)) + gdt_enqueue(gdt, xs, 0); + GDT_UNLOCK_GDT(gdt); + return (SUCCESSFULLY_QUEUED); + } + GDT_LOCK_GDT(gdt); + + switch (xs->cmd->opcode) { + case TEST_UNIT_READY: + case REQUEST_SENSE: + case INQUIRY: + case MODE_SENSE: + case START_STOP: + case READ_CAPACITY: + case SYNCHRONIZE_CACHE: +#if 0 + case VERIFY: +#endif + return (gdt_internal_cache_cmd(xs) ? COMPLETE : + TRY_AGAIN_LATER); + + case PREVENT_ALLOW: + GDT_DPRINTF(GDT_D_CMD, ("PREVENT/ALLOW ")); + /* XXX Not yet implemented */ + xs->error = XS_NOERROR; + return (COMPLETE); + + case READ_COMMAND: + case READ_BIG: + case WRITE_COMMAND: + case WRITE_BIG: + GDT_LOCK_GDT(gdt); + + /* + * When chaining commands we may get called with the + * first command in the queue, recognize this case + * easily. + */ + if (xs == LIST_FIRST(&gdt->sc_queue)) + xs = gdt_dequeue(gdt); + else { + /* A new command chain, start from the beginning. */ + gdt->sc_cmd_off = 0; + + /* Don't resort to queuing if we are polling. */ + dontqueue = xs->flags & SCSI_POLL; + + /* + * The queue, if existent, must be processed first, + * before the new command can be run. + */ + if (LIST_FIRST(&gdt->sc_queue) != NULL) { + /* If order cannot be preserved, punt. */ + if (dontqueue) { + GDT_UNLOCK_GDT(gdt); + xs->error = XS_DRIVER_STUFFUP; + return (TRY_AGAIN_LATER); + } + + /* + * Enqueue the new command, ponder on the front + * command of the queue instead. + */ + gdt_enqueue(gdt, xs, 0); + xs = gdt_dequeue(gdt); + } + } + + if (xs->cmdlen == 6) { + rw = (struct scsi_rw *)xs->cmd; + blockno = + _3btol(rw->addr) & (SRW_TOPADDR << 16 | 0xffff); + blockcnt = rw->length ? rw->length : 0x100; + } else { + rwb = (struct scsi_rw_big *)xs->cmd; + blockno = _4btol(rwb->addr); + blockcnt = _2btol(rwb->length); + } + if (blockno >= gdt->sc_hdr[target].hd_size || + blockno + blockcnt >= gdt->sc_hdr[target].hd_size) { + printf("%s: out of bounds %d-%d >= %d\n", + gdt->sc_dev.dv_xname, blockno, blockcnt, + gdt->sc_hdr[target].hd_size); + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); + } + + ccb = gdt_get_ccb(gdt, xs->flags); + + /* + * Are we out of commands, then queue. If we cannot queue, + * then punt. + */ + if (ccb == NULL) { + if (dontqueue) { + GDT_UNLOCK_GDT(gdt); + scsi_done(xs); + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); + } + if (xs->error) { + GDT_UNLOCK_GDT(gdt); + scsi_done(xs); + return (COMPLETE); + } + + /* Put back on the queue, in the front. */ + gdt_enqueue(gdt, xs, 1); + GDT_UNLOCK_GDT(gdt); + return (SUCCESSFULLY_QUEUED); + } + + ccb->gc_blockno = blockno; + ccb->gc_blockcnt = blockcnt; + ccb->gc_xs = xs; + ccb->gc_timeout = xs->timeout; + ccb->gc_service = GDT_CACHESERVICE; + gdt_ccb_set_cmd(ccb, GDT_GCF_SCSI); + + gdt_enqueue_ccb(gdt, ccb); + /* XXX what if enqueue did not start a transfer? */ + if (gdt_polling) { + if (!gdt_wait(gdt, ccb, ccb->gc_timeout)) { + GDT_UNLOCK_GDT(gdt); + printf("%s: command %d timed out\n", + gdt->sc_dev.dv_xname, ccb->gc_cmd_index); + xs->error = XS_TIMEOUT; + return (TRY_AGAIN_LATER); + } + } + + GDT_UNLOCK_GDT(gdt); + + if (gdt_polling) { + scsi_done(xs); + return (COMPLETE); + } + return (SUCCESSFULLY_QUEUED); + + default: + GDT_DPRINTF(GDT_D_CMD, ("unknown opc %d ", xs->cmd->opcode)); + /* XXX Not yet implemented */ + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); + } +} + +/* XXX Currently only for cacheservice, returns 0 if busy */ +int +gdt_exec_ccb(ccb) + struct gdt_ccb *ccb; +{ + struct scsi_xfer *xs = ccb->gc_xs; + struct scsi_link *link = xs->sc_link; + struct gdt_softc *gdt = link->adapter_softc; + u_int8_t target = link->target; + u_int32_t sg_canz; + int i, len, off; + u_int8_t *buf; + paddr_t pa; + + GDT_DPRINTF(GDT_D_CMD, ("gdt_exec_ccb(%p, %p) ", xs, ccb)); + + gdt->sc_cmd_cnt = 0; + /* + * XXX Yeah I know it's a always-true condition, but that may change + * later. + */ + if (gdt->sc_cmd_cnt == 0) + gdt->sc_set_sema0(gdt); + + gdt_enc32(gdt->sc_cmd + GDT_CMD_COMMANDINDEX, ccb->gc_cmd_index); + gdt_enc32(gdt->sc_cmd + GDT_CMD_BOARDNODE, GDT_LOCALBOARD); + gdt_enc16(gdt->sc_cmd + GDT_CMD_UNION + GDT_CACHE_DEVICENO, + target); + + switch (xs->cmd->opcode) { + case PREVENT_ALLOW: + /* XXX PREVENT_ALLOW support goes here */ + + gdt_enc32(gdt->sc_cmd + GDT_CMD_UNION + GDT_CACHE_BLOCKNO, + 1); + sg_canz = 0; + break; + + case WRITE_COMMAND: + case WRITE_BIG: + /* XXX WRITE_THR could be supported too */ + gdt->sc_cmd[GDT_CMD_OPCODE] = GDT_WRITE; + break; + + case READ_COMMAND: + case READ_BIG: + gdt->sc_cmd[GDT_CMD_OPCODE] = GDT_READ; + break; + } + + if (xs->cmd->opcode != PREVENT_ALLOW) { + gdt_enc32(gdt->sc_cmd + GDT_CMD_UNION + GDT_CACHE_BLOCKNO, + ccb->gc_blockno); + gdt_enc32(gdt->sc_cmd + GDT_CMD_UNION + GDT_CACHE_BLOCKCNT, + ccb->gc_blockcnt); + } + + if (gdt->sc_cache_feat & GDT_SCATTER_GATHER) { + gdt_enc32(gdt->sc_cmd + GDT_CMD_UNION + GDT_CACHE_DESTADDR, + 0xffffffff); + len = xs->datalen; + buf = xs->data; + for (i = 0; len > 0; i++) { + for (off = PAGE_SIZE, pa = vtophys(buf); off < len; + off += PAGE_SIZE) + if (pa + off != vtophys(buf + off)) + break; + + gdt_enc32(gdt->sc_cmd + GDT_CMD_UNION + + GDT_CACHE_SG_LST + i * GDT_SG_SZ + GDT_SG_PTR, pa); + gdt_enc32(gdt->sc_cmd + GDT_CMD_UNION + + GDT_CACHE_SG_LST + i * GDT_SG_SZ + GDT_SG_LEN, + MIN(off, len)); + GDT_DPRINTF(GDT_D_IO, ("#%d va %p pa %p len %x\n", i, + buf, (void *)pa, MIN(off, len))); + + len -= off; + buf += off; + } + sg_canz = i; + gdt_enc32(gdt->sc_cmd + GDT_CMD_UNION + GDT_CACHE_SG_LST + + sg_canz * GDT_SG_SZ + GDT_SG_LEN, 0); + } else { + gdt_enc32(gdt->sc_cmd + GDT_CMD_UNION + GDT_CACHE_DESTADDR, + vtophys(xs->data)); + sg_canz = 0; + } + gdt_enc32(gdt->sc_cmd + GDT_CMD_UNION + GDT_CACHE_SG_CANZ, sg_canz); + + gdt->sc_cmd_len = + roundup(GDT_CMD_UNION + GDT_CACHE_SG_LST + sg_canz * GDT_SG_SZ, + sizeof (u_int32_t)); + + if (gdt->sc_cmd_cnt > 0 && + gdt->sc_cmd_off + gdt->sc_cmd_len + GDT_DPMEM_COMMAND_OFFSET > + gdt->sc_ic_all_size) { + printf("%s: DPMEM overflow\n", gdt->sc_dev.dv_xname); + gdt_free_ccb(gdt, ccb); + xs->error = XS_BUSY; + return (0); + } + + gdt->sc_copy_cmd(gdt, ccb); + gdt->sc_release_event(gdt, ccb); + + xs->error = XS_NOERROR; + xs->resid = 0; + return (1); +} + +void +gdt_copy_internal_data(xs, data, size) + struct scsi_xfer *xs; + u_int8_t *data; + size_t size; +{ + size_t copy_cnt; + + GDT_DPRINTF(GDT_D_MISC, ("gdt_copy_internal_data ")); + + if (!xs->datalen) + printf("uio move not yet supported\n"); + else { + copy_cnt = MIN(size, xs->datalen); + bcopy(data, xs->data, copy_cnt); + } + + +} + +/* Emulated SCSI operation on cache device */ +int +gdt_internal_cache_cmd(xs) + struct scsi_xfer *xs; +{ + struct scsi_link *link = xs->sc_link; + struct gdt_softc *gdt = link->adapter_softc; + struct scsi_inquiry_data inq; + struct scsi_sense_data sd; + struct { + struct scsi_mode_header hd; + struct scsi_blk_desc bd; + union scsi_disk_pages dp; + } mpd; + struct scsi_read_cap_data rcd; + u_int8_t target = link->target; + + GDT_DPRINTF(GDT_D_CMD, ("gdt_internal_cache_cmd ")); + + switch (xs->cmd->opcode) { + case TEST_UNIT_READY: + case START_STOP: +#if 0 + case VERIFY: +#endif + GDT_DPRINTF(GDT_D_CMD, ("opc %d tgt %d ", xs->cmd->opcode, + target)); + break; + + case SYNCHRONIZE_CACHE: + GDT_DPRINTF(GDT_D_CMD, ("SYNCHRONIZE CACHE tgt %d ", target)); + break; + + case REQUEST_SENSE: + GDT_DPRINTF(GDT_D_CMD, ("REQUEST SENSE tgt %d ", target)); + bzero(&sd, sizeof sd); + sd.error_code = 0x70; + sd.segment = 0; + sd.flags = SKEY_NO_SENSE; + gdt_enc32(sd.info, 0); + sd.extra_len = 0; + gdt_copy_internal_data(xs, (u_int8_t *)&sd, sizeof sd); + break; + + case INQUIRY: + GDT_DPRINTF(GDT_D_CMD, ("INQUIRY tgt %d devtype %x ", target, + gdt->sc_hdr[target].hd_devtype)); + bzero(&inq, sizeof inq); + inq.device = + (gdt->sc_hdr[target].hd_devtype & 4) ? T_CDROM : T_DIRECT; + inq.dev_qual2 = + (gdt->sc_hdr[target].hd_devtype & 1) ? SID_REMOVABLE : 0; + inq.version = 2; + inq.response_format = 2; + inq.additional_length = 32; + strcpy(inq.vendor, "ICP "); + sprintf(inq.product, "Host drive #%02d", target); + strcpy(inq.revision, " "); + gdt_copy_internal_data(xs, (u_int8_t *)&inq, sizeof inq); + break; + + case MODE_SENSE: + GDT_DPRINTF(GDT_D_CMD, ("MODE SENSE tgt %d ", target)); + + bzero(&mpd, sizeof mpd); + switch (((struct scsi_mode_sense *)xs->cmd)->page) { + case 4: + /* scsi_disk.h says this should be 0x16 */ + mpd.dp.rigid_geometry.pg_length = 0x16; + mpd.hd.data_length = sizeof mpd.hd + sizeof mpd.bd + + mpd.dp.rigid_geometry.pg_length; + mpd.hd.blk_desc_len = sizeof mpd.bd; + + /* XXX */ + mpd.hd.dev_spec = + (gdt->sc_hdr[target].hd_devtype & 2) ? 0x80 : 0; + _lto3b(GDT_SECTOR_SIZE, mpd.bd.blklen); + mpd.dp.rigid_geometry.pg_code = 4; + _lto3b(gdt->sc_hdr[target].hd_size / + gdt->sc_hdr[target].hd_heads / + gdt->sc_hdr[target].hd_secs, + mpd.dp.rigid_geometry.ncyl); + mpd.dp.rigid_geometry.nheads = + gdt->sc_hdr[target].hd_heads; + gdt_copy_internal_data(xs, (u_int8_t *)&mpd, + sizeof mpd); + break; + + default: + printf("%s: mode sense page %d not simulated\n", + gdt->sc_dev.dv_xname, + ((struct scsi_mode_sense *)xs->cmd)->page); + xs->error = XS_DRIVER_STUFFUP; + return (0); + } + break; + + case READ_CAPACITY: + GDT_DPRINTF(GDT_D_CMD, ("READ CAPACITY tgt %d ", target)); + bzero(&rcd, sizeof rcd); + _lto4b(gdt->sc_hdr[target].hd_size - 1, rcd.addr); + _lto4b(GDT_SECTOR_SIZE, rcd.length); + gdt_copy_internal_data(xs, (u_int8_t *)&rcd, sizeof rcd); + break; + + default: + printf("gdt_internal_cache_cmd got bad opcode: %d\n", + xs->cmd->opcode); + xs->error = XS_DRIVER_STUFFUP; + return (0); + } + + xs->error = XS_NOERROR; + return (1); +} + +/* Start a raw SCSI operation */ +int +gdt_raw_scsi_cmd(xs) + struct scsi_xfer *xs; +{ + GDT_DPRINTF(GDT_D_CMD, ("gdt_raw_scsi_cmd ")); + + /* XXX Not yet implemented */ + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); +} + +void +gdt_clear_events(gdt) + struct gdt_softc *gdt; +{ + GDT_DPRINTF(GDT_D_MISC, ("gdt_clear_events(%p) ", gdt)); + + /* XXX To be implemented */ +} + +int +gdt_async_event(gdt, service) + struct gdt_softc *gdt; + int service; +{ + GDT_DPRINTF(GDT_D_INTR, ("gdt_async_event(%p, %d) ", gdt, service)); + + if (service == GDT_SCREENSERVICE) { + /* XXX To be implemented */ + } else { + /* XXX To be implemented */ + } + + return (0); +} + +int +gdt_sync_event(gdt, service, index, xs) + struct gdt_softc *gdt; + int service; + u_int8_t index; + struct scsi_xfer *xs; +{ + GDT_DPRINTF(GDT_D_INTR, + ("gdt_sync_event(%p, %d, %d, %p) ", gdt, service, index, xs)); + + if (service == GDT_SCREENSERVICE) { + /* XXX To be implemented */ + return (0); + } else { + if (gdt->sc_status == GDT_S_OK) { + /* XXX To be implemented */ + } else { + /* XXX To be implemented */ + return (0); + } + } + + return (1); +} + +int +gdt_intr(arg) + void *arg; +{ + struct gdt_softc *gdt = arg; + struct gdt_intr_ctx ctx; + int chain = 1, sync_val = 0; + struct scsi_xfer *xs; + int prev_cmd; + struct gdt_ccb *ccb; + + GDT_DPRINTF(GDT_D_INTR, ("gdt_intr(%p) ", gdt)); + + /* If polling and we were not called from gdt_wait, just return */ + if (gdt_polling && !gdt_from_wait) + return (0); + + if (!gdt_polling) + GDT_LOCK_GDT(gdt); + + ctx.istatus = gdt->sc_get_status(gdt); + if (!ctx.istatus) { + GDT_UNLOCK_GDT(gdt); + gdt->sc_status = GDT_S_NO_STATUS; + return (0); + } + + gdt_wait_index = 0; + ctx.service = ctx.info2 = 0; + + gdt->sc_intr(gdt, &ctx); + + gdt->sc_status = ctx.cmd_status; + gdt->sc_info = ctx.info; + gdt->sc_info2 = ctx.info2; + + if (gdt_from_wait) { + gdt_wait_gdt = gdt; + gdt_wait_index = ctx.istatus; + } + + switch (ctx.istatus) { + case GDT_ASYNCINDEX: + gdt_async_event(gdt, ctx.service); + goto finish; + + case GDT_SPEZINDEX: + /* XXX Not yet implemented */ + chain = 0; + goto finish; + } + + ccb = &gdt->sc_ccbs[ctx.istatus - 2]; + xs = ccb->gc_xs; + if (!gdt_polling) + untimeout(gdt_timeout, ccb); + ctx.service = ccb->gc_service; + prev_cmd = ccb->gc_flags & GDT_GCF_CMD_MASK; + gdt_free_ccb(gdt, ccb); + switch (prev_cmd) { + case GDT_GCF_UNUSED: + /* XXX Not yet implemented */ + chain = 0; + goto finish; + case GDT_GCF_INTERNAL: + chain = 0; + goto finish; + } + + sync_val = gdt_sync_event(gdt, ctx.service, ctx.istatus, xs); + + finish: + if (!gdt_polling) + GDT_UNLOCK_GDT(gdt); + + switch (sync_val) { + case 1: + xs->flags |= ITSDONE; + scsi_done(xs); + break; + + case 2: + gdt_enqueue(gdt, xs, 0); + } + + if (chain) + gdt_chain(gdt); + return (1); +} + +void +gdtminphys(bp) + struct buf *bp; +{ +#if 0 + u_int8_t *buf = bp->b_data; + paddr_t pa; + long off; +#endif + + GDT_DPRINTF(GDT_D_MISC, ("gdtminphys(0x%x) ", bp)); + +#if 1 + /* As this is way more than MAXPHYS it's really not necessary. */ + if (bp->b_bcount > ((GDT_MAXOFFSETS - 1) * PAGE_SIZE)) + bp->b_bcount = ((GDT_MAXOFFSETS - 1) * PAGE_SIZE); +#else + for (off = PAGE_SIZE, pa = vtophys(buf); off < bp->b_bcount; + off += PAGE_SIZE) + if (pa + off != vtophys(buf + off)) { + bp->b_bcount = off; + break; + } +#endif + minphys(bp); +} + +int +gdt_wait(gdt, ccb, timeout) + struct gdt_softc *gdt; + struct gdt_ccb *ccb; + int timeout; +{ + int rv = 0; + + GDT_DPRINTF(GDT_D_MISC, + ("gdt_wait(%p, %p, %d) ", gdt, ccb, timeout)); + + gdt_from_wait = 1; + do { + if (gdt_intr(gdt) && gdt == gdt_wait_gdt && + ccb->gc_cmd_index == gdt_wait_index) { + rv = 1; + break; + } + DELAY(1); + } while (--timeout); + gdt_from_wait = 0; + + while (gdt->sc_test_busy(gdt)) + DELAY(0); /* XXX correct? */ + + return (rv); +} + +int +gdt_internal_cmd(gdt, service, opcode, arg1, arg2, arg3) + struct gdt_softc *gdt; + u_int8_t service; + u_int16_t opcode; + u_int32_t arg1, arg2, arg3; +{ + int retries; + struct gdt_ccb *ccb; + + GDT_DPRINTF(GDT_D_CMD, ("gdt_internal_cmd(%p, %d, %d, %d, %d, %d) ", + gdt, service, opcode, arg1, arg2, arg3)); + + bzero(gdt->sc_cmd, GDT_CMD_SZ); + + for (retries = GDT_RETRIES; ; ) { + ccb = gdt_get_ccb(gdt, SCSI_NOSLEEP); + if (ccb == NULL) { + printf("%s: no free command index found\n", + gdt->sc_dev.dv_xname); + return (0); + } + ccb->gc_service = service; + gdt_ccb_set_cmd(ccb, GDT_GCF_INTERNAL); + + gdt->sc_set_sema0(gdt); + gdt_enc32(gdt->sc_cmd + GDT_CMD_COMMANDINDEX, + ccb->gc_cmd_index); + gdt_enc16(gdt->sc_cmd + GDT_CMD_OPCODE, opcode); + gdt_enc32(gdt->sc_cmd + GDT_CMD_BOARDNODE, GDT_LOCALBOARD); + + switch (service) { + case GDT_CACHESERVICE: + if (opcode == GDT_IOCTL) { + gdt_enc32(gdt->sc_cmd + GDT_CMD_UNION + + GDT_IOCTL_SUBFUNC, arg1); + gdt_enc32(gdt->sc_cmd + GDT_CMD_UNION + + GDT_IOCTL_CHANNEL, arg2); + gdt_enc16(gdt->sc_cmd + GDT_CMD_UNION + + GDT_IOCTL_PARAM_SIZE, (u_int16_t)arg3); + gdt_enc32(gdt->sc_cmd + GDT_CMD_UNION + + GDT_IOCTL_P_PARAM, + vtophys(gdt->sc_scratch)); + } else { + gdt_enc16(gdt->sc_cmd + GDT_CMD_UNION + + GDT_CACHE_DEVICENO, (u_int16_t)arg1); + gdt_enc32(gdt->sc_cmd + GDT_CMD_UNION + + GDT_CACHE_BLOCKNO, arg2); + } + break; + + case GDT_SCSIRAWSERVICE: + gdt_enc32(gdt->sc_cmd + GDT_CMD_UNION + + GDT_RAW_DIRECTION, arg1); + gdt->sc_cmd[GDT_CMD_UNION + GDT_RAW_BUS] = + (u_int8_t)arg2; + gdt->sc_cmd[GDT_CMD_UNION + GDT_RAW_TARGET] = + (u_int8_t)arg3; + gdt->sc_cmd[GDT_CMD_UNION + GDT_RAW_LUN] = + (u_int8_t)(arg3 >> 8); + } + + gdt->sc_cmd_len = GDT_CMD_SZ; + gdt->sc_cmd_off = 0; + gdt->sc_cmd_cnt = 0; + gdt->sc_copy_cmd(gdt, ccb); + gdt->sc_release_event(gdt, ccb); + DELAY(20); + if (!gdt_wait(gdt, ccb, GDT_POLL_TIMEOUT)) + return (0); + if (gdt->sc_status != GDT_S_BSY || --retries == 0) + break; + DELAY(1); + } + return (gdt->sc_status == GDT_S_OK); +} + +struct gdt_ccb * +gdt_get_ccb(gdt, flags) + struct gdt_softc *gdt; + int flags; +{ + struct gdt_ccb *ccb; + + GDT_DPRINTF(GDT_D_QUEUE, ("gdt_get_ccb(%p, 0x%x) ", gdt, flags)); + + GDT_LOCK_GDT(gdt); + + for (;;) { + ccb = TAILQ_FIRST(&gdt->sc_free_ccb); + if (ccb != NULL) + break; + if (flags & SCSI_NOSLEEP) + goto bail_out; + tsleep(&gdt->sc_free_ccb, PRIBIO, "gdt_ccb", 0); + } + + TAILQ_REMOVE(&gdt->sc_free_ccb, ccb, gc_chain); + + bail_out: + GDT_UNLOCK_GDT(gdt); + return (ccb); +} + +void +gdt_free_ccb(gdt, ccb) + struct gdt_softc *gdt; + struct gdt_ccb *ccb; +{ + GDT_DPRINTF(GDT_D_QUEUE, ("gdt_free_ccb(%p, %p) ", gdt, ccb)); + + GDT_LOCK_GDT(gdt); + + TAILQ_INSERT_HEAD(&gdt->sc_free_ccb, ccb, gc_chain); + + /* If the free list was empty, wake up potential waiters. */ + if (TAILQ_NEXT(ccb, gc_chain) == NULL) + wakeup(&gdt->sc_free_ccb); + + GDT_UNLOCK_GDT(gdt); +} + +void +gdt_enqueue_ccb(gdt, ccb) + struct gdt_softc *gdt; + struct gdt_ccb *ccb; +{ + GDT_DPRINTF(GDT_D_QUEUE, ("gdt_enqueue_ccb(%p, %p) ", gdt, ccb)); + + TAILQ_INSERT_TAIL(&gdt->sc_ccbq, ccb, gc_chain); + gdt_start_ccbs(gdt); +} + +void +gdt_start_ccbs(gdt) + struct gdt_softc *gdt; +{ + struct gdt_ccb *ccb; + + GDT_DPRINTF(GDT_D_QUEUE, ("gdt_start_ccbs(%p) ", gdt)); + + while ((ccb = TAILQ_FIRST(&gdt->sc_ccbq)) != NULL) { + if (ccb->gc_flags & GDT_GCF_WATCHDOG) + untimeout(gdt_watchdog, ccb); + + if (gdt_exec_ccb(ccb) == 0) { + ccb->gc_flags |= GDT_GCF_WATCHDOG; + timeout(gdt_watchdog, ccb, + (GDT_WATCH_TIMEOUT * hz) / 1000); + break; + } + TAILQ_REMOVE(&gdt->sc_ccbq, ccb, gc_chain); + + if ((ccb->gc_xs->flags & SCSI_POLL) == 0) + timeout(gdt_timeout, ccb, + (ccb->gc_timeout * hz) / 1000); + } +} + +void +gdt_chain(gdt) + struct gdt_softc *gdt; +{ + GDT_DPRINTF(GDT_D_INTR, ("gdt_chain(%p) ", gdt)); + + if (LIST_FIRST(&gdt->sc_queue)) + gdt_scsi_cmd(LIST_FIRST(&gdt->sc_queue)); +} + +void +gdt_timeout(arg) + void *arg; +{ + struct gdt_ccb *ccb = arg; + struct scsi_link *link = ccb->gc_xs->sc_link; + struct gdt_softc *gdt = link->adapter_softc; + + sc_print_addr(link); + printf("timed out\n"); + + /* XXX Test for multiple timeouts */ + + ccb->gc_xs->error = XS_TIMEOUT; + GDT_LOCK_GDT(gdt); + gdt_enqueue_ccb(gdt, ccb); + GDT_UNLOCK_GDT(gdt); +} + +void +gdt_watchdog(arg) + void *arg; +{ + struct gdt_ccb *ccb = arg; + struct scsi_link *link = ccb->gc_xs->sc_link; + struct gdt_softc *gdt = link->adapter_softc; + + GDT_LOCK_GDT(gdt); + ccb->gc_flags &= ~GDT_GCF_WATCHDOG; + gdt_start_ccbs(gdt); + GDT_UNLOCK_GDT(gdt); +} |