NetBSD Wiki/users/mlelstv/
disk-driver-template
Contents
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
}
}
Add a comment