svn commit: r189606 - in head/sys: conf dev/cfi

Sam Leffler sam at FreeBSD.org
Mon Mar 9 16:16:03 PDT 2009


Author: sam
Date: Mon Mar  9 23:16:02 2009
New Revision: 189606
URL: http://svn.freebsd.org/changeset/base/189606

Log:
  Add cfid, a disk interface to CFI flash devices; this enables construction
  of flash-based filesystems.
  
  Note this is not interlocked against the raw CFI device.

Added:
  head/sys/dev/cfi/cfi_disk.c   (contents, props changed)
Modified:
  head/sys/conf/files
  head/sys/dev/cfi/cfi_core.c
  head/sys/dev/cfi/cfi_dev.c
  head/sys/dev/cfi/cfi_var.h

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Mon Mar  9 23:10:19 2009	(r189605)
+++ head/sys/conf/files	Mon Mar  9 23:16:02 2009	(r189606)
@@ -702,6 +702,7 @@ dev/cardbus/cardbus_cis.c	optional cardb
 dev/cardbus/cardbus_device.c	optional cardbus
 dev/cfi/cfi_core.c		optional cfi
 dev/cfi/cfi_dev.c		optional cfi
+dev/cfi/cfi_disk.c		optional cfid
 dev/ciss/ciss.c			optional ciss
 dev/cm/smc90cx6.c		optional cm
 dev/cmx/cmx.c			optional cmx

Modified: head/sys/dev/cfi/cfi_core.c
==============================================================================
--- head/sys/dev/cfi/cfi_core.c	Mon Mar  9 23:10:19 2009	(r189605)
+++ head/sys/dev/cfi/cfi_core.c	Mon Mar  9 23:16:02 2009	(r189606)
@@ -51,6 +51,7 @@ extern struct cdevsw cfi_cdevsw;
 
 char cfi_driver_name[] = "cfi";
 devclass_t cfi_devclass;
+devclass_t cfi_diskclass;
 
 uint32_t
 cfi_read(struct cfi_softc *sc, u_int ofs)
@@ -284,6 +285,10 @@ cfi_attach(device_t dev) 
 	    "%s%u", cfi_driver_name, u);
 	sc->sc_nod->si_drv1 = sc;
 
+	device_add_child(dev, "cfid",
+	    devclass_find_free_unit(cfi_diskclass, 0));
+	bus_generic_attach(dev);
+
 	return (0);
 }
 

Modified: head/sys/dev/cfi/cfi_dev.c
==============================================================================
--- head/sys/dev/cfi/cfi_dev.c	Mon Mar  9 23:10:19 2009	(r189605)
+++ head/sys/dev/cfi/cfi_dev.c	Mon Mar  9 23:16:02 2009	(r189606)
@@ -74,7 +74,7 @@ struct cdevsw cfi_cdevsw = {
  * or the process stops writing. At that time we write the whole
  * sector to flash (see cfi_block_finish).
  */
-static int
+int
 cfi_block_start(struct cfi_softc *sc, u_int ofs)
 {
 	union {
@@ -124,7 +124,7 @@ cfi_block_start(struct cfi_softc *sc, u_
  * Finish updating the current block/sector by writing the compound
  * set of changes to the flash.
  */
-static int
+int
 cfi_block_finish(struct cfi_softc *sc)
 {
 	int error;

Added: head/sys/dev/cfi/cfi_disk.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/cfi/cfi_disk.c	Mon Mar  9 23:16:02 2009	(r189606)
@@ -0,0 +1,319 @@
+/*-
+ * Copyright (c) 2009 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bio.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>   
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/sysctl.h>
+#include <sys/taskqueue.h>
+
+#include <machine/bus.h>
+
+#include <dev/cfi/cfi_var.h>
+
+#include <geom/geom_disk.h>
+
+struct cfi_disk_softc {
+	struct cfi_softc *parent;
+	struct disk	*disk;
+	int		flags;
+#define	CFI_DISK_OPEN	0x0001
+	struct bio_queue_head bioq;	/* bio queue */
+	struct mtx	qlock;		/* bioq lock */
+	struct taskqueue *tq;		/* private task queue for i/o request */
+	struct task	iotask;		/* i/o processing */
+};
+
+#define	CFI_DISK_SECSIZE	512
+#define	CFI_DISK_MAXIOSIZE	65536
+
+static int cfi_disk_detach(device_t);
+static int cfi_disk_open(struct disk *);
+static int cfi_disk_close(struct disk *);
+static void cfi_io_proc(void *, int);
+static void cfi_disk_strategy(struct bio *);
+static int cfi_disk_ioctl(struct disk *, u_long, void *, int, struct thread *);
+
+static int
+cfi_disk_probe(device_t dev)
+{
+	return 0;
+}
+
+static int
+cfi_disk_attach(device_t dev)
+{
+	struct cfi_disk_softc *sc = device_get_softc(dev);
+
+	sc->parent = device_get_softc(device_get_parent(dev));
+	/* validate interface width; assumed by other code */
+	if (sc->parent->sc_width != 1 &&
+	    sc->parent->sc_width != 2 &&
+	    sc->parent->sc_width != 4)
+		return EINVAL;
+
+	sc->disk = disk_alloc();
+	if (sc->disk == NULL)
+		return ENOMEM;
+	sc->disk->d_name = "cfid";
+	sc->disk->d_unit = device_get_unit(dev);
+	sc->disk->d_open = cfi_disk_open;
+	sc->disk->d_close = cfi_disk_close;
+	sc->disk->d_strategy = cfi_disk_strategy;
+	sc->disk->d_ioctl = cfi_disk_ioctl;
+	sc->disk->d_dump = NULL;		/* NB: no dumps */
+	sc->disk->d_sectorsize = CFI_DISK_SECSIZE;
+	sc->disk->d_mediasize = sc->parent->sc_size;
+	sc->disk->d_maxsize = CFI_DISK_MAXIOSIZE;
+	/* NB: use stripesize to hold the erase/region size */
+	if (sc->parent->sc_regions)
+		sc->disk->d_stripesize = sc->parent->sc_region->r_blksz;
+	else
+		sc->disk->d_stripesize = sc->disk->d_mediasize;
+	sc->disk->d_drv1 = sc;
+	disk_create(sc->disk, DISK_VERSION);
+
+	mtx_init(&sc->qlock, "CFID I/O lock", NULL, MTX_DEF);
+	bioq_init(&sc->bioq);
+
+	sc->tq = taskqueue_create("cfid_taskq", M_NOWAIT,
+		taskqueue_thread_enqueue, &sc->tq);
+	taskqueue_start_threads(&sc->tq, 1, PI_DISK, "cfid taskq");
+
+	TASK_INIT(&sc->iotask, 0, cfi_io_proc, sc);
+
+	return 0;
+}
+
+static int
+cfi_disk_detach(device_t dev)
+{
+	struct cfi_disk_softc *sc = device_get_softc(dev);
+
+	if (sc->flags & CFI_DISK_OPEN)
+		return EBUSY;
+	taskqueue_free(sc->tq);
+	/* XXX drain bioq */
+	disk_destroy(sc->disk);
+	mtx_destroy(&sc->qlock);
+	return 0;
+}
+
+static int
+cfi_disk_open(struct disk *dp)
+{
+	struct cfi_disk_softc *sc = dp->d_drv1;
+
+	/* XXX no interlock with /dev/cfi */
+	sc->flags |= CFI_DISK_OPEN;
+	return 0;
+}
+
+static int
+cfi_disk_close(struct disk *dp)
+{
+	struct cfi_disk_softc *sc = dp->d_drv1;
+
+	sc->flags &= ~CFI_DISK_OPEN;
+	return 0;
+}
+
+static void
+cfi_disk_read(struct cfi_softc *sc, struct bio *bp)
+{
+	long resid;
+
+	KASSERT(sc->sc_width == 1 || sc->sc_width == 2 || sc->sc_width == 4,
+	    ("sc_width %d", sc->sc_width));
+
+	if (sc->sc_writing) {
+		bp->bio_error = cfi_block_finish(sc);
+		if (bp->bio_error) {
+			bp->bio_flags |= BIO_ERROR;
+			goto done;
+		}
+	}
+	if (bp->bio_offset > sc->sc_size) {
+		bp->bio_flags |= BIO_ERROR;
+		bp->bio_error = EIO;
+		goto done;
+	}
+	resid = bp->bio_bcount;
+	if (sc->sc_width == 1) {
+		uint8_t *dp = (uint8_t *)bp->bio_data;
+		while (resid > 0 && bp->bio_offset < sc->sc_size) {
+			*dp++ = cfi_read(sc, bp->bio_offset);
+			bp->bio_offset += 1, resid -= 1;
+		}
+	} else if (sc->sc_width == 2) {
+		uint16_t *dp = (uint16_t *)bp->bio_data;
+		while (resid > 0 && bp->bio_offset < sc->sc_size) {
+			*dp++ = cfi_read(sc, bp->bio_offset);
+			bp->bio_offset += 2, resid -= 2;
+		}
+	} else {
+		uint32_t *dp = (uint32_t *)bp->bio_data;
+		while (resid > 0 && bp->bio_offset < sc->sc_size) {
+			*dp++ = cfi_read(sc, bp->bio_offset);
+			bp->bio_offset += 4, resid -= 4;
+		}
+	}
+	bp->bio_resid = resid;
+done:
+	biodone(bp);
+}
+
+static void
+cfi_disk_write(struct cfi_softc *sc, struct bio *bp)
+{
+	long resid;
+	u_int top;
+
+	KASSERT(sc->sc_width == 1 || sc->sc_width == 2 || sc->sc_width == 4,
+	    ("sc_width %d", sc->sc_width));
+
+	if (bp->bio_offset > sc->sc_size) {
+		bp->bio_flags |= BIO_ERROR;
+		bp->bio_error = EIO;
+		goto done;
+	}
+	resid = bp->bio_bcount;
+	while (resid > 0) {
+		/*
+		 * Finish the current block if we're about to write
+		 * to a different block.
+		 */
+		if (sc->sc_writing) {
+			top = sc->sc_wrofs + sc->sc_wrbufsz;
+			if (bp->bio_offset < sc->sc_wrofs ||
+			    bp->bio_offset >= top)
+				cfi_block_finish(sc);
+		}
+
+		/* Start writing to a (new) block if applicable. */
+		if (!sc->sc_writing) {
+			bp->bio_error = cfi_block_start(sc, bp->bio_offset);
+			if (bp->bio_error) {
+				bp->bio_flags |= BIO_ERROR;
+				goto done;
+			}
+		}
+
+		top = sc->sc_wrofs + sc->sc_wrbufsz;
+		bcopy(bp->bio_data,
+		    sc->sc_wrbuf + bp->bio_offset - sc->sc_wrofs,
+		    MIN(top - bp->bio_offset, resid));
+		resid -= MIN(top - bp->bio_offset, resid);
+	}
+	bp->bio_resid = resid;
+done:
+	biodone(bp);
+}
+
+static void
+cfi_io_proc(void *arg, int pending)
+{
+	struct cfi_disk_softc *sc = arg;
+	struct cfi_softc *cfi = sc->parent;
+	struct bio *bp;
+
+	for (;;) {
+		mtx_lock(&sc->qlock);
+		bp = bioq_takefirst(&sc->bioq);
+		mtx_unlock(&sc->qlock);
+		if (bp == NULL)
+			break;
+
+		switch (bp->bio_cmd) {
+		case BIO_READ:
+			cfi_disk_read(cfi, bp);
+			break;
+		case BIO_WRITE:
+			cfi_disk_write(cfi, bp);
+			break;
+		}
+	}
+}
+
+static void
+cfi_disk_strategy(struct bio *bp)
+{
+	struct cfi_disk_softc *sc = bp->bio_disk->d_drv1;
+
+	if (sc == NULL)
+		goto invalid;
+	if (bp->bio_bcount == 0) {
+		bp->bio_resid = bp->bio_bcount;
+		biodone(bp);
+		return;
+	}
+	switch (bp->bio_cmd) {
+	case BIO_READ:
+	case BIO_WRITE:
+		mtx_lock(&sc->qlock);
+		/* no value in sorting requests? */
+		bioq_insert_tail(&sc->bioq, bp);
+		mtx_unlock(&sc->qlock);
+		taskqueue_enqueue(sc->tq, &sc->iotask);
+		return;
+	}
+	/* fall thru... */
+invalid:
+	bp->bio_flags |= BIO_ERROR;
+	bp->bio_error = EINVAL;
+	biodone(bp);
+}
+
+static int
+cfi_disk_ioctl(struct disk *dp, u_long cmd, void *data, int fflag,
+	struct thread *td)
+{
+	return EINVAL;
+}
+
+static device_method_t cfi_disk_methods[] = {
+	DEVMETHOD(device_probe,		cfi_disk_probe),
+	DEVMETHOD(device_attach,	cfi_disk_attach),
+	DEVMETHOD(device_detach,	cfi_disk_detach),
+
+	{ 0, 0 }
+};
+static driver_t cfi_disk_driver = {
+	"cfid",
+	cfi_disk_methods,
+	sizeof(struct cfi_disk_softc),
+};
+DRIVER_MODULE(cfid, cfi, cfi_disk_driver, cfi_diskclass, 0, NULL);

Modified: head/sys/dev/cfi/cfi_var.h
==============================================================================
--- head/sys/dev/cfi/cfi_var.h	Mon Mar  9 23:10:19 2009	(r189605)
+++ head/sys/dev/cfi/cfi_var.h	Mon Mar  9 23:16:02 2009	(r189606)
@@ -65,6 +65,7 @@ struct cfi_softc {
 
 extern char cfi_driver_name[];
 extern devclass_t cfi_devclass;
+extern devclass_t cfi_diskclass;
 
 int cfi_probe(device_t);
 int cfi_attach(device_t);
@@ -73,6 +74,8 @@ int cfi_detach(device_t);
 uint32_t cfi_read(struct cfi_softc *, u_int);
 uint8_t cfi_read_qry(struct cfi_softc *, u_int);
 int cfi_write_block(struct cfi_softc *);
+int cfi_block_start(struct cfi_softc *, u_int);
+int cfi_block_finish(struct cfi_softc *);
 
 #ifdef CFI_SUPPORT_STRATAFLASH
 int	cfi_intel_get_factory_pr(struct cfi_softc *sc, uint64_t *);


More information about the svn-src-head mailing list