diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/ic/aac.c | 3646 | ||||
-rw-r--r-- | sys/dev/ic/aac_tables.h | 4 | ||||
-rw-r--r-- | sys/dev/ic/aacreg.h | 1426 | ||||
-rw-r--r-- | sys/dev/ic/aacvar.h | 448 | ||||
-rw-r--r-- | sys/dev/pci/aac_pci.c | 39 |
5 files changed, 3814 insertions, 1749 deletions
diff --git a/sys/dev/ic/aac.c b/sys/dev/ic/aac.c index 3373f80911e..2ba71c4b898 100644 --- a/sys/dev/ic/aac.c +++ b/sys/dev/ic/aac.c @@ -1,9 +1,12 @@ -/* $OpenBSD: aac.c,v 1.24 2005/09/15 05:33:39 krw Exp $ */ +/* $OpenBSD: aac.c,v 1.25 2005/11/18 05:39:10 nate Exp $ */ /*- * Copyright (c) 2000 Michael Smith + * Copyright (c) 2001 Scott Long * Copyright (c) 2000 BSDi + * Copyright (c) 2001 Adaptec, Inc. * Copyright (c) 2000 Niklas Hallqvist + * Copyright (c) 2004 Nathan Binkert * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,7 +50,9 @@ #include <sys/buf.h> #include <sys/device.h> #include <sys/kernel.h> +#include <sys/kthread.h> #include <sys/malloc.h> +#include <sys/rwlock.h> #include <machine/bus.h> @@ -71,43 +76,51 @@ #define AAC_BIGSECS 63 /* mapping 255*63 */ #define AAC_SECS32 0x1f /* round capacity */ -void aac_bio_complete(struct aac_ccb *); -void aac_complete(void *, int); +struct scsi_xfer; + void aac_copy_internal_data(struct scsi_xfer *, u_int8_t *, size_t); -struct scsi_xfer *aac_dequeue(struct aac_softc *); -int aac_dequeue_fib(struct aac_softc *, int, u_int32_t *, - struct aac_fib **); char *aac_describe_code(struct aac_code_lookup *, u_int32_t); void aac_describe_controller(struct aac_softc *); void aac_enqueue(struct aac_softc *, struct scsi_xfer *, int); -void aac_enqueue_ccb(struct aac_softc *, struct aac_ccb *); -int aac_enqueue_fib(struct aac_softc *, int, struct aac_ccb *); +int aac_enqueue_fib(struct aac_softc *, int, struct aac_command *); +int aac_dequeue_fib(struct aac_softc *, int, u_int32_t *, + struct aac_fib **); +int aac_enqueue_response(struct aac_softc *sc, int queue, + struct aac_fib *fib); + void aac_eval_mapping(u_int32_t, int *, int *, int *); -int aac_exec_ccb(struct aac_ccb *); -void aac_free_ccb(struct aac_softc *, struct aac_ccb *); -struct aac_ccb *aac_get_ccb(struct aac_softc *, int); -#if 0 -void aac_handle_aif(struct aac_softc *, struct aac_aif_command *); -#endif +void aac_print_printf(struct aac_softc *); void aac_host_command(struct aac_softc *); void aac_host_response(struct aac_softc *); int aac_init(struct aac_softc *); +int aac_check_firmware(struct aac_softc *); int aac_internal_cache_cmd(struct scsi_xfer *); -int aac_map_command(struct aac_ccb *); -#ifdef AAC_DEBUG -void aac_print_fib(struct aac_softc *, struct aac_fib *, const char *); -#endif + +/* Command Processing */ +void aac_timeout(struct aac_softc *); +void aac_command_timeout(struct aac_command *); +int aac_map_command(struct aac_command *); +void aac_complete(void *); +int aac_bio_command(struct aac_softc *, struct aac_command **); +void aac_bio_complete(struct aac_command *); +int aac_wait_command(struct aac_command *, int); +void aac_create_thread(void *); +void aac_command_thread(void *); + +/* Command Buffer Management */ +void aac_map_command_sg(void *, bus_dma_segment_t *, int, int); +int aac_alloc_commands(struct aac_softc *); +void aac_free_commands(struct aac_softc *); +void aac_unmap_command(struct aac_command *); + int aac_raw_scsi_cmd(struct scsi_xfer *); int aac_scsi_cmd(struct scsi_xfer *); -int aac_start(struct aac_ccb *); -void aac_start_ccbs(struct aac_softc *); +void aac_startio(struct aac_softc *); void aac_startup(struct aac_softc *); +void aac_add_container(struct aac_softc *, struct aac_mntinforesp *, int); +void aac_shutdown(void *); int aac_sync_command(struct aac_softc *, u_int32_t, u_int32_t, u_int32_t, u_int32_t, u_int32_t, u_int32_t *); -int aac_sync_fib(struct aac_softc *, u_int32_t, u_int32_t, void *, - u_int16_t, void *, u_int16_t *); -void aac_timeout(void *); -void aac_unmap_command(struct aac_ccb *); void aac_watchdog(void *); struct cfdriver aac_cd = { @@ -126,15 +139,25 @@ struct scsi_device aac_dev = { NULL, NULL, NULL, NULL }; -/* i960Rx interface */ -int aac_rx_get_fwstatus(struct aac_softc *); -void aac_rx_qnotify(struct aac_softc *, int); -int aac_rx_get_istatus(struct aac_softc *); -void aac_rx_clear_istatus(struct aac_softc *, int); -void aac_rx_set_mailbox(struct aac_softc *, u_int32_t, u_int32_t, - u_int32_t, u_int32_t, u_int32_t); -int aac_rx_get_mailboxstatus(struct aac_softc *); -void aac_rx_set_interrupts(struct aac_softc *, int); +/* Falcon/PPC interface */ +int aac_fa_get_fwstatus(struct aac_softc *); +void aac_fa_qnotify(struct aac_softc *, int); +int aac_fa_get_istatus(struct aac_softc *); +void aac_fa_clear_istatus(struct aac_softc *, int); +void aac_fa_set_mailbox(struct aac_softc *, u_int32_t, u_int32_t, u_int32_t, + u_int32_t, u_int32_t); +int aac_fa_get_mailbox(struct aac_softc *, int); +void aac_fa_set_interrupts(struct aac_softc *, int); + +struct aac_interface aac_fa_interface = { + aac_fa_get_fwstatus, + aac_fa_qnotify, + aac_fa_get_istatus, + aac_fa_clear_istatus, + aac_fa_set_mailbox, + aac_fa_get_mailbox, + aac_fa_set_interrupts +}; /* StrongARM interface */ int aac_sa_get_fwstatus(struct aac_softc *); @@ -143,914 +166,921 @@ int aac_sa_get_istatus(struct aac_softc *); void aac_sa_clear_istatus(struct aac_softc *, int); void aac_sa_set_mailbox(struct aac_softc *, u_int32_t, u_int32_t, u_int32_t, u_int32_t, u_int32_t); -int aac_sa_get_mailboxstatus(struct aac_softc *); +int aac_sa_get_mailbox(struct aac_softc *, int); void aac_sa_set_interrupts(struct aac_softc *, int); -struct aac_interface aac_rx_interface = { - aac_rx_get_fwstatus, - aac_rx_qnotify, - aac_rx_get_istatus, - aac_rx_clear_istatus, - aac_rx_set_mailbox, - aac_rx_get_mailboxstatus, - aac_rx_set_interrupts -}; - struct aac_interface aac_sa_interface = { aac_sa_get_fwstatus, aac_sa_qnotify, aac_sa_get_istatus, aac_sa_clear_istatus, aac_sa_set_mailbox, - aac_sa_get_mailboxstatus, + aac_sa_get_mailbox, aac_sa_set_interrupts }; +/* i960Rx interface */ +int aac_rx_get_fwstatus(struct aac_softc *); +void aac_rx_qnotify(struct aac_softc *, int); +int aac_rx_get_istatus(struct aac_softc *); +void aac_rx_clear_istatus(struct aac_softc *, int); +void aac_rx_set_mailbox(struct aac_softc *, u_int32_t, u_int32_t, + u_int32_t, u_int32_t, u_int32_t); +int aac_rx_get_mailbox(struct aac_softc *, int); +void aac_rx_set_interrupts(struct aac_softc *, int); + +struct aac_interface aac_rx_interface = { + aac_rx_get_fwstatus, + aac_rx_qnotify, + aac_rx_get_istatus, + aac_rx_clear_istatus, + aac_rx_set_mailbox, + aac_rx_get_mailbox, + aac_rx_set_interrupts +}; + #ifdef AAC_DEBUG int aac_debug = AAC_DEBUG; #endif int -aac_attach(sc) - struct aac_softc *sc; +aac_attach(struct aac_softc *sc) { - int i, error; - bus_dma_segment_t seg; - int nsegs; - struct aac_ccb *ccb; + int error; - TAILQ_INIT(&sc->sc_free_ccb); - TAILQ_INIT(&sc->sc_ccbq); - TAILQ_INIT(&sc->sc_completed); - LIST_INIT(&sc->sc_queue); + /* + * Initialise per-controller queues. + */ + aac_initq_free(sc); + aac_initq_ready(sc); + aac_initq_busy(sc); + aac_initq_bio(sc); /* disable interrupts before we enable anything */ AAC_MASK_INTERRUPTS(sc); /* mark controller as suspended until we get ourselves organised */ - sc->sc_state |= AAC_STATE_SUSPEND; + sc->aac_state |= AAC_STATE_SUSPEND; /* - * Initialise the adapter. + * Check that the firmware on the card is supported. */ - error = aac_init(sc); + error = aac_check_firmware(sc); if (error) return (error); - /* - * Print a little information about the controller. + /* + * Initialize locks */ - aac_describe_controller(sc); + AAC_LOCK_INIT(&sc->aac_sync_lock, "AAC sync FIB lock"); + AAC_LOCK_INIT(&sc->aac_aifq_lock, "AAC AIF lock"); + AAC_LOCK_INIT(&sc->aac_io_lock, "AAC I/O lock"); + AAC_LOCK_INIT(&sc->aac_container_lock, "AAC container lock"); + TAILQ_INIT(&sc->aac_container_tqh); - /* Initialize the ccbs */ - for (i = 0; i < AAC_ADAP_NORM_CMD_ENTRIES; i++) { - ccb = &sc->sc_ccbs[i]; - error = bus_dmamap_create(sc->sc_dmat, - (AAC_MAXSGENTRIES - 1) << PGSHIFT, AAC_MAXSGENTRIES, - (AAC_MAXSGENTRIES - 1) << PGSHIFT, 0, - BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &ccb->ac_dmamap_xfer); - if (error) { - printf("%s: cannot create ccb dmamap (%d)", - sc->sc_dev.dv_xname, error); - /* XXX cleanup */ - return (1); - } - - /* allocate the FIB cluster in DMAable memory and load it */ - if (bus_dmamem_alloc(sc->sc_dmat, sizeof *ccb->ac_fib, 1, 0, - &seg, 1, &nsegs, BUS_DMA_NOWAIT)) { - printf("%s: can't allocate FIB structure\n", - sc->sc_dev.dv_xname); - /* XXX cleanup */ - return (1); - } - ccb->ac_fibphys = seg.ds_addr; - if (bus_dmamem_map(sc->sc_dmat, &seg, nsegs, - sizeof *ccb->ac_fib, (caddr_t *)&ccb->ac_fib, 0)) { - printf("%s: can't map FIB structure\n", - sc->sc_dev.dv_xname); - /* XXX cleanup */ - return (1); - } - - TAILQ_INSERT_TAIL(&sc->sc_free_ccb, &sc->sc_ccbs[i], - ac_chain); - } + /* Initialize the local AIF queue pointers */ + sc->aac_aifq_head = sc->aac_aifq_tail = AAC_AIFQ_LENGTH; - /* Fill in the prototype scsi_link. */ - sc->sc_link.adapter_softc = sc; - sc->sc_link.adapter = &aac_switch; - sc->sc_link.device = &aac_dev; /* - * XXX Theoretically this should be AAC_ADAP_NORM_CMD_ENTRIES but - * XXX in some configurations this can cause "not queued" errors. - * XXX A quarter of that number has been reported to be safe. + * Initialise the adapter. */ - sc->sc_link.openings = AAC_ADAP_NORM_CMD_ENTRIES / 4; - sc->sc_link.adapter_buswidth = AAC_MAX_CONTAINERS; - sc->sc_link.adapter_target = AAC_MAX_CONTAINERS; - - config_found(&sc->sc_dev, &sc->sc_link, scsiprint); - - return (0); -} - -/* - * Look up a text description of a numeric error code and return a pointer to - * same. - */ -char * -aac_describe_code(table, code) - struct aac_code_lookup *table; - u_int32_t code; -{ - int i; + error = aac_init(sc); + if (error) + return (error); - for (i = 0; table[i].string != NULL; i++) - if (table[i].code == code) - return (table[i].string); - return (table[i + 1].string); -} + /* Fill in the prototype scsi_link. */ + sc->aac_link.adapter_softc = sc; + sc->aac_link.adapter = &aac_switch; + sc->aac_link.device = &aac_dev; + sc->aac_link.openings = (sc->total_fibs - 8) / + (sc->aac_container_count ? sc->aac_container_count : 1); + sc->aac_link.adapter_buswidth = AAC_MAX_CONTAINERS; + sc->aac_link.adapter_target = AAC_MAX_CONTAINERS; -void -aac_describe_controller(sc) - struct aac_softc *sc; -{ - u_int8_t buf[AAC_FIB_DATASIZE]; /* XXX a bit big for the stack */ - u_int16_t bufsize; - struct aac_adapter_info *info; - u_int8_t arg; + config_found(&sc->aac_dev, &sc->aac_link, scsiprint); - arg = 0; - if (aac_sync_fib(sc, RequestAdapterInfo, 0, &arg, sizeof arg, &buf, - &bufsize)) { - printf("%s: RequestAdapterInfo failed\n", sc->sc_dev.dv_xname); - return; - } - if (bufsize != sizeof *info) { - printf("%s: " - "RequestAdapterInfo returned wrong data size (%d != %d)\n", - sc->sc_dev.dv_xname, bufsize, sizeof *info); - return; - } - info = (struct aac_adapter_info *)&buf[0]; + /* Create the AIF thread */ + sc->aifthread = 0; + sc->aifflags = 0; + kthread_create_deferred(aac_create_thread, sc); - printf("%s: %s %dMHz, %dMB, %s (%d) Kernel %d.%d-%d\n", - sc->sc_dev.dv_xname, - aac_describe_code(aac_cpu_variant, info->CpuVariant), - info->ClockSpeed, info->TotalMem / (1024 * 1024), - aac_describe_code(aac_battery_platform, info->batteryPlatform), - info->batteryPlatform, info->KernelRevision.external.comp.major, - info->KernelRevision.external.comp.minor, - info->KernelRevision.external.comp.dash); +#if 0 + /* Register the shutdown method to only be called post-dump */ + sc->aac_sdh = shutdownhook_establish(aac_shutdown, (void *)sc); +#endif - /* save the kernel revision structure for later use */ - sc->sc_revision = info->KernelRevision; + return (0); } -int -aac_init(sc) - struct aac_softc *sc; +void +aac_create_thread(void *arg) { - bus_dma_segment_t seg; - int nsegs; - int i, error; - int state = 0; - struct aac_adapter_init *ip; - u_int32_t code; - u_int8_t *qaddr; - - /* - * First wait for the adapter to come ready. - */ - for (i = 0; i < AAC_BOOT_TIMEOUT * 1000; i++) { - code = AAC_GET_FWSTATUS(sc); - if (code & AAC_SELF_TEST_FAILED) { - printf("%s: FATAL: selftest failed\n", - sc->sc_dev.dv_xname); - return (ENXIO); - } - if (code & AAC_KERNEL_PANIC) { - printf("%s: FATAL: controller kernel panic\n", - sc->sc_dev.dv_xname); - return (ENXIO); - } - if (code & AAC_UP_AND_RUNNING) - break; - DELAY(1000); - } - if (i == AAC_BOOT_TIMEOUT * 1000) { - printf("%s: FATAL: controller not coming ready, status %x\n", - sc->sc_dev.dv_xname, code); - return (ENXIO); - } - - if (bus_dmamem_alloc(sc->sc_dmat, sizeof *sc->sc_common, 1, 0, &seg, 1, - &nsegs, BUS_DMA_NOWAIT)) { - printf("%s: can't allocate common structure\n", - sc->sc_dev.dv_xname); - return (ENOMEM); - } - state++; - sc->sc_common_busaddr = seg.ds_addr; - if (bus_dmamem_map(sc->sc_dmat, &seg, nsegs, sizeof *sc->sc_common, - (caddr_t *)&sc->sc_common, 0)) { - printf("%s: can't map common structure\n", - sc->sc_dev.dv_xname); - error = ENOMEM; - goto bail_out; - } - state++; - bzero(sc->sc_common, sizeof *sc->sc_common); - - /* - * Fill in the init structure. This tells the adapter about - * the physical location * of various important shared data - * structures. - */ - ip = &sc->sc_common->ac_init; - ip->InitStructRevision = AAC_INIT_STRUCT_REVISION; - - ip->AdapterFibsPhysicalAddress = - sc->sc_common_busaddr + offsetof(struct aac_common, ac_fibs); - ip->AdapterFibsVirtualAddress = &sc->sc_common->ac_fibs[0]; - ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib); - ip->AdapterFibAlign = sizeof(struct aac_fib); - - ip->PrintfBufferAddress = - sc->sc_common_busaddr + offsetof(struct aac_common, ac_printf); - ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE; - - ip->HostPhysMemPages = 0; /* not used? */ - ip->HostElapsedSeconds = 0; /* reset later if invalid */ - - /* - * Initialise FIB queues. Note that it appears that the - * layout of the indexes and the segmentation of the entries - * is mandated by the adapter, which is only told about the - * base of the queue index fields. - * - * The initial values of the indices are assumed to inform the - * adapter of the sizes of the respective queues. - * - * The Linux driver uses a much more complex scheme whereby - * several header * records are kept for each queue. We use a - * couple of generic list manipulation functions which - * 'know' the size of each list by virtue of a table. - */ - qaddr = &sc->sc_common->ac_qbuf[0] + AAC_QUEUE_ALIGN; - qaddr -= (u_int32_t)qaddr % AAC_QUEUE_ALIGN; /* XXX not portable */ - sc->sc_queues = (struct aac_queue_table *)qaddr; - ip->CommHeaderAddress = sc->sc_common_busaddr + - ((char *)sc->sc_queues - (char *)sc->sc_common); - bzero(sc->sc_queues, sizeof(struct aac_queue_table)); - - sc->sc_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] = - AAC_HOST_NORM_CMD_ENTRIES; - sc->sc_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] = - AAC_HOST_NORM_CMD_ENTRIES; - sc->sc_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] = - AAC_HOST_HIGH_CMD_ENTRIES; - sc->sc_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] = - AAC_HOST_HIGH_CMD_ENTRIES; - sc->sc_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] = - AAC_ADAP_NORM_CMD_ENTRIES; - sc->sc_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] = - AAC_ADAP_NORM_CMD_ENTRIES; - sc->sc_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] = - AAC_ADAP_HIGH_CMD_ENTRIES; - sc->sc_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] = - AAC_ADAP_HIGH_CMD_ENTRIES; - sc->sc_queues-> - qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX] = - AAC_HOST_NORM_RESP_ENTRIES; - sc->sc_queues-> - qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX] = - AAC_HOST_NORM_RESP_ENTRIES; - sc->sc_queues-> - qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX] = - AAC_HOST_HIGH_RESP_ENTRIES; - sc->sc_queues-> - qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX] = - AAC_HOST_HIGH_RESP_ENTRIES; - sc->sc_queues-> - qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX] = - AAC_ADAP_NORM_RESP_ENTRIES; - sc->sc_queues-> - qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX] = - AAC_ADAP_NORM_RESP_ENTRIES; - sc->sc_queues-> - qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX] = - AAC_ADAP_HIGH_RESP_ENTRIES; - sc->sc_queues-> - qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX] = - AAC_ADAP_HIGH_RESP_ENTRIES; - sc->sc_qentries[AAC_HOST_NORM_CMD_QUEUE] = - &sc->sc_queues->qt_HostNormCmdQueue[0]; - sc->sc_qentries[AAC_HOST_HIGH_CMD_QUEUE] = - &sc->sc_queues->qt_HostHighCmdQueue[0]; - sc->sc_qentries[AAC_ADAP_NORM_CMD_QUEUE] = - &sc->sc_queues->qt_AdapNormCmdQueue[0]; - sc->sc_qentries[AAC_ADAP_HIGH_CMD_QUEUE] = - &sc->sc_queues->qt_AdapHighCmdQueue[0]; - sc->sc_qentries[AAC_HOST_NORM_RESP_QUEUE] = - &sc->sc_queues->qt_HostNormRespQueue[0]; - sc->sc_qentries[AAC_HOST_HIGH_RESP_QUEUE] = - &sc->sc_queues->qt_HostHighRespQueue[0]; - sc->sc_qentries[AAC_ADAP_NORM_RESP_QUEUE] = - &sc->sc_queues->qt_AdapNormRespQueue[0]; - sc->sc_qentries[AAC_ADAP_HIGH_RESP_QUEUE] = - &sc->sc_queues->qt_AdapHighRespQueue[0]; + struct aac_softc *sc = arg; - /* - * Do controller-type-specific initialisation - */ - switch (sc->sc_hwif) { - case AAC_HWIF_I960RX: - AAC_SETREG4(sc, AAC_RX_ODBR, ~0); - break; + if (kthread_create(aac_command_thread, sc, &sc->aifthread, "%s", + sc->aac_dev.dv_xname)) { + /* TODO disable aac */ + printf("%s: failed to create kernel thread, disabled", + sc->aac_dev.dv_xname); } + AAC_DPRINTF(AAC_D_MISC, ("%s: aac_create_thread\n", + sc->aac_dev.dv_xname)); - /* - * Give the init structure to the controller. - */ - if (aac_sync_command(sc, AAC_MONKER_INITSTRUCT, - sc->sc_common_busaddr + offsetof(struct aac_common, ac_init), 0, 0, - 0, NULL)) { - printf("%s: error establishing init structure\n", - sc->sc_dev.dv_xname); - error = EIO; - goto bail_out; - } - - aac_startup(sc); - - return (0); - - bail_out: - if (state > 1) - bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_common, - sizeof *sc->sc_common); - if (state > 0) - bus_dmamem_free(sc->sc_dmat, &seg, 1); - return (error); } /* * Probe for containers, create disks. */ void -aac_startup (sc) - struct aac_softc *sc; +aac_startup(struct aac_softc *sc) { - struct aac_mntinfo mi; - struct aac_mntinforesponse mir; - u_int16_t rsize; - int i, drv_cyls, drv_hds, drv_secs; + struct aac_fib *fib; + struct aac_mntinfo *mi; + struct aac_mntinforesp *mir = NULL; + int count = 0, i = 0; + + aac_alloc_sync_fib(sc, &fib, 0); + mi = (struct aac_mntinfo *)&fib->data[0]; + + AAC_DPRINTF(AAC_D_MISC, ("%s: aac startup\n", sc->aac_dev.dv_xname)); + + sc->aac_container_count = 0; /* loop over possible containers */ - mi.Command = VM_NameServe; - mi.MntType = FT_FILESYS; - for (i = 0; i < AAC_MAX_CONTAINERS; i++) { + do { /* request information on this container */ - mi.MntCount = i; - if (aac_sync_fib(sc, ContainerCommand, 0, &mi, sizeof mi, &mir, - &rsize)) { - printf("%s: error probing container %d", - sc->sc_dev.dv_xname, i); - continue; - } - /* check response size */ - if (rsize != sizeof mir) { - printf("%s: container info response wrong size " - "(%d should be %d)", - sc->sc_dev.dv_xname, rsize, sizeof mir); + bzero(mi, sizeof(struct aac_mntinfo)); + mi->Command = VM_NameServe; + mi->MntType = FT_FILESYS; + mi->MntCount = i; + if (aac_sync_fib(sc, ContainerCommand, 0, fib, + sizeof(struct aac_mntinfo))) { + printf("%s: error probing container %d\n", + sc->aac_dev.dv_xname, i); continue; } + mir = (struct aac_mntinforesp *)&fib->data[0]; + /* XXX Need to check if count changed */ + count = mir->MntRespCount; + +#if 0 + aac_add_container(sc, mir, 0); +#else /* * Check container volume type for validity. Note - * that many of the possible types * may never show - * up. + * that many of the possible types may never show up. */ - if (mir.Status == ST_OK && - mir.MntTable[0].VolType != CT_NONE) { + if (mir->Status == ST_OK && + mir->MntTable[0].VolType != CT_NONE) { + int drv_cyls, drv_hds, drv_secs; + AAC_DPRINTF(AAC_D_MISC, - ("%d: id %x name '%.16s' size %u type %d", i, - mir.MntTable[0].ObjectId, - mir.MntTable[0].FileSystemName, - mir.MntTable[0].Capacity, - mir.MntTable[0].VolType)); + ("%s: %d: id %x name '%.16s' size %u type %d\n", + sc->aac_dev.dv_xname, i, + mir->MntTable[0].ObjectId, + mir->MntTable[0].FileSystemName, + mir->MntTable[0].Capacity, + mir->MntTable[0].VolType)); - sc->sc_hdr[i].hd_present = 1; - sc->sc_hdr[i].hd_size = mir.MntTable[0].Capacity; + sc->aac_container_count++; + sc->aac_hdr[i].hd_present = 1; + sc->aac_hdr[i].hd_size = mir->MntTable[0].Capacity; /* * Evaluate mapping (sectors per head, heads per cyl) */ - sc->sc_hdr[i].hd_size &= ~AAC_SECS32; - aac_eval_mapping(sc->sc_hdr[i].hd_size, &drv_cyls, - &drv_hds, &drv_secs); - sc->sc_hdr[i].hd_heads = drv_hds; - sc->sc_hdr[i].hd_secs = drv_secs; + sc->aac_hdr[i].hd_size &= ~AAC_SECS32; + aac_eval_mapping(sc->aac_hdr[i].hd_size, &drv_cyls, + &drv_hds, &drv_secs); + sc->aac_hdr[i].hd_heads = drv_hds; + sc->aac_hdr[i].hd_secs = drv_secs; /* Round the size */ - sc->sc_hdr[i].hd_size = drv_cyls * drv_hds * drv_secs; + sc->aac_hdr[i].hd_size = drv_cyls * drv_hds * drv_secs; - sc->sc_hdr[i].hd_devtype = mir.MntTable[0].VolType; + sc->aac_hdr[i].hd_devtype = mir->MntTable[0].VolType; /* XXX Save the name too for use in IDENTIFY later */ } - } +#endif + + i++; + } while ((i < count) && (i < AAC_MAX_CONTAINERS)); + + aac_release_sync_fib(sc); + +#if 0 + /* poke the bus to actually attach the child devices */ + if (bus_generic_attach(sc->aac_dev)) + printf("%s: bus_generic_attach failed\n", + sc->aac_dev.dv_xname); +#endif + /* mark the controller up */ - sc->sc_state &= ~AAC_STATE_SUSPEND; + sc->aac_state &= ~AAC_STATE_SUSPEND; /* enable interrupts now */ AAC_UNMASK_INTERRUPTS(sc); } +#if 0 +/* + * Create a device to respresent a new container + */ void -aac_eval_mapping(size, cyls, heads, secs) - u_int32_t size; - int *cyls, *heads, *secs; +aac_add_container(struct aac_softc *sc, struct aac_mntinforesp *mir, int f) { - *cyls = size / AAC_HEADS / AAC_SECS; - if (*cyls < AAC_MAXCYLS) { - *heads = AAC_HEADS; - *secs = AAC_SECS; - } else { - /* Too high for 64 * 32 */ - *cyls = size / AAC_MEDHEADS / AAC_MEDSECS; - if (*cyls < AAC_MAXCYLS) { - *heads = AAC_MEDHEADS; - *secs = AAC_MEDSECS; - } else { - /* Too high for 127 * 63 */ - *cyls = size / AAC_BIGHEADS / AAC_BIGSECS; - *heads = AAC_BIGHEADS; - *secs = AAC_BIGSECS; - } + struct aac_container *co; + device_t child; + + /* + * Check container volume type for validity. Note that many of + * the possible types may never show up. + */ + if ((mir->Status == ST_OK) && (mir->MntTable[0].VolType != CT_NONE)) { + co = (struct aac_container *)malloc(sizeof *co, M_DEVBUF, + M_NOWAIT); + if (co == NULL) + panic("Out of memory?!\n"); + bzero(co, sizeof *co); + AAC_DPRINTF(AAC_D_MISC, + ("%s: id %x name '%.16s' size %u type %d\n", + sc->aac_dev.dv_xname, + mir->MntTable[0].ObjectId, + mir->MntTable[0].FileSystemName, + mir->MntTable[0].Capacity, + mir->MntTable[0].VolType); + + if ((child = device_add_child(sc->aac_dev, "aacd", -1)) == NULL) + printf("%s: device_add_child failed\n", + sc->aac_dev.dv_xname); + else + device_set_ivars(child, co); + device_set_desc(child, aac_describe_code(aac_container_types, + mir->MntTable[0].VolType)); + co->co_disk = child; + co->co_found = f; + bcopy(&mir->MntTable[0], &co->co_mntobj, + sizeof(struct aac_mntobj)); + AAC_LOCK_ACQUIRE(&sc->aac_container_lock); + TAILQ_INSERT_TAIL(&sc->aac_container_tqh, co, co_link); + AAC_LOCK_RELEASE(&sc->aac_container_lock); } } +#endif -int -aac_raw_scsi_cmd(xs) - struct scsi_xfer *xs; +#if 0 +/* + * Free all of the resources associated with (sc) + * + * Should not be called if the controller is active. + */ +void +aac_free(struct aac_softc *sc) { - AAC_DPRINTF(AAC_D_CMD, ("aac_raw_scsi_cmd ")); - /* XXX Not yet implemented */ - xs->error = XS_DRIVER_STUFFUP; - return (COMPLETE); + debug_called(1); + + /* remove the control device */ + if (sc->aac_dev_t != NULL) + destroy_dev(sc->aac_dev_t); + + /* throw away any FIB buffers, discard the FIB DMA tag */ + aac_free_commands(sc); + if (sc->aac_fib_dmat) + bus_dma_tag_destroy(sc->aac_fib_dmat); + + free(sc->aac_commands, M_AACBUF); + + /* destroy the common area */ + if (sc->aac_common) { + bus_dmamap_unload(sc->aac_common_dmat, sc->aac_common_dmamap); + bus_dmamem_free(sc->aac_common_dmat, sc->aac_common, + sc->aac_common_dmamap); + } + if (sc->aac_common_dmat) + bus_dma_tag_destroy(sc->aac_common_dmat); + + /* disconnect the interrupt handler */ + if (sc->aac_intr) + bus_teardown_intr(sc->aac_dev, sc->aac_irq, sc->aac_intr); + if (sc->aac_irq != NULL) + bus_release_resource(sc->aac_dev, SYS_RES_IRQ, sc->aac_irq_rid, + sc->aac_irq); + + /* destroy data-transfer DMA tag */ + if (sc->aac_buffer_dmat) + bus_dma_tag_destroy(sc->aac_buffer_dmat); + + /* destroy the parent DMA tag */ + if (sc->aac_parent_dmat) + bus_dma_tag_destroy(sc->aac_parent_dmat); + + /* release the register window mapping */ + if (sc->aac_regs_resource != NULL) + bus_release_resource(sc->aac_dev, SYS_RES_MEMORY, + sc->aac_regs_rid, sc->aac_regs_resource); } +/* + * Disconnect from the controller completely, in preparation for unload. + */ int -aac_scsi_cmd(xs) - struct scsi_xfer *xs; +aac_detach(device_t dev) { - struct scsi_link *link = xs->sc_link; - struct aac_softc *sc = link->adapter_softc; - u_int8_t target = link->target; - struct aac_ccb *ccb; - u_int32_t blockno, blockcnt; - struct scsi_rw *rw; - struct scsi_rw_big *rwb; - aac_lock_t lock; - int retval = SUCCESSFULLY_QUEUED; + struct aac_softc *sc; + struct aac_container *co; + struct aac_sim *sim; + int error; - AAC_DPRINTF(AAC_D_CMD, ("aac_scsi_cmd ")); + debug_called(1); - xs->error = XS_NOERROR; + sc = device_get_softc(dev); - if (target >= AAC_MAX_CONTAINERS || !sc->sc_hdr[target].hd_present || - link->lun != 0) { - /* - * XXX Should be XS_SENSE but that would require setting up a - * faked sense too. - */ - xs->error = XS_DRIVER_STUFFUP; - xs->flags |= ITSDONE; - scsi_done(xs); - return (COMPLETE); + if (sc->aac_state & AAC_STATE_OPEN) + return(EBUSY); + + /* Remove the child containers */ + while ((co = TAILQ_FIRST(&sc->aac_container_tqh)) != NULL) { + error = device_delete_child(dev, co->co_disk); + if (error) + return (error); + TAILQ_REMOVE(&sc->aac_container_tqh, co, co_link); + free(co, M_AACBUF); } - lock = AAC_LOCK(sc); + /* Remove the CAM SIMs */ + while ((sim = TAILQ_FIRST(&sc->aac_sim_tqh)) != NULL) { + TAILQ_REMOVE(&sc->aac_sim_tqh, sim, sim_link); + error = device_delete_child(dev, sim->sim_dev); + if (error) + return (error); + free(sim, M_AACBUF); + } - /* Don't double enqueue if we came from aac_chain. */ - if (xs != LIST_FIRST(&sc->sc_queue)) - aac_enqueue(sc, xs, 0); + if (sc->aifflags & AAC_AIFFLAGS_RUNNING) { + sc->aifflags |= AAC_AIFFLAGS_EXIT; + wakeup(sc->aifthread); + tsleep(sc->aac_dev, PUSER | PCATCH, "aacdch", 30 * hz); + } - while ((xs = aac_dequeue(sc))) { - xs->error = XS_NOERROR; - ccb = NULL; - link = xs->sc_link; - target = link->target; - - switch (xs->cmd->opcode) { - case TEST_UNIT_READY: - case REQUEST_SENSE: - case INQUIRY: - case MODE_SENSE: - case START_STOP: - case READ_CAPACITY: -#if 0 - case VERIFY: -#endif - if (!aac_internal_cache_cmd(xs)) { - AAC_UNLOCK(sc, lock); - return (TRY_AGAIN_LATER); - } - xs->flags |= ITSDONE; - scsi_done(xs); - goto ready; + if (sc->aifflags & AAC_AIFFLAGS_RUNNING) + panic("Cannot shutdown AIF thread\n"); - case PREVENT_ALLOW: - AAC_DPRINTF(AAC_D_CMD, ("PREVENT/ALLOW ")); - /* XXX Not yet implemented */ - xs->error = XS_NOERROR; - xs->flags |= ITSDONE; - scsi_done(xs); - goto ready; + if ((error = aac_shutdown(dev))) + return(error); - case SYNCHRONIZE_CACHE: - AAC_DPRINTF(AAC_D_CMD, ("SYNCHRONIZE_CACHE ")); - /* XXX Not yet implemented */ - xs->error = XS_NOERROR; - xs->flags |= ITSDONE; - scsi_done(xs); - goto ready; + EVENTHANDLER_DEREGISTER(shutdown_final, sc->eh); - default: - AAC_DPRINTF(AAC_D_CMD, - ("unknown opc %d ", xs->cmd->opcode)); - /* XXX Not yet implemented */ - xs->error = XS_DRIVER_STUFFUP; - xs->flags |= ITSDONE; - scsi_done(xs); - goto ready; + aac_free(sc); - case READ_COMMAND: - case READ_BIG: - case WRITE_COMMAND: - case WRITE_BIG: - AAC_DPRINTF(AAC_D_CMD, - ("rw opc %d ", xs->cmd->opcode)); - - if (xs->cmd->opcode != SYNCHRONIZE_CACHE) { - /* A read or write operation. */ - if (xs->cmdlen == 6) { - rw = (struct scsi_rw *)xs->cmd; - blockno = _3btol(rw->addr) & - (SRW_TOPADDR << 16 | 0xffff); - blockcnt = - rw->length ? rw->length : 0x100; - } else { - rwb = (struct scsi_rw_big *)xs->cmd; - blockno = _4btol(rwb->addr); - blockcnt = _2btol(rwb->length); - } - if (blockno >= sc->sc_hdr[target].hd_size || - blockno + blockcnt > - sc->sc_hdr[target].hd_size) { - printf( - "%s: out of bounds %u-%u >= %u\n", - sc->sc_dev.dv_xname, blockno, - blockcnt, - sc->sc_hdr[target].hd_size); - /* - * XXX Should be XS_SENSE but that - * would require setting up a faked - * sense too. - */ - xs->error = XS_DRIVER_STUFFUP; - xs->flags |= ITSDONE; - scsi_done(xs); - goto ready; - } - } + return(0); +} + +/* + * Bring the controller down to a dormant state and detach all child devices. + * + * This function is called before detach or system shutdown. + * + * Note that we can assume that the bioq on the controller is empty, as we won't + * allow shutdown if any device is open. + */ +int +aac_shutdown(device_t dev) +{ + struct aac_softc *sc; + struct aac_fib *fib; + struct aac_close_command *cc; - ccb = aac_get_ccb(sc, xs->flags); + debug_called(1); - /* - * We are out of commands, try again in a little while. - */ - if (ccb == NULL) { - xs->error = XS_DRIVER_STUFFUP; - AAC_UNLOCK(sc, lock); - return (TRY_AGAIN_LATER); - } + sc = device_get_softc(dev); - ccb->ac_blockno = blockno; - ccb->ac_blockcnt = blockcnt; - ccb->ac_xs = xs; - ccb->ac_timeout = xs->timeout; - - if (xs->cmd->opcode != SYNCHRONIZE_CACHE && - aac_map_command(ccb)) { - aac_free_ccb(sc, ccb); - xs->error = XS_DRIVER_STUFFUP; - xs->flags |= ITSDONE; - scsi_done(xs); - goto ready; - } + sc->aac_state |= AAC_STATE_SUSPEND; - aac_enqueue_ccb(sc, ccb); - /* XXX what if enqueue did not start a transfer? */ - if (xs->flags & SCSI_POLL) { -#if 0 - if (!aac_wait(sc, ccb, ccb->ac_timeout)) { - AAC_UNLOCK(sc, lock); - printf("%s: command timed out\n", - sc->sc_dev.dv_xname); - xs->error = XS_TIMEOUT; - return (TRY_AGAIN_LATER); - } - xs->flags |= ITSDONE; - scsi_done(xs); -#endif - } - } + /* + * Send a Container shutdown followed by a HostShutdown FIB to the + * controller to convince it that we don't want to talk to it anymore. + * We've been closed and all I/O completed already + */ + device_printf(sc->aac_dev, "shutting down controller..."); + + aac_alloc_sync_fib(sc, &fib, AAC_SYNC_LOCK_FORCE); + cc = (struct aac_close_command *)&fib->data[0]; - ready: + bzero(cc, sizeof(struct aac_close_command)); + cc->Command = VM_CloseAll; + cc->ContainerId = 0xffffffff; + if (aac_sync_fib(sc, ContainerCommand, 0, fib, + sizeof(struct aac_close_command))) + printf("FAILED.\n"); + else + printf("done\n"); + else { + fib->data[0] = 0; /* - * Don't process the queue if we are polling. + * XXX Issuing this command to the controller makes it + * shut down but also keeps it from coming back up + * without a reset of the PCI bus. This is not + * desirable if you are just unloading the driver + * module with the intent to reload it later. */ - if (xs->flags & SCSI_POLL) { - retval = COMPLETE; - break; + if (aac_sync_fib(sc, FsaHostShutdown, AAC_FIBSTATE_SHUTDOWN, + fib, 1)) { + printf("FAILED.\n"); + } else { + printf("done.\n"); } } - AAC_UNLOCK(sc, lock); - return (retval); + AAC_MASK_INTERRUPTS(sc); + + return(0); } -void -aac_copy_internal_data(xs, data, size) - struct scsi_xfer *xs; - u_int8_t *data; - size_t size; +/* + * Bring the controller to a quiescent state, ready for system suspend. + */ +int +aac_suspend(device_t dev) { - size_t copy_cnt; + struct aac_softc *sc; - AAC_DPRINTF(AAC_D_MISC, ("aac_copy_internal_data ")); + debug_called(1); - if (!xs->datalen) - printf("uio move not yet supported\n"); - else { - copy_cnt = MIN(size, xs->datalen); - bcopy(data, xs->data, copy_cnt); - } + sc = device_get_softc(dev); + + sc->aac_state |= AAC_STATE_SUSPEND; + + AAC_MASK_INTERRUPTS(sc); + return(0); } -/* Emulated SCSI operation on cache device */ +/* + * Bring the controller back to a state ready for operation. + */ int -aac_internal_cache_cmd(xs) - struct scsi_xfer *xs; +aac_resume(device_t dev) { - struct scsi_link *link = xs->sc_link; - struct aac_softc *sc = link->adapter_softc; - struct scsi_inquiry_data inq; - struct scsi_sense_data sd; - struct scsi_read_cap_data rcd; - u_int8_t target = link->target; + struct aac_softc *sc; - AAC_DPRINTF(AAC_D_CMD, ("aac_internal_cache_cmd ")); + debug_called(1); - switch (xs->cmd->opcode) { - case TEST_UNIT_READY: - case START_STOP: -#if 0 - case VERIFY: + sc = device_get_softc(dev); + + sc->aac_state &= ~AAC_STATE_SUSPEND; + AAC_UNMASK_INTERRUPTS(sc); + return(0); +} #endif - AAC_DPRINTF(AAC_D_CMD, ("opc %d tgt %d ", xs->cmd->opcode, - target)); - break; - case REQUEST_SENSE: - AAC_DPRINTF(AAC_D_CMD, ("REQUEST SENSE tgt %d ", target)); - bzero(&sd, sizeof sd); - sd.error_code = 0x70; - sd.segment = 0; - sd.flags = SKEY_NO_SENSE; - aac_enc32(sd.info, 0); - sd.extra_len = 0; - aac_copy_internal_data(xs, (u_int8_t *)&sd, sizeof sd); - break; +/* + * Take an interrupt. + */ +int +aac_intr(void *arg) +{ + struct aac_softc *sc = arg; + u_int16_t reason; - case INQUIRY: - AAC_DPRINTF(AAC_D_CMD, ("INQUIRY tgt %d devtype %x ", target, - sc->sc_hdr[target].hd_devtype)); - bzero(&inq, sizeof inq); - /* XXX How do we detect removable/CD-ROM devices? */ - inq.device = T_DIRECT; - inq.dev_qual2 = 0; - inq.version = 2; - inq.response_format = 2; - inq.additional_length = 32; - strlcpy(inq.vendor, "Adaptec", sizeof inq.vendor); - snprintf(inq.product, sizeof inq.product, "Container #%02d", - target); - strlcpy(inq.revision, " ", sizeof inq.revision); - aac_copy_internal_data(xs, (u_int8_t *)&inq, sizeof inq); - break; - case READ_CAPACITY: - AAC_DPRINTF(AAC_D_CMD, ("READ CAPACITY tgt %d ", target)); - bzero(&rcd, sizeof rcd); - _lto4b(sc->sc_hdr[target].hd_size - 1, rcd.addr); - _lto4b(AAC_BLOCK_SIZE, rcd.length); - aac_copy_internal_data(xs, (u_int8_t *)&rcd, sizeof rcd); - break; + /* + * Read the status register directly. This is faster than taking the + * driver lock and reading the queues directly. It also saves having + * to turn parts of the driver lock into a spin mutex, which would be + * ugly. + */ + reason = AAC_GET_ISTATUS(sc); + AAC_CLEAR_ISTATUS(sc, reason); + (void)AAC_GET_ISTATUS(sc); - default: - AAC_DPRINTF(AAC_D_CMD, ("unsupported scsi command %#x tgt %d ", - xs->cmd->opcode, target)); - xs->error = XS_DRIVER_STUFFUP; + if (reason == 0) return (0); + + AAC_DPRINTF(AAC_D_INTR, ("%s: intr: sc=%p: reason=%#x\n", + sc->aac_dev.dv_xname, sc, reason)); + + /* controller wants to talk to us */ + if (reason & (AAC_DB_PRINTF | AAC_DB_COMMAND_READY | + AAC_DB_RESPONSE_READY)) { + + if (reason & AAC_DB_RESPONSE_READY) { + /* handle completion processing */ + if (sc->aifflags & AAC_AIFFLAGS_RUNNING) { + sc->aifflags |= AAC_AIFFLAGS_COMPLETE; + } else { + AAC_LOCK_ACQUIRE(&sc->aac_io_lock); + aac_complete(sc); + AAC_LOCK_RELEASE(&sc->aac_io_lock); + } + } + + + /* + * XXX Make sure that we don't get fooled by strange messages + * that start with a NULL. + */ + if (reason & AAC_DB_PRINTF) + if (sc->aac_common->ac_printf[0] == 0) + sc->aac_common->ac_printf[0] = 32; + + /* + * This might miss doing the actual wakeup. However, the + * msleep that this is waking up has a timeout, so it will + * wake up eventually. AIFs and printfs are low enough + * priority that they can handle hanging out for a few seconds + * if needed. + */ + if (sc->aifthread) + wakeup(sc->aifthread); + } - xs->error = XS_NOERROR; return (1); } /* - * Take an interrupt. + * Command Processing */ -int -aac_intr(arg) - void *arg; + +/* + * Start as much queued I/O as possible on the controller + */ +void +aac_startio(struct aac_softc *sc) { - struct aac_softc *sc = arg; - u_int16_t reason; - int claimed = 0; + struct aac_command *cm; - AAC_DPRINTF(AAC_D_INTR, ("aac_intr(%p) ", sc)); + AAC_DPRINTF(AAC_D_CMD, ("%s: start command", sc->aac_dev.dv_xname)); - reason = AAC_GET_ISTATUS(sc); - AAC_DPRINTF(AAC_D_INTR, ("istatus 0x%04x ", reason)); - - /* controller wants to talk to the log? XXX should we defer this? */ - if (reason & AAC_DB_PRINTF) { - if (sc->sc_common->ac_printf[0]) { - printf("%s: ** %.*s", sc->sc_dev.dv_xname, - AAC_PRINTF_BUFSIZE, sc->sc_common->ac_printf); - sc->sc_common->ac_printf[0] = 0; - } - AAC_CLEAR_ISTATUS(sc, AAC_DB_PRINTF); - AAC_QNOTIFY(sc, AAC_DB_PRINTF); - claimed = 1; + if (sc->flags & AAC_QUEUE_FRZN) { + AAC_DPRINTF(AAC_D_CMD, (": queue frozen")); + return; } - /* Controller has a message for us? */ - if (reason & AAC_DB_COMMAND_READY) { - aac_host_command(sc); - AAC_CLEAR_ISTATUS(sc, AAC_DB_COMMAND_READY); - claimed = 1; - } - - /* Controller has a response for us? */ - if (reason & AAC_DB_RESPONSE_READY) { - aac_host_response(sc); - AAC_CLEAR_ISTATUS(sc, AAC_DB_RESPONSE_READY); - claimed = 1; + AAC_DPRINTF(AAC_D_CMD, ("\n")); + + for (;;) { + /* + * Try to get a command that's been put off for lack of + * resources + */ + cm = aac_dequeue_ready(sc); + + /* + * Try to build a command off the bio queue (ignore error + * return) + */ + if (cm == NULL) { + AAC_DPRINTF(AAC_D_CMD, ("\n")); + aac_bio_command(sc, &cm); + AAC_DPRINTF(AAC_D_CMD, ("%s: start done bio", + sc->aac_dev.dv_xname)); + } + + /* nothing to do? */ + if (cm == NULL) + break; + + /* + * Try to give the command to the controller. Any error is + * catastrophic since it means that bus_dmamap_load() failed. + */ + if (aac_map_command(cm) != 0) + panic("aac: error mapping command %p\n", cm); + + AAC_DPRINTF(AAC_D_CMD, ("\n%s: another command", + sc->aac_dev.dv_xname)); } - /* - * Spurious interrupts that we don't use - reset the mask and clear - * the interrupts. - */ - if (reason & (AAC_DB_SYNC_COMMAND | AAC_DB_COMMAND_NOT_FULL | - AAC_DB_RESPONSE_NOT_FULL)) { - AAC_UNMASK_INTERRUPTS(sc); - AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND | - AAC_DB_COMMAND_NOT_FULL | AAC_DB_RESPONSE_NOT_FULL); - claimed = 1; + AAC_DPRINTF(AAC_D_CMD, ("\n")); +} + +/* + * Deliver a command to the controller; allocate controller resources at the + * last moment when possible. + */ +int +aac_map_command(struct aac_command *cm) +{ + struct aac_softc *sc = cm->cm_sc; + int error = 0; + + AAC_DPRINTF(AAC_D_CMD, (": map command")); + + /* don't map more than once */ + if (cm->cm_flags & AAC_CMD_MAPPED) + panic("aac: command %p already mapped", cm); + + if (cm->cm_datalen != 0) { + error = bus_dmamap_load(sc->aac_dmat, cm->cm_datamap, + cm->cm_data, cm->cm_datalen, NULL, + BUS_DMA_NOWAIT); + if (error) + return (error); + + aac_map_command_sg(cm, cm->cm_datamap->dm_segs, + cm->cm_datamap->dm_nsegs, 0); + } else { + aac_map_command_sg(cm, NULL, 0, 0); } - return (claimed); + return (error); } /* * Handle notification of one or more FIBs coming from the controller. */ void -aac_host_command(struct aac_softc *sc) +aac_command_thread(void *arg) { + struct aac_softc *sc = arg; struct aac_fib *fib; u_int32_t fib_size; + int size, retval; + + AAC_DPRINTF(AAC_D_THREAD, ("%s: aac_command_thread: starting\n", + sc->aac_dev.dv_xname)); + AAC_LOCK_ACQUIRE(&sc->aac_io_lock); + sc->aifflags = AAC_AIFFLAGS_RUNNING; + + while ((sc->aifflags & AAC_AIFFLAGS_EXIT) == 0) { + + AAC_DPRINTF(AAC_D_THREAD, + ("%s: aac_command_thread: aifflags=%#x\n", + sc->aac_dev.dv_xname, sc->aifflags)); + retval = 0; + + if ((sc->aifflags & AAC_AIFFLAGS_PENDING) == 0) { + AAC_DPRINTF(AAC_D_THREAD, + ("%s: command thread sleeping\n", + sc->aac_dev.dv_xname)); + AAC_LOCK_RELEASE(&sc->aac_io_lock); + retval = tsleep(sc->aifthread, PRIBIO, "aifthd", + AAC_PERIODIC_INTERVAL * hz); + AAC_LOCK_ACQUIRE(&sc->aac_io_lock); + } - for (;;) { - if (aac_dequeue_fib(sc, AAC_HOST_NORM_CMD_QUEUE, &fib_size, - &fib)) - break; /* nothing to do */ + if ((sc->aifflags & AAC_AIFFLAGS_COMPLETE) != 0) { + aac_complete(sc); + sc->aifflags &= ~AAC_AIFFLAGS_COMPLETE; + } - switch(fib->Header.Command) { - case AifRequest: -#if 0 - aac_handle_aif(sc, - (struct aac_aif_command *)&fib->data[0]); -#endif + /* + * While we're here, check to see if any commands are stuck. + * This is pretty low-priority, so it's ok if it doesn't + * always fire. + */ + if (retval == EWOULDBLOCK) + aac_timeout(sc); - break; - default: - printf("%s: unknown command from controller\n", - sc->sc_dev.dv_xname); + /* Check the hardware printf message buffer */ + if (sc->aac_common->ac_printf[0] != 0) + aac_print_printf(sc); + + /* Also check to see if the adapter has a command for us. */ + while (aac_dequeue_fib(sc, AAC_HOST_NORM_CMD_QUEUE, + &fib_size, &fib) == 0) { + AAC_PRINT_FIB(sc, fib); - break; - } + + switch (fib->Header.Command) { + case AifRequest: + //aac_handle_aif(sc, fib); + break; + default: + printf("%s: unknown command from controller\n", + sc->aac_dev.dv_xname); + break; + } - /* XXX reply to FIBs requesting responses ?? */ - /* XXX how do we return these FIBs to the controller? */ + if ((fib->Header.XferState == 0) || + (fib->Header.StructType != AAC_FIBTYPE_TFIB)) + break; + + /* Return the AIF to the controller. */ + if (fib->Header.XferState & AAC_FIBSTATE_FROMADAP) { + fib->Header.XferState |= AAC_FIBSTATE_DONEHOST; + *(AAC_FSAStatus*)fib->data = ST_OK; + + /* XXX Compute the Size field? */ + size = fib->Header.Size; + if (size > sizeof(struct aac_fib)) { + size = sizeof(struct aac_fib); + fib->Header.Size = size; + } + + /* + * Since we did not generate this command, it + * cannot go through the normal + * enqueue->startio chain. + */ + aac_enqueue_response(sc, + AAC_ADAP_NORM_RESP_QUEUE, + fib); + } + } } + sc->aifflags &= ~AAC_AIFFLAGS_RUNNING; + AAC_LOCK_RELEASE(&sc->aac_io_lock); + +#if 0 + /* + * if we ever implement detach, we should have detach tsleep + * to wait for this thread to finish + */ + wakeup(sc->aac_dev); +#endif + + AAC_DPRINTF(AAC_D_THREAD, ("%s: aac_command_thread: exiting\n", + sc->aac_dev.dv_xname)); + kthread_exit(0); } /* - * Handle notification of one or more FIBs completed by the controller + * Process completed commands. */ void -aac_host_response(struct aac_softc *sc) +aac_complete(void *context) { - struct aac_ccb *ccb; + struct aac_softc *sc = (struct aac_softc *)context; + struct aac_command *cm; struct aac_fib *fib; u_int32_t fib_size; + AAC_DPRINTF(AAC_D_CMD, ("%s: complete", sc->aac_dev.dv_xname)); + + /* pull completed commands off the queue */ for (;;) { /* look for completed FIBs on our queue */ if (aac_dequeue_fib(sc, AAC_HOST_NORM_RESP_QUEUE, &fib_size, - &fib)) + &fib)) break; /* nothing to do */ - - /* get the command, unmap and queue for later processing */ - ccb = (struct aac_ccb *)fib->Header.SenderData; - if (ccb == NULL) { + + /* get the command, unmap and hand off for processing */ + cm = sc->aac_commands + fib->Header.SenderData; + if (cm == NULL) { AAC_PRINT_FIB(sc, fib); + break; + } + + aac_remove_busy(cm); + aac_unmap_command(cm); + cm->cm_flags |= AAC_CMD_COMPLETED; + + /* is there a completion handler? */ + if (cm->cm_complete != NULL) { + cm->cm_complete(cm); } else { - timeout_del(&ccb->ac_xs->stimeout); - aac_unmap_command(ccb); /* XXX defer? */ - aac_enqueue_completed(ccb); + /* assume that someone is sleeping on this command */ + wakeup(cm); } } - /* handle completion processing */ - aac_complete(sc, 0); + AAC_DPRINTF(AAC_D_CMD, ("\n")); + /* see if we can start some more I/O */ + sc->flags &= ~AAC_QUEUE_FRZN; + aac_startio(sc); } /* - * Process completed commands. + * Get a bio and build a command to go with it. */ -void -aac_complete(void *context, int pending) +int +aac_bio_command(struct aac_softc *sc, struct aac_command **cmp) { - struct aac_softc *sc = (struct aac_softc *)context; - struct aac_ccb *ccb; + struct aac_command *cm; + struct aac_fib *fib; + struct scsi_xfer *xs; + u_int8_t opcode = 0; - /* pull completed commands off the queue */ - for (;;) { - ccb = aac_dequeue_completed(sc); - if (ccb == NULL) - return; - ccb->ac_flags |= AAC_ACF_COMPLETED; + AAC_DPRINTF(AAC_D_CMD, ("%s: bio command", sc->aac_dev.dv_xname)); -#if 0 - /* is there a completion handler? */ - if (ccb->ac_complete != NULL) { - ccb->ac_complete(ccb); + /* get the resources we will need */ + if ((cm = aac_dequeue_bio(sc)) == NULL) + goto fail; + xs = cm->cm_private; + + /* build the FIB */ + fib = cm->cm_fib; + fib->Header.Size = sizeof(struct aac_fib_header); + fib->Header.XferState = + AAC_FIBSTATE_HOSTOWNED | + AAC_FIBSTATE_INITIALISED | + AAC_FIBSTATE_EMPTY | + AAC_FIBSTATE_FROMHOST | + AAC_FIBSTATE_REXPECTED | + AAC_FIBSTATE_NORM | + AAC_FIBSTATE_ASYNC | + AAC_FIBSTATE_FAST_RESPONSE; + + switch(xs->cmd->opcode) { + case READ_COMMAND: + case READ_BIG: + opcode = READ_COMMAND; + break; + case WRITE_COMMAND: + case WRITE_BIG: + opcode = WRITE_COMMAND; + break; + default: + panic("%s: invalid opcode %#x\n", sc->aac_dev.dv_xname, + xs->cmd->opcode); + } + + /* build the read/write request */ + if ((sc->flags & AAC_FLAGS_SG_64BIT) == 0) { + fib->Header.Command = ContainerCommand; + if (opcode == READ_COMMAND) { + struct aac_blockread *br; + br = (struct aac_blockread *)&fib->data[0]; + br->Command = VM_CtBlockRead; + br->ContainerId = xs->sc_link->target; + br->BlockNumber = cm->cm_blkno; + br->ByteCount = cm->cm_bcount * AAC_BLOCK_SIZE; + fib->Header.Size += sizeof(struct aac_blockread); + cm->cm_sgtable = &br->SgMap; + cm->cm_flags |= AAC_CMD_DATAIN; } else { - /* assume that someone is sleeping on this command */ - wakeup(ccb); + struct aac_blockwrite *bw; + bw = (struct aac_blockwrite *)&fib->data[0]; + bw->Command = VM_CtBlockWrite; + bw->ContainerId = xs->sc_link->target; + bw->BlockNumber = cm->cm_blkno; + bw->ByteCount = cm->cm_bcount * AAC_BLOCK_SIZE; + bw->Stable = CUNSTABLE; + fib->Header.Size += sizeof(struct aac_blockwrite); + cm->cm_flags |= AAC_CMD_DATAOUT; + cm->cm_sgtable = &bw->SgMap; + } + } else { + fib->Header.Command = ContainerCommand64; + if (opcode == READ_COMMAND) { + struct aac_blockread64 *br; + br = (struct aac_blockread64 *)&fib->data[0]; + br->Command = VM_CtHostRead64; + br->ContainerId = xs->sc_link->target; + br->BlockNumber = cm->cm_blkno; + br->SectorCount = cm->cm_bcount; + br->Pad = 0; + br->Flags = 0; + fib->Header.Size += sizeof(struct aac_blockread64); + cm->cm_flags |= AAC_CMD_DATAOUT; + (struct aac_sg_table64 *)cm->cm_sgtable = &br->SgMap64; + } else { + struct aac_blockwrite64 *bw; + bw = (struct aac_blockwrite64 *)&fib->data[0]; + bw->Command = VM_CtHostWrite64; + bw->ContainerId = xs->sc_link->target; + bw->BlockNumber = cm->cm_blkno; + bw->SectorCount = cm->cm_bcount; + bw->Pad = 0; + bw->Flags = 0; + fib->Header.Size += sizeof(struct aac_blockwrite64); + cm->cm_flags |= AAC_CMD_DATAIN; + (struct aac_sg_table64 *)cm->cm_sgtable = &bw->SgMap64; } -#else - aac_bio_complete(ccb); -#endif } + + *cmp = cm; + AAC_DPRINTF(AAC_D_CMD, ("\n")); + return(0); + +fail: + AAC_DPRINTF(AAC_D_CMD, ("\n")); + return(ENOMEM); } /* * Handle a bio-instigated command that has been completed. */ void -aac_bio_complete(struct aac_ccb *ccb) +aac_bio_complete(struct aac_command *cm) { - struct scsi_xfer *xs = ccb->ac_xs; - struct aac_softc *sc = xs->sc_link->adapter_softc; - struct buf *bp = xs->bp; struct aac_blockread_response *brr; struct aac_blockwrite_response *bwr; + struct scsi_xfer *xs = (struct scsi_xfer *)cm->cm_private; + struct buf *bp = xs->bp; AAC_FSAStatus status; + int s; + + AAC_DPRINTF(AAC_D_CMD, + ("%s: bio complete\n", cm->cm_sc->aac_dev.dv_xname)); + s = splbio(); + aac_release_command(cm); if (bp == NULL) - goto done; + goto exit; /* fetch relevant status and then release the command */ if (bp->b_flags & B_READ) { - brr = (struct aac_blockread_response *)&ccb->ac_fib->data[0]; + brr = (struct aac_blockread_response *)&cm->cm_fib->data[0]; status = brr->Status; } else { - bwr = (struct aac_blockwrite_response *)&ccb->ac_fib->data[0]; + bwr = (struct aac_blockwrite_response *)&cm->cm_fib->data[0]; status = bwr->Status; } - aac_free_ccb(sc, ccb); /* fix up the bio based on status */ if (status == ST_OK) { @@ -1058,32 +1088,634 @@ aac_bio_complete(struct aac_ccb *ccb) } else { bp->b_error = EIO; bp->b_flags |= B_ERROR; - - /* XXX be more verbose? */ - printf("%s: I/O error %d (%s)\n", sc->sc_dev.dv_xname, - status, AAC_COMMAND_STATUS(status)); + + /* pass an error string out to the disk layer */ + aac_describe_code(aac_command_status_table, status); } -done: + exit: + xs->error = XS_NOERROR; + xs->resid = 0; + xs->flags |= ITSDONE; scsi_done(xs); + splx(s); +} + +/* + * Submit a command to the controller, return when it completes. + * XXX This is very dangerous! If the card has gone out to lunch, we could + * be stuck here forever. At the same time, signals are not caught + * because there is a risk that a signal could wakeup the tsleep before + * the card has a chance to complete the command. The passed in timeout + * is ignored for the same reason. Since there is no way to cancel a + * command in progress, we should probably create a 'dead' queue where + * commands go that have been interrupted/timed-out/etc, that keeps them + * out of the free pool. That way, if the card is just slow, it won't + * spam the memory of a command that has been recycled. + */ +int +aac_wait_command(struct aac_command *cm, int timeout) +{ + struct aac_softc *sc = cm->cm_sc; + int error = 0; + + AAC_DPRINTF(AAC_D_CMD, (": wait for command")); + + /* Put the command on the ready queue and get things going */ + cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE; + aac_enqueue_ready(cm); + AAC_DPRINTF(AAC_D_CMD, ("\n")); + aac_startio(sc); + while (!(cm->cm_flags & AAC_CMD_COMPLETED) && (error != EWOULDBLOCK)) { + AAC_DPRINTF(AAC_D_MISC, ("%s: sleeping until command done\n", + sc->aac_dev.dv_xname)); + AAC_LOCK_RELEASE(&sc->aac_io_lock); + error = tsleep(cm, PRIBIO, "aacwait", timeout); + AAC_LOCK_ACQUIRE(&sc->aac_io_lock); + } + return (error); +} + +/* + *Command Buffer Management + */ + +/* + * Allocate a command. + */ +int +aac_alloc_command(struct aac_softc *sc, struct aac_command **cmp) +{ + struct aac_command *cm; + + AAC_DPRINTF(AAC_D_CMD, (": allocate command")); + if ((cm = aac_dequeue_free(sc)) == NULL) { + AAC_DPRINTF(AAC_D_CMD, (" failed")); + return (EBUSY); + } + + *cmp = cm; + return(0); +} + +/* + * Release a command back to the freelist. + */ +void +aac_release_command(struct aac_command *cm) +{ + AAC_DPRINTF(AAC_D_CMD, (": release command")); + + /* (re)initialise the command/FIB */ + cm->cm_sgtable = NULL; + cm->cm_flags = 0; + cm->cm_complete = NULL; + cm->cm_private = NULL; + cm->cm_fib->Header.XferState = AAC_FIBSTATE_EMPTY; + cm->cm_fib->Header.StructType = AAC_FIBTYPE_TFIB; + cm->cm_fib->Header.Flags = 0; + cm->cm_fib->Header.SenderSize = sizeof(struct aac_fib); + + /* + * These are duplicated in aac_start to cover the case where an + * intermediate stage may have destroyed them. They're left + * initialised here for debugging purposes only. + */ + cm->cm_fib->Header.ReceiverFibAddress = (u_int32_t)cm->cm_fibphys; + cm->cm_fib->Header.SenderData = 0; + + aac_enqueue_free(cm); +} + +/* + * Allocate and initialise commands/FIBs for this adapter. + */ +int +aac_alloc_commands(struct aac_softc *sc) +{ + struct aac_command *cm; + struct aac_fibmap *fm; + int i, error; + + if (sc->total_fibs + AAC_FIB_COUNT > sc->aac_max_fibs) + return (ENOMEM); + + fm = malloc(sizeof(struct aac_fibmap), M_DEVBUF, M_NOWAIT); + if (fm == NULL) + goto exit; + bzero(fm, sizeof(struct aac_fibmap)); + + /* allocate the FIBs in DMAable memory and load them */ + if (bus_dmamem_alloc(sc->aac_dmat, AAC_FIBMAP_SIZE, PAGE_SIZE, 0, + &fm->aac_seg, 1, &fm->aac_nsegs, BUS_DMA_NOWAIT)) { + printf("%s: can't alloc FIBs\n", sc->aac_dev.dv_xname); + error = ENOBUFS; + goto exit_alloc; + } + + if (bus_dmamem_map(sc->aac_dmat, &fm->aac_seg, 1, + AAC_FIBMAP_SIZE, (caddr_t *)&fm->aac_fibs, BUS_DMA_NOWAIT)) { + printf("%s: can't map FIB structure\n", sc->aac_dev.dv_xname); + error = ENOBUFS; + goto exit_map; + } + + if (bus_dmamap_create(sc->aac_dmat, AAC_FIBMAP_SIZE, 1, + AAC_FIBMAP_SIZE, 0, BUS_DMA_NOWAIT, &fm->aac_fibmap)) { + printf("%s: can't create dma map\n", sc->aac_dev.dv_xname); + error = ENOBUFS; + goto exit_create; + } + + if (bus_dmamap_load(sc->aac_dmat, fm->aac_fibmap, fm->aac_fibs, + AAC_FIBMAP_SIZE, NULL, BUS_DMA_NOWAIT)) { + printf("%s: can't load dma map\n", sc->aac_dev.dv_xname); + error = ENOBUFS; + goto exit_load; + } + + /* initialise constant fields in the command structure */ + AAC_LOCK_ACQUIRE(&sc->aac_io_lock); + bzero(fm->aac_fibs, AAC_FIB_COUNT * sizeof(struct aac_fib)); + for (i = 0; i < AAC_FIB_COUNT; i++) { + cm = sc->aac_commands + sc->total_fibs; + fm->aac_commands = cm; + cm->cm_sc = sc; + cm->cm_fib = fm->aac_fibs + i; + cm->cm_fibphys = fm->aac_fibmap->dm_segs[0].ds_addr + + (i * sizeof(struct aac_fib)); + cm->cm_index = sc->total_fibs; + + if (bus_dmamap_create(sc->aac_dmat, MAXBSIZE, AAC_MAXSGENTRIES, + MAXBSIZE, 0, BUS_DMA_NOWAIT, &cm->cm_datamap)) { + break; + } + aac_release_command(cm); + sc->total_fibs++; + } + + if (i > 0) { + TAILQ_INSERT_TAIL(&sc->aac_fibmap_tqh, fm, fm_link); + AAC_DPRINTF(AAC_D_MISC, ("%s: total_fibs= %d\n", + sc->aac_dev.dv_xname, + sc->total_fibs)); + AAC_LOCK_RELEASE(&sc->aac_io_lock); + return (0); + } + + exit_load: + bus_dmamap_destroy(sc->aac_dmat, fm->aac_fibmap); + exit_create: + bus_dmamem_unmap(sc->aac_dmat, (caddr_t)fm->aac_fibs, AAC_FIBMAP_SIZE); + exit_map: + bus_dmamem_free(sc->aac_dmat, &fm->aac_seg, fm->aac_nsegs); + exit_alloc: + free(fm, M_DEVBUF); + exit: + AAC_LOCK_RELEASE(&sc->aac_io_lock); + return (error); +} + +/* + * Free FIBs owned by this adapter. + */ +void +aac_free_commands(struct aac_softc *sc) +{ + struct aac_fibmap *fm; + struct aac_command *cm; + int i; + + while ((fm = TAILQ_FIRST(&sc->aac_fibmap_tqh)) != NULL) { + + TAILQ_REMOVE(&sc->aac_fibmap_tqh, fm, fm_link); + + /* + * We check against total_fibs to handle partially + * allocated blocks. + */ + for (i = 0; i < AAC_FIB_COUNT && sc->total_fibs--; i++) { + cm = fm->aac_commands + i; + bus_dmamap_destroy(sc->aac_dmat, cm->cm_datamap); + } + + bus_dmamap_unload(sc->aac_dmat, fm->aac_fibmap); + bus_dmamap_destroy(sc->aac_dmat, fm->aac_fibmap); + bus_dmamem_unmap(sc->aac_dmat, (caddr_t)fm->aac_fibs, + AAC_FIBMAP_SIZE); + bus_dmamem_free(sc->aac_dmat, &fm->aac_seg, fm->aac_nsegs); + free(fm, M_DEVBUF); + } +} + + +/* + * Command-mapping helper function - populate this command's s/g table. + */ +void +aac_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct aac_command *cm = arg; + struct aac_softc *sc = cm->cm_sc; + struct aac_fib *fib = cm->cm_fib; + int i; + + /* copy into the FIB */ + if (cm->cm_sgtable != NULL) { + if ((cm->cm_sc->flags & AAC_FLAGS_SG_64BIT) == 0) { + struct aac_sg_table *sg = cm->cm_sgtable; + sg->SgCount = nseg; + for (i = 0; i < nseg; i++) { + sg->SgEntry[i].SgAddress = segs[i].ds_addr; + sg->SgEntry[i].SgByteCount = segs[i].ds_len; + } + /* update the FIB size for the s/g count */ + fib->Header.Size += nseg * sizeof(struct aac_sg_entry); + } else { + struct aac_sg_table64 *sg; + sg = (struct aac_sg_table64 *)cm->cm_sgtable; + sg->SgCount = nseg; + for (i = 0; i < nseg; i++) { + sg->SgEntry64[i].SgAddress = segs[i].ds_addr; + sg->SgEntry64[i].SgByteCount = segs[i].ds_len; + } + /* update the FIB size for the s/g count */ + fib->Header.Size += nseg*sizeof(struct aac_sg_entry64); + } + } + + /* Fix up the address values in the FIB. Use the command array index + * instead of a pointer since these fields are only 32 bits. Shift + * the SenderFibAddress over to make room for the fast response bit. + */ + cm->cm_fib->Header.SenderFibAddress = (cm->cm_index << 1); + cm->cm_fib->Header.ReceiverFibAddress = cm->cm_fibphys; + + /* save a pointer to the command for speedy reverse-lookup */ + cm->cm_fib->Header.SenderData = cm->cm_index; + + if (cm->cm_flags & AAC_CMD_DATAIN) + bus_dmamap_sync(sc->aac_dmat, cm->cm_datamap, 0, + cm->cm_datamap->dm_mapsize, + BUS_DMASYNC_PREREAD); + if (cm->cm_flags & AAC_CMD_DATAOUT) + bus_dmamap_sync(sc->aac_dmat, cm->cm_datamap, 0, + cm->cm_datamap->dm_mapsize, + BUS_DMASYNC_PREWRITE); + cm->cm_flags |= AAC_CMD_MAPPED; + + /* put the FIB on the outbound queue */ + if (aac_enqueue_fib(sc, cm->cm_queue, cm) == EBUSY) { + aac_remove_busy(cm); + aac_unmap_command(cm); + aac_requeue_ready(cm); + } +} + +/* + * Unmap a command from controller-visible space. + */ +void +aac_unmap_command(struct aac_command *cm) +{ + struct aac_softc *sc = cm->cm_sc; + + if (!(cm->cm_flags & AAC_CMD_MAPPED)) + return; + + if (cm->cm_datalen != 0) { + if (cm->cm_flags & AAC_CMD_DATAIN) + bus_dmamap_sync(sc->aac_dmat, cm->cm_datamap, 0, + cm->cm_datamap->dm_mapsize, + BUS_DMASYNC_POSTREAD); + if (cm->cm_flags & AAC_CMD_DATAOUT) + bus_dmamap_sync(sc->aac_dmat, cm->cm_datamap, 0, + cm->cm_datamap->dm_mapsize, + BUS_DMASYNC_POSTWRITE); + + bus_dmamap_unload(sc->aac_dmat, cm->cm_datamap); + } + cm->cm_flags &= ~AAC_CMD_MAPPED; +} + +/* + * Hardware Interface + */ + +/* + * Initialise the adapter. + */ +int +aac_check_firmware(struct aac_softc *sc) +{ + u_int32_t major, minor, options; + + /* + * Retrieve the firmware version numbers. Dell PERC2/QC cards with + * firmware version 1.x are not compatible with this driver. + */ + if (sc->flags & AAC_FLAGS_PERC2QC) { + if (aac_sync_command(sc, AAC_MONKER_GETKERNVER, 0, 0, 0, 0, + NULL)) { + printf("%s: Error reading firmware version\n", + sc->aac_dev.dv_xname); + return (EIO); + } + + /* These numbers are stored as ASCII! */ + major = (AAC_GET_MAILBOX(sc, 1) & 0xff) - 0x30; + minor = (AAC_GET_MAILBOX(sc, 2) & 0xff) - 0x30; + if (major == 1) { + printf("%s: Firmware version %d.%d is not supported\n", + sc->aac_dev.dv_xname, major, minor); + return (EINVAL); + } + } + + /* + * Retrieve the capabilities/supported options word so we know what + * work-arounds to enable. + */ + if (aac_sync_command(sc, AAC_MONKER_GETINFO, 0, 0, 0, 0, NULL)) { + printf("%s: RequestAdapterInfo failed\n", + sc->aac_dev.dv_xname); + return (EIO); + } + options = AAC_GET_MAILBOX(sc, 1); + sc->supported_options = options; + + if ((options & AAC_SUPPORTED_4GB_WINDOW) != 0 && + (sc->flags & AAC_FLAGS_NO4GB) == 0) + sc->flags |= AAC_FLAGS_4GB_WINDOW; + if (options & AAC_SUPPORTED_NONDASD) + sc->flags |= AAC_FLAGS_ENABLE_CAM; + if ((options & AAC_SUPPORTED_SGMAP_HOST64) != 0 + && (sizeof(bus_addr_t) > 4)) { + printf("%s: Enabling 64-bit address support\n", + sc->aac_dev.dv_xname); + sc->flags |= AAC_FLAGS_SG_64BIT; + } + + /* Check for broken hardware that does a lower number of commands */ + if ((sc->flags & AAC_FLAGS_256FIBS) == 0) + sc->aac_max_fibs = AAC_MAX_FIBS; + else + sc->aac_max_fibs = 256; + + return (0); +} + +int +aac_init(struct aac_softc *sc) +{ + bus_dma_segment_t seg; + int nsegs; + int i, error; + int state = 0; + struct aac_adapter_init *ip; + time_t then; + u_int32_t code, qoffset; + + /* + * First wait for the adapter to come ready. + */ + then = time_second; + for (i = 0; i < AAC_BOOT_TIMEOUT * 1000; i++) { + code = AAC_GET_FWSTATUS(sc); + if (code & AAC_SELF_TEST_FAILED) { + printf("%s: FATAL: selftest failed\n", + sc->aac_dev.dv_xname); + return (ENXIO); + } + if (code & AAC_KERNEL_PANIC) { + printf("%s: FATAL: controller kernel panic\n", + sc->aac_dev.dv_xname); + return (ENXIO); + } + if (code & AAC_UP_AND_RUNNING) + break; + DELAY(1000); + } + if (i == AAC_BOOT_TIMEOUT * 1000) { + printf("%s: FATAL: controller not coming ready, status %x\n", + sc->aac_dev.dv_xname, code); + return (ENXIO); + } + + /* + * Work around a bug in the 2120 and 2200 that cannot DMA commands + * below address 8192 in physical memory. + * XXX If the padding is not needed, can it be put to use instead + * of ignored? + */ + if (bus_dmamem_alloc(sc->aac_dmat, AAC_COMMON_ALLOCSIZE, PAGE_SIZE, 0, + &seg, 1, &nsegs, BUS_DMA_NOWAIT)) { + printf("%s: can't allocate common structure\n", + sc->aac_dev.dv_xname); + return (ENOMEM); + } + state++; + + if (bus_dmamem_map(sc->aac_dmat, &seg, nsegs, AAC_COMMON_ALLOCSIZE, + (caddr_t *)&sc->aac_common, BUS_DMA_NOWAIT)) { + printf("%s: can't map common structure\n", + sc->aac_dev.dv_xname); + error = ENOMEM; + goto bail_out; + } + state++; + + if (bus_dmamap_create(sc->aac_dmat, AAC_COMMON_ALLOCSIZE, 1, + AAC_COMMON_ALLOCSIZE, 0, BUS_DMA_NOWAIT, &sc->aac_common_map)) { + printf("%s: can't create dma map\n", sc->aac_dev.dv_xname); + error = ENOBUFS; + goto bail_out; + } + state++; + + if (bus_dmamap_load(sc->aac_dmat, sc->aac_common_map, sc->aac_common, + AAC_COMMON_ALLOCSIZE, NULL, BUS_DMA_NOWAIT)) { + printf("%s: can't load dma map\n", sc->aac_dev.dv_xname); + error = ENOBUFS; + goto bail_out; + } + state++; + + sc->aac_common_busaddr = sc->aac_common_map->dm_segs[0].ds_addr; + + if (sc->aac_common_busaddr < 8192) { + (uint8_t *)sc->aac_common += 8192; + sc->aac_common_busaddr += 8192; + } + bzero(sc->aac_common, sizeof *sc->aac_common); + + /* Allocate some FIBs and associated command structs */ + TAILQ_INIT(&sc->aac_fibmap_tqh); + sc->aac_commands = malloc(AAC_MAX_FIBS * sizeof(struct aac_command), + M_DEVBUF, M_WAITOK); + bzero(sc->aac_commands, AAC_MAX_FIBS * sizeof(struct aac_command)); + while (sc->total_fibs < AAC_MAX_FIBS) { + if (aac_alloc_commands(sc) != 0) + break; + } + if (sc->total_fibs == 0) + goto out; + + /* + * Fill in the init structure. This tells the adapter about the + * physical location of various important shared data structures. + */ + ip = &sc->aac_common->ac_init; + ip->InitStructRevision = AAC_INIT_STRUCT_REVISION; + ip->MiniPortRevision = AAC_INIT_STRUCT_MINIPORT_REVISION; + + ip->AdapterFibsPhysicalAddress = sc->aac_common_busaddr + + offsetof(struct aac_common, ac_fibs); + ip->AdapterFibsVirtualAddress = 0; + ip->AdapterFibsSize = AAC_ADAPTER_FIBS * sizeof(struct aac_fib); + ip->AdapterFibAlign = sizeof(struct aac_fib); + + ip->PrintfBufferAddress = sc->aac_common_busaddr + + offsetof(struct aac_common, ac_printf); + ip->PrintfBufferSize = AAC_PRINTF_BUFSIZE; + + /* + * The adapter assumes that pages are 4K in size, except on some + * broken firmware versions that do the page->byte conversion twice, + * therefore 'assuming' that this value is in 16MB units (2^24). + * Round up since the granularity is so high. + */ + ip->HostPhysMemPages = ctob(physmem) / AAC_PAGE_SIZE; + if (sc->flags & AAC_FLAGS_BROKEN_MEMMAP) { + ip->HostPhysMemPages = + (ip->HostPhysMemPages + AAC_PAGE_SIZE) / AAC_PAGE_SIZE; + } + ip->HostElapsedSeconds = time_second; /* reset later if invalid */ + + /* + * Initialise FIB queues. Note that it appears that the layout of the + * indexes and the segmentation of the entries may be mandated by the + * adapter, which is only told about the base of the queue index fields. + * + * The initial values of the indices are assumed to inform the adapter + * of the sizes of the respective queues, and theoretically it could + * work out the entire layout of the queue structures from this. We + * take the easy route and just lay this area out like everyone else + * does. + * + * The Linux driver uses a much more complex scheme whereby several + * header records are kept for each queue. We use a couple of generic + * list manipulation functions which 'know' the size of each list by + * virtue of a table. + */ + qoffset = offsetof(struct aac_common, ac_qbuf) + AAC_QUEUE_ALIGN; + qoffset &= ~(AAC_QUEUE_ALIGN - 1); + sc->aac_queues = + (struct aac_queue_table *)((caddr_t)sc->aac_common + qoffset); + ip->CommHeaderAddress = sc->aac_common_busaddr + qoffset; + + sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] = + AAC_HOST_NORM_CMD_ENTRIES; + sc->aac_queues->qt_qindex[AAC_HOST_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] = + AAC_HOST_NORM_CMD_ENTRIES; + sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] = + AAC_HOST_HIGH_CMD_ENTRIES; + sc->aac_queues->qt_qindex[AAC_HOST_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] = + AAC_HOST_HIGH_CMD_ENTRIES; + sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_PRODUCER_INDEX] = + AAC_ADAP_NORM_CMD_ENTRIES; + sc->aac_queues->qt_qindex[AAC_ADAP_NORM_CMD_QUEUE][AAC_CONSUMER_INDEX] = + AAC_ADAP_NORM_CMD_ENTRIES; + sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_PRODUCER_INDEX] = + AAC_ADAP_HIGH_CMD_ENTRIES; + sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_CMD_QUEUE][AAC_CONSUMER_INDEX] = + AAC_ADAP_HIGH_CMD_ENTRIES; + sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]= + AAC_HOST_NORM_RESP_ENTRIES; + sc->aac_queues->qt_qindex[AAC_HOST_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]= + AAC_HOST_NORM_RESP_ENTRIES; + sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]= + AAC_HOST_HIGH_RESP_ENTRIES; + sc->aac_queues->qt_qindex[AAC_HOST_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]= + AAC_HOST_HIGH_RESP_ENTRIES; + sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_PRODUCER_INDEX]= + AAC_ADAP_NORM_RESP_ENTRIES; + sc->aac_queues->qt_qindex[AAC_ADAP_NORM_RESP_QUEUE][AAC_CONSUMER_INDEX]= + AAC_ADAP_NORM_RESP_ENTRIES; + sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX]= + AAC_ADAP_HIGH_RESP_ENTRIES; + sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX]= + AAC_ADAP_HIGH_RESP_ENTRIES; + sc->aac_qentries[AAC_HOST_NORM_CMD_QUEUE] = + &sc->aac_queues->qt_HostNormCmdQueue[0]; + sc->aac_qentries[AAC_HOST_HIGH_CMD_QUEUE] = + &sc->aac_queues->qt_HostHighCmdQueue[0]; + sc->aac_qentries[AAC_ADAP_NORM_CMD_QUEUE] = + &sc->aac_queues->qt_AdapNormCmdQueue[0]; + sc->aac_qentries[AAC_ADAP_HIGH_CMD_QUEUE] = + &sc->aac_queues->qt_AdapHighCmdQueue[0]; + sc->aac_qentries[AAC_HOST_NORM_RESP_QUEUE] = + &sc->aac_queues->qt_HostNormRespQueue[0]; + sc->aac_qentries[AAC_HOST_HIGH_RESP_QUEUE] = + &sc->aac_queues->qt_HostHighRespQueue[0]; + sc->aac_qentries[AAC_ADAP_NORM_RESP_QUEUE] = + &sc->aac_queues->qt_AdapNormRespQueue[0]; + sc->aac_qentries[AAC_ADAP_HIGH_RESP_QUEUE] = + &sc->aac_queues->qt_AdapHighRespQueue[0]; + + /* + * Do controller-type-specific initialisation + */ + switch (sc->aac_hwif) { + case AAC_HWIF_I960RX: + AAC_SETREG4(sc, AAC_RX_ODBR, ~0); + break; + } + + /* + * Give the init structure to the controller. + */ + if (aac_sync_command(sc, AAC_MONKER_INITSTRUCT, + sc->aac_common_busaddr + + offsetof(struct aac_common, ac_init), 0, 0, 0, + NULL)) { + printf("%s: error establishing init structure\n", + sc->aac_dev.dv_xname); + error = EIO; + goto bail_out; + } + + aac_describe_controller(sc); + aac_startup(sc); + + return (0); + + bail_out: + if (state > 3) + bus_dmamap_unload(sc->aac_dmat, sc->aac_common_map); + if (state > 2) + bus_dmamap_destroy(sc->aac_dmat, sc->aac_common_map); + if (state > 1) + bus_dmamem_unmap(sc->aac_dmat, (caddr_t)sc->aac_common, + sizeof *sc->aac_common); + if (state > 0) + bus_dmamem_free(sc->aac_dmat, &seg, 1); + + out: + return (error); } /* * Send a synchronous command to the controller and wait for a result. */ int -aac_sync_command(sc, command, arg0, arg1, arg2, arg3, sp) - struct aac_softc *sc; - u_int32_t command; - u_int32_t arg0; - u_int32_t arg1; - u_int32_t arg2; - u_int32_t arg3; - u_int32_t *sp; +aac_sync_command(struct aac_softc *sc, u_int32_t command, u_int32_t arg0, + u_int32_t arg1, u_int32_t arg2, u_int32_t arg3, u_int32_t *sp) { +// time_t then; int i; u_int32_t status; - aac_lock_t lock = AAC_LOCK(sc); + u_int16_t reason; /* populate the mailbox */ AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3); @@ -1093,188 +1725,485 @@ aac_sync_command(sc, command, arg0, arg1, arg2, arg3, sp) /* then set it to signal the adapter */ AAC_QNOTIFY(sc, AAC_DB_SYNC_COMMAND); + +#if 0 + /* spin waiting for the command to complete */ + then = time_second; + do { + if (time_second > (then + AAC_IMMEDIATE_TIMEOUT)) { + AAC_DPRINTF(AAC_D_MISC, ("timed out")); + return(EIO); + } + } while (!(AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND)); +#else DELAY(AAC_SYNC_DELAY); /* spin waiting for the command to complete */ for (i = 0; i < AAC_IMMEDIATE_TIMEOUT * 1000; i++) { - if (AAC_GET_ISTATUS(sc) & AAC_DB_SYNC_COMMAND) + reason = AAC_GET_ISTATUS(sc); + if (reason & AAC_DB_SYNC_COMMAND) + break; + reason = AAC_GET_ISTATUS(sc); + if (reason & AAC_DB_SYNC_COMMAND) + break; + reason = AAC_GET_ISTATUS(sc); + if (reason & AAC_DB_SYNC_COMMAND) break; DELAY(1000); } if (i == AAC_IMMEDIATE_TIMEOUT * 1000) { - AAC_UNLOCK(sc, lock); + printf("aac_sync_command: failed, reason=%#x\n", reason); return (EIO); } +#endif /* clear the completion flag */ AAC_CLEAR_ISTATUS(sc, AAC_DB_SYNC_COMMAND); /* get the command status */ - status = AAC_GET_MAILBOXSTATUS(sc); - AAC_UNLOCK(sc, lock); + status = AAC_GET_MAILBOX(sc, 0); + if (sp != NULL) *sp = status; - return (0); /* check command return status? */ + + return(0); +} + +/* + * Grab the sync fib area. + */ +int +aac_alloc_sync_fib(struct aac_softc *sc, struct aac_fib **fib, int flags) +{ + + /* + * If the force flag is set, the system is shutting down, or in + * trouble. Ignore the mutex. + */ + if (!(flags & AAC_SYNC_LOCK_FORCE)) + AAC_LOCK_ACQUIRE(&sc->aac_sync_lock); + + *fib = &sc->aac_common->ac_sync_fib; + + return (1); +} + +/* + * Release the sync fib area. + */ +void +aac_release_sync_fib(struct aac_softc *sc) +{ + AAC_LOCK_RELEASE(&sc->aac_sync_lock); } /* * Send a synchronous FIB to the controller and wait for a result. */ int -aac_sync_fib(sc, command, xferstate, data, datasize, result, resultsize) - struct aac_softc *sc; - u_int32_t command; - u_int32_t xferstate; - void *data; - u_int16_t datasize; - void *result; - u_int16_t *resultsize; +aac_sync_fib(struct aac_softc *sc, u_int32_t command, u_int32_t xferstate, + struct aac_fib *fib, u_int16_t datasize) { - struct aac_fib *fib = &sc->sc_common->ac_sync_fib; - if (datasize > AAC_FIB_DATASIZE) - return (EINVAL); + if (datasize > AAC_FIB_DATASIZE) { + printf("aac_sync_fib 1: datasize=%d AAC_FIB_DATASIZE\n", + datasize, AAC_FIB_DATASIZE); + return(EINVAL); + } /* * Set up the sync FIB */ fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED | - AAC_FIBSTATE_INITIALISED | AAC_FIBSTATE_EMPTY; + AAC_FIBSTATE_INITIALISED | + AAC_FIBSTATE_EMPTY; fib->Header.XferState |= xferstate; fib->Header.Command = command; fib->Header.StructType = AAC_FIBTYPE_TFIB; - fib->Header.Size = sizeof fib + datasize; - fib->Header.SenderSize = sizeof *fib; - fib->Header.SenderFibAddress = (u_int32_t)fib; - fib->Header.ReceiverFibAddress = - sc->sc_common_busaddr + offsetof(struct aac_common, ac_sync_fib); + fib->Header.Size = sizeof(struct aac_fib) + datasize; + fib->Header.SenderSize = sizeof(struct aac_fib); + fib->Header.SenderFibAddress = 0; /* Not needed */ + fib->Header.ReceiverFibAddress = sc->aac_common_busaddr + + offsetof(struct aac_common, + ac_sync_fib); /* - * Copy in data. + * Give the FIB to the controller, wait for a response. */ - if (data != NULL) { - bcopy(data, fib->data, datasize); - fib->Header.XferState |= - AAC_FIBSTATE_FROMHOST | AAC_FIBSTATE_NORM; + if (aac_sync_command(sc, AAC_MONKER_SYNCFIB, + fib->Header.ReceiverFibAddress, 0, 0, 0, NULL)) { + AAC_DPRINTF(AAC_D_IO, ("%s: aac_sync_fib: IO error\n", + sc->aac_dev.dv_xname)); + printf("aac_sync_fib 2\n"); + return(EIO); + } + + return (0); +} + +/***************************************************************************** + * Adapter-space FIB queue manipulation + * + * Note that the queue implementation here is a little funky; neither the PI or + * CI will ever be zero. This behaviour is a controller feature. + */ +static struct { + int size; + int notify; +} aac_qinfo[] = { + { AAC_HOST_NORM_CMD_ENTRIES, AAC_DB_COMMAND_NOT_FULL }, + { AAC_HOST_HIGH_CMD_ENTRIES, 0 }, + { AAC_ADAP_NORM_CMD_ENTRIES, AAC_DB_COMMAND_READY }, + { AAC_ADAP_HIGH_CMD_ENTRIES, 0 }, + { AAC_HOST_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_NOT_FULL }, + { AAC_HOST_HIGH_RESP_ENTRIES, 0 }, + { AAC_ADAP_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_READY }, + { AAC_ADAP_HIGH_RESP_ENTRIES, 0 } +}; + +/* + * Atomically insert an entry into the nominated queue, returns 0 on success + * or EBUSY if the queue is full. + * + * Note: it would be more efficient to defer notifying the controller in + * the case where we may be inserting several entries in rapid + * succession, but implementing this usefully may be difficult + * (it would involve a separate queue/notify interface). + */ +int +aac_enqueue_fib(struct aac_softc *sc, int queue, struct aac_command *cm) +{ + u_int32_t pi, ci; + int error; + u_int32_t fib_size; + u_int32_t fib_addr; + + fib_size = cm->cm_fib->Header.Size; + fib_addr = cm->cm_fib->Header.ReceiverFibAddress; + + /* get the producer/consumer indices */ + pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX]; + ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX]; + + /* wrap the queue? */ + if (pi >= aac_qinfo[queue].size) + pi = 0; + + /* check for queue full */ + if ((pi + 1) == ci) { + error = EBUSY; + goto out; } + /* populate queue entry */ + (sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size; + (sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr; + + /* update producer index */ + sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1; + /* - * Give the FIB to the controller, wait for a response. + * To avoid a race with its completion interrupt, place this command on + * the busy queue prior to advertising it to the controller. */ - if (aac_sync_command(sc, AAC_MONKER_SYNCFIB, - fib->Header.ReceiverFibAddress, 0, 0, 0, NULL)) { - return (EIO); + aac_enqueue_busy(cm); + + /* notify the adapter if we know how */ + if (aac_qinfo[queue].notify != 0) + AAC_QNOTIFY(sc, aac_qinfo[queue].notify); + + error = 0; + +out: + return (error); +} + +/* + * Atomically remove one entry from the nominated queue, returns 0 on success + * or ENOENT if the queue is empty. + */ +int +aac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size, + struct aac_fib **fib_addr) +{ + u_int32_t pi, ci; + u_int32_t fib_index; + int notify; + int error; + + /* get the producer/consumer indices */ + pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX]; + ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX]; + + /* check for queue empty */ + if (ci == pi) { + error = ENOENT; + goto out; } - /* - * Copy out the result - */ - if (result != NULL) { - *resultsize = fib->Header.Size - sizeof fib->Header; - bcopy(fib->data, result, *resultsize); + /* wrap the pi so the following test works */ + if (pi >= aac_qinfo[queue].size) + pi = 0; + + notify = 0; + if (ci == pi + 1) + notify++; + + /* wrap the queue? */ + if (ci >= aac_qinfo[queue].size) + ci = 0; + + /* fetch the entry */ + *fib_size = (sc->aac_qentries[queue] + ci)->aq_fib_size; + + switch (queue) { + case AAC_HOST_NORM_CMD_QUEUE: + case AAC_HOST_HIGH_CMD_QUEUE: + /* + * The aq_fib_addr is only 32 bits wide so it can't be counted + * on to hold an address. For AIF's, the adapter assumes + * that it's giving us an address into the array of AIF fibs. + * Therefore, we have to convert it to an index. + */ + fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr / + sizeof(struct aac_fib); + *fib_addr = &sc->aac_common->ac_fibs[fib_index]; + break; + + case AAC_HOST_NORM_RESP_QUEUE: + case AAC_HOST_HIGH_RESP_QUEUE: + { + struct aac_command *cm; + + /* + * As above, an index is used instead of an actual address. + * Gotta shift the index to account for the fast response + * bit. No other correction is needed since this value was + * originally provided by the driver via the SenderFibAddress + * field. + */ + fib_index = (sc->aac_qentries[queue] + ci)->aq_fib_addr; + cm = sc->aac_commands + (fib_index >> 1); + *fib_addr = cm->cm_fib; + + /* + * Is this a fast response? If it is, update the fib fields in + * local memory since the whole fib isn't DMA'd back up. + */ + if (fib_index & 0x01) { + (*fib_addr)->Header.XferState |= AAC_FIBSTATE_DONEADAP; + *((u_int32_t*)((*fib_addr)->data)) = AAC_ERROR_NORMAL; + } + break; } - return (0); + default: + panic("Invalid queue in aac_dequeue_fib()"); + break; + } + + + /* update consumer index */ + sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX] = ci + 1; + + /* if we have made the queue un-full, notify the adapter */ + if (notify && (aac_qinfo[queue].notify != 0)) + AAC_QNOTIFY(sc, aac_qinfo[queue].notify); + error = 0; + +out: + return (error); +} + +/* + * Put our response to an Adapter Initialed Fib on the response queue + */ +int +aac_enqueue_response(struct aac_softc *sc, int queue, struct aac_fib *fib) +{ + u_int32_t pi, ci; + int error; + u_int32_t fib_size; + u_int32_t fib_addr; + + /* Tell the adapter where the FIB is */ + fib_size = fib->Header.Size; + fib_addr = fib->Header.SenderFibAddress; + fib->Header.ReceiverFibAddress = fib_addr; + + /* get the producer/consumer indices */ + pi = sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX]; + ci = sc->aac_queues->qt_qindex[queue][AAC_CONSUMER_INDEX]; + + /* wrap the queue? */ + if (pi >= aac_qinfo[queue].size) + pi = 0; + + /* check for queue full */ + if ((pi + 1) == ci) { + error = EBUSY; + goto out; + } + + /* populate queue entry */ + (sc->aac_qentries[queue] + pi)->aq_fib_size = fib_size; + (sc->aac_qentries[queue] + pi)->aq_fib_addr = fib_addr; + + /* update producer index */ + sc->aac_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1; + + /* notify the adapter if we know how */ + if (aac_qinfo[queue].notify != 0) + AAC_QNOTIFY(sc, aac_qinfo[queue].notify); + + error = 0; + +out: + return(error); } void -aacminphys(bp) - struct buf *bp; +aac_command_timeout(struct aac_command *cm) { -#if 0 - u_int8_t *buf = bp->b_data; - paddr_t pa; - long off; -#endif + struct aac_softc *sc = cm->cm_sc; - AAC_DPRINTF(AAC_D_MISC, ("aacminphys(0x%x) ", bp)); + printf("%s: COMMAND %p (flags=%#x) TIMEOUT AFTER %d SECONDS\n", + sc->aac_dev.dv_xname, cm, cm->cm_flags, + (int)(time_second - cm->cm_timestamp)); - minphys(bp); + if (cm->cm_flags & AAC_CMD_TIMEDOUT) + return; + + cm->cm_flags |= AAC_CMD_TIMEDOUT; + + AAC_PRINT_FIB(sc, cm->cm_fib); + + if (cm->cm_flags & AAC_ON_AACQ_BIO) { + struct scsi_xfer *xs = cm->cm_private; + int s = splbio(); + xs->error = XS_DRIVER_STUFFUP; + xs->flags |= ITSDONE; + scsi_done(xs); + splx(s); + + aac_remove_bio(cm); + aac_unmap_command(cm); + } } +void +aac_timeout(struct aac_softc *sc) +{ + struct aac_command *cm; + time_t deadline; + + /* + * Traverse the busy command list and timeout any commands + * that are past their deadline. + */ + deadline = time_second - AAC_CMD_TIMEOUT; + TAILQ_FOREACH(cm, &sc->aac_busy, cm_link) { + if (cm->cm_timestamp < deadline) + aac_command_timeout(cm); + } +} + +/* + * Interface Function Vectors + */ + /* * Read the current firmware status word. */ int -aac_sa_get_fwstatus(sc) - struct aac_softc *sc; +aac_sa_get_fwstatus(struct aac_softc *sc) { return (AAC_GETREG4(sc, AAC_SA_FWSTATUS)); } int -aac_rx_get_fwstatus(sc) - struct aac_softc *sc; +aac_rx_get_fwstatus(struct aac_softc *sc) { return (AAC_GETREG4(sc, AAC_RX_FWSTATUS)); } +int +aac_fa_get_fwstatus(struct aac_softc *sc) +{ + return (AAC_GETREG4(sc, AAC_FA_FWSTATUS)); +} + /* * Notify the controller of a change in a given queue */ void -aac_sa_qnotify(sc, qbit) - struct aac_softc *sc; - int qbit; +aac_sa_qnotify(struct aac_softc *sc, int qbit) { AAC_SETREG2(sc, AAC_SA_DOORBELL1_SET, qbit); } void -aac_rx_qnotify(sc, qbit) - struct aac_softc *sc; - int qbit; +aac_rx_qnotify(struct aac_softc *sc, int qbit) { AAC_SETREG4(sc, AAC_RX_IDBR, qbit); } +void +aac_fa_qnotify(struct aac_softc *sc, int qbit) +{ + AAC_SETREG2(sc, AAC_FA_DOORBELL1, qbit); + AAC_FA_HACK(sc); +} + /* * Get the interrupt reason bits */ int -aac_sa_get_istatus(sc) - struct aac_softc *sc; +aac_sa_get_istatus(struct aac_softc *sc) { return (AAC_GETREG2(sc, AAC_SA_DOORBELL0)); } int -aac_rx_get_istatus(sc) - struct aac_softc *sc; +aac_rx_get_istatus(struct aac_softc *sc) { return (AAC_GETREG4(sc, AAC_RX_ODBR)); } +int +aac_fa_get_istatus(struct aac_softc *sc) +{ + return (AAC_GETREG2(sc, AAC_FA_DOORBELL0)); +} + /* * Clear some interrupt reason bits */ void -aac_sa_clear_istatus(sc, mask) - struct aac_softc *sc; - int mask; +aac_sa_clear_istatus(struct aac_softc *sc, int mask) { AAC_SETREG2(sc, AAC_SA_DOORBELL0_CLEAR, mask); } void -aac_rx_clear_istatus(sc, mask) - struct aac_softc *sc; - int mask; +aac_rx_clear_istatus(struct aac_softc *sc, int mask) { AAC_SETREG4(sc, AAC_RX_ODBR, mask); } +void +aac_fa_clear_istatus(struct aac_softc *sc, int mask) +{ + AAC_SETREG2(sc, AAC_FA_DOORBELL0_CLEAR, mask); + AAC_FA_HACK(sc); +} + /* * Populate the mailbox and set the command word */ void -aac_sa_set_mailbox(sc, command, arg0, arg1, arg2, arg3) - struct aac_softc *sc; - u_int32_t command; - u_int32_t arg0; - u_int32_t arg1; - u_int32_t arg2; - u_int32_t arg3; +aac_sa_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0, + u_int32_t arg1, u_int32_t arg2, u_int32_t arg3) { AAC_SETREG4(sc, AAC_SA_MAILBOX, command); AAC_SETREG4(sc, AAC_SA_MAILBOX + 4, arg0); @@ -1284,13 +2213,8 @@ aac_sa_set_mailbox(sc, command, arg0, arg1, arg2, arg3) } void -aac_rx_set_mailbox(sc, command, arg0, arg1, arg2, arg3) - struct aac_softc *sc; - u_int32_t command; - u_int32_t arg0; - u_int32_t arg1; - u_int32_t arg2; - u_int32_t arg3; +aac_rx_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0, + u_int32_t arg1, u_int32_t arg2, u_int32_t arg3) { AAC_SETREG4(sc, AAC_RX_MAILBOX, command); AAC_SETREG4(sc, AAC_RX_MAILBOX + 4, arg0); @@ -1299,31 +2223,52 @@ aac_rx_set_mailbox(sc, command, arg0, arg1, arg2, arg3) AAC_SETREG4(sc, AAC_RX_MAILBOX + 16, arg3); } +void +aac_fa_set_mailbox(struct aac_softc *sc, u_int32_t command, u_int32_t arg0, + u_int32_t arg1, u_int32_t arg2, u_int32_t arg3) +{ + AAC_SETREG4(sc, AAC_FA_MAILBOX, command); + AAC_FA_HACK(sc); + AAC_SETREG4(sc, AAC_FA_MAILBOX + 4, arg0); + AAC_FA_HACK(sc); + AAC_SETREG4(sc, AAC_FA_MAILBOX + 8, arg1); + AAC_FA_HACK(sc); + AAC_SETREG4(sc, AAC_FA_MAILBOX + 12, arg2); + AAC_FA_HACK(sc); + AAC_SETREG4(sc, AAC_FA_MAILBOX + 16, arg3); + AAC_FA_HACK(sc); +} + /* * Fetch the immediate command status word */ int -aac_sa_get_mailboxstatus(sc) - struct aac_softc *sc; +aac_sa_get_mailbox(struct aac_softc *sc, int mb) { - return (AAC_GETREG4(sc, AAC_SA_MAILBOX)); + return (AAC_GETREG4(sc, AAC_SA_MAILBOX + (mb * 4))); } int -aac_rx_get_mailboxstatus(sc) - struct aac_softc *sc; +aac_rx_get_mailbox(struct aac_softc *sc, int mb) { - return (AAC_GETREG4(sc, AAC_RX_MAILBOX)); + return (AAC_GETREG4(sc, AAC_RX_MAILBOX + (mb * 4))); +} + +int +aac_fa_get_mailbox(struct aac_softc *sc, int mb) +{ + return (AAC_GETREG4(sc, AAC_FA_MAILBOX + (mb * 4))); } /* * Set/clear interrupt masks */ void -aac_sa_set_interrupts(sc, enable) - struct aac_softc *sc; - int enable; +aac_sa_set_interrupts(struct aac_softc *sc, int enable) { + AAC_DPRINTF(AAC_D_INTR, ("%s: %sable interrupts\n", + sc->aac_dev.dv_xname, enable ? "en" : "dis")); + if (enable) AAC_SETREG2((sc), AAC_SA_MASK0_CLEAR, AAC_DB_INTERRUPTS); else @@ -1331,506 +2276,431 @@ aac_sa_set_interrupts(sc, enable) } void -aac_rx_set_interrupts(sc, enable) - struct aac_softc *sc; - int enable; +aac_rx_set_interrupts(struct aac_softc *sc, int enable) { + AAC_DPRINTF(AAC_D_INTR, ("%s: %sable interrupts", + sc->aac_dev.dv_xname, enable ? "en" : "dis")); + if (enable) AAC_SETREG4(sc, AAC_RX_OIMR, ~AAC_DB_INTERRUPTS); else AAC_SETREG4(sc, AAC_RX_OIMR, ~0); } -struct aac_ccb * -aac_get_ccb(sc, flags) - struct aac_softc *sc; - int flags; -{ - struct aac_ccb *ccb; - aac_lock_t lock; - - AAC_DPRINTF(AAC_D_QUEUE, ("aac_get_ccb(%p, 0x%x) ", sc, flags)); - - lock = AAC_LOCK(sc); - - for (;;) { - ccb = TAILQ_FIRST(&sc->sc_free_ccb); - if (ccb != NULL) - break; - if (flags & SCSI_NOSLEEP) - goto bail_out; - tsleep(&sc->sc_free_ccb, PRIBIO, "aac_ccb", 0); - } - - TAILQ_REMOVE(&sc->sc_free_ccb, ccb, ac_chain); - - /* initialise the command/FIB */ - ccb->ac_sgtable = NULL; - ccb->ac_flags = 0; - ccb->ac_fib->Header.XferState = AAC_FIBSTATE_EMPTY; - ccb->ac_fib->Header.StructType = AAC_FIBTYPE_TFIB; - ccb->ac_fib->Header.Flags = 0; - ccb->ac_fib->Header.SenderSize = sizeof(struct aac_fib); - - /* - * These are duplicated in aac_start to cover the case where an - * intermediate stage may have destroyed them. They're left - * initialised here for debugging purposes only. - */ - ccb->ac_fib->Header.SenderFibAddress = (u_int32_t)ccb->ac_fib; - ccb->ac_fib->Header.ReceiverFibAddress = ccb->ac_fibphys; - - bail_out: - AAC_UNLOCK(sc, lock); - return (ccb); -} - void -aac_free_ccb(sc, ccb) - struct aac_softc *sc; - struct aac_ccb *ccb; +aac_fa_set_interrupts(struct aac_softc *sc, int enable) { - aac_lock_t lock; - - AAC_DPRINTF(AAC_D_QUEUE, ("aac_free_ccb(%p, %p) ", sc, ccb)); + AAC_DPRINTF(AAC_D_INTR, ("%s: %sable interrupts", + sc->aac_dev.dv_xname, enable ? "en" : "dis")); - lock = AAC_LOCK(sc); - - TAILQ_INSERT_HEAD(&sc->sc_free_ccb, ccb, ac_chain); - - /* If the free list was empty, wake up potential waiters. */ - if (TAILQ_NEXT(ccb, ac_chain) == NULL) - wakeup(&sc->sc_free_ccb); - - AAC_UNLOCK(sc, lock); + if (enable) { + AAC_SETREG2((sc), AAC_FA_MASK0_CLEAR, AAC_DB_INTERRUPTS); + AAC_FA_HACK(sc); + } else { + AAC_SETREG2((sc), AAC_FA_MASK0, ~0); + AAC_FA_HACK(sc); + } } void -aac_enqueue_ccb(sc, ccb) - struct aac_softc *sc; - struct aac_ccb *ccb; +aac_eval_mapping(size, cyls, heads, secs) + u_int32_t size; + int *cyls, *heads, *secs; { - AAC_DPRINTF(AAC_D_QUEUE, ("aac_enqueue_ccb(%p, %p) ", sc, ccb)); - - timeout_set(&ccb->ac_xs->stimeout, aac_timeout, ccb); - TAILQ_INSERT_TAIL(&sc->sc_ccbq, ccb, ac_chain); - aac_start_ccbs(sc); + *cyls = size / AAC_HEADS / AAC_SECS; + if (*cyls < AAC_MAXCYLS) { + *heads = AAC_HEADS; + *secs = AAC_SECS; + } else { + /* Too high for 64 * 32 */ + *cyls = size / AAC_MEDHEADS / AAC_MEDSECS; + if (*cyls < AAC_MAXCYLS) { + *heads = AAC_MEDHEADS; + *secs = AAC_MEDSECS; + } else { + /* Too high for 127 * 63 */ + *cyls = size / AAC_BIGHEADS / AAC_BIGSECS; + *heads = AAC_BIGHEADS; + *secs = AAC_BIGSECS; + } + } } void -aac_start_ccbs(sc) - struct aac_softc *sc; +aac_copy_internal_data(struct scsi_xfer *xs, u_int8_t *data, size_t size) { - struct aac_ccb *ccb; - struct scsi_xfer *xs; - - AAC_DPRINTF(AAC_D_QUEUE, ("aac_start_ccbs(%p) ", sc)); - - while ((ccb = TAILQ_FIRST(&sc->sc_ccbq)) != NULL) { - - xs = ccb->ac_xs; - if (ccb->ac_flags & AAC_ACF_WATCHDOG) - timeout_del(&xs->stimeout); + struct aac_softc *sc = xs->sc_link->adapter_softc; + size_t copy_cnt; - if (aac_exec_ccb(ccb) == 0) { - ccb->ac_flags |= AAC_ACF_WATCHDOG; - timeout_set(&ccb->ac_xs->stimeout, aac_watchdog, ccb); - timeout_add(&xs->stimeout, - (AAC_WATCH_TIMEOUT * hz) / 1000); - break; - } - TAILQ_REMOVE(&sc->sc_ccbq, ccb, ac_chain); + AAC_DPRINTF(AAC_D_MISC, ("%s: aac_copy_internal_data\n", + sc->aac_dev.dv_xname)); - if ((xs->flags & SCSI_POLL) == 0) { - timeout_set(&ccb->ac_xs->stimeout, aac_timeout, ccb); - timeout_add(&xs->stimeout, - (ccb->ac_timeout * hz) / 1000); - } + if (!xs->datalen) + printf("%s: uio move not yet supported\n", + sc->aac_dev.dv_xname); + else { + copy_cnt = MIN(size, xs->datalen); + bcopy(data, xs->data, copy_cnt); } } +/* Emulated SCSI operation on cache device */ int -aac_exec_ccb(ccb) - struct aac_ccb *ccb; +aac_internal_cache_cmd(struct scsi_xfer *xs) { - struct scsi_xfer *xs = ccb->ac_xs; struct scsi_link *link = xs->sc_link; + struct aac_softc *sc = link->adapter_softc; + struct scsi_inquiry_data inq; + struct scsi_sense_data sd; + struct scsi_read_cap_data rcd; u_int8_t target = link->target; - int i; - struct aac_fib *fib; - struct aac_blockread *br; - struct aac_blockwrite *bw; - bus_dmamap_t xfer; - AAC_DPRINTF(AAC_D_CMD, ("aac_exec_ccb(%p, %p) ", xs, ccb)); - - /* build the FIB */ - fib = ccb->ac_fib; - fib->Header.XferState = AAC_FIBSTATE_HOSTOWNED | - AAC_FIBSTATE_INITIALISED | AAC_FIBSTATE_FROMHOST | - AAC_FIBSTATE_REXPECTED | AAC_FIBSTATE_NORM; - fib->Header.Command = ContainerCommand; - fib->Header.Size = sizeof(struct aac_fib_header); + AAC_DPRINTF(AAC_D_CMD, ("aac_internal_cache_cmd: ", + sc->aac_dev.dv_xname)); switch (xs->cmd->opcode) { - case PREVENT_ALLOW: - case SYNCHRONIZE_CACHE: - if (xs->cmd->opcode == PREVENT_ALLOW) { - /* XXX PREVENT_ALLOW support goes here */ - } else { - AAC_DPRINTF(AAC_D_CMD, - ("SYNCHRONIZE CACHE tgt %d ", target)); - } + case TEST_UNIT_READY: + case START_STOP: +#if 0 + case VERIFY: +#endif + AAC_DPRINTF(AAC_D_CMD, ("opc %#x tgt %d ", xs->cmd->opcode, + target)); break; - case WRITE_COMMAND: - case WRITE_BIG: - bw = (struct aac_blockwrite *)&fib->data[0]; - bw->Command = VM_CtBlockWrite; - bw->ContainerId = target; - bw->BlockNumber = ccb->ac_blockno; - bw->ByteCount = ccb->ac_blockcnt * DEV_BSIZE; - bw->Stable = CUNSTABLE; /* XXX what's appropriate here? */ - fib->Header.Size += sizeof(struct aac_blockwrite); - ccb->ac_sgtable = &bw->SgMap; + case REQUEST_SENSE: + AAC_DPRINTF(AAC_D_CMD, ("REQUEST SENSE tgt %d ", target)); + bzero(&sd, sizeof sd); + sd.error_code = 0x70; + sd.segment = 0; + sd.flags = SKEY_NO_SENSE; + aac_enc32(sd.info, 0); + sd.extra_len = 0; + aac_copy_internal_data(xs, (u_int8_t *)&sd, sizeof sd); break; - case READ_COMMAND: - case READ_BIG: - br = (struct aac_blockread *)&fib->data[0]; - br->Command = VM_CtBlockRead; - br->ContainerId = target; - br->BlockNumber = ccb->ac_blockno; - br->ByteCount = ccb->ac_blockcnt * DEV_BSIZE; - fib->Header.Size += sizeof(struct aac_blockread); - ccb->ac_sgtable = &br->SgMap; + case INQUIRY: + AAC_DPRINTF(AAC_D_CMD, ("INQUIRY tgt %d devtype %x ", target, + sc->aac_hdr[target].hd_devtype)); + bzero(&inq, sizeof inq); + /* XXX How do we detect removable/CD-ROM devices? */ + inq.device = T_DIRECT; + inq.dev_qual2 = 0; + inq.version = 2; + inq.response_format = 2; + inq.additional_length = 32; + strlcpy(inq.vendor, "Adaptec", sizeof inq.vendor); + snprintf(inq.product, sizeof inq.product, "Container #%02d", + target); + strlcpy(inq.revision, " ", sizeof inq.revision); + aac_copy_internal_data(xs, (u_int8_t *)&inq, sizeof inq); break; - } - if (xs->cmd->opcode != PREVENT_ALLOW && - xs->cmd->opcode != SYNCHRONIZE_CACHE) { - xfer = ccb->ac_dmamap_xfer; - ccb->ac_sgtable->SgCount = xfer->dm_nsegs; - for (i = 0; i < xfer->dm_nsegs; i++) { - ccb->ac_sgtable->SgEntry[i].SgAddress = - xfer->dm_segs[i].ds_addr; - ccb->ac_sgtable->SgEntry[i].SgByteCount = - xfer->dm_segs[i].ds_len; - AAC_DPRINTF(AAC_D_IO, - ("#%d va %p pa %p len %x\n", i, buf, - xfer->dm_segs[i].ds_addr, - xfer->dm_segs[i].ds_len)); - } + case READ_CAPACITY: + AAC_DPRINTF(AAC_D_CMD, ("READ CAPACITY tgt %d ", target)); + bzero(&rcd, sizeof rcd); + _lto4b(sc->aac_hdr[target].hd_size - 1, rcd.addr); + _lto4b(AAC_BLOCK_SIZE, rcd.length); + aac_copy_internal_data(xs, (u_int8_t *)&rcd, sizeof rcd); + break; - /* update the FIB size for the s/g count */ - fib->Header.Size += xfer->dm_nsegs * - sizeof(struct aac_sg_entry); + default: + AAC_DPRINTF(AAC_D_CMD, ("\n")); + printf("aac_internal_cache_cmd got bad opcode: %#x\n", + xs->cmd->opcode); + xs->error = XS_DRIVER_STUFFUP; + return (0); } - if (aac_start(ccb) == 0) { - xs->error = XS_NOERROR; - xs->resid = 0; - return (1); - } - return (0); + xs->error = XS_NOERROR; + return (1); } -/******************************************************************************** - * Deliver a command to the controller; allocate controller resources at the - * last moment when possible. - */ -int -aac_start(struct aac_ccb *ccb) +void +aacminphys(struct buf *bp) { - struct aac_softc *sc = ccb->ac_xs->sc_link->adapter_softc; - #if 0 - /* get the command mapped */ - aac_map_command(ccb); + u_int8_t *buf = bp->b_data; + paddr_t pa; + long off; #endif - /* fix up the address values */ - ccb->ac_fib->Header.SenderFibAddress = (u_int32_t)ccb->ac_fib; - ccb->ac_fib->Header.ReceiverFibAddress = ccb->ac_fibphys; - - /* save a pointer to the command for speedy reverse-lookup */ - ccb->ac_fib->Header.SenderData = (u_int32_t)ccb; /* XXX ack, sizing */ + AAC_DPRINTF(AAC_D_MISC, ("aacminphys(0x%x)\n", bp)); - /* put the FIB on the outbound queue */ - if (aac_enqueue_fib(sc, AAC_ADAP_NORM_CMD_QUEUE, ccb)) - return (EBUSY); +#if 0 /* As this is way more than MAXPHYS it's really not necessary. */ + if (bp->b_bcount > ((AAC_MAXOFFSETS - 1) * PAGE_SIZE)) + bp->b_bcount = ((AAC_MAXOFFSETS - 1) * PAGE_SIZE); +#endif - return (0); +#if 0 + for (off = PAGE_SIZE, pa = vtophys(buf); off < bp->b_bcount; + off += PAGE_SIZE) + if (pa + off != vtophys(buf + off)) { + bp->b_bcount = off; + break; + } +#endif + minphys(bp); } -/* - * Map a command into controller-visible space. - */ int -aac_map_command(struct aac_ccb *ccb) +aac_raw_scsi_cmd(struct scsi_xfer *xs) { - struct scsi_xfer *xs = ccb->ac_xs; +#ifdef AAC_DEBUG struct aac_softc *sc = xs->sc_link->adapter_softc; - int error; - -#if 0 - /* don't map more than once */ - if (ccb->ac_flags & AAC_CMD_MAPPED) - return; #endif + AAC_DPRINTF(AAC_D_CMD, ("%s: aac_raw_scsi_cmd\n", + sc->aac_dev.dv_xname)); - if (xs->datalen != 0) { - error = bus_dmamap_load(sc->sc_dmat, ccb->ac_dmamap_xfer, - xs->data, xs->datalen, NULL, - (xs->flags & SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : - BUS_DMA_WAITOK); - if (error) { - printf("%s: aac_scsi_cmd: ", sc->sc_dev.dv_xname); - if (error == EFBIG) - printf("more than %d dma segs\n", - AAC_MAXSGENTRIES); - else - printf("error %d loading dma map\n", error); - return (error); - } - - bus_dmamap_sync(sc->sc_dmat, ccb->ac_dmamap_xfer, 0, - ccb->ac_dmamap_xfer->dm_mapsize, - (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD : - BUS_DMASYNC_PREWRITE); - } - -#if 0 - ccb->ac_flags |= AAC_CMD_MAPPED; -#endif - return (0); + /* XXX Not yet implemented */ + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); } -/* - * Unmap a command from controller-visible space. - */ -void -aac_unmap_command(struct aac_ccb *ccb) +int +aac_scsi_cmd(struct scsi_xfer *xs) { - struct scsi_xfer *xs = ccb->ac_xs; - struct aac_softc *sc = xs->sc_link->adapter_softc; - -#if 0 - if (!(ccb->ac_flags & AAC_CMD_MAPPED)) - return; -#endif + struct scsi_link *link = xs->sc_link; + struct aac_softc *sc = link->adapter_softc; + u_int8_t target = link->target; + struct aac_command *cm; + u_int32_t blockno, blockcnt; + struct scsi_rw *rw; + struct scsi_rw_big *rwb; + int retval = SUCCESSFULLY_QUEUED; + int s = splbio(); - if (xs->datalen != 0) { - bus_dmamap_sync(sc->sc_dmat, ccb->ac_dmamap_xfer, 0, - ccb->ac_dmamap_xfer->dm_mapsize, - (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD : - BUS_DMASYNC_POSTWRITE); + xs->error = XS_NOERROR; - bus_dmamap_unload(sc->sc_dmat, ccb->ac_dmamap_xfer); + if (target >= AAC_MAX_CONTAINERS || !sc->aac_hdr[target].hd_present || + link->lun != 0) { + /* + * XXX Should be XS_SENSE but that would require setting up a + * faked sense too. + */ + xs->error = XS_DRIVER_STUFFUP; + xs->flags |= ITSDONE; + scsi_done(xs); + splx(s); + return (COMPLETE); } + + AAC_DPRINTF(AAC_D_CMD, ("%s: aac_scsi_cmd: ", sc->aac_dev.dv_xname)); + + xs->error = XS_NOERROR; + cm = NULL; + link = xs->sc_link; + target = link->target; + + switch (xs->cmd->opcode) { + case TEST_UNIT_READY: + case REQUEST_SENSE: + case INQUIRY: + case START_STOP: + case READ_CAPACITY: #if 0 - ccb->ac_flags &= ~AAC_CMD_MAPPED; + case VERIFY: #endif -} + if (!aac_internal_cache_cmd(xs)) { + splx(s); + return (TRY_AGAIN_LATER); + } + xs->flags |= ITSDONE; + scsi_done(xs); + goto ready; -void -aac_timeout(arg) - void *arg; -{ - struct aac_ccb *ccb = arg; - struct scsi_link *link = ccb->ac_xs->sc_link; - struct aac_softc *sc = link->adapter_softc; - aac_lock_t lock; + case PREVENT_ALLOW: + AAC_DPRINTF(AAC_D_CMD, ("PREVENT/ALLOW ")); + /* XXX Not yet implemented */ + xs->error = XS_NOERROR; + xs->flags |= ITSDONE; + scsi_done(xs); + goto ready; - sc_print_addr(link); - printf("timed out\n"); + case SYNCHRONIZE_CACHE: + AAC_DPRINTF(AAC_D_CMD, ("SYNCHRONIZE_CACHE ")); + /* XXX Not yet implemented */ + xs->error = XS_NOERROR; + xs->flags |= ITSDONE; + scsi_done(xs); + goto ready; - /* XXX Test for multiple timeouts */ + default: + AAC_DPRINTF(AAC_D_CMD, ("unknown opc %#x ", xs->cmd->opcode)); + /* XXX Not yet implemented */ + xs->error = XS_DRIVER_STUFFUP; + xs->flags |= ITSDONE; + scsi_done(xs); + goto ready; - ccb->ac_xs->error = XS_TIMEOUT; - lock = AAC_LOCK(sc); - aac_enqueue_ccb(sc, ccb); - AAC_UNLOCK(sc, lock); -} + case READ_COMMAND: + case READ_BIG: + case WRITE_COMMAND: + case WRITE_BIG: + AAC_DPRINTF(AAC_D_CMD, ("rw opc %#x ", xs->cmd->opcode)); + + /* A read or write operation. */ + if (xs->cmdlen == 6) { + rw = (struct scsi_rw *)xs->cmd; + blockno = _3btol(rw->addr) & + (SRW_TOPADDR << 16 | 0xffff); + blockcnt = rw->length ? rw->length : 0x100; + } else { + rwb = (struct scsi_rw_big *)xs->cmd; + blockno = _4btol(rwb->addr); + blockcnt = _2btol(rwb->length); + } -void -aac_watchdog(arg) - void *arg; -{ - struct aac_ccb *ccb = arg; - struct scsi_link *link = ccb->ac_xs->sc_link; - struct aac_softc *sc = link->adapter_softc; - aac_lock_t lock; + AAC_DPRINTF(AAC_D_CMD, ("blkno=%d bcount=%d ", + xs->cmd->opcode, blockno, blockcnt)); - lock = AAC_LOCK(sc); - ccb->ac_flags &= ~AAC_ACF_WATCHDOG; - aac_start_ccbs(sc); - AAC_UNLOCK(sc, lock); -} -/* - * Insert a command into the driver queue, either at the front or at the tail. - * It's ok to overload the freelist link as these structures are never on - * the freelist at this time. - */ -void -aac_enqueue(sc, xs, infront) - struct aac_softc *sc; - struct scsi_xfer *xs; - int infront; -{ - if (infront || LIST_FIRST(&sc->sc_queue) == NULL) { - if (LIST_FIRST(&sc->sc_queue) == NULL) - sc->sc_queuelast = xs; - LIST_INSERT_HEAD(&sc->sc_queue, xs, free_list); - return; - } - LIST_INSERT_AFTER(sc->sc_queuelast, xs, free_list); - sc->sc_queuelast = xs; -} + if (blockno >= sc->aac_hdr[target].hd_size || + blockno + blockcnt > sc->aac_hdr[target].hd_size) { + AAC_DPRINTF(AAC_D_CMD, ("\n")); + printf("%s: out of bounds %u-%u >= %u\n", + sc->aac_dev.dv_xname, blockno, + blockcnt, sc->aac_hdr[target].hd_size); + /* + * XXX Should be XS_SENSE but that + * would require setting up a faked + * sense too. + */ + xs->error = XS_DRIVER_STUFFUP; + xs->flags |= ITSDONE; + scsi_done(xs); + goto ready; + } -/* - * Pull a command off the front of the driver queue. - */ -struct scsi_xfer * -aac_dequeue(sc) - struct aac_softc *sc; -{ - struct scsi_xfer *xs; + if (aac_alloc_command(sc, &cm)) { + AAC_DPRINTF(AAC_D_CMD, + (": out of commands, try later\n")); + /* + * We are out of commands, try again + * in a little while. + */ + xs->error = XS_DRIVER_STUFFUP; + splx(s); + return (TRY_AGAIN_LATER); + } - xs = LIST_FIRST(&sc->sc_queue); - if (xs == NULL) - return (NULL); - LIST_REMOVE(xs, free_list); + /* fill out the command */ + cm->cm_data = (void *)xs->data; + cm->cm_datalen = xs->datalen; + cm->cm_complete = aac_bio_complete; + cm->cm_private = xs; + cm->cm_timestamp = time_second; + cm->cm_queue = AAC_ADAP_NORM_CMD_QUEUE; + cm->cm_blkno = blockno; + cm->cm_bcount = blockcnt; + + AAC_DPRINTF(AAC_D_CMD, ("\n")); + aac_enqueue_bio(cm); + aac_startio(sc); + + /* XXX what if enqueue did not start a transfer? */ + if (xs->flags & SCSI_POLL) { + if (!aac_wait_command(cm, xs->timeout)) + { + printf("%s: command timed out\n", + sc->aac_dev.dv_xname); + xs->error = XS_TIMEOUT; + splx(s); + return (TRY_AGAIN_LATER); + } + xs->flags |= ITSDONE; + scsi_done(xs); + } + } - if (LIST_FIRST(&sc->sc_queue) == NULL) - sc->sc_queuelast = NULL; + ready: + /* + * Don't process the queue if we are polling. + */ + if (xs->flags & SCSI_POLL) + retval = COMPLETE; - return (xs); + splx(s); + AAC_DPRINTF(AAC_D_CMD, ("%s: scsi_cmd complete\n", + sc->aac_dev.dv_xname)); + return (retval); } -/******************************************************************************** - * Adapter-space FIB queue manipulation - * - * Note that the queue implementation here is a little funky; neither the PI or - * CI will ever be zero. This behaviour is a controller feature. +/* + * Debugging and Diagnostics */ -static struct { - int size; - int notify; -} aac_qinfo[] = { - { AAC_HOST_NORM_CMD_ENTRIES, AAC_DB_COMMAND_NOT_FULL }, - { AAC_HOST_HIGH_CMD_ENTRIES, 0 }, - { AAC_ADAP_NORM_CMD_ENTRIES, AAC_DB_COMMAND_READY }, - { AAC_ADAP_HIGH_CMD_ENTRIES, 0 }, - { AAC_HOST_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_NOT_FULL }, - { AAC_HOST_HIGH_RESP_ENTRIES, 0 }, - { AAC_ADAP_NORM_RESP_ENTRIES, AAC_DB_RESPONSE_READY }, - { AAC_ADAP_HIGH_RESP_ENTRIES, 0 } -}; /* - * Atomically insert an entry into the nominated queue, returns 0 on success - * or EBUSY if the queue is full. - * - * XXX Note that it would be more efficient to defer notifying the controller - * in the case where we may be inserting several entries in rapid succession, - * but implementing this usefully is difficult. + * Print some information about the controller. */ -int -aac_enqueue_fib(struct aac_softc *sc, int queue, struct aac_ccb *ccb) +void +aac_describe_controller(struct aac_softc *sc) { - u_int32_t pi, ci; - int error; - aac_lock_t lock; - u_int32_t fib_size; - u_int32_t fib_addr; - - fib_size = ccb->ac_fib->Header.Size; - fib_addr = ccb->ac_fib->Header.ReceiverFibAddress; - - lock = AAC_LOCK(sc); - - /* get the producer/consumer indices */ - pi = sc->sc_queues->qt_qindex[queue][AAC_PRODUCER_INDEX]; - ci = sc->sc_queues->qt_qindex[queue][AAC_CONSUMER_INDEX]; + struct aac_fib *fib; + struct aac_adapter_info *info; - /* wrap the queue? */ - if (pi >= aac_qinfo[queue].size) - pi = 0; + aac_alloc_sync_fib(sc, &fib, 0); - /* check for queue full */ - if ((pi + 1) == ci) { - error = EBUSY; - goto out; + fib->data[0] = 0; + if (aac_sync_fib(sc, RequestAdapterInfo, 0, fib, 1)) { + printf("%s: RequestAdapterInfo failed 2\n", + sc->aac_dev.dv_xname); + aac_release_sync_fib(sc); + return; } + info = (struct aac_adapter_info *)&fib->data[0]; - /* populate queue entry */ - (sc->sc_qentries[queue] + pi)->aq_fib_size = fib_size; - (sc->sc_qentries[queue] + pi)->aq_fib_addr = fib_addr; + printf("%s: %s %dMHz, %dMB cache memory, %s\n", sc->aac_dev.dv_xname, + aac_describe_code(aac_cpu_variant, info->CpuVariant), + info->ClockSpeed, info->BufferMem / (1024 * 1024), + aac_describe_code(aac_battery_platform, info->batteryPlatform)); - /* update producer index */ - sc->sc_queues->qt_qindex[queue][AAC_PRODUCER_INDEX] = pi + 1; + /* save the kernel revision structure for later use */ + sc->aac_revision = info->KernelRevision; + printf("%s: Kernel %d.%d-%d, Build %d, S/N %6X\n", + sc->aac_dev.dv_xname, + info->KernelRevision.external.comp.major, + info->KernelRevision.external.comp.minor, + info->KernelRevision.external.comp.dash, + info->KernelRevision.buildNumber, + (u_int32_t)(info->SerialNumber & 0xffffff)); - /* notify the adapter if we know how */ - if (aac_qinfo[queue].notify != 0) - AAC_QNOTIFY(sc, aac_qinfo[queue].notify); + aac_release_sync_fib(sc); - error = 0; - -out: - AAC_UNLOCK(sc, lock); - return (error); +#if 0 + if (1 || bootverbose) { + device_printf(sc->aac_dev, "Supported Options=%b\n", + sc->supported_options, + "\20" + "\1SNAPSHOT" + "\2CLUSTERS" + "\3WCACHE" + "\4DATA64" + "\5HOSTTIME" + "\6RAID50" + "\7WINDOW4GB" + "\10SCSIUPGD" + "\11SOFTERR" + "\12NORECOND" + "\13SGMAP64" + "\14ALARM" + "\15NONDASD"); + } +#endif } /* - * Atomically remove one entry from the nominated queue, returns 0 on success - * or ENOENT if the queue is empty. + * Look up a text description of a numeric error code and return a pointer to + * same. */ -int -aac_dequeue_fib(struct aac_softc *sc, int queue, u_int32_t *fib_size, - struct aac_fib **fib_addr) +char * +aac_describe_code(struct aac_code_lookup *table, u_int32_t code) { - u_int32_t pi, ci; - int notify; - int error; - aac_lock_t lock; - - lock = AAC_LOCK(sc); - - /* get the producer/consumer indices */ - pi = sc->sc_queues->qt_qindex[queue][AAC_PRODUCER_INDEX]; - ci = sc->sc_queues->qt_qindex[queue][AAC_CONSUMER_INDEX]; - - /* check for queue empty */ - if (ci == pi) { - error = ENOENT; - goto out; - } - - notify = 0; - if (ci == pi + 1) - notify++; - - /* wrap the queue? */ - if (ci >= aac_qinfo[queue].size) - ci = 0; - - /* fetch the entry */ - *fib_size = (sc->sc_qentries[queue] + ci)->aq_fib_size; - *fib_addr = - (struct aac_fib *)(sc->sc_qentries[queue] + ci)->aq_fib_addr; - - /* update consumer index */ - sc->sc_queues->qt_qindex[queue][AAC_CONSUMER_INDEX] = ci + 1; - - /* if we have made the queue un-full, notify the adapter */ - if (notify && (aac_qinfo[queue].notify != 0)) - AAC_QNOTIFY(sc, aac_qinfo[queue].notify); - error = 0; + int i; -out: - AAC_UNLOCK(sc, lock); - return (error); + for (i = 0; table[i].string != NULL; i++) + if (table[i].code == code) + return(table[i].string); + return(table[i + 1].string); } #ifdef AAC_DEBUG @@ -1904,4 +2774,286 @@ aac_print_fib(struct aac_softc *sc, struct aac_fib *fib, const char *caller) break; } } + +/* + * Describe an AIF we have received. + */ +void +aac_print_aif(struct aac_softc *sc, struct aac_aif_command *aif) +{ + printf("%s: print_aif: ", sc->aac_dev.dv_xname); + + switch(aif->command) { + case AifCmdEventNotify: + printf("EventNotify(%d)\n", aif->seqNumber); + + switch(aif->data.EN.type) { + case AifEnGeneric: + /* Generic notification */ + printf("\t(Generic) %.*s\n", + (int)sizeof(aif->data.EN.data.EG), + aif->data.EN.data.EG.text); + break; + case AifEnTaskComplete: + /* Task has completed */ + printf("\t(TaskComplete)\n"); + break; + case AifEnConfigChange: + /* Adapter configuration change occurred */ + printf("\t(ConfigChange)\n"); + break; + case AifEnContainerChange: + /* Adapter specific container configuration change */ + printf("\t(ContainerChange) container %d,%d\n", + aif->data.EN.data.ECC.container[0], + aif->data.EN.data.ECC.container[1]); + break; + case AifEnDeviceFailure: + /* SCSI device failed */ + printf("\t(DeviceFailure) handle %d\n", + aif->data.EN.data.EDF.deviceHandle); + break; + case AifEnMirrorFailover: + /* Mirror failover started */ + printf("\t(MirrorFailover) container %d failed, " + "migrating from slice %d to %d\n", + aif->data.EN.data.EMF.container, + aif->data.EN.data.EMF.failedSlice, + aif->data.EN.data.EMF.creatingSlice); + break; + case AifEnContainerEvent: + /* Significant container event */ + printf("\t(ContainerEvent) container %d event %d\n", + aif->data.EN.data.ECE.container, + aif->data.EN.data.ECE.eventType); + break; + case AifEnFileSystemChange: + /* File system changed */ + printf("\t(FileSystemChange)\n"); + break; + case AifEnConfigPause: + /* Container pause event */ + printf("\t(ConfigPause)\n"); + break; + case AifEnConfigResume: + /* Container resume event */ + printf("\t(ConfigResume)\n"); + break; + case AifEnFailoverChange: + /* Failover space assignment changed */ + printf("\t(FailoverChange)\n"); + break; + case AifEnRAID5RebuildDone: + /* RAID5 rebuild finished */ + printf("\t(RAID5RebuildDone)\n"); + break; + case AifEnEnclosureManagement: + /* Enclosure management event */ + printf("\t(EnclosureManagement) EMPID %d unit %d " + "event %d\n", + aif->data.EN.data.EEE.empID, + aif->data.EN.data.EEE.unitID, + aif->data.EN.data.EEE.eventType); + break; + case AifEnBatteryEvent: + /* Significant NV battery event */ + printf("\t(BatteryEvent) %d (state was %d, is %d\n", + aif->data.EN.data.EBE.transition_type, + aif->data.EN.data.EBE.current_state, + aif->data.EN.data.EBE.prior_state); + break; + case AifEnAddContainer: + /* A new container was created. */ + printf("\t(AddContainer)\n"); + break; + case AifEnDeleteContainer: + /* A container was deleted. */ + printf("\t(DeleteContainer)\n"); + break; + case AifEnBatteryNeedsRecond: + /* The battery needs reconditioning */ + printf("\t(BatteryNeedsRecond)\n"); + break; + case AifEnClusterEvent: + /* Some cluster event */ + printf("\t(ClusterEvent) event %d\n", + aif->data.EN.data.ECLE.eventType); + break; + case AifEnDiskSetEvent: + /* A disk set event occured. */ + printf("(DiskSetEvent) event %d " + "diskset %lld creator %lld\n", + aif->data.EN.data.EDS.eventType, + aif->data.EN.data.EDS.DsNum, + aif->data.EN.data.EDS.CreatorId); + break; + case AifDenMorphComplete: + /* A morph operation completed */ + printf("\t(MorphComplete)\n"); + break; + case AifDenVolumeExtendComplete: + /* A volume expand operation completed */ + printf("\t(VolumeExtendComplete)\n"); + break; + default: + printf("\t(%d)\n", aif->data.EN.type); + break; + } + break; + case AifCmdJobProgress: + { + char *status; + switch(aif->data.PR[0].status) { + case AifJobStsSuccess: + status = "success"; break; + case AifJobStsFinished: + status = "finished"; break; + case AifJobStsAborted: + status = "aborted"; break; + case AifJobStsFailed: + status = "failed"; break; + case AifJobStsSuspended: + status = "suspended"; break; + case AifJobStsRunning: + status = "running"; break; + default: + status = "unknown status"; break; + } + + printf("JobProgress (%d) - %s (%d, %d)\n", + aif->seqNumber, status, + aif->data.PR[0].currentTick, + aif->data.PR[0].finalTick); + + switch(aif->data.PR[0].jd.type) { + case AifJobScsiZero: + /* SCSI dev clear operation */ + printf("\t(ScsiZero) handle %d\n", + aif->data.PR[0].jd.client.scsi_dh); + break; + case AifJobScsiVerify: + /* SCSI device Verify operation NO REPAIR */ + printf("\t(ScsiVerify) handle %d\n", + aif->data.PR[0].jd.client.scsi_dh); + break; + case AifJobScsiExercise: + /* SCSI device Exercise operation */ + printf("\t(ScsiExercise) handle %d\n", + aif->data.PR[0].jd.client.scsi_dh); + break; + case AifJobScsiVerifyRepair: + /* SCSI device Verify operation WITH repair */ + printf("\t(ScsiVerifyRepair) handle %d\n", + aif->data.PR[0].jd.client.scsi_dh); + break; + case AifJobCtrZero: + /* Container clear operation */ + printf("\t(ConatainerZero) container %d\n", + aif->data.PR[0].jd.client.container.src); + break; + case AifJobCtrCopy: + /* Container copy operation */ + printf("\t(ConatainerCopy) container %d to %d\n", + aif->data.PR[0].jd.client.container.src, + aif->data.PR[0].jd.client.container.dst); + break; + case AifJobCtrCreateMirror: + /* Container Create Mirror operation */ + printf("\t(ConatainerCreateMirror) container %d\n", + aif->data.PR[0].jd.client.container.src); + /* XXX two containers? */ + break; + case AifJobCtrMergeMirror: + /* Container Merge Mirror operation */ + printf("\t(ConatainerMergeMirror) container %d\n", + aif->data.PR[0].jd.client.container.src); + /* XXX two containers? */ + break; + case AifJobCtrScrubMirror: + /* Container Scrub Mirror operation */ + printf("\t(ConatainerScrubMirror) container %d\n", + aif->data.PR[0].jd.client.container.src); + break; + case AifJobCtrRebuildRaid5: + /* Container Rebuild Raid5 operation */ + printf("\t(ConatainerRebuildRaid5) container %d\n", + aif->data.PR[0].jd.client.container.src); + break; + case AifJobCtrScrubRaid5: + /* Container Scrub Raid5 operation */ + printf("\t(ConatainerScrubRaid5) container %d\n", + aif->data.PR[0].jd.client.container.src); + break; + case AifJobCtrMorph: + /* Container morph operation */ + printf("\t(ConatainerMorph) container %d\n", + aif->data.PR[0].jd.client.container.src); + /* XXX two containers? */ + break; + case AifJobCtrPartCopy: + /* Container Partition copy operation */ + printf("\t(ConatainerPartCopy) container %d to %d\n", + aif->data.PR[0].jd.client.container.src, + aif->data.PR[0].jd.client.container.dst); + break; + case AifJobCtrRebuildMirror: + /* Container Rebuild Mirror operation */ + printf("\t(ConatainerRebuildMirror) container %d\n", + aif->data.PR[0].jd.client.container.src); + break; + case AifJobCtrCrazyCache: + /* crazy cache */ + printf("\t(ConatainerCrazyCache) container %d\n", + aif->data.PR[0].jd.client.container.src); + /* XXX two containers? */ + break; + case AifJobFsCreate: + /* File System Create operation */ + printf("\t(FsCreate)\n"); + break; + case AifJobFsVerify: + /* File System Verify operation */ + printf("\t(FsVerivy)\n"); + break; + case AifJobFsExtend: + /* File System Extend operation */ + printf("\t(FsExtend)\n"); + break; + case AifJobApiFormatNTFS: + /* Format a drive to NTFS */ + printf("\t(FormatNTFS)\n"); + break; + case AifJobApiFormatFAT: + /* Format a drive to FAT */ + printf("\t(FormatFAT)\n"); + break; + case AifJobApiUpdateSnapshot: + /* update the read/write half of a snapshot */ + printf("\t(UpdateSnapshot)\n"); + break; + case AifJobApiFormatFAT32: + /* Format a drive to FAT32 */ + printf("\t(FormatFAT32)\n"); + break; + case AifJobCtlContinuousCtrVerify: + /* Adapter operation */ + printf("\t(ContinuousCtrVerify)\n"); + break; + default: + printf("\t(%d)\n", aif->data.PR[0].jd.type); + break; + } + break; + } + case AifCmdAPIReport: + printf("APIReport (%d)\n", aif->seqNumber); + break; + case AifCmdDriverNotify: + printf("DriverNotify (%d)\n", aif->seqNumber); + break; + default: + printf("AIF %d (%d)\n", aif->command, aif->seqNumber); + break; + } +} #endif diff --git a/sys/dev/ic/aac_tables.h b/sys/dev/ic/aac_tables.h index c0fedc3ca60..3ba773425fd 100644 --- a/sys/dev/ic/aac_tables.h +++ b/sys/dev/ic/aac_tables.h @@ -1,4 +1,4 @@ -/* $OpenBSD: aac_tables.h,v 1.2 2004/05/21 20:34:52 marco Exp $ */ +/* $OpenBSD: aac_tables.h,v 1.3 2005/11/18 05:39:10 nate Exp $ */ /*- * Copyright (c) 2000 Michael Smith @@ -83,7 +83,7 @@ static struct aac_code_lookup aac_cpu_variant[] = { { "i960HX", CPUI960_HX }, { "i960RX", CPUI960_RX }, { "StrongARM SA110", CPUARM_SA110 }, - { "PowerPC 603e", CPUPPC_603e }, + { "MPC824x", CPUMPC_824x }, { "Unknown StrongARM", CPUARM_xxx }, { "Unknown PowerPC", CPUPPC_xxx }, { "Intel GC80302 IOP", CPUI960_302}, diff --git a/sys/dev/ic/aacreg.h b/sys/dev/ic/aacreg.h index 1a66fa1a28a..4557e1485e4 100644 --- a/sys/dev/ic/aacreg.h +++ b/sys/dev/ic/aacreg.h @@ -1,9 +1,10 @@ -/* $OpenBSD: aacreg.h,v 1.6 2004/05/21 20:34:52 marco Exp $ */ +/* $OpenBSD: aacreg.h,v 1.7 2005/11/18 05:39:10 nate Exp $ */ /*- * Copyright (c) 2000 Michael Smith - * Copyright (c) 2000 Scott Long + * Copyright (c) 2000-2001 Scott Long * Copyright (c) 2000 BSDi + * Copyright (c) 2001 Adaptec, Inc. * Copyright (c) 2000 Niklas Hallqvist * All rights reserved. * @@ -28,7 +29,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: /c/ncvs/src/sys/dev/aac/aacreg.h,v 1.1 2000/09/13 03:20:34 msmith Exp $ + * $FreeBSD$ */ /* @@ -55,27 +56,37 @@ * We establish 4 command queues and matching response queues. Queues must * be 16-byte aligned, and are sized as follows: */ -#define AAC_HOST_NORM_CMD_ENTRIES 8 /* cmd adapter->host, normal pri */ -#define AAC_HOST_HIGH_CMD_ENTRIES 4 /* cmd adapter->host, high pri */ -#define AAC_ADAP_NORM_CMD_ENTRIES 512 /* cmd host->adapter, normal pri */ -#define AAC_ADAP_HIGH_CMD_ENTRIES 4 /* cmd host->adapter, high pri */ -#define AAC_HOST_NORM_RESP_ENTRIES 512 /* resp, adapter->host, normal pri */ -#define AAC_HOST_HIGH_RESP_ENTRIES 4 /* resp, adapter->host, high pri */ -#define AAC_ADAP_NORM_RESP_ENTRIES 8 /* resp, host->adapter, normal pri */ -#define AAC_ADAP_HIGH_RESP_ENTRIES 4 /* resp, host->adapter, high pri */ - -#define AAC_TOTALQ_LENGTH \ - (AAC_HOST_HIGH_CMD_ENTRIES + AAC_HOST_NORM_CMD_ENTRIES + \ - AAC_ADAP_HIGH_CMD_ENTRIES + AAC_ADAP_NORM_CMD_ENTRIES + \ - AAC_HOST_HIGH_RESP_ENTRIES + AAC_HOST_NORM_RESP_ENTRIES + \ - AAC_ADAP_HIGH_RESP_ENTRIES + AAC_ADAP_NORM_RESP_ENTRIES) - +#define AAC_HOST_NORM_CMD_ENTRIES 8 /* command adapter->host, + * normal priority */ +#define AAC_HOST_HIGH_CMD_ENTRIES 4 /* command adapter->host, + * high priority */ +#define AAC_ADAP_NORM_CMD_ENTRIES 512 /* command host->adapter, + * normal priority */ +#define AAC_ADAP_HIGH_CMD_ENTRIES 4 /* command host->adapter, + * high priority */ +#define AAC_HOST_NORM_RESP_ENTRIES 512 /* response, adapter->host, + * normal priority */ +#define AAC_HOST_HIGH_RESP_ENTRIES 4 /* response, adapter->host, + * high priority */ +#define AAC_ADAP_NORM_RESP_ENTRIES 8 /* response, host->adapter, + * normal priority */ +#define AAC_ADAP_HIGH_RESP_ENTRIES 4 /* response, host->adapter, + * high priority */ + +#define AAC_TOTALQ_LENGTH (AAC_HOST_HIGH_CMD_ENTRIES + \ + AAC_HOST_NORM_CMD_ENTRIES + \ + AAC_ADAP_HIGH_CMD_ENTRIES + \ + AAC_ADAP_NORM_CMD_ENTRIES + \ + AAC_HOST_HIGH_RESP_ENTRIES + \ + AAC_HOST_NORM_RESP_ENTRIES + \ + AAC_ADAP_HIGH_RESP_ENTRIES + \ + AAC_ADAP_NORM_RESP_ENTRIES) #define AAC_QUEUE_COUNT 8 #define AAC_QUEUE_ALIGN 16 struct aac_queue_entry { - u_int32_t aq_fib_size; /* FIB size in bytes */ - u_int32_t aq_fib_addr; /* receiver-space address of the FIB */ + u_int32_t aq_fib_size; /* FIB size in bytes */ + u_int32_t aq_fib_addr; /* receiver-space address of the FIB */ } __attribute__ ((__packed__)); #define AAC_PRODUCER_INDEX 0 @@ -87,48 +98,200 @@ struct aac_queue_entry { */ struct aac_queue_table { /* queue consumer/producer indexes (layout mandated by adapter) */ - u_int32_t qt_qindex[AAC_QUEUE_COUNT][2]; + u_int32_t qt_qindex[AAC_QUEUE_COUNT][2]; /* queue entry structures (layout mandated by adapter) */ - struct aac_queue_entry qt_HostNormCmdQueue[AAC_HOST_NORM_CMD_ENTRIES]; - struct aac_queue_entry qt_HostHighCmdQueue[AAC_HOST_HIGH_CMD_ENTRIES]; - struct aac_queue_entry qt_AdapNormCmdQueue[AAC_ADAP_NORM_CMD_ENTRIES]; - struct aac_queue_entry qt_AdapHighCmdQueue[AAC_ADAP_HIGH_CMD_ENTRIES]; - struct aac_queue_entry - qt_HostNormRespQueue[AAC_HOST_NORM_RESP_ENTRIES]; - struct aac_queue_entry - qt_HostHighRespQueue[AAC_HOST_HIGH_RESP_ENTRIES]; - struct aac_queue_entry - qt_AdapNormRespQueue[AAC_ADAP_NORM_RESP_ENTRIES]; - struct aac_queue_entry - qt_AdapHighRespQueue[AAC_ADAP_HIGH_RESP_ENTRIES]; + struct aac_queue_entry qt_HostNormCmdQueue [AAC_HOST_NORM_CMD_ENTRIES]; + struct aac_queue_entry qt_HostHighCmdQueue [AAC_HOST_HIGH_CMD_ENTRIES]; + struct aac_queue_entry qt_AdapNormCmdQueue [AAC_ADAP_NORM_CMD_ENTRIES]; + struct aac_queue_entry qt_AdapHighCmdQueue [AAC_ADAP_HIGH_CMD_ENTRIES]; + struct aac_queue_entry qt_HostNormRespQueue[AAC_HOST_NORM_RESP_ENTRIES]; + struct aac_queue_entry qt_HostHighRespQueue[AAC_HOST_HIGH_RESP_ENTRIES]; + struct aac_queue_entry qt_AdapNormRespQueue[AAC_ADAP_NORM_RESP_ENTRIES]; + struct aac_queue_entry qt_AdapHighRespQueue[AAC_ADAP_HIGH_RESP_ENTRIES]; +} __attribute__ ((__packed__)); + +/* + * Queue names + * + * Note that we base these at 0 in order to use them as array indices. Adaptec + * used base 1 for some unknown reason, and sorted them in a different order. + */ +#define AAC_HOST_NORM_CMD_QUEUE 0 +#define AAC_HOST_HIGH_CMD_QUEUE 1 +#define AAC_ADAP_NORM_CMD_QUEUE 2 +#define AAC_ADAP_HIGH_CMD_QUEUE 3 +#define AAC_HOST_NORM_RESP_QUEUE 4 +#define AAC_HOST_HIGH_RESP_QUEUE 5 +#define AAC_ADAP_NORM_RESP_QUEUE 6 +#define AAC_ADAP_HIGH_RESP_QUEUE 7 + +/* + * List structure used to chain FIBs (used by the adapter - we hang FIBs off + * our private command structure and don't touch these) + */ +struct aac_fib_list_entry { + u_int32_t Flink; + u_int32_t Blink; } __attribute__ ((__packed__)); /* + * FIB (FSA Interface Block?); this is the datastructure passed between the host + * and adapter. + */ +struct aac_fib_header { + u_int32_t XferState; + u_int16_t Command; + u_int8_t StructType; + u_int8_t Flags; + u_int16_t Size; + u_int16_t SenderSize; + u_int32_t SenderFibAddress; + u_int32_t ReceiverFibAddress; + u_int32_t SenderData; + union { + struct { + u_int32_t ReceiverTimeStart; + u_int32_t ReceiverTimeDone; + } _s; + struct aac_fib_list_entry FibLinks; + } _u; +} __attribute__ ((__packed__)); + +#define AAC_FIB_DATASIZE (512 - sizeof(struct aac_fib_header)) + +struct aac_fib { + struct aac_fib_header Header; + u_int8_t data[AAC_FIB_DATASIZE]; +} __attribute__ ((__packed__)); + +/* + * FIB commands + */ +typedef enum { + TestCommandResponse = 1, + TestAdapterCommand = 2, + + /* lowlevel and comm commands */ + LastTestCommand = 100, + ReinitHostNormCommandQueue = 101, + ReinitHostHighCommandQueue = 102, + ReinitHostHighRespQueue = 103, + ReinitHostNormRespQueue = 104, + ReinitAdapNormCommandQueue = 105, + ReinitAdapHighCommandQueue = 107, + ReinitAdapHighRespQueue = 108, + ReinitAdapNormRespQueue = 109, + InterfaceShutdown = 110, + DmaCommandFib = 120, + StartProfile = 121, + TermProfile = 122, + SpeedTest = 123, + TakeABreakPt = 124, + RequestPerfData = 125, + SetInterruptDefTimer= 126, + SetInterruptDefCount= 127, + GetInterruptDefStatus= 128, + LastCommCommand = 129, + + /* filesystem commands */ + NuFileSystem = 300, + UFS = 301, + HostFileSystem = 302, + LastFileSystemCommand = 303, + + /* Container Commands */ + ContainerCommand = 500, + ContainerCommand64 = 501, + + /* Cluster Commands */ + ClusterCommand = 550, + + /* Scsi Port commands (scsi passthrough) */ + ScsiPortCommand = 600, + + /* misc house keeping and generic adapter initiated commands */ + AifRequest = 700, + CheckRevision = 701, + FsaHostShutdown = 702, + RequestAdapterInfo = 703, + IsAdapterPaused = 704, + SendHostTime = 705, + LastMiscCommand = 706 +} AAC_FibCommands; + +/* + * FIB types + */ +#define AAC_FIBTYPE_TFIB 1 +#define AAC_FIBTYPE_TQE 2 +#define AAC_FIBTYPE_TCTPERF 3 + +/* + * FIB transfer state + */ +#define AAC_FIBSTATE_HOSTOWNED (1<<0) /* owned by the host */ +#define AAC_FIBSTATE_ADAPTEROWNED (1<<1) /* owned by the adapter */ +#define AAC_FIBSTATE_INITIALISED (1<<2) /* initialised */ +#define AAC_FIBSTATE_EMPTY (1<<3) /* empty */ +#define AAC_FIBSTATE_FROMPOOL (1<<4) /* allocated from pool */ +#define AAC_FIBSTATE_FROMHOST (1<<5) /* sent from the host */ +#define AAC_FIBSTATE_FROMADAP (1<<6) /* sent from the adapter */ +#define AAC_FIBSTATE_REXPECTED (1<<7) /* response is expected */ +#define AAC_FIBSTATE_RNOTEXPECTED (1<<8) /* response is not expected */ +#define AAC_FIBSTATE_DONEADAP (1<<9) /* processed by the adapter */ +#define AAC_FIBSTATE_DONEHOST (1<<10) /* processed by the host */ +#define AAC_FIBSTATE_HIGH (1<<11) /* high priority */ +#define AAC_FIBSTATE_NORM (1<<12) /* normal priority */ +#define AAC_FIBSTATE_ASYNC (1<<13) +#define AAC_FIBSTATE_ASYNCIO (1<<13) /* to be removed */ +#define AAC_FIBSTATE_PAGEFILEIO (1<<14) /* to be removed */ +#define AAC_FIBSTATE_SHUTDOWN (1<<15) +#define AAC_FIBSTATE_LAZYWRITE (1<<16) /* to be removed */ +#define AAC_FIBSTATE_ADAPMICROFIB (1<<17) +#define AAC_FIBSTATE_BIOSFIB (1<<18) +#define AAC_FIBSTATE_FAST_RESPONSE (1<<19) /* fast response capable */ +#define AAC_FIBSTATE_APIFIB (1<<20) + +/* + * FIB error values + */ +#define AAC_ERROR_NORMAL 0x00 +#define AAC_ERROR_PENDING 0x01 +#define AAC_ERROR_FATAL 0x02 +#define AAC_ERROR_INVALID_QUEUE 0x03 +#define AAC_ERROR_NOENTRIES 0x04 +#define AAC_ERROR_SENDFAILED 0x05 +#define AAC_ERROR_INVALID_QUEUE_PRIORITY 0x06 +#define AAC_ERROR_FIB_ALLOCATION_FAILED 0x07 +#define AAC_ERROR_FIB_DEALLOCATION_FAILED 0x08 + +/* * Adapter Init Structure: this is passed to the adapter with the * AAC_MONKER_INITSTRUCT command to point it at our control structures. */ struct aac_adapter_init { - u_int32_t InitStructRevision; -#define AAC_INIT_STRUCT_REVISION 3 - u_int32_t MiniPortRevision; - u_int32_t FilesystemRevision; - u_int32_t CommHeaderAddress; - u_int32_t FastIoCommAreaAddress; - u_int32_t AdapterFibsPhysicalAddress; - void *AdapterFibsVirtualAddress; - u_int32_t AdapterFibsSize; - u_int32_t AdapterFibAlign; - u_int32_t PrintfBufferAddress; - u_int32_t PrintfBufferSize; - u_int32_t HostPhysMemPages; - u_int32_t HostElapsedSeconds; -} __packed; + u_int32_t InitStructRevision; +#define AAC_INIT_STRUCT_REVISION 3 + u_int32_t MiniPortRevision; +#define AAC_INIT_STRUCT_MINIPORT_REVISION 1 + u_int32_t FilesystemRevision; + u_int32_t CommHeaderAddress; + u_int32_t FastIoCommAreaAddress; + u_int32_t AdapterFibsPhysicalAddress; + u_int32_t AdapterFibsVirtualAddress; + u_int32_t AdapterFibsSize; + u_int32_t AdapterFibAlign; + u_int32_t PrintfBufferAddress; + u_int32_t PrintfBufferSize; +#define AAC_PAGE_SIZE 4096 + u_int32_t HostPhysMemPages; + u_int32_t HostElapsedSeconds; +} __attribute__ ((__packed__)); /* * Shared data types */ - /* * Container types */ @@ -146,7 +309,8 @@ typedef enum { CT_RAID10, /* stripe of mirror */ CT_RAID00, /* stripe of stripe */ CT_VOLUME_OF_MIRRORS, /* volume of mirror */ - CT_PSEUDO_RAID3 /* really raid4 */ + CT_PSEUDO_RAID3, /* really raid4 */ + CT_RAID50, /* stripe of raid5 */ } AAC_FSAVolType; /* @@ -161,9 +325,10 @@ typedef enum { FT_SOCK, /* socket */ FT_FIFO, /* fifo */ FT_FILESYS, /* ADAPTEC's "FSA"(tm) filesystem */ - FT_DRIVE, /* phys disk - addressable in scsi by bus/target/lun */ + FT_DRIVE, /* physical disk - addressable in scsi by b/t/l */ FT_SLICE, /* virtual disk - raw volume - slice */ - FT_PARTITION, /* FSA part, inside slice, container building block */ + FT_PARTITION, /* FSA partition - carved out of a slice - building + * block for containers */ FT_VOLUME, /* Container - Volume Set */ FT_STRIPE, /* Container - Stripe Set */ FT_MIRROR, /* Container - Mirror Set */ @@ -175,53 +340,72 @@ typedef enum { * Host-side scatter/gather list for 32-bit commands. */ struct aac_sg_entry { - u_int32_t SgAddress; - u_int32_t SgByteCount; + u_int32_t SgAddress; + u_int32_t SgByteCount; +} __attribute__ ((__packed__)); + +struct aac_sg_entry64 { + u_int64_t SgAddress; + u_int32_t SgByteCount; } __attribute__ ((__packed__)); struct aac_sg_table { - u_int32_t SgCount; - struct aac_sg_entry SgEntry[0]; + u_int32_t SgCount; + struct aac_sg_entry SgEntry[0]; } __attribute__ ((__packed__)); /* * Host-side scatter/gather list for 64-bit commands. */ struct aac_sg_table64 { - u_int8_t SgCount; - u_int8_t SgSectorsPerPage; - u_int16_t SgByteOffset; - u_int64_t SgEntry[0]; + u_int32_t SgCount; + struct aac_sg_entry64 SgEntry64[0]; } __attribute__ ((__packed__)); /* * Container creation data */ struct aac_container_creation { - u_int8_t ViaBuildNumber; - u_int8_t MicroSecond; - u_int8_t Via; /* 1 = FSU, 2 = API, etc. */ - u_int8_t YearsSince1900; - u_int32_t Month:4; /* 1-12 */ - u_int32_t Day:6; /* 1-32 */ - u_int32_t Hour:6; /* 0-23 */ - u_int32_t Minute:6; /* 0-59 */ - u_int32_t Second:6; /* 0-59 */ - u_int64_t ViaAdapterSerialNumber; + u_int8_t ViaBuildNumber; + u_int8_t MicroSecond; + u_int8_t Via; /* 1 = FSU, 2 = API, etc. */ + u_int8_t YearsSince1900; + u_int32_t Month:4; /* 1-12 */ + u_int32_t Day:6; /* 1-32 */ + u_int32_t Hour:6; /* 0-23 */ + u_int32_t Minute:6; /* 0-59 */ + u_int32_t Second:6; /* 0-59 */ + u_int64_t ViaAdapterSerialNumber; } __attribute__ ((__packed__)); +/* + * Revision number handling + */ + +typedef enum { + RevApplication = 1, + RevDkiCli, + RevNetService, + RevApi, + RevFileSysDriver, + RevMiniportDriver, + RevAdapterSW, + RevMonitor, + RevRemoteApi +} RevComponent; + struct FsaRevision { union { - struct { - u_int8_t dash; - u_int8_t type; - u_int8_t minor; - u_int8_t major; - } comp; - u_int32_t ul; + struct { + u_int8_t dash; + u_int8_t type; + u_int8_t minor; + u_int8_t major; + } comp; + u_int32_t ul; } external; - u_int32_t buildNumber; -} __packed; + u_int32_t buildNumber; +} __attribute__ ((__packed__)); /* * Adapter Information @@ -246,7 +430,7 @@ typedef enum { CPUI960_RX, CPUARM_SA110, CPUARM_xxx, - CPUPPC_603e, + CPUMPC_824x, CPUPPC_xxx, CPUI960_302, CPUSUBTYPE__last @@ -291,7 +475,8 @@ typedef enum { /* * XXX the aac-2622 with no battery present reports PLATFORM_BAT_OPT_PRESENT */ -typedef enum { +typedef enum +{ PLATFORM_BAT_REQ_PRESENT = 1, /* BATTERY REQUIRED AND PRESENT */ PLATFORM_BAT_REQ_NOTPRESENT, /* BATTERY REQUIRED AND NOT PRESENT */ PLATFORM_BAT_OPT_PRESENT, /* BATTERY OPTIONAL AND PRESENT */ @@ -300,27 +485,50 @@ typedef enum { } AAC_BatteryPlatform; /* + * options supported by this board + * there has to be a one to one mapping of these defines and the ones in + * fsaapi.h, search for FSA_SUPPORT_SNAPSHOT + */ +#define AAC_SUPPORTED_SNAPSHOT 0x01 +#define AAC_SUPPORTED_CLUSTERS 0x02 +#define AAC_SUPPORTED_WRITE_CACHE 0x04 +#define AAC_SUPPORTED_64BIT_DATA 0x08 +#define AAC_SUPPORTED_HOST_TIME_FIB 0x10 +#define AAC_SUPPORTED_RAID50 0x20 +#define AAC_SUPPORTED_4GB_WINDOW 0x40 +#define AAC_SUPPORTED_SCSI_UPGRADEABLE 0x80 +#define AAC_SUPPORTED_SOFT_ERR_REPORT 0x100 +#define AAC_SUPPORTED_NOT_RECONDITION 0x200 +#define AAC_SUPPORTED_SGMAP_HOST64 0x400 +#define AAC_SUPPORTED_ALARM 0x800 +#define AAC_SUPPORTED_NONDASD 0x1000 + +/* * Structure used to respond to a RequestAdapterInfo fib. */ struct aac_adapter_info { - AAC_Platform PlatformBase; /* adapter type */ - AAC_CpuType CpuArchitecture; /* adapter CPU type */ - AAC_CpuSubType CpuVariant; /* adapter CPU subtype */ - u_int32_t ClockSpeed; /* adapter CPU clockspeed */ - u_int32_t ExecutionMem; /* adapter Execution Memory size */ - u_int32_t BufferMem; /* adapter Data Memory */ - u_int32_t TotalMem; /* adapter Total Memory */ - struct FsaRevision KernelRevision; /* adapter Kernel SW Revision */ - struct FsaRevision MonitorRevision; /* adapter Monitor/Diag SW Rev */ - struct FsaRevision HardwareRevision; /* TDB */ - struct FsaRevision BIOSRevision; /* adapter BIOS Revision */ - u_int32_t ClusteringEnabled; - u_int32_t ClusterChannelMask; - u_int64_t SerialNumber; - AAC_BatteryPlatform batteryPlatform; - u_int32_t SupportedOptions; /* supported features of this ctrlr */ - AAC_OemFlavor OemVariant; -} __packed; + AAC_Platform PlatformBase; /* adapter type */ + AAC_CpuType CpuArchitecture; /* adapter CPU type */ + AAC_CpuSubType CpuVariant; /* adapter CPU subtype */ + u_int32_t ClockSpeed; /* adapter CPU clockspeed */ + u_int32_t ExecutionMem; /* adapter Execution Memory + * size */ + u_int32_t BufferMem; /* adapter Data Memory */ + u_int32_t TotalMem; /* adapter Total Memory */ + struct FsaRevision KernelRevision; /* adapter Kernel Software + * Revision */ + struct FsaRevision MonitorRevision; /* adapter Monitor/Diagnostic + * Software Revision */ + struct FsaRevision HardwareRevision;/* TBD */ + struct FsaRevision BIOSRevision; /* adapter BIOS Revision */ + u_int32_t ClusteringEnabled; + u_int32_t ClusterChannelMask; + u_int64_t SerialNumber; + AAC_BatteryPlatform batteryPlatform; + u_int32_t SupportedOptions; /* supported features of this + * controller */ + AAC_OemFlavor OemVariant; +} __attribute__ ((__packed__)); /* * Monitor/Kernel interface. @@ -331,104 +539,588 @@ struct aac_adapter_info { */ #define AAC_MONKER_INITSTRUCT 0x05 #define AAC_MONKER_SYNCFIB 0x0c +#define AAC_MONKER_GETKERNVER 0x11 +#define AAC_MONKER_GETINFO 0x19 + +/* + * Adapter Status Register + * + * Phase Staus mailbox is 32bits: + * <31:16> = Phase Status + * <15:0> = Phase + * + * The adapter reports its present state through the phase. Only + * a single phase should be ever be set. Each phase can have multiple + * phase status bits to provide more detailed information about the + * state of the adapter. + */ +#define AAC_SELF_TEST_FAILED 0x00000004 +#define AAC_UP_AND_RUNNING 0x00000080 +#define AAC_KERNEL_PANIC 0x00000100 + +/* + * Data types relating to control and monitoring of the NVRAM/WriteCache + * subsystem. + */ + +#define AAC_NFILESYS 24 /* maximum number of filesystems */ + +/* + * NVRAM/Write Cache subsystem states + */ +typedef enum { + NVSTATUS_DISABLED = 0, /* present, clean, not being used */ + NVSTATUS_ENABLED, /* present, possibly dirty, ready for use */ + NVSTATUS_ERROR, /* present, dirty, contains dirty data */ + NVSTATUS_BATTERY, /* present, bad or low battery, may contain + * dirty data */ + NVSTATUS_UNKNOWN /* for bad/missing device */ +} AAC_NVSTATUS; + +/* + * NVRAM/Write Cache subsystem battery component states + * + */ +typedef enum { + NVBATTSTATUS_NONE = 0, /* battery has no power or is not present */ + NVBATTSTATUS_LOW, /* battery is low on power */ + NVBATTSTATUS_OK, /* battery is okay - normal operation possible + * only in this state */ + NVBATTSTATUS_RECONDITIONING /* no battery present - reconditioning + * in process */ +} AAC_NVBATTSTATUS; + +/* + * Battery transition type + */ +typedef enum { + NVBATT_TRANSITION_NONE = 0, /* battery now has no power or is not + * present */ + NVBATT_TRANSITION_LOW, /* battery is now low on power */ + NVBATT_TRANSITION_OK /* battery is now okay - normal + * operation possible only in this + * state */ +} AAC_NVBATT_TRANSITION; + +/* + * NVRAM Info structure returned for NVRAM_GetInfo call + */ +struct aac_nvramdevinfo { + u_int32_t NV_Enabled; /* write caching enabled */ + u_int32_t NV_Error; /* device in error state */ + u_int32_t NV_NDirty; /* count of dirty NVRAM buffers */ + u_int32_t NV_NActive; /* count of NVRAM buffers being + * written */ +} __attribute__ ((__packed__)); + +struct aac_nvraminfo { + AAC_NVSTATUS NV_Status; /* nvram subsystem status */ + AAC_NVBATTSTATUS NV_BattStatus; /* battery status */ + u_int32_t NV_Size; /* size of WriteCache NVRAM in + * bytes */ + u_int32_t NV_BufSize; /* size of NVRAM buffers in + * bytes */ + u_int32_t NV_NBufs; /* number of NVRAM buffers */ + u_int32_t NV_NDirty; /* Num dirty NVRAM buffers */ + u_int32_t NV_NClean; /* Num clean NVRAM buffers */ + u_int32_t NV_NActive; /* Num NVRAM buffers being + * written */ + u_int32_t NV_NBrokered; /* Num brokered NVRAM buffers */ + struct aac_nvramdevinfo NV_DevInfo[AAC_NFILESYS]; /* per device + * info */ + u_int32_t NV_BattNeedsReconditioning; /* boolean */ + u_int32_t NV_TotalSize; /* size of all non-volatile + * memories in bytes */ +} __attribute__ ((__packed__)); + +/* + * Data types relating to adapter-initiated FIBs + * + * Based on types and structures in <aifstruc.h> + */ + +/* + * Progress Reports + */ +typedef enum { + AifJobStsSuccess = 1, + AifJobStsFinished, + AifJobStsAborted, + AifJobStsFailed, + AifJobStsLastReportMarker = 100, /* All prior mean last report */ + AifJobStsSuspended, + AifJobStsRunning +} AAC_AifJobStatus; + +typedef enum { + AifJobScsiMin = 1, /* Minimum value for Scsi operation */ + AifJobScsiZero, /* SCSI device clear operation */ + AifJobScsiVerify, /* SCSI device Verify operation NO + * REPAIR */ + AifJobScsiExercise, /* SCSI device Exercise operation */ + AifJobScsiVerifyRepair, /* SCSI device Verify operation WITH + * repair */ + AifJobScsiMax = 99, /* Max Scsi value */ + AifJobCtrMin, /* Min Ctr op value */ + AifJobCtrZero, /* Container clear operation */ + AifJobCtrCopy, /* Container copy operation */ + AifJobCtrCreateMirror, /* Container Create Mirror operation */ + AifJobCtrMergeMirror, /* Container Merge Mirror operation */ + AifJobCtrScrubMirror, /* Container Scrub Mirror operation */ + AifJobCtrRebuildRaid5, /* Container Rebuild Raid5 operation */ + AifJobCtrScrubRaid5, /* Container Scrub Raid5 operation */ + AifJobCtrMorph, /* Container morph operation */ + AifJobCtrPartCopy, /* Container Partition copy operation */ + AifJobCtrRebuildMirror, /* Container Rebuild Mirror operation */ + AifJobCtrCrazyCache, /* crazy cache */ + AifJobCtrMax = 199, /* Max Ctr type operation */ + AifJobFsMin, /* Min Fs type operation */ + AifJobFsCreate, /* File System Create operation */ + AifJobFsVerify, /* File System Verify operation */ + AifJobFsExtend, /* File System Extend operation */ + AifJobFsMax = 299, /* Max Fs type operation */ + AifJobApiFormatNTFS, /* Format a drive to NTFS */ + AifJobApiFormatFAT, /* Format a drive to FAT */ + AifJobApiUpdateSnapshot, /* update the read/write half of a + * snapshot */ + AifJobApiFormatFAT32, /* Format a drive to FAT32 */ + AifJobApiMax = 399, /* Max API type operation */ + AifJobCtlContinuousCtrVerify, /* Adapter operation */ + AifJobCtlMax = 499 /* Max Adapter type operation */ +} AAC_AifJobType; + +struct aac_AifContainers { + u_int32_t src; /* from/master */ + u_int32_t dst; /* to/slave */ +} __attribute__ ((__packed__)); + +union aac_AifJobClient { + struct aac_AifContainers container; /* For Container and + * filesystem progress + * ops; */ + int32_t scsi_dh; /* For SCSI progress + * ops */ +}; + +struct aac_AifJobDesc { + u_int32_t jobID; /* DO NOT FILL IN! Will be + * filled in by AIF */ + AAC_AifJobType type; /* Operation that is being + * performed */ + union aac_AifJobClient client; /* Details */ +} __attribute__ ((__packed__)); + +struct aac_AifJobProgressReport { + struct aac_AifJobDesc jd; + AAC_AifJobStatus status; + u_int32_t finalTick; + u_int32_t currentTick; + u_int32_t jobSpecificData1; + u_int32_t jobSpecificData2; +} __attribute__ ((__packed__)); + +/* + * Event Notification + */ +typedef enum { + /* General application notifies start here */ + AifEnGeneric = 1, /* Generic notification */ + AifEnTaskComplete, /* Task has completed */ + AifEnConfigChange, /* Adapter config change occurred */ + AifEnContainerChange, /* Adapter specific container + * configuration change */ + AifEnDeviceFailure, /* SCSI device failed */ + AifEnMirrorFailover, /* Mirror failover started */ + AifEnContainerEvent, /* Significant container event */ + AifEnFileSystemChange, /* File system changed */ + AifEnConfigPause, /* Container pause event */ + AifEnConfigResume, /* Container resume event */ + AifEnFailoverChange, /* Failover space assignment changed */ + AifEnRAID5RebuildDone, /* RAID5 rebuild finished */ + AifEnEnclosureManagement, /* Enclosure management event */ + AifEnBatteryEvent, /* Significant NV battery event */ + AifEnAddContainer, /* A new container was created. */ + AifEnDeleteContainer, /* A container was deleted. */ + AifEnSMARTEvent, /* SMART Event */ + AifEnBatteryNeedsRecond, /* The battery needs reconditioning */ + AifEnClusterEvent, /* Some cluster event */ + AifEnDiskSetEvent, /* A disk set event occured. */ + AifDriverNotifyStart=199, /* Notifies for host driver go here */ + /* Host driver notifications start here */ + AifDenMorphComplete, /* A morph operation completed */ + AifDenVolumeExtendComplete /* Volume expand operation completed */ +} AAC_AifEventNotifyType; + +struct aac_AifEnsGeneric { + char text[132]; /* Generic text */ +} __attribute__ ((__packed__)); + +struct aac_AifEnsDeviceFailure { + u_int32_t deviceHandle; /* SCSI device handle */ +} __attribute__ ((__packed__)); + +struct aac_AifEnsMirrorFailover { + u_int32_t container; /* Container with failed element */ + u_int32_t failedSlice; /* Old slice which failed */ + u_int32_t creatingSlice; /* New slice used for auto-create */ +} __attribute__ ((__packed__)); + +struct aac_AifEnsContainerChange { + u_int32_t container[2]; /* container that changed, -1 if no + * container */ +} __attribute__ ((__packed__)); + +struct aac_AifEnsContainerEvent { + u_int32_t container; /* container number */ + u_int32_t eventType; /* event type */ +} __attribute__ ((__packed__)); + +struct aac_AifEnsEnclosureEvent { + u_int32_t empID; /* enclosure management proc number */ + u_int32_t unitID; /* unitId, fan id, power supply id, + * slot id, tempsensor id. */ + u_int32_t eventType; /* event type */ +} __attribute__ ((__packed__)); + +struct aac_AifEnsBatteryEvent { + AAC_NVBATT_TRANSITION transition_type; /* eg from low to ok */ + AAC_NVBATTSTATUS current_state; /* current batt state */ + AAC_NVBATTSTATUS prior_state; /* prev batt state */ +} __attribute__ ((__packed__)); + +struct aac_AifEnsDiskSetEvent { + u_int32_t eventType; + u_int64_t DsNum; + u_int64_t CreatorId; +} __attribute__ ((__packed__)); + +typedef enum { + CLUSTER_NULL_EVENT = 0, + CLUSTER_PARTNER_NAME_EVENT, /* change in partner hostname or + * adaptername from NULL to non-NULL */ + /* (partner's agent may be up) */ + CLUSTER_PARTNER_NULL_NAME_EVENT /* change in partner hostname or + * adaptername from non-null to NULL */ + /* (partner has rebooted) */ +} AAC_ClusterAifEvent; + +struct aac_AifEnsClusterEvent { + AAC_ClusterAifEvent eventType; +} __attribute__ ((__packed__)); + +struct aac_AifEventNotify { + AAC_AifEventNotifyType type; + union { + struct aac_AifEnsGeneric EG; + struct aac_AifEnsDeviceFailure EDF; + struct aac_AifEnsMirrorFailover EMF; + struct aac_AifEnsContainerChange ECC; + struct aac_AifEnsContainerEvent ECE; + struct aac_AifEnsEnclosureEvent EEE; + struct aac_AifEnsBatteryEvent EBE; + struct aac_AifEnsDiskSetEvent EDS; +/* struct aac_AifEnsSMARTEvent ES;*/ + struct aac_AifEnsClusterEvent ECLE; + } data; +} __attribute__ ((__packed__)); + +/* + * Adapter Initiated FIB command structures. Start with the adapter + * initiated FIBs that really come from the adapter, and get responded + * to by the host. + */ +#define AAC_AIF_REPORT_MAX_SIZE 64 + +typedef enum { + AifCmdEventNotify = 1, /* Notify of event */ + AifCmdJobProgress, /* Progress report */ + AifCmdAPIReport, /* Report from other user of API */ + AifCmdDriverNotify, /* Notify host driver of event */ + AifReqJobList = 100, /* Gets back complete job list */ + AifReqJobsForCtr, /* Gets back jobs for specific container */ + AifReqJobsForScsi, /* Gets back jobs for specific SCSI device */ + AifReqJobReport, /* Gets back a specific job report or list */ + AifReqTerminateJob, /* Terminates job */ + AifReqSuspendJob, /* Suspends a job */ + AifReqResumeJob, /* Resumes a job */ + AifReqSendAPIReport, /* API generic report requests */ + AifReqAPIJobStart, /* Start a job from the API */ + AifReqAPIJobUpdate, /* Update a job report from the API */ + AifReqAPIJobFinish /* Finish a job from the API */ +} AAC_AifCommand; + +struct aac_aif_command { + AAC_AifCommand command; /* Tell host what type of + * notify this is */ + u_int32_t seqNumber; /* To allow ordering of + * reports (if necessary) */ + union { + struct aac_AifEventNotify EN; /* Event notify */ + struct aac_AifJobProgressReport PR[1]; /* Progress report */ + u_int8_t AR[AAC_AIF_REPORT_MAX_SIZE]; + u_int8_t data[AAC_FIB_DATASIZE - 8]; + } data; +} __attribute__ ((__packed__)); + +/* + * Filesystem commands/data + * + * The adapter has a very complex filesystem interface, most of which we ignore. + * (And which seems not to be implemented, anyway.) + */ + +/* + * FSA commands + * (not used?) + */ +typedef enum { + Null = 0, + GetAttributes, + SetAttributes, + Lookup, + ReadLink, + Read, + Write, + Create, + MakeDirectory, + SymbolicLink, + MakeNode, + Removex, + RemoveDirectory, + Rename, + Link, + ReadDirectory, + ReadDirectoryPlus, + FileSystemStatus, + FileSystemInfo, + PathConfigure, + Commit, + Mount, + UnMount, + Newfs, + FsCheck, + FsSync, + SimReadWrite, + SetFileSystemStatus, + BlockRead, + BlockWrite, + NvramIoctl, + FsSyncWait, + ClearArchiveBit, + SetAcl, + GetAcl, + AssignAcl, + FaultInsertion, + CrazyCache +} AAC_FSACommand; /* * Command status values */ typedef enum { - ST_OK = 0, - ST_PERM = 1, - ST_NOENT = 2, - ST_IO = 5, - ST_NXIO = 6, - ST_E2BIG = 7, - ST_ACCES = 13, - ST_EXIST = 17, - ST_XDEV = 18, - ST_NODEV = 19, - ST_NOTDIR = 20, - ST_ISDIR = 21, - ST_INVAL = 22, - ST_FBIG = 27, - ST_NOSPC = 28, - ST_ROFS = 30, - ST_MLINK = 31, - ST_WOULDBLOCK = 35, - ST_NAMETOOLONG = 63, - ST_NOTEMPTY = 66, - ST_DQUOT = 69, - ST_STALE = 70, - ST_REMOTE = 71, - ST_BADHANDLE = 10001, - ST_NOT_SYNC = 10002, - ST_BAD_COOKIE = 10003, - ST_NOTSUPP = 10004, - ST_TOOSMALL = 10005, - ST_SERVERFAULT = 10006, - ST_BADTYPE = 10007, - ST_JUKEBOX = 10008, - ST_NOTMOUNTED = 10009, - ST_MAINTMODE = 10010, - ST_STALEACL = 10011 + ST_OK = 0, + ST_PERM = 1, + ST_NOENT = 2, + ST_IO = 5, + ST_NXIO = 6, + ST_E2BIG = 7, + ST_ACCES = 13, + ST_EXIST = 17, + ST_XDEV = 18, + ST_NODEV = 19, + ST_NOTDIR = 20, + ST_ISDIR = 21, + ST_INVAL = 22, + ST_FBIG = 27, + ST_NOSPC = 28, + ST_ROFS = 30, + ST_MLINK = 31, + ST_WOULDBLOCK = 35, + ST_NAMETOOLONG = 63, + ST_NOTEMPTY = 66, + ST_DQUOT = 69, + ST_STALE = 70, + ST_REMOTE = 71, + ST_BADHANDLE = 10001, + ST_NOT_SYNC = 10002, + ST_BAD_COOKIE = 10003, + ST_NOTSUPP = 10004, + ST_TOOSMALL = 10005, + ST_SERVERFAULT = 10006, + ST_BADTYPE = 10007, + ST_JUKEBOX = 10008, + ST_NOTMOUNTED = 10009, + ST_MAINTMODE = 10010, + ST_STALEACL = 10011 } AAC_FSAStatus; /* * Volume manager commands */ typedef enum _VM_COMMANDS { - VM_Null = 0, - VM_NameServe, - VM_ContainerConfig, - VM_Ioctl, - VM_FilesystemIoctl, - VM_CloseAll, - VM_CtBlockRead, - VM_CtBlockWrite, - VM_SliceBlockRead, /* raw access to configured "storage objects" */ - VM_SliceBlockWrite, - VM_DriveBlockRead, /* raw access to physical devices */ - VM_DriveBlockWrite, - VM_EnclosureMgt, /* enclosure management */ - VM_Unused, /* used to be diskset management */ - VM_CtBlockVerify, - VM_CtPerf, /* performance test */ - VM_CtBlockRead64, - VM_CtBlockWrite64, - VM_CtBlockVerify64, + VM_Null = 0, + VM_NameServe, + VM_ContainerConfig, + VM_Ioctl, + VM_FilesystemIoctl, + VM_CloseAll, + VM_CtBlockRead, + VM_CtBlockWrite, + VM_SliceBlockRead, /* raw access to configured storage objects */ + VM_SliceBlockWrite, + VM_DriveBlockRead, /* raw access to physical devices */ + VM_DriveBlockWrite, + VM_EnclosureMgt, /* enclosure management */ + VM_Unused, /* used to be diskset management */ + VM_CtBlockVerify, + VM_CtPerf, /* performance test */ + VM_CtBlockRead64, + VM_CtBlockWrite64, + VM_CtBlockVerify64, + VM_CtHostRead64, + VM_CtHostWrite64, } AAC_VMCommand; /* - * "Mountable object" + * "mountable object" */ struct aac_mntobj { - u_int32_t ObjectId; - char FileSystemName[16]; - struct aac_container_creation CreateInfo; - u_int32_t Capacity; - AAC_FSAVolType VolType; - AAC_FType ObjType; - u_int32_t ContentState; -#define AAC_FSCS_READONLY 0x0002 /* XXX need more information than this */ + u_int32_t ObjectId; + char FileSystemName[16]; + struct aac_container_creation CreateInfo; + u_int32_t Capacity; + u_int32_t VolType; + u_int32_t ObjType; + u_int32_t ContentState; +#define FSCS_READONLY 0x0002 /* XXX need more information + * than this */ union { - u_int32_t pad[8]; + u_int32_t pad[8]; } ObjExtension; - u_int32_t AlterEgoId; + u_int32_t AlterEgoId; } __attribute__ ((__packed__)); struct aac_mntinfo { - AAC_VMCommand Command; - AAC_FType MntType; - u_int32_t MntCount; + u_int32_t Command; + u_int32_t MntType; + u_int32_t MntCount; } __attribute__ ((__packed__)); -struct aac_mntinforesponse { - AAC_FSAStatus Status; - AAC_FType MntType; - u_int32_t MntRespCount; - struct aac_mntobj MntTable[1]; +struct aac_mntinforesp { + u_int32_t Status; + u_int32_t MntType; + u_int32_t MntRespCount; + struct aac_mntobj MntTable[1]; } __attribute__ ((__packed__)); /* + * Container shutdown command. + */ +struct aac_closecommand { + u_int32_t Command; + u_int32_t ContainerId; +} __attribute__ ((__packed__)); + +/* + * Container Config Command + */ +#define CT_GET_SCSI_METHOD 64 +struct aac_ctcfg { + u_int32_t Command; + u_int32_t cmd; + u_int32_t param; +} __attribute__ ((__packed__)); + +struct aac_ctcfg_resp { + u_int32_t Status; + u_int32_t resp; + u_int32_t param; +} __attribute__ ((__packed__)); + +/* + * 'Ioctl' commads + */ +#define AAC_SCSI_MAX_PORTS 10 +#define AAC_BUS_NO_EXIST 0 +#define AAC_BUS_VALID 1 +#define AAC_BUS_FAULTED 2 +#define AAC_BUS_DISABLED 3 +#define GetBusInfo 0x9 + +struct aac_getbusinf { + u_int32_t ProbeComplete; + u_int32_t BusCount; + u_int32_t TargetsPerBus; + u_int8_t InitiatorBusId[AAC_SCSI_MAX_PORTS]; + u_int8_t BusValid[AAC_SCSI_MAX_PORTS]; +} __attribute__ ((__packed__)); + +struct aac_vmioctl { + u_int32_t Command; + u_int32_t ObjType; + u_int32_t MethId; + u_int32_t ObjId; + u_int32_t IoctlCmd; + u_int32_t IoctlBuf[1]; /* Placeholder? */ +} __attribute__ ((__packed__)); + +struct aac_vmi_businf_resp { + u_int32_t Status; + u_int32_t ObjType; + u_int32_t MethId; + u_int32_t ObjId; + u_int32_t IoctlCmd; + struct aac_getbusinf BusInf; +} __attribute__ ((__packed__)); + +#define AAC_BTL_TO_HANDLE(b, t, l) \ + (((b & 0x3f) << 7) | ((l & 0x7) << 4) | (t & 0xf)) +#define GetDeviceProbeInfo 0x5 + +struct aac_vmi_devinfo_resp { + u_int32_t Status; + u_int32_t ObjType; + u_int32_t MethId; + u_int32_t ObjId; + u_int32_t IoctlCmd; + u_int8_t VendorId[8]; + u_int8_t ProductId[16]; + u_int8_t ProductRev[4]; + u_int32_t Inquiry7; + u_int32_t align1; + u_int32_t Inquiry0; + u_int32_t align2; + u_int32_t Inquiry1; + u_int32_t align3; + u_int32_t reserved[2]; + u_int8_t VendorSpecific[20]; + u_int32_t Smart:1; + u_int32_t AAC_Managed:1; + u_int32_t align4; + u_int32_t reserved2:6; + u_int32_t Bus; + u_int32_t Target; + u_int32_t Lun; + u_int32_t ultraEnable:1, + disconnectEnable:1, + fast20EnabledW:1, + scamDevice:1, + scamTolerant:1, + setForSync:1, + setForWide:1, + syncDevice:1, + wideDevice:1, + reserved1:7, + ScsiRate:8, + ScsiOffset:8; +}; /* Do not pack */ + +#define ResetBus 0x16 +struct aac_resetbus { + u_int32_t BusNumber; +}; + +/* * Write 'stability' options. */ typedef enum { @@ -453,68 +1145,199 @@ typedef enum { */ struct aac_blockread { - AAC_VMCommand Command; /* not FSACommand! */ - u_int32_t ContainerId; - u_int32_t BlockNumber; - u_int32_t ByteCount; - struct aac_sg_table SgMap; /* variable size */ + u_int32_t Command; /* not FSACommand! */ + u_int32_t ContainerId; + u_int32_t BlockNumber; + u_int32_t ByteCount; + struct aac_sg_table SgMap; /* variable size */ +} __attribute__ ((__packed__)); + +struct aac_blockread64 { + u_int32_t Command; + u_int16_t ContainerId; + u_int16_t SectorCount; + u_int32_t BlockNumber; + u_int16_t Pad; + u_int16_t Flags; + struct aac_sg_table64 SgMap64; } __attribute__ ((__packed__)); struct aac_blockread_response { - AAC_FSAStatus Status; - u_int32_t ByteCount; + u_int32_t Status; + u_int32_t ByteCount; } __attribute__ ((__packed__)); struct aac_blockwrite { - AAC_VMCommand Command; /* not FSACommand! */ - u_int32_t ContainerId; - u_int32_t BlockNumber; - u_int32_t ByteCount; - AAC_CacheLevel Stable; - struct aac_sg_table SgMap; /* variable size */ + u_int32_t Command; /* not FSACommand! */ + u_int32_t ContainerId; + u_int32_t BlockNumber; + u_int32_t ByteCount; + u_int32_t Stable; + struct aac_sg_table SgMap; /* variable size */ +} __attribute__ ((__packed__)); + +struct aac_blockwrite64 { + u_int32_t Command; /* not FSACommand! */ + u_int16_t ContainerId; + u_int16_t SectorCount; + u_int32_t BlockNumber; + u_int16_t Pad; + u_int16_t Flags; + struct aac_sg_table64 SgMap64; /* variable size */ } __attribute__ ((__packed__)); struct aac_blockwrite_response { - AAC_FSAStatus Status; - u_int32_t ByteCount; - AAC_CommitLevel Committed; + u_int32_t Status; + u_int32_t ByteCount; + u_int32_t Committed; } __attribute__ ((__packed__)); /* + * Container shutdown command. + */ +struct aac_close_command { + u_int32_t Command; + u_int32_t ContainerId; +}; + +/* + * SCSI Passthrough structures + */ +struct aac_srb32 { + u_int32_t function; + u_int32_t bus; + u_int32_t target; + u_int32_t lun; + u_int32_t timeout; + u_int32_t flags; + u_int32_t data_len; + u_int32_t retry_limit; + u_int32_t cdb_len; + u_int8_t cdb[16]; + struct aac_sg_table sg_map32; +}; + +enum { + AAC_SRB_FUNC_EXECUTE_SCSI = 0x00, + AAC_SRB_FUNC_CLAIM_DEVICE, + AAC_SRB_FUNC_IO_CONTROL, + AAC_SRB_FUNC_RECEIVE_EVENT, + AAC_SRB_FUNC_RELEASE_QUEUE, + AAC_SRB_FUNC_ATTACH_DEVICE, + AAC_SRB_FUNC_RELEASE_DEVICE, + AAC_SRB_FUNC_SHUTDOWN, + AAC_SRB_FUNC_FLUSH, + AAC_SRB_FUNC_ABORT_COMMAND = 0x10, + AAC_SRB_FUNC_RELEASE_RECOVERY, + AAC_SRB_FUNC_RESET_BUS, + AAC_SRB_FUNC_RESET_DEVICE, + AAC_SRB_FUNC_TERMINATE_IO, + AAC_SRB_FUNC_FLUSH_QUEUE, + AAC_SRB_FUNC_REMOVE_DEVICE, + AAC_SRB_FUNC_DOMAIN_VALIDATION +}; + +#define AAC_SRB_FLAGS_NO_DATA_XFER 0x0000 +#define AAC_SRB_FLAGS_DISABLE_DISCONNECT 0x0004 +#define AAC_SRB_FLAGS_DISABLE_SYNC_TRANSFER 0x0008 +#define AAC_SRB_FLAGS_BYPASS_FROZEN_QUEUE 0x0010 +#define AAC_SRB_FLAGS_DISABLE_AUTOSENSE 0x0020 +#define AAC_SRB_FLAGS_DATA_IN 0x0040 +#define AAC_SRB_FLAGS_DATA_OUT 0x0080 +#define AAC_SRB_FLAGS_UNSPECIFIED_DIRECTION \ + (AAC_SRB_FLAGS_DATA_IN | AAC_SRB_FLAGS_DATA_OUT) + +#define AAC_HOST_SENSE_DATA_MAX 30 + +struct aac_srb_response { + u_int32_t fib_status; + u_int32_t srb_status; + u_int32_t scsi_status; + u_int32_t data_len; + u_int32_t sense_len; + u_int8_t sense[AAC_HOST_SENSE_DATA_MAX]; +}; + +enum { + AAC_SRB_STS_PENDING = 0x00, + AAC_SRB_STS_SUCCESS, + AAC_SRB_STS_ABORTED, + AAC_SRB_STS_ABORT_FAILED, + AAC_SRB_STS_ERROR, + AAC_SRB_STS_BUSY, + AAC_SRB_STS_INVALID_REQUEST, + AAC_SRB_STS_INVALID_PATH_ID, + AAC_SRB_STS_NO_DEVICE, + AAC_SRB_STS_TIMEOUT, + AAC_SRB_STS_SELECTION_TIMEOUT, + AAC_SRB_STS_COMMAND_TIMEOUT, + AAC_SRB_STS_MESSAGE_REJECTED = 0x0D, + AAC_SRB_STS_BUS_RESET, + AAC_SRB_STS_PARITY_ERROR, + AAC_SRB_STS_REQUEST_SENSE_FAILED, + AAC_SRB_STS_NO_HBA, + AAC_SRB_STS_DATA_OVERRUN, + AAC_SRB_STS_UNEXPECTED_BUS_FREE, + AAC_SRB_STS_PHASE_SEQUENCE_FAILURE, + AAC_SRB_STS_BAD_SRB_BLOCK_LENGTH, + AAC_SRB_STS_REQUEST_FLUSHED, + AAC_SRB_STS_INVALID_LUN = 0x20, + AAC_SRB_STS_INVALID_TARGET_ID, + AAC_SRB_STS_BAD_FUNCTION, + AAC_SRB_STS_ERROR_RECOVERY +}; + +/* + * Register set for adapters based on the Falcon bridge and PPC core + */ + +#define AAC_FA_DOORBELL0_CLEAR 0x00 +#define AAC_FA_DOORBELL1_CLEAR 0x02 +#define AAC_FA_DOORBELL0 0x04 +#define AAC_FA_DOORBELL1 0x06 +#define AAC_FA_MASK0_CLEAR 0x08 +#define AAC_FA_MASK1_CLEAR 0x0a +#define AAC_FA_MASK0 0x0c +#define AAC_FA_MASK1 0x0e +#define AAC_FA_MAILBOX 0x10 +#define AAC_FA_FWSTATUS 0x2c /* Mailbox 7 */ +#define AAC_FA_INTSRC 0x900 + +#define AAC_FA_HACK(sc) (void)AAC_GETREG4(sc, AAC_FA_INTSRC) + +/* * Register definitions for the Adaptec AAC-364 'Jalapeno I/II' adapters, based * on the SA110 'StrongArm'. */ -#define AAC_REGSIZE 0x100 +#define AAC_REGSIZE 0x100 -/* doorbell 0 (adapter->host) */ -#define AAC_SA_DOORBELL0_CLEAR 0x98 -#define AAC_SA_DOORBELL0_SET 0x9c -#define AAC_SA_DOORBELL0 0x9c -#define AAC_SA_MASK0_CLEAR 0xa0 -#define AAC_SA_MASK0_SET 0xa4 +#define AAC_SA_DOORBELL0_CLEAR 0x98 /* doorbell 0 (adapter->host) */ +#define AAC_SA_DOORBELL0_SET 0x9c +#define AAC_SA_DOORBELL0 0x9c +#define AAC_SA_MASK0_CLEAR 0xa0 +#define AAC_SA_MASK0_SET 0xa4 -/* doorbell 1 (host->adapter) */ -#define AAC_SA_DOORBELL1_CLEAR 0x9a -#define AAC_SA_DOORBELL1_SET 0x9e -#define AAC_SA_MASK1_CLEAR 0xa2 -#define AAC_SA_MASK1_SET 0xa6 +#define AAC_SA_DOORBELL1_CLEAR 0x9a /* doorbell 1 (host->adapter) */ +#define AAC_SA_DOORBELL1_SET 0x9e +#define AAC_SA_DOORBELL1 0x9e +#define AAC_SA_MASK1_CLEAR 0xa2 +#define AAC_SA_MASK1_SET 0xa6 -/* mailbox (20 bytes) */ -#define AAC_SA_MAILBOX 0xa8 -#define AAC_SA_FWSTATUS 0xc4 +#define AAC_SA_MAILBOX 0xa8 /* mailbox (20 bytes) */ +#define AAC_SA_FWSTATUS 0xc4 /* - * Register definitions for the Adaptec 'Pablano' adapters, based on the - * i960Rx, and other related adapters. + * Register definitions for the Adaptec 'Pablano' adapters, based on the i960Rx, + * and other related adapters. */ -#define AAC_RX_IDBR 0x20 /* inbound doorbell */ -#define AAC_RX_IISR 0x24 /* inbound interrupt status */ -#define AAC_RX_IIMR 0x28 /* inbound interrupt mask */ -#define AAC_RX_ODBR 0x2c /* outbound doorbell */ -#define AAC_RX_OISR 0x30 /* outbound interrupt status */ -#define AAC_RX_OIMR 0x34 /* outbound interrupt mask */ +#define AAC_RX_IDBR 0x20 /* inbound doorbell register */ +#define AAC_RX_IISR 0x24 /* inbound interrupt status register */ +#define AAC_RX_IIMR 0x28 /* inbound interrupt mask register */ +#define AAC_RX_ODBR 0x2c /* outbound doorbell register */ +#define AAC_RX_OISR 0x30 /* outbound interrupt status register */ +#define AAC_RX_OIMR 0x34 /* outbound interrupt mask register */ #define AAC_RX_MAILBOX 0x50 /* mailbox (20 bytes) */ #define AAC_RX_FWSTATUS 0x6c @@ -539,182 +1362,13 @@ struct aac_blockwrite_response { * DOORBELL0 and setting it in DOORBELL1. * (ODBR and IDBR respectively for the i960Rx adapters) */ -#define AAC_DB_PRINTF (1<<5) - -/* - * Mask containing the interrupt bits we care about. We don't anticipate - * (or want) interrupts not in this mask. - */ -#define AAC_DB_INTERRUPTS \ - (AAC_DB_COMMAND_READY | AAC_DB_RESPONSE_READY | AAC_DB_PRINTF) - -/* - * Queue names - * - * Note that we base these at 0 in order to use them as array indices. Adaptec - * used base 1 for some unknown reason, and sorted them in a different order. - */ -#define AAC_HOST_NORM_CMD_QUEUE 0 -#define AAC_HOST_HIGH_CMD_QUEUE 1 -#define AAC_ADAP_NORM_CMD_QUEUE 2 -#define AAC_ADAP_HIGH_CMD_QUEUE 3 -#define AAC_HOST_NORM_RESP_QUEUE 4 -#define AAC_HOST_HIGH_RESP_QUEUE 5 -#define AAC_ADAP_NORM_RESP_QUEUE 6 -#define AAC_ADAP_HIGH_RESP_QUEUE 7 - -/* - * List structure used to chain FIBs (used by the adapter - we hang FIBs off - * our private command structure and don't touch these) - */ -struct aac_fib_list_entry { - struct fib_list_entry *Flink; - struct fib_list_entry *Blink; -} __packed; - -/* - * FIB (FSA Interface Block?); this is the datastructure passed between the - * host and adapter. - */ -struct aac_fib_header { - u_int32_t XferState; - u_int16_t Command; - u_int8_t StructType; - u_int8_t Flags; - u_int16_t Size; - u_int16_t SenderSize; - u_int32_t SenderFibAddress; - u_int32_t ReceiverFibAddress; - u_int32_t SenderData; - union { - struct { - u_int32_t ReceiverTimeStart; - u_int32_t ReceiverTimeDone; - } _s; - struct aac_fib_list_entry FibLinks; - } _u; -} __packed; - -#define AAC_FIB_DATASIZE (512 - sizeof(struct aac_fib_header)) - -struct aac_fib { - struct aac_fib_header Header; - u_int8_t data[AAC_FIB_DATASIZE]; -} __packed; - -/* - * FIB commands - */ -typedef enum { - TestCommandResponse = 1, - TestAdapterCommand = 2, - - /* lowlevel and comm commands */ - LastTestCommand = 100, - ReinitHostNormCommandQueue = 101, - ReinitHostHighCommandQueue = 102, - ReinitHostHighRespQueue = 103, - ReinitHostNormRespQueue = 104, - ReinitAdapNormCommandQueue = 105, - ReinitAdapHighCommandQueue = 107, - ReinitAdapHighRespQueue = 108, - ReinitAdapNormRespQueue = 109, - InterfaceShutdown = 110, - DmaCommandFib = 120, - StartProfile = 121, - TermProfile = 122, - SpeedTest = 123, - TakeABreakPt = 124, - RequestPerfData = 125, - SetInterruptDefTimer= 126, - SetInterruptDefCount= 127, - GetInterruptDefStatus= 128, - LastCommCommand = 129, - - /* filesystem commands */ - NuFileSystem = 300, - UFS = 301, - HostFileSystem = 302, - LastFileSystemCommand = 303, - - /* Container Commands */ - ContainerCommand = 500, - ContainerCommand64 = 501, - - /* Cluster Commands */ - ClusterCommand = 550, - - /* Scsi Port commands (scsi passthrough) */ - ScsiPortCommand = 600, - - /* misc house keeping and generic adapter initiated commands */ - AifRequest = 700, - CheckRevision = 701, - FsaHostShutdown = 702, - RequestAdapterInfo = 703, - IsAdapterPaused = 704, - SendHostTime = 705, - LastMiscCommand = 706 -} AAC_FibCommands; +#define AAC_DB_PRINTF (1<<5) /* adapter requests host printf */ +#define AAC_PRINTF_DONE (1<<5) /* Host completed printf processing */ /* - * FIB types - */ -#define AAC_FIBTYPE_TFIB 1 -#define AAC_FIBTYPE_TQE 2 -#define AAC_FIBTYPE_TCTPERF 3 - -/* - * FIB transfer state + * Mask containing the interrupt bits we care about. We don't anticipate (or + * want) interrupts not in this mask. */ -#define AAC_FIBSTATE_HOSTOWNED (1<<0) /* owned by the host */ -#define AAC_FIBSTATE_ADAPTEROWNED (1<<1) /* owned by the adapter */ -#define AAC_FIBSTATE_INITIALISED (1<<2) /* initialised */ -#define AAC_FIBSTATE_EMPTY (1<<3) /* empty */ -#define AAC_FIBSTATE_FROMPOOL (1<<4) /* allocated from pool */ -#define AAC_FIBSTATE_FROMHOST (1<<5) /* sent from the host */ -#define AAC_FIBSTATE_FROMADAP (1<<6) /* sent from the adapter */ -#define AAC_FIBSTATE_REXPECTED (1<<7) /* response is expected */ -#define AAC_FIBSTATE_RNOTEXPECTED (1<<8) /* response is not expected */ -#define AAC_FIBSTATE_DONEADAP (1<<9) /* processed by the adapter */ -#define AAC_FIBSTATE_DONEHOST (1<<10) /* processed by the host */ -#define AAC_FIBSTATE_HIGH (1<<11) /* high priority */ -#define AAC_FIBSTATE_NORM (1<<12) /* normal priority */ -#define AAC_FIBSTATE_ASYNC (1<<13) -#define AAC_FIBSTATE_ASYNCIO (1<<13) /* to be removed */ -#define AAC_FIBSTATE_PAGEFILEIO (1<<14) /* to be removed */ -#define AAC_FIBSTATE_SHUTDOWN (1<<15) -#define AAC_FIBSTATE_LAZYWRITE (1<<16) /* to be removed */ -#define AAC_FIBSTATE_ADAPMICROFIB (1<<17) -#define AAC_FIBSTATE_BIOSFIB (1<<18) -#define AAC_FIBSTATE_FAST_RESPONSE (1<<19) /* fast response capable */ -#define AAC_FIBSTATE_APIFIB (1<<20) - -/* - * FIB error values - */ -#define AAC_ERROR_NORMAL 0x00 -#define AAC_ERROR_PENDING 0x01 -#define AAC_ERROR_FATAL 0x02 -#define AAC_ERROR_INVALID_QUEUE 0x03 -#define AAC_ERROR_NOENTRIES 0x04 -#define AAC_ERROR_SENDFAILED 0x05 -#define AAC_ERROR_INVALID_QUEUE_PRIORITY 0x06 -#define AAC_ERROR_FIB_ALLOCATION_FAILED 0x07 -#define AAC_ERROR_FIB_DEALLOCATION_FAILED 0x08 - -/* - * Adapter Status Register - * - * Phase Staus mailbox is 32bits: - * <31:16> = Phase Status - * <15:0> = Phase - * - * The adapter reports its present state through the phase. Only - * a single phase should be ever be set. Each phase can have multiple - * phase status bits to provide more detailed information about the - * state of the adapter. - */ -#define AAC_SELF_TEST_FAILED 0x00000004 -#define AAC_UP_AND_RUNNING 0x00000080 -#define AAC_KERNEL_PANIC 0x00000100 +#define AAC_DB_INTERRUPTS (AAC_DB_COMMAND_READY | \ + AAC_DB_RESPONSE_READY | \ + AAC_DB_PRINTF) diff --git a/sys/dev/ic/aacvar.h b/sys/dev/ic/aacvar.h index e654d7595d6..2be60aba46a 100644 --- a/sys/dev/ic/aacvar.h +++ b/sys/dev/ic/aacvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: aacvar.h,v 1.4 2003/10/21 18:58:48 jmc Exp $ */ +/* $OpenBSD: aacvar.h,v 1.5 2005/11/18 05:39:10 nate Exp $ */ /*- * Copyright (c) 2000 Michael Smith @@ -38,17 +38,29 @@ * - Niklas Hallqvist */ +/* compatability */ +#define time_second (mono_time.tv_sec) + /* Debugging */ +// #define AAC_DEBUG 0x0 + #ifdef AAC_DEBUG #define AAC_DPRINTF(mask, args) if (aac_debug & (mask)) printf args -#define AAC_D_INTR 0x01 -#define AAC_D_MISC 0x02 -#define AAC_D_CMD 0x04 -#define AAC_D_QUEUE 0x08 -#define AAC_D_IO 0x10 +#define AAC_D_INTR 0x001 +#define AAC_D_MISC 0x002 +#define AAC_D_CMD 0x004 +#define AAC_D_QUEUE 0x008 +#define AAC_D_IO 0x010 +#define AAC_D_IOCTL 0x020 +#define AAC_D_LOCK 0x040 +#define AAC_D_THREAD 0x080 +#define AAC_D_FIB 0x100 extern int aac_debug; -#define AAC_PRINT_FIB(sc, fib) aac_print_fib((sc), (fib), __func__) +#define AAC_PRINT_FIB(sc, fib) do { \ + if (aac_debug & AAC_D_FIB) \ + aac_print_fib((sc), (fib), __func__); \ +} while (0) #else #define AAC_DPRINTF(mask, args) #define AAC_PRINT_FIB(sc, fib) @@ -67,6 +79,20 @@ struct aac_softc; #define AAC_ADAPTER_FIBS 8 /* + * FIBs are allocated in page-size chunks and can grow up to the 512 + * limit imposed by the hardware. + */ +#define AAC_FIB_COUNT (PAGE_SIZE/sizeof(struct aac_fib)) +#define AAC_MAX_FIBS 512 +#define AAC_FIBMAP_SIZE (PAGE_SIZE) + +/* + * The controller reports status events in AIFs. We hang on to a number of + * these in order to pass them out to user-space management tools. + */ +#define AAC_AIFQ_LENGTH 64 + +/* * Firmware messages are passed in the printf buffer. */ #define AAC_PRINTF_BUFSIZE 256 @@ -78,14 +104,25 @@ struct aac_softc; #define AAC_BOOT_TIMEOUT (3 * 60) /* - * Wait this long for a lost interrupt to get detected. + * Timeout for immediate commands. */ -#define AAC_WATCH_TIMEOUT 10000 /* 10000 * 1ms = 10s */ +#define AAC_IMMEDIATE_TIMEOUT 30 /* - * Timeout for immediate commands. + * Timeout for normal commands */ -#define AAC_IMMEDIATE_TIMEOUT 30 +#define AAC_CMD_TIMEOUT 30 /* seconds */ + +/* + * Rate at which we periodically check for timed out commands and kick the + * controller. + */ +#define AAC_PERIODIC_INTERVAL 20 /* seconds */ + +/* + * Wait this long for a lost interrupt to get detected. + */ +#define AAC_WATCH_TIMEOUT 10000 /* 10000 * 1ms = 10s */ /* * Delay 20ms after the qnotify in sync operations. Experimentally deduced. @@ -97,6 +134,7 @@ struct aac_softc; * ourselves to a reasonable maximum and ensure alignment. */ #define AAC_MAXSGENTRIES 64 /* max S/G entries, limit 65535 */ + /* * We gather a number of adapter-visible items into a single structure. * @@ -126,6 +164,7 @@ struct aac_common { /* fib for synchronous commands */ struct aac_fib ac_sync_fib; }; +#define AAC_COMMON_ALLOCSIZE (8192 + sizeof(struct aac_common)) /* * Interface operations @@ -137,42 +176,63 @@ struct aac_interface { void (*aif_set_istatus)(struct aac_softc *, int); void (*aif_set_mailbox)(struct aac_softc *, u_int32_t, u_int32_t, u_int32_t, u_int32_t, u_int32_t); - int (*aif_get_mailboxstatus)(struct aac_softc *); + int (*aif_get_mailbox)(struct aac_softc *, int mb); void (*aif_set_interrupts)(struct aac_softc *, int); }; -extern struct aac_interface aac_rx_interface; +extern struct aac_interface aac_fa_interface; extern struct aac_interface aac_sa_interface; +extern struct aac_interface aac_rx_interface; -#define AAC_GET_FWSTATUS(sc) ((sc)->sc_if.aif_get_fwstatus(sc)) +#define AAC_GET_FWSTATUS(sc) ((sc)->aac_if.aif_get_fwstatus(sc)) #define AAC_QNOTIFY(sc, qbit) \ - ((sc)->sc_if.aif_qnotify((sc), (qbit))) -#define AAC_GET_ISTATUS(sc) ((sc)->sc_if.aif_get_istatus(sc)) + ((sc)->aac_if.aif_qnotify((sc), (qbit))) +#define AAC_GET_ISTATUS(sc) ((sc)->aac_if.aif_get_istatus(sc)) #define AAC_CLEAR_ISTATUS(sc, mask) \ - ((sc)->sc_if.aif_set_istatus((sc), (mask))) + ((sc)->aac_if.aif_set_istatus((sc), (mask))) #define AAC_SET_MAILBOX(sc, command, arg0, arg1, arg2, arg3) \ do { \ - ((sc)->sc_if.aif_set_mailbox((sc), (command), (arg0), \ + ((sc)->aac_if.aif_set_mailbox((sc), (command), (arg0), \ (arg1), (arg2), (arg3))); \ } while(0) -#define AAC_GET_MAILBOXSTATUS(sc) \ - ((sc)->sc_if.aif_get_mailboxstatus(sc)) +#define AAC_GET_MAILBOX(sc, mb) \ + ((sc)->aac_if.aif_get_mailbox(sc, (mb))) #define AAC_MASK_INTERRUPTS(sc) \ - ((sc)->sc_if.aif_set_interrupts((sc), 0)) + ((sc)->aac_if.aif_set_interrupts((sc), 0)) #define AAC_UNMASK_INTERRUPTS(sc) \ - ((sc)->sc_if.aif_set_interrupts((sc), 1)) + ((sc)->aac_if.aif_set_interrupts((sc), 1)) #define AAC_SETREG4(sc, reg, val) \ - bus_space_write_4((sc)->sc_memt, (sc)->sc_memh, (reg), (val)) + bus_space_write_4((sc)->aac_memt, (sc)->aac_memh, (reg), (val)) #define AAC_GETREG4(sc, reg) \ - bus_space_read_4((sc)->sc_memt, (sc)->sc_memh, (reg)) + bus_space_read_4((sc)->aac_memt, (sc)->aac_memh, (reg)) #define AAC_SETREG2(sc, reg, val) \ - bus_space_write_2((sc)->sc_memt, (sc)->sc_memh, (reg), (val)) + bus_space_write_2((sc)->aac_memt, (sc)->aac_memh, (reg), (val)) #define AAC_GETREG2(sc, reg) \ - bus_space_read_2((sc)->sc_memt, (sc)->sc_memh, (reg)) + bus_space_read_2((sc)->aac_memt, (sc)->aac_memh, (reg)) #define AAC_SETREG1(sc, reg, val) \ - bus_space_write_1((sc)->sc_memt, (sc)->sc_memh, (reg), (val)) + bus_space_write_1((sc)->aac_memt, (sc)->aac_memh, (reg), (val)) #define AAC_GETREG1(sc, reg) \ - bus_space_read_1((sc)->sc_memt, (sc)->sc_memh, (reg)) + bus_space_read_1((sc)->aac_memt, (sc)->aac_memh, (reg)) + +/* Define the OS version specific locks */ +typedef struct rwlock aac_lock_t; +#define AAC_LOCK_INIT(l, s) do { \ + rw_init((l)); \ + AAC_DPRINTF(AAC_D_LOCK, ("%s: init lock @%s: %d\n", \ + sc->aac_dev.dv_xname, __FUNCTION__, __LINE__)); \ +} while (0) + +#define AAC_LOCK_ACQUIRE(l) do { \ + AAC_DPRINTF(AAC_D_LOCK, ("%s: lock @%s: %d\n", \ + sc->aac_dev.dv_xname, __FUNCTION__, __LINE__)); \ + rw_enter_write((l)); \ +} while (0) + +#define AAC_LOCK_RELEASE(l) do { \ + rw_exit_write((l)); \ + AAC_DPRINTF(AAC_D_LOCK, ("%s: unlock @%s: %d\n", \ + sc->aac_dev.dv_xname, __FUNCTION__, __LINE__)); \ +} while (0) /* * Per-container data structure @@ -180,69 +240,136 @@ extern struct aac_interface aac_sa_interface; struct aac_container { struct aac_mntobj co_mntobj; - struct device co_disk; + int co_found; + TAILQ_ENTRY(aac_container) co_link; }; /* * A command contol block, one for each corresponding command index of the * controller. */ -struct aac_ccb { - TAILQ_ENTRY(aac_ccb) ac_chain; - struct scsi_xfer *ac_xs; - struct aac_fib *ac_fib; /* FIB associated with this command */ - bus_addr_t ac_fibphys; /* bus address of the FIB */ - bus_dmamap_t ac_dmamap_xfer; - struct aac_sg_table *ac_sgtable;/* pointer to s/g table in command */ - int ac_timeout; - u_int32_t ac_blockno; - u_int32_t ac_blockcnt; - u_int8_t ac_flags; -#define AAC_ACF_WATCHDOG 0x1 -#define AAC_ACF_COMPLETED 0x2 +struct aac_command +{ + TAILQ_ENTRY(aac_command) cm_link; /* list linkage */ + + struct aac_softc *cm_sc; /* controller that owns us */ + + struct aac_fib *cm_fib; /* FIB for this command */ + bus_addr_t cm_fibphys; /* bus address of the FIB */ + void *cm_data; + size_t cm_datalen; + bus_dmamap_t cm_datamap; + struct aac_sg_table *cm_sgtable; /* pointer to s/g table */ + + u_int cm_flags; +#define AAC_CMD_MAPPED (1<<0) /* command has had its data mapped */ +#define AAC_CMD_DATAIN (1<<1) /* command involves data moving + * from controller to host */ +#define AAC_CMD_DATAOUT (1<<2) /* command involves data moving + * from host to controller */ +#define AAC_CMD_COMPLETED (1<<3) /* command has been completed */ +#define AAC_CMD_TIMEDOUT (1<<4) /* command taken too long */ +#define AAC_ON_AACQ_FREE (1<<5) +#define AAC_ON_AACQ_READY (1<<6) +#define AAC_ON_AACQ_BUSY (1<<7) +#define AAC_ON_AACQ_BIO (1<<8) +#define AAC_ON_AACQ_MASK ((1<<5)|(1<<6)|(1<<7)|(1<<8)) +#define AAC_QUEUE_FRZN (1<<9) /* Freeze the processing of + * commands on the queue. */ +#define AAC_ACF_WATCHDOG (1<<10) + + void (*cm_complete)(struct aac_command *); + void *cm_private; + u_int32_t cm_blkno; + u_int32_t cm_bcount; + time_t cm_timestamp; /* command creation time */ + int cm_queue; + int cm_index; +}; + +struct aac_fibmap { + TAILQ_ENTRY(aac_fibmap) fm_link; /* list linkage */ + struct aac_fib *aac_fibs; + bus_dmamap_t aac_fibmap; + bus_dma_segment_t aac_seg; + int aac_nsegs; + struct aac_command *aac_commands; +}; + +/* + * Command queue statistics + */ +#define AACQ_FREE 0 +#define AACQ_BIO 1 +#define AACQ_READY 2 +#define AACQ_BUSY 3 +#define AACQ_COUNT 4 /* total number of queues */ + +struct aac_qstat { + u_int32_t q_length; + u_int32_t q_max; }; /* * Per-controller structure. */ -struct aac_softc { - struct device sc_dev; - void *sc_ih; - struct scsi_link sc_link; /* Virtual SCSI bus for cache devs */ +struct aac_softc +{ + struct device aac_dev; + void *aac_ih; + struct scsi_link aac_link; /* Virtual SCSI bus for cache devs */ - bus_space_tag_t sc_memt; - bus_space_handle_t sc_memh; - bus_dma_tag_t sc_dmat; /* parent DMA tag */ + bus_space_tag_t aac_memt; + bus_space_handle_t aac_memh; + bus_dma_tag_t aac_dmat; /* parent DMA tag */ /* controller features, limits and status */ - int sc_state; + int aac_state; #define AAC_STATE_SUSPEND (1<<0) #define AAC_STATE_OPEN (1<<1) #define AAC_STATE_INTERRUPTS_ON (1<<2) #define AAC_STATE_AIF_SLEEPER (1<<3) - struct FsaRevision sc_revision; + struct FsaRevision aac_revision; - int sc_hwif; /* controller hardware interface */ + int aac_hwif; /* controller hardware interface */ #define AAC_HWIF_I960RX 0 #define AAC_HWIF_STRONGARM 1 +#define AAC_HWIF_FALCON 2 +#define AAC_HWIF_UNKNOWN -1 + + struct aac_common *aac_common; + bus_dmamap_t aac_common_map; + u_int32_t aac_common_busaddr; + struct aac_interface aac_if; - struct aac_common *sc_common; - u_int32_t sc_common_busaddr; - struct aac_interface sc_if; + /* command/fib resources */ + TAILQ_HEAD(,aac_fibmap) aac_fibmap_tqh; + u_int total_fibs; + struct aac_command *aac_commands; - /* XXX This should really be dynamic. It is very wasteful now. */ - struct aac_ccb sc_ccbs[AAC_ADAP_NORM_CMD_ENTRIES]; - TAILQ_HEAD(, aac_ccb) sc_free_ccb, sc_ccbq; - /* commands on hold for controller resources */ - TAILQ_HEAD(, aac_ccb) sc_ready; - /* commands which have been returned by the controller */ - TAILQ_HEAD(, aac_ccb) sc_completed; - LIST_HEAD(, scsi_xfer) sc_queue; - struct scsi_xfer *sc_queuelast; + /* command management */ + TAILQ_HEAD(,aac_command) aac_free; /* command structures + * available for reuse */ + TAILQ_HEAD(,aac_command) aac_ready; /* commands on hold for + * controller resources */ + TAILQ_HEAD(,aac_command) aac_busy; + TAILQ_HEAD(,aac_command) aac_bio; /* command management */ - struct aac_queue_table *sc_queues; - struct aac_queue_entry *sc_qentries[AAC_QUEUE_COUNT]; + struct aac_queue_table *aac_queues; + struct aac_queue_entry *aac_qentries[AAC_QUEUE_COUNT]; + + struct aac_qstat aac_qstat[AACQ_COUNT]; /* queue statistics */ + + /* connected containters */ + TAILQ_HEAD(,aac_container) aac_container_tqh; + aac_lock_t aac_container_lock; + + /* Protect the sync fib */ +#define AAC_SYNC_LOCK_FORCE (1 << 0) + aac_lock_t aac_sync_lock; + + aac_lock_t aac_io_lock; struct { u_int8_t hd_present; @@ -260,19 +387,58 @@ struct aac_softc { u_int8_t hd_ldr_no; u_int8_t hd_rw_attribs; u_int32_t hd_start_sec; - } sc_hdr[AAC_MAX_CONTAINERS]; + } aac_hdr[AAC_MAX_CONTAINERS]; + int aac_container_count; + + /* management interface */ + aac_lock_t aac_aifq_lock; + struct aac_aif_command aac_aifq[AAC_AIFQ_LENGTH]; + int aac_aifq_head; + int aac_aifq_tail; + struct selinfo aac_select; + struct proc *aifthread; + int aifflags; +#define AAC_AIFFLAGS_RUNNING (1 << 0) +#define AAC_AIFFLAGS_AIF (1 << 1) +#define AAC_AIFFLAGS_EXIT (1 << 2) +#define AAC_AIFFLAGS_EXITED (1 << 3) +#define AAC_AIFFLAGS_COMPLETE (1 << 4) +#define AAC_AIFFLAGS_PRINTF (1 << 5) +#define AAC_AIFFLAGS_PENDING (AAC_AIFFLAGS_AIF | AAC_AIFFLAGS_COMPLETE | \ + AAC_AIFFLAGS_PRINTF) + + u_int32_t flags; +#define AAC_FLAGS_PERC2QC (1 << 0) +#define AAC_FLAGS_ENABLE_CAM (1 << 1) /* No SCSI passthrough */ +#define AAC_FLAGS_CAM_NORESET (1 << 2) /* Fake SCSI resets */ +#define AAC_FLAGS_CAM_PASSONLY (1 << 3) /* Only create pass devices */ +#define AAC_FLAGS_SG_64BIT (1 << 4) /* Use 64-bit S/G addresses */ +#define AAC_FLAGS_4GB_WINDOW (1 << 5) /* Device can access host mem + * 2GB-4GB range */ +#define AAC_FLAGS_NO4GB (1 << 6) /* Can't access host mem >2GB*/ +#define AAC_FLAGS_256FIBS (1 << 7) /* Can only do 256 commands */ +#define AAC_FLAGS_BROKEN_MEMMAP (1 << 8) /* Broken HostPhysMemPages */ + + u_int32_t supported_options; + int aac_max_fibs; + void *aac_sdh; }; -/* XXX These have to become spinlocks in case of SMP */ -#define AAC_LOCK(sc) splbio() -#define AAC_UNLOCK(sc, lock) splx(lock) -typedef int aac_lock_t; +/* + * Public functions + */ +extern int aac_wait_command(struct aac_command *, int); +extern int aac_alloc_command(struct aac_softc *, struct aac_command **); +extern void aac_release_command(struct aac_command *); +extern int aac_alloc_sync_fib(struct aac_softc *, struct aac_fib **, int); +extern void aac_release_sync_fib(struct aac_softc *); +extern int aac_sync_fib(struct aac_softc *, u_int32_t, u_int32_t, + struct aac_fib *, u_int16_t); void aacminphys(struct buf *); int aac_attach(struct aac_softc *); int aac_intr(void *); -#ifdef __GNUC__ /* These all require correctly aligned buffers */ static __inline__ void aac_enc16(u_int8_t *, u_int16_t); static __inline__ void aac_enc32(u_int8_t *, u_int32_t); @@ -309,33 +475,121 @@ aac_dec32(addr) return letoh32(*(u_int32_t *)addr); } -/* - * Queue primitives - * - * These are broken out individually to make statistics gathering easier. - */ +/* Declarations copied from aac.c */ +#ifdef AAC_DEBUG +void aac_print_fib(struct aac_softc *, struct aac_fib *, const char *); +void aac_print_aif(struct aac_softc *, struct aac_aif_command *); +#endif +void aac_handle_aif(struct aac_softc *, struct aac_fib *); + + -static __inline__ void -aac_enqueue_completed(struct aac_ccb *ccb) -{ - struct aac_softc *sc = ccb->ac_xs->sc_link->adapter_softc; - aac_lock_t lock; - lock = AAC_LOCK(sc); - TAILQ_INSERT_TAIL(&sc->sc_completed, ccb, ac_chain); - AAC_UNLOCK(sc, lock); -} -static __inline__ struct aac_ccb * -aac_dequeue_completed(struct aac_softc *sc) +/* + * Queue primitives for driver queues. + */ +#define AACQ_ADD(sc, qname) \ + do { \ + struct aac_qstat *qs; \ + \ + qs = &(sc)->aac_qstat[qname]; \ + \ + qs->q_length++; \ + if (qs->q_length > qs->q_max) \ + qs->q_max = qs->q_length; \ + } while (0) + +#define AACQ_REMOVE(sc, qname) (sc)->aac_qstat[qname].q_length-- +#define AACQ_INIT(sc, qname) \ + do { \ + sc->aac_qstat[qname].q_length = 0; \ + sc->aac_qstat[qname].q_max = 0; \ + } while (0) + + +#define AACQ_COMMAND_QUEUE(name, index) \ +static __inline void \ +aac_initq_ ## name (struct aac_softc *sc) \ +{ \ + TAILQ_INIT(&sc->aac_ ## name); \ + AACQ_INIT(sc, index); \ +} \ +static __inline void \ +aac_enqueue_ ## name (struct aac_command *cm) \ +{ \ + AAC_DPRINTF(AAC_D_CMD, (": enqueue " #name)); \ + if ((cm->cm_flags & AAC_ON_AACQ_MASK) != 0) { \ + printf("command %p is on another queue, flags = %#x\n", \ + cm, cm->cm_flags); \ + panic("command is on another queue"); \ + } \ + TAILQ_INSERT_TAIL(&cm->cm_sc->aac_ ## name, cm, cm_link); \ + cm->cm_flags |= AAC_ON_ ## index; \ + AACQ_ADD(cm->cm_sc, index); \ +} \ +static __inline void \ +aac_requeue_ ## name (struct aac_command *cm) \ +{ \ + AAC_DPRINTF(AAC_D_CMD, (": requeue " #name)); \ + if ((cm->cm_flags & AAC_ON_AACQ_MASK) != 0) { \ + printf("command %p is on another queue, flags = %#x\n", \ + cm, cm->cm_flags); \ + panic("command is on another queue"); \ + } \ + TAILQ_INSERT_HEAD(&cm->cm_sc->aac_ ## name, cm, cm_link); \ + cm->cm_flags |= AAC_ON_ ## index; \ + AACQ_ADD(cm->cm_sc, index); \ +} \ +static __inline struct aac_command * \ +aac_dequeue_ ## name (struct aac_softc *sc) \ +{ \ + struct aac_command *cm; \ + \ + if ((cm = TAILQ_FIRST(&sc->aac_ ## name)) != NULL) { \ + AAC_DPRINTF(AAC_D_CMD, (": dequeue " #name)); \ + if ((cm->cm_flags & AAC_ON_ ## index) == 0) { \ + printf("dequeue - command %p not in queue, flags = %#x, " \ + "bit = %#x\n", cm, cm->cm_flags, \ + AAC_ON_ ## index); \ + panic("command not in queue"); \ + } \ + TAILQ_REMOVE(&sc->aac_ ## name, cm, cm_link); \ + cm->cm_flags &= ~AAC_ON_ ## index; \ + AACQ_REMOVE(sc, index); \ + } \ + return(cm); \ +} \ +static __inline void \ +aac_remove_ ## name (struct aac_command *cm) \ +{ \ + AAC_DPRINTF(AAC_D_CMD, (": remove " #name)); \ + if ((cm->cm_flags & AAC_ON_ ## index) == 0) { \ + printf("remove - command %p not in queue, flags = %#x, " \ + "bit = %#x\n", cm, cm->cm_flags, \ + AAC_ON_ ## index); \ + panic("command not in queue"); \ + } \ + TAILQ_REMOVE(&cm->cm_sc->aac_ ## name, cm, cm_link); \ + cm->cm_flags &= ~AAC_ON_ ## index; \ + AACQ_REMOVE(cm->cm_sc, index); \ +} \ +struct hack + +AACQ_COMMAND_QUEUE(free, AACQ_FREE); +AACQ_COMMAND_QUEUE(ready, AACQ_READY); +AACQ_COMMAND_QUEUE(busy, AACQ_BUSY); +AACQ_COMMAND_QUEUE(bio, AACQ_BIO); + +static __inline void +aac_print_printf(struct aac_softc *sc) { - struct aac_ccb *ccb; - aac_lock_t lock; - - lock = AAC_LOCK(sc); - if ((ccb = TAILQ_FIRST(&sc->sc_completed)) != NULL) - TAILQ_REMOVE(&sc->sc_completed, ccb, ac_chain); - AAC_UNLOCK(sc, lock); - return (ccb); + /* + * XXX We have the ability to read the length of the printf string + * from out of the mailboxes. + */ + printf("** %s: %.*s", sc->aac_dev.dv_xname, AAC_PRINTF_BUFSIZE, + sc->aac_common->ac_printf); + sc->aac_common->ac_printf[0] = 0; + AAC_QNOTIFY(sc, AAC_DB_PRINTF); } -#endif diff --git a/sys/dev/pci/aac_pci.c b/sys/dev/pci/aac_pci.c index 161b78354d2..f720476d938 100644 --- a/sys/dev/pci/aac_pci.c +++ b/sys/dev/pci/aac_pci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aac_pci.c,v 1.15 2005/08/09 04:10:10 mickey Exp $ */ +/* $OpenBSD: aac_pci.c,v 1.16 2005/11/18 05:39:10 nate Exp $ */ /*- * Copyright (c) 2000 Michael Smith @@ -44,6 +44,8 @@ #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/queue.h> +#include <sys/select.h> +#include <sys/rwlock.h> #include <machine/bus.h> #include <machine/endian.h> @@ -162,12 +164,10 @@ struct aac_ident { /* HP NetRAID-4M */ { PCI_VENDOR_DEC, PCI_PRODUCT_DEC_CPQ42XX, PCI_VENDOR_HP, PCI_PRODUCT_HP_NETRAID_4M, AAC_HWIF_STRONGARM }, - /* Adaptec ASR-2120S */ { PCI_VENDOR_ADP2, PCI_PRODUCT_ADP2_ASR2200S, PCI_VENDOR_ADP2, - PCI_PRODUCT_ADP2_AACASR2120S, AAC_HWIF_I960RX }, - /* Adaptec ASR-2200S */ + PCI_PRODUCT_ADP2_ASR2120S, AAC_HWIF_I960RX }, { PCI_VENDOR_ADP2, PCI_PRODUCT_ADP2_ASR2200S, PCI_VENDOR_ADP2, - PCI_PRODUCT_ADP2_AACASR2200S, AAC_HWIF_I960RX }, + PCI_PRODUCT_ADP2_ASR2200S, AAC_HWIF_I960RX }, { 0, 0, 0, 0 } }; @@ -250,8 +250,8 @@ aac_pci_attach(parent, self, aux) * Map control/status registers. */ if (pci_mapreg_map(pa, PCI_MAPREG_START, - PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT, 0, &sc->sc_memt, - &sc->sc_memh, &membase, &memsize, AAC_REGSIZE)) { + PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT, 0, &sc->aac_memt, + &sc->aac_memh, &membase, &memsize, AAC_REGSIZE)) { printf("can't find mem space\n"); goto bail_out; } @@ -262,9 +262,9 @@ aac_pci_attach(parent, self, aux) goto bail_out; } intrstr = pci_intr_string(pc, ih); - sc->sc_ih = pci_intr_establish(pc, ih, IPL_BIO, aac_intr, sc, - sc->sc_dev.dv_xname); - if (sc->sc_ih == NULL) { + sc->aac_ih = pci_intr_establish(pc, ih, IPL_BIO, aac_intr, sc, + sc->aac_dev.dv_xname); + if (sc->aac_ih == NULL) { printf("couldn't establish interrupt"); if (intrstr != NULL) printf(" at %s", intrstr); @@ -275,25 +275,30 @@ aac_pci_attach(parent, self, aux) if (intrstr != NULL) printf("%s\n", intrstr); - sc->sc_dmat = pa->pa_dmat; + sc->aac_dmat = pa->pa_dmat; for (m = aac_identifiers; m->vendor != 0; m++) if (m->vendor == PCI_VENDOR(pa->pa_id) && m->device == PCI_PRODUCT(pa->pa_id)) { if (m->subvendor == PCI_VENDOR(subsysid) && m->subdevice == PCI_PRODUCT(subsysid)) { - sc->sc_hwif = m->hwif; - switch(sc->sc_hwif) { + sc->aac_hwif = m->hwif; + switch(sc->aac_hwif) { case AAC_HWIF_I960RX: AAC_DPRINTF(AAC_D_MISC, ("set hardware up for i960Rx")); - sc->sc_if = aac_rx_interface; + sc->aac_if = aac_rx_interface; break; case AAC_HWIF_STRONGARM: AAC_DPRINTF(AAC_D_MISC, ("set hardware up for StrongARM")); - sc->sc_if = aac_sa_interface; + sc->aac_if = aac_sa_interface; + break; + case AAC_HWIF_FALCON: + AAC_DPRINTF(AAC_D_MISC, + ("set hardware up for Falcon/PPC")); + sc->aac_if = aac_fa_interface; break; } break; @@ -307,8 +312,8 @@ aac_pci_attach(parent, self, aux) bail_out: if (state > 1) - pci_intr_disestablish(pc, sc->sc_ih); + pci_intr_disestablish(pc, sc->aac_ih); if (state > 0) - bus_space_unmap(sc->sc_memt, sc->sc_memh, memsize); + bus_space_unmap(sc->aac_memt, sc->aac_memh, memsize); return; } |