File:  [NetBSD Developer Wiki] / wikisrc / users / mlelstv / disk-driver-template.mdwn
Revision 1.12: download - view: text, annotated - select for diffs
Thu Dec 8 11:39:34 2016 UTC (3 years, 2 months ago) by mlelstv
Branches: MAIN
CVS tags: HEAD
Add d_label routine to augment disklabel with driver specific information.

**Contents**

[[!toc levels=2]]

# Disk driver template when using dksubr library

## Declaration

<pre><code>
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
</code></pre>

----

## Pseudo-Device Attachment and Standard driver interface

<pre><code>
#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);
}
</code></pre>

----

## I/O callback

<pre><code>
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);
}
</code></pre>

----

## Startup and Shutdown

<pre><code>
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;
}
</code></pre>

----

## Autoconf interface

<pre><code>
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;
}
</code></pre>

----

## Kernel module interface

<pre><code>
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;
		}
		// 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;
}
</code></pre>

----

## DK driver interface

<pre><code>
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
}

</code></pre>

----

## Alternative when using a separate I/O thread

<pre><code>
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
	}
}
</code></pre>

----

CVSweb for NetBSD wikisrc <wikimaster@NetBSD.org> software: FreeBSD-CVSweb