#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/bio.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/dtype.h>
#include <sys/rman.h>
#include <dev/raid/amr/amrio.h>
#include <dev/raid/amr/amrreg.h>
#include <dev/raid/amr/amrvar.h>
#include <dev/raid/amr/amr_tables.h>
static int amrd_probe(device_t dev);
static int amrd_attach(device_t dev);
static int amrd_detach(device_t dev);
static d_open_t amrd_open;
static d_strategy_t amrd_strategy;
static d_dump_t amrd_dump;
static struct dev_ops amrd_ops = {
{ "amrd", 0, D_DISK },
.d_open = amrd_open,
.d_strategy = amrd_strategy,
.d_dump = amrd_dump,
};
static devclass_t amrd_devclass;
static device_method_t amrd_methods[] = {
DEVMETHOD(device_probe, amrd_probe),
DEVMETHOD(device_attach, amrd_attach),
DEVMETHOD(device_detach, amrd_detach),
DEVMETHOD_END
};
static driver_t amrd_driver = {
"amrd",
amrd_methods,
sizeof(struct amrd_softc)
};
DRIVER_MODULE_ORDERED(amrd, amr, amrd_driver, &amrd_devclass, NULL, NULL,
SI_ORDER_THIRD);
static int
amrd_open(struct dev_open_args *ap)
{
struct amrd_softc *sc = ap->a_head.a_dev->si_drv1;
debug_called(1);
if (sc == NULL)
return (ENXIO);
if (sc->amrd_controller->amr_state & AMR_STATE_SHUTDOWN)
return(ENXIO);
return (0);
}
static int
amrd_dump(struct dev_dump_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
off_t offset = ap->a_offset;
void *virtual = ap->a_virtual;
size_t length = ap->a_length;
struct amrd_softc *amrd_sc;
struct amr_softc *amr_sc;
int error = 0;
amrd_sc = (struct amrd_softc *)dev->si_drv1;
if (amrd_sc == NULL)
return(ENXIO);
amr_sc = (struct amr_softc *)amrd_sc->amrd_controller;
if (length > 0) {
int driveno = amrd_sc->amrd_drive - amr_sc->amr_drive;
error = amr_dump_blocks(amr_sc, driveno, offset / AMR_BLKSIZE,
virtual, (int)length / AMR_BLKSIZE);
}
return(error);
}
static int
amrd_strategy(struct dev_strategy_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct bio *bio = ap->a_bio;
struct buf *bp = bio->bio_buf;
struct amrd_softc *sc = (struct amrd_softc *)dev->si_drv1;
if (sc == NULL) {
bp->b_error = EINVAL;
goto bad;
}
bio->bio_driver_info = sc;
devstat_start_transaction(&sc->amrd_stats);
amr_submit_bio(sc->amrd_controller, bio);
return (0);
bad:
bp->b_flags |= B_ERROR;
bp->b_resid = bp->b_bcount;
biodone(bio);
return (0);
}
void
amrd_intr(void *data)
{
struct bio *bio = (struct bio *)data;
struct buf *bp = bio->bio_buf;
struct amrd_softc *sc = (struct amrd_softc *)bio->bio_driver_info;
debug_called(2);
devstat_end_transaction_buf(&sc->amrd_stats, bp);
if (bp->b_flags & B_ERROR) {
bp->b_error = EIO;
debug(1, "i/o error\n");
} else {
bp->b_resid = 0;
}
biodone(bio);
}
static int
amrd_probe(device_t dev)
{
debug_called(1);
device_set_desc(dev, "LSILogic MegaRAID logical drive");
return (0);
}
static int
amrd_attach(device_t dev)
{
struct amrd_softc *sc = (struct amrd_softc *)device_get_softc(dev);
device_t parent;
struct disk_info info;
debug_called(1);
parent = device_get_parent(dev);
sc->amrd_controller = (struct amr_softc *)device_get_softc(parent);
sc->amrd_unit = device_get_unit(dev);
sc->amrd_drive = device_get_ivars(dev);
sc->amrd_dev = dev;
device_printf(dev, "%uMB (%u sectors) RAID %d (%s)\n",
sc->amrd_drive->al_size / ((1024 * 1024) / AMR_BLKSIZE),
sc->amrd_drive->al_size, sc->amrd_drive->al_properties & AMR_DRV_RAID_MASK,
amr_describe_code(amr_table_drvstate, AMR_DRV_CURSTATE(sc->amrd_drive->al_state)));
devstat_add_entry(&sc->amrd_stats, "amrd", sc->amrd_unit, AMR_BLKSIZE,
DEVSTAT_NO_ORDERED_TAGS,
DEVSTAT_TYPE_STORARRAY | DEVSTAT_TYPE_IF_OTHER,
DEVSTAT_PRIORITY_ARRAY);
sc->amrd_dev_t = disk_create(sc->amrd_unit, &sc->amrd_disk, &amrd_ops);
sc->amrd_dev_t->si_drv1 = sc;
sc->amrd_dev_t->si_iosize_max = (AMR_NSEG - 1) * PAGE_SIZE;
bzero(&info, sizeof(info));
info.d_media_blksize = AMR_BLKSIZE;
info.d_media_blocks = sc->amrd_drive->al_size;
info.d_type = DTYPE_SCSI;
info.d_secpertrack = sc->amrd_drive->al_sectors;
info.d_nheads = sc->amrd_drive->al_heads;
info.d_ncylinders = sc->amrd_drive->al_cylinders;
info.d_secpercyl = sc->amrd_drive->al_sectors * sc->amrd_drive->al_heads;
disk_setdiskinfo(&sc->amrd_disk, &info);
return (0);
}
static int
amrd_detach(device_t dev)
{
struct amrd_softc *sc = (struct amrd_softc *)device_get_softc(dev);
debug_called(1);
#if 0
if (sc->amrd_disk->d_flags & DISKFLAG_OPEN)
return(EBUSY);
#endif
devstat_remove_entry(&sc->amrd_stats);
disk_destroy(&sc->amrd_disk);
return(0);
}