summaryrefslogtreecommitdiff
path: root/api
diff options
context:
space:
mode:
authorRafal Jaworowski <raj@semihalf.com>2008-01-09 19:39:36 +0100
committerRafal Jaworowski <raj@semihalf.com>2008-01-09 19:39:36 +0100
commit500856eb1707ed17d9204baa61dd59948d3b2899 (patch)
treee29338498cbf1b92ad496271e65934b890115545 /api
parent26a41790f8eba19ad450e18ae91351daf485b3e2 (diff)
API for external applications.
This is an API for external (standalone) applications running on top of U-Boot, and is meant to be more extensible and robust than the existing jumptable mechanism. It is similar to UNIX syscall approach. See api/README for more details. Included is the demo application using this new framework (api_examples). Please note this is still an experimental feature, and is turned off by default. Signed-off-by: Rafal Jaworowski <raj@semihalf.com>
Diffstat (limited to 'api')
-rw-r--r--api/Makefile40
-rw-r--r--api/README55
-rw-r--r--api/api.c670
-rw-r--r--api/api_net.c113
-rw-r--r--api/api_platform-arm.c60
-rw-r--r--api/api_platform-ppc.c79
-rw-r--r--api/api_private.h48
-rw-r--r--api/api_storage.c370
8 files changed, 1435 insertions, 0 deletions
diff --git a/api/Makefile b/api/Makefile
new file mode 100644
index 0000000000..94de3dc395
--- /dev/null
+++ b/api/Makefile
@@ -0,0 +1,40 @@
+#
+# (C) Copyright 2007 Semihalf
+#
+# 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 Foundatio; 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)libapi.a
+
+COBJS = api.o api_net.o api_storage.o api_platform-$(ARCH).o
+
+SRCS := $(COBJS:.o=.c)
+OBJS := $(addprefix $(obj),$(COBJS))
+
+all: $(LIB)
+
+$(LIB): $(obj).depend $(OBJS)
+ $(AR) $(ARFLAGS) $@ $(OBJS)
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
diff --git a/api/README b/api/README
new file mode 100644
index 0000000000..c8f9c457c4
--- /dev/null
+++ b/api/README
@@ -0,0 +1,55 @@
+U-Boot machine/arch independent API for external apps
+=====================================================
+
+1. Main assumptions
+
+ - there is a single entry point (syscall) to the API
+
+ - per current design the syscall is a C-callable function in the U-Boot
+ text, which might evolve into a real syscall using machine exception trap
+ once this initial version proves functional
+
+ - the consumer app is responsible for producing appropriate context (call
+ number and arguments)
+
+ - upon entry, the syscall dispatches the call to other (existing) U-Boot
+ functional areas like networking or storage operations
+
+ - consumer application will recognize the API is available by searching
+ a specified (assumed by convention) range of address space for the
+ signature
+
+ - the U-Boot integral part of the API is meant to be thin and non-intrusive,
+ leaving as much processing as possible on the consumer application side,
+ for example it doesn't keep states, but relies on hints from the app and
+ so on
+
+ - optional (CONFIG_API)
+
+
+2. Calls
+
+ - console related (getc, putc, tstc etc.)
+ - system (reset, platform info)
+ - time (delay, current)
+ - env vars (enumerate all, get, set)
+ - devices (enumerate all, open, close, read, write); currently two classes
+ of devices are recognized and supported: network and storage (ide, scsi,
+ usb etc.)
+
+
+3. Structure overview
+
+ - core API, integral part of U-Boot, mandatory
+ - implements the single entry point (mimics UNIX syscall)
+
+ - glue
+ - entry point at the consumer side, allows to make syscall, mandatory
+ part
+
+ - helper conveniency wrappers so that consumer app does not have to use
+ the syscall directly, but in a more friendly manner (a la libc calls),
+ optional part
+
+ - consumer application
+ - calls directly, or leverages the provided glue mid-layer
diff --git a/api/api.c b/api/api.c
new file mode 100644
index 0000000000..10f83eb691
--- /dev/null
+++ b/api/api.c
@@ -0,0 +1,670 @@
+/*
+ * (C) Copyright 2007 Semihalf
+ *
+ * Written by: Rafal Jaworowski <raj@semihalf.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 <config.h>
+
+#if defined(CONFIG_API)
+
+#include <command.h>
+#include <common.h>
+#include <malloc.h>
+#include <linux/types.h>
+#include <api_public.h>
+
+#include "api_private.h"
+
+#define DEBUG
+#undef DEBUG
+
+/* U-Boot routines needed */
+extern int do_reset (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
+extern uchar (*env_get_char)(int);
+extern uchar *env_get_addr(int);
+
+/*****************************************************************************
+ *
+ * This is the API core.
+ *
+ * API_ functions are part of U-Boot code and constitute the lowest level
+ * calls:
+ *
+ * - they know what values they need as arguments
+ * - their direct return value pertains to the API_ "shell" itself (0 on
+ * success, some error code otherwise)
+ * - if the call returns a value it is buried within arguments
+ *
+ ****************************************************************************/
+
+#ifdef DEBUG
+#define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt, ##args); } while (0)
+#else
+#define debugf(fmt, args...)
+#endif
+
+typedef int (*cfp_t)(va_list argp);
+
+static int calls_no;
+
+/*
+ * pseudo signature:
+ *
+ * int API_getc(int *c)
+ */
+static int API_getc(va_list ap)
+{
+ int *c;
+
+ if ((c = (int *)va_arg(ap, u_int32_t)) == NULL)
+ return API_EINVAL;
+
+ *c = getc();
+ return 0;
+}
+
+/*
+ * pseudo signature:
+ *
+ * int API_tstc(int *c)
+ */
+static int API_tstc(va_list ap)
+{
+ int *t;
+
+ if ((t = (int *)va_arg(ap, u_int32_t)) == NULL)
+ return API_EINVAL;
+
+ *t = tstc();
+ return 0;
+}
+
+/*
+ * pseudo signature:
+ *
+ * int API_putc(char *ch)
+ */
+static int API_putc(va_list ap)
+{
+ char *c;
+
+ if ((c = (char *)va_arg(ap, u_int32_t)) == NULL)
+ return API_EINVAL;
+
+ putc(*c);
+ return 0;
+}
+
+/*
+ * pseudo signature:
+ *
+ * int API_puts(char **s)
+ */
+static int API_puts(va_list ap)
+{
+ char *s;
+
+ if ((s = (char *)va_arg(ap, u_int32_t)) == NULL)
+ return API_EINVAL;
+
+ puts(s);
+ return 0;
+}
+
+/*
+ * pseudo signature:
+ *
+ * int API_reset(void)
+ */
+static int API_reset(va_list ap)
+{
+ do_reset(NULL, 0, 0, NULL);
+
+ /* NOT REACHED */
+ return 0;
+}
+
+/*
+ * pseudo signature:
+ *
+ * int API_get_sys_info(struct sys_info *si)
+ *
+ * fill out the sys_info struct containing selected parameters about the
+ * machine
+ */
+static int API_get_sys_info(va_list ap)
+{
+ struct sys_info *si;
+
+ si = (struct sys_info *)va_arg(ap, u_int32_t);
+ if (si == NULL)
+ return API_ENOMEM;
+
+ return (platform_sys_info(si)) ? 0 : API_ENODEV;
+}
+
+/*
+ * pseudo signature:
+ *
+ * int API_udelay(unsigned long *udelay)
+ */
+static int API_udelay(va_list ap)
+{
+ unsigned long *d;
+
+ if ((d = (unsigned long *)va_arg(ap, u_int32_t)) == NULL)
+ return API_EINVAL;
+
+ udelay(*d);
+ return 0;
+}
+
+/*
+ * pseudo signature:
+ *
+ * int API_get_timer(unsigned long *current, unsigned long *base)
+ */
+static int API_get_timer(va_list ap)
+{
+ unsigned long *base, *cur;
+
+ cur = (unsigned long *)va_arg(ap, u_int32_t);
+ if (cur == NULL)
+ return API_EINVAL;
+
+ base = (unsigned long *)va_arg(ap, u_int32_t);
+ if (base == NULL)
+ return API_EINVAL;
+
+ *cur = get_timer(*base);
+ return 0;
+}
+
+
+/*****************************************************************************
+ *
+ * pseudo signature:
+ *
+ * int API_dev_enum(struct device_info *)
+ *
+ *
+ * cookies uniqely identify the previously enumerated device instance and
+ * provide a hint for what to inspect in current enum iteration:
+ *
+ * - net: &eth_device struct address from list pointed to by eth_devices
+ *
+ * - storage: block_dev_desc_t struct address from &ide_dev_desc[n],
+ * &scsi_dev_desc[n] and similar tables
+ *
+ ****************************************************************************/
+
+static int API_dev_enum(va_list ap)
+{
+ struct device_info *di;
+
+ /* arg is ptr to the device_info struct we are going to fill out */
+ di = (struct device_info *)va_arg(ap, u_int32_t);
+ if (di == NULL)
+ return API_EINVAL;
+
+ if (di->cookie == NULL) {
+ /* start over - clean up enumeration */
+ dev_enum_reset(); /* XXX shouldn't the name contain 'stor'? */
+ debugf("RESTART ENUM\n");
+
+ /* net device enumeration first */
+ if (dev_enum_net(di))
+ return 0;
+ }
+
+ /*
+ * The hidden assumption is there can only be one active network
+ * device and it is identified upon enumeration (re)start, so there's
+ * no point in trying to find network devices in other cases than the
+ * (re)start and hence the 'next' device can only be storage
+ */
+ if (!dev_enum_storage(di))
+ /* make sure we mark there are no more devices */
+ di->cookie = NULL;
+
+ return 0;
+}
+
+
+static int API_dev_open(va_list ap)
+{
+ struct device_info *di;
+ int err = 0;
+
+ /* arg is ptr to the device_info struct */
+ di = (struct device_info *)va_arg(ap, u_int32_t);
+ if (di == NULL)
+ return API_EINVAL;
+
+ /* Allow only one consumer of the device at a time */
+ if (di->state == DEV_STA_OPEN)
+ return API_EBUSY;
+
+ if (di->cookie == NULL)
+ return API_ENODEV;
+
+ if (di->type & DEV_TYP_STOR)
+ err = dev_open_stor(di->cookie);
+
+ else if (di->type & DEV_TYP_NET)
+ err = dev_open_net(di->cookie);
+ else
+ err = API_ENODEV;
+
+ if (!err)
+ di->state = DEV_STA_OPEN;
+
+ return err;
+}
+
+
+static int API_dev_close(va_list ap)
+{
+ struct device_info *di;
+ int err = 0;
+
+ /* arg is ptr to the device_info struct */
+ di = (struct device_info *)va_arg(ap, u_int32_t);
+ if (di == NULL)
+ return API_EINVAL;
+
+ if (di->state == DEV_STA_CLOSED)
+ return 0;
+
+ if (di->cookie == NULL)
+ return API_ENODEV;
+
+ if (di->type & DEV_TYP_STOR)
+ err = dev_close_stor(di->cookie);
+
+ else if (di->type & DEV_TYP_NET)
+ err = dev_close_net(di->cookie);
+ else
+ /*
+ * In case of unknown device we cannot change its state, so
+ * only return error code
+ */
+ err = API_ENODEV;
+
+ if (!err)
+ di->state = DEV_STA_CLOSED;
+
+ return err;
+}
+
+
+/*
+ * Notice: this is for sending network packets only, as U-Boot does not
+ * support writing to storage at the moment (12.2007)
+ *
+ * pseudo signature:
+ *
+ * int API_dev_write(
+ * struct device_info *di,
+ * void *buf,
+ * int *len
+ * )
+ *
+ * buf: ptr to buffer from where to get the data to send
+ *
+ * len: length of packet to be sent (in bytes)
+ *
+ */
+static int API_dev_write(va_list ap)
+{
+ struct device_info *di;
+ void *buf;
+ int *len;
+ int err = 0;
+
+ /* 1. arg is ptr to the device_info struct */
+ di = (struct device_info *)va_arg(ap, u_int32_t);
+ if (di == NULL)
+ return API_EINVAL;
+
+ /* XXX should we check if device is open? i.e. the ->state ? */
+
+ if (di->cookie == NULL)
+ return API_ENODEV;
+
+ /* 2. arg is ptr to buffer from where to get data to write */
+ buf = (void *)va_arg(ap, u_int32_t);
+ if (buf == NULL)
+ return API_EINVAL;
+
+ /* 3. arg is length of buffer */
+ len = (int *)va_arg(ap, u_int32_t);
+ if (len == NULL)
+ return API_EINVAL;
+ if (*len <= 0)
+ return API_EINVAL;
+
+ if (di->type & DEV_TYP_STOR)
+ /*
+ * write to storage is currently not supported by U-Boot:
+ * no storage device implements block_write() method
+ */
+ return API_ENODEV;
+
+ else if (di->type & DEV_TYP_NET)
+ err = dev_write_net(di->cookie, buf, *len);
+ else
+ err = API_ENODEV;
+
+ return err;
+}
+
+
+/*
+ * pseudo signature:
+ *
+ * int API_dev_read(
+ * struct device_info *di,
+ * void *buf,
+ * size_t *len,
+ * unsigned long *start
+ * size_t *act_len
+ * )
+ *
+ * buf: ptr to buffer where to put the read data
+ *
+ * len: ptr to length to be read
+ * - network: len of packet to read (in bytes)
+ * - storage: # of blocks to read (can vary in size depending on define)
+ *
+ * start: ptr to start block (only used for storage devices, ignored for
+ * network)
+ *
+ * act_len: ptr to where to put the len actually read
+ */
+static int API_dev_read(va_list ap)
+{
+ struct device_info *di;
+ void *buf;
+ lbasize_t *len_stor, *act_len_stor;
+ lbastart_t *start;
+ int *len_net, *act_len_net;
+
+ /* 1. arg is ptr to the device_info struct */
+ di = (struct device_info *)va_arg(ap, u_int32_t);
+ if (di == NULL)
+ return API_EINVAL;
+
+ /* XXX should we check if device is open? i.e. the ->state ? */
+
+ if (di->cookie == NULL)
+ return API_ENODEV;
+
+ /* 2. arg is ptr to buffer from where to put the read data */
+ buf = (void *)va_arg(ap, u_int32_t);
+ if (buf == NULL)
+ return API_EINVAL;
+
+ if (di->type & DEV_TYP_STOR) {
+ /* 3. arg - ptr to var with # of blocks to read */
+ len_stor = (lbasize_t *)va_arg(ap, u_int32_t);
+ if (!len_stor)
+ return API_EINVAL;
+ if (*len_stor <= 0)
+ return API_EINVAL;
+
+ /* 4. arg - ptr to var with start block */
+ start = (lbastart_t *)va_arg(ap, u_int32_t);
+
+ /* 5. arg - ptr to var where to put the len actually read */
+ act_len_stor = (lbasize_t *)va_arg(ap, u_int32_t);
+ if (!act_len_stor)
+ return API_EINVAL;
+
+ *act_len_stor = dev_read_stor(di->cookie, buf, *len_stor, *start);
+
+ } else if (di->type & DEV_TYP_NET) {
+
+ /* 3. arg points to the var with length of packet to read */
+ len_net = (int *)va_arg(ap, u_int32_t);
+ if (!len_net)
+ return API_EINVAL;
+ if (*len_net <= 0)
+ return API_EINVAL;
+
+ /* 4. - ptr to var where to put the len actually read */
+ act_len_net = (int *)va_arg(ap, u_int32_t);
+ if (!act_len_net)
+ return API_EINVAL;
+
+ *act_len_net = dev_read_net(di->cookie, buf, *len_net);
+
+ } else
+ return API_ENODEV;
+
+ return 0;
+}
+
+
+/*
+ * pseudo signature:
+ *
+ * int API_env_get(const char *name, char **value)
+ *
+ * name: ptr to name of env var
+ */
+static int API_env_get(va_list ap)
+{
+ char *name, **value;
+
+ if ((name = (char *)va_arg(ap, u_int32_t)) == NULL)
+ return API_EINVAL;
+ if ((value = (char **)va_arg(ap, u_int32_t)) == NULL)
+ return API_EINVAL;
+
+ *value = getenv(name);
+
+ return 0;
+}
+
+/*
+ * pseudo signature:
+ *
+ * int API_env_set(const char *name, const char *value)
+ *
+ * name: ptr to name of env var
+ *
+ * value: ptr to value to be set
+ */
+static int API_env_set(va_list ap)
+{
+ char *name, *value;
+
+ if ((name = (char *)va_arg(ap, u_int32_t)) == NULL)
+ return API_EINVAL;
+ if ((value = (char *)va_arg(ap, u_int32_t)) == NULL)
+ return API_EINVAL;
+
+ setenv(name, value);
+
+ return 0;
+}
+
+/*
+ * pseudo signature:
+ *
+ * int API_env_enum(const char *last, char **next)
+ *
+ * last: ptr to name of env var found in last iteration
+ */
+static int API_env_enum(va_list ap)
+{
+ int i, n;
+ char *last, **next;
+
+ last = (char *)va_arg(ap, u_int32_t);
+
+ if ((next = (char **)va_arg(ap, u_int32_t)) == NULL)
+ return API_EINVAL;
+
+ if (last == NULL)
+ /* start over */
+ *next = ((char *)env_get_addr(0));
+ else {
+ *next = last;
+
+ for (i = 0; env_get_char(i) != '\0'; i = n + 1) {
+ for (n = i; env_get_char(n) != '\0'; ++n) {
+ if (n >= CFG_ENV_SIZE) {
+ /* XXX shouldn't we set *next = NULL?? */
+ return 0;
+ }
+ }
+
+ if (envmatch((uchar *)last, i) < 0)
+ continue;
+
+ /* try to get next name */
+ i = n + 1;
+ if (env_get_char(i) == '\0') {
+ /* no more left */
+ *next = NULL;
+ return 0;
+ }
+
+ *next = ((char *)env_get_addr(i));
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static cfp_t calls_table[API_MAXCALL] = { NULL, };
+
+/*
+ * The main syscall entry point - this is not reentrant, only one call is
+ * serviced until finished.
+ *
+ * e.g. syscall(1, int *, u_int32_t, u_int32_t, u_int32_t, u_int32_t);
+ *
+ * call: syscall number
+ *
+ * retval: points to the return value placeholder, this is the place the
+ * syscall puts its return value, if NULL the caller does not
+ * expect a return value
+ *
+ * ... syscall arguments (variable number)
+ *
+ * returns: 0 if the call not found, 1 if serviced
+ */
+int syscall(int call, int *retval, ...)
+{
+ va_list ap;
+ int rv;
+
+ if (call < 0 || call >= calls_no || calls_table[call] == NULL) {
+ debugf("invalid call #%d\n", call);
+ return 0;
+ }
+
+ if (calls_table[call] == NULL) {
+ debugf("syscall #%d does not have a handler\n", call);
+ return 0;
+ }
+
+ va_start(ap, retval);
+ rv = calls_table[call](ap);
+ if (retval != NULL)
+ *retval = rv;
+
+ return 1;
+}
+
+void api_init(void)
+{
+ struct api_signature *sig = NULL;
+
+ /* TODO put this into linker set one day... */
+ calls_table[API_RSVD] = NULL;
+ calls_table[API_GETC] = &API_getc;
+ calls_table[API_PUTC] = &API_putc;
+ calls_table[API_TSTC] = &API_tstc;
+ calls_table[API_PUTS] = &API_puts;
+ calls_table[API_RESET] = &API_reset;
+ calls_table[API_GET_SYS_INFO] = &API_get_sys_info;
+ calls_table[API_UDELAY] = &API_udelay;
+ calls_table[API_GET_TIMER] = &API_get_timer;
+ calls_table[API_DEV_ENUM] = &API_dev_enum;
+ calls_table[API_DEV_OPEN] = &API_dev_open;
+ calls_table[API_DEV_CLOSE] = &API_dev_close;
+ calls_table[API_DEV_READ] = &API_dev_read;
+ calls_table[API_DEV_WRITE] = &API_dev_write;
+ calls_table[API_ENV_GET] = &API_env_get;
+ calls_table[API_ENV_SET] = &API_env_set;
+ calls_table[API_ENV_ENUM] = &API_env_enum;
+ calls_no = API_MAXCALL;
+
+ debugf("API initialized with %d calls\n", calls_no);
+
+ dev_stor_init();
+
+ /*
+ * Produce the signature so the API consumers can find it
+ */
+ sig = malloc(sizeof(struct api_signature));
+ if (sig == NULL) {
+ printf("API: could not allocate memory for the signature!\n");
+ return;
+ }
+
+ debugf("API sig @ 0x%08x\n", sig);
+ memcpy(sig->magic, API_SIG_MAGIC, 8);
+ sig->version = API_SIG_VERSION;
+ sig->syscall = &syscall;
+ sig->checksum = 0;
+ sig->checksum = crc32(0, (unsigned char *)sig,
+ sizeof(struct api_signature));
+ debugf("syscall entry: 0x%08x\n", sig->syscall);
+}
+
+void platform_set_mr(struct sys_info *si, unsigned long start, unsigned long size,
+ int flags)
+{
+ int i;
+
+ if (!si->mr || !size || (flags == 0))
+ return;
+
+ /* find free slot */
+ for (i = 0; i < si->mr_no; i++)
+ if (si->mr[i].flags == 0) {
+ /* insert new mem region */
+ si->mr[i].start = start;
+ si->mr[i].size = size;
+ si->mr[i].flags = flags;
+ return;
+ }
+}
+
+#endif /* CONFIG_API */
diff --git a/api/api_net.c b/api/api_net.c
new file mode 100644
index 0000000000..9b20a1740c
--- /dev/null
+++ b/api/api_net.c
@@ -0,0 +1,113 @@
+/*
+ * (C) Copyright 2007 Semihalf
+ *
+ * Written by: Rafal Jaworowski <raj@semihalf.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 <config.h>
+
+#if defined(CONFIG_API)
+
+#include <common.h>
+#include <net.h>
+#include <linux/types.h>
+#include <api_public.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define DEBUG
+#undef DEBUG
+
+#if !defined(CONFIG_NET_MULTI)
+#error "API/net is currently only available for platforms with CONFIG_NET_MULTI"
+#endif
+
+#ifdef DEBUG
+#define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt, ##args); } while (0)
+#else
+#define debugf(fmt, args...)
+#endif
+
+#define errf(fmt, args...) do { printf("ERROR @ %s(): ", __func__); printf(fmt, ##args); } while (0)
+
+
+static int dev_valid_net(void *cookie)
+{
+ return ((void *)eth_get_dev() == cookie) ? 1 : 0;
+}
+
+int dev_open_net(void *cookie)
+{
+ if (!dev_valid_net(cookie))
+ return API_ENODEV;
+
+ if (eth_init(gd->bd) < 0)
+ return API_EIO;
+
+ return 0;
+}
+
+int dev_close_net(void *cookie)
+{
+ if (!dev_valid_net(cookie))
+ return API_ENODEV;
+
+ eth_halt();
+ return 0;
+}
+
+/*
+ * There can only be one active eth interface at a time - use what is
+ * currently set to eth_current
+ */
+int dev_enum_net(struct device_info *di)
+{
+ struct eth_device *eth_current = eth_get_dev();
+
+ di->type = DEV_TYP_NET;
+ di->cookie = (void *)eth_current;
+ if (di->cookie == NULL)
+ return 0;
+
+ memcpy(di->di_net.hwaddr, eth_current->enetaddr, 6);
+
+ debugf("device found, returning cookie 0x%08x\n",
+ (u_int32_t)di->cookie);
+
+ return 1;
+}
+
+int dev_write_net(void *cookie, void *buf, int len)
+{
+ /* XXX verify that cookie points to a valid net device??? */
+
+ return eth_send(buf, len);
+}
+
+int dev_read_net(void *cookie, void *buf, int len)
+{
+ /* XXX verify that cookie points to a valid net device??? */
+
+ return eth_receive(buf, len);
+}
+
+#endif /* CONFIG_API */
diff --git a/api/api_platform-arm.c b/api/api_platform-arm.c
new file mode 100644
index 0000000000..ca15ca5a98
--- /dev/null
+++ b/api/api_platform-arm.c
@@ -0,0 +1,60 @@
+/*
+ * (C) Copyright 2007 Semihalf
+ *
+ * Written by: Rafal Jaworowski <raj@semihalf.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
+ *
+ *
+ * This file contains routines that fetch data from ARM-dependent sources
+ * (bd_info etc.)
+ *
+ */
+
+#include <config.h>
+
+#if defined(CONFIG_API)
+
+#include <linux/types.h>
+#include <api_public.h>
+
+#include <asm/u-boot.h>
+#include <asm/global_data.h>
+
+#include "api_private.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * Important notice: handling of individual fields MUST be kept in sync with
+ * include/asm-arm/u-boot.h and include/asm-arm/global_data.h, so any changes
+ * need to reflect their current state and layout of structures involved!
+ */
+int platform_sys_info(struct sys_info *si)
+{
+ int i;
+
+ for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++)
+ platform_set_mr(si, gd->bd->bi_dram[i].start,
+ gd->bd->bi_dram[i].size, MR_ATTR_DRAM);
+
+ return 1;
+}
+
+#endif /* CONFIG_API */
diff --git a/api/api_platform-ppc.c b/api/api_platform-ppc.c
new file mode 100644
index 0000000000..ca9f9a5cd1
--- /dev/null
+++ b/api/api_platform-ppc.c
@@ -0,0 +1,79 @@
+/*
+ * (C) Copyright 2007 Semihalf
+ *
+ * Written by: Rafal Jaworowski <raj@semihalf.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
+ *
+ *
+ * This file contains routines that fetch data from PowerPC-dependent sources
+ * (bd_info etc.)
+ *
+ */
+
+#include <config.h>
+
+#if defined(CONFIG_API)
+
+#include <linux/types.h>
+#include <api_public.h>
+
+#include <asm/u-boot.h>
+#include <asm/global_data.h>
+
+#include "api_private.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * Important notice: handling of individual fields MUST be kept in sync with
+ * include/asm-ppc/u-boot.h and include/asm-ppc/global_data.h, so any changes
+ * need to reflect their current state and layout of structures involved!
+ */
+int platform_sys_info(struct sys_info *si)
+{
+ si->clk_bus = gd->bus_clk;
+ si->clk_cpu = gd->cpu_clk;
+
+#if defined(CONFIG_5xx) || defined(CONFIG_8xx) || defined(CONFIG_8260) || \
+ defined(CONFIG_E500) || defined(CONFIG_MPC86xx)
+#define bi_bar bi_immr_base
+#elif defined(CONFIG_MPC5xxx)
+#define bi_bar bi_mbar_base
+#elif defined(CONFIG_MPC83XX)
+#define bi_bar bi_immrbar
+#elif defined(CONFIG_MPC8220)
+#define bi_bar bi_mbar_base
+#endif
+
+#if defined(bi_bar)
+ si->bar = gd->bd->bi_bar;
+#undef bi_bar
+#else
+ si->bar = NULL;
+#endif
+
+ platform_set_mr(si, gd->bd->bi_memstart, gd->bd->bi_memsize, MR_ATTR_DRAM);
+ platform_set_mr(si, gd->bd->bi_flashstart, gd->bd->bi_flashsize, MR_ATTR_FLASH);
+ platform_set_mr(si, gd->bd->bi_sramstart, gd->bd->bi_sramsize, MR_ATTR_SRAM);
+
+ return 1;
+}
+
+#endif /* CONFIG_API */
diff --git a/api/api_private.h b/api/api_private.h
new file mode 100644
index 0000000000..94a7fc509c
--- /dev/null
+++ b/api/api_private.h
@@ -0,0 +1,48 @@
+/*
+ * (C) Copyright 2007 Semihalf
+ *
+ * Written by: Rafal Jaworowski <raj@semihalf.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 _API_PRIVATE_H_
+#define _API_PRIVATE_H_
+
+void api_init(void);
+void platform_set_mr(struct sys_info *, unsigned long, unsigned long, int);
+int platform_sys_info(struct sys_info *);
+
+void dev_enum_reset(void);
+int dev_enum_storage(struct device_info *);
+int dev_enum_net(struct device_info *);
+
+int dev_open_stor(void *);
+int dev_open_net(void *);
+int dev_close_stor(void *);
+int dev_close_net(void *);
+
+lbasize_t dev_read_stor(void *, void *, lbasize_t, lbastart_t);
+int dev_read_net(void *, void *, int);
+int dev_write_net(void *, void *, int);
+
+void dev_stor_init(void);
+
+#endif /* _API_PRIVATE_H_ */
diff --git a/api/api_storage.c b/api/api_storage.c
new file mode 100644
index 0000000000..7cd4efb45b
--- /dev/null
+++ b/api/api_storage.c
@@ -0,0 +1,370 @@
+/*
+ * (C) Copyright 2007 Semihalf
+ *
+ * Written by: Rafal Jaworowski <raj@semihalf.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 <config.h>
+
+#if defined(CONFIG_API)
+
+#include <common.h>
+#include <api_public.h>
+
+#define DEBUG
+#undef DEBUG
+
+#ifdef DEBUG
+#define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt, ##args); } while (0)
+#else
+#define debugf(fmt, args...)
+#endif
+
+#define errf(fmt, args...) do { printf("ERROR @ %s(): ", __func__); printf(fmt, ##args); } while (0)
+
+
+#define ENUM_IDE 0
+#define ENUM_USB 1
+#define ENUM_SCSI 2
+#define ENUM_MMC 3
+#define ENUM_MAX 4
+
+struct stor_spec {
+ int max_dev;
+ int enum_started;
+ int enum_ended;
+ int type; /* "external" type: DT_STOR_{IDE,USB,etc} */
+ char name[4];
+};
+
+static struct stor_spec specs[ENUM_MAX] = { { 0, 0, 0, 0, "" }, };
+
+
+void dev_stor_init(void)
+{
+#if (CONFIG_COMMANDS & CFG_CMD_IDE)
+ specs[ENUM_IDE].max_dev = CFG_IDE_MAXDEVICE;
+ specs[ENUM_IDE].enum_started = 0;
+ specs[ENUM_IDE].enum_ended = 0;
+ specs[ENUM_IDE].type = DEV_TYP_STOR | DT_STOR_IDE;
+ specs[ENUM_IDE].name = "ide";
+#endif
+#if (CONFIG_COMMANDS & CFG_CMD_USB)
+ specs[ENUM_USB].max_dev = USB_MAX_STOR_DEV;
+ specs[ENUM_USB].enum_started = 0;
+ specs[ENUM_USB].enum_ended = 0;
+ specs[ENUM_USB].type = DEV_TYP_STOR | DT_STOR_USB;
+ specs[ENUM_USB].name = "usb";
+#endif
+#if (CONFIG_COMMANDS & CFG_CMD_SCSI)
+ specs[ENUM_SCSI].max_dev = CFG_SCSI_MAX_DEVICE;
+ specs[ENUM_SCSI].enum_started = 0;
+ specs[ENUM_SCSI].enum_ended = 0;
+ specs[ENUM_SCSI].type = DEV_TYP_STOR | DT_STOR_SCSI;
+ specs[ENUM_SCSI].name = "scsi";
+#endif
+}
+
+/*
+ * Finds next available device in the storage group
+ *
+ * type: storage group type - ENUM_IDE, ENUM_SCSI etc.
+ *
+ * first: if 1 the first device in the storage group is returned (if
+ * exists), if 0 the next available device is searched
+ *
+ * more: returns 0/1 depending if there are more devices in this group
+ * available (for future iterations)
+ *
+ * returns: 0/1 depending if device found in this iteration
+ */
+static int dev_stor_get(int type, int first, int *more, struct device_info *di)
+{
+ int found = 0;
+ *more = 0;
+
+ int i;
+
+ block_dev_desc_t *dd;
+
+ if (first) {
+ di->cookie = (void *)get_dev(specs[type].name, 0);
+ found = 1;
+
+ } else {
+ for (i = 0; i < specs[type].max_dev; i++)
+ if (di->cookie == (void *)get_dev(specs[type].name, i)) {
+ /* previous cookie found -- advance to the
+ * next device, if possible */
+
+ if (++i >= specs[type].max_dev) {
+ /* out of range, no more to enum */
+ di->cookie = NULL;
+ break;
+ }
+
+ di->cookie = (void *)get_dev(specs[type].name, i);
+ found = 1;
+
+ /* provide hint if there are more devices in
+ * this group to enumerate */
+ if ((i + 1) < specs[type].max_dev)
+ *more = 1;
+
+ break;
+ }
+ }
+
+ if (found) {
+ di->type = specs[type].type;
+
+ if (di->cookie != NULL) {
+ dd = (block_dev_desc_t *)di->cookie;
+ if (dd->type == DEV_TYPE_UNKNOWN) {
+ debugf("device instance exists, but is not active..");
+ found = 0;
+ } else {
+ di->di_stor.block_count = dd->lba;
+ di->di_stor.block_size = dd->blksz;
+ }
+ }
+
+ } else
+ di->cookie = NULL;
+
+ return found;
+}
+
+
+/*
+ * returns: ENUM_IDE, ENUM_USB etc. based on block_dev_desc_t
+ */
+static int dev_stor_type(block_dev_desc_t *dd)
+{
+ int i, j;
+
+ for (i = ENUM_IDE; i < ENUM_MAX; i++)
+ for (j = 0; j < specs[i].max_dev; j++)
+ if (dd == get_dev(specs[i].name, j))
+ return i;
+
+ return ENUM_MAX;
+}
+
+
+/*
+ * returns: 0/1 whether cookie points to some device in this group
+ */
+static int dev_is_stor(int type, struct device_info *di)
+{
+ return (dev_stor_type(di->cookie) == type) ? 1 : 0;
+}
+
+
+static int dev_enum_stor(int type, struct device_info *di)
+{
+ int found = 0, more = 0;
+
+ debugf("called, type %d\n", type);
+
+ /*
+ * Formulae for enumerating storage devices:
+ * 1. if cookie (hint from previous enum call) is NULL we start again
+ * with enumeration, so return the first available device, done.
+ *
+ * 2. if cookie is not NULL, check if it identifies some device in
+ * this group:
+ *
+ * 2a. if cookie is a storage device from our group (IDE, USB etc.),
+ * return next available (if exists) in this group
+ *
+ * 2b. if it isn't device from our group, check if such devices were
+ * ever enumerated before:
+ * - if not, return the first available device from this group
+ * - else return 0
+ */
+
+ if (di->cookie == NULL) {
+
+ debugf("group%d - enum restart\n", type);
+
+ /*
+ * 1. Enumeration (re-)started: take the first available
+ * device, if exists
+ */
+ found = dev_stor_get(type, 1, &more, di);
+ specs[type].enum_started = 1;
+
+ } else if (dev_is_stor(type, di)) {
+
+ debugf("group%d - enum continued for the next device\n", type);
+
+ if (specs[type].enum_ended) {
+ debugf("group%d - nothing more to enum!\n", type);
+ return 0;
+ }
+
+ /* 2a. Attempt to take a next available device in the group */
+ found = dev_stor_get(type, 0, &more, di);
+
+ } else {
+
+ if (specs[type].enum_ended) {
+ debugf("group %d - already enumerated, skipping\n", type);
+ return 0;
+ }
+
+ debugf("group%d - first time enum\n", type);
+
+ if (specs[type].enum_started == 0) {
+ /*
+ * 2b. If enumerating devices in this group did not
+ * happen before, it means the cookie pointed to a
+ * device frome some other group (another storage
+ * group, or network); in this case try to take the
+ * first available device from our group
+ */
+ specs[type].enum_started = 1;
+
+ /*
+ * Attempt to take the first device in this group:
+ *'first element' flag is set
+ */
+ found = dev_stor_get(type, 1, &more, di);
+
+ } else {
+ errf("group%d - out of order iteration\n", type);
+ found = 0;
+ more = 0;
+ }
+ }
+
+ /*
+ * If there are no more devices in this group, consider its
+ * enumeration finished
+ */
+ specs[type].enum_ended = (!more) ? 1 : 0;
+
+ if (found)
+ debugf("device found, returning cookie 0x%08x\n",
+ (u_int32_t)di->cookie);
+ else
+ debugf("no device found\n");
+
+ return found;
+}
+
+void dev_enum_reset(void)
+{
+ int i;
+
+ for (i = 0; i < ENUM_MAX; i ++) {
+ specs[i].enum_started = 0;
+ specs[i].enum_ended = 0;
+ }
+}
+
+int dev_enum_storage(struct device_info *di)
+{
+ int i;
+
+ /*
+ * check: ide, usb, scsi, mmc
+ */
+ for (i = ENUM_IDE; i < ENUM_MAX; i ++) {
+ if (dev_enum_stor(i, di))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int dev_stor_is_valid(int type, block_dev_desc_t *dd)
+{
+ int i;
+
+ for (i = 0; i < specs[type].max_dev; i++)
+ if (dd == get_dev(specs[type].name, i))
+ if (dd->type != DEV_TYPE_UNKNOWN)
+ return 1;
+
+ return 0;
+}
+
+
+int dev_open_stor(void *cookie)
+{
+ int type = dev_stor_type(cookie);
+
+ if (type == ENUM_MAX)
+ return API_ENODEV;
+
+ if (dev_stor_is_valid(type, (block_dev_desc_t *)cookie))
+ return 0;
+
+ return API_ENODEV;
+}
+
+
+int dev_close_stor(void *cookie)
+{
+ /*
+ * Not much to do as we actually do not alter storage devices upon
+ * close
+ */
+ return 0;
+}
+
+
+static int dev_stor_index(block_dev_desc_t *dd)
+{
+ int i, type;
+
+ type = dev_stor_type(dd);
+ for (i = 0; i < specs[type].max_dev; i++)
+ if (dd == get_dev(specs[type].name, i))
+ return i;
+
+ return (specs[type].max_dev);
+}
+
+
+lbasize_t dev_read_stor(void *cookie, void *buf, lbasize_t len, lbastart_t start)
+{
+ int type;
+ block_dev_desc_t *dd = (block_dev_desc_t *)cookie;
+
+ if ((type = dev_stor_type(dd)) == ENUM_MAX)
+ return 0;
+
+ if (!dev_stor_is_valid(type, dd))
+ return 0;
+
+ if ((dd->block_read) == NULL) {
+ debugf("no block_read() for device 0x%08x\n");
+ return 0;
+ }
+
+ return (dd->block_read(dev_stor_index(dd), start, len, buf));
+}
+
+#endif /* CONFIG_API */