summaryrefslogtreecommitdiff
path: root/sys/dev/flash
diff options
context:
space:
mode:
authorahoka <ahoka@NetBSD.org>2011-02-26 18:07:13 +0000
committerahoka <ahoka@NetBSD.org>2011-02-26 18:07:13 +0000
commitb84aee73028fe564bfa78c0c2c568359ca7288a8 (patch)
treefe512736abd91f2864d5b0021de9e4af277634cd /sys/dev/flash
parent11ce3c28dd5240489a3d69366d65cc854b754c35 (diff)
Import the Flash and NAND subsytem code contributed by the University
of Szeged, Hungary. The commit includes: - Flash layer, which gives a common API to access flash devices - NAND controller subsystem for the flash layer - An example OMAP driver which is used on BeagleBoard or alike ARM boards
Diffstat (limited to 'sys/dev/flash')
-rw-r--r--sys/dev/flash/files.flash9
-rw-r--r--sys/dev/flash/flash.c695
-rw-r--r--sys/dev/flash/flash.h168
3 files changed, 872 insertions, 0 deletions
diff --git a/sys/dev/flash/files.flash b/sys/dev/flash/files.flash
new file mode 100644
index 00000000000..7871e2d9031
--- /dev/null
+++ b/sys/dev/flash/files.flash
@@ -0,0 +1,9 @@
+# $NetBSD: files.flash,v 1.1 2011/02/26 18:07:30 ahoka Exp $
+
+define flashbus { [offset = 0], [size = 0], [readonly = 0] }
+
+device flash
+attach flash at flashbus
+file dev/flash/flash.c flash
+
+defflag opt_flash.h FLASH_STATIC_PARTITIONS
diff --git a/sys/dev/flash/flash.c b/sys/dev/flash/flash.c
new file mode 100644
index 00000000000..33007cfe339
--- /dev/null
+++ b/sys/dev/flash/flash.c
@@ -0,0 +1,695 @@
+/* $NetBSD: flash.c,v 1.1 2011/02/26 18:07:30 ahoka Exp $ */
+
+/*-
+ * Copyright (c) 2011 Department of Software Engineering,
+ * University of Szeged, Hungary
+ * Copyright (c) 2011 Adam Hoka <ahoka@NetBSD.org>
+ * Copyright (c) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * 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.
+ */
+
+/*-
+ * Framework for storage devices based on Flash technology
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: flash.c,v 1.1 2011/02/26 18:07:30 ahoka Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/device.h>
+#include <sys/conf.h>
+#include <sys/kmem.h>
+#include <sys/uio.h>
+#include <sys/kernel.h>
+
+#include <sys/atomic.h>
+#include <sys/buf.h>
+#include <sys/bufq.h>
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/malloc.h>
+#include <sys/reboot.h>
+
+#include <sys/flashio.h>
+#include "flash.h"
+
+#define FLASH_DEBUG 1
+#ifdef FLASH_DEBUG
+#define DPRINTF(x) if (flashdebug) printf x
+#define DPRINTFN(n,x) if (flashdebug>(n)) printf x
+int flashdebug = FLASH_DEBUG;
+#else
+#define DPRINTF(x)
+#define DPRINTFN(n,x)
+#endif
+
+extern struct cfdriver flash_cd;
+
+dev_type_open(flashopen);
+dev_type_close(flashclose);
+dev_type_read(flashread);
+dev_type_write(flashwrite);
+dev_type_ioctl(flashioctl);
+dev_type_strategy(flashstrategy);
+dev_type_dump(flashdump);
+dev_type_size(flashsize);
+
+int flash_print(void *aux, const char *pnp);
+
+bool flash_shutdown(device_t dev, int how);
+int flash_nsectors(struct buf *bp);
+int flash_sector(struct buf *bp);
+
+static inline off_t flash_get_part_offset(struct flash_softc *fl,
+ size_t poffset);
+
+int flash_match(device_t parent, cfdata_t match, void *aux);
+void flash_attach(device_t parent, device_t self, void *aux);
+int flash_detach(device_t device, int flags);
+
+CFATTACH_DECL_NEW(flash, sizeof(struct flash_softc),
+ flash_match, flash_attach, flash_detach, NULL);
+
+/**
+ * Block device's operation
+ */
+const struct bdevsw flash_bdevsw = {
+ .d_open = flashopen,
+ .d_close = flashclose,
+ .d_strategy = flashstrategy,
+ .d_ioctl = flashioctl,
+ .d_dump = flashdump,
+ .d_psize = flashsize,
+ .d_flag = D_DISK | D_MPSAFE
+};
+
+/**
+ * Character device's operations
+ */
+const struct cdevsw flash_cdevsw = {
+ .d_open = flashopen,
+ .d_close = flashclose,
+ .d_read = flashread,
+ .d_write = flashwrite,
+ .d_ioctl = flashioctl,
+ .d_stop = nostop,
+ .d_tty = notty,
+ .d_poll = nopoll,
+ .d_mmap = nommap,
+ .d_kqfilter = nokqfilter,
+ .d_flag = D_DISK | D_MPSAFE
+};
+
+/* ARGSUSED */
+int
+flash_match(device_t parent, cfdata_t match, void *aux)
+{
+ /* pseudo device, always attaches */
+ return 1;
+}
+
+/* ARGSUSED */
+void
+flash_attach(device_t parent, device_t self, void *aux)
+{
+ struct flash_softc *sc = device_private(self);
+ struct flash_attach_args *faa = aux;
+ char pbuf[2][sizeof("9999 KB")];
+
+ sc->sc_dev = self;
+ sc->sc_parent_dev = parent;
+ sc->flash_if = faa->flash_if;
+ sc->hw_softc = device_private(parent);
+
+ format_bytes(pbuf[0], sizeof(pbuf[0]), sc->flash_if->size);
+ format_bytes(pbuf[1], sizeof(pbuf[1]), sc->flash_if->erasesize);
+
+ aprint_naive("\n");
+
+ switch (sc->flash_if->type) {
+ case FLASH_TYPE_NOR:
+ aprint_normal(": %s NOR flash\n", pbuf[0]);
+ break;
+
+ case FLASH_TYPE_NAND:
+ aprint_normal(": %s NAND flash\n", pbuf[0]);
+ break;
+
+ default:
+ aprint_normal(": %s unknown flash\n", pbuf[0]);
+ }
+
+ aprint_normal_dev(sc->sc_dev,
+ "size: 0x%jx, offset: 0x%jx",
+ (uintmax_t )sc->flash_if->partition.part_size,
+ (uintmax_t )sc->flash_if->partition.part_offset);
+
+ if (sc->flash_if->partition.part_flags & FLASH_PART_READONLY) {
+ sc->sc_readonly = true;
+ aprint_normal(", read only");
+ } else {
+ sc->sc_readonly = false;
+ }
+
+ aprint_normal("\n");
+
+ if (sc->flash_if->partition.part_size == 0) {
+ aprint_error_dev(self,
+ "partition size must be larger than 0\n");
+ return;
+ }
+
+ switch (sc->flash_if->type) {
+ case FLASH_TYPE_NOR:
+ aprint_normal_dev(sc->sc_dev,
+ "erase size %s bytes, write size %d bytes\n",
+ pbuf[1], sc->flash_if->writesize);
+ break;
+
+ case FLASH_TYPE_NAND:
+ default:
+ aprint_normal_dev(sc->sc_dev,
+ "erase size %s, page size %d bytes, write size %d bytes\n",
+ pbuf[1], sc->flash_if->page_size,
+ sc->flash_if->writesize);
+ break;
+ }
+
+ if (!pmf_device_register1(sc->sc_dev, NULL, NULL, flash_shutdown))
+ aprint_error_dev(sc->sc_dev,
+ "couldn't establish power handler\n");
+}
+
+int
+flash_detach(device_t device, int flags)
+{
+ struct flash_softc *sc = device_private(device);
+
+ pmf_device_deregister(sc->sc_dev);
+
+ /* freeing flash_if is our responsibility */
+ printf("freeing flash_if...");
+ kmem_free(sc->flash_if, sizeof(*sc->flash_if));
+ printf("done!\n");
+
+ return 0;
+}
+
+int
+flash_print(void *aux, const char *pnp)
+{
+ struct flash_attach_args *arg;
+ const char *type;
+
+ if (pnp != NULL) {
+ arg = aux;
+ switch (arg->flash_if->type) {
+ case FLASH_TYPE_NOR:
+ type = "NOR";
+ break;
+ case FLASH_TYPE_NAND:
+ type = "NAND";
+ break;
+ default:
+ panic("flash_print: unknown type %d",
+ arg->flash_if->type);
+ }
+ aprint_normal("%s flash at %s", type, pnp);
+ }
+ return UNCONF;
+}
+
+device_t
+flash_attach_mi(struct flash_interface *flash_if, device_t device)
+{
+ struct flash_attach_args arg;
+
+#ifdef DIAGNOSTIC
+ if (flash_if == NULL) {
+ aprint_error("flash_attach_mi: NULL\n");
+ return 0;
+ }
+#endif
+ arg.flash_if = flash_if;
+
+ return config_found_ia(device, "flashbus", &arg, flash_print);
+}
+
+/**
+ * flash_open - open the character device
+ * Checks if there is a driver registered to the minor number of the open
+ * request.
+ */
+int
+flashopen(dev_t dev, int flags, int fmt, struct lwp *l)
+{
+ int unit = minor(dev);
+ struct flash_softc *sc;
+
+ DPRINTFN(1, ("flash: opening device unit %d\n", unit));
+
+ if ((sc = device_lookup_private(&flash_cd, unit)) == NULL)
+ return ENXIO;
+
+ /* TODO return eperm if want to open for writing a read only dev */
+
+ /* reset buffer length */
+// sc->sc_cache->fc_len = 0;
+
+ return 0;
+}
+
+/**
+ * flash_close - close device
+ * We don't have to release any resources, so just return 0.
+ */
+int
+flashclose(dev_t dev, int flags, int fmt, struct lwp *l)
+{
+ int unit = minor(dev);
+ struct flash_softc *sc;
+ int err;
+
+ DPRINTFN(1, ("flash: closing flash device unit %d\n", unit));
+
+ if ((sc = device_lookup_private(&flash_cd, unit)) == NULL)
+ return ENXIO;
+
+ if (!sc->sc_readonly) {
+ err = flash_sync(sc->sc_dev);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * flash_read - read from character device
+ * This function uses the registered driver's read function to read the requested length to
+ * a buffer and then moves this buffer to userspace.
+ */
+int
+flashread(dev_t dev, struct uio *uio, int flag)
+{
+ return physio(flashstrategy, NULL, dev, B_READ, minphys, uio);
+}
+
+/**
+ * flash_write - write to character device
+ * This function moves the data into a buffer from userspace to kernel space,
+ * then uses the registered driver's write function to write out the data to
+ * the media.
+ */
+int
+flashwrite(dev_t dev, struct uio *uio, int flag)
+{
+ return physio(flashstrategy, NULL, dev, B_WRITE, minphys, uio);
+}
+
+void
+flashstrategy(struct buf *bp)
+{
+ struct flash_softc *sc;
+ const struct flash_interface *flash_if;
+ const struct flash_partition *part;
+ int unit, device_blks;
+
+ unit = minor(bp->b_dev);
+ sc = device_lookup_private(&flash_cd, unit);
+ if (sc == NULL) {
+ bp->b_error = ENXIO;
+ goto done;
+ }
+
+ flash_if = sc->flash_if;
+ part = &flash_if->partition;
+
+ /* divider */
+ KASSERT(flash_if->writesize != 0);
+
+ aprint_debug_dev(sc->sc_dev, "flash_strategy()\n");
+
+ if (!(bp->b_flags & B_READ) && sc->sc_readonly) {
+ bp->b_error = EACCES;
+ goto done;
+ }
+
+ /* check if length is not negative */
+ if (bp->b_blkno < 0) {
+ bp->b_error = EINVAL;
+ goto done;
+ }
+
+ /* zero lenght i/o */
+ if (bp->b_bcount == 0) {
+ goto done;
+ }
+
+ device_blks = sc->flash_if->size / DEV_BSIZE;
+ KASSERT(part->part_offset % DEV_BSIZE == 0);
+ bp->b_rawblkno = bp->b_blkno + (part->part_offset / DEV_BSIZE);
+
+ if (bounds_check_with_mediasize(bp, DEV_BSIZE, device_blks) <= 0) {
+ goto done;
+ }
+
+ bp->b_resid = bp->b_bcount;
+ flash_if->submit(sc->sc_parent_dev, bp);
+
+ return;
+done:
+ bp->b_resid = bp->b_bcount;
+ biodone(bp);
+}
+
+/*
+ * Handle the ioctl for the device
+ */
+int
+flashioctl(dev_t dev, u_long command, void *data, int flags, struct lwp *l)
+{
+ struct flash_erase_params *ep;
+ struct flash_info_params *ip;
+ struct flash_dump_params *dp;
+ struct flash_badblock_params *bbp;
+ struct flash_erase_instruction ei;
+ struct flash_softc *sc;
+ int unit, err;
+ size_t retlen;
+ flash_addr_t offset;
+
+ unit = minor(dev);
+ if ((sc = device_lookup_private(&flash_cd, unit)) == NULL)
+ return ENXIO;
+
+ err = 0;
+ switch (command) {
+ case FLASH_ERASE_BLOCK:
+ /**
+ * Set up an erase instruction then call the registered
+ * driver's erase operation.
+ */
+ ep = data;
+
+ if (sc->sc_readonly) {
+ return EACCES;
+ }
+
+ ei.ei_addr = ep->ep_addr;
+ ei.ei_len = ep->ep_len;
+ ei.ei_callback = NULL;
+
+ err = flash_erase(sc->sc_dev, &ei);
+ if (err) {
+ return err;
+ }
+
+ break;
+ case FLASH_BLOCK_ISBAD:
+ /**
+ * Set up an erase instruction then call the registered
+ * driver's erase operation.
+ */
+ bbp = data;
+
+ err = flash_block_isbad(sc->sc_dev, bbp->bbp_addr);
+ if (err == EIO) {
+ bbp->bbp_isbad = true;
+ err = 0;
+ } else if (err) {
+ return err;
+ } else {
+ bbp->bbp_isbad = false;
+ }
+
+ break;
+ case FLASH_BLOCK_MARKBAD:
+ bbp = data;
+
+ err = flash_block_markbad(sc->sc_dev, bbp->bbp_addr);
+
+ break;
+ case FLASH_DUMP:
+ dp = data;
+ offset = dp->dp_block * sc->flash_if->erasesize;
+ DPRINTF(("Reading from block: %jd len: %jd\n",
+ (intmax_t )dp->dp_block, (intmax_t )dp->dp_len));
+ err = flash_read(sc->sc_parent_dev, offset, dp->dp_len,
+ &retlen, dp->dp_buf);
+ if (err)
+ return err;
+ if (retlen != dp->dp_len) {
+ dp->dp_len = -1;
+ dp->dp_buf = NULL;
+ }
+
+ break;
+ case FLASH_GET_INFO:
+ ip = data;
+
+ ip->ip_page_size = sc->flash_if->page_size;
+ ip->ip_erase_size = sc->flash_if->erasesize;
+ ip->ip_flash_type = sc->flash_if->type;
+ ip->ip_flash_size = sc->flash_if->size;
+ break;
+ default:
+ err = ENODEV;
+ }
+
+ return err;
+}
+int
+flashsize(dev_t dev)
+{
+ return -1;
+}
+
+int
+flashdump(dev_t dev, daddr_t blkno, void *va, size_t size)
+{
+ return EACCES;
+}
+
+bool
+flash_shutdown(device_t self, int how)
+{
+ struct flash_softc *sc = device_private(self);
+
+ if ((how & RB_NOSYNC) == 0 && !sc->sc_readonly)
+ flash_sync(self);
+
+ return true;
+}
+
+const struct flash_interface *
+flash_get_interface(dev_t dev)
+{
+ struct flash_softc *sc;
+ int unit;
+
+ unit = minor(dev);
+ if ((sc = device_lookup_private(&flash_cd, unit)) == NULL)
+ return NULL;
+
+ return sc->flash_if;
+}
+
+const struct flash_softc *
+flash_get_softc(dev_t dev)
+{
+ struct flash_softc *sc;
+ int unit;
+
+ unit = minor(dev);
+ sc = device_lookup_private(&flash_cd, unit);
+
+ return sc;
+}
+
+device_t
+flash_get_device(dev_t dev)
+{
+ struct flash_softc *sc;
+ int unit;
+
+ unit = minor(dev);
+ sc = device_lookup_private(&flash_cd, unit);
+
+ return sc->sc_dev;
+}
+
+static inline off_t
+flash_get_part_offset(struct flash_softc *fl, size_t poffset)
+{
+ return fl->flash_if->partition.part_offset + poffset;
+}
+
+int
+flash_erase(device_t self, struct flash_erase_instruction *ei)
+{
+ struct flash_softc *sc = device_private(self);
+ KASSERT(ei != NULL);
+ struct flash_erase_instruction e = *ei;
+
+ if (sc->sc_readonly)
+ return EACCES;
+
+ /* adjust for flash partition */
+ e.ei_addr += sc->flash_if->partition.part_offset;
+
+ /* bounds check for flash partition */
+ if (e.ei_addr + e.ei_len > sc->flash_if->partition.part_size +
+ sc->flash_if->partition.part_offset)
+ return EINVAL;
+
+ return sc->flash_if->erase(device_parent(self), &e);
+}
+
+int
+flash_read(device_t self,
+ off_t offset, size_t len, size_t *retlen, uint8_t *buf)
+{
+ struct flash_softc *sc = device_private(self);
+
+ offset += sc->flash_if->partition.part_offset;
+
+ if (offset + len > sc->flash_if->partition.part_size +
+ sc->flash_if->partition.part_offset)
+ return EINVAL;
+
+ return sc->flash_if->read(device_parent(self),
+ offset, len, retlen, buf);
+}
+
+int
+flash_write(device_t self,
+ off_t offset, size_t len, size_t *retlen, const uint8_t *buf)
+{
+ struct flash_softc *sc = device_private(self);
+
+ if (sc->sc_readonly)
+ return EACCES;
+
+ offset += sc->flash_if->partition.part_offset;
+
+ if (offset + len > sc->flash_if->partition.part_size +
+ sc->flash_if->partition.part_offset)
+ return EINVAL;
+
+ return sc->flash_if->write(device_parent(self),
+ offset, len, retlen, buf);
+}
+
+int
+flash_block_markbad(device_t self, uint64_t offset)
+{
+ struct flash_softc *sc = device_private(self);
+
+ if (sc->sc_readonly)
+ return EACCES;
+
+ offset += sc->flash_if->partition.part_offset;
+
+ if (offset + sc->flash_if->erasesize >=
+ sc->flash_if->partition.part_size +
+ sc->flash_if->partition.part_offset)
+ return EINVAL;
+
+ return sc->flash_if->block_markbad(device_parent(self), offset);
+}
+
+int
+flash_block_isbad(device_t self, uint64_t offset)
+{
+ struct flash_softc *sc = device_private(self);
+
+ offset += sc->flash_if->partition.part_offset;
+
+ if (offset + sc->flash_if->erasesize >
+ sc->flash_if->partition.part_size +
+ sc->flash_if->partition.part_offset)
+ return EINVAL;
+
+ return sc->flash_if->block_isbad(device_parent(self), offset);
+}
+
+int
+flash_sync(device_t self)
+{
+ struct flash_softc *sc = device_private(self);
+
+ if (sc->sc_readonly)
+ return EACCES;
+
+ /* noop now TODO: implement */
+ return 0;
+}
+
+MODULE(MODULE_CLASS_DRIVER, flash, NULL);
+
+#ifdef _MODULE
+#include "ioconf.c"
+#endif
+
+static int
+flash_modcmd(modcmd_t cmd, void *opaque)
+{
+ int error = 0;
+#ifdef _MODULE
+ int bmaj = -1, cmaj = -1;
+#endif
+
+ switch (cmd) {
+ case MODULE_CMD_INIT:
+#ifdef _MODULE
+ error = config_init_component(cfdriver_ioconf_flash,
+ cfattach_ioconf_flash, cfdata_ioconf_flash);
+ if (error)
+ return error;
+ error = devsw_attach("flash", &flash_bdevsw, &bmaj,
+ &flash_cdevsw, &cmaj);
+ if (error)
+ config_fini_component(cfdriver_ioconf_flash,
+ cfattach_ioconf_flash, cfdata_ioconf_flash);
+#endif
+ return error;
+ case MODULE_CMD_FINI:
+#ifdef _MODULE
+ devsw_detach(&flash_bdevsw, &flash_cdevsw);
+ error = config_fini_component(cfdriver_ioconf_flash,
+ cfattach_ioconf_flash, cfdata_ioconf_flash);
+#endif
+ return error;
+ default:
+ return ENOTTY;
+ }
+}
diff --git a/sys/dev/flash/flash.h b/sys/dev/flash/flash.h
new file mode 100644
index 00000000000..ead0b3cda27
--- /dev/null
+++ b/sys/dev/flash/flash.h
@@ -0,0 +1,168 @@
+/* $NetBSD: flash.h,v 1.1 2011/02/26 18:07:30 ahoka Exp $ */
+
+/*-
+ * Copyright (c) 2011 Department of Software Engineering,
+ * University of Szeged, Hungary
+ * Copyright (c) 2011 Adam Hoka <ahoka@NetBSD.org>
+ * Copyright (c) 2010 David Tengeri <dtengeri@inf.u-szeged.hu>
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by the Department of Software Engineering, University of Szeged, Hungary
+ *
+ * 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.
+ */
+
+#ifndef _FLASH_H_
+#define _FLASH_H_
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/module.h>
+#include <sys/buf.h>
+#include <sys/flashio.h>
+
+/**
+ * flash_softc - private structure for flash layer driver
+ */
+
+struct flash_softc {
+ device_t sc_dev;
+ device_t sc_parent_dev; /* Hardware (parent) device */
+ void *hw_softc; /* Hardware device private softc */
+ struct flash_interface *flash_if; /* Hardware interface */
+
+ bool sc_readonly; /* read only flash device */
+};
+
+struct flash_attach_args {
+ struct flash_interface *flash_if; /* Hardware interface */
+};
+
+device_t flash_attach_mi(struct flash_interface *, device_t);
+const struct flash_interface *flash_get_interface(dev_t);
+const struct flash_softc *flash_get_softc(dev_t);
+device_t flash_get_device(dev_t);
+
+/**
+ * struct erase_instruction - instructions to erase a flash eraseblock
+ * @fd: flash descriptor
+ * @addr: start address of the erase operation
+ * @len: the erase length
+ * @callback: callback operation, called when erase finished
+ * @priv: private data
+ * @state: the erase operation's result
+ */
+struct flash_erase_instruction {
+ flash_addr_t ei_addr;
+ flash_addr_t ei_len;
+ void (*ei_callback) (struct flash_erase_instruction *);
+ u_long ei_priv;
+ u_char ei_state;
+};
+
+enum {
+ FLASH_PART_READONLY = (1<<0),
+ FLASH_PART_FILESYSTEM = (1<<2)
+};
+
+struct flash_partition {
+ flash_addr_t part_offset;
+ flash_addr_t part_size;
+ int part_flags;
+};
+
+/**
+ * struct flash_interface - interface for flash operations
+ * @type: type of flash device
+ * @size: size of flash
+ * @page_size: page size of flash
+ * @erasesize: erase size of flash
+ * @writesize: minimum write size of flash
+ * @minor: minor number of the character device attached to this driver
+ * @erase: erase operation of the flash
+ * @read: read operation of the flash
+ * @write: write operation of the flash
+ * @block_markbad: marks a block as bad on the flash
+ * @block_isbad: checks if a block is bad on flash
+ */
+struct flash_interface {
+ int (*erase) (device_t, struct flash_erase_instruction *);
+ int (*read) (device_t, off_t, size_t, size_t *, uint8_t *);
+ int (*write) (device_t, off_t, size_t, size_t *, const uint8_t *);
+ int (*block_markbad)(device_t, uint64_t);
+ int (*block_isbad)(device_t, uint64_t);
+ int (*sync) (device_t);
+
+ int (*submit)(device_t, struct buf *);
+
+ /* storage for partition info */
+ struct flash_partition partition;
+
+ /* total size of mtd */
+ flash_addr_t size;
+ uint32_t page_size;
+ uint32_t erasesize;
+ uint32_t writesize;
+ uint32_t minor;
+ uint8_t type;
+};
+
+/**
+ * struct cache - for caching writes on block device
+ */
+struct flash_cache {
+ size_t fc_len;
+ flash_addr_t fc_block;
+ uint8_t *fc_data;
+};
+
+/* flash operations should be used through these */
+int flash_erase(device_t, struct flash_erase_instruction *);
+int flash_read(device_t, off_t, size_t, size_t *, uint8_t *);
+int flash_write(device_t, off_t, size_t, size_t *, const uint8_t *);
+int flash_block_markbad(device_t, uint64_t);
+int flash_block_isbad(device_t, uint64_t);
+int flash_sync(device_t);
+
+/*
+ * check_pattern - checks the buffer only contains the byte pattern
+ * @buf: the buffer to check
+ * @patt: the pattern to match
+ * @offset: the starting byte number, the matching starts from here
+ * @size: the buffer size
+ *
+ * This functions checks if the buffer only contains a specified byte pattern.
+ * Returns %0 if found something else, %1 otherwise.
+ */
+static inline int
+check_pattern(const void *buf, uint8_t patt, size_t offset, size_t size)
+{
+ size_t i;
+ for (i = offset; i < size; i++) {
+ if (((const uint8_t *) buf)[i] != patt)
+ return 0;
+ }
+ return 1;
+}
+
+#endif /* _FLASH_H_ */