summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgibbs <gibbs@FreeBSD.org>1998-09-15 07:24:17 +0000
committergibbs <gibbs@FreeBSD.org>1998-09-15 07:24:17 +0000
commitb845ffee8074eddeeb44108a8646161fbcf33aff (patch)
tree29b6ded2164fbd85ddc231ce2953cec74858b5bc
parentfd4d41dcf7b1812c21942f9a24ef7ee737d2f1dd (diff)
downloadFreeBSD-src-b845ffee8074eddeeb44108a8646161fbcf33aff.zip
FreeBSD-src-b845ffee8074eddeeb44108a8646161fbcf33aff.tar.gz
Massive overhaul of the aic7xxx driver:
- Convert to CAM - Use a new DMA based queuing and paging scheme - Add preliminary target mode support - Add support for the aic789X chips - Take advantage of external SRAM on more controllers. - Numerous bug fixes and performance improvements.
-rw-r--r--sys/dev/aic7xxx/93cx6.c174
-rw-r--r--sys/dev/aic7xxx/93cx6.h95
-rw-r--r--sys/dev/aic7xxx/aic7xxx.c4823
-rw-r--r--sys/dev/aic7xxx/aic7xxx.h524
-rw-r--r--sys/dev/aic7xxx/aic7xxx.reg458
-rw-r--r--sys/dev/aic7xxx/aic7xxx.seq1686
-rw-r--r--sys/dev/aic7xxx/aic7xxx_93cx6.c174
-rw-r--r--sys/dev/aic7xxx/aic7xxx_93cx6.h95
-rw-r--r--sys/dev/aic7xxx/aicasm.c422
-rw-r--r--sys/dev/aic7xxx/aicasm.h19
-rw-r--r--sys/dev/aic7xxx/aicasm/aicasm.c422
-rw-r--r--sys/dev/aic7xxx/aicasm/aicasm.h19
-rw-r--r--sys/dev/aic7xxx/aicasm/aicasm_gram.y186
-rw-r--r--sys/dev/aic7xxx/aicasm/aicasm_scan.l55
-rw-r--r--sys/dev/aic7xxx/aicasm/aicasm_symbol.c7
-rw-r--r--sys/dev/aic7xxx/aicasm/aicasm_symbol.h42
-rw-r--r--sys/dev/aic7xxx/aicasm_gram.y186
-rw-r--r--sys/dev/aic7xxx/aicasm_scan.l55
-rw-r--r--sys/dev/aic7xxx/aicasm_symbol.c7
-rw-r--r--sys/dev/aic7xxx/aicasm_symbol.h42
-rw-r--r--sys/dev/aic7xxx/sequencer.h50
21 files changed, 8428 insertions, 1113 deletions
diff --git a/sys/dev/aic7xxx/93cx6.c b/sys/dev/aic7xxx/93cx6.c
new file mode 100644
index 0000000..d7f8ab5
--- /dev/null
+++ b/sys/dev/aic7xxx/93cx6.c
@@ -0,0 +1,174 @@
+/*
+ * Interface for the 93C66/56/46/26/06 serial eeprom parts.
+ *
+ * Copyright (c) 1995, 1996 Daniel M. Eischen
+ * 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 immediately at the beginning of the file, without modification,
+ * 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. Absolutely no warranty of function or purpose is made by the author
+ * Daniel M. Eischen.
+ * 4. Modifications may be freely made to this file if the above conditions
+ * are met.
+ *
+ * $Id: 93cx6.c,v 1.10 1997/02/22 09:38:36 peter Exp $
+ */
+
+/*
+ * The instruction set of the 93C66/56/46/26/06 chips are as follows:
+ *
+ * Start OP *
+ * Function Bit Code Address** Data Description
+ * -------------------------------------------------------------------
+ * READ 1 10 A5 - A0 Reads data stored in memory,
+ * starting at specified address
+ * EWEN 1 00 11XXXX Write enable must preceed
+ * all programming modes
+ * ERASE 1 11 A5 - A0 Erase register A5A4A3A2A1A0
+ * WRITE 1 01 A5 - A0 D15 - D0 Writes register
+ * ERAL 1 00 10XXXX Erase all registers
+ * WRAL 1 00 01XXXX D15 - D0 Writes to all registers
+ * EWDS 1 00 00XXXX Disables all programming
+ * instructions
+ * *Note: A value of X for address is a don't care condition.
+ * **Note: There are 8 address bits for the 93C56/66 chips unlike
+ * the 93C46/26/06 chips which have 6 address bits.
+ *
+ * The 93C46 has a four wire interface: clock, chip select, data in, and
+ * data out. In order to perform one of the above functions, you need
+ * to enable the chip select for a clock period (typically a minimum of
+ * 1 usec, with the clock high and low a minimum of 750 and 250 nsec
+ * respectively). While the chip select remains high, you can clock in
+ * the instructions (above) starting with the start bit, followed by the
+ * OP code, Address, and Data (if needed). For the READ instruction, the
+ * requested 16-bit register contents is read from the data out line but
+ * is preceded by an initial zero (leading 0, followed by 16-bits, MSB
+ * first). The clock cycling from low to high initiates the next data
+ * bit to be sent from the chip.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <machine/bus_memio.h>
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <dev/aic7xxx/93cx6.h>
+
+/*
+ * Right now, we only have to read the SEEPROM. But we make it easier to
+ * add other 93Cx6 functions.
+ */
+static struct seeprom_cmd {
+ unsigned char len;
+ unsigned char bits[3];
+} seeprom_read = {3, {1, 1, 0}};
+
+/*
+ * Wait for the SEERDY to go high; about 800 ns.
+ */
+#define CLOCK_PULSE(sd, rdy) \
+ while ((SEEPROM_STATUS_INB(sd) & rdy) == 0) { \
+ ; /* Do nothing */ \
+ } \
+ (void)SEEPROM_INB(sd); /* Clear clock */
+
+/*
+ * Read the serial EEPROM and returns 1 if successful and 0 if
+ * not successful.
+ */
+int
+read_seeprom(sd, buf, start_addr, count)
+ struct seeprom_descriptor *sd;
+ u_int16_t *buf;
+ bus_size_t start_addr;
+ bus_size_t count;
+{
+ int i = 0;
+ u_int k = 0;
+ u_int16_t v;
+ u_int8_t temp;
+
+ /*
+ * Read the requested registers of the seeprom. The loop
+ * will range from 0 to count-1.
+ */
+ for (k = start_addr; k < count + start_addr; k++) {
+ /* Send chip select for one clock cycle. */
+ temp = sd->sd_MS ^ sd->sd_CS;
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+
+ /*
+ * Now we're ready to send the read command followed by the
+ * address of the 16-bit register we want to read.
+ */
+ for (i = 0; i < seeprom_read.len; i++) {
+ if (seeprom_read.bits[i] != 0)
+ temp ^= sd->sd_DO;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ if (seeprom_read.bits[i] != 0)
+ temp ^= sd->sd_DO;
+ }
+ /* Send the 6 or 8 bit address (MSB first, LSB last). */
+ for (i = (sd->sd_chip - 1); i >= 0; i--) {
+ if ((k & (1 << i)) != 0)
+ temp ^= sd->sd_DO;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ if ((k & (1 << i)) != 0)
+ temp ^= sd->sd_DO;
+ }
+
+ /*
+ * Now read the 16 bit register. An initial 0 precedes the
+ * register contents which begins with bit 15 (MSB) and ends
+ * with bit 0 (LSB). The initial 0 will be shifted off the
+ * top of our word as we let the loop run from 0 to 16.
+ */
+ v = 0;
+ for (i = 16; i >= 0; i--) {
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ v <<= 1;
+ if (SEEPROM_DATA_INB(sd) & sd->sd_DI)
+ v |= 1;
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ }
+
+ buf[k - start_addr] = v;
+
+ /* Reset the chip select for the next command cycle. */
+ temp = sd->sd_MS;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ }
+#ifdef 93CX6_DUMP_EEPROM
+ printf("\nSerial EEPROM:");
+ for (k = 0; k < count; k = k + 1) {
+ if (((k % 8) == 0) && (k != 0)) {
+ printf ("\n ");
+ }
+ printf (" 0x%x", buf[k]);
+ }
+ printf ("\n");
+#endif
+ return (1);
+}
diff --git a/sys/dev/aic7xxx/93cx6.h b/sys/dev/aic7xxx/93cx6.h
new file mode 100644
index 0000000..32645e1
--- /dev/null
+++ b/sys/dev/aic7xxx/93cx6.h
@@ -0,0 +1,95 @@
+/*
+ * Interface to the 93C46 serial EEPROM that is used to store BIOS
+ * settings for the aic7xxx based adaptec SCSI controllers. It can
+ * also be used for 93C26 and 93C06 serial EEPROMS.
+ *
+ * Copyright (c) 1994, 1995 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$
+ */
+
+#include <sys/param.h>
+#if !defined(__NetBSD__)
+#include <sys/systm.h>
+#endif
+
+#ifdef KERNEL
+
+typedef enum {
+ C46 = 6,
+ C56_66 = 8
+} seeprom_chip_t;
+
+struct seeprom_descriptor {
+ bus_space_tag_t sd_tag;
+ bus_space_handle_t sd_bsh;
+ bus_size_t sd_control_offset;
+ bus_size_t sd_status_offset;
+ bus_size_t sd_dataout_offset;
+ seeprom_chip_t sd_chip;
+ u_int16_t sd_MS;
+ u_int16_t sd_RDY;
+ u_int16_t sd_CS;
+ u_int16_t sd_CK;
+ u_int16_t sd_DO;
+ u_int16_t sd_DI;
+};
+
+/*
+ * This function will read count 16-bit words from the serial EEPROM and
+ * return their value in buf. The port address of the aic7xxx serial EEPROM
+ * control register is passed in as offset. The following parameters are
+ * also passed in:
+ *
+ * CS - Chip select
+ * CK - Clock
+ * DO - Data out
+ * DI - Data in
+ * RDY - SEEPROM ready
+ * MS - Memory port mode select
+ *
+ * A failed read attempt returns 0, and a successful read returns 1.
+ */
+
+#define SEEPROM_INB(sd) \
+ bus_space_read_1(sd->sd_tag, sd->sd_bsh, sd->sd_control_offset)
+#define SEEPROM_OUTB(sd, value) \
+ bus_space_write_1(sd->sd_tag, sd->sd_bsh, sd->sd_control_offset, value)
+#define SEEPROM_STATUS_INB(sd) \
+ bus_space_read_1(sd->sd_tag, sd->sd_bsh, sd->sd_status_offset)
+#define SEEPROM_DATA_INB(sd) \
+ bus_space_read_1(sd->sd_tag, sd->sd_bsh, sd->sd_dataout_offset)
+
+int read_seeprom(struct seeprom_descriptor *sd, u_int16_t *buf,
+ bus_size_t start_addr, bus_size_t count);
+
+#endif /* KERNEL */
diff --git a/sys/dev/aic7xxx/aic7xxx.c b/sys/dev/aic7xxx/aic7xxx.c
new file mode 100644
index 0000000..67366f4
--- /dev/null
+++ b/sys/dev/aic7xxx/aic7xxx.c
@@ -0,0 +1,4823 @@
+/*
+ * 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 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.113 1997/04/05 21:41:13 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_TARGETS (~0)
+#define ALL_LUNS (~0)
+#define ALL_CHANNELS '\0'
+
+#define SIM_IS_SCSIBUS_B(ahc, sim) \
+ (sim == ahc->sim_b)
+#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 ccb_scb_ptr spriv_ptr0
+#define ccb_ahc_ptr spriv_ptr1
+
+struct ahc_devinfo {
+ int target_offset;
+ u_int16_t target_mask;
+ u_int8_t target;
+ char channel;
+};
+
+typedef enum {
+ SEARCH_COMPLETE,
+ SEARCH_COUNT,
+ SEARCH_REMOVE
+} ahc_search_action;
+
+u_long ahc_unit = 0;
+
+#ifdef AHC_DEBUG
+static int ahc_debug = AHC_DEBUG;
+#endif
+
+#if NPCI > 0
+void ahc_pci_intr(struct ahc_softc *ahc);
+#endif
+
+static void ahc_dump_targcmd(struct target_cmd *cmd);
+static void ahc_shutdown(int howto, void *arg);
+static void ahcminphys(struct buf *bp);
+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 struct scb *
+ ahc_get_scb(struct ahc_softc *ahc);
+static void ahc_free_scb(struct ahc_softc *ahc, struct scb *scb);
+static struct scb *
+ ahc_alloc_scb(struct ahc_softc *ahc);
+static void ahc_fetch_devinfo(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static void ahc_compile_devinfo(struct ahc_devinfo *devinfo,
+ u_int target, char channel);
+static u_int8_t ahc_abort_wscb(struct ahc_softc *ahc, u_int8_t scbpos,
+ u_int8_t prev);
+static void ahc_done(struct ahc_softc *ahc, struct scb *scbp);
+static void ahc_handle_target_cmd(struct ahc_softc *ahc);
+static void ahc_handle_seqint(struct ahc_softc *ahc, u_int8_t intstat);
+static void ahc_handle_scsiint(struct ahc_softc *ahc,
+ u_int8_t intstat);
+static void ahc_handle_reqinit(struct ahc_softc *ahc,
+ struct scb *scb);
+static int ahc_parse_msg(struct ahc_softc *ahc, struct scb *scb,
+ struct ahc_devinfo *devinfo);
+static void ahc_handle_devreset(struct ahc_softc *ahc, int target,
+ char channel, 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_int8_t tag);
+#ifdef AHC_DEBUG
+static void ahc_print_scb(struct scb *scb);
+#endif
+static u_int8_t ahc_find_scb(struct ahc_softc *ahc, struct scb *scb);
+static int ahc_search_qinfifo(struct ahc_softc *ahc, int target,
+ char channel, int lun, u_int8_t tag,
+ u_int32_t status, ahc_search_action action);
+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_int8_t tag,
+ u_int32_t status);
+static u_int8_t ahc_rem_scb_from_disc_list(struct ahc_softc *ahc,
+ u_int8_t prev, u_int8_t 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_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_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_construct_sdtr(struct ahc_softc *ahc,
+ u_int8_t period, u_int8_t offset);
+
+static void ahc_construct_wdtr(struct ahc_softc *ahc,
+ u_int8_t 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 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_int8_t ahc_index_busy_tcl(struct ahc_softc *ahc,
+ u_int8_t 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 ahc_set_ccb_status(union ccb* ccb,
+ cam_status status);
+
+static __inline u_int32_t
+ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index)
+{
+ return (ahc->hscb_busaddr + (sizeof(struct hardware_scb) * index));
+}
+
+#define AHC_BUSRESET_DELAY 25 /* Reset delay in us */
+
+static __inline void
+pause_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 ((ahc_inb(ahc, HCNTRL) & PAUSE) == 0)
+ ;
+}
+
+static __inline void
+unpause_sequencer(struct ahc_softc *ahc, int unpause_always)
+{
+ if ((ahc->flags & AHC_HANDLING_REQINITS) == 0
+ && (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 void
+restart_sequencer(struct ahc_softc *ahc)
+{
+ pause_sequencer(ahc);
+ ahc_outb(ahc, SEQCTL, FASTMODE|SEQRESET);
+ unpause_sequencer(ahc, /*unpause_always*/TRUE);
+}
+
+static __inline u_int8_t
+ahc_index_busy_tcl(struct ahc_softc *ahc, u_int8_t tcl, int unbusy)
+{
+ u_int8_t scbid;
+
+ scbid = ahc->untagged_scbs[tcl];
+ if (unbusy)
+ ahc->untagged_scbs[tcl] = SCB_LIST_NULL;
+
+ return (scbid);
+}
+
+static __inline void
+ahc_busy_tcl(struct ahc_softc *ahc, struct scb *scb)
+{
+ ahc->untagged_scbs[scb->hscb->tcl] = scb->hscb->tag;
+}
+
+static __inline void
+ahc_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_status
+ahc_ccb_status(union ccb* ccb)
+{
+ return (ccb->ccb_h.status & CAM_STATUS_MASK);
+}
+
+static __inline void
+ahc_set_ccb_status(union ccb* ccb, cam_status status)
+{
+ ccb->ccb_h.status &= ~CAM_STATUS_MASK;
+ ccb->ccb_h.status |= status;
+}
+
+char *
+ahc_name(struct ahc_softc *ahc)
+{
+ static char name[10];
+
+ sprintf(name, "ahc%d", ahc->unit);
+ return (name);
+}
+
+#ifdef AHC_DEBUG
+static void
+ahc_print_scb(struct scb *scb)
+{
+ struct hardware_scb *hscb = scb->hscb;
+
+ printf("scb:%p control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n",
+ scb,
+ hscb->control,
+ hscb->tcl,
+ hscb->cmdlen,
+ hscb->cmdpointer );
+ printf(" datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n",
+ hscb->datalen,
+ hscb->data,
+ hscb->SG_count,
+ hscb->SG_pointer);
+ printf(" sg_addr:%lx sg_len:%ld\n",
+ scb->ahc_dma[0].addr,
+ scb->ahc_dma[0].len);
+ printf(" cdb:%x %x %x %x %x %x %x %x %x %x %x %x\n",
+ hscb->cmdstore[0], hscb->cmdstore[1], hscb->cmdstore[2],
+ hscb->cmdstore[3], hscb->cmdstore[4], hscb->cmdstore[5],
+ hscb->cmdstore[6], hscb->cmdstore[7], hscb->cmdstore[8],
+ hscb->cmdstore[9], hscb->cmdstore[10], hscb->cmdstore[11]);
+}
+#endif
+
+static struct {
+ u_int8_t errno;
+ char *errmesg;
+} hard_error[] = {
+ { ILLHADDR, "Illegal Host Access" },
+ { ILLSADDR, "Illegal Sequencer Address referrenced" },
+ { ILLOPCODE, "Illegal Opcode in sequencer program" },
+ { SQPARERR, "Sequencer Parity Error" },
+ { DPARERR, "Data-path Parity Error" },
+ { MPARERR, "Scratch or SCB Memory Parity Error" },
+ { PCIERRSTAT, "PCI Error detected" },
+ { CIOPARERR, "CIOBUS Parity Error" },
+};
+
+
+/*
+ * Valid SCSIRATE values. (p. 3-17)
+ * Provides a mapping of tranfer periods in ns to the proper value to
+ * stick in the scsiscfr reg to use that transfer rate.
+ */
+#define AHC_SYNCRATE_ULTRA2 0
+#define AHC_SYNCRATE_ULTRA 2
+#define AHC_SYNCRATE_FAST 5
+static struct ahc_syncrate ahc_syncrates[] = {
+ /* ultra2 fast/ultra period rate */
+ { 0x13, 0x000, 10, "40.0" },
+ { 0x14, 0x000, 11, "33.0" },
+ { 0x15, 0x100, 12, "20.0" },
+ { 0x16, 0x110, 15, "16.0" },
+ { 0x17, 0x120, 18, "13.4" },
+ { 0x18, 0x000, 25, "10.0" },
+ { 0x19, 0x010, 31, "8.0" },
+ { 0x1a, 0x020, 37, "6.67" },
+ { 0x1b, 0x030, 43, "5.7" },
+ { 0x10, 0x040, 50, "5.0" },
+ { 0x00, 0x050, 56, "4.4" },
+ { 0x00, 0x060, 62, "4.0" },
+ { 0x00, 0x070, 68, "3.6" },
+ { 0x00, 0x000, 0, NULL }
+};
+
+/*
+ * Allocate a controller structure for a new device and initialize it.
+ */
+struct ahc_softc *
+ahc_alloc(int unit, u_int32_t iobase, vm_offset_t maddr, ahc_chip chip,
+ ahc_feature features, ahc_flag flags, struct scb_data *scb_data)
+{
+ /*
+ * find unit and check we have that many defined
+ */
+ struct ahc_softc *ahc;
+ size_t alloc_size;
+
+ /*
+ * Allocate a storage area for us
+ */
+ if (scb_data == NULL)
+ /*
+ * We are not sharing SCB space with another controller
+ * so allocate our own SCB data space.
+ */
+ alloc_size = sizeof(struct full_ahc_softc);
+ else
+ alloc_size = sizeof(struct ahc_softc);
+ ahc = malloc(alloc_size, M_DEVBUF, M_NOWAIT);
+ if (!ahc) {
+ printf("ahc%d: cannot malloc!\n", unit);
+ return NULL;
+ }
+ bzero(ahc, alloc_size);
+ if (scb_data == NULL) {
+ struct full_ahc_softc* full_softc = (struct full_ahc_softc*)ahc;
+ ahc->scb_data = &full_softc->scb_data_storage;
+ STAILQ_INIT(&ahc->scb_data->free_scbs);
+ } else
+ ahc->scb_data = scb_data;
+ LIST_INIT(&ahc->pending_ccbs);
+ ahc->unit = unit;
+
+ /*
+ * XXX This should be done by the bus specific probe stubs with
+ * the bus layer providing the bsh and tag. Unfortunately,
+ * we need to clean up how we configure things before this
+ * can happen.
+ */
+ if (maddr != NULL) {
+ ahc->tag = I386_BUS_SPACE_MEM;
+ ahc->bsh = (bus_space_handle_t)maddr;
+ } else {
+ ahc->tag = I386_BUS_SPACE_IO;
+ ahc->bsh = (bus_space_handle_t)iobase;
+ }
+ ahc->chip = chip;
+ ahc->features = features;
+ ahc->flags = flags;
+ ahc->unpause = (ahc_inb(ahc, HCNTRL) & IRQMS) | INTEN;
+ ahc->pause = ahc->unpause | PAUSE;
+
+ return (ahc);
+}
+
+void
+ahc_free(ahc)
+ struct ahc_softc *ahc;
+{
+ free(ahc, M_DEVBUF);
+ return;
+}
+
+int
+ahc_reset(struct ahc_softc *ahc)
+{
+ u_int8_t hcntrl;
+ u_int8_t sblkctl;
+ int wait;
+
+ ahc_outb(ahc, HCNTRL, CHIPRST | ahc->pause);
+ /*
+ * Ensure that the reset has finished
+ */
+ wait = 1000;
+ while (--wait && !(ahc_inb(ahc, HCNTRL) & CHIPRSTACK))
+ DELAY(1000);
+ if (wait == 0) {
+ printf("%s: WARNING - Failed chip reset! "
+ "Trying to initialize anyway.\n", ahc_name(ahc));
+ }
+ ahc_outb(ahc, HCNTRL, ahc->pause);
+
+ /* Determine channel configuration */
+ sblkctl = ahc_inb(ahc, SBLKCTL) & (SELBUSB|SELWIDE);
+ /* No Twin Channel PCI cards */
+ if ((ahc->chip & AHC_PCI) != 0)
+ sblkctl &= ~SELBUSB;
+ switch (sblkctl) {
+ case 0:
+ /* Single Narrow Channel */
+ break;
+ case 2:
+ /* Wide Channel */
+ ahc->features |= AHC_WIDE;
+ break;
+ case 8:
+ /* Twin Channel */
+ ahc->features |= AHC_TWIN;
+ break;
+ default:
+ printf(" Unsupported adapter type. Ignoring\n");
+ return(-1);
+ }
+ return (0);
+}
+
+/*
+ * Look up the valid period to SCSIRATE conversion in our table.
+ * Return the period and offset that should be sent to the target
+ * if this was the beginning of an SDTR.
+ */
+static struct ahc_syncrate *
+ahc_find_syncrate(struct ahc_softc *ahc, u_int *period, u_int maxsync)
+{
+ struct ahc_syncrate *syncrate;
+
+ syncrate = &ahc_syncrates[maxsync];
+ while ((syncrate->rate != NULL)
+ && ((ahc->features & AHC_ULTRA2) == 0
+ || (syncrate->sxfr_ultra2 != 0))) {
+
+ if (*period <= syncrate->period) {
+ /*
+ * When responding to a target that requests
+ * sync, the requested rate may fall between
+ * two rates that we can output, but still be
+ * a rate that we can receive. Because of this,
+ * we want to respond to the target with
+ * the same rate that it sent to us even
+ * if the period we use to send data to it
+ * is lower. Only lower the response period
+ * if we must.
+ */
+ if (syncrate == &ahc_syncrates[maxsync]) {
+ *period = syncrate->period;
+ }
+ break;
+ }
+ syncrate++;
+ }
+
+ if ((*period == 0)
+ || (syncrate->rate == NULL)
+ || ((ahc->features & AHC_ULTRA2) != 0
+ && (syncrate->sxfr_ultra2 == 0))) {
+ /* Use asynchronous transfers. */
+ *period = 0;
+ syncrate = NULL;
+ }
+ return (syncrate);
+}
+
+static u_int
+ahc_find_period(struct ahc_softc *ahc, u_int scsirate, u_int maxsync)
+{
+ struct ahc_syncrate *syncrate;
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ scsirate &= SXFR_ULTRA2;
+ } else {
+ scsirate &= SXFR;
+ }
+
+ syncrate = &ahc_syncrates[maxsync];
+ while (syncrate->rate != NULL) {
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ if (syncrate->sxfr_ultra2 == 0)
+ break;
+ else if (scsirate == syncrate->sxfr_ultra2)
+ return (syncrate->period);
+ } else if (scsirate == (syncrate->sxfr & ~ULTRA_SXFR)) {
+ return (syncrate->period);
+ }
+ syncrate++;
+ }
+ return (0); /* async */
+}
+
+static void
+ahc_validate_offset(struct ahc_softc *ahc, struct ahc_syncrate *syncrate,
+ u_int *offset, int wide)
+{
+ u_int maxoffset;
+
+ /* Limit offset to what we can do */
+ if (syncrate == NULL) {
+ maxoffset = 0;
+ } else if ((ahc->features & AHC_ULTRA2) != 0) {
+ maxoffset = MAX_OFFSET_ULTRA2;
+ } else {
+ if (wide)
+ maxoffset = MAX_OFFSET_16BIT;
+ else
+ maxoffset = MAX_OFFSET_8BIT;
+ }
+ *offset = MIN(*offset, maxoffset);
+}
+
+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)
+{
+ u_int old_period;
+ u_int old_offset;
+
+ if (syncrate == NULL) {
+ period = 0;
+ offset = 0;
+ }
+
+ old_period = ahc->transinfo[devinfo->target_offset].current.period;
+ old_offset = ahc->transinfo[devinfo->target_offset].current.offset;
+
+ if ((type & AHC_TRANS_CUR) != 0
+ && (old_period != period || old_offset != offset)) {
+ struct ccb_trans_settings neg;
+ u_int scsirate;
+
+ scsirate = ahc->transinfo[devinfo->target_offset].scsirate;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+
+ scsirate &= ~SXFR_ULTRA2;
+
+ if (syncrate != NULL) {
+ scsirate |= syncrate->sxfr_ultra2;
+ }
+
+ if ((type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE) {
+ ahc_outb(ahc, SCSIOFFSET, offset);
+ }
+ ahc_outb(ahc, TARG_OFFSET + devinfo->target_offset,
+ offset);
+ } else {
+
+ scsirate &= ~(SXFR|SOFS);
+ /*
+ * Ensure Ultra mode is set properly for
+ * this target.
+ */
+ ahc->ultraenb &= ~devinfo->target_mask;
+ if (syncrate != NULL) {
+ if (syncrate->sxfr & ULTRA_SXFR) {
+ ahc->ultraenb |= devinfo->target_mask;
+ }
+ scsirate |= syncrate->sxfr & SXFR;
+ scsirate |= offset & SOFS;
+ }
+ if ((type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE) {
+ u_int8_t sxfrctl0;
+
+ sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+ sxfrctl0 &= ~FAST20;
+ if (ahc->ultraenb & devinfo->target_mask)
+ sxfrctl0 |= FAST20;
+ ahc_outb(ahc, SXFRCTL0, sxfrctl0);
+ }
+ }
+ if ((type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE)
+ ahc_outb(ahc, SCSIRATE, scsirate);
+
+ ahc->transinfo[devinfo->target_offset].scsirate = scsirate;
+ ahc->transinfo[devinfo->target_offset].current.period = period;
+ ahc->transinfo[devinfo->target_offset].current.offset = offset;
+
+ /* Update the syncrates in any pending scbs */
+ ahc_update_pending_syncrates(ahc);
+
+ /*
+ * Tell the SCSI layer about the
+ * new transfer parameters.
+ */
+ neg.sync_period = period;
+ neg.sync_offset = offset;
+ neg.valid = CCB_TRANS_SYNC_RATE_VALID
+ | CCB_TRANS_SYNC_OFFSET_VALID;
+ xpt_setup_ccb(&neg.ccb_h, path, /*priority*/1);
+ xpt_async(AC_TRANSFER_NEG, path, &neg);
+ if (bootverbose) {
+ if (neg.sync_offset != 0) {
+ printf("%s: target %d synchronous at %sMHz, "
+ "offset = 0x%x\n", ahc_name(ahc),
+ devinfo->target, syncrate->rate, offset);
+ } else {
+ printf("%s: target %d using "
+ "asynchronous transfers\n",
+ ahc_name(ahc), devinfo->target);
+ }
+ }
+ }
+
+ if ((type & AHC_TRANS_GOAL) != 0) {
+ ahc->transinfo[devinfo->target_offset].goal.period = period;
+ ahc->transinfo[devinfo->target_offset].goal.offset = offset;
+ }
+
+ if ((type & AHC_TRANS_USER) != 0) {
+ ahc->transinfo[devinfo->target_offset].user.period = period;
+ ahc->transinfo[devinfo->target_offset].user.offset = offset;
+ }
+}
+
+static void
+ahc_set_width(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ struct cam_path *path, u_int width, u_int type)
+{
+ u_int oldwidth;
+
+ oldwidth = ahc->transinfo[devinfo->target_offset].current.width;
+
+ if ((type & AHC_TRANS_CUR) != 0 && oldwidth != width) {
+ struct ccb_trans_settings neg;
+ u_int8_t scsirate;
+
+ scsirate = ahc->transinfo[devinfo->target_offset].scsirate;
+ scsirate &= ~WIDEXFER;
+ if (width == MSG_EXT_WDTR_BUS_16_BIT)
+ scsirate |= WIDEXFER;
+
+ ahc->transinfo[devinfo->target_offset].scsirate = scsirate;
+
+ if ((type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE)
+ ahc_outb(ahc, SCSIRATE, scsirate);
+
+ ahc->transinfo[devinfo->target_offset].current.width = width;
+
+ /* Tell the SCSI layer about the new transfer params */
+ neg.bus_width = width;
+ neg.valid = CCB_TRANS_BUS_WIDTH_VALID;
+ xpt_setup_ccb(&neg.ccb_h, path, /*priority*/1);
+ xpt_async(AC_TRANSFER_NEG, path, &neg);
+ if (bootverbose) {
+ printf("%s: target %d using %dbit transfers\n",
+ ahc_name(ahc), devinfo->target,
+ 8 * (0x01 << neg.bus_width));
+ }
+ }
+ if ((type & AHC_TRANS_GOAL) != 0) {
+ ahc->transinfo[devinfo->target_offset].goal.width = width;
+ }
+ if ((type & AHC_TRANS_USER) != 0) {
+ ahc->transinfo[devinfo->target_offset].user.width = width;
+ }
+}
+
+/*
+ * Attach all the sub-devices we can find
+ */
+int
+ahc_attach(struct ahc_softc *ahc)
+{
+ struct ccb_setasync csa;
+ struct cam_devq *devq;
+ int bus_id;
+
+ /*
+ * Create the device queue for our SIM.
+ */
+ devq = cam_simq_alloc(ahc->scb_data->maxscbs);
+ if (devq == NULL)
+ return (0);
+
+ /*
+ * Construct our SIM entry
+ */
+ ahc->sim = cam_sim_alloc(ahc_action, ahc_poll, "ahc", ahc, ahc->unit,
+ 1, ahc->scb_data->maxscbs, devq);
+ if (ahc->sim == NULL) {
+ cam_simq_free(devq);
+ return (0);
+ }
+ bus_id = (ahc->flags & AHC_CHANNEL_B_PRIMARY) ? 1 : 0;
+
+ if (xpt_bus_register(ahc->sim, bus_id) != CAM_SUCCESS) {
+ cam_sim_free(ahc->sim, /*free_devq*/TRUE);
+ return (0);
+ }
+
+ if (xpt_create_path(&ahc->path, /*periph*/NULL,
+ cam_sim_path(ahc->sim), CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+ xpt_bus_deregister(cam_sim_path(ahc->sim));
+ cam_sim_free(ahc->sim, /*free_devq*/TRUE);
+ return (0);
+ }
+
+ xpt_setup_ccb(&csa.ccb_h, ahc->path, /*priority*/5);
+ csa.ccb_h.func_code = XPT_SASYNC_CB;
+ csa.event_enable = AC_LOST_DEVICE;
+ csa.callback = ahc_async;
+ csa.callback_arg = ahc->sim;
+ xpt_action((union ccb *)&csa);
+
+ if (ahc->features & AHC_TWIN) {
+ ahc->sim_b = cam_sim_alloc(ahc_action, ahc_poll, "ahc",
+ ahc, ahc->unit, 1,
+ ahc->scb_data->maxscbs, devq);
+
+ if (ahc->sim_b == NULL) {
+ printf("ahc_attach: Unable to attach second "
+ "bus due to resource shortage");
+ /*
+ * Must return success or the first bus
+ * won't get attached either.
+ */
+ return (1);
+ }
+
+ bus_id = (ahc->flags & AHC_CHANNEL_B_PRIMARY) ? 0 : 1;
+ if (xpt_bus_register(ahc->sim_b, bus_id) != CAM_SUCCESS) {
+ printf("ahc_attach: Unable to attach second "
+ "bus due to resource shortage");
+ /*
+ * We do not want to destroy the device queue
+ * because the first bus is using it.
+ */
+ cam_sim_free(ahc->sim_b, /*free_devq*/FALSE);
+ ahc->sim_b = NULL;
+ return (1);
+ }
+
+ if (xpt_create_path(&ahc->path_b, /*periph*/NULL,
+ cam_sim_path(ahc->sim_b),
+ CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+ xpt_bus_deregister(cam_sim_path(ahc->sim_b));
+ cam_sim_free(ahc->sim_b, /*free_devq*/FALSE);
+ ahc->sim_b = NULL;
+ return (1);
+ }
+ xpt_setup_ccb(&csa.ccb_h, ahc->path_b, /*priority*/5);
+ csa.ccb_h.func_code = XPT_SASYNC_CB;
+ csa.event_enable = AC_LOST_DEVICE;
+ csa.callback = ahc_async;
+ csa.callback_arg = ahc->sim_b;
+ xpt_action((union ccb *)&csa);
+ }
+ return (1);
+}
+
+static void
+ahc_fetch_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ u_int8_t saved_tcl;
+
+ saved_tcl = ahc_inb(ahc, SAVED_TCL);
+ ahc_compile_devinfo(devinfo, (saved_tcl >> 4) & 0x0f,
+ (saved_tcl & SELBUSB) ? 'B': 'A');
+}
+
+static void
+ahc_compile_devinfo(struct ahc_devinfo *devinfo, u_int target, char channel)
+{
+ devinfo->target = target;
+ devinfo->target_offset = target;
+ devinfo->channel = channel;
+ if (channel == 'B')
+ devinfo->target_offset += 8;
+ devinfo->target_mask = (0x01 << devinfo->target_offset);
+}
+
+/*
+ * Catch an interrupt from the adapter
+ */
+void
+ahc_intr(void *arg)
+{
+ struct ahc_softc *ahc;
+ u_int8_t intstat;
+
+ ahc = (struct ahc_softc *)arg;
+
+ intstat = ahc_inb(ahc, INTSTAT);
+
+ /*
+ * Any interrupts to process?
+ */
+#if NPCI > 0
+ if ((intstat & INT_PEND) == 0) {
+ if ((ahc->chip & AHC_PCI) != 0
+ && (ahc->unsolicited_ints > 500)) {
+ if ((ahc_inb(ahc, ERROR) & PCIERRSTAT) != 0)
+ ahc_pci_intr(ahc);
+ ahc->unsolicited_ints = 0;
+ } else {
+ ahc->unsolicited_ints++;
+ }
+ return;
+ } else {
+ ahc->unsolicited_ints = 0;
+ }
+#else
+ if ((intstat & INT_PEND) == 0)
+ return;
+#endif
+
+ if (intstat & CMDCMPLT) {
+ struct scb *scb;
+ u_int8_t scb_index;
+
+ ahc_outb(ahc, CLRINT, CLRCMDINT);
+ while (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL) {
+ scb_index = ahc->qoutfifo[ahc->qoutfifonext];
+ ahc->qoutfifo[ahc->qoutfifonext++] = SCB_LIST_NULL;
+
+ if (scb_index == TARGET_CMD_CMPLT) {
+ ahc_handle_target_cmd(ahc);
+ continue;
+ }
+
+ scb = ahc->scb_data->scbarray[scb_index];
+ if (!scb || !(scb->flags & SCB_ACTIVE)) {
+ printf("%s: WARNING no command for scb %d "
+ "(cmdcmplt)\nQOUTPOS = %d\n",
+ ahc_name(ahc), scb_index,
+ ahc->qoutfifonext - 1);
+ continue;
+ }
+
+ /*
+ * Save off the residual
+ * if there is one.
+ */
+ if (scb->hscb->residual_SG_count != 0)
+ ahc_calc_residual(scb);
+ ahc_done(ahc, scb);
+ }
+ }
+ if (intstat & BRKADRINT) {
+ /*
+ * We upset the sequencer :-(
+ * Lookup the error message
+ */
+ int i, error, num_errors;
+
+ error = ahc_inb(ahc, ERROR);
+ num_errors = sizeof(hard_error)/sizeof(hard_error[0]);
+ for (i = 0; error != 1 && i < num_errors; i++)
+ error >>= 1;
+ panic("%s: brkadrint, %s at seqaddr = 0x%x\n",
+ ahc_name(ahc), hard_error[i].errmesg,
+ ahc_inb(ahc, SEQADDR0) |
+ (ahc_inb(ahc, SEQADDR1) << 8));
+
+ /* Tell everyone that this HBA is no longer availible */
+ ahc_abort_scbs(ahc, ALL_TARGETS, ALL_CHANNELS,
+ ALL_LUNS, SCB_LIST_NULL, CAM_NO_HBA);
+ }
+ if (intstat & SEQINT)
+ ahc_handle_seqint(ahc, intstat);
+
+ if (intstat & SCSIINT)
+ ahc_handle_scsiint(ahc, intstat);
+}
+
+static void
+ahc_handle_target_cmd(struct ahc_softc *ahc)
+{
+ struct tmode_tstate *tstate;
+ struct tmode_lstate *lstate;
+ struct ccb_accept_tio *atio;
+ struct target_cmd *cmd;
+ u_int8_t *byte;
+ int initiator;
+ int target;
+ int lun;
+
+ cmd = &ahc->targetcmds[ahc->next_targetcmd];
+ ahc->next_targetcmd++;
+ if (ahc->next_targetcmd >= ahc->num_targetcmds)
+ ahc->next_targetcmd = 0;
+
+ initiator = cmd->icl >> 4;
+ target = cmd->targ_id;
+ lun = (cmd->identify & MSG_IDENTIFY_LUNMASK);
+
+ xpt_print_path(ahc->path);
+ printf("Received Target Command (%d:%d:%d)\n",
+ initiator, target, lun);
+ ahc_dump_targcmd(cmd);
+
+ byte = cmd->bytes;
+ tstate = ahc->enabled_targets[target];
+ lstate = NULL;
+ if (tstate != NULL && lun < 8)
+ lstate = tstate->enabled_luns[lun];
+
+ /*
+ * XXX Need to have a default TMODE devce that attaches to luns
+ * that wouldn't otherwise be enabled and returns the proper
+ * inquiry information. After all, we don't want to duplicate
+ * this code in each driver. For now, simply drop it on the
+ * floor.
+ */
+ if (lstate == NULL) {
+ printf("Incoming Command on disabled lun\n");
+ return;
+ }
+
+ atio = (struct ccb_accept_tio*)SLIST_FIRST(&lstate->accept_tios);
+ /* XXX Should reconnect and return BUSY status */
+ if (atio == NULL) {
+ printf("No ATIOs for incoming command\n");
+ return;
+ }
+
+ /*
+ * Package it up and send it off to
+ * whomever has this lun enabled.
+ */
+ atio->init_id = initiator;
+ if (byte[0] != 0xFF) {
+ /* Tag was included */
+ atio->tag_action = *byte++;
+ atio->tag_id = *byte++;
+ atio->ccb_h.flags = CAM_TAG_ACTION_VALID;
+ } else {
+ byte++;
+ atio->ccb_h.flags = 0;
+ }
+
+ /* Okay. Now determine the cdb size based on the command code */
+ switch (*byte >> CMD_GROUP_CODE_SHIFT) {
+ case 0:
+ atio->cdb_len = 6;
+ break;
+ case 1:
+ case 2:
+ atio->cdb_len = 10;
+ break;
+ case 4:
+ atio->cdb_len = 16;
+ break;
+ case 5:
+ atio->cdb_len = 12;
+ break;
+ case 3:
+ default:
+ /* Only copy the opcode. */
+ atio->cdb_len = 1;
+ printf("Reserved or VU command code type encountered\n");
+ break;
+ }
+ bcopy(byte, atio->cdb_io.cdb_bytes, atio->cdb_len);
+
+ SLIST_REMOVE_HEAD(&lstate->accept_tios, sim_links.sle);
+ atio->ccb_h.status |= CAM_CDB_RECVD;
+
+ if ((cmd->identify & MSG_IDENTIFY_DISCFLAG) == 0) {
+ /*
+ * We weren't allowed to disconnect.
+ * We're hanging on the bus until a
+ * continue target I/O comes in response
+ * to this accept tio.
+ */
+ xpt_print_path(atio->ccb_h.path);
+ printf("Incoming Command did not disconnect %x\n", lstate);
+ ahc->pending_device = lstate;
+ }
+ xpt_done((union ccb*)atio);
+}
+
+static void
+ahc_handle_seqint(struct ahc_softc *ahc, u_int8_t intstat)
+{
+ struct scb *scb;
+ struct ahc_devinfo devinfo;
+
+ ahc_fetch_devinfo(ahc, &devinfo);
+
+ /*
+ * Clear the upper byte that holds SEQINT status
+ * codes and clear the SEQINT bit. We will unpause
+ * the sequencer, if appropriate, after servicing
+ * the request.
+ */
+ ahc_outb(ahc, CLRINT, CLRSEQINT);
+ switch (intstat & SEQINT_MASK) {
+ case NO_MATCH:
+ {
+ /* Ensure we don't leave the selection hardware on */
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+ printf("%s:%c:%d: no active SCB for reconnecting "
+ "target - issuing BUS DEVICE RESET\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target);
+ printf("SAVED_TCL == 0x%x, ARG_1 == 0x%x, SEQ_FLAGS == 0x%x\n",
+ ahc_inb(ahc, SAVED_TCL), ahc_inb(ahc, ARG_1),
+ ahc_inb(ahc, SEQ_FLAGS));
+ break;
+ }
+ case SEND_REJECT:
+ {
+ u_int8_t rejbyte = ahc_inb(ahc, ACCUM);
+ printf("%s:%c:%d: Warning - unknown message received from "
+ "target (0x%x). Rejecting\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target, rejbyte);
+ break;
+ }
+ case NO_IDENT:
+ {
+ /*
+ * The reconnecting target either did not send an identify
+ * message, or did, but we didn't find and SCB to match and
+ * before it could respond to our ATN/abort, it hit a dataphase.
+ * The only safe thing to do is to blow it away with a bus
+ * reset.
+ */
+ int found;
+
+ printf("%s:%c:%d: Target did not send an IDENTIFY message. "
+ "LASTPHASE = 0x%x, SAVED_TCL == 0x%x\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target,
+ ahc_inb(ahc, LASTPHASE), ahc_inb(ahc, SAVED_TCL));
+ found = ahc_reset_channel(ahc, devinfo.channel,
+ /*initiate reset*/TRUE);
+ printf("%s: Issued Channel %c Bus Reset. "
+ "%d SCBs aborted\n", ahc_name(ahc), devinfo.channel,
+ found);
+ break;
+ }
+ case BAD_PHASE:
+ if (ahc_inb(ahc, LASTPHASE) == P_BUSFREE) {
+ printf("%s:%c:%d: Missed busfree.\n", ahc_name(ahc),
+ devinfo.channel, devinfo.target);
+ restart_sequencer(ahc);
+ return;
+ } else {
+ printf("%s:%c:%d: unknown scsi bus phase. Attempting "
+ "to continue\n", ahc_name(ahc), devinfo.channel,
+ devinfo.target);
+ }
+ break;
+ case EXTENDED_MSG:
+ {
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGIN;
+ ahc->msg_len = 0;
+ ahc->msg_index = 0;
+
+ /*
+ * To actually receive the message, simply turn on
+ * REQINIT interrupts and let our interrupt handler
+ * do the rest (REQINIT should already be true).
+ */
+ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENREQINIT);
+ ahc->flags |= AHC_HANDLING_REQINITS;
+ return;
+ }
+ case REJECT_MSG:
+ {
+ /*
+ * What we care about here is if we had an
+ * outstanding SDTR or WDTR message for this
+ * target. If we did, this is a signal that
+ * the target is refusing negotiation.
+ */
+
+ u_int8_t scb_index;
+ u_int8_t last_msg;
+
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc->scb_data->scbarray[scb_index];
+
+ last_msg = ahc_inb(ahc, LAST_MSG);
+
+ if ((last_msg == MSG_IDENTIFYFLAG)
+ && (scb->hscb->control & MSG_SIMPLE_Q_TAG) != 0) {
+ struct ccb_trans_settings neg;
+
+ printf("%s:%c:%d: refuses tagged commands. Performing "
+ "non-tagged I/O\n", ahc_name(ahc),
+ devinfo.channel, devinfo.target);
+
+ ahc->tagenable &= ~devinfo.target_mask;
+ neg.flags = 0;
+ neg.valid = CCB_TRANS_TQ_VALID;
+ xpt_setup_ccb(&neg.ccb_h, scb->ccb->ccb_h.path,
+ /*priority*/1);
+ xpt_async(AC_TRANSFER_NEG, scb->ccb->ccb_h.path, &neg);
+ /*
+ * Resend the identify for this CCB as the target
+ * may believe that the selection is invalid otherwise.
+ */
+ ahc_outb(ahc, SCB_CONTROL, ahc_inb(ahc, SCB_CONTROL)
+ & ~MSG_SIMPLE_Q_TAG);
+ scb->hscb->control &= ~MSG_SIMPLE_Q_TAG;
+ scb->ccb->ccb_h.flags &= ~CAM_TAG_ACTION_VALID;
+ ahc_outb(ahc, MSG_OUT, MSG_IDENTIFYFLAG);
+ ahc_outb(ahc, SCSISIGO, ahc_inb(ahc, SCSISIGO) | ATNO);
+
+ /*
+ * Requeue all tagged commands for this target
+ * currently in our posession so they can be
+ * converted to untagged commands.
+ */
+ ahc_search_qinfifo(ahc, SCB_TARGET(scb),
+ SCB_CHANNEL(scb),
+ SCB_LUN(scb),
+ /*tag*/SCB_LIST_NULL,
+ CAM_REQUEUE_REQ,
+ SEARCH_COMPLETE);
+ } else if ((last_msg == MSG_IDENTIFYFLAG
+ || last_msg == HOST_MSG)
+ && (scb->flags & SCB_MSGOUT_WDTR) != 0) {
+ struct ahc_target_tinfo *tinfo;
+
+ /* note 8bit xfers and clear flag */
+ printf("%s:%c:%d: refuses WIDE negotiation. Using "
+ "8bit transfers\n", ahc_name(ahc),
+ devinfo.channel, devinfo.target);
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ ahc->wdtrpending &= ~devinfo.target_mask;
+ ahc_set_width(ahc, &devinfo, scb->ccb->ccb_h.path,
+ MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL);
+ ahc_set_syncrate(ahc, &devinfo, scb->ccb->ccb_h.path,
+ /*syncrate*/NULL, /*period*/0,
+ /*offset*/0, AHC_TRANS_ACTIVE);
+ tinfo = &ahc->transinfo[devinfo.target_offset];
+ if (tinfo->goal.period) {
+ /* Start the sync negotiation */
+ ahc->sdtrpending |= devinfo.target_mask;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ ahc_outb(ahc, MSG_OUT, HOST_MSG);
+ ahc_outb(ahc, SCSISIGO,
+ ahc_inb(ahc, SCSISIGO) | ATNO);
+ }
+ } else if ((last_msg == MSG_IDENTIFYFLAG
+ || last_msg == HOST_MSG)
+ && (scb->flags & SCB_MSGOUT_SDTR) != 0) {
+
+ /* note asynch xfers and clear flag */
+ ahc_set_syncrate(ahc, &devinfo, scb->ccb->ccb_h.path,
+ /*syncrate*/NULL, /*period*/0,
+ /*offset*/0,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL);
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ ahc->sdtrpending &= ~devinfo.target_mask;
+ printf("%s:%c:%d: refuses synchronous negotiation. "
+ "Using asynchronous transfers\n",
+ ahc_name(ahc),
+ devinfo.channel, devinfo.target);
+ } else {
+ /*
+ * Otherwise, we ignore it.
+ */
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOWMISC)
+ printf("%s:%c:%d: Message reject -- ignored\n",
+ ahc_name(ahc), devinfo.channel,
+ devinfo.target);
+#endif
+ break;
+ }
+ break;
+ }
+ case BAD_STATUS:
+ {
+ u_int8_t scb_index;
+ struct hardware_scb *hscb;
+ struct ccb_scsiio *csio;
+ /*
+ * The sequencer will notify us when a command
+ * has an error that would be of interest to
+ * the kernel. This allows us to leave the sequencer
+ * running in the common case of command completes
+ * without error. The sequencer will already have
+ * dma'd the SCB back up to us, so we can reference
+ * the in kernel copy directly.
+ */
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc->scb_data->scbarray[scb_index];
+ hscb = scb->hscb;
+
+ /*
+ * Set the default return value to 0 (don't
+ * send sense). The sense code will change
+ * this if needed.
+ */
+ ahc_outb(ahc, RETURN_1, 0);
+ if (!(scb && (scb->flags & SCB_ACTIVE))) {
+ printf("%s:%c:%d: ahc_intr - referenced scb "
+ "not valid during seqint 0x%x scb(%d)\n",
+ ahc_name(ahc), devinfo.channel,
+ devinfo.target, intstat, scb_index);
+ goto unpause;
+ }
+
+ /* Don't want to clobber the original sense code */
+ if ((scb->flags & SCB_SENSE) != 0) {
+ /*
+ * Clear the SCB_SENSE Flag and have
+ * the sequencer do a normal command
+ * complete.
+ */
+ scb->flags &= ~SCB_SENSE;
+ ahc_set_ccb_status(scb->ccb, CAM_AUTOSENSE_FAIL);
+ break;
+ }
+ ahc_set_ccb_status(scb->ccb, CAM_SCSI_STATUS_ERROR);
+ csio = &scb->ccb->csio;
+ csio->scsi_status = hscb->status;
+ switch (hscb->status) {
+ case SCSI_STATUS_OK:
+ printf("%s: Interrupted for staus of 0???\n",
+ ahc_name(ahc));
+ break;
+ case SCSI_STATUS_CMD_TERMINATED:
+ case SCSI_STATUS_CHECK_COND:
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOWSENSE) {
+ xpt_print_path(csio->ccb_h.path);
+ printf("SCB %d: requests Check Status\n",
+ scb->hscb->tag);
+ }
+#endif
+
+ if ((csio->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) {
+ struct ahc_dma_seg *sg = scb->ahc_dma;
+ struct scsi_sense *sc =
+ (struct scsi_sense *)(&hscb->cmdstore);
+ struct ahc_target_tinfo *tinfo;
+
+ /*
+ * Save off the residual if there is one.
+ */
+ if (hscb->residual_SG_count != 0)
+ ahc_calc_residual(scb);
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOWSENSE) {
+ xpt_print_path(csio->ccb_h.path);
+ printf("Sending Sense\n");
+ }
+#endif
+ /*
+ * bzero from the sense data before having
+ * the drive fill it. The SCSI spec mandates
+ * that any untransfered data should be
+ * assumed to be zero.
+ */
+ bzero(&csio->sense_data,
+ sizeof(csio->sense_data));
+ sc->opcode = REQUEST_SENSE;
+ sc->byte2 = SCB_LUN(scb) << 5;
+ sc->unused[0] = 0;
+ sc->unused[1] = 0;
+ sc->length = csio->sense_len;
+ sc->control = 0;
+
+ sg->addr = vtophys(&csio->sense_data);
+ sg->len = csio->sense_len;
+
+ /*
+ * Would be nice to preserve DISCENB here,
+ * but due to the way we page SCBs, we can't.
+ */
+ hscb->control = 0;
+ /*
+ * This request sense could be because the
+ * the device lost power or in some other
+ * way has lost our transfer negotiations.
+ * Renegotiate if appropriate.
+ */
+ ahc_set_width(ahc, &devinfo,
+ scb->ccb->ccb_h.path,
+ MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_CUR);
+ ahc_set_syncrate(ahc, &devinfo,
+ scb->ccb->ccb_h.path,
+ /*syncrate*/NULL, /*period*/0,
+ /*offset*/0, AHC_TRANS_CUR);
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ tinfo = &ahc->transinfo[devinfo.target_offset];
+ if (tinfo->goal.width) {
+ ahc->wdtrpending |= devinfo.target_mask;
+ hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_MSGOUT_WDTR;
+ } else if (tinfo->goal.period) {
+ ahc->sdtrpending |= devinfo.target_mask;
+ hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ }
+ hscb->status = 0;
+ hscb->SG_count = 1;
+ hscb->SG_pointer = scb->ahc_dmaphys;
+ hscb->data = sg->addr;
+ hscb->datalen = sg->len;
+ hscb->cmdpointer = hscb->cmdstore_busaddr;
+ hscb->cmdlen = sizeof(*sc);
+ scb->sg_count = hscb->SG_count;
+ scb->flags |= SCB_SENSE;
+ /*
+ * Ensure the target is busy since this
+ * will be an untagged request.
+ */
+ ahc_busy_tcl(ahc, scb);
+ ahc_outb(ahc, RETURN_1, SEND_SENSE);
+
+ /*
+ * Ensure we have enough time to actually
+ * retrieve the sense.
+ */
+ untimeout(ahc_timeout, (caddr_t)scb,
+ scb->ccb->ccb_h.timeout_ch);
+ scb->ccb->ccb_h.timeout_ch =
+ timeout(ahc_timeout, (caddr_t)scb, 5 * hz);
+ /* Freeze the queue while the sense occurs. */
+ ahc_freeze_devq(ahc, scb->ccb->ccb_h.path);
+ ahc_freeze_ccb(scb->ccb);
+ break;
+ }
+ break;
+ case SCSI_STATUS_BUSY:
+ case SCSI_STATUS_QUEUE_FULL:
+ /*
+ * Requeue any transactions that haven't been
+ * sent yet.
+ */
+ ahc_freeze_devq(ahc, scb->ccb->ccb_h.path);
+ ahc_freeze_ccb(scb->ccb);
+ break;
+ }
+ break;
+ }
+ case TARGET_SYNC_CMD:
+ {
+ /*
+ * We've already processed the command. If the command
+ * is still pending, don't unpause the sequencer until
+ * it returns.
+ */
+ xpt_print_path(ahc->path);
+ printf("Saw a target sync cmd\n");
+ if (ahc->pending_device != NULL) {
+ printf(" Pending device too.\n");
+ return;
+ }
+ break;
+ }
+ case TARGET_MSG_HELP:
+ {
+ /*
+ * XXX Handle BDR, Abort, Abort Tag, and transfer negotiations.
+ */
+ restart_sequencer(ahc);
+ return;
+ }
+ case AWAITING_MSG:
+ {
+ int scb_index;
+
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc->scb_data->scbarray[scb_index];
+
+ /*
+ * To facilitate adding multiple messages together,
+ * each routine should increment the index and len
+ * variables instead of setting them explicitly.
+ */
+ ahc->msg_index = 0;
+ ahc->msg_len = 0;
+
+ /*
+ * This SCB had MK_MESSAGE set in its control byte or
+ * we have explicitly set HOST_MSG in MSG_OUT,
+ * informing the sequencer that we want to send a
+ * special message to this target.
+ */
+ if ((scb->flags & SCB_DEVICE_RESET) == 0
+ && ahc_inb(ahc, MSG_OUT) == MSG_IDENTIFYFLAG
+ && (scb->hscb->control & TAG_ENB) != 0) {
+ ahc->msg_buf[ahc->msg_index++] =
+ scb->ccb->csio.tag_action;
+ ahc->msg_buf[ahc->msg_index++] =
+ scb->hscb->tag;
+ ahc->msg_len += 2;
+ }
+
+ if (scb->flags & SCB_DEVICE_RESET) {
+ ahc->msg_buf[ahc->msg_index++] = MSG_BUS_DEV_RESET;
+ ahc->msg_len++;
+ xpt_print_path(scb->ccb->ccb_h.path);
+ printf("Bus Device Reset Message Sent\n");
+ } else if (scb->flags & SCB_ABORT) {
+ if ((scb->hscb->control & TAG_ENB) != 0)
+ ahc->msg_buf[ahc->msg_index++] = MSG_ABORT_TAG;
+ else
+ ahc->msg_buf[ahc->msg_index++] = MSG_ABORT;
+ ahc->msg_len++;
+ xpt_print_path(scb->ccb->ccb_h.path);
+ printf("Abort Message Sent\n");
+ } else if (scb->flags & SCB_MSGOUT_WDTR) {
+ struct ahc_target_tinfo *tinfo;
+
+ tinfo = &ahc->transinfo[devinfo.target_offset];
+ ahc_construct_wdtr(ahc, tinfo->goal.width);
+ } else if (scb->flags & SCB_MSGOUT_SDTR) {
+ struct ahc_target_tinfo *tinfo;
+ u_int period;
+ u_int maxsync;
+
+ /*
+ * Now that the target is actually selected, we
+ * can further refine our sync rate based on the
+ * output transceiver mode.
+ */
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ if ((ahc_inb(ahc, SBLKCTL) & ENAB40) != 0
+ && (ahc_inb(ahc, SSTAT2) & EXP_ACTIVE) == 0) {
+ maxsync = AHC_SYNCRATE_ULTRA2;
+ } else {
+ maxsync = AHC_SYNCRATE_ULTRA;
+ }
+ } else if ((ahc->features & AHC_ULTRA) != 0) {
+ maxsync = AHC_SYNCRATE_ULTRA;
+ } else {
+ maxsync = AHC_SYNCRATE_FAST;
+ }
+ tinfo = &ahc->transinfo[devinfo.target_offset];
+ period = tinfo->goal.period;
+ ahc_find_syncrate(ahc, &period, maxsync);
+ ahc_construct_sdtr(ahc, period, tinfo->goal.offset);
+ } else {
+ printf("ahc_intr: AWAITING_MSG for an SCB that "
+ "does not have a waiting message");
+ panic("SCB = %d, SCB Control = %x, MSG_OUT = %x "
+ "SCB flags = %x", scb_index, scb->hscb->control,
+ ahc_inb(ahc, MSG_OUT), scb->flags);
+ }
+
+ /*
+ * Record the fact that we attempted to send a message.
+ */
+ scb->flags |= SCB_MSGOUT_SENT;
+
+ /*
+ * To actually send the message, simply turn on
+ * REQINIT interrupts and let our interrupt handler
+ * do the rest (REQINIT should already be true).
+ */
+ ahc->msg_index = 0;
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ ahc->flags |= AHC_HANDLING_REQINITS;
+ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENREQINIT);
+
+ return;
+ }
+ case DATA_OVERRUN:
+ {
+ /*
+ * When the sequencer detects an overrun, it
+ * places the controller in "BITBUCKET" mode
+ * and allows the target to complete its transfer.
+ * Unfortunately, none of the counters get updated
+ * when the controller is in this mode, so we have
+ * no way of knowing how large the overrun was.
+ */
+ u_int8_t scbindex = ahc_inb(ahc, SCB_TAG);
+ u_int8_t lastphase = ahc_inb(ahc, LASTPHASE);
+ int i;
+
+ scb = ahc->scb_data->scbarray[scbindex];
+ xpt_print_path(scb->ccb->ccb_h.path);
+ printf("data overrun detected in %s phase."
+ " Tag == 0x%x.\n",
+ lastphase == P_DATAIN ? "Data-In" : "Data-Out",
+ scb->hscb->tag);
+ xpt_print_path(scb->ccb->ccb_h.path);
+ printf("%s seen Data Phase. Length = %d. NumSGs = %d.\n",
+ ahc_inb(ahc, SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
+ scb->ccb->csio.dxfer_len, scb->sg_count);
+ for (i = 0; i < scb->sg_count - 1; i++) {
+ printf("sg[%d] - Addr 0x%x : Length %d\n",
+ i,
+ scb->ahc_dma[i].addr,
+ scb->ahc_dma[i].len);
+ }
+ /*
+ * Set this and it will take affect when the
+ * target does a command complete.
+ */
+ ahc_freeze_devq(ahc, scb->ccb->ccb_h.path);
+ ahc_set_ccb_status(scb->ccb, CAM_DATA_RUN_ERR);
+ ahc_freeze_ccb(scb->ccb);
+ break;
+ }
+ case TRACEPOINT:
+ {
+ printf("TRACEPOINT: RETURN_2 = %d\n", ahc_inb(ahc, RETURN_2));
+#if 0
+ printf("SSTAT1 == 0x%x\n", ahc_inb(ahc, SSTAT1));
+ printf("SSTAT0 == 0x%x\n", ahc_inb(ahc, SSTAT0));
+ printf(", SCSISIGI == 0x%x\n", ahc_inb(ahc, SCSISIGI));
+ printf("TRACEPOINT: CCHCNT = %d, SG_COUNT = %d\n",
+ ahc_inb(ahc, CCHCNT), ahc_inb(ahc, SG_COUNT));
+ printf("TRACEPOINT: SCB_TAG = %d\n", ahc_inb(ahc, SCB_TAG));
+ printf("TRACEPOINT1: CCHADDR = %d, CCHCNT = %d, SCBPTR = %d\n",
+ ahc_inb(ahc, CCHADDR)
+ | (ahc_inb(ahc, CCHADDR+1) << 8)
+ | (ahc_inb(ahc, CCHADDR+2) << 16)
+ | (ahc_inb(ahc, CCHADDR+3) << 24),
+ ahc_inb(ahc, CCHCNT)
+ | (ahc_inb(ahc, CCHCNT+1) << 8)
+ | (ahc_inb(ahc, CCHCNT+2) << 16),
+ ahc_inb(ahc, SCBPTR));
+ printf("TRACEPOINT: WAITING_SCBH = %d\n", ahc_inb(ahc, WAITING_SCBH));
+ printf("TRACEPOINT: SCB_TAG = %d\n", ahc_inb(ahc, SCB_TAG));
+#endif
+ break;
+ }
+#if NOT_YET
+ /* XXX Fill these in later */
+ case MESG_BUFFER_BUSY:
+ break;
+ case MSGIN_PHASEMIS:
+ break;
+#endif
+ default:
+ printf("ahc_intr: seqint, "
+ "intstat == 0x%x, scsisigi = 0x%x\n",
+ intstat, ahc_inb(ahc, SCSISIGI));
+ break;
+ }
+
+unpause:
+ /*
+ * The sequencer is paused immediately on
+ * a SEQINT, so we should restart it when
+ * we're done.
+ */
+ unpause_sequencer(ahc, /*unpause_always*/TRUE);
+}
+
+static void
+ahc_handle_scsiint(struct ahc_softc *ahc, u_int8_t intstat)
+{
+ u_int8_t scb_index;
+ u_int8_t status;
+ struct scb *scb;
+
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ status = ahc_inb(ahc, SSTAT1);
+
+ if (scb_index < ahc->scb_data->numscbs) {
+ scb = ahc->scb_data->scbarray[scb_index];
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ scb = NULL;
+ } else
+ scb = NULL;
+
+ if ((status & SCSIRSTI) != 0) {
+ char channel;
+ channel = 'A';
+ if ((ahc->features & AHC_TWIN) != 0
+ && ((ahc_inb(ahc, SBLKCTL) & SELBUSB) != 0))
+ channel = 'B';
+ printf("%s: Someone reset channel %c\n",
+ ahc_name(ahc), channel);
+ ahc_reset_channel(ahc, channel, /* Initiate Reset */FALSE);
+ } else if ((status & BUSFREE) != 0 && (status & SELTO) == 0) {
+ /*
+ * First look at what phase we were last in.
+ * If its message out, chances are pretty good
+ * that the busfree was in response to one of
+ * our abort requests.
+ */
+ u_int8_t lastphase = ahc_inb(ahc, LASTPHASE);
+ u_int8_t saved_tcl = ahc_inb(ahc, SAVED_TCL);
+ u_int8_t target = (saved_tcl >> 4) & 0x0f;
+ char channel = saved_tcl & SELBUSB ? 'B': 'A';
+ int printerror = 1;
+
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+ if (lastphase == P_MESGOUT) {
+ u_int message;
+ u_int tag;
+
+ message = ahc_inb(ahc, SINDEX);
+
+ tag = SCB_LIST_NULL;
+ switch (message) {
+ case MSG_ABORT_TAG:
+ tag = scb->hscb->tag;
+ /* FALLTRHOUGH */
+ case MSG_ABORT:
+ xpt_print_path(scb->ccb->ccb_h.path);
+ printf("SCB %d - Abort %s Completed.\n",
+ tag == SCB_LIST_NULL ? "" : "Tag",
+ scb->hscb->tag);
+ if ((scb->flags & SCB_RECOVERY_SCB) != 0) {
+ ahc_set_ccb_status(scb->ccb,
+ CAM_REQ_ABORTED);
+ ahc_done(ahc, scb);
+ }
+ printerror = 0;
+ break;
+ case MSG_BUS_DEV_RESET:
+ ahc_handle_devreset(ahc, target, channel,
+ CAM_BDR_SENT, AC_SENT_BDR,
+ "Bus Device Reset Sent",
+ /*verbose_only*/FALSE);
+ printerror = 0;
+ break;
+ default:
+ break;
+ }
+ }
+ if (printerror != 0) {
+ if (scb != NULL) {
+ u_int8_t tag;
+
+ if ((scb->hscb->control & TAG_ENB) != 0)
+ tag = scb->hscb->tag;
+ else
+ tag = SCB_LIST_NULL;
+ ahc_abort_scbs(ahc, target, channel,
+ SCB_LUN(scb), tag,
+ CAM_UNEXP_BUSFREE);
+ } else {
+ ahc_abort_scbs(ahc, target, channel,
+ ALL_LUNS, SCB_LIST_NULL,
+ CAM_UNEXP_BUSFREE);
+ printf("%s: ", ahc_name(ahc));
+ }
+ printf("Unexpected busfree. LASTPHASE == 0x%x\n"
+ "SEQADDR == 0x%x\n",
+ lastphase, ahc_inb(ahc, SEQADDR0)
+ | (ahc_inb(ahc, SEQADDR1) << 8));
+ }
+ ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+ ahc_outb(ahc, SIMODE1,
+ ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENREQINIT));
+ ahc->flags &= ~AHC_HANDLING_REQINITS;
+ ahc_outb(ahc, CLRSINT1, CLRBUSFREE);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ restart_sequencer(ahc);
+ } else if ((status & SELTO) != 0) {
+ u_int8_t scbptr;
+ u_int8_t nextscb;
+
+ scbptr = ahc_inb(ahc, WAITING_SCBH);
+ ahc_outb(ahc, SCBPTR, scbptr);
+ scb_index = ahc_inb(ahc, SCB_TAG);
+
+ if (scb_index < ahc->scb_data->numscbs) {
+ scb = ahc->scb_data->scbarray[scb_index];
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ scb = NULL;
+ } else
+ scb = NULL;
+
+ if (scb == NULL) {
+ printf("%s: ahc_intr - referenced scb not "
+ "valid during SELTO scb(%d, %d)\n",
+ ahc_name(ahc), scbptr, scb_index);
+ } else {
+ /*
+ * Clear any pending messages for the timed out
+ * target.
+ */
+ ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+ ahc_handle_devreset(ahc, SCB_TARGET(scb),
+ SCB_CHANNEL(scb), CAM_SEL_TIMEOUT,
+ /*ac_code*/0, "Selection Timeout",
+ /*verbose_only*/TRUE);
+ }
+ /* Stop the selection */
+ ahc_outb(ahc, SCSISEQ, 0);
+
+ ahc_outb(ahc, SIMODE1,
+ ahc_inb(ahc, SIMODE1) & ~ENREQINIT);
+ ahc->flags &= ~AHC_HANDLING_REQINITS;
+
+ ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRBUSFREE);
+
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+
+ restart_sequencer(ahc);
+ } else if (scb == NULL) {
+ printf("%s: ahc_intr - referenced scb not "
+ "valid during scsiint 0x%x scb(%d)\n"
+ "SIMODE0 = 0x%x, SIMODE1 = 0x%x, SSTAT0 = 0x%x\n"
+ "SEQADDR = 0x%x\n", ahc_name(ahc),
+ status, scb_index, ahc_inb(ahc, SIMODE0),
+ ahc_inb(ahc, SIMODE1), ahc_inb(ahc, SSTAT0),
+ ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
+ ahc_outb(ahc, CLRSINT1, status);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ unpause_sequencer(ahc, /*unpause_always*/TRUE);
+ scb = NULL;
+ } else if ((status & SCSIPERR) != 0) {
+ /*
+ * Determine the bus phase and
+ * queue an appropriate message
+ */
+ char *phase;
+ u_int8_t mesg_out = MSG_NOOP;
+ u_int8_t lastphase = ahc_inb(ahc, LASTPHASE);
+
+ xpt_print_path(scb->ccb->ccb_h.path);
+
+ switch (lastphase) {
+ case P_DATAOUT:
+ phase = "Data-Out";
+ break;
+ case P_DATAIN:
+ phase = "Data-In";
+ mesg_out = MSG_INITIATOR_DET_ERR;
+ break;
+ case P_COMMAND:
+ phase = "Command";
+ break;
+ case P_MESGOUT:
+ phase = "Message-Out";
+ break;
+ case P_STATUS:
+ phase = "Status";
+ mesg_out = MSG_INITIATOR_DET_ERR;
+ break;
+ case P_MESGIN:
+ phase = "Message-In";
+ mesg_out = MSG_PARITY_ERROR;
+ break;
+ default:
+ phase = "unknown";
+ break;
+ }
+ printf("parity error during %s phase.\n", phase);
+
+ printf("SEQADDR == 0x%x\n", ahc_inb(ahc, SEQADDR0)
+ | (ahc_inb(ahc, SEQADDR1) << 8));
+
+ printf("SCSIRATE == 0x%x\n", ahc_inb(ahc, SCSIRATE));
+
+ /*
+ * We've set the hardware to assert ATN if we
+ * get a parity error on "in" phases, so all we
+ * need to do is stuff the message buffer with
+ * the appropriate message. "In" phases have set
+ * mesg_out to something other than MSG_NOP.
+ */
+ if (mesg_out != MSG_NOOP) {
+ ahc_outb(ahc, MSG_OUT, mesg_out);
+ }
+ ahc_outb(ahc, CLRSINT1, CLRSCSIPERR);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ unpause_sequencer(ahc, /*unpause_always*/TRUE);
+ } else if ((status & REQINIT) != 0
+ && (ahc->flags & AHC_HANDLING_REQINITS) != 0) {
+ ahc_handle_reqinit(ahc, scb);
+ } else {
+ xpt_print_path(scb->ccb->ccb_h.path);
+ printf("Unknown SCSIINT. Status = 0x%x\n", status);
+ ahc_outb(ahc, CLRSINT1, status);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ unpause_sequencer(ahc, /*unpause_always*/TRUE);
+ }
+}
+
+static void
+ahc_handle_reqinit(struct ahc_softc *ahc, struct scb *scb)
+{
+ struct ahc_devinfo devinfo;
+ u_int8_t simode1;
+
+ ahc_fetch_devinfo(ahc, &devinfo);
+
+ switch (ahc->msg_type) {
+ case MSG_TYPE_INITIATOR_MSGOUT:
+ {
+ int lastbyte;
+ int phasemis;
+ u_int8_t bus_phase;
+
+ if (ahc->msg_len == 0)
+ panic("REQINIT interrupt with no active message");
+
+ lastbyte = (ahc->msg_index == ahc->msg_len - 1);
+ bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+ phasemis = bus_phase != P_MESGOUT;
+
+ if (lastbyte || phasemis) {
+ /* Time to end our message session */
+ ahc->msg_len = 0;
+ ahc->msg_type = MSG_TYPE_NONE;
+ simode1 = ahc_inb(ahc, SIMODE1) & ~ENREQINIT;
+ ahc_outb(ahc, SIMODE1, simode1);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc->flags &= ~AHC_HANDLING_REQINITS;
+
+ if (phasemis == 0) {
+ ahc_outb(ahc, SINDEX,
+ ahc->msg_buf[ahc->msg_index]);
+ ahc_outb(ahc, RETURN_1, 0);
+ } else {
+ ahc_outb(ahc, RETURN_1, MSGOUT_PHASEMIS);
+ }
+
+ unpause_sequencer(ahc, /* unpause_always */TRUE);
+ } else {
+ /*
+ * Clear our interrupt status and present the byte
+ * on the bus, but don't unpause the sequencer.
+ */
+ ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc_outb(ahc, SCSIDATL, ahc->msg_buf[ahc->msg_index++]);
+ }
+ break;
+ }
+ case MSG_TYPE_INITIATOR_MSGIN:
+ {
+ int phasemis;
+ int done;
+
+ phasemis = (ahc_inb(ahc, SCSISIGI) & PHASE_MASK) != P_MESGIN;
+
+ if (phasemis == 0) {
+
+ ahc->msg_len++;
+ /* Pull the byte in without acking it */
+ ahc->msg_buf[ahc->msg_index] = ahc_inb(ahc, SCSIBUSL);
+ done = ahc_parse_msg(ahc, scb, &devinfo);
+ /* Ack the byte */
+ ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc_inb(ahc, SCSIDATL);
+ ahc->msg_index++;
+ }
+ if (phasemis || done) {
+ /* Time to end our message session */
+ ahc->msg_len = 0;
+ ahc->msg_type = MSG_TYPE_NONE;
+ simode1 = ahc_inb(ahc, SIMODE1) & ~ENREQINIT;
+ ahc->flags &= ~AHC_HANDLING_REQINITS;
+ ahc_outb(ahc, SIMODE1, simode1);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ unpause_sequencer(ahc, /* unpause_always */TRUE);
+ }
+ break;
+ }
+ default:
+ panic("Unknown REQINIT message type");
+ }
+}
+
+static int
+ahc_parse_msg(struct ahc_softc *ahc, struct scb *scb,
+ struct ahc_devinfo *devinfo)
+{
+ int reject;
+ int done;
+ u_int targ_scsirate;
+
+ done = FALSE;
+ reject = FALSE;
+ targ_scsirate = ahc->transinfo[devinfo->target_offset].scsirate;
+ /*
+ * Parse as much of the message as is availible,
+ * rejecting it if we don't support it. When
+ * the entire message is availible and has been
+ * handled, return TRUE indicating that we have
+ * parsed an entire message.
+ */
+ if (ahc->msg_buf[0] != MSG_EXTENDED) {
+ reject = TRUE;
+ }
+
+ /*
+ * Just accept the length byte outright and perform
+ * more checking once we know the message type.
+ */
+ if (!reject && (ahc->msg_len > 2)) {
+ switch (ahc->msg_buf[2]) {
+ case MSG_EXT_SDTR:
+ {
+ struct ahc_syncrate *syncrate;
+ u_int period;
+ u_int offset;
+ u_int saved_offset;
+ u_int maxsync;
+
+ if (ahc->msg_buf[1] != MSG_EXT_SDTR_LEN) {
+ reject = TRUE;
+ break;
+ }
+
+ /*
+ * Wait until we have both args before validating
+ * and acting on this message.
+ */
+ if (ahc->msg_len < (MSG_EXT_SDTR_LEN + /*preamble*/2))
+ break;
+
+ period = ahc->msg_buf[3];
+ saved_offset = offset = ahc->msg_buf[4];
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ if ((ahc_inb(ahc, SBLKCTL) & ENAB40) != 0
+ && (ahc_inb(ahc, SSTAT2) & EXP_ACTIVE) == 0) {
+ maxsync = AHC_SYNCRATE_ULTRA2;
+ } else {
+ maxsync = AHC_SYNCRATE_ULTRA;
+ }
+ } else if ((ahc->features & AHC_ULTRA) != 0) {
+ maxsync = AHC_SYNCRATE_ULTRA;
+ } else {
+ maxsync = AHC_SYNCRATE_FAST;
+ }
+ syncrate = ahc_find_syncrate(ahc, &period, maxsync);
+ ahc_validate_offset(ahc, syncrate, &offset,
+ targ_scsirate & WIDEXFER);
+ ahc_set_syncrate(ahc, devinfo, scb->ccb->ccb_h.path,
+ syncrate, period, offset,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL);
+
+ /*
+ * See if we initiated Sync Negotiation
+ * and didn't have to fall down to async
+ * transfers.
+ */
+ if ((scb->flags & (SCB_MSGOUT_SDTR|SCB_MSGOUT_SENT))
+ == (SCB_MSGOUT_SDTR|SCB_MSGOUT_SENT)) {
+ /* We started it */
+ if (saved_offset != offset) {
+ /* Went too low - force async */
+ reject = TRUE;
+ }
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ ahc->sdtrpending &= ~devinfo->target_mask;
+ } else {
+ /*
+ * Send our own SDTR in reply
+ */
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ ahc->sdtrpending |= devinfo->target_mask;
+ xpt_print_path(scb->ccb->ccb_h.path);
+ printf("Sending SDTR!!\n");
+ ahc_outb(ahc, MSG_OUT, HOST_MSG);
+ ahc_outb(ahc, SCSISIGO,
+ ahc_inb(ahc, SCSISIGO) | ATNO);
+ }
+ done = TRUE;
+ break;
+ }
+ case MSG_EXT_WDTR:
+ {
+ struct ccb_trans_settings neg;
+ u_int8_t bus_width;
+
+ if (ahc->msg_buf[1] != MSG_EXT_WDTR_LEN) {
+ reject = TRUE;
+ break;
+ }
+
+ /*
+ * Wait until we have our arg before validating
+ * and acting on this message.
+ */
+ if (ahc->msg_len < (MSG_EXT_WDTR_LEN + /*preamble*/2))
+ break;
+
+ bus_width = ahc->msg_buf[3];
+ if ((scb->flags & (SCB_MSGOUT_WDTR|SCB_MSGOUT_SENT))
+ == (SCB_MSGOUT_WDTR|SCB_MSGOUT_SENT)) {
+ /*
+ * Don't send a WDTR back to the
+ * target, since we asked first.
+ */
+ switch (bus_width){
+ default:
+ /*
+ * How can we do anything greater
+ * than 16bit transfers on a 16bit
+ * bus?
+ */
+ reject = TRUE;
+ printf("%s: target %d requested %dBit "
+ "transfers. Rejecting...\n",
+ ahc_name(ahc), devinfo->target,
+ 8 * (0x01 << bus_width));
+ /* FALLTHROUGH */
+ case MSG_EXT_WDTR_BUS_8_BIT:
+ bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ break;
+ case MSG_EXT_WDTR_BUS_16_BIT:
+ break;
+ }
+ scb->flags &= ~SCB_MSGOUT_WDTR;
+ ahc->wdtrpending &= ~devinfo->target_mask;
+ } else {
+ /*
+ * Send our own WDTR in reply
+ */
+ printf("Sending WDTR!\n");
+ scb->flags &= ~SCB_MSGOUT_BITS;
+ scb->flags |= SCB_MSGOUT_WDTR;
+ switch (bus_width) {
+ default:
+ if (ahc->features & AHC_WIDE) {
+ /* Respond Wide */
+ bus_width =
+ MSG_EXT_WDTR_BUS_16_BIT;
+ break;
+ }
+ /* FALLTHROUGH */
+ case MSG_EXT_WDTR_BUS_8_BIT:
+ bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ break;
+ }
+ ahc_outb(ahc, MSG_OUT, HOST_MSG);
+ ahc_outb(ahc, SCSISIGO,
+ ahc_inb(ahc, SCSISIGO) | ATNO);
+ ahc->wdtrpending |= devinfo->target_mask;
+ }
+ ahc_set_width(ahc, devinfo, scb->ccb->ccb_h.path,
+ bus_width,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL);
+
+ /* After a wide message, we are async */
+ ahc_set_syncrate(ahc, devinfo, scb->ccb->ccb_h.path,
+ /*syncrate*/NULL, /*period*/0,
+ /*offset*/0, AHC_TRANS_ACTIVE);
+ if ((ahc->wdtrpending & devinfo->target_mask) == 0
+ && (reject == 0)) {
+ struct ahc_target_tinfo *tinfo;
+
+ scb->flags &= ~SCB_MSGOUT_WDTR;
+ tinfo = &ahc->transinfo[devinfo->target_offset];
+ if (tinfo->goal.period) {
+ /* Start the sync negotiation */
+ ahc->sdtrpending |=
+ devinfo->target_mask;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ ahc_outb(ahc, MSG_OUT, HOST_MSG);
+ ahc_outb(ahc, SCSISIGO,
+ ahc_inb(ahc, SCSISIGO) | ATNO);
+ }
+ }
+ done = TRUE;
+ break;
+ }
+ default:
+ /* Unknown extended message. Reject it. */
+ reject = TRUE;
+ break;
+ }
+ }
+
+ if (reject) {
+ /*
+ * Assert attention and setup to
+ * reject the message.
+ */
+ ahc_outb(ahc, MSG_OUT, MSG_MESSAGE_REJECT);
+ ahc_outb(ahc, SCSISIGO, ahc_inb(ahc, SCSISIGO) | ATNO);
+ done = TRUE;
+ }
+ return (done);
+}
+
+static void
+ahc_handle_devreset(struct ahc_softc *ahc, int target, char channel,
+ cam_status status, ac_code acode, char *message,
+ int verbose_only)
+{
+ struct ahc_devinfo devinfo;
+ struct cam_path *path;
+ path_id_t path_id;
+ u_int16_t targ_mask;
+ u_int8_t targ_scsirate;
+ int scratch_offset = target;
+ int found;
+ int error;
+
+ ahc_compile_devinfo(&devinfo, target, channel);
+
+ if (channel == 'B')
+ path_id = cam_sim_path(ahc->sim_b);
+ else
+ path_id = cam_sim_path(ahc->sim);
+
+ error = xpt_create_path(&path, /*periph*/NULL, path_id, target,
+ CAM_LUN_WILDCARD);
+ /*
+ * Go back to async/narrow transfers and renegotiate.
+ */
+ if (error == CAM_REQ_CMP) {
+ ahc_set_width(ahc, &devinfo, path, MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_CUR);
+ ahc_set_syncrate(ahc, &devinfo, path, /*syncrate*/NULL,
+ /*period*/0, /*offset*/0, AHC_TRANS_CUR);
+ }
+ found = ahc_abort_scbs(ahc, target, channel, ALL_LUNS,
+ SCB_LIST_NULL, status);
+
+ if (error == CAM_REQ_CMP && acode != 0)
+ xpt_async(AC_SENT_BDR, path, NULL);
+
+ if (error == CAM_REQ_CMP)
+ xpt_free_path(path);
+
+ if (message != NULL
+ && (verbose_only == 0 || bootverbose != 0))
+ printf("%s: %s on %c:%d. %d SCBs aborted\n", ahc_name(ahc),
+ message, channel, target, found);
+}
+/*
+ * We have an scb which has been processed by the
+ * adaptor, now we look to see how the operation
+ * went.
+ */
+static void
+ahc_done(struct ahc_softc *ahc, struct scb *scb)
+{
+ union ccb *ccb;
+
+ CAM_DEBUG(scb->ccb->ccb_h.path, CAM_DEBUG_TRACE,
+ ("ahc_done - scb %d\n", scb->hscb->tag));
+
+ ccb = scb->ccb;
+ LIST_REMOVE(&ccb->ccb_h, sim_links.le);
+
+ untimeout(ahc_timeout, (caddr_t)scb, ccb->ccb_h.timeout_ch);
+
+ if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
+ bus_dmasync_op_t op;
+
+ if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
+ op = BUS_DMASYNC_POSTREAD;
+ else
+ op = BUS_DMASYNC_POSTWRITE;
+ bus_dmamap_sync(ahc->dmat, scb->dmamap, op);
+ bus_dmamap_unload(ahc->dmat, scb->dmamap);
+ }
+
+ /*
+ * Unbusy this target/channel/lun.
+ * XXX if we are holding two commands per lun,
+ * send the next command.
+ */
+ ahc_index_busy_tcl(ahc, scb->hscb->tcl, /*unbusy*/TRUE);
+
+ if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("CONT_TARGET_IO complete\n");
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ ahc_free_scb(ahc, scb);
+ xpt_done(ccb);
+ return;
+ }
+
+ /*
+ * If the recovery SCB completes, we have to be
+ * out of our timeout.
+ */
+ if ((scb->flags & SCB_RECOVERY_SCB) != 0) {
+
+ struct ccb_hdr *ccbh;
+
+ /*
+ * We were able to complete the command successfully,
+ * so reinstate the timeouts for all other pending
+ * commands.
+ */
+ ccbh = ahc->pending_ccbs.lh_first;
+ while (ccbh != NULL) {
+ struct scb *pending_scb;
+
+ pending_scb = (struct scb *)ccbh->ccb_scb_ptr;
+ ccbh->timeout_ch =
+ timeout(ahc_timeout, pending_scb,
+ (ccbh->timeout * hz)/1000);
+ ccbh = LIST_NEXT(ccbh, sim_links.le);
+ }
+
+ /*
+ * Ensure that we didn't put a second instance of this
+ * SCB into the QINFIFO.
+ */
+ ahc_search_qinfifo(ahc, SCB_TARGET(scb), SCB_CHANNEL(scb),
+ SCB_LUN(scb), scb->hscb->tag, /*status*/0,
+ SEARCH_REMOVE);
+ if (ahc_ccb_status(ccb) == CAM_BDR_SENT)
+ ahc_set_ccb_status(ccb, CAM_CMD_TIMEOUT);
+ xpt_print_path(ccb->ccb_h.path);
+ printf("no longer in timeout, status = %x\n",
+ ccb->ccb_h.status);
+ }
+
+ if ((scb->flags & (SCB_MSGOUT_WDTR|SCB_MSGOUT_SDTR)) != 0) {
+ /*
+ * Turn off the pending flags for any DTR messages
+ * regardless of whether they completed successfully
+ * or not. This ensures that we don't have lingering
+ * state after we abort an SCB.
+ */
+ u_int16_t mask;
+
+ mask = (0x01 << (SCB_TARGET(scb)
+ | (SCB_IS_SCSIBUS_B(scb) ? SELBUSB : 0)));
+ if (scb->flags & SCB_MSGOUT_WDTR)
+ ahc->wdtrpending &= ~mask;
+ if (scb->flags & SCB_MSGOUT_SDTR)
+ ahc->sdtrpending &= ~mask;
+ }
+ /* Don't clobber any existing error state */
+ if (ahc_ccb_status(ccb) == CAM_REQ_INPROG) {
+ ccb->ccb_h.status |= CAM_REQ_CMP;
+ } else if ((scb->flags & SCB_SENSE) != 0) {
+ /* We performed autosense retrieval */
+ scb->ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
+ }
+ ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
+ ahc_free_scb(ahc, scb);
+ xpt_done(ccb);
+}
+
+/*
+ * Determine the number of SCBs available on the controller
+ */
+int
+ahc_probe_scbs(struct ahc_softc *ahc) {
+ int i;
+
+ for (i = 0; i < AHC_SCB_MAX; i++) {
+ ahc_outb(ahc, SCBPTR, i);
+ ahc_outb(ahc, SCB_CONTROL, i);
+ if (ahc_inb(ahc, SCB_CONTROL) != i)
+ break;
+ ahc_outb(ahc, SCBPTR, 0);
+ if (ahc_inb(ahc, SCB_CONTROL) != 0)
+ break;
+ }
+
+ return (i);
+}
+
+/*
+ * Start the board, ready for normal operation
+ */
+int
+ahc_init(struct ahc_softc *ahc)
+{
+ int max_targ = 15;
+ int i;
+ int term;
+ u_int8_t scsi_conf, sxfrctl1;
+
+#ifdef AHC_PRINT_SRAM
+ printf("Scratch Ram:");
+ for (i = 0x20; i < 0x5f; i++) {
+ if (((i % 8) == 0) && (i != 0)) {
+ printf ("\n ");
+ }
+ printf (" 0x%x", ahc_inb(ahc, i));
+ }
+ if ((ahc->features & AHC_MORE_SRAM) != 0) {
+ for (i = 0x70; i < 0x7f; i++) {
+ if (((i % 8) == 0) && (i != 0)) {
+ printf ("\n ");
+ }
+ printf (" 0x%x", ahc_inb(ahc, i));
+ }
+ }
+ printf ("\n");
+#endif
+
+ /*
+ * Assume we have a board at this stage and it has been reset.
+ */
+ if ((ahc->flags & AHC_USEDEFAULTS) != 0) {
+ ahc->our_id = ahc->our_id_b = 7;
+ }
+
+ /*
+ * XXX Would be better to use a per device flag, but PCI and EISA
+ * devices don't have them yet.
+ */
+ if ((AHC_TMODE_ENABLE & (0x01 << ahc->unit)) != 0)
+ ahc->flags |= AHC_TARGETMODE;
+
+ if ((ahc->features & AHC_TWIN) != 0) {
+ printf("Twin Channel, A SCSI Id=%d, B SCSI Id=%d, ",
+ ahc->our_id, ahc->our_id_b);
+ } else {
+ if ((ahc->features & AHC_WIDE) != 0) {
+ printf("Wide ");
+ } else {
+ printf("Single ");
+ }
+ printf("Channel %c, SCSI Id=%d, ", ahc->channel, ahc->our_id);
+ }
+
+ ahc_outb(ahc, SEQ_FLAGS, 0);
+
+ /* Determine the number of SCBs and initialize them */
+
+ if (ahc->scb_data->maxhscbs == 0) {
+ ahc->scb_data->maxhscbs = ahc_probe_scbs(ahc);
+ /* SCB 0 heads the free list */
+ ahc_outb(ahc, FREE_SCBH, 0);
+ for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+ ahc_outb(ahc, SCBPTR, i);
+
+ /* Clear the control byte. */
+ ahc_outb(ahc, SCB_CONTROL, 0);
+
+ /* Set the next pointer */
+ ahc_outb(ahc, SCB_NEXT, i+1);
+
+ /* Make the tag number invalid */
+ ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
+ }
+
+ /* Make that the last SCB terminates the free list */
+ ahc_outb(ahc, SCBPTR, i-1);
+ ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
+
+ /* Ensure we clear the 0 SCB's control byte. */
+ ahc_outb(ahc, SCBPTR, 0);
+ ahc_outb(ahc, SCB_CONTROL, 0);
+
+ ahc->scb_data->maxhscbs = i;
+ }
+
+ if (ahc->scb_data->maxhscbs == 0)
+ panic("%s: No SCB space found", ahc_name(ahc));
+
+ if (ahc->scb_data->maxhscbs < AHC_SCB_MAX) {
+ ahc->flags |= AHC_PAGESCBS;
+ ahc->scb_data->maxscbs = AHC_SCB_MAX;
+ if ((ahc->flags & AHC_TARGETMODE) != 0) {
+ /* Steal one slot for TMODE commands */
+ ahc->scb_data->maxscbs--;
+ }
+ printf("%d/%d SCBs\n", ahc->scb_data->maxhscbs,
+ ahc->scb_data->maxscbs);
+ } else {
+ ahc->scb_data->maxscbs = ahc->scb_data->maxhscbs;
+ if ((ahc->flags & AHC_TARGETMODE) != 0) {
+ /* Steal one slot for TMODE commands */
+ ahc->scb_data->maxscbs--;
+ }
+ ahc->flags &= ~AHC_PAGESCBS;
+ printf("%d SCBs\n", ahc->scb_data->maxhscbs);
+ }
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOWMISC) {
+ printf("%s: hardware scb %d bytes; kernel scb %d bytes; "
+ "ahc_dma %d bytes\n",
+ ahc_name(ahc),
+ sizeof(struct hardware_scb),
+ sizeof(struct scb),
+ sizeof(struct ahc_dma_seg));
+ }
+#endif /* AHC_DEBUG */
+
+ /* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels*/
+ if (ahc->features & AHC_TWIN) {
+
+ /*
+ * The device is gated to channel B after a chip reset,
+ * so set those values first
+ */
+ term = (ahc->flags & AHC_TERM_ENB_B) != 0 ? STPWEN : 0;
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SCSIID_ULTRA2, ahc->our_id_b);
+ else
+ ahc_outb(ahc, SCSIID, ahc->our_id_b);
+ scsi_conf = ahc_inb(ahc, SCSICONF + 1);
+ sxfrctl1 = ahc_inb(ahc, SXFRCTL1);
+ ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
+ |term
+ |ENSTIMER|ACTNEGEN);
+ ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+ ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
+
+ if (scsi_conf & RESET_SCSI) {
+ /* Reset the bus */
+ if (bootverbose)
+ printf("%s: Resetting Channel B\n",
+ ahc_name(ahc));
+ ahc_reset_current_bus(ahc);
+ }
+
+ /* Select Channel A */
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
+ }
+ term = (ahc->flags & AHC_TERM_ENB_A) != 0 ? STPWEN : 0;
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SCSIID_ULTRA2, ahc->our_id);
+ else
+ ahc_outb(ahc, SCSIID, ahc->our_id);
+ scsi_conf = ahc_inb(ahc, SCSICONF);
+ sxfrctl1 = ahc_inb(ahc, SXFRCTL1);
+ ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
+ |term
+ |ENSTIMER|ACTNEGEN);
+ ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+ ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ /* Wait for our transceiver status to settle */
+ i = 1000000;
+ while (--i && ((ahc_inb(ahc, SBLKCTL) & (ENAB40|ENAB20)) == 0))
+ DELAY(100);
+
+ if (i == 0)
+ panic("%s: Transceiver state never settled\n",
+ ahc_name(ahc));
+ }
+
+ if (scsi_conf & RESET_SCSI) {
+ /* Reset the bus */
+ if (bootverbose)
+ printf("%s: Resetting Channel %c\n", ahc_name(ahc),
+ ahc->channel);
+
+ ahc_reset_current_bus(ahc);
+ }
+
+ /*
+ * Look at the information that board initialization or
+ * the board bios has left us. In the lower four bits of each
+ * target's scratch space any value other than 0 indicates
+ * that we should initiate synchronous transfers. If it's zero,
+ * the user or the BIOS has decided to disable synchronous
+ * negotiation to that target so we don't activate the needsdtr
+ * flag.
+ */
+ ahc->ultraenb = 0;
+ ahc->tagenable = ALL_TARGETS;
+
+ /* Grab the disconnection disable table and invert it for our needs */
+ if (ahc->flags & AHC_USEDEFAULTS) {
+ printf("%s: Host Adapter Bios disabled. Using default SCSI "
+ "device parameters\n", ahc_name(ahc));
+ ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B;
+ ahc->discenable = ALL_TARGETS;
+ if ((ahc->features & AHC_ULTRA) != 0)
+ ahc->ultraenb = 0xffff;
+ } else {
+ ahc->discenable = ~((ahc_inb(ahc, DISC_DSB + 1) << 8)
+ | ahc_inb(ahc, DISC_DSB));
+ if ((ahc->features & (AHC_ULTRA|AHC_ULTRA2)) != 0)
+ ahc->ultraenb = (ahc_inb(ahc, ULTRA_ENB + 1) << 8)
+ | ahc_inb(ahc, ULTRA_ENB);
+ }
+
+ if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0)
+ max_targ = 7;
+
+ for (i = 0; i <= max_targ; i++) {
+ struct ahc_target_tinfo *transinfo;
+
+ transinfo = &ahc->transinfo[i];
+ /* Default to async narrow across the board */
+ bzero(transinfo, sizeof(*transinfo));
+ if (ahc->flags & AHC_USEDEFAULTS) {
+ if ((ahc->features & AHC_WIDE) != 0)
+ transinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+
+ /*
+ * These will be truncated when we determine the
+ * connection type we have with the target.
+ */
+ transinfo->user.period = ahc_syncrates->period;
+ transinfo->user.offset = ~0;
+ } else {
+ u_int scsirate;
+ u_int16_t mask;
+
+ /* Take the settings leftover in scratch RAM. */
+ scsirate = ahc_inb(ahc, TARG_SCSIRATE + i);
+ mask = (0x01 << i);
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ u_int offset;
+
+ if ((scsirate & SOFS) == 0x0F) {
+ /*
+ * Haven't negotiated yet,
+ * so the format is different.
+ */
+ scsirate = (scsirate & SXFR) >> 4
+ | (ahc->ultraenb & mask)
+ ? 0x18 : 0x10
+ | (scsirate & WIDEXFER);
+ offset = MAX_OFFSET_ULTRA2;
+ } else
+ offset = ahc_inb(ahc, TARG_OFFSET + i);
+ ahc_find_period(ahc, scsirate,
+ AHC_SYNCRATE_ULTRA2);
+ if (offset == 0)
+ transinfo->user.period = 0;
+ else
+ transinfo->user.offset = ~0;
+ } else if ((scsirate & SOFS) != 0) {
+ transinfo->user.period =
+ ahc_find_period(ahc, scsirate,
+ (ahc->ultraenb & mask)
+ ? AHC_SYNCRATE_ULTRA
+ : AHC_SYNCRATE_FAST);
+ if ((scsirate & SOFS) != 0
+ && transinfo->user.period != 0) {
+ transinfo->user.offset = ~0;
+ }
+ }
+ if ((scsirate & WIDEXFER) != 0
+ && (ahc->features & AHC_WIDE) != 0) {
+ transinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+ }
+
+ }
+ }
+ ahc->sdtrpending = 0;
+ ahc->wdtrpending = 0;
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOWMISC)
+ printf("NEEDSDTR == 0x%x\nNEEDWDTR == 0x%x\n"
+ "DISCENABLE == 0x%x\nULTRAENB == 0x%x\n",
+ ahc->needsdtr_orig, ahc->needwdtr_orig,
+ ahc->discenable, ahc->ultraenb);
+#endif
+ /*
+ * Allocate enough "hardware scbs" to handle
+ * the maximum number of concurrent transactions
+ * we can have active. We have to use contigmalloc
+ * if this array crosses a page boundary since the
+ * sequencer depends on this array being physically
+ * contiguous.
+ */
+ if (ahc->scb_data->hscbs == NULL) {
+ size_t array_size;
+
+ array_size = ahc->scb_data->maxscbs*sizeof(struct hardware_scb);
+ if (array_size > PAGE_SIZE) {
+ ahc->scb_data->hscbs = (struct hardware_scb *)
+ contigmalloc(array_size, M_DEVBUF,
+ M_NOWAIT, 0ul, 0xffffffff,
+ PAGE_SIZE, 0x10000);
+ } else {
+ ahc->scb_data->hscbs = (struct hardware_scb *)
+ malloc(array_size, M_DEVBUF, M_NOWAIT);
+ }
+
+ if (ahc->scb_data->hscbs == NULL) {
+ printf("%s: unable to allocate hardware SCB array. "
+ "Failing attach\n", ahc_name(ahc));
+ return (-1);
+ }
+ /* At least the control byte of each hscb needs to be zeroed */
+ bzero(ahc->scb_data->hscbs, array_size);
+ }
+
+ if ((ahc->flags & AHC_TARGETMODE) != 0) {
+ size_t array_size;
+
+ ahc->num_targetcmds = 32;
+ array_size = ahc->num_targetcmds * sizeof(struct target_cmd);
+ ahc->targetcmds = malloc(array_size, M_DEVBUF, M_NOWAIT);
+
+ if (ahc->targetcmds == NULL) {
+ printf("%s: unable to allocate targetcmd array. "
+ "Failing attach\n", ahc_name(ahc));
+ return (-1);
+ }
+
+ bzero(ahc->targetcmds, array_size);
+ ahc_outb(ahc, TMODE_CMDADDR_NEXT, 0);
+ }
+
+ /*
+ * Tell the sequencer where it can find the our arrays in memory.
+ */
+ {
+ u_int32_t physaddr;
+
+ /* Tell the sequencer where it can find the hscb array. */
+ physaddr = vtophys(ahc->scb_data->hscbs);
+ ahc_outb(ahc, HSCB_ADDR, physaddr & 0xFF);
+ ahc_outb(ahc, HSCB_ADDR + 1, (physaddr >> 8) & 0xFF);
+ ahc_outb(ahc, HSCB_ADDR + 2, (physaddr >> 16) & 0xFF);
+ ahc_outb(ahc, HSCB_ADDR + 3, (physaddr >> 24) & 0xFF);
+ ahc->hscb_busaddr = physaddr;
+
+ physaddr = vtophys(ahc->qoutfifo);
+ ahc_outb(ahc, SCBID_ADDR, physaddr & 0xFF);
+ ahc_outb(ahc, SCBID_ADDR + 1, (physaddr >> 8) & 0xFF);
+ ahc_outb(ahc, SCBID_ADDR + 2, (physaddr >> 16) & 0xFF);
+ ahc_outb(ahc, SCBID_ADDR + 3, (physaddr >> 24) & 0xFF);
+
+ if ((ahc->flags & AHC_TARGETMODE) != 0) {
+ physaddr = vtophys(ahc->targetcmds);
+ ahc_outb(ahc, TMODE_CMDADDR, physaddr & 0xFF);
+ ahc_outb(ahc, TMODE_CMDADDR + 1,
+ (physaddr >> 8) & 0xFF);
+ ahc_outb(ahc, TMODE_CMDADDR + 2,
+ (physaddr >> 16) & 0xFF);
+ ahc_outb(ahc, TMODE_CMDADDR + 3,
+ (physaddr >> 24) & 0xFF);
+
+ ahc_outb(ahc, CMDSIZE_TABLE, 5);
+ ahc_outb(ahc, CMDSIZE_TABLE + 1, 9);
+ ahc_outb(ahc, CMDSIZE_TABLE + 2, 9);
+ ahc_outb(ahc, CMDSIZE_TABLE + 3, 0);
+ ahc_outb(ahc, CMDSIZE_TABLE + 4, 15);
+ ahc_outb(ahc, CMDSIZE_TABLE + 5, 11);
+ ahc_outb(ahc, CMDSIZE_TABLE + 6, 0);
+ ahc_outb(ahc, CMDSIZE_TABLE + 7, 0);
+ }
+
+ /* There are no untagged SCBs active yet. */
+ for (i = 0; i < sizeof(ahc->untagged_scbs); i++) {
+ ahc->untagged_scbs[i] = SCB_LIST_NULL;
+ }
+ for (i = 0; i < sizeof(ahc->qoutfifo); i++) {
+ ahc->qoutfifo[i] = SCB_LIST_NULL;
+ }
+ }
+
+ /* Our Q FIFOs are empty. */
+ ahc_outb(ahc, KERNEL_QINPOS, 0);
+ ahc_outb(ahc, QINPOS, 0);
+ ahc_outb(ahc, QOUTPOS, 0);
+
+ /*
+ * Use the built in queue management registers
+ * if they are available.
+ */
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ ahc_outb(ahc, QOFF_CTLSTA, SCB_QSIZE_256);
+ ahc_outb(ahc, SDSCB_QOFF, 0);
+ ahc_outb(ahc, SNSCB_QOFF, 0);
+ ahc_outb(ahc, HNSCB_QOFF, 0);
+ }
+
+
+ /* We don't have any waiting selections */
+ ahc_outb(ahc, WAITING_SCBH, SCB_LIST_NULL);
+
+ /* Our disconnection list is empty too */
+ ahc_outb(ahc, DISCONNECTED_SCBH, SCB_LIST_NULL);
+
+ /* Message out buffer starts empty */
+ ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+
+ /*
+ * Load the Sequencer program and Enable the adapter
+ * in "fast" mode.
+ */
+ if (bootverbose)
+ printf("%s: Downloading Sequencer Program...",
+ ahc_name(ahc));
+
+ ahc_loadseq(ahc);
+
+ /* We have to wait until after any system dumps... */
+ at_shutdown(ahc_shutdown, ahc, SHUTDOWN_FINAL);
+
+ return (0);
+}
+
+static void
+ahcminphys(struct buf *bp)
+{
+/*
+ * Even though the card can transfer up to 16megs per command
+ * we are limited by the number of segments in the dma segment
+ * list that we can hold. The worst case is that all pages are
+ * discontinuous physically, hense the "page per segment" limit
+ * enforced here.
+ */
+ if (bp->b_bcount > ((AHC_NSEG - 1) * PAGE_SIZE)) {
+ bp->b_bcount = ((AHC_NSEG - 1) * PAGE_SIZE);
+ }
+}
+
+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)
+{
+ int our_id;
+
+ /*
+ * If we are not configured for target mode, someone
+ * is really confused to be sending this to us.
+ */
+ if ((ahc->flags & AHC_TARGETMODE) == 0)
+ return (CAM_REQ_INVALID);
+
+ /* Range check target and lun */
+ if (cam_sim_bus(sim) == 0)
+ our_id = ahc->our_id;
+ else
+ our_id = ahc->our_id_b;
+ if (ccb->ccb_h.target_id > ((ahc->features & AHC_WIDE) ? 15 : 7)
+ || ((ahc->features & AHC_MULTI_TID) == 0
+ && (ccb->ccb_h.target_id != our_id)))
+ return (CAM_TID_INVALID);
+
+ if (ccb->ccb_h.target_lun > 8)
+ return (CAM_LUN_INVALID);
+
+ *tstate = ahc->enabled_targets[ccb->ccb_h.target_id];
+ *lstate = NULL;
+ if (*tstate != NULL)
+ *lstate = (*tstate)->enabled_luns[ccb->ccb_h.target_lun];
+
+ if (notfound_failure != 0 && *lstate == NULL)
+ return (CAM_PATH_INVALID);
+
+ return (CAM_REQ_CMP);
+}
+
+static void
+ahc_action(struct cam_sim *sim, union ccb *ccb)
+{
+ struct ahc_softc *ahc;
+ struct tmode_lstate *lstate;
+ int target_id;
+ int s;
+
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("ahc_action\n"));
+
+ ahc = (struct ahc_softc *)cam_sim_softc(sim);
+
+ target_id = ccb->ccb_h.target_id;
+
+ switch (ccb->ccb_h.func_code) {
+ /* Common cases first */
+ case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */
+ case XPT_CONT_TARGET_IO:/* Continue Host Target I/O Connection*/
+ {
+ struct tmode_tstate *tstate;
+ cam_status status;
+
+ status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate,
+ &lstate, TRUE);
+
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ xpt_done(ccb);
+ break;
+ }
+ if (ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) {
+ SLIST_INSERT_HEAD(&lstate->accept_tios, &ccb->ccb_h,
+ sim_links.sle);
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+ break;
+ }
+
+ /*
+ * The target_id represents the target we attempt to
+ * select. In target mode, this is the initiator of
+ * the original command.
+ */
+ target_id = ccb->csio.init_id;
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Sending a continue TIO\n");
+ /* FALLTHROUGH */
+ }
+ case XPT_SCSI_IO: /* Execute the requested I/O operation */
+ case XPT_RESET_DEV: /* Bus Device Reset the specified SCSI device */
+ {
+ struct scb *scb;
+ struct hardware_scb *hscb;
+ struct ahc_target_tinfo *tinfo;
+ u_int16_t mask;
+
+ /*
+ * get an scb to use.
+ */
+ if ((scb = ahc_get_scb(ahc)) == NULL) {
+ int s;
+
+ s = splcam();
+ ahc->flags |= AHC_RESOURCE_SHORTAGE;
+ splx(s);
+ xpt_freeze_simq(ahc->sim, /*count*/1);
+ ahc_set_ccb_status(ccb, CAM_REQUEUE_REQ);
+ xpt_done(ccb);
+ return;
+ }
+
+ hscb = scb->hscb;
+
+ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE,
+ ("start scb(%p)\n", scb));
+ scb->ccb = ccb;
+ /*
+ * So we can find the SCB when an abort is requested
+ */
+ ccb->ccb_h.ccb_scb_ptr = scb;
+ ccb->ccb_h.ccb_ahc_ptr = ahc;
+
+ /*
+ * Put all the arguments for the xfer in the scb
+ */
+ hscb->tcl = ((target_id << 4) & 0xF0)
+ | (SIM_IS_SCSIBUS_B(ahc, sim) ? SELBUSB : 0)
+ | (ccb->ccb_h.target_lun & 0x07);
+
+ mask = SCB_TARGET_MASK(scb);
+ tinfo = &ahc->transinfo[SCB_TARGET_OFFSET(scb)];
+
+ hscb->scsirate = tinfo->scsirate;
+ hscb->scsioffset = tinfo->current.offset;
+ if ((ahc->ultraenb & mask) != 0)
+ hscb->control |= ULTRAENB;
+
+ if ((ahc->discenable & mask) != 0
+ && (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) == 0)
+ hscb->control |= DISCENB;
+
+ if (ccb->ccb_h.func_code == XPT_RESET_DEV) {
+ hscb->cmdpointer = NULL;
+ scb->flags |= SCB_DEVICE_RESET;
+ hscb->control |= MK_MESSAGE;
+ ahc_execute_scb(scb, NULL, 0, 0);
+ } else {
+ if (ccb->ccb_h.func_code == XPT_SCSI_IO) {
+ if (tinfo->current.width != tinfo->goal.width) {
+ if ((ahc->wdtrpending & mask) == 0) {
+ ahc->wdtrpending |= mask;
+ hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_MSGOUT_WDTR;
+ }
+ } else if ((tinfo->current.period
+ != tinfo->goal.period)
+ && (ahc->sdtrpending & mask) == 0) {
+ ahc->sdtrpending |= mask;
+ hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_MSGOUT_SDTR;
+ }
+ } else {
+ if (ahc->pending_device == lstate) {
+ scb->flags |= SCB_TARGET_IMMEDIATE;
+ ahc->pending_device = NULL;
+ }
+ hscb->control |= TARGET_SCB;
+ hscb->cmdpointer = IDENTIFY_SEEN;
+ if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) {
+ hscb->cmdpointer |= SPHASE_PENDING;
+ hscb->status = ccb->csio.scsi_status;
+ }
+
+ /* Overloaded with tag ID */
+ hscb->cmdlen = ccb->csio.tag_id;
+ /*
+ * Overloaded with our target ID to
+ * use for reselection.
+ */
+ hscb->next = ccb->ccb_h.target_id;
+ }
+ if (ccb->ccb_h.flags & CAM_TAG_ACTION_VALID)
+ hscb->control |= ccb->csio.tag_action;
+
+ ahc_setup_data(ahc, &ccb->csio, scb);
+ }
+ break;
+ }
+ case XPT_NOTIFY_ACK:
+ case XPT_IMMED_NOTIFY:
+ {
+ struct tmode_tstate *tstate;
+ struct tmode_lstate *lstate;
+ cam_status status;
+
+ status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate,
+ &lstate, TRUE);
+
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ xpt_done(ccb);
+ break;
+ }
+ if (ccb->ccb_h.func_code == XPT_NOTIFY_ACK) {
+ /* Clear notification state */
+ }
+ SLIST_INSERT_HEAD(&lstate->immed_notifies, &ccb->ccb_h,
+ sim_links.sle);
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+ break;
+ }
+ case XPT_EN_LUN: /* Enable LUN as a target */
+ {
+ struct tmode_tstate *tstate;
+ struct tmode_lstate *lstate;
+ struct ccb_en_lun *cel;
+ cam_status status;
+ int target;
+ int lun;
+
+ status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate, &lstate,
+ /* notfound_failure*/FALSE);
+
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ xpt_done(ccb);
+ break;
+ }
+
+ cel = &ccb->cel;
+ target = ccb->ccb_h.target_id;
+ lun = ccb->ccb_h.target_lun;
+ if (cel->enable != 0) {
+ /* Are we already enabled?? */
+ if (lstate != NULL) {
+ ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
+ xpt_done(ccb);
+ break;
+ }
+
+ if (cel->grp6_len != 0
+ || cel->grp7_len != 0) {
+ /*
+ * Don't (yet?) support vendor
+ * specific commands.
+ */
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ }
+
+ /*
+ * Seems to be okay.
+ * Setup our data structures.
+ */
+ if (tstate == NULL) {
+ tstate = malloc(sizeof(*tstate),
+ M_DEVBUF, M_NOWAIT);
+ if (tstate == NULL) {
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ xpt_done(ccb);
+ break;
+ }
+ bzero(tstate, sizeof(*tstate));
+ ahc->enabled_targets[target] = tstate;
+ }
+ lstate = malloc(sizeof(*lstate), M_DEVBUF, M_NOWAIT);
+ if (lstate == NULL) {
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ xpt_done(ccb);
+ break;
+ }
+ bzero(lstate, sizeof(*lstate));
+ SLIST_INIT(&lstate->accept_tios);
+ SLIST_INIT(&lstate->immed_notifies);
+ tstate->enabled_luns[lun] = lstate;
+ if ((ahc->features & AHC_MULTI_TID) != 0) {
+ u_int16_t targid_mask;
+
+ pause_sequencer(ahc);
+ targid_mask = ahc_inb(ahc, TARGID)
+ | (ahc_inb(ahc, TARGID + 1) << 8);
+
+ targid_mask |= (0x01 << target);
+ ahc_outb(ahc, TARGID, targid_mask);
+ ahc_outb(ahc, TARGID+1, (targid_mask >> 8));
+ unpause_sequencer(ahc, /*always?*/FALSE);
+ }
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Lun now enabled for target mode\n");
+ xpt_done(ccb);
+ break;
+ } else {
+ /* XXX Fully Implement Disable */
+ if (lstate == NULL) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ xpt_done(ccb);
+ break;
+ }
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ break;
+ }
+ case XPT_ABORT: /* Abort the specified CCB */
+ /* XXX Implement */
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ case XPT_SET_TRAN_SETTINGS:
+ {
+ struct ahc_devinfo devinfo;
+ struct ccb_trans_settings *cts;
+ struct ahc_target_tinfo *tinfo;
+ u_int update_type;
+ int s;
+
+ cts = &ccb->cts;
+ ahc_compile_devinfo(&devinfo, cts->ccb_h.target_id,
+ SIM_IS_SCSIBUS_B(ahc, sim) ? 'B' : 'A');
+ tinfo = &ahc->transinfo[devinfo.target_offset];
+ update_type = 0;
+ if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0)
+ update_type |= AHC_TRANS_GOAL;
+ if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0)
+ update_type |= AHC_TRANS_USER;
+
+ s = splcam();
+
+ if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) {
+ if ((cts->flags & CCB_TRANS_DISC_ENB) != 0)
+ ahc->discenable |= devinfo.target_mask;
+ else
+ ahc->discenable &= ~devinfo.target_mask;
+ }
+
+ if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) {
+ if ((cts->flags & CCB_TRANS_TAG_ENB) != 0)
+ ahc->tagenable |= devinfo.target_mask;
+ else
+ ahc->tagenable &= ~devinfo.target_mask;
+ }
+
+ if ((cts->valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) {
+ switch (cts->bus_width) {
+ case MSG_EXT_WDTR_BUS_16_BIT:
+ if ((ahc->features & AHC_WIDE) != 0)
+ break;
+ /* FALLTHROUGH to 8bit */
+ case MSG_EXT_WDTR_BUS_32_BIT:
+ case MSG_EXT_WDTR_BUS_8_BIT:
+ default:
+ cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ break;
+ }
+ if ((update_type & AHC_TRANS_GOAL) != 0)
+ tinfo->goal.width = cts->bus_width;
+ if ((update_type & AHC_TRANS_USER) != 0)
+ tinfo->user.width = cts->bus_width;
+ }
+
+ if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) {
+ struct ahc_syncrate *syncrate;
+ u_int maxsync;
+
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ maxsync = AHC_SYNCRATE_ULTRA2;
+ else if ((ahc->features & AHC_ULTRA) != 0)
+ maxsync = AHC_SYNCRATE_ULTRA;
+ else
+ maxsync = AHC_SYNCRATE_FAST;
+
+ if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) {
+ if (cts->sync_offset != 0)
+ cts->sync_offset = ~0;
+ } else {
+ cts->sync_offset = 0;
+ }
+
+ syncrate = ahc_find_syncrate(ahc, &cts->sync_period,
+ maxsync);
+ ahc_validate_offset(ahc, syncrate, &cts->sync_offset,
+ tinfo->goal.width);
+
+ /* We use a period of 0 to represent async */
+ if (cts->sync_offset == 0)
+ cts->sync_period = 0;
+
+ if ((update_type & AHC_TRANS_GOAL) != 0) {
+ tinfo->goal.period = cts->sync_period;
+ tinfo->goal.offset = cts->sync_offset;
+ }
+ if ((update_type & AHC_TRANS_USER) != 0) {
+ tinfo->user.period = cts->sync_period;
+ tinfo->user.offset = cts->sync_offset;
+ }
+ }
+ splx(s);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_GET_TRAN_SETTINGS:
+ /* Get default/user set transfer settings for the target */
+ {
+ struct ahc_devinfo devinfo;
+ struct ccb_trans_settings *cts;
+ struct ahc_target_tinfo *targ_info;
+ struct ahc_transinfo *tinfo;
+ int s;
+
+ cts = &ccb->cts;
+ ahc_compile_devinfo(&devinfo, cts->ccb_h.target_id,
+ SIM_IS_SCSIBUS_B(ahc, sim) ? 'B' : 'A');
+ targ_info = &ahc->transinfo[devinfo.target_offset];
+
+ if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0)
+ tinfo = &targ_info->current;
+ else
+ tinfo = &targ_info->user;
+
+ s = splcam();
+
+ cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB);
+ if ((ahc->discenable & devinfo.target_mask) != 0)
+ cts->flags |= CCB_TRANS_DISC_ENB;
+
+ if ((ahc->tagenable & devinfo.target_mask) != 0)
+ cts->flags |= CCB_TRANS_TAG_ENB;
+
+ cts->sync_period = tinfo->period;
+ cts->sync_offset = tinfo->offset;
+ cts->bus_width = tinfo->width;
+
+ splx(s);
+
+ cts->valid = CCB_TRANS_SYNC_RATE_VALID
+ | CCB_TRANS_SYNC_OFFSET_VALID
+ | CCB_TRANS_BUS_WIDTH_VALID
+ | CCB_TRANS_DISC_VALID
+ | CCB_TRANS_TQ_VALID;
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_CALC_GEOMETRY:
+ {
+ struct ccb_calc_geometry *ccg;
+ u_int32_t size_mb;
+ u_int32_t secs_per_cylinder;
+ int extended;
+
+ ccg = &ccb->ccg;
+ size_mb = ccg->volume_size
+ / ((1024L * 1024L) / ccg->block_size);
+ extended = SIM_IS_SCSIBUS_B(ahc, sim)
+ ? ahc->flags & AHC_EXTENDED_TRANS_B
+ : ahc->flags & AHC_EXTENDED_TRANS_A;
+
+ if (size_mb > 1024 && extended) {
+ ccg->heads = 255;
+ ccg->secs_per_track = 63;
+ } else {
+ ccg->heads = 64;
+ ccg->secs_per_track = 32;
+ }
+ secs_per_cylinder = ccg->heads * ccg->secs_per_track;
+ ccg->cylinders = ccg->volume_size / secs_per_cylinder;
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_RESET_BUS: /* Reset the specified SCSI bus */
+ {
+ struct cam_path *path;
+ char channel;
+ int found;
+
+ s = splcam();
+ if (SIM_IS_SCSIBUS_B(ahc, sim)) {
+ channel = 'B';
+ path = ahc->path_b;
+ } else {
+ channel = 'A';
+ path = ahc->path;
+ }
+ found = ahc_reset_channel(ahc, channel, /*initiate reset*/TRUE);
+ splx(s);
+ if (bootverbose) {
+ xpt_print_path(path);
+ printf("SCSI bus reset delivered. "
+ "%d SCBs aborted.\n", found);
+ }
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_TERM_IO: /* Terminate the I/O process */
+ /* XXX Implement */
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ case XPT_PATH_INQ: /* Path routing inquiry */
+ {
+ struct ccb_pathinq *cpi = &ccb->cpi;
+
+ cpi->version_num = 1; /* XXX??? */
+ cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE;
+ if ((ahc->features & AHC_WIDE) != 0)
+ cpi->hba_inquiry |= PI_WIDE_16;
+ if ((ahc->flags & AHC_TARGETMODE) != 0) {
+ cpi->target_sprt = PIT_PROCESSOR
+ | PIT_DISCONNECT
+ | PIT_TERM_IO;
+ } else {
+ cpi->target_sprt = 0;
+ }
+ cpi->hba_misc = 0;
+ cpi->hba_eng_cnt = 0;
+ cpi->max_target = (ahc->features & AHC_WIDE) ? 15 : 7;
+ cpi->max_lun = 7;
+ if (SIM_IS_SCSIBUS_B(ahc, sim))
+ cpi->initiator_id = ahc->our_id_b;
+ else
+ cpi->initiator_id = ahc->our_id;
+ cpi->bus_id = cam_sim_bus(sim);
+ strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
+ strncpy(cpi->hba_vid, "Adaptec", HBA_IDLEN);
+ strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
+ cpi->unit_number = cam_sim_unit(sim);
+ cpi->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+ break;
+ }
+ default:
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ }
+}
+
+static void
+ahc_async(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
+{
+ struct ahc_softc *ahc;
+ struct cam_sim *sim;
+
+ sim = (struct cam_sim *)callback_arg;
+ ahc = (struct ahc_softc *)cam_sim_softc(sim);
+ switch (code) {
+ case AC_LOST_DEVICE:
+ {
+ struct ahc_devinfo devinfo;
+
+ ahc_compile_devinfo(&devinfo, xpt_path_target_id(path),
+ SIM_IS_SCSIBUS_B(ahc, sim) ? 'B' : 'A');
+
+ /*
+ * Revert to async/narrow transfers
+ * for the next device.
+ */
+ pause_sequencer(ahc);
+ ahc_set_width(ahc, &devinfo, path, MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_GOAL|AHC_TRANS_CUR);
+ ahc_set_syncrate(ahc, &devinfo, path, /*syncrate*/NULL,
+ /*period*/0, /*offset*/0,
+ AHC_TRANS_GOAL|AHC_TRANS_CUR);
+ unpause_sequencer(ahc, /*unpause always*/FALSE);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void
+ahc_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments,
+ int error)
+{
+ struct scb *scb;
+ union ccb *ccb;
+ struct ahc_softc *ahc;
+ int s;
+
+ scb = (struct scb *)arg;
+ ccb = scb->ccb;
+ ahc = (struct ahc_softc *)ccb->ccb_h.ccb_ahc_ptr;
+
+ if (nsegments != 0) {
+ struct ahc_dma_seg *sg;
+ bus_dma_segment_t *end_seg;
+ bus_dmasync_op_t op;
+
+ end_seg = dm_segs + nsegments;
+
+ /* Copy the first SG into the data pointer area */
+ scb->hscb->SG_pointer = scb->ahc_dmaphys;
+ scb->hscb->data = dm_segs->ds_addr;
+ scb->hscb->datalen = dm_segs->ds_len;
+ dm_segs++;
+
+ /* Copy the remaining segments into our SG list */
+ sg = scb->ahc_dma;
+ while (dm_segs < end_seg) {
+ sg->addr = dm_segs->ds_addr;
+ sg->len = dm_segs->ds_len;
+ sg++;
+ dm_segs++;
+ }
+
+ if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
+ op = BUS_DMASYNC_PREREAD;
+ else
+ op = BUS_DMASYNC_PREWRITE;
+
+ bus_dmamap_sync(ahc->dmat, scb->dmamap, op);
+ } else {
+ scb->hscb->SG_pointer = 0;
+ scb->hscb->data = 0;
+ scb->hscb->datalen = 0;
+ }
+
+ scb->sg_count = scb->hscb->SG_count = nsegments;
+
+ s = splcam();
+
+ /*
+ * Last time we need to check if this SCB needs to
+ * be aborted.
+ */
+ if (ahc_ccb_status(ccb) != CAM_REQ_INPROG) {
+ if (nsegments != 0)
+ bus_dmamap_unload(ahc->dmat, scb->dmamap);
+ ahc_free_scb(ahc, scb);
+ xpt_done(ccb);
+ splx(s);
+ return;
+ }
+
+ /* Busy this tcl if we are untagged */
+ if ((scb->hscb->control & TAG_ENB) == 0)
+ ahc_busy_tcl(ahc, scb);
+
+ LIST_INSERT_HEAD(&ahc->pending_ccbs, &ccb->ccb_h,
+ sim_links.le);
+
+ scb->flags |= SCB_ACTIVE;
+ ccb->ccb_h.status |= CAM_SIM_QUEUED;
+
+ ccb->ccb_h.timeout_ch =
+ timeout(ahc_timeout, (caddr_t)scb,
+ (ccb->ccb_h.timeout * hz) / 1000);
+
+ if ((scb->flags & SCB_TARGET_IMMEDIATE) != 0) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Returning an immediate CTIO\n");
+ if ((ahc->flags & AHC_PAGESCBS) == 0)
+ ahc_outb(ahc, SCBPTR, scb->hscb->tag);
+ ahc_outb(ahc, SCB_TAG, scb->hscb->tag);
+ unpause_sequencer(ahc, /*unpause_always*/TRUE);
+ } else {
+
+ ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
+
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+ } else {
+ pause_sequencer(ahc);
+ ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+ unpause_sequencer(ahc, /*unpause_always*/FALSE);
+ }
+ }
+
+ splx(s);
+}
+
+static void
+ahc_poll(struct cam_sim *sim)
+{
+ ahc_intr(cam_sim_softc(sim));
+}
+
+static void
+ahc_setup_data(struct ahc_softc *ahc, struct ccb_scsiio *csio,
+ struct scb *scb)
+{
+ struct hardware_scb *hscb;
+ struct ccb_hdr *ccb_h;
+
+ hscb = scb->hscb;
+ ccb_h = &csio->ccb_h;
+
+ if (ccb_h->func_code == XPT_SCSI_IO) {
+ hscb->cmdlen = csio->cdb_len;
+ if ((ccb_h->flags & CAM_CDB_POINTER) != 0) {
+ if ((ccb_h->flags & CAM_CDB_PHYS) == 0)
+ if (hscb->cmdlen <= 16) {
+ memcpy(hscb->cmdstore,
+ csio->cdb_io.cdb_ptr,
+ hscb->cmdlen);
+ hscb->cmdpointer =
+ hscb->cmdstore_busaddr;
+ } else
+ hscb->cmdpointer =
+ vtophys(csio->cdb_io.cdb_ptr);
+ else
+ hscb->cmdpointer =
+ (u_int32_t)csio->cdb_io.cdb_ptr;
+ } else {
+ /*
+ * CCB CDB Data Storage area is only 16 bytes
+ * so no additional testing is required
+ */
+ memcpy(hscb->cmdstore, csio->cdb_io.cdb_bytes,
+ hscb->cmdlen);
+ hscb->cmdpointer = hscb->cmdstore_busaddr;
+ }
+ }
+
+ /* Only use S/G if there is a transfer */
+ if ((ccb_h->flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
+ if ((ccb_h->flags & CAM_SCATTER_VALID) == 0) {
+ /* We've been given a pointer to a single buffer */
+ if ((ccb_h->flags & CAM_DATA_PHYS) == 0) {
+ int s;
+ int error;
+
+ s = splsoftvm();
+ error = bus_dmamap_load(ahc->dmat,
+ scb->dmamap,
+ csio->data_ptr,
+ csio->dxfer_len,
+ ahc_execute_scb,
+ scb, /*flags*/0);
+ if (error == EINPROGRESS) {
+ /*
+ * So as to maintain ordering,
+ * freeze the controller queue
+ * until our mapping is
+ * returned.
+ */
+ xpt_freeze_simq(ahc->sim,
+ /*count*/1);
+ scb->ccb->ccb_h.status |=
+ CAM_RELEASE_SIMQ;
+ }
+ splx(s);
+ } else {
+ struct bus_dma_segment seg;
+
+ /* Pointer to physical buffer */
+ if (csio->dxfer_len > AHC_MAXTRANSFER_SIZE)
+ panic("ahc_setup_data - Transfer size "
+ "larger than can device max");
+
+ seg.ds_addr = (bus_addr_t)csio->data_ptr;
+ seg.ds_len = csio->dxfer_len;
+ ahc_execute_scb(scb, &seg, 1, 0);
+ }
+ } else {
+ struct bus_dma_segment *segs;
+
+ if ((ccb_h->flags & CAM_DATA_PHYS) != 0)
+ panic("ahc_setup_data - Physical segment "
+ "pointers unsupported");
+
+ if ((ccb_h->flags & CAM_SG_LIST_PHYS) == 0)
+ panic("ahc_setup_data - Virtual segment "
+ "addresses unsupported");
+
+ /* Just use the segments provided */
+ segs = (struct bus_dma_segment *)csio->data_ptr;
+ ahc_execute_scb(scb, segs, csio->sglist_cnt, 0);
+ }
+ if (ccb_h->func_code == XPT_CONT_TARGET_IO) {
+ hscb->cmdpointer |= DPHASE_PENDING;
+ if ((ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN)
+ hscb->cmdpointer |= (TARGET_DATA_IN << 8);
+ }
+ } else {
+ ahc_execute_scb(scb, NULL, 0, 0);
+ }
+}
+
+static void
+ahc_freeze_devq(struct ahc_softc *ahc, struct cam_path *path)
+{
+ int target;
+ char channel;
+ int lun;
+
+ target = xpt_path_target_id(path);
+ lun = xpt_path_lun_id(path);
+ channel = xpt_path_sim(path)->bus_id == 0 ? 'A' : 'B';
+
+ ahc_search_qinfifo(ahc, target, channel, lun,
+ /*tag*/SCB_LIST_NULL, CAM_REQUEUE_REQ,
+ SEARCH_COMPLETE);
+}
+
+/*
+ * An scb (and hence an scb entry on the board) is put onto the
+ * free list.
+ */
+static void
+ahc_free_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+ struct hardware_scb *hscb;
+ int opri;
+
+ hscb = scb->hscb;
+
+ opri = splcam();
+
+ if ((ahc->flags & AHC_RESOURCE_SHORTAGE) != 0
+ && (scb->ccb->ccb_h.status & CAM_RELEASE_SIMQ) == 0) {
+ scb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
+ ahc->flags &= ~AHC_RESOURCE_SHORTAGE;
+ }
+
+ /* Clean up for the next user */
+ scb->flags = SCB_FREE;
+ hscb->control = 0;
+ hscb->status = 0;
+
+ STAILQ_INSERT_HEAD(&ahc->scb_data->free_scbs, scb, links);
+ splx(opri);
+}
+
+/*
+ * Get a free scb, either one already assigned to a hardware slot
+ * on the adapter or one that will require an SCB to be paged out before
+ * use. If there are none, see if we can allocate a new SCB. Otherwise
+ * either return an error or sleep.
+ */
+static struct scb *
+ahc_get_scb(struct ahc_softc *ahc)
+{
+ struct scb *scbp;
+ int opri;
+
+ opri = splcam();
+ if ((scbp = STAILQ_FIRST(&ahc->scb_data->free_scbs))) {
+ STAILQ_REMOVE_HEAD(&ahc->scb_data->free_scbs, links);
+ } else if (ahc->scb_data->numscbs < ahc->scb_data->maxscbs) {
+ scbp = ahc_alloc_scb(ahc);
+ if (scbp == NULL)
+ printf("%s: Can't malloc SCB\n", ahc_name(ahc));
+ }
+
+ splx(opri);
+
+ return (scbp);
+}
+
+
+static struct scb *
+ahc_alloc_scb(struct ahc_softc *ahc)
+{
+ static struct ahc_dma_seg *next_sg_array = NULL;
+ static int sg_arrays_free = 0;
+ struct scb *newscb;
+ int error;
+
+ newscb = (struct scb *) malloc(sizeof(struct scb), M_DEVBUF, M_NOWAIT);
+ if (newscb != NULL) {
+ bzero(newscb, sizeof(struct scb));
+ error = bus_dmamap_create(ahc->dmat, /*flags*/0,
+ &newscb->dmamap);
+ if (error != 0)
+ printf("%s: Unable to allocate SCB dmamap - error %d\n",
+ ahc_name(ahc), error);
+
+ if (error == 0 && next_sg_array == NULL) {
+ size_t alloc_size = sizeof(struct ahc_dma_seg)
+ * AHC_NSEG;
+ sg_arrays_free = PAGE_SIZE / alloc_size;
+ alloc_size *= sg_arrays_free;
+ if (alloc_size == 0)
+ panic("%s: SG list doesn't fit in a page",
+ ahc_name(ahc));
+ next_sg_array = (struct ahc_dma_seg *)
+ malloc(alloc_size, M_DEVBUF, M_NOWAIT);
+ }
+ if (error == 0 && next_sg_array != NULL) {
+ struct hardware_scb *hscb;
+
+ newscb->ahc_dma = next_sg_array;
+ newscb->ahc_dmaphys = vtophys(next_sg_array);
+ sg_arrays_free--;
+ if (sg_arrays_free == 0)
+ next_sg_array = NULL;
+ else
+ next_sg_array = &next_sg_array[AHC_NSEG];
+ hscb = &ahc->scb_data->hscbs[ahc->scb_data->numscbs];
+ newscb->hscb = hscb;
+ hscb->control = 0;
+ hscb->status = 0;
+ hscb->tag = ahc->scb_data->numscbs;
+ hscb->residual_data_count[2] = 0;
+ hscb->residual_data_count[1] = 0;
+ hscb->residual_data_count[0] = 0;
+ hscb->residual_SG_count = 0;
+ hscb->cmdstore_busaddr =
+ ahc_hscb_busaddr(ahc, hscb->tag)
+ + offsetof(struct hardware_scb, cmdstore);
+ /*
+ * Place in the scbarray
+ * Never is removed.
+ */
+ ahc->scb_data->scbarray[hscb->tag] = newscb;
+ ahc->scb_data->numscbs++;
+ } else {
+ free(newscb, M_DEVBUF);
+ newscb = NULL;
+ }
+ }
+ return newscb;
+}
+
+static void
+ahc_loadseq(struct ahc_softc *ahc)
+{
+ struct patch *cur_patch;
+ int i;
+ int downloaded;
+ int skip_addr;
+ u_int8_t download_consts[4];
+
+ /* Setup downloadable constant table */
+ download_consts[TMODE_NUMCMDS] = ahc->num_targetcmds;
+
+ cur_patch = patches;
+ downloaded = 0;
+ skip_addr = 0;
+ ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+ ahc_outb(ahc, SEQADDR0, 0);
+ ahc_outb(ahc, SEQADDR1, 0);
+
+ for (i = 0; i < sizeof(seqprog)/4; i++) {
+ if (ahc_check_patch(ahc, &cur_patch, i, &skip_addr) == 0) {
+ /*
+ * Don't download this instruction as it
+ * is in a patch that was removed.
+ */
+ continue;
+ }
+ ahc_download_instr(ahc, i, download_consts);
+ downloaded++;
+ }
+ ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE);
+ restart_sequencer(ahc);
+
+ if (bootverbose)
+ printf(" %d instructions downloaded\n", downloaded);
+}
+
+static int
+ahc_check_patch(struct ahc_softc *ahc, struct patch **start_patch,
+ int start_instr, int *skip_addr)
+{
+ struct patch *cur_patch;
+ struct patch *last_patch;
+ int patch_index;
+ int num_patches;
+
+ num_patches = sizeof(patches)/sizeof(struct patch);
+ last_patch = &patches[num_patches];
+ cur_patch = *start_patch;
+
+ while (cur_patch < last_patch && start_instr == cur_patch->begin) {
+
+ if (cur_patch->patch_func(ahc) == 0) {
+ int skip;
+
+ /* Start rejecting code */
+ *skip_addr = start_instr + cur_patch->skip_instr;
+ cur_patch += cur_patch->skip_patch;
+ } else {
+ /* Accepted this patch. Advance to the next
+ * one and wait for our intruction pointer to
+ * hit this point.
+ */
+ cur_patch++;
+ }
+ }
+
+ *start_patch = cur_patch;
+ if (start_instr < *skip_addr)
+ /* Still skipping */
+ return (0);
+
+ return (1);
+}
+
+static void
+ahc_download_instr(struct ahc_softc *ahc, int instrptr, u_int8_t *dconsts)
+{
+ union ins_formats instr;
+ struct ins_format1 *fmt1_ins;
+ struct ins_format3 *fmt3_ins;
+ int fmt3;
+ u_int8_t opcode;
+
+ /* Structure copy */
+ instr = *(union ins_formats*)&seqprog[instrptr * 4];
+
+ fmt1_ins = &instr.format1;
+ fmt3_ins = NULL;
+
+ /* Pull the opcode */
+ opcode = instr.format1.opcode;
+ switch (opcode) {
+ case AIC_OP_JMP:
+ case AIC_OP_JC:
+ case AIC_OP_JNC:
+ case AIC_OP_CALL:
+ case AIC_OP_JNE:
+ case AIC_OP_JNZ:
+ case AIC_OP_JE:
+ case AIC_OP_JZ:
+ {
+ struct patch *cur_patch;
+ int address_offset;
+ u_int address;
+ int skip_addr;
+ int i;
+
+ fmt3_ins = &instr.format3;
+ address_offset = 0;
+ address = fmt3_ins->address;
+ cur_patch = patches;
+ skip_addr = 0;
+
+ for (i = 0; i < address;) {
+
+ ahc_check_patch(ahc, &cur_patch, i, &skip_addr);
+
+ if (skip_addr > i) {
+ int end_addr;
+
+ end_addr = MIN(address, skip_addr);
+ address_offset += end_addr - i;
+ i = skip_addr;
+ } else {
+ i++;
+ }
+ }
+ address -= address_offset;
+ fmt3_ins->address = address;
+ /* FALLTHROUGH */
+ }
+ case AIC_OP_OR:
+ case AIC_OP_AND:
+ case AIC_OP_XOR:
+ case AIC_OP_ADD:
+ case AIC_OP_ADC:
+ case AIC_OP_BMOV:
+ if (fmt1_ins->parity != 0) {
+ fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
+ }
+ fmt1_ins->parity = 0;
+ /* FALLTHROUGH */
+ case AIC_OP_ROL:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ int i, count;
+
+ /* Calculate odd parity for the instruction */
+ for (i = 0, count = 0; i < 31; i++) {
+ u_int32_t mask;
+
+ mask = 0x01 << i;
+ if ((instr.integer & mask) != 0)
+ count++;
+ }
+ if ((count & 0x01) == 0)
+ instr.format1.parity = 1;
+ } else {
+ /* Compress the instruction for older sequencers */
+ if (fmt3_ins != NULL) {
+ instr.integer =
+ fmt3_ins->immediate
+ | (fmt3_ins->source << 8)
+ | (fmt3_ins->address << 16)
+ | (fmt3_ins->opcode << 25);
+ } else {
+ instr.integer =
+ fmt1_ins->immediate
+ | (fmt1_ins->source << 8)
+ | (fmt1_ins->destination << 16)
+ | (fmt1_ins->ret << 24)
+ | (fmt1_ins->opcode << 25);
+ }
+ }
+ ahc_outsb(ahc, SEQRAM, instr.bytes, 4);
+ break;
+ default:
+ panic("Unknown opcode encountered in seq program");
+ break;
+ }
+}
+
+static void
+ahc_set_recoveryscb(struct ahc_softc *ahc, struct scb *scb) {
+
+ if ((scb->flags & SCB_RECOVERY_SCB) == 0) {
+ struct ccb_hdr *ccbh;
+
+ scb->flags |= SCB_RECOVERY_SCB;
+
+ /*
+ * Take all queued, but not sent SCBs out of the equation.
+ * Also ensure that no new CCBs are queued to us while we
+ * try to fix this problem.
+ */
+ if ((scb->ccb->ccb_h.status & CAM_RELEASE_SIMQ) == 0) {
+ xpt_freeze_simq(ahc->sim, /*count*/1);
+ scb->ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
+ }
+
+ /*
+ * Go through all of our pending SCBs and remove
+ * any scheduled timeouts for them. We will reschedule
+ * them after we've successfully fixed this problem.
+ */
+ ccbh = ahc->pending_ccbs.lh_first;
+ while (ccbh != NULL) {
+ struct scb *pending_scb;
+
+ pending_scb = (struct scb *)ccbh->ccb_scb_ptr;
+ untimeout(ahc_timeout, pending_scb, ccbh->timeout_ch);
+ ccbh = ccbh->sim_links.le.le_next;
+ }
+ }
+}
+
+static void
+ahc_timeout(void *arg)
+{
+ struct scb *scb;
+ struct ahc_softc *ahc;
+ int s, found;
+ u_int8_t bus_state;
+ int target;
+ int lun;
+ char channel;
+
+ scb = (struct scb *)arg;
+ ahc = (struct ahc_softc *)scb->ccb->ccb_h.ccb_ahc_ptr;
+
+ s = splcam();
+
+ /*
+ * Ensure that the card doesn't do anything
+ * behind our back. Also make sure that we
+ * didn't "just" miss an interrupt that would
+ * affect this timeout.
+ */
+ do {
+ ahc_intr(ahc);
+ pause_sequencer(ahc);
+ } while (ahc_inb(ahc, INTSTAT) & INT_PEND);
+
+ if ((scb->flags & SCB_ACTIVE) == 0) {
+ /* Previous timeout took care of me already */
+ printf("Timedout SCB handled by another timeout\n");
+ unpause_sequencer(ahc, /*unpause_always*/TRUE);
+ splx(s);
+ return;
+ }
+
+ target = SCB_TARGET(scb);
+ channel = SCB_CHANNEL(scb);
+ lun = SCB_LUN(scb);
+
+ xpt_print_path(scb->ccb->ccb_h.path);
+ printf("SCB 0x%x - timed out ", scb->hscb->tag);
+ /*
+ * Take a snapshot of the bus state and print out
+ * some information so we can track down driver bugs.
+ */
+ bus_state = ahc_inb(ahc, LASTPHASE);
+
+ switch(bus_state)
+ {
+ case P_DATAOUT:
+ printf("in dataout phase");
+ break;
+ case P_DATAIN:
+ printf("in datain phase");
+ break;
+ case P_COMMAND:
+ printf("in command phase");
+ break;
+ case P_MESGOUT:
+ printf("in message out phase");
+ break;
+ case P_STATUS:
+ printf("in status phase");
+ break;
+ case P_MESGIN:
+ printf("in message in phase");
+ break;
+ case P_BUSFREE:
+ printf("while idle, LASTPHASE == 0x%x",
+ bus_state);
+ break;
+ default:
+ /*
+ * We aren't in a valid phase, so assume we're
+ * idle.
+ */
+ printf("invalid phase, LASTPHASE == 0x%x",
+ bus_state);
+ bus_state = P_BUSFREE;
+ break;
+ }
+
+ printf(", SCSISIGI == 0x%x\n", ahc_inb(ahc, SCSISIGI));
+
+ printf("SEQADDR == 0x%x\n", ahc_inb(ahc, SEQADDR0)
+ | (ahc_inb(ahc, SEQADDR1) << 8));
+ printf("SSTAT1 == 0x%x\n", ahc_inb(ahc, SSTAT1));
+#if 0
+ printf("SCSIRATE == 0x%x\n", ahc_inb(ahc, SCSIRATE));
+ printf("CCSCBCTL == 0x%x\n", ahc_inb(ahc, CCSCBCTL));
+ printf("CCSCBCNT == 0x%x\n", ahc_inb(ahc, CCSCBCNT));
+ printf("DFCNTRL == 0x%x\n", ahc_inb(ahc, DFCNTRL));
+ printf("DFSTATUS == 0x%x\n", ahc_inb(ahc, DFSTATUS));
+ printf("CCHCNT == 0x%x\n", ahc_inb(ahc, CCHCNT));
+#endif
+ /* Decide our course of action */
+ if (scb->flags & SCB_DEVICE_RESET) {
+ /*
+ * Been down this road before.
+ * Do a full bus reset.
+ */
+bus_reset:
+ ahc_set_ccb_status(scb->ccb, CAM_CMD_TIMEOUT);
+ found = ahc_reset_channel(ahc, channel, /*Initiate Reset*/TRUE);
+ printf("%s: Issued Channel %c Bus Reset. "
+ "%d SCBs aborted\n", ahc_name(ahc), channel, found);
+ } else {
+ /*
+ * Send a Bus Device Reset message:
+ * The target that is holding up the bus may not
+ * be the same as the one that triggered this timeout
+ * (different commands have different timeout lengths).
+ * Our strategy here is to queue a BDR message
+ * to the timed out target if the bus is idle.
+ * Otherwise, if we have an active target we stuff the
+ * message buffer with a BDR message and assert ATN
+ * in the hopes that the target will let go of the bus
+ * and go to the mesgout phase. If this fails, we'll
+ * get another timeout 2 seconds later which will attempt
+ * a bus reset.
+ */
+ u_int8_t active_scb_index;
+
+ active_scb_index = ahc_inb(ahc, SCB_TAG);
+
+ if (bus_state != P_BUSFREE
+ && (active_scb_index < ahc->scb_data->numscbs)) {
+ struct scb *active_scb;
+
+ /*
+ * If the active SCB is not from our device,
+ * assume that another device is hogging the bus
+ * and wait for it's timeout to expire before
+ * taking addition action.
+ */
+ active_scb = ahc->scb_data->scbarray[active_scb_index];
+ if (active_scb->hscb->tcl != scb->hscb->tcl
+ && (scb->flags & SCB_OTHERTCL_TIMEOUT) == 0) {
+ struct ccb_hdr *ccbh;
+ u_int newtimeout;
+
+ scb->flags |= SCB_OTHERTCL_TIMEOUT;
+ newtimeout = MAX(active_scb->ccb->ccb_h.timeout,
+ scb->ccb->ccb_h.timeout);
+ ccbh = &scb->ccb->ccb_h;
+ scb->ccb->ccb_h.timeout_ch =
+ timeout(ahc_timeout, scb,
+ (newtimeout * hz) / 1000);
+ splx(s);
+ return;
+ }
+ ahc_set_recoveryscb(ahc, active_scb);
+ ahc_outb(ahc, MSG_OUT, MSG_BUS_DEV_RESET);
+ ahc_outb(ahc, SCSISIGO, bus_state|ATNO);
+ xpt_print_path(active_scb->ccb->ccb_h.path);
+ printf("BDR message in message buffer\n");
+ active_scb->flags |= SCB_DEVICE_RESET;
+ active_scb->ccb->ccb_h.timeout_ch =
+ timeout(ahc_timeout, (caddr_t)active_scb, 2 * hz);
+ unpause_sequencer(ahc, /*unpause_always*/TRUE);
+ } else {
+ int disconnected;
+
+ if (ahc_search_qinfifo(ahc, target, channel, lun,
+ scb->hscb->tag, /*status*/0,
+ SEARCH_COUNT) > 0) {
+ disconnected = FALSE;
+ } else {
+ disconnected = TRUE;
+ }
+
+ if (disconnected) {
+ u_int8_t hscb_index;
+
+ ahc_set_recoveryscb(ahc, scb);
+ /*
+ * Simply set the MK_MESSAGE control bit.
+ */
+ scb->hscb->control |= MK_MESSAGE;
+ scb->flags |= SCB_QUEUED_MSG
+ | SCB_DEVICE_RESET;
+ hscb_index = ahc_find_scb(ahc, scb);
+ if (hscb_index != SCB_LIST_NULL) {
+ u_int8_t scb_control;
+ u_int8_t saved_scbptr;
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ ahc_outb(ahc, SCBPTR, hscb_index);
+ scb_control = ahc_inb(ahc, SCB_CONTROL);
+ ahc_outb(ahc, SCB_CONTROL,
+ scb_control | MK_MESSAGE);
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+ }
+ /*
+ * Actually re-queue this SCB in case we can
+ * select the device before it reconnects.
+ * Clear out any entries in the QINFIFO first
+ * so we are the next SCB for this target
+ * to run.
+ */
+ ahc_search_qinfifo(ahc, SCB_TARGET(scb),
+ channel, SCB_LUN(scb),
+ SCB_LIST_NULL,
+ CAM_REQUEUE_REQ,
+ SEARCH_COMPLETE);
+ xpt_print_path(scb->ccb->ccb_h.path);
+ printf("Queuing a BDR SCB\n");
+ ahc->qinfifo[ahc->qinfifonext++] =
+ scb->hscb->tag;
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ ahc_outb(ahc, HNSCB_QOFF,
+ ahc->qinfifonext);
+ } else {
+ ahc_outb(ahc, KERNEL_QINPOS,
+ ahc->qinfifonext);
+ }
+ scb->ccb->ccb_h.timeout_ch =
+ timeout(ahc_timeout, (caddr_t)scb, 2 * hz);
+ unpause_sequencer(ahc, /*unpause_always*/FALSE);
+ } else {
+ /* Go "immediatly" to the bus reset */
+ /* This shouldn't happen */
+ ahc_set_recoveryscb(ahc, scb);
+ xpt_print_path(scb->ccb->ccb_h.path);
+ printf("SCB %d: Immediate reset. "
+ "Flags = 0x%x\n", scb->hscb->tag,
+ scb->flags);
+ goto bus_reset;
+ }
+ }
+ }
+ splx(s);
+}
+
+/*
+ * Look through the SCB array of the card and attempt to find the
+ * hardware SCB that corresponds to the passed in SCB. Return
+ * SCB_LIST_NULL if unsuccessful. This routine assumes that the
+ * card is already paused.
+ */
+static u_int8_t
+ahc_find_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+ u_int8_t saved_scbptr;
+ u_int8_t curindex;
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ for (curindex = 0; curindex < ahc->scb_data->maxhscbs; curindex++) {
+ ahc_outb(ahc, SCBPTR, curindex);
+ if (ahc_inb(ahc, SCB_TAG) == scb->hscb->tag)
+ break;
+ }
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+ if (curindex >= ahc->scb_data->maxhscbs)
+ curindex = SCB_LIST_NULL;
+
+ return curindex;
+}
+
+static int
+ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
+ int lun, u_int8_t tag, u_int32_t status,
+ ahc_search_action action)
+{
+ struct scb *scbp;
+ u_int8_t qinpos;
+ u_int8_t qintail;
+ int found;
+
+ qinpos = ahc_inb(ahc, QINPOS);
+ qintail = ahc->qinfifonext;
+ found = 0;
+
+ /*
+ * Start with an empty queue. Entries that are not chosen
+ * for removal will be re-added to the queue as we go.
+ */
+ ahc->qinfifonext = qinpos;
+
+ while (qinpos != qintail) {
+ scbp = ahc->scb_data->scbarray[ahc->qinfifo[qinpos]];
+ if (ahc_match_scb(scbp, target, channel, lun, tag)) {
+ /*
+ * We found an scb that needs to be removed.
+ */
+ switch (action) {
+ case SEARCH_COMPLETE:
+ if (ahc_ccb_status(scbp->ccb) == CAM_REQ_INPROG)
+ ahc_set_ccb_status(scbp->ccb, status);
+ ahc_freeze_ccb(scbp->ccb);
+ ahc_done(ahc, scbp);
+ break;
+ case SEARCH_COUNT:
+ ahc->qinfifo[ahc->qinfifonext++] =
+ scbp->hscb->tag;
+ break;
+ case SEARCH_REMOVE:
+ break;
+ }
+ found++;
+ } else {
+ ahc->qinfifo[ahc->qinfifonext++] = scbp->hscb->tag;
+ }
+ qinpos++;
+ }
+
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+ } else {
+ ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+ }
+
+ return (found);
+}
+
+
+/*
+ * Abort all SCBs that match the given description (target/channel/lun/tag),
+ * setting their status to the passed in status if the status has not already
+ * been modified from CAM_REQ_INPROG. This routine assumes that the sequencer
+ * is paused before it is called.
+ */
+static int
+ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel,
+ int lun, u_int8_t tag, u_int32_t status)
+{
+ struct scb *scbp;
+ u_int8_t active_scb;
+ int i;
+ int found;
+
+ /* restore this when we're done */
+ active_scb = ahc_inb(ahc, SCBPTR);
+
+ found = ahc_search_qinfifo(ahc, target, channel, lun, tag,
+ CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+ /*
+ * Search waiting for selection list.
+ */
+ {
+ u_int8_t next, prev;
+
+ next = ahc_inb(ahc, WAITING_SCBH); /* Start at head of list. */
+ prev = SCB_LIST_NULL;
+
+ while (next != SCB_LIST_NULL) {
+ u_int8_t scb_index;
+
+ ahc_outb(ahc, SCBPTR, next);
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ if (scb_index >= ahc->scb_data->numscbs) {
+ panic("Waiting List inconsistency. "
+ "SCB index == %d, yet numscbs == %d.",
+ scb_index, ahc->scb_data->numscbs);
+ }
+ scbp = ahc->scb_data->scbarray[scb_index];
+ if (ahc_match_scb(scbp, target, channel, lun, tag)) {
+
+ next = ahc_abort_wscb(ahc, next, prev);
+ } else {
+
+ prev = next;
+ next = ahc_inb(ahc, SCB_NEXT);
+ }
+ }
+ }
+ /*
+ * Go through the disconnected list and remove any entries we
+ * have queued for completion, 0'ing their control byte too.
+ */
+ {
+ u_int8_t next, prev;
+
+ next = ahc_inb(ahc, DISCONNECTED_SCBH);
+ prev = SCB_LIST_NULL;
+
+ while (next != SCB_LIST_NULL) {
+ u_int8_t scb_index;
+
+ ahc_outb(ahc, SCBPTR, next);
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ if (scb_index >= ahc->scb_data->numscbs) {
+ panic("Disconnected List inconsistency. "
+ "SCB index == %d, yet numscbs == %d.",
+ scb_index, ahc->scb_data->numscbs);
+ }
+ scbp = ahc->scb_data->scbarray[scb_index];
+ if (ahc_match_scb(scbp, target, channel, lun, tag)) {
+ next = ahc_rem_scb_from_disc_list(ahc, prev,
+ next);
+ } else {
+ prev = next;
+ next = ahc_inb(ahc, SCB_NEXT);
+ }
+ }
+ }
+ /*
+ * Go through the hardware SCB array looking for commands that
+ * were active but not on any list.
+ */
+ for(i = 0; i < ahc->scb_data->maxhscbs; i++) {
+ u_int8_t scbid;
+
+ ahc_outb(ahc, SCBPTR, i);
+ scbid = ahc_inb(ahc, SCB_TAG);
+ if (scbid < ahc->scb_data->numscbs) {
+ scbp = ahc->scb_data->scbarray[scbid];
+ if (ahc_match_scb(scbp, target, channel, lun, tag)) {
+ ahc_add_curscb_to_free_list(ahc);
+ }
+ }
+ }
+ /*
+ * Go through the pending CCB list and look for
+ * commands for this target that are still active.
+ * These are other tagged commands that were
+ * disconnected when the reset occured.
+ */
+ {
+ struct ccb_hdr *ccb_h;
+
+
+ ccb_h = ahc->pending_ccbs.lh_first;
+
+ while (ccb_h != NULL) {
+ scbp = (struct scb *)ccb_h->ccb_scb_ptr;
+ ccb_h = ccb_h->sim_links.le.le_next;
+ if (ahc_match_scb(scbp, target, channel, lun, tag)) {
+ if (ahc_ccb_status(scbp->ccb) == CAM_REQ_INPROG)
+ ahc_set_ccb_status(scbp->ccb, status);
+ ahc_freeze_ccb(scbp->ccb);
+ ahc_done(ahc, scbp);
+ found++;
+ }
+ }
+ }
+ ahc_outb(ahc, SCBPTR, active_scb);
+ return found;
+}
+
+static u_int8_t
+ahc_rem_scb_from_disc_list(struct ahc_softc *ahc, u_int8_t prev, u_int8_t scbptr)
+{
+ u_int8_t next;
+
+ ahc_outb(ahc, SCBPTR, scbptr);
+ next = ahc_inb(ahc, SCB_NEXT);
+
+ ahc_outb(ahc, SCB_CONTROL, 0);
+
+ ahc_add_curscb_to_free_list(ahc);
+
+ if (prev != SCB_LIST_NULL) {
+ ahc_outb(ahc, SCBPTR, prev);
+ ahc_outb(ahc, SCB_NEXT, next);
+ } else
+ ahc_outb(ahc, DISCONNECTED_SCBH, next);
+
+ return next;
+}
+
+static void
+ahc_add_curscb_to_free_list(struct ahc_softc *ahc)
+{
+ /* Invalidate the tag so that ahc_find_scb doesn't think it's active */
+ ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
+
+ ahc_outb(ahc, SCB_NEXT, ahc_inb(ahc, FREE_SCBH));
+ ahc_outb(ahc, FREE_SCBH, ahc_inb(ahc, SCBPTR));
+}
+
+/*
+ * Manipulate the waiting for selection list and return the
+ * scb that follows the one that we remove.
+ */
+static u_int8_t
+ahc_abort_wscb(struct ahc_softc *ahc, u_int8_t scbpos, u_int8_t prev)
+{
+ u_int8_t curscb, next;
+
+ /*
+ * Select the SCB we want to abort and
+ * pull the next pointer out of it.
+ */
+ curscb = ahc_inb(ahc, SCBPTR);
+ ahc_outb(ahc, SCBPTR, scbpos);
+ next = ahc_inb(ahc, SCB_NEXT);
+
+ /* Clear the necessary fields */
+ ahc_outb(ahc, SCB_CONTROL, 0);
+
+ ahc_add_curscb_to_free_list(ahc);
+
+ /* update the waiting list */
+ if (prev == SCB_LIST_NULL) {
+ /* First in the list */
+ ahc_outb(ahc, WAITING_SCBH, next);
+
+ /*
+ * Ensure we aren't attempting to perform
+ * selection for this entry.
+ */
+ ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+ } else {
+ /*
+ * Select the scb that pointed to us
+ * and update its next pointer.
+ */
+ ahc_outb(ahc, SCBPTR, prev);
+ ahc_outb(ahc, SCB_NEXT, next);
+ }
+
+ /*
+ * Point us back at the original scb position.
+ */
+ ahc_outb(ahc, SCBPTR, curscb);
+ return next;
+}
+
+static void
+ahc_clear_intstat(struct ahc_softc *ahc)
+{
+ /* Clear any interrupt conditions this may have caused */
+ ahc_outb(ahc, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO);
+ ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
+ |CLRBUSFREE|CLRSCSIPERR|CLRPHASECHG|
+ CLRREQINIT);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+}
+
+static void
+ahc_reset_current_bus(struct ahc_softc *ahc)
+{
+ u_int8_t scsiseq;
+
+ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENSCSIRST);
+ scsiseq = ahc_inb(ahc, SCSISEQ);
+ ahc_outb(ahc, SCSISEQ, scsiseq | SCSIRSTO);
+ DELAY(AHC_BUSRESET_DELAY);
+ /* Turn off the bus reset */
+ ahc_outb(ahc, SCSISEQ, scsiseq & ~SCSIRSTO);
+
+ ahc_clear_intstat(ahc);
+
+ /* Re-enable reset interrupts */
+ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENSCSIRST);
+}
+
+static int
+ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
+{
+ u_int target, max_target;
+ int found;
+ u_int8_t sblkctl;
+ char cur_channel;
+ struct cam_path *path;
+
+ pause_sequencer(ahc);
+ /*
+ * Clean up all the state information for the
+ * pending transactions on this bus.
+ */
+ found = ahc_abort_scbs(ahc, ALL_TARGETS, channel, ALL_LUNS,
+ SCB_LIST_NULL, CAM_SCSI_BUS_RESET);
+ path = channel == 'B' ? ahc->path_b : ahc->path;
+
+ /* Notify the XPT that a bus reset occurred */
+ xpt_async(AC_BUS_RESET, path, NULL);
+
+ /*
+ * Revert to async/narrow transfers until we renegotiate.
+ */
+ max_target = (ahc->features & AHC_WIDE) ? 15 : 7;
+ for (target = 0; target <= max_target; target++) {
+ struct ahc_devinfo devinfo;
+
+ ahc_compile_devinfo(&devinfo, target, channel);
+ ahc_set_width(ahc, &devinfo, path, MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_CUR);
+ ahc_set_syncrate(ahc, &devinfo, path, /*syncrate*/NULL,
+ /*period*/0, /*offset*/0, AHC_TRANS_CUR);
+ }
+
+ /*
+ * Reset the bus if we are initiating this reset and
+ * restart/unpause the sequencer
+ */
+ sblkctl = ahc_inb(ahc, SBLKCTL);
+ cur_channel = 'A';
+ if ((ahc->features & AHC_TWIN) != 0
+ && ((sblkctl & SELBUSB) != 0))
+ cur_channel = 'B';
+ if (cur_channel != channel) {
+ /* Case 1: Command for another bus is active
+ * Stealthily reset the other bus without
+ * upsetting the current bus.
+ */
+ ahc_outb(ahc, SBLKCTL, sblkctl ^ SELBUSB);
+ ahc_outb(ahc, SIMODE1,
+ ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENREQINIT));
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+ if (initiate_reset)
+ ahc_reset_current_bus(ahc);
+ ahc_clear_intstat(ahc);
+ ahc_outb(ahc, SBLKCTL, sblkctl);
+ unpause_sequencer(ahc, /*unpause_always*/FALSE);
+ } else {
+ /* Case 2: A command from this bus is active or we're idle */
+ ahc_outb(ahc, SIMODE1,
+ ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENREQINIT));
+ ahc->flags &= ~AHC_HANDLING_REQINITS;
+ ahc->msg_type = MSG_TYPE_NONE;
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+ if (initiate_reset)
+ ahc_reset_current_bus(ahc);
+ ahc_clear_intstat(ahc);
+ restart_sequencer(ahc);
+ }
+ return found;
+}
+
+static int
+ahc_match_scb (struct scb *scb, int target, char channel,
+ int lun, u_int8_t tag)
+{
+ int targ = SCB_TARGET(scb);
+ char chan = SCB_CHANNEL(scb);
+ int slun = SCB_LUN(scb);
+ int match;
+
+ match = ((chan == channel) || (channel == ALL_CHANNELS));
+ if (match != 0)
+ match = ((targ == target) || (target == ALL_TARGETS));
+ if (match != 0)
+ match = ((lun == slun) || (lun == ALL_LUNS));
+ if (match != 0)
+ match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
+
+ return match;
+}
+
+static void
+ahc_construct_sdtr(struct ahc_softc *ahc, u_int8_t period, u_int8_t offset)
+{
+ ahc->msg_buf[ahc->msg_index++] = MSG_EXTENDED;
+ ahc->msg_buf[ahc->msg_index++] = MSG_EXT_SDTR_LEN;
+ ahc->msg_buf[ahc->msg_index++] = MSG_EXT_SDTR;
+ ahc->msg_buf[ahc->msg_index++] = period;
+ ahc->msg_buf[ahc->msg_index++] = offset;
+ ahc->msg_len += 5;
+}
+
+static void
+ahc_construct_wdtr(struct ahc_softc *ahc, u_int8_t bus_width)
+{
+ ahc->msg_buf[ahc->msg_index++] = MSG_EXTENDED;
+ ahc->msg_buf[ahc->msg_index++] = MSG_EXT_WDTR_LEN;
+ ahc->msg_buf[ahc->msg_index++] = MSG_EXT_WDTR;
+ ahc->msg_buf[ahc->msg_index++] = bus_width;
+ ahc->msg_len += 4;
+}
+
+static void
+ahc_calc_residual(struct scb *scb)
+{
+ struct hardware_scb *hscb;
+
+ hscb = scb->hscb;
+
+ /*
+ * If the disconnected flag is still set, this is bogus
+ * residual information left over from a sequencer
+ * pagin/pageout, so ignore this case.
+ */
+ if ((scb->hscb->control & DISCONNECTED) == 0) {
+ u_int32_t resid;
+ int resid_sgs;
+ int sg;
+
+ /*
+ * Remainder of the SG where the transfer
+ * stopped.
+ */
+ resid = (hscb->residual_data_count[2] << 16)
+ | (hscb->residual_data_count[1] <<8)
+ | (hscb->residual_data_count[0]);
+
+ /*
+ * Add up the contents of all residual
+ * SG segments that are after the SG where
+ * the transfer stopped.
+ */
+ resid_sgs = scb->hscb->residual_SG_count - 1/*current*/;
+ sg = scb->sg_count - resid_sgs - 1/*first SG*/;
+ while (resid_sgs > 0) {
+
+ resid += scb->ahc_dma[sg].len;
+ sg++;
+ resid_sgs--;
+ }
+ if ((scb->flags & SCB_SENSE) == 0) {
+
+ scb->ccb->csio.resid = resid;
+ } else {
+
+ scb->ccb->csio.sense_resid = resid;
+ }
+ }
+
+ /*
+ * Clean out the residual information in this SCB for its
+ * next consumer.
+ */
+ hscb->residual_data_count[0] = 0;
+ hscb->residual_data_count[1] = 0;
+ hscb->residual_data_count[2] = 0;
+ hscb->residual_SG_count = 0;
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOWMISC) {
+ sc_print_addr(xs->sc_link);
+ printf("Handled Residual of %ld bytes\n" ,xs->resid);
+ }
+#endif
+}
+
+static void
+ahc_update_pending_syncrates(struct ahc_softc *ahc)
+{
+ /*
+ * Traverse the pending SCB list and ensure that all of the
+ * SCBs there have the proper settings.
+ */
+ struct ccb_hdr *ccbh;
+ int pending_ccb_count;
+ int i;
+ u_int saved_scbptr;
+
+ /*
+ * We were able to complete the command successfully,
+ * so reinstate the timeouts for all other pending
+ * commands.
+ */
+ ccbh = LIST_FIRST(&ahc->pending_ccbs);
+ pending_ccb_count = 0;
+ while (ccbh != NULL) {
+ struct scb *pending_scb;
+ struct hardware_scb *pending_hscb;
+ struct ahc_target_tinfo *tinfo;
+ struct ahc_devinfo devinfo;
+
+ pending_scb = (struct scb *)ccbh->ccb_scb_ptr;
+ pending_hscb = pending_scb->hscb;
+ ahc_compile_devinfo(&devinfo, SCB_TARGET(pending_scb),
+ SCB_CHANNEL(pending_scb));
+ tinfo = &ahc->transinfo[devinfo.target_offset];
+ pending_hscb->control &= ~ULTRAENB;
+ if ((ahc->ultraenb & devinfo.target_mask) != 0)
+ pending_hscb->control |= ULTRAENB;
+ pending_hscb->scsirate = tinfo->scsirate;
+ pending_hscb->scsioffset = tinfo->current.offset;
+ pending_ccb_count++;
+ ccbh = LIST_NEXT(ccbh, sim_links.le);
+ }
+
+ if (pending_ccb_count == 0)
+ return;
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ /* Ensure that the hscbs down on the card match the new information */
+ for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+ u_int scb_tag;
+
+ ahc_outb(ahc, SCBPTR, i);
+ scb_tag = ahc_inb(ahc, SCB_TAG);
+ if (scb_tag != SCB_LIST_NULL) {
+ struct scb *pending_scb;
+ struct hardware_scb *pending_hscb;
+ struct ahc_target_tinfo *tinfo;
+ struct ahc_devinfo devinfo;
+ u_int control;
+
+ pending_scb = ahc->scb_data->scbarray[scb_tag];
+ pending_hscb = pending_scb->hscb;
+ ahc_compile_devinfo(&devinfo, SCB_TARGET(pending_scb),
+ SCB_CHANNEL(pending_scb));
+ tinfo = &ahc->transinfo[devinfo.target_offset];
+ control = ahc_inb(ahc, SCB_CONTROL);
+ control &= ~ULTRAENB;
+ if ((ahc->ultraenb & devinfo.target_mask) != 0)
+ control |= ULTRAENB;
+ ahc_outb(ahc, SCB_CONTROL, control);
+ ahc_outb(ahc, SCB_SCSIRATE, tinfo->scsirate);
+ ahc_outb(ahc, SCB_SCSIOFFSET, tinfo->current.offset);
+ }
+ }
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+}
+
+static void
+ahc_dump_targcmd(struct target_cmd *cmd)
+{
+ u_int8_t *byte;
+ u_int8_t *last_byte;
+ int initiator;
+ int target;
+ int lun;
+ int i;
+
+ byte = &cmd->icl;
+ /* Debugging info for received commands */
+ last_byte = &cmd[1].icl;
+
+ i = 0;
+ while (byte < last_byte) {
+ if (i == 0)
+ printf("\t");
+ printf("%#x", *byte++);
+ i++;
+ if (i == 8) {
+ printf("\n");
+ i = 0;
+ } else {
+ printf(", ");
+ }
+ }
+}
+
+static void
+ahc_shutdown(int howto, void *arg)
+{
+ struct ahc_softc *ahc;
+ int i;
+
+ ahc = (struct ahc_softc *)arg;
+
+ ahc_reset(ahc);
+ ahc_outb(ahc, SCSISEQ, 0);
+ ahc_outb(ahc, SXFRCTL0, 0);
+ ahc_outb(ahc, DSPCISTATUS, 0);
+
+ for (i = TARG_SCSIRATE; i < HA_274_BIOSCTRL; i++)
+ ahc_outb(ahc, i, 0);
+}
diff --git a/sys/dev/aic7xxx/aic7xxx.h b/sys/dev/aic7xxx/aic7xxx.h
new file mode 100644
index 0000000..06f7d4a
--- /dev/null
+++ b/sys/dev/aic7xxx/aic7xxx.h
@@ -0,0 +1,524 @@
+/*
+ * Interface to the generic driver for the aic7xxx based adaptec
+ * SCSI controllers. This is used to implement product specific
+ * probe and attach routines.
+ *
+ * Copyright (c) 1994, 1995, 1996, 1997, 1998 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.h,v 1.40 1997/02/25 03:05:35 gibbs Exp $
+ */
+
+#ifndef _AIC7XXX_H_
+#define _AIC7XXX_H_
+
+#include "ahc.h" /* for NAHC from config */
+#include "opt_aic7xxx.h" /* for config options */
+
+#include <pci/pcivar.h> /* for pcici_t */
+
+#define AHC_MAXTRANSFER_SIZE 0x00ffffff /* limited by 24bit counter */
+#define AHC_NSEG 32 /* The number of dma segments supported.
+ * AHC_NSEG can be maxed out at 256 entries,
+ * but the kernel will never need to transfer
+ * such a large (1MB) request. To reduce the
+ * driver's memory consumption, we reduce the
+ * max to 32. 16 would work if all transfers
+ * are paged alined since the kernel will only
+ * generate at most a 64k transfer, but to
+ * handle non-page aligned transfers, you need
+ * 17, so we round to the next power of two
+ * to make allocating SG space easy and
+ * efficient.
+ */
+
+#define AHC_SCB_MAX 255 /*
+ * Up to 255 SCBs on some types of aic7xxx
+ * based boards. The aic7870 have 16 internal
+ * SCBs, but external SRAM bumps this to 255.
+ * The aic7770 family have only 4, and the
+ * aic7850 has only 3.
+ */
+
+
+#if defined(__FreeBSD__)
+extern u_long ahc_unit;
+#endif
+
+struct ahc_dma_seg {
+ u_int32_t addr;
+ u_int32_t len;
+};
+
+typedef enum {
+ AHC_NONE = 0x0000,
+ AHC_CHIPID_MASK = 0x00FF,
+ AHC_AIC7770 = 0x0001,
+ AHC_AIC7850 = 0x0002,
+ AHC_AIC7860 = 0x0003,
+ AHC_AIC7870 = 0x0004,
+ AHC_AIC7880 = 0x0005,
+ AHC_AIC7890 = 0x0006,
+ AHC_AIC7895 = 0x0007,
+ AHC_AIC7896 = 0x0008,
+ AHC_VL = 0x0100, /* Bus type VL */
+ AHC_EISA = 0x0200, /* Bus type EISA */
+ AHC_PCI = 0x0400, /* Bus type PCI */
+} ahc_chip;
+
+typedef enum {
+ AHC_FENONE = 0x0000,
+ AHC_ULTRA = 0x0001, /* Supports 20MHz Transfers */
+ AHC_ULTRA2 = 0x0002, /* Supports 40MHz Transfers */
+ AHC_WIDE = 0x0004, /* Wide Channel */
+ AHC_TWIN = 0x0008, /* Twin Channel */
+ AHC_MORE_SRAM = 0x0010, /* 80 bytes instead of 64 */
+ AHC_CMD_CHAN = 0x0020, /* Has a Command DMA Channel */
+ AHC_QUEUE_REGS = 0x0040, /* Has Queue management registers */
+ AHC_SG_PRELOAD = 0x0080, /* Can perform auto-SG preload */
+ AHC_SPIOCAP = 0x0100, /* Has a Serial Port I/O Cap Register */
+ AHC_MULTI_TID = 0x0200, /* Has bitmask of TIDs for select-in */
+ AHC_AIC7770_FE = AHC_FENONE,
+ AHC_AIC7850_FE = AHC_FENONE|AHC_SPIOCAP,
+ AHC_AIC7860_FE = AHC_ULTRA|AHC_SPIOCAP,
+ AHC_AIC7870_FE = AHC_FENONE,
+ AHC_AIC7880_FE = AHC_ULTRA,
+ AHC_AIC7890_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2|AHC_QUEUE_REGS|AHC_SG_PRELOAD|AHC_MULTI_TID,
+ AHC_AIC7895_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA,
+ AHC_AIC7896_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2|AHC_QUEUE_REGS|AHC_SG_PRELOAD|AHC_MULTI_TID,
+} ahc_feature;
+
+typedef enum {
+ AHC_FNONE = 0x000,
+ AHC_PAGESCBS = 0x001,/* Enable SCB paging */
+ AHC_CHANNEL_B_PRIMARY = 0x002,/*
+ * On twin channel adapters, probe
+ * channel B first since it is the
+ * primary bus.
+ */
+ AHC_USEDEFAULTS = 0x004,/*
+ * For cards without an seeprom
+ * or a BIOS to initialize the chip's
+ * SRAM, we use the default target
+ * settings.
+ */
+ AHC_INDIRECT_PAGING = 0x008,
+ AHC_SHARED_SRAM = 0x010,
+ AHC_LARGE_SEEPROM = 0x020,/* Uses C56_66 not C46 */
+ AHC_EXTENDED_TRANS_A = 0x100,
+ AHC_EXTENDED_TRANS_B = 0x200,
+ AHC_TERM_ENB_A = 0x400,
+ AHC_TERM_ENB_B = 0x800,
+ AHC_HANDLING_REQINITS = 0x1000,
+ AHC_TARGETMODE = 0x2000,/*
+ * Allow target operations on this
+ * controller.
+ */
+ AHC_NEWEEPROM_FMT = 0x4000,
+ AHC_RESOURCE_SHORTAGE = 0x8000
+} ahc_flag;
+
+typedef enum {
+ SCB_FREE = 0x0000,
+ SCB_OTHERTCL_TIMEOUT = 0x0002,/*
+ * Another device was active
+ * during the first timeout for
+ * this SCB so we gave ourselves
+ * an additional timeout period
+ * in case it was hogging the
+ * bus.
+ */
+ SCB_DEVICE_RESET = 0x0004,
+ SCB_SENSE = 0x0008,
+ SCB_RECOVERY_SCB = 0x0040,
+ SCB_MSGOUT_SENT = 0x0200,
+ SCB_MSGOUT_SDTR = 0x0400,
+ SCB_MSGOUT_WDTR = 0x0800,
+ SCB_MSGOUT_BITS = (SCB_MSGOUT_SDTR|SCB_MSGOUT_WDTR
+ |SCB_MSGOUT_SENT),
+ SCB_ABORT = 0x1000,
+ SCB_QUEUED_MSG = 0x2000,
+ SCB_ACTIVE = 0x4000,
+ SCB_TARGET_IMMEDIATE = 0x8000
+} scb_flag;
+
+/*
+ * The driver keeps up to MAX_SCB scb structures per card in memory. The SCB
+ * consists of a "hardware SCB" mirroring the fields availible on the card
+ * and additional information the kernel stores for each transaction.
+ */
+struct hardware_scb {
+/*0*/ u_int8_t control;
+/*1*/ u_int8_t tcl; /* 4/1/3 bits */
+/*2*/ u_int8_t status;
+/*3*/ u_int8_t SG_count;
+/*4*/ u_int32_t SG_pointer;
+/*8*/ u_int8_t residual_SG_count;
+/*9*/ u_int8_t residual_data_count[3];
+/*12*/ u_int32_t data;
+/*16*/ u_int32_t datalen; /* Really only three bytes, but its
+ * faster to treat it as a long on
+ * a quad boundary.
+ */
+/*20*/ u_int32_t cmdpointer;
+/*24*/ u_int8_t cmdlen;
+/*25*/ u_int8_t tag; /* Index into our kernel SCB array.
+ * Also used as the tag for tagged I/O
+ */
+/*26*/ u_int8_t next; /* Used for threading SCBs in the
+ * "Waiting for Selection" and
+ * "Disconnected SCB" lists down
+ * in the sequencer.
+ */
+/*27*/ u_int8_t scsirate; /* Value for SCSIRATE register */
+/*28*/ u_int8_t scsioffset; /* Value for SCSIOFFSET register */
+/*29*/ u_int8_t spare[3]; /*
+ * Spare space available on
+ * all controller types.
+ */
+/*32*/ u_int8_t cmdstore[16]; /*
+ * CDB storage for controllers
+ * supporting 64 byte SCBs.
+ */
+/*48*/ u_int32_t cmdstore_busaddr; /*
+ * Address of command store for
+ * 32byte SCB adapters
+ */
+/*48*/ u_int8_t spare_64[12]; /*
+ * Pad to 64 bytes.
+ */
+};
+
+struct scb {
+ struct hardware_scb *hscb;
+ STAILQ_ENTRY(scb) links; /* for chaining */
+ union ccb *ccb; /* the ccb for this cmd */
+ scb_flag flags;
+ bus_dmamap_t dmamap;
+ struct ahc_dma_seg *ahc_dma;/* Pointer to SG segments */
+ u_int32_t ahc_dmaphys;/* Phsical address of SG list */
+ u_int sg_count;/* How full ahc_dma_seg is */
+};
+
+struct scb_data {
+ struct hardware_scb *hscbs; /* Array of hardware SCBs */
+ struct scb *scbarray[AHC_SCB_MAX]; /* Array of kernel SCBs */
+ STAILQ_HEAD(, scb) free_scbs; /*
+ * Pool of SCBs ready to be assigned
+ * commands to execute.
+ */
+ u_int8_t numscbs;
+ u_int8_t maxhscbs; /* Number of SCBs on the card */
+ u_int8_t maxscbs; /*
+ * Max SCBs we allocate total including
+ * any that will force us to page SCBs
+ */
+};
+
+/*
+ * Connection desciptor for select-in requests in target mode.
+ * The first byte is the connecting target, followed by identify
+ * message and optional tag information, terminated by 0xFF. The
+ * remainder is the command to execute.
+ */
+struct target_cmd {
+ u_int8_t icl; /* Really only holds Initiator ID */
+ u_int8_t targ_id; /* Target ID we were selected at */
+ u_int8_t identify; /* Identify message */
+ u_int8_t bytes[29];
+};
+
+/*
+ * Per lun target mode state including accept TIO CCB
+ * and immediate notify CCB pools.
+ */
+struct tmode_lstate {
+ SLIST_HEAD(, ccb_hdr) accept_tios;
+ SLIST_HEAD(, ccb_hdr) immed_notifies;
+};
+
+/*
+ * Per target mode enabled target state. Esentially just an array of
+ * pointers to lun target state.
+ */
+struct tmode_tstate {
+ struct tmode_lstate* enabled_luns[8];
+};
+
+/*
+ * Define the format of the aic7XX0 SEEPROM registers (16 bits).
+ */
+
+struct seeprom_config {
+/*
+ * SCSI ID Configuration Flags
+ */
+ u_int16_t device_flags[16]; /* words 0-15 */
+#define CFXFER 0x0007 /* synchronous transfer rate */
+#define CFSYNCH 0x0008 /* enable synchronous transfer */
+#define CFDISC 0x0010 /* enable disconnection */
+#define CFWIDEB 0x0020 /* wide bus device */
+#define CFSYNCHISULTRA 0x0040 /* CFSYNCH is an ultra offset (2940AU)*/
+/* UNUSED 0x0080 */
+#define CFSTART 0x0100 /* send start unit SCSI command */
+#define CFINCBIOS 0x0200 /* include in BIOS scan */
+#define CFRNFOUND 0x0400 /* report even if not found */
+#define CFMULTILUN 0x0800 /* Probe multiple luns in BIOS scan */
+/* UNUSED 0xf000 */
+
+/*
+ * BIOS Control Bits
+ */
+ u_int16_t bios_control; /* word 16 */
+#define CFSUPREM 0x0001 /* support all removeable drives */
+#define CFSUPREMB 0x0002 /* support removeable drives for boot only */
+#define CFBIOSEN 0x0004 /* BIOS enabled */
+/* UNUSED 0x0008 */
+#define CFSM2DRV 0x0010 /* support more than two drives */
+#define CF284XEXTEND 0x0020 /* extended translation (284x cards) */
+/* UNUSED 0x0060 */
+#define CFEXTEND 0x0080 /* extended translation enabled */
+/* UNUSED 0xff00 */
+
+/*
+ * Host Adapter Control Bits
+ */
+ u_int16_t adapter_control; /* word 17 */
+#define CFAUTOTERM 0x0001 /* Perform Auto termination */
+#define CFULTRAEN 0x0002 /* Ultra SCSI speed enable */
+#define CF284XSELTO 0x0003 /* Selection timeout (284x cards) */
+#define CF284XFIFO 0x000C /* FIFO Threshold (284x cards) */
+#define CFSTERM 0x0004 /* SCSI low byte termination */
+#define CFWSTERM 0x0008 /* SCSI high byte termination */
+#define CFSPARITY 0x0010 /* SCSI parity */
+#define CF284XSTERM 0x0020 /* SCSI low byte term (284x cards) */
+#define CFRESETB 0x0040 /* reset SCSI bus at boot */
+#define CFCHNLBPRIMARY 0x0100 /* aic7895 probe B channel first */
+#define CFSEAUTOTERM 0x0400 /* aic7890 Perform SE Auto Termination*/
+#define CFLVDSTERM 0x0800 /* aic7890 LVD Termination */
+/* UNUSED 0xf080 */
+
+/*
+ * Bus Release, Host Adapter ID
+ */
+ u_int16_t brtime_id; /* word 18 */
+#define CFSCSIID 0x000f /* host adapter SCSI ID */
+/* UNUSED 0x00f0 */
+#define CFBRTIME 0xff00 /* bus release time */
+
+/*
+ * Maximum targets
+ */
+ u_int16_t max_targets; /* word 19 */
+#define CFMAXTARG 0x00ff /* maximum targets */
+/* UNUSED 0xff00 */
+ u_int16_t res_1[11]; /* words 20-30 */
+ u_int16_t checksum; /* word 31 */
+};
+
+#define AHC_TRANS_CUR 0x01 /* Modify current neogtiation status */
+#define AHC_TRANS_ACTIVE 0x03 /* Assume this is the active target */
+#define AHC_TRANS_GOAL 0x04 /* Modify negotiation goal */
+#define AHC_TRANS_USER 0x08 /* Modify user negotiation settings */
+
+struct ahc_transinfo {
+ u_int8_t width;
+ u_int8_t period;
+ u_int8_t offset;
+};
+
+struct ahc_target_tinfo {
+ u_int8_t scsirate;
+ struct ahc_transinfo current;
+ struct ahc_transinfo goal;
+ struct ahc_transinfo user;
+};
+
+struct ahc_syncrate {
+ int sxfr_ultra2;
+ int sxfr;
+ /* Rates in Ultra mode have bit 8 of sxfr set */
+#define ULTRA_SXFR 0x100
+ u_int8_t period; /* Period to send to SCSI target */
+ char *rate;
+};
+
+typedef enum {
+ MSG_TYPE_NONE = 0x00,
+ MSG_TYPE_INITIATOR_MSGOUT = 0x01,
+ MSG_TYPE_INITIATOR_MSGIN = 0x02
+} ahc_msg_type;
+
+struct ahc_softc {
+ bus_space_tag_t tag;
+ bus_space_handle_t bsh;
+ bus_dma_tag_t dmat;
+ struct scb_data *scb_data;
+
+ /*
+ * CCBs that have been send to the controller
+ */
+ LIST_HEAD(, ccb_hdr) pending_ccbs;
+
+ /*
+ * Target mode related state kept on a per enabled lun basis.
+ * Targets that are not enabled will have null entries.
+ */
+ struct tmode_tstate* enabled_targets[16];
+
+ /*
+ * Device instance currently on the bus awaiting a continue TIO
+ * for a command that was not given the disconnect priveledge.
+ */
+ struct tmode_lstate* pending_device;
+
+ /*
+ * Card characteristics
+ */
+ ahc_chip chip;
+ ahc_feature features;
+ ahc_flag flags;
+
+ /* Values to store in the SEQCTL register for pause and unpause */
+ u_int8_t unpause;
+ u_int8_t pause;
+
+ /* Command Queues */
+ u_int8_t qoutfifonext;
+ u_int8_t qinfifonext;
+ u_int8_t qoutfifo[256];
+ u_int8_t qinfifo[256];
+
+ /*
+ * 256 byte array storing the SCBID of outstanding
+ * untagged SCBs indexed by TCL.
+ */
+ u_int8_t untagged_scbs[256];
+
+ /*
+ * User/Current/Active Negotiation settings
+ */
+ struct ahc_target_tinfo transinfo[16];
+
+ /*
+ * Per target state bitmasks.
+ */
+ u_int16_t ultraenb; /* Using ultra sync rate */
+ u_int16_t sdtrpending; /* Pending SDTR request */
+ u_int16_t wdtrpending; /* Pending WDTR request */
+ u_int16_t discenable; /* Disconnection allowed */
+ u_int16_t tagenable; /* Tagged Queuing allowed */
+
+ /*
+ * Hooks into the XPT.
+ */
+ struct cam_sim *sim;
+ struct cam_sim *sim_b;
+ struct cam_path *path;
+ struct cam_path *path_b;
+
+ int unit;
+
+ /* Channel Names ('A', 'B', etc.) */
+ char channel;
+ char channel_b;
+
+ /* Initiator Bus ID */
+ u_int8_t our_id;
+ u_int8_t our_id_b;
+
+ /*
+ * PCI error detection and data for running the
+ * PCI error interrupt handler.
+ */
+ int unsolicited_ints;
+ pcici_t pci_config_id;
+
+ /* Hmmm. */
+ struct target_cmd *targetcmds;
+ int next_targetcmd;
+ int num_targetcmds;
+
+ /*
+ * Incoming and outgoing message handling.
+ */
+ ahc_msg_type msg_type;
+ u_int8_t msg_buf[8]; /* Message we are sending */
+ u_int msg_len; /* Length of message to send */
+ u_int msg_index; /* Current index in message */
+
+ /*
+ * "Bus" addresses of our data structures.
+ */
+ u_int32_t hscb_busaddr;
+};
+
+struct full_ahc_softc {
+ struct ahc_softc softc;
+ struct scb_data scb_data_storage;
+};
+
+/* #define AHC_DEBUG */
+#ifdef AHC_DEBUG
+/* Different debugging levels used when AHC_DEBUG is defined */
+#define AHC_SHOWMISC 0x0001
+#define AHC_SHOWCMDS 0x0002
+#define AHC_SHOWSCBS 0x0004
+#define AHC_SHOWABORTS 0x0008
+#define AHC_SHOWSENSE 0x0010
+#define AHC_SHOWSCBCNT 0x0020
+
+extern int ahc_debug; /* Initialized in i386/scsi/aic7xxx.c */
+#endif
+
+char *ahc_name(struct ahc_softc *ahc);
+
+struct ahc_softc *ahc_alloc(int unit, u_int32_t io_base,
+ vm_offset_t maddr, ahc_chip chip,
+ ahc_feature features, ahc_flag flags,
+ struct scb_data *scb_data);
+int ahc_reset(struct ahc_softc *ahc);
+void ahc_free(struct ahc_softc *);
+int ahc_probe_scbs(struct ahc_softc *);
+int ahc_init(struct ahc_softc *);
+int ahc_attach(struct ahc_softc *);
+void ahc_intr(void *arg);
+
+#define ahc_inb(ahc, port) \
+ bus_space_read_1((ahc)->tag, (ahc)->bsh, port)
+
+#define ahc_outb(ahc, port, value) \
+ bus_space_write_1((ahc)->tag, (ahc)->bsh, port, value)
+
+#define ahc_outsb(ahc, port, valp, count) \
+ bus_space_write_multi_1((ahc)->tag, (ahc)->bsh, port, valp, count)
+
+#endif /* _AIC7XXX_H_ */
diff --git a/sys/dev/aic7xxx/aic7xxx.reg b/sys/dev/aic7xxx/aic7xxx.reg
index b166c33..fff2b6f 100644
--- a/sys/dev/aic7xxx/aic7xxx.reg
+++ b/sys/dev/aic7xxx/aic7xxx.reg
@@ -1,7 +1,7 @@
/*
* Aic7xxx register and scratch ram definitions.
*
- * Copyright (c) 1994-1997 Justin Gibbs.
+ * Copyright (c) 1994-1998 Justin Gibbs.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -10,10 +10,7 @@
* 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
+ * 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
@@ -35,7 +32,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aic7xxx.reg,v 1.5 1997/08/13 17:02:24 gibbs Exp $
+ * $Id: aic7xxx.reg,v 1.4 1997/06/27 19:38:39 gibbs Exp $
*/
/*
@@ -164,6 +161,7 @@ register SCSIRATE {
access_mode RW
bit WIDEXFER 0x80 /* Wide transfer control */
mask SXFR 0x70 /* Sync transfer rate */
+ mask SXFR_ULTRA2 0x7f /* Sync transfer rate */
mask SOFS 0x0f /* Sync offset */
}
@@ -177,6 +175,13 @@ register SCSIID {
access_mode RW
mask TID 0xf0 /* Target ID mask */
mask OID 0x0f /* Our ID mask */
+ /*
+ * SCSI Maximum Offset (p. 4-61 aic7890/91 Data Book)
+ * The aic7890/91 allow an offset of up to 127 transfers in both wide
+ * and narrow mode.
+ */
+ alias SCSIOFFSET
+ mask SOFS_ULTRA2 0x7f /* Sync offset U2 chips */
}
/*
@@ -230,14 +235,15 @@ register CLRSINT0 {
register SSTAT0 {
address 0x00b
access_mode RO
- bit TARGET 0x80 /* Board acting as target */
- bit SELDO 0x40 /* Selection Done */
- bit SELDI 0x20 /* Board has been selected */
- bit SELINGO 0x10 /* Selection In Progress */
- bit SWRAP 0x08 /* 24bit counter wrap */
- bit SDONE 0x04 /* STCNT = 0x000000 */
- bit SPIORDY 0x02 /* SCSI PIO Ready */
- bit DMADONE 0x01 /* DMA transfer completed */
+ bit TARGET 0x80 /* Board acting as target */
+ bit SELDO 0x40 /* Selection Done */
+ bit SELDI 0x20 /* Board has been selected */
+ bit SELINGO 0x10 /* Selection In Progress */
+ bit SWRAP 0x08 /* 24bit counter wrap */
+ bit IOERR 0x08 /* LVD Tranceiver mode changed */
+ bit SDONE 0x04 /* STCNT = 0x000000 */
+ bit SPIORDY 0x02 /* SCSI PIO Ready */
+ bit DMADONE 0x01 /* DMA transfer completed */
}
/*
@@ -279,6 +285,7 @@ register SSTAT2 {
address 0x00d
access_mode RO
bit OVERRUN 0x80
+ bit EXP_ACTIVE 0x10 /* SCSI Expander Active */
mask SFCNT 0x1f
}
@@ -293,14 +300,13 @@ register SSTAT3 {
}
/*
- * SCSI Test Control (p. 3-27)
+ * SCSI ID for the aic7890/91 chips
*/
-register SCSITEST {
+register SCSIID_ULTRA2 {
address 0x00f
access_mode RW
- bit RQAKCNT 0x04
- bit CNTRTEST 0x02
- bit CMODE 0x01
+ mask TID 0xf0 /* Target ID mask */
+ mask OID 0x0f /* Our ID mask */
}
/*
@@ -315,6 +321,7 @@ register SIMODE0 {
bit ENSELDI 0x20
bit ENSELINGO 0x10
bit ENSWRAP 0x08
+ bit ENIOERR 0x08 /* LVD Tranceiver mode changes */
bit ENSDONE 0x04
bit ENSPIORDY 0x02
bit ENDMADONE 0x01
@@ -378,6 +385,7 @@ register SELTIMER {
bit STAGE3 0x04
bit STAGE2 0x02
bit STAGE1 0x01
+ alias TARGIDIN
}
/*
@@ -393,6 +401,36 @@ register SELID {
}
/*
+ * Target Mode Selecting in ID bitmask (aic7890/91/96/97)
+ */
+register TARGID {
+ address 0x01b
+ size 2
+ access_mode RW
+}
+
+/*
+ * Serial Port I/O Cabability register (p. 4-95 aic7860 Data Book)
+ * Indicates if external logic has been attached to the chip to
+ * perform the tasks of accessing a serial eeprom, testing termination
+ * strength, and performing cable detection. On the aic7860, most of
+ * these features are handled on chip, but on the aic7855 an attached
+ * aic3800 does the grunt work.
+ */
+register SPIOCAP {
+ address 0x01b
+ access_mode RW
+ bit SOFT1 0x80
+ bit SOFT0 0x40
+ bit SOFTCMDEN 0x20
+ bit HAS_BRDCTL 0x10 /* External Board control */
+ bit SEEPROM 0x08 /* External serial eeprom logic */
+ bit EEPROM 0x04 /* Writable external BIOS ROM */
+ bit ROM 0x02 /* Logic for accessing external ROM */
+ bit SSPIOCPS 0x01 /* Termination and cable detection */
+}
+
+/*
* SCSI Block Control (p. 3-32)
* Controls Bus type and channel selection. In a twin channel configuration
* addresses 0x00-0x1e are gated to the appropriate channel based on this
@@ -406,7 +444,10 @@ register SBLKCTL {
bit DIAGLEDON 0x40 /* Aic78X0 only */
bit AUTOFLUSHDIS 0x20
bit SELBUSB 0x08
+ bit ENAB40 0x08 /* LVD transceiver active */
+ bit ENAB20 0x04 /* SE/HVD transceiver active */
bit SELWIDE 0x02
+ bit XCVR 0x01 /* External transceiver active */
}
/*
@@ -529,6 +570,19 @@ register BCTL {
bit ENABLE 0x01
}
+register DSCOMMAND0 {
+ address 0x084
+ access_mode RW
+ bit CACHETHEN 0x80
+ bit DPARCKEN 0x40
+ bit MPARCKEN 0x20
+ bit EXTREQLCK 0x10
+ bit INTSCBRAMSEL 0x08
+ bit RAMPS 0x04
+ bit USCBSIZE32 0x02
+ bit CIOPARCKEN 0x01
+}
+
/*
* On the aic78X0 chips, Board Control is replaced by the DSCommand
* register (p. 4-64)
@@ -622,32 +676,26 @@ register INTSTAT {
mask NO_IDENT 0x20|SEQINT /* no IDENTIFY after reconnect*/
mask NO_MATCH 0x30|SEQINT /* no cmd match for reconnect */
mask EXTENDED_MSG 0x40|SEQINT /* Extended message received */
- mask NO_MATCH_BUSY 0x50|SEQINT /* Couldn't find BUSY SCB */
+ mask ABORT_REQUESTED 0x50|SEQINT /* Reconect of aborted SCB */
mask REJECT_MSG 0x60|SEQINT /* Reject message received */
mask BAD_STATUS 0x70|SEQINT /* Bad status from target */
mask RESIDUAL 0x80|SEQINT /* Residual byte count != 0 */
- mask ABORT_CMDCMPLT 0x91 /*
- * Command tagged for abort
- * completed successfully.
- */
mask AWAITING_MSG 0xa0|SEQINT /*
* Kernel requested to specify
- * a message to this target
- * (command was null), so tell
- * it that it can fill the
- * message buffer.
- */
- mask MSG_BUFFER_BUSY 0xc0|SEQINT /*
- * Sequencer wants to use the
- * message buffer, but it
- * already contains a message
+ * a message to this target
+ * (command was null), so tell
+ * it that it can fill the
+ * message buffer.
*/
- mask MSGIN_PHASEMIS 0xd0|SEQINT /*
+ mask TARGET_MSG_HELP 0xb0|SEQINT
+ mask TARGET_SYNC_CMD 0xc0|SEQINT
+ mask TRACEPOINT 0xd0|SEQINT
+ mask MSGIN_PHASEMIS 0xe0|SEQINT /*
* Target changed phase on us
* when we were expecting
* another msgin byte.
*/
- mask DATA_OVERRUN 0xe0|SEQINT /*
+ mask DATA_OVERRUN 0xf0|SEQINT /*
* Target attempted to write
* beyond the bounds of its
* command.
@@ -665,7 +713,11 @@ register INTSTAT {
register ERROR {
address 0x092
access_mode RO
- bit PARERR 0x08
+ bit CIOPARERR 0x80 /* Ultra2 only */
+ bit PCIERRSTAT 0x40 /* PCI only */
+ bit MPARERR 0x20 /* PCI only */
+ bit DPARERR 0x10 /* PCI only */
+ bit SQPARERR 0x08
bit ILLOPCODE 0x04
bit ILLSADDR 0x02
bit ILLHADDR 0x01
@@ -677,6 +729,7 @@ register ERROR {
register CLRINT {
address 0x092
access_mode WO
+ bit CLRPARERR 0x10 /* PCI only */
bit CLRBRKADRINT 0x08
bit CLRSCSIINT 0x04
bit CLRCMDINT 0x02
@@ -686,6 +739,7 @@ register CLRINT {
register DFCNTRL {
address 0x093
access_mode RW
+ bit PRELOADEN 0x80 /* aic7890 only */
bit WIDEODD 0x40
bit SCSIEN 0x20
bit SDMAEN 0x10
@@ -700,6 +754,7 @@ register DFCNTRL {
register DFSTATUS {
address 0x094
access_mode RO
+ bit PRELOAD_AVAIL 0x80
bit DWORDEMP 0x20
bit MREQPEND 0x10
bit HDONE 0x08
@@ -762,17 +817,25 @@ register QOUTCNT {
}
/*
+ * Special Function
+ */
+register SFUNCT {
+ address 0x09f
+ access_mode RW
+}
+
+/*
* SCB Definition (p. 5-4)
*/
scb {
address 0x0a0
SCB_CONTROL {
size 1
- bit MK_MESSAGE 0x80
+ bit TARGET_SCB 0x80
bit DISCENB 0x40
bit TAG_ENB 0x20
- bit MUST_DMAUP_SCB 0x10
- bit ABORT_SCB 0x08
+ bit MK_MESSAGE 0x10
+ bit ULTRAENB 0x08
bit DISCONNECTED 0x04
mask SCB_TAG_TYPE 0x03
}
@@ -801,15 +864,20 @@ scb {
size 4
}
SCB_DATACNT {
- size 3
- }
- SCB_LINKED_NEXT {
- size 1
+ /*
+ * Really only 3 bytes, but padded to make
+ * the kernel's job easier.
+ */
+ size 4
}
SCB_CMDPTR {
+ alias SCB_TARGET_PHASES
+ alias SCB_TARGET_ID /* Byte 2 */
+ bit TARGET_DATA_IN 0x1 /* In the second byte */
size 4
}
SCB_CMDLEN {
+ alias SCB_INITIATOR_TAG
size 1
}
SCB_TAG {
@@ -818,14 +886,29 @@ scb {
SCB_NEXT {
size 1
}
- SCB_PREV {
+ SCB_SCSIRATE {
+ size 1
+ }
+ SCB_SCSIOFFSET {
size 1
}
- SCB_BUSYTARGETS {
+ SCB_SPARE {
+ size 3
+ }
+ SCB_CMDSTORE {
+ size 16
+ }
+ SCB_CMDSTORE_BUSADDR {
size 4
}
+ SCB_64BYTE_SPARE {
+ size 12
+ }
}
+const SCB_32BYTE_SIZE 28
+const SCB_64BYTE_SIZE 48
+
const SG_SIZEOF 0x08 /* sizeof(struct ahc_dma) */
/* --------------------- AHA-2840-only definitions -------------------- */
@@ -851,6 +934,117 @@ register STATUS_2840 {
register DSPCISTATUS {
address 0x086
+ mask DFTHRSH_100 0xc0
+}
+
+register CCHADDR {
+ address 0x0E0
+ size 8
+}
+
+register CCHCNT {
+ address 0x0E8
+}
+
+register CCSGRAM {
+ address 0x0E9
+}
+
+register CCSGADDR {
+ address 0x0EA
+}
+
+register CCSGCTL {
+ address 0x0EB
+ bit CCSGDONE 0x80
+ bit CCSGEN 0x08
+ bit FLAG 0x02
+ bit CCSGRESET 0x01
+}
+
+register CCSCBCNT {
+ address 0xEF
+}
+
+register CCSCBCTL {
+ address 0x0EE
+ bit CCSCBDONE 0x80
+ bit ARRDONE 0x40 /* SCB Array prefetch done */
+ bit CCARREN 0x10
+ bit CCSCBEN 0x08
+ bit CCSCBDIR 0x04
+ bit CCSCBRESET 0x01
+}
+
+register CCSCBADDR {
+ address 0x0ED
+}
+
+register CCSCBRAM {
+ address 0xEC
+}
+
+/*
+ * SCB bank address (7895/7896/97 only)
+ */
+register SCBBADDR {
+ address 0x0F0
+ access_mode RW
+}
+
+register CCSCBPTR {
+ address 0x0F1
+}
+
+register HNSCB_QOFF {
+ address 0x0F4
+}
+
+register SNSCB_QOFF {
+ address 0x0F6
+}
+
+register SDSCB_QOFF {
+ address 0x0F8
+}
+
+register QOFF_CTLSTA {
+ address 0x0FA
+ bit SCB_AVAIL 0x40
+ bit SNSCB_ROLLOVER 0x20
+ bit SDSCB_ROLLOVER 0x10
+ mask SCB_QSIZE 0x07
+ mask SCB_QSIZE_256 0x06
+}
+
+register DFF_THRSH {
+ address 0x0FB
+ mask WR_DFTHRSH 0x70
+ mask RD_DFTHRSH 0x07
+ mask RD_DFTHRSH_MIN 0x00
+ mask RD_DFTHRSH_25 0x01
+ mask RD_DFTHRSH_50 0x02
+ mask RD_DFTHRSH_63 0x03
+ mask RD_DFTHRSH_75 0x04
+ mask RD_DFTHRSH_85 0x05
+ mask RD_DFTHRSH_90 0x06
+ mask RD_DFTHRSH_MAX 0x07
+ mask WR_DFTHRSH_MIN 0x00
+ mask WR_DFTHRSH_25 0x10
+ mask WR_DFTHRSH_50 0x20
+ mask WR_DFTHRSH_63 0x30
+ mask WR_DFTHRSH_75 0x40
+ mask WR_DFTHRSH_85 0x50
+ mask WR_DFTHRSH_90 0x60
+ mask WR_DFTHRSH_MAX 0x70
+}
+
+register SG_CACHEPTR {
+ access_mode RW
+ address 0x0fc
+ mask SG_USER_DATA 0xfc
+ bit LAST_SEG 0x02
+ bit LAST_SEG_DONE 0x01
}
register BRDCTL {
@@ -863,6 +1057,12 @@ register BRDCTL {
bit BRDRW 0x04
bit BRDCTL1 0x02
bit BRDCTL0 0x01
+ /* 7890 Definitions */
+ bit BRDDAT4 0x10
+ bit BRDDAT3 0x08
+ bit BRDDAT2 0x04
+ bit BRDRW_ULTRA2 0x02
+ bit BRDSTB_ULTRA2 0x01
}
/*
@@ -921,9 +1121,13 @@ scratch_ram {
/*
* 1 byte per target starting at this address for configuration values
*/
- TARG_SCRATCH {
+ TARG_SCSIRATE {
+ alias CMDSIZE_TABLE
size 16
}
+ /*
+ * Bit vector of targets that have ULTRA enabled.
+ */
ULTRA_ENB {
size 2
}
@@ -934,18 +1138,16 @@ scratch_ram {
size 2
}
/*
- * Length of pending message
+ * Single byte buffer used to designate the type or message
+ * to send to a target.
*/
- MSG_LEN {
- size 1
- }
- /* We reserve 8bytes to store outgoing messages */
MSG_OUT {
- size 8
+ size 1
}
/* Parameters for DMA Logic */
DMAPARAMS {
size 1
+ bit PRELOADEN 0x80
bit WIDEODD 0x40
bit SCSIEN 0x20
bit SDMAEN 0x10
@@ -958,13 +1160,15 @@ scratch_ram {
}
SEQ_FLAGS {
size 1
- bit RESELECTED 0x80
- bit IDENTIFY_SEEN 0x40
- bit TAGGED_SCB 0x20
- bit DPHASE 0x10
- bit PAGESCBS 0x04
- bit WIDE_BUS 0x02
- bit TWIN_BUS 0x01
+ bit IDENTIFY_SEEN 0x80
+ bit SCBPTR_VALID 0x40
+ bit DPHASE 0x20
+ /* Target flags */
+ bit TARG_CMD_PENDING 0x10
+ bit CMDPHASE_PENDING 0x08
+ bit DPHASE_PENDING 0x04
+ bit SPHASE_PENDING 0x02
+ bit NO_DISCONNECT 0x01
}
/*
* Temporary storage for the
@@ -974,27 +1178,15 @@ scratch_ram {
SAVED_TCL {
size 1
}
+ /* Working value of the number of SG segments left */
SG_COUNT {
size 1
}
- /* working value of SG pointer */
+ /* Working value of SG pointer */
SG_NEXT {
size 4
}
/*
- * head of list of SCBs awaiting
- * selection
- */
- WAITING_SCBH {
- size 1
- }
- SAVED_LINKPTR {
- size 1
- }
- SAVED_SCBPTR {
- size 1
- }
- /*
* The last bus phase as seen by the sequencer.
*/
LASTPHASE {
@@ -1011,23 +1203,12 @@ scratch_ram {
mask P_MESGIN CDI|IOI|MSGI
mask P_BUSFREE 0x01
}
- MSGIN_EXT_LEN {
- size 1
- }
- MSGIN_EXT_OPCODE {
- size 1
- }
/*
- * location 3, stores the last
- * byte of an extended message if
- * it passes the two bytes of space
- * we allow now. This byte isn't
- * used for anything, it just makes
- * the code shorter for tossing
- * extra bytes.
+ * head of list of SCBs awaiting
+ * selection
*/
- MSGIN_EXT_BYTES {
- size 3
+ WAITING_SCBH {
+ size 1
}
/*
* head of list of SCBs that are
@@ -1044,18 +1225,40 @@ scratch_ram {
FREE_SCBH {
size 1
}
+ /*
+ * Address of the hardware scb array in the host.
+ */
HSCB_ADDR {
size 4
}
- CUR_SCBID {
+ /*
+ * Address of the 256 byte array storing the SCBID of outstanding
+ * untagged SCBs indexed by TCL.
+ */
+ SCBID_ADDR {
+ size 4
+ }
+ /*
+ * Address of the array of command descriptors used to store
+ * information about incoming selections.
+ */
+ TMODE_CMDADDR {
+ size 4
+ }
+ KERNEL_QINPOS {
+ size 1
+ }
+ QINPOS {
+ size 1
+ }
+ QOUTPOS {
size 1
}
/*
- * Running count of commands placed in
- * the QOUTFIFO. This is cleared by the
- * kernel driver every FIFODEPTH commands.
+ * Offset into the command descriptor array for the next
+ * available desciptor to use.
*/
- CMDOUTCNT {
+ TMODE_CMDADDR_NEXT {
size 1
}
ARG_1 {
@@ -1063,8 +1266,30 @@ scratch_ram {
mask SEND_MSG 0x80
mask SEND_SENSE 0x40
mask SEND_REJ 0x20
+ mask MSGOUT_PHASEMIS 0x10
alias RETURN_1
}
+ ARG_2 {
+ size 1
+ alias RETURN_2
+ }
+
+ /*
+ * Snapshot of MSG_OUT taken after each message is sent.
+ */
+ LAST_MSG {
+ size 1
+ }
+
+ /*
+ * Number of times we have filled the CCSGRAM with prefetched
+ * SG elements.
+ */
+ PREFETCH_CNT {
+ size 1
+ }
+
+
/*
* These are reserved registers in the card's scratch ram. Some of
* the values are specified in the AHA2742 technical reference manual
@@ -1073,7 +1298,10 @@ scratch_ram {
SCSICONF {
address 0x05a
size 1
+ bit TERM_ENB 0x80
bit RESET_SCSI 0x40
+ mask HSCSIID 0x07 /* our SCSI ID */
+ mask HWSCSIID 0x0f /* our SCSI ID if Wide Bus */
}
HOSTCONF {
address 0x05d
@@ -1086,29 +1314,51 @@ scratch_ram {
mask BIOSDISABLED 0x30
bit CHANNEL_B_PRIMARY 0x08
}
+ /*
+ * Per target SCSI offset values for Ultra2 controllers.
+ */
+ TARG_OFFSET {
+ address 0x070
+ size 16
+ }
}
const SCB_LIST_NULL 0xff
+const TARGET_CMD_CMPLT 0xfe
+
+const CCSGADDR_MAX 0x80
+const CCSGRAM_MAXSEGS 16
+/* Offsets into the SCBID array where different data is stored */
+const QOUTFIFO_OFFSET 0
+const QINFIFO_OFFSET 1
+const UNTAGGEDSCB_OFFSET 2
/* WDTR Message values */
-const BUS_8_BIT 0x00
+const BUS_8_BIT 0x00
const BUS_16_BIT 0x01
const BUS_32_BIT 0x02
+
+/* Offset maximums */
const MAX_OFFSET_8BIT 0x0f
-const MAX_OFFSET_16BIT 0x08
+const MAX_OFFSET_16BIT 0x08
+const MAX_OFFSET_ULTRA2 0x7f
+const HOST_MSG 0xff
+
+/* Target mode command processing constants */
+const CMD_GROUP_CODE_SHIFT 0x05
+const CMD_GROUP0_BYTE_DELTA -4
+const CMD_GROUP2_BYTE_DELTA 9
+const CMD_GROUP3_BYTE_DELTA -15
+const CMD_GROUP4_BYTE_DELTA 4
+const CMD_GROUP5_BYTE_DELTA 11
+
/*
* Downloaded (kernel inserted) constants
*/
-const SCBCOUNT download /* The number of SCBs on this card */
-const COMP_SCBCOUNT download /* Two's complement of max SCBID */
-/*
- * The maximum number of entries allowed in the QIN/OUTFIFO.
- */
-const FIFODEPTH download /* Two's complement of SCBCOUNT */
+
/*
- * Mask of bits to test against when looking at the Queue Count
- * registers. Works around a bug on aic7850 chips.
+ * Number of command descriptors in the command descriptor array.
*/
-const QCNTMASK download
+const TMODE_NUMCMDS download
diff --git a/sys/dev/aic7xxx/aic7xxx.seq b/sys/dev/aic7xxx/aic7xxx.seq
index f3fad03..160f15a 100644
--- a/sys/dev/aic7xxx/aic7xxx.seq
+++ b/sys/dev/aic7xxx/aic7xxx.seq
@@ -1,7 +1,7 @@
/*
* Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
*
- * Copyright (c) 1994-1997 Justin Gibbs.
+ * Copyright (c) 1994-1998 Justin Gibbs.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -10,14 +10,11 @@
* 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
+ * 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
+ * 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
@@ -35,11 +32,11 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aic7xxx.seq,v 1.76 1997/09/27 19:37:27 gibbs Exp $
+ * $Id: aic7xxx.seq,v 1.77 1998/06/28 02:58:57 gibbs Exp $
*/
#include <dev/aic7xxx/aic7xxx.reg>
-#include <scsi/scsi_message.h>
+#include <cam/scsi/scsi_message.h>
/*
* A few words on the waiting SCB list:
@@ -59,36 +56,58 @@
* automatically consume the entries.
*/
-/*
- * We assume that the kernel driver may reset us at any time, even in the
- * middle of a DMA, so clear DFCNTRL too.
- */
reset:
clr SCSISIGO; /* De-assert BSY */
/* Always allow reselection */
- mvi SCSISEQ, ENRSELI|ENAUTOATNP;
+ if ((ahc->flags & AHC_TARGETMODE) != 0) {
+ mvi SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP;
+ } else {
+ mvi SCSISEQ, ENRSELI|ENAUTOATNP;
+ }
+
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ /* Ensure that no DMA operations are in progress */
+ clr CCSGCTL;
+ clr CCSCBCTL;
+ }
+
call clear_target_state;
poll_for_work:
- test SSTAT0,SELDO jnz select;
- test SSTAT0,SELDI jnz reselect;
+ and SXFRCTL0, ~SPIOEN;
+ if ((ahc->features & AHC_QUEUE_REGS) == 0) {
+ mov A, QINPOS;
+ }
+poll_for_work_loop:
+ if ((ahc->features & AHC_QUEUE_REGS) == 0) {
+ and SEQCTL, ~PAUSEDIS;
+ }
+ test SSTAT0, SELDO|SELDI jnz selection;
test SCSISEQ, ENSELO jnz poll_for_work;
-.if ( TWIN_CHANNEL )
- /*
- * Twin channel devices cannot handle things like SELTO
- * interrupts on the "background" channel. So, if we
- * are selecting, keep polling the current channel util
- * either a selection or reselection occurs.
- */
- xor SBLKCTL,SELBUSB; /* Toggle to the other bus */
- test SSTAT0,SELDO jnz select;
- test SSTAT0,SELDI jnz reselect;
- test SCSISEQ, ENSELO jnz poll_for_work;
- xor SBLKCTL,SELBUSB; /* Toggle back */
-.endif
+ if ((ahc->features & AHC_TWIN) != 0) {
+ /*
+ * Twin channel devices cannot handle things like SELTO
+ * interrupts on the "background" channel. So, if we
+ * are selecting, keep polling the current channel util
+ * either a selection or reselection occurs.
+ */
+ xor SBLKCTL,SELBUSB; /* Toggle to the other bus */
+ test SSTAT0, SELDO|SELDI jnz selection;
+ test SCSISEQ, ENSELO jnz poll_for_work;
+ xor SBLKCTL,SELBUSB; /* Toggle back */
+ }
cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
test_queue:
/* Has the driver posted any work for us? */
- test QINCNT,QCNTMASK jz poll_for_work;
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ test QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
+ mov NONE, SNSCB_QOFF;
+ inc QINPOS;
+ } else {
+ or SEQCTL, PAUSEDIS;
+ cmp KERNEL_QINPOS, A je poll_for_work_loop;
+ inc QINPOS;
+ and SEQCTL, ~PAUSEDIS;
+ }
/*
* We have at least one queued SCB now and we don't have any
@@ -96,78 +115,39 @@ test_queue:
* any SCBs available for use, pull the tag from the QINFIFO
* and get to work on it.
*/
-.if ( SCB_PAGING )
- mov ALLZEROS call get_free_or_disc_scb;
- cmp SINDEX, SCB_LIST_NULL je poll_for_work;
-.endif
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ mov ALLZEROS call get_free_or_disc_scb;
+ }
+
dequeue_scb:
- mov CUR_SCBID,QINFIFO;
-.if !( SCB_PAGING )
- /* In the non-paging case, the SCBID == hardware SCB index */
- mov SCBPTR, CUR_SCBID;
-.endif
+ add A, -1, QINPOS;
+ mvi QINFIFO_OFFSET call fetch_byte;
+
+ if ((ahc->flags & AHC_PAGESCBS) == 0) {
+ /* In the non-paging case, the SCBID == hardware SCB index */
+ mov SCBPTR, RETURN_2;
+ }
dma_queued_scb:
/*
* DMA the SCB from host ram into the current SCB location.
*/
mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
- mov CUR_SCBID call dma_scb;
+ mov RETURN_2 call dma_scb;
/*
- * See if there is not already an active SCB for this target. This code
- * locks out on a per target basis instead of target/lun. Although this
- * is not ideal for devices that have multiple luns active at the same
- * time, it is faster than looping through all SCB's looking for active
- * commands. We also don't have enough spare SCB space for us to store the
- * SCBID of the currently busy transaction for each target/lun making it
- * impossible to link up the SCBs.
+ * Preset the residual fields in case we never go through a data phase.
+ * This isn't done by the host so we can avoid a DMA to clear these
+ * fields for the normal case of I/O that completes without underrun
+ * or overrun conditions.
*/
-test_busy:
- test SCB_CONTROL, TAG_ENB|ABORT_SCB jnz start_scb;
- mvi SEQCTL, PAUSEDIS|FASTMODE;
- mov SAVED_SCBPTR, SCBPTR;
- mov SCB_TCL call index_untagged_scb;
- mov ARG_1, SINDIR; /*
- * ARG_1 should
- * now have the SCB ID of
- * any active, non-tagged,
- * command for this target.
- */
- cmp ARG_1, SCB_LIST_NULL je make_busy;
-.if ( SCB_PAGING )
- /*
- * Put this SCB back onto the free list. It
- * may be necessary to satisfy the search for
- * the active SCB.
- */
- mov SCBPTR, SAVED_SCBPTR;
- call add_scb_to_free_list;
- /* Find the active SCB */
- mov ALLZEROS call findSCB;
- /*
- * If we couldn't find it, tell the kernel. This should
- * never happen.
- */
- cmp SINDEX, SCB_LIST_NULL jne paged_busy_link;
- mvi INTSTAT, NO_MATCH_BUSY;
-paged_busy_link:
- /* Link us in */
- mov SCB_LINKED_NEXT, CUR_SCBID;
- /* Put it back on the disconnected list */
- call add_scb_to_disc_list;
- mvi SEQCTL, FASTMODE;
- jmp poll_for_work;
-.else
-simple_busy_link:
- mov SCBPTR, ARG_1;
- mov SCB_LINKED_NEXT, CUR_SCBID;
- mvi SEQCTL, FASTMODE;
- jmp poll_for_work;
-.endif
-make_busy:
- mov DINDIR, CUR_SCBID;
- mov SCBPTR, SAVED_SCBPTR;
- mvi SEQCTL, FASTMODE;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov SCB_RESID_DCNT, SCB_DATACNT, 3;
+ } else {
+ mov SCB_RESID_DCNT[0],SCB_DATACNT[0];
+ mov SCB_RESID_DCNT[1],SCB_DATACNT[1];
+ mov SCB_RESID_DCNT[2],SCB_DATACNT[2];
+ }
+ mov SCB_RESID_SGCNT, SCB_SGCOUNT;
start_scb:
/*
@@ -178,37 +158,243 @@ start_scb:
mov WAITING_SCBH, SCBPTR;
start_waiting:
/*
- * Pull the first entry off of the waiting SCB list
- * We don't have to "test_busy" because only transactions that
- * have passed that test can be in the WAITING_SCB list.
+ * Pull the first entry off of the waiting SCB list.
*/
mov SCBPTR, WAITING_SCBH;
call start_selection;
jmp poll_for_work;
start_selection:
-.if ( TWIN_CHANNEL )
- and SINDEX,~SELBUSB,SBLKCTL;/* Clear the channel select bit */
- and A,SELBUSB,SCB_TCL; /* Get new channel bit */
- or SINDEX,A;
- mov SBLKCTL,SINDEX; /* select channel */
-.endif
+ if ((ahc->features & AHC_TWIN) != 0) {
+ and SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */
+ and A,SELBUSB,SCB_TCL; /* Get new channel bit */
+ or SINDEX,A;
+ mov SBLKCTL,SINDEX; /* select channel */
+ }
initialize_scsiid:
- and A, TID, SCB_TCL; /* Get target ID */
- and SCSIID, OID; /* Clear old target */
- or SCSIID, A;
- mvi SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ and A, TID, SCB_TCL; /* Get target ID */
+ and SCSIID_ULTRA2, OID; /* Clear old target */
+ or SCSIID_ULTRA2, A;
+ } else {
+ and A, TID, SCB_TCL; /* Get target ID */
+ and SCSIID, OID; /* Clear old target */
+ or SCSIID, A;
+ }
+ if ((ahc->flags & AHC_TARGETMODE) != 0) {
+ and SINDEX, TARGET_SCB, SCB_CONTROL;
+ or SCSISEQ, ENSELO|ENAUTOATNO|ENSELI
+ |ENRSELI|ENAUTOATNP, SINDEX ret ;
+ } else {
+ mvi SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret;
+ }
+
+/*
+ * Initialize transfer settings and clear the SCSI channel.
+ * SINDEX should contain any additional bit's the client wants
+ * set in SXFRCTL0. We also assume that the current SCB is
+ * a valid SCB for the target we wish to talk to.
+ */
+initialize_channel:
+ or SXFRCTL0, CLRSTCNT|CLRCHN, SINDEX;
+set_transfer_settings:
+ if ((ahc->features & AHC_ULTRA) != 0) {
+ test SCB_CONTROL, ULTRAENB jz . + 2;
+ or SXFRCTL0, FAST20;
+ }
+/*
+ * Initialize SCSIRATE with the appropriate value for this target.
+ */
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ bmov SCSIRATE, SCB_SCSIRATE, 2 ret;
+ } else {
+ mov SCSIRATE, SCB_SCSIRATE ret;
+ }
+
+selection:
+ test SSTAT0,SELDO jnz select_out;
+ mvi CLRSINT0, CLRSELDI;
+select_in:
+ if ((ahc->flags & AHC_TARGETMODE) != 0) {
+ test SSTAT0, TARGET jz initiator_reselect;
+ /*
+ * We've just been selected. Assert BSY and
+ * setup the phase for receiving messages
+ * from the target.
+ */
+ mvi SCSISIGO, P_MESGOUT|BSYO;
+
+ /*
+ * LAST_MSG gives an indication to the host of what
+ * went wrong should we need to terminate this selection
+ * before doing real work. Initialize it to SCB_LIST_NULL to
+ * indicate an improper initiator selection.
+ */
+ mvi LAST_MSG, SCB_LIST_NULL;
+
+ /*
+ * Setup the DMA for sending the identify and
+ * command information. We keep a count of the
+ * number of bytes to send to the host in ARG_2.
+ */
+ or SEQ_FLAGS, CMDPHASE_PENDING;
+ mov A, TMODE_CMDADDR_NEXT;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mvi DINDEX, CCHADDR;
+ mvi TMODE_CMDADDR call set_32byte_addr;
+ mvi CCSCBCTL, CCSCBRESET;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi TMODE_CMDADDR call set_32byte_addr;
+ mvi DFCNTRL, FIFORESET;
+ }
+
+ /* Initiator that selected us */
+ and SAVED_TCL, SELID_MASK, SELID;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, SAVED_TCL;
+ } else {
+ mov DFDAT, SAVED_TCL;
+ }
+
+ /* The Target ID we were selected at */
+ if ((ahc->features & AHC_MULTI_TID) != 0) {
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ and CCSCBRAM, 0x0f, TARGIDIN;
+ } else {
+ and DFDAT, 0x0f, TARGIDIN;
+ }
+ } else {
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ and CCSCBRAM, OID, SCSIID;
+ } else {
+ and DFDAT, OID, SCSIID;
+ }
+ }
+
+ /*
+ * If ATN isn't asserted, the target isn't interested
+ * in talking to us. Go directly to bus free.
+ */
+ test SCSISIGI, ATNI jz target_busfree;
+
+ /*
+ * Watch ATN closely now as we pull in messages from the
+ * initiator. We follow the guidlines from section 6.5
+ * of the SCSI-2 spec for what messages are allowed when.
+ */
+ call targ_inb;
+
+ /*
+ * Our first message must be one of IDENTIFY, ABORT, or
+ * BUS_DEVICE_RESET.
+ */
+ test DINDEX, MSG_IDENTIFYFLAG jz more_first_messages;
+ /* Store for host */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, DINDEX;
+ } else {
+ mov DFDAT, DINDEX;
+ }
+ mvi ARG_2, 3;
+
+ /* Remember for disconnection decision */
+ test DINDEX, MSG_IDENTIFY_DISCFLAG jnz . + 2;
+ /* XXX Honor per target settings too */
+ or SEQ_FLAGS, NO_DISCONNECT;
+
+ test SCSISIGI, ATNI jz ident_messages_done;
+ call targ_inb;
+ /*
+ * If this is a tagged request, the tagged message must
+ * immediately follow the identify. We test for a valid
+ * tag message by seeing if it is >= MSG_SIMPLE_Q_TAG and
+ * < MSG_IGN_WIDE_RESIDUE.
+ */
+ add A, -MSG_SIMPLE_Q_TAG, DINDEX;
+ jnc ident_messages_done;
+ add A, -MSG_IGN_WIDE_RESIDUE, DINDEX;
+ jc ident_messages_done;
+ /* Store for host */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, DINDEX;
+ } else {
+ mov DFDAT, DINDEX;
+ }
+
+ /*
+ * If the initiator doesn't feel like providing a tag number,
+ * we've got a failed selection and must transition to bus
+ * free.
+ */
+ test SCSISIGI, ATNI jz target_busfree;
+ /*
+ * Store the tag for the host.
+ */
+ call targ_inb;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, DINDEX;
+ } else {
+ mov DFDAT, DINDEX;
+ }
+ add ARG_2, 2;
+ jmp ident_messages_done;
+
+more_first_messages:
+ /*
+ * Hmm. Now we're down to only accepting
+ * either an ABORT or BDR.
+ */
+ cmp DINDEX, MSG_ABORT je . + 2;
+ cmp DINDEX, MSG_BUS_DEV_RESET jne target_busfree;
+
+ /* Record the event and notify the host */
+ mov LAST_MSG, DINDEX;
+ jmp target_busfree;
+
+ident_messages_done:
+ mvi LAST_MSG, MSG_NOOP; /* We are so far successful */
+ /* Terminate the ident list */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mvi CCSCBRAM, SCB_LIST_NULL;
+ } else {
+ mvi DFDAT, SCB_LIST_NULL;
+ }
+ or SEQ_FLAGS, TARG_CMD_PENDING;
+ jmp target_ITloop;
+
+/*
+ * We carefully toggle SPIOEN to allow us to return the
+ * message byte we receive so it can be checked prior to
+ * driving REQ on the bus for the next byte.
+ */
+targ_inb:
+ /* Drive REQ on the bus by enabling SCSI PIO */
+ or SXFRCTL0, SPIOEN;
+ /* Wait for the byte */
+ test SSTAT0, SPIORDY jz .;
+ /* Prevent our read from triggering another REQ */
+ and SXFRCTL0, ~SPIOEN;
+ mov DINDEX, SCSIDATL ret;
+ }
+
/*
* Reselection has been initiated by a target. Make a note that we've been
* reselected, but haven't seen an IDENTIFY message from the target yet.
*/
-reselect:
- clr MSG_LEN; /* Don't have anything in the mesg buffer */
- mvi CLRSINT0, CLRSELDI;
+initiator_reselect:
/* XXX test for and handle ONE BIT condition */
and SAVED_TCL, SELID_MASK, SELID;
- or SEQ_FLAGS,RESELECTED;
- jmp select2;
+ or SXFRCTL0, SPIOEN|CLRCHN;
+ mvi CLRSINT1,CLRBUSFREE;
+ or SIMODE1, ENBUSFREE; /*
+ * We aren't expecting a
+ * bus free, so interrupt
+ * the kernel driver if it
+ * happens.
+ */
+ mvi MSG_OUT, MSG_NOOP; /* No message to send */
+ jmp ITloop;
/*
* After the selection, remove this SCB from the "waiting SCB"
@@ -216,106 +402,203 @@ reselect:
* WAITING_SCBH. Our next pointer will be set to null the next time this
* SCB is used, so don't bother with it now.
*/
-select:
+select_out:
/* Turn off the selection hardware */
- mvi SCSISEQ, ENRSELI|ENAUTOATNP; /*
- * ATN on parity errors
- * for "in" phases
- */
+ if ((ahc->flags & AHC_TARGETMODE) != 0) {
+ mvi SCSISEQ, ENSELI|ENRSELI|ENAUTOATNP;
+ } else {
+ mvi SCSISEQ, ENRSELI|ENAUTOATNP;
+ }
mvi CLRSINT0, CLRSELDO;
mov SCBPTR, WAITING_SCBH;
mov WAITING_SCBH,SCB_NEXT;
mov SAVED_TCL, SCB_TCL;
+ if ((ahc->flags & AHC_TARGETMODE) != 0) {
+ test SSTAT0, TARGET jz initiator_select;
+
+ /*
+ * We've just re-selected an initiator.
+ * Assert BSY and setup the phase for
+ * sending our identify messages.
+ */
+ mvi SCSISIGO, P_MESGIN|BSYO;
+
+ /*
+ * Start out with a simple identify message.
+ */
+ and A, LID, SCB_TCL;
+ or A, MSG_IDENTIFYFLAG call target_outb;
+
+ /*
+ * If we are the result of a tagged command, send
+ * a simple Q tag and the tag id.
+ */
+ test SCB_CONTROL, TAG_ENB jz . + 3;
+ mvi MSG_SIMPLE_Q_TAG call target_outb;
+ mov SCB_TAG call target_outb;
+target_synccmd:
+ /*
+ * Now determine what phases the host wants us
+ * to go through.
+ */
+ mov SEQ_FLAGS, SCB_TARGET_PHASES;
+
+target_ITloop:
+ /*
+ * XXX Start honoring ATN signals now that
+ * we properly identified ourself.
+ */
+ test SEQ_FLAGS, CMDPHASE_PENDING jnz target_cmdphase;
+ test SEQ_FLAGS, DPHASE_PENDING jnz target_dphase;
+ test SEQ_FLAGS, SPHASE_PENDING jnz target_sphase;
+
+ /*
+ * No more work to do. Either disconnect or not depending
+ * on the state of NO_DISCONNECT.
+ */
+ test SEQ_FLAGS, NO_DISCONNECT jz target_disconnect;
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ mov ALLZEROS call get_free_or_disc_scb;
+ }
+ call complete_target_cmd;
+ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+ mov SCB_TAG call dma_scb;
+ jmp target_synccmd;
+
+target_disconnect:
+ mvi SCSISIGO, P_MESGIN|BSYO;
+ mvi MSG_DISCONNECT call target_outb;
+
+target_busfree:
+ and SXFRCTL0, ~SPIOEN;
+ clr SCSISIGO;
+ call complete_target_cmd;
+ cmp LAST_MSG, MSG_NOOP je . + 2;
+ mvi INTSTAT, TARGET_MSG_HELP;
+ call clear_target_state;
+ jmp poll_for_work;
+
+target_cmdphase:
+ /*
+ * Add one for the terminating byte
+ * and one for the command code.
+ */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ add CCHCNT, 2, ARG_2;
+ } else {
+ add HCNT[0], 2, ARG_2;
+ clr HCNT[1];
+ clr HCNT[2];
+ }
+ mvi SCSISIGO, P_COMMAND|BSYO;
+ call targ_inb;
+ mov A, DINDEX;
+ /* Store for host */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, A;
+ } else {
+ mov DFDAT, A;
+ }
+
+ /*
+ * Determine the number of bytes to read
+ * based on the command group code using an adding
+ * jump table. Count is one less than the total
+ * since we've already fetched the first byte.
+ */
+ shr A, CMD_GROUP_CODE_SHIFT;
+ add SINDEX, TARG_SCSIRATE, A;
+ mov A, SINDIR;
+
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ add CCHCNT, A;
+ } else {
+ add HCNT[0], A;
+ }
+
+ test A, 0xFF jz command_phase_done;
+command_loop:
+ or SXFRCTL0, SPIOEN;
+ test SSTAT0, SPIORDY jz .;
+ cmp A, 1 jne . + 2;
+ and SXFRCTL0, ~SPIOEN; /* Last Byte */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov CCSCBRAM, SCSIDATL;
+ } else {
+ mov DFDAT, SCSIDATL;
+ }
+ dec A;
+ test A, 0xFF jnz command_loop;
+
+command_phase_done:
+ and SEQ_FLAGS, ~CMDPHASE_PENDING;
+ jmp target_ITloop;
+
+target_dphase:
+ /*
+ * Data direction flags are from the
+ * perspective of the initiator.
+ */
+ and SXFRCTL0, ~SPIOEN;
+ or SXFRCTL0, CLRCHN;
+ test SCB_TARGET_PHASES[1], TARGET_DATA_IN jz . + 4;
+ mvi SCSISIGO, P_DATAIN|BSYO;
+ mvi LASTPHASE, P_DATAOUT;
+ jmp p_data;
+ mvi SCSISIGO, P_DATAOUT|BSYO;
+ mvi LASTPHASE, P_DATAIN;
+ jmp p_data;
+
+target_sphase:
+ mvi SCSISIGO, P_STATUS|BSYO;
+ mov SCB_TARGET_STATUS call target_outb;
+ /* XXX Watch for ATN for parity errors??? */
+ mvi SCSISIGO, P_MESGIN|BSYO;
+ /* MSG_CMDCMPLT is 0, but we can't do an immediate of 0 */
+ mov ALLZEROS call target_outb;
+ jmp target_busfree;
+
+complete_target_cmd:
+ test SEQ_FLAGS, TARG_CMD_PENDING jnz . + 2;
+ mov SCB_TAG jmp complete_post;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ or CCSCBCTL, CCSCBEN|CCSCBRESET;
+ test CCSCBCTL, CCSCBDONE jz .;
+ clr CCSCBCTL;
+ } else {
+ or DFCNTRL, HDMAEN|FIFOFLUSH;
+ call dma_finish;
+ }
+ inc TMODE_CMDADDR_NEXT;
+ cmp TMODE_CMDADDR_NEXT, TMODE_NUMCMDS jne . + 2;
+ clr TMODE_CMDADDR_NEXT;
+ mvi TARGET_CMD_CMPLT jmp complete_post;
+ }
+initiator_select:
+ mvi SPIOEN call initialize_channel;
/*
- * As soon as we get a successful selection, the target should go
- * into the message out phase since we have ATN asserted. Prepare
- * the message to send.
- *
- * Messages are stored in scratch RAM starting with a length byte
- * followed by the message itself.
- */
-
-mk_identify:
- and MSG_OUT,0x7,SCB_TCL; /* lun */
- and A,DISCENB,SCB_CONTROL; /* mask off disconnect privledge */
- or MSG_OUT,A; /* or in disconnect privledge */
- or MSG_OUT,MSG_IDENTIFYFLAG;
- mvi MSG_LEN, 1;
-
-/*
- * Send a tag message if TAG_ENB is set in the SCB control block.
- * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
- */
-mk_tag:
- test SCB_CONTROL,TAG_ENB jz mk_message;
- and MSG_OUT[1],TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
- mov MSG_OUT[2],SCB_TAG;
- add MSG_LEN,2; /* update message length */
-
-/*
- * Interrupt the driver, and allow it to tweak the message buffer
- * if it asks.
+ * We aren't expecting a bus free, so interrupt
+ * the kernel driver if it happens.
*/
-mk_message:
- test SCB_CONTROL,MK_MESSAGE jz select2;
- mvi INTSTAT,AWAITING_MSG;
-
-select2:
mvi CLRSINT1,CLRBUSFREE;
- or SIMODE1, ENBUSFREE; /*
- * We aren't expecting a
- * bus free, so interrupt
- * the kernel driver if it
- * happens.
- */
-/*
- * Initialize Ultra mode setting and clear the SCSI channel.
- */
- or SXFRCTL0, CLRSTCNT|SPIOEN|CLRCHN;
-.if ( ULTRA )
-ultra:
- mvi SINDEX, ULTRA_ENB+1;
- test SAVED_TCL, 0x80 jnz ultra_2; /* Target ID > 7 */
- dec SINDEX;
-ultra_2:
- mov FUNCTION1,SAVED_TCL;
- mov A,FUNCTION1;
- test SINDIR, A jz ndx_dtr;
- or SXFRCTL0, FAST20;
-.endif
-
+ or SIMODE1, ENBUSFREE;
/*
- * Initialize SCSIRATE with the appropriate value for this target.
- * The SCSIRATE settings for each target are stored in an array
- * based at TARG_SCRATCH.
+ * As soon as we get a successful selection, the target should go
+ * into the message out phase since we have ATN asserted.
*/
-ndx_dtr:
- shr A,4,SAVED_TCL;
- test SBLKCTL,SELBUSB jz ndx_dtr_2;
- or SAVED_TCL, SELBUSB; /* Add the channel bit while we're here */
- or A,0x08; /* Channel B entries add 8 */
-ndx_dtr_2:
- add SINDEX,TARG_SCRATCH,A;
- mov SCSIRATE,SINDIR;
-
+ mvi MSG_OUT, MSG_IDENTIFYFLAG;
+ or SEQ_FLAGS, IDENTIFY_SEEN;
/*
- * Main loop for information transfer phases. If BSY is false, then
- * we have a bus free condition, expected or not. Otherwise, wait
- * for the target to assert REQ before checking MSG, C/D and I/O
- * for the bus phase.
- *
+ * Main loop for information transfer phases. Wait for the target
+ * to assert REQ before checking MSG, C/D and I/O for the bus phase.
*/
ITloop:
- test SSTAT1,REQINIT jz ITloop;
- test SSTAT1, SCSIPERR jnz ITloop;
+ call phase_lock;
- and A,PHASE_MASK,SCSISIGI;
- mov LASTPHASE,A;
- mov SCSISIGO,A;
+ mov A, LASTPHASE;
- cmp ALLZEROS,A je p_dataout;
- cmp A,P_DATAIN je p_datain;
+ test A, ~P_DATAIN jz p_data;
cmp A,P_COMMAND je p_command;
cmp A,P_MESGOUT je p_mesgout;
cmp A,P_STATUS je p_status;
@@ -328,60 +611,88 @@ await_busfree:
and SIMODE1, ~ENBUSFREE;
call clear_target_state;
mov NONE, SCSIDATL; /* Ack the last byte */
+ and SXFRCTL0, ~SPIOEN;
test SSTAT1,REQINIT|BUSFREE jz .;
test SSTAT1, BUSFREE jnz poll_for_work;
mvi INTSTAT, BAD_PHASE;
clear_target_state:
- clr DFCNTRL;
+ clr DFCNTRL; /*
+ * We assume that the kernel driver
+ * may reset us at any time, even
+ * in the middle of a DMA, so clear
+ * DFCNTRL too.
+ */
clr SCSIRATE; /*
* We don't know the target we will
* connect to, so default to narrow
* transfers to avoid parity problems.
*/
- and SXFRCTL0, ~FAST20;
+ and SXFRCTL0, ~(FAST20);
mvi LASTPHASE, P_BUSFREE;
/* clear target specific flags */
- and SEQ_FLAGS,~(RESELECTED|IDENTIFY_SEEN|TAGGED_SCB|DPHASE) ret;
-
-p_dataout:
- mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET;
- jmp data_phase_init;
+ clr SEQ_FLAGS ret;
/*
* If we re-enter the data phase after going through another phase, the
* STCNT may have been cleared, so restore it from the residual field.
*/
data_phase_reinit:
- mvi DINDEX, STCNT;
- mvi SCB_RESID_DCNT call bcopy_3;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov STCNT, SCB_RESID_DCNT, 3;
+ } else {
+ mvi DINDEX, STCNT;
+ mvi SCB_RESID_DCNT call bcopy_3;
+ }
jmp data_phase_loop;
-p_datain:
- mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
-data_phase_init:
+p_data:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ mvi DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
+ } else {
+ mvi DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
+ }
+ test LASTPHASE, IOI jnz . + 2;
+ or DMAPARAMS, DIRECTION;
call assert; /*
* Ensure entering a data
* phase is okay - seen identify, etc.
*/
-
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mvi CCSGADDR, CCSGADDR_MAX;
+ }
test SEQ_FLAGS, DPHASE jnz data_phase_reinit;
+ /* We have seen a data phase */
+ or SEQ_FLAGS, DPHASE;
+
/*
* Initialize the DMA address and counter from the SCB.
* Also set SG_COUNT and SG_NEXT in memory since we cannot
* modify the values in the SCB itself until we see a
* save data pointers message.
*/
- mvi DINDEX, HADDR;
- mvi SCB_DATAPTR call bcopy_7;
-
- call set_stcnt_from_hcnt;
-
- mov SG_COUNT,SCB_SGCOUNT;
-
- mvi DINDEX, SG_NEXT;
- mvi SCB_SGPTR call bcopy_4;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov HADDR, SCB_DATAPTR, 7;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SCB_DATAPTR call bcopy_7;
+ }
+
+ if ((ahc->features & AHC_ULTRA2) == 0) {
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov STCNT, HCNT, 3;
+ } else {
+ call set_stcnt_from_hcnt;
+ }
+ }
+
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov SG_COUNT, SCB_SGCOUNT, 5;
+ } else {
+ mvi DINDEX, SG_COUNT;
+ mvi SCB_SGCOUNT call bcopy_5;
+ }
data_phase_loop:
/* Guard against overruns */
@@ -393,18 +704,40 @@ data_phase_loop:
* had an overrun.
*/
or SXFRCTL1,BITBUCKET;
- mvi HCNT[0], 0xff;
- mvi HCNT[1], 0xff;
- mvi HCNT[2], 0xff;
- call set_stcnt_from_hcnt;
-
+ and DMAPARAMS, ~(HDMAEN|SDMAEN);
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ bmov HCNT, ALLONES, 3;
+ } else if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov STCNT, ALLONES, 3;
+ } else {
+ mvi STCNT[0], 0xFF;
+ mvi STCNT[1], 0xFF;
+ mvi STCNT[2], 0xFF;
+ }
data_phase_inbounds:
-/* If we are the last SG block, ensure wideodd is off. */
+/* If we are the last SG block, tell the hardware. */
cmp SG_COUNT,0x01 jne data_phase_wideodd;
- and DMAPARAMS, ~WIDEODD;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ or SG_CACHEPTR, LAST_SEG;
+ } else {
+ and DMAPARAMS, ~WIDEODD;
+ }
data_phase_wideodd:
- mov DMAPARAMS call dma;
-
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ mov SINDEX, ALLONES;
+ mov DFCNTRL, DMAPARAMS;
+ test SSTAT0, SDONE jnz .;/* Wait for preload to complete */
+data_phase_dma_loop:
+ test SSTAT0, SDONE jnz data_phase_dma_done;
+ test SSTAT1,PHASEMIS jz data_phase_dma_loop; /* ie. underrun */
+data_phase_dma_phasemis:
+ test SSTAT0,SDONE jnz . + 2;
+ mov SINDEX,ALLZEROS; /* Remeber the phasemiss */
+ } else {
+ mov DMAPARAMS call dma;
+ }
+
+data_phase_dma_done:
/* Go tell the host about any overruns */
test SXFRCTL1,BITBUCKET jnz data_phase_overrun;
@@ -418,11 +751,6 @@ sg_advance:
dec SG_COUNT; /* one less segment to go */
test SG_COUNT, 0xff jz data_phase_finish; /* Are we done? */
-
- clr A; /* add sizeof(struct scatter) */
- add SG_NEXT[0],SG_SIZEOF;
- adc SG_NEXT[1],A;
-
/*
* Load a struct scatter and set up the data address and length.
* If the working value of the SG count is nonzero, then
@@ -431,49 +759,105 @@ sg_advance:
* This, like all DMA's, assumes little-endian host data storage.
*/
sg_load:
- mvi DINDEX, HADDR;
- mvi SG_NEXT call bcopy_4;
-
- mvi HCNT[0],SG_SIZEOF;
- clr HCNT[1];
- clr HCNT[2];
-
- or DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
-
- call dma_finish;
-
-/*
- * Copy data from FIFO into SCB data pointer and data count. This assumes
- * that the SG segments are of the form:
- *
- * struct ahc_dma_seg {
- * u_int32_t addr; four bytes, little-endian order
- * u_int32_t len; four bytes, little endian order
- * };
- */
- mvi HADDR call dfdat_in_7;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ /*
+ * Do we have any prefetch left???
+ */
+ cmp CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail;
+
+ /*
+ * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes.
+ */
+ add A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT;
+ mvi A, CCSGADDR_MAX;
+ jc . + 2;
+ shl A, 3, SG_COUNT;
+ mov CCHCNT, A;
+ bmov CCHADDR, SG_NEXT, 4;
+ mvi CCSGCTL, CCSGEN|CCSGRESET;
+ test CCSGCTL, CCSGDONE jz .;
+ and CCSGCTL, ~CCSGEN;
+ test CCSGCTL, CCSGEN jnz .;
+ mvi CCSGCTL, CCSGRESET;
+prefetched_segs_avail:
+ bmov HADDR, CCSGRAM, 8;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SG_NEXT call bcopy_4;
+
+ mvi HCNT[0],SG_SIZEOF;
+ clr HCNT[1];
+ clr HCNT[2];
+
+ or DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+
+ call dma_finish;
+
+ /*
+ * Copy data from FIFO into SCB data pointer and data count.
+ * This assumes that the SG segments are of the form:
+ * struct ahc_dma_seg {
+ * u_int32_t addr; four bytes, little-endian order
+ * u_int32_t len; four bytes, little endian order
+ * };
+ */
+ mvi HADDR call dfdat_in_7;
+ }
+
+ if ((ahc->features & AHC_ULTRA2) == 0) {
+ /* Load STCNT as well. It is a mirror of HCNT */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov STCNT, HCNT, 3;
+ } else {
+ call set_stcnt_from_hcnt;
+ }
+ }
+
+/* Advance the SG pointer */
+ clr A; /* add sizeof(struct scatter) */
+ add SG_NEXT[0],SG_SIZEOF;
+ adc SG_NEXT[1],A;
-/* Load STCNT as well. It is a mirror of HCNT */
- call set_stcnt_from_hcnt;
test SSTAT1,PHASEMIS jz data_phase_loop;
+ /* Ensure the last seg is visable at the shaddow layer */
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ or DFCNTRL, PRELOADEN;
+ }
data_phase_finish:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ call ultra2_dmafinish;
+ }
/*
* After a DMA finishes, save the SG and STCNT residuals back into the SCB
* We use STCNT instead of HCNT, since it's a reflection of how many bytes
* were transferred on the SCSI (as opposed to the host) bus.
*/
- mov SCB_RESID_DCNT[0],STCNT[0];
- mov SCB_RESID_DCNT[1],STCNT[1];
- mov SCB_RESID_DCNT[2],STCNT[2];
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov SCB_RESID_DCNT, STCNT, 3;
+ } else {
+ mov SCB_RESID_DCNT[0],STCNT[0];
+ mov SCB_RESID_DCNT[1],STCNT[1];
+ mov SCB_RESID_DCNT[2],STCNT[2];
+ }
mov SCB_RESID_SGCNT, SG_COUNT;
- /* We have seen a data phase */
- or SEQ_FLAGS, DPHASE;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
+ }
+ if ((ahc->flags & AHC_TARGETMODE) != 0) {
+ test SEQ_FLAGS, DPHASE_PENDING jz . + 3;
+ and SEQ_FLAGS, ~DPHASE_PENDING;
+ jmp target_ITloop;
+ }
jmp ITloop;
data_phase_overrun:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ call ultra2_dmafinish;
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
+ }
/*
* Turn off BITBUCKET mode and notify the host
*/
@@ -481,23 +865,62 @@ data_phase_overrun:
mvi INTSTAT,DATA_OVERRUN;
jmp ITloop;
+ultra2_dmafinish:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ test DFCNTRL, DIRECTION jnz ultra2_dmahalt;
+ and DFCNTRL, ~SCSIEN;
+ test DFCNTRL, SCSIEN jnz .;
+ or DFCNTRL, FIFOFLUSH;
+ test DFSTATUS, FIFOEMP jz . - 1;
+ultra2_dmahalt:
+ and DFCNTRL, ~(SCSIEN|HDMAEN);
+ test DFCNTRL, HDMAEN jnz .;
+ ret;
+ }
+
/*
* Command phase. Set up the DMA registers and let 'er rip.
*/
p_command:
call assert;
-/*
- * Load HADDR and HCNT.
- */
- mvi DINDEX, HADDR;
- mvi SCB_CMDPTR call bcopy_5;
- clr HCNT[1];
- clr HCNT[2];
-
- call set_stcnt_from_hcnt;
-
- mvi (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mov HCNT[0], SCB_CMDLEN;
+ bmov HCNT[1], ALLZEROS, 2;
+ if ((ahc->features & AHC_ULTRA2) == 0) {
+ bmov STCNT, HCNT, 3;
+ }
+ add NONE, -17, SCB_CMDLEN;
+ jc dma_cmd_data;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ mvi DFCNTRL, (PRELOADEN|SCSIEN|DIRECTION);
+ } else {
+ mvi DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
+ }
+ bmov DFDAT, SCB_CMDSTORE, 16;
+ jmp cmd_loop;
+dma_cmd_data:
+ bmov HADDR, SCB_CMDPTR, 4;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SCB_CMDPTR call bcopy_5;
+ clr HCNT[1];
+ clr HCNT[2];
+ }
+
+ if ((ahc->features & AHC_ULTRA2) == 0) {
+ if ((ahc->features & AHC_CMD_CHAN) == 0) {
+ call set_stcnt_from_hcnt;
+ }
+ mvi DFCNTRL, (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET);
+ } else {
+ mvi DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
+ }
+cmd_loop:
+ test SSTAT0, SDONE jnz . + 2;
+ test SSTAT1, PHASEMIS jz cmd_loop;
+ and DFCNTRL, ~(SCSIEN|HDMAEN|SDMAEN);
+ test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz .;
jmp ITloop;
/*
@@ -511,54 +934,85 @@ p_status:
jmp ITloop;
/*
- * Message out phase. If there is not an active message, but the target
- * took us into this phase anyway, build a no-op message and send it.
+ * Message out phase. If MSG_OUT is 0x80, build I full indentify message
+ * sequence and send it to the target. In addition, if the MK_MESSAGE bit
+ * is set in the SCB_CONTROL byte, interrupt the host and allow it to send
+ * it's own message.
+ *
+ * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
+ * This is done to allow the hsot to send messages outside of an identify
+ * sequence while protecting the seqencer from testing the MK_MESSAGE bit
+ * on an SCB that might not be for the current nexus. (For example, a
+ * BDR message in responce to a bad reselection would leave us pointed to
+ * an SCB that doesn't have anything to do with the current target).
+ * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
+ * bus device reset).
+ *
+ * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
+ * in case the target decides to put us in this phase for some strange
+ * reason.
*/
p_mesgout:
- test MSG_LEN, 0xff jnz p_mesgout_start;
- mvi MSG_NOOP call mk_mesg; /* build NOP message */
-p_mesgout_start:
+ mov SINDEX, MSG_OUT;
+ cmp SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
+p_mesgout_identify:
+ if ((ahc->features & AHC_WIDE) != 0) {
+ and SINDEX,0xf,SCB_TCL; /* lun */
+ } else {
+ and SINDEX,0x7,SCB_TCL; /* lun */
+ }
+ and A,DISCENB,SCB_CONTROL; /* mask off disconnect privledge */
+ or SINDEX,A; /* or in disconnect privledge */
+ or SINDEX,MSG_IDENTIFYFLAG;
+p_mesgout_mk_message:
+ test SCB_CONTROL,MK_MESSAGE jz p_mesgout_tag;
+ mov SCSIDATL, SINDEX; /* Send the last byte */
+ jmp p_mesgout_from_host + 1;/* Skip HOST_MSG test */
/*
- * Set up automatic PIO transfer from MSG_OUT. Bit 3 in
- * SXFRCTL0 (SPIOEN) is already on.
+ * Send a tag message if TAG_ENB is set in the SCB control block.
+ * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
*/
- mvi SINDEX,MSG_OUT;
- mov DINDEX,MSG_LEN;
-
+p_mesgout_tag:
+ test SCB_CONTROL,TAG_ENB jz p_mesgout_onebyte;
+ mov SCSIDATL, SINDEX; /* Send the identify message */
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ and SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ mov SCB_TAG jmp p_mesgout_onebyte;
/*
- * When target asks for a byte, drop ATN if it's the last one in
- * the message. Otherwise, keep going until the message is exhausted.
- * ATN must be dropped *at least* 90ns before we ack the last byte, so
- * the code is aranged to execute two instructions before the byte is
- * transferred to give a good margin of safety
- *
- * Keep an eye out for a phase change, in case the target issues
- * a MESSAGE REJECT.
+ * Interrupt the driver, and allow it to send a message
+ * if it asks.
*/
-p_mesgout_loop:
- test SSTAT1, REQINIT jz p_mesgout_loop;
- test SSTAT1, SCSIPERR jnz p_mesgout_loop;
- and LASTPHASE, PHASE_MASK, SCSISIGI;
- cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
-p_mesgout_testretry:
- test DINDEX,0xff jnz p_mesgout_dropatn;
- or SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
- jmp p_mesgout_start;
+p_mesgout_from_host:
+ cmp SINDEX, HOST_MSG jne p_mesgout_onebyte;
+ mvi INTSTAT,AWAITING_MSG;
+ nop;
+ /*
+ * Did the host detect a phase change?
+ */
+ cmp RETURN_1, MSGOUT_PHASEMIS je p_mesgout_done;
+
+p_mesgout_onebyte:
+ mvi CLRSINT1, CLRATNO;
+ mov SCSIDATL, SINDEX;
+
/*
* If the next bus phase after ATN drops is a message out, it means
* that the target is requesting that the last message(s) be resent.
*/
-p_mesgout_dropatn:
- cmp DINDEX,1 jne p_mesgout_outb; /* last byte? */
- mvi CLRSINT1,CLRATNO; /* drop ATN */
-p_mesgout_outb:
- dec DINDEX;
- mov SCSIDATL,SINDIR;
- jmp p_mesgout_loop;
+ call phase_lock;
+ cmp LASTPHASE, P_MESGOUT jne p_mesgout_done;
+ or SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
+ jmp p_mesgout;
p_mesgout_done:
mvi CLRSINT1,CLRATNO; /* Be sure to turn ATNO off */
- clr MSG_LEN; /* no active msg */
+ mov LAST_MSG, MSG_OUT;
+ cmp MSG_OUT, MSG_IDENTIFYFLAG jne . + 2;
+ and SCB_CONTROL, ~MK_MESSAGE;
+ mvi MSG_OUT, MSG_NOOP; /* No message left */
jmp ITloop;
/*
@@ -595,8 +1049,8 @@ mesgin_complete:
/*
* We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO,
* and trigger a completion interrupt. Before doing so, check to see if there
- * is a residual or the status byte is something other than NO_ERROR (0). In
- * either of these conditions, we upload the SCB back to the host so it can
+ * is a residual or the status byte is something other than STATUS_GOOD (0).
+ * In either of these conditions, we upload the SCB back to the host so it can
* process this information. In the case of a non zero status byte, we
* additionally interrupt the kernel driver synchronously, allowing it to
* decide if sense should be retrieved. If the kernel driver wishes to request
@@ -614,59 +1068,18 @@ mesgin_complete:
* First check for residuals
*/
test SCB_RESID_SGCNT,0xff jnz upload_scb;
- test SCB_TARGET_STATUS,0xff jz status_ok; /* Good Status? */
+ test SCB_TARGET_STATUS,0xff jz complete; /* Good Status? */
upload_scb:
mvi DMAPARAMS, FIFORESET;
mov SCB_TAG call dma_scb;
check_status:
- test SCB_TARGET_STATUS,0xff jz status_ok; /* Just a residual? */
+ test SCB_TARGET_STATUS,0xff jz complete; /* Just a residual? */
mvi INTSTAT,BAD_STATUS; /* let driver know */
- cmp RETURN_1, SEND_SENSE jne status_ok;
+ nop;
+ cmp RETURN_1, SEND_SENSE jne complete;
/* This SCB becomes the next to execute as it will retrieve sense */
- mov SCB_LINKED_NEXT, SCB_TAG;
- jmp dma_next_scb;
-
-status_ok:
-/* First, mark this target as free. */
- test SCB_CONTROL,TAG_ENB jnz complete; /*
- * Tagged commands
- * don't busy the
- * target.
- */
- mov SAVED_SCBPTR, SCBPTR;
- mov SAVED_LINKPTR, SCB_LINKED_NEXT;
- mov SCB_TCL call index_untagged_scb;
- mov DINDIR, SAVED_LINKPTR;
- mov SCBPTR, SAVED_SCBPTR;
-
-complete:
- /* Post the SCB and issue an interrupt */
-.if ( SCB_PAGING )
- /*
- * Spin loop until there is space
- * in the QOUTFIFO.
- */
- cmp CMDOUTCNT, FIFODEPTH je .;
- inc CMDOUTCNT;
-.endif
- mov QOUTFIFO,SCB_TAG;
- mvi INTSTAT,CMDCMPLT;
- test SCB_CONTROL, ABORT_SCB jz dma_next_scb;
- mvi INTSTAT, ABORT_CMDCMPLT;
-
-dma_next_scb:
- cmp SCB_LINKED_NEXT, SCB_LIST_NULL je add_to_free_list;
-.if !( SCB_PAGING )
- /* Only DMA on top of ourselves if we are the SCB to download */
- mov A, SCB_LINKED_NEXT;
- cmp SCB_TAG, A je dma_next_scb2;
- call add_scb_to_free_list;
- mov SCBPTR, A;
- jmp add_to_waiting_list;
-.endif
-dma_next_scb2:
mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
- mov SCB_LINKED_NEXT call dma_scb;
+ mov SCB_TAG call dma_scb;
add_to_waiting_list:
mov SCB_NEXT,WAITING_SCBH;
mov WAITING_SCBH, SCBPTR;
@@ -676,10 +1089,41 @@ add_to_waiting_list:
*/
call start_selection;
jmp await_busfree;
+
+complete:
+ /* If we are untagged, clear our address up in host ram */
+ test SCB_CONTROL, TAG_ENB jnz complete_queue;
+ mov A, SAVED_TCL;
+ mvi UNTAGGEDSCB_OFFSET call post_byte_setup;
+ mvi SCB_LIST_NULL call post_byte;
+
+complete_queue:
+ mov SCB_TAG call complete_post;
+
add_to_free_list:
call add_scb_to_free_list;
jmp await_busfree;
+complete_post:
+ /* Post the SCBID in SINDEX and issue an interrupt */
+ mov ARG_1, SINDEX;
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ mov A, SDSCB_QOFF;
+ } else {
+ mov A, QOUTPOS;
+ }
+ mvi QOUTFIFO_OFFSET call post_byte_setup;
+ mov ARG_1 call post_byte;
+ if ((ahc->features & AHC_QUEUE_REGS) == 0) {
+ inc QOUTPOS;
+ }
+ if ((ahc->flags & AHC_TARGETMODE) != 0) {
+ test SEQ_FLAGS, NO_DISCONNECT jz . + 3;
+ mvi INTSTAT,TARGET_SYNC_CMD|CMDCMPLT;
+ ret;
+ }
+ mvi INTSTAT,CMDCMPLT ret;
+
/*
* Is it an extended message? Copy the message to our message buffer and
* notify the host. The host will tell us whether to reject this message,
@@ -687,22 +1131,8 @@ add_to_free_list:
* or simply to do nothing.
*/
mesgin_extended:
- mvi MSGIN_EXT_LEN call inb_next;
- mov A, MSGIN_EXT_LEN;
-mesgin_extended_loop:
- mov DINDEX call inb_next;
- dec A;
- cmp DINDEX, MSGIN_EXT_BYTES+3 jne mesgin_extended_loop_test;
- dec DINDEX; /* dump by repeatedly filling the last byte */
-mesgin_extended_loop_test:
- test A, 0xFF jnz mesgin_extended_loop;
-mesgin_extended_intr:
mvi INTSTAT,EXTENDED_MSG; /* let driver know */
- cmp RETURN_1,SEND_REJ je rej_mesgin;
- cmp RETURN_1,SEND_MSG jne mesgin_done;
-/* The kernel has setup a message to be sent */
- or SCSISIGO,ATNO,LASTPHASE; /* turn on ATNO */
- jmp mesgin_done;
+ jmp ITloop;
/*
* Is it a disconnect message? Set a flag in the SCB to remind us
@@ -710,9 +1140,7 @@ mesgin_extended_intr:
*/
mesgin_disconnect:
or SCB_CONTROL,DISCONNECTED;
-.if ( SCB_PAGING )
call add_scb_to_disc_list;
-.endif
jmp await_busfree;
/*
@@ -724,21 +1152,25 @@ mesgin_disconnect:
*/
mesgin_sdptrs:
test SEQ_FLAGS, DPHASE jz mesgin_done;
- mov SCB_SGCOUNT,SG_COUNT;
- /* The SCB SGPTR becomes the next one we'll download */
- mvi DINDEX, SCB_SGPTR;
- mvi SG_NEXT call bcopy_4;
+ /*
+ * The SCB SGPTR becomes the next one we'll download,
+ * and the SCB DATAPTR becomes the current SHADDR.
+ * Use the residual number since STCNT is corrupted by
+ * any message transfer.
+ */
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov SCB_SGCOUNT, SG_COUNT, 5;
+ bmov SCB_DATAPTR, SHADDR, 4;
+ bmov SCB_DATACNT, SCB_RESID_DCNT, 3;
+ } else {
+ mvi DINDEX, SCB_SGCOUNT;
+ mvi SG_COUNT call bcopy_5;
- /* The SCB DATAPTR0 becomes the current SHADDR */
- mvi DINDEX, SCB_DATAPTR;
- mvi SHADDR call bcopy_4;
-
-/*
- * Use the residual number since STCNT is corrupted by any message transfer.
- */
- mvi SCB_RESID_DCNT call bcopy_3;
-
+ mvi DINDEX, SCB_DATAPTR;
+ mvi SHADDR call bcopy_4;
+ mvi SCB_RESID_DCNT call bcopy_3;
+ }
jmp mesgin_done;
/*
@@ -761,24 +1193,32 @@ mesgin_rdptrs:
* clearing the "disconnected" bit so we don't "find" it by accident later.
*/
mesgin_identify:
- test A,0x78 jnz rej_mesgin; /*!DiscPriv|!LUNTAR|!Reserved*/
- and A,0x07; /* lun in lower three bits */
+
+ if ((ahc->features & AHC_WIDE) != 0) {
+ and A,0x0f; /* lun in lower four bits */
+ } else {
+ and A,0x07; /* lun in lower three bits */
+ }
or SAVED_TCL,A; /* SAVED_TCL should be complete now */
- mov SAVED_TCL call index_untagged_scb;
- mov ARG_1, SINDIR;
-.if ( SCB_PAGING )
- cmp ARG_1,SCB_LIST_NULL jne use_findSCB;
-.else
- cmp ARG_1,SCB_LIST_NULL je snoop_tag;
- /* Directly index the SCB */
- mov SCBPTR,ARG_1;
- test SCB_CONTROL,DISCONNECTED jz not_found;
+
+ mvi ARG_2, SCB_LIST_NULL; /* SCBID of prev SCB in disc List */
+ call get_untagged_SCBID;
+ cmp ARG_1, SCB_LIST_NULL je snoop_tag;
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ test SEQ_FLAGS, SCBPTR_VALID jz use_retrieveSCB;
+ }
+ /*
+ * If the SCB was found in the disconnected list (as is
+ * always the case in non-paging scenarios), SCBPTR is already
+ * set to the correct SCB. So, simply setup the SCB and get
+ * on with things.
+ */
+ call rem_scb_from_disc_list;
jmp setup_SCB;
-.endif
/*
* Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
* If we get one, we use the tag returned to find the proper
- * SCB. With SCB paging, this requires using findSCB for both tagged
+ * SCB. With SCB paging, this requires using search for both tagged
* and non-tagged transactions since the SCB may exist in any slot.
* If we're not using SCB paging, we can use the tag as the direct
* index to the SCB.
@@ -786,43 +1226,36 @@ mesgin_identify:
snoop_tag:
mov NONE,SCSIDATL; /* ACK Identify MSG */
snoop_tag_loop:
- test SSTAT1,REQINIT jz snoop_tag_loop;
- test SSTAT1, SCSIPERR jnz snoop_tag_loop;
- and LASTPHASE, PHASE_MASK, SCSISIGI;
+ call phase_lock;
cmp LASTPHASE, P_MESGIN jne not_found;
cmp SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
get_tag:
- or SEQ_FLAGS, TAGGED_SCB;
mvi ARG_1 call inb_next; /* tag value */
-/*
- * See if the tag is in range. The tag is < SCBCOUNT if we add
- * the complement of SCBCOUNT to the incomming tag and there is
- * no carry.
- */
- add SINDEX,COMP_SCBCOUNT,ARG_1;
- jc not_found;
-.if ! ( SCB_PAGING )
-index_by_tag:
- mov SCBPTR,ARG_1;
- mov A, SAVED_TCL;
- cmp SCB_TCL,A jne not_found;
- test SCB_CONTROL,TAG_ENB jz not_found;
- test SCB_CONTROL,DISCONNECTED jz not_found;
-.else
-/*
- * Ensure that the SCB the tag points to is for an SCB transaction
- * to the reconnecting target.
- */
-use_findSCB:
- mov ALLZEROS call findSCB; /* Have to search */
- cmp SINDEX, SCB_LIST_NULL je not_found;
-.endif
+ /*
+ * Ensure that the SCB the tag points to is for
+ * an SCB transaction to the reconnecting target.
+ */
+use_retrieveSCB:
+ call retrieveSCB;
setup_SCB:
+ mov A, SAVED_TCL;
+ cmp SCB_TCL, A jne not_found_cleanup_scb;
+ test SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb;
and SCB_CONTROL,~DISCONNECTED;
or SEQ_FLAGS,IDENTIFY_SEEN; /* make note of IDENTIFY */
+ call set_transfer_settings;
+ /* See if the host wants to send a message upon reconnection */
+ test SCB_CONTROL, MK_MESSAGE jz mesgin_done;
+ and SCB_CONTROL, ~MK_MESSAGE;
+ mvi HOST_MSG call mk_mesg;
jmp mesgin_done;
+not_found_cleanup_scb:
+ test SCB_CONTROL, DISCONNECTED jz . + 3;
+ call add_scb_to_disc_list;
+ jmp not_found;
+ call add_scb_to_free_list;
not_found:
mvi INTSTAT, NO_MATCH;
mvi MSG_BUS_DEV_RESET call mk_mesg;
@@ -847,23 +1280,8 @@ mesgin_reject:
* if there is no active message already. SINDEX is returned intact.
*/
mk_mesg:
- mvi SEQCTL, PAUSEDIS|FASTMODE;
- test MSG_LEN,0xff jz mk_mesg1; /* Should always succeed */
-
- /*
- * Hmmm. For some reason the mesg buffer is in use.
- * Tell the driver. It should look at SINDEX to find
- * out what we wanted to use the buffer for and resolve
- * the conflict.
- */
- mvi SEQCTL,FASTMODE;
- mvi INTSTAT,MSG_BUFFER_BUSY;
-
-mk_mesg1:
or SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */
- mvi MSG_LEN,1; /* length = 1 */
- mov MSG_OUT,SINDEX; /* 1-byte message */
- mvi SEQCTL,FASTMODE ret;
+ mov MSG_OUT,SINDEX ret;
/*
* Functions to read data in Automatic PIO mode.
@@ -899,6 +1317,19 @@ inb_first:
inb_last:
mov NONE,SCSIDATL ret; /*dummy read from latch to ACK*/
+if ((ahc->flags & AHC_TARGETMODE) != 0) {
+ /*
+ * Send a byte to an initiator in Automatic PIO mode.
+ * SPIOEN must be on prior to calling this routine.
+ */
+target_outb:
+ or SXFRCTL0, SPIOEN;
+ test SSTAT0, SPIORDY jz .;
+ mov SCSIDATL, SINDEX;
+ test SSTAT0, SPIORDY jz .;
+ ret;
+}
+
mesgin_phasemis:
/*
* We expected to receive another byte, but the target changed phase
@@ -951,8 +1382,10 @@ dma_halt:
* to drain the data fifo until there is space for the input
* latch to drain and HDMAEN de-asserts.
*/
- mov NONE, DFDAT;
- test DFCNTRL, HDMAEN jnz dma_halt;
+ if ((ahc->features & AHC_ULTRA2) == 0) {
+ mov NONE, DFDAT;
+ }
+ test DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
return:
ret;
@@ -961,60 +1394,162 @@ return:
* message.
*/
assert:
- test SEQ_FLAGS,RESELECTED jz return; /* reselected? */
test SEQ_FLAGS,IDENTIFY_SEEN jnz return; /* seen IDENTIFY? */
mvi INTSTAT,NO_IDENT ret; /* no - tell the kernel */
-.if ( SCB_PAGING )
/*
* Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL)
- * or by the SCBIDn ARG_1. The search begins at the SCB index passed in
- * via SINDEX. If the SCB cannot be found, SINDEX will be SCB_LIST_NULL,
- * otherwise, SCBPTR is set to the proper SCB.
+ * or by the SCBID ARG_1. The search begins at the SCB index passed in
+ * via SINDEX which is an SCB that must be on the disconnected list. If
+ * the SCB cannot be found, SINDEX will be SCB_LIST_NULL, otherwise, SCBPTR
+ * is set to the proper SCB.
*/
findSCB:
- mov SCBPTR,SINDEX; /* switch to next SCB */
+ mov SCBPTR,SINDEX; /* Initialize SCBPTR */
+ cmp ARG_1, SCB_LIST_NULL jne findSCB_by_SCBID;
+ mov A, SAVED_TCL;
+ mvi SCB_TCL jmp findSCB_loop; /* &SCB_TCL -> SINDEX */
+findSCB_by_SCBID:
mov A, ARG_1; /* Tag passed in ARG_1 */
- cmp SCB_TAG,A jne findSCB_loop;
- test SCB_CONTROL,DISCONNECTED jnz foundSCB;/*should be disconnected*/
+ mvi SCB_TAG jmp findSCB_loop; /* &SCB_TAG -> SINDEX */
+findSCB_next:
+ mov ARG_2, SCBPTR;
+ cmp SCB_NEXT, SCB_LIST_NULL je notFound;
+ mov SCBPTR,SCB_NEXT;
+ dec SINDEX; /* Last comparison moved us too far */
findSCB_loop:
- inc SINDEX;
- cmp SINDEX,SCBCOUNT jne findSCB;
+ cmp SINDIR, A jne findSCB_next;
+ mov SINDEX, SCBPTR ret;
+notFound:
+ mvi SINDEX, SCB_LIST_NULL ret;
+
/*
- * We didn't find it. If we're paging, pull an SCB and DMA down the
- * one we want. If we aren't paging or the SCB we dma down has the
- * abort flag set, return not found.
+ * Retrieve an SCB by SCBID first searching the disconnected list falling
+ * back to DMA'ing the SCB down from the host. This routine assumes that
+ * ARG_1 is the SCBID of interrest and that SINDEX is the position in the
+ * disconnected list to start the search from. If SINDEX is SCB_LIST_NULL,
+ * we go directly to the host for the SCB.
+ */
+retrieveSCB:
+ test SEQ_FLAGS, SCBPTR_VALID jz retrieve_from_host;
+ mov SCBPTR call findSCB; /* Continue the search */
+ cmp SINDEX, SCB_LIST_NULL je retrieve_from_host;
+
+/*
+ * This routine expects SINDEX to contain the index of the SCB to be
+ * removed, SCBPTR to be pointing to that SCB, and ARG_2 to be the
+ * SCBID of the SCB just previous to this one in the list or SCB_LIST_NULL
+ * if it is at the head.
*/
- mov ALLZEROS call get_free_or_disc_scb;
- mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
- mov ARG_1 call dma_scb;
- test SCB_RESID_SGCNT, 0xff jz . + 2;
- or SCB_CONTROL, MUST_DMAUP_SCB;
- test SCB_CONTROL, ABORT_SCB jz return;
-find_error:
- mvi SINDEX, SCB_LIST_NULL ret;
-foundSCB:
- test SCB_CONTROL, ABORT_SCB jnz find_error;
rem_scb_from_disc_list:
/* Remove this SCB from the disconnection list */
- cmp SCB_NEXT,SCB_LIST_NULL je unlink_prev;
- mov SAVED_LINKPTR, SCB_PREV;
- mov SCBPTR, SCB_NEXT;
- mov SCB_PREV, SAVED_LINKPTR;
- mov SCBPTR, SINDEX;
-unlink_prev:
- cmp SCB_PREV,SCB_LIST_NULL je rHead;/* At the head of the list */
- mov SAVED_LINKPTR, SCB_NEXT;
- mov SCBPTR, SCB_PREV;
- mov SCB_NEXT, SAVED_LINKPTR;
+ cmp ARG_2, SCB_LIST_NULL je rHead;
+ mov DINDEX, SCB_NEXT;
+ mov SCBPTR, ARG_2;
+ mov SCB_NEXT, DINDEX;
mov SCBPTR, SINDEX ret;
rHead:
mov DISCONNECTED_SCBH,SCB_NEXT ret;
-.else
- ret;
-.endif
+retrieve_from_host:
+/*
+ * We didn't find it. Pull an SCB and DMA down the one we want.
+ * We should never get here in the non-paging case.
+ */
+ mov ALLZEROS call get_free_or_disc_scb;
+ mvi DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+ /* Jump instead of call as we want to return anyway */
+ mov ARG_1 jmp dma_scb;
+
+/*
+ * Determine whether a target is using tagged or non-tagged transactions
+ * by first looking for a matching transaction based on the TCL and if
+ * that fails, looking up this device in the host's untagged SCB array.
+ * The TCL to search for is assumed to be in SAVED_TCL. The value is
+ * returned in ARG_1 (SCB_LIST_NULL for tagged, SCBID for non-tagged).
+ * The SCBPTR_VALID bit is set in SEQ_FLAGS if we found the information
+ * in an SCB instead of having to go to the host.
+ */
+get_untagged_SCBID:
+ cmp DISCONNECTED_SCBH, SCB_LIST_NULL je get_SCBID_from_host;
+ mvi ARG_1, SCB_LIST_NULL;
+ mov DISCONNECTED_SCBH call findSCB;
+ cmp SINDEX, SCB_LIST_NULL je get_SCBID_from_host;
+ or SEQ_FLAGS, SCBPTR_VALID;/* Was in disconnected list */
+ test SCB_CONTROL, TAG_ENB jnz . + 2;
+ mov ARG_1, SCB_TAG ret;
+ mvi ARG_1, SCB_LIST_NULL ret;
+
+/*
+ * Fetch a byte from host memory given an index of (A + (256 * SINDEX))
+ * and a base address of SCBID_ADDR. The byte is returned in RETURN_2.
+ */
+fetch_byte:
+ mov ARG_2, SINDEX;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mvi DINDEX, CCHADDR;
+ mvi SCBID_ADDR call set_1byte_addr;
+ mvi CCHCNT, 1;
+ mvi CCSGCTL, CCSGEN|CCSGRESET;
+ test CCSGCTL, CCSGDONE jz .;
+ mvi CCSGCTL, CCSGRESET;
+ bmov RETURN_2, CCSGRAM, 1 ret;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SCBID_ADDR call set_1byte_addr;
+ mvi HCNT[0], 1;
+ clr HCNT[1];
+ clr HCNT[2];
+ mvi DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+ call dma_finish;
+ mov RETURN_2, DFDAT ret;
+ }
+
+/*
+ * Prepare the hardware to post a byte to host memory given an
+ * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR.
+ */
+post_byte_setup:
+ mov ARG_2, SINDEX;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mvi DINDEX, CCHADDR;
+ mvi SCBID_ADDR call set_1byte_addr;
+ mvi CCHCNT, 1;
+ mvi CCSCBCTL, CCSCBRESET ret;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi SCBID_ADDR call set_1byte_addr;
+ mvi HCNT[0], 1;
+ clr HCNT[1];
+ clr HCNT[2];
+ mvi DFCNTRL, FIFORESET ret;
+ }
+
+post_byte:
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ bmov CCSCBRAM, SINDEX, 1;
+ or CCSCBCTL, CCSCBEN|CCSCBRESET;
+ test CCSCBCTL, CCSCBDONE jz .;
+ clr CCSCBCTL ret;
+ } else {
+ mov DFDAT, SINDEX;
+ or DFCNTRL, HDMAEN|FIFOFLUSH;
+ jmp dma_finish;
+ }
+
+get_SCBID_from_host:
+ mov A, SAVED_TCL;
+ mvi UNTAGGEDSCB_OFFSET call fetch_byte;
+ mov RETURN_1, RETURN_2 ret;
+
+phase_lock:
+ test SSTAT1, REQINIT jz phase_lock;
+ test SSTAT1, SCSIPERR jnz phase_lock;
+ and LASTPHASE, PHASE_MASK, SCSISIGI;
+ mov SCSISIGO, LASTPHASE ret;
+
+if ((ahc->features & AHC_CMD_CHAN) == 0) {
set_stcnt_from_hcnt:
mov STCNT[0], HCNT[0];
mov STCNT[1], HCNT[1];
@@ -1031,58 +1566,120 @@ bcopy_3:
mov DINDIR, SINDIR;
mov DINDIR, SINDIR;
mov DINDIR, SINDIR ret;
+}
-dma_scb:
- /*
- * SCB index is in SINDEX. Determine the physical address in
- * the host where this SCB is located and load HADDR with it.
- */
- shr DINDEX, 3, SINDEX;
- shl A, 5, SINDEX;
- add HADDR[0], A, HSCB_ADDR[0];
- mov A, DINDEX;
- adc HADDR[1], A, HSCB_ADDR[1];
+if ((ahc->flags & AHC_TARGETMODE) != 0) {
+/*
+ * Setup addr assuming that A is an index into
+ * an array of 32byte objects, SINDEX contains
+ * the base address of that array, and DINDEX
+ * contains the base address of the location
+ * to store the indexed address.
+ */
+set_32byte_addr:
+ shr ARG_2, 3, A;
+ shl A, 5;
+ jmp set_1byte_addr;
+}
+
+/*
+ * Setup addr assuming that A is an index into
+ * an array of 64byte objects, SINDEX contains
+ * the base address of that array, and DINDEX
+ * contains the base address of the location
+ * to store the indexed address.
+ */
+set_64byte_addr:
+ shr ARG_2, 2, A;
+ shl A, 6;
+
+/*
+ * Setup addr assuming that A + (ARG_1 * 256) is an
+ * index into an array of 1byte objects, SINDEX contains
+ * the base address of that array, and DINDEX contains
+ * the base address of the location to store the computed
+ * address.
+ */
+set_1byte_addr:
+ add DINDIR, A, SINDIR;
+ mov A, ARG_2;
+ adc DINDIR, A, SINDIR;
clr A;
- adc HADDR[2], A, HSCB_ADDR[2];
- adc HADDR[3], A, HSCB_ADDR[3];
- /* Setup Count */
- mvi HCNT[0], 28;
- clr HCNT[1];
- clr HCNT[2];
- mov DFCNTRL, DMAPARAMS;
- test DMAPARAMS, DIRECTION jnz dma_scb_fromhost;
- /* Fill it with the SCB data */
+ adc DINDIR, A, SINDIR;
+ adc DINDIR, A, SINDIR ret;
+
+/*
+ * Either post or fetch and SCB from host memory based on the
+ * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX.
+ */
+dma_scb:
+ mov A, SINDEX;
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ mvi DINDEX, CCHADDR;
+ mvi HSCB_ADDR call set_64byte_addr;
+ mov CCSCBPTR, SCBPTR;
+ test DMAPARAMS, DIRECTION jz dma_scb_tohost;
+ mvi CCHCNT, SCB_64BYTE_SIZE;
+ mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
+ cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
+ jmp dma_scb_finish;
+dma_scb_tohost:
+ mvi CCHCNT, SCB_32BYTE_SIZE;
+ if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+ mvi CCSCBCTL, CCSCBRESET;
+ bmov CCSCBRAM, SCB_CONTROL, SCB_32BYTE_SIZE;
+ or CCSCBCTL, CCSCBEN|CCSCBRESET;
+ test CCSCBCTL, CCSCBDONE jz .;
+ } else {
+ mvi CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
+ cmp CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
+ }
+dma_scb_finish:
+ clr CCSCBCTL;
+ test CCSCBCTL, CCARREN|CCSCBEN jnz .;
+ ret;
+ } else {
+ mvi DINDEX, HADDR;
+ mvi HSCB_ADDR call set_64byte_addr;
+ mvi HCNT[0], SCB_32BYTE_SIZE;
+ clr HCNT[1];
+ clr HCNT[2];
+ mov DFCNTRL, DMAPARAMS;
+ test DMAPARAMS, DIRECTION jnz dma_scb_fromhost;
+ /* Fill it with the SCB data */
copy_scb_tofifo:
- mvi SINDEX, SCB_CONTROL;
- add A, 28, SINDEX;
+ mvi SINDEX, SCB_CONTROL;
+ add A, SCB_32BYTE_SIZE, SINDEX;
copy_scb_tofifo_loop:
- mov DFDAT,SINDIR;
- mov DFDAT,SINDIR;
- mov DFDAT,SINDIR;
- mov DFDAT,SINDIR;
- mov DFDAT,SINDIR;
- mov DFDAT,SINDIR;
- mov DFDAT,SINDIR;
- cmp SINDEX, A jne copy_scb_tofifo_loop;
- or DFCNTRL, HDMAEN|FIFOFLUSH;
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+ mov DFDAT,SINDIR;
+ cmp SINDEX, A jne copy_scb_tofifo_loop;
+ or DFCNTRL, HDMAEN|FIFOFLUSH;
dma_scb_fromhost:
- call dma_finish;
- /* If we were putting the SCB, we are done */
- test DMAPARAMS, DIRECTION jz return;
- mvi SCB_CONTROL call dfdat_in_7;
- call dfdat_in_7_continued;
- call dfdat_in_7_continued;
- jmp dfdat_in_7_continued;
+ call dma_finish;
+ /* If we were putting the SCB, we are done */
+ test DMAPARAMS, DIRECTION jz return;
+ mvi SCB_CONTROL call dfdat_in_7;
+ call dfdat_in_7_continued;
+ call dfdat_in_7_continued;
+ jmp dfdat_in_7_continued;
dfdat_in_7:
- mov DINDEX,SINDEX;
+ mov DINDEX,SINDEX;
dfdat_in_7_continued:
- mov DINDIR,DFDAT;
- mov DINDIR,DFDAT;
- mov DINDIR,DFDAT;
- mov DINDIR,DFDAT;
- mov DINDIR,DFDAT;
- mov DINDIR,DFDAT;
- mov DINDIR,DFDAT ret;
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT;
+ mov DINDIR,DFDAT ret;
+ }
+
/*
* Wait for DMA from host memory to data FIFO to complete, then disable
@@ -1095,27 +1692,14 @@ dma_finish:
test DFCNTRL, HDMAEN jnz .;
ret;
-index_untagged_scb:
- mov DINDEX, SINDEX;
- shr DINDEX, 4;
- and DINDEX, 0x03; /* Bottom two bits of tid */
- add DINDEX, SCB_BUSYTARGETS;
- shr A, 6, SINDEX; /* Target ID divided by 4 */
- test SINDEX, SELBUSB jz index_untagged_scb2;
- add A, 2; /* Add 2 positions */
-index_untagged_scb2:
- mov SCBPTR, A; /*
- * Select the SCB with this
- * target's information.
- */
- mov SINDEX, DINDEX ret;
-
add_scb_to_free_list:
- mov SCB_NEXT, FREE_SCBH;
- mvi SCB_TAG, SCB_LIST_NULL;
- mov FREE_SCBH, SCBPTR ret;
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ mov SCB_NEXT, FREE_SCBH;
+ mov FREE_SCBH, SCBPTR;
+ }
+ mvi SCB_TAG, SCB_LIST_NULL ret;
-.if ( SCB_PAGING )
+if ((ahc->flags & AHC_PAGESCBS) != 0) {
get_free_or_disc_scb:
cmp FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
cmp DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
@@ -1123,25 +1707,15 @@ return_error:
mvi SINDEX, SCB_LIST_NULL ret;
dequeue_disc_scb:
mov SCBPTR, DISCONNECTED_SCBH;
-/*
- * If we have a residual, then we are in the middle of some I/O
- * and we have to send this SCB back up to the kernel so that the
- * saved data pointers and residual information isn't lost.
- */
- test SCB_CONTROL, MUST_DMAUP_SCB jz . + 3;
- and SCB_CONTROL, ~MUST_DMAUP_SCB;
- jmp dma_up_scb;
- test SCB_RESID_SGCNT,0xff jnz dma_up_scb;
- cmp SCB_LINKED_NEXT, SCB_LIST_NULL je unlink_disc_scb;
dma_up_scb:
mvi DMAPARAMS, FIFORESET;
mov SCB_TAG call dma_scb;
unlink_disc_scb:
- /* jmp instead of call since we want to return anyway */
- mov SCBPTR jmp rem_scb_from_disc_list;
+ mov DISCONNECTED_SCBH, SCB_NEXT ret;
dequeue_free_scb:
mov SCBPTR, FREE_SCBH;
mov FREE_SCBH, SCB_NEXT ret;
+}
add_scb_to_disc_list:
/*
@@ -1149,11 +1723,5 @@ add_scb_to_disc_list:
* candidates for paging out an SCB if one is needed for a new command.
* Modifying the disconnected list is a critical(pause dissabled) section.
*/
- mvi SCB_PREV, SCB_LIST_NULL;
mov SCB_NEXT, DISCONNECTED_SCBH;
- mov DISCONNECTED_SCBH, SCBPTR;
- cmp SCB_NEXT,SCB_LIST_NULL je return;
- mov SCBPTR,SCB_NEXT;
- mov SCB_PREV,DISCONNECTED_SCBH;
- mov SCBPTR,DISCONNECTED_SCBH ret;
-.endif
+ mov DISCONNECTED_SCBH, SCBPTR ret;
diff --git a/sys/dev/aic7xxx/aic7xxx_93cx6.c b/sys/dev/aic7xxx/aic7xxx_93cx6.c
new file mode 100644
index 0000000..d7f8ab5
--- /dev/null
+++ b/sys/dev/aic7xxx/aic7xxx_93cx6.c
@@ -0,0 +1,174 @@
+/*
+ * Interface for the 93C66/56/46/26/06 serial eeprom parts.
+ *
+ * Copyright (c) 1995, 1996 Daniel M. Eischen
+ * 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 immediately at the beginning of the file, without modification,
+ * 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. Absolutely no warranty of function or purpose is made by the author
+ * Daniel M. Eischen.
+ * 4. Modifications may be freely made to this file if the above conditions
+ * are met.
+ *
+ * $Id: 93cx6.c,v 1.10 1997/02/22 09:38:36 peter Exp $
+ */
+
+/*
+ * The instruction set of the 93C66/56/46/26/06 chips are as follows:
+ *
+ * Start OP *
+ * Function Bit Code Address** Data Description
+ * -------------------------------------------------------------------
+ * READ 1 10 A5 - A0 Reads data stored in memory,
+ * starting at specified address
+ * EWEN 1 00 11XXXX Write enable must preceed
+ * all programming modes
+ * ERASE 1 11 A5 - A0 Erase register A5A4A3A2A1A0
+ * WRITE 1 01 A5 - A0 D15 - D0 Writes register
+ * ERAL 1 00 10XXXX Erase all registers
+ * WRAL 1 00 01XXXX D15 - D0 Writes to all registers
+ * EWDS 1 00 00XXXX Disables all programming
+ * instructions
+ * *Note: A value of X for address is a don't care condition.
+ * **Note: There are 8 address bits for the 93C56/66 chips unlike
+ * the 93C46/26/06 chips which have 6 address bits.
+ *
+ * The 93C46 has a four wire interface: clock, chip select, data in, and
+ * data out. In order to perform one of the above functions, you need
+ * to enable the chip select for a clock period (typically a minimum of
+ * 1 usec, with the clock high and low a minimum of 750 and 250 nsec
+ * respectively). While the chip select remains high, you can clock in
+ * the instructions (above) starting with the start bit, followed by the
+ * OP code, Address, and Data (if needed). For the READ instruction, the
+ * requested 16-bit register contents is read from the data out line but
+ * is preceded by an initial zero (leading 0, followed by 16-bits, MSB
+ * first). The clock cycling from low to high initiates the next data
+ * bit to be sent from the chip.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <machine/bus_memio.h>
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <dev/aic7xxx/93cx6.h>
+
+/*
+ * Right now, we only have to read the SEEPROM. But we make it easier to
+ * add other 93Cx6 functions.
+ */
+static struct seeprom_cmd {
+ unsigned char len;
+ unsigned char bits[3];
+} seeprom_read = {3, {1, 1, 0}};
+
+/*
+ * Wait for the SEERDY to go high; about 800 ns.
+ */
+#define CLOCK_PULSE(sd, rdy) \
+ while ((SEEPROM_STATUS_INB(sd) & rdy) == 0) { \
+ ; /* Do nothing */ \
+ } \
+ (void)SEEPROM_INB(sd); /* Clear clock */
+
+/*
+ * Read the serial EEPROM and returns 1 if successful and 0 if
+ * not successful.
+ */
+int
+read_seeprom(sd, buf, start_addr, count)
+ struct seeprom_descriptor *sd;
+ u_int16_t *buf;
+ bus_size_t start_addr;
+ bus_size_t count;
+{
+ int i = 0;
+ u_int k = 0;
+ u_int16_t v;
+ u_int8_t temp;
+
+ /*
+ * Read the requested registers of the seeprom. The loop
+ * will range from 0 to count-1.
+ */
+ for (k = start_addr; k < count + start_addr; k++) {
+ /* Send chip select for one clock cycle. */
+ temp = sd->sd_MS ^ sd->sd_CS;
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+
+ /*
+ * Now we're ready to send the read command followed by the
+ * address of the 16-bit register we want to read.
+ */
+ for (i = 0; i < seeprom_read.len; i++) {
+ if (seeprom_read.bits[i] != 0)
+ temp ^= sd->sd_DO;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ if (seeprom_read.bits[i] != 0)
+ temp ^= sd->sd_DO;
+ }
+ /* Send the 6 or 8 bit address (MSB first, LSB last). */
+ for (i = (sd->sd_chip - 1); i >= 0; i--) {
+ if ((k & (1 << i)) != 0)
+ temp ^= sd->sd_DO;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ if ((k & (1 << i)) != 0)
+ temp ^= sd->sd_DO;
+ }
+
+ /*
+ * Now read the 16 bit register. An initial 0 precedes the
+ * register contents which begins with bit 15 (MSB) and ends
+ * with bit 0 (LSB). The initial 0 will be shifted off the
+ * top of our word as we let the loop run from 0 to 16.
+ */
+ v = 0;
+ for (i = 16; i >= 0; i--) {
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ v <<= 1;
+ if (SEEPROM_DATA_INB(sd) & sd->sd_DI)
+ v |= 1;
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ }
+
+ buf[k - start_addr] = v;
+
+ /* Reset the chip select for the next command cycle. */
+ temp = sd->sd_MS;
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ SEEPROM_OUTB(sd, temp);
+ CLOCK_PULSE(sd, sd->sd_RDY);
+ }
+#ifdef 93CX6_DUMP_EEPROM
+ printf("\nSerial EEPROM:");
+ for (k = 0; k < count; k = k + 1) {
+ if (((k % 8) == 0) && (k != 0)) {
+ printf ("\n ");
+ }
+ printf (" 0x%x", buf[k]);
+ }
+ printf ("\n");
+#endif
+ return (1);
+}
diff --git a/sys/dev/aic7xxx/aic7xxx_93cx6.h b/sys/dev/aic7xxx/aic7xxx_93cx6.h
new file mode 100644
index 0000000..32645e1
--- /dev/null
+++ b/sys/dev/aic7xxx/aic7xxx_93cx6.h
@@ -0,0 +1,95 @@
+/*
+ * Interface to the 93C46 serial EEPROM that is used to store BIOS
+ * settings for the aic7xxx based adaptec SCSI controllers. It can
+ * also be used for 93C26 and 93C06 serial EEPROMS.
+ *
+ * Copyright (c) 1994, 1995 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$
+ */
+
+#include <sys/param.h>
+#if !defined(__NetBSD__)
+#include <sys/systm.h>
+#endif
+
+#ifdef KERNEL
+
+typedef enum {
+ C46 = 6,
+ C56_66 = 8
+} seeprom_chip_t;
+
+struct seeprom_descriptor {
+ bus_space_tag_t sd_tag;
+ bus_space_handle_t sd_bsh;
+ bus_size_t sd_control_offset;
+ bus_size_t sd_status_offset;
+ bus_size_t sd_dataout_offset;
+ seeprom_chip_t sd_chip;
+ u_int16_t sd_MS;
+ u_int16_t sd_RDY;
+ u_int16_t sd_CS;
+ u_int16_t sd_CK;
+ u_int16_t sd_DO;
+ u_int16_t sd_DI;
+};
+
+/*
+ * This function will read count 16-bit words from the serial EEPROM and
+ * return their value in buf. The port address of the aic7xxx serial EEPROM
+ * control register is passed in as offset. The following parameters are
+ * also passed in:
+ *
+ * CS - Chip select
+ * CK - Clock
+ * DO - Data out
+ * DI - Data in
+ * RDY - SEEPROM ready
+ * MS - Memory port mode select
+ *
+ * A failed read attempt returns 0, and a successful read returns 1.
+ */
+
+#define SEEPROM_INB(sd) \
+ bus_space_read_1(sd->sd_tag, sd->sd_bsh, sd->sd_control_offset)
+#define SEEPROM_OUTB(sd, value) \
+ bus_space_write_1(sd->sd_tag, sd->sd_bsh, sd->sd_control_offset, value)
+#define SEEPROM_STATUS_INB(sd) \
+ bus_space_read_1(sd->sd_tag, sd->sd_bsh, sd->sd_status_offset)
+#define SEEPROM_DATA_INB(sd) \
+ bus_space_read_1(sd->sd_tag, sd->sd_bsh, sd->sd_dataout_offset)
+
+int read_seeprom(struct seeprom_descriptor *sd, u_int16_t *buf,
+ bus_size_t start_addr, bus_size_t count);
+
+#endif /* KERNEL */
diff --git a/sys/dev/aic7xxx/aicasm.c b/sys/dev/aic7xxx/aicasm.c
index c05950d..66d30ce 100644
--- a/sys/dev/aic7xxx/aicasm.c
+++ b/sys/dev/aic7xxx/aicasm.c
@@ -1,7 +1,7 @@
/*
* Aic7xxx SCSI host adapter firmware asssembler
*
- * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 1997, 1998 Justin T. Gibbs.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -10,10 +10,7 @@
* 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
+ * 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
@@ -28,7 +25,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aicasm.c,v 1.18 1997/06/27 19:38:45 gibbs Exp $
+ * $Id: aicasm.c,v 1.19 1997/09/03 03:44:38 gibbs Exp $
*/
#include <sys/types.h>
#include <sys/mman.h>
@@ -43,13 +40,24 @@
#include "aicasm_symbol.h"
#include "sequencer.h"
-static void usage __P((void));
-static void back_patch __P((void));
-static void output_code __P((FILE *ofile));
-static void output_listing __P((FILE *listfile, char *ifilename,
- char *options));
-static struct patch *next_patch __P((struct patch *cur_patch, int options,
- int instrptr));
+typedef struct patch {
+ STAILQ_ENTRY(patch) links;
+ int patch_func;
+ u_int begin;
+ u_int skip_instr;
+ u_int skip_patch;
+} patch_t;
+
+STAILQ_HEAD(patch_list, patch) patches;
+
+static void usage(void);
+static void back_patch(void);
+static void output_code(FILE *ofile);
+static void output_listing(FILE *listfile, char *ifilename);
+static int dump_scope(scope_t *scope);
+static void emit_patch(scope_t *scope, int patch);
+static int check_patch(patch_t **start_patch, int start_instr,
+ int *skip_addr, int *func_vals);
struct path_list search_path;
int includes_search_curdir;
@@ -62,8 +70,8 @@ char *listfilename;
FILE *listfile;
static STAILQ_HEAD(,instruction) seq_program;
-static STAILQ_HEAD(, patch) patch_list;
-symlist_t patch_options;
+struct scope_list scope_stack;
+symlist_t patch_functions;
#if DEBUG
extern int yy_flex_debug;
@@ -82,19 +90,24 @@ main(argc, argv)
int ch;
int retval;
char *inputfilename;
- char *options;
+ scope_t *sentinal;
+ STAILQ_INIT(&patches);
SLIST_INIT(&search_path);
STAILQ_INIT(&seq_program);
- STAILQ_INIT(&patch_list);
- SLIST_INIT(&patch_options);
+ SLIST_INIT(&scope_stack);
+
+ /* Set Sentinal scope node */
+ sentinal = scope_alloc();
+ sentinal->type = SCOPE_ROOT;
+
includes_search_curdir = 1;
appname = *argv;
regfile = NULL;
listfile = NULL;
- options = NULL;
#if DEBUG
yy_flex_debug = 0;
+ yydebug = 0;
#endif
while ((ch = getopt(argc, argv, "d:l:n:o:r:I:O:")) != -1) {
switch(ch) {
@@ -138,10 +151,6 @@ main(argc, argv)
}
ofilename = optarg;
break;
- case 'O':
- /* Patches to include in the listing */
- options = optarg;
- break;
case 'r':
if ((regfile = fopen(optarg, "w")) == NULL) {
perror(optarg);
@@ -207,13 +216,33 @@ main(argc, argv)
include_file(*argv, SOURCE_FILE);
retval = yyparse();
if (retval == 0) {
+ if (SLIST_FIRST(&scope_stack) == NULL
+ || SLIST_FIRST(&scope_stack)->type != SCOPE_ROOT) {
+ stop("Unterminated conditional expression",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+
+ /* Process outmost scope */
+ process_scope(SLIST_FIRST(&scope_stack));
+ /*
+ * Decend the tree of scopes and insert/emit
+ * patches as appropriate. We perform a depth first
+ * tranversal, recursively handling each scope.
+ */
+ /* start at the root scope */
+ dump_scope(SLIST_FIRST(&scope_stack));
+
+ /* Patch up forward jump addresses */
back_patch();
+
if (ofile != NULL)
output_code(ofile);
- if (regfile != NULL)
+ if (regfile != NULL) {
symtable_dump(regfile);
+ }
if (listfile != NULL)
- output_listing(listfile, inputfilename, options);
+ output_listing(listfile, inputfilename);
}
stop(NULL, 0);
@@ -228,7 +257,7 @@ usage()
(void)fprintf(stderr,
"usage: %-16s [-nostdinc] [-I-] [-I directory] [-o output_file]
[-r register_output_file] [-l program_list_file]
- [-O option_name[|options_name2]] input_file\n",
+ input_file\n",
appname);
exit(EX_USAGE);
}
@@ -255,12 +284,9 @@ back_patch()
/* NOTREACHED */
}
f3_instr = &cur_instr->format.format3;
- address = ((f3_instr->opcode_addr & ADDR_HIGH_BIT) << 8)
- | f3_instr->address;
+ address = f3_instr->address;
address += cur_instr->patch_label->info.linfo->address;
- f3_instr->opcode_addr &= ~ADDR_HIGH_BIT;
- f3_instr->opcode_addr |= (address >> 8) & ADDR_HIGH_BIT;
- f3_instr->address = address & 0xFF;
+ f3_instr->address = address;
}
}
}
@@ -284,6 +310,7 @@ output_code(ofile)
for(cur_instr = seq_program.stqh_first;
cur_instr != NULL;
cur_instr = cur_instr->links.stqe_next) {
+
fprintf(ofile, "\t0x%02x, 0x%02x, 0x%02x, 0x%02x,\n",
cur_instr->format.bytes[0],
cur_instr->format.bytes[1],
@@ -291,60 +318,128 @@ output_code(ofile)
cur_instr->format.bytes[3]);
instrcount++;
}
- fprintf(ofile, "};\n");
+ fprintf(ofile, "};\n\n");
/*
- * Output the patch list, option definitions first.
+ * Output patch information. Patch functions first.
*/
- for(cur_node = patch_options.slh_first;
+ for(cur_node = SLIST_FIRST(&patch_functions);
cur_node != NULL;
- cur_node = cur_node->links.sle_next) {
- fprintf(ofile, "#define\t%-16s\t0x%x\n", cur_node->symbol->name,
- cur_node->symbol->info.condinfo->value);
+ cur_node = SLIST_NEXT(cur_node,links)) {
+ fprintf(ofile,
+"static int ahc_patch%d_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch%d_func(struct ahc_softc *ahc)
+{
+ return (%s);
+}\n\n",
+ cur_node->symbol->info.condinfo->func_num,
+ cur_node->symbol->info.condinfo->func_num,
+ cur_node->symbol->name);
}
fprintf(ofile,
-"struct patch {
- int options;
- int negative;
- int begin;
- int end;
+"typedef int patch_func_t __P((struct ahc_softc *));
+struct patch {
+ patch_func_t *patch_func;
+ u_int32_t begin :10,
+ skip_instr :10,
+ skip_patch :12;
} patches[] = {\n");
- for(cur_patch = patch_list.stqh_first;
+ for(cur_patch = STAILQ_FIRST(&patches);
cur_patch != NULL;
- cur_patch = cur_patch->links.stqe_next)
+ cur_patch = STAILQ_NEXT(cur_patch,links)) {
+ fprintf(ofile, "\t{ ahc_patch%d_func, %d, %d, %d },\n",
+ cur_patch->patch_func, cur_patch->begin,
+ cur_patch->skip_instr, cur_patch->skip_patch);
+ }
- fprintf(ofile, "\t{ 0x%08x, %d, 0x%03x, 0x%03x },\n",
- cur_patch->options, cur_patch->negative, cur_patch->begin,
- cur_patch->end);
+ fprintf(ofile, "\n};\n");
- fprintf(ofile, "\t{ 0x%08x, %d, 0x%03x, 0x%03x }\n};\n",
- 0, 0, 0, 0);
-
fprintf(stderr, "%s: %d instructions used\n", appname, instrcount);
}
+static int
+dump_scope(scope_t *scope)
+{
+ scope_t *cur_scope;
+ int patches_emitted = 0;
+
+ /*
+ * Emit the first patch for this scope
+ */
+ emit_patch(scope, 0);
+
+ /*
+ * Dump each scope within this one.
+ */
+ cur_scope = TAILQ_FIRST(&scope->inner_scope);
+
+ while (cur_scope != NULL) {
+
+ dump_scope(cur_scope);
+
+ cur_scope = TAILQ_NEXT(cur_scope, scope_links);
+ }
+
+ /*
+ * Emit the second, closing, patch for this scope
+ */
+ emit_patch(scope, 1);
+}
+
+void
+emit_patch(scope_t *scope, int patch)
+{
+ patch_info_t *pinfo;
+ patch_t *new_patch;
+
+ pinfo = &scope->patches[patch];
+
+ if (pinfo->skip_instr == 0)
+ /* No-Op patch */
+ return;
+
+ new_patch = (patch_t *)malloc(sizeof(*new_patch));
+
+ if (new_patch == NULL)
+ stop("Could not malloc patch structure", EX_OSERR);
+
+ memset(new_patch, 0, sizeof(*new_patch));
+
+ if (patch == 0) {
+ new_patch->patch_func = scope->func_num;
+ new_patch->begin = scope->begin_addr;
+ } else {
+ new_patch->patch_func = 0;
+ new_patch->begin = scope->end_addr;
+ }
+ new_patch->skip_instr = pinfo->skip_instr;
+ new_patch->skip_patch = pinfo->skip_patch;
+ STAILQ_INSERT_TAIL(&patches, new_patch, links);
+}
+
void
-output_listing(listfile, ifilename, patches)
- FILE *listfile;
- char *ifilename;
- char *patches;
+output_listing(FILE *listfile, char *ifilename)
{
+ char buf[1024];
FILE *ifile;
- int line;
struct instruction *cur_instr;
+ patch_t *cur_patch;
+ symbol_node_t *cur_func;
+ int *func_values;
int instrcount;
int instrptr;
- char buf[1024];
- patch_t *cur_patch;
- char *option_spec;
- int options;
+ int line;
+ int func_count;
+ int skip_addr;
instrcount = 0;
instrptr = 0;
line = 1;
- options = 1; /* All code outside of patch blocks */
+ skip_addr = 0;
if ((ifile = fopen(ifilename, "r")) == NULL) {
perror(ifilename);
stop(NULL, EX_DATAERR);
@@ -353,31 +448,66 @@ output_listing(listfile, ifilename, patches)
/*
* Determine which options to apply to this listing.
*/
- while ((option_spec = strsep(&patches, "|")) != NULL) {
- symbol_t *symbol;
-
- symbol = symtable_get(option_spec);
- if (symbol->type != CONDITIONAL) {
- stop("Invalid option specified in patch list for "
- "program listing", EX_USAGE);
- /* NOTREACHED */
+ for (func_count = 0, cur_func = SLIST_FIRST(&patch_functions);
+ cur_func != NULL;
+ cur_func = SLIST_NEXT(cur_func, links))
+ func_count++;
+
+ if (func_count != 0) {
+ func_values = (int *)malloc(func_count * sizeof(int));
+
+ if (func_values == NULL)
+ stop("Could not malloc", EX_OSERR);
+
+ func_values[0] = 0; /* FALSE func */
+ func_count--;
+
+ /*
+ * Ask the user to fill in the return values for
+ * the rest of the functions.
+ */
+
+
+ for (cur_func = SLIST_FIRST(&patch_functions);
+ cur_func != NULL && SLIST_NEXT(cur_func, links) != NULL;
+ cur_func = SLIST_NEXT(cur_func, links), func_count--) {
+ int input;
+
+ fprintf(stdout, "\n(%s)\n", cur_func->symbol->name);
+ fprintf(stdout,
+ "Enter the return value for "
+ "this expression[T/F]:");
+
+ while (1) {
+
+ input = getchar();
+ input = toupper(input);
+
+ if (input == 'T') {
+ func_values[func_count] = 1;
+ break;
+ } else if (input == 'F') {
+ func_values[func_count] = 0;
+ break;
+ }
+ }
}
- options |= symbol->info.condinfo->value;
+ fprintf(stdout, "\nThanks!\n");
}
- cur_patch = patch_list.stqh_first;
- for(cur_instr = seq_program.stqh_first;
+ /* Now output the listing */
+ cur_patch = STAILQ_FIRST(&patches);
+ for(cur_instr = STAILQ_FIRST(&seq_program);
cur_instr != NULL;
- cur_instr = cur_instr->links.stqe_next,instrcount++) {
+ cur_instr = STAILQ_NEXT(cur_instr, links), instrcount++) {
- cur_patch = next_patch(cur_patch, options, instrcount);
- if (cur_patch
- && cur_patch->begin <= instrcount
- && cur_patch->end > instrcount)
+ if (check_patch(&cur_patch, instrcount,
+ &skip_addr, func_values) == 0) {
/* Don't count this instruction as it is in a patch
* that was removed.
*/
continue;
+ }
while (line < cur_instr->srcline) {
fgets(buf, sizeof(buf), ifile);
@@ -401,29 +531,39 @@ output_listing(listfile, ifilename, patches)
fclose(ifile);
}
-static struct patch *
-next_patch(cur_patch, options, instrptr)
- struct patch *cur_patch;
- int options;
- int instrptr;
+static int
+check_patch(patch_t **start_patch, int start_instr,
+ int *skip_addr, int *func_vals)
{
- while(cur_patch != NULL) {
- if (((cur_patch->options & options) != 0
- && cur_patch->negative == FALSE)
- || ((cur_patch->options & options) == 0
- && cur_patch->negative == TRUE)
- || (instrptr >= cur_patch->end)) {
- /*
- * Either we want to keep this section of code,
- * or we have consumed this patch. Skip to the
- * next patch.
+ patch_t *cur_patch;
+
+ cur_patch = *start_patch;
+
+ while (cur_patch != NULL && start_instr == cur_patch->begin) {
+ if (func_vals[cur_patch->patch_func] == 0) {
+ int skip;
+
+ /* Start rejecting code */
+ *skip_addr = start_instr + cur_patch->skip_instr;
+ for (skip = cur_patch->skip_patch;
+ skip > 0 && cur_patch != NULL;
+ skip--)
+ cur_patch = STAILQ_NEXT(cur_patch, links);
+ } else {
+ /* Accepted this patch. Advance to the next
+ * one and wait for our intruction pointer to
+ * hit this point.
*/
- cur_patch = cur_patch->links.stqe_next;
- } else
- /* Found an okay patch */
- break;
+ cur_patch = STAILQ_NEXT(cur_patch, links);
+ }
}
- return (cur_patch);
+
+ *start_patch = cur_patch;
+ if (start_instr < *skip_addr)
+ /* Still skipping */
+ return (0);
+
+ return (1);
}
/*
@@ -471,7 +611,7 @@ stop(string, err_code)
}
}
- symlist_free(&patch_options);
+ symlist_free(&patch_functions);
symtable_close();
exit(err_code);
@@ -491,15 +631,85 @@ seq_alloc()
return new_instr;
}
-patch_t *
-patch_alloc()
+scope_t *
+scope_alloc()
{
- patch_t *new_patch;
+ scope_t *new_scope;
- new_patch = (patch_t *)malloc(sizeof(patch_t));
- if (new_patch == NULL)
- stop("Unable to malloc patch object", EX_SOFTWARE);
- memset(new_patch, 0, sizeof(*new_patch));
- STAILQ_INSERT_TAIL(&patch_list, new_patch, links);
- return new_patch;
+ new_scope = (scope_t *)malloc(sizeof(scope_t));
+ if (new_scope == NULL)
+ stop("Unable to malloc scope object", EX_SOFTWARE);
+ memset(new_scope, 0, sizeof(*new_scope));
+ TAILQ_INIT(&new_scope->inner_scope);
+
+ if (SLIST_FIRST(&scope_stack) != NULL) {
+ TAILQ_INSERT_TAIL(&SLIST_FIRST(&scope_stack)->inner_scope,
+ new_scope, scope_links);
+ }
+ /* This patch is now the current scope */
+ SLIST_INSERT_HEAD(&scope_stack, new_scope, scope_stack_links);
+ return new_scope;
+}
+
+void
+process_scope(scope_t *scope)
+{
+ /*
+ * We are "leaving" this scope. We should now have
+ * enough information to process the lists of scopes
+ * we encapsulate.
+ */
+ scope_t *cur_scope;
+ u_int skip_patch_count;
+ u_int skip_instr_count;
+
+ cur_scope = TAILQ_LAST(&scope->inner_scope, scope_tailq);
+ skip_patch_count = 0;
+ skip_instr_count = 0;
+ while (cur_scope != NULL) {
+ u_int patch0_patch_skip;
+
+ patch0_patch_skip = 0;
+ switch (cur_scope->type) {
+ case SCOPE_IF:
+ case SCOPE_ELSE_IF:
+ if (skip_instr_count != 0) {
+ /* Create a tail patch */
+ patch0_patch_skip++;
+ cur_scope->patches[1].skip_patch =
+ skip_patch_count + 1;
+ cur_scope->patches[1].skip_instr =
+ skip_instr_count;
+ }
+
+ /* Count Head patch */
+ patch0_patch_skip++;
+
+ /* Count any patches contained in our inner scope */
+ patch0_patch_skip += cur_scope->inner_scope_patches;
+
+ cur_scope->patches[0].skip_patch = patch0_patch_skip;
+ cur_scope->patches[0].skip_instr =
+ cur_scope->end_addr - cur_scope->begin_addr;
+
+ skip_instr_count += cur_scope->patches[0].skip_instr;
+
+ skip_patch_count += patch0_patch_skip;
+ if (cur_scope->type == SCOPE_IF) {
+ scope->inner_scope_patches += skip_patch_count;
+ skip_patch_count = 0;
+ skip_instr_count = 0;
+ }
+ break;
+ case SCOPE_ELSE:
+ /* Count any patches contained in our innter scope */
+ skip_patch_count += cur_scope->inner_scope_patches;
+
+ skip_instr_count += cur_scope->end_addr
+ - cur_scope->begin_addr;
+ break;
+ }
+
+ cur_scope = TAILQ_PREV(cur_scope, scope_tailq, scope_links);
+ }
}
diff --git a/sys/dev/aic7xxx/aicasm.h b/sys/dev/aic7xxx/aicasm.h
index a81afc1..5080675 100644
--- a/sys/dev/aic7xxx/aicasm.h
+++ b/sys/dev/aic7xxx/aicasm.h
@@ -10,10 +10,7 @@
* 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
+ * 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
@@ -28,7 +25,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aic7xxx_asm.h,v 1.2 1997/03/16 07:28:30 gibbs Exp $
+ * $Id: aicasm.h,v 1.3 1997/06/27 19:38:47 gibbs Exp $
*/
#include <sys/queue.h>
@@ -56,13 +53,15 @@ typedef enum {
SLIST_HEAD(path_list, path_entry);
extern struct path_list search_path;
-extern struct symlist patch_options;
+extern struct scope_list scope_stack;
+extern struct symlist patch_functions;
extern int includes_search_curdir; /* False if we've seen -I- */
extern char *appname;
extern int yylineno;
extern char *yyfilename;
-void stop __P((const char *errstring, int err_code));
-void include_file __P((char *file_name, include_type type));
-struct instruction *seq_alloc __P((void));
-struct patch *patch_alloc __P((void));
+void stop(const char *errstring, int err_code);
+void include_file(char *file_name, include_type type);
+struct instruction *seq_alloc(void);
+struct scope *scope_alloc(void);
+void process_scope(struct scope *);
diff --git a/sys/dev/aic7xxx/aicasm/aicasm.c b/sys/dev/aic7xxx/aicasm/aicasm.c
index c05950d..66d30ce 100644
--- a/sys/dev/aic7xxx/aicasm/aicasm.c
+++ b/sys/dev/aic7xxx/aicasm/aicasm.c
@@ -1,7 +1,7 @@
/*
* Aic7xxx SCSI host adapter firmware asssembler
*
- * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 1997, 1998 Justin T. Gibbs.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -10,10 +10,7 @@
* 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
+ * 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
@@ -28,7 +25,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aicasm.c,v 1.18 1997/06/27 19:38:45 gibbs Exp $
+ * $Id: aicasm.c,v 1.19 1997/09/03 03:44:38 gibbs Exp $
*/
#include <sys/types.h>
#include <sys/mman.h>
@@ -43,13 +40,24 @@
#include "aicasm_symbol.h"
#include "sequencer.h"
-static void usage __P((void));
-static void back_patch __P((void));
-static void output_code __P((FILE *ofile));
-static void output_listing __P((FILE *listfile, char *ifilename,
- char *options));
-static struct patch *next_patch __P((struct patch *cur_patch, int options,
- int instrptr));
+typedef struct patch {
+ STAILQ_ENTRY(patch) links;
+ int patch_func;
+ u_int begin;
+ u_int skip_instr;
+ u_int skip_patch;
+} patch_t;
+
+STAILQ_HEAD(patch_list, patch) patches;
+
+static void usage(void);
+static void back_patch(void);
+static void output_code(FILE *ofile);
+static void output_listing(FILE *listfile, char *ifilename);
+static int dump_scope(scope_t *scope);
+static void emit_patch(scope_t *scope, int patch);
+static int check_patch(patch_t **start_patch, int start_instr,
+ int *skip_addr, int *func_vals);
struct path_list search_path;
int includes_search_curdir;
@@ -62,8 +70,8 @@ char *listfilename;
FILE *listfile;
static STAILQ_HEAD(,instruction) seq_program;
-static STAILQ_HEAD(, patch) patch_list;
-symlist_t patch_options;
+struct scope_list scope_stack;
+symlist_t patch_functions;
#if DEBUG
extern int yy_flex_debug;
@@ -82,19 +90,24 @@ main(argc, argv)
int ch;
int retval;
char *inputfilename;
- char *options;
+ scope_t *sentinal;
+ STAILQ_INIT(&patches);
SLIST_INIT(&search_path);
STAILQ_INIT(&seq_program);
- STAILQ_INIT(&patch_list);
- SLIST_INIT(&patch_options);
+ SLIST_INIT(&scope_stack);
+
+ /* Set Sentinal scope node */
+ sentinal = scope_alloc();
+ sentinal->type = SCOPE_ROOT;
+
includes_search_curdir = 1;
appname = *argv;
regfile = NULL;
listfile = NULL;
- options = NULL;
#if DEBUG
yy_flex_debug = 0;
+ yydebug = 0;
#endif
while ((ch = getopt(argc, argv, "d:l:n:o:r:I:O:")) != -1) {
switch(ch) {
@@ -138,10 +151,6 @@ main(argc, argv)
}
ofilename = optarg;
break;
- case 'O':
- /* Patches to include in the listing */
- options = optarg;
- break;
case 'r':
if ((regfile = fopen(optarg, "w")) == NULL) {
perror(optarg);
@@ -207,13 +216,33 @@ main(argc, argv)
include_file(*argv, SOURCE_FILE);
retval = yyparse();
if (retval == 0) {
+ if (SLIST_FIRST(&scope_stack) == NULL
+ || SLIST_FIRST(&scope_stack)->type != SCOPE_ROOT) {
+ stop("Unterminated conditional expression",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+
+ /* Process outmost scope */
+ process_scope(SLIST_FIRST(&scope_stack));
+ /*
+ * Decend the tree of scopes and insert/emit
+ * patches as appropriate. We perform a depth first
+ * tranversal, recursively handling each scope.
+ */
+ /* start at the root scope */
+ dump_scope(SLIST_FIRST(&scope_stack));
+
+ /* Patch up forward jump addresses */
back_patch();
+
if (ofile != NULL)
output_code(ofile);
- if (regfile != NULL)
+ if (regfile != NULL) {
symtable_dump(regfile);
+ }
if (listfile != NULL)
- output_listing(listfile, inputfilename, options);
+ output_listing(listfile, inputfilename);
}
stop(NULL, 0);
@@ -228,7 +257,7 @@ usage()
(void)fprintf(stderr,
"usage: %-16s [-nostdinc] [-I-] [-I directory] [-o output_file]
[-r register_output_file] [-l program_list_file]
- [-O option_name[|options_name2]] input_file\n",
+ input_file\n",
appname);
exit(EX_USAGE);
}
@@ -255,12 +284,9 @@ back_patch()
/* NOTREACHED */
}
f3_instr = &cur_instr->format.format3;
- address = ((f3_instr->opcode_addr & ADDR_HIGH_BIT) << 8)
- | f3_instr->address;
+ address = f3_instr->address;
address += cur_instr->patch_label->info.linfo->address;
- f3_instr->opcode_addr &= ~ADDR_HIGH_BIT;
- f3_instr->opcode_addr |= (address >> 8) & ADDR_HIGH_BIT;
- f3_instr->address = address & 0xFF;
+ f3_instr->address = address;
}
}
}
@@ -284,6 +310,7 @@ output_code(ofile)
for(cur_instr = seq_program.stqh_first;
cur_instr != NULL;
cur_instr = cur_instr->links.stqe_next) {
+
fprintf(ofile, "\t0x%02x, 0x%02x, 0x%02x, 0x%02x,\n",
cur_instr->format.bytes[0],
cur_instr->format.bytes[1],
@@ -291,60 +318,128 @@ output_code(ofile)
cur_instr->format.bytes[3]);
instrcount++;
}
- fprintf(ofile, "};\n");
+ fprintf(ofile, "};\n\n");
/*
- * Output the patch list, option definitions first.
+ * Output patch information. Patch functions first.
*/
- for(cur_node = patch_options.slh_first;
+ for(cur_node = SLIST_FIRST(&patch_functions);
cur_node != NULL;
- cur_node = cur_node->links.sle_next) {
- fprintf(ofile, "#define\t%-16s\t0x%x\n", cur_node->symbol->name,
- cur_node->symbol->info.condinfo->value);
+ cur_node = SLIST_NEXT(cur_node,links)) {
+ fprintf(ofile,
+"static int ahc_patch%d_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch%d_func(struct ahc_softc *ahc)
+{
+ return (%s);
+}\n\n",
+ cur_node->symbol->info.condinfo->func_num,
+ cur_node->symbol->info.condinfo->func_num,
+ cur_node->symbol->name);
}
fprintf(ofile,
-"struct patch {
- int options;
- int negative;
- int begin;
- int end;
+"typedef int patch_func_t __P((struct ahc_softc *));
+struct patch {
+ patch_func_t *patch_func;
+ u_int32_t begin :10,
+ skip_instr :10,
+ skip_patch :12;
} patches[] = {\n");
- for(cur_patch = patch_list.stqh_first;
+ for(cur_patch = STAILQ_FIRST(&patches);
cur_patch != NULL;
- cur_patch = cur_patch->links.stqe_next)
+ cur_patch = STAILQ_NEXT(cur_patch,links)) {
+ fprintf(ofile, "\t{ ahc_patch%d_func, %d, %d, %d },\n",
+ cur_patch->patch_func, cur_patch->begin,
+ cur_patch->skip_instr, cur_patch->skip_patch);
+ }
- fprintf(ofile, "\t{ 0x%08x, %d, 0x%03x, 0x%03x },\n",
- cur_patch->options, cur_patch->negative, cur_patch->begin,
- cur_patch->end);
+ fprintf(ofile, "\n};\n");
- fprintf(ofile, "\t{ 0x%08x, %d, 0x%03x, 0x%03x }\n};\n",
- 0, 0, 0, 0);
-
fprintf(stderr, "%s: %d instructions used\n", appname, instrcount);
}
+static int
+dump_scope(scope_t *scope)
+{
+ scope_t *cur_scope;
+ int patches_emitted = 0;
+
+ /*
+ * Emit the first patch for this scope
+ */
+ emit_patch(scope, 0);
+
+ /*
+ * Dump each scope within this one.
+ */
+ cur_scope = TAILQ_FIRST(&scope->inner_scope);
+
+ while (cur_scope != NULL) {
+
+ dump_scope(cur_scope);
+
+ cur_scope = TAILQ_NEXT(cur_scope, scope_links);
+ }
+
+ /*
+ * Emit the second, closing, patch for this scope
+ */
+ emit_patch(scope, 1);
+}
+
+void
+emit_patch(scope_t *scope, int patch)
+{
+ patch_info_t *pinfo;
+ patch_t *new_patch;
+
+ pinfo = &scope->patches[patch];
+
+ if (pinfo->skip_instr == 0)
+ /* No-Op patch */
+ return;
+
+ new_patch = (patch_t *)malloc(sizeof(*new_patch));
+
+ if (new_patch == NULL)
+ stop("Could not malloc patch structure", EX_OSERR);
+
+ memset(new_patch, 0, sizeof(*new_patch));
+
+ if (patch == 0) {
+ new_patch->patch_func = scope->func_num;
+ new_patch->begin = scope->begin_addr;
+ } else {
+ new_patch->patch_func = 0;
+ new_patch->begin = scope->end_addr;
+ }
+ new_patch->skip_instr = pinfo->skip_instr;
+ new_patch->skip_patch = pinfo->skip_patch;
+ STAILQ_INSERT_TAIL(&patches, new_patch, links);
+}
+
void
-output_listing(listfile, ifilename, patches)
- FILE *listfile;
- char *ifilename;
- char *patches;
+output_listing(FILE *listfile, char *ifilename)
{
+ char buf[1024];
FILE *ifile;
- int line;
struct instruction *cur_instr;
+ patch_t *cur_patch;
+ symbol_node_t *cur_func;
+ int *func_values;
int instrcount;
int instrptr;
- char buf[1024];
- patch_t *cur_patch;
- char *option_spec;
- int options;
+ int line;
+ int func_count;
+ int skip_addr;
instrcount = 0;
instrptr = 0;
line = 1;
- options = 1; /* All code outside of patch blocks */
+ skip_addr = 0;
if ((ifile = fopen(ifilename, "r")) == NULL) {
perror(ifilename);
stop(NULL, EX_DATAERR);
@@ -353,31 +448,66 @@ output_listing(listfile, ifilename, patches)
/*
* Determine which options to apply to this listing.
*/
- while ((option_spec = strsep(&patches, "|")) != NULL) {
- symbol_t *symbol;
-
- symbol = symtable_get(option_spec);
- if (symbol->type != CONDITIONAL) {
- stop("Invalid option specified in patch list for "
- "program listing", EX_USAGE);
- /* NOTREACHED */
+ for (func_count = 0, cur_func = SLIST_FIRST(&patch_functions);
+ cur_func != NULL;
+ cur_func = SLIST_NEXT(cur_func, links))
+ func_count++;
+
+ if (func_count != 0) {
+ func_values = (int *)malloc(func_count * sizeof(int));
+
+ if (func_values == NULL)
+ stop("Could not malloc", EX_OSERR);
+
+ func_values[0] = 0; /* FALSE func */
+ func_count--;
+
+ /*
+ * Ask the user to fill in the return values for
+ * the rest of the functions.
+ */
+
+
+ for (cur_func = SLIST_FIRST(&patch_functions);
+ cur_func != NULL && SLIST_NEXT(cur_func, links) != NULL;
+ cur_func = SLIST_NEXT(cur_func, links), func_count--) {
+ int input;
+
+ fprintf(stdout, "\n(%s)\n", cur_func->symbol->name);
+ fprintf(stdout,
+ "Enter the return value for "
+ "this expression[T/F]:");
+
+ while (1) {
+
+ input = getchar();
+ input = toupper(input);
+
+ if (input == 'T') {
+ func_values[func_count] = 1;
+ break;
+ } else if (input == 'F') {
+ func_values[func_count] = 0;
+ break;
+ }
+ }
}
- options |= symbol->info.condinfo->value;
+ fprintf(stdout, "\nThanks!\n");
}
- cur_patch = patch_list.stqh_first;
- for(cur_instr = seq_program.stqh_first;
+ /* Now output the listing */
+ cur_patch = STAILQ_FIRST(&patches);
+ for(cur_instr = STAILQ_FIRST(&seq_program);
cur_instr != NULL;
- cur_instr = cur_instr->links.stqe_next,instrcount++) {
+ cur_instr = STAILQ_NEXT(cur_instr, links), instrcount++) {
- cur_patch = next_patch(cur_patch, options, instrcount);
- if (cur_patch
- && cur_patch->begin <= instrcount
- && cur_patch->end > instrcount)
+ if (check_patch(&cur_patch, instrcount,
+ &skip_addr, func_values) == 0) {
/* Don't count this instruction as it is in a patch
* that was removed.
*/
continue;
+ }
while (line < cur_instr->srcline) {
fgets(buf, sizeof(buf), ifile);
@@ -401,29 +531,39 @@ output_listing(listfile, ifilename, patches)
fclose(ifile);
}
-static struct patch *
-next_patch(cur_patch, options, instrptr)
- struct patch *cur_patch;
- int options;
- int instrptr;
+static int
+check_patch(patch_t **start_patch, int start_instr,
+ int *skip_addr, int *func_vals)
{
- while(cur_patch != NULL) {
- if (((cur_patch->options & options) != 0
- && cur_patch->negative == FALSE)
- || ((cur_patch->options & options) == 0
- && cur_patch->negative == TRUE)
- || (instrptr >= cur_patch->end)) {
- /*
- * Either we want to keep this section of code,
- * or we have consumed this patch. Skip to the
- * next patch.
+ patch_t *cur_patch;
+
+ cur_patch = *start_patch;
+
+ while (cur_patch != NULL && start_instr == cur_patch->begin) {
+ if (func_vals[cur_patch->patch_func] == 0) {
+ int skip;
+
+ /* Start rejecting code */
+ *skip_addr = start_instr + cur_patch->skip_instr;
+ for (skip = cur_patch->skip_patch;
+ skip > 0 && cur_patch != NULL;
+ skip--)
+ cur_patch = STAILQ_NEXT(cur_patch, links);
+ } else {
+ /* Accepted this patch. Advance to the next
+ * one and wait for our intruction pointer to
+ * hit this point.
*/
- cur_patch = cur_patch->links.stqe_next;
- } else
- /* Found an okay patch */
- break;
+ cur_patch = STAILQ_NEXT(cur_patch, links);
+ }
}
- return (cur_patch);
+
+ *start_patch = cur_patch;
+ if (start_instr < *skip_addr)
+ /* Still skipping */
+ return (0);
+
+ return (1);
}
/*
@@ -471,7 +611,7 @@ stop(string, err_code)
}
}
- symlist_free(&patch_options);
+ symlist_free(&patch_functions);
symtable_close();
exit(err_code);
@@ -491,15 +631,85 @@ seq_alloc()
return new_instr;
}
-patch_t *
-patch_alloc()
+scope_t *
+scope_alloc()
{
- patch_t *new_patch;
+ scope_t *new_scope;
- new_patch = (patch_t *)malloc(sizeof(patch_t));
- if (new_patch == NULL)
- stop("Unable to malloc patch object", EX_SOFTWARE);
- memset(new_patch, 0, sizeof(*new_patch));
- STAILQ_INSERT_TAIL(&patch_list, new_patch, links);
- return new_patch;
+ new_scope = (scope_t *)malloc(sizeof(scope_t));
+ if (new_scope == NULL)
+ stop("Unable to malloc scope object", EX_SOFTWARE);
+ memset(new_scope, 0, sizeof(*new_scope));
+ TAILQ_INIT(&new_scope->inner_scope);
+
+ if (SLIST_FIRST(&scope_stack) != NULL) {
+ TAILQ_INSERT_TAIL(&SLIST_FIRST(&scope_stack)->inner_scope,
+ new_scope, scope_links);
+ }
+ /* This patch is now the current scope */
+ SLIST_INSERT_HEAD(&scope_stack, new_scope, scope_stack_links);
+ return new_scope;
+}
+
+void
+process_scope(scope_t *scope)
+{
+ /*
+ * We are "leaving" this scope. We should now have
+ * enough information to process the lists of scopes
+ * we encapsulate.
+ */
+ scope_t *cur_scope;
+ u_int skip_patch_count;
+ u_int skip_instr_count;
+
+ cur_scope = TAILQ_LAST(&scope->inner_scope, scope_tailq);
+ skip_patch_count = 0;
+ skip_instr_count = 0;
+ while (cur_scope != NULL) {
+ u_int patch0_patch_skip;
+
+ patch0_patch_skip = 0;
+ switch (cur_scope->type) {
+ case SCOPE_IF:
+ case SCOPE_ELSE_IF:
+ if (skip_instr_count != 0) {
+ /* Create a tail patch */
+ patch0_patch_skip++;
+ cur_scope->patches[1].skip_patch =
+ skip_patch_count + 1;
+ cur_scope->patches[1].skip_instr =
+ skip_instr_count;
+ }
+
+ /* Count Head patch */
+ patch0_patch_skip++;
+
+ /* Count any patches contained in our inner scope */
+ patch0_patch_skip += cur_scope->inner_scope_patches;
+
+ cur_scope->patches[0].skip_patch = patch0_patch_skip;
+ cur_scope->patches[0].skip_instr =
+ cur_scope->end_addr - cur_scope->begin_addr;
+
+ skip_instr_count += cur_scope->patches[0].skip_instr;
+
+ skip_patch_count += patch0_patch_skip;
+ if (cur_scope->type == SCOPE_IF) {
+ scope->inner_scope_patches += skip_patch_count;
+ skip_patch_count = 0;
+ skip_instr_count = 0;
+ }
+ break;
+ case SCOPE_ELSE:
+ /* Count any patches contained in our innter scope */
+ skip_patch_count += cur_scope->inner_scope_patches;
+
+ skip_instr_count += cur_scope->end_addr
+ - cur_scope->begin_addr;
+ break;
+ }
+
+ cur_scope = TAILQ_PREV(cur_scope, scope_tailq, scope_links);
+ }
}
diff --git a/sys/dev/aic7xxx/aicasm/aicasm.h b/sys/dev/aic7xxx/aicasm/aicasm.h
index a81afc1..5080675 100644
--- a/sys/dev/aic7xxx/aicasm/aicasm.h
+++ b/sys/dev/aic7xxx/aicasm/aicasm.h
@@ -10,10 +10,7 @@
* 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
+ * 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
@@ -28,7 +25,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aic7xxx_asm.h,v 1.2 1997/03/16 07:28:30 gibbs Exp $
+ * $Id: aicasm.h,v 1.3 1997/06/27 19:38:47 gibbs Exp $
*/
#include <sys/queue.h>
@@ -56,13 +53,15 @@ typedef enum {
SLIST_HEAD(path_list, path_entry);
extern struct path_list search_path;
-extern struct symlist patch_options;
+extern struct scope_list scope_stack;
+extern struct symlist patch_functions;
extern int includes_search_curdir; /* False if we've seen -I- */
extern char *appname;
extern int yylineno;
extern char *yyfilename;
-void stop __P((const char *errstring, int err_code));
-void include_file __P((char *file_name, include_type type));
-struct instruction *seq_alloc __P((void));
-struct patch *patch_alloc __P((void));
+void stop(const char *errstring, int err_code);
+void include_file(char *file_name, include_type type);
+struct instruction *seq_alloc(void);
+struct scope *scope_alloc(void);
+void process_scope(struct scope *);
diff --git a/sys/dev/aic7xxx/aicasm/aicasm_gram.y b/sys/dev/aic7xxx/aicasm/aicasm_gram.y
index fa657c0..09a62b9 100644
--- a/sys/dev/aic7xxx/aicasm/aicasm_gram.y
+++ b/sys/dev/aic7xxx/aicasm/aicasm_gram.y
@@ -2,7 +2,7 @@
/*
* Parser for the Aic7xxx SCSI Host adapter sequencer assembler.
*
- * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 1997-1998 Justin T. Gibbs.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,7 @@
* 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
+ * 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
@@ -29,7 +26,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aicasm_gram.y,v 1.3 1997/09/03 03:44:40 gibbs Exp $
+ * $Id: aicasm_gram.y,v 1.4 1997/09/27 19:37:28 gibbs Exp $
*/
#include <stdio.h>
@@ -56,7 +53,6 @@ static symbol_ref_t sindex;
static int instruction_ptr;
static int sram_or_scb_offset;
static int download_constant_count;
-static patch_t *cur_patch;
static void process_bitmask __P((int mask_type, symbol_t *sym, int mask));
static void initialize_symbol __P((symbol_t *symbol));
@@ -118,11 +114,13 @@ static int is_download_const __P((expression_t *immed));
%token <str> T_PATH
+%token <sym> T_CEXPR
+
%token T_EOF T_INCLUDE
%token <value> T_SHR T_SHL T_ROR T_ROL
-%token <value> T_MVI T_MOV T_CLR
+%token <value> T_MVI T_MOV T_CLR T_BMOV
%token <value> T_JMP T_JC T_JNC T_JE T_JNE T_JNZ T_JZ T_CALL
@@ -150,7 +148,7 @@ static int is_download_const __P((expression_t *immed));
%token T_NL
-%token T_IF T_ELSE T_ENDIF
+%token T_IF T_ELSE T_ELSE_IF T_ENDIF
%type <sym_ref> reg_symbol address destination source opt_source
@@ -670,64 +668,89 @@ address:
;
conditional:
- T_IF
+ T_IF T_CEXPR '{'
{
- if (cur_patch != NULL) {
- stop("Nested .if directive", EX_DATAERR);
- /* NOTREACHED */
- }
- cur_patch = patch_alloc();
- cur_patch->begin = instruction_ptr;
- }
- option_list
-;
+ scope_t *new_scope;
-conditional:
- T_ELSE
+ add_conditional($2);
+ new_scope = scope_alloc();
+ new_scope->type = SCOPE_IF;
+ new_scope->begin_addr = instruction_ptr;
+ new_scope->func_num = $2->info.condinfo->func_num;
+ }
+| T_ELSE T_IF T_CEXPR '{'
{
- patch_t *next_patch;
+ scope_t *new_scope;
+ scope_t *scope_context;
+ scope_t *last_scope;
+
+ /*
+ * Ensure that the previous scope is either an
+ * if or and else if.
+ */
+ scope_context = SLIST_FIRST(&scope_stack);
+ last_scope = TAILQ_LAST(&scope_context->inner_scope,
+ scope_tailq);
+ if (last_scope == NULL
+ || last_scope->type == T_ELSE) {
- if (cur_patch == NULL) {
- stop(".else outsize of .if", EX_DATAERR);
+ stop("'else if' without leading 'if'", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ add_conditional($3);
+ new_scope = scope_alloc();
+ new_scope->type = SCOPE_ELSE_IF;
+ new_scope->begin_addr = instruction_ptr;
+ new_scope->func_num = $3->info.condinfo->func_num;
+ }
+| T_ELSE '{'
+ {
+ scope_t *new_scope;
+ scope_t *scope_context;
+ scope_t *last_scope;
+
+ /*
+ * Ensure that the previous scope is either an
+ * if or and else if.
+ */
+ scope_context = SLIST_FIRST(&scope_stack);
+ last_scope = TAILQ_LAST(&scope_context->inner_scope,
+ scope_tailq);
+ if (last_scope == NULL
+ || last_scope->type == SCOPE_ELSE) {
+
+ stop("'else' without leading 'if'", EX_DATAERR);
/* NOTREACHED */
}
- cur_patch->end = instruction_ptr;
- next_patch = patch_alloc();
- next_patch->options = cur_patch->options;
- next_patch->negative = cur_patch->negative ? FALSE : TRUE;
- cur_patch = next_patch;
- cur_patch->begin = instruction_ptr;
+ new_scope = scope_alloc();
+ new_scope->type = SCOPE_ELSE;
+ new_scope->begin_addr = instruction_ptr;
}
;
conditional:
- T_ENDIF
+ '}'
{
- if (cur_patch == NULL) {
- stop(".endif outsize of .if", EX_DATAERR);
+ scope_t *scope_context;
+ scope_t *last_scope;
+
+ scope_context = SLIST_FIRST(&scope_stack);
+ if (scope_context->type == SCOPE_ROOT) {
+ stop("Unexpected '}' encountered", EX_DATAERR);
/* NOTREACHED */
}
- cur_patch->end = instruction_ptr;
- cur_patch = NULL;
- }
-;
-option_list:
- '(' option_symbol_list ')'
-| '!' option_list
- {
- cur_patch->negative = cur_patch->negative ? FALSE : TRUE;
- }
-;
+ scope_context->end_addr = instruction_ptr;
-option_symbol_list:
- T_SYMBOL
- {
- add_conditional($1);
- }
-| option_list '|' T_SYMBOL
- {
- add_conditional($3);
+ /* Pop the scope */
+ SLIST_REMOVE_HEAD(&scope_stack, scope_stack_links);
+
+ process_scope(scope_context);
+
+ if (SLIST_FIRST(&scope_stack) == NULL) {
+ stop("Unexpected '}' encountered", EX_DATAERR);
+ /* NOTREACHED */
+ }
}
;
@@ -804,6 +827,13 @@ code:
;
code:
+ T_BMOV destination ',' source ',' immediate ret ';'
+ {
+ format_1_instr(AIC_OP_BMOV, &$2, &$6, &$4, $7);
+ }
+;
+
+code:
T_MOV destination ',' source ret ';'
{
expression_t immed;
@@ -1118,7 +1148,8 @@ format_1_instr(opcode, dest, immed, src, ret)
/* Allocate sequencer space for the instruction and fill it out */
instr = seq_alloc();
f1_instr = &instr->format.format1;
- f1_instr->opcode_ret = (opcode << 1) | (ret ? RETURN_BIT : 0);
+ f1_instr->ret = ret ? 1 : 0;
+ f1_instr->opcode = opcode;
f1_instr->destination = dest->symbol->info.rinfo->address
+ dest->offset;
f1_instr->source = src->symbol->info.rinfo->address
@@ -1126,7 +1157,7 @@ format_1_instr(opcode, dest, immed, src, ret)
f1_instr->immediate = immed->value;
if (is_download_const(immed))
- f1_instr->opcode_ret |= DOWNLOAD_CONST_IMMEDIATE;
+ f1_instr->parity = 1;
symlist_free(&immed->referenced_syms);
instruction_ptr++;
@@ -1154,7 +1185,8 @@ format_2_instr(opcode, dest, places, src, ret)
/* Allocate sequencer space for the instruction and fill it out */
instr = seq_alloc();
f2_instr = &instr->format.format2;
- f2_instr->opcode_ret = (AIC_OP_ROL << 1) | (ret ? RETURN_BIT : 0);
+ f2_instr->ret = ret ? 1 : 0;
+ f2_instr->opcode = AIC_OP_ROL;
f2_instr->destination = dest->symbol->info.rinfo->address
+ dest->offset;
f2_instr->source = src->symbol->info.rinfo->address
@@ -1225,15 +1257,14 @@ format_3_instr(opcode, src, immed, address)
instr->patch_label = address->symbol;
} else
addr = address->symbol->info.linfo->address + address->offset;
- f3_instr->opcode_addr = (opcode << 1)
- | ((addr >> 8) & 0x01);
- f3_instr->address = addr & 0xff;
+ f3_instr->opcode = opcode;
+ f3_instr->address = addr;
f3_instr->source = src->symbol->info.rinfo->address
+ src->offset;
f3_instr->immediate = immed->value;
if (is_download_const(immed))
- f3_instr->opcode_addr |= DOWNLOAD_CONST_IMMEDIATE;
+ f3_instr->parity = 1;
symlist_free(&immed->referenced_syms);
instruction_ptr++;
@@ -1326,19 +1357,38 @@ static void
add_conditional(symbol)
symbol_t *symbol;
{
- static int numoptions = 1;
+ static int numfuncs;
- if (symbol->type == UNINITIALIZED) {
- symbol->type = CONDITIONAL;
- initialize_symbol(symbol);
- symbol->info.condinfo->value = 0x01 << numoptions++;
- symlist_add(&patch_options, symbol, SYMLIST_INSERT_HEAD);
- } else if (symbol->type != CONDITIONAL) {
- stop("Conditional symbol mirrors other symbol",
+ if (numfuncs == 0) {
+ /* add a special conditional, "0" */
+ symbol_t *false_func;
+
+ false_func = symtable_get("0");
+ if (false_func->type != UNINITIALIZED) {
+ stop("Conditional expression '0' "
+ "conflicts with a symbol", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ false_func->type = CONDITIONAL;
+ initialize_symbol(false_func);
+ false_func->info.condinfo->func_num = numfuncs++;
+ symlist_add(&patch_functions, false_func, SYMLIST_INSERT_HEAD);
+ }
+
+ /* This condition has occurred before */
+ if (symbol->type == CONDITIONAL)
+ return;
+
+ if (symbol->type != UNINITIALIZED) {
+ stop("Conditional expression conflicts with a symbol",
EX_DATAERR);
/* NOTREACHED */
}
- cur_patch->options |= symbol->info.condinfo->value;
+
+ symbol->type = CONDITIONAL;
+ initialize_symbol(symbol);
+ symbol->info.condinfo->func_num = numfuncs++;
+ symlist_add(&patch_functions, symbol, SYMLIST_INSERT_HEAD);
}
void
diff --git a/sys/dev/aic7xxx/aicasm/aicasm_scan.l b/sys/dev/aic7xxx/aicasm/aicasm_scan.l
index 8446376..44bc834 100644
--- a/sys/dev/aic7xxx/aicasm/aicasm_scan.l
+++ b/sys/dev/aic7xxx/aicasm/aicasm_scan.l
@@ -2,7 +2,7 @@
/*
* Lexical Analyzer for the Aic7xxx SCSI Host adapter sequencer assembler.
*
- * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 1997-1998 Justin T. Gibbs.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,7 @@
* 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
+ * 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
@@ -29,7 +26,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aicasm_scan.l,v 1.4 1997/09/03 03:44:42 gibbs Exp $
+ * $Id: aicasm_scan.l,v 1.5 1997/09/27 19:37:29 gibbs Exp $
*/
#include <sys/types.h>
@@ -43,6 +40,11 @@
#include "aicasm.h"
#include "aicasm_symbol.h"
#include "y.tab.h"
+
+#define MAX_STR_CONST 256
+char string_buf[MAX_STR_CONST];
+char *string_buf_ptr;
+int parren_count;
%}
PATH [-/A-Za-z0-9_.]*[./][-/A-Za-z0-9_.]*
@@ -50,6 +52,8 @@ WORD [A-Za-z_][-A-Za-z_0-9]*
SPACE [ \t]+
%x COMMENT
+%x CEXPR
+%x INCLUDE
%%
\n { ++yylineno; }
@@ -60,6 +64,32 @@ SPACE [ \t]+
<COMMENT>"*"+[^*/\n]* ;
<COMMENT>"/"+[^*/\n]* ;
<COMMENT>"*"+"/" { BEGIN INITIAL; }
+if[ \t]*\( {
+ string_buf_ptr = string_buf;
+ parren_count = 1;
+ BEGIN CEXPR;
+ return T_IF;
+ }
+<CEXPR>\( { *string_buf_ptr++ = '('; parren_count++; }
+<CEXPR>\) {
+ parren_count--;
+ if (parren_count == 0) {
+ /* All done */
+ BEGIN INITIAL;
+ *string_buf_ptr = '\0';
+ yylval.sym = symtable_get(string_buf);
+ return T_CEXPR;
+ } else {
+ *string_buf_ptr++ = ')';
+ }
+ }
+<CEXPR>\n { ++yylineno; }
+<CEXPR>[^()\n]+ {
+ char *yptr = yytext;
+
+ while (*yptr != '\0')
+ *string_buf_ptr++ = *yptr++;
+ }
{SPACE} ;
@@ -109,6 +139,7 @@ jnz { return T_JNZ; }
call { return T_CALL; }
add { return T_ADD; }
adc { return T_ADC; }
+bmov { return T_BMOV; }
inc { return T_INC; }
dec { return T_DEC; }
stc { return T_STC; }
@@ -120,12 +151,10 @@ and { return T_AND; }
or { return T_OR; }
ret { return T_RET; }
nop { return T_NOP; }
-.if { return T_IF; }
-.else { return T_ELSE; }
-.endif { return T_ENDIF; }
+else { return T_ELSE; }
/* Allowed Symbols */
-[-+,:()~|&."{};<>[\]!] { return yytext[0]; }
+[-+,:()~|&."{};<>[\]!] { return yytext[0]; }
/* Number processing */
0[0-7]* {
@@ -144,7 +173,11 @@ nop { return T_NOP; }
}
/* Include Files */
-#include { return T_INCLUDE; }
+#include { return T_INCLUDE; BEGIN INCLUDE;}
+<INCLUDE>[<>\"] { return yytext[0]; }
+<INCLUDE>{PATH} { yylval.str = strdup(yytext); return T_PATH; }
+<INCLUDE>; { BEGIN INITIAL; return yytext[0]; }
+<INCLUDE>. { stop("Invalid include line", EX_DATAERR); }
/* For parsing C include files with #define foo */
#define { yylval.value = TRUE; return T_CONST; }
diff --git a/sys/dev/aic7xxx/aicasm/aicasm_symbol.c b/sys/dev/aic7xxx/aicasm/aicasm_symbol.c
index c9f645a..f808e89 100644
--- a/sys/dev/aic7xxx/aicasm/aicasm_symbol.c
+++ b/sys/dev/aic7xxx/aicasm/aicasm_symbol.c
@@ -10,10 +10,7 @@
* 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
+ * 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
@@ -28,7 +25,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aicasm_symbol.c,v 1.3 1997/09/03 03:44:44 gibbs Exp $
+ * $Id: aicasm_symbol.c,v 1.4 1997/09/27 19:37:30 gibbs Exp $
*/
diff --git a/sys/dev/aic7xxx/aicasm/aicasm_symbol.h b/sys/dev/aic7xxx/aicasm/aicasm_symbol.h
index 199e3ba..ba98b43 100644
--- a/sys/dev/aic7xxx/aicasm/aicasm_symbol.h
+++ b/sys/dev/aic7xxx/aicasm/aicasm_symbol.h
@@ -10,10 +10,7 @@
* 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
+ * 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
@@ -28,7 +25,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aicasm_symbol.h,v 1.2 1997/06/27 19:38:56 gibbs Exp $
+ * $Id: aicasm_symbol.h,v 1.3 1997/09/27 19:37:30 gibbs Exp $
*/
#include <sys/queue.h>
@@ -82,7 +79,7 @@ struct label_info {
};
struct cond_info {
- int value;
+ int func_num;
};
typedef struct expression_info {
@@ -113,13 +110,32 @@ typedef struct symbol_node {
symbol_t *symbol;
}symbol_node_t;
-typedef struct patch {
- STAILQ_ENTRY(patch) links;
- int negative;
- int begin;
- int end;
- int options;
-} patch_t;
+typedef enum {
+ SCOPE_ROOT,
+ SCOPE_IF,
+ SCOPE_ELSE_IF,
+ SCOPE_ELSE
+} scope_type;
+
+typedef struct patch_info {
+ int skip_patch;
+ int skip_instr;
+} patch_info_t;
+
+typedef struct scope {
+ SLIST_ENTRY(scope) scope_stack_links;
+ TAILQ_ENTRY(scope) scope_links;
+ TAILQ_HEAD(, scope) inner_scope;
+ scope_type type;
+ int inner_scope_patches;
+ int begin_addr;
+ int end_addr;
+ patch_info_t patches[2];
+ int func_num;
+} scope_t;
+
+SLIST_HEAD(scope_list, scope);
+TAILQ_HEAD(scope_tailq, scope);
void symbol_delete __P((symbol_t *symbol));
diff --git a/sys/dev/aic7xxx/aicasm_gram.y b/sys/dev/aic7xxx/aicasm_gram.y
index fa657c0..09a62b9 100644
--- a/sys/dev/aic7xxx/aicasm_gram.y
+++ b/sys/dev/aic7xxx/aicasm_gram.y
@@ -2,7 +2,7 @@
/*
* Parser for the Aic7xxx SCSI Host adapter sequencer assembler.
*
- * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 1997-1998 Justin T. Gibbs.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,7 @@
* 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
+ * 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
@@ -29,7 +26,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aicasm_gram.y,v 1.3 1997/09/03 03:44:40 gibbs Exp $
+ * $Id: aicasm_gram.y,v 1.4 1997/09/27 19:37:28 gibbs Exp $
*/
#include <stdio.h>
@@ -56,7 +53,6 @@ static symbol_ref_t sindex;
static int instruction_ptr;
static int sram_or_scb_offset;
static int download_constant_count;
-static patch_t *cur_patch;
static void process_bitmask __P((int mask_type, symbol_t *sym, int mask));
static void initialize_symbol __P((symbol_t *symbol));
@@ -118,11 +114,13 @@ static int is_download_const __P((expression_t *immed));
%token <str> T_PATH
+%token <sym> T_CEXPR
+
%token T_EOF T_INCLUDE
%token <value> T_SHR T_SHL T_ROR T_ROL
-%token <value> T_MVI T_MOV T_CLR
+%token <value> T_MVI T_MOV T_CLR T_BMOV
%token <value> T_JMP T_JC T_JNC T_JE T_JNE T_JNZ T_JZ T_CALL
@@ -150,7 +148,7 @@ static int is_download_const __P((expression_t *immed));
%token T_NL
-%token T_IF T_ELSE T_ENDIF
+%token T_IF T_ELSE T_ELSE_IF T_ENDIF
%type <sym_ref> reg_symbol address destination source opt_source
@@ -670,64 +668,89 @@ address:
;
conditional:
- T_IF
+ T_IF T_CEXPR '{'
{
- if (cur_patch != NULL) {
- stop("Nested .if directive", EX_DATAERR);
- /* NOTREACHED */
- }
- cur_patch = patch_alloc();
- cur_patch->begin = instruction_ptr;
- }
- option_list
-;
+ scope_t *new_scope;
-conditional:
- T_ELSE
+ add_conditional($2);
+ new_scope = scope_alloc();
+ new_scope->type = SCOPE_IF;
+ new_scope->begin_addr = instruction_ptr;
+ new_scope->func_num = $2->info.condinfo->func_num;
+ }
+| T_ELSE T_IF T_CEXPR '{'
{
- patch_t *next_patch;
+ scope_t *new_scope;
+ scope_t *scope_context;
+ scope_t *last_scope;
+
+ /*
+ * Ensure that the previous scope is either an
+ * if or and else if.
+ */
+ scope_context = SLIST_FIRST(&scope_stack);
+ last_scope = TAILQ_LAST(&scope_context->inner_scope,
+ scope_tailq);
+ if (last_scope == NULL
+ || last_scope->type == T_ELSE) {
- if (cur_patch == NULL) {
- stop(".else outsize of .if", EX_DATAERR);
+ stop("'else if' without leading 'if'", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ add_conditional($3);
+ new_scope = scope_alloc();
+ new_scope->type = SCOPE_ELSE_IF;
+ new_scope->begin_addr = instruction_ptr;
+ new_scope->func_num = $3->info.condinfo->func_num;
+ }
+| T_ELSE '{'
+ {
+ scope_t *new_scope;
+ scope_t *scope_context;
+ scope_t *last_scope;
+
+ /*
+ * Ensure that the previous scope is either an
+ * if or and else if.
+ */
+ scope_context = SLIST_FIRST(&scope_stack);
+ last_scope = TAILQ_LAST(&scope_context->inner_scope,
+ scope_tailq);
+ if (last_scope == NULL
+ || last_scope->type == SCOPE_ELSE) {
+
+ stop("'else' without leading 'if'", EX_DATAERR);
/* NOTREACHED */
}
- cur_patch->end = instruction_ptr;
- next_patch = patch_alloc();
- next_patch->options = cur_patch->options;
- next_patch->negative = cur_patch->negative ? FALSE : TRUE;
- cur_patch = next_patch;
- cur_patch->begin = instruction_ptr;
+ new_scope = scope_alloc();
+ new_scope->type = SCOPE_ELSE;
+ new_scope->begin_addr = instruction_ptr;
}
;
conditional:
- T_ENDIF
+ '}'
{
- if (cur_patch == NULL) {
- stop(".endif outsize of .if", EX_DATAERR);
+ scope_t *scope_context;
+ scope_t *last_scope;
+
+ scope_context = SLIST_FIRST(&scope_stack);
+ if (scope_context->type == SCOPE_ROOT) {
+ stop("Unexpected '}' encountered", EX_DATAERR);
/* NOTREACHED */
}
- cur_patch->end = instruction_ptr;
- cur_patch = NULL;
- }
-;
-option_list:
- '(' option_symbol_list ')'
-| '!' option_list
- {
- cur_patch->negative = cur_patch->negative ? FALSE : TRUE;
- }
-;
+ scope_context->end_addr = instruction_ptr;
-option_symbol_list:
- T_SYMBOL
- {
- add_conditional($1);
- }
-| option_list '|' T_SYMBOL
- {
- add_conditional($3);
+ /* Pop the scope */
+ SLIST_REMOVE_HEAD(&scope_stack, scope_stack_links);
+
+ process_scope(scope_context);
+
+ if (SLIST_FIRST(&scope_stack) == NULL) {
+ stop("Unexpected '}' encountered", EX_DATAERR);
+ /* NOTREACHED */
+ }
}
;
@@ -804,6 +827,13 @@ code:
;
code:
+ T_BMOV destination ',' source ',' immediate ret ';'
+ {
+ format_1_instr(AIC_OP_BMOV, &$2, &$6, &$4, $7);
+ }
+;
+
+code:
T_MOV destination ',' source ret ';'
{
expression_t immed;
@@ -1118,7 +1148,8 @@ format_1_instr(opcode, dest, immed, src, ret)
/* Allocate sequencer space for the instruction and fill it out */
instr = seq_alloc();
f1_instr = &instr->format.format1;
- f1_instr->opcode_ret = (opcode << 1) | (ret ? RETURN_BIT : 0);
+ f1_instr->ret = ret ? 1 : 0;
+ f1_instr->opcode = opcode;
f1_instr->destination = dest->symbol->info.rinfo->address
+ dest->offset;
f1_instr->source = src->symbol->info.rinfo->address
@@ -1126,7 +1157,7 @@ format_1_instr(opcode, dest, immed, src, ret)
f1_instr->immediate = immed->value;
if (is_download_const(immed))
- f1_instr->opcode_ret |= DOWNLOAD_CONST_IMMEDIATE;
+ f1_instr->parity = 1;
symlist_free(&immed->referenced_syms);
instruction_ptr++;
@@ -1154,7 +1185,8 @@ format_2_instr(opcode, dest, places, src, ret)
/* Allocate sequencer space for the instruction and fill it out */
instr = seq_alloc();
f2_instr = &instr->format.format2;
- f2_instr->opcode_ret = (AIC_OP_ROL << 1) | (ret ? RETURN_BIT : 0);
+ f2_instr->ret = ret ? 1 : 0;
+ f2_instr->opcode = AIC_OP_ROL;
f2_instr->destination = dest->symbol->info.rinfo->address
+ dest->offset;
f2_instr->source = src->symbol->info.rinfo->address
@@ -1225,15 +1257,14 @@ format_3_instr(opcode, src, immed, address)
instr->patch_label = address->symbol;
} else
addr = address->symbol->info.linfo->address + address->offset;
- f3_instr->opcode_addr = (opcode << 1)
- | ((addr >> 8) & 0x01);
- f3_instr->address = addr & 0xff;
+ f3_instr->opcode = opcode;
+ f3_instr->address = addr;
f3_instr->source = src->symbol->info.rinfo->address
+ src->offset;
f3_instr->immediate = immed->value;
if (is_download_const(immed))
- f3_instr->opcode_addr |= DOWNLOAD_CONST_IMMEDIATE;
+ f3_instr->parity = 1;
symlist_free(&immed->referenced_syms);
instruction_ptr++;
@@ -1326,19 +1357,38 @@ static void
add_conditional(symbol)
symbol_t *symbol;
{
- static int numoptions = 1;
+ static int numfuncs;
- if (symbol->type == UNINITIALIZED) {
- symbol->type = CONDITIONAL;
- initialize_symbol(symbol);
- symbol->info.condinfo->value = 0x01 << numoptions++;
- symlist_add(&patch_options, symbol, SYMLIST_INSERT_HEAD);
- } else if (symbol->type != CONDITIONAL) {
- stop("Conditional symbol mirrors other symbol",
+ if (numfuncs == 0) {
+ /* add a special conditional, "0" */
+ symbol_t *false_func;
+
+ false_func = symtable_get("0");
+ if (false_func->type != UNINITIALIZED) {
+ stop("Conditional expression '0' "
+ "conflicts with a symbol", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ false_func->type = CONDITIONAL;
+ initialize_symbol(false_func);
+ false_func->info.condinfo->func_num = numfuncs++;
+ symlist_add(&patch_functions, false_func, SYMLIST_INSERT_HEAD);
+ }
+
+ /* This condition has occurred before */
+ if (symbol->type == CONDITIONAL)
+ return;
+
+ if (symbol->type != UNINITIALIZED) {
+ stop("Conditional expression conflicts with a symbol",
EX_DATAERR);
/* NOTREACHED */
}
- cur_patch->options |= symbol->info.condinfo->value;
+
+ symbol->type = CONDITIONAL;
+ initialize_symbol(symbol);
+ symbol->info.condinfo->func_num = numfuncs++;
+ symlist_add(&patch_functions, symbol, SYMLIST_INSERT_HEAD);
}
void
diff --git a/sys/dev/aic7xxx/aicasm_scan.l b/sys/dev/aic7xxx/aicasm_scan.l
index 8446376..44bc834 100644
--- a/sys/dev/aic7xxx/aicasm_scan.l
+++ b/sys/dev/aic7xxx/aicasm_scan.l
@@ -2,7 +2,7 @@
/*
* Lexical Analyzer for the Aic7xxx SCSI Host adapter sequencer assembler.
*
- * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 1997-1998 Justin T. Gibbs.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,7 @@
* 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
+ * 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
@@ -29,7 +26,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aicasm_scan.l,v 1.4 1997/09/03 03:44:42 gibbs Exp $
+ * $Id: aicasm_scan.l,v 1.5 1997/09/27 19:37:29 gibbs Exp $
*/
#include <sys/types.h>
@@ -43,6 +40,11 @@
#include "aicasm.h"
#include "aicasm_symbol.h"
#include "y.tab.h"
+
+#define MAX_STR_CONST 256
+char string_buf[MAX_STR_CONST];
+char *string_buf_ptr;
+int parren_count;
%}
PATH [-/A-Za-z0-9_.]*[./][-/A-Za-z0-9_.]*
@@ -50,6 +52,8 @@ WORD [A-Za-z_][-A-Za-z_0-9]*
SPACE [ \t]+
%x COMMENT
+%x CEXPR
+%x INCLUDE
%%
\n { ++yylineno; }
@@ -60,6 +64,32 @@ SPACE [ \t]+
<COMMENT>"*"+[^*/\n]* ;
<COMMENT>"/"+[^*/\n]* ;
<COMMENT>"*"+"/" { BEGIN INITIAL; }
+if[ \t]*\( {
+ string_buf_ptr = string_buf;
+ parren_count = 1;
+ BEGIN CEXPR;
+ return T_IF;
+ }
+<CEXPR>\( { *string_buf_ptr++ = '('; parren_count++; }
+<CEXPR>\) {
+ parren_count--;
+ if (parren_count == 0) {
+ /* All done */
+ BEGIN INITIAL;
+ *string_buf_ptr = '\0';
+ yylval.sym = symtable_get(string_buf);
+ return T_CEXPR;
+ } else {
+ *string_buf_ptr++ = ')';
+ }
+ }
+<CEXPR>\n { ++yylineno; }
+<CEXPR>[^()\n]+ {
+ char *yptr = yytext;
+
+ while (*yptr != '\0')
+ *string_buf_ptr++ = *yptr++;
+ }
{SPACE} ;
@@ -109,6 +139,7 @@ jnz { return T_JNZ; }
call { return T_CALL; }
add { return T_ADD; }
adc { return T_ADC; }
+bmov { return T_BMOV; }
inc { return T_INC; }
dec { return T_DEC; }
stc { return T_STC; }
@@ -120,12 +151,10 @@ and { return T_AND; }
or { return T_OR; }
ret { return T_RET; }
nop { return T_NOP; }
-.if { return T_IF; }
-.else { return T_ELSE; }
-.endif { return T_ENDIF; }
+else { return T_ELSE; }
/* Allowed Symbols */
-[-+,:()~|&."{};<>[\]!] { return yytext[0]; }
+[-+,:()~|&."{};<>[\]!] { return yytext[0]; }
/* Number processing */
0[0-7]* {
@@ -144,7 +173,11 @@ nop { return T_NOP; }
}
/* Include Files */
-#include { return T_INCLUDE; }
+#include { return T_INCLUDE; BEGIN INCLUDE;}
+<INCLUDE>[<>\"] { return yytext[0]; }
+<INCLUDE>{PATH} { yylval.str = strdup(yytext); return T_PATH; }
+<INCLUDE>; { BEGIN INITIAL; return yytext[0]; }
+<INCLUDE>. { stop("Invalid include line", EX_DATAERR); }
/* For parsing C include files with #define foo */
#define { yylval.value = TRUE; return T_CONST; }
diff --git a/sys/dev/aic7xxx/aicasm_symbol.c b/sys/dev/aic7xxx/aicasm_symbol.c
index c9f645a..f808e89 100644
--- a/sys/dev/aic7xxx/aicasm_symbol.c
+++ b/sys/dev/aic7xxx/aicasm_symbol.c
@@ -10,10 +10,7 @@
* 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
+ * 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
@@ -28,7 +25,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aicasm_symbol.c,v 1.3 1997/09/03 03:44:44 gibbs Exp $
+ * $Id: aicasm_symbol.c,v 1.4 1997/09/27 19:37:30 gibbs Exp $
*/
diff --git a/sys/dev/aic7xxx/aicasm_symbol.h b/sys/dev/aic7xxx/aicasm_symbol.h
index 199e3ba..ba98b43 100644
--- a/sys/dev/aic7xxx/aicasm_symbol.h
+++ b/sys/dev/aic7xxx/aicasm_symbol.h
@@ -10,10 +10,7 @@
* 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
+ * 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
@@ -28,7 +25,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: aicasm_symbol.h,v 1.2 1997/06/27 19:38:56 gibbs Exp $
+ * $Id: aicasm_symbol.h,v 1.3 1997/09/27 19:37:30 gibbs Exp $
*/
#include <sys/queue.h>
@@ -82,7 +79,7 @@ struct label_info {
};
struct cond_info {
- int value;
+ int func_num;
};
typedef struct expression_info {
@@ -113,13 +110,32 @@ typedef struct symbol_node {
symbol_t *symbol;
}symbol_node_t;
-typedef struct patch {
- STAILQ_ENTRY(patch) links;
- int negative;
- int begin;
- int end;
- int options;
-} patch_t;
+typedef enum {
+ SCOPE_ROOT,
+ SCOPE_IF,
+ SCOPE_ELSE_IF,
+ SCOPE_ELSE
+} scope_type;
+
+typedef struct patch_info {
+ int skip_patch;
+ int skip_instr;
+} patch_info_t;
+
+typedef struct scope {
+ SLIST_ENTRY(scope) scope_stack_links;
+ TAILQ_ENTRY(scope) scope_links;
+ TAILQ_HEAD(, scope) inner_scope;
+ scope_type type;
+ int inner_scope_patches;
+ int begin_addr;
+ int end_addr;
+ patch_info_t patches[2];
+ int func_num;
+} scope_t;
+
+SLIST_HEAD(scope_list, scope);
+TAILQ_HEAD(scope_tailq, scope);
void symbol_delete __P((symbol_t *symbol));
diff --git a/sys/dev/aic7xxx/sequencer.h b/sys/dev/aic7xxx/sequencer.h
index fd69435..0fe929b 100644
--- a/sys/dev/aic7xxx/sequencer.h
+++ b/sys/dev/aic7xxx/sequencer.h
@@ -2,7 +2,7 @@
* Instruction formats for the sequencer program downloaded to
* Aic7xxx SCSI host adapters
*
- * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 1997, 1998 Justin T. Gibbs.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,7 @@
* 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
+ * 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
@@ -36,40 +33,44 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: sequencer.h,v 1.2 1997/06/27 19:38:52 gibbs Exp $
+ * $Id: sequencer.h,v 1.3 1997/09/27 19:37:31 gibbs Exp $
*/
struct ins_format1 {
- u_int8_t immediate;
- u_int8_t source;
- u_int8_t destination;
- u_int8_t opcode_ret;
-#define DOWNLOAD_CONST_IMMEDIATE 0x80
+ u_int32_t immediate : 8,
+ source : 9,
+ destination : 9,
+ ret : 1,
+ opcode : 4,
+ parity : 1;
};
struct ins_format2 {
- u_int8_t shift_control;
- u_int8_t source;
- u_int8_t destination;
- u_int8_t opcode_ret;
-#define RETURN_BIT 0x01
+ u_int32_t shift_control : 8,
+ source : 9,
+ destination : 9,
+ ret : 1,
+ opcode : 4,
+ parity : 1;
};
struct ins_format3 {
- u_int8_t immediate;
- u_int8_t source;
- u_int8_t address;
- u_int8_t opcode_addr;
-#define ADDR_HIGH_BIT 0x01
+ u_int32_t immediate : 8,
+ source : 9,
+ address : 10,
+ opcode : 4,
+ parity : 1;
};
-struct instruction {
- union {
+union ins_formats {
struct ins_format1 format1;
struct ins_format2 format2;
struct ins_format3 format3;
u_int8_t bytes[4];
- } format;
+ u_int32_t integer;
+};
+struct instruction {
+ union ins_formats format;
u_int srcline;
struct symbol *patch_label;
STAILQ_ENTRY(instruction) links;
@@ -81,6 +82,7 @@ struct instruction {
#define AIC_OP_ADD 0x3
#define AIC_OP_ADC 0x4
#define AIC_OP_ROL 0x5
+#define AIC_OP_BMOV 0x6
#define AIC_OP_JMP 0x8
#define AIC_OP_JC 0x9
OpenPOWER on IntegriCloud