Home | History | Annotate | Line # | Download | only in isa
wds.c revision 1.23.2.2
      1  1.23.2.2   thorpej /*	$NetBSD: wds.c,v 1.23.2.2 1997/11/04 06:17:09 thorpej Exp $	*/
      2       1.1   mycroft 
      3       1.8   mycroft #undef WDSDIAG
      4       1.8   mycroft #ifdef DDB
      5       1.1   mycroft #define	integrate
      6       1.8   mycroft #else
      7       1.8   mycroft #define	integrate	static inline
      8       1.8   mycroft #endif
      9       1.1   mycroft 
     10       1.1   mycroft /*
     11       1.1   mycroft  * XXX
     12       1.1   mycroft  * sense data
     13       1.1   mycroft  * aborts
     14       1.1   mycroft  * resets
     15       1.1   mycroft  */
     16       1.1   mycroft 
     17      1.19   thorpej /*-
     18      1.19   thorpej  * Copyright (c) 1997 The NetBSD Foundation, Inc.
     19      1.19   thorpej  * All rights reserved.
     20      1.19   thorpej  *
     21      1.19   thorpej  * This code is derived from software contributed to The NetBSD Foundation
     22      1.19   thorpej  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
     23      1.19   thorpej  * NASA Ames Research Center.
     24      1.19   thorpej  *
     25      1.19   thorpej  * Redistribution and use in source and binary forms, with or without
     26      1.19   thorpej  * modification, are permitted provided that the following conditions
     27      1.19   thorpej  * are met:
     28      1.19   thorpej  * 1. Redistributions of source code must retain the above copyright
     29      1.19   thorpej  *    notice, this list of conditions and the following disclaimer.
     30      1.19   thorpej  * 2. Redistributions in binary form must reproduce the above copyright
     31      1.19   thorpej  *    notice, this list of conditions and the following disclaimer in the
     32      1.19   thorpej  *    documentation and/or other materials provided with the distribution.
     33      1.19   thorpej  * 3. All advertising materials mentioning features or use of this software
     34      1.19   thorpej  *    must display the following acknowledgement:
     35      1.19   thorpej  *	This product includes software developed by the NetBSD
     36      1.19   thorpej  *	Foundation, Inc. and its contributors.
     37      1.19   thorpej  * 4. Neither the name of The NetBSD Foundation nor the names of its
     38      1.19   thorpej  *    contributors may be used to endorse or promote products derived
     39      1.19   thorpej  *    from this software without specific prior written permission.
     40      1.19   thorpej  *
     41      1.19   thorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     42      1.19   thorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     43      1.19   thorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     44      1.19   thorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     45      1.19   thorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     46      1.19   thorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     47      1.19   thorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     48      1.19   thorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     49      1.19   thorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     50      1.19   thorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     51      1.19   thorpej  * POSSIBILITY OF SUCH DAMAGE.
     52      1.19   thorpej  */
     53      1.19   thorpej 
     54       1.1   mycroft /*
     55       1.1   mycroft  * Copyright (c) 1994, 1995 Julian Highfield.  All rights reserved.
     56      1.15   mycroft  * Portions copyright (c) 1994, 1996, 1997
     57      1.15   mycroft  *	Charles M. Hannum.  All rights reserved.
     58       1.1   mycroft  *
     59       1.1   mycroft  * Redistribution and use in source and binary forms, with or without
     60       1.1   mycroft  * modification, are permitted provided that the following conditions
     61       1.1   mycroft  * are met:
     62       1.1   mycroft  * 1. Redistributions of source code must retain the above copyright
     63       1.1   mycroft  *    notice, this list of conditions and the following disclaimer.
     64       1.1   mycroft  * 2. Redistributions in binary form must reproduce the above copyright
     65       1.1   mycroft  *    notice, this list of conditions and the following disclaimer in the
     66       1.1   mycroft  *    documentation and/or other materials provided with the distribution.
     67       1.1   mycroft  * 3. All advertising materials mentioning features or use of this software
     68       1.1   mycroft  *    must display the following acknowledgement:
     69       1.1   mycroft  *	This product includes software developed by Julian Highfield.
     70       1.1   mycroft  * 4. The name of the author may not be used to endorse or promote products
     71       1.1   mycroft  *    derived from this software without specific prior written permission.
     72       1.1   mycroft  *
     73       1.1   mycroft  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     74       1.1   mycroft  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     75       1.1   mycroft  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     76       1.1   mycroft  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     77       1.1   mycroft  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     78       1.1   mycroft  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     79       1.1   mycroft  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     80       1.1   mycroft  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     81       1.1   mycroft  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     82       1.1   mycroft  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     83       1.1   mycroft  */
     84       1.1   mycroft 
     85       1.1   mycroft /*
     86       1.1   mycroft  * This driver is for the WD7000 family of SCSI controllers:
     87       1.1   mycroft  *   the WD7000-ASC, a bus-mastering DMA controller,
     88       1.1   mycroft  *   the WD7000-FASST2, an -ASC with new firmware and scatter-gather,
     89       1.1   mycroft  *   and the WD7000-ASE, which was custom manufactured for Apollo
     90       1.1   mycroft  *      workstations and seems to include an -ASC as well as floppy
     91       1.1   mycroft  *      and ESDI interfaces.
     92       1.1   mycroft  *
     93       1.1   mycroft  * Loosely based on Theo Deraadt's unfinished attempt.
     94       1.1   mycroft  */
     95       1.1   mycroft 
     96       1.1   mycroft #include <sys/types.h>
     97       1.1   mycroft #include <sys/param.h>
     98       1.1   mycroft #include <sys/systm.h>
     99       1.1   mycroft #include <sys/kernel.h>
    100       1.1   mycroft #include <sys/errno.h>
    101       1.1   mycroft #include <sys/ioctl.h>
    102       1.1   mycroft #include <sys/device.h>
    103       1.1   mycroft #include <sys/malloc.h>
    104       1.1   mycroft #include <sys/buf.h>
    105       1.1   mycroft #include <sys/proc.h>
    106       1.1   mycroft #include <sys/user.h>
    107       1.1   mycroft 
    108      1.19   thorpej #include <machine/bus.h>
    109       1.7   mycroft #include <machine/intr.h>
    110       1.1   mycroft 
    111      1.18    bouyer #include <dev/scsipi/scsi_all.h>
    112      1.18    bouyer #include <dev/scsipi/scsipi_all.h>
    113      1.18    bouyer #include <dev/scsipi/scsiconf.h>
    114       1.1   mycroft 
    115       1.1   mycroft #include <dev/isa/isavar.h>
    116       1.1   mycroft #include <dev/isa/isadmavar.h>
    117      1.15   mycroft 
    118       1.1   mycroft #include <dev/isa/wdsreg.h>
    119       1.3       cgd 
    120      1.15   mycroft #define	WDS_ISA_IOSIZE	8
    121      1.15   mycroft 
    122       1.3       cgd #ifndef DDB
    123       1.3       cgd #define Debugger() panic("should call debugger here (wds.c)")
    124       1.3       cgd #endif /* ! DDB */
    125       1.1   mycroft 
    126      1.19   thorpej #define	WDS_MAXXFER	((WDS_NSEG - 1) << PGSHIFT)
    127      1.19   thorpej 
    128       1.1   mycroft #define WDS_MBX_SIZE	16
    129       1.1   mycroft 
    130       1.1   mycroft #define WDS_SCB_MAX	32
    131       1.1   mycroft #define	SCB_HASH_SIZE	32	/* hash table size for phystokv */
    132       1.1   mycroft #define	SCB_HASH_SHIFT	9
    133       1.1   mycroft #define	SCB_HASH(x)	((((long)(x))>>SCB_HASH_SHIFT) & (SCB_HASH_SIZE - 1))
    134       1.1   mycroft 
    135       1.1   mycroft #define	wds_nextmbx(wmb, mbx, mbio) \
    136       1.1   mycroft 	if ((wmb) == &(mbx)->mbio[WDS_MBX_SIZE - 1])	\
    137       1.1   mycroft 		(wmb) = &(mbx)->mbio[0];		\
    138       1.1   mycroft 	else						\
    139       1.1   mycroft 		(wmb)++;
    140       1.1   mycroft 
    141       1.1   mycroft struct wds_mbx {
    142       1.1   mycroft 	struct wds_mbx_out mbo[WDS_MBX_SIZE];
    143       1.1   mycroft 	struct wds_mbx_in mbi[WDS_MBX_SIZE];
    144       1.1   mycroft 	struct wds_mbx_out *cmbo;	/* Collection Mail Box out */
    145       1.1   mycroft 	struct wds_mbx_out *tmbo;	/* Target Mail Box out */
    146       1.1   mycroft 	struct wds_mbx_in *tmbi;	/* Target Mail Box in */
    147       1.1   mycroft };
    148       1.1   mycroft 
    149       1.1   mycroft struct wds_softc {
    150       1.1   mycroft 	struct device sc_dev;
    151      1.15   mycroft 
    152      1.15   mycroft 	bus_space_tag_t sc_iot;
    153      1.15   mycroft 	bus_space_handle_t sc_ioh;
    154      1.19   thorpej 	bus_dma_tag_t sc_dmat;
    155      1.19   thorpej 	bus_dmamap_t sc_dmamap_mbox;	/* maps the mailbox */
    156       1.1   mycroft 	void *sc_ih;
    157       1.1   mycroft 
    158      1.19   thorpej 	struct wds_mbx *sc_mbx;
    159      1.19   thorpej #define	wmbx	(sc->sc_mbx)
    160       1.1   mycroft 	struct wds_scb *sc_scbhash[SCB_HASH_SIZE];
    161       1.1   mycroft 	TAILQ_HEAD(, wds_scb) sc_free_scb, sc_waiting_scb;
    162       1.1   mycroft 	int sc_numscbs, sc_mbofull;
    163      1.18    bouyer 	struct scsipi_link sc_link;	/* prototype for subdevs */
    164      1.15   mycroft 
    165  1.23.2.2   thorpej 	LIST_HEAD(, scsipi_xfer) sc_queue;
    166  1.23.2.2   thorpej 	struct scsipi_xfer *sc_queuelast;
    167  1.23.2.2   thorpej 
    168      1.15   mycroft 	int sc_revision;
    169      1.19   thorpej 	int sc_maxsegs;
    170      1.15   mycroft };
    171      1.15   mycroft 
    172      1.15   mycroft struct wds_probe_data {
    173      1.15   mycroft #ifdef notyet
    174      1.15   mycroft 	int sc_irq, sc_drq;
    175      1.15   mycroft #endif
    176       1.1   mycroft 	int sc_scsi_dev;
    177       1.1   mycroft };
    178       1.1   mycroft 
    179      1.15   mycroft integrate void
    180      1.15   mycroft 	wds_wait __P((bus_space_tag_t, bus_space_handle_t, int, int, int));
    181      1.15   mycroft int     wds_cmd __P((bus_space_tag_t, bus_space_handle_t, u_char *, int));
    182       1.1   mycroft integrate void wds_finish_scbs __P((struct wds_softc *));
    183       1.1   mycroft int     wdsintr __P((void *));
    184       1.1   mycroft integrate void wds_reset_scb __P((struct wds_softc *, struct wds_scb *));
    185       1.1   mycroft void    wds_free_scb __P((struct wds_softc *, struct wds_scb *));
    186  1.23.2.1   thorpej integrate int wds_init_scb __P((struct wds_softc *, struct wds_scb *));
    187      1.19   thorpej struct	wds_scb *wds_get_scb __P((struct wds_softc *, int));
    188       1.1   mycroft struct	wds_scb *wds_scb_phys_kv __P((struct wds_softc *, u_long));
    189       1.1   mycroft void	wds_queue_scb __P((struct wds_softc *, struct wds_scb *));
    190       1.1   mycroft void	wds_collect_mbo __P((struct wds_softc *));
    191       1.1   mycroft void	wds_start_scbs __P((struct wds_softc *));
    192       1.1   mycroft void    wds_done __P((struct wds_softc *, struct wds_scb *, u_char));
    193      1.15   mycroft int	wds_find __P((bus_space_tag_t, bus_space_handle_t, struct wds_probe_data *));
    194      1.15   mycroft void	wds_attach __P((struct wds_softc *, struct wds_probe_data *));
    195      1.19   thorpej void	wds_init __P((struct wds_softc *, int));
    196       1.1   mycroft void	wds_inquire_setup_information __P((struct wds_softc *));
    197       1.1   mycroft void    wdsminphys __P((struct buf *));
    198      1.18    bouyer int     wds_scsi_cmd __P((struct scsipi_xfer *));
    199       1.2   mycroft void	wds_sense  __P((struct wds_softc *, struct wds_scb *));
    200      1.18    bouyer int	wds_poll __P((struct wds_softc *, struct scsipi_xfer *, int));
    201       1.1   mycroft int	wds_ipoll __P((struct wds_softc *, struct wds_scb *, int));
    202       1.1   mycroft void	wds_timeout __P((void *));
    203      1.19   thorpej int	wds_create_scbs __P((struct wds_softc *, void *, size_t));
    204  1.23.2.2   thorpej void	wds_enqueue __P((struct wds_softc *, struct scsipi_xfer *, int));
    205  1.23.2.2   thorpej struct scsipi_xfer *wds_dequeue __P((struct wds_softc *));
    206       1.1   mycroft 
    207      1.18    bouyer struct scsipi_adapter wds_switch = {
    208       1.1   mycroft 	wds_scsi_cmd,
    209       1.1   mycroft 	wdsminphys,
    210       1.1   mycroft 	0,
    211       1.1   mycroft 	0,
    212       1.1   mycroft };
    213       1.1   mycroft 
    214       1.1   mycroft /* the below structure is so we have a default dev struct for our link struct */
    215      1.18    bouyer struct scsipi_device wds_dev = {
    216       1.1   mycroft 	NULL,			/* Use default error handler */
    217       1.1   mycroft 	NULL,			/* have a queue, served by this */
    218       1.1   mycroft 	NULL,			/* have no async handler */
    219       1.1   mycroft 	NULL,			/* Use default 'done' routine */
    220       1.1   mycroft };
    221       1.1   mycroft 
    222       1.1   mycroft int	wdsprobe __P((struct device *, void *, void *));
    223       1.1   mycroft void	wdsattach __P((struct device *, struct device *, void *));
    224       1.1   mycroft 
    225       1.1   mycroft struct cfattach wds_ca = {
    226       1.1   mycroft 	sizeof(struct wds_softc), wdsprobe, wdsattach
    227       1.1   mycroft };
    228       1.1   mycroft 
    229       1.1   mycroft struct cfdriver wds_cd = {
    230       1.1   mycroft 	NULL, "wds", DV_DULL
    231       1.1   mycroft };
    232       1.1   mycroft 
    233       1.1   mycroft #define	WDS_ABORT_TIMEOUT	2000	/* time to wait for abort (mSec) */
    234       1.1   mycroft 
    235      1.19   thorpej /* XXX Should put this in a better place. */
    236      1.19   thorpej #define	offsetof(type, member)	((size_t)(&((type *)0)->member))
    237      1.19   thorpej 
    238  1.23.2.2   thorpej /*
    239  1.23.2.2   thorpej  * Insert a scsipi_xfer into the software queue.  We overload xs->free_list
    240  1.23.2.2   thorpej  * to avoid having to allocate additional resources (since we're used
    241  1.23.2.2   thorpej  * only during resource shortages anyhow.
    242  1.23.2.2   thorpej  */
    243  1.23.2.2   thorpej void
    244  1.23.2.2   thorpej wds_enqueue(sc, xs, infront)
    245  1.23.2.2   thorpej 	struct wds_softc *sc;
    246  1.23.2.2   thorpej 	struct scsipi_xfer *xs;
    247  1.23.2.2   thorpej 	int infront;
    248  1.23.2.2   thorpej {
    249  1.23.2.2   thorpej 
    250  1.23.2.2   thorpej 	if (infront || sc->sc_queue.lh_first == NULL) {
    251  1.23.2.2   thorpej 		if (sc->sc_queue.lh_first == NULL)
    252  1.23.2.2   thorpej 			sc->sc_queuelast = xs;
    253  1.23.2.2   thorpej 		LIST_INSERT_HEAD(&sc->sc_queue, xs, free_list);
    254  1.23.2.2   thorpej 		return;
    255  1.23.2.2   thorpej 	}
    256  1.23.2.2   thorpej 
    257  1.23.2.2   thorpej 	LIST_INSERT_AFTER(sc->sc_queuelast, xs, free_list);
    258  1.23.2.2   thorpej 	sc->sc_queuelast = xs;
    259  1.23.2.2   thorpej }
    260  1.23.2.2   thorpej 
    261  1.23.2.2   thorpej /*
    262  1.23.2.2   thorpej  * Pull a scsipi_xfer off the front of the software queue.
    263  1.23.2.2   thorpej  */
    264  1.23.2.2   thorpej struct scsipi_xfer *
    265  1.23.2.2   thorpej wds_dequeue(sc)
    266  1.23.2.2   thorpej 	struct wds_softc *sc;
    267  1.23.2.2   thorpej {
    268  1.23.2.2   thorpej 	struct scsipi_xfer *xs;
    269  1.23.2.2   thorpej 
    270  1.23.2.2   thorpej 	xs = sc->sc_queue.lh_first;
    271  1.23.2.2   thorpej 	LIST_REMOVE(xs, free_list);
    272  1.23.2.2   thorpej 
    273  1.23.2.2   thorpej 	if (sc->sc_queue.lh_first == NULL)
    274  1.23.2.2   thorpej 		sc->sc_queuelast = NULL;
    275  1.23.2.2   thorpej 
    276  1.23.2.2   thorpej 	return (xs);
    277  1.23.2.2   thorpej }
    278  1.23.2.2   thorpej 
    279       1.1   mycroft integrate void
    280      1.15   mycroft wds_wait(iot, ioh, port, mask, val)
    281      1.15   mycroft 	bus_space_tag_t iot;
    282      1.15   mycroft 	bus_space_handle_t ioh;
    283       1.1   mycroft 	int port;
    284      1.15   mycroft 	int mask, val;
    285       1.1   mycroft {
    286       1.1   mycroft 
    287      1.15   mycroft 	while ((bus_space_read_1(iot, ioh, port) & mask) != val)
    288       1.1   mycroft 		;
    289       1.1   mycroft }
    290       1.1   mycroft 
    291       1.1   mycroft /*
    292       1.1   mycroft  * Write a command to the board's I/O ports.
    293       1.1   mycroft  */
    294       1.1   mycroft int
    295      1.15   mycroft wds_cmd(iot, ioh, ibuf, icnt)
    296      1.15   mycroft 	bus_space_tag_t iot;
    297      1.15   mycroft 	bus_space_handle_t ioh;
    298       1.1   mycroft 	u_char *ibuf;
    299       1.1   mycroft 	int icnt;
    300       1.1   mycroft {
    301       1.1   mycroft 	u_char c;
    302       1.1   mycroft 
    303      1.15   mycroft 	wds_wait(iot, ioh, WDS_STAT, WDSS_RDY, WDSS_RDY);
    304       1.1   mycroft 
    305       1.1   mycroft 	while (icnt--) {
    306      1.15   mycroft 		bus_space_write_1(iot, ioh, WDS_CMD, *ibuf++);
    307      1.15   mycroft 		wds_wait(iot, ioh, WDS_STAT, WDSS_RDY, WDSS_RDY);
    308      1.15   mycroft 		c = bus_space_read_1(iot, ioh, WDS_STAT);
    309       1.1   mycroft 		if (c & WDSS_REJ)
    310       1.1   mycroft 			return 1;
    311       1.1   mycroft 	}
    312       1.1   mycroft 
    313       1.1   mycroft 	return 0;
    314       1.1   mycroft }
    315       1.1   mycroft 
    316       1.1   mycroft /*
    317       1.1   mycroft  * Check for the presence of a WD7000 SCSI controller.
    318       1.1   mycroft  */
    319       1.1   mycroft int
    320       1.1   mycroft wdsprobe(parent, match, aux)
    321       1.1   mycroft 	struct device *parent;
    322       1.1   mycroft 	void *match, *aux;
    323       1.1   mycroft {
    324      1.15   mycroft 	struct isa_attach_args *ia = aux;
    325      1.15   mycroft 	bus_space_tag_t iot = ia->ia_iot;
    326      1.15   mycroft 	bus_space_handle_t ioh;
    327      1.15   mycroft 	struct wds_probe_data wpd;
    328      1.15   mycroft 	int rv;
    329      1.22   thorpej 
    330      1.22   thorpej 	/* Disallow wildcarded i/o address. */
    331      1.22   thorpej 	if (ia->ia_iobase == ISACF_PORT_DEFAULT)
    332      1.22   thorpej 		return (0);
    333      1.15   mycroft 
    334      1.15   mycroft 	if (bus_space_map(iot, ia->ia_iobase, WDS_ISA_IOSIZE, 0, &ioh))
    335      1.15   mycroft 		return (0);
    336      1.15   mycroft 
    337      1.15   mycroft 	rv = wds_find(iot, ioh, &wpd);
    338      1.15   mycroft 
    339      1.15   mycroft 	bus_space_unmap(iot, ioh, WDS_ISA_IOSIZE);
    340      1.15   mycroft 
    341      1.15   mycroft 	if (rv) {
    342      1.15   mycroft #ifdef notyet
    343      1.15   mycroft 		if (ia->ia_irq != -1 && ia->ia_irq != wpd.sc_irq)
    344      1.15   mycroft 			return (0);
    345      1.15   mycroft 		if (ia->ia_drq != -1 && ia->ia_drq != wpd.sc_drq)
    346      1.15   mycroft 			return (0);
    347      1.15   mycroft 		ia->ia_irq = wpd.sc_irq;
    348      1.15   mycroft 		ia->ia_drq = wpd.sc_drq;
    349      1.15   mycroft #else
    350      1.15   mycroft 		if (ia->ia_irq == -1)
    351      1.15   mycroft 			return (0);
    352      1.15   mycroft 		if (ia->ia_drq == -1)
    353      1.15   mycroft 			return (0);
    354       1.1   mycroft #endif
    355      1.15   mycroft 		ia->ia_msize = 0;
    356      1.15   mycroft 		ia->ia_iosize = WDS_ISA_IOSIZE;
    357      1.15   mycroft 	}
    358      1.15   mycroft 	return (rv);
    359       1.1   mycroft }
    360       1.1   mycroft 
    361       1.1   mycroft /*
    362       1.1   mycroft  * Attach all available units.
    363       1.1   mycroft  */
    364       1.1   mycroft void
    365       1.1   mycroft wdsattach(parent, self, aux)
    366       1.1   mycroft 	struct device *parent, *self;
    367       1.1   mycroft 	void *aux;
    368       1.1   mycroft {
    369       1.1   mycroft 	struct isa_attach_args *ia = aux;
    370       1.1   mycroft 	struct wds_softc *sc = (void *)self;
    371      1.15   mycroft 	bus_space_tag_t iot = ia->ia_iot;
    372      1.15   mycroft 	bus_space_handle_t ioh;
    373      1.15   mycroft 	struct wds_probe_data wpd;
    374      1.15   mycroft 	isa_chipset_tag_t ic = ia->ia_ic;
    375      1.15   mycroft 
    376      1.15   mycroft 	printf("\n");
    377      1.15   mycroft 
    378      1.23   thorpej 	if (bus_space_map(iot, ia->ia_iobase, WDS_ISA_IOSIZE, 0, &ioh)) {
    379      1.23   thorpej 		printf("%s: can't map i/o space\n", sc->sc_dev.dv_xname);
    380      1.23   thorpej 		return;
    381      1.23   thorpej 	}
    382      1.15   mycroft 
    383      1.15   mycroft 	sc->sc_iot = iot;
    384      1.15   mycroft 	sc->sc_ioh = ioh;
    385      1.20   mycroft 	sc->sc_dmat = ia->ia_dmat;
    386      1.23   thorpej 	if (!wds_find(iot, ioh, &wpd)) {
    387      1.23   thorpej 		printf("%s: wds_find failed\n", sc->sc_dev.dv_xname);
    388      1.23   thorpej 		return;
    389      1.23   thorpej 	}
    390      1.15   mycroft 
    391      1.15   mycroft 	bus_space_write_1(iot, ioh, WDS_HCR, WDSH_DRQEN);
    392      1.15   mycroft #ifdef notyet
    393      1.15   mycroft 	if (wpd.sc_drq != -1)
    394      1.17   thorpej 		isa_dmacascade(parent, wpd.sc_drq);
    395      1.15   mycroft 
    396      1.15   mycroft 	sc->sc_ih = isa_intr_establish(ic, wpd.sc_irq, IST_EDGE, IPL_BIO,
    397      1.15   mycroft 	    wdsintr, sc);
    398      1.15   mycroft #else
    399      1.15   mycroft 	if (ia->ia_drq != -1)
    400      1.17   thorpej 		isa_dmacascade(parent, ia->ia_drq);
    401       1.1   mycroft 
    402      1.15   mycroft 	sc->sc_ih = isa_intr_establish(ic, ia->ia_irq, IST_EDGE, IPL_BIO,
    403      1.15   mycroft 	    wdsintr, sc);
    404      1.15   mycroft #endif
    405      1.15   mycroft 	if (sc->sc_ih == NULL) {
    406      1.15   mycroft 		printf("%s: couldn't establish interrupt\n",
    407      1.15   mycroft 		    sc->sc_dev.dv_xname);
    408      1.15   mycroft 		return;
    409      1.15   mycroft 	}
    410       1.1   mycroft 
    411      1.15   mycroft 	wds_attach(sc, &wpd);
    412      1.15   mycroft }
    413      1.13   mycroft 
    414      1.15   mycroft void
    415      1.15   mycroft wds_attach(sc, wpd)
    416      1.15   mycroft 	struct wds_softc *sc;
    417      1.15   mycroft 	struct wds_probe_data *wpd;
    418      1.15   mycroft {
    419       1.1   mycroft 
    420       1.1   mycroft 	TAILQ_INIT(&sc->sc_free_scb);
    421       1.1   mycroft 	TAILQ_INIT(&sc->sc_waiting_scb);
    422  1.23.2.2   thorpej 	LIST_INIT(&sc->sc_queue);
    423      1.19   thorpej 
    424      1.19   thorpej 	wds_init(sc, 0);
    425       1.1   mycroft 	wds_inquire_setup_information(sc);
    426       1.1   mycroft 
    427       1.1   mycroft 	/*
    428      1.18    bouyer 	 * fill in the prototype scsipi_link.
    429       1.1   mycroft 	 */
    430      1.18    bouyer 	sc->sc_link.scsipi_scsi.channel = SCSI_CHANNEL_ONLY_ONE;
    431       1.1   mycroft 	sc->sc_link.adapter_softc = sc;
    432      1.18    bouyer 	sc->sc_link.scsipi_scsi.adapter_target = wpd->sc_scsi_dev;
    433       1.1   mycroft 	sc->sc_link.adapter = &wds_switch;
    434       1.1   mycroft 	sc->sc_link.device = &wds_dev;
    435       1.1   mycroft 	/* XXX */
    436       1.1   mycroft 	/* I don't think the -ASE can handle openings > 1. */
    437       1.1   mycroft 	/* It gives Vendor Error 26 whenever I try it.     */
    438       1.1   mycroft 	sc->sc_link.openings = 1;
    439      1.18    bouyer 	sc->sc_link.scsipi_scsi.max_target = 7;
    440      1.18    bouyer 	sc->sc_link.type = BUS_SCSI;
    441       1.1   mycroft 
    442       1.1   mycroft 	/*
    443       1.1   mycroft 	 * ask the adapter what subunits are present
    444       1.1   mycroft 	 */
    445      1.15   mycroft 	config_found(&sc->sc_dev, &sc->sc_link, scsiprint);
    446       1.1   mycroft }
    447       1.1   mycroft 
    448       1.1   mycroft integrate void
    449       1.1   mycroft wds_finish_scbs(sc)
    450       1.1   mycroft 	struct wds_softc *sc;
    451       1.1   mycroft {
    452       1.1   mycroft 	struct wds_mbx_in *wmbi;
    453       1.1   mycroft 	struct wds_scb *scb;
    454       1.1   mycroft 	int i;
    455       1.1   mycroft 
    456       1.1   mycroft 	wmbi = wmbx->tmbi;
    457       1.1   mycroft 
    458       1.1   mycroft 	if (wmbi->stat == WDS_MBI_FREE) {
    459       1.1   mycroft 		for (i = 0; i < WDS_MBX_SIZE; i++) {
    460       1.1   mycroft 			if (wmbi->stat != WDS_MBI_FREE) {
    461      1.12  christos 				printf("%s: mbi not in round-robin order\n",
    462       1.1   mycroft 				    sc->sc_dev.dv_xname);
    463       1.1   mycroft 				goto AGAIN;
    464       1.1   mycroft 			}
    465       1.1   mycroft 			wds_nextmbx(wmbi, wmbx, mbi);
    466       1.1   mycroft 		}
    467       1.1   mycroft #ifdef WDSDIAGnot
    468      1.12  christos 		printf("%s: mbi interrupt with no full mailboxes\n",
    469       1.1   mycroft 		    sc->sc_dev.dv_xname);
    470       1.1   mycroft #endif
    471       1.1   mycroft 		return;
    472       1.1   mycroft 	}
    473       1.1   mycroft 
    474       1.1   mycroft AGAIN:
    475       1.1   mycroft 	do {
    476       1.1   mycroft 		scb = wds_scb_phys_kv(sc, phystol(wmbi->scb_addr));
    477       1.1   mycroft 		if (!scb) {
    478      1.12  christos 			printf("%s: bad mbi scb pointer; skipping\n",
    479       1.1   mycroft 			    sc->sc_dev.dv_xname);
    480       1.1   mycroft 			goto next;
    481       1.1   mycroft 		}
    482       1.1   mycroft 
    483       1.1   mycroft #ifdef WDSDEBUG
    484       1.1   mycroft 		if (wds_debug) {
    485      1.18    bouyer 			u_char *cp = &scb->scsipi_cmd;
    486      1.12  christos 			printf("op=%x %x %x %x %x %x\n",
    487       1.1   mycroft 			    cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
    488      1.12  christos 			printf("stat %x for mbi addr = 0x%08x, ",
    489       1.1   mycroft 			    wmbi->stat, wmbi);
    490      1.12  christos 			printf("scb addr = 0x%x\n", scb);
    491       1.1   mycroft 		}
    492       1.1   mycroft #endif /* WDSDEBUG */
    493       1.1   mycroft 
    494       1.1   mycroft 		untimeout(wds_timeout, scb);
    495       1.1   mycroft 		wds_done(sc, scb, wmbi->stat);
    496       1.1   mycroft 
    497       1.1   mycroft 	next:
    498       1.1   mycroft 		wmbi->stat = WDS_MBI_FREE;
    499       1.1   mycroft 		wds_nextmbx(wmbi, wmbx, mbi);
    500       1.1   mycroft 	} while (wmbi->stat != WDS_MBI_FREE);
    501       1.1   mycroft 
    502       1.1   mycroft 	wmbx->tmbi = wmbi;
    503       1.1   mycroft }
    504       1.1   mycroft 
    505       1.1   mycroft /*
    506       1.1   mycroft  * Process an interrupt.
    507       1.1   mycroft  */
    508       1.1   mycroft int
    509       1.1   mycroft wdsintr(arg)
    510       1.1   mycroft 	void *arg;
    511       1.1   mycroft {
    512       1.1   mycroft 	struct wds_softc *sc = arg;
    513      1.15   mycroft 	bus_space_tag_t iot = sc->sc_iot;
    514      1.15   mycroft 	bus_space_handle_t ioh = sc->sc_ioh;
    515       1.5  christos 	u_char c;
    516       1.1   mycroft 
    517       1.1   mycroft 	/* Was it really an interrupt from the board? */
    518      1.15   mycroft 	if ((bus_space_read_1(iot, ioh, WDS_STAT) & WDSS_IRQ) == 0)
    519       1.1   mycroft 		return 0;
    520       1.1   mycroft 
    521       1.1   mycroft 	/* Get the interrupt status byte. */
    522      1.15   mycroft 	c = bus_space_read_1(iot, ioh, WDS_IRQSTAT) & WDSI_MASK;
    523       1.1   mycroft 
    524       1.1   mycroft 	/* Acknowledge (which resets) the interrupt. */
    525      1.15   mycroft 	bus_space_write_1(iot, ioh, WDS_IRQACK, 0x00);
    526       1.1   mycroft 
    527       1.1   mycroft 	switch (c) {
    528       1.1   mycroft 	case WDSI_MSVC:
    529       1.1   mycroft 		wds_finish_scbs(sc);
    530       1.1   mycroft 		break;
    531       1.1   mycroft 
    532       1.1   mycroft 	case WDSI_MFREE:
    533       1.1   mycroft 		wds_start_scbs(sc);
    534       1.1   mycroft 		break;
    535       1.1   mycroft 
    536       1.1   mycroft 	default:
    537      1.12  christos 		printf("%s: unrecognized interrupt type %02x",
    538       1.5  christos 		    sc->sc_dev.dv_xname, c);
    539       1.1   mycroft 		break;
    540       1.1   mycroft 	}
    541       1.1   mycroft 
    542       1.1   mycroft 	return 1;
    543       1.1   mycroft }
    544       1.1   mycroft 
    545       1.1   mycroft integrate void
    546       1.1   mycroft wds_reset_scb(sc, scb)
    547       1.1   mycroft 	struct wds_softc *sc;
    548       1.1   mycroft 	struct wds_scb *scb;
    549       1.1   mycroft {
    550       1.1   mycroft 
    551       1.1   mycroft 	scb->flags = 0;
    552       1.1   mycroft }
    553       1.1   mycroft 
    554       1.1   mycroft /*
    555       1.1   mycroft  * Free the command structure, the outgoing mailbox and the data buffer.
    556       1.1   mycroft  */
    557       1.1   mycroft void
    558       1.1   mycroft wds_free_scb(sc, scb)
    559       1.1   mycroft 	struct wds_softc *sc;
    560       1.1   mycroft 	struct wds_scb *scb;
    561       1.1   mycroft {
    562       1.1   mycroft 	int s;
    563       1.1   mycroft 
    564       1.1   mycroft 	s = splbio();
    565       1.1   mycroft 
    566       1.1   mycroft 	wds_reset_scb(sc, scb);
    567       1.1   mycroft 	TAILQ_INSERT_HEAD(&sc->sc_free_scb, scb, chain);
    568       1.1   mycroft 
    569       1.1   mycroft 	/*
    570       1.1   mycroft 	 * If there were none, wake anybody waiting for one to come free,
    571       1.1   mycroft 	 * starting with queued entries.
    572       1.1   mycroft 	 */
    573       1.1   mycroft 	if (scb->chain.tqe_next == 0)
    574       1.1   mycroft 		wakeup(&sc->sc_free_scb);
    575       1.1   mycroft 
    576       1.1   mycroft 	splx(s);
    577       1.1   mycroft }
    578       1.1   mycroft 
    579  1.23.2.1   thorpej integrate int
    580      1.19   thorpej wds_init_scb(sc, scb)
    581       1.1   mycroft 	struct wds_softc *sc;
    582      1.19   thorpej 	struct wds_scb *scb;
    583       1.1   mycroft {
    584      1.19   thorpej 	bus_dma_tag_t dmat = sc->sc_dmat;
    585  1.23.2.1   thorpej 	int hashnum, error;
    586       1.1   mycroft 
    587      1.19   thorpej 	/*
    588      1.19   thorpej 	 * XXX Should we put a DIAGNOSTIC check for multiple
    589      1.19   thorpej 	 * XXX SCB inits here?
    590      1.19   thorpej 	 */
    591       1.1   mycroft 
    592      1.19   thorpej 	bzero(scb, sizeof(struct wds_scb));
    593       1.1   mycroft 
    594       1.1   mycroft 	/*
    595      1.19   thorpej 	 * Create DMA maps for this SCB.
    596       1.1   mycroft 	 */
    597  1.23.2.1   thorpej 	error = bus_dmamap_create(dmat, sizeof(struct wds_scb), 1,
    598  1.23.2.1   thorpej 	    sizeof(struct wds_scb), 0, BUS_DMA_NOWAIT, &scb->dmamap_self);
    599  1.23.2.1   thorpej 	if (error) {
    600  1.23.2.1   thorpej 		printf("%s: can't create scb dmamap_self\n",
    601  1.23.2.1   thorpej 		    sc->sc_dev.dv_xname);
    602  1.23.2.1   thorpej 		return (error);
    603  1.23.2.1   thorpej 	}
    604       1.1   mycroft 
    605  1.23.2.1   thorpej 	error = bus_dmamap_create(dmat, WDS_MAXXFER, WDS_NSEG, WDS_MAXXFER,
    606  1.23.2.1   thorpej 	    0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &scb->dmamap_xfer);
    607  1.23.2.1   thorpej 	if (error) {
    608  1.23.2.1   thorpej 		printf("%s: can't create scb dmamap_xfer\n",
    609  1.23.2.1   thorpej 		    sc->sc_dev.dv_xname);
    610  1.23.2.1   thorpej 		bus_dmamap_destroy(dmat, scb->dmamap_self);
    611  1.23.2.1   thorpej 		return (error);
    612  1.23.2.1   thorpej 	}
    613       1.1   mycroft 
    614      1.19   thorpej 	/*
    615      1.19   thorpej 	 * Load the permanent DMA maps.
    616      1.19   thorpej 	 */
    617  1.23.2.1   thorpej 	error = bus_dmamap_load(dmat, scb->dmamap_self, scb,
    618  1.23.2.1   thorpej 	    sizeof(struct wds_scb), NULL, BUS_DMA_NOWAIT);
    619  1.23.2.1   thorpej 	if (error) {
    620  1.23.2.1   thorpej 		printf("%s: can't load scb dmamap_self\n",
    621  1.23.2.1   thorpej 		    sc->sc_dev.dv_xname);
    622  1.23.2.1   thorpej 		bus_dmamap_destroy(dmat, scb->dmamap_self);
    623  1.23.2.1   thorpej 		bus_dmamap_destroy(dmat, scb->dmamap_xfer);
    624  1.23.2.1   thorpej 		return (error);
    625  1.23.2.1   thorpej 	}
    626       1.1   mycroft 
    627       1.1   mycroft 	/*
    628       1.1   mycroft 	 * put in the phystokv hash table
    629       1.1   mycroft 	 * Never gets taken out.
    630       1.1   mycroft 	 */
    631      1.19   thorpej 	scb->hashkey = scb->dmamap_self->dm_segs[0].ds_addr;
    632       1.1   mycroft 	hashnum = SCB_HASH(scb->hashkey);
    633       1.1   mycroft 	scb->nexthash = sc->sc_scbhash[hashnum];
    634       1.1   mycroft 	sc->sc_scbhash[hashnum] = scb;
    635       1.1   mycroft 	wds_reset_scb(sc, scb);
    636  1.23.2.1   thorpej 	return (0);
    637       1.1   mycroft }
    638       1.1   mycroft 
    639       1.1   mycroft /*
    640      1.19   thorpej  * Create a set of scbs and add them to the free list.
    641      1.19   thorpej  */
    642      1.19   thorpej int
    643      1.19   thorpej wds_create_scbs(sc, mem, size)
    644      1.19   thorpej 	struct wds_softc *sc;
    645      1.19   thorpej 	void *mem;
    646      1.19   thorpej 	size_t size;
    647      1.19   thorpej {
    648      1.19   thorpej 	bus_dma_segment_t seg;
    649      1.19   thorpej 	struct wds_scb *scb;
    650      1.19   thorpej 	int rseg, error;
    651      1.19   thorpej 
    652      1.19   thorpej 	if (sc->sc_numscbs >= WDS_SCB_MAX)
    653      1.19   thorpej 		return (0);
    654      1.19   thorpej 
    655      1.19   thorpej 	if ((scb = mem) != NULL)
    656      1.19   thorpej 		goto have_mem;
    657      1.19   thorpej 
    658      1.19   thorpej 	size = NBPG;
    659      1.19   thorpej 	error = bus_dmamem_alloc(sc->sc_dmat, size, NBPG, 0, &seg, 1, &rseg,
    660      1.19   thorpej 	    BUS_DMA_NOWAIT);
    661  1.23.2.1   thorpej 	if (error) {
    662  1.23.2.1   thorpej 		printf("%s: can't allocate memory for scbs\n",
    663  1.23.2.1   thorpej 		    sc->sc_dev.dv_xname);
    664      1.19   thorpej 		return (error);
    665  1.23.2.1   thorpej 	}
    666      1.19   thorpej 
    667      1.19   thorpej 	error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, size,
    668      1.19   thorpej 	    (caddr_t *)&scb, BUS_DMA_NOWAIT|BUS_DMAMEM_NOSYNC);
    669      1.19   thorpej 	if (error) {
    670  1.23.2.1   thorpej 		printf("%s: can't map memory for scbs\n",
    671  1.23.2.1   thorpej 		    sc->sc_dev.dv_xname);
    672      1.19   thorpej 		bus_dmamem_free(sc->sc_dmat, &seg, rseg);
    673      1.19   thorpej 		return (error);
    674      1.19   thorpej 	}
    675      1.19   thorpej 
    676      1.19   thorpej  have_mem:
    677      1.19   thorpej 	bzero(scb, size);
    678  1.23.2.1   thorpej 	while (size > sizeof(struct wds_scb) && sc->sc_numscbs < WDS_SCB_MAX) {
    679  1.23.2.1   thorpej 		error = wds_init_scb(sc, scb);
    680  1.23.2.1   thorpej 		if (error) {
    681  1.23.2.1   thorpej 			printf("%s: can't initialize scb\n",
    682  1.23.2.1   thorpej 			    sc->sc_dev.dv_xname);
    683  1.23.2.1   thorpej 			return (error);
    684  1.23.2.1   thorpej 		}
    685      1.19   thorpej 		TAILQ_INSERT_TAIL(&sc->sc_free_scb, scb, chain);
    686      1.19   thorpej 		(caddr_t)scb += ALIGN(sizeof(struct wds_scb));
    687      1.19   thorpej 		size -= ALIGN(sizeof(struct wds_scb));
    688  1.23.2.1   thorpej 		sc->sc_numscbs++;
    689      1.19   thorpej 	}
    690      1.19   thorpej 
    691      1.19   thorpej 	return (0);
    692      1.19   thorpej }
    693      1.19   thorpej 
    694      1.19   thorpej /*
    695       1.1   mycroft  * Get a free scb
    696       1.1   mycroft  *
    697       1.1   mycroft  * If there are none, see if we can allocate a new one.  If so, put it in
    698       1.1   mycroft  * the hash table too otherwise either return an error or sleep.
    699       1.1   mycroft  */
    700       1.1   mycroft struct wds_scb *
    701      1.19   thorpej wds_get_scb(sc, flags)
    702       1.1   mycroft 	struct wds_softc *sc;
    703       1.1   mycroft 	int flags;
    704       1.1   mycroft {
    705       1.1   mycroft 	struct wds_scb *scb;
    706       1.1   mycroft 	int s;
    707       1.1   mycroft 
    708       1.1   mycroft 	s = splbio();
    709       1.1   mycroft 
    710       1.1   mycroft 	/*
    711       1.1   mycroft 	 * If we can and have to, sleep waiting for one to come free
    712       1.1   mycroft 	 * but only if we can't allocate a new one.
    713       1.1   mycroft 	 */
    714       1.1   mycroft 	for (;;) {
    715       1.1   mycroft 		scb = sc->sc_free_scb.tqh_first;
    716       1.1   mycroft 		if (scb) {
    717       1.1   mycroft 			TAILQ_REMOVE(&sc->sc_free_scb, scb, chain);
    718       1.1   mycroft 			break;
    719       1.1   mycroft 		}
    720       1.1   mycroft 		if (sc->sc_numscbs < WDS_SCB_MAX) {
    721  1.23.2.1   thorpej 			/*
    722  1.23.2.1   thorpej 			 * wds_create_scbs() might have managed to create
    723  1.23.2.1   thorpej 			 * one before it failed.  If so, don't abort,
    724  1.23.2.1   thorpej 			 * just grab it and continue to hobble along.
    725  1.23.2.1   thorpej 			 */
    726  1.23.2.1   thorpej 			if (wds_create_scbs(sc, NULL, 0) != 0 &&
    727  1.23.2.1   thorpej 			    sc->sc_free_scb.tqh_first == NULL) {
    728      1.19   thorpej 				printf("%s: can't allocate scbs\n",
    729       1.1   mycroft 				    sc->sc_dev.dv_xname);
    730       1.1   mycroft 				goto out;
    731       1.1   mycroft 			}
    732      1.19   thorpej 			continue;
    733       1.1   mycroft 		}
    734       1.1   mycroft 		if ((flags & SCSI_NOSLEEP) != 0)
    735       1.1   mycroft 			goto out;
    736       1.1   mycroft 		tsleep(&sc->sc_free_scb, PRIBIO, "wdsscb", 0);
    737       1.1   mycroft 	}
    738       1.1   mycroft 
    739       1.1   mycroft 	scb->flags |= SCB_ALLOC;
    740       1.1   mycroft 
    741       1.1   mycroft out:
    742       1.1   mycroft 	splx(s);
    743       1.1   mycroft 	return (scb);
    744       1.1   mycroft }
    745       1.1   mycroft 
    746       1.1   mycroft struct wds_scb *
    747       1.1   mycroft wds_scb_phys_kv(sc, scb_phys)
    748       1.1   mycroft 	struct wds_softc *sc;
    749       1.1   mycroft 	u_long scb_phys;
    750       1.1   mycroft {
    751       1.1   mycroft 	int hashnum = SCB_HASH(scb_phys);
    752       1.1   mycroft 	struct wds_scb *scb = sc->sc_scbhash[hashnum];
    753       1.1   mycroft 
    754       1.1   mycroft 	while (scb) {
    755       1.1   mycroft 		if (scb->hashkey == scb_phys)
    756       1.1   mycroft 			break;
    757       1.1   mycroft 		/* XXX Check to see if it matches the sense command block. */
    758       1.1   mycroft 		if (scb->hashkey == (scb_phys - sizeof(struct wds_cmd)))
    759       1.1   mycroft 			break;
    760       1.1   mycroft 		scb = scb->nexthash;
    761       1.1   mycroft 	}
    762      1.21   mycroft 	return (scb);
    763       1.1   mycroft }
    764       1.1   mycroft 
    765       1.1   mycroft /*
    766       1.1   mycroft  * Queue a SCB to be sent to the controller, and send it if possible.
    767       1.1   mycroft  */
    768       1.1   mycroft void
    769       1.1   mycroft wds_queue_scb(sc, scb)
    770       1.1   mycroft 	struct wds_softc *sc;
    771       1.1   mycroft 	struct wds_scb *scb;
    772       1.1   mycroft {
    773       1.1   mycroft 
    774       1.1   mycroft 	TAILQ_INSERT_TAIL(&sc->sc_waiting_scb, scb, chain);
    775       1.1   mycroft 	wds_start_scbs(sc);
    776       1.1   mycroft }
    777       1.1   mycroft 
    778       1.1   mycroft /*
    779       1.1   mycroft  * Garbage collect mailboxes that are no longer in use.
    780       1.1   mycroft  */
    781       1.1   mycroft void
    782       1.1   mycroft wds_collect_mbo(sc)
    783       1.1   mycroft 	struct wds_softc *sc;
    784       1.1   mycroft {
    785       1.1   mycroft 	struct wds_mbx_out *wmbo;	/* Mail Box Out pointer */
    786      1.11  christos #ifdef WDSDIAG
    787       1.1   mycroft 	struct wds_scb *scb;
    788      1.11  christos #endif
    789       1.1   mycroft 
    790       1.1   mycroft 	wmbo = wmbx->cmbo;
    791       1.1   mycroft 
    792       1.1   mycroft 	while (sc->sc_mbofull > 0) {
    793       1.1   mycroft 		if (wmbo->cmd != WDS_MBO_FREE)
    794       1.1   mycroft 			break;
    795       1.1   mycroft 
    796       1.1   mycroft #ifdef WDSDIAG
    797       1.1   mycroft 		scb = wds_scb_phys_kv(sc, phystol(wmbo->scb_addr));
    798       1.1   mycroft 		scb->flags &= ~SCB_SENDING;
    799       1.1   mycroft #endif
    800       1.1   mycroft 
    801       1.1   mycroft 		--sc->sc_mbofull;
    802       1.1   mycroft 		wds_nextmbx(wmbo, wmbx, mbo);
    803       1.1   mycroft 	}
    804       1.1   mycroft 
    805       1.1   mycroft 	wmbx->cmbo = wmbo;
    806       1.1   mycroft }
    807       1.1   mycroft 
    808       1.1   mycroft /*
    809       1.1   mycroft  * Send as many SCBs as we have empty mailboxes for.
    810       1.1   mycroft  */
    811       1.1   mycroft void
    812       1.1   mycroft wds_start_scbs(sc)
    813       1.1   mycroft 	struct wds_softc *sc;
    814       1.1   mycroft {
    815      1.15   mycroft 	bus_space_tag_t iot = sc->sc_iot;
    816      1.15   mycroft 	bus_space_handle_t ioh = sc->sc_ioh;
    817       1.1   mycroft 	struct wds_mbx_out *wmbo;	/* Mail Box Out pointer */
    818       1.1   mycroft 	struct wds_scb *scb;
    819       1.1   mycroft 	u_char c;
    820       1.1   mycroft 
    821       1.1   mycroft 	wmbo = wmbx->tmbo;
    822       1.1   mycroft 
    823       1.5  christos 	while ((scb = sc->sc_waiting_scb.tqh_first) != NULL) {
    824       1.1   mycroft 		if (sc->sc_mbofull >= WDS_MBX_SIZE) {
    825       1.1   mycroft 			wds_collect_mbo(sc);
    826       1.1   mycroft 			if (sc->sc_mbofull >= WDS_MBX_SIZE) {
    827       1.1   mycroft 				c = WDSC_IRQMFREE;
    828      1.15   mycroft 				wds_cmd(iot, ioh, &c, sizeof c);
    829       1.1   mycroft 				break;
    830       1.1   mycroft 			}
    831       1.1   mycroft 		}
    832       1.1   mycroft 
    833       1.1   mycroft 		TAILQ_REMOVE(&sc->sc_waiting_scb, scb, chain);
    834       1.1   mycroft #ifdef WDSDIAG
    835       1.1   mycroft 		scb->flags |= SCB_SENDING;
    836       1.1   mycroft #endif
    837       1.1   mycroft 
    838       1.1   mycroft 		/* Link scb to mbo. */
    839       1.1   mycroft 		if (scb->flags & SCB_SENSE)
    840      1.19   thorpej 			ltophys(scb->dmamap_self->dm_segs[0].ds_addr +
    841      1.19   thorpej 			    offsetof(struct wds_scb, sense), wmbo->scb_addr);
    842       1.1   mycroft 		else
    843      1.19   thorpej 			ltophys(scb->dmamap_self->dm_segs[0].ds_addr +
    844      1.19   thorpej 			    offsetof(struct wds_scb, cmd), wmbo->scb_addr);
    845       1.1   mycroft 		/* XXX What about aborts? */
    846       1.1   mycroft 		wmbo->cmd = WDS_MBO_START;
    847       1.1   mycroft 
    848       1.1   mycroft 		/* Tell the card to poll immediately. */
    849       1.1   mycroft 		c = WDSC_MSTART(wmbo - wmbx->mbo);
    850      1.15   mycroft 		wds_cmd(sc->sc_iot, sc->sc_ioh, &c, sizeof c);
    851       1.1   mycroft 
    852       1.1   mycroft 		if ((scb->flags & SCB_POLLED) == 0)
    853       1.1   mycroft 			timeout(wds_timeout, scb, (scb->timeout * hz) / 1000);
    854       1.1   mycroft 
    855       1.1   mycroft 		++sc->sc_mbofull;
    856       1.1   mycroft 		wds_nextmbx(wmbo, wmbx, mbo);
    857       1.1   mycroft 	}
    858       1.1   mycroft 
    859       1.1   mycroft 	wmbx->tmbo = wmbo;
    860       1.1   mycroft }
    861       1.1   mycroft 
    862       1.1   mycroft /*
    863       1.1   mycroft  * Process the result of a SCSI command.
    864       1.1   mycroft  */
    865       1.1   mycroft void
    866       1.1   mycroft wds_done(sc, scb, stat)
    867       1.1   mycroft 	struct wds_softc *sc;
    868       1.1   mycroft 	struct wds_scb *scb;
    869       1.1   mycroft 	u_char stat;
    870       1.1   mycroft {
    871      1.19   thorpej 	bus_dma_tag_t dmat = sc->sc_dmat;
    872      1.18    bouyer 	struct scsipi_xfer *xs = scb->xs;
    873       1.1   mycroft 
    874       1.1   mycroft 	/* XXXXX */
    875       1.1   mycroft 
    876       1.1   mycroft 	/* Don't release the SCB if it was an internal command. */
    877       1.1   mycroft 	if (xs == 0) {
    878       1.1   mycroft 		scb->flags |= SCB_DONE;
    879       1.1   mycroft 		return;
    880       1.1   mycroft 	}
    881       1.1   mycroft 
    882       1.1   mycroft 	/* Sense handling. */
    883       1.1   mycroft 	if (xs->error == XS_SENSE) {
    884      1.18    bouyer 		bcopy(&scb->sense_data, &xs->sense.scsi_sense,
    885      1.18    bouyer 			sizeof (struct scsipi_sense_data));
    886       1.1   mycroft 	} else {
    887      1.19   thorpej 		/*
    888      1.19   thorpej 		 * If we were a data transfer, unload the map that described
    889      1.19   thorpej 		 * the data buffer.
    890      1.19   thorpej 		 */
    891      1.19   thorpej 		if (xs->datalen) {
    892      1.19   thorpej 			bus_dmamap_sync(dmat, scb->dmamap_xfer,
    893      1.19   thorpej 			    (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD :
    894      1.19   thorpej 			    BUS_DMASYNC_POSTWRITE);
    895      1.19   thorpej 			bus_dmamap_unload(dmat, scb->dmamap_xfer);
    896      1.19   thorpej 		}
    897       1.1   mycroft 		if (xs->error == XS_NOERROR) {
    898       1.1   mycroft 			/* If all went well, or an error is acceptable. */
    899       1.1   mycroft 			if (stat == WDS_MBI_OK) {
    900       1.1   mycroft 				/* OK, set the result */
    901       1.1   mycroft 				xs->resid = 0;
    902       1.1   mycroft 			} else {
    903       1.1   mycroft 				/* Check the mailbox status. */
    904       1.1   mycroft 				switch (stat) {
    905       1.1   mycroft 				case WDS_MBI_OKERR:
    906      1.19   thorpej 					/*
    907      1.19   thorpej 					 * SCSI error recorded in scb,
    908      1.19   thorpej 					 * counts as WDS_MBI_OK
    909      1.19   thorpej 					 */
    910       1.1   mycroft 					switch (scb->cmd.venderr) {
    911       1.1   mycroft 					case 0x00:
    912      1.19   thorpej 						printf("%s: Is this "
    913      1.19   thorpej 						    "an error?\n",
    914      1.19   thorpej 						    sc->sc_dev.dv_xname);
    915      1.19   thorpej 						/* Experiment. */
    916      1.19   thorpej 						xs->error = XS_DRIVER_STUFFUP;
    917       1.1   mycroft 						break;
    918       1.1   mycroft 					case 0x01:
    919      1.19   thorpej #if 0
    920      1.19   thorpej 						printf("%s: OK, see SCSI "
    921      1.19   thorpej 						    "error field.\n",
    922      1.19   thorpej 						    sc->sc_dev.dv_xname);
    923      1.19   thorpej #endif
    924      1.19   thorpej 						if (scb->cmd.stat ==
    925      1.19   thorpej 						    SCSI_CHECK) {
    926       1.1   mycroft 							/* Do sense. */
    927      1.19   thorpej 							wds_sense(sc, scb);
    928       1.1   mycroft 							return;
    929      1.19   thorpej 						} else if (scb->cmd.stat ==
    930      1.19   thorpej 						    SCSI_BUSY) {
    931       1.1   mycroft 							xs->error = XS_BUSY;
    932       1.1   mycroft 						}
    933       1.1   mycroft 						break;
    934       1.1   mycroft 					case 0x40:
    935      1.19   thorpej #if 0
    936      1.19   thorpej 						printf("%s: DMA underrun!\n",
    937      1.19   thorpej 						    sc->sc_dev.dv_xname);
    938      1.19   thorpej #endif
    939      1.19   thorpej 						/*
    940      1.19   thorpej 						 * Hits this if the target
    941      1.19   thorpej 						 * returns fewer that datalen
    942      1.19   thorpej 						 * bytes (eg my CD-ROM, which
    943      1.19   thorpej 						 * returns a short version
    944      1.19   thorpej 						 * string, or if DMA is
    945      1.19   thorpej 						 * turned off etc.
    946      1.19   thorpej 						 */
    947       1.1   mycroft 						xs->resid = 0;
    948       1.1   mycroft 						break;
    949       1.1   mycroft 					default:
    950      1.19   thorpej 						printf("%s: VENDOR ERROR "
    951      1.19   thorpej 						    "%02x, scsi %02x\n",
    952      1.19   thorpej 						    sc->sc_dev.dv_xname,
    953      1.19   thorpej 						    scb->cmd.venderr,
    954      1.19   thorpej 						    scb->cmd.stat);
    955      1.19   thorpej 						/* Experiment. */
    956      1.19   thorpej 						xs->error = XS_DRIVER_STUFFUP;
    957       1.1   mycroft 						break;
    958       1.1   mycroft 					}
    959       1.1   mycroft 					break;
    960       1.1   mycroft 				case WDS_MBI_ETIME:
    961       1.1   mycroft 					/*
    962       1.1   mycroft 					 * The documentation isn't clear on
    963       1.1   mycroft 					 * what conditions might generate this,
    964       1.1   mycroft 					 * but selection timeouts are the only
    965       1.1   mycroft 					 * one I can think of.
    966       1.1   mycroft 					 */
    967       1.1   mycroft 					xs->error = XS_SELTIMEOUT;
    968       1.1   mycroft 					break;
    969       1.1   mycroft 				case WDS_MBI_ERESET:
    970       1.1   mycroft 				case WDS_MBI_ETARCMD:
    971       1.1   mycroft 				case WDS_MBI_ERESEL:
    972       1.1   mycroft 				case WDS_MBI_ESEL:
    973       1.1   mycroft 				case WDS_MBI_EABORT:
    974       1.1   mycroft 				case WDS_MBI_ESRESET:
    975       1.1   mycroft 				case WDS_MBI_EHRESET:
    976       1.1   mycroft 					xs->error = XS_DRIVER_STUFFUP;
    977       1.1   mycroft 					break;
    978       1.1   mycroft 				}
    979       1.1   mycroft 			}
    980       1.1   mycroft 		} /* else sense */
    981       1.1   mycroft 	} /* XS_NOERROR */
    982       1.1   mycroft 
    983       1.1   mycroft 	wds_free_scb(sc, scb);
    984       1.1   mycroft 	xs->flags |= ITSDONE;
    985      1.18    bouyer 	scsipi_done(xs);
    986  1.23.2.2   thorpej 
    987  1.23.2.2   thorpej 	/*
    988  1.23.2.2   thorpej 	 * If there are queue entries in the software queue, try to
    989  1.23.2.2   thorpej 	 * run the first one.  We should be more or less guaranteed
    990  1.23.2.2   thorpej 	 * to succeed, since we just freed a CCB.
    991  1.23.2.2   thorpej 	 *
    992  1.23.2.2   thorpej 	 * NOTE: wds_scsi_cmd() relies on our calling it with
    993  1.23.2.2   thorpej 	 * the first entry in the queue.
    994  1.23.2.2   thorpej 	 */
    995  1.23.2.2   thorpej 	if ((xs = sc->sc_queue.lh_first) != NULL)
    996  1.23.2.2   thorpej 		(void) wds_scsi_cmd(xs);
    997       1.1   mycroft }
    998       1.1   mycroft 
    999       1.1   mycroft int
   1000      1.15   mycroft wds_find(iot, ioh, sc)
   1001      1.15   mycroft 	bus_space_tag_t iot;
   1002      1.15   mycroft 	bus_space_handle_t ioh;
   1003      1.15   mycroft 	struct wds_probe_data *sc;
   1004       1.1   mycroft {
   1005       1.1   mycroft 	int i;
   1006       1.1   mycroft 
   1007       1.1   mycroft 	/* XXXXX */
   1008       1.1   mycroft 
   1009       1.1   mycroft 	/*
   1010       1.1   mycroft 	 * Sending a command causes the CMDRDY bit to clear.
   1011       1.1   mycroft  	 */
   1012      1.15   mycroft 	for (i = 5; i; i--) {
   1013      1.15   mycroft 		if ((bus_space_read_1(iot, ioh, WDS_STAT) & WDSS_RDY) != 0)
   1014      1.15   mycroft 			break;
   1015      1.15   mycroft 		delay(100);
   1016       1.1   mycroft 	}
   1017      1.15   mycroft 	if (!i)
   1018      1.16      marc 		return 0;
   1019       1.1   mycroft 
   1020      1.15   mycroft 	bus_space_write_1(iot, ioh, WDS_CMD, WDSC_NOOP);
   1021      1.15   mycroft 	if ((bus_space_read_1(iot, ioh, WDS_STAT) & WDSS_RDY) != 0)
   1022      1.16      marc 		return 0;
   1023       1.1   mycroft 
   1024      1.15   mycroft 	bus_space_write_1(iot, ioh, WDS_HCR, WDSH_SCSIRESET|WDSH_ASCRESET);
   1025       1.1   mycroft 	delay(10000);
   1026      1.15   mycroft 	bus_space_write_1(iot, ioh, WDS_HCR, 0x00);
   1027       1.1   mycroft 	delay(500000);
   1028      1.15   mycroft 	wds_wait(iot, ioh, WDS_STAT, WDSS_RDY, WDSS_RDY);
   1029      1.15   mycroft 	if (bus_space_read_1(iot, ioh, WDS_IRQSTAT) != 1)
   1030      1.15   mycroft 		if (bus_space_read_1(iot, ioh, WDS_IRQSTAT) != 7)
   1031      1.16      marc 			return 0;
   1032      1.15   mycroft 
   1033      1.15   mycroft 	for (i = 2000; i; i--) {
   1034      1.15   mycroft 		if ((bus_space_read_1(iot, ioh, WDS_STAT) & WDSS_RDY) != 0)
   1035      1.15   mycroft 			break;
   1036      1.15   mycroft 		delay(100);
   1037       1.1   mycroft 	}
   1038      1.15   mycroft 	if (!i)
   1039      1.16      marc 		return 0;
   1040       1.1   mycroft 
   1041      1.15   mycroft 	if (sc) {
   1042      1.15   mycroft #ifdef notyet
   1043      1.15   mycroft 		sc->sc_irq = ...;
   1044      1.15   mycroft 		sc->sc_drq = ...;
   1045      1.15   mycroft #endif
   1046       1.1   mycroft 		/* XXX Can we do this better? */
   1047       1.1   mycroft 		sc->sc_scsi_dev = 7;
   1048       1.1   mycroft 	}
   1049       1.1   mycroft 
   1050      1.16      marc 	return 1;
   1051       1.1   mycroft }
   1052       1.1   mycroft 
   1053       1.1   mycroft /*
   1054       1.1   mycroft  * Initialise the board and driver.
   1055       1.1   mycroft  */
   1056       1.1   mycroft void
   1057      1.19   thorpej wds_init(sc, isreset)
   1058       1.1   mycroft 	struct wds_softc *sc;
   1059      1.19   thorpej 	int isreset;
   1060       1.1   mycroft {
   1061      1.15   mycroft 	bus_space_tag_t iot = sc->sc_iot;
   1062      1.15   mycroft 	bus_space_handle_t ioh = sc->sc_ioh;
   1063      1.19   thorpej 	bus_dma_segment_t seg;
   1064       1.1   mycroft 	struct wds_setup init;
   1065       1.1   mycroft 	u_char c;
   1066      1.19   thorpej 	int i, rseg;
   1067      1.19   thorpej 
   1068      1.19   thorpej 	if (isreset)
   1069      1.19   thorpej 		goto doinit;
   1070      1.19   thorpej 
   1071      1.19   thorpej 	/*
   1072      1.19   thorpej 	 * Allocate the mailbox.
   1073      1.19   thorpej 	 */
   1074      1.19   thorpej 	if (bus_dmamem_alloc(sc->sc_dmat, NBPG, NBPG, 0, &seg, 1,
   1075      1.19   thorpej 	    &rseg, BUS_DMA_NOWAIT) ||
   1076      1.19   thorpej 	    bus_dmamem_map(sc->sc_dmat, &seg, rseg, NBPG,
   1077      1.19   thorpej 	    (caddr_t *)&wmbx, BUS_DMA_NOWAIT|BUS_DMAMEM_NOSYNC))
   1078      1.19   thorpej 		panic("wds_init: can't create or map mailbox");
   1079      1.19   thorpej 
   1080      1.21   mycroft 	/*
   1081      1.21   mycroft 	 * Since DMA memory allocation is always rounded up to a
   1082      1.21   mycroft 	 * page size, create some scbs from the leftovers.
   1083      1.21   mycroft 	 */
   1084      1.21   mycroft 	if (wds_create_scbs(sc, ((caddr_t)wmbx) +
   1085      1.21   mycroft 	    ALIGN(sizeof(struct wds_mbx)),
   1086      1.21   mycroft 	    NBPG - ALIGN(sizeof(struct wds_mbx))))
   1087      1.21   mycroft 		panic("wds_init: can't create scbs");
   1088      1.19   thorpej 
   1089      1.19   thorpej 	/*
   1090      1.19   thorpej 	 * Create and load the mailbox DMA map.
   1091      1.19   thorpej 	 */
   1092      1.19   thorpej 	if (bus_dmamap_create(sc->sc_dmat, sizeof(struct wds_mbx), 1,
   1093      1.19   thorpej 	    sizeof(struct wds_mbx), 0, BUS_DMA_NOWAIT, &sc->sc_dmamap_mbox) ||
   1094      1.19   thorpej 	    bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap_mbox, wmbx,
   1095      1.19   thorpej 	    sizeof(struct wds_mbx), NULL, BUS_DMA_NOWAIT))
   1096      1.19   thorpej 		panic("wds_ionit: can't craete or load mailbox dma map");
   1097       1.1   mycroft 
   1098      1.19   thorpej  doinit:
   1099       1.1   mycroft 	/*
   1100       1.1   mycroft 	 * Set up initial mail box for round-robin operation.
   1101       1.1   mycroft 	 */
   1102       1.1   mycroft 	for (i = 0; i < WDS_MBX_SIZE; i++) {
   1103       1.1   mycroft 		wmbx->mbo[i].cmd = WDS_MBO_FREE;
   1104       1.6   mycroft 		wmbx->mbi[i].stat = WDS_MBI_FREE;
   1105       1.1   mycroft 	}
   1106       1.1   mycroft 	wmbx->cmbo = wmbx->tmbo = &wmbx->mbo[0];
   1107       1.1   mycroft 	wmbx->tmbi = &wmbx->mbi[0];
   1108       1.1   mycroft 	sc->sc_mbofull = 0;
   1109       1.1   mycroft 
   1110       1.1   mycroft 	init.opcode = WDSC_INIT;
   1111      1.18    bouyer 	init.scsi_id = sc->sc_link.scsipi_scsi.adapter_target;
   1112       1.1   mycroft 	init.buson_t = 48;
   1113       1.1   mycroft 	init.busoff_t = 24;
   1114       1.1   mycroft 	init.xx = 0;
   1115      1.19   thorpej 	ltophys(sc->sc_dmamap_mbox->dm_segs[0].ds_addr, init.mbaddr);
   1116       1.1   mycroft 	init.nomb = init.nimb = WDS_MBX_SIZE;
   1117      1.15   mycroft 	wds_cmd(iot, ioh, (u_char *)&init, sizeof init);
   1118       1.1   mycroft 
   1119      1.15   mycroft 	wds_wait(iot, ioh, WDS_STAT, WDSS_INIT, WDSS_INIT);
   1120       1.1   mycroft 
   1121       1.1   mycroft 	c = WDSC_DISUNSOL;
   1122      1.15   mycroft 	wds_cmd(iot, ioh, &c, sizeof c);
   1123       1.1   mycroft }
   1124       1.1   mycroft 
   1125       1.1   mycroft /*
   1126       1.1   mycroft  * Read the board's firmware revision information.
   1127       1.1   mycroft  */
   1128       1.1   mycroft void
   1129       1.1   mycroft wds_inquire_setup_information(sc)
   1130       1.1   mycroft 	struct wds_softc *sc;
   1131       1.1   mycroft {
   1132      1.15   mycroft 	bus_space_tag_t iot = sc->sc_iot;
   1133      1.15   mycroft 	bus_space_handle_t ioh = sc->sc_ioh;
   1134       1.1   mycroft 	struct wds_scb *scb;
   1135       1.1   mycroft 	u_char *j;
   1136       1.1   mycroft 	int s;
   1137       1.1   mycroft 
   1138      1.21   mycroft 	sc->sc_maxsegs = 1;
   1139      1.19   thorpej 
   1140      1.21   mycroft 	scb = wds_get_scb(sc, SCSI_NOSLEEP);
   1141      1.21   mycroft 	if (scb == 0)
   1142      1.21   mycroft 		panic("wds_inquire_setup_information: no scb available");
   1143      1.19   thorpej 
   1144       1.1   mycroft 	scb->xs = NULL;
   1145       1.1   mycroft 	scb->timeout = 40;
   1146       1.1   mycroft 
   1147       1.1   mycroft 	bzero(&scb->cmd, sizeof scb->cmd);
   1148       1.1   mycroft 	scb->cmd.write = 0x80;
   1149       1.1   mycroft 	scb->cmd.opcode = WDSX_GETFIRMREV;
   1150       1.1   mycroft 
   1151       1.1   mycroft 	/* Will poll card, await result. */
   1152      1.15   mycroft 	bus_space_write_1(iot, ioh, WDS_HCR, WDSH_DRQEN);
   1153       1.1   mycroft 	scb->flags |= SCB_POLLED;
   1154       1.1   mycroft 
   1155       1.1   mycroft 	s = splbio();
   1156       1.1   mycroft 	wds_queue_scb(sc, scb);
   1157       1.1   mycroft 	splx(s);
   1158       1.1   mycroft 
   1159       1.1   mycroft 	if (wds_ipoll(sc, scb, scb->timeout))
   1160       1.1   mycroft 		goto out;
   1161       1.1   mycroft 
   1162       1.1   mycroft 	/* Print the version number. */
   1163      1.21   mycroft 	printf("%s: version %x.%02x ", sc->sc_dev.dv_xname,
   1164      1.21   mycroft 	    scb->cmd.targ, scb->cmd.scb.opcode);
   1165       1.1   mycroft 	sc->sc_revision = (scb->cmd.targ << 8) | scb->cmd.scb.opcode;
   1166       1.1   mycroft 	/* Print out the version string. */
   1167       1.1   mycroft 	j = 2 + &(scb->cmd.targ);
   1168       1.1   mycroft 	while ((*j >= 32) && (*j < 128)) {
   1169      1.12  christos 		printf("%c", *j);
   1170       1.1   mycroft 		j++;
   1171       1.1   mycroft 	}
   1172       1.1   mycroft 
   1173      1.19   thorpej 	/*
   1174      1.19   thorpej 	 * Determine if we can use scatter/gather.
   1175      1.19   thorpej 	 */
   1176      1.19   thorpej 	if (sc->sc_revision >= 0x800)
   1177      1.19   thorpej 		sc->sc_maxsegs = WDS_NSEG;
   1178      1.19   thorpej 
   1179       1.1   mycroft out:
   1180      1.12  christos 	printf("\n");
   1181      1.19   thorpej 
   1182      1.19   thorpej 	/*
   1183      1.19   thorpej 	 * Free up the resources used by this scb.
   1184      1.19   thorpej 	 */
   1185      1.21   mycroft 	wds_free_scb(sc, scb);
   1186       1.1   mycroft }
   1187       1.1   mycroft 
   1188       1.1   mycroft void
   1189       1.1   mycroft wdsminphys(bp)
   1190       1.1   mycroft 	struct buf *bp;
   1191       1.1   mycroft {
   1192       1.1   mycroft 
   1193      1.19   thorpej 	if (bp->b_bcount > WDS_MAXXFER)
   1194      1.19   thorpej 		bp->b_bcount = WDS_MAXXFER;
   1195       1.1   mycroft 	minphys(bp);
   1196       1.1   mycroft }
   1197       1.1   mycroft 
   1198       1.1   mycroft /*
   1199       1.1   mycroft  * Send a SCSI command.
   1200       1.1   mycroft  */
   1201       1.1   mycroft int
   1202       1.1   mycroft wds_scsi_cmd(xs)
   1203      1.18    bouyer 	struct scsipi_xfer *xs;
   1204       1.1   mycroft {
   1205      1.18    bouyer 	struct scsipi_link *sc_link = xs->sc_link;
   1206       1.1   mycroft 	struct wds_softc *sc = sc_link->adapter_softc;
   1207      1.19   thorpej 	bus_dma_tag_t dmat = sc->sc_dmat;
   1208       1.1   mycroft 	struct wds_scb *scb;
   1209       1.1   mycroft 	struct wds_scat_gath *sg;
   1210      1.19   thorpej 	int error, seg, flags, s;
   1211  1.23.2.2   thorpej 	int fromqueue = 0, dontqueue = 0;
   1212       1.5  christos #ifdef TFS
   1213       1.1   mycroft 	struct iovec *iovp;
   1214       1.5  christos #endif
   1215       1.1   mycroft 
   1216       1.1   mycroft 	if (xs->flags & SCSI_RESET) {
   1217       1.1   mycroft 		/* XXX Fix me! */
   1218      1.12  christos 		printf("%s: reset!\n", sc->sc_dev.dv_xname);
   1219      1.19   thorpej 		wds_init(sc, 1);
   1220       1.1   mycroft 		return COMPLETE;
   1221       1.1   mycroft 	}
   1222       1.1   mycroft 
   1223  1.23.2.2   thorpej 	s = splbio();		/* protect the queue */
   1224  1.23.2.2   thorpej 
   1225  1.23.2.2   thorpej 	/*
   1226  1.23.2.2   thorpej 	 * If we're running the queue from wds_done(), we've been
   1227  1.23.2.2   thorpej 	 * called with the first queue entry as our argument.
   1228  1.23.2.2   thorpej 	 */
   1229  1.23.2.2   thorpej 	if (xs == sc->sc_queue.lh_first) {
   1230  1.23.2.2   thorpej 		xs = wds_dequeue(sc);
   1231  1.23.2.2   thorpej 		fromqueue = 1;
   1232  1.23.2.2   thorpej 		goto get_scb;
   1233  1.23.2.2   thorpej 	}
   1234  1.23.2.2   thorpej 
   1235  1.23.2.2   thorpej 	/* Polled requests can't be queued for later. */
   1236  1.23.2.2   thorpej 	dontqueue = xs->flags & SCSI_POLL;
   1237  1.23.2.2   thorpej 
   1238  1.23.2.2   thorpej 	/*
   1239  1.23.2.2   thorpej 	 * If there are jobs in the queue, run them first.
   1240  1.23.2.2   thorpej 	 */
   1241  1.23.2.2   thorpej 	if (sc->sc_queue.lh_first != NULL) {
   1242  1.23.2.2   thorpej 		/*
   1243  1.23.2.2   thorpej 		 * If we can't queue, we have to abort, since
   1244  1.23.2.2   thorpej 		 * we have to preserve order.
   1245  1.23.2.2   thorpej 		 */
   1246  1.23.2.2   thorpej 		if (dontqueue) {
   1247  1.23.2.2   thorpej 			splx(s);
   1248  1.23.2.2   thorpej 			xs->error = XS_DRIVER_STUFFUP;
   1249  1.23.2.2   thorpej 			return (TRY_AGAIN_LATER);
   1250  1.23.2.2   thorpej 		}
   1251  1.23.2.2   thorpej 
   1252  1.23.2.2   thorpej 		/*
   1253  1.23.2.2   thorpej 		 * Swap with the first queue entry.
   1254  1.23.2.2   thorpej 		 */
   1255  1.23.2.2   thorpej 		wds_enqueue(sc, xs, 0);
   1256  1.23.2.2   thorpej 		xs = wds_dequeue(sc);
   1257  1.23.2.2   thorpej 		fromqueue = 1;
   1258  1.23.2.2   thorpej 	}
   1259  1.23.2.2   thorpej 
   1260  1.23.2.2   thorpej  get_scb:
   1261       1.1   mycroft 	flags = xs->flags;
   1262      1.19   thorpej 	if ((scb = wds_get_scb(sc, flags)) == NULL) {
   1263  1.23.2.2   thorpej 		/*
   1264  1.23.2.2   thorpej 		 * If we can't queue, we lose.
   1265  1.23.2.2   thorpej 		 */
   1266  1.23.2.2   thorpej 		if (dontqueue) {
   1267  1.23.2.2   thorpej 			splx(s);
   1268  1.23.2.2   thorpej 			xs->error = XS_DRIVER_STUFFUP;
   1269  1.23.2.2   thorpej 			return (TRY_AGAIN_LATER);
   1270  1.23.2.2   thorpej 		}
   1271  1.23.2.2   thorpej 
   1272  1.23.2.2   thorpej 		/*
   1273  1.23.2.2   thorpej 		 * Stuff ourselves into the queue, in front
   1274  1.23.2.2   thorpej 		 * if we came off in the first place.
   1275  1.23.2.2   thorpej 		 */
   1276  1.23.2.2   thorpej 		wds_enqueue(sc, xs, fromqueue);
   1277  1.23.2.2   thorpej 		splx(s);
   1278  1.23.2.2   thorpej 		return (SUCCESSFULLY_QUEUED);
   1279       1.1   mycroft 	}
   1280  1.23.2.2   thorpej 
   1281  1.23.2.2   thorpej 	splx(s);		/* done playing with the queue */
   1282  1.23.2.2   thorpej 
   1283       1.1   mycroft 	scb->xs = xs;
   1284       1.1   mycroft 	scb->timeout = xs->timeout;
   1285       1.1   mycroft 
   1286       1.1   mycroft 	if (xs->flags & SCSI_DATA_UIO) {
   1287       1.1   mycroft 		/* XXX Fix me! */
   1288       1.1   mycroft 		/* Let's not worry about UIO. There isn't any code for the *
   1289       1.1   mycroft 		 * non-SG boards anyway! */
   1290      1.19   thorpej 		printf("%s: UIO is untested and disabled!\n",
   1291      1.19   thorpej 		    sc->sc_dev.dv_xname);
   1292       1.1   mycroft 		goto bad;
   1293       1.1   mycroft 	}
   1294       1.1   mycroft 
   1295       1.1   mycroft 	/* Zero out the command structure. */
   1296       1.1   mycroft 	bzero(&scb->cmd, sizeof scb->cmd);
   1297       1.1   mycroft 	bcopy(xs->cmd, &scb->cmd.scb, xs->cmdlen < 12 ? xs->cmdlen : 12);
   1298       1.1   mycroft 
   1299       1.1   mycroft 	/* Set up some of the command fields. */
   1300      1.18    bouyer 	scb->cmd.targ = (xs->sc_link->scsipi_scsi.target << 5) |
   1301      1.18    bouyer 						xs->sc_link->scsipi_scsi.lun;
   1302       1.1   mycroft 
   1303       1.1   mycroft 	/* NOTE: cmd.write may be OK as 0x40 (disable direction checking)
   1304       1.1   mycroft 	 * on boards other than the WD-7000V-ASE. Need this for the ASE:
   1305       1.1   mycroft  	 */
   1306       1.1   mycroft 	scb->cmd.write = (xs->flags & SCSI_DATA_IN) ? 0x80 : 0x00;
   1307       1.1   mycroft 
   1308      1.19   thorpej 	if (xs->datalen) {
   1309       1.1   mycroft 		sg = scb->scat_gath;
   1310       1.1   mycroft 		seg = 0;
   1311       1.1   mycroft #ifdef TFS
   1312       1.1   mycroft 		if (flags & SCSI_DATA_UIO) {
   1313      1.19   thorpej 			error = bus_Dmamap_load_uio(dmat,
   1314      1.19   thorpej 			    scb->dmamap_xfer, (struct uio *)xs->data,
   1315      1.19   thorpej 			    (flags & SCSI_NOSLEEP) ? BUS_DMA_NOWAIT :
   1316      1.19   thorpej 			    BUS_DMA_WAITOK);
   1317       1.1   mycroft 		} else
   1318       1.1   mycroft #endif /* TFS */
   1319       1.1   mycroft 		{
   1320      1.19   thorpej 			error = bus_dmamap_load(dmat,
   1321      1.19   thorpej 			    scb->dmamap_xfer, xs->data, xs->datalen, NULL,
   1322      1.19   thorpej 			    (flags & SCSI_NOSLEEP) ? BUS_DMA_NOWAIT :
   1323      1.19   thorpej 			    BUS_DMA_WAITOK);
   1324      1.19   thorpej 		}
   1325       1.1   mycroft 
   1326      1.19   thorpej 		if (error) {
   1327      1.19   thorpej 			if (error == EFBIG) {
   1328      1.19   thorpej 				printf("%s: wds_scsi_cmd, more than %d"
   1329      1.19   thorpej 				    " dma segments\n",
   1330      1.19   thorpej 				    sc->sc_dev.dv_xname, sc->sc_maxsegs);
   1331      1.19   thorpej 			} else {
   1332      1.19   thorpej 				printf("%s: wds_scsi_cmd, error %d loading"
   1333      1.19   thorpej 				    " dma map\n",
   1334      1.19   thorpej 				    sc->sc_dev.dv_xname, error);
   1335      1.19   thorpej 			}
   1336      1.19   thorpej 			goto bad;
   1337      1.19   thorpej 		}
   1338       1.1   mycroft 
   1339      1.19   thorpej 		bus_dmamap_sync(dmat, scb->dmamap_xfer,
   1340      1.19   thorpej 		    (flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD :
   1341      1.19   thorpej 		    BUS_DMASYNC_PREWRITE);
   1342       1.1   mycroft 
   1343      1.19   thorpej 		if (sc->sc_maxsegs > 1) {
   1344      1.19   thorpej 			/*
   1345      1.19   thorpej 			 * Load the hardware scatter/gather map with the
   1346      1.19   thorpej 			 * contents of the DMA map.
   1347      1.19   thorpej 			 */
   1348      1.19   thorpej 			for (seg = 0; seg < scb->dmamap_xfer->dm_nsegs;
   1349      1.19   thorpej 			    seg++) {
   1350      1.19   thorpej 				ltophys(scb->dmamap_xfer->dm_segs[seg].ds_addr,
   1351      1.19   thorpej 				    scb->scat_gath[seg].seg_addr);
   1352      1.19   thorpej 				ltophys(scb->dmamap_xfer->dm_segs[seg].ds_len,
   1353      1.19   thorpej 				    scb->scat_gath[seg].seg_len);
   1354      1.19   thorpej 			}
   1355       1.1   mycroft 
   1356       1.1   mycroft 			/*
   1357      1.19   thorpej 			 * Set up for scatter/gather transfer.
   1358      1.19   thorpej 			 */
   1359      1.19   thorpej 			scb->cmd.opcode = WDSX_SCSISG;
   1360      1.19   thorpej 			ltophys(scb->dmamap_self->dm_segs[0].ds_addr +
   1361      1.19   thorpej 			    offsetof(struct wds_scb, scat_gath),
   1362      1.19   thorpej 			    scb->cmd.data);
   1363      1.19   thorpej 			ltophys(scb->dmamap_self->dm_nsegs *
   1364      1.19   thorpej 			    sizeof(struct wds_scat_gath), scb->cmd.len);
   1365      1.19   thorpej 		} else {
   1366      1.19   thorpej 			/*
   1367      1.19   thorpej 			 * This board is an ASC or an ASE, and the
   1368      1.19   thorpej 			 * transfer has been mapped contig for us.
   1369       1.1   mycroft 			 */
   1370      1.19   thorpej 			scb->cmd.opcode = WDSX_SCSICMD;
   1371      1.19   thorpej 			ltophys(scb->dmamap_xfer->dm_segs[0].ds_addr,
   1372      1.19   thorpej 			    scb->cmd.data);
   1373      1.19   thorpej 			ltophys(scb->dmamap_xfer->dm_segs[0].ds_len,
   1374      1.19   thorpej 			    scb->cmd.len);
   1375       1.1   mycroft 		}
   1376       1.1   mycroft 	} else {
   1377       1.1   mycroft 		scb->cmd.opcode = WDSX_SCSICMD;
   1378       1.1   mycroft 		ltophys(0, scb->cmd.data);
   1379       1.1   mycroft 		ltophys(0, scb->cmd.len);
   1380       1.1   mycroft 	}
   1381       1.1   mycroft 
   1382       1.1   mycroft 	scb->cmd.stat = 0x00;
   1383       1.1   mycroft 	scb->cmd.venderr = 0x00;
   1384       1.1   mycroft 	ltophys(0, scb->cmd.link);
   1385       1.1   mycroft 
   1386       1.1   mycroft 	/* XXX Do we really want to do this? */
   1387       1.1   mycroft 	if (flags & SCSI_POLL) {
   1388       1.1   mycroft 		/* Will poll card, await result. */
   1389      1.15   mycroft 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, WDS_HCR, WDSH_DRQEN);
   1390       1.1   mycroft 		scb->flags |= SCB_POLLED;
   1391       1.1   mycroft 	} else {
   1392       1.1   mycroft 		/* Will send command, let interrupt routine handle result. */
   1393      1.15   mycroft 		bus_space_write_1(sc->sc_iot, sc->sc_ioh, WDS_HCR,
   1394      1.15   mycroft 		    WDSH_IRQEN | WDSH_DRQEN);
   1395       1.1   mycroft 	}
   1396       1.1   mycroft 
   1397       1.1   mycroft 	s = splbio();
   1398       1.1   mycroft 	wds_queue_scb(sc, scb);
   1399       1.1   mycroft 	splx(s);
   1400       1.1   mycroft 
   1401       1.1   mycroft 	if ((flags & SCSI_POLL) == 0)
   1402       1.1   mycroft 		return SUCCESSFULLY_QUEUED;
   1403       1.1   mycroft 
   1404       1.1   mycroft 	if (wds_poll(sc, xs, scb->timeout)) {
   1405       1.1   mycroft 		wds_timeout(scb);
   1406       1.1   mycroft 		if (wds_poll(sc, xs, scb->timeout))
   1407       1.1   mycroft 			wds_timeout(scb);
   1408       1.1   mycroft 	}
   1409       1.1   mycroft 	return COMPLETE;
   1410       1.1   mycroft 
   1411       1.1   mycroft bad:
   1412       1.1   mycroft 	xs->error = XS_DRIVER_STUFFUP;
   1413       1.1   mycroft 	wds_free_scb(sc, scb);
   1414       1.1   mycroft 	return COMPLETE;
   1415       1.1   mycroft }
   1416       1.1   mycroft 
   1417       1.1   mycroft /*
   1418       1.1   mycroft  * Send a sense request.
   1419       1.1   mycroft  */
   1420       1.2   mycroft void
   1421       1.1   mycroft wds_sense(sc, scb)
   1422       1.1   mycroft 	struct wds_softc *sc;
   1423       1.1   mycroft 	struct wds_scb *scb;
   1424       1.1   mycroft {
   1425      1.18    bouyer 	struct scsipi_xfer *xs = scb->xs;
   1426      1.18    bouyer 	struct scsipi_sense *ss = (void *)&scb->sense.scb;
   1427       1.1   mycroft 	int s;
   1428       1.1   mycroft 
   1429       1.1   mycroft 	/* XXXXX */
   1430       1.1   mycroft 
   1431       1.1   mycroft 	/* Send sense request SCSI command. */
   1432       1.1   mycroft 	xs->error = XS_SENSE;
   1433       1.1   mycroft 	scb->flags |= SCB_SENSE;
   1434       1.1   mycroft 
   1435       1.1   mycroft 	/* Next, setup a request sense command block */
   1436       1.1   mycroft 	bzero(ss, sizeof(*ss));
   1437       1.1   mycroft 	ss->opcode = REQUEST_SENSE;
   1438      1.18    bouyer 	ss->byte2 = xs->sc_link->scsipi_scsi.lun << 5;
   1439      1.18    bouyer 	ss->length = sizeof(struct scsipi_sense_data);
   1440       1.1   mycroft 
   1441       1.1   mycroft 	/* Set up some of the command fields. */
   1442       1.1   mycroft 	scb->sense.targ = scb->cmd.targ;
   1443       1.1   mycroft 	scb->sense.write = 0x80;
   1444       1.1   mycroft 	scb->sense.opcode = WDSX_SCSICMD;
   1445      1.19   thorpej 	ltophys(scb->dmamap_self->dm_segs[0].ds_addr +
   1446      1.19   thorpej 	    offsetof(struct wds_scb, sense_data), scb->sense.data);
   1447      1.18    bouyer 	ltophys(sizeof(struct scsipi_sense_data), scb->sense.len);
   1448       1.1   mycroft 
   1449       1.1   mycroft 	s = splbio();
   1450       1.1   mycroft 	wds_queue_scb(sc, scb);
   1451       1.1   mycroft 	splx(s);
   1452       1.1   mycroft 
   1453       1.1   mycroft 	/*
   1454       1.1   mycroft 	 * There's no reason for us to poll here.  There are two cases:
   1455       1.1   mycroft 	 * 1) If it's a polling operation, then we're called from the interrupt
   1456       1.1   mycroft 	 *    handler, and we return and continue polling.
   1457       1.1   mycroft 	 * 2) If it's an interrupt-driven operation, then it gets completed
   1458       1.1   mycroft 	 *    later on when the REQUEST SENSE finishes.
   1459       1.1   mycroft 	 */
   1460       1.1   mycroft }
   1461       1.1   mycroft 
   1462       1.1   mycroft /*
   1463       1.1   mycroft  * Poll a particular unit, looking for a particular scb
   1464       1.1   mycroft  */
   1465       1.1   mycroft int
   1466       1.1   mycroft wds_poll(sc, xs, count)
   1467       1.1   mycroft 	struct wds_softc *sc;
   1468      1.18    bouyer 	struct scsipi_xfer *xs;
   1469       1.1   mycroft 	int count;
   1470       1.1   mycroft {
   1471      1.15   mycroft 	bus_space_tag_t iot = sc->sc_iot;
   1472      1.15   mycroft 	bus_space_handle_t ioh = sc->sc_ioh;
   1473       1.1   mycroft 
   1474       1.1   mycroft 	/* timeouts are in msec, so we loop in 1000 usec cycles */
   1475       1.1   mycroft 	while (count) {
   1476       1.1   mycroft 		/*
   1477       1.1   mycroft 		 * If we had interrupts enabled, would we
   1478       1.1   mycroft 		 * have got an interrupt?
   1479       1.1   mycroft 		 */
   1480      1.15   mycroft 		if (bus_space_read_1(iot, ioh, WDS_STAT) & WDSS_IRQ)
   1481       1.1   mycroft 			wdsintr(sc);
   1482       1.1   mycroft 		if (xs->flags & ITSDONE)
   1483       1.1   mycroft 			return 0;
   1484       1.1   mycroft 		delay(1000);	/* only happens in boot so ok */
   1485       1.1   mycroft 		count--;
   1486       1.1   mycroft 	}
   1487       1.1   mycroft 	return 1;
   1488       1.1   mycroft }
   1489       1.1   mycroft 
   1490       1.1   mycroft /*
   1491       1.1   mycroft  * Poll a particular unit, looking for a particular scb
   1492       1.1   mycroft  */
   1493       1.1   mycroft int
   1494       1.1   mycroft wds_ipoll(sc, scb, count)
   1495       1.1   mycroft 	struct wds_softc *sc;
   1496       1.1   mycroft 	struct wds_scb *scb;
   1497       1.1   mycroft 	int count;
   1498       1.1   mycroft {
   1499      1.15   mycroft 	bus_space_tag_t iot = sc->sc_iot;
   1500      1.15   mycroft 	bus_space_handle_t ioh = sc->sc_ioh;
   1501       1.1   mycroft 
   1502       1.1   mycroft 	/* timeouts are in msec, so we loop in 1000 usec cycles */
   1503       1.1   mycroft 	while (count) {
   1504       1.1   mycroft 		/*
   1505       1.1   mycroft 		 * If we had interrupts enabled, would we
   1506       1.1   mycroft 		 * have got an interrupt?
   1507       1.1   mycroft 		 */
   1508      1.15   mycroft 		if (bus_space_read_1(iot, ioh, WDS_STAT) & WDSS_IRQ)
   1509       1.1   mycroft 			wdsintr(sc);
   1510       1.1   mycroft 		if (scb->flags & SCB_DONE)
   1511       1.1   mycroft 			return 0;
   1512       1.1   mycroft 		delay(1000);	/* only happens in boot so ok */
   1513       1.1   mycroft 		count--;
   1514       1.1   mycroft 	}
   1515       1.1   mycroft 	return 1;
   1516       1.1   mycroft }
   1517       1.1   mycroft 
   1518       1.1   mycroft void
   1519       1.1   mycroft wds_timeout(arg)
   1520       1.1   mycroft 	void *arg;
   1521       1.1   mycroft {
   1522       1.1   mycroft 	struct wds_scb *scb = arg;
   1523      1.18    bouyer 	struct scsipi_xfer *xs = scb->xs;
   1524      1.18    bouyer 	struct scsipi_link *sc_link = xs->sc_link;
   1525       1.1   mycroft 	struct wds_softc *sc = sc_link->adapter_softc;
   1526       1.1   mycroft 	int s;
   1527       1.1   mycroft 
   1528      1.18    bouyer 	scsi_print_addr(sc_link);
   1529      1.12  christos 	printf("timed out");
   1530       1.1   mycroft 
   1531       1.1   mycroft 	s = splbio();
   1532       1.1   mycroft 
   1533       1.1   mycroft #ifdef WDSDIAG
   1534       1.1   mycroft 	/*
   1535       1.1   mycroft 	 * If The scb's mbx is not free, then the board has gone south?
   1536       1.1   mycroft 	 */
   1537       1.1   mycroft 	wds_collect_mbo(sc);
   1538       1.1   mycroft 	if (scb->flags & SCB_SENDING) {
   1539      1.12  christos 		printf("%s: not taking commands!\n", sc->sc_dev.dv_xname);
   1540       1.1   mycroft 		Debugger();
   1541       1.1   mycroft 	}
   1542       1.1   mycroft #endif
   1543       1.1   mycroft 
   1544       1.1   mycroft 	/*
   1545       1.1   mycroft 	 * If it has been through before, then
   1546       1.1   mycroft 	 * a previous abort has failed, don't
   1547       1.1   mycroft 	 * try abort again
   1548       1.1   mycroft 	 */
   1549       1.1   mycroft 	if (scb->flags & SCB_ABORT) {
   1550       1.1   mycroft 		/* abort timed out */
   1551      1.12  christos 		printf(" AGAIN\n");
   1552       1.1   mycroft 		/* XXX Must reset! */
   1553       1.1   mycroft 	} else {
   1554       1.1   mycroft 		/* abort the operation that has timed out */
   1555      1.12  christos 		printf("\n");
   1556       1.1   mycroft 		scb->xs->error = XS_TIMEOUT;
   1557       1.1   mycroft 		scb->timeout = WDS_ABORT_TIMEOUT;
   1558       1.1   mycroft 		scb->flags |= SCB_ABORT;
   1559       1.1   mycroft 		wds_queue_scb(sc, scb);
   1560       1.1   mycroft 	}
   1561       1.1   mycroft 
   1562       1.1   mycroft 	splx(s);
   1563       1.1   mycroft }
   1564