Commit 500856eb authored by Rafal Jaworowski's avatar Rafal Jaworowski

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: default avatarRafal Jaworowski <raj@semihalf.com>
parent 26a41790
...@@ -243,6 +243,9 @@ LIBS += $(shell if [ -d post/board/$(BOARDDIR) ]; then echo \ ...@@ -243,6 +243,9 @@ LIBS += $(shell if [ -d post/board/$(BOARDDIR) ]; then echo \
"post/board/$(BOARDDIR)/libpost$(BOARD).a"; fi) "post/board/$(BOARDDIR)/libpost$(BOARD).a"; fi)
LIBS += common/libcommon.a LIBS += common/libcommon.a
LIBS += libfdt/libfdt.a LIBS += libfdt/libfdt.a
ifeq ($(CONFIG_API),y)
LIBS += api/libapi.a
endif
LIBS := $(addprefix $(obj),$(LIBS)) LIBS := $(addprefix $(obj),$(LIBS))
.PHONY : $(LIBS) .PHONY : $(LIBS)
...@@ -255,6 +258,10 @@ PLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) - ...@@ -255,6 +258,10 @@ PLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -
SUBDIRS = tools \ SUBDIRS = tools \
examples examples
ifeq ($(CONFIG_API),y)
SUBDIRS += api_examples
endif
.PHONY : $(SUBDIRS) .PHONY : $(SUBDIRS)
ifeq ($(CONFIG_NAND_U_BOOT),y) ifeq ($(CONFIG_NAND_U_BOOT),y)
...@@ -2749,6 +2756,7 @@ clean: ...@@ -2749,6 +2756,7 @@ clean:
rm -f $(obj)board/bf537-stamp/u-boot.lds $(obj)board/bf561-ezkit/u-boot.lds rm -f $(obj)board/bf537-stamp/u-boot.lds $(obj)board/bf561-ezkit/u-boot.lds
rm -f $(obj)include/bmp_logo.h rm -f $(obj)include/bmp_logo.h
rm -f $(obj)nand_spl/u-boot-spl $(obj)nand_spl/u-boot-spl.map rm -f $(obj)nand_spl/u-boot-spl $(obj)nand_spl/u-boot-spl.map
rm -f $(obj)api_examples/demo
clobber: clean clobber: clean
find $(OBJTREE) -type f \( -name .depend \ find $(OBJTREE) -type f \( -name .depend \
...@@ -2762,6 +2770,8 @@ clobber: clean ...@@ -2762,6 +2770,8 @@ clobber: clean
rm -f $(obj)tools/inca-swap-bytes $(obj)cpu/mpc824x/bedbug_603e.c rm -f $(obj)tools/inca-swap-bytes $(obj)cpu/mpc824x/bedbug_603e.c
rm -f $(obj)include/asm/proc $(obj)include/asm/arch $(obj)include/asm rm -f $(obj)include/asm/proc $(obj)include/asm/arch $(obj)include/asm
[ ! -d $(OBJTREE)/nand_spl ] || find $(obj)nand_spl -lname "*" -print | xargs rm -f [ ! -d $(OBJTREE)/nand_spl ] || find $(obj)nand_spl -lname "*" -print | xargs rm -f
find $(obj)api_examples -lname "*" -print | xargs rm -f
rm -f $(obj)api_examples/demo
ifeq ($(OBJTREE),$(SRCTREE)) ifeq ($(OBJTREE),$(SRCTREE))
mrproper \ mrproper \
......
#
# (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
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
This diff is collapsed.
/*
* (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 */
/*
* (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 */
/*
* (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 */
/*
* (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_ */
/*
* (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);