
This core provides unified access to different block controllers (SATA, SCSI).
Signed-off-by: Pavel Herrmann morpheus.ibis@gmail.com --- Makefile | 1 + drivers/blockctrl/Makefile | 42 ++++++ drivers/blockctrl/core.c | 349 +++++++++++++++++++++++++++++++++++++++++++++ include/dm/blockctrl.h | 75 ++++++++++ 4 files changed, 467 insertions(+) create mode 100644 drivers/blockctrl/Makefile create mode 100644 drivers/blockctrl/core.c create mode 100644 include/dm/blockctrl.h
diff --git a/Makefile b/Makefile index e43fd9d..4420484 100644 --- a/Makefile +++ b/Makefile @@ -304,6 +304,7 @@ LIBS-y += test/libtest.o LIBS-$(CONFIG_DM) += common/dm/libdm.o LIBS-$(CONFIG_DM) += drivers/demo/libdemo.o LIBS-${CONFIG_DM_BLOCK} += drivers/blockdev/libblockdev.o +LIBS-${CONFIG_DM_BLOCK} += drivers/blockctrl/libblockctrl.o
ifneq ($(CONFIG_AM33XX)$(CONFIG_OMAP34XX)$(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX),) LIBS-y += $(CPUDIR)/omap-common/libomap-common.o diff --git a/drivers/blockctrl/Makefile b/drivers/blockctrl/Makefile new file mode 100644 index 0000000..21a9094 --- /dev/null +++ b/drivers/blockctrl/Makefile @@ -0,0 +1,42 @@ +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)libblockctrl.o + +COBJS-${CONFIG_DM_BLOCK} := core.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/blockctrl/core.c b/drivers/blockctrl/core.c new file mode 100644 index 0000000..1f1f834 --- /dev/null +++ b/drivers/blockctrl/core.c @@ -0,0 +1,349 @@ +/* + * (C) Copyright 2012 + * Pavel Herrmann morpheus.ibis@gmail.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <dm/blockctrl.h> +#include <dm/manager.h> +#include <malloc.h> + +struct blockctrl_core_entry { + struct list_head list; + struct instance *instance; + struct blockctrl_ops *ops; +}; + +static struct blockctrl_core_entry *get_entry_by_instance(struct instance *dev) +{ + struct blockctrl_core_entry *entry; + struct core_instance *core = get_core_instance(CORE_BLOCKCTRL); + + if (!core) + return NULL; + + list_for_each_entry(entry, &core->succ, list) { + if (entry->instance == dev) + return entry; + } + + return NULL; +} + +/* Core API functions */ +static int get_count(struct core_instance *core) +{ + int count = 0; + struct blockctrl_core_entry *entry; + + list_for_each_entry(entry, &core->succ, list) { + count++; + } + + return count; +} + +static struct instance *get_child(struct core_instance *core, int index) +{ + struct blockctrl_core_entry *entry = NULL; + + list_for_each_entry(entry, &core->succ, list) { + if (!index) + return entry->instance; + index--; + } + + return NULL; +} + +static int bind(struct core_instance *core, struct instance *dev, void *ops, + void *data) +{ + struct blockctrl_core_entry *entry; + + if (ops == NULL) + return -EINVAL; + + entry = malloc(sizeof(*entry)); + if (entry == NULL) + return -ENOMEM; + + INIT_LIST_HEAD(&entry->list); + entry->instance = dev; + entry->ops = ops; + list_add_tail(&entry->list, &core->succ); + + return 0; +} + +static int unbind(struct core_instance *core, struct instance *dev) +{ + struct blockctrl_core_entry *entry, *n; + + list_for_each_entry_safe(entry, n, &core->succ, list) { + if (entry->instance == dev) { + list_del(&entry->list); + free(entry); + } + } + + return 0; +} + +static int replace(struct core_instance *core, struct instance *new, + struct instance *old) +{ + struct blockctrl_core_entry *entry = get_entry_by_instance(old); + + if (!entry) + return -ENOENT; + + entry->instance = new; + + return 0; +} + +static int init(struct core_instance *core) +{ + INIT_LIST_HEAD(&core->succ); + core->private_data = NULL; + + return 0; +} + +static int reloc(struct core_instance *core, struct core_instance *old) +{ + struct blockctrl_core_entry *entry, *new; + + /* no private_data to copy, yet */ + + /* fixup links in old list and prepare new list head */ + /* FIXME */ + /* list_fix_reloc(&old->succ); */ + INIT_LIST_HEAD(&core->succ); + core->private_data = NULL; + + /* copy list entries to new memory */ + list_for_each_entry(entry, &old->succ, list) { + new = malloc(sizeof(*new)); + if (!new) + return -ENOMEM; + + INIT_LIST_HEAD(&new->list); + new->instance = entry->instance; + new->ops = entry->ops; + list_add_tail(&new->list, &core->succ); + /*no free at this point, old memory should not be freed*/ + } + + return 0; +} + +static int destroy(struct core_instance *core) +{ + struct blockctrl_core_entry *entry, *n; + + /* destroy private data */ + free(core->private_data); + core->private_data = NULL; + + /* destroy successor list */ + list_for_each_entry_safe(entry, n, &core->succ, list) { + list_del(&entry->list); + free(entry); + } + + return 0; +} + +U_BOOT_CORE(CORE_BLOCKCTRL, + init, + reloc, + destroy, + get_count, + get_child, + bind, + unbind, + replace); + +lbaint_t blockctrl_read(struct instance *i, int port, lbaint_t start, + lbaint_t length, void *buffer) +{ + struct blockctrl_core_entry *entry; + struct blockctrl_ops *driver_ops; + int error; + + entry = get_entry_by_instance(i); + if (!entry) + return -ENOENT; + + error = driver_activate(i); + if (error) + return error; + + driver_ops = entry->ops; + if (!driver_ops || !driver_ops->read) + return -EINVAL; + + return driver_ops->read(i, port, start, length, buffer); +} + +lbaint_t blockctrl_write(struct instance *i, int port, lbaint_t start, + lbaint_t length, void *buffer) +{ + struct blockctrl_core_entry *entry; + struct blockctrl_ops *driver_ops; + int error; + + entry = get_entry_by_instance(i); + if (!entry) + return -ENOENT; + + error = driver_activate(i); + if (error) + return error; + + driver_ops = entry->ops; + if (!driver_ops || !driver_ops->write) + return -EINVAL; + + return driver_ops->write(i, port, start, length, buffer); +} + +int blockctrl_scan(struct instance *i, int port) +{ + struct blockctrl_core_entry *entry; + struct blockctrl_ops *driver_ops; + int error; + + entry = get_entry_by_instance(i); + if (!entry) + return -ENOENT; + + error = driver_activate(i); + if (error) + return error; + + driver_ops = entry->ops; + if (!driver_ops || !driver_ops->scan) + return -EINVAL; + + return driver_ops->scan(i, port); +} + +int blockctrl_get_port_count(struct instance *i) +{ + struct blockctrl_core_entry *entry; + struct blockctrl_ops *driver_ops; + int error; + + entry = get_entry_by_instance(i); + if (!entry) + return -ENOENT; + + error = driver_activate(i); + if (error) + return error; + + driver_ops = entry->ops; + if (!driver_ops || !driver_ops->get_port_count) + return -EINVAL; + + return driver_ops->get_port_count(i); +} + +int blockctrl_get_port_option(struct instance *i, int port, + enum blockctrl_port_option_code op, struct option *result) +{ + struct blockctrl_core_entry *entry; + struct blockctrl_ops *driver_ops; + int error; + + entry = get_entry_by_instance(i); + if (!entry) + return -ENOENT; + + error = driver_activate(i); + if (error) + return error; + + driver_ops = entry->ops; + if (!driver_ops || !driver_ops->get_port_option) + return -EINVAL; + + return driver_ops->get_port_option(i, port, op, result); +} + +struct instance *blockctrl_rescan_port(struct instance *i, int port) +{ + /* we assume that all children of blockctrl are blockdev_ata */ + struct instance *child = NULL; + struct blockctrl_core_entry *entry; + struct driver_instance *di; + struct blockdev_ata_platform_data *pdata; + struct driver_info *info; + int nfound; + + entry = get_entry_by_instance(i); + if (!entry) + return NULL; + + list_for_each_entry(di, &i->succ, list) { + if (di->i.info) + pdata = di->i.info->platform_data; + else + pdata = NULL; + + if (pdata && (pdata->port_number == port)) + child = &di->i; + } + + /* + * If we have an active link, we check whether this is a new device, + * in which case we simply bind a new instance, or an old device, + * in which case we reactivate the device to force partition rescan. + * If we dont have an active link, we remove any device attached. + */ + nfound = blockctrl_scan(i, port); + if (!nfound) { + if (child) { + /* rescan the disk size and partitions, just in case */ + driver_remove(child); + scan_partitions(child); + } else { + pdata = malloc(sizeof(*pdata)); + pdata->port_number = port; + info = malloc(sizeof(*info)); + info->platform_data = pdata; + info->name = "blockdev_ata"; + child = driver_bind(i, info); + scan_partitions(child); + return child; + } + } else { + /* link is not active, remove and unbind the child device */ + if (child) { + driver_remove(child); + driver_unbind(child); + } + } + + return NULL; +} diff --git a/include/dm/blockctrl.h b/include/dm/blockctrl.h new file mode 100644 index 0000000..4b6d582 --- /dev/null +++ b/include/dm/blockctrl.h @@ -0,0 +1,75 @@ +/* + * (C) Copyright 2012 + * Pavel Herrmann morpheus.ibis@gmail.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _DM_BLOCKCTRL_H_ +#define _DM_BLOCKCTRL_H 1 + +#include <dm/structures.h> +#include <dm/blockdev.h> + +enum blockctrl_port_option_code { + BLKP_OPT_IFTYPE = 0, + BLKP_OPT_IFSPEED, + BLKP_OPT_BLOCKSIZE, + BLKP_OPT_BLOCKCOUNT, + BLKP_OPT_REMOVABLE, + BLKP_OPT_LBA48, + BLKP_OPT_VENDOR, + BLKP_OPT_PRODUCT, + BLKP_OPT_REVISION, +}; + +enum blockctrl_iftype { + BLKP_IFTYPE_SATA, + BLKP_IFTYPE_PATA, + BLKP_IFTYPE_SCSI, + BLKP_IFTYPE_VIRTUAL, +}; + + +struct blockctrl_ops { + lbaint_t (*read)(struct instance *i, int port, lbaint_t start, + lbaint_t length, void *buffer); + lbaint_t (*write)(struct instance *i, int port, lbaint_t start, + lbaint_t length, void *buffer); + int (*scan)(struct instance *i, int port); + int (*get_port_count)(struct instance *i); + int (*get_port_option)(struct instance *i, int port, + enum blockctrl_port_option_code op, + struct option *result); +}; + +/* driver wrappers */ +lbaint_t blockctrl_read(struct instance *i, int port, lbaint_t start, + lbaint_t length, void *buffer); +lbaint_t blockctrl_write(struct instance *i, int port, lbaint_t start, + lbaint_t length, void *buffer); +int blockctrl_scan(struct instance *i, int port); +int blockctrl_get_port_count(struct instance *i); +int blockctrl_get_port_option(struct instance *i, int port, + enum blockctrl_port_option_code op, struct option *result); + +/* command helpers */ +struct instance *blockctrl_rescan_port(struct instance *i, int port); + +#endif