summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcvs2svn <cvs2svn@FreeBSD.org>2000-09-22 22:19:56 +0000
committercvs2svn <cvs2svn@FreeBSD.org>2000-09-22 22:19:56 +0000
commit8460d7052a36f37e7d18b53c42642b36f38a4115 (patch)
treefa8ddfecd89511368d84b5f4743048f75f0959e7
parent104ee923e2ad53596a834b173d1641de43564b05 (diff)
Notes
-rw-r--r--sys/dev/aic7xxx/aic7xxx_93cx6.c213
-rw-r--r--sys/dev/aic7xxx/aic7xxx_93cx6.h92
-rw-r--r--sys/dev/aic7xxx/aic7xxx_freebsd.c1822
-rw-r--r--sys/dev/aic7xxx/aic7xxx_freebsd.h454
-rw-r--r--sys/dev/aic7xxx/aic7xxx_inline.h366
-rw-r--r--sys/dev/aic7xxx/aic7xxx_pci.c1927
-rw-r--r--sys/dev/aic7xxx/aicasm/aicasm.c770
-rw-r--r--sys/dev/aic7xxx/aicasm/aicasm.h78
-rw-r--r--sys/dev/aic7xxx/aicasm/aicasm_gram.y1455
-rw-r--r--sys/dev/aic7xxx/aicasm/aicasm_insformat.h129
-rw-r--r--sys/dev/aic7xxx/aicasm/aicasm_scan.l302
-rw-r--r--sys/dev/aic7xxx/aicasm/aicasm_symbol.c468
-rw-r--r--sys/dev/aic7xxx/aicasm/aicasm_symbol.h177
13 files changed, 8253 insertions, 0 deletions
diff --git a/sys/dev/aic7xxx/aic7xxx_93cx6.c b/sys/dev/aic7xxx/aic7xxx_93cx6.c
new file mode 100644
index 000000000000..a7dc77b2704d
--- /dev/null
+++ b/sys/dev/aic7xxx/aic7xxx_93cx6.c
@@ -0,0 +1,213 @@
+/*
+ * 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, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aic7xxx_93cx6.c#3 $
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * 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.
+ *
+ */
+
+#ifdef __linux__
+#include "aic7xxx_linux.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+#endif
+
+#ifdef __FreeBSD__
+#include <dev/aic7xxx/aic7xxx_freebsd.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aic7xxx_93cx6.h>
+#endif
+
+/*
+ * Right now, we only have to read the SEEPROM. But we make it easier to
+ * add other 93Cx6 functions.
+ */
+static struct seeprom_cmd {
+ uint8_t len;
+ uint8_t 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;
+ uint16_t *buf;
+ u_int start_addr;
+ u_int count;
+{
+ int i = 0;
+ u_int k = 0;
+ uint16_t v;
+ uint8_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 AHC_DUMP_EEPROM
+ printf("\nSerial EEPROM:\n\t");
+ for (k = 0; k < count; k = k + 1) {
+ if (((k % 8) == 0) && (k != 0)) {
+ printf ("\n\t");
+ }
+ printf (" 0x%x", buf[k]);
+ }
+ printf ("\n");
+#endif
+ return (1);
+}
+
+int
+verify_cksum(struct seeprom_config *sc)
+{
+ int i;
+ int maxaddr;
+ uint32_t checksum;
+ uint16_t *scarray;
+
+ maxaddr = (sizeof(*sc)/2) - 1;
+ checksum = 0;
+ scarray = (uint16_t *)sc;
+
+ for (i = 0; i < maxaddr; i++)
+ checksum = checksum + scarray[i];
+ if (checksum == 0
+ || (checksum & 0xFFFF) != sc->checksum) {
+ return (0);
+ } else {
+ return(1);
+ }
+}
diff --git a/sys/dev/aic7xxx/aic7xxx_93cx6.h b/sys/dev/aic7xxx/aic7xxx_93cx6.h
new file mode 100644
index 000000000000..323bfe8fa05f
--- /dev/null
+++ b/sys/dev/aic7xxx/aic7xxx_93cx6.h
@@ -0,0 +1,92 @@
+/*
+ * Interface to the 93C46/56 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, 2000 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aic7xxx_93cx6.h#3 $
+ *
+ * $FreeBSD$
+ */
+#ifndef _AIC7XXX_93CX6_H_
+#define _AIC7XXX_93CX6_H_
+
+typedef enum {
+ C46 = 6,
+ C56_66 = 8
+} seeprom_chip_t;
+
+struct seeprom_descriptor {
+ struct ahc_softc *sd_ahc;
+ u_int sd_control_offset;
+ u_int sd_status_offset;
+ u_int sd_dataout_offset;
+ seeprom_chip_t sd_chip;
+ uint16_t sd_MS;
+ uint16_t sd_RDY;
+ uint16_t sd_CS;
+ uint16_t sd_CK;
+ uint16_t sd_DO;
+ uint16_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) \
+ ahc_inb(sd->sd_ahc, sd->sd_control_offset)
+#define SEEPROM_OUTB(sd, value) \
+do { \
+ ahc_outb(sd->sd_ahc, sd->sd_control_offset, value); \
+ ahc_flush_device_writes(sd->sd_ahc); \
+} while(0)
+
+#define SEEPROM_STATUS_INB(sd) \
+ ahc_inb(sd->sd_ahc, sd->sd_status_offset)
+#define SEEPROM_DATA_INB(sd) \
+ ahc_inb(sd->sd_ahc, sd->sd_dataout_offset)
+
+int read_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+ u_int start_addr, u_int count);
+int verify_cksum(struct seeprom_config *sc);
+
+#endif /* _AIC7XXX_93CX6_H_ */
diff --git a/sys/dev/aic7xxx/aic7xxx_freebsd.c b/sys/dev/aic7xxx/aic7xxx_freebsd.c
new file mode 100644
index 000000000000..6f1b4d13cd08
--- /dev/null
+++ b/sys/dev/aic7xxx/aic7xxx_freebsd.c
@@ -0,0 +1,1822 @@
+/*
+ * Bus independent FreeBSD shim for the aic7xxx based adaptec SCSI controllers
+ *
+ * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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$
+ *
+ * $FreeBSD$
+ */
+
+#include <dev/aic7xxx/aic7xxx_freebsd.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+
+#include <sys/eventhandler.h>
+
+#ifndef AHC_TMODE_ENABLE
+#define AHC_TMODE_ENABLE 0
+#endif
+
+#define ccb_scb_ptr spriv_ptr0
+#define ccb_ahc_ptr spriv_ptr1
+
+#ifdef AHC_DEBUG
+static int ahc_debug = AHC_DEBUG;
+#endif
+
+static void ahc_freebsd_intr(void *arg);
+
+#if UNUSED
+static void ahc_dump_targcmd(struct target_cmd *cmd);
+#endif
+static void ahc_action(struct cam_sim *sim, union ccb *ccb);
+static void ahc_get_tran_settings(struct ahc_softc *ahc,
+ int our_id, char channel,
+ struct ccb_trans_settings *cts);
+static void ahc_async(void *callback_arg, uint32_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 cam_sim *sim,
+ struct ccb_scsiio *csio, struct scb *scb);
+static void ahc_abort_ccb(struct ahc_softc *ahc, struct cam_sim *sim,
+ union ccb *ccb);
+static int ahc_create_path(struct ahc_softc *ahc,
+ char channel, u_int target, u_int lun,
+ struct cam_path **path);
+
+static void ahc_set_recoveryscb(struct ahc_softc *ahc, struct scb *scb);
+
+static int
+ahc_create_path(struct ahc_softc *ahc, char channel, u_int target,
+ u_int lun, struct cam_path **path)
+{
+ path_id_t path_id;
+
+ if (channel == 'B')
+ path_id = cam_sim_path(ahc->platform_data->sim_b);
+ else
+ path_id = cam_sim_path(ahc->platform_data->sim);
+
+ return (xpt_create_path(path, /*periph*/NULL,
+ path_id, target, lun));
+}
+
+/*
+ * Attach all the sub-devices we can find
+ */
+int
+ahc_attach(struct ahc_softc *ahc)
+{
+ char ahc_info[256];
+ struct ccb_setasync csa;
+ struct cam_devq *devq;
+ int bus_id;
+ int bus_id2;
+ struct cam_sim *sim;
+ struct cam_sim *sim2;
+ struct cam_path *path;
+ struct cam_path *path2;
+ long s;
+ int count;
+ int error;
+
+ count = 0;
+ sim = NULL;
+ sim2 = NULL;
+
+ ahc_controller_info(ahc, ahc_info);
+ printf("%s\n", ahc_info);
+ ahc_lock(ahc, &s);
+ /* Hook up our interrupt handler */
+ if ((error = bus_setup_intr(ahc->dev_softc, ahc->platform_data->irq,
+ INTR_TYPE_CAM, ahc_freebsd_intr, ahc,
+ &ahc->platform_data->ih)) != 0) {
+ device_printf(ahc->dev_softc, "bus_setup_intr() failed: %d\n",
+ error);
+ goto fail;
+ }
+
+ /*
+ * Attach secondary channel first if the user has
+ * declared it the primary channel.
+ */
+ if ((ahc->flags & AHC_CHANNEL_B_PRIMARY) != 0) {
+ bus_id = 1;
+ bus_id2 = 0;
+ } else {
+ bus_id = 0;
+ bus_id2 = 1;
+ }
+
+ /*
+ * Create the device queue for our SIM(s).
+ */
+ devq = cam_simq_alloc(AHC_SCB_MAX);
+ if (devq == NULL)
+ goto fail;
+
+ /*
+ * Construct our first channel SIM entry
+ */
+ sim = cam_sim_alloc(ahc_action, ahc_poll, "ahc", ahc,
+ device_get_unit(ahc->dev_softc),
+ 1, AHC_SCB_MAX, devq);
+ if (sim == NULL) {
+ cam_simq_free(devq);
+ goto fail;
+ }
+
+ if (xpt_bus_register(sim, bus_id) != CAM_SUCCESS) {
+ cam_sim_free(sim, /*free_devq*/TRUE);
+ sim = NULL;
+ goto fail;
+ }
+
+ if (xpt_create_path(&path, /*periph*/NULL,
+ cam_sim_path(sim), CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+ xpt_bus_deregister(cam_sim_path(sim));
+ cam_sim_free(sim, /*free_devq*/TRUE);
+ sim = NULL;
+ goto fail;
+ }
+
+ xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5);
+ csa.ccb_h.func_code = XPT_SASYNC_CB;
+ csa.event_enable = AC_LOST_DEVICE;
+ csa.callback = ahc_async;
+ csa.callback_arg = sim;
+ xpt_action((union ccb *)&csa);
+ count++;
+
+ if (ahc->features & AHC_TWIN) {
+ sim2 = cam_sim_alloc(ahc_action, ahc_poll, "ahc",
+ ahc, device_get_unit(ahc->dev_softc), 1,
+ AHC_SCB_MAX, devq);
+
+ if (sim2 == NULL) {
+ printf("ahc_attach: Unable to attach second "
+ "bus due to resource shortage");
+ goto fail;
+ }
+
+ if (xpt_bus_register(sim2, bus_id2) != 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(sim2, /*free_devq*/FALSE);
+ goto fail;
+ }
+
+ if (xpt_create_path(&path2, /*periph*/NULL,
+ cam_sim_path(sim2),
+ CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+ xpt_bus_deregister(cam_sim_path(sim2));
+ cam_sim_free(sim2, /*free_devq*/FALSE);
+ sim2 = NULL;
+ goto fail;
+ }
+ xpt_setup_ccb(&csa.ccb_h, path2, /*priority*/5);
+ csa.ccb_h.func_code = XPT_SASYNC_CB;
+ csa.event_enable = AC_LOST_DEVICE;
+ csa.callback = ahc_async;
+ csa.callback_arg = sim2;
+ xpt_action((union ccb *)&csa);
+ count++;
+ }
+
+fail:
+ if ((ahc->flags & AHC_CHANNEL_B_PRIMARY) != 0) {
+ ahc->platform_data->sim_b = sim;
+ ahc->platform_data->path_b = path;
+ ahc->platform_data->sim = sim2;
+ ahc->platform_data->path = path2;
+ } else {
+ ahc->platform_data->sim = sim;
+ ahc->platform_data->path = path;
+ ahc->platform_data->sim_b = sim2;
+ ahc->platform_data->path_b = path2;
+ }
+ ahc_unlock(ahc, &s);
+
+ if (count != 0)
+ /* We have to wait until after any system dumps... */
+ EVENTHANDLER_REGISTER(shutdown_final, ahc_shutdown,
+ ahc, SHUTDOWN_PRI_DEFAULT);
+
+ return (count);
+}
+
+/*
+ * Catch an interrupt from the adapter
+ */
+void
+ahc_freebsd_intr(void *arg)
+{
+ struct ahc_softc *ahc;
+
+ ahc = (struct ahc_softc *)arg;
+ ahc_intr(ahc);
+}
+
+/*
+ * We have an scb which has been processed by the
+ * adaptor, now we look to see how the operation
+ * went.
+ */
+void
+ahc_done(struct ahc_softc *ahc, struct scb *scb)
+{
+ union ccb *ccb;
+
+ CAM_DEBUG(scb->io_ctx->ccb_h.path, CAM_DEBUG_TRACE,
+ ("ahc_done - scb %d\n", scb->hscb->tag));
+
+ ccb = scb->io_ctx;
+ LIST_REMOVE(scb, pending_links);
+ if (ccb->ccb_h.func_code == XPT_SCSI_IO
+ && ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) == 0
+ || ccb->csio.tag_action == CAM_TAG_ACTION_NONE)
+ && (ahc->features & AHC_SCB_BTT) == 0) {
+ struct scb_tailq *untagged_q;
+
+ untagged_q = &ahc->untagged_queues[ccb->ccb_h.target_id];
+ TAILQ_REMOVE(untagged_q, scb, links.tqe);
+ ahc_run_untagged_queue(ahc, untagged_q);
+ }
+
+ 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->buffer_dmat, scb->dmamap, op);
+ bus_dmamap_unload(ahc->buffer_dmat, scb->dmamap);
+ }
+
+ if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) {
+ if (ahc_get_transaction_status(scb) == CAM_REQ_INPROG)
+ ccb->ccb_h.status |= CAM_REQ_CMP;
+ ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
+ 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 scb *list_scb;
+
+ /*
+ * We were able to complete the command successfully,
+ * so reinstate the timeouts for all other pending
+ * commands.
+ */
+ LIST_FOREACH(list_scb, &ahc->pending_scbs, pending_links) {
+ union ccb *ccb;
+
+ ccb = list_scb->io_ctx;
+ ccb->ccb_h.timeout_ch =
+ timeout(ahc_timeout, list_scb,
+ (ccb->ccb_h.timeout * hz)/1000);
+ }
+
+ /*
+ * Ensure that we didn't put a second instance of this
+ * SCB into the QINFIFO.
+ */
+ ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
+ SCB_GET_CHANNEL(ahc, scb),
+ SCB_GET_LUN(scb), scb->hscb->tag,
+ ROLE_INITIATOR, /*status*/0,
+ SEARCH_REMOVE);
+ if (ahc_get_transaction_status(scb) == CAM_BDR_SENT
+ || ahc_get_transaction_status(scb) == CAM_REQ_ABORTED)
+ ahc_set_transaction_status(scb, CAM_CMD_TIMEOUT);
+ ahc_print_path(ahc, scb);
+ printf("no longer in timeout, status = %x\n",
+ ccb->ccb_h.status);
+ }
+
+ /* Don't clobber any existing error state */
+ if (ahc_get_transaction_status(scb) == CAM_REQ_INPROG) {
+ ccb->ccb_h.status |= CAM_REQ_CMP;
+ } else if ((scb->flags & SCB_SENSE) != 0) {
+ /*
+ * We performed autosense retrieval.
+ *
+ * Zero any sense not transferred by the
+ * device. The SCSI spec mandates that any
+ * untransfered data should be assumed to be
+ * zero. Complete the 'bounce' of sense information
+ * through buffers accessible via bus-space by
+ * copying it into the clients csio.
+ */
+ memset(&ccb->csio.sense_data, 0, sizeof(ccb->csio.sense_data));
+ memcpy(&ccb->csio.sense_data,
+ &ahc->scb_data->sense[scb->hscb->tag],
+ (scb->sg_list->len & AHC_SG_LEN_MASK)
+ - ccb->csio.sense_resid);
+ scb->io_ctx->ccb_h.status |= CAM_AUTOSNS_VALID;
+ }
+ ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
+ ahc_free_scb(ahc, scb);
+ xpt_done(ccb);
+}
+
+static void
+ahc_action(struct cam_sim *sim, union ccb *ccb)
+{
+ struct ahc_softc *ahc;
+ struct tmode_lstate *lstate;
+ u_int target_id;
+ u_int our_id;
+ long 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;
+ our_id = SIM_SCSI_ID(ahc, sim);
+
+ 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) {
+ if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) {
+ /* Response from the black hole device */
+ tstate = NULL;
+ lstate = ahc->black_hole;
+ } else {
+ ccb->ccb_h.status = status;
+ xpt_done(ccb);
+ break;
+ }
+ }
+ if (ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) {
+
+ ahc_lock(ahc, &s);
+ SLIST_INSERT_HEAD(&lstate->accept_tios, &ccb->ccb_h,
+ sim_links.sle);
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+ if ((ahc->flags & AHC_TQINFIFO_BLOCKED) != 0)
+ ahc_run_tqinfifo(ahc, /*paused*/FALSE);
+ ahc_unlock(ahc, &s);
+ break;
+ }
+
+ /*
+ * The target_id represents the target we attempt to
+ * select. In target mode, this is the initiator of
+ * the original command.
+ */
+ our_id = target_id;
+ target_id = ccb->csio.init_id;
+ /* 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_initiator_tinfo *tinfo;
+ struct tmode_tstate *tstate;
+ uint16_t mask;
+
+ /*
+ * get an scb to use.
+ */
+ if ((scb = ahc_get_scb(ahc)) == NULL) {
+
+ ahc_lock(ahc, &s);
+ ahc->flags |= AHC_RESOURCE_SHORTAGE;
+ ahc_unlock(ahc, &s);
+ xpt_freeze_simq(sim, /*count*/1);
+ ahc_set_transaction_status(scb, 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->io_ctx = 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->control = 0;
+ hscb->scsiid = BUILD_SCSIID(ahc, sim, target_id, our_id);
+ hscb->lun = ccb->ccb_h.target_lun;
+ mask = SCB_GET_TARGET_MASK(ahc, scb);
+ tinfo = ahc_fetch_transinfo(ahc, SIM_CHANNEL(ahc, sim), our_id,
+ target_id, &tstate);
+
+ hscb->scsirate = tinfo->scsirate;
+ hscb->scsioffset = tinfo->current.offset;
+ if ((tstate->ultraenb & mask) != 0)
+ hscb->control |= ULTRAENB;
+
+ if ((tstate->discenable & mask) != 0
+ && (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) == 0)
+ hscb->control |= DISCENB;
+
+ if ((ccb->ccb_h.flags & CAM_NEGOTIATE) != 0
+ && (tinfo->current.width != 0 || tinfo->current.period != 0)) {
+ scb->flags |= SCB_NEGOTIATE;
+ hscb->control |= MK_MESSAGE;
+ }
+
+ if (ccb->ccb_h.func_code == XPT_RESET_DEV) {
+ hscb->cdb_len = 0;
+ scb->flags |= SCB_DEVICE_RESET;
+ hscb->control |= MK_MESSAGE;
+ ahc_execute_scb(scb, NULL, 0, 0);
+ } else {
+ if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) {
+ struct target_data *tdata;
+
+ tdata = &hscb->shared_data.tdata;
+ if (ahc->pending_device == lstate) {
+ scb->flags |= SCB_TARGET_IMMEDIATE;
+ ahc->pending_device = NULL;
+ }
+ hscb->control |= TARGET_SCB;
+ tdata->target_phases = IDENTIFY_SEEN;
+ if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) {
+ tdata->target_phases |= SPHASE_PENDING;
+ tdata->scsi_status =
+ ccb->csio.scsi_status;
+ }
+ tdata->initiator_tag = ccb->csio.tag_id;
+ }
+ if (ccb->ccb_h.flags & CAM_TAG_ACTION_VALID)
+ hscb->control |= ccb->csio.tag_action;
+
+ ahc_setup_data(ahc, sim, &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;
+ }
+ SLIST_INSERT_HEAD(&lstate->immed_notifies, &ccb->ccb_h,
+ sim_links.sle);
+ ccb->ccb_h.status = CAM_REQ_INPROG;
+ ahc_send_lstate_events(ahc, lstate);
+ break;
+ }
+ case XPT_EN_LUN: /* Enable LUN as a target */
+ ahc_handle_en_lun(ahc, sim, ccb);
+ xpt_done(ccb);
+ break;
+ case XPT_ABORT: /* Abort the specified CCB */
+ {
+ ahc_abort_ccb(ahc, sim, ccb);
+ break;
+ }
+ case XPT_SET_TRAN_SETTINGS:
+ {
+#ifdef AHC_NEW_TRAN_SETTINGS
+ struct ahc_devinfo devinfo;
+ struct ccb_trans_settings *cts;
+ struct ccb_trans_settings_scsi *scsi;
+ struct ccb_trans_settings_spi *spi;
+ struct ahc_initiator_tinfo *tinfo;
+ struct tmode_tstate *tstate;
+ uint16_t *discenable;
+ uint16_t *tagenable;
+ u_int update_type;
+
+ cts = &ccb->cts;
+ scsi = &cts->proto_specific.scsi;
+ spi = &cts->xport_specific.spi;
+ ahc_compile_devinfo(&devinfo, SIM_SCSI_ID(ahc, sim),
+ cts->ccb_h.target_id,
+ cts->ccb_h.target_lun,
+ SIM_CHANNEL(ahc, sim),
+ ROLE_UNKNOWN);
+ tinfo = ahc_fetch_transinfo(ahc, devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target, &tstate);
+ update_type = 0;
+ if (cts->type == CTS_TYPE_CURRENT_SETTINGS) {
+ update_type |= AHC_TRANS_GOAL;
+ discenable = &tstate->discenable;
+ tagenable = &tstate->tagenable;
+ tinfo->current.protocol_version =
+ cts->protocol_version;
+ tinfo->current.transport_version =
+ cts->transport_version;
+ tinfo->goal.protocol_version =
+ cts->protocol_version;
+ tinfo->goal.transport_version =
+ cts->transport_version;
+ } else if (cts->type == CTS_TYPE_USER_SETTINGS) {
+ update_type |= AHC_TRANS_USER;
+ discenable = &ahc->user_discenable;
+ tagenable = &ahc->user_tagenable;
+ tinfo->user.protocol_version =
+ cts->protocol_version;
+ tinfo->user.transport_version =
+ cts->transport_version;
+ } else {
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ }
+
+ ahc_lock(ahc, &s);
+
+ if ((spi->valid & CTS_SPI_VALID_DISC) != 0) {
+ if ((spi->flags & CTS_SPI_FLAGS_DISC_ENB) != 0)
+ *discenable |= devinfo.target_mask;
+ else
+ *discenable &= ~devinfo.target_mask;
+ }
+
+ if ((scsi->valid & CTS_SCSI_VALID_TQ) != 0) {
+ if ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB) != 0)
+ *tagenable |= devinfo.target_mask;
+ else
+ *tagenable &= ~devinfo.target_mask;
+ }
+
+ if ((spi->valid & CTS_SPI_VALID_BUS_WIDTH) != 0) {
+ ahc_validate_width(ahc, &spi->bus_width);
+ ahc_set_width(ahc, &devinfo, spi->bus_width,
+ update_type, /*paused*/FALSE);
+ }
+
+ if ((spi->valid & CTS_SPI_VALID_PPR_OPTIONS) == 0) {
+ if (update_type == AHC_TRANS_USER)
+ spi->ppr_options = tinfo->user.ppr_options;
+ else
+ spi->ppr_options = tinfo->goal.ppr_options;
+ }
+
+ if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) == 0) {
+ if (update_type == AHC_TRANS_USER)
+ spi->sync_offset = tinfo->user.offset;
+ else
+ spi->sync_offset = tinfo->goal.offset;
+ }
+
+ if ((spi->valid & CTS_SPI_VALID_SYNC_RATE) == 0) {
+ if (update_type == AHC_TRANS_USER)
+ spi->sync_period = tinfo->user.period;
+ else
+ spi->sync_period = tinfo->goal.period;
+ }
+
+ if (((spi->valid & CTS_SPI_VALID_SYNC_RATE) != 0)
+ || ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) != 0)) {
+ struct ahc_syncrate *syncrate;
+ u_int maxsync;
+
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ maxsync = AHC_SYNCRATE_DT;
+ else if ((ahc->features & AHC_ULTRA) != 0)
+ maxsync = AHC_SYNCRATE_ULTRA;
+ else
+ maxsync = AHC_SYNCRATE_FAST;
+
+ syncrate = ahc_find_syncrate(ahc, &spi->sync_period,
+ &spi->ppr_options,
+ maxsync);
+ ahc_validate_offset(ahc, syncrate, &spi->sync_offset,
+ spi->bus_width);
+
+ /* We use a period of 0 to represent async */
+ if (spi->sync_offset == 0) {
+ spi->sync_period = 0;
+ spi->ppr_options = 0;
+ }
+
+ ahc_set_syncrate(ahc, &devinfo, syncrate,
+ spi->sync_period, spi->sync_offset,
+ spi->ppr_options, update_type,
+ /*paused*/FALSE);
+ }
+ ahc_unlock(ahc, &s);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+#else
+ struct ahc_devinfo devinfo;
+ struct ccb_trans_settings *cts;
+ struct ahc_initiator_tinfo *tinfo;
+ struct tmode_tstate *tstate;
+ uint16_t *discenable;
+ uint16_t *tagenable;
+ u_int update_type;
+ long s;
+
+ cts = &ccb->cts;
+ ahc_compile_devinfo(&devinfo, SIM_SCSI_ID(ahc, sim),
+ cts->ccb_h.target_id,
+ cts->ccb_h.target_lun,
+ SIM_CHANNEL(ahc, sim),
+ ROLE_UNKNOWN);
+ tinfo = ahc_fetch_transinfo(ahc, devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target, &tstate);
+ update_type = 0;
+ if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) {
+ update_type |= AHC_TRANS_GOAL;
+ discenable = &tstate->discenable;
+ tagenable = &tstate->tagenable;
+ } else if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) {
+ update_type |= AHC_TRANS_USER;
+ discenable = &ahc->user_discenable;
+ tagenable = &ahc->user_tagenable;
+ } else {
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ break;
+ }
+
+ ahc_lock(ahc, &s);
+
+ if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) {
+ if ((cts->flags & CCB_TRANS_DISC_ENB) != 0)
+ *discenable |= devinfo.target_mask;
+ else
+ *discenable &= ~devinfo.target_mask;
+ }
+
+ if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) {
+ if ((cts->flags & CCB_TRANS_TAG_ENB) != 0)
+ *tagenable |= devinfo.target_mask;
+ else
+ *tagenable &= ~devinfo.target_mask;
+ }
+
+ if ((cts->valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) {
+ ahc_validate_width(ahc, &cts->bus_width);
+ ahc_set_width(ahc, &devinfo, cts->bus_width,
+ update_type, /*paused*/FALSE);
+ }
+
+ if ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) == 0) {
+ if (update_type == AHC_TRANS_USER)
+ cts->sync_offset = tinfo->user.offset;
+ else
+ cts->sync_offset = tinfo->goal.offset;
+ }
+
+ if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) == 0) {
+ if (update_type == AHC_TRANS_USER)
+ cts->sync_period = tinfo->user.period;
+ else
+ cts->sync_period = tinfo->goal.period;
+ }
+
+ if (((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0)
+ || ((cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0)) {
+ struct ahc_syncrate *syncrate;
+ u_int ppr_options;
+ u_int maxsync;
+
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ maxsync = AHC_SYNCRATE_DT;
+ else if ((ahc->features & AHC_ULTRA) != 0)
+ maxsync = AHC_SYNCRATE_ULTRA;
+ else
+ maxsync = AHC_SYNCRATE_FAST;
+
+ ppr_options = 0;
+ if (cts->sync_period <= 9)
+ ppr_options = MSG_EXT_PPR_DT_REQ;
+
+ syncrate = ahc_find_syncrate(ahc, &cts->sync_period,
+ &ppr_options,
+ maxsync);
+ ahc_validate_offset(ahc, syncrate, &cts->sync_offset,
+ MSG_EXT_WDTR_BUS_8_BIT);
+
+ /* We use a period of 0 to represent async */
+ if (cts->sync_offset == 0) {
+ cts->sync_period = 0;
+ ppr_options = 0;
+ }
+
+ if (ppr_options == MSG_EXT_PPR_DT_REQ
+ && tinfo->user.transport_version >= 3) {
+ tinfo->goal.transport_version =
+ tinfo->user.transport_version;
+ tinfo->current.transport_version =
+ tinfo->user.transport_version;
+ }
+
+ ahc_set_syncrate(ahc, &devinfo, syncrate,
+ cts->sync_period, cts->sync_offset,
+ ppr_options, update_type,
+ /*paused*/FALSE);
+ }
+ ahc_unlock(ahc, &s);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_done(ccb);
+#endif
+ break;
+ }
+ case XPT_GET_TRAN_SETTINGS:
+ /* Get default/user set transfer settings for the target */
+ {
+
+ ahc_lock(ahc, &s);
+ ahc_get_tran_settings(ahc, SIM_SCSI_ID(ahc, sim),
+ SIM_CHANNEL(ahc, sim), &ccb->cts);
+ ahc_unlock(ahc, &s);
+ xpt_done(ccb);
+ break;
+ }
+ case XPT_CALC_GEOMETRY:
+ {
+ struct ccb_calc_geometry *ccg;
+ uint32_t size_mb;
+ uint32_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 */
+ {
+ int found;
+
+ ahc_lock(ahc, &s);
+ found = ahc_reset_channel(ahc, SIM_CHANNEL(ahc, sim),
+ /*initiate reset*/TRUE);
+ ahc_unlock(ahc, &s);
+ if (bootverbose) {
+ xpt_print_path(SIM_PATH(ahc, sim));
+ 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 = (ahc->flags & AHC_INITIATORMODE)
+ ? 0 : PIM_NOINITIATOR;
+ cpi->hba_eng_cnt = 0;
+ cpi->max_target = (ahc->features & AHC_WIDE) ? 15 : 7;
+ cpi->max_lun = 64;
+ if (SIM_IS_SCSIBUS_B(ahc, sim)) {
+ cpi->initiator_id = ahc->our_id_b;
+ if ((ahc->flags & AHC_RESET_BUS_B) == 0)
+ cpi->hba_misc |= PIM_NOBUSRESET;
+ } else {
+ cpi->initiator_id = ahc->our_id;
+ if ((ahc->flags & AHC_RESET_BUS_A) == 0)
+ cpi->hba_misc |= PIM_NOBUSRESET;
+ }
+ cpi->bus_id = cam_sim_bus(sim);
+ cpi->base_transfer_speed = 3300;
+ 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);
+#ifdef AHC_NEW_TRAN_SETTINGS
+ cpi->protocol = PROTO_SCSI;
+ cpi->protocol_version = SCSI_REV_2;
+ cpi->transport = XPORT_SPI;
+ cpi->transport_version = 2;
+ cpi->xport_specific.spi.ppr_options = SID_SPI_CLOCK_ST;
+ if ((ahc->features & AHC_DT) != 0) {
+ cpi->transport_version = 3;
+ cpi->xport_specific.spi.ppr_options =
+ SID_SPI_CLOCK_DT_ST;
+ }
+#endif
+ 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_get_tran_settings(struct ahc_softc *ahc, int our_id, char channel,
+ struct ccb_trans_settings *cts)
+{
+#ifdef AHC_NEW_TRAN_SETTINGS
+ struct ahc_devinfo devinfo;
+ struct ccb_trans_settings_scsi *scsi;
+ struct ccb_trans_settings_spi *spi;
+ struct ahc_initiator_tinfo *targ_info;
+ struct tmode_tstate *tstate;
+ struct ahc_transinfo *tinfo;
+
+ scsi = &cts->proto_specific.scsi;
+ spi = &cts->xport_specific.spi;
+ ahc_compile_devinfo(&devinfo, our_id,
+ cts->ccb_h.target_id,
+ cts->ccb_h.target_lun,
+ channel, ROLE_UNKNOWN);
+ targ_info = ahc_fetch_transinfo(ahc, devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target, &tstate);
+
+ if (cts->type == CTS_TYPE_CURRENT_SETTINGS)
+ tinfo = &targ_info->current;
+ else
+ tinfo = &targ_info->user;
+
+ scsi->flags &= ~CTS_SCSI_FLAGS_TAG_ENB;
+ spi->flags &= ~CTS_SPI_FLAGS_DISC_ENB;
+ if (cts->type == CTS_TYPE_USER_SETTINGS) {
+ if ((ahc->user_discenable & devinfo.target_mask) != 0)
+ spi->flags |= CTS_SPI_FLAGS_DISC_ENB;
+
+ if ((ahc->user_tagenable & devinfo.target_mask) != 0)
+ scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB;
+ } else {
+ if ((tstate->discenable & devinfo.target_mask) != 0)
+ spi->flags |= CTS_SPI_FLAGS_DISC_ENB;
+
+ if ((tstate->tagenable & devinfo.target_mask) != 0)
+ scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB;
+ }
+ cts->protocol_version = tinfo->protocol_version;
+ cts->transport_version = tinfo->transport_version;
+
+ spi->sync_period = tinfo->period;
+ spi->sync_offset = tinfo->offset;
+ spi->bus_width = tinfo->width;
+ spi->ppr_options = tinfo->ppr_options;
+
+ cts->protocol = PROTO_SCSI;
+ cts->transport = XPORT_SPI;
+ scsi->valid = CTS_SCSI_VALID_TQ;
+ spi->valid = CTS_SPI_VALID_SYNC_RATE
+ | CTS_SPI_VALID_SYNC_OFFSET
+ | CTS_SPI_VALID_BUS_WIDTH
+ | CTS_SPI_VALID_DISC
+ | CTS_SPI_VALID_PPR_OPTIONS;
+
+ cts->ccb_h.status = CAM_REQ_CMP;
+#else
+ struct ahc_devinfo devinfo;
+ struct ahc_initiator_tinfo *targ_info;
+ struct tmode_tstate *tstate;
+ struct ahc_transinfo *tinfo;
+ long s;
+
+ ahc_compile_devinfo(&devinfo, our_id,
+ cts->ccb_h.target_id,
+ cts->ccb_h.target_lun,
+ channel, ROLE_UNKNOWN);
+ targ_info = ahc_fetch_transinfo(ahc, devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target, &tstate);
+
+ if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0)
+ tinfo = &targ_info->current;
+ else
+ tinfo = &targ_info->user;
+
+ ahc_lock(ahc, &s);
+
+ cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB);
+ if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) {
+ if ((ahc->user_discenable & devinfo.target_mask) != 0)
+ cts->flags |= CCB_TRANS_DISC_ENB;
+
+ if ((ahc->user_tagenable & devinfo.target_mask) != 0)
+ cts->flags |= CCB_TRANS_TAG_ENB;
+ } else {
+ if ((tstate->discenable & devinfo.target_mask) != 0)
+ cts->flags |= CCB_TRANS_DISC_ENB;
+
+ if ((tstate->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;
+
+ ahc_unlock(ahc, &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;
+
+ cts->ccb_h.status = CAM_REQ_CMP;
+#endif
+}
+
+static void
+ahc_async(void *callback_arg, uint32_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;
+ long s;
+
+ ahc_compile_devinfo(&devinfo, SIM_SCSI_ID(ahc, sim),
+ xpt_path_target_id(path),
+ xpt_path_lun_id(path),
+ SIM_CHANNEL(ahc, sim),
+ ROLE_UNKNOWN);
+
+ /*
+ * Revert to async/narrow transfers
+ * for the next device.
+ */
+ ahc_lock(ahc, &s);
+ ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_GOAL|AHC_TRANS_CUR, /*paused*/FALSE);
+ ahc_set_syncrate(ahc, &devinfo, /*syncrate*/NULL,
+ /*period*/0, /*offset*/0, /*ppr_options*/0,
+ AHC_TRANS_GOAL|AHC_TRANS_CUR,
+ /*paused*/FALSE);
+ ahc_unlock(ahc, &s);
+ 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;
+ long s;
+
+ scb = (struct scb *)arg;
+ ccb = scb->io_ctx;
+ ahc = (struct ahc_softc *)ccb->ccb_h.ccb_ahc_ptr;
+
+ if (error != 0) {
+ if (error == EFBIG)
+ ahc_set_transaction_status(scb, CAM_REQ_TOO_BIG);
+ else
+ ahc_set_transaction_status(scb, CAM_REQ_CMP_ERR);
+ if (nsegments != 0)
+ bus_dmamap_unload(ahc->buffer_dmat, scb->dmamap);
+ ahc_free_scb(ahc, scb);
+ xpt_done(ccb);
+ return;
+ }
+ 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 segments into our SG list */
+ sg = scb->sg_list;
+ while (dm_segs < end_seg) {
+ sg->addr = dm_segs->ds_addr;
+/* XXX Add in the 5th byte of the address later. */
+ sg->len = dm_segs->ds_len;
+ sg++;
+ dm_segs++;
+ }
+
+ /*
+ * Note where to find the SG entries in bus space.
+ * We also set the full residual flag which the
+ * sequencer will clear as soon as a data transfer
+ * occurs.
+ */
+ scb->hscb->sgptr = scb->sg_list_phys | SG_FULL_RESID;
+
+ if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
+ op = BUS_DMASYNC_PREREAD;
+ else
+ op = BUS_DMASYNC_PREWRITE;
+
+ bus_dmamap_sync(ahc->buffer_dmat, scb->dmamap, op);
+
+ if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) {
+ struct target_data *tdata;
+
+ tdata = &scb->hscb->shared_data.tdata;
+ tdata->target_phases |= DPHASE_PENDING;
+ if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT)
+ tdata->data_phase = P_DATAOUT;
+ else
+ tdata->data_phase = P_DATAIN;
+
+ /*
+ * If the transfer is of an odd length and in the
+ * "in" direction (scsi->HostBus), then it may
+ * trigger a bug in the 'WideODD' feature of
+ * non-Ultra2 chips. Force the total data-length
+ * to be even by adding an extra, 1 byte, SG,
+ * element. We do this even if we are not currently
+ * negotiated wide as negotiation could occur before
+ * this command is executed.
+ */
+ if ((ahc->bugs & AHC_TMODE_WIDEODD_BUG) != 0
+ && (ccb->csio.dxfer_len & 0x1) != 0
+ && (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
+
+ nsegments++;
+ if (nsegments > AHC_NSEG) {
+
+ ahc_set_transaction_status(scb,
+ CAM_REQ_TOO_BIG);
+ bus_dmamap_unload(ahc->buffer_dmat,
+ scb->dmamap);
+ ahc_free_scb(ahc, scb);
+ xpt_done(ccb);
+ return;
+ }
+ sg->addr = ahc->dma_bug_buf;
+ sg->len = 1;
+ sg++;
+ }
+ }
+ sg--;
+ sg->len |= AHC_DMA_LAST_SEG;
+
+ /* Copy the first SG into the "current" data pointer area */
+ scb->hscb->dataptr = scb->sg_list->addr;
+ scb->hscb->datacnt = scb->sg_list->len;
+ } else {
+ scb->hscb->sgptr = SG_LIST_NULL;
+ scb->hscb->dataptr = 0;
+ scb->hscb->datacnt = 0;
+ }
+
+ scb->sg_count = nsegments;
+
+ ahc_lock(ahc, &s);
+
+ /*
+ * Last time we need to check if this SCB needs to
+ * be aborted.
+ */
+ if (ahc_get_transaction_status(scb) != CAM_REQ_INPROG) {
+ if (nsegments != 0)
+ bus_dmamap_unload(ahc->buffer_dmat,
+ scb->dmamap);
+ ahc_free_scb(ahc, scb);
+ xpt_done(ccb);
+ ahc_unlock(ahc, &s);
+ return;
+ }
+
+ LIST_INSERT_HEAD(&ahc->pending_scbs, scb, pending_links);
+
+ ccb->ccb_h.status |= CAM_SIM_QUEUED;
+
+ if (ccb->ccb_h.timeout != CAM_TIME_INFINITY) {
+ if (ccb->ccb_h.timeout == CAM_TIME_DEFAULT)
+ ccb->ccb_h.timeout = 5 * 1000;
+ ccb->ccb_h.timeout_ch =
+ timeout(ahc_timeout, (caddr_t)scb,
+ (ccb->ccb_h.timeout * hz) / 1000);
+ }
+
+ /*
+ * We only allow one untagged transaction
+ * per target in the initiator role unless
+ * we are storing a full busy target *lun*
+ * table in SCB space.
+ */
+ if ((scb->hscb->control & (TARGET_SCB|TAG_ENB)) == 0
+ && (ahc->features & AHC_SCB_BTT) == 0) {
+ struct scb_tailq *untagged_q;
+
+ untagged_q = &(ahc->untagged_queues[ccb->ccb_h.target_id]);
+ TAILQ_INSERT_TAIL(untagged_q, scb, links.tqe);
+ if (TAILQ_FIRST(untagged_q) != scb) {
+ ahc_unlock(ahc, &s);
+ return;
+ }
+ }
+ scb->flags |= SCB_ACTIVE;
+
+ if ((scb->flags & SCB_TARGET_IMMEDIATE) != 0) {
+ pause_sequencer(ahc);
+ if ((ahc->flags & AHC_PAGESCBS) == 0)
+ ahc_outb(ahc, SCBPTR, scb->hscb->tag);
+ ahc_outb(ahc, SCB_TAG, scb->hscb->tag);
+ ahc_outb(ahc, RETURN_1, CONT_MSG_LOOP);
+ unpause_sequencer(ahc);
+ } else {
+ ahc_queue_scb(ahc, scb);
+ }
+
+ ahc_unlock(ahc, &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 cam_sim *sim,
+ 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->cdb_len = csio->cdb_len;
+ if ((ccb_h->flags & CAM_CDB_POINTER) != 0) {
+
+ if (hscb->cdb_len > sizeof(hscb->cdb32)
+ || (ccb_h->flags & CAM_CDB_PHYS) != 0) {
+ ahc_set_transaction_status(scb,
+ CAM_REQ_INVALID);
+ xpt_done(scb->io_ctx);
+ ahc_free_scb(ahc, scb);
+ return;
+ }
+ if (hscb->cdb_len > 12) {
+ memcpy(hscb->cdb32,
+ csio->cdb_io.cdb_ptr,
+ hscb->cdb_len);
+ hscb->shared_data.cdb_ptr = scb->cdb32_busaddr;
+ } else {
+ memcpy(hscb->shared_data.cdb,
+ csio->cdb_io.cdb_ptr,
+ hscb->cdb_len);
+ }
+ } else {
+ if (hscb->cdb_len > 12) {
+ memcpy(hscb->cdb32, csio->cdb_io.cdb_bytes,
+ hscb->cdb_len);
+ hscb->shared_data.cdb_ptr = scb->cdb32_busaddr;
+ } else {
+ memcpy(hscb->shared_data.cdb,
+ csio->cdb_io.cdb_bytes,
+ hscb->cdb_len);
+ }
+ }
+ }
+
+ /* 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->buffer_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(sim,
+ /*count*/1);
+ scb->io_ctx->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);
+ }
+ } else {
+ ahc_execute_scb(scb, NULL, 0, 0);
+ }
+}
+
+static void
+ahc_set_recoveryscb(struct ahc_softc *ahc, struct scb *scb) {
+
+ if ((scb->flags & SCB_RECOVERY_SCB) == 0) {
+ struct scb *list_scb;
+
+ 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->io_ctx->ccb_h.status & CAM_RELEASE_SIMQ) == 0) {
+ xpt_freeze_simq(SCB_GET_SIM(ahc, scb), /*count*/1);
+ scb->io_ctx->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.
+ */
+ LIST_FOREACH(list_scb, &ahc->pending_scbs, pending_links) {
+ union ccb *ccb;
+
+ ccb = list_scb->io_ctx;
+ untimeout(ahc_timeout, list_scb, ccb->ccb_h.timeout_ch);
+ }
+ }
+}
+
+void
+ahc_timeout(void *arg)
+{
+ struct scb *scb;
+ struct ahc_softc *ahc;
+ long s;
+ int found;
+ u_int last_phase;
+ int target;
+ int lun;
+ int i;
+ char channel;
+
+ scb = (struct scb *)arg;
+ ahc = (struct ahc_softc *)scb->io_ctx->ccb_h.ccb_ahc_ptr;
+
+ ahc_lock(ahc, &s);
+
+ /*
+ * 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);
+
+ ahc_print_path(ahc, scb);
+ if ((scb->flags & SCB_ACTIVE) == 0) {
+ /* Previous timeout took care of me already */
+ printf("Timedout SCB %d handled by another timeout\n",
+ scb->hscb->tag);
+ unpause_sequencer(ahc);
+ ahc_unlock(ahc, &s);
+ return;
+ }
+
+ target = SCB_GET_TARGET(ahc, scb);
+ channel = SCB_GET_CHANNEL(ahc, scb);
+ lun = SCB_GET_LUN(scb);
+
+ 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.
+ */
+ last_phase = ahc_inb(ahc, LASTPHASE);
+
+ for (i = 0; i < num_phases; i++) {
+ if (last_phase == phase_table[i].phase)
+ break;
+ }
+ printf("%s", phase_table[i].phasemsg);
+
+ printf(", SEQADDR == 0x%x\n",
+ ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
+
+ if (scb->sg_count > 0) {
+ for (i = 0; i < scb->sg_count; i++) {
+ printf("sg[%d] - Addr 0x%x : Length %d\n",
+ i,
+ scb->sg_list[i].addr,
+ scb->sg_list[i].len);
+ }
+ }
+ if (scb->flags & (SCB_DEVICE_RESET|SCB_ABORT)) {
+ /*
+ * Been down this road before.
+ * Do a full bus reset.
+ */
+bus_reset:
+ ahc_set_transaction_status(scb, 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 {
+ /*
+ * If we are a target, transition to bus free and report
+ * the timeout.
+ *
+ * The target/initiator that is holding up the bus may not
+ * be the same as the one that triggered this timeout
+ * (different commands have different timeout lengths).
+ * If the bus is idle and we are actiing as the initiator
+ * for this request, queue a BDR message to the timed out
+ * target. Otherwise, if the timed out transaction is
+ * active:
+ * Initiator transaction:
+ * 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.
+ *
+ * Target transaction:
+ * Transition to BUS FREE and report the error.
+ * It's good to be the target!
+ */
+ u_int active_scb_index;
+
+ active_scb_index = ahc_inb(ahc, SCB_TAG);
+
+ if (last_phase != 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 additional action.
+ */
+ active_scb = &ahc->scb_data->scbarray[active_scb_index];
+ if (active_scb->hscb->scsiid != scb->hscb->scsiid
+ || active_scb->hscb->lun != scb->hscb->lun) {
+ struct ccb_hdr *ccbh;
+ u_int newtimeout;
+
+ ahc_print_path(ahc, scb);
+ printf("Other SCB Timeout%s",
+ (scb->flags & SCB_OTHERTCL_TIMEOUT) != 0
+ ? " again\n" : "\n");
+ scb->flags |= SCB_OTHERTCL_TIMEOUT;
+ newtimeout =
+ MAX(active_scb->io_ctx->ccb_h.timeout,
+ scb->io_ctx->ccb_h.timeout);
+ ccbh = &scb->io_ctx->ccb_h;
+ scb->io_ctx->ccb_h.timeout_ch =
+ timeout(ahc_timeout, scb,
+ (newtimeout * hz) / 1000);
+ ahc_unlock(ahc, &s);
+ return;
+ }
+
+ /* It's us */
+ if ((scb->hscb->control & TARGET_SCB) != 0) {
+
+ /*
+ * Send back any queued up transactions
+ * and properly record the error condition.
+ */
+ ahc_freeze_devq(ahc, scb);
+ ahc_set_transaction_status(scb,
+ CAM_CMD_TIMEOUT);
+ ahc_freeze_scb(scb);
+ ahc_done(ahc, scb);
+
+ /* Will clear us from the bus */
+ restart_sequencer(ahc);
+ return;
+ }
+
+ ahc_set_recoveryscb(ahc, active_scb);
+ ahc_outb(ahc, MSG_OUT, MSG_BUS_DEV_RESET);
+ ahc_outb(ahc, SCSISIGO, last_phase|ATNO);
+ ahc_print_path(ahc, active_scb);
+ printf("BDR message in message buffer\n");
+ active_scb->flags |= SCB_DEVICE_RESET;
+ active_scb->io_ctx->ccb_h.timeout_ch =
+ timeout(ahc_timeout, (caddr_t)active_scb, 2 * hz);
+ unpause_sequencer(ahc);
+ } else {
+ int disconnected;
+
+ /* XXX Shouldn't panic. Just punt instead */
+ if ((scb->hscb->control & TARGET_SCB) != 0)
+ panic("Timed-out target SCB but bus idle");
+
+ if (last_phase != P_BUSFREE
+ && (ahc_inb(ahc, SSTAT0) & TARGET) != 0) {
+ /* XXX What happened to the SCB? */
+ /* Hung target selection. Goto busfree */
+ printf("%s: Hung target selection\n",
+ ahc_name(ahc));
+ restart_sequencer(ahc);
+ return;
+ }
+
+ if (ahc_search_qinfifo(ahc, target, channel, lun,
+ scb->hscb->tag, ROLE_INITIATOR,
+ /*status*/0, SEARCH_COUNT) > 0) {
+ disconnected = FALSE;
+ } else {
+ disconnected = TRUE;
+ }
+
+ if (disconnected) {
+ u_int active_scb;
+
+ 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;
+
+ /*
+ * Mark the cached copy of this SCB in the
+ * disconnected list too, so that a reconnect
+ * at this point causes a BDR or abort.
+ */
+ active_scb = ahc_inb(ahc, SCBPTR);
+ if (ahc_search_disc_list(ahc, target,
+ channel, lun,
+ scb->hscb->tag,
+ /*stop_on_first*/TRUE,
+ /*remove*/FALSE,
+ /*save_state*/FALSE)) {
+ u_int scb_control;
+
+ scb_control = ahc_inb(ahc, SCB_CONTROL);
+ scb_control |= MK_MESSAGE;
+ ahc_outb(ahc, SCB_CONTROL, scb_control);
+ }
+ ahc_outb(ahc, SCBPTR, active_scb);
+
+ /*
+ * 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_GET_TARGET(ahc, scb),
+ channel, SCB_GET_LUN(scb),
+ SCB_LIST_NULL,
+ ROLE_INITIATOR,
+ CAM_REQUEUE_REQ,
+ SEARCH_COMPLETE);
+ ahc_print_path(ahc, scb);
+ 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->io_ctx->ccb_h.timeout_ch =
+ timeout(ahc_timeout, (caddr_t)scb, 2 * hz);
+ unpause_sequencer(ahc);
+ } else {
+ /* Go "immediatly" to the bus reset */
+ /* This shouldn't happen */
+ ahc_set_recoveryscb(ahc, scb);
+ ahc_print_path(ahc, scb);
+ printf("SCB %d: Immediate reset. "
+ "Flags = 0x%x\n", scb->hscb->tag,
+ scb->flags);
+ goto bus_reset;
+ }
+ }
+ }
+ ahc_unlock(ahc, &s);
+}
+
+static void
+ahc_abort_ccb(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
+{
+ union ccb *abort_ccb;
+
+ abort_ccb = ccb->cab.abort_ccb;
+ switch (abort_ccb->ccb_h.func_code) {
+ case XPT_ACCEPT_TARGET_IO:
+ case XPT_IMMED_NOTIFY:
+ case XPT_CONT_TARGET_IO:
+ {
+ struct tmode_tstate *tstate;
+ struct tmode_lstate *lstate;
+ struct ccb_hdr_slist *list;
+ cam_status status;
+
+ status = ahc_find_tmode_devs(ahc, sim, abort_ccb, &tstate,
+ &lstate, TRUE);
+
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ break;
+ }
+
+ if (abort_ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO)
+ list = &lstate->accept_tios;
+ else if (abort_ccb->ccb_h.func_code == XPT_IMMED_NOTIFY)
+ list = &lstate->immed_notifies;
+ else
+ list = NULL;
+
+ if (list != NULL) {
+ struct ccb_hdr *curelm;
+ int found;
+
+ curelm = SLIST_FIRST(list);
+ found = 0;
+ if (curelm == &abort_ccb->ccb_h) {
+ found = 1;
+ SLIST_REMOVE_HEAD(list, sim_links.sle);
+ } else {
+ while(curelm != NULL) {
+ struct ccb_hdr *nextelm;
+
+ nextelm =
+ SLIST_NEXT(curelm, sim_links.sle);
+
+ if (nextelm == &abort_ccb->ccb_h) {
+ found = 1;
+ SLIST_NEXT(curelm,
+ sim_links.sle) =
+ SLIST_NEXT(nextelm,
+ sim_links.sle);
+ break;
+ }
+ curelm = nextelm;
+ }
+ }
+
+ if (found) {
+ abort_ccb->ccb_h.status = CAM_REQ_ABORTED;
+ xpt_done(abort_ccb);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ } else {
+ printf("Not found\n");
+ ccb->ccb_h.status = CAM_PATH_INVALID;
+ }
+ break;
+ }
+ /* FALLTHROUGH */
+ }
+ case XPT_SCSI_IO:
+ /* XXX Fully implement the hard ones */
+ ccb->ccb_h.status = CAM_UA_ABORT;
+ break;
+ default:
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ break;
+ }
+ xpt_done(ccb);
+}
+
+void
+ahc_send_async(struct ahc_softc *ahc, char channel, u_int target,
+ u_int lun, ac_code code)
+{
+ struct ccb_trans_settings cts;
+ struct cam_path *path;
+ void *arg;
+ int error;
+
+ arg = NULL;
+ error = ahc_create_path(ahc, channel, target, lun, &path);
+
+ if (error != CAM_REQ_CMP)
+ return;
+
+ switch (code) {
+ case AC_TRANSFER_NEG:
+#ifdef AHC_NEW_TRAN_SETTINGS
+ cts.type = CTS_TYPE_CURRENT_SETTINGS;
+#else
+ cts.flags = CCB_TRANS_CURRENT_SETTINGS;
+#endif
+ cts.ccb_h.path = path;
+ cts.ccb_h.target_id = target;
+ cts.ccb_h.target_lun = lun;
+ ahc_get_tran_settings(ahc, channel == 'A' ? ahc->our_id
+ : ahc->our_id_b,
+ channel, &cts);
+ arg = &cts;
+ break;
+ case AC_SENT_BDR:
+ case AC_BUS_RESET:
+ break;
+ default:
+ panic("ahc_send_async: Unexpected async event");
+ }
+ xpt_async(code, path, arg);
+}
+
+void
+ahc_platform_set_tags(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo, int enable)
+{
+}
+
+int
+ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg)
+{
+ ahc->platform_data =
+ malloc(sizeof(struct ahc_platform_data), M_DEVBUF, M_NOWAIT);
+ if (ahc->platform_data == NULL)
+ return (ENOMEM);
+ memset(ahc->platform_data, 0, sizeof(struct ahc_platform_data));
+ return (0);
+}
+
+void
+ahc_platform_free(struct ahc_softc *ahc)
+{
+ if (ahc->platform_data != NULL) {
+ if (ahc->platform_data->regs != NULL)
+ bus_release_resource(ahc->dev_softc,
+ ahc->platform_data->regs_res_type,
+ ahc->platform_data->regs_res_id,
+ ahc->platform_data->regs);
+
+ if (ahc->platform_data->irq != NULL)
+ bus_release_resource(ahc->dev_softc,
+ ahc->platform_data->irq_res_type,
+ 0, ahc->platform_data->irq);
+
+ free(ahc->platform_data, M_DEVBUF);
+ }
+}
+
+int
+ahc_softc_comp(struct ahc_softc *lahc, struct ahc_softc *rahc)
+{
+ /* We don't sort softcs under FreeBSD so report equal always */
+ return (0);
+}
+
+#if UNUSED
+static void
+ahc_dump_targcmd(struct target_cmd *cmd)
+{
+ uint8_t *byte;
+ uint8_t *last_byte;
+ int i;
+
+ byte = &cmd->initiator_channel;
+ /* Debugging info for received commands */
+ last_byte = &cmd[1].initiator_channel;
+
+ i = 0;
+ while (byte < last_byte) {
+ if (i == 0)
+ printf("\t");
+ printf("%#x", *byte++);
+ i++;
+ if (i == 8) {
+ printf("\n");
+ i = 0;
+ } else {
+ printf(", ");
+ }
+ }
+}
+#endif
diff --git a/sys/dev/aic7xxx/aic7xxx_freebsd.h b/sys/dev/aic7xxx/aic7xxx_freebsd.h
new file mode 100644
index 000000000000..4acb1285337f
--- /dev/null
+++ b/sys/dev/aic7xxx/aic7xxx_freebsd.h
@@ -0,0 +1,454 @@
+/*
+ * FreeBSD platform specific driver option settings, data structures,
+ * function declarations and includes.
+ *
+ * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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$
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AIC7XXX_FREEBSD_H_
+#define _AIC7XXX_FREEBSD_H_
+
+#include <opt_aic7xxx.h> /* for config options */
+#include <pci.h> /* for NPCI */
+
+#include <stddef.h> /* For offsetof */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h> /* For device_t */
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+
+#if NPCI > 0
+#define AHC_SUPPORT_PCI 1
+#ifdef AHC_ALLOW_MEMIO
+#include <machine/bus_memio.h>
+#endif
+#endif
+#include <machine/bus_pio.h>
+#include <machine/bus.h>
+#include <machine/clock.h>
+#include <machine/resource.h>
+
+#include <sys/rman.h>
+
+#if NPCI > 0
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+#endif
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_debug.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_message.h>
+
+/****************************** Platform Macros *******************************/
+#define SIM_IS_SCSIBUS_B(ahc, sim) \
+ ((sim) == ahc->platform_data->sim_b)
+#define SIM_CHANNEL(ahc, sim) \
+ (((sim) == ahc->platform_data->sim_b) ? 'B' : 'A')
+#define SIM_SCSI_ID(ahc, sim) \
+ (((sim) == ahc->platform_data->sim_b) ? ahc->our_id_b : ahc->our_id)
+#define SIM_PATH(ahc, sim) \
+ (((sim) == ahc->platform_data->sim_b) ? ahc->platform_data->path_b \
+ : ahc->platform_data->path)
+#define BUILD_SCSIID(ahc, sim, target_id, our_id) \
+ ((((target_id) << TID_SHIFT) & TID) | (our_id) \
+ | (SIM_IS_SCSIBUS_B(ahc, sim) ? TWIN_CHNLB : 0))
+
+#define SCB_GET_SIM(ahc, scb) \
+ (SCB_GET_CHANNEL(ahc, scb) == 'A' ? (ahc)->platform_data->sim \
+ : (ahc)->platform_data->sim_b)
+
+/************************* Forward Declarations *******************************/
+typedef device_t ahc_dev_softc_t;
+typedef union ccb *ahc_io_ctx_t;
+
+/***************************** Bus Space/DMA **********************************/
+#define ahc_dma_tag_create(ahc, parent_tag, alignment, boundary, \
+ lowaddr, highaddr, filter, filterarg, \
+ maxsize, nsegments, maxsegsz, flags, \
+ dma_tagp) \
+ bus_dma_tag_create(parent_tag, alignment, boundary, \
+ lowaddr, highaddr, filter, filterarg, \
+ maxsize, nsegments, maxsegsz, flags, \
+ dma_tagp)
+
+#define ahc_dma_tag_destroy(ahc, tag) \
+ bus_dma_tag_destroy(tag)
+
+#define ahc_dmamem_alloc(ahc, dmat, vaddr, flags, mapp) \
+ bus_dmamem_alloc(dmat, vaddr, flags, mapp)
+
+#define ahc_dmamem_free(ahc, dmat, vaddr, map) \
+ bus_dmamem_free(dmat, vaddr, map)
+
+#define ahc_dmamap_create(ahc, tag, flags, mapp) \
+ bus_dmamap_create(tag, flags, mapp)
+
+#define ahc_dmamap_destroy(ahc, tag, map) \
+ bus_dmamap_destroy(tag, map)
+
+#define ahc_dmamap_load(ahc, dmat, map, addr, buflen, callback, \
+ callback_arg, flags) \
+ bus_dmamap_load(dmat, map, addr, buflen, callback, callback_arg, flags)
+
+#define ahc_dmamap_unload(ahc, tag, map) \
+ bus_dmamap_unload(tag, map)
+
+#define ahc_dmamap_sync(ahc, dma_tag, dmamap, op) \
+ bus_dmamap_sync(dma_tag_dmamap, op)
+
+/************************ Tunable Driver Parameters **************************/
+/*
+ * The number of dma segments supported. The sequencer can handle any number
+ * of physically contiguous S/G entrys. To reduce the driver's memory
+ * consumption, we limit the number supported to be sufficient to handle
+ * the largest mapping supported by the kernel, MAXPHYS. Assuming the
+ * transfer is as fragmented as possible and unaligned, this turns out to
+ * be the number of paged sized transfers in MAXPHYS plus an extra element
+ * to handle any unaligned residual. The sequencer fetches SG elements
+ * in 128 byte chucks, so make the number per-transaction a nice multiple
+ * of 16 (8 byte S/G elements).
+ */
+/* XXX Worth the space??? */
+#define AHC_NSEG (roundup(btoc(MAXPHYS) + 1, 16))
+
+/* This driver supports target mode */
+#define AHC_TARGET_MODE 1
+
+/************************** Softc/SCB Platform Data ***************************/
+struct ahc_platform_data {
+ /*
+ * Hooks into the XPT.
+ */
+ struct cam_sim *sim;
+ struct cam_sim *sim_b;
+ struct cam_path *path;
+ struct cam_path *path_b;
+
+ int regs_res_type;
+ int regs_res_id;
+ int irq_res_type;
+ struct resource *regs;
+ struct resource *irq;
+ void *ih;
+};
+
+struct scb_platform_data {
+};
+
+/***************************** Core Includes **********************************/
+#include <dev/aic7xxx/aic7xxx.h>
+
+/*************************** Device Access ************************************/
+#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)
+
+#define ahc_insb(ahc, port, valp, count) \
+ bus_space_read_multi_1((ahc)->tag, (ahc)->bsh, port, valp, count)
+
+static __inline void ahc_flush_device_writes(struct ahc_softc *);
+
+static __inline void
+ahc_flush_device_writes(struct ahc_softc *ahc)
+{
+ /* XXX Is this sufficient for all architectures??? */
+ ahc_inb(ahc, INTSTAT);
+}
+
+/**************************** Locking Primitives ******************************/
+/* Lock protecting internal data structures */
+static __inline void ahc_lockinit(struct ahc_softc *);
+static __inline void ahc_lock(struct ahc_softc *, unsigned long *flags);
+static __inline void ahc_unlock(struct ahc_softc *, unsigned long *flags);
+
+/* Lock held during command compeletion to the upper layer */
+static __inline void ahc_done_lockinit(struct ahc_softc *);
+static __inline void ahc_done_lock(struct ahc_softc *, unsigned long *flags);
+static __inline void ahc_done_unlock(struct ahc_softc *, unsigned long *flags);
+
+static __inline void
+ahc_lockinit(struct ahc_softc *ahc)
+{
+}
+
+static __inline void
+ahc_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+ *flags = splcam();
+}
+
+static __inline void
+ahc_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+ splx(*flags);
+}
+
+/* Lock held during command compeletion to the upper layer */
+static __inline void
+ahc_done_lockinit(struct ahc_softc *ahc)
+{
+}
+
+static __inline void
+ahc_done_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+}
+
+static __inline void
+ahc_done_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+}
+
+/****************************** OS Primitives *********************************/
+#define ahc_delay DELAY
+
+/************************** Transaction Operations ****************************/
+static __inline void ahc_set_transaction_status(struct scb *, uint32_t);
+static __inline void ahc_set_scsi_status(struct scb *, uint32_t);
+static __inline uint32_t ahc_get_transaction_status(struct scb *);
+static __inline uint32_t ahc_get_scsi_status(struct scb *);
+static __inline void ahc_set_transaction_tag(struct scb *, int, u_int);
+static __inline u_long ahc_get_transfer_length(struct scb *);
+static __inline int ahc_get_transfer_dir(struct scb *);
+static __inline void ahc_set_residual(struct scb *, u_long);
+static __inline void ahc_set_sense_residual(struct scb *, u_long);
+static __inline u_long ahc_get_residual(struct scb *);
+static __inline int ahc_perform_autosense(struct scb *);
+static __inline uint32_t ahc_get_sense_bufsize(struct ahc_softc*, struct scb*);
+static __inline void ahc_freeze_ccb(union ccb *ccb);
+static __inline void ahc_freeze_scb(struct scb *scb);
+static __inline void ahc_platform_freeze_devq(struct ahc_softc *, struct scb *);
+static __inline int ahc_platform_abort_scbs(struct ahc_softc *ahc, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status);
+
+static __inline
+void ahc_set_transaction_status(struct scb *scb, uint32_t status)
+{
+ scb->io_ctx->ccb_h.status &= ~CAM_STATUS_MASK;
+ scb->io_ctx->ccb_h.status |= status;
+}
+
+static __inline
+void ahc_set_scsi_status(struct scb *scb, uint32_t status)
+{
+ scb->io_ctx->csio.scsi_status = status;
+}
+
+static __inline
+uint32_t ahc_get_transaction_status(struct scb *scb)
+{
+ return (scb->io_ctx->ccb_h.status & CAM_STATUS_MASK);
+}
+
+static __inline
+uint32_t ahc_get_scsi_status(struct scb *scb)
+{
+ return (scb->io_ctx->csio.scsi_status);
+}
+
+static __inline
+void ahc_set_transaction_tag(struct scb *scb, int enabled, u_int type)
+{
+ scb->io_ctx->csio.tag_action = type;
+ if (enabled)
+ scb->io_ctx->ccb_h.flags |= CAM_TAG_ACTION_VALID;
+ else
+ scb->io_ctx->ccb_h.flags &= ~CAM_TAG_ACTION_VALID;
+}
+
+static __inline
+u_long ahc_get_transfer_length(struct scb *scb)
+{
+ return (scb->io_ctx->csio.dxfer_len);
+}
+
+static __inline
+int ahc_get_transfer_dir(struct scb *scb)
+{
+ return (scb->io_ctx->ccb_h.flags & CAM_DIR_MASK);
+}
+
+static __inline
+void ahc_set_residual(struct scb *scb, u_long resid)
+{
+ scb->io_ctx->csio.resid = resid;
+}
+
+static __inline
+void ahc_set_sense_residual(struct scb *scb, u_long resid)
+{
+ scb->io_ctx->csio.sense_resid = resid;
+}
+
+static __inline
+u_long ahc_get_residual(struct scb *scb)
+{
+ return (scb->io_ctx->csio.resid);
+}
+
+static __inline
+int ahc_perform_autosense(struct scb *scb)
+{
+ return (!(scb->io_ctx->ccb_h.flags & CAM_DIS_AUTOSENSE));
+}
+
+static __inline uint32_t
+ahc_get_sense_bufsize(struct ahc_softc *ahc, struct scb *scb)
+{
+ return (sizeof(struct scsi_sense_data));
+}
+
+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 void
+ahc_freeze_scb(struct scb *scb)
+{
+ ahc_freeze_ccb(scb->io_ctx);
+}
+
+static __inline void
+ahc_platform_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
+{
+ /* Nothing to do here for FreeBSD */
+}
+
+static __inline int
+ahc_platform_abort_scbs(struct ahc_softc *ahc, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status)
+{
+ /* Nothing to do here for FreeBSD */
+ return (0);
+}
+
+/********************************** PCI ***************************************/
+#ifdef AHC_SUPPORT_PCI
+static __inline uint32_t ahc_pci_read_config(ahc_dev_softc_t pci,
+ int reg, int width);
+static __inline void ahc_pci_write_config(ahc_dev_softc_t pci,
+ int reg, uint32_t value,
+ int width);
+static __inline int ahc_get_pci_function(ahc_dev_softc_t);
+static __inline int ahc_get_pci_slot(ahc_dev_softc_t);
+static __inline int ahc_get_pci_bus(ahc_dev_softc_t);
+
+int ahc_pci_map_registers(struct ahc_softc *ahc);
+int ahc_pci_map_int(struct ahc_softc *ahc);
+
+static __inline uint32_t
+ahc_pci_read_config(ahc_dev_softc_t pci, int reg, int width)
+{
+ return (pci_read_config(pci, reg, width));
+}
+
+static __inline void
+ahc_pci_write_config(ahc_dev_softc_t pci, int reg, uint32_t value, int width)
+{
+ pci_write_config(pci, reg, value, width);
+}
+
+static __inline int
+ahc_get_pci_function(ahc_dev_softc_t pci)
+{
+ return (pci_get_function(pci));
+}
+
+static __inline int
+ahc_get_pci_slot(ahc_dev_softc_t pci)
+{
+ return (pci_get_slot(pci));
+}
+
+static __inline int
+ahc_get_pci_bus(ahc_dev_softc_t pci)
+{
+ return (pci_get_bus(pci));
+}
+#endif
+/******************************** VL/EISA *************************************/
+int aic7770_map_registers(struct ahc_softc *ahc);
+int aic7770_map_int(struct ahc_softc *ahc);
+
+/********************************* Debug **************************************/
+static __inline void ahc_print_path(struct ahc_softc *, struct scb *);
+static __inline void ahc_platform_dump_card_state(struct ahc_softc *ahc);
+
+static __inline void
+ahc_print_path(struct ahc_softc *ahc, struct scb *scb)
+{
+ xpt_print_path(scb->io_ctx->ccb_h.path);
+}
+
+static __inline void
+ahc_platform_dump_card_state(struct ahc_softc *ahc)
+{
+ /* Nothing to do here for FreeBSD */
+}
+/**************************** Transfer Settings *******************************/
+void ahc_notify_xfer_settings_change(struct ahc_softc *,
+ struct ahc_devinfo *);
+void ahc_platform_set_tags(struct ahc_softc *, struct ahc_devinfo *,
+ int /*enable*/);
+
+/***************************** Initialization *********************************/
+int ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg);
+void ahc_platform_free(struct ahc_softc *ahc);
+int ahc_attach(struct ahc_softc *);
+int ahc_softc_comp(struct ahc_softc *lahc, struct ahc_softc *rahc);
+
+/************************ Misc Function Declarations **************************/
+timeout_t ahc_timeout;
+void ahc_done(struct ahc_softc *ahc, struct scb *scb);
+void ahc_send_async(struct ahc_softc *, char /*channel*/,
+ u_int /*target*/, u_int /*lun*/, ac_code);
+#endif /* _AIC7XXX_FREEBSD_H_ */
diff --git a/sys/dev/aic7xxx/aic7xxx_inline.h b/sys/dev/aic7xxx/aic7xxx_inline.h
new file mode 100644
index 000000000000..7bf63dee9d33
--- /dev/null
+++ b/sys/dev/aic7xxx/aic7xxx_inline.h
@@ -0,0 +1,366 @@
+/*
+ * Inline routines shareable across OS platforms.
+ *
+ * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aic7xxx_inline.h#3 $
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _AIC7XXX_INLINE_H_
+#define _AIC7XXX_INLINE_H_
+
+/************************* Sequencer Execution Control ************************/
+static __inline int sequencer_paused(struct ahc_softc *ahc);
+static __inline void ahc_pause_bug_fix(struct ahc_softc *ahc);
+static __inline void pause_sequencer(struct ahc_softc *ahc);
+static __inline void unpause_sequencer(struct ahc_softc *ahc);
+
+/*
+ * Work around any chip bugs related to halting sequencer execution.
+ * On Ultra2 controllers, we must clear the CIOBUS stretch signal by
+ * reading a register that will set this signal and deassert it.
+ * Without this workaround, if the chip is paused, by an interrupt or
+ * manual pause while accessing scb ram, accesses to certain registers
+ * will hang the system (infinite pci retries).
+ */
+static __inline void
+ahc_pause_bug_fix(struct ahc_softc *ahc)
+{
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ (void)ahc_inb(ahc, CCSCBCTL);
+}
+
+/*
+ * Determine whether the sequencer has halted code execution.
+ * Returns non-zero status if the sequencer is stopped.
+ */
+static __inline int
+sequencer_paused(struct ahc_softc *ahc)
+{
+ return ((ahc_inb(ahc, HCNTRL) & PAUSE) != 0);
+}
+
+/*
+ * Request that the sequencer stop and wait, indefinitely, for it
+ * to stop. The sequencer will only acknowledge that it is paused
+ * once it has reached an instruction boundary and PAUSEDIS is
+ * cleared in the SEQCTL register. The sequencer may use PAUSEDIS
+ * for critical sections.
+ */
+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 (sequencer_paused(ahc) == 0)
+ ;
+
+ ahc_pause_bug_fix(ahc);
+}
+
+/*
+ * Allow the sequencer to continue program execution.
+ * We check here to ensure that no additional interrupt
+ * sources that would cause the sequencer to halt have been
+ * asserted. If, for example, a SCSI bus reset is detected
+ * while we are fielding a different, pausing, interrupt type,
+ * we don't want to release the sequencer before going back
+ * into our interrupt handler and dealing with this new
+ * condition.
+ */
+static __inline void
+unpause_sequencer(struct ahc_softc *ahc)
+{
+ if ((ahc_inb(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0)
+ ahc_outb(ahc, HCNTRL, ahc->unpause);
+}
+
+/*********************** Untagged Transaction Routines ************************/
+u_int ahc_index_busy_tcl(struct ahc_softc *ahc,
+ u_int tcl, int unbusy);
+static __inline void ahc_freeze_untagged_queues(struct ahc_softc *ahc);
+static __inline void ahc_release_untagged_queues(struct ahc_softc *ahc);
+
+/*
+ * Block our completion routine from starting the next untagged
+ * transaction for this target or target lun.
+ */
+static __inline void
+ahc_freeze_untagged_queues(struct ahc_softc *ahc)
+{
+ if ((ahc->features & AHC_SCB_BTT) == 0)
+ ahc->untagged_queue_lock++;
+}
+
+/*
+ * Allow the next untagged transaction for this target or target lun
+ * to be executed. We use a counting semaphore to allow the lock
+ * to be acquired recursively. Once the count drops to zero, the
+ * transaction queues will be run.
+ */
+static __inline void
+ahc_release_untagged_queues(struct ahc_softc *ahc)
+{
+ if ((ahc->features & AHC_SCB_BTT) == 0) {
+ ahc->untagged_queue_lock--;
+ if (ahc->untagged_queue_lock == 0)
+ ahc_run_untagged_queues(ahc);
+ }
+}
+
+/************************** Memory mapping routines ***************************/
+static __inline struct ahc_dma_seg *
+ ahc_sg_bus_to_virt(struct scb *scb,
+ uint32_t sg_busaddr);
+static __inline uint32_t
+ ahc_sg_virt_to_bus(struct scb *scb,
+ struct ahc_dma_seg *sg);
+static __inline uint32_t
+ ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index);
+
+static __inline struct ahc_dma_seg *
+ahc_sg_bus_to_virt(struct scb *scb, uint32_t sg_busaddr)
+{
+ int sg_index;
+
+ sg_index = (sg_busaddr - scb->sg_list_phys)/sizeof(struct ahc_dma_seg);
+ /* sg_list_phys points to entry 1, not 0 */
+ sg_index++;
+
+ return (&scb->sg_list[sg_index]);
+}
+
+static __inline uint32_t
+ahc_sg_virt_to_bus(struct scb *scb, struct ahc_dma_seg *sg)
+{
+ int sg_index;
+
+ /* sg_list_phys points to entry 1, not 0 */
+ sg_index = sg - &scb->sg_list[1];
+
+ return (scb->sg_list_phys + (sg_index * sizeof(*scb->sg_list)));
+}
+
+static __inline uint32_t
+ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index)
+{
+ return (ahc->scb_data->hscb_busaddr
+ + (sizeof(struct hardware_scb) * index));
+}
+
+/******************************** Debugging ***********************************/
+static __inline char *ahc_name(struct ahc_softc *ahc);
+
+static __inline char *
+ahc_name(struct ahc_softc *ahc)
+{
+ return (ahc->name);
+}
+
+/*********************** Miscelaneous Support Functions ***********************/
+
+static __inline int ahc_check_residual(struct scb *scb);
+static __inline struct ahc_initiator_tinfo *
+ ahc_fetch_transinfo(struct ahc_softc *ahc,
+ char channel, u_int our_id,
+ u_int remote_id,
+ struct tmode_tstate **tstate);
+static __inline struct scb*
+ ahc_get_scb(struct ahc_softc *ahc);
+static __inline void ahc_free_scb(struct ahc_softc *ahc, struct scb *scb);
+static __inline void ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb);
+
+/*
+ * Determine whether the sequencer reported a residual
+ * for this SCB/transaction.
+ */
+static __inline int
+ahc_check_residual(struct scb *scb)
+{
+ struct status_pkt *sp;
+
+ sp = &scb->hscb->shared_data.status;
+ if ((scb->hscb->sgptr & SG_RESID_VALID) != 0)
+ return (1);
+ return (0);
+}
+
+/*
+ * Return pointers to the transfer negotiation information
+ * for the specified our_id/remote_id pair.
+ */
+static __inline struct ahc_initiator_tinfo *
+ahc_fetch_transinfo(struct ahc_softc *ahc, char channel, u_int our_id,
+ u_int remote_id, struct tmode_tstate **tstate)
+{
+ /*
+ * Transfer data structures are stored from the perspective
+ * of the target role. Since the parameters for a connection
+ * in the initiator role to a given target are the same as
+ * when the roles are reversed, we pretend we are the target.
+ */
+ if (channel == 'B')
+ our_id += 8;
+ *tstate = ahc->enabled_targets[our_id];
+ return (&(*tstate)->transinfo[remote_id]);
+}
+
+/*
+ * Get a free scb. If there are none, see if we can allocate a new SCB.
+ */
+static __inline struct scb *
+ahc_get_scb(struct ahc_softc *ahc)
+{
+ struct scb *scbp;
+
+ if ((scbp = SLIST_FIRST(&ahc->scb_data->free_scbs))) {
+ SLIST_REMOVE_HEAD(&ahc->scb_data->free_scbs, links.sle);
+ } else {
+ ahc_alloc_scbs(ahc);
+ scbp = SLIST_FIRST(&ahc->scb_data->free_scbs);
+ if (scbp != NULL)
+ SLIST_REMOVE_HEAD(&ahc->scb_data->free_scbs, links.sle);
+ }
+
+ return (scbp);
+}
+
+/*
+ * Return an SCB resource to the free list.
+ */
+static __inline void
+ahc_free_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+ struct hardware_scb *hscb;
+
+ hscb = scb->hscb;
+#if 0
+ /* What do we do to generically handle driver resource shortages??? */
+ 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;
+ }
+#endif
+ /* Clean up for the next user */
+ scb->flags = SCB_FREE;
+ hscb->control = 0;
+
+ SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs, scb, links.sle);
+}
+
+/*
+ * Tell the sequencer about a new transaction to execute.
+ */
+static __inline void
+ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+ ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+ } else {
+ if ((ahc->features & AHC_AUTOPAUSE) == 0)
+ pause_sequencer(ahc);
+ ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+ if ((ahc->features & AHC_AUTOPAUSE) == 0)
+ unpause_sequencer(ahc);
+ }
+}
+
+/************************** Interrupt Processing ******************************/
+static __inline void ahc_intr(struct ahc_softc *ahc);
+
+/*
+ * Catch an interrupt from the adapter
+ */
+static __inline void
+ahc_intr(struct ahc_softc *ahc)
+{
+ u_int intstat;
+
+ intstat = ahc_inb(ahc, INTSTAT);
+
+ /*
+ * Any interrupts to process?
+ */
+#if AHC_PCI_CONFIG > 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) {
+ ahc_outb(ahc, CLRINT, CLRCMDINT);
+ /*
+ * Ensure that the chip sees that we've cleared
+ * this interrupt before we walk the output fifo.
+ * Otherwise, we may, due to posted bus writes,
+ * clear the interrupt after we finish the scan,
+ * and after the sequencer has added new entries
+ * and asserted the interrupt again.
+ */
+ ahc_flush_device_writes(ahc);
+ ahc_run_qoutfifo(ahc);
+#ifdef AHC_TARGET_MODE
+ if ((ahc->flags & AHC_TARGETMODE) != 0)
+ ahc_run_tqinfifo(ahc, /*paused*/FALSE);
+#endif
+ }
+ if (intstat & BRKADRINT)
+ ahc_handle_brkadrint(ahc);
+
+ if ((intstat & (SEQINT|SCSIINT)) != 0)
+ ahc_pause_bug_fix(ahc);
+
+ if ((intstat & SEQINT) != 0)
+ ahc_handle_seqint(ahc, intstat);
+
+ if ((intstat & SCSIINT) != 0)
+ ahc_handle_scsiint(ahc, intstat);
+}
+
+#endif /* _AIC7XXX_INLINE_H_ */
diff --git a/sys/dev/aic7xxx/aic7xxx_pci.c b/sys/dev/aic7xxx/aic7xxx_pci.c
new file mode 100644
index 000000000000..00a5213ed629
--- /dev/null
+++ b/sys/dev/aic7xxx/aic7xxx_pci.c
@@ -0,0 +1,1927 @@
+/*
+ * Product specific probe and attach routines for:
+ * 3940, 2940, aic7895, aic7890, aic7880,
+ * aic7870, aic7860 and aic7850 SCSI controllers
+ *
+ * Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000 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.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aic7xxx_pci.c#4 $
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __linux__
+#include "aic7xxx_linux.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+#endif
+
+#ifdef __FreeBSD__
+#include <dev/aic7xxx/aic7xxx_freebsd.h>
+#include <dev/aic7xxx/aic7xxx_inline.h>
+#include <dev/aic7xxx/aic7xxx_93cx6.h>
+#endif
+
+#define AHC_PCI_IOADDR PCIR_MAPS /* I/O Address */
+#define AHC_PCI_MEMADDR (PCIR_MAPS + 4) /* Mem I/O Address */
+
+static __inline uint64_t
+ahc_compose_id(u_int device, u_int vendor, u_int subdevice, u_int subvendor)
+{
+ uint64_t id;
+
+ id = subvendor
+ | (subdevice << 16)
+ | ((uint64_t)vendor << 32)
+ | ((uint64_t)device << 48);
+
+ return (id);
+}
+
+#define ID_ALL_MASK 0xFFFFFFFFFFFFFFFFull
+#define ID_DEV_VENDOR_MASK 0xFFFFFFFF00000000ull
+#define ID_AIC7850 0x5078900400000000ull
+#define ID_AHA_2910_15_20_30C 0x5078900478509004ull
+#define ID_AIC7855 0x5578900400000000ull
+#define ID_AIC7859 0x3860900400000000ull
+#define ID_AHA_2930CU 0x3860900438699004ull
+#define ID_AIC7860 0x6078900400000000ull
+#define ID_AIC7860C 0x6078900478609004ull
+#define ID_AHA_1480A 0x6075900400000000ull
+#define ID_AHA_2940AU_0 0x6178900400000000ull
+#define ID_AHA_2940AU_1 0x6178900478619004ull
+#define ID_AHA_2940AU_CN 0x2178900478219004ull
+#define ID_AHA_2930C_VAR 0x6038900438689004ull
+
+#define ID_AIC7870 0x7078900400000000ull
+#define ID_AHA_2940 0x7178900400000000ull
+#define ID_AHA_3940 0x7278900400000000ull
+#define ID_AHA_398X 0x7378900400000000ull
+#define ID_AHA_2944 0x7478900400000000ull
+#define ID_AHA_3944 0x7578900400000000ull
+#define ID_AHA_4944 0x7678900400000000ull
+
+#define ID_AIC7880 0x8078900400000000ull
+#define ID_AIC7880_B 0x8078900478809004ull
+#define ID_AHA_2940U 0x8178900400000000ull
+#define ID_AHA_3940U 0x8278900400000000ull
+#define ID_AHA_2944U 0x8478900400000000ull
+#define ID_AHA_3944U 0x8578900400000000ull
+#define ID_AHA_398XU 0x8378900400000000ull
+#define ID_AHA_4944U 0x8678900400000000ull
+#define ID_AHA_2940UB 0x8178900478819004ull
+#define ID_AHA_2930U 0x8878900478889004ull
+#define ID_AHA_2940U_PRO 0x8778900478879004ull
+#define ID_AHA_2940U_CN 0x0078900478009004ull
+
+#define ID_AIC7895 0x7895900478959004ull
+#define ID_AIC7895_RAID_PORT 0x7893900478939004ull
+#define ID_AHA_2940U_DUAL 0x7895900478919004ull
+#define ID_AHA_3940AU 0x7895900478929004ull
+#define ID_AHA_3944AU 0x7895900478949004ull
+
+#define ID_AIC7890 0x001F9005000F9005ull
+#define ID_AAA_131U2 0x0013900500039005ull
+#define ID_AHA_2930U2 0x0011900501819005ull
+#define ID_AHA_2940U2B 0x00109005A1009005ull
+#define ID_AHA_2940U2_OEM 0x0010900521809005ull
+#define ID_AHA_2940U2 0x00109005A1809005ull
+#define ID_AHA_2950U2B 0x00109005E1009005ull
+
+#define ID_AIC7892 0x008F9005FFFF9005ull
+#define ID_AHA_29160 0x00809005E2A09005ull
+#define ID_AHA_29160_CPQ 0x00809005E2A00E11ull
+#define ID_AHA_29160N 0x0080900562A09005ull
+#define ID_AHA_29160B 0x00809005E2209005ull
+#define ID_AHA_19160B 0x0081900562A19005ull
+
+#define ID_AIC7896 0x005F9005FFFF9005ull
+#define ID_AHA_3950U2B_0 0x00509005FFFF9005ull
+#define ID_AHA_3950U2B_1 0x00509005F5009005ull
+#define ID_AHA_3950U2D_0 0x00519005FFFF9005ull
+#define ID_AHA_3950U2D_1 0x00519005B5009005ull
+
+#define ID_AIC7899 0x00CF9005FFFF9005ull
+#define ID_AHA_3960D 0x00C09005F6209005ull /* AKA AHA-39160 */
+#define ID_AHA_3960D_CPQ 0x00C09005F6200E11ull
+
+#define ID_AIC7810 0x1078900400000000ull
+#define ID_AIC7815 0x7815900400000000ull
+
+static ahc_device_setup_t ahc_aic7850_setup;
+static ahc_device_setup_t ahc_aic7855_setup;
+static ahc_device_setup_t ahc_aic7860_setup;
+static ahc_device_setup_t ahc_aic7870_setup;
+static ahc_device_setup_t ahc_aha394X_setup;
+static ahc_device_setup_t ahc_aha494X_setup;
+static ahc_device_setup_t ahc_aha398X_setup;
+static ahc_device_setup_t ahc_aic7880_setup;
+static ahc_device_setup_t ahc_2940Pro_setup;
+static ahc_device_setup_t ahc_aha394XU_setup;
+static ahc_device_setup_t ahc_aha398XU_setup;
+static ahc_device_setup_t ahc_aic7890_setup;
+static ahc_device_setup_t ahc_aic7892_setup;
+static ahc_device_setup_t ahc_aic7895_setup;
+static ahc_device_setup_t ahc_aic7896_setup;
+static ahc_device_setup_t ahc_aic7899_setup;
+static ahc_device_setup_t ahc_raid_setup;
+static ahc_device_setup_t ahc_aha394XX_setup;
+static ahc_device_setup_t ahc_aha494XX_setup;
+static ahc_device_setup_t ahc_aha398XX_setup;
+
+struct ahc_pci_identity ahc_pci_ident_table [] =
+{
+ /* aic7850 based controllers */
+ {
+ ID_AHA_2910_15_20_30C,
+ ID_ALL_MASK,
+ "Adaptec 2910/15/20/30C SCSI adapter",
+ ahc_aic7850_setup
+ },
+ /* aic7860 based controllers */
+ {
+ ID_AHA_2930CU,
+ ID_ALL_MASK,
+ "Adaptec 2930CU SCSI adapter",
+ ahc_aic7860_setup
+ },
+ {
+ ID_AHA_1480A & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 1480A Ultra SCSI adapter",
+ ahc_aic7860_setup
+ },
+ {
+ ID_AHA_2940AU_0 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2940A Ultra SCSI adapter",
+ ahc_aic7860_setup
+ },
+ {
+ ID_AHA_2940AU_CN & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2940A/CN Ultra SCSI adapter",
+ ahc_aic7860_setup
+ },
+ {
+ ID_AHA_2930C_VAR & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2930C SCSI adapter (VAR)",
+ ahc_aic7860_setup
+ },
+ /* aic7870 based controllers */
+ {
+ ID_AHA_2940,
+ ID_ALL_MASK,
+ "Adaptec 2940 SCSI adapter",
+ ahc_aic7870_setup
+ },
+ {
+ ID_AHA_3940,
+ ID_ALL_MASK,
+ "Adaptec 3940 SCSI adapter",
+ ahc_aha394X_setup
+ },
+ {
+ ID_AHA_398X,
+ ID_ALL_MASK,
+ "Adaptec 398X SCSI RAID adapter",
+ ahc_aha398X_setup
+ },
+ {
+ ID_AHA_2944,
+ ID_ALL_MASK,
+ "Adaptec 2944 SCSI adapter",
+ ahc_aic7870_setup
+ },
+ {
+ ID_AHA_3944,
+ ID_ALL_MASK,
+ "Adaptec 3944 SCSI adapter",
+ ahc_aha394X_setup
+ },
+ {
+ ID_AHA_4944,
+ ID_ALL_MASK,
+ "Adaptec 4944 SCSI adapter",
+ ahc_aha494X_setup
+ },
+ /* aic7880 based controllers */
+ {
+ ID_AHA_2940U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2940 Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ {
+ ID_AHA_3940U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 3940 Ultra SCSI adapter",
+ ahc_aha394XU_setup
+ },
+ {
+ ID_AHA_2944U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2944 Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ {
+ ID_AHA_3944U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 3944 Ultra SCSI adapter",
+ ahc_aha394XU_setup
+ },
+ {
+ ID_AHA_398XU & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 398X Ultra SCSI RAID adapter",
+ ahc_aha398XU_setup
+ },
+ {
+ /*
+ * XXX Don't know the slot numbers
+ * so we can't identify channels
+ */
+ ID_AHA_4944U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 4944 Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ {
+ ID_AHA_2930U & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2930 Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ {
+ ID_AHA_2940U_PRO & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2940 Pro Ultra SCSI adapter",
+ ahc_2940Pro_setup
+ },
+ {
+ ID_AHA_2940U_CN & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec 2940/CN Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ /* aic7890 based controllers */
+ {
+ ID_AHA_2930U2,
+ ID_ALL_MASK,
+ "Adaptec 2930 Ultra2 SCSI adapter",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AHA_2940U2B,
+ ID_ALL_MASK,
+ "Adaptec 2940B Ultra2 SCSI adapter",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AHA_2940U2_OEM,
+ ID_ALL_MASK,
+ "Adaptec 2940 Ultra2 SCSI adapter (OEM)",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AHA_2940U2,
+ ID_ALL_MASK,
+ "Adaptec 2940 Ultra2 SCSI adapter",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AHA_2950U2B,
+ ID_ALL_MASK,
+ "Adaptec 2950 Ultra2 SCSI adapter",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AAA_131U2,
+ ID_ALL_MASK,
+ "Adaptec AAA-131 Ultra2 RAID adapter",
+ ahc_aic7890_setup
+ },
+ /* aic7892 based controllers */
+ {
+ ID_AHA_29160,
+ ID_ALL_MASK,
+ "Adaptec 29160 Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ {
+ ID_AHA_29160_CPQ,
+ ID_ALL_MASK,
+ "Adaptec (Compaq OEM) 29160 Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ {
+ ID_AHA_29160N,
+ ID_ALL_MASK,
+ "Adaptec 29160N Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ {
+ ID_AHA_29160B,
+ ID_ALL_MASK,
+ "Adaptec 29160B Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ {
+ ID_AHA_19160B,
+ ID_ALL_MASK,
+ "Adaptec 19160B Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ /* aic7895 based controllers */
+ {
+ ID_AHA_2940U_DUAL,
+ ID_ALL_MASK,
+ "Adaptec 2940/DUAL Ultra SCSI adapter",
+ ahc_aic7895_setup
+ },
+ {
+ ID_AHA_3940AU,
+ ID_ALL_MASK,
+ "Adaptec 3940A Ultra SCSI adapter",
+ ahc_aic7895_setup
+ },
+ {
+ ID_AHA_3944AU,
+ ID_ALL_MASK,
+ "Adaptec 3944A Ultra SCSI adapter",
+ ahc_aic7895_setup
+ },
+ /* aic7896/97 based controllers */
+ {
+ ID_AHA_3950U2B_0,
+ ID_ALL_MASK,
+ "Adaptec 3950B Ultra2 SCSI adapter",
+ ahc_aic7896_setup
+ },
+ {
+ ID_AHA_3950U2B_1,
+ ID_ALL_MASK,
+ "Adaptec 3950B Ultra2 SCSI adapter",
+ ahc_aic7896_setup
+ },
+ {
+ ID_AHA_3950U2D_0,
+ ID_ALL_MASK,
+ "Adaptec 3950D Ultra2 SCSI adapter",
+ ahc_aic7896_setup
+ },
+ {
+ ID_AHA_3950U2D_1,
+ ID_ALL_MASK,
+ "Adaptec 3950D Ultra2 SCSI adapter",
+ ahc_aic7896_setup
+ },
+ /* aic7899 based controllers */
+ {
+ ID_AHA_3960D,
+ ID_ALL_MASK,
+ "Adaptec 3960D Ultra160 SCSI adapter",
+ ahc_aic7899_setup
+ },
+ {
+ ID_AHA_3960D_CPQ,
+ ID_ALL_MASK,
+ "Adaptec (Compaq OEM) 3960D Ultra160 SCSI adapter",
+ ahc_aic7899_setup
+ },
+ /* Generic chip probes for devices we don't know 'exactly' */
+ {
+ ID_AIC7850 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7850 SCSI adapter",
+ ahc_aic7850_setup
+ },
+ {
+ ID_AIC7855 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7855 SCSI adapter",
+ ahc_aic7855_setup
+ },
+ {
+ ID_AIC7859 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7859 Ultra SCSI adapter",
+ ahc_aic7860_setup
+ },
+ {
+ ID_AIC7860 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7860 SCSI adapter",
+ ahc_aic7860_setup
+ },
+ {
+ ID_AIC7870 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7870 SCSI adapter",
+ ahc_aic7870_setup
+ },
+ {
+ ID_AIC7880 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7880 Ultra SCSI adapter",
+ ahc_aic7880_setup
+ },
+ {
+ ID_AIC7890 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7890/91 Ultra2 SCSI adapter",
+ ahc_aic7890_setup
+ },
+ {
+ ID_AIC7892 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7892 Ultra160 SCSI adapter",
+ ahc_aic7892_setup
+ },
+ {
+ ID_AIC7895 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7895 Ultra SCSI adapter",
+ ahc_aic7895_setup
+ },
+ {
+ ID_AIC7895_RAID_PORT & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7895 Ultra SCSI adapter (RAID PORT)",
+ ahc_aic7895_setup
+ },
+ {
+ ID_AIC7896 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7896/97 Ultra2 SCSI adapter",
+ ahc_aic7896_setup
+ },
+ {
+ ID_AIC7899 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7899 Ultra160 SCSI adapter",
+ ahc_aic7899_setup
+ },
+ {
+ ID_AIC7810 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7810 RAID memory controller",
+ ahc_raid_setup
+ },
+ {
+ ID_AIC7815 & ID_DEV_VENDOR_MASK,
+ ID_DEV_VENDOR_MASK,
+ "Adaptec aic7815 RAID memory controller",
+ ahc_raid_setup
+ }
+};
+
+const int ahc_num_pci_devs = NUM_ELEMENTS(ahc_pci_ident_table);
+
+#define AHC_394X_SLOT_CHANNEL_A 4
+#define AHC_394X_SLOT_CHANNEL_B 5
+
+#define AHC_398X_SLOT_CHANNEL_A 4
+#define AHC_398X_SLOT_CHANNEL_B 8
+#define AHC_398X_SLOT_CHANNEL_C 12
+
+#define AHC_494X_SLOT_CHANNEL_A 4
+#define AHC_494X_SLOT_CHANNEL_B 5
+#define AHC_494X_SLOT_CHANNEL_C 6
+#define AHC_494X_SLOT_CHANNEL_D 7
+
+#define DEVCONFIG 0x40
+#define SCBSIZE32 0x00010000ul /* aic789X only */
+#define MPORTMODE 0x00000400ul /* aic7870 only */
+#define RAMPSM 0x00000200ul /* aic7870 only */
+#define VOLSENSE 0x00000100ul
+#define SCBRAMSEL 0x00000080ul
+#define MRDCEN 0x00000040ul
+#define EXTSCBTIME 0x00000020ul /* aic7870 only */
+#define EXTSCBPEN 0x00000010ul /* aic7870 only */
+#define BERREN 0x00000008ul
+#define DACEN 0x00000004ul
+#define STPWLEVEL 0x00000002ul
+#define DIFACTNEGEN 0x00000001ul /* aic7870 only */
+
+#define CSIZE_LATTIME 0x0c
+#define CACHESIZE 0x0000003ful /* only 5 bits */
+#define LATTIME 0x0000ff00ul
+
+static int ahc_ext_scbram_present(struct ahc_softc *ahc);
+static void ahc_scbram_config(struct ahc_softc *ahc, int enable,
+ int pcheck, int fast, int large);
+static void ahc_probe_ext_scbram(struct ahc_softc *ahc);
+static void check_extport(struct ahc_softc *ahc, u_int *sxfrctl1);
+static void configure_termination(struct ahc_softc *ahc,
+ struct seeprom_descriptor *sd,
+ u_int adapter_control,
+ u_int *sxfrctl1);
+
+static void ahc_new_term_detect(struct ahc_softc *ahc,
+ int *enableSEC_low,
+ int *enableSEC_high,
+ int *enablePRI_low,
+ int *enablePRI_high,
+ int *eeprom_present);
+static void aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+ int *internal68_present,
+ int *externalcable_present,
+ int *eeprom_present);
+static void aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+ int *externalcable_present,
+ int *eeprom_present);
+static int acquire_seeprom(struct ahc_softc *ahc,
+ struct seeprom_descriptor *sd);
+static void release_seeprom(struct seeprom_descriptor *sd);
+static void write_brdctl(struct ahc_softc *ahc, uint8_t value);
+static uint8_t read_brdctl(struct ahc_softc *ahc);
+
+struct ahc_pci_identity *
+ahc_find_pci_device(ahc_dev_softc_t pci)
+{
+ uint64_t full_id;
+ uint16_t device;
+ uint16_t vendor;
+ uint16_t subdevice;
+ uint16_t subvendor;
+ struct ahc_pci_identity *entry;
+ u_int i;
+
+ vendor = ahc_pci_read_config(pci, PCIR_DEVVENDOR, /*bytes*/2);
+ device = ahc_pci_read_config(pci, PCIR_DEVICE, /*bytes*/2);
+ subvendor = ahc_pci_read_config(pci, PCIR_SUBVEND_0, /*bytes*/2);
+ subdevice = ahc_pci_read_config(pci, PCIR_SUBDEV_0, /*bytes*/2);
+ full_id = ahc_compose_id(device,
+ vendor,
+ subdevice,
+ subvendor);
+
+ for (i = 0; i < ahc_num_pci_devs; i++) {
+ entry = &ahc_pci_ident_table[i];
+ if (entry->full_id == (full_id & entry->id_mask))
+ return (entry);
+ }
+ return (NULL);
+}
+
+int
+ahc_pci_config(struct ahc_softc *ahc, struct ahc_pci_identity *entry)
+{
+ struct ahc_probe_config probe_config;
+ struct scb_data *shared_scb_data;
+ u_int command;
+ u_int our_id = 0;
+ u_int sxfrctl1;
+ u_int scsiseq;
+ u_int dscommand0;
+ int error;
+ uint8_t sblkctl;
+
+ shared_scb_data = NULL;
+ ahc_init_probe_config(&probe_config);
+ error = entry->setup(ahc->dev_softc, &probe_config);
+ if (error != 0)
+ return (error);
+ probe_config.chip |= AHC_PCI;
+ probe_config.description = entry->name;
+
+ error = ahc_pci_map_registers(ahc);
+ if (error != 0)
+ return (error);
+
+ /* Ensure busmastering is enabled */
+ command = ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, /*bytes*/1);
+ command |= PCIM_CMD_BUSMASTEREN;
+ ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, /*bytes*/1);
+
+ /* On all PCI adapters, we allow SCB paging */
+ probe_config.flags |= AHC_PAGESCBS;
+
+ error = ahc_softc_init(ahc, &probe_config);
+ if (error != 0)
+ return (error);
+
+ /* Remeber how the card was setup in case there is no SEEPROM */
+ pause_sequencer(ahc);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ our_id = ahc_inb(ahc, SCSIID_ULTRA2) & OID;
+ else
+ our_id = ahc_inb(ahc, SCSIID) & OID;
+ sxfrctl1 = ahc_inb(ahc, SXFRCTL1) & STPWEN;
+ scsiseq = ahc_inb(ahc, SCSISEQ);
+
+ error = ahc_reset(ahc);
+ if (error != 0)
+ return (ENXIO);
+
+ if ((ahc->features & AHC_DT) != 0) {
+ u_int sfunct;
+
+ /* Perform ALT-Mode Setup */
+ sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+ ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+ ahc_outb(ahc, OPTIONMODE, OPTIONMODE_DEFAULTS);
+ /* Send CRC info in target mode every 4K */
+ ahc_outb(ahc, TARGCRCCNT, 0);
+ ahc_outb(ahc, TARGCRCCNT + 1, 0x10);
+ ahc_outb(ahc, SFUNCT, sfunct);
+
+ /* Normal mode setup */
+ ahc_outb(ahc, CRCCONTROL1, CRCVALCHKEN|CRCENDCHKEN|CRCREQCHKEN
+ |TARGCRCENDEN|TARGCRCCNTEN);
+ }
+
+ error = ahc_pci_map_int(ahc);
+ if (error != 0)
+ return (error);
+ dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+ dscommand0 |= MPARCKEN;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+
+ /*
+ * DPARCKEN doesn't work correctly on
+ * some MBs so don't use it.
+ */
+ dscommand0 &= ~DPARCKEN;
+ dscommand0 |= CACHETHEN;
+ }
+
+ /*
+ * Handle chips that must have cache line
+ * streaming (dis/en)abled.
+ */
+ if ((ahc->bugs & AHC_CACHETHEN_DIS_BUG) != 0)
+ dscommand0 |= CACHETHEN;
+
+ if ((ahc->bugs & AHC_CACHETHEN_BUG) != 0)
+ dscommand0 &= ~CACHETHEN;
+
+ ahc_outb(ahc, DSCOMMAND0, dscommand0);
+
+ ahc->pci_cachesize =
+ ahc_pci_read_config(ahc->dev_softc, CSIZE_LATTIME,
+ /*bytes*/1) & CACHESIZE;
+ ahc->pci_cachesize *= 4;
+
+ /* See if we have a SEEPROM and perform auto-term */
+ check_extport(ahc, &sxfrctl1);
+
+ /*
+ * Take the LED out of diagnostic mode
+ */
+ sblkctl = ahc_inb(ahc, SBLKCTL);
+ ahc_outb(ahc, SBLKCTL, (sblkctl & ~(DIAGLEDEN|DIAGLEDON)));
+
+ /*
+ * I don't know where this is set in the SEEPROM or by the
+ * BIOS, so we default to 100% on Ultra or slower controllers
+ * and 75% on ULTRA2 controllers.
+ */
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ ahc_outb(ahc, DFF_THRSH, RD_DFTHRSH_75|WR_DFTHRSH_75);
+ } else {
+ ahc_outb(ahc, DSPCISTATUS, DFTHRSH_100);
+ }
+
+ if (ahc->flags & AHC_USEDEFAULTS) {
+ /*
+ * PCI Adapter default setup
+ * Should only be used if the adapter does not have
+ * a SEEPROM.
+ */
+ /* See if someone else set us up already */
+ if (scsiseq != 0) {
+ printf("%s: Using left over BIOS settings\n",
+ ahc_name(ahc));
+ ahc->flags &= ~AHC_USEDEFAULTS;
+ } else {
+ /*
+ * Assume only one connector and always turn
+ * on termination.
+ */
+ our_id = 0x07;
+ sxfrctl1 = STPWEN;
+ }
+ ahc_outb(ahc, SCSICONF, our_id|ENSPCHK|RESET_SCSI);
+
+ ahc->our_id = our_id;
+ }
+
+ /*
+ * Take a look to see if we have external SRAM.
+ * We currently do not attempt to use SRAM that is
+ * shared among multiple controllers.
+ */
+ ahc_probe_ext_scbram(ahc);
+
+ /*
+ * Record our termination setting for the
+ * generic initialization routine.
+ */
+ if ((sxfrctl1 & STPWEN) != 0)
+ ahc->flags |= AHC_TERM_ENB_A;
+
+ /* Core initialization */
+ error = ahc_init(ahc);
+ if (error != 0)
+ return (error);
+
+ /*
+ * Link this softc in with all other ahc instances.
+ */
+ ahc_softc_insert(ahc);
+
+ return (0);
+}
+
+/*
+ * Test for the presense of external sram in an
+ * "unshared" configuration.
+ */
+static int
+ahc_ext_scbram_present(struct ahc_softc *ahc)
+{
+ int ramps;
+ int single_user;
+ uint32_t devconfig;
+
+ devconfig = ahc_pci_read_config(ahc->dev_softc,
+ DEVCONFIG, /*bytes*/4);
+ single_user = (devconfig & MPORTMODE) != 0;
+
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ramps = (ahc_inb(ahc, DSCOMMAND0) & RAMPS) != 0;
+ else if ((ahc->chip & AHC_CHIPID_MASK) >= AHC_AIC7870)
+ ramps = (devconfig & RAMPSM) != 0;
+ else
+ ramps = 0;
+
+ if (ramps && single_user)
+ return (1);
+ return (0);
+}
+
+/*
+ * Enable external scbram.
+ */
+static void
+ahc_scbram_config(struct ahc_softc *ahc, int enable, int pcheck,
+ int fast, int large)
+{
+ uint32_t devconfig;
+
+ if (ahc->features & AHC_MULTI_FUNC) {
+ /*
+ * Set the SCB Base addr (highest address bit)
+ * depending on which channel we are.
+ */
+ ahc_outb(ahc, SCBBADDR, ahc_get_pci_function(ahc->dev_softc));
+ }
+
+ devconfig = ahc_pci_read_config(ahc->dev_softc, DEVCONFIG, /*bytes*/4);
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ u_int dscommand0;
+
+ dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+ if (enable)
+ dscommand0 &= ~INTSCBRAMSEL;
+ else
+ dscommand0 |= INTSCBRAMSEL;
+ if (large)
+ dscommand0 &= ~USCBSIZE32;
+ else
+ dscommand0 |= USCBSIZE32;
+ ahc_outb(ahc, DSCOMMAND0, dscommand0);
+ } else {
+ if (fast)
+ devconfig &= ~EXTSCBTIME;
+ else
+ devconfig |= EXTSCBTIME;
+ if (enable)
+ devconfig &= ~SCBRAMSEL;
+ else
+ devconfig |= SCBRAMSEL;
+ if (large)
+ devconfig &= ~SCBSIZE32;
+ else
+ devconfig |= SCBSIZE32;
+ }
+ if (pcheck)
+ devconfig |= EXTSCBPEN;
+ else
+ devconfig &= ~EXTSCBPEN;
+
+ ahc_pci_write_config(ahc->dev_softc, DEVCONFIG, devconfig, /*bytes*/4);
+}
+
+/*
+ * Take a look to see if we have external SRAM.
+ * We currently do not attempt to use SRAM that is
+ * shared among multiple controllers.
+ */
+static void
+ahc_probe_ext_scbram(struct ahc_softc *ahc)
+{
+ int num_scbs;
+ int test_num_scbs;
+ int enable;
+ int pcheck;
+ int fast;
+ int large;
+
+ enable = FALSE;
+ pcheck = FALSE;
+ fast = FALSE;
+ large = FALSE;
+ num_scbs = 0;
+
+ if (ahc_ext_scbram_present(ahc) == 0)
+ goto done;
+
+ /*
+ * Probe for the best parameters to use.
+ */
+ ahc_scbram_config(ahc, /*enable*/TRUE, pcheck, fast, large);
+ num_scbs = ahc_probe_scbs(ahc);
+ if (num_scbs == 0) {
+ /* The SRAM wasn't really present. */
+ goto done;
+ }
+ enable = TRUE;
+
+ /*
+ * Clear any outstanding parity error
+ * and ensure that parity error reporting
+ * is enabled.
+ */
+ ahc_outb(ahc, SEQCTL, 0);
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+
+ /* Now see if we can do parity */
+ ahc_scbram_config(ahc, enable, /*pcheck*/TRUE, fast, large);
+ num_scbs = ahc_probe_scbs(ahc);
+ if ((ahc_inb(ahc, INTSTAT) & BRKADRINT) == 0
+ || (ahc_inb(ahc, ERROR) & MPARERR) == 0)
+ pcheck = TRUE;
+
+ /* Clear any resulting parity error */
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+
+ /* Now see if we can do fast timing */
+ ahc_scbram_config(ahc, enable, pcheck, /*fast*/TRUE, large);
+ test_num_scbs = ahc_probe_scbs(ahc);
+ if (test_num_scbs == num_scbs
+ && ((ahc_inb(ahc, INTSTAT) & BRKADRINT) == 0
+ || (ahc_inb(ahc, ERROR) & MPARERR) == 0))
+ fast = TRUE;
+
+ /*
+ * See if we can use large SCBs and still maintain
+ * the same overall count of SCBs.
+ */
+ if ((ahc->features & AHC_LARGE_SCBS) != 0) {
+ ahc_scbram_config(ahc, enable, pcheck, fast, /*large*/TRUE);
+ test_num_scbs = ahc_probe_scbs(ahc);
+ if (test_num_scbs >= num_scbs) {
+ large = TRUE;
+ num_scbs = test_num_scbs;
+ if (num_scbs >= 64) {
+ /*
+ * We have enough space to move the
+ * "busy targets table" into SCB space
+ * and make it qualify all the way to the
+ * lun level.
+ */
+ ahc->flags |= AHC_SCB_BTT;
+ }
+ }
+ }
+done:
+ /*
+ * Disable parity error reporting until we
+ * can load instruction ram.
+ */
+ ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS);
+ /* Clear any latched parity error */
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+ if (bootverbose && enable) {
+ printf("%s: External SRAM, %s access%s, %dbytes/SCB\n",
+ ahc_name(ahc), fast ? "fast" : "slow",
+ pcheck ? ", parity checking enabled" : "",
+ large ? 64 : 32);
+ }
+ ahc_scbram_config(ahc, enable, pcheck, fast, large);
+}
+
+/*
+ * Check the external port logic for a serial eeprom
+ * and termination/cable detection contrls.
+ */
+static void
+check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
+{
+ struct seeprom_descriptor sd;
+ struct seeprom_config sc;
+ u_int scsi_conf;
+ u_int adapter_control;
+ int have_seeprom;
+ int have_autoterm;
+
+ sd.sd_ahc = ahc;
+ sd.sd_control_offset = SEECTL;
+ sd.sd_status_offset = SEECTL;
+ sd.sd_dataout_offset = SEECTL;
+
+ /*
+ * For some multi-channel devices, the c46 is simply too
+ * small to work. For the other controller types, we can
+ * get our information from either SEEPROM type. Set the
+ * type to start our probe with accordingly.
+ */
+ if (ahc->flags & AHC_LARGE_SEEPROM)
+ sd.sd_chip = C56_66;
+ else
+ sd.sd_chip = C46;
+
+ sd.sd_MS = SEEMS;
+ sd.sd_RDY = SEERDY;
+ sd.sd_CS = SEECS;
+ sd.sd_CK = SEECK;
+ sd.sd_DO = SEEDO;
+ sd.sd_DI = SEEDI;
+
+ have_seeprom = acquire_seeprom(ahc, &sd);
+ if (have_seeprom) {
+
+ if (bootverbose)
+ printf("%s: Reading SEEPROM...", ahc_name(ahc));
+
+ for (;;) {
+ u_int start_addr;
+
+ start_addr = 32 * (ahc->channel - 'A');
+
+ have_seeprom = read_seeprom(&sd, (uint16_t *)&sc,
+ start_addr, sizeof(sc)/2);
+
+ if (have_seeprom)
+ have_seeprom = verify_cksum(&sc);
+
+ if (have_seeprom != 0 || sd.sd_chip == C56_66) {
+ if (bootverbose) {
+ if (have_seeprom == 0)
+ printf ("checksum error\n");
+ else
+ printf ("done.\n");
+ }
+ break;
+ }
+ sd.sd_chip = C56_66;
+ }
+ }
+
+#if 0
+ if (!have_seeprom) {
+ /*
+ * Pull scratch ram settings and treat them as
+ * if they are the contents of an seeprom if
+ * the 'ADPT' signature is found in SCB2.
+ */
+ ahc_outb(ahc, SCBPTR, 2);
+ if (ahc_inb(ahc, SCB_BASE) == 'A'
+ && ahc_inb(ahc, SCB_BASE + 1) == 'D'
+ && ahc_inb(ahc, SCB_BASE + 2) == 'P'
+ && ahc_inb(ahc, SCB_BASE + 3) == 'T') {
+ uint8_t *sc_bytes;
+ int i;
+
+ sc_bytes = (uint8_t *)&sc;
+ for (i = 0; i < 64; i++)
+ sc_bytes[i] = ahc_inb(ahc, TARG_SCSIRATE + i);
+ /* Byte 0x1c is stored in byte 4 of SCB2 */
+ sc_bytes[0x1c] = ahc_inb(ahc, SCB_BASE + 4);
+ have_seeprom = verify_cksum(&sc);
+ }
+ }
+#endif
+
+ if (!have_seeprom) {
+ if (bootverbose)
+ printf("%s: No SEEPROM available.\n", ahc_name(ahc));
+ ahc->flags |= AHC_USEDEFAULTS;
+ } else {
+ /*
+ * Put the data we've collected down into SRAM
+ * where ahc_init will find it.
+ */
+ int i;
+ int max_targ = sc.max_targets & CFMAXTARG;
+ uint16_t discenable;
+ uint16_t ultraenb;
+
+ discenable = 0;
+ ultraenb = 0;
+ if ((sc.adapter_control & CFULTRAEN) != 0) {
+ /*
+ * Determine if this adapter has a "newstyle"
+ * SEEPROM format.
+ */
+ for (i = 0; i < max_targ; i++) {
+ if ((sc.device_flags[i] & CFSYNCHISULTRA) != 0){
+ ahc->flags |= AHC_NEWEEPROM_FMT;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < max_targ; i++) {
+ u_int scsirate;
+ uint16_t target_mask;
+
+ target_mask = 0x01 << i;
+ if (sc.device_flags[i] & CFDISC)
+ discenable |= target_mask;
+ if ((ahc->flags & AHC_NEWEEPROM_FMT) != 0) {
+ if ((sc.device_flags[i] & CFSYNCHISULTRA) != 0)
+ ultraenb |= target_mask;
+ } else if ((sc.adapter_control & CFULTRAEN) != 0) {
+ ultraenb |= target_mask;
+ }
+ if ((sc.device_flags[i] & CFXFER) == 0x04
+ && (ultraenb & target_mask) != 0) {
+ /* Treat 10MHz as a non-ultra speed */
+ sc.device_flags[i] &= ~CFXFER;
+ ultraenb &= ~target_mask;
+ }
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ u_int offset;
+
+ if (sc.device_flags[i] & CFSYNCH)
+ offset = MAX_OFFSET_ULTRA2;
+ else
+ offset = 0;
+ ahc_outb(ahc, TARG_OFFSET + i, offset);
+
+ /*
+ * The ultra enable bits contain the
+ * high bit of the ultra2 sync rate
+ * field.
+ */
+ scsirate = (sc.device_flags[i] & CFXFER)
+ | ((ultraenb & target_mask)
+ ? 0x8 : 0x0);
+ if (sc.device_flags[i] & CFWIDEB)
+ scsirate |= WIDEXFER;
+ } else {
+ scsirate = (sc.device_flags[i] & CFXFER) << 4;
+ if (sc.device_flags[i] & CFSYNCH)
+ scsirate |= SOFS;
+ if (sc.device_flags[i] & CFWIDEB)
+ scsirate |= WIDEXFER;
+ }
+ ahc_outb(ahc, TARG_SCSIRATE + i, scsirate);
+ }
+ ahc->our_id = sc.brtime_id & CFSCSIID;
+
+ scsi_conf = (ahc->our_id & 0x7);
+ if (sc.adapter_control & CFSPARITY)
+ scsi_conf |= ENSPCHK;
+ if (sc.adapter_control & CFRESETB)
+ scsi_conf |= RESET_SCSI;
+
+ if (sc.bios_control & CFEXTEND)
+ ahc->flags |= AHC_EXTENDED_TRANS_A;
+ if (ahc->features & AHC_ULTRA
+ && (ahc->flags & AHC_NEWEEPROM_FMT) == 0) {
+ /* Should we enable Ultra mode? */
+ if (!(sc.adapter_control & CFULTRAEN))
+ /* Treat us as a non-ultra card */
+ ultraenb = 0;
+ }
+
+ if (sc.signature == CFSIGNATURE) {
+ uint32_t devconfig;
+
+ /* Honor the STPWLEVEL settings */
+ devconfig = ahc_pci_read_config(ahc->dev_softc,
+ DEVCONFIG, /*bytes*/4);
+ devconfig &= ~STPWLEVEL;
+ if ((sc.bios_control & CFSTPWLEVEL) != 0)
+ devconfig |= STPWLEVEL;
+ ahc_pci_write_config(ahc->dev_softc, DEVCONFIG,
+ devconfig, /*bytes*/4);
+ }
+ /* Set SCSICONF info */
+ ahc_outb(ahc, SCSICONF, scsi_conf);
+ ahc_outb(ahc, DISC_DSB, ~(discenable & 0xff));
+ ahc_outb(ahc, DISC_DSB + 1, ~((discenable >> 8) & 0xff));
+ ahc_outb(ahc, ULTRA_ENB, ultraenb & 0xff);
+ ahc_outb(ahc, ULTRA_ENB + 1, (ultraenb >> 8) & 0xff);
+ }
+
+ /*
+ * Cards that have the external logic necessary to talk to
+ * a SEEPROM, are almost certain to have the remaining logic
+ * necessary for auto-termination control. This assumption
+ * hasn't failed yet...
+ */
+ have_autoterm = have_seeprom;
+ if (have_seeprom)
+ adapter_control = sc.adapter_control;
+ else
+ adapter_control = CFAUTOTERM;
+
+ /*
+ * Some low-cost chips have SEEPROM and auto-term control built
+ * in, instead of using a GAL. They can tell us directly
+ * if the termination logic is enabled.
+ */
+ if ((ahc->features & AHC_SPIOCAP) != 0) {
+ if ((ahc_inb(ahc, SPIOCAP) & SSPIOCPS) != 0)
+ have_autoterm = TRUE;
+ else
+ have_autoterm = FALSE;
+ }
+
+ if (have_autoterm)
+ configure_termination(ahc, &sd, adapter_control, sxfrctl1);
+
+ release_seeprom(&sd);
+}
+
+static void
+configure_termination(struct ahc_softc *ahc,
+ struct seeprom_descriptor *sd,
+ u_int adapter_control,
+ u_int *sxfrctl1)
+{
+ uint8_t brddat;
+
+ brddat = 0;
+
+ /*
+ * Update the settings in sxfrctl1 to match the
+ * termination settings
+ */
+ *sxfrctl1 = 0;
+
+ /*
+ * SEECS must be on for the GALS to latch
+ * the data properly. Be sure to leave MS
+ * on or we will release the seeprom.
+ */
+ SEEPROM_OUTB(sd, sd->sd_MS | sd->sd_CS);
+ if ((adapter_control & CFAUTOTERM) != 0
+ || (ahc->features & AHC_NEW_TERMCTL) != 0) {
+ int internal50_present;
+ int internal68_present;
+ int externalcable_present;
+ int eeprom_present;
+ int enableSEC_low;
+ int enableSEC_high;
+ int enablePRI_low;
+ int enablePRI_high;
+
+ enableSEC_low = 0;
+ enableSEC_high = 0;
+ enablePRI_low = 0;
+ enablePRI_high = 0;
+ if ((ahc->features & AHC_NEW_TERMCTL) != 0) {
+ ahc_new_term_detect(ahc, &enableSEC_low,
+ &enableSEC_high,
+ &enablePRI_low,
+ &enablePRI_high,
+ &eeprom_present);
+ if ((adapter_control & CFSEAUTOTERM) == 0) {
+ if (bootverbose)
+ printf("%s: Manual SE Termination\n",
+ ahc_name(ahc));
+ enableSEC_low = (adapter_control & CFSELOWTERM);
+ enableSEC_high =
+ (adapter_control & CFSEHIGHTERM);
+ }
+ if ((adapter_control & CFAUTOTERM) == 0) {
+ if (bootverbose)
+ printf("%s: Manual LVD Termination\n",
+ ahc_name(ahc));
+ enablePRI_low = (adapter_control & CFSTERM);
+ enablePRI_high = (adapter_control & CFWSTERM);
+ }
+ /* Make the table calculations below happy */
+ internal50_present = 0;
+ internal68_present = 1;
+ externalcable_present = 1;
+ } else if ((ahc->features & AHC_SPIOCAP) != 0) {
+ aic785X_cable_detect(ahc, &internal50_present,
+ &externalcable_present,
+ &eeprom_present);
+ } else {
+ aic787X_cable_detect(ahc, &internal50_present,
+ &internal68_present,
+ &externalcable_present,
+ &eeprom_present);
+ }
+
+ if ((ahc->features & AHC_WIDE) == 0)
+ internal68_present = 0;
+
+ if (bootverbose) {
+ if ((ahc->features & AHC_ULTRA2) == 0) {
+ printf("%s: internal 50 cable %s present, "
+ "internal 68 cable %s present\n",
+ ahc_name(ahc),
+ internal50_present ? "is":"not",
+ internal68_present ? "is":"not");
+
+ printf("%s: external cable %s present\n",
+ ahc_name(ahc),
+ externalcable_present ? "is":"not");
+ }
+ printf("%s: BIOS eeprom %s present\n",
+ ahc_name(ahc), eeprom_present ? "is" : "not");
+ }
+
+ if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0) {
+ /*
+ * The 50 pin connector is a separate bus,
+ * so force it to always be terminated.
+ * In the future, perform current sensing
+ * to determine if we are in the middle of
+ * a properly terminated bus.
+ */
+ internal50_present = 0;
+ }
+
+ /*
+ * Now set the termination based on what
+ * we found.
+ * Flash Enable = BRDDAT7
+ * Secondary High Term Enable = BRDDAT6
+ * Secondary Low Term Enable = BRDDAT5 (7890)
+ * Primary High Term Enable = BRDDAT4 (7890)
+ */
+ if ((ahc->features & AHC_ULTRA2) == 0
+ && (internal50_present != 0)
+ && (internal68_present != 0)
+ && (externalcable_present != 0)) {
+ printf("%s: Illegal cable configuration!!. "
+ "Only two connectors on the "
+ "adapter may be used at a "
+ "time!\n", ahc_name(ahc));
+ }
+
+ if ((ahc->features & AHC_WIDE) != 0
+ && ((externalcable_present == 0)
+ || (internal68_present == 0)
+ || (enableSEC_high != 0))) {
+ brddat |= BRDDAT6;
+ if (bootverbose) {
+ if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0)
+ printf("%s: 68 pin termination "
+ "Enabled\n", ahc_name(ahc));
+ else
+ printf("%s: %sHigh byte termination "
+ "Enabled\n", ahc_name(ahc),
+ enableSEC_high ? "Secondary "
+ : "");
+ }
+ }
+
+ if (((internal50_present ? 1 : 0)
+ + (internal68_present ? 1 : 0)
+ + (externalcable_present ? 1 : 0)) <= 1
+ || (enableSEC_low != 0)) {
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ brddat |= BRDDAT5;
+ else
+ *sxfrctl1 |= STPWEN;
+ if (bootverbose) {
+ if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0)
+ printf("%s: 50 pin termination "
+ "Enabled\n", ahc_name(ahc));
+ else
+ printf("%s: %sLow byte termination "
+ "Enabled\n", ahc_name(ahc),
+ enableSEC_low ? "Secondary "
+ : "");
+ }
+ }
+
+ if (enablePRI_low != 0) {
+ *sxfrctl1 |= STPWEN;
+ if (bootverbose)
+ printf("%s: Primary Low Byte termination "
+ "Enabled\n", ahc_name(ahc));
+ }
+
+ /*
+ * Setup STPWEN before setting up the rest of
+ * the termination per the tech note on the U160 cards.
+ */
+ ahc_outb(ahc, SXFRCTL1, *sxfrctl1);
+
+ if (enablePRI_high != 0) {
+ brddat |= BRDDAT4;
+ if (bootverbose)
+ printf("%s: Primary High Byte "
+ "termination Enabled\n",
+ ahc_name(ahc));
+ }
+
+ write_brdctl(ahc, brddat);
+
+ } else {
+ if ((adapter_control & CFSTERM) != 0) {
+ *sxfrctl1 |= STPWEN;
+
+ if (bootverbose)
+ printf("%s: %sLow byte termination Enabled\n",
+ ahc_name(ahc),
+ (ahc->features & AHC_ULTRA2) ? "Primary "
+ : "");
+ }
+
+ if ((adapter_control & CFWSTERM) != 0) {
+ brddat |= BRDDAT6;
+ if (bootverbose)
+ printf("%s: %sHigh byte termination Enabled\n",
+ ahc_name(ahc),
+ (ahc->features & AHC_ULTRA2)
+ ? "Secondary " : "");
+ }
+
+ /*
+ * Setup STPWEN before setting up the rest of
+ * the termination per the tech note on the U160 cards.
+ */
+ ahc_outb(ahc, SXFRCTL1, *sxfrctl1);
+
+ write_brdctl(ahc, brddat);
+ }
+ SEEPROM_OUTB(sd, sd->sd_MS); /* Clear CS */
+}
+
+static void
+ahc_new_term_detect(struct ahc_softc *ahc, int *enableSEC_low,
+ int *enableSEC_high, int *enablePRI_low,
+ int *enablePRI_high, int *eeprom_present)
+{
+ uint8_t brdctl;
+
+ /*
+ * BRDDAT7 = Eeprom
+ * BRDDAT6 = Enable Secondary High Byte termination
+ * BRDDAT5 = Enable Secondary Low Byte termination
+ * BRDDAT4 = Enable Primary high byte termination
+ * BRDDAT3 = Enable Primary low byte termination
+ */
+ brdctl = read_brdctl(ahc);
+ *eeprom_present = brdctl & BRDDAT7;
+ *enableSEC_high = (brdctl & BRDDAT6);
+ *enableSEC_low = (brdctl & BRDDAT5);
+ *enablePRI_high = (brdctl & BRDDAT4);
+ *enablePRI_low = (brdctl & BRDDAT3);
+}
+
+static void
+aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+ int *internal68_present, int *externalcable_present,
+ int *eeprom_present)
+{
+ uint8_t brdctl;
+
+ /*
+ * First read the status of our cables.
+ * Set the rom bank to 0 since the
+ * bank setting serves as a multiplexor
+ * for the cable detection logic.
+ * BRDDAT5 controls the bank switch.
+ */
+ write_brdctl(ahc, 0);
+
+ /*
+ * Now read the state of the internal
+ * connectors. BRDDAT6 is INT50 and
+ * BRDDAT7 is INT68.
+ */
+ brdctl = read_brdctl(ahc);
+ *internal50_present = !(brdctl & BRDDAT6);
+ *internal68_present = !(brdctl & BRDDAT7);
+
+ /*
+ * Set the rom bank to 1 and determine
+ * the other signals.
+ */
+ write_brdctl(ahc, BRDDAT5);
+
+ /*
+ * Now read the state of the external
+ * connectors. BRDDAT6 is EXT68 and
+ * BRDDAT7 is EPROMPS.
+ */
+ brdctl = read_brdctl(ahc);
+ *externalcable_present = !(brdctl & BRDDAT6);
+ *eeprom_present = brdctl & BRDDAT7;
+}
+
+static void
+aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+ int *externalcable_present, int *eeprom_present)
+{
+ uint8_t brdctl;
+
+ ahc_outb(ahc, BRDCTL, BRDRW|BRDCS);
+ ahc_outb(ahc, BRDCTL, 0);
+ brdctl = ahc_inb(ahc, BRDCTL);
+ *internal50_present = !(brdctl & BRDDAT5);
+ *externalcable_present = !(brdctl & BRDDAT6);
+
+ *eeprom_present = (ahc_inb(ahc, SPIOCAP) & EEPROM) != 0;
+}
+
+static int
+acquire_seeprom(struct ahc_softc *ahc, struct seeprom_descriptor *sd)
+{
+ int wait;
+
+ if ((ahc->features & AHC_SPIOCAP) != 0
+ && (ahc_inb(ahc, SPIOCAP) & SEEPROM) == 0)
+ return (0);
+
+ /*
+ * Request access of the memory port. When access is
+ * granted, SEERDY will go high. We use a 1 second
+ * timeout which should be near 1 second more than
+ * is needed. Reason: after the chip reset, there
+ * should be no contention.
+ */
+ SEEPROM_OUTB(sd, sd->sd_MS);
+ wait = 1000; /* 1 second timeout in msec */
+ while (--wait && ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0)) {
+ ahc_delay(1000); /* delay 1 msec */
+ }
+ if ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0) {
+ SEEPROM_OUTB(sd, 0);
+ return (0);
+ }
+ return(1);
+}
+
+static void
+release_seeprom(struct seeprom_descriptor *sd)
+{
+ /* Release access to the memory port and the serial EEPROM. */
+ SEEPROM_OUTB(sd, 0);
+}
+
+static void
+write_brdctl(struct ahc_softc *ahc, uint8_t value)
+{
+ uint8_t brdctl;
+
+ if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+ brdctl = BRDSTB;
+ if (ahc->channel == 'B')
+ brdctl |= BRDCS;
+ } else if ((ahc->features & AHC_ULTRA2) != 0) {
+ brdctl = 0;
+ } else {
+ brdctl = BRDSTB|BRDCS;
+ }
+ ahc_outb(ahc, BRDCTL, brdctl);
+ ahc_delay(20);
+ brdctl |= value;
+ ahc_outb(ahc, BRDCTL, brdctl);
+ ahc_delay(20);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ brdctl |= BRDSTB_ULTRA2;
+ else
+ brdctl &= ~BRDSTB;
+ ahc_outb(ahc, BRDCTL, brdctl);
+ ahc_delay(20);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ brdctl = 0;
+ else
+ brdctl &= ~BRDCS;
+ ahc_outb(ahc, BRDCTL, brdctl);
+}
+
+static uint8_t
+read_brdctl(ahc)
+ struct ahc_softc *ahc;
+{
+ uint8_t brdctl;
+ uint8_t value;
+
+ if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+ brdctl = BRDRW;
+ if (ahc->channel == 'B')
+ brdctl |= BRDCS;
+ } else if ((ahc->features & AHC_ULTRA2) != 0) {
+ brdctl = BRDRW_ULTRA2;
+ } else {
+ brdctl = BRDRW|BRDCS;
+ }
+ ahc_outb(ahc, BRDCTL, brdctl);
+ ahc_delay(20);
+ value = ahc_inb(ahc, BRDCTL);
+ ahc_outb(ahc, BRDCTL, 0);
+ return (value);
+}
+
+#define DPE 0x80
+#define SSE 0x40
+#define RMA 0x20
+#define RTA 0x10
+#define STA 0x08
+#define DPR 0x01
+
+void
+ahc_pci_intr(struct ahc_softc *ahc)
+{
+ u_int error;
+ u_int status1;
+
+ error = ahc_inb(ahc, ERROR);
+ if ((error & PCIERRSTAT) == 0)
+ return;
+
+ status1 = ahc_pci_read_config(ahc->dev_softc,
+ PCIR_STATUS + 1, /*bytes*/1);
+
+ printf("%s: PCI error Interrupt at seqaddr = 0x%x\n",
+ ahc_name(ahc),
+ ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
+
+ if (status1 & DPE) {
+ printf("%s: Data Parity Error Detected during address "
+ "or write data phase\n", ahc_name(ahc));
+ }
+ if (status1 & SSE) {
+ printf("%s: Signaled System Error Detected\n", ahc_name(ahc));
+ }
+ if (status1 & RMA) {
+ printf("%s: Signaled a Master Abort\n", ahc_name(ahc));
+ }
+ if (status1 & RTA) {
+ printf("%s: Received a Target Abort\n", ahc_name(ahc));
+ }
+ if (status1 & STA) {
+ printf("%s: Signaled a Target Abort\n", ahc_name(ahc));
+ }
+ if (status1 & DPR) {
+ printf("%s: Data Parity Error has been reported via PERR#\n",
+ ahc_name(ahc));
+ }
+ if ((status1 & (DPE|SSE|RMA|RTA|STA|DPR)) == 0) {
+ printf("%s: Latched PCIERR interrupt with "
+ "no status bits set\n", ahc_name(ahc));
+ }
+ ahc_pci_write_config(ahc->dev_softc, PCIR_STATUS + 1,
+ status1, /*bytes*/1);
+
+ if (status1 & (DPR|RMA|RTA)) {
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ }
+
+ unpause_sequencer(ahc);
+}
+
+static int
+ahc_aic7850_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ probe_config->channel = 'A';
+ probe_config->chip = AHC_AIC7850;
+ probe_config->features = AHC_AIC7850_FE;
+ probe_config->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG
+ | AHC_PCI_MWI_BUG;
+ return (0);
+}
+
+static int
+ahc_aic7855_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ probe_config->channel = 'A';
+ probe_config->chip = AHC_AIC7855;
+ probe_config->features = AHC_AIC7855_FE;
+ probe_config->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG
+ | AHC_PCI_MWI_BUG;
+ return (0);
+}
+
+static int
+ahc_aic7860_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ uint8_t rev;
+
+ probe_config->channel = 'A';
+ probe_config->chip = AHC_AIC7860;
+ probe_config->features = AHC_AIC7860_FE;
+ probe_config->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG
+ | AHC_PCI_MWI_BUG;
+ rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+ if (rev >= 1)
+ probe_config->bugs |= AHC_PCI_2_1_RETRY_BUG;
+ return (0);
+}
+
+static int
+ahc_aic7870_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ probe_config->channel = 'A';
+ probe_config->chip = AHC_AIC7870;
+ probe_config->features = AHC_AIC7870_FE;
+ probe_config->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG
+ | AHC_PCI_MWI_BUG;
+ return (0);
+}
+
+static int
+ahc_aha394X_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ int error;
+
+ error = ahc_aic7870_setup(pci, probe_config);
+ if (error == 0)
+ error = ahc_aha394XX_setup(pci, probe_config);
+ return (error);
+}
+
+static int
+ahc_aha398X_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ int error;
+
+ error = ahc_aic7870_setup(pci, probe_config);
+ if (error == 0)
+ error = ahc_aha398XX_setup(pci, probe_config);
+ return (error);
+}
+
+static int
+ahc_aha494X_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ int error;
+
+ error = ahc_aic7870_setup(pci, probe_config);
+ if (error == 0)
+ error = ahc_aha494XX_setup(pci, probe_config);
+ return (error);
+}
+
+static int
+ahc_aic7880_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ uint8_t rev;
+
+ probe_config->channel = 'A';
+ probe_config->chip = AHC_AIC7880;
+ probe_config->features = AHC_AIC7880_FE;
+ probe_config->bugs |= AHC_TMODE_WIDEODD_BUG;
+ rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+ if (rev >= 1) {
+ probe_config->bugs |= AHC_PCI_2_1_RETRY_BUG;
+ } else {
+ probe_config->bugs |= AHC_CACHETHEN_BUG;
+ }
+ return (0);
+}
+
+static int
+ahc_2940Pro_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ int error;
+
+ probe_config->flags |= AHC_INT50_SPEEDFLEX;
+ error = ahc_aic7880_setup(pci, probe_config);
+ return (0);
+}
+
+static int
+ahc_aha394XU_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ int error;
+
+ error = ahc_aic7880_setup(pci, probe_config);
+ if (error == 0)
+ error = ahc_aha394XX_setup(pci, probe_config);
+ return (error);
+}
+
+static int
+ahc_aha398XU_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ int error;
+
+ error = ahc_aic7880_setup(pci, probe_config);
+ if (error == 0)
+ error = ahc_aha398XX_setup(pci, probe_config);
+ return (error);
+}
+
+static int
+ahc_aic7890_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ uint8_t rev;
+
+ probe_config->channel = 'A';
+ probe_config->chip = AHC_AIC7890;
+ probe_config->features = AHC_AIC7890_FE;
+ probe_config->flags |= AHC_NEWEEPROM_FMT;
+ rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+ if (rev == 0)
+ probe_config->bugs |= AHC_AUTOFLUSH_BUG|AHC_CACHETHEN_BUG;
+ return (0);
+}
+
+static int
+ahc_aic7892_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ probe_config->channel = 'A';
+ probe_config->chip = AHC_AIC7892;
+ probe_config->features = AHC_AIC7892_FE;
+ probe_config->flags |= AHC_NEWEEPROM_FMT;
+ probe_config->bugs |= AHC_SCBCHAN_UPLOAD_BUG;
+ return (0);
+}
+
+static int
+ahc_aic7895_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ uint8_t rev;
+
+ probe_config->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+ /*
+ * The 'C' revision of the aic7895 has a few additional features.
+ */
+ rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+ if (rev >= 4) {
+ probe_config->chip = AHC_AIC7895C;
+ probe_config->features = AHC_AIC7895C_FE;
+ } else {
+ u_int command;
+
+ probe_config->chip = AHC_AIC7895;
+ probe_config->features = AHC_AIC7895_FE;
+
+ /*
+ * The BIOS disables the use of MWI transactions
+ * since it does not have the MWI bug work around
+ * we have. Disabling MWI reduces performance, so
+ * turn it on again.
+ */
+ command = ahc_pci_read_config(pci, PCIR_COMMAND, /*bytes*/1);
+ command |= PCIM_CMD_MWRICEN;
+ ahc_pci_write_config(pci, PCIR_COMMAND, command, /*bytes*/1);
+ probe_config->bugs |= AHC_PCI_MWI_BUG;
+ }
+ /*
+ * XXX Does CACHETHEN really not work??? What about PCI retry?
+ * on C level chips. Need to test, but for now, play it safe.
+ */
+ probe_config->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_PCI_2_1_RETRY_BUG
+ | AHC_CACHETHEN_BUG;
+
+#if 0
+ uint32_t devconfig;
+
+ /*
+ * Cachesize must also be zero due to stray DAC
+ * problem when sitting behind some bridges.
+ */
+ ahc_pci_write_config(pci, CSIZE_LATTIME, 0, /*bytes*/1);
+ devconfig = ahc_pci_read_config(pci, DEVCONFIG, /*bytes*/1);
+ devconfig |= MRDCEN;
+ ahc_pci_write_config(pci, DEVCONFIG, devconfig, /*bytes*/1);
+#endif
+ probe_config->flags |= AHC_NEWEEPROM_FMT;
+ return (0);
+}
+
+static int
+ahc_aic7896_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ probe_config->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+ probe_config->chip = AHC_AIC7896;
+ probe_config->features = AHC_AIC7896_FE;
+ probe_config->flags |= AHC_NEWEEPROM_FMT;
+ probe_config->bugs |= AHC_CACHETHEN_DIS_BUG;
+ return (0);
+}
+
+static int
+ahc_aic7899_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ probe_config->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+ probe_config->chip = AHC_AIC7899;
+ probe_config->features = AHC_AIC7899_FE;
+ probe_config->flags |= AHC_NEWEEPROM_FMT;
+ probe_config->bugs |= AHC_SCBCHAN_UPLOAD_BUG;
+ return (0);
+}
+
+static int
+ahc_raid_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ printf("RAID functionality unsupported\n");
+ return (ENXIO);
+}
+
+static int
+ahc_aha394XX_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ switch (ahc_get_pci_slot(pci)) {
+ case AHC_394X_SLOT_CHANNEL_A:
+ probe_config->channel = 'A';
+ break;
+ case AHC_394X_SLOT_CHANNEL_B:
+ probe_config->channel = 'B';
+ break;
+ default:
+ printf("adapter at unexpected slot %d\n"
+ "unable to map to a channel\n",
+ ahc_get_pci_slot(pci));
+ probe_config->channel = 'A';
+ }
+ return (0);
+}
+
+static int
+ahc_aha398XX_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ switch (ahc_get_pci_slot(pci)) {
+ case AHC_398X_SLOT_CHANNEL_A:
+ probe_config->channel = 'A';
+ break;
+ case AHC_398X_SLOT_CHANNEL_B:
+ probe_config->channel = 'B';
+ break;
+ case AHC_398X_SLOT_CHANNEL_C:
+ probe_config->channel = 'C';
+ break;
+ default:
+ printf("adapter at unexpected slot %d\n"
+ "unable to map to a channel\n",
+ ahc_get_pci_slot(pci));
+ probe_config->channel = 'A';
+ break;
+ }
+ probe_config->flags |= AHC_LARGE_SEEPROM;
+ return (0);
+}
+
+static int
+ahc_aha494XX_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+ switch (ahc_get_pci_slot(pci)) {
+ case AHC_494X_SLOT_CHANNEL_A:
+ probe_config->channel = 'A';
+ break;
+ case AHC_494X_SLOT_CHANNEL_B:
+ probe_config->channel = 'B';
+ break;
+ case AHC_494X_SLOT_CHANNEL_C:
+ probe_config->channel = 'C';
+ break;
+ case AHC_494X_SLOT_CHANNEL_D:
+ probe_config->channel = 'D';
+ break;
+ default:
+ printf("adapter at unexpected slot %d\n"
+ "unable to map to a channel\n",
+ ahc_get_pci_slot(pci));
+ probe_config->channel = 'A';
+ }
+ probe_config->flags |= AHC_LARGE_SEEPROM;
+ return (0);
+}
diff --git a/sys/dev/aic7xxx/aicasm/aicasm.c b/sys/dev/aic7xxx/aicasm/aicasm.c
new file mode 100644
index 000000000000..361d7e833010
--- /dev/null
+++ b/sys/dev/aic7xxx/aicasm/aicasm.c
@@ -0,0 +1,770 @@
+/*
+ * Aic7xxx SCSI host adapter firmware asssembler
+ *
+ * Copyright (c) 1997, 1998, 2000 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.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aicasm/aicasm.c#4 $
+ *
+ * $FreeBSD$
+ */
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_insformat.h"
+
+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(void);
+static void output_listing(char *ifilename);
+static void 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;
+char *appname;
+FILE *ofile;
+char *ofilename;
+char *regfilename;
+FILE *regfile;
+char *listfilename;
+FILE *listfile;
+
+static STAILQ_HEAD(,instruction) seq_program;
+struct cs_tailq cs_tailq;
+struct scope_list scope_stack;
+symlist_t patch_functions;
+
+#if DEBUG
+extern int yy_flex_debug;
+extern int yydebug;
+#endif
+extern FILE *yyin;
+extern int yyparse(void);
+
+int main(int argc, char *argv[]);
+
+int
+main(int argc, char *argv[])
+{
+ extern char *optarg;
+ extern int optind;
+ int ch;
+ int retval;
+ char *inputfilename;
+ scope_t *sentinal;
+
+ STAILQ_INIT(&patches);
+ SLIST_INIT(&search_path);
+ STAILQ_INIT(&seq_program);
+ TAILQ_INIT(&cs_tailq);
+ 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;
+#if DEBUG
+ yy_flex_debug = 0;
+ yydebug = 0;
+#endif
+ while ((ch = getopt(argc, argv, "d:l:n:o:r:I:O:")) != -1) {
+ switch(ch) {
+ case 'd':
+#if DEBUG
+ if (strcmp(optarg, "s") == 0) {
+ yy_flex_debug = 1;
+ } else if (strcmp(optarg, "p") == 0) {
+ yydebug = 1;
+ } else {
+ fprintf(stderr, "%s: -d Requires either an "
+ "'s' or 'p' argument\n", appname);
+ usage();
+ }
+#else
+ stop("-d: Assembler not built with debugging "
+ "information", EX_SOFTWARE);
+#endif
+ break;
+ case 'l':
+ /* Create a program listing */
+ if ((listfile = fopen(optarg, "w")) == NULL) {
+ perror(optarg);
+ stop(NULL, EX_CANTCREAT);
+ }
+ listfilename = optarg;
+ break;
+ case 'n':
+ /* Don't complain about the -nostdinc directrive */
+ if (strcmp(optarg, "ostdinc")) {
+ fprintf(stderr, "%s: Unknown option -%c%s\n",
+ appname, ch, optarg);
+ usage();
+ /* NOTREACHED */
+ }
+ break;
+ case 'o':
+ if ((ofile = fopen(optarg, "w")) == NULL) {
+ perror(optarg);
+ stop(NULL, EX_CANTCREAT);
+ }
+ ofilename = optarg;
+ break;
+ case 'r':
+ if ((regfile = fopen(optarg, "w")) == NULL) {
+ perror(optarg);
+ stop(NULL, EX_CANTCREAT);
+ }
+ regfilename = optarg;
+ break;
+ case 'I':
+ {
+ path_entry_t include_dir;
+
+ if (strcmp(optarg, "-") == 0) {
+ if (includes_search_curdir == 0) {
+ fprintf(stderr, "%s: Warning - '-I-' "
+ "specified multiple "
+ "times\n", appname);
+ }
+ includes_search_curdir = 0;
+ for (include_dir = search_path.slh_first;
+ include_dir != NULL;
+ include_dir = include_dir->links.sle_next)
+ /*
+ * All entries before a '-I-' only
+ * apply to includes specified with
+ * quotes instead of "<>".
+ */
+ include_dir->quoted_includes_only = 1;
+ } else {
+ include_dir =
+ (path_entry_t)malloc(sizeof(*include_dir));
+ if (include_dir == NULL) {
+ perror(optarg);
+ stop(NULL, EX_OSERR);
+ }
+ include_dir->directory = strdup(optarg);
+ if (include_dir->directory == NULL) {
+ perror(optarg);
+ stop(NULL, EX_OSERR);
+ }
+ include_dir->quoted_includes_only = 0;
+ SLIST_INSERT_HEAD(&search_path, include_dir,
+ links);
+ }
+ break;
+ }
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1) {
+ fprintf(stderr, "%s: No input file specifiled\n", appname);
+ usage();
+ /* NOTREACHED */
+ }
+
+ symtable_open();
+ inputfilename = *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();
+ if (regfile != NULL) {
+ symtable_dump(regfile);
+ }
+ if (listfile != NULL)
+ output_listing(inputfilename);
+ }
+
+ stop(NULL, 0);
+ /* NOTREACHED */
+ return (0);
+}
+
+static void
+usage()
+{
+
+ (void)fprintf(stderr,
+"usage: %-16s [-nostdinc] [-I-] [-I directory] [-o output_file]
+ [-r register_output_file] [-l program_list_file]
+ input_file\n",
+ appname);
+ exit(EX_USAGE);
+}
+
+static void
+back_patch()
+{
+ struct instruction *cur_instr;
+
+ for(cur_instr = seq_program.stqh_first;
+ cur_instr != NULL;
+ cur_instr = cur_instr->links.stqe_next) {
+ if (cur_instr->patch_label != NULL) {
+ struct ins_format3 *f3_instr;
+ u_int address;
+
+ if (cur_instr->patch_label->type != LABEL) {
+ char buf[255];
+
+ snprintf(buf, sizeof(buf),
+ "Undefined label %s",
+ cur_instr->patch_label->name);
+ stop(buf, EX_DATAERR);
+ /* NOTREACHED */
+ }
+ f3_instr = &cur_instr->format.format3;
+ address = f3_instr->address;
+ address += cur_instr->patch_label->info.linfo->address;
+ f3_instr->address = address;
+ }
+ }
+}
+
+static void
+output_code()
+{
+ struct instruction *cur_instr;
+ patch_t *cur_patch;
+ critical_section_t *cs;
+ symbol_node_t *cur_node;
+ int instrcount;
+
+ instrcount = 0;
+ fprintf(ofile,
+"/*
+ * DO NOT EDIT - This file is automatically generated.
+ */\n");
+
+ fprintf(ofile, "static uint8_t seqprog[] = {\n");
+ 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",
+#if BYTE_ORDER == LITTLE_ENDIAN
+ cur_instr->format.bytes[0],
+ cur_instr->format.bytes[1],
+ cur_instr->format.bytes[2],
+ cur_instr->format.bytes[3]);
+#else
+ cur_instr->format.bytes[3],
+ cur_instr->format.bytes[2],
+ cur_instr->format.bytes[1],
+ cur_instr->format.bytes[0]);
+#endif
+ instrcount++;
+ }
+ fprintf(ofile, "};\n\n");
+
+ /*
+ * Output patch information. Patch functions first.
+ */
+ for(cur_node = SLIST_FIRST(&patch_functions);
+ cur_node != NULL;
+ 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,
+"typedef int patch_func_t (struct ahc_softc *);
+struct patch {
+ patch_func_t *patch_func;
+ uint32_t begin :10,
+ skip_instr :10,
+ skip_patch :12;
+} patches[] = {\n");
+
+ for(cur_patch = STAILQ_FIRST(&patches);
+ cur_patch != NULL;
+ 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, "\n};\n");
+
+ fprintf(ofile,
+"struct cs {
+ u_int16_t begin;
+ u_int16_t end;
+} critical_sections[] = {\n");
+
+ for(cs = TAILQ_FIRST(&cs_tailq);
+ cs != NULL;
+ cs = TAILQ_NEXT(cs, links)) {
+ fprintf(ofile, "\t{ %d, %d },\n",
+ cs->begin_addr, cs->end_addr);
+ }
+
+ fprintf(ofile, "\n};\n");
+
+ fprintf(stderr, "%s: %d instructions used\n", appname, instrcount);
+}
+
+static void
+dump_scope(scope_t *scope)
+{
+ scope_t *cur_scope;
+
+ /*
+ * 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(char *ifilename)
+{
+ char buf[1024];
+ FILE *ifile;
+ struct instruction *cur_instr;
+ patch_t *cur_patch;
+ symbol_node_t *cur_func;
+ int *func_values;
+ int instrcount;
+ int instrptr;
+ int line;
+ int func_count;
+ int skip_addr;
+
+ instrcount = 0;
+ instrptr = 0;
+ line = 1;
+ skip_addr = 0;
+ if ((ifile = fopen(ifilename, "r")) == NULL) {
+ perror(ifilename);
+ stop(NULL, EX_DATAERR);
+ }
+
+ /*
+ * Determine which options to apply to this listing.
+ */
+ for (func_count = 0, cur_func = SLIST_FIRST(&patch_functions);
+ cur_func != NULL;
+ cur_func = SLIST_NEXT(cur_func, links))
+ func_count++;
+
+ func_values = NULL;
+ 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;
+ }
+ }
+ if (isatty(fileno(stdin)) == 0)
+ putchar(input);
+ }
+ fprintf(stdout, "\nThanks!\n");
+ }
+
+ /* Now output the listing */
+ cur_patch = STAILQ_FIRST(&patches);
+ for(cur_instr = STAILQ_FIRST(&seq_program);
+ cur_instr != NULL;
+ cur_instr = STAILQ_NEXT(cur_instr, links), 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);
+ fprintf(listfile, "\t\t%s", buf);
+ line++;
+ }
+ fprintf(listfile, "%03x %02x%02x%02x%02x", instrptr,
+#if BYTE_ORDER == LITTLE_ENDIAN
+ cur_instr->format.bytes[0],
+ cur_instr->format.bytes[1],
+ cur_instr->format.bytes[2],
+ cur_instr->format.bytes[3]);
+#else
+ cur_instr->format.bytes[3],
+ cur_instr->format.bytes[2],
+ cur_instr->format.bytes[1],
+ cur_instr->format.bytes[0]);
+#endif
+ fgets(buf, sizeof(buf), ifile);
+ fprintf(listfile, "\t%s", buf);
+ line++;
+ instrptr++;
+ }
+ /* Dump the remainder of the file */
+ while(fgets(buf, sizeof(buf), ifile) != NULL)
+ fprintf(listfile, "\t\t%s", buf);
+
+ fclose(ifile);
+}
+
+static int
+check_patch(patch_t **start_patch, int start_instr,
+ int *skip_addr, int *func_vals)
+{
+ 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 = STAILQ_NEXT(cur_patch, links);
+ }
+ }
+
+ *start_patch = cur_patch;
+ if (start_instr < *skip_addr)
+ /* Still skipping */
+ return (0);
+
+ return (1);
+}
+
+/*
+ * Print out error information if appropriate, and clean up before
+ * terminating the program.
+ */
+void
+stop(const char *string, int err_code)
+{
+ if (string != NULL) {
+ fprintf(stderr, "%s: ", appname);
+ if (yyfilename != NULL) {
+ fprintf(stderr, "Stopped at file %s, line %d - ",
+ yyfilename, yylineno);
+ }
+ fprintf(stderr, "%s\n", string);
+ }
+
+ if (ofile != NULL) {
+ fclose(ofile);
+ if (err_code != 0) {
+ fprintf(stderr, "%s: Removing %s due to error\n",
+ appname, ofilename);
+ unlink(ofilename);
+ }
+ }
+
+ if (regfile != NULL) {
+ fclose(regfile);
+ if (err_code != 0) {
+ fprintf(stderr, "%s: Removing %s due to error\n",
+ appname, regfilename);
+ unlink(regfilename);
+ }
+ }
+
+ if (listfile != NULL) {
+ fclose(listfile);
+ if (err_code != 0) {
+ fprintf(stderr, "%s: Removing %s due to error\n",
+ appname, listfilename);
+ unlink(listfilename);
+ }
+ }
+
+ symlist_free(&patch_functions);
+ symtable_close();
+
+ exit(err_code);
+}
+
+struct instruction *
+seq_alloc()
+{
+ struct instruction *new_instr;
+
+ new_instr = (struct instruction *)malloc(sizeof(struct instruction));
+ if (new_instr == NULL)
+ stop("Unable to malloc instruction object", EX_SOFTWARE);
+ memset(new_instr, 0, sizeof(*new_instr));
+ STAILQ_INSERT_TAIL(&seq_program, new_instr, links);
+ new_instr->srcline = yylineno;
+ return new_instr;
+}
+
+critical_section_t *
+cs_alloc()
+{
+ critical_section_t *new_cs;
+
+ new_cs= (critical_section_t *)malloc(sizeof(critical_section_t));
+ if (new_cs == NULL)
+ stop("Unable to malloc critical_section object", EX_SOFTWARE);
+ memset(new_cs, 0, sizeof(*new_cs));
+
+ TAILQ_INSERT_TAIL(&cs_tailq, new_cs, links);
+ return new_cs;
+}
+
+scope_t *
+scope_alloc()
+{
+ scope_t *new_scope;
+
+ 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;
+ case SCOPE_ROOT:
+ stop("Unexpected scope type encountered", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+
+ 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
new file mode 100644
index 000000000000..a3b30fe0e5b8
--- /dev/null
+++ b/sys/dev/aic7xxx/aicasm/aicasm.h
@@ -0,0 +1,78 @@
+/*
+ * Assembler for the sequencer program downloaded to Aic7xxx SCSI host adapters
+ *
+ * Copyright (c) 1997 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aicasm/aicasm.h#3 $
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+typedef struct path_entry {
+ char *directory;
+ int quoted_includes_only;
+ SLIST_ENTRY(path_entry) links;
+} *path_entry_t;
+
+typedef enum {
+ QUOTED_INCLUDE,
+ BRACKETED_INCLUDE,
+ SOURCE_FILE
+} include_type;
+
+SLIST_HEAD(path_list, path_entry);
+
+extern struct path_list search_path;
+extern struct cs_tailq cs_tailq;
+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(const char *errstring, int err_code);
+void include_file(char *file_name, include_type type);
+struct instruction *seq_alloc(void);
+struct critical_section *cs_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
new file mode 100644
index 000000000000..8ef77111d91a
--- /dev/null
+++ b/sys/dev/aic7xxx/aicasm/aicasm_gram.y
@@ -0,0 +1,1455 @@
+%{
+/*
+ * Parser for the Aic7xxx SCSI Host adapter sequencer assembler.
+ *
+ * Copyright (c) 1997, 1998, 2000 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aicasm/aicasm_gram.y#4 $
+ *
+ * $FreeBSD$
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include <sys/types.h>
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_insformat.h"
+
+int yylineno;
+char *yyfilename;
+static symbol_t *cur_symbol;
+static symtype cur_symtype;
+static symbol_t *accumulator;
+static symbol_ref_t allones;
+static symbol_ref_t allzeros;
+static symbol_ref_t none;
+static symbol_ref_t sindex;
+static int instruction_ptr;
+static int sram_or_scb_offset;
+static int download_constant_count;
+static int in_critical_section;
+
+static void process_bitmask(int mask_type, symbol_t *sym, int mask);
+static void initialize_symbol(symbol_t *symbol);
+static void process_register(symbol_t **p_symbol);
+static void format_1_instr(int opcode, symbol_ref_t *dest,
+ expression_t *immed, symbol_ref_t *src, int ret);
+static void format_2_instr(int opcode, symbol_ref_t *dest,
+ expression_t *places, symbol_ref_t *src, int ret);
+static void format_3_instr(int opcode, symbol_ref_t *src,
+ expression_t *immed, symbol_ref_t *address);
+static void test_readable_symbol(symbol_t *symbol);
+static void test_writable_symbol(symbol_t *symbol);
+static void type_check(symbol_t *symbol, expression_t *expression, int and_op);
+static void make_expression(expression_t *immed, int value);
+static void add_conditional(symbol_t *symbol);
+static int is_download_const(expression_t *immed);
+
+#define YYDEBUG 1
+#define SRAM_SYMNAME "SRAM_BASE"
+#define SCB_SYMNAME "SCB_BASE"
+%}
+
+%union {
+ int value;
+ char *str;
+ symbol_t *sym;
+ symbol_ref_t sym_ref;
+ expression_t expression;
+}
+
+%token T_REGISTER
+
+%token <value> T_CONST
+
+%token T_DOWNLOAD
+
+%token T_SCB
+
+%token T_SRAM
+
+%token T_ALIAS
+
+%token T_SIZE
+
+%token <value> T_ADDRESS
+
+%token T_ACCESS_MODE
+
+%token <value> T_MODE
+
+%token T_BEGIN_CS
+
+%token T_END_CS
+
+%token T_BIT
+
+%token T_MASK
+
+%token <value> T_NUMBER
+
+%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 T_BMOV
+
+%token <value> T_JMP T_JC T_JNC T_JE T_JNE T_JNZ T_JZ T_CALL
+
+%token <value> T_ADD T_ADC
+
+%token <value> T_INC T_DEC
+
+%token <value> T_STC T_CLC
+
+%token <value> T_CMP T_NOT T_XOR
+
+%token <value> T_TEST T_AND
+
+%token <value> T_OR
+
+%token T_RET
+
+%token T_NOP
+
+%token T_ACCUM T_ALLONES T_ALLZEROS T_NONE T_SINDEX
+
+%token T_A
+
+%token <sym> T_SYMBOL
+
+%token T_NL
+
+%token T_IF T_ELSE T_ELSE_IF T_ENDIF
+
+%type <sym_ref> reg_symbol address destination source opt_source
+
+%type <expression> expression immediate immediate_or_a
+
+%type <value> ret f1_opcode f2_opcode jmp_jc_jnc_call jz_jnz je_jne
+
+%type <value> numerical_value
+
+%left '|'
+%left '&'
+%left '+' '-'
+%right '~'
+%nonassoc UMINUS
+%%
+
+program:
+ include
+| program include
+| register
+| program register
+| constant
+| program constant
+| scratch_ram
+| program scratch_ram
+| scb
+| program scb
+| label
+| program label
+| critical_section_start
+| program critical_section_start
+| critical_section_end
+| program critical_section_end
+| conditional
+| program conditional
+| code
+| program code
+;
+
+include:
+ T_INCLUDE '<' T_PATH '>'
+ { include_file($3, BRACKETED_INCLUDE); }
+| T_INCLUDE '"' T_PATH '"'
+ { include_file($3, QUOTED_INCLUDE); }
+;
+
+register:
+ T_REGISTER { cur_symtype = REGISTER; } reg_definition
+;
+
+reg_definition:
+ T_SYMBOL '{'
+ {
+ if ($1->type != UNINITIALIZED) {
+ stop("Register multiply defined", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ cur_symbol = $1;
+ cur_symbol->type = cur_symtype;
+ initialize_symbol(cur_symbol);
+ }
+ reg_attribute_list
+ '}'
+ {
+ /*
+ * Default to allowing everything in for registers
+ * with no bit or mask definitions.
+ */
+ if (cur_symbol->info.rinfo->valid_bitmask == 0)
+ cur_symbol->info.rinfo->valid_bitmask = 0xFF;
+
+ if (cur_symbol->info.rinfo->size == 0)
+ cur_symbol->info.rinfo->size = 1;
+
+ /*
+ * This might be useful for registers too.
+ */
+ if (cur_symbol->type != REGISTER) {
+ if (cur_symbol->info.rinfo->address == 0)
+ cur_symbol->info.rinfo->address =
+ sram_or_scb_offset;
+ sram_or_scb_offset +=
+ cur_symbol->info.rinfo->size;
+ }
+ cur_symbol = NULL;
+ }
+;
+
+reg_attribute_list:
+ reg_attribute
+| reg_attribute_list reg_attribute
+;
+
+reg_attribute:
+ reg_address
+| size
+| access_mode
+| bit_defn
+| mask_defn
+| alias
+| accumulator
+| allones
+| allzeros
+| none
+| sindex
+;
+
+reg_address:
+ T_ADDRESS T_NUMBER
+ {
+ cur_symbol->info.rinfo->address = $2;
+ }
+;
+
+size:
+ T_SIZE T_NUMBER
+ {
+ cur_symbol->info.rinfo->size = $2;
+ }
+;
+
+access_mode:
+ T_ACCESS_MODE T_MODE
+ {
+ cur_symbol->info.rinfo->mode = $2;
+ }
+;
+
+bit_defn:
+ T_BIT T_SYMBOL T_NUMBER
+ {
+ process_bitmask(BIT, $2, $3);
+ }
+;
+
+mask_defn:
+ T_MASK T_SYMBOL expression
+ {
+ process_bitmask(MASK, $2, $3.value);
+ }
+;
+
+alias:
+ T_ALIAS T_SYMBOL
+ {
+ if ($2->type != UNINITIALIZED) {
+ stop("Re-definition of register alias",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $2->type = ALIAS;
+ initialize_symbol($2);
+ $2->info.ainfo->parent = cur_symbol;
+ }
+;
+
+accumulator:
+ T_ACCUM
+ {
+ if (accumulator != NULL) {
+ stop("Only one accumulator definition allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ accumulator = cur_symbol;
+ }
+;
+
+allones:
+ T_ALLONES
+ {
+ if (allones.symbol != NULL) {
+ stop("Only one definition of allones allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ allones.symbol = cur_symbol;
+ }
+;
+
+allzeros:
+ T_ALLZEROS
+ {
+ if (allzeros.symbol != NULL) {
+ stop("Only one definition of allzeros allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ allzeros.symbol = cur_symbol;
+ }
+;
+
+none:
+ T_NONE
+ {
+ if (none.symbol != NULL) {
+ stop("Only one definition of none allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ none.symbol = cur_symbol;
+ }
+;
+
+sindex:
+ T_SINDEX
+ {
+ if (sindex.symbol != NULL) {
+ stop("Only one definition of sindex allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ sindex.symbol = cur_symbol;
+ }
+;
+
+expression:
+ expression '|' expression
+ {
+ $$.value = $1.value | $3.value;
+ symlist_merge(&$$.referenced_syms,
+ &$1.referenced_syms,
+ &$3.referenced_syms);
+ }
+| expression '&' expression
+ {
+ $$.value = $1.value & $3.value;
+ symlist_merge(&$$.referenced_syms,
+ &$1.referenced_syms,
+ &$3.referenced_syms);
+ }
+| expression '+' expression
+ {
+ $$.value = $1.value + $3.value;
+ symlist_merge(&$$.referenced_syms,
+ &$1.referenced_syms,
+ &$3.referenced_syms);
+ }
+| expression '-' expression
+ {
+ $$.value = $1.value - $3.value;
+ symlist_merge(&($$.referenced_syms),
+ &($1.referenced_syms),
+ &($3.referenced_syms));
+ }
+| '(' expression ')'
+ {
+ $$ = $2;
+ }
+| '~' expression
+ {
+ $$ = $2;
+ $$.value = (~$$.value) & 0xFF;
+ }
+| '-' expression %prec UMINUS
+ {
+ $$ = $2;
+ $$.value = -$$.value;
+ }
+| T_NUMBER
+ {
+ $$.value = $1;
+ SLIST_INIT(&$$.referenced_syms);
+ }
+| T_SYMBOL
+ {
+ symbol_t *symbol;
+
+ symbol = $1;
+ switch (symbol->type) {
+ case ALIAS:
+ symbol = $1->info.ainfo->parent;
+ case REGISTER:
+ case SCBLOC:
+ case SRAMLOC:
+ $$.value = symbol->info.rinfo->address;
+ break;
+ case MASK:
+ case BIT:
+ $$.value = symbol->info.minfo->mask;
+ break;
+ case DOWNLOAD_CONST:
+ case CONST:
+ $$.value = symbol->info.cinfo->value;
+ break;
+ case UNINITIALIZED:
+ default:
+ {
+ char buf[255];
+
+ snprintf(buf, sizeof(buf),
+ "Undefined symbol %s referenced",
+ symbol->name);
+ stop(buf, EX_DATAERR);
+ /* NOTREACHED */
+ break;
+ }
+ }
+ SLIST_INIT(&$$.referenced_syms);
+ symlist_add(&$$.referenced_syms, symbol, SYMLIST_INSERT_HEAD);
+ }
+;
+
+constant:
+ T_CONST T_SYMBOL numerical_value
+ {
+ if ($2->type != UNINITIALIZED) {
+ stop("Re-definition of symbol as a constant",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $2->type = CONST;
+ initialize_symbol($2);
+ $2->info.cinfo->value = $3;
+ $2->info.cinfo->define = $1;
+ }
+| T_CONST T_SYMBOL T_DOWNLOAD
+ {
+ if ($1) {
+ stop("Invalid downloaded constant declaration",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ if ($2->type != UNINITIALIZED) {
+ stop("Re-definition of symbol as a downloaded constant",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $2->type = DOWNLOAD_CONST;
+ initialize_symbol($2);
+ $2->info.cinfo->value = download_constant_count++;
+ $2->info.cinfo->define = FALSE;
+ }
+;
+
+numerical_value:
+ T_NUMBER
+ {
+ $$ = $1;
+ }
+| '-' T_NUMBER
+ {
+ $$ = -$2;
+ }
+;
+
+scratch_ram:
+ T_SRAM '{'
+ {
+ cur_symbol = symtable_get(SRAM_SYMNAME);
+ cur_symtype = SRAMLOC;
+ if (cur_symbol->type != UNINITIALIZED) {
+ stop("Only one SRAM definition allowed",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ cur_symbol->type = SRAMLOC;
+ initialize_symbol(cur_symbol);
+ }
+ reg_address
+ {
+ sram_or_scb_offset = cur_symbol->info.rinfo->address;
+ }
+ scb_or_sram_reg_list
+ '}'
+ {
+ cur_symbol = NULL;
+ }
+;
+
+scb:
+ T_SCB '{'
+ {
+ cur_symbol = symtable_get(SCB_SYMNAME);
+ cur_symtype = SCBLOC;
+ if (cur_symbol->type != UNINITIALIZED) {
+ stop("Only one SRAM definition allowed",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ cur_symbol->type = SCBLOC;
+ initialize_symbol(cur_symbol);
+ /* 64 bytes of SCB space */
+ cur_symbol->info.rinfo->size = 64;
+ }
+ reg_address
+ {
+ sram_or_scb_offset = cur_symbol->info.rinfo->address;
+ }
+ scb_or_sram_reg_list
+ '}'
+ {
+ cur_symbol = NULL;
+ }
+;
+
+scb_or_sram_reg_list:
+ reg_definition
+| scb_or_sram_reg_list reg_definition
+;
+
+reg_symbol:
+ T_SYMBOL
+ {
+ process_register(&$1);
+ $$.symbol = $1;
+ $$.offset = 0;
+ }
+| T_SYMBOL '[' T_SYMBOL ']'
+ {
+ process_register(&$1);
+ if ($3->type != CONST) {
+ stop("register offset must be a constant", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ if (($3->info.cinfo->value + 1) > $1->info.rinfo->size) {
+ stop("Accessing offset beyond range of register",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $$.symbol = $1;
+ $$.offset = $3->info.cinfo->value;
+ }
+| T_SYMBOL '[' T_NUMBER ']'
+ {
+ process_register(&$1);
+ if (($3 + 1) > $1->info.rinfo->size) {
+ stop("Accessing offset beyond range of register",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $$.symbol = $1;
+ $$.offset = $3;
+ }
+| T_A
+ {
+ if (accumulator == NULL) {
+ stop("No accumulator has been defined", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $$.symbol = accumulator;
+ $$.offset = 0;
+ }
+;
+
+destination:
+ reg_symbol
+ {
+ test_writable_symbol($1.symbol);
+ $$ = $1;
+ }
+;
+
+immediate:
+ expression
+ { $$ = $1; }
+;
+
+immediate_or_a:
+ expression
+ {
+ $$ = $1;
+ }
+| T_A
+ {
+ SLIST_INIT(&$$.referenced_syms);
+ $$.value = 0;
+ }
+;
+
+source:
+ reg_symbol
+ {
+ test_readable_symbol($1.symbol);
+ $$ = $1;
+ }
+;
+
+opt_source:
+ {
+ $$.symbol = NULL;
+ $$.offset = 0;
+ }
+| ',' source
+ { $$ = $2; }
+;
+
+ret:
+ { $$ = 0; }
+| T_RET
+ { $$ = 1; }
+;
+
+critical_section_start:
+ T_BEGIN_CS
+ {
+ critical_section_t *cs;
+
+ if (in_critical_section != FALSE) {
+ stop("Critical Section within Critical Section",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ cs = cs_alloc();
+ cs->begin_addr = instruction_ptr;
+ in_critical_section = TRUE;
+ }
+
+critical_section_end:
+ T_END_CS
+ {
+ critical_section_t *cs;
+
+ if (in_critical_section == FALSE) {
+ stop("Unballanced 'end_cs'", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ cs = TAILQ_LAST(&cs_tailq, cs_tailq);
+ cs->end_addr = instruction_ptr;
+ in_critical_section = FALSE;
+ }
+
+label:
+ T_SYMBOL ':'
+ {
+ if ($1->type != UNINITIALIZED) {
+ stop("Program label multiply defined", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ $1->type = LABEL;
+ initialize_symbol($1);
+ $1->info.linfo->address = instruction_ptr;
+ }
+;
+
+address:
+ T_SYMBOL
+ {
+ $$.symbol = $1;
+ $$.offset = 0;
+ }
+| T_SYMBOL '+' T_NUMBER
+ {
+ $$.symbol = $1;
+ $$.offset = $3;
+ }
+| T_SYMBOL '-' T_NUMBER
+ {
+ $$.symbol = $1;
+ $$.offset = -$3;
+ }
+| '.'
+ {
+ $$.symbol = NULL;
+ $$.offset = 0;
+ }
+| '.' '+' T_NUMBER
+ {
+ $$.symbol = NULL;
+ $$.offset = $3;
+ }
+| '.' '-' T_NUMBER
+ {
+ $$.symbol = NULL;
+ $$.offset = -$3;
+ }
+;
+
+conditional:
+ T_IF T_CEXPR '{'
+ {
+ scope_t *new_scope;
+
+ 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 '{'
+ {
+ 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) {
+
+ 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 */
+ }
+ new_scope = scope_alloc();
+ new_scope->type = SCOPE_ELSE;
+ new_scope->begin_addr = instruction_ptr;
+ }
+;
+
+conditional:
+ '}'
+ {
+ scope_t *scope_context;
+
+ scope_context = SLIST_FIRST(&scope_stack);
+ if (scope_context->type == SCOPE_ROOT) {
+ stop("Unexpected '}' encountered", EX_DATAERR);
+ /* NOTREACHED */
+ }
+
+ scope_context->end_addr = instruction_ptr;
+
+ /* 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 */
+ }
+ }
+;
+
+f1_opcode:
+ T_AND { $$ = AIC_OP_AND; }
+| T_XOR { $$ = AIC_OP_XOR; }
+| T_ADD { $$ = AIC_OP_ADD; }
+| T_ADC { $$ = AIC_OP_ADC; }
+;
+
+code:
+ f1_opcode destination ',' immediate_or_a opt_source ret ';'
+ {
+ format_1_instr($1, &$2, &$4, &$5, $6);
+ }
+;
+
+code:
+ T_OR reg_symbol ',' immediate_or_a opt_source ret ';'
+ {
+ format_1_instr(AIC_OP_OR, &$2, &$4, &$5, $6);
+ }
+;
+
+code:
+ T_INC destination opt_source ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 1);
+ format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4);
+ }
+;
+
+code:
+ T_DEC destination opt_source ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, -1);
+ format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4);
+ }
+;
+
+code:
+ T_CLC ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, -1);
+ format_1_instr(AIC_OP_ADD, &none, &immed, &allzeros, $2);
+ }
+| T_CLC T_MVI destination ',' immediate_or_a ret ';'
+ {
+ format_1_instr(AIC_OP_ADD, &$3, &$5, &allzeros, $6);
+ }
+;
+
+code:
+ T_STC ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 1);
+ format_1_instr(AIC_OP_ADD, &none, &immed, &allones, $2);
+ }
+| T_STC destination ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 1);
+ format_1_instr(AIC_OP_ADD, &$2, &immed, &allones, $3);
+ }
+;
+
+code:
+ T_BMOV destination ',' source ',' immediate_or_a ret ';'
+ {
+ format_1_instr(AIC_OP_BMOV, &$2, &$6, &$4, $7);
+ }
+;
+
+code:
+ T_MOV destination ',' source ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0xff);
+ format_1_instr(AIC_OP_AND, &$2, &immed, &$4, $5);
+ }
+;
+
+code:
+ T_MVI destination ',' immediate_or_a ret ';'
+ {
+ format_1_instr(AIC_OP_OR, &$2, &$4, &allzeros, $5);
+ }
+;
+
+code:
+ T_NOT destination opt_source ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0xff);
+ format_1_instr(AIC_OP_XOR, &$2, &immed, &$3, $4);
+ }
+;
+
+code:
+ T_CLR destination ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0xff);
+ format_1_instr(AIC_OP_AND, &$2, &immed, &allzeros, $3);
+ }
+;
+
+code:
+ T_NOP ret ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0xff);
+ format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, $2);
+ }
+;
+
+code:
+ T_RET ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0xff);
+ format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, TRUE);
+ }
+;
+
+ /*
+ * This grammer differs from the one in the aic7xxx
+ * reference manual since the grammer listed there is
+ * ambiguous and causes a shift/reduce conflict.
+ * It also seems more logical as the "immediate"
+ * argument is listed as the second arg like the
+ * other formats.
+ */
+
+f2_opcode:
+ T_SHL { $$ = AIC_OP_SHL; }
+| T_SHR { $$ = AIC_OP_SHR; }
+| T_ROL { $$ = AIC_OP_ROL; }
+| T_ROR { $$ = AIC_OP_ROR; }
+;
+
+code:
+ f2_opcode destination ',' expression opt_source ret ';'
+ {
+ format_2_instr($1, &$2, &$4, &$5, $6);
+ }
+;
+
+jmp_jc_jnc_call:
+ T_JMP { $$ = AIC_OP_JMP; }
+| T_JC { $$ = AIC_OP_JC; }
+| T_JNC { $$ = AIC_OP_JNC; }
+| T_CALL { $$ = AIC_OP_CALL; }
+;
+
+jz_jnz:
+ T_JZ { $$ = AIC_OP_JZ; }
+| T_JNZ { $$ = AIC_OP_JNZ; }
+;
+
+je_jne:
+ T_JE { $$ = AIC_OP_JE; }
+| T_JNE { $$ = AIC_OP_JNE; }
+;
+
+code:
+ jmp_jc_jnc_call address ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0);
+ format_3_instr($1, &sindex, &immed, &$2);
+ }
+;
+
+code:
+ T_OR reg_symbol ',' immediate jmp_jc_jnc_call address ';'
+ {
+ format_3_instr($5, &$2, &$4, &$6);
+ }
+;
+
+code:
+ T_TEST source ',' immediate_or_a jz_jnz address ';'
+ {
+ format_3_instr($5, &$2, &$4, &$6);
+ }
+;
+
+code:
+ T_CMP source ',' immediate_or_a je_jne address ';'
+ {
+ format_3_instr($5, &$2, &$4, &$6);
+ }
+;
+
+code:
+ T_MOV source jmp_jc_jnc_call address ';'
+ {
+ expression_t immed;
+
+ make_expression(&immed, 0);
+ format_3_instr($3, &$2, &immed, &$4);
+ }
+;
+
+code:
+ T_MVI immediate jmp_jc_jnc_call address ';'
+ {
+ format_3_instr($3, &allzeros, &$2, &$4);
+ }
+;
+
+%%
+
+static void
+process_bitmask(int mask_type, symbol_t *sym, int mask)
+{
+ /*
+ * Add the current register to its
+ * symbol list, if it already exists,
+ * warn if we are setting it to a
+ * different value, or in the bit to
+ * the "allowed bits" of this register.
+ */
+ if (sym->type == UNINITIALIZED) {
+ sym->type = mask_type;
+ initialize_symbol(sym);
+ if (mask_type == BIT) {
+ if (mask == 0) {
+ stop("Bitmask with no bits set", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ if ((mask & ~(0x01 << (ffs(mask) - 1))) != 0) {
+ stop("Bitmask with more than one bit set",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+ }
+ sym->info.minfo->mask = mask;
+ } else if (sym->type != mask_type) {
+ stop("Bit definition mirrors a definition of the same "
+ " name, but a different type", EX_DATAERR);
+ /* NOTREACHED */
+ } else if (mask != sym->info.minfo->mask) {
+ stop("Bitmask redefined with a conflicting value", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ /* Fail if this symbol is already listed */
+ if (symlist_search(&(sym->info.minfo->symrefs),
+ cur_symbol->name) != NULL) {
+ stop("Bitmask defined multiple times for register", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ symlist_add(&(sym->info.minfo->symrefs), cur_symbol,
+ SYMLIST_INSERT_HEAD);
+ cur_symbol->info.rinfo->valid_bitmask |= mask;
+ cur_symbol->info.rinfo->typecheck_masks = TRUE;
+}
+
+static void
+initialize_symbol(symbol_t *symbol)
+{
+ switch (symbol->type) {
+ case UNINITIALIZED:
+ stop("Call to initialize_symbol with type field unset",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ break;
+ case REGISTER:
+ case SRAMLOC:
+ case SCBLOC:
+ symbol->info.rinfo =
+ (struct reg_info *)malloc(sizeof(struct reg_info));
+ if (symbol->info.rinfo == NULL) {
+ stop("Can't create register info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.rinfo, 0,
+ sizeof(struct reg_info));
+ break;
+ case ALIAS:
+ symbol->info.ainfo =
+ (struct alias_info *)malloc(sizeof(struct alias_info));
+ if (symbol->info.ainfo == NULL) {
+ stop("Can't create alias info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.ainfo, 0,
+ sizeof(struct alias_info));
+ break;
+ case MASK:
+ case BIT:
+ symbol->info.minfo =
+ (struct mask_info *)malloc(sizeof(struct mask_info));
+ if (symbol->info.minfo == NULL) {
+ stop("Can't create bitmask info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.minfo, 0, sizeof(struct mask_info));
+ SLIST_INIT(&(symbol->info.minfo->symrefs));
+ break;
+ case CONST:
+ case DOWNLOAD_CONST:
+ symbol->info.cinfo =
+ (struct const_info *)malloc(sizeof(struct const_info));
+ if (symbol->info.cinfo == NULL) {
+ stop("Can't create alias info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.cinfo, 0,
+ sizeof(struct const_info));
+ break;
+ case LABEL:
+ symbol->info.linfo =
+ (struct label_info *)malloc(sizeof(struct label_info));
+ if (symbol->info.linfo == NULL) {
+ stop("Can't create label info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.linfo, 0,
+ sizeof(struct label_info));
+ break;
+ case CONDITIONAL:
+ symbol->info.condinfo =
+ (struct cond_info *)malloc(sizeof(struct cond_info));
+ if (symbol->info.condinfo == NULL) {
+ stop("Can't create conditional info", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ memset(symbol->info.condinfo, 0,
+ sizeof(struct cond_info));
+ break;
+ default:
+ stop("Call to initialize_symbol with invalid symbol type",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ break;
+ }
+}
+
+static void
+process_register(symbol_t **p_symbol)
+{
+ char buf[255];
+ symbol_t *symbol = *p_symbol;
+
+ if (symbol->type == UNINITIALIZED) {
+ snprintf(buf, sizeof(buf), "Undefined register %s",
+ symbol->name);
+ stop(buf, EX_DATAERR);
+ /* NOTREACHED */
+ } else if (symbol->type == ALIAS) {
+ *p_symbol = symbol->info.ainfo->parent;
+ } else if ((symbol->type != REGISTER)
+ && (symbol->type != SCBLOC)
+ && (symbol->type != SRAMLOC)) {
+ snprintf(buf, sizeof(buf),
+ "Specified symbol %s is not a register",
+ symbol->name);
+ stop(buf, EX_DATAERR);
+ }
+}
+
+static void
+format_1_instr(int opcode, symbol_ref_t *dest, expression_t *immed,
+ symbol_ref_t *src, int ret)
+{
+ struct instruction *instr;
+ struct ins_format1 *f1_instr;
+
+ if (src->symbol == NULL)
+ src = dest;
+
+ /* Test register permissions */
+ test_writable_symbol(dest->symbol);
+ test_readable_symbol(src->symbol);
+
+ /* Ensure that immediate makes sense for this destination */
+ type_check(dest->symbol, immed, opcode);
+
+ /* Allocate sequencer space for the instruction and fill it out */
+ instr = seq_alloc();
+ f1_instr = &instr->format.format1;
+ 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
+ + src->offset;
+ f1_instr->immediate = immed->value;
+
+ if (is_download_const(immed))
+ f1_instr->parity = 1;
+
+ symlist_free(&immed->referenced_syms);
+ instruction_ptr++;
+}
+
+static void
+format_2_instr(int opcode, symbol_ref_t *dest, expression_t *places,
+ symbol_ref_t *src, int ret)
+{
+ struct instruction *instr;
+ struct ins_format2 *f2_instr;
+ uint8_t shift_control;
+
+ if (src->symbol == NULL)
+ src = dest;
+
+ /* Test register permissions */
+ test_writable_symbol(dest->symbol);
+ test_readable_symbol(src->symbol);
+
+ /* Allocate sequencer space for the instruction and fill it out */
+ instr = seq_alloc();
+ f2_instr = &instr->format.format2;
+ 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
+ + src->offset;
+ if (places->value > 8 || places->value <= 0) {
+ stop("illegal shift value", EX_DATAERR);
+ /* NOTREACHED */
+ }
+ switch (opcode) {
+ case AIC_OP_SHL:
+ if (places->value == 8)
+ shift_control = 0xf0;
+ else
+ shift_control = (places->value << 4) | places->value;
+ break;
+ case AIC_OP_SHR:
+ if (places->value == 8) {
+ shift_control = 0xf8;
+ } else {
+ shift_control = (places->value << 4)
+ | (8 - places->value)
+ | 0x08;
+ }
+ break;
+ case AIC_OP_ROL:
+ shift_control = places->value & 0x7;
+ break;
+ case AIC_OP_ROR:
+ shift_control = (8 - places->value) | 0x08;
+ break;
+ default:
+ shift_control = 0; /* Quiet Compiler */
+ stop("Invalid shift operation specified", EX_SOFTWARE);
+ /* NOTREACHED */
+ break;
+ };
+ f2_instr->shift_control = shift_control;
+ symlist_free(&places->referenced_syms);
+ instruction_ptr++;
+}
+
+static void
+format_3_instr(int opcode, symbol_ref_t *src,
+ expression_t *immed, symbol_ref_t *address)
+{
+ struct instruction *instr;
+ struct ins_format3 *f3_instr;
+ int addr;
+
+ /* Test register permissions */
+ test_readable_symbol(src->symbol);
+
+ /* Ensure that immediate makes sense for this source */
+ type_check(src->symbol, immed, opcode);
+
+ /* Allocate sequencer space for the instruction and fill it out */
+ instr = seq_alloc();
+ f3_instr = &instr->format.format3;
+ if (address->symbol == NULL) {
+ /* 'dot' referrence. Use the current instruction pointer */
+ addr = instruction_ptr + address->offset;
+ } else if (address->symbol->type == UNINITIALIZED) {
+ /* forward reference */
+ addr = address->offset;
+ instr->patch_label = address->symbol;
+ } else
+ addr = address->symbol->info.linfo->address + address->offset;
+ 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->parity = 1;
+
+ symlist_free(&immed->referenced_syms);
+ instruction_ptr++;
+}
+
+static void
+test_readable_symbol(symbol_t *symbol)
+{
+ if (symbol->info.rinfo->mode == WO) {
+ stop("Write Only register specified as source",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+}
+
+static void
+test_writable_symbol(symbol_t *symbol)
+{
+ if (symbol->info.rinfo->mode == RO) {
+ stop("Read Only register specified as destination",
+ EX_DATAERR);
+ /* NOTREACHED */
+ }
+}
+
+static void
+type_check(symbol_t *symbol, expression_t *expression, int opcode)
+{
+ symbol_node_t *node;
+ int and_op;
+ char buf[255];
+
+ and_op = FALSE;
+ if (opcode == AIC_OP_AND || opcode == AIC_OP_JNZ || AIC_OP_JZ)
+ and_op = TRUE;
+
+ /*
+ * Make sure that we aren't attempting to write something
+ * that hasn't been defined. If this is an and operation,
+ * this is a mask, so "undefined" bits are okay.
+ */
+ if (and_op == FALSE
+ && (expression->value & ~symbol->info.rinfo->valid_bitmask) != 0) {
+ snprintf(buf, sizeof(buf),
+ "Invalid bit(s) 0x%x in immediate written to %s",
+ expression->value & ~symbol->info.rinfo->valid_bitmask,
+ symbol->name);
+ stop(buf, EX_DATAERR);
+ /* NOTREACHED */
+ }
+
+ /*
+ * Now make sure that all of the symbols referenced by the
+ * expression are defined for this register.
+ */
+ if(symbol->info.rinfo->typecheck_masks != FALSE) {
+ for(node = expression->referenced_syms.slh_first;
+ node != NULL;
+ node = node->links.sle_next) {
+ if ((node->symbol->type == MASK
+ || node->symbol->type == BIT)
+ && symlist_search(&node->symbol->info.minfo->symrefs,
+ symbol->name) == NULL) {
+ snprintf(buf, sizeof(buf),
+ "Invalid bit or mask %s "
+ "for register %s",
+ node->symbol->name, symbol->name);
+ stop(buf, EX_DATAERR);
+ /* NOTREACHED */
+ }
+ }
+ }
+}
+
+static void
+make_expression(expression_t *immed, int value)
+{
+ SLIST_INIT(&immed->referenced_syms);
+ immed->value = value & 0xff;
+}
+
+static void
+add_conditional(symbol_t *symbol)
+{
+ static int numfuncs;
+
+ 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 */
+ }
+
+ symbol->type = CONDITIONAL;
+ initialize_symbol(symbol);
+ symbol->info.condinfo->func_num = numfuncs++;
+ symlist_add(&patch_functions, symbol, SYMLIST_INSERT_HEAD);
+}
+
+void
+yyerror(const char *string)
+{
+ stop(string, EX_DATAERR);
+}
+
+static int
+is_download_const(expression_t *immed)
+{
+ if ((immed->referenced_syms.slh_first != NULL)
+ && (immed->referenced_syms.slh_first->symbol->type == DOWNLOAD_CONST))
+ return (TRUE);
+
+ return (FALSE);
+}
diff --git a/sys/dev/aic7xxx/aicasm/aicasm_insformat.h b/sys/dev/aic7xxx/aicasm/aicasm_insformat.h
new file mode 100644
index 000000000000..7c6026090a94
--- /dev/null
+++ b/sys/dev/aic7xxx/aicasm/aicasm_insformat.h
@@ -0,0 +1,129 @@
+/*
+ * Instruction formats for the sequencer program downloaded to
+ * Aic7xxx SCSI host adapters
+ *
+ * Copyright (c) 1997, 1998, 2000 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aicasm/aicasm_insformat.h#3 $
+ *
+ * $FreeBSD$
+ */
+
+#if linux
+#include <endian.h>
+#else
+#include <machine/endian.h>
+#endif
+
+struct ins_format1 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint32_t immediate : 8,
+ source : 9,
+ destination : 9,
+ ret : 1,
+ opcode : 4,
+ parity : 1;
+#else
+ uint32_t parity : 1,
+ opcode : 4,
+ ret : 1,
+ destination : 9,
+ source : 9,
+ immediate : 8;
+#endif
+};
+
+struct ins_format2 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint32_t shift_control : 8,
+ source : 9,
+ destination : 9,
+ ret : 1,
+ opcode : 4,
+ parity : 1;
+#else
+ uint32_t parity : 1,
+ opcode : 4,
+ ret : 1,
+ destination : 9,
+ source : 9,
+ shift_control : 8;
+#endif
+};
+
+struct ins_format3 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint32_t immediate : 8,
+ source : 9,
+ address : 10,
+ opcode : 4,
+ parity : 1;
+#else
+ uint32_t parity : 1,
+ opcode : 4,
+ address : 10,
+ source : 9,
+ immediate : 8;
+#endif
+};
+
+union ins_formats {
+ struct ins_format1 format1;
+ struct ins_format2 format2;
+ struct ins_format3 format3;
+ uint8_t bytes[4];
+ uint32_t integer;
+};
+struct instruction {
+ union ins_formats format;
+ u_int srcline;
+ struct symbol *patch_label;
+ STAILQ_ENTRY(instruction) links;
+};
+
+#define AIC_OP_OR 0x0
+#define AIC_OP_AND 0x1
+#define AIC_OP_XOR 0x2
+#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
+#define AIC_OP_JNC 0xa
+#define AIC_OP_CALL 0xb
+#define AIC_OP_JNE 0xc
+#define AIC_OP_JNZ 0xd
+#define AIC_OP_JE 0xe
+#define AIC_OP_JZ 0xf
+
+/* Pseudo Ops */
+#define AIC_OP_SHL 0x10
+#define AIC_OP_SHR 0x20
+#define AIC_OP_ROR 0x30
diff --git a/sys/dev/aic7xxx/aicasm/aicasm_scan.l b/sys/dev/aic7xxx/aicasm/aicasm_scan.l
new file mode 100644
index 000000000000..5709d6ff68d6
--- /dev/null
+++ b/sys/dev/aic7xxx/aicasm/aicasm_scan.l
@@ -0,0 +1,302 @@
+%{
+/*
+ * Lexical Analyzer for the Aic7xxx SCSI Host adapter sequencer assembler.
+ *
+ * Copyright (c) 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aicasm/aicasm_scan.l#3 $
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#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_.]*
+WORD [A-Za-z_][-A-Za-z_0-9]*
+SPACE [ \t]+
+
+%x COMMENT
+%x CEXPR
+%x INCLUDE
+
+%%
+\n { ++yylineno; }
+"/*" { BEGIN COMMENT; /* Enter comment eating state */ }
+<COMMENT>"/*" { fprintf(stderr, "Warning! Comment within comment."); }
+<COMMENT>\n { ++yylineno; }
+<COMMENT>[^*/\n]* ;
+<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') {
+ /* Remove duplicate spaces */
+ if (*yptr == '\t')
+ *yptr = ' ';
+ if (*yptr == ' '
+ && string_buf_ptr != string_buf
+ && string_buf_ptr[-1] == ' ')
+ yptr++;
+ else
+ *string_buf_ptr++ = *yptr++;
+ }
+ }
+
+{SPACE} ;
+
+ /* Register/SCB/SRAM definition keywords */
+register { return T_REGISTER; }
+const { yylval.value = FALSE; return T_CONST; }
+download { return T_DOWNLOAD; }
+address { return T_ADDRESS; }
+access_mode { return T_ACCESS_MODE; }
+RW|RO|WO {
+ if (strcmp(yytext, "RW") == 0)
+ yylval.value = RW;
+ else if (strcmp(yytext, "RO") == 0)
+ yylval.value = RO;
+ else
+ yylval.value = WO;
+ return T_MODE;
+ }
+BEGIN_CRITICAL { return T_BEGIN_CS; }
+END_CRITICAL { return T_END_CS; }
+bit { return T_BIT; }
+mask { return T_MASK; }
+alias { return T_ALIAS; }
+size { return T_SIZE; }
+scb { return T_SCB; }
+scratch_ram { return T_SRAM; }
+accumulator { return T_ACCUM; }
+allones { return T_ALLONES; }
+allzeros { return T_ALLZEROS; }
+none { return T_NONE; }
+sindex { return T_SINDEX; }
+A { return T_A; }
+
+ /* Opcodes */
+shl { return T_SHL; }
+shr { return T_SHR; }
+ror { return T_ROR; }
+rol { return T_ROL; }
+mvi { return T_MVI; }
+mov { return T_MOV; }
+clr { return T_CLR; }
+jmp { return T_JMP; }
+jc { return T_JC; }
+jnc { return T_JNC; }
+je { return T_JE; }
+jne { return T_JNE; }
+jz { return T_JZ; }
+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; }
+clc { return T_CLC; }
+cmp { return T_CMP; }
+not { return T_NOT; }
+xor { return T_XOR; }
+test { return T_TEST;}
+and { return T_AND; }
+or { return T_OR; }
+ret { return T_RET; }
+nop { return T_NOP; }
+else { return T_ELSE; }
+
+ /* Allowed Symbols */
+[-+,:()~|&."{};<>[\]!] { return yytext[0]; }
+
+ /* Number processing */
+0[0-7]* {
+ yylval.value = strtol(yytext, NULL, 8);
+ return T_NUMBER;
+ }
+
+0[xX][0-9a-fA-F]+ {
+ yylval.value = strtoul(yytext + 2, NULL, 16);
+ return T_NUMBER;
+ }
+
+[1-9][0-9]* {
+ yylval.value = strtol(yytext, NULL, 10);
+ return T_NUMBER;
+ }
+
+ /* Include Files */
+#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; }
+ /* Throw away macros */
+#define[^\n]*[()]+[^\n]* ;
+{PATH} { yylval.str = strdup(yytext); return T_PATH; }
+
+{WORD} { yylval.sym = symtable_get(yytext); return T_SYMBOL; }
+
+. {
+ char buf[255];
+
+ snprintf(buf, sizeof(buf), "Invalid character "
+ "'%c'", yytext[0]);
+ stop(buf, EX_DATAERR);
+ }
+%%
+
+typedef struct include {
+ YY_BUFFER_STATE buffer;
+ int lineno;
+ char *filename;
+ SLIST_ENTRY(include) links;
+}include_t;
+
+SLIST_HEAD(, include) include_stack;
+
+void
+include_file(char *file_name, include_type type)
+{
+ FILE *newfile;
+ include_t *include;
+
+ newfile = NULL;
+ /* Try the current directory first */
+ if (includes_search_curdir != 0 || type == SOURCE_FILE)
+ newfile = fopen(file_name, "r");
+
+ if (newfile == NULL && type != SOURCE_FILE) {
+ path_entry_t include_dir;
+ for (include_dir = search_path.slh_first;
+ include_dir != NULL;
+ include_dir = include_dir->links.sle_next) {
+ char fullname[PATH_MAX];
+
+ if ((include_dir->quoted_includes_only == TRUE)
+ && (type != QUOTED_INCLUDE))
+ continue;
+
+ snprintf(fullname, sizeof(fullname),
+ "%s/%s", include_dir->directory, file_name);
+
+ if ((newfile = fopen(fullname, "r")) != NULL)
+ break;
+ }
+ }
+
+ if (newfile == NULL) {
+ perror(file_name);
+ stop("Unable to open input file", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+
+ if (type != SOURCE_FILE) {
+ include = (include_t *)malloc(sizeof(include_t));
+ if (include == NULL) {
+ stop("Unable to allocate include stack entry",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ include->buffer = YY_CURRENT_BUFFER;
+ include->lineno = yylineno;
+ include->filename = yyfilename;
+ SLIST_INSERT_HEAD(&include_stack, include, links);
+ }
+ yy_switch_to_buffer(yy_create_buffer(newfile, YY_BUF_SIZE));
+ yylineno = 1;
+ yyfilename = strdup(file_name);
+}
+
+int
+yywrap()
+{
+ include_t *include;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER);
+ (void)fclose(yyin);
+ if (yyfilename != NULL)
+ free(yyfilename);
+ yyfilename = NULL;
+ include = include_stack.slh_first;
+ if (include != NULL) {
+ yy_switch_to_buffer(include->buffer);
+ yylineno = include->lineno;
+ yyfilename = include->filename;
+ SLIST_REMOVE_HEAD(&include_stack, links);
+ free(include);
+ return (0);
+ }
+ return (1);
+}
diff --git a/sys/dev/aic7xxx/aicasm/aicasm_symbol.c b/sys/dev/aic7xxx/aicasm/aicasm_symbol.c
new file mode 100644
index 000000000000..1a238507ba7a
--- /dev/null
+++ b/sys/dev/aic7xxx/aicasm/aicasm_symbol.c
@@ -0,0 +1,468 @@
+/*
+ * Aic7xxx SCSI host adapter firmware asssembler symbol table implementation
+ *
+ * Copyright (c) 1997 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aicasm/aicasm_symbol.c#3 $
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#ifdef __linux__
+#include <db1/db.h>
+#else
+#include <db.h>
+#endif
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include "aicasm_symbol.h"
+#include "aicasm.h"
+
+static DB *symtable;
+
+symbol_t *
+symbol_create(char *name)
+{
+ symbol_t *new_symbol;
+
+ new_symbol = (symbol_t *)malloc(sizeof(symbol_t));
+ if (new_symbol == NULL) {
+ perror("Unable to create new symbol");
+ exit(EX_SOFTWARE);
+ }
+ memset(new_symbol, 0, sizeof(*new_symbol));
+ new_symbol->name = strdup(name);
+ new_symbol->type = UNINITIALIZED;
+ return (new_symbol);
+}
+
+void
+symbol_delete(symbol_t *symbol)
+{
+ if (symtable != NULL) {
+ DBT key;
+
+ key.data = symbol->name;
+ key.size = strlen(symbol->name);
+ symtable->del(symtable, &key, /*flags*/0);
+ }
+ switch(symbol->type) {
+ case SCBLOC:
+ case SRAMLOC:
+ case REGISTER:
+ if (symbol->info.rinfo != NULL)
+ free(symbol->info.rinfo);
+ break;
+ case ALIAS:
+ if (symbol->info.ainfo != NULL)
+ free(symbol->info.ainfo);
+ break;
+ case MASK:
+ case BIT:
+ if (symbol->info.minfo != NULL) {
+ symlist_free(&symbol->info.minfo->symrefs);
+ free(symbol->info.minfo);
+ }
+ break;
+ case DOWNLOAD_CONST:
+ case CONST:
+ if (symbol->info.cinfo != NULL)
+ free(symbol->info.cinfo);
+ break;
+ case LABEL:
+ if (symbol->info.linfo != NULL)
+ free(symbol->info.linfo);
+ break;
+ case UNINITIALIZED:
+ default:
+ break;
+ }
+ free(symbol->name);
+ free(symbol);
+}
+
+void
+symtable_open()
+{
+ symtable = dbopen(/*filename*/NULL,
+ O_CREAT | O_NONBLOCK | O_RDWR, /*mode*/0, DB_HASH,
+ /*openinfo*/NULL);
+
+ if (symtable == NULL) {
+ perror("Symbol table creation failed");
+ exit(EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+}
+
+void
+symtable_close()
+{
+ if (symtable != NULL) {
+ DBT key;
+ DBT data;
+
+ while (symtable->seq(symtable, &key, &data, R_FIRST) == 0) {
+ symbol_t *stored_ptr;
+
+ memcpy(&stored_ptr, data.data, sizeof(stored_ptr));
+ symbol_delete(stored_ptr);
+ }
+ symtable->close(symtable);
+ }
+}
+
+/*
+ * The semantics of get is to return an uninitialized symbol entry
+ * if a lookup fails.
+ */
+symbol_t *
+symtable_get(char *name)
+{
+ symbol_t *stored_ptr;
+ DBT key;
+ DBT data;
+ int retval;
+
+ key.data = (void *)name;
+ key.size = strlen(name);
+
+ if ((retval = symtable->get(symtable, &key, &data, /*flags*/0)) != 0) {
+ if (retval == -1) {
+ perror("Symbol table get operation failed");
+ exit(EX_SOFTWARE);
+ /* NOTREACHED */
+ } else if (retval == 1) {
+ /* Symbol wasn't found, so create a new one */
+ symbol_t *new_symbol;
+
+ new_symbol = symbol_create(name);
+ data.data = &new_symbol;
+ data.size = sizeof(new_symbol);
+ if (symtable->put(symtable, &key, &data,
+ /*flags*/0) !=0) {
+ perror("Symtable put failed");
+ exit(EX_SOFTWARE);
+ }
+ return (new_symbol);
+ } else {
+ perror("Unexpected return value from db get routine");
+ exit(EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ }
+ memcpy(&stored_ptr, data.data, sizeof(stored_ptr));
+ return (stored_ptr);
+}
+
+symbol_node_t *
+symlist_search(symlist_t *symlist, char *symname)
+{
+ symbol_node_t *curnode;
+
+ curnode = symlist->slh_first;
+ while(curnode != NULL) {
+ if (strcmp(symname, curnode->symbol->name) == 0)
+ break;
+ curnode = curnode->links.sle_next;
+ }
+ return (curnode);
+}
+
+void
+symlist_add(symlist_t *symlist, symbol_t *symbol, int how)
+{
+ symbol_node_t *newnode;
+
+ newnode = (symbol_node_t *)malloc(sizeof(symbol_node_t));
+ if (newnode == NULL) {
+ stop("symlist_add: Unable to malloc symbol_node", EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+ newnode->symbol = symbol;
+ if (how == SYMLIST_SORT) {
+ symbol_node_t *curnode;
+ int mask;
+
+ mask = FALSE;
+ switch(symbol->type) {
+ case REGISTER:
+ case SCBLOC:
+ case SRAMLOC:
+ break;
+ case BIT:
+ case MASK:
+ mask = TRUE;
+ break;
+ default:
+ stop("symlist_add: Invalid symbol type for sorting",
+ EX_SOFTWARE);
+ /* NOTREACHED */
+ }
+
+ curnode = symlist->slh_first;
+ if (curnode == NULL
+ || (mask && (curnode->symbol->info.minfo->mask >
+ newnode->symbol->info.minfo->mask))
+ || (!mask && (curnode->symbol->info.rinfo->address >
+ newnode->symbol->info.rinfo->address))) {
+ SLIST_INSERT_HEAD(symlist, newnode, links);
+ return;
+ }
+
+ while (1) {
+ if (curnode->links.sle_next == NULL) {
+ SLIST_INSERT_AFTER(curnode, newnode,
+ links);
+ break;
+ } else {
+ symbol_t *cursymbol;
+
+ cursymbol = curnode->links.sle_next->symbol;
+ if ((mask && (cursymbol->info.minfo->mask >
+ symbol->info.minfo->mask))
+ || (!mask &&(cursymbol->info.rinfo->address >
+ symbol->info.rinfo->address))){
+ SLIST_INSERT_AFTER(curnode, newnode,
+ links);
+ break;
+ }
+ }
+ curnode = curnode->links.sle_next;
+ }
+ } else {
+ SLIST_INSERT_HEAD(symlist, newnode, links);
+ }
+}
+
+void
+symlist_free(symlist_t *symlist)
+{
+ symbol_node_t *node1, *node2;
+
+ node1 = symlist->slh_first;
+ while (node1 != NULL) {
+ node2 = node1->links.sle_next;
+ free(node1);
+ node1 = node2;
+ }
+ SLIST_INIT(symlist);
+}
+
+void
+symlist_merge(symlist_t *symlist_dest, symlist_t *symlist_src1,
+ symlist_t *symlist_src2)
+{
+ symbol_node_t *node;
+
+ *symlist_dest = *symlist_src1;
+ while((node = symlist_src2->slh_first) != NULL) {
+ SLIST_REMOVE_HEAD(symlist_src2, links);
+ SLIST_INSERT_HEAD(symlist_dest, node, links);
+ }
+
+ /* These are now empty */
+ SLIST_INIT(symlist_src1);
+ SLIST_INIT(symlist_src2);
+}
+
+void
+symtable_dump(FILE *ofile)
+{
+ /*
+ * Sort the registers by address with a simple insertion sort.
+ * Put bitmasks next to the first register that defines them.
+ * Put constants at the end.
+ */
+ symlist_t registers;
+ symlist_t masks;
+ symlist_t constants;
+ symlist_t download_constants;
+ symlist_t aliases;
+
+ SLIST_INIT(&registers);
+ SLIST_INIT(&masks);
+ SLIST_INIT(&constants);
+ SLIST_INIT(&download_constants);
+ SLIST_INIT(&aliases);
+
+ if (symtable != NULL) {
+ DBT key;
+ DBT data;
+ int flag = R_FIRST;
+
+ while (symtable->seq(symtable, &key, &data, flag) == 0) {
+ symbol_t *cursym;
+
+ memcpy(&cursym, data.data, sizeof(cursym));
+ switch(cursym->type) {
+ case REGISTER:
+ case SCBLOC:
+ case SRAMLOC:
+ symlist_add(&registers, cursym, SYMLIST_SORT);
+ break;
+ case MASK:
+ case BIT:
+ symlist_add(&masks, cursym, SYMLIST_SORT);
+ break;
+ case CONST:
+ if (cursym->info.cinfo->define == FALSE) {
+ symlist_add(&constants, cursym,
+ SYMLIST_INSERT_HEAD);
+ }
+ break;
+ case DOWNLOAD_CONST:
+ symlist_add(&download_constants, cursym,
+ SYMLIST_INSERT_HEAD);
+ break;
+ case ALIAS:
+ symlist_add(&aliases, cursym,
+ SYMLIST_INSERT_HEAD);
+ break;
+ default:
+ break;
+ }
+ flag = R_NEXT;
+ }
+
+ /* Put in the masks and bits */
+ while (masks.slh_first != NULL) {
+ symbol_node_t *curnode;
+ symbol_node_t *regnode;
+ char *regname;
+
+ curnode = masks.slh_first;
+ SLIST_REMOVE_HEAD(&masks, links);
+
+ regnode =
+ curnode->symbol->info.minfo->symrefs.slh_first;
+ regname = regnode->symbol->name;
+ regnode = symlist_search(&registers, regname);
+ SLIST_INSERT_AFTER(regnode, curnode, links);
+ }
+
+ /* Add the aliases */
+ while (aliases.slh_first != NULL) {
+ symbol_node_t *curnode;
+ symbol_node_t *regnode;
+ char *regname;
+
+ curnode = aliases.slh_first;
+ SLIST_REMOVE_HEAD(&aliases, links);
+
+ regname = curnode->symbol->info.ainfo->parent->name;
+ regnode = symlist_search(&registers, regname);
+ SLIST_INSERT_AFTER(regnode, curnode, links);
+ }
+
+ /* Output what we have */
+ fprintf(ofile,
+"/*
+ * DO NOT EDIT - This file is automatically generated.
+ */\n");
+ while (registers.slh_first != NULL) {
+ symbol_node_t *curnode;
+ u_int8_t value;
+ char *tab_str;
+ char *tab_str2;
+
+ curnode = registers.slh_first;
+ SLIST_REMOVE_HEAD(&registers, links);
+ switch(curnode->symbol->type) {
+ case REGISTER:
+ case SCBLOC:
+ case SRAMLOC:
+ fprintf(ofile, "\n");
+ value = curnode->symbol->info.rinfo->address;
+ tab_str = "\t";
+ tab_str2 = "\t\t";
+ break;
+ case ALIAS:
+ {
+ symbol_t *parent;
+
+ parent = curnode->symbol->info.ainfo->parent;
+ value = parent->info.rinfo->address;
+ tab_str = "\t";
+ tab_str2 = "\t\t";
+ break;
+ }
+ case MASK:
+ case BIT:
+ value = curnode->symbol->info.minfo->mask;
+ tab_str = "\t\t";
+ tab_str2 = "\t";
+ break;
+ default:
+ value = 0; /* Quiet compiler */
+ tab_str = NULL;
+ tab_str2 = NULL;
+ stop("symtable_dump: Invalid symbol type "
+ "encountered", EX_SOFTWARE);
+ break;
+ }
+ fprintf(ofile, "#define%s%-16s%s0x%02x\n",
+ tab_str, curnode->symbol->name, tab_str2,
+ value);
+ free(curnode);
+ }
+ fprintf(ofile, "\n\n");
+
+ while (constants.slh_first != NULL) {
+ symbol_node_t *curnode;
+
+ curnode = constants.slh_first;
+ SLIST_REMOVE_HEAD(&constants, links);
+ fprintf(ofile, "#define\t%-8s\t0x%02x\n",
+ curnode->symbol->name,
+ curnode->symbol->info.cinfo->value);
+ free(curnode);
+ }
+
+
+ fprintf(ofile, "\n\n/* Downloaded Constant Definitions */\n");
+
+ while (download_constants.slh_first != NULL) {
+ symbol_node_t *curnode;
+
+ curnode = download_constants.slh_first;
+ SLIST_REMOVE_HEAD(&download_constants, links);
+ fprintf(ofile, "#define\t%-8s\t0x%02x\n",
+ curnode->symbol->name,
+ curnode->symbol->info.cinfo->value);
+ free(curnode);
+ }
+ }
+}
+
diff --git a/sys/dev/aic7xxx/aicasm/aicasm_symbol.h b/sys/dev/aic7xxx/aicasm/aicasm_symbol.h
new file mode 100644
index 000000000000..ec070c898819
--- /dev/null
+++ b/sys/dev/aic7xxx/aicasm/aicasm_symbol.h
@@ -0,0 +1,177 @@
+/*
+ * Aic7xxx SCSI host adapter firmware asssembler symbol table definitions
+ *
+ * Copyright (c) 1997 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aicasm/aicasm_symbol.h#3 $
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+typedef enum {
+ UNINITIALIZED,
+ REGISTER,
+ ALIAS,
+ SCBLOC,
+ SRAMLOC,
+ MASK,
+ BIT,
+ CONST,
+ DOWNLOAD_CONST,
+ LABEL,
+ CONDITIONAL
+}symtype;
+
+typedef enum {
+ RO = 0x01,
+ WO = 0x02,
+ RW = 0x03
+}amode_t;
+
+struct reg_info {
+ u_int8_t address;
+ int size;
+ amode_t mode;
+ u_int8_t valid_bitmask;
+ int typecheck_masks;
+};
+
+typedef SLIST_HEAD(symlist, symbol_node) symlist_t;
+
+struct mask_info {
+ symlist_t symrefs;
+ u_int8_t mask;
+};
+
+struct const_info {
+ u_int8_t value;
+ int define;
+};
+
+struct alias_info {
+ struct symbol *parent;
+};
+
+struct label_info {
+ int address;
+};
+
+struct cond_info {
+ int func_num;
+};
+
+typedef struct expression_info {
+ symlist_t referenced_syms;
+ int value;
+} expression_t;
+
+typedef struct symbol {
+ char *name;
+ symtype type;
+ union {
+ struct reg_info *rinfo;
+ struct mask_info *minfo;
+ struct const_info *cinfo;
+ struct alias_info *ainfo;
+ struct label_info *linfo;
+ struct cond_info *condinfo;
+ }info;
+} symbol_t;
+
+typedef struct symbol_ref {
+ symbol_t *symbol;
+ int offset;
+} symbol_ref_t;
+
+typedef struct symbol_node {
+ SLIST_ENTRY(symbol_node) links;
+ symbol_t *symbol;
+} symbol_node_t;
+
+typedef struct critical_section {
+ TAILQ_ENTRY(critical_section) links;
+ int begin_addr;
+ int end_addr;
+} critical_section_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;
+
+TAILQ_HEAD(cs_tailq, critical_section);
+SLIST_HEAD(scope_list, scope);
+TAILQ_HEAD(scope_tailq, scope);
+
+void symbol_delete __P((symbol_t *symbol));
+
+void symtable_open __P((void));
+
+void symtable_close __P((void));
+
+symbol_t *
+ symtable_get __P((char *name));
+
+symbol_node_t *
+ symlist_search __P((symlist_t *symlist, char *symname));
+
+void
+ symlist_add __P((symlist_t *symlist, symbol_t *symbol, int how));
+#define SYMLIST_INSERT_HEAD 0x00
+#define SYMLIST_SORT 0x01
+
+void symlist_free __P((symlist_t *symlist));
+
+void symlist_merge __P((symlist_t *symlist_dest, symlist_t *symlist_src1,
+ symlist_t *symlist_src2));
+void symtable_dump __P((FILE *ofile));