Contents

  1. Disk driver template when using dksubr library
    1. Declaration
    2. Pseudo-Device Attachment and Standard driver interface
    3. I/O callback
    4. Startup and Shutdown
    5. Autoconf interface
    6. Kernel module interface
    7. DK driver interface
    8. Alternative when using a separate I/O thread

Disk driver template when using dksubr library

Declaration


static dev_type_open(xxxopen);
static dev_type_close(xxxclose);
static dev_type_read(xxxread);
static dev_type_write(xxxwrite);
static dev_type_ioctl(xxxioctl);
static dev_type_strategy(xxxstrategy);
static dev_type_dump(xxxdump);
static dev_type_size(xxxsize);
static dev_type_discard(xxxdiscard);

const struct bdevsw xxx_bdevsww= {
    .d_open = xxxopen,
    .d_close = xxxclose,
    .d_strategy = xxxstrategy,
    .d_ioctl = xxxioctl,
    .d_dump = xxxdump,
    .d_psize = xxxsize,
    .d_discard = xxxdiscard,
    .d_flag = D_DISK | D_MPSAFE
};

const struct cdevsw xxx_cdevsw = {
    .d_open = xxxopen,
    .d_close = xxxclose,
    .d_read = xxxread,
    .d_write = xxxwrite,
    .d_ioctl = xxxioctl,
    .d_stop = nostop,
    .d_tty = notty,
    .d_poll = nopoll,
    .d_mmap = nommap,
    .d_kqfilter = nokqfilter,
    .d_discard = xxxdiscard,
    .d_flag = D_DISK | D_MPSAFE
};

static void     xxxminphys(struct buf *bp);
static int      xxxdiskstart(device_t, struct buf *bp);
static void     xxx_iosize(device_t, int *);
static int      xxx_dumpblocks(device_t, void *, daddr_t, int);
static int      xxx_lastclose(device_t);
static int      xxx_discard(device_t, off_t, off_t);

static const struct dkdriver xxxdkdriver = {
    .d_open = xxxopen,
    .d_close = xxxclose,
    .d_strategy = xxxstrategy,
    .d_minphys  = xxxminphys,
    .d_diskstart = xxx_diskstart,
    .d_discard = xxx_discard,
    .d_dumpblocks = xxx_dumpblocks, /* optional */
    .d_iosize = xxx_iosize,     /* optional */
    .d_lastclose = xxx_lastclose,   /* optional */
    .d_firstopen = xxx_firstopen,   /* optional */
    .d_label = xxx_label,       /* optional */
};

static int      xxx_match(device_t, cfdata_t, void *);
static void     xxx_attach(device_t, device_t, void *);
static int      xxx_detach(device_t, int);
static int      xxx_activate(device_t, enum devact);

struct xxx_softc {
    struct dk_softc         sc_dksc; /* generic disk interface */
    // private data
};

CFATTACH_DECL3_NEW(xxx, sizeof(struct xxx_softc),
    xxx_match, xxx_attach, xxx_detach, NULL, NULL, NULL, DVF_DETACH_SHUTDOWN);
    extern struct cfdriver xxx_cd;  
extern struct   cfdriver xxx_cd;

#ifdef PSEUDODEVICE
static void     xxxattach(int);
static device_t xxx_create(int);
static int      xxx_destroy(device_t);
#endif

static int      xxx_init(device_t);
static int      xxx_finish(device_t);

static int      xxxdone(struct buf *);


#define DEVPROLOG \
    struct xxx_softc *sc; \
    struct dk_softc *dksc; \
    int unit; \
\
    unit = DISKUNIT(dev); \
    if ((sc = device_lookup_private(&xxx_cd, unit)) == NULL) \
        return ENXIO; \
    dksc = &sc->sc_dksc

Pseudo-Device Attachment and Standard driver interface


#ifdef PSEUDODEVICE
void
xxxattach(int num)
{
#ifndef _MODULE
    int error;

    error = config_cfattach_attach(xxx_cd.cd_name, &xxx_ca);
    if (error) {
        aprint_error("%s: unable to register cfattach %d\n",
            xxx_cd.cd_name, error);
        return;
    }

    // some global initialization
#endif
}

static device_t
xxx_create(int unit)
{
    cfdata_t cf;
    device_t dv;

    cf = malloc(sizeof(*cf), M_DEVBUF, M_WAITOK);
    cf->cf_name = xxx_cd.cd_name;
    cf->cf_atname = xxx_cd.cd_name;
    cf->cf_unit = unit;
    cf->cf_fstate = FSTATE_STAR;

    dv = config_attach_pseudo(cf);
    if (dv == NULL) {
        aprint_error("%s: failed to attach pseudo device\n",
            xxx_cd.cd_name);
        free(cf, M_DEVBUF);
    }

    return dv;
}

static int
xxx_destroy(device_t dv)
{
    int error;
    cfdata_t cf;

    cf = device_cfdata(dv);
    error = config_detach(dev, DETACH_QUIET);
    if (error)
        return error;
    free(cf, M_DEVBUF);
    return 0;
}
#endif

static int
xxxopen(dev_t dev, int flags, int fmt, struct lwp *l)
{
    DEVPROLOG;

    return dk_open(dksc, dev, flags, fmt, l);
}

static int
xxxclose(dev_t dev, int flags, int fmt, struct lwp *l)
{
    DEVPROLOG;

    return dk_close(dksc, dev, flags, fmt, l);
}

static int
xxxread(dev_t dev, struct uio *uio, int ioflag)
{

    return physio(xxxstrategy, NULL, dev, B_READ, xxxminphys, uio);
}

static int
xxxwrite(dev_t dev, struct uio *uio, int ioflag)
{

    return physio(xxxstrategy, NULL, dev, B_WRITE, xxxminphys, uio);
}

static int
xxxioctl(dev_t dev, u_long cmd, void *addr, int32_t flag, struct lwp *l)
{
    int error;
    DEVPROLOG;

    error = dk_ioctl(dksc, dev, cmd, addr, flag, l);
    if (error != EPASSTHROUGH)
        return error;

    error = 0;

    switch (cmd) {
    // private IOCTLs
    default:
        error = ENOTTY;
    }

    return error;
}

static void
xxxstrategy(struct buf *bp)
{
    DEVPROLOG;

    dk_strategy(dksc, bp);
}

static int
xxxdiscard(dev_t dev, off_t pos, off_t len)
{
    DEVPROLOG;

    return dk_discard(dksc, dev, pos, len);
}

static int
xxxsize(dev_t dev)
{
    DEVPROLOG;

    return dk_size(dksc, dev);
}

static int
xxxdump(dev_t dev, daddr_t blkno, void *va, size_t size)
{
    DEVPROLOG;

    return dk_dump(dksc, dev, blkno, va, size);
}

static void
xxxminphys(struct buf *bp)
{
    struct xxx_softc *sc;
    int unit;

    unit = DISKUNIT(bp->b_dev);
    if ((sc = device_lookup_private(&xxx_cd, unit)) == NULL)
        return;

    xxx_iosize(sc->sc_dv, &bp->b_count);
    minphys(bp);
}

I/O callback


static void
xxxdone(struct xxx_softc *sc, struct buf *bp)
{
    struct dk_softc *dksc = &sc->sc_dksc;

    dk_done(dksc, bp);
    dk_start(dksc, NULL);
}

Startup and Shutdown


static int
xxx_init(device_t self)
{
    struct xxx_softc *sc = device_private(self);
    struct dk_softc *dksc = &sc->sc_dk;

    dk_init(dksc, self, DKTYPE_xxx);
    disk_init(&dksc->sc_dkdev, dksc->sc_xname, &xxdkdriver);

    dk_attach(dksc);
    disk_attach(&dksc->sc_dkdev);
    // initialize dksc->sc_dkdev.dk_geom

    bufq_alloc(&dksc->sc_bufq, BUFQ_DISK_DEFAULT_STRAT, BUFQ_SORT_RAWBLOCK);

    // possibly deferred with config_interrupts()
    dkwedge_discover(&dksc->sc_dkdev);

    return 0;
}

static int
xxx_finish(device_t self)
{
    struct xxx_softc *sc = device_private(self);
    struct dk_softc *dksc = &sc->sc_dksc;

    dkwedge_delall(&dksc->sc_dkdev);

    dk_drain(dksc);
    bufq_free(dksc->sc_bufq);

    dk_detach(dksc);
    disk_detach(&dksc->sc_dkdev);
    disk_destroy(&dksc->sc_dkdev);

    return 0;
}

Autoconf interface


static int
xxx_match(device_t self, cfdata_t cfdata, void *aux)
{
    return 1;
}

static void
xxx_attach(device_t parent, device_t self, void *aux)
{
#ifndef _MODULE
    xxx_init(self);
#endif
}

static void
xxx_detach(device_t self, int flags)
{
#ifndef _MODULE
    xxx_finish(self);
#endif
}

static int
xxx_activate(device_t self, enum devact act)
{
    // switch (act) {
    // case DVACT_ACTIVATE:
    //  return 0;
    // case DVACT_DEACTIVATE:
    //  return 0;
    // }

    return EOPNOTSUPP;
}

Kernel module interface


MODULE(MODULE_CLASS_DRIVER, xxx, "dk_subr");

static int
xxx_modcmd(modcmd_t cmd, void *data)
{
    int error;
#ifdef _MODULE
    int bmajor, cmajor;
#endif

    error = 0;
    switch (cmd) {
    case MODULE_CMD_INIT:
#ifdef _MODULE
        bmajor = cmajor = -1;
        error = devsw_attach("xxx", &xxx_bdevsw, &bmajor,
            &xxx_cdevsw, &cmajor);
        if (error) {
            aprint_error("%s: devsw_attach failed %d\n",
                xxx_cd.cd_name, error);
            break;
        }
        error = config_cfdriver_attach(&xxx_cd);
        if (error) {
            aprint_error("%s: config_cfdriver_attach failed %d\n",
                xxx_cd.cd_name, error);
            devsw_detach(&xxx_bdevsw, &xxx_cdevsw);
            break;
        }
        error = config_cfattach_attach(&xxx_cd);
        if (error) {
            aprint_error("%s: config_cfattach_attach failed %d\n",
                xxx_cd.cd_name, error);
            config_cfdriver_detach(&xxx_cd);
            devsw_detach(&xxx_bdevsw, &xxx_cdevsw);
            break;
        }
#endif
        // some global initialization

#ifdef PSEUDODEVICE
        xxxattach(0);
#endif
        break;
    case MODULE_CMD_FINI:
        // outside of #ifdef _MODULE to allow removal of builtins
        // some global finalization
#ifdef _MODULE
        error = config_cfattach_detach(&xxx_cd.cd_name, &xxx_ca);
        if (error)
            break;
        config_cfdriver_detach(&xxx_cd);
        devsw_detach(&xxx_bdevsw, &xxx_cdevsw);
#endif
        break;
    case MODULE_CMD_STAT:
        error = ENOTTY;
        break;
    default:
        error = ENOTTY;
        break;
    }

    return error;
}

DK driver interface


static int
xxx_firstopen(device_t dv)
{
    // private startup

    return 0;
}

static int
xxx_lastclose(device_t dv)
{
    // private cleanup

    return 0;
}

static int
xxx_diskstart(device_t dv, struct buf *bp)
{
    // issue I/O for bp
    // return EAGAIN if controller busy
}

static int
xxx_dumpblocks(device_t dv, void *va, daddr_t blkno, int nblk)
{
    // issue polling I/O to dump a page
    // return error
}

static void
xxx_iosize(device_t dv, int *countp)
{
    // limit *countp as necessary
}

static int
xxx_discard(device_t dv, off_t pos, off_t len)
{
    // issue request to discard bytes
    // return error
}

static void
xxx_label(device_t dv, struct disklabel *lp)
{
    // lp is initialized for generic disk
    // augment with driver specific information
}


Alternative when using a separate I/O thread


static void
xxxstrategy(struct buf *bp)
{
    DEVPROLOG;

    if (dk_strategy_defer(dksc, bp))
        return;

    // wake up I/O thread
}

static void
xxxdone(struct xxx_softc *sc, struct buf *bp)
{
    struct dk_softc *dksc = &sc->sc_dksc;

    dk_done(dksc, bp);
    // wake up I/O thread
}

static void
xxx_iothread(struct dk_softc *dksc)
{
    while (!shutting_down) {
        if (dk_strategy_pending(dksc))
            dk_start(dksc, NULL);
        // sleep
    }
}