Enable blockdev core to automatically create partitions based on DOS partition
table. This code is based on /disk/, and should eventually replace it.

Signed-off-by: Pavel Herrmann <morpheus.i...@gmail.com>
---
 drivers/blockdev/Makefile                |   1 +
 drivers/blockdev/core.c                  | 752 +++++++++++++++++++++++++++++++
 drivers/blockdev/part_types/part_dos.c   | 148 ++++++
 drivers/blockdev/part_types/part_dos.h   |  49 ++
 drivers/blockdev/part_types/part_types.h |  34 ++
 5 files changed, 984 insertions(+)
 create mode 100644 drivers/blockdev/core.c
 create mode 100644 drivers/blockdev/part_types/part_dos.c
 create mode 100644 drivers/blockdev/part_types/part_dos.h
 create mode 100644 drivers/blockdev/part_types/part_types.h

diff --git a/drivers/blockdev/Makefile b/drivers/blockdev/Makefile
index 693e236..a988924 100644
--- a/drivers/blockdev/Makefile
+++ b/drivers/blockdev/Makefile
@@ -22,6 +22,7 @@ include $(TOPDIR)/config.mk
 LIB    := $(obj)libblockdev.o
 
 COBJS-${CONFIG_DM_BLOCK} := core.o
+COBJS-${CONFIG_DOS_PARTITION} += part_types/part_dos.o
 
 COBJS  := $(COBJS-y)
 SRCS   := $(COBJS:.o=.c)
diff --git a/drivers/blockdev/core.c b/drivers/blockdev/core.c
new file mode 100644
index 0000000..bf1c70d
--- /dev/null
+++ b/drivers/blockdev/core.c
@@ -0,0 +1,752 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.i...@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/blockdev.h>
+#include <dm/manager.h>
+#include <malloc.h>
+#include "part_types/part_types.h"
+
+static const char * const blockdev_name[] = {"unk", "unk", "ata", "sd", "usb"};
+static const char * const iftype_name[] = {"unknown", "-", "ATAPI", "SD/MMC",
+       "USB"};
+
+#define BLOCKDEV_PARTITION_SEPARATOR ':'
+
+#define BLOCKDEV_IFTYPE_BITS 4
+#define BLOCKDEV_IFTYPE_COUNT (1<<BLOCKDEV_IFTYPE_BITS)
+#define BLOCKDEV_IFTYPE_MAX BLOCKDEV_IFTYPE_COUNT-1
+
+struct blockdev_id {
+       struct {
+               unsigned type:BLOCKDEV_IFTYPE_BITS;
+               unsigned number:(16-BLOCKDEV_IFTYPE_BITS);
+       } disk;
+       unsigned partition:16;
+};
+
+static struct blockdev_id invalid_id = {{0xf, 0xfff}, 0xffff};
+static struct blockdev_id empty_id = {{0x0, 0x0}, 0x0};
+
+static inline int id_cmp(const struct blockdev_id *left,
+       const struct blockdev_id *right)
+{
+       return memcmp(left, right, sizeof(struct blockdev_id));
+}
+
+struct blockdev_core_entry {
+       struct list_head        list;
+       struct instance         *instance;
+       struct blockdev_ops     *ops;
+       struct blockdev_id      name;
+};
+
+struct blockdev_core_private {
+       /* Put cache here */
+};
+
+struct bdid_instance_pair {
+       struct blockdev_id      id;
+       struct instance         *inst;
+};
+
+static struct blockdev_core_entry *get_entry_by_instance(struct instance *i)
+{
+       struct blockdev_core_entry *tmp;
+       struct core_instance *core = get_core_instance(CORE_BLOCKDEV);
+
+       if (!core)
+               return NULL;
+
+       list_for_each_entry(tmp, &core->succ, list)
+               if (tmp->instance == i)
+                       return tmp;
+
+       return NULL;
+}
+
+static struct blockdev_core_entry *get_entry_by_id(struct blockdev_id id)
+{
+       struct blockdev_core_entry *tmp;
+       struct core_instance *core = get_core_instance(CORE_BLOCKDEV);
+
+       if (!core)
+               return NULL;
+
+       list_for_each_entry(tmp, &core->succ, list)
+               if (!id_cmp(&tmp->name, &id))
+                       return tmp;
+
+       return NULL;
+}
+
+static inline int try_match_name(enum blockdev_iftype type,
+       struct blockdev_id *id, char **name)
+{
+       int len = strlen(blockdev_name[type]);
+
+       if (!strncmp(*name, blockdev_name[type], len)) {
+               id->disk.type = type;
+               *name += len;
+               return 0;
+       }
+       return 1;
+}
+
+static struct blockdev_id get_id_from_name(char *name)
+{
+       struct blockdev_id disk_id = empty_id;
+
+       if (!try_match_name(BLOCKDEV_IFTYPE_UNKNOWN, &disk_id, &name))
+               goto get_number;
+       if (!try_match_name(BLOCKDEV_IFTYPE_ATA, &disk_id, &name))
+               goto get_number;
+       if (!try_match_name(BLOCKDEV_IFTYPE_SD, &disk_id, &name))
+               goto get_number;
+       if (!try_match_name(BLOCKDEV_IFTYPE_USB, &disk_id, &name))
+               goto get_number;
+
+       return invalid_id;
+
+get_number:
+       /* get disk number from name */
+       if ((*name < '0') || (*name > '9'))
+               return invalid_id;
+
+       disk_id.disk.number *= 10;
+       disk_id.disk.number += (*name-'0');
+       name++;
+
+       switch (*name) {
+       case 0:
+               return disk_id;
+       case BLOCKDEV_PARTITION_SEPARATOR:
+               name += 1;
+               goto get_part;
+       default:
+               goto get_number;
+       }
+       return invalid_id;
+
+get_part:
+       /* get partition number fron name */
+       if ((*name < '0') || (*name > '9'))
+               return invalid_id;
+
+       disk_id.partition *= 10;
+       disk_id.partition += (*name-'0');
+       name++;
+
+       switch (*name) {
+       case 0:
+               return disk_id;
+       default:
+               goto get_part;
+       }
+
+       return invalid_id;
+}
+
+static int get_free_index(struct core_instance *core, enum blockdev_iftype 
type)
+{
+       int retval = 0;
+       struct blockdev_core_entry *entry;
+
+       list_for_each_entry(entry, &core->succ, list) {
+               if ((entry->name.disk.type == type) &&
+                       (entry->name.disk.number >= retval))
+                               retval = entry->name.disk.number + 1;
+       }
+
+       return retval;
+}
+
+static struct blockdev_id create_id_from_hint(struct core_instance *core,
+       struct instance *dev, void* data)
+{
+       struct blockdev_id retval = empty_id;
+       struct blockdev_core_entry *entry;
+       struct blockdev_core_hint *hint = data;
+
+       /* no hint means we have no idea what type of device we have */
+       if (!hint)
+               retval.disk.type = BLOCKDEV_IFTYPE_UNKNOWN;
+       else {
+               /* for a partition, we find its parent and use its name */
+               if (hint->iftype == BLOCKDEV_IFTYPE_PARTITION) {
+                       entry = get_entry_by_instance(dev->bus);
+                       retval.disk = entry->name.disk;
+                       retval.partition = hint->part_number;
+                       return retval;
+               /* if we have a valid hint for a disk, get a free index */
+               } else {
+                       retval.disk.type = hint->iftype;
+                       retval.disk.number = get_free_index(core, hint->iftype);
+                       retval.partition = 0;
+               }
+       }
+
+       return retval;
+}
+
+/* Core API functions */
+static int get_count(struct core_instance *core)
+{
+       int cnt = 0;
+       struct blockdev_core_entry *entry = NULL;
+
+       list_for_each_entry(entry, &core->succ, list)
+               cnt++;
+
+       return cnt;
+}
+
+static struct instance *get_child(struct core_instance *core, int index)
+{
+       struct blockdev_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 blockdev_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;
+       entry->name = create_id_from_hint(core, dev, data);
+       list_add_tail(&entry->list, &core->succ);
+
+       return 0;
+}
+
+static int unbind(struct core_instance *core, struct instance *dev)
+{
+       struct blockdev_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 blockdev_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 blockdev_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;
+               new->name = entry->name;
+               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 blockdev_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_BLOCKDEV,
+       init,
+       reloc,
+       destroy,
+       get_count,
+       get_child,
+       bind,
+       unbind,
+       replace);
+
+/* Driver wrapping API */
+lbaint_t blockdev_read(struct instance *i, lbaint_t start, lbaint_t blkcnt,
+       void *buffer)
+{
+       struct blockdev_core_entry *entry = NULL;
+       struct blockdev_ops *device_ops = NULL;
+       int error;
+
+       entry = get_entry_by_instance(i);
+       if (!entry)
+               return -ENOENT;
+
+       error = driver_activate(i);
+       if (error)
+               return error;
+
+       device_ops = entry->ops;
+       if (!device_ops || !device_ops->read)
+               return -EINVAL;
+
+       return device_ops->read(i, start, blkcnt, buffer);
+}
+
+lbaint_t blockdev_write(struct instance *i, lbaint_t start, lbaint_t blkcnt,
+       void *buffer)
+{
+       struct blockdev_core_entry *entry = NULL;
+       struct blockdev_ops *device_ops = NULL;
+       int error;
+
+       entry = get_entry_by_instance(i);
+       if (!entry)
+               return -ENOENT;
+
+       error = driver_activate(i);
+       if (error)
+               return error;
+
+       device_ops = entry->ops;
+       if (!device_ops || !device_ops->write)
+               return -EINVAL;
+
+       return device_ops->write(i, start, blkcnt, buffer);
+}
+
+lbaint_t blockdev_erase(struct instance *i, lbaint_t start, lbaint_t blkcnt)
+{
+       struct blockdev_core_entry *entry = NULL;
+       struct blockdev_ops *device_ops = NULL;
+       int error;
+
+       entry = get_entry_by_instance(i);
+       if (!entry)
+               return -ENOENT;
+
+       error = driver_activate(i);
+       if (error)
+               return error;
+
+       device_ops = entry->ops;
+       if (!device_ops)
+               return -EINVAL;
+
+       return device_ops->erase(i, start, blkcnt);
+}
+
+int blockdev_get_option(struct instance *i, enum blockdev_option_code op,
+       struct option *result)
+{
+       struct blockdev_core_entry *entry = NULL;
+       struct blockdev_ops *device_ops = NULL;
+       int error;
+
+       entry = get_entry_by_instance(i);
+       if (!entry)
+               return -ENOENT;
+
+       error = driver_activate(i);
+       if (error)
+               return error;
+
+       device_ops = entry->ops;
+       if (!device_ops)
+               return -EINVAL;
+
+       return device_ops->get_option(i, op, result);
+}
+
+int blockdev_set_option(struct instance *i, enum blockdev_option_code op,
+       struct option *value)
+{
+       struct blockdev_core_entry *entry = NULL;
+       struct blockdev_ops *device_ops = NULL;
+       int error;
+
+       entry = get_entry_by_instance(i);
+       if (!entry)
+               return -ENOENT;
+
+       error = driver_activate(i);
+       if (error)
+               return error;
+
+       device_ops = entry->ops;
+       if (!device_ops)
+               return -EINVAL;
+
+       return device_ops->set_option(i, op, value);
+}
+
+/* Command related functions */
+struct instance *get_blockdev_by_name(char *name)
+{
+       struct blockdev_id disk_id = empty_id;
+       struct blockdev_core_entry *entry;
+
+       if (!name)
+               return NULL;
+
+       disk_id = get_id_from_name(name);
+
+       if (id_cmp(&disk_id, &invalid_id)) {
+               entry = get_entry_by_id(disk_id);
+               if (entry)
+                       return entry->instance;
+       }
+
+       return NULL;
+}
+
+int scan_partitions(struct instance *dev)
+{
+       struct blockdev_core_entry *entry;
+       struct driver_instance *di, *tmp;
+
+       entry = get_entry_by_instance(dev);
+       /* ignore if instance is partition or not a blockdev */
+       if (!entry || (entry->name.partition != 0))
+               return -EINVAL;
+
+       /* remove all children */
+       list_for_each_entry_safe(di, tmp, &dev->succ, list) {
+               driver_remove(&di->i);
+               driver_unbind(&di->i);
+       }
+
+       /* determine type of partition table and scan for partitions */
+#ifdef CONFIG_DOS_PARTITION
+       if (!test_partitions_dos(dev))
+               return scan_partitions_dos(dev);
+#endif
+
+       return -ENOENT;
+}
+
+static inline int part_number_overflow(int number)
+{
+       /* just support 128 partitions for now */
+       return (number > 128);
+}
+
+int add_partition(struct instance *parent, lbaint_t start, lbaint_t length,
+       unsigned int number)
+{
+       struct blockdev_partition_platform_data *platform = NULL;
+       struct driver_info *info = NULL;
+
+       /* check for overflow in partition number */
+       if (part_number_overflow(number))
+               return -EINVAL;
+
+       platform = malloc(sizeof(*platform));
+       info = malloc(sizeof(*info));
+       if (!platform || !info) {
+               /* malloc went wrong, cleanup and indicate imminent death */
+               free(platform);
+               free(info);
+               return -ENOMEM;
+       }
+
+       platform->offset = start;
+       platform->block_count = length;
+       platform->part_number = number;
+       info->name = "blockdev_partition";
+       info->platform_data = platform;
+       if (!driver_bind(parent, info))
+               return -ENOMEM;
+
+       return 0;
+}
+
+/* Info printing stuff */
+static char *type_name(unsigned type)
+{
+       switch (type) {
+       case BLOCKDEV_TYPE_UNKNOWN:
+               return "Unknown/Not Connected";
+       case BLOCKDEV_TYPE_HARDDISK:
+               return "Hard drive";
+       case BLOCKDEV_TYPE_TAPE:
+               return "Tape";
+       case BLOCKDEV_TYPE_CDROM:
+               return "CDROM";
+       case BLOCKDEV_TYPE_OPDISK:
+               return "Optical disk";
+       default:
+               return "Unknown";
+       };
+}
+
+static inline int get_opt_u(struct blockdev_ops *ops, struct instance *dev,
+       enum blockdev_option_code code, struct option *opt)
+{
+       int retval = ops->get_option(dev, code, opt);
+       if (retval)
+               return retval;
+
+       /* If we dont get the correct type we fail. */
+       if (OPTION_TYPE(*opt) != OPTION_TYPE_U)
+               retval = -EINVAL;
+
+       /* If we get a mallocated string we should free it. */
+       if (opt->flags & OPTION_PTR_MALLOCED)
+               free(opt->data.data_s);
+
+       return retval;
+}
+
+static inline int get_opt_s(struct blockdev_ops *ops, struct instance *dev,
+       enum blockdev_option_code code, struct option *opt)
+{
+       int retval = ops->get_option(dev, code, opt);
+       if (retval)
+               return retval;
+
+       /* If we dont get the correct type we fail. */
+       if (OPTION_TYPE(*opt) != OPTION_TYPE_S)
+               retval = -EINVAL;
+
+       return retval;
+}
+
+int print_blockdev_info(struct instance *dev)
+{
+       struct option opt;
+       unsigned int type = 0;
+       unsigned int block_size = 0;
+       lbaint_t offset = 0;
+       lbaint_t block_count = 0;
+       char *vendor = NULL;
+       char *product = NULL;
+       char *revision = NULL;
+       int vendor_malloc = 0;
+       int product_malloc = 0;
+       int revision_malloc = 0;
+       struct blockdev_id id = empty_id;
+       struct blockdev_core_entry *entry = NULL;
+       struct blockdev_ops *ops = NULL;
+       int retval = 0;
+       enum blockdev_iftype iftype;
+
+       entry = get_entry_by_instance(dev);
+       if (!entry)
+               return -ENOENT;
+       else {
+               id = entry->name;
+               ops = entry->ops;
+       }
+
+       /* we are not using blockdev_get_option, so we activate manually here */
+       retval = driver_activate(dev);
+       if (retval)
+               return retval;
+
+       retval = get_opt_u(ops, dev, BLKD_OPT_TYPE, &opt);
+       if (retval)
+               return retval;
+       type = opt.data.data_u;
+
+       if (type == BLOCKDEV_TYPE_PARTITION) {
+               /* get options that make sense for a partition */
+               retval = get_opt_u(ops, dev, BLKD_OPT_BLOCKCOUNT, &opt);
+               if (retval)
+                       return retval;
+               block_count = opt.data.data_u;
+
+               retval = get_opt_u(ops, dev, BLKD_OPT_OFFSET, &opt);
+               if (retval)
+                       return retval;
+               offset = opt.data.data_u;
+
+               /* print some information message */
+               printf("%s%d:%d\n\tpartition on %s%d\n\t"
+                       "offset: %lu\n\tblock count: %lu\n\n",
+                       blockdev_name[id.disk.type], id.disk.number,
+                       id.partition, blockdev_name[id.disk.type],
+                       id.disk.number, offset, block_count);
+
+       } else {
+               /* get options that make sense for a disk */
+               retval = get_opt_u(ops, dev, BLKD_OPT_IFTYPE, &opt);
+               if (retval)
+                       return retval;
+               iftype = opt.data.data_u;
+
+               retval = get_opt_u(ops, dev, BLKD_OPT_BLOCKSIZE, &opt);
+               if (retval)
+                       return retval;
+               block_size = opt.data.data_u;
+
+               retval = get_opt_u(ops, dev, BLKD_OPT_BLOCKCOUNT, &opt);
+               if (retval)
+                       return retval;
+               block_count = opt.data.data_u;
+
+               retval = get_opt_s(ops, dev, BLKD_OPT_VENDOR, &opt);
+               if (retval)
+                       return retval;
+               vendor = opt.data.data_s;
+               vendor_malloc = opt.flags & OPTION_PTR_MALLOCED;
+
+               retval = get_opt_s(ops, dev, BLKD_OPT_PRODUCT, &opt);
+               if (retval)
+                       return retval;
+               product = opt.data.data_s;
+               product_malloc = opt.flags & OPTION_PTR_MALLOCED;
+
+               retval = get_opt_s(ops, dev, BLKD_OPT_REVISION, &opt);
+               if (retval)
+                       return retval;
+               revision = opt.data.data_s;
+               revision_malloc = opt.flags & OPTION_PTR_MALLOCED;
+
+               /* print some information message */
+               printf("%s%d\n\tvendor: %s\n\tproduct: %s\n\t"
+                       "revision: %s\n\ttype: %s\n\tiftype: %s\n\t"
+                       "block size: %d\n\tblock count: %lu\n\n",
+                       blockdev_name[id.disk.type], id.disk.number,
+                       vendor, product, revision, type_name(type),
+                       iftype_name[iftype], block_size, block_count);
+
+               /*cleanup if we got dynamic memory pointers*/
+               if (vendor_malloc)
+                       free(vendor);
+
+               if (product_malloc)
+                       free(product);
+
+               if (revision_malloc)
+                       free(revision);
+       }
+
+       return retval;
+}
+
+static void sort_bdid_i(struct bdid_instance_pair *data, size_t count)
+{
+       /* use bubble sort for now */
+       int a, b;
+       struct bdid_instance_pair tswap;
+
+       for (a = 1; a < count; a++) {
+               for (b = a; b > 0; b--) {
+                       if (id_cmp(&data[b].id, &data[b-1].id) < 0) {
+                               /*swap position b and b-1 */
+                               tswap = data[b-1];
+                               data[b-1] = data[b];
+                               data[b] = tswap;
+                       }
+               }
+       }
+}
+
+int print_blockdev_info_all(void)
+{
+       struct core_instance *core = NULL;
+       struct bdid_instance_pair *sorted_pairs = NULL;
+       struct blockdev_core_entry *entry = NULL;
+       int count = 0;
+       int idx = 0;
+
+       core = get_core_instance(CORE_BLOCKDEV);
+       if (!core)
+               return -ENOMEM;
+
+       count = core_get_count(CORE_BLOCKDEV);
+       sorted_pairs = malloc(sizeof(*sorted_pairs) * count);
+       if (!sorted_pairs)
+               return -ENOMEM;
+
+       /* get list of all instances and associated IDs */
+       list_for_each_entry(entry, &core->succ, list) {
+               sorted_pairs[idx].id = entry->name;
+               sorted_pairs[idx].inst = entry->instance;
+               idx++;
+       }
+
+       /* sort isntances by ID */
+       sort_bdid_i(sorted_pairs, count);
+
+       /* print info about each instance */
+       for (idx = 0; idx < count; idx++)
+               print_blockdev_info(sorted_pairs[idx].inst);
+       return 0;
+}
diff --git a/drivers/blockdev/part_types/part_dos.c 
b/drivers/blockdev/part_types/part_dos.c
new file mode 100644
index 0000000..7d19818
--- /dev/null
+++ b/drivers/blockdev/part_types/part_dos.c
@@ -0,0 +1,148 @@
+/*
+ * (C) Copyright 2001
+ * Raymond Lo, l...@routefree.com
+ * Wolfgang Denk, DENX Software Engineering, w...@denx.de.
+ *
+ * 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
+ */
+
+/*
+ * Support for harddisk partitions.
+ *
+ * To be compatible with LinuxPPC and Apple we use the standard Apple
+ * SCSI disk partitioning scheme. For more information see:
+ * 
http://developer.apple.com/techpubs/mac/Devices/Devices-126.html#MARKER-14-92
+ */
+
+#include <common.h>
+#include <ide.h>
+#include "part_dos.h"
+#include <dm/blockdev.h>
+
+#if defined(CONFIG_CMD_IDE) || \
+       defined(CONFIG_CMD_MG_DISK) || \
+       defined(CONFIG_CMD_SATA) || \
+       defined(CONFIG_CMD_SCSI) || \
+       defined(CONFIG_CMD_USB) || \
+       defined(CONFIG_MMC) || \
+       defined(CONFIG_SYSTEMACE)
+
+/* Convert char[4] in little endian format to the host format integer
+ */
+static inline int le32_to_int(unsigned char *le32)
+{
+       return ((le32[3] << 24) +
+               (le32[2] << 16) +
+               (le32[1] << 8) +
+               le32[0]);
+}
+
+static inline int is_extended(int part_type)
+{
+       return (part_type == 0x5 ||
+               part_type == 0xf ||
+               part_type == 0x85);
+}
+
+int test_partitions_dos(struct instance *dev)
+{
+       struct option blksz;
+       int error = blockdev_get_option(dev, BLKD_OPT_BLOCKSIZE, &blksz);
+       if (error)
+               return error;
+
+       ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, blksz.data.data_u);
+
+       if ((blockdev_read(dev, 0, 1, buffer) != 1) ||
+           (buffer[DOS_PART_MAGIC_OFFSET + 0] != 0x55) ||
+           (buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa)) {
+               return -EINVAL;
+       }
+       return 0;
+}
+
+
+
+int scan_partitions_dos(struct instance *dev)
+{
+       struct dos_partition *pt;
+       lbaint_t extpt_sector = 0;
+       struct option blksz;
+       int error = blockdev_get_option(dev, BLKD_OPT_BLOCKSIZE, &blksz);
+       if (error)
+               return error;
+
+       ALLOC_CACHE_ALIGN_BUFFER(unsigned char, buffer, blksz.data.data_u);
+
+       if (blockdev_read(dev, 0, 1, buffer) != 1) {
+               printf("** Can't read partition table **\n");
+               return -EINVAL;
+       }
+
+       if (buffer[DOS_PART_MAGIC_OFFSET] != 0x55 ||
+               buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) {
+               printf("bad MBR sector signature 0x%02x%02x\n",
+                       buffer[DOS_PART_MAGIC_OFFSET],
+                       buffer[DOS_PART_MAGIC_OFFSET + 1]);
+               return -EINVAL;
+       }
+
+       pt = (struct dos_partition *) (buffer + DOS_PART_TBL_OFFSET);
+       int i;
+       for (i = 0; i < 4; i++, pt++) {
+               if (((pt->boot_ind & ~0x80) == 0) &&
+                               (pt->sys_ind != 0) &&
+                               (is_extended(pt->sys_ind) == 0)) {
+                       add_partition(dev, le32_to_int(pt->start4),
+                               le32_to_int(pt->size4), i+1);
+               }
+               if (is_extended(pt->sys_ind))
+                       extpt_sector = le32_to_int(pt->start4);
+       }
+
+       if (extpt_sector == 0)
+               return 0;
+
+       /* repeat once for extended partitions */
+       if (blockdev_read(dev, extpt_sector, 1, buffer) != 1) {
+               printf("** Can't read extended partition table **\n");
+               return -EINVAL;
+       }
+
+       if (buffer[DOS_PART_MAGIC_OFFSET] != 0x55 ||
+               buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) {
+               printf("bad MBR sector signature 0x%02x%02x\n",
+                       buffer[DOS_PART_MAGIC_OFFSET],
+                       buffer[DOS_PART_MAGIC_OFFSET + 1]);
+               return -EINVAL;
+       }
+
+       pt = (struct dos_partition *) (buffer + DOS_PART_TBL_OFFSET);
+       for (i = 0; i < 4; i++, pt++) {
+               if (((pt->boot_ind & ~0x80) == 0) &&
+                               (pt->sys_ind != 0) &&
+                               (is_extended(pt->sys_ind) == 0)) {
+                       add_partition(dev, le32_to_int(pt->start4),
+                               le32_to_int(pt->size4), i+5);
+               }
+       }
+
+       return 0;
+}
+#endif
diff --git a/drivers/blockdev/part_types/part_dos.h 
b/drivers/blockdev/part_types/part_dos.h
new file mode 100644
index 0000000..98b0293
--- /dev/null
+++ b/drivers/blockdev/part_types/part_dos.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, w...@denx.de.
+ *
+ * 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 _DISK_PART_DOS_H
+#define _DISK_PART_DOS_H
+
+
+#define DOS_PART_TBL_OFFSET    0x1be
+#define DOS_PART_MAGIC_OFFSET  0x1fe
+#define DOS_PBR_FSTYPE_OFFSET  0x36
+#define DOS_PBR32_FSTYPE_OFFSET        0x52
+#define DOS_PBR_MEDIA_TYPE_OFFSET      0x15
+#define DOS_MBR        0
+#define DOS_PBR        1
+
+struct dos_partition {
+       unsigned char boot_ind;         /* 0x80 - active                */
+       unsigned char head;             /* starting head                */
+       unsigned char sector;           /* starting sector              */
+       unsigned char cyl;              /* starting cylinder            */
+       unsigned char sys_ind;          /* What partition type          */
+       unsigned char end_head;         /* end head                     */
+       unsigned char end_sector;       /* end sector                   */
+       unsigned char end_cyl;          /* end cylinder                 */
+       unsigned char start4[4];        /* starting sector counting from 0*/
+       unsigned char size4[4];         /* nr of sectors in partition   */
+};
+
+#endif /* _DISK_PART_DOS_H */
diff --git a/drivers/blockdev/part_types/part_types.h 
b/drivers/blockdev/part_types/part_types.h
new file mode 100644
index 0000000..d5e6f61
--- /dev/null
+++ b/drivers/blockdev/part_types/part_types.h
@@ -0,0 +1,34 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.i...@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 _BLOCKDEV_PART_TYPES_H_
+#define _BLOCKDEV_PART_TYPES_H_ 1
+
+#include <dm/structures.h>
+
+#ifdef CONFIG_DOS_PARTITION
+int test_partitions_dos(struct instance *i);
+int scan_partitions_dos(struct instance *i);
+#endif
+
+#endif
-- 
1.7.12

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to