#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bio.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/kernel.h>
#include <sys/sx.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/stat.h>
#include <machine/resource.h>
#include <machine/bus.h>
#include <machine/clock.h>
#include <sys/rman.h>
#include <geom/geom_disk.h>
#include <dev/mlx/mlxio.h>
#include <dev/mlx/mlxvar.h>
#include <dev/mlx/mlxreg.h>
static struct cdevsw mlx_cdevsw = {
.d_version = D_VERSION,
.d_open = mlx_open,
.d_close = mlx_close,
.d_ioctl = mlx_ioctl,
.d_name = "mlx",
};
static int mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
static int mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
static void mlx_v3_intaction(struct mlx_softc *sc, int action);
static int mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2, int first);
static int mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
static int mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
static void mlx_v4_intaction(struct mlx_softc *sc, int action);
static int mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2, int first);
static int mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc);
static int mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
static void mlx_v5_intaction(struct mlx_softc *sc, int action);
static int mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2, int first);
static void mlx_periodic(void *data);
static void mlx_periodic_enquiry(struct mlx_command *mc);
static void mlx_periodic_eventlog_poll(struct mlx_softc *sc);
static void mlx_periodic_eventlog_respond(struct mlx_command *mc);
static void mlx_periodic_rebuild(struct mlx_command *mc);
static void mlx_pause_action(struct mlx_softc *sc);
static void mlx_pause_done(struct mlx_command *mc);
static void *mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize,
void (*complete)(struct mlx_command *mc));
static int mlx_flush(struct mlx_softc *sc);
static int mlx_check(struct mlx_softc *sc, int drive);
static int mlx_rebuild(struct mlx_softc *sc, int channel, int target);
static int mlx_wait_command(struct mlx_command *mc);
static int mlx_poll_command(struct mlx_command *mc);
void mlx_startio_cb(void *arg,
bus_dma_segment_t *segs,
int nsegments, int error);
static void mlx_startio(struct mlx_softc *sc);
static void mlx_completeio(struct mlx_command *mc);
static int mlx_user_command(struct mlx_softc *sc,
struct mlx_usercommand *mu);
void mlx_user_cb(void *arg, bus_dma_segment_t *segs,
int nsegments, int error);
static struct mlx_command *mlx_alloccmd(struct mlx_softc *sc);
static void mlx_releasecmd(struct mlx_command *mc);
static void mlx_freecmd(struct mlx_command *mc);
static int mlx_getslot(struct mlx_command *mc);
static void mlx_setup_dmamap(struct mlx_command *mc,
bus_dma_segment_t *segs,
int nsegments, int error);
static void mlx_unmapcmd(struct mlx_command *mc);
static int mlx_shutdown_locked(struct mlx_softc *sc);
static int mlx_start(struct mlx_command *mc);
static int mlx_done(struct mlx_softc *sc, int startio);
static void mlx_complete(struct mlx_softc *sc);
static char *mlx_diagnose_command(struct mlx_command *mc);
static void mlx_describe_controller(struct mlx_softc *sc);
static int mlx_fw_message(struct mlx_softc *sc, int status, int param1, int param2);
static struct mlx_sysdrive *mlx_findunit(struct mlx_softc *sc, int unit);
void
mlx_free(struct mlx_softc *sc)
{
struct mlx_command *mc;
debug_called(1);
if (sc->mlx_dev_t != NULL)
destroy_dev(sc->mlx_dev_t);
if (sc->mlx_intr)
bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr);
MLX_IO_LOCK(sc);
callout_stop(&sc->mlx_timeout);
while ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) {
TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
mlx_freecmd(mc);
}
MLX_IO_UNLOCK(sc);
callout_drain(&sc->mlx_timeout);
if (sc->mlx_buffer_dmat)
bus_dma_tag_destroy(sc->mlx_buffer_dmat);
if (sc->mlx_sgbusaddr)
bus_dmamap_unload(sc->mlx_sg_dmat, sc->mlx_sg_dmamap);
if (sc->mlx_sgtable)
bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
if (sc->mlx_sg_dmat)
bus_dma_tag_destroy(sc->mlx_sg_dmat);
if (sc->mlx_irq != NULL)
bus_release_resource(sc->mlx_dev, SYS_RES_IRQ, 0, sc->mlx_irq);
if (sc->mlx_parent_dmat)
bus_dma_tag_destroy(sc->mlx_parent_dmat);
if (sc->mlx_mem != NULL)
bus_release_resource(sc->mlx_dev, sc->mlx_mem_type, sc->mlx_mem_rid, sc->mlx_mem);
if (sc->mlx_enq2 != NULL)
free(sc->mlx_enq2, M_DEVBUF);
sx_destroy(&sc->mlx_config_lock);
mtx_destroy(&sc->mlx_io_lock);
}
static void
mlx_dma_map_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
struct mlx_softc *sc = (struct mlx_softc *)arg;
debug_called(1);
sc->mlx_sgbusaddr = segs->ds_addr;
}
static int
mlx_sglist_map(struct mlx_softc *sc)
{
size_t segsize;
int error, ncmd;
debug_called(1);
if (sc->mlx_sgbusaddr)
bus_dmamap_unload(sc->mlx_sg_dmat, sc->mlx_sg_dmamap);
if (sc->mlx_sgtable)
bus_dmamem_free(sc->mlx_sg_dmat, sc->mlx_sgtable, sc->mlx_sg_dmamap);
if (sc->mlx_sg_dmat)
bus_dma_tag_destroy(sc->mlx_sg_dmat);
sc->mlx_sgbusaddr = 0;
sc->mlx_sgtable = NULL;
sc->mlx_sg_dmat = NULL;
if (sc->mlx_enq2 == NULL) {
ncmd = 2;
} else {
ncmd = sc->mlx_enq2->me_max_commands;
}
segsize = sizeof(struct mlx_sgentry) * MLX_NSEG * ncmd;
error = bus_dma_tag_create(sc->mlx_parent_dmat,
1, 0,
BUS_SPACE_MAXADDR,
BUS_SPACE_MAXADDR,
NULL, NULL,
segsize, 1,
BUS_SPACE_MAXSIZE_32BIT,
0,
NULL, NULL,
&sc->mlx_sg_dmat);
if (error != 0) {
device_printf(sc->mlx_dev, "can't allocate scatter/gather DMA tag\n");
return(ENOMEM);
}
error = bus_dmamem_alloc(sc->mlx_sg_dmat, (void **)&sc->mlx_sgtable,
BUS_DMA_NOWAIT, &sc->mlx_sg_dmamap);
if (error) {
device_printf(sc->mlx_dev, "can't allocate s/g table\n");
return(ENOMEM);
}
(void)bus_dmamap_load(sc->mlx_sg_dmat, sc->mlx_sg_dmamap, sc->mlx_sgtable,
segsize, mlx_dma_map_sg, sc, 0);
return(0);
}
int
mlx_attach(struct mlx_softc *sc)
{
struct mlx_enquiry_old *meo;
int rid, error, fwminor, hscode, hserror, hsparam1, hsparam2, hsmsg;
debug_called(1);
TAILQ_INIT(&sc->mlx_work);
TAILQ_INIT(&sc->mlx_freecmds);
bioq_init(&sc->mlx_bioq);
switch(sc->mlx_iftype) {
case MLX_IFTYPE_2:
case MLX_IFTYPE_3:
sc->mlx_tryqueue = mlx_v3_tryqueue;
sc->mlx_findcomplete = mlx_v3_findcomplete;
sc->mlx_intaction = mlx_v3_intaction;
sc->mlx_fw_handshake = mlx_v3_fw_handshake;
break;
case MLX_IFTYPE_4:
sc->mlx_tryqueue = mlx_v4_tryqueue;
sc->mlx_findcomplete = mlx_v4_findcomplete;
sc->mlx_intaction = mlx_v4_intaction;
sc->mlx_fw_handshake = mlx_v4_fw_handshake;
break;
case MLX_IFTYPE_5:
sc->mlx_tryqueue = mlx_v5_tryqueue;
sc->mlx_findcomplete = mlx_v5_findcomplete;
sc->mlx_intaction = mlx_v5_intaction;
sc->mlx_fw_handshake = mlx_v5_fw_handshake;
break;
default:
return(ENXIO);
}
MLX_IO_LOCK(sc);
sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
MLX_IO_UNLOCK(sc);
hsmsg = 0;
DELAY(1000);
while ((hscode = sc->mlx_fw_handshake(sc, &hserror, &hsparam1, &hsparam2,
hsmsg == 0)) != 0) {
if (hsmsg == 0) {
device_printf(sc->mlx_dev, "controller initialisation in progress...\n");
hsmsg = 1;
}
if (hscode == 2) {
hscode = mlx_fw_message(sc, hserror, hsparam1, hsparam2);
if (hscode != 0) {
return(ENXIO);
}
}
}
if (hsmsg == 1)
device_printf(sc->mlx_dev, "initialisation complete.\n");
rid = 0;
sc->mlx_irq = bus_alloc_resource_any(sc->mlx_dev, SYS_RES_IRQ, &rid,
RF_SHAREABLE | RF_ACTIVE);
if (sc->mlx_irq == NULL) {
device_printf(sc->mlx_dev, "can't allocate interrupt\n");
return(ENXIO);
}
error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO |
INTR_ENTROPY | INTR_MPSAFE, NULL, mlx_intr, sc, &sc->mlx_intr);
if (error) {
device_printf(sc->mlx_dev, "can't set up interrupt\n");
return(ENXIO);
}
error = bus_dma_tag_create(sc->mlx_parent_dmat,
1, 0,
BUS_SPACE_MAXADDR,
BUS_SPACE_MAXADDR,
NULL, NULL,
MLX_MAXPHYS,
MLX_NSEG,
BUS_SPACE_MAXSIZE_32BIT,
0,
busdma_lock_mutex,
&sc->mlx_io_lock,
&sc->mlx_buffer_dmat);
if (error != 0) {
device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n");
return(ENOMEM);
}
error = mlx_sglist_map(sc);
if (error != 0) {
device_printf(sc->mlx_dev, "can't make initial s/g list mapping\n");
return(error);
}
sc->mlx_currevent = -1;
MLX_IO_LOCK(sc);
if ((sc->mlx_enq2 = mlx_enquire(sc, MLX_CMD_ENQUIRY2, sizeof(struct mlx_enquiry2), NULL)) == NULL) {
MLX_IO_UNLOCK(sc);
device_printf(sc->mlx_dev, "ENQUIRY2 failed\n");
return(ENXIO);
}
fwminor = (sc->mlx_enq2->me_firmware_id >> 8) & 0xff;
switch(sc->mlx_iftype) {
case MLX_IFTYPE_2:
if ((meo = mlx_enquire(sc, MLX_CMD_ENQUIRY_OLD, sizeof(struct mlx_enquiry_old), NULL)) == NULL) {
MLX_IO_UNLOCK(sc);
device_printf(sc->mlx_dev, "ENQUIRY_OLD failed\n");
return(ENXIO);
}
sc->mlx_enq2->me_firmware_id = ('0' << 24) | (0 << 16) | (meo->me_fwminor << 8) | meo->me_fwmajor;
if (meo->me_fwminor < 42) {
device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
device_printf(sc->mlx_dev, " *** WARNING *** Use revision 2.42 or later\n");
}
free(meo, M_DEVBUF);
break;
case MLX_IFTYPE_3:
if (fwminor < 51) {
device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
device_printf(sc->mlx_dev, " *** WARNING *** Use revision 3.51 or later\n");
}
break;
case MLX_IFTYPE_4:
if (fwminor < 6) {
device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
device_printf(sc->mlx_dev, " *** WARNING *** Use revision 4.06 or later\n");
}
break;
case MLX_IFTYPE_5:
if (fwminor < 7) {
device_printf(sc->mlx_dev, " *** WARNING *** This firmware revision is not recommended\n");
device_printf(sc->mlx_dev, " *** WARNING *** Use revision 5.07 or later\n");
}
break;
default:
MLX_IO_UNLOCK(sc);
return(ENXIO);
}
MLX_IO_UNLOCK(sc);
error = mlx_sglist_map(sc);
if (error != 0) {
device_printf(sc->mlx_dev, "can't make final s/g list mapping\n");
return(error);
}
sc->mlx_background = 0;
sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
sc->mlx_dev_t = make_dev(&mlx_cdevsw, 0, UID_ROOT, GID_OPERATOR,
S_IRUSR | S_IWUSR, "mlx%d", device_get_unit(sc->mlx_dev));
sc->mlx_dev_t->si_drv1 = sc;
callout_reset(&sc->mlx_timeout, hz, mlx_periodic, sc);
mlx_describe_controller(sc);
return(0);
}
void
mlx_startup(struct mlx_softc *sc)
{
struct mlx_enq_sys_drive *mes;
struct mlx_sysdrive *dr;
int i;
debug_called(1);
MLX_IO_LOCK(sc);
mes = mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(*mes) * MLX_MAXDRIVES, NULL);
MLX_IO_UNLOCK(sc);
if (mes == NULL) {
device_printf(sc->mlx_dev, "error fetching drive status\n");
return;
}
MLX_CONFIG_LOCK(sc);
for (i = 0, dr = &sc->mlx_sysdrive[0];
(i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
i++, dr++) {
if (dr->ms_disk == 0) {
dr->ms_size = mes[i].sd_size;
dr->ms_raidlevel = mes[i].sd_raidlevel & 0xf;
dr->ms_state = mes[i].sd_state;
if (sc->mlx_geom == MLX_GEOM_128_32) {
dr->ms_heads = 128;
dr->ms_sectors = 32;
dr->ms_cylinders = dr->ms_size / (128 * 32);
} else {
dr->ms_heads = 255;
dr->ms_sectors = 63;
dr->ms_cylinders = dr->ms_size / (255 * 63);
}
dr->ms_disk = device_add_child(sc->mlx_dev, NULL, DEVICE_UNIT_ANY);
if (dr->ms_disk == 0)
device_printf(sc->mlx_dev, "device_add_child failed\n");
device_set_ivars(dr->ms_disk, dr);
}
}
free(mes, M_DEVBUF);
bus_attach_children(sc->mlx_dev);
MLX_IO_LOCK(sc);
sc->mlx_state &= ~MLX_STATE_SHUTDOWN;
sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
MLX_IO_UNLOCK(sc);
MLX_CONFIG_UNLOCK(sc);
}
int
mlx_detach(device_t dev)
{
struct mlx_softc *sc = device_get_softc(dev);
struct mlxd_softc *mlxd;
int i, error;
debug_called(1);
error = EBUSY;
MLX_CONFIG_LOCK(sc);
if (sc->mlx_state & MLX_STATE_OPEN)
goto out;
for (i = 0; i < MLX_MAXDRIVES; i++) {
if (sc->mlx_sysdrive[i].ms_disk != 0) {
mlxd = device_get_softc(sc->mlx_sysdrive[i].ms_disk);
if (mlxd->mlxd_flags & MLXD_OPEN) {
device_printf(sc->mlx_sysdrive[i].ms_disk, "still open, can't detach\n");
goto out;
}
}
}
if ((error = mlx_shutdown(dev)))
goto out;
MLX_CONFIG_UNLOCK(sc);
mlx_free(sc);
return (0);
out:
MLX_CONFIG_UNLOCK(sc);
return(error);
}
int
mlx_shutdown(device_t dev)
{
struct mlx_softc *sc = device_get_softc(dev);
int error;
MLX_CONFIG_LOCK(sc);
error = mlx_shutdown_locked(sc);
MLX_CONFIG_UNLOCK(sc);
return (error);
}
static int
mlx_shutdown_locked(struct mlx_softc *sc)
{
int error;
debug_called(1);
MLX_CONFIG_ASSERT_LOCKED(sc);
MLX_IO_LOCK(sc);
sc->mlx_state |= MLX_STATE_SHUTDOWN;
sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
device_printf(sc->mlx_dev, "flushing cache...");
if (mlx_flush(sc)) {
printf("failed\n");
} else {
printf("done\n");
}
MLX_IO_UNLOCK(sc);
error = bus_generic_detach(sc->mlx_dev);
return (error);
}
int
mlx_suspend(device_t dev)
{
struct mlx_softc *sc = device_get_softc(dev);
debug_called(1);
MLX_IO_LOCK(sc);
sc->mlx_state |= MLX_STATE_SUSPEND;
device_printf(sc->mlx_dev, "flushing cache...");
printf("%s\n", mlx_flush(sc) ? "failed" : "done");
sc->mlx_intaction(sc, MLX_INTACTION_DISABLE);
MLX_IO_UNLOCK(sc);
return(0);
}
int
mlx_resume(device_t dev)
{
struct mlx_softc *sc = device_get_softc(dev);
debug_called(1);
MLX_IO_LOCK(sc);
sc->mlx_state &= ~MLX_STATE_SUSPEND;
sc->mlx_intaction(sc, MLX_INTACTION_ENABLE);
MLX_IO_UNLOCK(sc);
return(0);
}
void
mlx_intr(void *arg)
{
struct mlx_softc *sc = (struct mlx_softc *)arg;
debug_called(1);
MLX_IO_LOCK(sc);
mlx_done(sc, 1);
MLX_IO_UNLOCK(sc);
};
int
mlx_submit_buf(struct mlx_softc *sc, struct bio *bp)
{
debug_called(1);
MLX_IO_ASSERT_LOCKED(sc);
bioq_insert_tail(&sc->mlx_bioq, bp);
sc->mlx_waitbufs++;
mlx_startio(sc);
return(0);
}
int
mlx_open(struct cdev *dev, int flags, int fmt, struct thread *td)
{
struct mlx_softc *sc = dev->si_drv1;
MLX_CONFIG_LOCK(sc);
MLX_IO_LOCK(sc);
sc->mlx_state |= MLX_STATE_OPEN;
MLX_IO_UNLOCK(sc);
MLX_CONFIG_UNLOCK(sc);
return(0);
}
int
mlx_close(struct cdev *dev, int flags, int fmt, struct thread *td)
{
struct mlx_softc *sc = dev->si_drv1;
MLX_CONFIG_LOCK(sc);
MLX_IO_LOCK(sc);
sc->mlx_state &= ~MLX_STATE_OPEN;
MLX_IO_UNLOCK(sc);
MLX_CONFIG_UNLOCK(sc);
return (0);
}
int
mlx_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td)
{
struct mlx_softc *sc = dev->si_drv1;
struct mlx_rebuild_request *rb = (struct mlx_rebuild_request *)addr;
struct mlx_rebuild_status *rs = (struct mlx_rebuild_status *)addr;
int *arg = (int *)addr;
struct mlx_pause *mp;
struct mlx_sysdrive *dr;
struct mlxd_softc *mlxd;
int i, error;
switch(cmd) {
case MLX_NEXT_CHILD:
MLX_CONFIG_LOCK(sc);
for (i = 0; i < MLX_MAXDRIVES; i++) {
if (sc->mlx_sysdrive[i].ms_disk != 0) {
if (*arg == -1) {
*arg = device_get_unit(sc->mlx_sysdrive[i].ms_disk);
MLX_CONFIG_UNLOCK(sc);
return(0);
}
if (*arg == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
*arg = -1;
}
}
MLX_CONFIG_UNLOCK(sc);
return(ENOENT);
case MLX_RESCAN_DRIVES:
bus_topo_lock();
mlx_startup(sc);
bus_topo_unlock();
return(0);
case MLX_DETACH_DRIVE:
MLX_CONFIG_LOCK(sc);
if (((dr = mlx_findunit(sc, *arg)) == NULL) ||
((mlxd = device_get_softc(dr->ms_disk)) == NULL)) {
MLX_CONFIG_UNLOCK(sc);
return(ENOENT);
}
device_printf(dr->ms_disk, "detaching...");
error = 0;
if (mlxd->mlxd_flags & MLXD_OPEN) {
error = EBUSY;
goto detach_out;
}
MLX_IO_LOCK(sc);
if (mlx_flush(sc)) {
MLX_IO_UNLOCK(sc);
error = EBUSY;
goto detach_out;
}
MLX_IO_UNLOCK(sc);
if ((error = device_delete_child(sc->mlx_dev, dr->ms_disk)) != 0)
goto detach_out;
dr->ms_disk = 0;
detach_out:
MLX_CONFIG_UNLOCK(sc);
if (error) {
printf("failed\n");
} else {
printf("done\n");
}
return(error);
case MLX_PAUSE_CHANNEL:
if (!(sc->mlx_feature & MLX_FEAT_PAUSEWORKS))
return(EOPNOTSUPP);
mp = (struct mlx_pause *)addr;
if ((mp->mp_when < 0) || (mp->mp_when > 3600))
return(EINVAL);
if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30)))
return(EINVAL);
MLX_IO_LOCK(sc);
if ((mp->mp_which == MLX_PAUSE_CANCEL) && (sc->mlx_pause.mp_when != 0)) {
sc->mlx_pause.mp_which = 0;
} else {
mp->mp_which &= ((1 << sc->mlx_enq2->me_actual_channels) -1);
if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0)) {
MLX_IO_UNLOCK(sc);
return(EBUSY);
}
sc->mlx_pause.mp_which = mp->mp_which;
sc->mlx_pause.mp_when = time_second + mp->mp_when;
sc->mlx_pause.mp_howlong = sc->mlx_pause.mp_when + mp->mp_howlong;
}
MLX_IO_UNLOCK(sc);
return(0);
case MLX_COMMAND:
return(mlx_user_command(sc, (struct mlx_usercommand *)addr));
case MLX_REBUILDASYNC:
MLX_IO_LOCK(sc);
if (sc->mlx_background != 0) {
MLX_IO_UNLOCK(sc);
rb->rr_status = 0x0106;
return(EBUSY);
}
rb->rr_status = mlx_rebuild(sc, rb->rr_channel, rb->rr_target);
switch (rb->rr_status) {
case 0:
error = 0;
break;
case 0x10000:
error = ENOMEM;
break;
case 0x0002:
error = EBUSY;
break;
case 0x0104:
error = EIO;
break;
case 0x0105:
error = ERANGE;
break;
case 0x0106:
error = EBUSY;
break;
default:
error = EINVAL;
break;
}
if (error == 0)
sc->mlx_background = MLX_BACKGROUND_REBUILD;
MLX_IO_UNLOCK(sc);
return(error);
case MLX_REBUILDSTAT:
MLX_IO_LOCK(sc);
*rs = sc->mlx_rebuildstat;
MLX_IO_UNLOCK(sc);
return(0);
case MLX_GET_SYSDRIVE:
error = ENOENT;
MLX_CONFIG_LOCK(sc);
bus_topo_lock();
mlxd = devclass_get_softc(devclass_find("mlxd"), *arg);
bus_topo_unlock();
if ((mlxd != NULL) && (mlxd->mlxd_drive >= sc->mlx_sysdrive) &&
(mlxd->mlxd_drive < (sc->mlx_sysdrive + MLX_MAXDRIVES))) {
error = 0;
*arg = mlxd->mlxd_drive - sc->mlx_sysdrive;
}
MLX_CONFIG_UNLOCK(sc);
return(error);
default:
return(ENOTTY);
}
}
int
mlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd,
caddr_t addr, int32_t flag, struct thread *td)
{
int *arg = (int *)addr;
int error, result;
switch(cmd) {
case MLXD_STATUS:
MLX_IO_LOCK(sc);
*arg = drive->ms_state;
MLX_IO_UNLOCK(sc);
return(0);
case MLXD_CHECKASYNC:
MLX_IO_LOCK(sc);
if (sc->mlx_background != 0) {
MLX_IO_UNLOCK(sc);
*arg = 0x0106;
return(EBUSY);
}
result = mlx_check(sc, drive - &sc->mlx_sysdrive[0]);
switch (result) {
case 0:
error = 0;
break;
case 0x10000:
error = ENOMEM;
break;
case 0x0002:
error = EIO;
break;
case 0x0105:
error = ERANGE;
break;
case 0x0106:
error = EBUSY;
break;
default:
error = EINVAL;
break;
}
if (error == 0)
sc->mlx_background = MLX_BACKGROUND_CHECK;
MLX_IO_UNLOCK(sc);
*arg = result;
return(error);
}
return(ENOIOCTL);
}
static void
mlx_periodic(void *data)
{
struct mlx_softc *sc = (struct mlx_softc *)data;
debug_called(1);
MLX_IO_ASSERT_LOCKED(sc);
if ((sc->mlx_pause.mp_which != 0) &&
(sc->mlx_pause.mp_when > 0) &&
(time_second >= sc->mlx_pause.mp_when)){
mlx_pause_action(sc);
sc->mlx_pause.mp_when = 0;
sysbeep(500, SBT_1S);
} else if ((sc->mlx_pause.mp_which != 0) &&
(sc->mlx_pause.mp_when == 0)) {
if (time_second >= sc->mlx_pause.mp_howlong) {
mlx_pause_action(sc);
sc->mlx_pause.mp_which = 0;
sysbeep(500, SBT_1S);
} else {
sysbeep((time_second % 5) * 100 + 500, SBT_1S / 8);
}
} else if (time_second > (sc->mlx_lastpoll + 10)) {
sc->mlx_lastpoll = time_second;
mlx_enquire(sc, (sc->mlx_iftype == MLX_IFTYPE_2) ? MLX_CMD_ENQUIRY_OLD : MLX_CMD_ENQUIRY,
imax(sizeof(struct mlx_enquiry), sizeof(struct mlx_enquiry_old)), mlx_periodic_enquiry);
mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(struct mlx_enq_sys_drive) * MLX_MAXDRIVES,
mlx_periodic_enquiry);
}
mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild);
mlx_done(sc, 1);
callout_reset(&sc->mlx_timeout, hz, mlx_periodic, sc);
}
static void
mlx_periodic_enquiry(struct mlx_command *mc)
{
struct mlx_softc *sc = mc->mc_sc;
debug_called(1);
MLX_IO_ASSERT_LOCKED(sc);
if (mc->mc_status != 0) {
device_printf(sc->mlx_dev, "periodic enquiry failed - %s\n", mlx_diagnose_command(mc));
goto out;
}
switch(mc->mc_mailbox[0]) {
case MLX_CMD_ENQUIRY_OLD:
{
struct mlx_enquiry *me = (struct mlx_enquiry *)mc->mc_data;
struct mlx_enquiry_old *meo = (struct mlx_enquiry_old *)mc->mc_data;
int i;
for (i = (sizeof(me->me_dead) / sizeof(me->me_dead[0])) - 1; i >= 0; i--) {
me->me_dead[i].dd_chan = meo->me_dead[i].dd_chan;
me->me_dead[i].dd_targ = meo->me_dead[i].dd_targ;
}
me->me_misc_flags = 0;
me->me_rebuild_count = meo->me_rebuild_count;
me->me_dead_count = meo->me_dead_count;
me->me_critical_sd_count = meo->me_critical_sd_count;
me->me_event_log_seq_num = 0;
me->me_offline_sd_count = meo->me_offline_sd_count;
me->me_max_commands = meo->me_max_commands;
me->me_rebuild_flag = meo->me_rebuild_flag;
me->me_fwmajor = meo->me_fwmajor;
me->me_fwminor = meo->me_fwminor;
me->me_status_flags = meo->me_status_flags;
me->me_flash_age = meo->me_flash_age;
for (i = (sizeof(me->me_drvsize) / sizeof(me->me_drvsize[0])) - 1; i >= 0; i--) {
if (i > ((sizeof(meo->me_drvsize) / sizeof(meo->me_drvsize[0])) - 1)) {
me->me_drvsize[i] = 0;
} else {
me->me_drvsize[i] = meo->me_drvsize[i];
}
}
me->me_num_sys_drvs = meo->me_num_sys_drvs;
}
case MLX_CMD_ENQUIRY:
{
struct mlx_enquiry *me = (struct mlx_enquiry *)mc->mc_data;
if (sc->mlx_currevent == -1) {
sc->mlx_currevent = sc->mlx_lastevent = me->me_event_log_seq_num;
} else if ((me->me_event_log_seq_num != sc->mlx_lastevent) && !(sc->mlx_flags & MLX_EVENTLOG_BUSY)) {
sc->mlx_currevent = me->me_event_log_seq_num;
debug(1, "event log pointer was %d, now %d\n", sc->mlx_lastevent, sc->mlx_currevent);
sc->mlx_flags |= MLX_EVENTLOG_BUSY;
mlx_periodic_eventlog_poll(sc);
}
break;
}
case MLX_CMD_ENQSYSDRIVE:
{
struct mlx_enq_sys_drive *mes = (struct mlx_enq_sys_drive *)mc->mc_data;
struct mlx_sysdrive *dr;
int i;
for (i = 0, dr = &sc->mlx_sysdrive[0];
(i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
i++) {
if (dr->ms_state != mes[i].sd_state) {
switch(mes[i].sd_state) {
case MLX_SYSD_OFFLINE:
device_printf(dr->ms_disk, "drive offline\n");
break;
case MLX_SYSD_ONLINE:
device_printf(dr->ms_disk, "drive online\n");
break;
case MLX_SYSD_CRITICAL:
device_printf(dr->ms_disk, "drive critical\n");
break;
}
dr->ms_state = mes[i].sd_state;
}
}
break;
}
default:
device_printf(sc->mlx_dev, "%s: unknown command 0x%x", __func__, mc->mc_mailbox[0]);
break;
}
out:
free(mc->mc_data, M_DEVBUF);
mlx_releasecmd(mc);
}
static void
mlx_eventlog_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
{
struct mlx_command *mc;
mc = (struct mlx_command *)arg;
mlx_setup_dmamap(mc, segs, nsegments, error);
mlx_make_type3(mc, MLX_CMD_LOGOP, MLX_LOGOP_GET, 1,
mc->mc_sc->mlx_lastevent, 0, 0, mc->mc_dataphys, 0);
mc->mc_complete = mlx_periodic_eventlog_respond;
mc->mc_private = mc;
if (mlx_start(mc) != 0) {
mlx_releasecmd(mc);
free(mc->mc_data, M_DEVBUF);
mc->mc_data = NULL;
}
}
static void
mlx_periodic_eventlog_poll(struct mlx_softc *sc)
{
struct mlx_command *mc;
void *result = NULL;
int error = 0;
debug_called(1);
MLX_IO_ASSERT_LOCKED(sc);
error = 1;
if ((mc = mlx_alloccmd(sc)) == NULL)
goto out;
if ((result = malloc(1024, M_DEVBUF,
M_NOWAIT)) == NULL)
goto out;
if (mlx_getslot(mc))
goto out;
mc->mc_data = result;
mc->mc_length = 1024;
error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
mc->mc_length, mlx_eventlog_cb, mc, BUS_DMA_NOWAIT);
out:
if (error != 0) {
if (mc != NULL)
mlx_releasecmd(mc);
if ((result != NULL) && (mc->mc_data != NULL))
free(result, M_DEVBUF);
}
}
static char *mlx_sense_messages[] = {
"because write recovery failed",
"because of SCSI bus reset failure",
"because of double check condition",
"because it was removed",
"because of gross error on SCSI chip",
"because of bad tag returned from drive",
"because of timeout on SCSI command",
"because of reset SCSI command issued from system",
"because busy or parity error count exceeded limit",
"because of 'kill drive' command from system",
"because of selection timeout",
"due to SCSI phase sequence error",
"due to unknown status"
};
static void
mlx_periodic_eventlog_respond(struct mlx_command *mc)
{
struct mlx_softc *sc = mc->mc_sc;
struct mlx_eventlog_entry *el = (struct mlx_eventlog_entry *)mc->mc_data;
char *reason;
debug_called(1);
MLX_IO_ASSERT_LOCKED(sc);
sc->mlx_lastevent++;
if (mc->mc_status == 0) {
switch(el->el_type) {
case MLX_LOGMSG_SENSE:
if ((el->el_sensekey == 9) &&
(el->el_asc == 0x80)) {
if (el->el_asq < nitems(mlx_sense_messages)) {
reason = mlx_sense_messages[el->el_asq];
} else {
reason = "for unknown reason";
}
device_printf(sc->mlx_dev, "physical drive %d:%d killed %s\n",
el->el_channel, el->el_target, reason);
}
if ((el->el_sensekey == 6) && (el->el_asc == 0x29)) {
device_printf(sc->mlx_dev, "physical drive %d:%d reset\n",
el->el_channel, el->el_target);
}
if (!((el->el_sensekey == 0) ||
((el->el_sensekey == 2) &&
(el->el_asc == 0x04) &&
((el->el_asq == 0x01) ||
(el->el_asq == 0x02))))) {
device_printf(sc->mlx_dev, "physical drive %d:%d error log: sense = %d asc = %x asq = %x\n",
el->el_channel, el->el_target, el->el_sensekey, el->el_asc, el->el_asq);
device_printf(sc->mlx_dev, " info %4D csi %4D\n", el->el_information, ":", el->el_csi, ":");
}
break;
default:
device_printf(sc->mlx_dev, "unknown log message type 0x%x\n", el->el_type);
break;
}
} else {
device_printf(sc->mlx_dev, "error reading message log - %s\n", mlx_diagnose_command(mc));
sc->mlx_lastevent = sc->mlx_currevent;
}
free(mc->mc_data, M_DEVBUF);
mlx_releasecmd(mc);
if (sc->mlx_lastevent != sc->mlx_currevent) {
mlx_periodic_eventlog_poll(sc);
} else {
sc->mlx_flags &= ~MLX_EVENTLOG_BUSY;
}
}
static void
mlx_periodic_rebuild(struct mlx_command *mc)
{
struct mlx_softc *sc = mc->mc_sc;
struct mlx_rebuild_status *mr = (struct mlx_rebuild_status *)mc->mc_data;
MLX_IO_ASSERT_LOCKED(sc);
switch(mc->mc_status) {
case 0:
sc->mlx_rebuildstat = *mr;
if (sc->mlx_background == 0) {
sc->mlx_background = MLX_BACKGROUND_SPONTANEOUS;
device_printf(sc->mlx_dev, "background check/rebuild operation started\n");
}
break;
case 0x0105:
switch(sc->mlx_background) {
case MLX_BACKGROUND_CHECK:
device_printf(sc->mlx_dev, "consistency check completed\n");
break;
case MLX_BACKGROUND_REBUILD:
device_printf(sc->mlx_dev, "drive rebuild completed\n");
break;
case MLX_BACKGROUND_SPONTANEOUS:
default:
if (sc->mlx_rebuildstat.rs_code != MLX_REBUILDSTAT_IDLE) {
device_printf(sc->mlx_dev, "background check/rebuild operation completed\n");
}
}
sc->mlx_background = 0;
sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
break;
}
free(mc->mc_data, M_DEVBUF);
mlx_releasecmd(mc);
}
static void
mlx_pause_action(struct mlx_softc *sc)
{
struct mlx_command *mc;
int failsafe, i, command;
MLX_IO_ASSERT_LOCKED(sc);
if (sc->mlx_pause.mp_when == 0) {
command = MLX_CMD_STARTCHANNEL;
failsafe = 0;
} else {
command = MLX_CMD_STOPCHANNEL;
failsafe = ((sc->mlx_pause.mp_howlong - time_second) + 5) / 30;
if (failsafe > 0xf) {
failsafe = 0xf;
sc->mlx_pause.mp_howlong = time_second + (0xf * 30) - 5;
}
}
for (i = 0; i < sc->mlx_enq2->me_actual_channels; i++) {
if ((1 << i) & sc->mlx_pause.mp_which) {
if ((mc = mlx_alloccmd(sc)) == NULL)
goto fail;
mc->mc_flags |= MLX_CMD_PRIORITY;
if (mlx_getslot(mc))
goto fail;
mlx_make_type2(mc, command, (failsafe << 4) | i, 0, 0, 0, 0, 0, 0, 0);
mc->mc_complete = mlx_pause_done;
mc->mc_private = sc;
if (mlx_start(mc))
goto fail;
return;
fail:
device_printf(sc->mlx_dev, "%s failed for channel %d\n",
command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", i);
if (mc != NULL)
mlx_releasecmd(mc);
}
}
}
static void
mlx_pause_done(struct mlx_command *mc)
{
struct mlx_softc *sc = mc->mc_sc;
int command = mc->mc_mailbox[0];
int channel = mc->mc_mailbox[2] & 0xf;
MLX_IO_ASSERT_LOCKED(sc);
if (mc->mc_status != 0) {
device_printf(sc->mlx_dev, "%s command failed - %s\n",
command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", mlx_diagnose_command(mc));
} else if (command == MLX_CMD_STOPCHANNEL) {
device_printf(sc->mlx_dev, "channel %d pausing for %ld seconds\n",
channel, (long)(sc->mlx_pause.mp_howlong - time_second));
} else {
device_printf(sc->mlx_dev, "channel %d resuming\n", channel);
}
mlx_releasecmd(mc);
}
static void
mlx_enquire_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
{
struct mlx_softc *sc;
struct mlx_command *mc;
mc = (struct mlx_command *)arg;
if (error)
return;
mlx_setup_dmamap(mc, segs, nsegments, error);
sc = mc->mc_sc;
mlx_make_type2(mc, mc->mc_command, 0, 0, 0, 0, 0, 0, mc->mc_dataphys, 0);
if (mc->mc_complete != NULL) {
if ((error = mlx_start(mc)) != 0)
return;
} else {
if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) :
mlx_poll_command(mc))
return;
if (mc->mc_status != 0) {
device_printf(sc->mlx_dev, "ENQUIRY failed - %s\n",
mlx_diagnose_command(mc));
return;
}
}
}
static void *
mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize, void (* complete)(struct mlx_command *mc))
{
struct mlx_command *mc;
void *result;
int error;
debug_called(1);
MLX_IO_ASSERT_LOCKED(sc);
error = 1;
result = NULL;
if ((mc = mlx_alloccmd(sc)) == NULL)
goto out;
if ((result = malloc(bufsize, M_DEVBUF, M_NOWAIT)) == NULL)
goto out;
mc->mc_flags |= MLX_CMD_PRIORITY | MLX_CMD_DATAOUT;
if (mlx_getslot(mc))
goto out;
mc->mc_data = result;
mc->mc_length = bufsize;
mc->mc_command = command;
if (complete != NULL) {
mc->mc_complete = complete;
mc->mc_private = mc;
}
error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
mc->mc_length, mlx_enquire_cb, mc, BUS_DMA_NOWAIT);
out:
if ((mc != NULL) && (mc->mc_complete == NULL))
mlx_releasecmd(mc);
if ((error != 0) && (result != NULL)) {
free(result, M_DEVBUF);
result = NULL;
}
return(result);
}
static int
mlx_flush(struct mlx_softc *sc)
{
struct mlx_command *mc;
int error;
debug_called(1);
MLX_IO_ASSERT_LOCKED(sc);
error = 1;
if ((mc = mlx_alloccmd(sc)) == NULL)
goto out;
if (mlx_getslot(mc))
goto out;
mlx_make_type2(mc, MLX_CMD_FLUSH, 0, 0, 0, 0, 0, 0, 0, 0);
if (mlx_poll_command(mc))
goto out;
if (mc->mc_status != 0) {
device_printf(sc->mlx_dev, "FLUSH failed - %s\n", mlx_diagnose_command(mc));
goto out;
}
error = 0;
out:
if (mc != NULL)
mlx_releasecmd(mc);
return(error);
}
static int
mlx_check(struct mlx_softc *sc, int drive)
{
struct mlx_command *mc;
int error;
debug_called(1);
MLX_IO_ASSERT_LOCKED(sc);
error = 0x10000;
if ((mc = mlx_alloccmd(sc)) == NULL)
goto out;
if (mlx_getslot(mc))
goto out;
mlx_make_type2(mc, MLX_CMD_CHECKASYNC, 0, 0, 0, 0, 0, drive | 0x80, 0, 0);
if (mlx_wait_command(mc))
goto out;
if (mc->mc_status != 0) {
device_printf(sc->mlx_dev, "CHECK ASYNC failed - %s\n", mlx_diagnose_command(mc));
} else {
device_printf(sc->mlx_sysdrive[drive].ms_disk, "consistency check started");
}
error = mc->mc_status;
out:
if (mc != NULL)
mlx_releasecmd(mc);
return(error);
}
static int
mlx_rebuild(struct mlx_softc *sc, int channel, int target)
{
struct mlx_command *mc;
int error;
debug_called(1);
MLX_IO_ASSERT_LOCKED(sc);
error = 0x10000;
if ((mc = mlx_alloccmd(sc)) == NULL)
goto out;
if (mlx_getslot(mc))
goto out;
mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, 0, 0);
if (mlx_wait_command(mc))
goto out;
if (mc->mc_status != 0) {
device_printf(sc->mlx_dev, "REBUILD ASYNC failed - %s\n", mlx_diagnose_command(mc));
} else {
device_printf(sc->mlx_dev, "drive rebuild started for %d:%d\n", channel, target);
}
error = mc->mc_status;
out:
if (mc != NULL)
mlx_releasecmd(mc);
return(error);
}
static int
mlx_wait_command(struct mlx_command *mc)
{
struct mlx_softc *sc = mc->mc_sc;
int error, count;
debug_called(1);
MLX_IO_ASSERT_LOCKED(sc);
mc->mc_complete = NULL;
mc->mc_private = mc;
if ((error = mlx_start(mc)) != 0)
return(error);
count = 0;
while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 30)) {
mtx_sleep(mc->mc_private, &sc->mlx_io_lock, PRIBIO | PCATCH, "mlxwcmd", hz);
}
if (mc->mc_status != 0) {
device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc));
return(EIO);
}
return(0);
}
static int
mlx_poll_command(struct mlx_command *mc)
{
struct mlx_softc *sc = mc->mc_sc;
int error, count;
debug_called(1);
MLX_IO_ASSERT_LOCKED(sc);
mc->mc_complete = NULL;
mc->mc_private = NULL;
if ((error = mlx_start(mc)) != 0)
return(error);
count = 0;
do {
mlx_done(mc->mc_sc, 1);
} while ((mc->mc_status == MLX_STATUS_BUSY) && (count++ < 15000000));
if (mc->mc_status != MLX_STATUS_BUSY) {
TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
return(0);
}
device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc));
return(EIO);
}
void
mlx_startio_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
{
struct mlx_command *mc;
struct mlxd_softc *mlxd;
struct mlx_softc *sc;
struct bio *bp;
int blkcount;
int driveno;
int cmd;
mc = (struct mlx_command *)arg;
mlx_setup_dmamap(mc, segs, nsegments, error);
sc = mc->mc_sc;
bp = mc->mc_private;
if (bp->bio_cmd == BIO_READ) {
mc->mc_flags |= MLX_CMD_DATAIN;
cmd = MLX_CMD_READSG;
} else {
mc->mc_flags |= MLX_CMD_DATAOUT;
cmd = MLX_CMD_WRITESG;
}
mlxd = bp->bio_disk->d_drv1;
driveno = mlxd->mlxd_drive - sc->mlx_sysdrive;
blkcount = howmany(bp->bio_bcount, MLX_BLKSIZE);
if ((bp->bio_pblkno + blkcount) > sc->mlx_sysdrive[driveno].ms_size)
device_printf(sc->mlx_dev,
"I/O beyond end of unit (%lld,%d > %lu)\n",
(long long)bp->bio_pblkno, blkcount,
(u_long)sc->mlx_sysdrive[driveno].ms_size);
if (sc->mlx_iftype == MLX_IFTYPE_2) {
mlx_make_type1(mc, (cmd == MLX_CMD_WRITESG) ? MLX_CMD_WRITESG_OLD :
MLX_CMD_READSG_OLD,
blkcount & 0xff,
bp->bio_pblkno,
driveno,
mc->mc_sgphys,
mc->mc_nsgent & 0x3f);
} else {
mlx_make_type5(mc, cmd,
blkcount & 0xff,
(driveno << 3) | ((blkcount >> 8) & 0x07),
bp->bio_pblkno,
mc->mc_sgphys,
mc->mc_nsgent & 0x3f);
}
if (mlx_start(mc) != 0) {
mc->mc_status = MLX_STATUS_WEDGED;
mlx_completeio(mc);
}
sc->mlx_state &= ~MLX_STATE_QFROZEN;
}
static void
mlx_startio(struct mlx_softc *sc)
{
struct mlx_command *mc;
struct bio *bp;
int error;
MLX_IO_ASSERT_LOCKED(sc);
for (;;) {
if (sc->mlx_state & MLX_STATE_QFROZEN)
break;
if ((bp = bioq_first(&sc->mlx_bioq)) == NULL)
break;
if ((mc = mlx_alloccmd(sc)) == NULL)
break;
if (mlx_getslot(mc) != 0) {
mlx_releasecmd(mc);
break;
}
bioq_remove(&sc->mlx_bioq, bp);
sc->mlx_waitbufs--;
mc->mc_complete = mlx_completeio;
mc->mc_private = bp;
mc->mc_data = bp->bio_data;
mc->mc_length = bp->bio_bcount;
error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
mc->mc_length, mlx_startio_cb, mc, 0);
if (error == EINPROGRESS) {
sc->mlx_state |= MLX_STATE_QFROZEN;
break;
}
}
}
static void
mlx_completeio(struct mlx_command *mc)
{
struct mlx_softc *sc = mc->mc_sc;
struct bio *bp = mc->mc_private;
struct mlxd_softc *mlxd = bp->bio_disk->d_drv1;
MLX_IO_ASSERT_LOCKED(sc);
if (mc->mc_status != MLX_STATUS_OK) {
bp->bio_error = EIO;
bp->bio_flags |= BIO_ERROR;
switch(mc->mc_status) {
case MLX_STATUS_RDWROFFLINE:
device_printf(mlxd->mlxd_dev, "drive offline\n");
mlxd->mlxd_drive->ms_state = MLX_SYSD_OFFLINE;
break;
default:
device_printf(sc->mlx_dev, "I/O error - %s\n", mlx_diagnose_command(mc));
#if 0
device_printf(sc->mlx_dev, " b_bcount %ld blkcount %ld b_pblkno %d\n",
bp->bio_bcount, bp->bio_bcount / MLX_BLKSIZE, bp->bio_pblkno);
device_printf(sc->mlx_dev, " %13D\n", mc->mc_mailbox, " ");
#endif
break;
}
}
mlx_releasecmd(mc);
mlxd_intr(bp);
}
void
mlx_user_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
{
struct mlx_usercommand *mu;
struct mlx_command *mc;
struct mlx_dcdb *dcdb;
mc = (struct mlx_command *)arg;
if (error)
return;
mlx_setup_dmamap(mc, segs, nsegments, error);
mu = (struct mlx_usercommand *)mc->mc_private;
dcdb = NULL;
if (mc->mc_mailbox[0] == MLX_CMD_DIRECT_CDB) {
dcdb = (struct mlx_dcdb *)mc->mc_data;
dcdb->dcdb_physaddr = mc->mc_dataphys + sizeof(*dcdb);
mu->mu_bufptr = 8;
}
if (mu->mu_datasize > 0) {
mc->mc_mailbox[mu->mu_bufptr ] = mc->mc_dataphys & 0xff;
mc->mc_mailbox[mu->mu_bufptr + 1] = (mc->mc_dataphys >> 8) & 0xff;
mc->mc_mailbox[mu->mu_bufptr + 2] = (mc->mc_dataphys >> 16) & 0xff;
mc->mc_mailbox[mu->mu_bufptr + 3] = (mc->mc_dataphys >> 24) & 0xff;
}
debug(0, "command fixup");
if (mlx_wait_command(mc) != 0)
return;
}
static int
mlx_user_command(struct mlx_softc *sc, struct mlx_usercommand *mu)
{
struct mlx_command *mc;
void *kbuf;
int error;
debug_called(0);
kbuf = NULL;
mc = NULL;
error = ENOMEM;
MLX_IO_LOCK(sc);
if ((mc = mlx_alloccmd(sc)) == NULL) {
MLX_IO_UNLOCK(sc);
return(error);
}
bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox));
debug(0, "got command buffer");
if (mu->mu_datasize > 0) {
if (mu->mu_datasize > MLX_MAXPHYS) {
error = EINVAL;
goto out;
}
MLX_IO_UNLOCK(sc);
kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK);
if ((error = copyin(mu->mu_buf, kbuf, mu->mu_datasize))) {
MLX_IO_LOCK(sc);
goto out;
}
MLX_IO_LOCK(sc);
debug(0, "got kernel buffer");
}
if (mlx_getslot(mc))
goto out;
debug(0, "got a slot");
if (mu->mu_datasize > 0) {
if ((mu->mu_bufptr < 0) || (mu->mu_bufptr > (sizeof(mu->mu_command) -
sizeof(u_int32_t)))) {
error = EINVAL;
goto out;
}
}
mc->mc_data = kbuf;
mc->mc_length = mu->mu_datasize;
mc->mc_private = mu;
error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data,
mc->mc_length, mlx_user_cb, mc, BUS_DMA_NOWAIT);
if (error)
goto out;
mu->mu_status = mc->mc_status;
if (mu->mu_datasize > 0) {
MLX_IO_UNLOCK(sc);
error = copyout(kbuf, mu->mu_buf, mu->mu_datasize);
MLX_IO_LOCK(sc);
}
out:
mlx_releasecmd(mc);
MLX_IO_UNLOCK(sc);
if (kbuf != NULL)
free(kbuf, M_DEVBUF);
return(error);
}
static int
mlx_getslot(struct mlx_command *mc)
{
struct mlx_softc *sc = mc->mc_sc;
int slot, limit;
debug_called(1);
MLX_IO_ASSERT_LOCKED(sc);
if (sc->mlx_enq2 != NULL) {
limit = sc->mlx_enq2->me_max_commands;
} else {
limit = 2;
}
if (sc->mlx_busycmds >= ((mc->mc_flags & MLX_CMD_PRIORITY) ? limit : limit - 4))
return(EBUSY);
for (slot = 0; slot < limit; slot++) {
debug(2, "try slot %d", slot);
if (sc->mlx_busycmd[slot] == NULL)
break;
}
if (slot < limit) {
sc->mlx_busycmd[slot] = mc;
sc->mlx_busycmds++;
}
if (slot >= limit)
return(EBUSY);
debug(2, "got slot %d", slot);
mc->mc_slot = slot;
return(0);
}
static void
mlx_setup_dmamap(struct mlx_command *mc, bus_dma_segment_t *segs, int nsegments,
int error)
{
struct mlx_softc *sc = mc->mc_sc;
struct mlx_sgentry *sg;
int i;
debug_called(1);
if (sc->mlx_enq2 && (nsegments > sc->mlx_enq2->me_max_sg))
panic("MLX: too many s/g segments (%d, max %d)", nsegments,
sc->mlx_enq2->me_max_sg);
sg = sc->mlx_sgtable + (mc->mc_slot * MLX_NSEG);
mc->mc_nsgent = nsegments;
mc->mc_sgphys = sc->mlx_sgbusaddr +
(mc->mc_slot * MLX_NSEG * sizeof(struct mlx_sgentry));
mc->mc_dataphys = segs[0].ds_addr;
for (i = 0; i < nsegments; i++, sg++) {
sg->sg_addr = segs[i].ds_addr;
sg->sg_count = segs[i].ds_len;
}
if (mc->mc_flags & MLX_CMD_DATAIN)
bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap,
BUS_DMASYNC_PREREAD);
if (mc->mc_flags & MLX_CMD_DATAOUT)
bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap,
BUS_DMASYNC_PREWRITE);
}
static void
mlx_unmapcmd(struct mlx_command *mc)
{
struct mlx_softc *sc = mc->mc_sc;
debug_called(1);
if (mc->mc_data != NULL) {
if (mc->mc_flags & MLX_CMD_DATAIN)
bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTREAD);
if (mc->mc_flags & MLX_CMD_DATAOUT)
bus_dmamap_sync(sc->mlx_buffer_dmat, mc->mc_dmamap, BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->mlx_buffer_dmat, mc->mc_dmamap);
}
}
static int
mlx_start(struct mlx_command *mc)
{
struct mlx_softc *sc = mc->mc_sc;
int i;
debug_called(1);
mc->mc_mailbox[0x1] = mc->mc_slot;
mc->mc_status = MLX_STATUS_BUSY;
mc->mc_timeout = time_second + 60;
for (i = 100000; i > 0; i--) {
if (sc->mlx_tryqueue(sc, mc)) {
TAILQ_INSERT_TAIL(&sc->mlx_work, mc, mc_link);
return (0);
} else if (i > 1)
mlx_done(sc, 0);
}
sc->mlx_busycmd[mc->mc_slot] = NULL;
device_printf(sc->mlx_dev, "controller wedged (not taking commands)\n");
mc->mc_status = MLX_STATUS_WEDGED;
mlx_complete(sc);
return(EIO);
}
static int
mlx_done(struct mlx_softc *sc, int startio)
{
struct mlx_command *mc;
int result;
u_int8_t slot;
u_int16_t status;
debug_called(2);
MLX_IO_ASSERT_LOCKED(sc);
result = 0;
for (;;) {
if (sc->mlx_findcomplete(sc, &slot, &status)) {
result = 1;
mc = sc->mlx_busycmd[slot];
if (mc != NULL) {
if (mc->mc_status == MLX_STATUS_BUSY) {
mc->mc_status = status;
sc->mlx_busycmd[slot] = NULL;
sc->mlx_busycmds--;
} else {
device_printf(sc->mlx_dev, "duplicate done event for slot %d\n", slot);
}
} else {
device_printf(sc->mlx_dev, "done event for nonbusy slot %d\n", slot);
}
} else {
break;
}
}
if (result && startio)
mlx_startio(sc);
mlx_complete(sc);
return(result);
}
static void
mlx_complete(struct mlx_softc *sc)
{
struct mlx_command *mc, *nc;
debug_called(2);
MLX_IO_ASSERT_LOCKED(sc);
mc = TAILQ_FIRST(&sc->mlx_work);
while (mc != NULL) {
nc = TAILQ_NEXT(mc, mc_link);
if (mc->mc_status != MLX_STATUS_BUSY) {
mlx_unmapcmd(mc);
if (mc->mc_complete != NULL) {
TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
mc->mc_complete(mc);
} else if (mc->mc_private != NULL) {
TAILQ_REMOVE(&sc->mlx_work, mc, mc_link);
wakeup_one(mc->mc_private);
} else {
}
}
mc = nc;
}
}
static struct mlx_command *
mlx_alloccmd(struct mlx_softc *sc)
{
struct mlx_command *mc;
int error;
debug_called(1);
MLX_IO_ASSERT_LOCKED(sc);
if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL)
TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link);
if (mc == NULL) {
mc = (struct mlx_command *)malloc(sizeof(*mc), M_DEVBUF, M_NOWAIT | M_ZERO);
if (mc != NULL) {
mc->mc_sc = sc;
error = bus_dmamap_create(sc->mlx_buffer_dmat, 0, &mc->mc_dmamap);
if (error) {
free(mc, M_DEVBUF);
return(NULL);
}
}
}
return(mc);
}
static void
mlx_releasecmd(struct mlx_command *mc)
{
debug_called(1);
MLX_IO_ASSERT_LOCKED(mc->mc_sc);
TAILQ_INSERT_HEAD(&mc->mc_sc->mlx_freecmds, mc, mc_link);
}
static void
mlx_freecmd(struct mlx_command *mc)
{
struct mlx_softc *sc = mc->mc_sc;
debug_called(1);
bus_dmamap_destroy(sc->mlx_buffer_dmat, mc->mc_dmamap);
free(mc, M_DEVBUF);
}
static int
mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
{
int i;
debug_called(2);
MLX_IO_ASSERT_LOCKED(sc);
if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_FULL)) {
for (i = 0; i < 13; i++)
MLX_V3_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_FULL);
return(1);
}
return(0);
}
static int
mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
{
debug_called(2);
MLX_IO_ASSERT_LOCKED(sc);
if (MLX_V3_GET_ODBR(sc) & MLX_V3_ODB_SAVAIL) {
*slot = MLX_V3_GET_STATUS_IDENT(sc);
*status = MLX_V3_GET_STATUS(sc);
MLX_V3_PUT_ODBR(sc, MLX_V3_ODB_SAVAIL);
MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
return(1);
}
return(0);
}
static void
mlx_v3_intaction(struct mlx_softc *sc, int action)
{
debug_called(1);
MLX_IO_ASSERT_LOCKED(sc);
switch(action) {
case MLX_INTACTION_DISABLE:
MLX_V3_PUT_IER(sc, 0);
sc->mlx_state &= ~MLX_STATE_INTEN;
break;
case MLX_INTACTION_ENABLE:
MLX_V3_PUT_IER(sc, 1);
sc->mlx_state |= MLX_STATE_INTEN;
break;
}
}
static int
mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2,
int first)
{
u_int8_t fwerror;
debug_called(2);
if (first) {
MLX_V3_PUT_IDBR(sc, MLX_V3_IDB_SACK);
DELAY(1000);
}
if (!(MLX_V3_GET_IDBR(sc) & MLX_V3_IDB_INIT_BUSY))
return(0);
fwerror = MLX_V3_GET_FWERROR(sc);
if (!(fwerror & MLX_V3_FWERROR_PEND))
return(1);
*error = fwerror & ~MLX_V3_FWERROR_PEND;
*param1 = MLX_V3_GET_FWERROR_PARAM1(sc);
*param2 = MLX_V3_GET_FWERROR_PARAM2(sc);
MLX_V3_PUT_FWERROR(sc, 0);
return(2);
}
static int
mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
{
int i;
debug_called(2);
MLX_IO_ASSERT_LOCKED(sc);
if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_FULL)) {
for (i = 0; i < 13; i++)
MLX_V4_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
bus_barrier(sc->mlx_mem, MLX_V4_MAILBOX, MLX_V4_MAILBOX_LENGTH,
BUS_SPACE_BARRIER_WRITE);
MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_HWMBOX_CMD);
return(1);
}
return(0);
}
static int
mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
{
debug_called(2);
MLX_IO_ASSERT_LOCKED(sc);
if (MLX_V4_GET_ODBR(sc) & MLX_V4_ODB_HWSAVAIL) {
*slot = MLX_V4_GET_STATUS_IDENT(sc);
*status = MLX_V4_GET_STATUS(sc);
MLX_V4_PUT_ODBR(sc, MLX_V4_ODB_HWMBOX_ACK);
MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
return(1);
}
return(0);
}
static void
mlx_v4_intaction(struct mlx_softc *sc, int action)
{
debug_called(1);
MLX_IO_ASSERT_LOCKED(sc);
switch(action) {
case MLX_INTACTION_DISABLE:
MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK | MLX_V4_IER_DISINT);
sc->mlx_state &= ~MLX_STATE_INTEN;
break;
case MLX_INTACTION_ENABLE:
MLX_V4_PUT_IER(sc, MLX_V4_IER_MASK & ~MLX_V4_IER_DISINT);
sc->mlx_state |= MLX_STATE_INTEN;
break;
}
}
static int
mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2,
int first)
{
u_int8_t fwerror;
debug_called(2);
if (first) {
MLX_V4_PUT_IDBR(sc, MLX_V4_IDB_SACK);
DELAY(1000);
}
if (!(MLX_V4_GET_IDBR(sc) & MLX_V4_IDB_INIT_BUSY))
return(0);
fwerror = MLX_V4_GET_FWERROR(sc);
if (!(fwerror & MLX_V4_FWERROR_PEND))
return(1);
*error = fwerror & ~MLX_V4_FWERROR_PEND;
*param1 = MLX_V4_GET_FWERROR_PARAM1(sc);
*param2 = MLX_V4_GET_FWERROR_PARAM2(sc);
MLX_V4_PUT_FWERROR(sc, 0);
return(2);
}
static int
mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc)
{
int i;
debug_called(2);
MLX_IO_ASSERT_LOCKED(sc);
if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_EMPTY) {
for (i = 0; i < 13; i++)
MLX_V5_PUT_MAILBOX(sc, i, mc->mc_mailbox[i]);
MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_HWMBOX_CMD);
return(1);
}
return(0);
}
static int
mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status)
{
debug_called(2);
MLX_IO_ASSERT_LOCKED(sc);
if (MLX_V5_GET_ODBR(sc) & MLX_V5_ODB_HWSAVAIL) {
*slot = MLX_V5_GET_STATUS_IDENT(sc);
*status = MLX_V5_GET_STATUS(sc);
MLX_V5_PUT_ODBR(sc, MLX_V5_ODB_HWMBOX_ACK);
MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
return(1);
}
return(0);
}
static void
mlx_v5_intaction(struct mlx_softc *sc, int action)
{
debug_called(1);
MLX_IO_ASSERT_LOCKED(sc);
switch(action) {
case MLX_INTACTION_DISABLE:
MLX_V5_PUT_IER(sc, 0xff & MLX_V5_IER_DISINT);
sc->mlx_state &= ~MLX_STATE_INTEN;
break;
case MLX_INTACTION_ENABLE:
MLX_V5_PUT_IER(sc, 0xff & ~MLX_V5_IER_DISINT);
sc->mlx_state |= MLX_STATE_INTEN;
break;
}
}
static int
mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2,
int first)
{
u_int8_t fwerror;
debug_called(2);
if (first) {
MLX_V5_PUT_IDBR(sc, MLX_V5_IDB_SACK);
DELAY(1000);
}
if (MLX_V5_GET_IDBR(sc) & MLX_V5_IDB_INIT_DONE)
return(0);
fwerror = MLX_V5_GET_FWERROR(sc);
if (!(fwerror & MLX_V5_FWERROR_PEND))
return(1);
*error = fwerror & ~MLX_V5_FWERROR_PEND;
*param1 = MLX_V5_GET_FWERROR_PARAM1(sc);
*param2 = MLX_V5_GET_FWERROR_PARAM2(sc);
MLX_V5_PUT_FWERROR(sc, 0xff);
return(2);
}
static char *mlx_status_messages[] = {
"normal completion",
"irrecoverable data error",
"drive does not exist, or is offline",
"attempt to write beyond end of drive",
"bad data encountered",
"invalid log entry request",
"attempt to rebuild online drive",
"new disk failed during rebuild",
"invalid channel/target",
"rebuild/check already in progress",
"one or more disks are dead",
"invalid or non-redundant drive",
"channel is busy",
"channel is not stopped",
"rebuild successfully terminated",
"unsupported command",
"check condition received",
"device is busy",
"selection or command timeout",
"command terminated abnormally",
""
};
static struct
{
int command;
u_int16_t status;
int msg;
} mlx_messages[] = {
{MLX_CMD_READSG, 0x0001, 1},
{MLX_CMD_READSG, 0x0002, 1},
{MLX_CMD_READSG, 0x0105, 3},
{MLX_CMD_READSG, 0x010c, 4},
{MLX_CMD_WRITESG, 0x0001, 1},
{MLX_CMD_WRITESG, 0x0002, 1},
{MLX_CMD_WRITESG, 0x0105, 3},
{MLX_CMD_READSG_OLD, 0x0001, 1},
{MLX_CMD_READSG_OLD, 0x0002, 1},
{MLX_CMD_READSG_OLD, 0x0105, 3},
{MLX_CMD_WRITESG_OLD, 0x0001, 1},
{MLX_CMD_WRITESG_OLD, 0x0002, 1},
{MLX_CMD_WRITESG_OLD, 0x0105, 3},
{MLX_CMD_LOGOP, 0x0105, 5},
{MLX_CMD_REBUILDASYNC, 0x0002, 6},
{MLX_CMD_REBUILDASYNC, 0x0004, 7},
{MLX_CMD_REBUILDASYNC, 0x0105, 8},
{MLX_CMD_REBUILDASYNC, 0x0106, 9},
{MLX_CMD_REBUILDASYNC, 0x0107, 14},
{MLX_CMD_CHECKASYNC, 0x0002, 10},
{MLX_CMD_CHECKASYNC, 0x0105, 11},
{MLX_CMD_CHECKASYNC, 0x0106, 9},
{MLX_CMD_STOPCHANNEL, 0x0106, 12},
{MLX_CMD_STOPCHANNEL, 0x0105, 8},
{MLX_CMD_STARTCHANNEL, 0x0005, 13},
{MLX_CMD_STARTCHANNEL, 0x0105, 8},
{MLX_CMD_DIRECT_CDB, 0x0002, 16},
{MLX_CMD_DIRECT_CDB, 0x0008, 17},
{MLX_CMD_DIRECT_CDB, 0x000e, 18},
{MLX_CMD_DIRECT_CDB, 0x000f, 19},
{MLX_CMD_DIRECT_CDB, 0x0105, 8},
{0, 0x0104, 14},
{-1, 0, 0}
};
static char *
mlx_diagnose_command(struct mlx_command *mc)
{
static char unkmsg[80];
int i;
for (i = 0; mlx_messages[i].command != -1; i++)
if (((mc->mc_mailbox[0] == mlx_messages[i].command) || (mlx_messages[i].command == 0)) &&
(mc->mc_status == mlx_messages[i].status))
return(mlx_status_messages[mlx_messages[i].msg]);
sprintf(unkmsg, "unknown response 0x%x for command 0x%x", (int)mc->mc_status, (int)mc->mc_mailbox[0]);
return(unkmsg);
}
static struct
{
int hwid;
char *name;
} mlx_controller_names[] = {
{0x01, "960P/PD"},
{0x02, "960PL"},
{0x10, "960PG"},
{0x11, "960PJ"},
{0x12, "960PR"},
{0x13, "960PT"},
{0x14, "960PTL0"},
{0x15, "960PRL"},
{0x16, "960PTL1"},
{0x20, "1164PVX"},
{-1, NULL}
};
static void
mlx_describe_controller(struct mlx_softc *sc)
{
static char buf[80];
char *model;
int i;
for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
if ((sc->mlx_enq2->me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
model = mlx_controller_names[i].name;
break;
}
}
if (model == NULL) {
sprintf(buf, " model 0x%x", sc->mlx_enq2->me_hardware_id & 0xff);
model = buf;
}
device_printf(sc->mlx_dev, "DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
model,
sc->mlx_enq2->me_actual_channels,
sc->mlx_enq2->me_actual_channels > 1 ? "s" : "",
sc->mlx_enq2->me_firmware_id & 0xff,
(sc->mlx_enq2->me_firmware_id >> 8) & 0xff,
(sc->mlx_enq2->me_firmware_id >> 24) & 0xff,
(sc->mlx_enq2->me_firmware_id >> 16) & 0xff,
sc->mlx_enq2->me_mem_size / (1024 * 1024));
if (bootverbose) {
device_printf(sc->mlx_dev, " Hardware ID 0x%08x\n", sc->mlx_enq2->me_hardware_id);
device_printf(sc->mlx_dev, " Firmware ID 0x%08x\n", sc->mlx_enq2->me_firmware_id);
device_printf(sc->mlx_dev, " Configured/Actual channels %d/%d\n", sc->mlx_enq2->me_configured_channels,
sc->mlx_enq2->me_actual_channels);
device_printf(sc->mlx_dev, " Max Targets %d\n", sc->mlx_enq2->me_max_targets);
device_printf(sc->mlx_dev, " Max Tags %d\n", sc->mlx_enq2->me_max_tags);
device_printf(sc->mlx_dev, " Max System Drives %d\n", sc->mlx_enq2->me_max_sys_drives);
device_printf(sc->mlx_dev, " Max Arms %d\n", sc->mlx_enq2->me_max_arms);
device_printf(sc->mlx_dev, " Max Spans %d\n", sc->mlx_enq2->me_max_spans);
device_printf(sc->mlx_dev, " DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", sc->mlx_enq2->me_mem_size,
sc->mlx_enq2->me_cache_size, sc->mlx_enq2->me_flash_size, sc->mlx_enq2->me_nvram_size);
device_printf(sc->mlx_dev, " DRAM type %d\n", sc->mlx_enq2->me_mem_type);
device_printf(sc->mlx_dev, " Clock Speed %dns\n", sc->mlx_enq2->me_clock_speed);
device_printf(sc->mlx_dev, " Hardware Speed %dns\n", sc->mlx_enq2->me_hardware_speed);
device_printf(sc->mlx_dev, " Max Commands %d\n", sc->mlx_enq2->me_max_commands);
device_printf(sc->mlx_dev, " Max SG Entries %d\n", sc->mlx_enq2->me_max_sg);
device_printf(sc->mlx_dev, " Max DP %d\n", sc->mlx_enq2->me_max_dp);
device_printf(sc->mlx_dev, " Max IOD %d\n", sc->mlx_enq2->me_max_iod);
device_printf(sc->mlx_dev, " Max Comb %d\n", sc->mlx_enq2->me_max_comb);
device_printf(sc->mlx_dev, " Latency %ds\n", sc->mlx_enq2->me_latency);
device_printf(sc->mlx_dev, " SCSI Timeout %ds\n", sc->mlx_enq2->me_scsi_timeout);
device_printf(sc->mlx_dev, " Min Free Lines %d\n", sc->mlx_enq2->me_min_freelines);
device_printf(sc->mlx_dev, " Rate Constant %d\n", sc->mlx_enq2->me_rate_const);
device_printf(sc->mlx_dev, " MAXBLK %d\n", sc->mlx_enq2->me_maxblk);
device_printf(sc->mlx_dev, " Blocking Factor %d sectors\n", sc->mlx_enq2->me_blocking_factor);
device_printf(sc->mlx_dev, " Cache Line Size %d blocks\n", sc->mlx_enq2->me_cacheline);
device_printf(sc->mlx_dev, " SCSI Capability %s%dMHz, %d bit\n",
sc->mlx_enq2->me_scsi_cap & (1<<4) ? "differential " : "",
(1 << ((sc->mlx_enq2->me_scsi_cap >> 2) & 3)) * 10,
8 << (sc->mlx_enq2->me_scsi_cap & 0x3));
device_printf(sc->mlx_dev, " Firmware Build Number %d\n", sc->mlx_enq2->me_firmware_build);
device_printf(sc->mlx_dev, " Fault Management Type %d\n", sc->mlx_enq2->me_fault_mgmt_type);
device_printf(sc->mlx_dev, " Features %b\n", sc->mlx_enq2->me_firmware_features,
"\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
}
}
static int
mlx_fw_message(struct mlx_softc *sc, int error, int param1, int param2)
{
switch(error) {
case 0x00:
device_printf(sc->mlx_dev, "physical drive %d:%d not responding\n", param2, param1);
break;
case 0x08:
if (!(sc->mlx_flags & MLX_SPINUP_REPORTED)) {
device_printf(sc->mlx_dev, "spinning up drives...\n");
sc->mlx_flags |= MLX_SPINUP_REPORTED;
}
break;
case 0x30:
device_printf(sc->mlx_dev, "configuration checksum error\n");
break;
case 0x60:
device_printf(sc->mlx_dev, "mirror race recovery failed\n");
break;
case 0x70:
device_printf(sc->mlx_dev, "mirror race recovery in progress\n");
break;
case 0x90:
device_printf(sc->mlx_dev, "physical drive %d:%d COD mismatch\n", param2, param1);
break;
case 0xa0:
device_printf(sc->mlx_dev, "logical drive installation aborted\n");
break;
case 0xb0:
device_printf(sc->mlx_dev, "mirror race on a critical system drive\n");
break;
case 0xd0:
device_printf(sc->mlx_dev, "new controller configuration found\n");
break;
case 0xf0:
device_printf(sc->mlx_dev, "FATAL MEMORY PARITY ERROR\n");
return(1);
default:
device_printf(sc->mlx_dev, "unknown firmware initialisation error %02x:%02x:%02x\n", error, param1, param2);
break;
}
return(0);
}
static struct mlx_sysdrive *
mlx_findunit(struct mlx_softc *sc, int unit)
{
int i;
MLX_CONFIG_ASSERT_LOCKED(sc);
for (i = 0; i < MLX_MAXDRIVES; i++) {
if (sc->mlx_sysdrive[i].ms_disk != 0) {
if (unit == device_get_unit(sc->mlx_sysdrive[i].ms_disk))
return(&sc->mlx_sysdrive[i]);
}
}
return(NULL);
}