xref: /freebsd/sys/dev/mlx/mlx.c (revision 1ac4b82b11c460d0ef82bf760b71a8b4d34be28a)
11ac4b82bSMike Smith /*-
21ac4b82bSMike Smith  * Copyright (c) 1999 Michael Smith
31ac4b82bSMike Smith  * All rights reserved.
41ac4b82bSMike Smith  *
51ac4b82bSMike Smith  * Redistribution and use in source and binary forms, with or without
61ac4b82bSMike Smith  * modification, are permitted provided that the following conditions
71ac4b82bSMike Smith  * are met:
81ac4b82bSMike Smith  * 1. Redistributions of source code must retain the above copyright
91ac4b82bSMike Smith  *    notice, this list of conditions and the following disclaimer.
101ac4b82bSMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
111ac4b82bSMike Smith  *    notice, this list of conditions and the following disclaimer in the
121ac4b82bSMike Smith  *    documentation and/or other materials provided with the distribution.
131ac4b82bSMike Smith  *
141ac4b82bSMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
151ac4b82bSMike Smith  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161ac4b82bSMike Smith  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171ac4b82bSMike Smith  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181ac4b82bSMike Smith  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191ac4b82bSMike Smith  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201ac4b82bSMike Smith  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211ac4b82bSMike Smith  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221ac4b82bSMike Smith  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231ac4b82bSMike Smith  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241ac4b82bSMike Smith  * SUCH DAMAGE.
251ac4b82bSMike Smith  *
261ac4b82bSMike Smith  *	$FreeBSD$
271ac4b82bSMike Smith  */
281ac4b82bSMike Smith 
291ac4b82bSMike Smith /*
301ac4b82bSMike Smith  * Driver for the Mylex DAC960 family of RAID controllers.
311ac4b82bSMike Smith  */
321ac4b82bSMike Smith 
331ac4b82bSMike Smith #include <sys/param.h>
341ac4b82bSMike Smith #include <sys/systm.h>
351ac4b82bSMike Smith #include <sys/malloc.h>
361ac4b82bSMike Smith #include <sys/kernel.h>
371ac4b82bSMike Smith 
381ac4b82bSMike Smith #include <sys/buf.h>
391ac4b82bSMike Smith #include <sys/bus.h>
401ac4b82bSMike Smith #include <sys/conf.h>
411ac4b82bSMike Smith #include <sys/devicestat.h>
421ac4b82bSMike Smith #include <sys/disk.h>
431ac4b82bSMike Smith 
441ac4b82bSMike Smith #include <machine/resource.h>
451ac4b82bSMike Smith #include <machine/bus.h>
461ac4b82bSMike Smith #include <machine/clock.h>
471ac4b82bSMike Smith #include <sys/rman.h>
481ac4b82bSMike Smith 
491ac4b82bSMike Smith #include <dev/mlx/mlxio.h>
501ac4b82bSMike Smith #include <dev/mlx/mlxvar.h>
511ac4b82bSMike Smith #include <dev/mlx/mlxreg.h>
521ac4b82bSMike Smith 
531ac4b82bSMike Smith #if 0
541ac4b82bSMike Smith #define debug(fmt, args...)	printf("%s: " fmt "\n", __FUNCTION__ , ##args)
551ac4b82bSMike Smith #else
561ac4b82bSMike Smith #define debug(fmt, args...)
571ac4b82bSMike Smith #endif
581ac4b82bSMike Smith 
591ac4b82bSMike Smith #define MLX_CDEV_MAJOR	130
601ac4b82bSMike Smith 
611ac4b82bSMike Smith static struct cdevsw mlx_cdevsw = {
621ac4b82bSMike Smith 		/* open */	mlx_open,
631ac4b82bSMike Smith 		/* close */	mlx_close,
641ac4b82bSMike Smith 		/* read */	noread,
651ac4b82bSMike Smith 		/* write */	nowrite,
661ac4b82bSMike Smith 		/* ioctl */	mlx_ioctl,
671ac4b82bSMike Smith 		/* poll */	nopoll,
681ac4b82bSMike Smith 		/* mmap */	nommap,
691ac4b82bSMike Smith 		/* strategy */	nostrategy,
701ac4b82bSMike Smith 		/* name */ 	"mlx",
711ac4b82bSMike Smith 		/* maj */	MLX_CDEV_MAJOR,
721ac4b82bSMike Smith 		/* dump */	nodump,
731ac4b82bSMike Smith 		/* psize */ 	nopsize,
741ac4b82bSMike Smith 		/* flags */	0,
751ac4b82bSMike Smith 		/* bmaj */	254	/* XXX magic no-bdev */
761ac4b82bSMike Smith };
771ac4b82bSMike Smith 
781ac4b82bSMike Smith static int	cdev_registered = 0;
791ac4b82bSMike Smith devclass_t	mlx_devclass;
801ac4b82bSMike Smith 
811ac4b82bSMike Smith /*
821ac4b82bSMike Smith  * Per-interface accessor methods
831ac4b82bSMike Smith  */
841ac4b82bSMike Smith static int			mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
851ac4b82bSMike Smith static int			mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
861ac4b82bSMike Smith static void			mlx_v3_intaction(struct mlx_softc *sc, int action);
871ac4b82bSMike Smith 
881ac4b82bSMike Smith /*
891ac4b82bSMike Smith  * Status monitoring
901ac4b82bSMike Smith  */
911ac4b82bSMike Smith static void			mlx_periodic(void *data);
921ac4b82bSMike Smith static void			mlx_periodic_enquiry(struct mlx_command *mc);
931ac4b82bSMike Smith static void			mlx_periodic_eventlog_poll(struct mlx_softc *sc);
941ac4b82bSMike Smith static void			mlx_periodic_eventlog_respond(struct mlx_command *mc);
951ac4b82bSMike Smith static void			mlx_periodic_rebuild(struct mlx_command *mc);
961ac4b82bSMike Smith 
971ac4b82bSMike Smith /*
981ac4b82bSMike Smith  * Channel Pause
991ac4b82bSMike Smith  */
1001ac4b82bSMike Smith static void			mlx_pause_action(struct mlx_softc *sc);
1011ac4b82bSMike Smith static void			mlx_pause_done(struct mlx_command *mc);
1021ac4b82bSMike Smith 
1031ac4b82bSMike Smith /*
1041ac4b82bSMike Smith  * Command submission.
1051ac4b82bSMike Smith  */
1061ac4b82bSMike Smith static void			*mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize,
1071ac4b82bSMike Smith 					     void (*complete)(struct mlx_command *mc));
1081ac4b82bSMike Smith static int			mlx_flush(struct mlx_softc *sc);
1091ac4b82bSMike Smith static int			mlx_rebuild(struct mlx_softc *sc, int channel, int target);
1101ac4b82bSMike Smith static int			mlx_wait_command(struct mlx_command *mc);
1111ac4b82bSMike Smith static int			mlx_poll_command(struct mlx_command *mc);
1121ac4b82bSMike Smith static void			mlx_startio(struct mlx_softc *sc);
1131ac4b82bSMike Smith static void			mlx_completeio(struct mlx_command *mc);
1141ac4b82bSMike Smith static int			mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu);
1151ac4b82bSMike Smith 
1161ac4b82bSMike Smith /*
1171ac4b82bSMike Smith  * Command buffer allocation.
1181ac4b82bSMike Smith  */
1191ac4b82bSMike Smith static struct mlx_command	*mlx_alloccmd(struct mlx_softc *sc);
1201ac4b82bSMike Smith static void			mlx_releasecmd(struct mlx_command *mc);
1211ac4b82bSMike Smith static void			mlx_freecmd(struct mlx_command *mc);
1221ac4b82bSMike Smith 
1231ac4b82bSMike Smith /*
1241ac4b82bSMike Smith  * Command management.
1251ac4b82bSMike Smith  */
1261ac4b82bSMike Smith static int			mlx_getslot(struct mlx_command *mc);
1271ac4b82bSMike Smith static void			mlx_mapcmd(struct mlx_command *mc);
1281ac4b82bSMike Smith static void			mlx_unmapcmd(struct mlx_command *mc);
1291ac4b82bSMike Smith static int			mlx_start(struct mlx_command *mc);
1301ac4b82bSMike Smith static int			mlx_done(struct mlx_softc *sc);
1311ac4b82bSMike Smith static void			mlx_complete(struct mlx_softc *sc);
1321ac4b82bSMike Smith 
1331ac4b82bSMike Smith /*
1341ac4b82bSMike Smith  * Debugging.
1351ac4b82bSMike Smith  */
1361ac4b82bSMike Smith static char			*mlx_diagnose_command(struct mlx_command *mc);
1371ac4b82bSMike Smith static char			*mlx_name_controller(u_int32_t hwid);
1381ac4b82bSMike Smith 
1391ac4b82bSMike Smith 
1401ac4b82bSMike Smith /*
1411ac4b82bSMike Smith  * Utility functions.
1421ac4b82bSMike Smith  */
1431ac4b82bSMike Smith static struct mlx_sysdrive	*mlx_findunit(struct mlx_softc *sc, int unit);
1441ac4b82bSMike Smith 
1451ac4b82bSMike Smith /********************************************************************************
1461ac4b82bSMike Smith  ********************************************************************************
1471ac4b82bSMike Smith                                                                 Public Interfaces
1481ac4b82bSMike Smith  ********************************************************************************
1491ac4b82bSMike Smith  ********************************************************************************/
1501ac4b82bSMike Smith 
1511ac4b82bSMike Smith /********************************************************************************
1521ac4b82bSMike Smith  * Free all of the resources associated with (sc)
1531ac4b82bSMike Smith  *
1541ac4b82bSMike Smith  * Should not be called if the controller is active.
1551ac4b82bSMike Smith  */
1561ac4b82bSMike Smith void
1571ac4b82bSMike Smith mlx_free(struct mlx_softc *sc)
1581ac4b82bSMike Smith {
1591ac4b82bSMike Smith     struct mlx_command	*mc;
1601ac4b82bSMike Smith 
1611ac4b82bSMike Smith     debug("called");
1621ac4b82bSMike Smith 
1631ac4b82bSMike Smith     /* cancel status timeout */
1641ac4b82bSMike Smith     untimeout(mlx_periodic, sc, sc->mlx_timeout);
1651ac4b82bSMike Smith 
1661ac4b82bSMike Smith     /* throw away any command buffers */
1671ac4b82bSMike Smith     while ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) {
1681ac4b82bSMike Smith 	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
1691ac4b82bSMike Smith 	mlx_freecmd(mc);
1701ac4b82bSMike Smith     }
1711ac4b82bSMike Smith 
1721ac4b82bSMike Smith     /* destroy data-transfer DMA tag */
1731ac4b82bSMike Smith     if (sc->mlx_buffer_dmat)
1741ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_buffer_dmat);
1751ac4b82bSMike Smith 
1761ac4b82bSMike Smith     /* free and destroy DMA memory and tag for s/g lists */
1771ac4b82bSMike Smith     if (sc->mlx_sgtable)
1781ac4b82bSMike Smith 	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
1791ac4b82bSMike Smith     if (sc->mlx_sg_dmat)
1801ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_sg_dmat);
1811ac4b82bSMike Smith 
1821ac4b82bSMike Smith     /* disconnect the interrupt handler */
1831ac4b82bSMike Smith     if (sc->mlx_intr)
1841ac4b82bSMike Smith 	bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr);
1851ac4b82bSMike Smith     if (sc->mlx_irq != NULL)
1861ac4b82bSMike Smith 	bus_release_resource(sc->mlx_dev, SYS_RES_IRQ, 0, sc->mlx_irq);
1871ac4b82bSMike Smith 
1881ac4b82bSMike Smith     /* destroy the parent DMA tag */
1891ac4b82bSMike Smith     if (sc->mlx_parent_dmat)
1901ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_parent_dmat);
1911ac4b82bSMike Smith 
1921ac4b82bSMike Smith     /* release the register window mapping */
1931ac4b82bSMike Smith     if (sc->mlx_mem != NULL)
1941ac4b82bSMike Smith 	bus_release_resource(sc->mlx_dev, SYS_RES_MEMORY,
1951ac4b82bSMike Smith 			     (sc->mlx_iftype == MLX_IFTYPE_3) ? MLX_CFG_BASE1 : MLX_CFG_BASE0, sc->mlx_mem);
1961ac4b82bSMike Smith }
1971ac4b82bSMike Smith 
1981ac4b82bSMike Smith /********************************************************************************
1991ac4b82bSMike Smith  * Map the scatter/gather table into bus space
2001ac4b82bSMike Smith  */
2011ac4b82bSMike Smith static void
2021ac4b82bSMike Smith mlx_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
2031ac4b82bSMike Smith {
2041ac4b82bSMike Smith     struct mlx_softc	*sc = (struct mlx_softc *)arg;
2051ac4b82bSMike Smith 
2061ac4b82bSMike Smith     debug("called");
2071ac4b82bSMike Smith 
2081ac4b82bSMike Smith     /* save base of s/g table's address in bus space */
2091ac4b82bSMike Smith     sc->mlx_sgbusaddr = segs->ds_addr;
2101ac4b82bSMike Smith }
2111ac4b82bSMike Smith 
2121ac4b82bSMike Smith static int
2131ac4b82bSMike Smith mlx_sglist_map(struct mlx_softc *sc)
2141ac4b82bSMike Smith {
2151ac4b82bSMike Smith     size_t	segsize;
2161ac4b82bSMike Smith     int		error;
2171ac4b82bSMike Smith 
2181ac4b82bSMike Smith     debug("called");
2191ac4b82bSMike Smith 
2201ac4b82bSMike Smith     /* destroy any existing mappings */
2211ac4b82bSMike Smith     if (sc->mlx_sgtable)
2221ac4b82bSMike Smith 	bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
2231ac4b82bSMike Smith     if (sc->mlx_sg_dmat)
2241ac4b82bSMike Smith 	bus_dma_tag_destroy(sc->mlx_sg_dmat);
2251ac4b82bSMike Smith 
2261ac4b82bSMike Smith     /*
2271ac4b82bSMike Smith      * Create a single tag describing a region large enough to hold all of
2281ac4b82bSMike Smith      * the s/g lists we will need.
2291ac4b82bSMike Smith      */
2301ac4b82bSMike Smith     segsize = sizeof(struct mlx_sgentry) * MLX_NSEG * sc->mlx_maxiop;
2311ac4b82bSMike Smith     error = bus_dma_tag_create(sc->mlx_parent_dmat, 	/* parent */
2321ac4b82bSMike Smith 			       1, 0, 			/* alignment, boundary */
2331ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR,	/* lowaddr */
2341ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR, 	/* highaddr */
2351ac4b82bSMike Smith 			       NULL, NULL, 		/* filter, filterarg */
2361ac4b82bSMike Smith 			       segsize, 1,		/* maxsize, nsegments */
2371ac4b82bSMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
2381ac4b82bSMike Smith 			       0,			/* flags */
2391ac4b82bSMike Smith 			       &sc->mlx_sg_dmat);
2401ac4b82bSMike Smith     if (error != 0) {
2411ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate scatter/gather DMA tag\n");
2421ac4b82bSMike Smith 	return(ENOMEM);
2431ac4b82bSMike Smith     }
2441ac4b82bSMike Smith 
2451ac4b82bSMike Smith     /*
2461ac4b82bSMike Smith      * Allocate enough s/g maps for all commands and permanently map them into
2471ac4b82bSMike Smith      * controller-visible space.
2481ac4b82bSMike Smith      *
2491ac4b82bSMike Smith      * XXX this assumes we can get enough space for all the s/g maps in one
2501ac4b82bSMike Smith      * contiguous slab.  We may need to switch to a more complex arrangement where
2511ac4b82bSMike Smith      * we allocate in smaller chunks and keep a lookup table from slot to bus address.
2521ac4b82bSMike Smith      */
2531ac4b82bSMike Smith     error = bus_dmamem_alloc(sc->mlx_sg_dmat, (void **)&sc->mlx_sgtable, BUS_DMA_NOWAIT, &sc->mlx_sg_dmamap);
2541ac4b82bSMike Smith     if (error) {
2551ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate s/g table\n");
2561ac4b82bSMike Smith 	return(ENOMEM);
2571ac4b82bSMike Smith     }
2581ac4b82bSMike Smith     bus_dmamap_load(sc->mlx_sg_dmat, sc->mlx_sg_dmamap, sc->mlx_sgtable, segsize, mlx_dma_map_sg, sc, 0);
2591ac4b82bSMike Smith     return(0);
2601ac4b82bSMike Smith }
2611ac4b82bSMike Smith 
2621ac4b82bSMike Smith /********************************************************************************
2631ac4b82bSMike Smith  * Initialise the controller and softc
2641ac4b82bSMike Smith  */
2651ac4b82bSMike Smith int
2661ac4b82bSMike Smith mlx_attach(struct mlx_softc *sc)
2671ac4b82bSMike Smith {
2681ac4b82bSMike Smith     struct mlx_enquiry	*me;
2691ac4b82bSMike Smith     struct mlx_enquiry2	*me2;
2701ac4b82bSMike Smith     int			rid, error;
2711ac4b82bSMike Smith 
2721ac4b82bSMike Smith     debug("called");
2731ac4b82bSMike Smith 
2741ac4b82bSMike Smith     /*
2751ac4b82bSMike Smith      * Initialise per-controller queues.
2761ac4b82bSMike Smith      */
2771ac4b82bSMike Smith     TAILQ_INIT(&sc->mlx_donecmd);
2781ac4b82bSMike Smith     TAILQ_INIT(&sc->mlx_freecmds);
2791ac4b82bSMike Smith     bufq_init(&sc->mlx_bufq);
2801ac4b82bSMike Smith 
2811ac4b82bSMike Smith     /*
2821ac4b82bSMike Smith      * Select accessor methods based on controller interface type.
2831ac4b82bSMike Smith      */
2841ac4b82bSMike Smith     switch(sc->mlx_iftype) {
2851ac4b82bSMike Smith     case MLX_IFTYPE_3:
2861ac4b82bSMike Smith 	sc->mlx_tryqueue	= mlx_v3_tryqueue;
2871ac4b82bSMike Smith 	sc->mlx_findcomplete	= mlx_v3_findcomplete;
2881ac4b82bSMike Smith 	sc->mlx_intaction	= mlx_v3_intaction;
2891ac4b82bSMike Smith 	break;
2901ac4b82bSMike Smith     default:
2911ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "attaching unsupported interface version %d\n", sc->mlx_iftype);
2921ac4b82bSMike Smith 	return(ENXIO);		/* should never happen */
2931ac4b82bSMike Smith     }
2941ac4b82bSMike Smith 
2951ac4b82bSMike Smith     /* disable interrupts before we start talking to the controller */
2961ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
2971ac4b82bSMike Smith 
2981ac4b82bSMike Smith     /*
2991ac4b82bSMike Smith      * Allocate and connect our interrupt.
3001ac4b82bSMike Smith      */
3011ac4b82bSMike Smith     rid = 0;
3021ac4b82bSMike Smith     sc->mlx_irq = bus_alloc_resource(sc->mlx_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
3031ac4b82bSMike Smith     if (sc->mlx_irq == NULL) {
3041ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "couldn't allocate interrupt\n");
3051ac4b82bSMike Smith 	mlx_free(sc);
3061ac4b82bSMike Smith 	return(ENXIO);
3071ac4b82bSMike Smith     }
3081ac4b82bSMike Smith     error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO,  mlx_intr, sc, &sc->mlx_intr);
3091ac4b82bSMike Smith     if (error) {
3101ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "couldn't set up interrupt\n");
3111ac4b82bSMike Smith 	mlx_free(sc);
3121ac4b82bSMike Smith 	return(ENXIO);
3131ac4b82bSMike Smith     }
3141ac4b82bSMike Smith 
3151ac4b82bSMike Smith     /*
3161ac4b82bSMike Smith      * Create DMA tag for mapping buffers into controller-addressable space.
3171ac4b82bSMike Smith      */
3181ac4b82bSMike Smith     error = bus_dma_tag_create(sc->mlx_parent_dmat, 	/* parent */
3191ac4b82bSMike Smith 			       1, 0, 			/* alignment, boundary */
3201ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR,	/* lowaddr */
3211ac4b82bSMike Smith 			       BUS_SPACE_MAXADDR, 	/* highaddr */
3221ac4b82bSMike Smith 			       NULL, NULL, 		/* filter, filterarg */
3231ac4b82bSMike Smith 			       MAXBSIZE, MLX_NSEG,	/* maxsize, nsegments */
3241ac4b82bSMike Smith 			       BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
3251ac4b82bSMike Smith 			       0,			/* flags */
3261ac4b82bSMike Smith 			       &sc->mlx_buffer_dmat);
3271ac4b82bSMike Smith     if (error != 0) {
3281ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n");
3291ac4b82bSMike Smith 	return(ENOMEM);
3301ac4b82bSMike Smith     }
3311ac4b82bSMike Smith 
3321ac4b82bSMike Smith     /*
3331ac4b82bSMike Smith      * Create an initial set of s/g mappings.
3341ac4b82bSMike Smith      */
3351ac4b82bSMike Smith     sc->mlx_maxiop = 2;
3361ac4b82bSMike Smith     error = mlx_sglist_map(sc);
3371ac4b82bSMike Smith     if (error != 0) {
3381ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "couldn't make initial s/g list mapping\n");
3391ac4b82bSMike Smith 	return(error);
3401ac4b82bSMike Smith     }
3411ac4b82bSMike Smith 
3421ac4b82bSMike Smith     /*
3431ac4b82bSMike Smith      * Probe the controller for more information.
3441ac4b82bSMike Smith      */
3451ac4b82bSMike Smith     /* send an ENQUIRY to the controller */
3461ac4b82bSMike Smith     if ((me = mlx_enquire(sc, MLX_CMD_ENQUIRY, sizeof(*me), NULL)) == NULL) {
3471ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "ENQUIRY failed\n");
3481ac4b82bSMike Smith 	return(ENXIO);
3491ac4b82bSMike Smith     }
3501ac4b82bSMike Smith 
3511ac4b82bSMike Smith     /* pull information out of the ENQUIRY result */
3521ac4b82bSMike Smith     sc->mlx_fwminor = me->me_fwminor;
3531ac4b82bSMike Smith     sc->mlx_fwmajor = me->me_fwmajor;
3541ac4b82bSMike Smith     sc->mlx_maxiop = me->me_max_commands;
3551ac4b82bSMike Smith     sc->mlx_lastevent = sc->mlx_currevent = me->me_event_log_seq_num;
3561ac4b82bSMike Smith     free(me, M_DEVBUF);
3571ac4b82bSMike Smith 
3581ac4b82bSMike Smith     /* send an ENQUIRY2 to the controller */
3591ac4b82bSMike Smith     if ((me2 = mlx_enquire(sc, MLX_CMD_ENQUIRY2, sizeof(*me2), NULL)) == NULL) {
3601ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "ENQUIRY2 failed\n");
3611ac4b82bSMike Smith 	return(ENXIO);
3621ac4b82bSMike Smith     }
3631ac4b82bSMike Smith 
3641ac4b82bSMike Smith     /* pull information out of the ENQUIRY2 result */
3651ac4b82bSMike Smith     sc->mlx_nchan = me2->me_configured_channels;
3661ac4b82bSMike Smith     sc->mlx_maxiosize = me2->me_maxblk;
3671ac4b82bSMike Smith     sc->mlx_maxtarg = me2->me_max_targets;
3681ac4b82bSMike Smith     sc->mlx_maxtags = me2->me_max_tags;
3691ac4b82bSMike Smith     sc->mlx_scsicap = me2->me_scsi_cap;
3701ac4b82bSMike Smith     sc->mlx_hwid = me2->me_hardware_id;
3711ac4b82bSMike Smith 
3721ac4b82bSMike Smith     /* print a little information about the controller and ourselves */
3731ac4b82bSMike Smith     device_printf(sc->mlx_dev, "Mylex %s, firmware %d.%d, %dMB RAM\n",
3741ac4b82bSMike Smith 	   mlx_name_controller(sc->mlx_hwid), sc->mlx_fwmajor, sc->mlx_fwminor,
3751ac4b82bSMike Smith 	me2->me_mem_size / (1024 * 1024));
3761ac4b82bSMike Smith     free(me2, M_DEVBUF);
3771ac4b82bSMike Smith 
3781ac4b82bSMike Smith     /*
3791ac4b82bSMike Smith      * Do quirk/feature related things.
3801ac4b82bSMike Smith      */
3811ac4b82bSMike Smith     switch(sc->mlx_iftype) {
3821ac4b82bSMike Smith     case MLX_IFTYPE_3:
3831ac4b82bSMike Smith 	if (sc->mlx_fwminor != 51) {
3841ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is NOT SUPPORTED\n");
3851ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, " *** WARNING *** Use revision 3.51 only\n");
3861ac4b82bSMike Smith 	}
3871ac4b82bSMike Smith 	break;
3881ac4b82bSMike Smith     default:
3891ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "interface version corrupted to %d\n", sc->mlx_iftype);
3901ac4b82bSMike Smith 	return(ENXIO);		/* should never happen */
3911ac4b82bSMike Smith     }
3921ac4b82bSMike Smith 
3931ac4b82bSMike Smith     /*
3941ac4b82bSMike Smith      * Create the final set of s/g mappings now that we know how many commands
3951ac4b82bSMike Smith      * the controller actually supports.
3961ac4b82bSMike Smith      */
3971ac4b82bSMike Smith     error = mlx_sglist_map(sc);
3981ac4b82bSMike Smith     if (error != 0) {
3991ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "couldn't make initial s/g list mapping\n");
4001ac4b82bSMike Smith 	return(error);
4011ac4b82bSMike Smith     }
4021ac4b82bSMike Smith 
4031ac4b82bSMike Smith     /*
4041ac4b82bSMike Smith      * No rebuild or check is in progress.
4051ac4b82bSMike Smith      */
4061ac4b82bSMike Smith     sc->mlx_rebuild = -1;
4071ac4b82bSMike Smith     sc->mlx_check = -1;
4081ac4b82bSMike Smith 
4091ac4b82bSMike Smith     /*
4101ac4b82bSMike Smith      * Register the control device on first attach.
4111ac4b82bSMike Smith      */
4121ac4b82bSMike Smith     if (cdev_registered++ == 0)
4131ac4b82bSMike Smith 	cdevsw_add(&mlx_cdevsw);
4141ac4b82bSMike Smith 
4151ac4b82bSMike Smith     /*
4161ac4b82bSMike Smith      * Start the timeout routine.
4171ac4b82bSMike Smith      */
4181ac4b82bSMike Smith     sc->mlx_timeout = timeout(mlx_periodic, sc, hz);
4191ac4b82bSMike Smith 
4201ac4b82bSMike Smith     return(0);
4211ac4b82bSMike Smith }
4221ac4b82bSMike Smith 
4231ac4b82bSMike Smith /********************************************************************************
4241ac4b82bSMike Smith  * Locate disk resources and attach children to them.
4251ac4b82bSMike Smith  */
4261ac4b82bSMike Smith void
4271ac4b82bSMike Smith mlx_startup(struct mlx_softc *sc)
4281ac4b82bSMike Smith {
4291ac4b82bSMike Smith     struct mlx_enq_sys_drive	*mes;
4301ac4b82bSMike Smith     struct mlx_sysdrive		*dr;
4311ac4b82bSMike Smith     int				i, error;
4321ac4b82bSMike Smith 
4331ac4b82bSMike Smith     debug("called");
4341ac4b82bSMike Smith 
4351ac4b82bSMike Smith     /*
4361ac4b82bSMike Smith      * Scan all the system drives and attach children for those that
4371ac4b82bSMike Smith      * don't currently have them.
4381ac4b82bSMike Smith      */
4391ac4b82bSMike Smith     mes = mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(*mes) * MLX_MAXDRIVES, NULL);
4401ac4b82bSMike Smith     if (mes == NULL) {
4411ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "error fetching drive status");
4421ac4b82bSMike Smith 	return;
4431ac4b82bSMike Smith     }
4441ac4b82bSMike Smith 
4451ac4b82bSMike Smith     /* iterate over drives returned */
4461ac4b82bSMike Smith     for (i = 0, dr = &sc->mlx_sysdrive[0];
4471ac4b82bSMike Smith 	 (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
4481ac4b82bSMike Smith 	 i++, dr++) {
4491ac4b82bSMike Smith 	/* are we already attached to this drive? */
4501ac4b82bSMike Smith     	if (dr->ms_disk == 0) {
4511ac4b82bSMike Smith 	    /* pick up drive information */
4521ac4b82bSMike Smith 	    dr->ms_size = mes[i].sd_size;
4531ac4b82bSMike Smith 	    dr->ms_raidlevel = mes[i].sd_raidlevel;
4541ac4b82bSMike Smith 	    dr->ms_state = mes[i].sd_state;
4551ac4b82bSMike Smith 
4561ac4b82bSMike Smith 	    /* generate geometry information */
4571ac4b82bSMike Smith 	    if (sc->mlx_geom == MLX_GEOM_128_32) {
4581ac4b82bSMike Smith 		dr->ms_heads = 128;
4591ac4b82bSMike Smith 		dr->ms_sectors = 32;
4601ac4b82bSMike Smith 		dr->ms_cylinders = dr->ms_size / (128 * 32);
4611ac4b82bSMike Smith 	    } else {        /* MLX_GEOM_255/63 */
4621ac4b82bSMike Smith 		dr->ms_heads = 255;
4631ac4b82bSMike Smith 		dr->ms_sectors = 63;
4641ac4b82bSMike Smith 		dr->ms_cylinders = dr->ms_size / (255 * 63);
4651ac4b82bSMike Smith 	    }
4661ac4b82bSMike Smith 	    dr->ms_disk =  device_add_child(sc->mlx_dev, /*"mlxd"*/NULL, -1, dr);
4671ac4b82bSMike Smith 	    if (dr->ms_disk == 0)
4681ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "device_add_child failed\n");
4691ac4b82bSMike Smith 	}
4701ac4b82bSMike Smith     }
4711ac4b82bSMike Smith     free(mes, M_DEVBUF);
4721ac4b82bSMike Smith     if ((error = bus_generic_attach(sc->mlx_dev)) != 0)
4731ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "bus_generic_attach returned %d", error);
4741ac4b82bSMike Smith 
4751ac4b82bSMike Smith     /* mark controller back up */
4761ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_SHUTDOWN;
4771ac4b82bSMike Smith 
4781ac4b82bSMike Smith     /* enable interrupts */
4791ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
4801ac4b82bSMike Smith }
4811ac4b82bSMike Smith 
4821ac4b82bSMike Smith /********************************************************************************
4831ac4b82bSMike Smith  * Disconnect from the controller completely, in preparation for unload.
4841ac4b82bSMike Smith  */
4851ac4b82bSMike Smith int
4861ac4b82bSMike Smith mlx_detach(device_t dev)
4871ac4b82bSMike Smith {
4881ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
4891ac4b82bSMike Smith     int			error;
4901ac4b82bSMike Smith 
4911ac4b82bSMike Smith     debug("called");
4921ac4b82bSMike Smith 
4931ac4b82bSMike Smith     if (sc->mlx_state & MLX_STATE_OPEN)
4941ac4b82bSMike Smith 	return(EBUSY);
4951ac4b82bSMike Smith 
4961ac4b82bSMike Smith     if ((error = mlx_shutdown(dev)))
4971ac4b82bSMike Smith 	return(error);
4981ac4b82bSMike Smith 
4991ac4b82bSMike Smith     mlx_free(sc);
5001ac4b82bSMike Smith 
5011ac4b82bSMike Smith     /*
5021ac4b82bSMike Smith      * Deregister the control device on last detach.
5031ac4b82bSMike Smith      */
5041ac4b82bSMike Smith     if (--cdev_registered == 0)
5051ac4b82bSMike Smith 	cdevsw_remove(&mlx_cdevsw);
5061ac4b82bSMike Smith 
5071ac4b82bSMike Smith     return(0);
5081ac4b82bSMike Smith }
5091ac4b82bSMike Smith 
5101ac4b82bSMike Smith /********************************************************************************
5111ac4b82bSMike Smith  * Bring the controller down to a dormant state and detach all child devices.
5121ac4b82bSMike Smith  *
5131ac4b82bSMike Smith  * This function is called before detach, system shutdown, or before performing
5141ac4b82bSMike Smith  * an operation which may add or delete system disks.  (Call mlx_startup to
5151ac4b82bSMike Smith  * resume normal operation.)
5161ac4b82bSMike Smith  *
5171ac4b82bSMike Smith  * Note that we can assume that the bufq on the controller is empty, as we won't
5181ac4b82bSMike Smith  * allow shutdown if any device is open.
5191ac4b82bSMike Smith  */
5201ac4b82bSMike Smith int
5211ac4b82bSMike Smith mlx_shutdown(device_t dev)
5221ac4b82bSMike Smith {
5231ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
5241ac4b82bSMike Smith     struct mlxd_softc	*mlxd;
5251ac4b82bSMike Smith     int			i, s, error;
5261ac4b82bSMike Smith 
5271ac4b82bSMike Smith     debug("called");
5281ac4b82bSMike Smith 
5291ac4b82bSMike Smith     s = splbio();
5301ac4b82bSMike Smith     error = 0;
5311ac4b82bSMike Smith 
5321ac4b82bSMike Smith     /* assume we're going to shut down */
5331ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_SHUTDOWN;
5341ac4b82bSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
5351ac4b82bSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
5361ac4b82bSMike Smith 	    mlxd = device_get_softc(sc->mlx_sysdrive[i].ms_disk);
5371ac4b82bSMike Smith 	    if (mlxd->mlxd_flags & MLXD_OPEN) {		/* drive is mounted, abort shutdown */
5381ac4b82bSMike Smith 		sc->mlx_state &= ~MLX_STATE_SHUTDOWN;
5391ac4b82bSMike Smith 		device_printf(sc->mlx_sysdrive[i].ms_disk, "still open, can't shutdown\n");
5401ac4b82bSMike Smith 		error = EBUSY;
5411ac4b82bSMike Smith 		goto out;
5421ac4b82bSMike Smith 	    }
5431ac4b82bSMike Smith 	}
5441ac4b82bSMike Smith     }
5451ac4b82bSMike Smith 
5461ac4b82bSMike Smith     /* flush controller */
5471ac4b82bSMike Smith     device_printf(sc->mlx_dev, "flushing cache...");
5481ac4b82bSMike Smith     if (mlx_flush(sc)) {
5491ac4b82bSMike Smith 	printf("failed\n");
5501ac4b82bSMike Smith     } else {
5511ac4b82bSMike Smith 	printf("done\n");
5521ac4b82bSMike Smith     }
5531ac4b82bSMike Smith 
5541ac4b82bSMike Smith     /* delete all our child devices */
5551ac4b82bSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
5561ac4b82bSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
5571ac4b82bSMike Smith 	    if ((error = device_delete_child(sc->mlx_dev, sc->mlx_sysdrive[i].ms_disk)) != 0)
5581ac4b82bSMike Smith 		goto out;
5591ac4b82bSMike Smith 	    sc->mlx_sysdrive[i].ms_disk = 0;
5601ac4b82bSMike Smith 	}
5611ac4b82bSMike Smith     }
5621ac4b82bSMike Smith 
5631ac4b82bSMike Smith     bus_generic_detach(sc->mlx_dev);
5641ac4b82bSMike Smith 
5651ac4b82bSMike Smith  out:
5661ac4b82bSMike Smith     splx(s);
5671ac4b82bSMike Smith     return(error);
5681ac4b82bSMike Smith }
5691ac4b82bSMike Smith 
5701ac4b82bSMike Smith /********************************************************************************
5711ac4b82bSMike Smith  * Bring the controller to a quiescent state, ready for system suspend.
5721ac4b82bSMike Smith  */
5731ac4b82bSMike Smith int
5741ac4b82bSMike Smith mlx_suspend(device_t dev)
5751ac4b82bSMike Smith {
5761ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
5771ac4b82bSMike Smith     int			s;
5781ac4b82bSMike Smith 
5791ac4b82bSMike Smith     debug("called");
5801ac4b82bSMike Smith 
5811ac4b82bSMike Smith     s = splbio();
5821ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_SUSPEND;
5831ac4b82bSMike Smith 
5841ac4b82bSMike Smith     /* flush controller */
5851ac4b82bSMike Smith     device_printf(sc->mlx_dev, "flushing cache...");
5861ac4b82bSMike Smith     printf("%s\n", mlx_flush(sc) ? "failed" : "done");
5871ac4b82bSMike Smith 
5881ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
5891ac4b82bSMike Smith     splx(s);
5901ac4b82bSMike Smith 
5911ac4b82bSMike Smith     return(0);
5921ac4b82bSMike Smith }
5931ac4b82bSMike Smith 
5941ac4b82bSMike Smith /********************************************************************************
5951ac4b82bSMike Smith  * Bring the controller back to a state ready for operation.
5961ac4b82bSMike Smith  */
5971ac4b82bSMike Smith int
5981ac4b82bSMike Smith mlx_resume(device_t dev)
5991ac4b82bSMike Smith {
6001ac4b82bSMike Smith     struct mlx_softc	*sc = device_get_softc(dev);
6011ac4b82bSMike Smith 
6021ac4b82bSMike Smith     debug("called");
6031ac4b82bSMike Smith 
6041ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_SUSPEND;
6051ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
6061ac4b82bSMike Smith 
6071ac4b82bSMike Smith     return(0);
6081ac4b82bSMike Smith }
6091ac4b82bSMike Smith 
6101ac4b82bSMike Smith /*******************************************************************************
6111ac4b82bSMike Smith  * Take an interrupt, or be poked by other code to look for interrupt-worthy
6121ac4b82bSMike Smith  * status.
6131ac4b82bSMike Smith  */
6141ac4b82bSMike Smith void
6151ac4b82bSMike Smith mlx_intr(void *arg)
6161ac4b82bSMike Smith {
6171ac4b82bSMike Smith     struct mlx_softc	*sc = (struct mlx_softc *)arg;
6181ac4b82bSMike Smith     int			worked;
6191ac4b82bSMike Smith 
6201ac4b82bSMike Smith     debug("called");
6211ac4b82bSMike Smith 
6221ac4b82bSMike Smith     /* ack the interrupt */
6231ac4b82bSMike Smith     sc->mlx_intaction(sc, MLX_INTACTION_ACKNOWLEDGE);
6241ac4b82bSMike Smith 
6251ac4b82bSMike Smith     /* spin collecting finished commands */
6261ac4b82bSMike Smith     worked = 0;
6271ac4b82bSMike Smith     while (mlx_done(sc))
6281ac4b82bSMike Smith 	worked = 1;
6291ac4b82bSMike Smith 
6301ac4b82bSMike Smith     /* did we do anything? */
6311ac4b82bSMike Smith     if (worked)
6321ac4b82bSMike Smith 	mlx_complete(sc);
6331ac4b82bSMike Smith };
6341ac4b82bSMike Smith 
6351ac4b82bSMike Smith /*******************************************************************************
6361ac4b82bSMike Smith  * Receive a buf structure from a child device and queue it on a particular
6371ac4b82bSMike Smith  * disk resource, then poke the disk resource to start as much work as it can.
6381ac4b82bSMike Smith  */
6391ac4b82bSMike Smith int
6401ac4b82bSMike Smith mlx_submit_buf(struct mlx_softc *sc, struct buf *bp)
6411ac4b82bSMike Smith {
6421ac4b82bSMike Smith     debug("called");
6431ac4b82bSMike Smith 
6441ac4b82bSMike Smith     bufq_insert_tail(&sc->mlx_bufq, bp);
6451ac4b82bSMike Smith     sc->mlx_waitbufs++;
6461ac4b82bSMike Smith     mlx_startio(sc);
6471ac4b82bSMike Smith     return(0);
6481ac4b82bSMike Smith }
6491ac4b82bSMike Smith 
6501ac4b82bSMike Smith /********************************************************************************
6511ac4b82bSMike Smith  * Accept an open operation on the control device.
6521ac4b82bSMike Smith  */
6531ac4b82bSMike Smith int
6541ac4b82bSMike Smith mlx_open(dev_t dev, int flags, int fmt, struct proc *p)
6551ac4b82bSMike Smith {
6561ac4b82bSMike Smith     int			unit = minor(dev);
6571ac4b82bSMike Smith     struct mlx_softc	*sc = devclass_get_softc(mlx_devclass, unit);
6581ac4b82bSMike Smith 
6591ac4b82bSMike Smith     sc->mlx_state |= MLX_STATE_OPEN;
6601ac4b82bSMike Smith     return(0);
6611ac4b82bSMike Smith }
6621ac4b82bSMike Smith 
6631ac4b82bSMike Smith /********************************************************************************
6641ac4b82bSMike Smith  * Accept the last close on the control device.
6651ac4b82bSMike Smith  */
6661ac4b82bSMike Smith int
6671ac4b82bSMike Smith mlx_close(dev_t dev, int flags, int fmt, struct proc *p)
6681ac4b82bSMike Smith {
6691ac4b82bSMike Smith     int			unit = minor(dev);
6701ac4b82bSMike Smith     struct mlx_softc	*sc = devclass_get_softc(mlx_devclass, unit);
6711ac4b82bSMike Smith 
6721ac4b82bSMike Smith     sc->mlx_state &= ~MLX_STATE_OPEN;
6731ac4b82bSMike Smith     return (0);
6741ac4b82bSMike Smith }
6751ac4b82bSMike Smith 
6761ac4b82bSMike Smith /********************************************************************************
6771ac4b82bSMike Smith  * Handle controller-specific control operations.
6781ac4b82bSMike Smith  */
6791ac4b82bSMike Smith int
6801ac4b82bSMike Smith mlx_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
6811ac4b82bSMike Smith {
6821ac4b82bSMike Smith     int			unit = minor(dev);
6831ac4b82bSMike Smith     struct mlx_softc	*sc = devclass_get_softc(mlx_devclass, unit);
6841ac4b82bSMike Smith     int			*arg = (int *)addr;
6851ac4b82bSMike Smith     struct mlx_pause	*mp;
6861ac4b82bSMike Smith     struct mlx_sysdrive	*dr;
6871ac4b82bSMike Smith     struct mlxd_softc	*mlxd;
6881ac4b82bSMike Smith     int			i, error;
6891ac4b82bSMike Smith 
6901ac4b82bSMike Smith     switch(cmd) {
6911ac4b82bSMike Smith 	/*
6921ac4b82bSMike Smith 	 * Enumerate connected system drives; returns the first system drive's
6931ac4b82bSMike Smith 	 * unit number if *arg is -1, or the next unit after *arg if it's
6941ac4b82bSMike Smith 	 * a valid unit on this controller.
6951ac4b82bSMike Smith 	 */
6961ac4b82bSMike Smith     case MLX_NEXT_CHILD:
6971ac4b82bSMike Smith 	/* search system drives */
6981ac4b82bSMike Smith 	for (i = 0; i < MLX_MAXDRIVES; i++) {
6991ac4b82bSMike Smith 	    /* is this one attached? */
7001ac4b82bSMike Smith 	    if (sc->mlx_sysdrive[i].ms_disk != 0) {
7011ac4b82bSMike Smith 		/* looking for the next one we come across? */
7021ac4b82bSMike Smith 		if (*arg == -1) {
7031ac4b82bSMike Smith 		    *arg = device_get_unit(sc->mlx_sysdrive[i].ms_disk);
7041ac4b82bSMike Smith 		    return(0);
7051ac4b82bSMike Smith 		}
7061ac4b82bSMike Smith 		/* we want the one after this one */
7071ac4b82bSMike Smith 		if (*arg == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
7081ac4b82bSMike Smith 		    *arg = -1;
7091ac4b82bSMike Smith 	    }
7101ac4b82bSMike Smith 	}
7111ac4b82bSMike Smith 	return(ENOENT);
7121ac4b82bSMike Smith 
7131ac4b82bSMike Smith 	/*
7141ac4b82bSMike Smith 	 * Scan the controller to see whether new drives have appeared.
7151ac4b82bSMike Smith 	 */
7161ac4b82bSMike Smith     case MLX_RESCAN_DRIVES:
7171ac4b82bSMike Smith 	mlx_startup(sc);
7181ac4b82bSMike Smith 	return(0);
7191ac4b82bSMike Smith 
7201ac4b82bSMike Smith 	/*
7211ac4b82bSMike Smith 	 * Disconnect from the specified drive; it may be about to go
7221ac4b82bSMike Smith 	 * away.
7231ac4b82bSMike Smith 	 */
7241ac4b82bSMike Smith     case MLX_DETACH_DRIVE:			/* detach one drive */
7251ac4b82bSMike Smith 
7261ac4b82bSMike Smith 	if (((dr = mlx_findunit(sc, *arg)) == NULL) ||
7271ac4b82bSMike Smith 	    ((mlxd = device_get_softc(dr->ms_disk)) == NULL))
7281ac4b82bSMike Smith 	    return(ENOENT);
7291ac4b82bSMike Smith 
7301ac4b82bSMike Smith 	device_printf(dr->ms_disk, "detaching...");
7311ac4b82bSMike Smith 	error = 0;
7321ac4b82bSMike Smith 	if (mlxd->mlxd_flags & MLXD_OPEN) {
7331ac4b82bSMike Smith 	    error = EBUSY;
7341ac4b82bSMike Smith 	    goto detach_out;
7351ac4b82bSMike Smith 	}
7361ac4b82bSMike Smith 
7371ac4b82bSMike Smith 	/* flush controller */
7381ac4b82bSMike Smith 	if (mlx_flush(sc)) {
7391ac4b82bSMike Smith 	    error = EBUSY;
7401ac4b82bSMike Smith 	    goto detach_out;
7411ac4b82bSMike Smith 	}
7421ac4b82bSMike Smith 
7431ac4b82bSMike Smith 	/* nuke drive */
7441ac4b82bSMike Smith 	if ((error = device_delete_child(sc->mlx_dev, dr->ms_disk)) != 0)
7451ac4b82bSMike Smith 	    goto detach_out;
7461ac4b82bSMike Smith 	dr->ms_disk = 0;
7471ac4b82bSMike Smith 	bus_generic_detach(sc->mlx_dev);
7481ac4b82bSMike Smith 
7491ac4b82bSMike Smith     detach_out:
7501ac4b82bSMike Smith 	if (error) {
7511ac4b82bSMike Smith 	    printf("failed\n");
7521ac4b82bSMike Smith 	} else {
7531ac4b82bSMike Smith 	    printf("done\n");
7541ac4b82bSMike Smith 	}
7551ac4b82bSMike Smith 	return(error);
7561ac4b82bSMike Smith 
7571ac4b82bSMike Smith 	/*
7581ac4b82bSMike Smith 	 * Pause one or more SCSI channels for a period of time, to assist
7591ac4b82bSMike Smith 	 * in the process of hot-swapping devices.
7601ac4b82bSMike Smith 	 *
7611ac4b82bSMike Smith 	 * Note that at least the 3.51 firmware on the DAC960PL doesn't seem
7621ac4b82bSMike Smith 	 * to do this right.
7631ac4b82bSMike Smith 	 */
7641ac4b82bSMike Smith     case MLX_PAUSE_CHANNEL:			/* schedule a channel pause */
7651ac4b82bSMike Smith 	/* Does this command work on this firmware? */
7661ac4b82bSMike Smith 	if (!(sc->mlx_feature & MLX_FEAT_PAUSEWORKS))
7671ac4b82bSMike Smith 	    return(EOPNOTSUPP);
7681ac4b82bSMike Smith 
7691ac4b82bSMike Smith 	mp = (struct mlx_pause *)addr;
7701ac4b82bSMike Smith 	if ((mp->mp_which == MLX_PAUSE_CANCEL) && (sc->mlx_pause.mp_when != 0)) {
7711ac4b82bSMike Smith 	    /* cancel a pending pause operation */
7721ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = 0;
7731ac4b82bSMike Smith 	} else {
7741ac4b82bSMike Smith 	    /* fix for legal channels */
7751ac4b82bSMike Smith 	    mp->mp_which &= ((1 << sc->mlx_nchan) -1);
7761ac4b82bSMike Smith 	    /* check time values */
7771ac4b82bSMike Smith 	    if ((mp->mp_when < 0) || (mp->mp_when > 3600))
7781ac4b82bSMike Smith 		return(EINVAL);
7791ac4b82bSMike Smith 	    if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30)))
7801ac4b82bSMike Smith 		return(EINVAL);
7811ac4b82bSMike Smith 
7821ac4b82bSMike Smith 	    /* check for a pause currently running */
7831ac4b82bSMike Smith 	    if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0))
7841ac4b82bSMike Smith 		return(EBUSY);
7851ac4b82bSMike Smith 
7861ac4b82bSMike Smith 	    /* looks ok, go with it */
7871ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = mp->mp_which;
7881ac4b82bSMike Smith 	    sc->mlx_pause.mp_when = time_second + mp->mp_when;
7891ac4b82bSMike Smith 	    sc->mlx_pause.mp_howlong = sc->mlx_pause.mp_when + mp->mp_howlong;
7901ac4b82bSMike Smith 	}
7911ac4b82bSMike Smith 	return(0);
7921ac4b82bSMike Smith 
7931ac4b82bSMike Smith 	/*
7941ac4b82bSMike Smith 	 * Accept a command passthrough-style.
7951ac4b82bSMike Smith 	 */
7961ac4b82bSMike Smith     case MLX_COMMAND:
7971ac4b82bSMike Smith 	return(mlx_user_command(sc, (struct mlx_usercommand *)addr));
7981ac4b82bSMike Smith 
7991ac4b82bSMike Smith     default:
8001ac4b82bSMike Smith 	return(ENOTTY);
8011ac4b82bSMike Smith     }
8021ac4b82bSMike Smith }
8031ac4b82bSMike Smith 
8041ac4b82bSMike Smith /********************************************************************************
8051ac4b82bSMike Smith  * Handle operations requested by a System Drive connected to this controller.
8061ac4b82bSMike Smith  */
8071ac4b82bSMike Smith int
8081ac4b82bSMike Smith mlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd,
8091ac4b82bSMike Smith 		caddr_t addr, int32_t flag, struct proc *p)
8101ac4b82bSMike Smith {
8111ac4b82bSMike Smith     struct mlxd_rebuild		*mr = (struct mlxd_rebuild *)addr;
8121ac4b82bSMike Smith     struct mlxd_rebuild_status	*mp = (struct mlxd_rebuild_status *)addr;
8131ac4b82bSMike Smith     int				*arg = (int *)addr;
8141ac4b82bSMike Smith     int				error;
8151ac4b82bSMike Smith 
8161ac4b82bSMike Smith     switch(cmd) {
8171ac4b82bSMike Smith 	/*
8181ac4b82bSMike Smith 	 * Return the current status of this drive.
8191ac4b82bSMike Smith 	 */
8201ac4b82bSMike Smith     case MLXD_STATUS:
8211ac4b82bSMike Smith 	*arg = drive->ms_state;
8221ac4b82bSMike Smith 	return(0);
8231ac4b82bSMike Smith 
8241ac4b82bSMike Smith 	/*
8251ac4b82bSMike Smith 	 * Start a background rebuild on this drive.
8261ac4b82bSMike Smith 	 */
8271ac4b82bSMike Smith     case MLXD_REBUILDASYNC:
8281ac4b82bSMike Smith 	/* XXX lock? */
8291ac4b82bSMike Smith 	if (sc->mlx_rebuild >= 0)
8301ac4b82bSMike Smith 	    return(EBUSY);
8311ac4b82bSMike Smith 	sc->mlx_rebuild = drive - &sc->mlx_sysdrive[0];
8321ac4b82bSMike Smith 
8331ac4b82bSMike Smith 	switch (mlx_rebuild(sc, mr->rb_channel, mr->rb_target)) {
8341ac4b82bSMike Smith 	case 0:
8351ac4b82bSMike Smith 	    drive->ms_state = MLX_SYSD_REBUILD;
8361ac4b82bSMike Smith 	    error = 0;
8371ac4b82bSMike Smith 	    break;
8381ac4b82bSMike Smith 	case 0x10000:
8391ac4b82bSMike Smith 	    error = ENOMEM;		/* couldn't set up the command */
8401ac4b82bSMike Smith 	    break;
8411ac4b82bSMike Smith 	case 0x0002:
8421ac4b82bSMike Smith 	case 0x0106:
8431ac4b82bSMike Smith 	    error = EBUSY;
8441ac4b82bSMike Smith 	    break;
8451ac4b82bSMike Smith 	case 0x0004:
8461ac4b82bSMike Smith 	    error = EIO;
8471ac4b82bSMike Smith 	    break;
8481ac4b82bSMike Smith 	case 0x0105:
8491ac4b82bSMike Smith 	    error = ERANGE;
8501ac4b82bSMike Smith 	    break;
8511ac4b82bSMike Smith 	default:
8521ac4b82bSMike Smith 	    error = EINVAL;
8531ac4b82bSMike Smith 	    break;
8541ac4b82bSMike Smith 	}
8551ac4b82bSMike Smith 	if (error != 0)
8561ac4b82bSMike Smith 	    sc->mlx_rebuild = -1;
8571ac4b82bSMike Smith 	return(error);
8581ac4b82bSMike Smith 
8591ac4b82bSMike Smith 	/*
8601ac4b82bSMike Smith 	 * Start a background consistency check on this drive.
8611ac4b82bSMike Smith 	 */
8621ac4b82bSMike Smith     case MLXD_CHECKASYNC:		/* start a background consistency check */
8631ac4b82bSMike Smith 	/* XXX implement */
8641ac4b82bSMike Smith 	break;
8651ac4b82bSMike Smith 
8661ac4b82bSMike Smith 	/*
8671ac4b82bSMike Smith 	 * Get the status of the current rebuild or consistency check.
8681ac4b82bSMike Smith 	 */
8691ac4b82bSMike Smith     case MLXD_REBUILDSTAT:
8701ac4b82bSMike Smith 
8711ac4b82bSMike Smith 	if (sc->mlx_rebuild >= 0) {	/* may be a second or so out of date */
8721ac4b82bSMike Smith 	    mp->rs_drive = sc->mlx_rebuild;
8731ac4b82bSMike Smith 	    mp->rs_size = sc->mlx_sysdrive[sc->mlx_rebuild].ms_size;
8741ac4b82bSMike Smith 	    mp->rs_remaining = sc->mlx_rebuildstat;
8751ac4b82bSMike Smith 	    return(0);
8761ac4b82bSMike Smith 	} else if (sc->mlx_check >= 0) {
8771ac4b82bSMike Smith 	    /* XXX implement */
8781ac4b82bSMike Smith 	} else {
8791ac4b82bSMike Smith 	    /* XXX should return status of last completed operation? */
8801ac4b82bSMike Smith 	    return(EINVAL);
8811ac4b82bSMike Smith 	}
8821ac4b82bSMike Smith 
8831ac4b82bSMike Smith     }
8841ac4b82bSMike Smith     return(ENOIOCTL);
8851ac4b82bSMike Smith }
8861ac4b82bSMike Smith 
8871ac4b82bSMike Smith 
8881ac4b82bSMike Smith /********************************************************************************
8891ac4b82bSMike Smith  ********************************************************************************
8901ac4b82bSMike Smith                                                                 Status Monitoring
8911ac4b82bSMike Smith  ********************************************************************************
8921ac4b82bSMike Smith  ********************************************************************************/
8931ac4b82bSMike Smith 
8941ac4b82bSMike Smith #define MLX_PERIODIC_ISBUSY(sc)	(sc->mlx_polling <= 0)
8951ac4b82bSMike Smith #define MLX_PERIODIC_BUSY(sc)	atomic_add_int(&sc->mlx_polling, 1);
8961ac4b82bSMike Smith #define MLX_PERIODIC_UNBUSY(sc) atomic_subtract_int(&sc->mlx_polling, 1);
8971ac4b82bSMike Smith 
8981ac4b82bSMike Smith /********************************************************************************
8991ac4b82bSMike Smith  * Fire off commands to periodically check the status of connected drives.
9001ac4b82bSMike Smith  */
9011ac4b82bSMike Smith static void
9021ac4b82bSMike Smith mlx_periodic(void *data)
9031ac4b82bSMike Smith {
9041ac4b82bSMike Smith     struct mlx_softc *sc = (struct mlx_softc *)data;
9051ac4b82bSMike Smith 
9061ac4b82bSMike Smith     debug("called");
9071ac4b82bSMike Smith 
9081ac4b82bSMike Smith     /*
9091ac4b82bSMike Smith      * Run a bus pause?
9101ac4b82bSMike Smith      */
9111ac4b82bSMike Smith     if ((sc->mlx_pause.mp_which != 0) &&
9121ac4b82bSMike Smith 	(sc->mlx_pause.mp_when > 0) &&
9131ac4b82bSMike Smith 	(time_second >= sc->mlx_pause.mp_when)){
9141ac4b82bSMike Smith 
9151ac4b82bSMike Smith 	mlx_pause_action(sc);		/* pause is running */
9161ac4b82bSMike Smith 	sc->mlx_pause.mp_when = 0;
9171ac4b82bSMike Smith 	sysbeep(500, hz);
9181ac4b82bSMike Smith 
9191ac4b82bSMike Smith 	/*
9201ac4b82bSMike Smith 	 * Bus pause still running?
9211ac4b82bSMike Smith 	 */
9221ac4b82bSMike Smith     } else if ((sc->mlx_pause.mp_which != 0) &&
9231ac4b82bSMike Smith 	       (sc->mlx_pause.mp_when == 0)) {
9241ac4b82bSMike Smith 
9251ac4b82bSMike Smith 	/* time to stop bus pause? */
9261ac4b82bSMike Smith 	if (time_second >= sc->mlx_pause.mp_howlong) {
9271ac4b82bSMike Smith 	    mlx_pause_action(sc);
9281ac4b82bSMike Smith 	    sc->mlx_pause.mp_which = 0;	/* pause is complete */
9291ac4b82bSMike Smith 	    sysbeep(500, hz);
9301ac4b82bSMike Smith 	} else {
9311ac4b82bSMike Smith 	    sysbeep((time_second % 5) * 100 + 500, hz/8);
9321ac4b82bSMike Smith 	}
9331ac4b82bSMike Smith 
9341ac4b82bSMike Smith 	/*
9351ac4b82bSMike Smith 	 * Run normal periodic activities?
9361ac4b82bSMike Smith 	 */
9371ac4b82bSMike Smith     } else if (MLX_PERIODIC_ISBUSY(sc)) {
9381ac4b82bSMike Smith 
9391ac4b82bSMike Smith 	/* time to perform a periodic status poll? XXX tuneable interval? */
9401ac4b82bSMike Smith 	if (time_second > (sc->mlx_lastpoll + 5)) {
9411ac4b82bSMike Smith 	    sc->mlx_lastpoll = time_second;
9421ac4b82bSMike Smith 
9431ac4b82bSMike Smith 	    /* for caution's sake */
9441ac4b82bSMike Smith 	    if (sc->mlx_polling < 0) {
9451ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "mlx_polling < 0\n");
9461ac4b82bSMike Smith 		atomic_set_int(&sc->mlx_polling, 0);
9471ac4b82bSMike Smith 	    }
9481ac4b82bSMike Smith 
9491ac4b82bSMike Smith 	    /*
9501ac4b82bSMike Smith 	     * Check controller status.
9511ac4b82bSMike Smith 	     */
9521ac4b82bSMike Smith 	    MLX_PERIODIC_BUSY(sc);
9531ac4b82bSMike Smith 	    mlx_enquire(sc, MLX_CMD_ENQUIRY, sizeof(struct mlx_enquiry), mlx_periodic_enquiry);
9541ac4b82bSMike Smith 
9551ac4b82bSMike Smith 	    /*
9561ac4b82bSMike Smith 	     * Check system drive status.
9571ac4b82bSMike Smith 	     *
9581ac4b82bSMike Smith 	     * XXX This might be better left to event-driven detection, eg. I/O to an offline
9591ac4b82bSMike Smith 	     *     drive will detect it's offline, rebuilds etc. should detect the drive is back
9601ac4b82bSMike Smith 	     *     online.
9611ac4b82bSMike Smith 	     */
9621ac4b82bSMike Smith 	    MLX_PERIODIC_BUSY(sc);
9631ac4b82bSMike Smith 	    mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(struct mlx_enq_sys_drive) * MLX_MAXDRIVES,
9641ac4b82bSMike Smith 			mlx_periodic_enquiry);
9651ac4b82bSMike Smith 	}
9661ac4b82bSMike Smith 
9671ac4b82bSMike Smith 	/*
9681ac4b82bSMike Smith 	 * Get drive rebuild/check status
9691ac4b82bSMike Smith 	 */
9701ac4b82bSMike Smith 	if (sc->mlx_rebuild >= 0) {
9711ac4b82bSMike Smith 	    MLX_PERIODIC_BUSY(sc);
9721ac4b82bSMike Smith 	    mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild);
9731ac4b82bSMike Smith 	}
9741ac4b82bSMike Smith     } else {
9751ac4b82bSMike Smith 	/*
9761ac4b82bSMike Smith 	 * If things are still running from the last poll, complain about it.
9771ac4b82bSMike Smith 	 *
9781ac4b82bSMike Smith 	 * XXX If this becomes an issue, we should have some way of telling what
9791ac4b82bSMike Smith 	 *     has become stuck.
9801ac4b82bSMike Smith 	 */
9811ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "poll still busy (%d)\n", sc->mlx_polling);
9821ac4b82bSMike Smith     }
9831ac4b82bSMike Smith 
9841ac4b82bSMike Smith     /* XXX check for wedged/timed out commands? */
9851ac4b82bSMike Smith 
9861ac4b82bSMike Smith     /* reschedule another poll next second or so */
9871ac4b82bSMike Smith     sc->mlx_timeout = timeout(mlx_periodic, sc, hz);
9881ac4b82bSMike Smith }
9891ac4b82bSMike Smith 
9901ac4b82bSMike Smith /********************************************************************************
9911ac4b82bSMike Smith  * Handle the result of an ENQUIRY command instigated by periodic status polling.
9921ac4b82bSMike Smith  */
9931ac4b82bSMike Smith static void
9941ac4b82bSMike Smith mlx_periodic_enquiry(struct mlx_command *mc)
9951ac4b82bSMike Smith {
9961ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
9971ac4b82bSMike Smith 
9981ac4b82bSMike Smith     debug("called");
9991ac4b82bSMike Smith 
10001ac4b82bSMike Smith     /* Command completed OK? */
10011ac4b82bSMike Smith     if (mc->mc_status != 0) {
10021ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "periodic enquiry failed\n");
10031ac4b82bSMike Smith 	goto out;
10041ac4b82bSMike Smith     }
10051ac4b82bSMike Smith 
10061ac4b82bSMike Smith     /* respond to command */
10071ac4b82bSMike Smith     switch(mc->mc_mailbox[0]) {
10081ac4b82bSMike Smith 	/*
10091ac4b82bSMike Smith 	 * Generic controller status update.  We could do more with this than just
10101ac4b82bSMike Smith 	 * checking the event log.
10111ac4b82bSMike Smith 	 */
10121ac4b82bSMike Smith     case MLX_CMD_ENQUIRY:
10131ac4b82bSMike Smith     {
10141ac4b82bSMike Smith 	struct mlx_enquiry		*me = (struct mlx_enquiry *)mc->mc_data;
10151ac4b82bSMike Smith 
10161ac4b82bSMike Smith 	/* New stuff in the event log? */
10171ac4b82bSMike Smith 	if (me->me_event_log_seq_num != sc->mlx_lastevent) {
10181ac4b82bSMike Smith 	    /* record where current events are up to */
10191ac4b82bSMike Smith 	    sc->mlx_currevent = me->me_event_log_seq_num;
10201ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "event log pointer was %d, now %d\n",
10211ac4b82bSMike Smith 			  sc->mlx_lastevent, sc->mlx_currevent);
10221ac4b82bSMike Smith 
10231ac4b82bSMike Smith 	    /* start poll of event log */
10241ac4b82bSMike Smith 	    mlx_periodic_eventlog_poll(sc);
10251ac4b82bSMike Smith 	}
10261ac4b82bSMike Smith 	break;
10271ac4b82bSMike Smith     }
10281ac4b82bSMike Smith     case MLX_CMD_ENQSYSDRIVE:
10291ac4b82bSMike Smith     {
10301ac4b82bSMike Smith 	struct mlx_enq_sys_drive	*mes = (struct mlx_enq_sys_drive *)mc->mc_data;
10311ac4b82bSMike Smith 	struct mlx_sysdrive		*dr;
10321ac4b82bSMike Smith 	int				i;
10331ac4b82bSMike Smith 
10341ac4b82bSMike Smith 	for (i = 0, dr = &sc->mlx_sysdrive[0];
10351ac4b82bSMike Smith 	     (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
10361ac4b82bSMike Smith 	     i++) {
10371ac4b82bSMike Smith 
10381ac4b82bSMike Smith 	    /* if disk is being rebuilt, we should not check it */
10391ac4b82bSMike Smith 	    if (dr->ms_state == MLX_SYSD_REBUILD) {
10401ac4b82bSMike Smith 		/* has state been changed by controller? */
10411ac4b82bSMike Smith 		if (dr->ms_state != mes[i].sd_state) {
10421ac4b82bSMike Smith 		    switch(mes[i].sd_state) {
10431ac4b82bSMike Smith 		    case MLX_SYSD_OFFLINE:
10441ac4b82bSMike Smith 			device_printf(dr->ms_disk, "drive offline\n");
10451ac4b82bSMike Smith 			break;
10461ac4b82bSMike Smith 		    case MLX_SYSD_ONLINE:
10471ac4b82bSMike Smith 			device_printf(dr->ms_disk, "drive online\n");
10481ac4b82bSMike Smith 			break;
10491ac4b82bSMike Smith 		    case MLX_SYSD_CRITICAL:
10501ac4b82bSMike Smith 			device_printf(dr->ms_disk, "drive critical\n");
10511ac4b82bSMike Smith 			break;
10521ac4b82bSMike Smith 		    }
10531ac4b82bSMike Smith 		    /* save new state */
10541ac4b82bSMike Smith 		    dr->ms_state = mes[i].sd_state;
10551ac4b82bSMike Smith 		}
10561ac4b82bSMike Smith 	    }
10571ac4b82bSMike Smith 	}
10581ac4b82bSMike Smith 	break;
10591ac4b82bSMike Smith     }
10601ac4b82bSMike Smith     default:
10611ac4b82bSMike Smith 	break;
10621ac4b82bSMike Smith     }
10631ac4b82bSMike Smith 
10641ac4b82bSMike Smith  out:
10651ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
10661ac4b82bSMike Smith     mlx_releasecmd(mc);
10671ac4b82bSMike Smith     /* this event is done */
10681ac4b82bSMike Smith     MLX_PERIODIC_UNBUSY(sc);
10691ac4b82bSMike Smith }
10701ac4b82bSMike Smith 
10711ac4b82bSMike Smith /********************************************************************************
10721ac4b82bSMike Smith  * Instigate a poll for one event log message on (sc).
10731ac4b82bSMike Smith  * We only poll for one message at a time, to keep our command usage down.
10741ac4b82bSMike Smith  */
10751ac4b82bSMike Smith static void
10761ac4b82bSMike Smith mlx_periodic_eventlog_poll(struct mlx_softc *sc)
10771ac4b82bSMike Smith {
10781ac4b82bSMike Smith     struct mlx_command	*mc;
10791ac4b82bSMike Smith     void		*result = NULL;
10801ac4b82bSMike Smith     int			error;
10811ac4b82bSMike Smith 
10821ac4b82bSMike Smith     debug("called");
10831ac4b82bSMike Smith 
10841ac4b82bSMike Smith     /* presume we are going to create another event */
10851ac4b82bSMike Smith     MLX_PERIODIC_BUSY(sc);
10861ac4b82bSMike Smith 
10871ac4b82bSMike Smith     /* get ourselves a command buffer */
10881ac4b82bSMike Smith     error = 1;
10891ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
10901ac4b82bSMike Smith 	goto out;
10911ac4b82bSMike Smith     /* allocate the response structure */
10921ac4b82bSMike Smith     if ((result = malloc(sizeof(struct mlx_eventlog_entry), M_DEVBUF, M_NOWAIT)) == NULL)
10931ac4b82bSMike Smith 	goto out;
10941ac4b82bSMike Smith     /* get a command slot */
10951ac4b82bSMike Smith     if (mlx_getslot(mc))
10961ac4b82bSMike Smith 	goto out;
10971ac4b82bSMike Smith 
10981ac4b82bSMike Smith     /* map the command so the controller can see it */
10991ac4b82bSMike Smith     mc->mc_data = result;
11001ac4b82bSMike Smith     mc->mc_length = sizeof(struct mlx_eventlog_entry);
11011ac4b82bSMike Smith     mlx_mapcmd(mc);
11021ac4b82bSMike Smith 
11031ac4b82bSMike Smith     /* build the command to get one entry */
11041ac4b82bSMike Smith     mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1, sc->mlx_lastevent, 0, 0, mc->mc_dataphys, 0);
11051ac4b82bSMike Smith     mc->mc_complete = mlx_periodic_eventlog_respond;
11061ac4b82bSMike Smith     mc->mc_private = mc;
11071ac4b82bSMike Smith 
11081ac4b82bSMike Smith     /* start the command */
11091ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
11101ac4b82bSMike Smith 	goto out;
11111ac4b82bSMike Smith 
11121ac4b82bSMike Smith     error = 0;			/* success */
11131ac4b82bSMike Smith  out:
11141ac4b82bSMike Smith     if (mc != NULL)
11151ac4b82bSMike Smith 	mlx_releasecmd(mc);
11161ac4b82bSMike Smith     if ((error != 0) && (result != NULL)) {
11171ac4b82bSMike Smith 	free(result, M_DEVBUF);
11181ac4b82bSMike Smith     }
11191ac4b82bSMike Smith     /* abort this event */
11201ac4b82bSMike Smith     if (error != 0)
11211ac4b82bSMike Smith 	MLX_PERIODIC_UNBUSY(sc);
11221ac4b82bSMike Smith }
11231ac4b82bSMike Smith 
11241ac4b82bSMike Smith /********************************************************************************
11251ac4b82bSMike Smith  * Handle the result of polling for a log message, generate diagnostic output.
11261ac4b82bSMike Smith  * If this wasn't the last message waiting for us, we'll go collect another.
11271ac4b82bSMike Smith  */
11281ac4b82bSMike Smith static char *mlx_sense_messages[] = {
11291ac4b82bSMike Smith     "because write recovery failed",
11301ac4b82bSMike Smith     "because of SCSI bus reset failure",
11311ac4b82bSMike Smith     "because of double check condition",
11321ac4b82bSMike Smith     "because it was removed",
11331ac4b82bSMike Smith     "because of gross error on SCSI chip",
11341ac4b82bSMike Smith     "because of bad tag returned from drive",
11351ac4b82bSMike Smith     "because of timeout on SCSI command",
11361ac4b82bSMike Smith     "because of reset SCSI command issued from system",
11371ac4b82bSMike Smith     "because busy or parity error count exceeded limit",
11381ac4b82bSMike Smith     "because of 'kill drive' command from system",
11391ac4b82bSMike Smith     "because of selection timeout",
11401ac4b82bSMike Smith     "due to SCSI phase sequence error",
11411ac4b82bSMike Smith     "due to unknown status"
11421ac4b82bSMike Smith };
11431ac4b82bSMike Smith 
11441ac4b82bSMike Smith static void
11451ac4b82bSMike Smith mlx_periodic_eventlog_respond(struct mlx_command *mc)
11461ac4b82bSMike Smith {
11471ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
11481ac4b82bSMike Smith     struct mlx_eventlog_entry	*el = (struct mlx_eventlog_entry *)mc->mc_data;
11491ac4b82bSMike Smith     char			*reason;
11501ac4b82bSMike Smith 
11511ac4b82bSMike Smith     debug("called");
11521ac4b82bSMike Smith 
11531ac4b82bSMike Smith     if (mc->mc_status == 0) {
11541ac4b82bSMike Smith 	sc->mlx_lastevent++;		/* got the message OK */
11551ac4b82bSMike Smith 
11561ac4b82bSMike Smith 	/* handle event log message */
11571ac4b82bSMike Smith 	switch(el->el_type) {
11581ac4b82bSMike Smith 	    /*
11591ac4b82bSMike Smith 	     * This is the only sort of message we understand at the moment.
11601ac4b82bSMike Smith 	     * The tests here are probably incomplete.
11611ac4b82bSMike Smith 	     */
11621ac4b82bSMike Smith 	case MLX_LOGMSG_SENSE:	/* sense data */
11631ac4b82bSMike Smith 	    /* Mylex vendor-specific message indicating a drive was killed? */
11641ac4b82bSMike Smith 	    if ((el->el_sensekey == 9) &&
11651ac4b82bSMike Smith 		(el->el_asc == 0x80)) {
11661ac4b82bSMike Smith 		if (el->el_asq < (sizeof(mlx_sense_messages) / sizeof(mlx_sense_messages[0]))) {
11671ac4b82bSMike Smith 		    reason = mlx_sense_messages[el->el_asq];
11681ac4b82bSMike Smith 		} else {
11691ac4b82bSMike Smith 		    reason = "for unknown reason";
11701ac4b82bSMike Smith 		}
11711ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d killed %s\n",
11721ac4b82bSMike Smith 			      el->el_channel, el->el_target, reason);
11731ac4b82bSMike Smith 	    }
11741ac4b82bSMike Smith 	    /* SCSI drive was reset? */
11751ac4b82bSMike Smith 	    if ((el->el_sensekey == 6) && (el->el_asc == 0x29)) {
11761ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d reset\n",
11771ac4b82bSMike Smith 			      el->el_channel, el->el_target);
11781ac4b82bSMike Smith 	    }
11791ac4b82bSMike Smith 	    /* SCSI drive error? */
11801ac4b82bSMike Smith 	    if (!((el->el_sensekey == 0) ||
11811ac4b82bSMike Smith 		  ((el->el_sensekey == 2) &&
11821ac4b82bSMike Smith 		   (el->el_asc == 0x04) &&
11831ac4b82bSMike Smith 		   ((el->el_asq == 0x01) ||
11841ac4b82bSMike Smith 		    (el->el_asq == 0x02))))) {
11851ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "physical drive %d:%d error log: sense = %d asc = %x asq = %x\n",
11861ac4b82bSMike Smith 			      el->el_channel, el->el_target, el->el_sensekey, el->el_asc, el->el_asq);
11871ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "  info %4D csi %4D\n", el->el_information, ":", el->el_csi, ":");
11881ac4b82bSMike Smith 	    }
11891ac4b82bSMike Smith 	    break;
11901ac4b82bSMike Smith 
11911ac4b82bSMike Smith 	default:
11921ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "unknown log message type 0x%x\n", el->el_type);
11931ac4b82bSMike Smith 	    break;
11941ac4b82bSMike Smith 	}
11951ac4b82bSMike Smith     } else {
11961ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "error reading message log - %s\n", mlx_diagnose_command(mc));
11971ac4b82bSMike Smith     }
11981ac4b82bSMike Smith 
11991ac4b82bSMike Smith     /* dispose of command and data */
12001ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
12011ac4b82bSMike Smith     mlx_releasecmd(mc);
12021ac4b82bSMike Smith 
12031ac4b82bSMike Smith     /* is there another message to obtain? */
12041ac4b82bSMike Smith     if (sc->mlx_lastevent != sc->mlx_currevent)
12051ac4b82bSMike Smith 	mlx_periodic_eventlog_poll(sc);
12061ac4b82bSMike Smith 
12071ac4b82bSMike Smith     /* this event is done */
12081ac4b82bSMike Smith     MLX_PERIODIC_UNBUSY(sc);
12091ac4b82bSMike Smith }
12101ac4b82bSMike Smith 
12111ac4b82bSMike Smith /********************************************************************************
12121ac4b82bSMike Smith  * Handle the completion of a rebuild operation.
12131ac4b82bSMike Smith  */
12141ac4b82bSMike Smith static void
12151ac4b82bSMike Smith mlx_periodic_rebuild(struct mlx_command *mc)
12161ac4b82bSMike Smith {
12171ac4b82bSMike Smith     struct mlx_softc		*sc = mc->mc_sc;
12181ac4b82bSMike Smith     struct mlx_rebuild_stat	*mr = (struct mlx_rebuild_stat *)mc->mc_private;
12191ac4b82bSMike Smith 
12201ac4b82bSMike Smith     switch(mc->mc_status) {
12211ac4b82bSMike Smith     case 0:				/* all OK, rebuild still running */
12221ac4b82bSMike Smith 	sc->mlx_rebuildstat = mr->rb_remaining;
12231ac4b82bSMike Smith 	break;
12241ac4b82bSMike Smith 
12251ac4b82bSMike Smith     case 0x0105:			/* rebuild/check finished */
12261ac4b82bSMike Smith 	if (sc->mlx_rebuild >= 0) {
12271ac4b82bSMike Smith 	    device_printf(sc->mlx_sysdrive[sc->mlx_rebuild].ms_disk, "rebuild completed\n");
12281ac4b82bSMike Smith 	    sc->mlx_rebuild = -1;
12291ac4b82bSMike Smith 	} else if (sc->mlx_check >= 0) {
12301ac4b82bSMike Smith 	    device_printf(sc->mlx_sysdrive[sc->mlx_check].ms_disk, "consistency check completed\n");
12311ac4b82bSMike Smith 	    sc->mlx_check = -1;
12321ac4b82bSMike Smith 	} else {
12331ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "consistency check completed\n");
12341ac4b82bSMike Smith 	}
12351ac4b82bSMike Smith 	break;
12361ac4b82bSMike Smith     }
12371ac4b82bSMike Smith     free(mc->mc_data, M_DEVBUF);
12381ac4b82bSMike Smith     mlx_releasecmd(mc);
12391ac4b82bSMike Smith     /* this event is done */
12401ac4b82bSMike Smith     MLX_PERIODIC_UNBUSY(sc);
12411ac4b82bSMike Smith }
12421ac4b82bSMike Smith 
12431ac4b82bSMike Smith /********************************************************************************
12441ac4b82bSMike Smith  ********************************************************************************
12451ac4b82bSMike Smith                                                                     Channel Pause
12461ac4b82bSMike Smith  ********************************************************************************
12471ac4b82bSMike Smith  ********************************************************************************/
12481ac4b82bSMike Smith 
12491ac4b82bSMike Smith /********************************************************************************
12501ac4b82bSMike Smith  * It's time to perform a channel pause action for (sc), either start or stop
12511ac4b82bSMike Smith  * the pause.
12521ac4b82bSMike Smith  */
12531ac4b82bSMike Smith static void
12541ac4b82bSMike Smith mlx_pause_action(struct mlx_softc *sc)
12551ac4b82bSMike Smith {
12561ac4b82bSMike Smith     struct mlx_command	*mc;
12571ac4b82bSMike Smith     int			failsafe, i, command;
12581ac4b82bSMike Smith 
12591ac4b82bSMike Smith     /* What are we doing here? */
12601ac4b82bSMike Smith     if (sc->mlx_pause.mp_when == 0) {
12611ac4b82bSMike Smith 	command = MLX_CMD_STARTCHANNEL;
12621ac4b82bSMike Smith 	failsafe = 0;
12631ac4b82bSMike Smith 
12641ac4b82bSMike Smith     } else {
12651ac4b82bSMike Smith 	command = MLX_CMD_STOPCHANNEL;
12661ac4b82bSMike Smith 
12671ac4b82bSMike Smith 	/*
12681ac4b82bSMike Smith 	 * Channels will always start again after the failsafe period,
12691ac4b82bSMike Smith 	 * which is specified in multiples of 30 seconds.
12701ac4b82bSMike Smith 	 * This constrains us to a maximum pause of 450 seconds.
12711ac4b82bSMike Smith 	 */
12721ac4b82bSMike Smith 	failsafe = ((sc->mlx_pause.mp_howlong - time_second) + 5) / 30;
12731ac4b82bSMike Smith 	if (failsafe > 0xf) {
12741ac4b82bSMike Smith 	    failsafe = 0xf;
12751ac4b82bSMike Smith 	    sc->mlx_pause.mp_howlong = time_second + (0xf * 30) - 5;
12761ac4b82bSMike Smith 	}
12771ac4b82bSMike Smith     }
12781ac4b82bSMike Smith 
12791ac4b82bSMike Smith     /* build commands for every channel requested */
12801ac4b82bSMike Smith     for (i = 0; i < sc->mlx_nchan; i++) {
12811ac4b82bSMike Smith 	if ((1 << i) & sc->mlx_pause.mp_which) {
12821ac4b82bSMike Smith 
12831ac4b82bSMike Smith 	    /* get ourselves a command buffer */
12841ac4b82bSMike Smith 	    if ((mc = mlx_alloccmd(sc)) == NULL)
12851ac4b82bSMike Smith 		goto fail;
12861ac4b82bSMike Smith 	    /* get a command slot */
12871ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_PRIORITY;
12881ac4b82bSMike Smith 	    if (mlx_getslot(mc))
12891ac4b82bSMike Smith 		goto fail;
12901ac4b82bSMike Smith 
12911ac4b82bSMike Smith 	    /* build the command */
12921ac4b82bSMike Smith 	    mlx_make_type2(mc, command, (failsafe << 4) | i, 0, 0, 0, 0, 0, 0, 0);
12931ac4b82bSMike Smith 	    mc->mc_complete = mlx_pause_done;
12941ac4b82bSMike Smith 	    mc->mc_private = sc;		/* XXX not needed */
12951ac4b82bSMike Smith 	    if (mlx_start(mc))
12961ac4b82bSMike Smith 		goto fail;
12971ac4b82bSMike Smith 	    /* command submitted OK */
12981ac4b82bSMike Smith 	    return;
12991ac4b82bSMike Smith 
13001ac4b82bSMike Smith 	fail:
13011ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "%s failed for channel %d\n",
13021ac4b82bSMike Smith 			  command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", i);
13031ac4b82bSMike Smith 	    if (mc != NULL)
13041ac4b82bSMike Smith 		mlx_releasecmd(mc);
13051ac4b82bSMike Smith 	}
13061ac4b82bSMike Smith     }
13071ac4b82bSMike Smith }
13081ac4b82bSMike Smith 
13091ac4b82bSMike Smith static void
13101ac4b82bSMike Smith mlx_pause_done(struct mlx_command *mc)
13111ac4b82bSMike Smith {
13121ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
13131ac4b82bSMike Smith     int			command = mc->mc_mailbox[0];
13141ac4b82bSMike Smith     int			channel = mc->mc_mailbox[2] & 0xf;
13151ac4b82bSMike Smith 
13161ac4b82bSMike Smith     if (mc->mc_status != 0) {
13171ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "%s command failed - %s\n",
13181ac4b82bSMike Smith 		      command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", mlx_diagnose_command(mc));
13191ac4b82bSMike Smith     } else if (command == MLX_CMD_STOPCHANNEL) {
13201ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "channel %d pausing for %ld seconds\n",
13211ac4b82bSMike Smith 		      channel, sc->mlx_pause.mp_howlong - time_second);
13221ac4b82bSMike Smith     } else {
13231ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "channel %d resuming\n", channel);
13241ac4b82bSMike Smith     }
13251ac4b82bSMike Smith     mlx_releasecmd(mc);
13261ac4b82bSMike Smith }
13271ac4b82bSMike Smith 
13281ac4b82bSMike Smith /********************************************************************************
13291ac4b82bSMike Smith  ********************************************************************************
13301ac4b82bSMike Smith                                                                Command Submission
13311ac4b82bSMike Smith  ********************************************************************************
13321ac4b82bSMike Smith  ********************************************************************************/
13331ac4b82bSMike Smith 
13341ac4b82bSMike Smith /********************************************************************************
13351ac4b82bSMike Smith  * Perform an Enquiry command using a type-3 command buffer and a return a single
13361ac4b82bSMike Smith  * linear result buffer.  If the completion function is specified, it will
13371ac4b82bSMike Smith  * be called with the completed command (and the result response will not be
13381ac4b82bSMike Smith  * valid until that point).  Otherwise, the command will either be busy-waited
13391ac4b82bSMike Smith  * for (interrupts not enabled), or slept for.
13401ac4b82bSMike Smith  */
13411ac4b82bSMike Smith static void *
13421ac4b82bSMike Smith mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (* complete)(struct mlx_command *mc))
13431ac4b82bSMike Smith {
13441ac4b82bSMike Smith     struct mlx_command	*mc;
13451ac4b82bSMike Smith     void		*result;
13461ac4b82bSMike Smith     int			error;
13471ac4b82bSMike Smith 
13481ac4b82bSMike Smith     debug("called");
13491ac4b82bSMike Smith 
13501ac4b82bSMike Smith     /* get ourselves a command buffer */
13511ac4b82bSMike Smith     error = 1;
13521ac4b82bSMike Smith     result = NULL;
13531ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
13541ac4b82bSMike Smith 	goto out;
13551ac4b82bSMike Smith     /* allocate the response structure */
13561ac4b82bSMike Smith     if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL)
13571ac4b82bSMike Smith 	goto out;
13581ac4b82bSMike Smith     /* get a command slot */
13591ac4b82bSMike Smith     mc->mc_flags |= MLX_CMD_PRIORITY | MLX_CMD_DATAOUT;
13601ac4b82bSMike Smith     if (mlx_getslot(mc))
13611ac4b82bSMike Smith 	goto out;
13621ac4b82bSMike Smith 
13631ac4b82bSMike Smith     /* map the command so the controller can see it */
13641ac4b82bSMike Smith     mc->mc_data = result;
13651ac4b82bSMike Smith     mc->mc_length = bufsize;
13661ac4b82bSMike Smith     mlx_mapcmd(mc);
13671ac4b82bSMike Smith 
13681ac4b82bSMike Smith     /* build an enquiry command */
13691ac4b82bSMike Smith     mlx_make_type2(mc, command, 0, 0, 0, 0, 0, 0, mc->mc_dataphys, 0);
13701ac4b82bSMike Smith 
13711ac4b82bSMike Smith     /* do we want a completion callback? */
13721ac4b82bSMike Smith     if (complete != NULL) {
13731ac4b82bSMike Smith 	mc->mc_complete = complete;
13741ac4b82bSMike Smith 	mc->mc_private = mc;
13751ac4b82bSMike Smith 	if ((error = mlx_start(mc)) != 0)
13761ac4b82bSMike Smith 	    goto out;
13771ac4b82bSMike Smith     } else {
13781ac4b82bSMike Smith 	/* run the command in either polled or wait mode */
13791ac4b82bSMike Smith 	if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc))
13801ac4b82bSMike Smith 	    goto out;
13811ac4b82bSMike Smith 
13821ac4b82bSMike Smith 	/* command completed OK? */
13831ac4b82bSMike Smith 	if (mc->mc_status != 0) {
13841ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "ENQUIRY failed - %s\n", mlx_diagnose_command(mc));
13851ac4b82bSMike Smith 	    goto out;
13861ac4b82bSMike Smith 	}
13871ac4b82bSMike Smith     }
13881ac4b82bSMike Smith     error = 0;			/* success */
13891ac4b82bSMike Smith  out:
13901ac4b82bSMike Smith     /* we got a command, but nobody else will free it */
13911ac4b82bSMike Smith     if ((complete == NULL) && (mc != NULL))
13921ac4b82bSMike Smith 	mlx_releasecmd(mc);
13931ac4b82bSMike Smith     if ((error != 0) && (result != NULL)) {
13941ac4b82bSMike Smith 	free(result, M_DEVBUF);
13951ac4b82bSMike Smith 	result = NULL;
13961ac4b82bSMike Smith     }
13971ac4b82bSMike Smith     return(result);
13981ac4b82bSMike Smith }
13991ac4b82bSMike Smith 
14001ac4b82bSMike Smith 
14011ac4b82bSMike Smith /********************************************************************************
14021ac4b82bSMike Smith  * Perform a Flush command on the nominated controller.
14031ac4b82bSMike Smith  *
14041ac4b82bSMike Smith  * May be called with interrupts enabled or disabled; will not return until
14051ac4b82bSMike Smith  * the flush operation completes or fails.
14061ac4b82bSMike Smith  */
14071ac4b82bSMike Smith static int
14081ac4b82bSMike Smith mlx_flush(struct mlx_softc *sc)
14091ac4b82bSMike Smith {
14101ac4b82bSMike Smith     struct mlx_command	*mc;
14111ac4b82bSMike Smith     int			error;
14121ac4b82bSMike Smith 
14131ac4b82bSMike Smith     debug("called");
14141ac4b82bSMike Smith 
14151ac4b82bSMike Smith     /* get ourselves a command buffer */
14161ac4b82bSMike Smith     error = 1;
14171ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
14181ac4b82bSMike Smith 	goto out;
14191ac4b82bSMike Smith     /* get a command slot */
14201ac4b82bSMike Smith     if (mlx_getslot(mc))
14211ac4b82bSMike Smith 	goto out;
14221ac4b82bSMike Smith 
14231ac4b82bSMike Smith     /* build a flush command */
14241ac4b82bSMike Smith     mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0);
14251ac4b82bSMike Smith 
14261ac4b82bSMike Smith     /* run the command in either polled or wait mode */
14271ac4b82bSMike Smith     if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc))
14281ac4b82bSMike Smith 	goto out;
14291ac4b82bSMike Smith 
14301ac4b82bSMike Smith     /* command completed OK? */
14311ac4b82bSMike Smith     if (mc->mc_status != 0) {
14321ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "FLUSH failed - %s\n", mlx_diagnose_command(mc));
14331ac4b82bSMike Smith 	goto out;
14341ac4b82bSMike Smith     }
14351ac4b82bSMike Smith 
14361ac4b82bSMike Smith     error = 0;			/* success */
14371ac4b82bSMike Smith  out:
14381ac4b82bSMike Smith     if (mc != NULL)
14391ac4b82bSMike Smith 	mlx_releasecmd(mc);
14401ac4b82bSMike Smith     return(error);
14411ac4b82bSMike Smith }
14421ac4b82bSMike Smith 
14431ac4b82bSMike Smith /********************************************************************************
14441ac4b82bSMike Smith  * Start a background rebuild on the nominated controller/channel/target.
14451ac4b82bSMike Smith  *
14461ac4b82bSMike Smith  * May be called with interrupts enabled or disabled; will return as soon as the
14471ac4b82bSMike Smith  * operation has started or been refused.
14481ac4b82bSMike Smith  */
14491ac4b82bSMike Smith static int
14501ac4b82bSMike Smith mlx_rebuild(struct mlx_softc *sc, int channel, int target)
14511ac4b82bSMike Smith {
14521ac4b82bSMike Smith     struct mlx_command	*mc;
14531ac4b82bSMike Smith     int			error;
14541ac4b82bSMike Smith 
14551ac4b82bSMike Smith     debug("called");
14561ac4b82bSMike Smith 
14571ac4b82bSMike Smith     /* get ourselves a command buffer */
14581ac4b82bSMike Smith     error = 0x10000;
14591ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
14601ac4b82bSMike Smith 	goto out;
14611ac4b82bSMike Smith     /* get a command slot */
14621ac4b82bSMike Smith     if (mlx_getslot(mc))
14631ac4b82bSMike Smith 	goto out;
14641ac4b82bSMike Smith 
14651ac4b82bSMike Smith     /* build a rebuild command */
14661ac4b82bSMike Smith     mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, 0, 0);
14671ac4b82bSMike Smith 
14681ac4b82bSMike Smith     /* run the command in either polled or wait mode */
14691ac4b82bSMike Smith     if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc))
14701ac4b82bSMike Smith 	goto out;
14711ac4b82bSMike Smith 
14721ac4b82bSMike Smith     /* command completed OK? */
14731ac4b82bSMike Smith     if (mc->mc_status != 0) {
14741ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "REBUILD ASYNC failed - %s\n", mlx_diagnose_command(mc));
14751ac4b82bSMike Smith     } else {
14761ac4b82bSMike Smith 	device_printf(sc->mlx_sysdrive[sc->mlx_rebuild].ms_disk, "rebuild started");
14771ac4b82bSMike Smith     }
14781ac4b82bSMike Smith     error = mc->mc_status;
14791ac4b82bSMike Smith 
14801ac4b82bSMike Smith  out:
14811ac4b82bSMike Smith     if (mc != NULL)
14821ac4b82bSMike Smith 	mlx_releasecmd(mc);
14831ac4b82bSMike Smith     return(error);
14841ac4b82bSMike Smith }
14851ac4b82bSMike Smith 
14861ac4b82bSMike Smith /********************************************************************************
14871ac4b82bSMike Smith  * Run the command (mc) and return when it completes.
14881ac4b82bSMike Smith  *
14891ac4b82bSMike Smith  * Interrupts need to be enabled; returns nonzero on error.
14901ac4b82bSMike Smith  */
14911ac4b82bSMike Smith static int
14921ac4b82bSMike Smith mlx_wait_command(struct mlx_command *mc)
14931ac4b82bSMike Smith {
14941ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
14951ac4b82bSMike Smith     int			error, count;
14961ac4b82bSMike Smith 
14971ac4b82bSMike Smith     debug("called");
14981ac4b82bSMike Smith 
14991ac4b82bSMike Smith     mc->mc_complete = NULL;
15001ac4b82bSMike Smith     mc->mc_private = mc;		/* wake us when you're done */
15011ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
15021ac4b82bSMike Smith 	return(error);
15031ac4b82bSMike Smith 
15041ac4b82bSMike Smith     count = 0;
15051ac4b82bSMike Smith     /* XXX better timeout? */
15061ac4b82bSMike Smith     while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 30)) {
15071ac4b82bSMike Smith 	tsleep(mc->mc_private, PRIBIO | PCATCH, "mlxwcmd", hz);
15081ac4b82bSMike Smith     }
15091ac4b82bSMike Smith 
15101ac4b82bSMike Smith     if (mc->mc_status != 0) {
15111ac4b82bSMike Smith 	device_printf(sc->mlx_dev, "I/O error 0x%x\n", mc->mc_status);
15121ac4b82bSMike Smith 	return(EIO);
15131ac4b82bSMike Smith     }
15141ac4b82bSMike Smith     return(0);
15151ac4b82bSMike Smith }
15161ac4b82bSMike Smith 
15171ac4b82bSMike Smith 
15181ac4b82bSMike Smith /********************************************************************************
15191ac4b82bSMike Smith  * Start the command (mc) and busy-wait for it to complete.
15201ac4b82bSMike Smith  *
15211ac4b82bSMike Smith  * Should only be used when interrupts are not available. Returns 0 on
15221ac4b82bSMike Smith  * success, nonzero on error.
15231ac4b82bSMike Smith  * Successfully completed commands are dequeued.
15241ac4b82bSMike Smith  */
15251ac4b82bSMike Smith static int
15261ac4b82bSMike Smith mlx_poll_command(struct mlx_command *mc)
15271ac4b82bSMike Smith {
15281ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
15291ac4b82bSMike Smith     int			error, count, s;
15301ac4b82bSMike Smith 
15311ac4b82bSMike Smith     debug("called");
15321ac4b82bSMike Smith 
15331ac4b82bSMike Smith     mc->mc_complete = NULL;
15341ac4b82bSMike Smith     mc->mc_private = NULL;	/* we will poll for it */
15351ac4b82bSMike Smith     if ((error = mlx_start(mc)) != 0)
15361ac4b82bSMike Smith 	return(error);
15371ac4b82bSMike Smith 
15381ac4b82bSMike Smith     count = 0;
15391ac4b82bSMike Smith     do {
15401ac4b82bSMike Smith 	/* poll for completion */
15411ac4b82bSMike Smith 	mlx_done(mc->mc_sc);
15421ac4b82bSMike Smith     } while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 10000));
15431ac4b82bSMike Smith     if (mc->mc_status != MLX_STATUS_BUSY) {
15441ac4b82bSMike Smith 	s = splbio();
15451ac4b82bSMike Smith 	TAILQ_REMOVE(&sc->mlx_donecmd, mc, mc_link);
15461ac4b82bSMike Smith 	splx(s);
15471ac4b82bSMike Smith 	return(0);
15481ac4b82bSMike Smith     }
15491ac4b82bSMike Smith     device_printf(sc->mlx_dev, "I/O error 0x%x\n", mc->mc_status);
15501ac4b82bSMike Smith     return(EIO);
15511ac4b82bSMike Smith }
15521ac4b82bSMike Smith 
15531ac4b82bSMike Smith /********************************************************************************
15541ac4b82bSMike Smith  * Pull as much work off the softc's work queue as possible and give it to the
15551ac4b82bSMike Smith  * controller.  Leave a couple of slots free for emergencies.
15561ac4b82bSMike Smith  *
15571ac4b82bSMike Smith  * Must be called at splbio or in an equivalent fashion that prevents
15581ac4b82bSMike Smith  * reentry or activity on the bufq..
15591ac4b82bSMike Smith  */
15601ac4b82bSMike Smith static void
15611ac4b82bSMike Smith mlx_startio(struct mlx_softc *sc)
15621ac4b82bSMike Smith {
15631ac4b82bSMike Smith     struct mlx_command	*mc;
15641ac4b82bSMike Smith     struct mlxd_softc	*mlxd;
15651ac4b82bSMike Smith     struct buf		*bp;
15661ac4b82bSMike Smith     int			blkcount;
15671ac4b82bSMike Smith     int			driveno;
15681ac4b82bSMike Smith     int			cmd;
15691ac4b82bSMike Smith 
15701ac4b82bSMike Smith     /* spin until something prevents us from doing any work */
15711ac4b82bSMike Smith     for (;;) {
15721ac4b82bSMike Smith 
15731ac4b82bSMike Smith 	/* see if there's work to be done */
15741ac4b82bSMike Smith 	if ((bp = bufq_first(&sc->mlx_bufq)) == NULL)
15751ac4b82bSMike Smith 	    break;
15761ac4b82bSMike Smith 	/* get a command */
15771ac4b82bSMike Smith 	if ((mc = mlx_alloccmd(sc)) == NULL)
15781ac4b82bSMike Smith 	    break;
15791ac4b82bSMike Smith 	/* get a slot for the command */
15801ac4b82bSMike Smith 	if (mlx_getslot(mc) != 0) {
15811ac4b82bSMike Smith 	    mlx_releasecmd(mc);
15821ac4b82bSMike Smith 	    break;
15831ac4b82bSMike Smith 	}
15841ac4b82bSMike Smith 	/* get the buf containing our work */
15851ac4b82bSMike Smith 	bufq_remove(&sc->mlx_bufq, bp);
15861ac4b82bSMike Smith 	sc->mlx_waitbufs--;
15871ac4b82bSMike Smith 
15881ac4b82bSMike Smith 	/* connect the buf to the command */
15891ac4b82bSMike Smith 	mc->mc_complete = mlx_completeio;
15901ac4b82bSMike Smith 	mc->mc_private = bp;
15911ac4b82bSMike Smith 	mc->mc_data = bp->b_data;
15921ac4b82bSMike Smith 	mc->mc_length = bp->b_bcount;
15931ac4b82bSMike Smith 	if (bp->b_flags & B_READ) {
15941ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_DATAIN;
15951ac4b82bSMike Smith 	    cmd = MLX_CMD_READOLDSG;
15961ac4b82bSMike Smith 	} else {
15971ac4b82bSMike Smith 	    mc->mc_flags |= MLX_CMD_DATAOUT;
15981ac4b82bSMike Smith 	    cmd = MLX_CMD_WRITEOLDSG;
15991ac4b82bSMike Smith 	}
16001ac4b82bSMike Smith 
16011ac4b82bSMike Smith 	/* map the command so the controller can work with it */
16021ac4b82bSMike Smith 	mlx_mapcmd(mc);
16031ac4b82bSMike Smith 
16041ac4b82bSMike Smith 	/* build a suitable I/O command (assumes 512-byte rounded transfers) */
16051ac4b82bSMike Smith 	mlxd = (struct mlxd_softc *)bp->b_driver1;
16061ac4b82bSMike Smith 	driveno = mlxd->mlxd_drive - &sc->mlx_sysdrive[0];
16071ac4b82bSMike Smith 	blkcount = bp->b_bcount / MLX_BLKSIZE;
16081ac4b82bSMike Smith 
16091ac4b82bSMike Smith 	if ((bp->b_blkno + blkcount) > sc->mlx_sysdrive[driveno].ms_size)
16101ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "I/O beyond end of unit (%u,%d > %u)\n",
16111ac4b82bSMike Smith 			  bp->b_blkno, blkcount, sc->mlx_sysdrive[driveno].ms_size);
16121ac4b82bSMike Smith 
16131ac4b82bSMike Smith 	/*
16141ac4b82bSMike Smith 	 * Build the I/O command.  Note that the SG list type bits are set to zero,
16151ac4b82bSMike Smith 	 * denoting the format of SG list that we are using.
16161ac4b82bSMike Smith 	 */
16171ac4b82bSMike Smith 	mlx_make_type5(mc, cmd,
16181ac4b82bSMike Smith 		       blkcount & 0xff, 				/* xfer length low byte */
16191ac4b82bSMike Smith 		       (driveno << 4) | ((blkcount >> 8) & 0x0f),	/* target and length high nybble */
16201ac4b82bSMike Smith 		       bp->b_blkno,					/* physical block number */
16211ac4b82bSMike Smith 		       mc->mc_sgphys,					/* location of SG list */
16221ac4b82bSMike Smith 		       mc->mc_nsgent & 0x3f);				/* size of SG list (top 2 bits clear) */
16231ac4b82bSMike Smith 
16241ac4b82bSMike Smith 
16251ac4b82bSMike Smith 	/* try to give command to controller */
16261ac4b82bSMike Smith 	if (mlx_start(mc) != 0) {
16271ac4b82bSMike Smith 	    /* fail the command */
16281ac4b82bSMike Smith 	    mc->mc_status = MLX_STATUS_WEDGED;
16291ac4b82bSMike Smith 	    mlx_completeio(mc);
16301ac4b82bSMike Smith 	}
16311ac4b82bSMike Smith     }
16321ac4b82bSMike Smith }
16331ac4b82bSMike Smith 
16341ac4b82bSMike Smith /********************************************************************************
16351ac4b82bSMike Smith  * Handle completion of an I/O command.
16361ac4b82bSMike Smith  */
16371ac4b82bSMike Smith static void
16381ac4b82bSMike Smith mlx_completeio(struct mlx_command *mc)
16391ac4b82bSMike Smith {
16401ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
16411ac4b82bSMike Smith     struct buf		*bp = (struct buf *)mc->mc_private;
16421ac4b82bSMike Smith     struct mlxd_softc	*mlxd = (struct mlxd_softc *)bp->b_driver1;
16431ac4b82bSMike Smith 
16441ac4b82bSMike Smith     if (mc->mc_status != MLX_STATUS_OK) {	/* could be more verbose here? */
16451ac4b82bSMike Smith 	bp->b_error = EIO;
16461ac4b82bSMike Smith 	bp->b_flags |= B_ERROR;
16471ac4b82bSMike Smith 
16481ac4b82bSMike Smith 	switch(mc->mc_status) {
16491ac4b82bSMike Smith 	case MLX_STATUS_RDWROFFLINE:		/* system drive has gone offline */
16501ac4b82bSMike Smith 	    device_printf(mlxd->mlxd_dev, "drive offline\n");
16511ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "drive offline\n");
16521ac4b82bSMike Smith 	    mlxd->mlxd_drive->ms_state = MLX_SYSD_OFFLINE;
16531ac4b82bSMike Smith 	    break;
16541ac4b82bSMike Smith 
16551ac4b82bSMike Smith 	default:				/* other I/O error */
16561ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "I/O error - %s\n", mlx_diagnose_command(mc));
16571ac4b82bSMike Smith #if 0
16581ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "  b_bcount %ld  blkcount %ld  b_blkno %d\n",
16591ac4b82bSMike Smith 			  bp->b_bcount, bp->b_bcount / MLX_BLKSIZE, bp->b_blkno);
16601ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "  %13D\n", mc->mc_mailbox, " ");
16611ac4b82bSMike Smith #endif
16621ac4b82bSMike Smith 	    break;
16631ac4b82bSMike Smith 	}
16641ac4b82bSMike Smith     }
16651ac4b82bSMike Smith     mlx_releasecmd(mc);
16661ac4b82bSMike Smith     mlxd_intr(bp);
16671ac4b82bSMike Smith }
16681ac4b82bSMike Smith 
16691ac4b82bSMike Smith /********************************************************************************
16701ac4b82bSMike Smith  * Take a command from user-space and try to run it.
16711ac4b82bSMike Smith  */
16721ac4b82bSMike Smith static int
16731ac4b82bSMike Smith mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu)
16741ac4b82bSMike Smith {
16751ac4b82bSMike Smith     struct mlx_command	*mc;
16761ac4b82bSMike Smith     void		*kbuf;
16771ac4b82bSMike Smith     int			error;
16781ac4b82bSMike Smith 
16791ac4b82bSMike Smith     kbuf = NULL;
16801ac4b82bSMike Smith     mc = NULL;
16811ac4b82bSMike Smith     error = ENOMEM;
16821ac4b82bSMike Smith     /* get a kernel buffer for the transfer */
16831ac4b82bSMike Smith     if (mu->mu_datasize > 0) {
16841ac4b82bSMike Smith 	if ((kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK)) == NULL)
16851ac4b82bSMike Smith 	    goto out;
16861ac4b82bSMike Smith 	if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) < sizeof(u_int32_t)))) {
16871ac4b82bSMike Smith 	    error = EINVAL;
16881ac4b82bSMike Smith 	    goto out;
16891ac4b82bSMike Smith 	}
16901ac4b82bSMike Smith     }
16911ac4b82bSMike Smith     /* get ourselves a command buffer */
16921ac4b82bSMike Smith     if ((mc = mlx_alloccmd(sc)) == NULL)
16931ac4b82bSMike Smith 	goto out;
16941ac4b82bSMike Smith 
16951ac4b82bSMike Smith     /* copy the command and data */
16961ac4b82bSMike Smith     bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox));
16971ac4b82bSMike Smith     if ((mu->mu_datasize > 0) && ((error = copyin(mu->mu_buf, kbuf, mu->mu_datasize))))
16981ac4b82bSMike Smith 	goto out;
16991ac4b82bSMike Smith 
17001ac4b82bSMike Smith     /* get a command slot */
17011ac4b82bSMike Smith     if (mlx_getslot(mc))
17021ac4b82bSMike Smith 	goto out;
17031ac4b82bSMike Smith 
17041ac4b82bSMike Smith     /* map the command so the controller can see it */
17051ac4b82bSMike Smith     mc->mc_data = kbuf;
17061ac4b82bSMike Smith     mc->mc_length = mu->mu_datasize;
17071ac4b82bSMike Smith     mlx_mapcmd(mc);
17081ac4b82bSMike Smith 
17091ac4b82bSMike Smith     /* if there's a data buffer, fix up the command */
17101ac4b82bSMike Smith     if (mu->mu_datasize > 0) {
17111ac4b82bSMike Smith 	mc->mc_mailbox[mu->mu_bufptr    ] =  mc->mc_length        & 0xff;
17121ac4b82bSMike Smith 	mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_length >> 8)  & 0xff;
17131ac4b82bSMike Smith 	mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_length >> 16) & 0xff;
17141ac4b82bSMike Smith 	mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_length >> 24) & 0xff;
17151ac4b82bSMike Smith     }
17161ac4b82bSMike Smith 
17171ac4b82bSMike Smith     /* submit the command and wait */
17181ac4b82bSMike Smith     if ((error = mlx_wait_command(mc)) != 0)
17191ac4b82bSMike Smith 	goto out;
17201ac4b82bSMike Smith 
17211ac4b82bSMike Smith     /* copy out status and data */
17221ac4b82bSMike Smith     mu->mu_status = mc->mc_status;
17231ac4b82bSMike Smith     if ((mu->mu_datasize > 0) && ((error = copyout(kbuf, mu->mu_buf, mu->mu_datasize))))
17241ac4b82bSMike Smith 	goto out;
17251ac4b82bSMike Smith     error = 0;
17261ac4b82bSMike Smith 
17271ac4b82bSMike Smith  out:
17281ac4b82bSMike Smith     mlx_releasecmd(mc);
17291ac4b82bSMike Smith     if (kbuf != NULL)
17301ac4b82bSMike Smith 	free(kbuf, M_DEVBUF);
17311ac4b82bSMike Smith     return(error);
17321ac4b82bSMike Smith }
17331ac4b82bSMike Smith 
17341ac4b82bSMike Smith /********************************************************************************
17351ac4b82bSMike Smith  ********************************************************************************
17361ac4b82bSMike Smith                                                         Command I/O to Controller
17371ac4b82bSMike Smith  ********************************************************************************
17381ac4b82bSMike Smith  ********************************************************************************/
17391ac4b82bSMike Smith 
17401ac4b82bSMike Smith /********************************************************************************
17411ac4b82bSMike Smith  * Find a free command slot for (mc).
17421ac4b82bSMike Smith  *
17431ac4b82bSMike Smith  * Don't hand out a slot to a normal-priority command unless there are at least
17441ac4b82bSMike Smith  * 4 slots free for priority commands.
17451ac4b82bSMike Smith  */
17461ac4b82bSMike Smith static int
17471ac4b82bSMike Smith mlx_getslot(struct mlx_command *mc)
17481ac4b82bSMike Smith {
17491ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
17501ac4b82bSMike Smith     int			s, slot, limit;
17511ac4b82bSMike Smith 
17521ac4b82bSMike Smith     debug("called  mc %p  sc %p", mc, sc);
17531ac4b82bSMike Smith 
17541ac4b82bSMike Smith     /* enforce slot-usage limit */
17551ac4b82bSMike Smith     limit = (mc->mc_flags & MLX_CMD_PRIORITY) ? sc->mlx_maxiop : sc->mlx_maxiop - 4;
17561ac4b82bSMike Smith     if (sc->mlx_busycmds > limit)
17571ac4b82bSMike Smith 	return(EBUSY);
17581ac4b82bSMike Smith 
17591ac4b82bSMike Smith     /*
17601ac4b82bSMike Smith      * Allocate an outstanding command slot
17611ac4b82bSMike Smith      *
17621ac4b82bSMike Smith      * XXX linear search is slow
17631ac4b82bSMike Smith      */
17641ac4b82bSMike Smith     s = splbio();
17651ac4b82bSMike Smith     for (slot = 0; slot < sc->mlx_maxiop; slot++) {
17661ac4b82bSMike Smith 	debug("try slot %d", slot);
17671ac4b82bSMike Smith 	if (sc->mlx_busycmd[slot] == NULL)
17681ac4b82bSMike Smith 	    break;
17691ac4b82bSMike Smith     }
17701ac4b82bSMike Smith     if (slot < sc->mlx_maxiop) {
17711ac4b82bSMike Smith 	sc->mlx_busycmd[slot] = mc;
17721ac4b82bSMike Smith 	sc->mlx_busycmds++;
17731ac4b82bSMike Smith     }
17741ac4b82bSMike Smith     splx(s);
17751ac4b82bSMike Smith 
17761ac4b82bSMike Smith     /* out of slots? */
17771ac4b82bSMike Smith     if (slot >= sc->mlx_maxiop)
17781ac4b82bSMike Smith 	return(EBUSY);
17791ac4b82bSMike Smith 
17801ac4b82bSMike Smith     debug("got slot %d", slot);
17811ac4b82bSMike Smith     mc->mc_slot = slot;
17821ac4b82bSMike Smith     return(0);
17831ac4b82bSMike Smith }
17841ac4b82bSMike Smith 
17851ac4b82bSMike Smith /********************************************************************************
17861ac4b82bSMike Smith  * Map/unmap (mc)'s data in the controller's addressable space.
17871ac4b82bSMike Smith  */
17881ac4b82bSMike Smith static void
17891ac4b82bSMike Smith mlx_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
17901ac4b82bSMike Smith {
17911ac4b82bSMike Smith     struct mlx_command	*mc = (struct mlx_command *)arg;
17921ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
17931ac4b82bSMike Smith     struct mlx_sgentry	*sg;
17941ac4b82bSMike Smith     int			i;
17951ac4b82bSMike Smith 
17961ac4b82bSMike Smith     debug("called");
17971ac4b82bSMike Smith 
17981ac4b82bSMike Smith     /* get base address of s/g table */
17991ac4b82bSMike Smith     sg = sc->mlx_sgtable + (mc->mc_slot * MLX_NSEG);
18001ac4b82bSMike Smith 
18011ac4b82bSMike Smith     /* save s/g table information in command */
18021ac4b82bSMike Smith     mc->mc_nsgent = nsegments;
18031ac4b82bSMike Smith     mc->mc_sgphys = sc->mlx_sgbusaddr + (mc->mc_slot * MLX_NSEG * sizeof(struct mlx_sgentry));
18041ac4b82bSMike Smith     mc->mc_dataphys = segs[0].ds_addr;
18051ac4b82bSMike Smith 
18061ac4b82bSMike Smith     /* populate s/g table */
18071ac4b82bSMike Smith     for (i = 0; i < nsegments; i++, sg++) {
18081ac4b82bSMike Smith 	sg->sg_addr = segs[i].ds_addr;
18091ac4b82bSMike Smith 	sg->sg_count = segs[i].ds_len;
18101ac4b82bSMike Smith     }
18111ac4b82bSMike Smith }
18121ac4b82bSMike Smith 
18131ac4b82bSMike Smith static void
18141ac4b82bSMike Smith mlx_mapcmd(struct mlx_command *mc)
18151ac4b82bSMike Smith {
18161ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
18171ac4b82bSMike Smith 
18181ac4b82bSMike Smith     debug("called");
18191ac4b82bSMike Smith 
18201ac4b82bSMike Smith     /* if the command involves data at all */
18211ac4b82bSMike Smith     if (mc->mc_data != NULL) {
18221ac4b82bSMike Smith 
18231ac4b82bSMike Smith 	/* map the data buffer into bus space and build the s/g list */
18241ac4b82bSMike Smith 	bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, mc->mc_length,
18251ac4b82bSMike Smith 			mlx_setup_dmamap, mc, 0);
18261ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAIN)
18271ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREREAD);
18281ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAOUT)
18291ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_PREWRITE);
18301ac4b82bSMike Smith     }
18311ac4b82bSMike Smith }
18321ac4b82bSMike Smith 
18331ac4b82bSMike Smith static void
18341ac4b82bSMike Smith mlx_unmapcmd(struct mlx_command *mc)
18351ac4b82bSMike Smith {
18361ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
18371ac4b82bSMike Smith 
18381ac4b82bSMike Smith     debug("called");
18391ac4b82bSMike Smith 
18401ac4b82bSMike Smith     /* if the command involved data at all */
18411ac4b82bSMike Smith     if (mc->mc_data != NULL) {
18421ac4b82bSMike Smith 
18431ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAIN)
18441ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTREAD);
18451ac4b82bSMike Smith 	if (mc->mc_flags & MLX_CMD_DATAOUT)
18461ac4b82bSMike Smith 	    bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTWRITE);
18471ac4b82bSMike Smith 
18481ac4b82bSMike Smith 	bus_dmamap_unload(sc->mlx_buffer_dmat, mc->mc_dmamap);
18491ac4b82bSMike Smith     }
18501ac4b82bSMike Smith }
18511ac4b82bSMike Smith 
18521ac4b82bSMike Smith /********************************************************************************
18531ac4b82bSMike Smith  * Try to deliver (mc) to the controller.  Take care of any completed commands
18541ac4b82bSMike Smith  * that we encounter while doing so.
18551ac4b82bSMike Smith  *
18561ac4b82bSMike Smith  * Can be called at any interrupt level, with or without interrupts enabled.
18571ac4b82bSMike Smith  */
18581ac4b82bSMike Smith static int
18591ac4b82bSMike Smith mlx_start(struct mlx_command *mc)
18601ac4b82bSMike Smith {
18611ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
18621ac4b82bSMike Smith     int			i, s, done, worked;
18631ac4b82bSMike Smith 
18641ac4b82bSMike Smith     debug("called");
18651ac4b82bSMike Smith 
18661ac4b82bSMike Smith     /* save the slot number as ident so we can handle this command when complete */
18671ac4b82bSMike Smith     mc->mc_mailbox[0x1] = mc->mc_slot;
18681ac4b82bSMike Smith 
18691ac4b82bSMike Smith     /* set impossible status so that a woken sleeper can tell the command is in progress */
18701ac4b82bSMike Smith     mc->mc_status = MLX_STATUS_BUSY;
18711ac4b82bSMike Smith 
18721ac4b82bSMike Smith     /* assume we don't collect any completed commands */
18731ac4b82bSMike Smith     worked = 0;
18741ac4b82bSMike Smith 
18751ac4b82bSMike Smith     /* spin waiting for the mailbox */
18761ac4b82bSMike Smith     for (i = 100000, done = 0; (i > 0) && !done; i--) {
18771ac4b82bSMike Smith 	s = splbio();
18781ac4b82bSMike Smith 	done = sc->mlx_tryqueue(sc, mc);
18791ac4b82bSMike Smith 	splx(s);
18801ac4b82bSMike Smith 	/* check for command completion while we're at it */
18811ac4b82bSMike Smith 	if (mlx_done(sc))
18821ac4b82bSMike Smith 	    worked = 1;
18831ac4b82bSMike Smith     }
18841ac4b82bSMike Smith     /* check to see if we picked up any completed commands */
18851ac4b82bSMike Smith     if (worked)
18861ac4b82bSMike Smith 	mlx_complete(sc);
18871ac4b82bSMike Smith 
18881ac4b82bSMike Smith     /* command is enqueued */
18891ac4b82bSMike Smith     if (done)
18901ac4b82bSMike Smith 	return(0);
18911ac4b82bSMike Smith 
18921ac4b82bSMike Smith     /*
18931ac4b82bSMike Smith      * We couldn't get the controller to take the command.  Revoke the slot
18941ac4b82bSMike Smith      * that the command was given and return it with a bad status.
18951ac4b82bSMike Smith      */
18961ac4b82bSMike Smith     sc->mlx_busycmd[mc->mc_slot] = NULL;
18971ac4b82bSMike Smith     device_printf(sc->mlx_dev, "controller wedged (not taking commands)\n");
18981ac4b82bSMike Smith     mc->mc_status = MLX_STATUS_WEDGED;
18991ac4b82bSMike Smith     return(EIO);
19001ac4b82bSMike Smith }
19011ac4b82bSMike Smith 
19021ac4b82bSMike Smith /********************************************************************************
19031ac4b82bSMike Smith  * Look at the controller (sc) and see if a command has been completed.
19041ac4b82bSMike Smith  * If so, move the command buffer to the done queue for later collection
19051ac4b82bSMike Smith  * and free the slot for immediate reuse.
19061ac4b82bSMike Smith  *
19071ac4b82bSMike Smith  * Returns nonzero if anything was added to the done queue.
19081ac4b82bSMike Smith  */
19091ac4b82bSMike Smith static int
19101ac4b82bSMike Smith mlx_done(struct mlx_softc *sc)
19111ac4b82bSMike Smith {
19121ac4b82bSMike Smith     struct mlx_command	*mc;
19131ac4b82bSMike Smith     int			s;
19141ac4b82bSMike Smith     u_int8_t		slot;
19151ac4b82bSMike Smith     u_int16_t		status;
19161ac4b82bSMike Smith 
19171ac4b82bSMike Smith     debug("called");
19181ac4b82bSMike Smith 
19191ac4b82bSMike Smith     s = splbio();
19201ac4b82bSMike Smith     mc = NULL;
19211ac4b82bSMike Smith     slot = 0;
19221ac4b82bSMike Smith 
19231ac4b82bSMike Smith     /* poll for a completed command's identifier and status */
19241ac4b82bSMike Smith     if (sc->mlx_findcomplete(sc, &slot, &status)) {
19251ac4b82bSMike Smith 	mc = sc->mlx_busycmd[slot];		/* find command */
19261ac4b82bSMike Smith 	if (mc != NULL) {			/* paranoia */
19271ac4b82bSMike Smith 	    if (mc->mc_status == MLX_STATUS_BUSY) {
19281ac4b82bSMike Smith 		mc->mc_status = status;			/* save status */
19291ac4b82bSMike Smith 
19301ac4b82bSMike Smith 		/* move completed command to 'done' queue */
19311ac4b82bSMike Smith 		TAILQ_INSERT_TAIL(&sc->mlx_donecmd, mc, mc_link);
19321ac4b82bSMike Smith 
19331ac4b82bSMike Smith 		/* free slot for reuse */
19341ac4b82bSMike Smith 		sc->mlx_busycmd[slot] = NULL;
19351ac4b82bSMike Smith 		sc->mlx_busycmds--;
19361ac4b82bSMike Smith 	    } else {
19371ac4b82bSMike Smith 		device_printf(sc->mlx_dev, "duplicate done event for slot %d\n", slot);
19381ac4b82bSMike Smith 		mc = NULL;
19391ac4b82bSMike Smith 	    }
19401ac4b82bSMike Smith 	} else {
19411ac4b82bSMike Smith 	    device_printf(sc->mlx_dev, "done event for nonbusy slot %d\n", slot);
19421ac4b82bSMike Smith 	}
19431ac4b82bSMike Smith     }
19441ac4b82bSMike Smith     splx(s);
19451ac4b82bSMike Smith 
19461ac4b82bSMike Smith     if (mc != NULL) {
19471ac4b82bSMike Smith 	/* unmap the command's data buffer */
19481ac4b82bSMike Smith 	mlx_unmapcmd(mc);
19491ac4b82bSMike Smith 	return(1);
19501ac4b82bSMike Smith     }
19511ac4b82bSMike Smith     return(0);
19521ac4b82bSMike Smith }
19531ac4b82bSMike Smith 
19541ac4b82bSMike Smith /********************************************************************************
19551ac4b82bSMike Smith  * Handle completion for all commands on (sc)'s done queue.
19561ac4b82bSMike Smith  */
19571ac4b82bSMike Smith static void
19581ac4b82bSMike Smith mlx_complete(struct mlx_softc *sc)
19591ac4b82bSMike Smith {
19601ac4b82bSMike Smith     struct mlx_command	*mc, *nc;
19611ac4b82bSMike Smith     int			s, count;
19621ac4b82bSMike Smith 
19631ac4b82bSMike Smith     debug("called");
19641ac4b82bSMike Smith 
19651ac4b82bSMike Smith     s = splbio();
19661ac4b82bSMike Smith     count = 0;
19671ac4b82bSMike Smith 
19681ac4b82bSMike Smith     /* scan the list of done commands */
19691ac4b82bSMike Smith     mc = TAILQ_FIRST(&sc->mlx_donecmd);
19701ac4b82bSMike Smith     while (mc != NULL) {
19711ac4b82bSMike Smith 	nc = TAILQ_NEXT(mc, mc_link);
19721ac4b82bSMike Smith 
19731ac4b82bSMike Smith 	/* XXX this is slightly bogus */
19741ac4b82bSMike Smith 	if (count++ > (sc->mlx_maxiop * 2))
19751ac4b82bSMike Smith 	    panic("mlx_donecmd list corrupt!");
19761ac4b82bSMike Smith 
19771ac4b82bSMike Smith 	/*
19781ac4b82bSMike Smith 	 * Does the command have a completion handler?
19791ac4b82bSMike Smith 	 */
19801ac4b82bSMike Smith 	if (mc->mc_complete != NULL) {
19811ac4b82bSMike Smith 	    /* remove from list and give to handler */
19821ac4b82bSMike Smith 	    TAILQ_REMOVE(&sc->mlx_donecmd, mc, mc_link);
19831ac4b82bSMike Smith 	    mc->mc_complete(mc);
19841ac4b82bSMike Smith 
19851ac4b82bSMike Smith 	    /*
19861ac4b82bSMike Smith 	     * Is there a sleeper waiting on this command?
19871ac4b82bSMike Smith 	     */
19881ac4b82bSMike Smith 	} else if (mc->mc_private != NULL) {	/* sleeping caller wants to know about it */
19891ac4b82bSMike Smith 
19901ac4b82bSMike Smith 	    /* remove from list and wake up sleeper */
19911ac4b82bSMike Smith 	    TAILQ_REMOVE(&sc->mlx_donecmd, mc, mc_link);
19921ac4b82bSMike Smith 	    wakeup_one(mc->mc_private);
19931ac4b82bSMike Smith 
19941ac4b82bSMike Smith 	    /*
19951ac4b82bSMike Smith 	     * Leave the command for a caller that's polling for it.
19961ac4b82bSMike Smith 	     */
19971ac4b82bSMike Smith 	} else {
19981ac4b82bSMike Smith 	}
19991ac4b82bSMike Smith 	mc = nc;
20001ac4b82bSMike Smith     }
20011ac4b82bSMike Smith     splx(s);
20021ac4b82bSMike Smith 
20031ac4b82bSMike Smith     /* queue some more work if there is any */
20041ac4b82bSMike Smith     mlx_startio(sc);
20051ac4b82bSMike Smith }
20061ac4b82bSMike Smith 
20071ac4b82bSMike Smith /********************************************************************************
20081ac4b82bSMike Smith  ********************************************************************************
20091ac4b82bSMike Smith                                                         Command Buffer Management
20101ac4b82bSMike Smith  ********************************************************************************
20111ac4b82bSMike Smith  ********************************************************************************/
20121ac4b82bSMike Smith 
20131ac4b82bSMike Smith /********************************************************************************
20141ac4b82bSMike Smith  * Get a new command buffer.
20151ac4b82bSMike Smith  *
20161ac4b82bSMike Smith  * This may return NULL in low-memory cases.
20171ac4b82bSMike Smith  *
20181ac4b82bSMike Smith  * Note that using malloc() is expensive (the command buffer is << 1 page) but
20191ac4b82bSMike Smith  * necessary if we are to be a loadable module before the zone allocator is fixed.
20201ac4b82bSMike Smith  *
20211ac4b82bSMike Smith  * If possible, we recycle a command buffer that's been used before.
20221ac4b82bSMike Smith  *
20231ac4b82bSMike Smith  * XXX Note that command buffers are not cleaned out - it is the caller's
20241ac4b82bSMike Smith  *     responsibility to ensure that all required fields are filled in before
20251ac4b82bSMike Smith  *     using a buffer.
20261ac4b82bSMike Smith  */
20271ac4b82bSMike Smith static struct mlx_command *
20281ac4b82bSMike Smith mlx_alloccmd(struct mlx_softc *sc)
20291ac4b82bSMike Smith {
20301ac4b82bSMike Smith     struct mlx_command	*mc;
20311ac4b82bSMike Smith     int			error;
20321ac4b82bSMike Smith     int			s;
20331ac4b82bSMike Smith 
20341ac4b82bSMike Smith     debug("called");
20351ac4b82bSMike Smith 
20361ac4b82bSMike Smith     s = splbio();
20371ac4b82bSMike Smith     if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL)
20381ac4b82bSMike Smith 	TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
20391ac4b82bSMike Smith     splx(s);
20401ac4b82bSMike Smith 
20411ac4b82bSMike Smith     /* allocate a new command buffer? */
20421ac4b82bSMike Smith     if (mc == NULL) {
20431ac4b82bSMike Smith 	mc = (struct mlx_command *)malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT);
20441ac4b82bSMike Smith 	if (mc != NULL) {
20451ac4b82bSMike Smith 	    bzero(mc, sizeof(*mc));
20461ac4b82bSMike Smith 	    mc->mc_sc = sc;
20471ac4b82bSMike Smith 	    error = bus_dmamap_create(sc->mlx_buffer_dmat, 0, &mc->mc_dmamap);
20481ac4b82bSMike Smith 	    if (error) {
20491ac4b82bSMike Smith 		free(mc, M_DEVBUF);
20501ac4b82bSMike Smith 		return(NULL);
20511ac4b82bSMike Smith 	    }
20521ac4b82bSMike Smith 	}
20531ac4b82bSMike Smith     }
20541ac4b82bSMike Smith     return(mc);
20551ac4b82bSMike Smith }
20561ac4b82bSMike Smith 
20571ac4b82bSMike Smith /********************************************************************************
20581ac4b82bSMike Smith  * Release a command buffer for recycling.
20591ac4b82bSMike Smith  *
20601ac4b82bSMike Smith  * XXX It might be a good idea to limit the number of commands we save for reuse
20611ac4b82bSMike Smith  *     if it's shown that this list bloats out massively.
20621ac4b82bSMike Smith  */
20631ac4b82bSMike Smith static void
20641ac4b82bSMike Smith mlx_releasecmd(struct mlx_command *mc)
20651ac4b82bSMike Smith {
20661ac4b82bSMike Smith     int		s;
20671ac4b82bSMike Smith 
20681ac4b82bSMike Smith     debug("called");
20691ac4b82bSMike Smith 
20701ac4b82bSMike Smith     s = splbio();
20711ac4b82bSMike Smith     TAILQ_INSERT_HEAD(&mc->mc_sc->mlx_freecmds, mc, mc_link);
20721ac4b82bSMike Smith     splx(s);
20731ac4b82bSMike Smith }
20741ac4b82bSMike Smith 
20751ac4b82bSMike Smith /********************************************************************************
20761ac4b82bSMike Smith  * Permanently discard a command buffer.
20771ac4b82bSMike Smith  */
20781ac4b82bSMike Smith static void
20791ac4b82bSMike Smith mlx_freecmd(struct mlx_command *mc)
20801ac4b82bSMike Smith {
20811ac4b82bSMike Smith     struct mlx_softc	*sc = mc->mc_sc;
20821ac4b82bSMike Smith 
20831ac4b82bSMike Smith     debug("called");
20841ac4b82bSMike Smith 
20851ac4b82bSMike Smith     bus_dmamap_destroy(sc->mlx_buffer_dmat, mc->mc_dmamap);
20861ac4b82bSMike Smith     free(mc, M_DEVBUF);
20871ac4b82bSMike Smith }
20881ac4b82bSMike Smith 
20891ac4b82bSMike Smith 
20901ac4b82bSMike Smith /********************************************************************************
20911ac4b82bSMike Smith  ********************************************************************************
20921ac4b82bSMike Smith                                                 Type 3 interface accessor methods
20931ac4b82bSMike Smith  ********************************************************************************
20941ac4b82bSMike Smith  ********************************************************************************/
20951ac4b82bSMike Smith 
20961ac4b82bSMike Smith /********************************************************************************
20971ac4b82bSMike Smith  * Try to give (mc) to the controller.  Returns 1 if successful, 0 on failure
20981ac4b82bSMike Smith  * (the controller is not ready to take a command).
20991ac4b82bSMike Smith  *
21001ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
21011ac4b82bSMike Smith  */
21021ac4b82bSMike Smith static int
21031ac4b82bSMike Smith mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
21041ac4b82bSMike Smith {
21051ac4b82bSMike Smith     int		i;
21061ac4b82bSMike Smith 
21071ac4b82bSMike Smith     debug("called");
21081ac4b82bSMike Smith 
21091ac4b82bSMike Smith     /* ready for our command? */
21101ac4b82bSMike Smith     if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_FULL)) {
21111ac4b82bSMike Smith 	/* copy mailbox data to window */
21121ac4b82bSMike Smith 	for (i = 0; i < 13; i++)
21131ac4b82bSMike Smith 	    MLX_V3_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
21141ac4b82bSMike Smith 
21151ac4b82bSMike Smith 	/* post command */
21161ac4b82bSMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_GET_IDBR(sc) | MLX_V3_IDB_FULL);
21171ac4b82bSMike Smith 	return(1);
21181ac4b82bSMike Smith     }
21191ac4b82bSMike Smith     return(0);
21201ac4b82bSMike Smith }
21211ac4b82bSMike Smith 
21221ac4b82bSMike Smith /********************************************************************************
21231ac4b82bSMike Smith  * See if a command has been completed, if so acknowledge its completion
21241ac4b82bSMike Smith  * and recover the slot number and status code.
21251ac4b82bSMike Smith  *
21261ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
21271ac4b82bSMike Smith  */
21281ac4b82bSMike Smith static int
21291ac4b82bSMike Smith mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
21301ac4b82bSMike Smith {
21311ac4b82bSMike Smith 
21321ac4b82bSMike Smith     debug("called");
21331ac4b82bSMike Smith 
21341ac4b82bSMike Smith     /* status available? */
21351ac4b82bSMike Smith     if (MLX_V3_GET_ODBR(sc) & MLX_V3_ODB_SAVAIL) {
21361ac4b82bSMike Smith 	*slot = MLX_V3_GET_STATUS_IDENT(sc);		/* get command identifier */
21371ac4b82bSMike Smith 	*status = MLX_V3_GET_STATUS(sc);		/* get status */
21381ac4b82bSMike Smith 
21391ac4b82bSMike Smith 	/* acknowledge completion */
21401ac4b82bSMike Smith 	MLX_V3_PUT_ODBR(sc, MLX_V3_GET_ODBR(sc) | MLX_V3_ODB_SAVAIL);
21411ac4b82bSMike Smith 	MLX_V3_PUT_IDBR(sc, MLX_V3_GET_IDBR(sc) | MLX_V3_IDB_SACK);
21421ac4b82bSMike Smith 	return(1);
21431ac4b82bSMike Smith     }
21441ac4b82bSMike Smith     return(0);
21451ac4b82bSMike Smith }
21461ac4b82bSMike Smith 
21471ac4b82bSMike Smith /********************************************************************************
21481ac4b82bSMike Smith  * Enable/disable interrupts as requested. (No acknowledge required)
21491ac4b82bSMike Smith  *
21501ac4b82bSMike Smith  * Must be called at splbio or in a fashion that prevents reentry.
21511ac4b82bSMike Smith  */
21521ac4b82bSMike Smith static void
21531ac4b82bSMike Smith mlx_v3_intaction(struct mlx_softc *sc, int action)
21541ac4b82bSMike Smith {
21551ac4b82bSMike Smith     debug("called");
21561ac4b82bSMike Smith 
21571ac4b82bSMike Smith     switch(action) {
21581ac4b82bSMike Smith     case MLX_INTACTION_DISABLE:
21591ac4b82bSMike Smith 	MLX_V3_PUT_IER(sc, 0);
21601ac4b82bSMike Smith 	sc->mlx_state &= ~MLX_STATE_INTEN;
21611ac4b82bSMike Smith 	break;
21621ac4b82bSMike Smith     case MLX_INTACTION_ENABLE:
21631ac4b82bSMike Smith 	MLX_V3_PUT_IER(sc, 1);
21641ac4b82bSMike Smith 	sc->mlx_state |= MLX_STATE_INTEN;
21651ac4b82bSMike Smith 	break;
21661ac4b82bSMike Smith     }
21671ac4b82bSMike Smith }
21681ac4b82bSMike Smith 
21691ac4b82bSMike Smith 
21701ac4b82bSMike Smith /********************************************************************************
21711ac4b82bSMike Smith  ********************************************************************************
21721ac4b82bSMike Smith                                                                         Debugging
21731ac4b82bSMike Smith  ********************************************************************************
21741ac4b82bSMike Smith  ********************************************************************************/
21751ac4b82bSMike Smith 
21761ac4b82bSMike Smith /********************************************************************************
21771ac4b82bSMike Smith  * Return a status message describing (mc)
21781ac4b82bSMike Smith  */
21791ac4b82bSMike Smith static char *mlx_status_messages[] = {
21801ac4b82bSMike Smith     "normal completion",			/* 00 */
21811ac4b82bSMike Smith     "irrecoverable data error",			/* 01 */
21821ac4b82bSMike Smith     "drive does not exist, or is offline",	/* 02 */
21831ac4b82bSMike Smith     "attempt to write beyond end of drive",	/* 03 */
21841ac4b82bSMike Smith     "bad data encountered",			/* 04 */
21851ac4b82bSMike Smith     "invalid log entry request",		/* 05 */
21861ac4b82bSMike Smith     "attempt to rebuild online drive",		/* 06 */
21871ac4b82bSMike Smith     "new disk failed during rebuild",		/* 07 */
21881ac4b82bSMike Smith     "invalid channel/target",			/* 08 */
21891ac4b82bSMike Smith     "rebuild/check already in progress",	/* 09 */
21901ac4b82bSMike Smith     "one or more disks are dead",		/* 10 */
21911ac4b82bSMike Smith     "invalid or non-redundant drive",		/* 11 */
21921ac4b82bSMike Smith     "channel is busy",				/* 12 */
21931ac4b82bSMike Smith     "channel is not stopped",			/* 13 */
21941ac4b82bSMike Smith };
21951ac4b82bSMike Smith 
21961ac4b82bSMike Smith static struct
21971ac4b82bSMike Smith {
21981ac4b82bSMike Smith     int		command;
21991ac4b82bSMike Smith     u_int16_t	status;
22001ac4b82bSMike Smith     int		msg;
22011ac4b82bSMike Smith } mlx_messages[] = {
22021ac4b82bSMike Smith     {MLX_CMD_READOLDSG,		0x0001,	 1},
22031ac4b82bSMike Smith     {MLX_CMD_READOLDSG,		0x0002,	 1},
22041ac4b82bSMike Smith     {MLX_CMD_READOLDSG,		0x0105,	 3},
22051ac4b82bSMike Smith     {MLX_CMD_READOLDSG,		0x010c,	 4},
22061ac4b82bSMike Smith     {MLX_CMD_WRITEOLDSG,	0x0001,	 1},
22071ac4b82bSMike Smith     {MLX_CMD_WRITEOLDSG,	0x0002,	 1},
22081ac4b82bSMike Smith     {MLX_CMD_WRITEOLDSG,	0x0105,	 3},
22091ac4b82bSMike Smith     {MLX_CMD_LOGOP,		0x0105,	 5},
22101ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0002,  6},
22111ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0004,  7},
22121ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0105,  8},
22131ac4b82bSMike Smith     {MLX_CMD_REBUILDASYNC,	0x0106,  9},
22141ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0002, 10},
22151ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0105, 11},
22161ac4b82bSMike Smith     {MLX_CMD_CHECKASYNC,	0x0106,  9},
22171ac4b82bSMike Smith     {MLX_CMD_STOPCHANNEL,	0x0106, 12},
22181ac4b82bSMike Smith     {MLX_CMD_STOPCHANNEL,	0x0105,  8},
22191ac4b82bSMike Smith     {MLX_CMD_STARTCHANNEL,	0x0005, 13},
22201ac4b82bSMike Smith     {MLX_CMD_STARTCHANNEL,	0x0105,  8},
22211ac4b82bSMike Smith     {-1, 0, 0}
22221ac4b82bSMike Smith };
22231ac4b82bSMike Smith 
22241ac4b82bSMike Smith static char *
22251ac4b82bSMike Smith mlx_diagnose_command(struct mlx_command *mc)
22261ac4b82bSMike Smith {
22271ac4b82bSMike Smith     static char	unkmsg[80];
22281ac4b82bSMike Smith     int		i;
22291ac4b82bSMike Smith 
22301ac4b82bSMike Smith     /* look up message in table */
22311ac4b82bSMike Smith     for (i = 0; mlx_messages[i].command != -1; i++)
22321ac4b82bSMike Smith 	if ((mc->mc_mailbox[0] == mlx_messages[i].command) &&
22331ac4b82bSMike Smith 	    (mc->mc_status == mlx_messages[i].command))
22341ac4b82bSMike Smith 	    return(mlx_status_messages[mlx_messages[i].msg]);
22351ac4b82bSMike Smith 
22361ac4b82bSMike Smith     sprintf(unkmsg, "unknown response 0x%x for command 0x%x", (int)mc->mc_status, (int)mc->mc_mailbox[0]);
22371ac4b82bSMike Smith     return(unkmsg);
22381ac4b82bSMike Smith }
22391ac4b82bSMike Smith 
22401ac4b82bSMike Smith /*******************************************************************************
22411ac4b82bSMike Smith  * Return a string describing the controller (hwid)
22421ac4b82bSMike Smith  */
22431ac4b82bSMike Smith static char *
22441ac4b82bSMike Smith mlx_name_controller(u_int32_t hwid)
22451ac4b82bSMike Smith {
22461ac4b82bSMike Smith     static char		buf[80];
22471ac4b82bSMike Smith     char		smbuf[16];
22481ac4b82bSMike Smith     char		*submodel;
22491ac4b82bSMike Smith     int			nchn;
22501ac4b82bSMike Smith 
22511ac4b82bSMike Smith     switch(hwid & 0xff) {
22521ac4b82bSMike Smith     case 0x01:
22531ac4b82bSMike Smith 	submodel = "P/PD";
22541ac4b82bSMike Smith 	break;
22551ac4b82bSMike Smith     case 0x02:
22561ac4b82bSMike Smith 	submodel = "PL";
22571ac4b82bSMike Smith 	break;
22581ac4b82bSMike Smith     case 0x10:
22591ac4b82bSMike Smith 	submodel = "PG";
22601ac4b82bSMike Smith 	break;
22611ac4b82bSMike Smith     default:
22621ac4b82bSMike Smith 	sprintf(smbuf, " model 0x%x", hwid & 0xff);
22631ac4b82bSMike Smith 	submodel = smbuf;
22641ac4b82bSMike Smith 	break;
22651ac4b82bSMike Smith     }
22661ac4b82bSMike Smith     nchn = (hwid >> 8) & 0xff;
22671ac4b82bSMike Smith     sprintf(buf, "DAC960%s, %d channel%s", submodel, nchn, nchn > 1 ? "s" : "");
22681ac4b82bSMike Smith     return(buf);
22691ac4b82bSMike Smith }
22701ac4b82bSMike Smith 
22711ac4b82bSMike Smith /********************************************************************************
22721ac4b82bSMike Smith  ********************************************************************************
22731ac4b82bSMike Smith                                                                 Utility Functions
22741ac4b82bSMike Smith  ********************************************************************************
22751ac4b82bSMike Smith  ********************************************************************************/
22761ac4b82bSMike Smith 
22771ac4b82bSMike Smith /********************************************************************************
22781ac4b82bSMike Smith  * Find the disk whose unit number is (unit) on this controller
22791ac4b82bSMike Smith  */
22801ac4b82bSMike Smith static struct mlx_sysdrive *
22811ac4b82bSMike Smith mlx_findunit(struct mlx_softc *sc, int unit)
22821ac4b82bSMike Smith {
22831ac4b82bSMike Smith     int		i;
22841ac4b82bSMike Smith 
22851ac4b82bSMike Smith     /* search system drives */
22861ac4b82bSMike Smith     for (i = 0; i < MLX_MAXDRIVES; i++) {
22871ac4b82bSMike Smith 	/* is this one attached? */
22881ac4b82bSMike Smith 	if (sc->mlx_sysdrive[i].ms_disk != 0) {
22891ac4b82bSMike Smith 	    /* is this the one? */
22901ac4b82bSMike Smith 	    if (unit == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
22911ac4b82bSMike Smith 		return(&sc->mlx_sysdrive[i]);
22921ac4b82bSMike Smith 	}
22931ac4b82bSMike Smith     }
22941ac4b82bSMike Smith     return(NULL);
22951ac4b82bSMike Smith }
2296