OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k/trunk/ecos-2.0/packages/redboot/v2_0/src
    from Rev 1254 to Rev 1765
    Reverse comparison

Rev 1254 → Rev 1765

/mfill.c
0,0 → 1,113
//==========================================================================
//
// mfill.c
//
// RedBoot memory fill (mfill) routine
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2002-08-06
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
 
RedBoot_cmd("mfill",
"Fill a block of memory with a pattern",
"-b <location> -l <length> -p <pattern> [-1|-2|-4]",
do_mfill
);
 
void
do_mfill(int argc, char *argv[])
{
// Fill a region of memory with a pattern
struct option_info opts[6];
unsigned long base, pat;
long len;
bool base_set, len_set, pat_set;
bool set_32bit, set_16bit, set_8bit;
 
init_opts(&opts[0], 'b', true, OPTION_ARG_TYPE_NUM,
(void **)&base, (bool *)&base_set, "base address");
init_opts(&opts[1], 'l', true, OPTION_ARG_TYPE_NUM,
(void **)&len, (bool *)&len_set, "length");
init_opts(&opts[2], 'p', true, OPTION_ARG_TYPE_NUM,
(void **)&pat, (bool *)&pat_set, "pattern");
init_opts(&opts[3], '4', false, OPTION_ARG_TYPE_FLG,
(void *)&set_32bit, (bool *)0, "fill 32 bit units");
init_opts(&opts[4], '2', false, OPTION_ARG_TYPE_FLG,
(void **)&set_16bit, (bool *)0, "fill 16 bit units");
init_opts(&opts[5], '1', false, OPTION_ARG_TYPE_FLG,
(void **)&set_8bit, (bool *)0, "fill 8 bit units");
if (!scan_opts(argc, argv, 1, opts, 6, 0, 0, "")) {
return;
}
if (!base_set || !len_set) {
diag_printf("usage: mfill -b <addr> -l <length> [-p <pattern>] [-1|-2|-4]\n");
return;
}
if (!pat_set) {
pat = 0;
}
// No checks here
if (set_8bit) {
// Fill 8 bits at a time
while ((len -= sizeof(cyg_uint8)) >= 0) {
*((cyg_uint8 *)base)++ = (cyg_uint8)pat;
}
} else if (set_16bit) {
// Fill 16 bits at a time
while ((len -= sizeof(cyg_uint16)) >= 0) {
*((cyg_uint16 *)base)++ = (cyg_uint16)pat;
}
} else {
// Default - 32 bits
while ((len -= sizeof(cyg_uint32)) >= 0) {
*((cyg_uint32 *)base)++ = (cyg_uint32)pat;
}
}
}
/decompress.c
0,0 → 1,338
//==========================================================================
//
// decompress.c
//
// RedBoot decompress support
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): jskov
// Contributors: jskov, gthomas, tkoeller
// Date: 2001-03-08
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
 
#ifdef CYGPKG_COMPRESS_ZLIB
#include <cyg/compress/zlib.h>
static z_stream stream;
static bool stream_end;
 
#define __ZLIB_MAGIC__ 0x5A4C4942 // 'ZLIB'
 
//
// Free memory [blocks] are stored as a linked list of "struct _block"
// When free, 'size' is the size of the whole block
// When allocated, 'size' is the allocation size
// The 'magic' is kept to insure that the block being freed is reasonable
//
struct _block {
int size;
long magic; // Must be __ZLIB_MAGIC__
struct _block *next;
struct _block *self;
};
static struct _block *memlist;
 
#ifdef CYGOPT_REDBOOT_FIS_ZLIB_COMMON_BUFFER
# define ZLIB_COMPRESSION_OVERHEAD CYGNUM_REDBOOT_FIS_ZLIB_COMMON_BUFFER_SIZE
#else
# define ZLIB_COMPRESSION_OVERHEAD 0xC000
#endif
static void *zlib_workspace;
 
//
// This function is run as part of RedBoot's initialization.
// It will allocate some memory from the "workspace" pool for
// use by the gzip/zlib routines. This allows the memory usage
// of RedBoot to be more finely controlled than if we simply
// used the generic 'malloc() from the heap' functionality.
//
static void
_zlib_init(void)
{
#ifdef CYGOPT_REDBOOT_FIS_ZLIB_COMMON_BUFFER
zlib_workspace = fis_zlib_common_buffer;
#else
// Allocate some RAM for use by the gzip/zlib routines
workspace_end -= ZLIB_COMPRESSION_OVERHEAD;
zlib_workspace = workspace_end;
#endif
}
 
RedBoot_init(_zlib_init, RedBoot_INIT_FIRST);
 
// #define DEBUG_ZLIB_MALLOC
#ifdef DEBUG_ZLIB_MALLOC
static void
show_memlist(char *when)
{
struct _block *bp = memlist;
 
diag_printf("memory list after %s\n", when);
while (bp != (struct _block *)0) {
diag_printf(" %08p %5d %08p\n", bp, bp->size, bp->next);
bp = bp->next;
}
}
#endif
 
// Note: these have to be global and match the prototype used by the
// gzip/zlib package since we are exactly replacing them.
 
void
*zcalloc(void *opaque, unsigned int items, unsigned int size)
{
voidpf res;
int len = (items*size);
struct _block *bp = memlist;
struct _block *nbp, *pbp;
 
// Simple, first-fit algorithm
pbp = (struct _block *)0;
while (bp) {
if (bp->size > len) {
nbp = (struct _block *)((char *)bp + len + sizeof(struct _block));
nbp->next = bp->next;
nbp->size = bp->size - len;
if (pbp) {
pbp->next = nbp;
} else {
memlist = nbp;
}
res = bp;
break;
}
pbp = bp;
bp = bp->next;
}
if (bp) {
bp->size = len;
bp->magic = __ZLIB_MAGIC__;
bp->self = bp;
res = bp+1;
memset(res, 0, len);
} else {
res = 0; // No memory left
}
#ifdef DEBUG_ZLIB_MALLOC
diag_printf("%s(%d,%d) = %p\n", __FUNCTION__, items, size, res);
show_memlist(__FUNCTION__);
#endif
return res;
}
 
void
zcfree(void *opaque, void *ptr)
{
struct _block *bp, *pbp, *nbp;
int size;
 
if (!ptr) return; // Safety
bp = (struct _block *)((char *)ptr - sizeof(struct _block));
if ((bp->magic != __ZLIB_MAGIC__) || (bp->self != bp)) {
diag_printf("%s(%p) - invalid block\n", __FUNCTION__, ptr);
return;
}
size = bp->size;
#ifdef DEBUG_ZLIB_MALLOC
diag_printf("%s(%p) = %d bytes\n", __FUNCTION__, ptr, size);
#endif
// See if this block is adjacent to a free block
nbp = memlist;
pbp = (struct _block *)0;
while (nbp) {
if ((char *)bp+size+sizeof(struct _block) == (char *)nbp) {
// The block being freed fits just before
bp->next = nbp->next;
bp->size = nbp->size + size + sizeof(struct _block);
#ifdef DEBUG_ZLIB_MALLOC
diag_printf("Free before\n");
#endif
if (pbp) {
pbp->next = bp;
// See if this new block and the previous one can
// be combined.
if ((char *)pbp+pbp->size == (char *)bp) {
#ifdef DEBUG_ZLIB_MALLOC
diag_printf("Collapse [before] - p: %p/%d/%p, n: %p/%d/%p\n",
pbp, pbp->size, pbp->next,
bp, bp->size, bp->next);
#endif
pbp->size += bp->size;
pbp->next = bp->next;
}
} else {
memlist = bp;
}
#ifdef DEBUG_ZLIB_MALLOC
show_memlist(__FUNCTION__);
#endif
return;
} else
if ((char *)nbp+nbp->size == (char *)bp) {
// The block being freed fits just after
nbp->size += size + sizeof(struct _block);
// See if it will now collapse with the following block
#ifdef DEBUG_ZLIB_MALLOC
diag_printf("Free after\n");
#endif
if (nbp->next != (struct _block *)0) {
if ((char *)nbp+nbp->size == (char *)nbp->next) {
#ifdef DEBUG_ZLIB_MALLOC
diag_printf("Collapse [after] - p: %p/%d/%p, n: %p/%d/%p\n",
nbp, nbp->size, nbp->next,
nbp->next, nbp->next->size, nbp->next->next);
#endif
nbp->size += (nbp->next)->size;
nbp->next = (nbp->next)->next;
}
}
#ifdef DEBUG_ZLIB_MALLOC
show_memlist(__FUNCTION__);
#endif
return;
} else {
pbp = nbp;
nbp = nbp->next;
}
}
}
 
//
// This function is called to initialize a gzip/zlib stream.
// Note that it also reinitializes the memory pool every time.
//
static int
gzip_init(_pipe_t* p)
{
int err;
struct _block *bp;
 
bp = (struct _block *)zlib_workspace;
memlist = bp;
bp->next = 0;
bp->size = ZLIB_COMPRESSION_OVERHEAD;
stream.zalloc = zcalloc;
stream.zfree = zcfree;
stream.next_in = NULL;
stream.avail_in = 0;
stream.next_out = NULL;
stream.avail_out = 0;
err = inflateInit(&stream);
stream_end = false;
 
return err;
}
 
//
// This function is called during the decompression cycle to
// actually cause a buffer to be filled with uncompressed data.
//
static int
gzip_inflate(_pipe_t* p)
{
int err, bytes_out;
 
if (stream_end)
return Z_STREAM_END;
stream.next_in = p->in_buf;
stream.avail_in = p->in_avail;
stream.next_out = p->out_buf;
stream.avail_out = p->out_max;
err = inflate(&stream, Z_SYNC_FLUSH);
bytes_out = stream.next_out - p->out_buf;
p->out_size += bytes_out;
p->out_buf = stream.next_out;
p->msg = stream.msg;
p->in_avail = stream.avail_in;
p->in_buf = stream.next_in;
 
// Let upper layers process any inflated bytes at
// end of stream.
if (err == Z_STREAM_END && bytes_out) {
stream_end = true;
err = Z_OK;
}
 
return err;
}
 
//
// Called when the input data is completed or an error has
// occured. This allows for clean up as well as passing error
// information up.
//
static int
gzip_close(_pipe_t* p, int err)
{
switch (err) {
case Z_STREAM_END:
err = 0;
break;
case Z_OK:
if (stream_end) {
break;
}
// Decompression didn't complete
p->msg = "premature end of input";
// fall-through
default:
err = -1;
break;
}
 
inflateEnd(&stream);
 
return err;
}
 
//
// Exported interfaces
//
_decompress_fun_init* _dc_init = gzip_init;
_decompress_fun_inflate* _dc_inflate = gzip_inflate;
_decompress_fun_close* _dc_close = gzip_close;
#endif // CYGPKG_COMPRESS_ZLIB
/net/timers.c
0,0 → 1,135
//==========================================================================
//
// net/timers.c
//
// Stand-alone networking support for RedBoot
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <net/net.h>
 
static timer_t *tmr_list;
 
 
/*
* Set a timer. Caller is responsible for providing the timer_t struct.
*/
void
__timer_set(timer_t *t, unsigned long delay,
tmr_handler_t handler, void *user_data)
{
timer_t *p;
 
t->delay = delay;
t->start = MS_TICKS();
t->handler = handler;
t->user_data = user_data;
 
for (p = tmr_list; p; p = p->next)
if (p == t) {
return;
}
 
t->next = tmr_list;
tmr_list = t;
}
 
 
/*
* Remove a given timer from timer list.
*/
void
__timer_cancel(timer_t *t)
{
timer_t *prev, *p;
 
for (prev = NULL, p = tmr_list; p; prev = p, p = p->next)
if (p == t) {
if (prev)
prev->next = p->next;
else
tmr_list = p->next;
return;
}
}
 
 
/*
* Poll timer list for timer expirations.
*/
void
__timer_poll(void)
{
timer_t *prev, *t;
 
prev = NULL;
t = tmr_list;
while (t) {
if ((MS_TICKS() - t->start) >= t->delay) {
 
/* remove it before calling handler */
if (prev)
prev->next = t->next;
else
tmr_list = t->next;
/* now, call the handler */
t->handler(t->user_data);
/*
* handler may be time consuming, so start
* from beginning of list.
*/
prev = NULL;
t = tmr_list;
} else {
prev = t;
t = t->next;
}
}
}
/net/ip.c
0,0 → 1,216
//==========================================================================
//
// net/ip.c
//
// Stand-alone IP networking support for RedBoot
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <net/net.h>
 
#ifndef CYGDAT_REDBOOT_DEFAULT_IP_ADDR
# define CYGDAT_REDBOOT_DEFAULT_IP_ADDR 0, 0, 0, 0
#endif
#ifndef CYGDAT_REDBOOT_DEFAULT_IP_ADDR_MASK
# define CYGDAT_REDBOOT_DEFAULT_IP_ADDR_MASK 255, 255, 255, 0
#endif
#ifndef CYGDAT_REDBOOT_DEFAULT_GATEWAY_IP_ADDR
# define CYGDAT_REDBOOT_DEFAULT_GATEWAY_IP_ADDR 0, 0, 0, 0
#endif
 
ip_addr_t __local_ip_addr = { CYGDAT_REDBOOT_DEFAULT_IP_ADDR };
#ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
ip_addr_t __local_ip_mask = { CYGDAT_REDBOOT_DEFAULT_IP_ADDR_MASK };
ip_addr_t __local_ip_gate = { CYGDAT_REDBOOT_DEFAULT_GATEWAY_IP_ADDR };
#endif
 
static word ip_ident;
 
#ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
/*
* See if an address is on the local network
*/
int
__ip_addr_local(ip_addr_t *addr)
{
return !(
((__local_ip_addr[0] ^ (*addr)[0]) & __local_ip_mask[0]) |
((__local_ip_addr[1] ^ (*addr)[1]) & __local_ip_mask[1]) |
((__local_ip_addr[2] ^ (*addr)[2]) & __local_ip_mask[2]) |
((__local_ip_addr[3] ^ (*addr)[3]) & __local_ip_mask[3]));
}
#endif
 
/*
* Match given IP address to our address.
* Check for broadcast matches as well.
*/
static int
ip_addr_match(ip_addr_t addr)
{
if (addr[0] == 255 && addr[1] == 255 && addr[2] == 255 && addr[3] == 255)
return 1;
 
if (!memcmp(addr, __local_ip_addr, sizeof(ip_addr_t)))
return 1;
 
/*
* Consider it an address match if we haven't gotten our IP address yet.
* Some DHCP servers will address IP packets to the assigned address
* instead of a IP broadcast address.
*/
if (__local_ip_addr[0] == 0 && __local_ip_addr[1] == 0 &&
__local_ip_addr[2] == 0 && __local_ip_addr[3] == 0)
return 1;
 
return 0;
}
 
 
extern void __tcp_handler(pktbuf_t *, ip_route_t *);
 
/*
* Handle IP packets coming from the polled ethernet interface.
*/
void
__ip_handler(pktbuf_t *pkt, enet_addr_t *src_enet_addr)
{
ip_header_t *ip = pkt->ip_hdr;
ip_route_t r;
int hdr_bytes;
 
/* first make sure its ours and has a good checksum. */
if (!ip_addr_match(ip->destination) ||
__sum((word *)ip, ip->hdr_len << 2, 0) != 0) {
__pktbuf_free(pkt);
return;
}
 
memcpy(r.ip_addr, ip->source, sizeof(ip_addr_t));
memcpy(r.enet_addr, src_enet_addr, sizeof(enet_addr_t));
 
hdr_bytes = ip->hdr_len << 2;
pkt->pkt_bytes = ntohs(ip->length) - hdr_bytes;
 
switch (ip->protocol) {
 
#if NET_SUPPORT_ICMP
case IP_PROTO_ICMP:
pkt->icmp_hdr = (icmp_header_t *)(((char *)ip) + hdr_bytes);
__icmp_handler(pkt, &r);
break;
#endif
 
#if NET_SUPPORT_TCP
case IP_PROTO_TCP:
pkt->tcp_hdr = (tcp_header_t *)(((char *)ip) + hdr_bytes);
__tcp_handler(pkt, &r);
break;
#endif
 
#if NET_SUPPORT_UDP
case IP_PROTO_UDP:
pkt->udp_hdr = (udp_header_t *)(((char *)ip) + hdr_bytes);
__udp_handler(pkt, &r);
break;
#endif
 
default:
__pktbuf_free(pkt);
break;
}
}
 
 
/*
* Send an IP packet.
*
* The IP data field should contain pkt->pkt_bytes of data.
* pkt->[udp|tcp|icmp]_hdr points to the IP data field. Any
* IP options are assumed to be already in place in the IP
* options field.
*/
int
__ip_send(pktbuf_t *pkt, int protocol, ip_route_t *dest)
{
ip_header_t *ip = pkt->ip_hdr;
int hdr_bytes;
unsigned short cksum;
/*
* Figure out header length. The use udp_hdr is
* somewhat arbitrary, but works because it is
* a union with other IP protocol headers.
*/
hdr_bytes = (((char *)pkt->udp_hdr) - ((char *)ip));
 
pkt->pkt_bytes += hdr_bytes;
 
ip->version = 4;
ip->hdr_len = hdr_bytes >> 2;
ip->tos = 0;
ip->length = htons(pkt->pkt_bytes);
ip->ident = htons(ip_ident);
ip_ident++;
ip->fragment = 0;
ip->ttl = 255;
ip->ttl = 64;
ip->protocol = protocol;
ip->checksum = 0;
memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
memcpy(ip->destination, dest->ip_addr, sizeof(ip_addr_t));
cksum = __sum((word *)ip, hdr_bytes, 0);
ip->checksum = htons(cksum);
 
__enet_send(pkt, &dest->enet_addr, ETH_TYPE_IP);
return 0;
}
 
 
/net/tftp_client.c
0,0 → 1,259
//==========================================================================
//
// net/tftp_client.c
//
// Stand-alone TFTP support for RedBoot
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
// TFTP client support
 
#include <redboot.h> // have_net
#include <net/net.h>
#include <net/tftp.h>
#include <net/tftp_support.h>
 
// So we remember which ports have been used
static int get_port = 7700;
 
static struct {
bool open;
int total_timeouts;
unsigned short last_good_block;
int avail, actual_len;
struct sockaddr_in local_addr, from_addr;
char data[SEGSIZE+sizeof(struct tftphdr)];
char *bufp;
} tftp_stream;
 
int
tftp_stream_open(connection_info_t *info,
int *err)
{
struct tftphdr *hdr = (struct tftphdr *)tftp_stream.data;
char *cp, *fp;
char test_buf;
 
if (!have_net || tftp_stream.open) {
*err = TFTP_INVALID; // Already open
return -1;
}
 
// Create initial request
hdr->th_opcode = htons(RRQ); // Read file
cp = (char *)&hdr->th_stuff;
fp = info->filename;
while (*fp) *cp++ = *fp++;
*cp++ = '\0';
// Since this is used for downloading data, OCTET (binary) is the
// only mode that makes sense.
fp = "OCTET";
while (*fp) *cp++ = *fp++;
*cp++ = '\0';
 
memset((char *)&tftp_stream.local_addr, 0, sizeof(tftp_stream.local_addr));
tftp_stream.local_addr.sin_family = AF_INET;
tftp_stream.local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
tftp_stream.local_addr.sin_port = htons(get_port++);
 
if (info->server->sin_port == 0) {
info->server->sin_port = htons(TFTP_PORT);
}
 
// Send request - note: RFC 1350 (TFTP rev 2) indicates that this should be
// only as long as required to hold the request, with the nul terminator.
// Some servers silently go to lunch if the request is not the correct size.
if (__udp_sendto(tftp_stream.data, cp-(char *)hdr,
info->server, &tftp_stream.local_addr) < 0) {
// Problem sending request
*err = TFTP_NETERR;
return -1;
}
 
tftp_stream.open = true;
tftp_stream.avail = 0;
tftp_stream.actual_len = -1;
tftp_stream.last_good_block = 0;
tftp_stream.total_timeouts = 0;
tftp_stream.from_addr.sin_port = 0;
 
// Try and read the first byte [block] since no errors are
// reported until then.
if (tftp_stream_read(&test_buf, 1, err) == 1) {
// Back up [rewind] over this datum
tftp_stream.bufp--;
tftp_stream.avail++;
return 0; // Open and first read successful
} else {
tftp_stream.open = false;
return -1; // Couldn't read
}
}
 
void
tftp_stream_close(int *err)
{
tftp_stream.open = false;
}
 
int
tftp_stream_read(char *buf,
int len,
int *err)
{
int total_bytes = 0;
int size, recv_len, data_len;
struct timeval timeout;
struct tftphdr *hdr = (struct tftphdr *)tftp_stream.data;
 
while (total_bytes < len) {
// Move any bytes which we've already read/buffered
if (tftp_stream.avail > 0) {
size = tftp_stream.avail;
if (size > (len - total_bytes)) size = len - total_bytes;
memcpy(buf, tftp_stream.bufp, size);
buf += size;
tftp_stream.bufp += size;
tftp_stream.avail -= size;
total_bytes += size;
} else {
if (tftp_stream.last_good_block != 0) {
// Send out the ACK
hdr->th_opcode = htons(ACK);
hdr->th_block = htons(tftp_stream.last_good_block);
if (__udp_sendto(tftp_stream.data, 4 /* FIXME */,
&tftp_stream.from_addr, &tftp_stream.local_addr) < 0) {
// Problem sending ACK
*err = TFTP_NETERR;
return -1;
}
}
if ((tftp_stream.actual_len >= 0) && (tftp_stream.actual_len < SEGSIZE)) {
// Out of data
break;
}
timeout.tv_sec = TFTP_TIMEOUT_PERIOD;
timeout.tv_usec = 0;
recv_len = sizeof(tftp_stream.data);
if ((data_len = __udp_recvfrom(&tftp_stream.data[0], recv_len, &tftp_stream.from_addr,
&tftp_stream.local_addr, &timeout)) < 0) {
// No data, try again
if ((++tftp_stream.total_timeouts > TFTP_TIMEOUT_MAX) ||
(tftp_stream.last_good_block == 0)) {
// Timeout - no data received
*err = TFTP_TIMEOUT;
return -1;
}
} else {
if (ntohs(hdr->th_opcode) == DATA) {
if (ntohs(hdr->th_block) == (tftp_stream.last_good_block+1)) {
// Consume this data
data_len -= 4; /* Sizeof TFTP header */
tftp_stream.avail = tftp_stream.actual_len = data_len;
tftp_stream.bufp = hdr->th_data;
tftp_stream.last_good_block++;
}
} else {
if (ntohs(hdr->th_opcode) == ERROR) {
*err = ntohs(hdr->th_code);
return -1;
} else {
// What kind of packet is this?
*err = TFTP_PROTOCOL;
return -1;
}
}
}
}
}
return total_bytes;
}
 
char *
tftp_error(int err)
{
char *errmsg = "Unknown error";
 
switch (err) {
case TFTP_ENOTFOUND:
return "file not found";
case TFTP_EACCESS:
return "access violation";
case TFTP_ENOSPACE:
return "disk full or allocation exceeded";
case TFTP_EBADOP:
return "illegal TFTP operation";
case TFTP_EBADID:
return "unknown transfer ID";
case TFTP_EEXISTS:
return "file already exists";
case TFTP_ENOUSER:
return "no such user";
case TFTP_TIMEOUT:
return "operation timed out";
case TFTP_NETERR:
return "some sort of network error";
case TFTP_INVALID:
return "invalid parameter";
case TFTP_PROTOCOL:
return "protocol violation";
case TFTP_TOOLARGE:
return "file is larger than buffer";
}
return errmsg;
}
 
//
// RedBoot interface
//
GETC_IO_FUNCS(tftp_io, tftp_stream_open, tftp_stream_close,
0, tftp_stream_read, tftp_error);
RedBoot_load(tftp, tftp_io, true, true, 0);
 
/net/net_io.c
0,0 → 1,774
//==========================================================================
//
// net/net_io.c
//
// Stand-alone network logical I/O support for RedBoot
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
#include <cyg/io/eth/eth_drv.h> // Logical driver interfaces
#include <net/net.h>
#include <cyg/hal/hal_misc.h> // Helper functions
#include <cyg/hal/hal_if.h> // HAL I/O interfaces
#include <cyg/hal/drv_api.h>
#include <cyg/hal/hal_intr.h>
 
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
#include <flash_config.h>
 
RedBoot_config_option("GDB connection port",
gdb_port,
ALWAYS_ENABLED, true,
CONFIG_INT,
CYGNUM_REDBOOT_NETWORKING_TCP_PORT
);
RedBoot_config_option("Network debug at boot time",
net_debug,
ALWAYS_ENABLED, true,
CONFIG_BOOL,
false
);
// Note: the following options are related. If 'bootp' is false, then
// the other values are used in the configuration. Because of the way
// that configuration tables are generated, they should have names which
// are related. The configuration options will show up lexicographically
// ordered, thus the peculiar naming. In this case, the 'use' option is
// negated (if false, the others apply) which makes the names even more
// confusing.
RedBoot_config_option("Use BOOTP for network configuration",
bootp,
ALWAYS_ENABLED, true,
CONFIG_BOOL,
true
);
RedBoot_config_option("Local IP address",
bootp_my_ip,
"bootp", false,
CONFIG_IP,
0
);
#ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
RedBoot_config_option("Local IP address mask",
bootp_my_ip_mask,
"bootp", false,
CONFIG_IP,
0
);
RedBoot_config_option("Gateway IP address",
bootp_my_gateway_ip,
"bootp", false,
CONFIG_IP,
0
);
#endif
RedBoot_config_option("Default server IP address",
bootp_server_ip,
"bootp", false,
CONFIG_IP,
0
);
 
// Note: the following options are related too.
RedBoot_config_option("Force console for special debug messages",
info_console_force,
ALWAYS_ENABLED, true,
CONFIG_BOOL,
false
);
RedBoot_config_option("Console number for special debug messages",
info_console_number,
"info_console_force", true,
CONFIG_INT,
0
);
#endif
 
#define TCP_CHANNEL CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS
 
#ifdef DEBUG_TCP
int show_tcp = 0;
#endif
 
static tcp_socket_t tcp_sock;
static int state;
static int _timeout = 500;
static int orig_console, orig_debug;
 
static int in_buflen = 0;
static unsigned char in_buf[64];
static unsigned char *in_bufp;
static int out_buflen = 0;
static unsigned char out_buf[1024];
static unsigned char *out_bufp;
static bool flush_output_lines = false;
 
// Functions in this module
static void net_io_flush(void);
static void net_io_revert_console(void);
static void net_io_putc(void*, cyg_uint8);
 
// Special characters used by Telnet - must be interpretted here
#define TELNET_IAC 0xFF // Interpret as command (escape)
#define TELNET_IP 0xF4 // Interrupt process
#define TELNET_WONT 0xFC // I Won't do it
#define TELNET_DO 0xFD // Will you XXX
#define TELNET_TM 0x06 // Time marker (special DO/WONT after IP)
 
static cyg_bool
_net_io_getc_nonblock(void* __ch_data, cyg_uint8* ch)
{
if (in_buflen == 0) {
__tcp_poll();
if (tcp_sock.state == _CLOSE_WAIT) {
// This connection is breaking
if (tcp_sock.data_bytes == 0 && tcp_sock.rxcnt == 0) {
__tcp_close(&tcp_sock);
return false;
}
}
if (tcp_sock.state == _CLOSED) {
// The connection is gone
net_io_revert_console();
*ch = '\n';
return true;
}
in_buflen = __tcp_read(&tcp_sock, in_buf, sizeof(in_buf));
in_bufp = in_buf;
#ifdef DEBUG_TCP
if (show_tcp && (in_buflen > 0)) {
int old_console;
old_console = start_console();
diag_printf("%s:%d\n", __FUNCTION__, __LINE__);
diag_dump_buf(in_buf, in_buflen);
end_console(old_console);
}
#endif // DEBUG_TCP
}
if (in_buflen) {
*ch = *in_bufp++;
in_buflen--;
return true;
} else {
return false;
}
}
 
static cyg_bool
net_io_getc_nonblock(void* __ch_data, cyg_uint8* ch)
{
cyg_uint8 esc;
 
if (!_net_io_getc_nonblock(__ch_data, ch))
return false;
 
if (gdb_active || *ch != TELNET_IAC)
return true;
 
// Telnet escape - need to read/handle more
while (!_net_io_getc_nonblock(__ch_data, &esc)) ;
 
switch (esc) {
case TELNET_IAC:
// The other special case - escaped escape
return true;
case TELNET_IP:
// Special case for ^C == Interrupt Process
*ch = 0x03;
// Just in case the other end needs synchronizing
net_io_putc(__ch_data, TELNET_IAC);
net_io_putc(__ch_data, TELNET_WONT);
net_io_putc(__ch_data, TELNET_TM);
net_io_flush();
return true;
case TELNET_DO:
// Telnet DO option
while (!_net_io_getc_nonblock(__ch_data, &esc)) ;
// Respond with WONT option
net_io_putc(__ch_data, TELNET_IAC);
net_io_putc(__ch_data, TELNET_WONT);
net_io_putc(__ch_data, esc);
return false; // Ignore this whole thing!
default:
return false;
}
}
 
static cyg_uint8
net_io_getc(void* __ch_data)
{
cyg_uint8 ch;
int idle_timeout = 10; // 10ms
 
CYGARC_HAL_SAVE_GP();
while (true) {
if (net_io_getc_nonblock(__ch_data, &ch)) break;
if (--idle_timeout == 0) {
net_io_flush();
idle_timeout = 10;
}
}
CYGARC_HAL_RESTORE_GP();
return ch;
}
 
static void
net_io_flush(void)
{
int n;
char *bp = out_buf;
 
#ifdef DEBUG_TCP
if (show_tcp) {
int old_console;
old_console = start_console();
diag_printf("%s.%d\n", __FUNCTION__, __LINE__);
diag_dump_buf(out_buf, out_buflen);
end_console(old_console);
}
#endif // SHOW_TCP
n = __tcp_write_block(&tcp_sock, bp, out_buflen);
if (n < 0) {
// The connection is gone!
net_io_revert_console();
} else {
out_buflen -= n;
bp += n;
}
out_bufp = out_buf; out_buflen = 0;
// Check interrupt flag
if (CYGACC_CALL_IF_CONSOLE_INTERRUPT_FLAG()) {
CYGACC_CALL_IF_CONSOLE_INTERRUPT_FLAG_SET(0);
cyg_hal_user_break(0);
}
}
 
static void
net_io_putc(void* __ch_data, cyg_uint8 c)
{
static bool have_dollar, have_hash;
static int hash_count;
 
CYGARC_HAL_SAVE_GP();
*out_bufp++ = c;
if (c == '$') have_dollar = true;
if (have_dollar && (c == '#')) {
have_hash = true;
hash_count = 0;
}
if ((++out_buflen == sizeof(out_buf)) ||
(flush_output_lines && c == '\n') ||
(have_hash && (++hash_count == 3))) {
net_io_flush();
have_dollar = false;
}
CYGARC_HAL_RESTORE_GP();
}
 
static void
net_io_write(void* __ch_data, const cyg_uint8* __buf, cyg_uint32 __len)
{
int old_console;
 
old_console = start_console();
diag_printf("%s.%d\n", __FUNCTION__, __LINE__);
end_console(old_console);
#if 0
CYGARC_HAL_SAVE_GP();
 
while(__len-- > 0)
net_io_putc(__ch_data, *__buf++);
 
CYGARC_HAL_RESTORE_GP();
#endif
}
 
static void
net_io_read(void* __ch_data, cyg_uint8* __buf, cyg_uint32 __len)
{
int old_console;
 
old_console = start_console();
diag_printf("%s.%d\n", __FUNCTION__, __LINE__);
end_console(old_console);
#if 0
CYGARC_HAL_SAVE_GP();
 
while(__len-- > 0)
*__buf++ = net_io_getc(__ch_data);
 
CYGARC_HAL_RESTORE_GP();
#endif
}
 
static cyg_bool
net_io_getc_timeout(void* __ch_data, cyg_uint8* ch)
{
int delay_count;
cyg_bool res;
 
CYGARC_HAL_SAVE_GP();
net_io_flush(); // Make sure any output has been sent
delay_count = _timeout;
 
for(;;) {
res = net_io_getc_nonblock(__ch_data, ch);
if (res || 0 == delay_count--)
break;
}
 
CYGARC_HAL_RESTORE_GP();
 
return res;
}
 
static int
net_io_control(void *__ch_data, __comm_control_cmd_t __func, ...)
{
static int vector = 0;
int ret = 0;
static int irq_state = 0;
 
CYGARC_HAL_SAVE_GP();
 
switch (__func) {
case __COMMCTL_IRQ_ENABLE:
irq_state = 1;
if (vector == 0) {
vector = eth_drv_int_vector();
}
HAL_INTERRUPT_UNMASK(vector);
break;
case __COMMCTL_IRQ_DISABLE:
ret = irq_state;
irq_state = 0;
if (vector == 0) {
vector = eth_drv_int_vector();
}
HAL_INTERRUPT_MASK(vector);
break;
case __COMMCTL_DBG_ISR_VECTOR:
ret = vector;
break;
case __COMMCTL_SET_TIMEOUT:
{
va_list ap;
 
va_start(ap, __func);
 
ret = _timeout;
_timeout = va_arg(ap, cyg_uint32);
 
va_end(ap);
break;
}
case __COMMCTL_FLUSH_OUTPUT:
net_io_flush();
break;
case __COMMCTL_ENABLE_LINE_FLUSH:
flush_output_lines = true;
break;
case __COMMCTL_DISABLE_LINE_FLUSH:
flush_output_lines = false;
break;
default:
break;
}
CYGARC_HAL_RESTORE_GP();
return ret;
}
 
static int
net_io_isr(void *__ch_data, int* __ctrlc,
CYG_ADDRWORD __vector, CYG_ADDRWORD __data)
{
char ch;
 
CYGARC_HAL_SAVE_GP();
*__ctrlc = 0;
if (net_io_getc_nonblock(__ch_data, &ch)) {
if (ch == 0x03) {
*__ctrlc = 1;
}
}
CYGARC_HAL_RESTORE_GP();
return CYG_ISR_HANDLED;
}
 
// TEMP
 
int
start_console(void)
{
int cur_console =
CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
 
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
int i = 0;
if ( flash_get_config( "info_console_force", &i, CONFIG_BOOL) )
if ( i )
if ( ! flash_get_config( "info_console_number", &i, CONFIG_INT) )
i = 0; // the default, if that call failed.
if ( i )
CYGACC_CALL_IF_SET_CONSOLE_COMM(i);
else
#endif
CYGACC_CALL_IF_SET_CONSOLE_COMM(0);
 
return cur_console;
}
 
void
end_console(int old_console)
{
// Restore original console
CYGACC_CALL_IF_SET_CONSOLE_COMM(old_console);
}
// TEMP
 
static void
net_io_revert_console(void)
{
#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
console_selected = false;
#endif
CYGACC_CALL_IF_SET_CONSOLE_COMM(orig_console);
CYGACC_CALL_IF_SET_DEBUG_COMM(orig_debug);
console_echo = true;
}
 
static void
net_io_assume_console(void)
{
#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
console_selected = true;
#endif
console_echo = false;
orig_console = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
CYGACC_CALL_IF_SET_CONSOLE_COMM(TCP_CHANNEL);
orig_debug = CYGACC_CALL_IF_SET_DEBUG_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
CYGACC_CALL_IF_SET_DEBUG_COMM(TCP_CHANNEL);
}
 
static void
net_io_init(void)
{
static int init = 0;
if (!init) {
hal_virtual_comm_table_t* comm;
int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
 
// Setup procs in the vector table
CYGACC_CALL_IF_SET_CONSOLE_COMM(TCP_CHANNEL);
comm = CYGACC_CALL_IF_CONSOLE_PROCS();
//CYGACC_COMM_IF_CH_DATA_SET(*comm, chan);
CYGACC_COMM_IF_WRITE_SET(*comm, net_io_write);
CYGACC_COMM_IF_READ_SET(*comm, net_io_read);
CYGACC_COMM_IF_PUTC_SET(*comm, net_io_putc);
CYGACC_COMM_IF_GETC_SET(*comm, net_io_getc);
CYGACC_COMM_IF_CONTROL_SET(*comm, net_io_control);
CYGACC_COMM_IF_DBG_ISR_SET(*comm, net_io_isr);
CYGACC_COMM_IF_GETC_TIMEOUT_SET(*comm, net_io_getc_timeout);
 
// Disable interrupts via this interface to set static
// state into correct state.
net_io_control( comm, __COMMCTL_IRQ_DISABLE );
// Restore original console
CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);
 
init = 1;
gdb_active = false;
}
__tcp_listen(&tcp_sock, gdb_port);
state = tcp_sock.state;
#ifdef DEBUG_TCP
diag_printf("show tcp = %p\n", (void *)&show_tcp);
#endif
}
 
// Check for incoming TCP debug connection
void
net_io_test(bool is_idle)
{
if (!is_idle) return; // Only care about idle case
if (!have_net) return;
__tcp_poll();
if (state != tcp_sock.state) {
// Something has changed
if (tcp_sock.state == _ESTABLISHED) {
// A new connection has arrived
net_io_assume_console();
in_bufp = in_buf; in_buflen = 1; *in_bufp = '\r';
out_bufp = out_buf; out_buflen = 0;
}
if (tcp_sock.state == _CLOSED) {
net_io_init(); // Get ready for another connection
}
}
state = tcp_sock.state;
}
 
// This schedules the 'net_io_test()' function to be run by RedBoot's
// main command loop when idle (i.e. when no input arrives after some
// period of time).
RedBoot_idle(net_io_test, RedBoot_IDLE_NETIO);
 
//
// Network initialization
//
#include <cyg/io/eth/eth_drv.h>
#include <cyg/io/eth/netdev.h>
#include <cyg/hal/hal_tables.h>
 
// Define table boundaries
CYG_HAL_TABLE_BEGIN( __NETDEVTAB__, netdev );
CYG_HAL_TABLE_END( __NETDEVTAB_END__, netdev );
 
RedBoot_init(net_init, RedBoot_INIT_LAST);
 
static void
show_addrs(void)
{
diag_printf("IP: %s", inet_ntoa((in_addr_t *)&__local_ip_addr));
#ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
diag_printf("/%s", inet_ntoa((in_addr_t *)&__local_ip_mask));
diag_printf(", Gateway: %s\n", inet_ntoa((in_addr_t *)&__local_ip_gate));
#else
diag_printf(", ");
#endif
diag_printf("Default server: %s", inet_ntoa(&my_bootp_info.bp_siaddr));
#ifdef CYGPKG_REDBOOT_NETWORKING_DNS
show_dns();
#endif
diag_printf("\n");
}
 
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
static void
flash_get_IP(char *id, ip_addr_t *val)
{
ip_addr_t my_ip;
int i;
 
if (flash_get_config(id, &my_ip, CONFIG_IP)) {
if (my_ip[0] != 0 || my_ip[1] != 0 ||
my_ip[2] != 0 || my_ip[3] != 0) {
// 'id' is set to something so let it override any static IP
for (i=0; i<4; i++)
(*val)[i] = my_ip[i];
}
}
}
#endif
 
void
net_init(void)
{
cyg_netdevtab_entry_t *t;
 
// Set defaults as appropriate
#ifdef CYGSEM_REDBOOT_DEFAULT_NO_BOOTP
use_bootp = false;
#else
use_bootp = true;
#endif
#ifdef CYGDBG_REDBOOT_NET_DEBUG
net_debug = true;
#else
net_debug = false;
#endif
gdb_port = CYGNUM_REDBOOT_NETWORKING_TCP_PORT;
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
// Fetch values from saved config data, if available
flash_get_config("net_debug", &net_debug, CONFIG_BOOL);
flash_get_config("gdb_port", &gdb_port, CONFIG_INT);
flash_get_config("bootp", &use_bootp, CONFIG_BOOL);
if (!use_bootp)
{
flash_get_IP("bootp_my_ip", &__local_ip_addr);
#ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
flash_get_IP("bootp_my_ip_mask", &__local_ip_mask);
flash_get_IP("bootp_my_gateway_ip", &__local_ip_gate);
#endif
flash_get_config("bootp_server_ip", &my_bootp_info.bp_siaddr,
CONFIG_IP);
}
#endif
# ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
// Don't override if the user has deliberately set something more
// verbose.
if (0 == cyg_io_eth_net_debug)
cyg_io_eth_net_debug = net_debug;
# endif
have_net = false;
// Make sure the recv buffers are set up
eth_drv_buffers_init();
__pktbuf_init();
// Initialize all network devices
for (t = &__NETDEVTAB__[0]; t != &__NETDEVTAB_END__; t++) {
if (t->init(t)) {
t->status = CYG_NETDEVTAB_STATUS_AVAIL;
} else {
// What to do if device init fails?
t->status = 0; // Device not [currently] available
}
}
if (!__local_enet_sc) {
diag_printf("No network interfaces found\n");
return;
}
// Initialize the network [if present]
if (use_bootp) {
if (__bootp_find_local_ip(&my_bootp_info) == 0) {
have_net = true;
} else {
// Is it an unset address, or has it been set to a static addr
if (__local_ip_addr[0] == 0 && __local_ip_addr[1] == 0 &&
__local_ip_addr[2] == 0 && __local_ip_addr[3] == 0) {
diag_printf("Ethernet %s: MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
__local_enet_sc->dev_name,
__local_enet_addr[0],
__local_enet_addr[1],
__local_enet_addr[2],
__local_enet_addr[3],
__local_enet_addr[4],
__local_enet_addr[5]);
diag_printf("Can't get BOOTP info for device!\n");
} else {
diag_printf("Can't get BOOTP info, using default IP address\n");
have_net = true;
}
}
} else {
have_net = true; // Assume values in FLASH were OK
}
if (have_net) {
diag_printf("Ethernet %s: MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
__local_enet_sc->dev_name,
__local_enet_addr[0],
__local_enet_addr[1],
__local_enet_addr[2],
__local_enet_addr[3],
__local_enet_addr[4],
__local_enet_addr[5]);
 
#ifdef CYGPKG_REDBOOT_NETWORKING_DNS
redboot_dns_res_init();
#endif
show_addrs();
net_io_init();
}
}
 
static char usage[] = "[-l <local_ip_address>] [-h <server_address>]";
 
// Exported CLI function
static void do_ip_addr(int argc, char *argv[]);
RedBoot_cmd("ip_address",
"Set/change IP addresses",
usage,
do_ip_addr
);
 
void
do_ip_addr(int argc, char *argv[])
{
struct option_info opts[3];
char *ip_addr, *host_addr;
bool ip_addr_set, host_addr_set;
struct sockaddr_in host;
#ifdef CYGPKG_REDBOOT_NETWORKING_DNS
char *dns_addr;
bool dns_addr_set;
#endif
int num_opts;
 
init_opts(&opts[0], 'l', true, OPTION_ARG_TYPE_STR,
(void **)&ip_addr, (bool *)&ip_addr_set, "local IP address");
init_opts(&opts[1], 'h', true, OPTION_ARG_TYPE_STR,
(void **)&host_addr, (bool *)&host_addr_set, "default server address");
num_opts = 2;
#ifdef CYGPKG_REDBOOT_NETWORKING_DNS
init_opts(&opts[2], 'd', true, OPTION_ARG_TYPE_STR,
(void **)&dns_addr, (bool *)&dns_addr_set, "DNS server address");
num_opts++;
#endif
if (!scan_opts(argc, argv, 1, opts, num_opts, 0, 0, "")) {
return;
}
if (ip_addr_set) {
if (!_gethostbyname(ip_addr, (in_addr_t *)&host)) {
diag_printf("Invalid local IP address: %s\n", ip_addr);
return;
}
// Of course, each address goes in its own place :-)
memcpy(&__local_ip_addr, &host.sin_addr, sizeof(host.sin_addr));
}
if (host_addr_set) {
if (!_gethostbyname(host_addr, (in_addr_t *)&host)) {
diag_printf("Invalid server address: %s\n", host_addr);
return;
}
my_bootp_info.bp_siaddr = host.sin_addr;
}
#ifdef CYGPKG_REDBOOT_NETWORKING_DNS
if (dns_addr_set) {
set_dns(dns_addr);
}
#endif
show_addrs();
if (!have_net) {
have_net = true;
net_io_init();
}
}
 
// EOF net_io.c
/net/http_client.c
0,0 → 1,240
//==========================================================================
//
// net/http_client.c
//
// Stand-alone HTTP support for RedBoot
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2002-05-22
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
// HTTP client support
 
#include <redboot.h> // have_net
#include <net/net.h>
#include <net/http.h>
 
// So we remember which ports have been used
static int get_port = 7800;
 
static struct _stream{
bool open;
int avail, actual_len, pos, filelen;
char data[4096];
char *bufp;
tcp_socket_t sock;
} http_stream;
 
static __inline__ int
min(int a, int b)
{
if (a < b)
return a;
else
return b;
}
 
int
http_stream_open(connection_info_t *info, int *err)
{
int res;
struct _stream *s = &http_stream;
 
info->server->sin_port = 80; // HTTP port
if ((res = __tcp_open(&s->sock, info->server, get_port++, 5000, err)) < 0) {
*err = HTTP_OPEN;
return -1;
}
diag_sprintf(s->data, "GET %s HTTP/1.0\r\n\r\n", info->filename);
__tcp_write_block(&s->sock, s->data, strlen(s->data));
s->avail = 0;
s->open = true;
s->pos = 0;
return 0;
}
 
void
http_stream_close(int *err)
{
struct _stream *s = &http_stream;
 
if (s->open) {
__tcp_close(&s->sock);
s->open = false;
}
}
 
int
http_stream_read(char *buf,
int len,
int *err)
{
struct _stream *s = &http_stream;
int total = 0;
int cnt, code;
 
if (!s->open) {
return -1; // Shouldn't happen, but...
}
while (len) {
while (s->avail == 0) {
// Need to wait for some data to arrive
__tcp_poll();
if (s->sock.state != _ESTABLISHED) {
if (s->sock.state == _CLOSE_WAIT) {
// This connection is breaking
if (s->sock.data_bytes == 0 && s->sock.rxcnt == 0) {
__tcp_close(&s->sock);
return total;
}
} else if (s->sock.state == _CLOSED) {
// The connection is gone
s->open = false;
return -1;
} else {
*err = HTTP_IO;
return -1;
}
}
s->actual_len = __tcp_read(&s->sock, s->data, sizeof(s->data));
if (s->actual_len > 0) {
s->bufp = s->data;
s->avail = s->actual_len;
if (s->pos == 0) {
// First data - need to scan HTTP response header
if (strncmp(s->bufp, "HTTP/", 5) == 0) {
// Should look like "HTTP/1.1 200 OK"
s->bufp += 5;
s->avail -= 5;
// Find first space
while ((s->avail > 0) && (*s->bufp != ' ')) {
s->bufp++;
s->avail--;
}
// Now the integer response
code = 0;
while ((s->avail > 0) && (*s->bufp == ' ')) {
s->bufp++;
s->avail--;
}
while ((s->avail > 0) && isdigit(*s->bufp)) {
code = (code * 10) + (*s->bufp - '0');
s->bufp++;
s->avail--;
}
// Make sure it says OK
while ((s->avail > 0) && (*s->bufp == ' ')) {
s->bufp++;
s->avail--;
}
if (strncmp(s->bufp, "OK", 2)) {
*err = HTTP_BADHDR;
return -1;
}
// Find \r\n\r\n - end of HTTP preamble
while (s->avail > 4) {
// This could be done faster, but not simpler
if (strncmp(s->bufp, "\r\n\r\n", 4) == 0) {
s->bufp += 4;
s->avail -= 4;
#if 0 // DEBUG - show header
*(s->bufp-2) = '\0';
diag_printf(s->data);
#endif
break;
}
s->avail--;
s->bufp++;
}
s->pos++;
} else {
// Unrecognized response
*err = HTTP_BADHDR;
return -1;
}
}
} else if (s->actual_len < 0) {
*err = HTTP_IO;
return -1;
}
}
cnt = min(len, s->avail);
memcpy(buf, s->bufp, cnt);
s->avail -= cnt;
s->bufp += cnt;
buf += cnt;
total += cnt;
len -= cnt;
}
return total;
}
 
char *
http_error(int err)
{
char *errmsg = "Unknown error";
 
switch (err) {
case HTTP_NOERR:
return "";
case HTTP_BADHDR:
return "Unrecognized HTTP response";
case HTTP_OPEN:
return "Can't connect to host";
case HTTP_IO:
return "I/O error";
}
return errmsg;
}
 
//
// RedBoot interface
//
GETC_IO_FUNCS(http_io, http_stream_open, http_stream_close,
0, http_stream_read, http_error);
RedBoot_load(http, http_io, true, true, 0);
/net/cksum.c
0,0 → 1,124
//==========================================================================
//
// net/cksum.c
//
// Stand-alone network checksum support for RedBoot
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <net/net.h>
 
/*
* Do a one's complement checksum.
* The data being checksum'd is in network byte order.
* The returned checksum is in network byte order.
*/
unsigned short
__sum(word *w, int len, int init_sum)
{
int sum = init_sum;
 
union {
volatile unsigned char c[2];
volatile unsigned short s;
} su;
 
union {
volatile unsigned short s[2];
volatile int i;
} iu;
 
while ((len -= 2) >= 0)
sum += *w++;
 
if (len == -1) {
su.c[0] = *(char *)w;
su.c[1] = 0;
sum += su.s;
}
 
iu.i = sum;
sum = iu.s[0] + iu.s[1];
if (sum > 65535)
sum -= 65535;
 
su.s = ~sum;
 
return (su.c[0] << 8) | su.c[1];
}
 
 
/*
* Compute a partial checksum for the UDP/TCP pseudo header.
*/
int
__pseudo_sum(ip_header_t *ip)
{
int sum;
word *p;
 
union {
volatile unsigned char c[2];
volatile unsigned short s;
} su;
p = (word *)ip->source;
sum = *p++;
sum += *p++;
sum += *p++;
sum += *p++;
su.c[0] = 0;
su.c[1] = ip->protocol;
sum += su.s;
 
sum += ip->length;
return sum;
}
/net/arp.c
0,0 → 1,210
//==========================================================================
//
// net/arp.c
//
// Stand-alone ARP support for RedBoot
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <net/net.h>
 
static struct {
int waiting;
char *eth;
char *ip;
} arp_req;
 
/*
* Handle incoming ARP packets.
*/
void
__arp_handler(pktbuf_t *pkt)
{
arp_header_t *arp = pkt->arp_hdr;
int hw_type, protocol;
 
/*
* Only handle ethernet hardware and IP protocol.
*/
protocol = ntohs(arp->protocol);
hw_type = ntohs(arp->hw_type);
if ((hw_type == ARP_HW_ETHER) && (protocol == ETH_TYPE_IP)) {
/*
* Handle requests for our ethernet address.
*/
if (!memcmp(arp->target_ip, __local_ip_addr, 4)) {
if (ntohs(arp->opcode) == ARP_REQUEST) {
/* format response. */
arp->opcode = htons(ARP_REPLY);
memcpy(arp->target_ip, arp->sender_ip,
sizeof(ip_addr_t));
memcpy(arp->target_enet, arp->sender_enet,
sizeof(enet_addr_t));
memcpy(arp->sender_ip, __local_ip_addr,
sizeof(ip_addr_t));
memcpy(arp->sender_enet, __local_enet_addr,
sizeof(enet_addr_t));
pkt->pkt_bytes = sizeof(arp_header_t);
__enet_send(pkt, &arp->target_enet, ETH_TYPE_ARP);
 
} else if (ntohs(arp->opcode) == ARP_REPLY && arp_req.waiting) {
if (!memcmp(arp_req.ip, arp->sender_ip, sizeof(ip_addr_t))) {
memcpy(arp_req.eth, arp->sender_enet, sizeof(enet_addr_t));
arp_req.waiting = 0;
}
}
}
}
__pktbuf_free(pkt);
}
 
 
/*
* Find the ethernet address of the machine with the given
* ip address.
* Return 0 and fills in 'eth_addr' if successful,
* -1 if unsuccessful.
*/
int
__arp_request(ip_addr_t *ip_addr, enet_addr_t *eth_addr)
{
pktbuf_t *pkt;
arp_header_t *arp;
unsigned long retry_start;
enet_addr_t bcast_addr;
int retry;
 
// Special case request for self
if (!memcmp(ip_addr, __local_ip_addr, 4)) {
memcpy(eth_addr, __local_enet_addr, sizeof(enet_addr_t));
return 0;
}
 
/* just fail if can't get a buffer */
if ((pkt = __pktbuf_alloc(ARP_PKT_SIZE)) == NULL)
return -1;
 
arp = pkt->arp_hdr;
arp->opcode = htons(ARP_REQUEST);
arp->hw_type = htons(ARP_HW_ETHER);
arp->protocol = htons(0x800);
arp->hw_len = sizeof(enet_addr_t);
arp->proto_len = sizeof(ip_addr_t);
 
memcpy(arp->sender_ip, __local_ip_addr, sizeof(ip_addr_t));
memcpy(arp->sender_enet, __local_enet_addr, sizeof(enet_addr_t));
memcpy(arp->target_ip, ip_addr, sizeof(ip_addr_t));
 
bcast_addr[0] = 255;
bcast_addr[1] = 255;
bcast_addr[2] = 255;
bcast_addr[3] = 255;
bcast_addr[4] = 255;
bcast_addr[5] = 255;
 
arp_req.eth = (char *)eth_addr;
arp_req.ip = (char *)ip_addr;
arp_req.waiting = 1;
 
retry = 8;
while (retry-- > 0) {
 
/* send the packet */
pkt->pkt_bytes = sizeof(arp_header_t);
__enet_send(pkt, &bcast_addr, ETH_TYPE_ARP);
 
retry_start = MS_TICKS();
while ((MS_TICKS_DELAY() - retry_start) < 250) {
__enet_poll();
if (!arp_req.waiting) {
__pktbuf_free(pkt);
return 0;
}
}
}
__pktbuf_free(pkt);
return -1;
}
 
#define NUM_ARP 16
static ip_route_t routes[NUM_ARP];
 
int
__arp_lookup(ip_addr_t *host, ip_route_t *rt)
{
int i;
static int next_arp = 0;
 
for (i = 0; i < NUM_ARP; i++) {
if (memcmp(host, &routes[i].ip_addr, sizeof(*host)) == 0) {
// This is a known host
memcpy(rt, &routes[i], sizeof(*rt));
return 0;
}
}
memcpy(&rt->ip_addr, host, sizeof(*host));
if (((*host)[0] == 0xFF) && ((*host)[1] == 0xFF) && ((*host)[2] == 0xFF)) {
memset(&rt->enet_addr, 0xFF, sizeof(&rt->enet_addr));
return 0;
#ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
} else if (!__ip_addr_local(host)) {
// non-local IP address -- look up Gateway's Ethernet address
host = &__local_ip_gate;
#endif
}
if (__arp_request(host, &rt->enet_addr) < 0) {
return -1;
} else {
memcpy(&routes[next_arp], rt, sizeof(*rt));
if (++next_arp == NUM_ARP) next_arp = 0;
return 0;
}
}
 
/net/bootp.c
0,0 → 1,226
//==========================================================================
//
// net/bootp.c
//
// Stand-alone minimal BOOTP support for RedBoot
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
#include <net/net.h>
#include <net/bootp.h>
 
extern int net_debug;
 
#define SHOULD_BE_RANDOM 0x12345555
 
/* How many milliseconds to wait before retrying the request */
#define RETRY_TIME 1000
#define MAX_RETRIES 30
 
static bootp_header_t *bp_info;
#ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
static const unsigned char dhcpCookie[] = {99,130,83,99};
static const unsigned char dhcpEndOption[] = {255};
static const unsigned char dhcpRequestOption[] = {52,1,3};
#endif
 
static void
bootp_handler(udp_socket_t *skt, char *buf, int len,
ip_route_t *src_route, word src_port)
{
bootp_header_t *b;
#ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
unsigned char *p,*end;
int optlen;
#endif
 
b = (bootp_header_t *)buf;
if (bp_info) {
memset(bp_info,0,sizeof *bp_info);
if (len > sizeof *bp_info)
len = sizeof *bp_info;
memcpy(bp_info, b, len);
}
 
// Only accept pure REPLY responses
if (b->bp_op != BOOTREPLY)
return;
// Must be sent to me, as well!
if (memcmp(b->bp_chaddr, __local_enet_addr, 6))
return;
memcpy(__local_ip_addr, &b->bp_yiaddr, 4);
#ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
memcpy(__local_ip_gate, &b->bp_giaddr, 4);
if (memcmp(b->bp_vend, dhcpCookie, sizeof(dhcpCookie)))
return;
optlen = len - (b->bp_vend - ((unsigned char*)b));
p = b->bp_vend+4;
end = ((unsigned char*)b) + len;
while (p < end) {
unsigned char tag = *p;
if (tag == TAG_END)
break;
if (tag == TAG_PAD)
optlen = 1;
else {
optlen = p[1];
p += 2;
switch (tag) {
case TAG_SUBNET_MASK: // subnet mask
memcpy(__local_ip_mask,p,4);
break;
case TAG_GATEWAY: // router
memcpy(__local_ip_gate,p,4);
break;
default:
break;
}
}
p += optlen;
}
#endif
}
 
#define AddOption(p,d) do {memcpy(p,d,sizeof d); p += sizeof d;} while (0)
 
/*
* Find our IP address and copy to __local_ip_addr.
* Return zero if successful, -1 if not.
*/
int
__bootp_find_local_ip(bootp_header_t *info)
{
udp_socket_t udp_skt;
bootp_header_t b;
ip_route_t r;
int retry;
unsigned long start;
ip_addr_t saved_ip_addr;
#ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
unsigned char *p;
#endif
int txSize;
 
bp_info = info;
 
memset(&b, 0, sizeof(b));
 
b.bp_op = BOOTREQUEST;
b.bp_htype = HTYPE_ETHERNET;
b.bp_hlen = 6;
b.bp_xid = SHOULD_BE_RANDOM;
#ifdef CYGSEM_REDBOOT_NETWORKING_USE_GATEWAY
p = b.bp_vend;
AddOption(p,dhcpCookie);
AddOption(p,dhcpRequestOption);
AddOption(p,dhcpEndOption);
 
// Some servers insist on a minimum amount of "vendor" data
if (p < &b.bp_vend[BP_MIN_VEND_SIZE]) p = &b.bp_vend[BP_MIN_VEND_SIZE];
txSize = p - (unsigned char*)&b;
#else
txSize = sizeof(b);
#endif
 
memcpy( saved_ip_addr, __local_ip_addr, sizeof(__local_ip_addr) );
memset( __local_ip_addr, 0, sizeof(__local_ip_addr) );
 
memcpy(b.bp_chaddr, __local_enet_addr, 6);
 
/* fill out route for a broadcast */
r.ip_addr[0] = 255;
r.ip_addr[1] = 255;
r.ip_addr[2] = 255;
r.ip_addr[3] = 255;
r.enet_addr[0] = 255;
r.enet_addr[1] = 255;
r.enet_addr[2] = 255;
r.enet_addr[3] = 255;
r.enet_addr[4] = 255;
r.enet_addr[5] = 255;
 
/* setup a socket listener for bootp replies */
__udp_install_listener(&udp_skt, IPPORT_BOOTPC, bootp_handler);
 
retry = MAX_RETRIES;
while (retry-- > 0) {
start = MS_TICKS();
 
__udp_send((char *)&b, txSize, &r, IPPORT_BOOTPS, IPPORT_BOOTPC);
 
do {
__enet_poll();
if (__local_ip_addr[0] || __local_ip_addr[1] ||
__local_ip_addr[2] || __local_ip_addr[3]) {
/* success */
__udp_remove_listener(IPPORT_BOOTPC);
return 0;
}
} while ((MS_TICKS_DELAY() - start) < RETRY_TIME);
}
 
/* timed out */
__udp_remove_listener(IPPORT_BOOTPC);
net_debug = 0;
memcpy( __local_ip_addr, saved_ip_addr, sizeof(__local_ip_addr));
return -1;
}
 
 
/net/dns.c
0,0 → 1,275
//=============================================================================
//
// dns.c
//
// DNS client code
//
//=============================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//=============================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): jskov
// Contributors:jskov
// Date: 2001-09-26
// Description: Provides DNS lookup as per RFC 1034/1035.
//
// Note: This is a stripped down clone of dns.c from the CYGPKG_NS_DNS
// package which does not use malloc/free and has been tweaked to
// use UDP via RedBoot's network stack. Also adds commands
// to set the DNS server IP at runtime.
//
//####DESCRIPTIONEND####
//
//=============================================================================
 
#include <cyg/hal/drv_api.h>
#include <cyg/infra/cyg_type.h>
#include <cyg/infra/cyg_trac.h> /* Tracing support */
 
#include <net/net.h>
#include <redboot.h>
/* #include <cyg/ns/dns/dns.h> - it's been moved to redboot.h */
#include <cyg/ns/dns/dns_priv.h>
 
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
#include <flash_config.h>
 
RedBoot_config_option("DNS server IP address",
dns_ip,
ALWAYS_ENABLED, true,
CONFIG_IP,
0
);
#endif
 
/* So we remember which ports have been used */
static int get_port = 7700;
 
#define DOMAIN_PORT 53
 
/* Some magic to make dns_impl.inl compile under RedBoot */
#define sprintf diag_sprintf
 
struct sockaddr_in server;
 
/* static buffers so we can make do without malloc */
static struct hostent _hent;
static char* _h_addr_list[2];
static struct in_addr _h_addr_list0;
static int _hent_alloc = 0;
 
#define _STRING_COUNT 2
#define _STRING_LENGTH 64
static char _strings[_STRING_COUNT][_STRING_LENGTH];
static int _strings_alloc = 0;
 
/* as in dns.c proper */
static short id = 0; /* ID of the last query */
static int s = -1; /* Socket to the DNS server */
static cyg_drv_mutex_t dns_mutex; /* Mutex to stop multiple queries as once */
static char * domainname=NULL; /* Domain name used for queries */
 
 
/* Allocate space for string of length (len). Return NULL on
failure. */
static char*
alloc_string(int len)
{
int i;
 
if (len > _STRING_LENGTH)
return NULL;
 
for (i = 0; i < _STRING_COUNT; i++) {
if (_strings_alloc & (1 << i)) continue;
_strings_alloc |= (1<<i);
return _strings[i];
}
return NULL;
}
 
static void
free_string(char* s)
{
int i;
for (i = 0; i < _STRING_COUNT; i++) {
if (_strings[i] == s) {
_strings_alloc &= ~(1<<i);
break;
}
}
}
 
/* Deallocate the memory taken to hold a hent structure */
static void
free_hent(struct hostent * hent)
{
if (hent->h_name) {
free_string(hent->h_name);
}
_hent_alloc = 0;
}
 
/* Allocate hent structure with room for one in_addr. Returns NULL on
failure. */
static struct hostent*
alloc_hent(void)
{
struct hostent *hent;
 
if (_hent_alloc) return NULL;
 
hent = &_hent;
memset(hent, 0, sizeof(struct hostent));
hent->h_addr_list = _h_addr_list;
hent->h_addr_list[0] = (char*)&_h_addr_list0;
hent->h_addr_list[1] = NULL;
_hent_alloc = 1;
 
return hent;
}
 
static __inline__ void
free_stored_hent(void)
{
free_hent( &_hent );
}
 
static __inline__ void
store_hent(struct hostent *hent)
{
hent=hent; // avoid warning
}
 
/* Send the query to the server and read the response back. Return -1
if it fails, otherwise put the response back in msg and return the
length of the response. */
static int
send_recv(char * msg, int len, int msglen)
{
struct dns_header *dns_hdr;
int finished = false;
int read = 0;
 
dns_hdr = (struct dns_header *) msg;
 
do {
int len_togo = len;
struct timeval timeout;
struct sockaddr_in local_addr, from_addr;
 
memset((char *)&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = htonl(INADDR_ANY);
local_addr.sin_port = htons(get_port++);
 
if (__udp_sendto(msg, len_togo, &server, &local_addr) < 0)
return -1;
 
memset((char *)&from_addr, 0, sizeof(from_addr));
 
timeout.tv_sec = CYGNUM_REDBOOT_NETWORKING_DNS_TIMEOUT;
timeout.tv_usec = 0;
 
read = __udp_recvfrom(msg, len, &from_addr, &local_addr, &timeout);
if (read < 0)
return -1;
 
/* Reply to an old query. Ignore it */
if (ntohs(dns_hdr->id) != (id-1)) {
continue;
}
finished = true;
} while (!finished);
 
return read;
}
void
set_dns(char* new_ip)
{
in_addr_t dns_ip;
 
memset(&server.sin_addr, 0, sizeof(server.sin_addr));
if (!inet_aton(new_ip, &dns_ip)) {
diag_printf("Bad DNS server address: %s\n", new_ip);
} else {
memcpy(&server.sin_addr, &dns_ip, sizeof(dns_ip));
/* server config is valid */
s = 0;
}
}
 
void
show_dns(void)
{
diag_printf(", DNS server IP: %s", inet_ntoa((in_addr_t *)&server.sin_addr));
if (0 == server.sin_addr.s_addr) {
s = -1;
}
}
 
/* Initialise the resolver. Open a socket and bind it to the address
of the server. return -1 if something goes wrong, otherwise 0 */
int
redboot_dns_res_init(void)
{
memset((char *)&server, 0, sizeof(server));
server.sin_len = sizeof(server);
server.sin_family = AF_INET;
server.sin_port = htons(DOMAIN_PORT);
cyg_drv_mutex_init(&dns_mutex);
 
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
{
ip_addr_t dns_ip;
 
flash_get_config("dns_ip", &dns_ip, CONFIG_IP);
if (dns_ip[0] == 0 && dns_ip[1] == 0 && dns_ip[2] == 0 && dns_ip[3] == 0)
return -1;
memcpy(&server.sin_addr, &dns_ip, sizeof(dns_ip));
/* server config is valid */
s = 0;
}
#else
// Use static configuration
set_dns(__Xstr(CYGPKG_REDBOOT_NETWORKING_DNS_IP));
#endif
 
return 0;
}
 
/* Include the DNS client implementation code */
#include <cyg/ns/dns/dns_impl.inl>
/net/tcp.c
0,0 → 1,914
//==========================================================================
//
// net/tcp.c
//
// Stand-alone TCP networking support for RedBoot
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <net/net.h>
 
#define MAX_TCP_SEGMENT (ETH_MAX_PKTLEN - (sizeof(eth_header_t) + sizeof(ip_header_t)))
#define MAX_TCP_DATA (MAX_TCP_SEGMENT - sizeof(tcp_header_t))
 
 
/* sequence number comparison macros */
#define SEQ_LT(a,b) ((int)((a)-(b)) < 0)
#define SEQ_LE(a,b) ((int)((a)-(b)) <= 0)
#define SEQ_GT(a,b) ((int)((a)-(b)) > 0)
#define SEQ_GE(a,b) ((int)((a)-(b)) >= 0)
 
/* Set a timer which will send an RST and abort a connection. */
static timer_t abort_timer;
 
static void do_retrans(void *p);
static void do_close(void *p);
 
#ifdef BSP_LOG
static char *
flags_to_str(octet f)
{
static char str[7], *p;
 
p = str;
 
if (f & TCP_FLAG_FIN)
*p++ = 'F';
if (f & TCP_FLAG_SYN)
*p++ = 'S';
if (f & TCP_FLAG_RST)
*p++ = 'R';
if (f & TCP_FLAG_PSH)
*p++ = 'P';
if (f & TCP_FLAG_ACK)
*p++ = 'A';
if (f & TCP_FLAG_URG)
*p++ = 'U';
*p = '\0';
return str;
}
#endif
 
/*
* A major assumption is that only a very small number of sockets will
* active, so a simple linear search of those sockets is acceptible.
*/
static tcp_socket_t *tcp_list;
 
/*
* Format and send an outgoing segment.
*/
static void
tcp_send(tcp_socket_t *s, int flags, int resend)
{
tcp_header_t *tcp;
ip_header_t *ip;
pktbuf_t *pkt = &s->pkt;
unsigned short cksum;
dword tcp_magic;
int tcp_magic_size = sizeof(tcp_magic);
 
ip = pkt->ip_hdr;
tcp = pkt->tcp_hdr;
 
if (flags & TCP_FLAG_SYN) {
/* If SYN, assume no data and send MSS option in tcp header */
pkt->pkt_bytes = sizeof(tcp_header_t) + 4;
tcp->hdr_len = 6;
tcp_magic = htonl(0x02040000 | MAX_TCP_DATA);
memcpy((unsigned char *)(tcp+1), &tcp_magic, tcp_magic_size);
s->data_bytes = 0;
} else {
pkt->pkt_bytes = s->data_bytes + sizeof(tcp_header_t);
tcp->hdr_len = 5;
}
 
/* tcp header */
tcp->reserved = 0;
tcp->seqnum = htonl(s->seq);
tcp->acknum = htonl(s->ack);
tcp->checksum = 0;
 
if (!resend) {
tcp->src_port = htons(s->our_port);
tcp->dest_port = htons(s->his_port);
tcp->flags = flags;
/* always set PUSH flag if sending data */
if (s->data_bytes)
tcp->flags |= TCP_FLAG_PSH;
tcp->window = htons(MAX_TCP_DATA);
tcp->urgent = 0;
 
/* fill in some pseudo-header fields */
memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
memcpy(ip->destination, s->his_addr.ip_addr, sizeof(ip_addr_t));
ip->protocol = IP_PROTO_TCP;
}
 
/* another pseudo-header field */
ip->length = htons(pkt->pkt_bytes);
 
/* compute tcp checksum */
cksum = __sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip));
tcp->checksum = htons(cksum);
 
__ip_send(pkt, IP_PROTO_TCP, &s->his_addr);
 
BSPLOG(bsp_log("tcp_send: state[%d] flags[%s] ack[%x] data[%d].\n",
s->state, flags_to_str(tcp->flags), s->ack, s->data_bytes));
 
if (s->state == _TIME_WAIT) {
// If 'reuse' is set on socket, close after 1 second, otherwise 2 minutes
__timer_set(&s->timer, s->reuse ? 1000 : 120000, do_close, s);
}
else if ((tcp->flags & (TCP_FLAG_FIN | TCP_FLAG_SYN)) || s->data_bytes)
__timer_set(&s->timer, 1000, do_retrans, s);
}
 
static pktbuf_t ack_pkt;
static word ack_buf[ETH_MIN_PKTLEN/sizeof(word)];
 
/*
* Send an ack.
*/
static void
send_ack(tcp_socket_t *s)
{
tcp_header_t *tcp;
ip_header_t *ip;
unsigned short cksum;
 
ack_pkt.buf = ack_buf;
ack_pkt.bufsize = sizeof(ack_buf);
ack_pkt.ip_hdr = ip = (ip_header_t *)ack_buf;
ack_pkt.tcp_hdr = tcp = (tcp_header_t *)(ip + 1);
ack_pkt.pkt_bytes = sizeof(tcp_header_t);
 
/* tcp header */
tcp->hdr_len = 5;
tcp->reserved = 0;
tcp->seqnum = htonl(s->seq);
tcp->acknum = htonl(s->ack);
tcp->checksum = 0;
 
tcp->src_port = htons(s->our_port);
tcp->dest_port = htons(s->his_port);
tcp->flags = TCP_FLAG_ACK;
 
tcp->window = htons(MAX_TCP_DATA);
tcp->urgent = 0;
 
/* fill in some pseudo-header fields */
memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
memcpy(ip->destination, s->his_addr.ip_addr, sizeof(ip_addr_t));
ip->protocol = IP_PROTO_TCP;
 
/* another pseudo-header field */
ip->length = htons(sizeof(tcp_header_t));
 
/* compute tcp checksum */
cksum = __sum((word *)tcp, sizeof(*tcp), __pseudo_sum(ip));
tcp->checksum = htons(cksum);
 
__ip_send(&ack_pkt, IP_PROTO_TCP, &s->his_addr);
}
 
 
/*
* Send a reset for a bogus incoming segment.
*/
static void
send_reset(pktbuf_t *pkt, ip_route_t *r)
{
ip_header_t *ip = pkt->ip_hdr;
tcp_header_t *tcp = pkt->tcp_hdr;
dword seq, ack;
word src, dest;
word cksum;
 
seq = ntohl(tcp->acknum);
ack = ntohl(tcp->seqnum);
src = ntohs(tcp->dest_port);
dest = ntohs(tcp->src_port);
 
tcp = (tcp_header_t *)(ip + 1);
pkt->pkt_bytes = sizeof(tcp_header_t);
/* tcp header */
tcp->hdr_len = 5;
tcp->reserved = 0;
tcp->seqnum = htonl(seq);
tcp->acknum = htonl(ack);
tcp->window = htons(1024);
tcp->urgent = 0;
tcp->checksum = 0;
tcp->src_port = htons(src);
tcp->dest_port = htons(dest);
tcp->flags = TCP_FLAG_RST | TCP_FLAG_ACK;
 
/* fill in some pseudo-header fields */
memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
memcpy(ip->destination, r->ip_addr, sizeof(ip_addr_t));
ip->protocol = IP_PROTO_TCP;
ip->length = htons(pkt->pkt_bytes);
 
/* compute tcp checksum */
cksum = __sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip));
tcp->checksum = htons(cksum);
 
__ip_send(pkt, IP_PROTO_TCP, r);
}
 
 
 
/*
* Remove given socket from socket list.
*/
static void
unlink_socket(tcp_socket_t *s)
{
tcp_socket_t *prev, *tp;
 
for (prev = NULL, tp = tcp_list; tp; prev = tp, tp = tp->next)
if (tp == s) {
BSPLOG(bsp_log("unlink tcp socket.\n"));
if (prev)
prev->next = s->next;
else
tcp_list = s->next;
}
}
 
/*
* Retransmit last packet.
*/
static void
do_retrans(void *p)
{
BSPLOG(bsp_log("tcp do_retrans.\n"));
tcp_send((tcp_socket_t *)p, 0, 1);
}
 
 
static void
do_close(void *p)
{
BSPLOG(bsp_log("tcp do_close.\n"));
/* close connection */
((tcp_socket_t *)p)->state = _CLOSED;
unlink_socket(p);
}
 
 
static void
free_rxlist(tcp_socket_t *s)
{
pktbuf_t *p;
 
BSPLOG(bsp_log("tcp free_rxlist.\n"));
 
while ((p = s->rxlist) != NULL) {
s->rxlist = p->next;
__pktbuf_free(p);
}
}
 
 
/*
* Handle a conection reset.
*/
static void
do_reset(tcp_socket_t *s)
{
/* close connection */
s->state = _CLOSED;
__timer_cancel(&s->timer);
free_rxlist(s);
unlink_socket(s);
}
 
 
/*
* Extract data from incoming tcp segment.
* Returns true if packet is queued on rxlist, false otherwise.
*/
static int
handle_data(tcp_socket_t *s, pktbuf_t *pkt)
{
tcp_header_t *tcp = pkt->tcp_hdr;
unsigned int diff, seq;
int data_len;
char *data_ptr;
pktbuf_t *p;
 
data_len = pkt->pkt_bytes - (tcp->hdr_len << 2);
data_ptr = ((char *)tcp) + (tcp->hdr_len << 2);
 
seq = ntohl(tcp->seqnum);
 
BSPLOG(bsp_log("tcp data: seq[%x] len[%d].\n", seq, data_len));
 
if (SEQ_LE(seq, s->ack)) {
/*
* Figure difference between which byte we're expecting and which byte
* is sent first. Adjust data length and data pointer accordingly.
*/
diff = s->ack - seq;
data_len -= diff;
data_ptr += diff;
 
if (data_len > 0) {
/* queue the new data */
s->ack += data_len;
pkt->next = NULL;
if ((p = s->rxlist) != NULL) {
while (p->next)
p = p->next;
p->next = pkt;
BSPLOG(bsp_log("tcp data: Add pkt[%x] len[%d].\n",
pkt, data_len));
} else {
s->rxlist = pkt;
s->rxcnt = data_len;
s->rxptr = data_ptr;
BSPLOG(bsp_log("tcp data: pkt[%x] len[%d].\n",
pkt, data_len));
}
return 1;
}
}
return 0;
}
 
 
static void
handle_ack(tcp_socket_t *s, pktbuf_t *pkt)
{
tcp_header_t *tcp = pkt->tcp_hdr;
dword ack;
int advance;
char *dp;
 
/* process ack value in packet */
ack = ntohl(tcp->acknum);
 
BSPLOG(bsp_log("Rcvd tcp ACK %x\n", ack));
 
if (SEQ_GT(ack, s->seq)) {
__timer_cancel(&s->timer);
advance = ack - s->seq;
if (advance > s->data_bytes)
advance = s->data_bytes;
 
BSPLOG(bsp_log("seq advance %d", advance));
 
if (advance > 0) {
s->seq += advance;
s->data_bytes -= advance;
if (s->data_bytes) {
/* other end ack'd only part of the pkt */
BSPLOG(bsp_log(" %d bytes left", s->data_bytes));
dp = (char *)(s->pkt.tcp_hdr + 1);
memcpy(dp, dp + advance, s->data_bytes);
}
}
}
BSPLOG(bsp_log("\n"));
}
 
 
/*
* Handle incoming TCP packets.
*/
void
__tcp_handler(pktbuf_t *pkt, ip_route_t *r)
{
tcp_header_t *tcp = pkt->tcp_hdr;
ip_header_t *ip = pkt->ip_hdr;
tcp_socket_t *prev,*s;
dword ack;
int queued = 0;
 
/* set length for pseudo sum calculation */
ip->length = htons(pkt->pkt_bytes);
 
if (__sum((word *)tcp, pkt->pkt_bytes, __pseudo_sum(ip)) == 0) {
for (prev = NULL, s = tcp_list; s; prev = s, s = s->next) {
if (s->our_port == ntohs(tcp->dest_port)) {
if (s->his_port == 0)
break;
if (s->his_port == ntohs(tcp->src_port) &&
!memcmp(r->ip_addr, s->his_addr.ip_addr, sizeof(ip_addr_t)))
break;
}
}
 
if (s) {
/* found the socket this packet belongs to */
/* refresh his ethernet address */
memcpy(s->his_addr.enet_addr, r->enet_addr, sizeof(enet_addr_t));
 
if (s->state != _SYN_RCVD && tcp->flags & TCP_FLAG_RST) {
BSPLOG(bsp_log("TCP_FLAG_RST rcvd\n"));
do_reset(s);
__pktbuf_free(pkt);
return;
}
 
switch (s->state) {
 
case _SYN_SENT:
/* active open not supported */
if (tcp->flags != (TCP_FLAG_SYN | TCP_FLAG_ACK)) {
do_reset(s);
__pktbuf_free(pkt);
return;
}
s->state = _ESTABLISHED;
s->ack = ntohl(tcp->seqnum) + 1;
s->seq = ntohl(tcp->acknum);
send_ack(s);
break;
 
case _LISTEN:
if (tcp->flags & TCP_FLAG_SYN) {
s->state = _SYN_RCVD;
s->ack = ntohl(tcp->seqnum) + 1;
s->his_port = ntohs(tcp->src_port);
memcpy(s->his_addr.ip_addr, r->ip_addr, sizeof(ip_addr_t));
s->data_bytes = 0;
 
BSPLOG(bsp_log("SYN from %d.%d.%d.%d:%d (seq %x)\n",
s->his_addr.ip_addr[0],s->his_addr.ip_addr[1],
s->his_addr.ip_addr[2],s->his_addr.ip_addr[3],
s->his_port, ntohl(tcp->seqnum)));
 
tcp_send(s, TCP_FLAG_SYN | TCP_FLAG_ACK, 0);
}
else
send_reset(pkt, r);
break;
 
case _SYN_RCVD:
BSPLOG(bsp_log("_SYN_RCVD timer cancel.\n"));
__timer_cancel(&s->timer);
 
/* go back to _LISTEN state if reset */
if (tcp->flags & TCP_FLAG_RST) {
s->state = _LISTEN;
 
BSPLOG(bsp_log("_SYN_RCVD --> _LISTEN\n"));
 
} else if (tcp->flags & TCP_FLAG_SYN) {
/* apparently our SYN/ACK was lost? */
tcp_send(s, 0, 1);
 
BSPLOG(bsp_log("retransmitting SYN/ACK\n"));
 
} else if ((tcp->flags & TCP_FLAG_ACK) &&
ntohl(tcp->acknum) == (s->seq + 1)) {
/* we've established the connection */
s->state = _ESTABLISHED;
s->seq++;
 
BSPLOG(bsp_log("ACK received - connection established\n"));
}
break;
 
case _ESTABLISHED:
case _CLOSE_WAIT:
ack = s->ack; /* save original ack */
if (tcp->flags & TCP_FLAG_ACK)
handle_ack(s, pkt);
 
queued = handle_data(s, pkt);
 
if ((tcp->flags & TCP_FLAG_FIN) &&
ntohl(tcp->seqnum) == s->ack) {
 
BSPLOG(bsp_log("FIN received - going to _CLOSE_WAIT\n"));
 
s->ack++;
s->state = _CLOSE_WAIT;
}
/*
* Send an ack if neccessary.
*/
if (s->ack != ack || pkt->pkt_bytes > (tcp->hdr_len << 2))
send_ack(s);
break;
 
case _LAST_ACK:
if (tcp->flags & TCP_FLAG_ACK) {
handle_ack(s, pkt);
if (ntohl(tcp->acknum) == (s->seq + 1)) {
BSPLOG(bsp_log("_LAST_ACK --> _CLOSED\n"));
s->state = _CLOSED;
unlink_socket(s);
}
}
break;
 
case _FIN_WAIT_1:
if (tcp->flags & TCP_FLAG_ACK) {
handle_ack(s, pkt);
if (ntohl(tcp->acknum) == (s->seq + 1)) {
/* got ACK for FIN packet */
s->seq++;
if (tcp->flags & TCP_FLAG_FIN) {
BSPLOG(bsp_log("_FIN_WAIT_1 --> _TIME_WAIT\n"));
s->ack++;
s->state = _TIME_WAIT;
send_ack(s);
} else {
s->state = _FIN_WAIT_2;
BSPLOG(bsp_log("_FIN_WAIT_1 --> _FIN_WAIT_2\n"));
}
break; /* All done for now */
}
}
/* At this point, no ACK for FIN has been seen, so check for
simultaneous close */
if (tcp->flags & TCP_FLAG_FIN) {
BSPLOG(bsp_log("_FIN_WAIT_1 --> _CLOSING\n"));
__timer_cancel(&s->timer);
s->ack++;
s->state = _CLOSING;
/* FIN is resent so the timeout and retry for this packet
will also take care of timeout and resend of the
previously sent FIN (which got us to FIN_WAIT_1). While
not technically correct, resending FIN only causes a
duplicate FIN (same sequence number) which should be
ignored by the other end. */
tcp_send(s, TCP_FLAG_FIN | TCP_FLAG_ACK, 0);
}
break;
 
case _FIN_WAIT_2:
queued = handle_data(s, pkt);
if (tcp->flags & TCP_FLAG_FIN) {
BSPLOG(bsp_log("_FIN_WAIT_2 --> _TIME_WAIT\n"));
s->ack++;
s->state = _TIME_WAIT;
send_ack(s);
}
break;
 
case _CLOSING:
if (tcp->flags & TCP_FLAG_ACK) {
handle_ack(s, pkt);
if (ntohl(tcp->acknum) == (s->seq + 1)) {
/* got ACK for FIN packet */
BSPLOG(bsp_log("_CLOSING --> _TIME_WAIT\n"));
__timer_cancel(&s->timer);
s->state = _TIME_WAIT;
}
}
break;
 
case _TIME_WAIT:
BSPLOG(bsp_log("_TIME_WAIT resend.\n"));
if (tcp->flags & TCP_FLAG_FIN)
tcp_send(s, 0, 1); /* just resend ack */
break;
}
} else {
BSPLOG(bsp_log("Unexpected segment from: %d.%d.%d.%d:%d\n",
r->ip_addr[0], r->ip_addr[1], r->ip_addr[3],
r->ip_addr[4], ntohs(tcp->src_port)));
send_reset(pkt, r);
}
}
if (!queued)
__pktbuf_free(pkt);
}
 
 
void
__tcp_poll(void)
{
__enet_poll();
MS_TICKS_DELAY();
__timer_poll();
}
 
 
int
__tcp_listen(tcp_socket_t *s, word port)
{
BSPLOG(bsp_log("tcp_listen: s[%p] port[%x]\n", s, port));
 
memset(s, 0, sizeof(tcp_socket_t));
s->state = _LISTEN;
s->our_port = port;
s->pkt.buf = (word *)s->pktbuf;
s->pkt.bufsize = ETH_MAX_PKTLEN;
s->pkt.ip_hdr = (ip_header_t *)s->pkt.buf;
s->pkt.tcp_hdr = (tcp_header_t *)(s->pkt.ip_hdr + 1);
 
s->next = tcp_list;
 
#if 0
/* limit to one open socket at a time */
if (s->next) {
BSPLOG(bsp_log("tcp_listen: recursion error\n"));
BSPLOG(while(1));
}
#endif
tcp_list = s;
 
return 0;
}
 
/*
* SO_REUSEADDR, no 2MSL.
*/
void
__tcp_so_reuseaddr(tcp_socket_t *s)
{
// BSPLOG(bsp_log("__tcp_so_reuseaddr.\n"));
s->reuse = 0x01;
}
 
/*
* Block while waiting for all data to be transmitted.
*/
void
__tcp_drain(tcp_socket_t *s)
{
// BSPLOG(bsp_log("__tcp_drain.\n"));
while (s->state != _CLOSED && s->data_bytes)
__tcp_poll();
// BSPLOG(bsp_log("__tcp_drain done.\n"));
}
 
 
/*
* Close the tcp connection.
*/
static void
do_abort(void *s)
{
BSPLOG(bsp_log("do_abort: send RST\n"));
tcp_send((tcp_socket_t *)s, TCP_FLAG_ACK | TCP_FLAG_RST, 0);
__timer_cancel(&abort_timer);
((tcp_socket_t *)s)->state = _CLOSED;
free_rxlist((tcp_socket_t *)s);
unlink_socket((tcp_socket_t *)s);
}
 
void
__tcp_abort(tcp_socket_t *s, unsigned long delay)
{
__timer_set(&abort_timer, delay, do_abort, s);
}
 
/*
* Close the tcp connection.
*/
void
__tcp_close(tcp_socket_t *s)
{
__tcp_drain(s);
if (s->state == _ESTABLISHED || s->state == _SYN_RCVD) {
BSPLOG(bsp_log("__tcp_close: going to _FIN_WAIT_1\n"));
s->state = _FIN_WAIT_1;
tcp_send(s, TCP_FLAG_ACK | TCP_FLAG_FIN, 0);
} else if (s->state == _CLOSE_WAIT) {
 
BSPLOG(bsp_log("__tcp_close: going to _LAST_ACK\n"));
 
s->state = _LAST_ACK;
tcp_send(s, TCP_FLAG_ACK | TCP_FLAG_FIN, 0);
}
free_rxlist(s);
}
 
 
/*
* Wait for connection to be fully closed.
*/
void
__tcp_close_wait(tcp_socket_t *s)
{
BSPLOG(bsp_log("__tcp_close_wait.\n"));
while (s->state != _CLOSED)
__tcp_poll();
BSPLOG(bsp_log("__tcp_close_wait done.\n"));
}
 
 
/*
* Read up to 'len' bytes without blocking.
*/
int
__tcp_read(tcp_socket_t *s, char *buf, int len)
{
int nread;
pktbuf_t *pkt;
tcp_header_t *tcp;
 
if (len <= 0 || s->rxcnt == 0)
return 0;
 
if (s->state != _ESTABLISHED && s->rxcnt == 0)
return -1;
 
nread = 0;
while (len) {
if (len < s->rxcnt) {
memcpy(buf, s->rxptr, len);
BSPLOG(bsp_log("tcp_read: read %d bytes.\n", len));
s->rxptr += len;
s->rxcnt -= len;
nread += len;
 
BSPLOG(bsp_log("tcp_read: %d bytes left in rxlist head.\n",
s->rxcnt));
 
break;
} else {
memcpy(buf, s->rxptr, s->rxcnt);
BSPLOG(bsp_log("tcp_read: read %d bytes. pkt[%x] freed.\n",
s->rxcnt, s->rxlist));
nread += s->rxcnt;
buf += s->rxcnt;
len -= s->rxcnt;
 
/* setup for next packet in list */
pkt = s->rxlist;
s->rxlist = pkt->next;
__pktbuf_free(pkt);
 
if ((pkt = s->rxlist) != NULL) {
tcp = pkt->tcp_hdr;
s->rxcnt = pkt->pkt_bytes - (tcp->hdr_len << 2);
s->rxptr = ((char *)tcp) + (tcp->hdr_len << 2);
 
BSPLOG(bsp_log("tcp_read: next pkt[%x] has %d bytes.\n",
s->rxlist, s->rxcnt));
} else {
 
BSPLOG(bsp_log("tcp_read: no more data.\n"));
 
s->rxcnt = 0;
break;
}
}
}
return nread;
}
 
 
/*
* Write up to 'len' bytes without blocking
*/
int
__tcp_write(tcp_socket_t *s, char *buf, int len)
{
tcp_header_t *tcp = s->pkt.tcp_hdr;
 
if (len <= 0)
return 0;
 
if (s->state != _ESTABLISHED && s->state != _CLOSE_WAIT)
return -1;
 
if (s->data_bytes)
return 0;
 
if (len > MAX_TCP_DATA)
len = MAX_TCP_DATA;
 
memcpy(tcp + 1, buf, len);
s->data_bytes = len;
 
tcp_send(s, TCP_FLAG_ACK, 0);
 
return len;
}
 
/*
* Write 'len' bytes from 'buf', blocking until sent.
* If connection collapses, return -1
*/
int
__tcp_write_block(tcp_socket_t *s, char *buf, int len)
{
int total = 0;
int n;
 
while (len) {
if (s->state == _CLOSE_WAIT) {
// This connection is tring to close
// This connection is breaking
if (s->data_bytes == 0 && s->rxcnt == 0)
__tcp_close(s);
}
if (s->state == _CLOSED) {
// The connection is gone!
return -1;
}
n = __tcp_write(s, buf, len);
if (n > 0) {
len -= n;
buf += n;
}
__tcp_poll();
}
__tcp_drain(s);
return total;
}
 
/*
* Establish a new [outgoing] connection, with a timeout.
*/
int
__tcp_open(tcp_socket_t *s, struct sockaddr_in *host,
word port, int timeout, int *err)
{
// Fill in socket details
memset(s, 0, sizeof(tcp_socket_t));
s->state = _SYN_SENT;
s->our_port = port;
s->his_port = host->sin_port;
s->pkt.buf = (word *)s->pktbuf;
s->pkt.bufsize = ETH_MAX_PKTLEN;
s->pkt.ip_hdr = (ip_header_t *)s->pkt.buf;
s->pkt.tcp_hdr = (tcp_header_t *)(s->pkt.ip_hdr + 1);
s->seq = (port << 16) | 0xDE77;
s->ack = 0;
if (__arp_lookup((ip_addr_t *)&host->sin_addr, &s->his_addr) < 0) {
diag_printf("%s: Can't find address of server\n", __FUNCTION__);
return -1;
}
s->next = tcp_list;
tcp_list = s;
 
// Send off the SYN packet to open the connection
tcp_send(s, TCP_FLAG_SYN, 0);
// Wait for connection to establish
while (s->state != _ESTABLISHED) {
if (s->state == _CLOSED) {
diag_printf("TCP open - host closed connection\n");
return -1;
}
if (--timeout <= 0) {
diag_printf("TCP open - connection timed out\n");
return -1;
}
MS_TICKS_DELAY();
__tcp_poll();
}
return 0;
}
 
 
/net/icmp.c
0,0 → 1,126
//==========================================================================
//
// net/icmp.c
//
// Stand-alone ICMP networking support for RedBoot
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <net/net.h>
 
/*
* Handle ICMP packets.
*/
static void default_icmp_handler(pktbuf_t *pkt, ip_route_t *dest);
 
static icmp_handler_t icmp_handler = default_icmp_handler;
 
/*
* Install a user defined user_handler for incoming icmp packets.
* Returns zero if successful, -1 if the user_handler is already used.
*/
int
__icmp_install_listener(icmp_handler_t user_handler)
{
if (icmp_handler == user_handler) {
return -1;
}
icmp_handler = user_handler;
return 0;
}
 
 
/*
* Replace a user defined handler by the default handler.
*/
void
__icmp_remove_listener(void)
{
if (icmp_handler != default_icmp_handler) {
icmp_handler = default_icmp_handler;
}
}
 
/*
* ICMP entry point with an IP packet pkt and the destination dest a reply
* should be sent to.
*/
void
__icmp_handler(pktbuf_t *pkt, ip_route_t *dest)
{
(*icmp_handler)(pkt, dest);
 
BSPLOG(bsp_log("icmp: dest[%s] type[%d] seq[%d]\n",
inet_ntoa(pkt->ip_hdr->destination),
pkt->icmp_hdr->type,
pkt->icmp_hdr->seqnum));
__pktbuf_free(pkt);
}
 
 
/*
* The default ICMP handler only handles ICMP incoming echo request and
* outgoing echo reply.
*/
static void
default_icmp_handler(pktbuf_t *pkt, ip_route_t *dest)
{
word cksum;
 
if (pkt->icmp_hdr->type == ICMP_TYPE_ECHOREQUEST
&& pkt->icmp_hdr->code == 0
&& __sum((word *)pkt->icmp_hdr, pkt->pkt_bytes, 0) == 0) {
 
pkt->icmp_hdr->type = ICMP_TYPE_ECHOREPLY;
pkt->icmp_hdr->checksum = 0;
cksum = __sum((word *)pkt->icmp_hdr, pkt->pkt_bytes, 0);
pkt->icmp_hdr->checksum = htons(cksum);
__ip_send(pkt, IP_PROTO_ICMP, dest);
}
}
/net/udp.c
0,0 → 1,268
//==========================================================================
//
// net/udp.c
//
// Stand-alone UDP networking support for RedBoot
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
#include <net/net.h>
 
#ifdef UDP_STATS
static int udp_rx_total;
static int udp_rx_handled;
static int udp_rx_cksum;
static int udp_rx_dropped;
#endif
 
#define MAX_UDP_DATA (ETH_MAX_PKTLEN - (ETH_HDR_SIZE + \
sizeof(ip_header_t) + \
sizeof(udp_header_t)))
 
/*
* A major assumption is that only a very small number of sockets will
* active, so a simple linear search of those sockets is acceptible.
*/
static udp_socket_t *udp_list;
 
 
/*
* Install a handler for incoming udp packets.
* Caller provides the udp_socket_t structure.
* Returns zero if successful, -1 if socket is already used.
*/
int
__udp_install_listener(udp_socket_t *s, word port, udp_handler_t handler)
{
udp_socket_t *p;
 
/*
* Make sure we only have one handler per port.
*/
for (p = udp_list; p; p = p->next)
if (p->our_port == port)
return -1;
s->our_port = htons(port);
s->handler = handler;
s->next = udp_list;
udp_list = s;
 
return 0;
}
 
 
/*
* Remove the handler for the given socket.
*/
void
__udp_remove_listener(word port)
{
udp_socket_t *prev, *s;
 
for (prev = NULL, s = udp_list; s; prev = s, s = s->next)
if (s->our_port == htons(port)) {
if (prev)
prev->next = s->next;
else
udp_list = s->next;
}
}
 
 
/*
* Handle incoming UDP packets.
*/
void
__udp_handler(pktbuf_t *pkt, ip_route_t *r)
{
udp_header_t *udp = pkt->udp_hdr;
ip_header_t *ip = pkt->ip_hdr;
udp_socket_t *s;
 
if (udp->checksum == 0xffff)
udp->checksum = 0;
 
/* copy length for pseudo sum calculation */
ip->length = udp->length;
 
if (__sum((word *)udp, ntohs(udp->length), __pseudo_sum(ip)) == 0) {
for (s = udp_list; s; s = s->next) {
if (s->our_port == udp->dest_port) {
(*s->handler)(s, ((char *)udp) + sizeof(udp_header_t),
ntohs(udp->length) - sizeof(udp_header_t),
r, ntohs(udp->src_port));
__pktbuf_free(pkt);
return;
}
}
}
__pktbuf_free(pkt);
}
 
 
/*
* Send a UDP packet.
*/
int
__udp_send(char *buf, int len, ip_route_t *dest_ip,
word dest_port, word src_port)
{
pktbuf_t *pkt;
udp_header_t *udp;
ip_header_t *ip;
unsigned short cksum;
int ret;
 
/* dumb */
if (len > MAX_UDP_DATA)
return -1;
 
/* just drop it if can't get a buffer */
if ((pkt = __pktbuf_alloc(ETH_MAX_PKTLEN)) == NULL)
return -1;
 
udp = pkt->udp_hdr;
ip = pkt->ip_hdr;
 
pkt->pkt_bytes = len + sizeof(udp_header_t);
 
udp->src_port = htons(src_port);
udp->dest_port = htons(dest_port);
udp->length = htons(pkt->pkt_bytes);
udp->checksum = 0;
 
memcpy(((char *)udp) + sizeof(udp_header_t), buf, len);
 
/* fill in some pseudo-header fields */
memcpy(ip->source, __local_ip_addr, sizeof(ip_addr_t));
memcpy(ip->destination, dest_ip->ip_addr, sizeof(ip_addr_t));
ip->protocol = IP_PROTO_UDP;
ip->length = udp->length;
 
cksum = __sum((word *)udp, pkt->pkt_bytes, __pseudo_sum(ip));
udp->checksum = htons(cksum);
 
ret = __ip_send(pkt, IP_PROTO_UDP, dest_ip);
__pktbuf_free(pkt);
return ret;
}
 
int
__udp_sendto(char *data, int len, struct sockaddr_in *server,
struct sockaddr_in *local)
{
ip_route_t rt;
 
if (__arp_lookup((ip_addr_t *)&server->sin_addr, &rt) < 0) {
diag_printf("%s: Can't find address of server\n", __FUNCTION__);
return -1;
} else {
__udp_send(data, len, &rt, ntohs(server->sin_port), ntohs(local->sin_port));
return 0;
}
}
 
static char *recvfrom_buf;
static int recvfrom_len;
static struct sockaddr_in *recvfrom_server;
 
static void
__udp_recvfrom_handler(udp_socket_t *skt, char *buf, int len,
ip_route_t *src_route, word src_port)
{
if (recvfrom_server == NULL)
return;
 
if (recvfrom_server->sin_port && recvfrom_server->sin_port != htons(src_port))
return;
 
// Move data to waiting buffer
recvfrom_len = len;
memcpy(recvfrom_buf, buf, len);
if (recvfrom_server) {
recvfrom_server->sin_port = htons(src_port);
memcpy(&recvfrom_server->sin_addr, &src_route->ip_addr, sizeof(src_route->ip_addr));
recvfrom_buf = (char *)0; // Tell reader we got a packet
} else {
diag_printf("udp_recvfrom - dropped packet of %d bytes\n", len);
}
}
 
int
__udp_recvfrom(char *data, int len, struct sockaddr_in *server,
struct sockaddr_in *local, struct timeval *timo)
{
int res, my_port, total_ms;
udp_socket_t skt;
unsigned long start;
 
my_port = ntohs(local->sin_port);
if (__udp_install_listener(&skt, my_port, __udp_recvfrom_handler) < 0) {
return -1;
}
recvfrom_buf = data;
recvfrom_len = len;
recvfrom_server = server;
total_ms = (timo->tv_sec * 1000) + (timo->tv_usec / 1000);
start = MS_TICKS();
res = -1;
while ((MS_TICKS_DELAY() - start) < total_ms) {
__enet_poll(); // Handle the hardware
if (!recvfrom_buf) {
// Data have arrived
res = recvfrom_len;
break;
}
}
__udp_remove_listener(my_port);
return res;
}
/net/inet_addr.c
0,0 → 1,118
//==========================================================================
//
// net/inet_addr.c
//
// Stand-alone IP address parsing for RedBoot
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
#include <net/net.h>
 
bool
inet_aton(const char *s, in_addr_t *addr)
{
int i, val, radix, digit;
unsigned long res = 0;
bool first;
char c;
for (i = 0; i < 4; i++) {
// Parse next digit string
first = true;
val = 0;
radix = 10;
while ((c = *s++) != '\0') {
if (first && (c == '0') && (_tolower(*s) == 'x')) {
radix = 16;
s++; // Skip over 0x
c = *s++;
}
first = false;
if (_is_hex(c) && ((digit = _from_hex(c)) < radix)) {
// Valid digit
val = (val * radix) + digit;
} else if (c == '.' && i < 3) { // all but last terminate by '.'
break;
} else {
return false;
}
}
// merge result
#ifdef __LITTLE_ENDIAN__
res |= val << ((3-i)*8); // 24, 16, 8, 0
#else
res = (res << 8) | val;
#endif
if ('\0' == c) {
if (0 == i) { // first field found end of string
res = val; // no shifting, use it as the whole thing
break; // permit entering a single number
}
if (3 > i) // we found end of string before getting 4 fields
return false;
}
// after that we check that it was 0..255 only
if (val &~0xff) return false;
}
addr->s_addr = htonl(res);
return true;
}
 
// Assumes address is in network byte order
char *
inet_ntoa(in_addr_t *addr)
{
static char str[32];
unsigned char *ap;
 
ap = (unsigned char *)addr;
diag_sprintf(str, "%d.%d.%d.%d", ap[0], ap[1], ap[2], ap[3]);
return str;
}
/net/pktbuf.c
0,0 → 1,167
//==========================================================================
//
// net/pktbuf.c
//
// Stand-alone network packet support for RedBoot
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
#include <net/net.h>
 
#define MAX_PKTBUF CYGNUM_REDBOOT_NETWORKING_MAX_PKTBUF
 
#define BUFF_STATS 1
 
#if BUFF_STATS
int max_alloc = 0;
int num_alloc = 0;
int num_free = 0;
#endif
 
static pktbuf_t pktbuf_list[MAX_PKTBUF];
static word bufdata[MAX_PKTBUF][ETH_MAX_PKTLEN/2 + 1];
static pktbuf_t *free_list;
 
 
/*
* Initialize the free list.
*/
void
__pktbuf_init(void)
{
int i;
word *p;
static int init = 0;
 
if (init) return;
init = 1;
 
for (i = 0; i < MAX_PKTBUF; i++) {
p = bufdata[i];
if ((((unsigned long)p) & 2) != 0)
++p;
pktbuf_list[i].buf = p;
pktbuf_list[i].bufsize = ETH_MAX_PKTLEN;
pktbuf_list[i].next = free_list;
free_list = &pktbuf_list[i];
}
}
 
void
__pktbuf_dump(void)
{
int i;
for (i = 0; i < MAX_PKTBUF; i++) {
diag_printf("Buf[%d]/%p: buf: %p, len: %d/%d, next: %p\n",
i,
(void*)&pktbuf_list[i],
(void*)pktbuf_list[i].buf,
pktbuf_list[i].bufsize,
pktbuf_list[i].pkt_bytes,
(void*)pktbuf_list[i].next);
}
diag_printf("Free list = %p\n", (void*)free_list);
}
 
/*
* simple pktbuf allocation
*/
pktbuf_t *
__pktbuf_alloc(int nbytes)
{
pktbuf_t *p = free_list;
 
if (p) {
free_list = p->next;
p->ip_hdr = (ip_header_t *)p->buf;
p->tcp_hdr = (tcp_header_t *)(p->ip_hdr + 1);
p->pkt_bytes = 0;
#if BUFF_STATS
++num_alloc;
if ((num_alloc - num_free) > max_alloc)
max_alloc = num_alloc - num_free;
#endif
}
return p;
}
 
 
/*
* free a pktbuf.
*/
void
__pktbuf_free(pktbuf_t *pkt)
{
#if BUFF_STATS
--num_alloc;
#endif
#ifdef BSP_LOG
{
int i;
word *p;
 
for (i = 0; i < MAX_PKTBUF; i++) {
p = bufdata[i];
if ((((unsigned long)p) & 2) == 0)
++p;
if (p == (word *)pkt)
break;
}
if (i < MAX_PKTBUF) {
BSPLOG(bsp_log("__pktbuf_free: bad pkt[%x].\n", pkt));
BSPLOG(while(1));
}
}
#endif
pkt->next = free_list;
free_list = pkt;
}
 
 
/net/enet.c
0,0 → 1,248
//==========================================================================
//
// net/enet.c
//
// Stand-alone ethernet [link-layer] support for RedBoot
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
#include <net/net.h>
#include <cyg/io/eth/eth_drv.h> // Logical driver interfaces
 
//#define ENET_STATS 1
 
#ifdef ENET_STATS
static int num_ip = 0;
static int num_arp = 0;
#ifdef NET_SUPPORT_RARP
static int num_rarp = 0;
#endif
static int num_received = 0;
static int num_transmitted = 0;
#endif
 
//
// Support for user handlers of additional ethernet packets (nonIP)
//
 
#define NUM_EXTRA_HANDLERS 4
static struct {
int type;
pkt_handler_t handler;
} eth_handlers[NUM_EXTRA_HANDLERS];
 
pkt_handler_t
__eth_install_listener(int eth_type, pkt_handler_t handler)
{
int i, empty;
pkt_handler_t old;
 
if (eth_type > 0x800 || handler != (pkt_handler_t)0) {
empty = -1;
for (i = 0; i < NUM_EXTRA_HANDLERS; i++) {
if (eth_handlers[i].type == eth_type) {
// Replace existing handler
old = eth_handlers[i].handler;
eth_handlers[i].handler = handler;
return old;
}
if (eth_handlers[i].type == 0) {
empty = i;
}
}
if (empty >= 0) {
// Found a free slot
eth_handlers[empty].type = eth_type;
eth_handlers[empty].handler = handler;
return (pkt_handler_t)0;
}
}
diag_printf("** Warning: can't install listener for ethernet type 0x%02x\n", eth_type);
return (pkt_handler_t)0;
}
 
void
__eth_remove_listener(int eth_type)
{
int i;
for (i = 0; i < NUM_EXTRA_HANDLERS; i++) {
if (eth_handlers[i].type == eth_type) {
eth_handlers[i].type = 0;
}
}
}
 
/*
* Non-blocking poll of ethernet link. Process packets until no more
* are available.
*/
void
__enet_poll(void)
{
pktbuf_t *pkt;
eth_header_t eth_hdr;
int i, type;
#ifdef DEBUG_PKT_EXHAUSTION
static bool was_exhausted = false;
#endif
 
while (true) {
/*
* Try to get a free pktbuf and return if none
* are available.
*/
if ((pkt = __pktbuf_alloc(ETH_MAX_PKTLEN)) == NULL) {
#ifdef DEBUG_PKT_EXHAUSTION
if (!was_exhausted) {
int old = start_console(); // Force output to standard port
diag_printf("__enet_poll: no more buffers\n");
__pktbuf_dump();
was_exhausted = true;
end_console(old);
}
#endif
return;
}
#ifdef DEBUG_PKT_EXHAUSTION
was_exhausted = false; // Report the next time we're out of buffers
#endif
 
if ((pkt->pkt_bytes = eth_drv_read((char *)&eth_hdr, (char *)pkt->buf,
ETH_MAX_PKTLEN)) > 0) {
#ifdef ENET_STATS
++num_received;
#endif
switch (type = ntohs(eth_hdr.type)) {
 
case ETH_TYPE_IP:
#ifdef ENET_STATS
++num_ip;
#endif
pkt->ip_hdr = (ip_header_t *)pkt->buf;
__ip_handler(pkt, &eth_hdr.source);
break;
 
case ETH_TYPE_ARP:
#ifdef ENET_STATS
++num_arp;
#endif
pkt->arp_hdr = (arp_header_t *)pkt->buf;
__arp_handler(pkt);
break;
 
#ifdef NET_SUPPORT_RARP
case ETH_TYPE_RARP:
#ifdef ENET_STATS
++num_rarp;
#endif
pkt->arp_hdr = (arp_header_t *)pkt->buf;
__rarp_handler(pkt);
break;
#endif
 
default:
if (type > 0x800) {
for (i = 0; i < NUM_EXTRA_HANDLERS; i++) {
if (eth_handlers[i].type == type) {
(eth_handlers[i].handler)(pkt, &eth_hdr);
}
}
}
__pktbuf_free(pkt);
break;
}
} else {
__pktbuf_free(pkt);
break;
}
}
}
 
 
 
/*
* Send an ethernet packet.
*/
void
__enet_send(pktbuf_t *pkt, enet_addr_t *dest, int eth_type)
{
eth_header_t eth_hdr;
 
// Set up ethernet header
memcpy(&eth_hdr.destination, dest, sizeof(enet_addr_t));
memcpy(&eth_hdr.source, __local_enet_addr, sizeof(enet_addr_t));
eth_hdr.type = htons(eth_type);
 
eth_drv_write((char *)&eth_hdr, (char *)pkt->buf, pkt->pkt_bytes);
#ifdef ENET_STATS
++num_transmitted;
#endif
}
 
#ifdef __LITTLE_ENDIAN__
 
unsigned long
ntohl(unsigned long x)
{
return (((x & 0x000000FF) << 24) |
((x & 0x0000FF00) << 8) |
((x & 0x00FF0000) >> 8) |
((x & 0xFF000000) >> 24));
}
 
unsigned long
ntohs(unsigned short x)
{
return (((x & 0x00FF) << 8) |
((x & 0xFF00) >> 8));
}
 
#endif
/net/ping.c
0,0 → 1,238
//==========================================================================
//
// ping.c
//
// Network utility - ping
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2001-01-22
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
#include <net/net.h>
 
#ifndef CYGPKG_REDBOOT_NETWORKING
#error CYGPKG_REDBOOT_NETWORKING required!
#else
 
static void do_ping(int argc, char *argv[]);
RedBoot_cmd("ping",
"Network connectivity test",
"[-v] [-n <count>] [-l <length>] [-t <timeout>] [-r <rate>]\n"
" [-i <IP_addr>] -h <IP_addr>",
do_ping
);
 
static bool icmp_received;
static icmp_header_t hold_hdr;
 
static void
handle_icmp(pktbuf_t *pkt, ip_route_t *src_route)
{
icmp_header_t *icmp;
unsigned short cksum;
 
icmp = pkt->icmp_hdr;
if (icmp->type == ICMP_TYPE_ECHOREQUEST
&& icmp->code == 0
&& __sum((word *)icmp, pkt->pkt_bytes, 0) == 0) {
 
icmp->type = ICMP_TYPE_ECHOREPLY;
icmp->checksum = 0;
cksum = __sum((word *)icmp, pkt->pkt_bytes, 0);
icmp->checksum = htons(cksum);
__ip_send(pkt, IP_PROTO_ICMP, src_route);
} else if (icmp->type == ICMP_TYPE_ECHOREPLY) {
memcpy(&hold_hdr, icmp, sizeof(*icmp));
icmp_received = true;
}
}
 
static void
do_ping(int argc, char *argv[])
{
struct option_info opts[7];
long count, timeout, length, rate, start_time, end_time, timer, received, tries;
char *local_ip_addr, *host_ip_addr;
bool local_ip_addr_set, host_ip_addr_set, count_set,
timeout_set, length_set, rate_set, verbose;
struct sockaddr_in local_addr, host_addr;
ip_addr_t hold_addr;
icmp_header_t *icmp;
pktbuf_t *pkt;
ip_header_t *ip;
unsigned short cksum;
ip_route_t dest_ip;
 
init_opts(&opts[0], 'n', true, OPTION_ARG_TYPE_NUM,
(void **)&count, (bool *)&count_set, "<count> - number of packets to test");
init_opts(&opts[1], 't', true, OPTION_ARG_TYPE_NUM,
(void **)&timeout, (bool *)&timeout_set, "<timeout> - max #ms per packet [rount trip]");
init_opts(&opts[2], 'i', true, OPTION_ARG_TYPE_STR,
(void **)&local_ip_addr, (bool *)&local_ip_addr_set, "local IP address");
init_opts(&opts[3], 'h', true, OPTION_ARG_TYPE_STR,
(void **)&host_ip_addr, (bool *)&host_ip_addr_set, "host name or IP address");
init_opts(&opts[4], 'l', true, OPTION_ARG_TYPE_NUM,
(void **)&length, (bool *)&length_set, "<length> - size of payload");
init_opts(&opts[5], 'v', false, OPTION_ARG_TYPE_FLG,
(void **)&verbose, (bool *)0, "verbose operation");
init_opts(&opts[6], 'r', true, OPTION_ARG_TYPE_NUM,
(void **)&rate, (bool *)&rate_set, "<rate> - time between packets");
if (!scan_opts(argc, argv, 1, opts, 7, (void **)0, 0, "")) {
diag_printf("PING - Invalid option specified\n");
return;
}
// Set defaults; this has to be done _after_ the scan, since it will
// have destroyed all values not explicitly set.
if (local_ip_addr_set) {
if (!_gethostbyname(local_ip_addr, (in_addr_t *)&local_addr)) {
diag_printf("PING - Invalid local name: %s\n", local_ip_addr);
return;
}
} else {
memcpy((in_addr_t *)&local_addr, __local_ip_addr, sizeof(__local_ip_addr));
}
if (host_ip_addr_set) {
if (!_gethostbyname(host_ip_addr, (in_addr_t *)&host_addr)) {
diag_printf("PING - Invalid host name: %s\n", host_ip_addr);
return;
}
if (__arp_lookup((ip_addr_t *)&host_addr.sin_addr, &dest_ip) < 0) {
diag_printf("PING: Cannot reach server '%s' (%s)\n",
host_ip_addr, inet_ntoa((in_addr_t *)&host_addr));
return;
}
} else {
diag_printf("PING - host name or IP address required\n");
return;
}
#define DEFAULT_LENGTH 64
#define DEFAULT_COUNT 10
#define DEFAULT_TIMEOUT 1000
#define DEFAULT_RATE 1000
if (!rate_set) {
rate = DEFAULT_RATE;
}
if (!length_set) {
length = DEFAULT_LENGTH;
}
if ((length < 64) || (length > 1400)) {
diag_printf("Invalid length specified: %ld\n", length);
return;
}
if (!count_set) {
count = DEFAULT_COUNT;
}
if (!timeout_set) {
timeout = DEFAULT_TIMEOUT;
}
// Note: two prints here because 'inet_ntoa' returns a static pointer
diag_printf("Network PING - from %s",
inet_ntoa((in_addr_t *)&local_addr));
diag_printf(" to %s\n",
inet_ntoa((in_addr_t *)&host_addr));
received = 0;
__icmp_install_listener(handle_icmp);
for (tries = 0; tries < count; tries++) {
// The network stack uses the global variable '__local_ip_addr'
memcpy(hold_addr, __local_ip_addr, sizeof(hold_addr));
memcpy(__local_ip_addr, &local_addr, sizeof(__local_ip_addr));
// Build 'ping' request
if ((pkt = __pktbuf_alloc(ETH_MAX_PKTLEN)) == NULL) {
// Give up if no packets - something is wrong
break;
}
 
icmp = pkt->icmp_hdr;
ip = pkt->ip_hdr;
pkt->pkt_bytes = length + sizeof(icmp_header_t);
 
icmp->type = ICMP_TYPE_ECHOREQUEST;
icmp->code = 0;
icmp->checksum = 0;
icmp->seqnum = htons(tries+1);
cksum = __sum((word *)icmp, pkt->pkt_bytes, 0);
icmp->checksum = htons(cksum);
memcpy(ip->source, (in_addr_t *)&local_addr, sizeof(ip_addr_t));
memcpy(ip->destination, (in_addr_t *)&host_addr, sizeof(ip_addr_t));
ip->protocol = IP_PROTO_ICMP;
ip->length = htons(pkt->pkt_bytes);
 
__ip_send(pkt, IP_PROTO_ICMP, &dest_ip);
__pktbuf_free(pkt);
 
start_time = MS_TICKS();
timer = start_time + timeout;
icmp_received = false;
while (!icmp_received && (MS_TICKS_DELAY() < timer)) {
__enet_poll();
}
end_time = MS_TICKS();
 
timer = MS_TICKS() + rate;
while (MS_TICKS_DELAY() < timer) {
__enet_poll();
}
 
// Clean up
memcpy(__local_ip_addr, &hold_addr, sizeof(__local_ip_addr));
 
if (icmp_received) {
received++;
if (verbose) {
diag_printf(" seq: %ld, time: %ld (ticks)\n",
ntohs(hold_hdr.seqnum), end_time-start_time);
}
}
}
__icmp_remove_listener();
// Report
diag_printf("PING - received %ld of %ld expected\n", received, count);
}
 
#endif //CYGPKG_REDBOOT_NETWORKING
/dump.c
0,0 → 1,158
//==========================================================================
//
// dump.c
//
// RedBoot dump support
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2002-08-06
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
 
RedBoot_cmd("dump",
"Display (hex dump) a range of memory",
"-b <location> [-l <length>] [-s] [-1|2|4]",
do_dump
);
RedBoot_cmd("x",
"Display (hex dump) a range of memory",
"-b <location> [-l <length>] [-s] [-1|2|4]",
do_x
);
 
void
do_dump(int argc, char *argv[])
{
struct option_info opts[6];
unsigned long base, len;
bool base_set, len_set;
static unsigned long _base, _len;
static char _size = 1;
bool srec_dump, set_32bit, set_16bit, set_8bit;
int i, n, off, cksum;
cyg_uint8 ch;
 
init_opts(&opts[0], 'b', true, OPTION_ARG_TYPE_NUM,
(void **)&base, (bool *)&base_set, "base address");
init_opts(&opts[1], 'l', true, OPTION_ARG_TYPE_NUM,
(void **)&len, (bool *)&len_set, "length");
init_opts(&opts[2], 's', false, OPTION_ARG_TYPE_FLG,
(void **)&srec_dump, 0, "dump data using Morotola S-records");
init_opts(&opts[3], '4', false, OPTION_ARG_TYPE_FLG,
(void *)&set_32bit, (bool *)0, "dump 32 bit units");
init_opts(&opts[4], '2', false, OPTION_ARG_TYPE_FLG,
(void **)&set_16bit, (bool *)0, "dump 16 bit units");
init_opts(&opts[5], '1', false, OPTION_ARG_TYPE_FLG,
(void **)&set_8bit, (bool *)0, "dump 8 bit units");
if (!scan_opts(argc, argv, 1, opts, 6, 0, 0, "")) {
return;
}
if (!base_set) {
if (_base == 0) {
diag_printf("Dump what [location]?\n");
return;
}
base = _base;
if (!len_set) {
len = _len;
len_set = true;
}
}
 
if (set_32bit) {
_size = 4;
} else if (set_16bit) {
_size = 2;
} else if (set_8bit) {
_size = 1;
}
 
if (!len_set) {
len = 32;
}
if (srec_dump) {
off = 0;
while (off < len) {
n = (len > 16) ? 16 : len;
cksum = n+5;
diag_printf("S3%02X%08X", n+5, off+base);
for (i = 0; i < 4; i++) {
cksum += (((base+off)>>(i*8)) & 0xFF);
}
for (i = 0; i < n; i++) {
ch = *(cyg_uint8 *)(base+off+i);
diag_printf("%02X", ch);
cksum += ch;
}
diag_printf("%02X\n", ~cksum & 0xFF);
off += n;
}
} else {
switch( _size ) {
case 1:
diag_dump_buf((void *)base, len);
break;
case 2:
diag_dump_buf_16bit((void *)base, len);
break;
case 4:
diag_dump_buf_32bit((void *)base, len);
break;
}
}
_base = base + len;
_len = len;
}
 
// Simple alias for the dump command
void
do_x(int argc, char *argv[])
{
do_dump(argc, argv);
}
/io.c
0,0 → 1,651
//==========================================================================
//
// io.c
//
// RedBoot I/O support
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas,hmt,jlarmour
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include "redboot.h"
 
#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
// GDB interface functions
extern void ungetDebugChar(char c);
#endif
 
static void
do_channel(int argc, char *argv[]);
 
#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
RedBoot_cmd("channel",
"Display/switch console channel",
"[-1|<channel number>]",
do_channel
);
#else
RedBoot_cmd("channel",
"Display/switch console channel",
"[<channel number>]",
do_channel
);
#endif
 
static void
do_channel(int argc, char *argv[])
{
int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
 
if (argc == 2) {
#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
if (strcmp( argv[1], "-1") == 0) {
console_selected = false;
console_echo = true;
} else
#endif
{
unsigned long chan;
if ( !parse_num( argv[1], &chan, NULL, NULL) ) {
diag_printf("** Error: invalid channel '%s'\n", argv[1]);
} else {
if (chan < CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS) {
CYGACC_CALL_IF_SET_CONSOLE_COMM(chan);
CYGACC_CALL_IF_SET_DEBUG_COMM(chan);
if (chan != cur)
console_echo = true;
}
else {
diag_printf("**Error: bad channel number '%s'\n", argv[1]);
}
}
}
}
/* else display */
else {
diag_printf("Current console channel id: ");
#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
if (!console_selected)
diag_printf("-1\n");
else
#endif
diag_printf("%d\n", cur);
}
}
 
void
mon_write_char(char c)
{
hal_virtual_comm_table_t *__chan;
 
#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
if (!console_selected) {
int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
int i;
// Send output to all channels
for (i = 0; i < CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS; i++) {
CYGACC_CALL_IF_SET_CONSOLE_COMM(i);
__chan = CYGACC_CALL_IF_CONSOLE_PROCS();
CYGACC_COMM_IF_PUTC(*__chan, c);
}
CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);
} else
#endif
{
__chan = CYGACC_CALL_IF_CONSOLE_PROCS();
if (__chan)
CYGACC_COMM_IF_PUTC(*__chan, c);
else {
__chan = CYGACC_CALL_IF_DEBUG_PROCS();
CYGACC_COMM_IF_PUTC(*__chan, c);
}
}
}
 
static void
mon_read_char(char *c)
{
hal_virtual_comm_table_t* __chan = CYGACC_CALL_IF_CONSOLE_PROCS();
if (__chan)
*c = CYGACC_COMM_IF_GETC(*__chan);
else {
__chan = CYGACC_CALL_IF_DEBUG_PROCS();
*c = CYGACC_COMM_IF_GETC(*__chan);
}
}
 
#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
static int _mon_timeout;
#endif
 
static bool
mon_read_char_with_timeout(char *c)
{
bool res = false;
hal_virtual_comm_table_t *__chan;
 
#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
if (!console_selected) {
int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
int i, j, tot;
// Try input from all channels
tot = 0;
while (tot < _mon_timeout) {
for (i = 0; i < CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS; i++, tot++) {
CYGACC_CALL_IF_SET_CONSOLE_COMM(i);
__chan = CYGACC_CALL_IF_CONSOLE_PROCS();
res = CYGACC_COMM_IF_GETC_TIMEOUT(*__chan, c);
if (res) {
// Input available on this channel, make it be the console
if (*c != '\0') {
// Don't chose this unless real data have arrived
console_selected = true;
CYGACC_CALL_IF_SET_DEBUG_COMM(i);
// Disable interrupts on all channels but this one
for (j = 0; j < CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS; j++) {
if (i != j) {
CYGACC_CALL_IF_SET_CONSOLE_COMM(j);
__chan = CYGACC_CALL_IF_CONSOLE_PROCS();
CYGACC_COMM_IF_CONTROL(*__chan, __COMMCTL_IRQ_DISABLE);
}
}
CYGACC_CALL_IF_SET_CONSOLE_COMM(i);
return res;
}
}
}
}
CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);
} else
#endif
{
__chan = CYGACC_CALL_IF_CONSOLE_PROCS();
if (__chan)
res = CYGACC_COMM_IF_GETC_TIMEOUT(*__chan, c);
else {
__chan = CYGACC_CALL_IF_DEBUG_PROCS();
res = CYGACC_COMM_IF_GETC_TIMEOUT(*__chan, c);
}
}
return res;
}
 
static void
mon_set_read_char_timeout(int ms)
{
hal_virtual_comm_table_t *__chan;
 
#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
if (!console_selected) {
int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
int i;
// Set timeout to minimum on each channel; total amounts to desired value
_mon_timeout = ms;
ms = 1;
for (i = 0; i < CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS; i++) {
CYGACC_CALL_IF_SET_CONSOLE_COMM(i);
if ((__chan = CYGACC_CALL_IF_CONSOLE_PROCS()) != 0) {
CYGACC_COMM_IF_CONTROL(*__chan, __COMMCTL_SET_TIMEOUT, ms);
}
}
CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);
} else
#endif
{
if ((__chan = CYGACC_CALL_IF_CONSOLE_PROCS()) != 0) {
CYGACC_COMM_IF_CONTROL(*__chan, __COMMCTL_SET_TIMEOUT, ms);
}
if ((__chan = CYGACC_CALL_IF_DEBUG_PROCS()) != 0) {
CYGACC_COMM_IF_CONTROL(*__chan, __COMMCTL_SET_TIMEOUT, ms);
}
}
}
 
#ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
#define __STRINGIFY(x) #x
#define _STRINGIFY(x) __STRINGIFY(x)
#define _STARTUP_STR _STRINGIFY(CYG_HAL_STARTUP) "}"
 
//
// Read a character from script.
// Return true if script character found, false if not.
//
static int
getc_script(char *cp)
{
static bool newline = true;
bool skip;
 
while (script && *script) {
if (newline && *script == '{') {
skip = false;
++script;
 
// skip if it isn't for this startup type
if (strncmp(script, _STARTUP_STR, strlen(_STARTUP_STR)))
skip = true;
 
// skip past "{...}"
while (*script && *script++ != '}')
;
 
// skip script line if neccessary
if (skip) {
while (*script && *script++ != '\n')
;
} else
newline = false;
 
} else {
*cp = *script++;
if (*cp == '\n') {
newline = true;
} else {
newline = false;
}
return true;
}
}
return false;
}
#endif
 
//
// Read a line of input from the user
// Return:
// _GETS_OK: 'n' valid characters received
// _GETS_GDB: '$' (GDB lead-in)
// _GETS_TIMEOUT: No input before timeout
// _GETS_CTRLC: ^C typed
//
int
_rb_gets_preloaded(char *buf, int buflen, int timeout)
{
char *ip = buf; // Insertion point
char *eol = buf; // End of line
char c;
bool res = false;
static char last_ch = '\0';
int _timeout;
#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
//
// Command line history support
// ^P - Select previous line from history
// ^N - Select next line from history
// ^A - Move insertion [cursor] to start of line
// ^E - Move cursor to end of line
// ^B - Move cursor back [previous character]
// ^F - Move cursor forward [next character]
//
#define _CL_NUM_LINES CYGNUM_REDBOOT_CMD_LINE_EDITING // Number of lines to keep
static char _cl_lines[_CL_NUM_LINES][CYGPKG_REDBOOT_MAX_CMD_LINE];
static int _cl_index = -1; // Last known command line
static int _cl_max_index = -1; // Last command in buffers
int _index = _cl_index; // Last saved line
char *xp;
#endif
 
// Display current buffer data
while (*eol) {
mon_write_char(*eol++);
}
ip = eol;
 
while (true) {
#ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
if (getc_script(&c))
do_idle(false);
else
#endif
if ((timeout > 0) && (eol == buf)) {
#define MIN_TIMEOUT 50
_timeout = timeout > MIN_TIMEOUT ? MIN_TIMEOUT : timeout;
mon_set_read_char_timeout(_timeout);
while (timeout > 0) {
res = mon_read_char_with_timeout(&c);
if (res) {
// Got a character
do_idle(false);
break;
}
timeout -= _timeout;
}
if (res == false) {
do_idle(true);
return _GETS_TIMEOUT; // Input timed out
}
} else {
mon_read_char(&c);
}
*eol = '\0';
switch (c) {
#define CTRL(c) ((c)&0x1F)
#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
case CTRL('P'):
// Fetch the previous line into the buffer
if (_index >= 0) {
// Erase the previous line [crude]
while (ip != buf) {
mon_write_char('\b');
mon_write_char(' ');
mon_write_char('\b');
ip--;
}
strcpy(buf, _cl_lines[_index]);
while (*ip) {
mon_write_char(*ip++);
}
eol = ip;
// Move to previous line
_index--;
if (_index < 0) {
_index = _cl_max_index;
}
} else {
mon_write_char(0x07); // Audible bell on most devices
}
break;
case CTRL('N'):
// Fetch the next line into the buffer
if (_index >= 0) {
if (++_index > _cl_max_index) _index = 0;
// Erase the previous line [crude]
while (ip != buf) {
mon_write_char('\b');
mon_write_char(' ');
mon_write_char('\b');
ip--;
}
strcpy(buf, _cl_lines[_index]);
while (*ip) {
mon_write_char(*ip++);
}
eol = ip;
} else {
mon_write_char(0x07); // Audible bell on most devices
}
break;
case CTRL('B'):
// Move insertion point backwards
if (ip != buf) {
mon_write_char('\b');
ip--;
}
break;
case CTRL('F'):
// Move insertion point forwards
if (ip != eol) {
mon_write_char(*ip++);
}
break;
case CTRL('E'):
// Move insertion point to end of line
while (ip != eol) {
mon_write_char(*ip++);
}
break;
case CTRL('A'):
// Move insertion point to beginning of line
if (ip != buf) {
xp = eol;
while (xp-- != buf) {
mon_write_char('\b');
}
}
ip = buf;
break;
case CTRL('K'):
// Kill to the end of line
if (ip != eol) {
xp = ip;
while (xp++ != eol) {
mon_write_char(' ');
}
while (--xp != ip) {
mon_write_char('\b');
}
eol = ip;
}
break;
case CTRL('D'):
// Erase the character under the cursor
if (ip != eol) {
xp = ip;
eol--;
while (xp != eol) {
*xp = *(xp+1);
mon_write_char(*xp++);
}
mon_write_char(' '); // Erases last character
mon_write_char('\b');
while (xp-- != ip) {
mon_write_char('\b');
}
}
break;
#endif // CYGNUM_REDBOOT_CMD_LINE_EDITING
case CTRL('C'): // ^C
// Abort current input
diag_printf("^C\n");
*buf = '\0'; // Nothing useful in buffer
return _GETS_CTRLC;
case '\n':
case '\r':
// If previous character was the "other" end-of-line, ignore this one
if (((c == '\n') && (last_ch == '\r')) ||
((c == '\r') && (last_ch == '\n'))) {
c = '\0';
break;
}
// End of line
if (console_echo) {
mon_write_char('\r');
mon_write_char('\n');
}
last_ch = c;
#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
if (cmd_history && (buf != eol)) {
// Save current line - only when enabled
if (++_cl_index == _CL_NUM_LINES) _cl_index = 0;
if (_cl_index > _cl_max_index) _cl_max_index = _cl_index;
strcpy(_cl_lines[_cl_index], buf);
}
#endif
return _GETS_OK;
case '\b':
case 0x7F: // DEL
if (ip != buf) {
#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
if (ip != eol) {
ip--;
mon_write_char('\b');
xp = ip;
while (xp != (eol-1)) {
*xp = *(xp+1);
mon_write_char(*xp++);
}
mon_write_char(' '); // Erases last character
mon_write_char('\b');
while (xp-- != ip) {
mon_write_char('\b');
}
} else {
if (console_echo) {
mon_write_char('\b');
mon_write_char(' ');
mon_write_char('\b');
}
ip--;
}
eol--;
#else
if (console_echo) {
mon_write_char('\b');
mon_write_char(' ');
mon_write_char('\b');
}
ip--;
eol--;
#endif
}
break;
#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
case '+': // fall through
case '$':
if (ip == buf || last_ch != '\\')
{
// Give up and try GDB protocol
ungetDebugChar(c); // Push back character so stubs will see it
return _GETS_GDB;
}
if (last_ch == '\\') {
#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
if (ip == eol) {
// Just save \$ as $
eol = --ip;
} else {
mon_write_char('\b');
*--ip = c;
mon_write_char(c);
break;
}
#else
ip--; // Save \$ as $
#endif
}
// else fall through
#endif
default:
#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
// If the insertion point is not at the end of line, make space for it
if (ip != eol) {
xp = eol;
*++eol = '\0';
while (xp != ip) {
*xp = *(xp-1);
xp--;
}
}
#endif
if (console_echo) {
mon_write_char(c);
}
if (ip == eol) {
// Advance both pointers
*ip++ = c;
eol = ip;
#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
} else {
// Just insert the character
*ip++ = c;
xp = ip;
while (xp != eol) {
mon_write_char(*xp++);
}
while (xp-- != ip) {
mon_write_char('\b');
}
#endif
}
}
last_ch = c;
if (ip == buf + buflen - 1) { // Buffer full
*ip = '\0';
return buflen;
}
}
}
 
int
_rb_gets(char *buf, int buflen, int timeout)
{
*buf = '\0'; // Empty buffer
return _rb_gets_preloaded(buf, buflen, timeout);
}
 
static bool
_verify_action(int timeout, char *fmt, va_list ap)
{
char ans[8];
int ret;
#ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
// Don't ask if we're executing a script
if (script && *script)
return 1;
#endif
 
diag_vprintf(fmt, ap);
diag_printf(" - continue (y/n)? ");
if ((ret = _rb_gets(ans, sizeof(ans), timeout)) > 0) {
return ((ans[0] == 'y') || (ans[0] == 'Y'));
} else {
if (ret == _GETS_TIMEOUT) {
diag_printf(" ** Timed out!\n");
}
return 0; // Timed out or ^C
}
}
 
bool
verify_action(char *fmt, ...)
{
va_list ap;
 
va_start(ap, fmt);
return _verify_action(0, fmt, ap);
}
 
bool
verify_action_with_timeout(int timeout, char *fmt, ...)
{
va_list ap;
 
va_start(ap, fmt);
return _verify_action(timeout, fmt, ap);
}
/syscall.c
0,0 → 1,651
/*==========================================================================
//
// syscall.c
//
// Redboot syscall handling for GNUPro bsp support
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): msalter
// Contributors: msalter
// Date: 1999-02-20
// Purpose: Temporary support for gnupro bsp
//
//####DESCRIPTIONEND####
//
//=========================================================================*/
 
#include <redboot.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/drv_api.h>
#include <cyg/hal/hal_stub.h>
 
#ifdef CYGSEM_REDBOOT_BSP_SYSCALLS
 
#define NEWLIB_EIO 5 /* I/O error */
#define NEWLIB_ENOSYS 88 /* Syscall not supported */
 
/*
* Clients of this BSP will need to have access to BSP functions and
* data structures. Because, the client and the BSP may not be linked
* together, a structure of vectors is used to gain this access. A
* pointer to this structure can be gotten via a syscall. This syscall
* is made automatically from within the crt0.o file.
*/
typedef struct {
int version; /* version number for future expansion */
const void **__ictrl_table;
void **__exc_table;
void *__dbg_vector;
void *__kill_vector;
void *__console_procs;
void *__debug_procs;
void (*__flush_dcache)(void *__p, int __nbytes);
void (*__flush_icache)(void *__p, int __nbytes);
void *__cpu_data;
void *__board_data;
void *__sysinfo;
int (*__set_debug_comm)(int __comm_id);
int (*__set_console_comm)(int __comm_id);
int (*__set_serial_baud)(int __comm_id, int baud);
void *__dbg_data;
void (*__reset)(void);
int __console_interrupt_flag;
} __shared_t;
 
static __shared_t __shared_data = { 2 };
 
// this is used by newlib's mode_t so we should match it
#ifdef __GNUC__
#define _ST_INT32 __attribute__ ((__mode__ (__SI__)))
#else
#define _ST_INT32
#endif
typedef unsigned int newlib_mode_t _ST_INT32;
typedef short newlib_dev_t;
typedef unsigned short newlib_ino_t;
typedef unsigned short newlib_nlink_t;
typedef long newlib_off_t;
typedef unsigned short newlib_uid_t;
typedef unsigned short newlib_gid_t;
typedef long newlib_time_t;
typedef long newlib_long_t;
 
struct newlib_stat
{
newlib_dev_t st_dev;
newlib_ino_t st_ino;
newlib_mode_t st_mode;
newlib_nlink_t st_nlink;
newlib_uid_t st_uid;
newlib_gid_t st_gid;
newlib_dev_t st_rdev;
newlib_off_t st_size;
// We assume we've been compiled with the same flags as newlib here
#if defined(__svr4__) && !defined(__PPC__) && !defined(__sun__)
newlib_time_t st_atime;
newlib_time_t st_mtime;
newlib_time_t st_ctime;
#else
newlib_time_t st_atime;
newlib_long_t st_spare1;
newlib_time_t st_mtime;
newlib_long_t st_spare2;
newlib_time_t st_ctime;
newlib_long_t st_spare3;
newlib_long_t st_blksize;
newlib_long_t st_blocks;
newlib_long_t st_spare4[2];
#endif
};
#define NEWLIB_S_IFCHR 0020000 // character special file
 
static inline char __getc(void)
{
char c;
hal_virtual_comm_table_t* __chan = CYGACC_CALL_IF_CONSOLE_PROCS();
if (__chan)
c = CYGACC_COMM_IF_GETC(*__chan);
else {
__chan = CYGACC_CALL_IF_DEBUG_PROCS();
c = CYGACC_COMM_IF_GETC(*__chan);
}
return c;
}
 
static inline void __putc(char c)
{
hal_virtual_comm_table_t* __chan = CYGACC_CALL_IF_CONSOLE_PROCS();
if (__chan)
CYGACC_COMM_IF_PUTC(*__chan, c);
else {
__chan = CYGACC_CALL_IF_DEBUG_PROCS();
CYGACC_COMM_IF_PUTC(*__chan, c);
}
}
 
 
static inline void __flush(void)
{
hal_virtual_comm_table_t* __chan = CYGACC_CALL_IF_CONSOLE_PROCS();
 
if (__chan == NULL)
__chan = CYGACC_CALL_IF_DEBUG_PROCS();
 
CYGACC_COMM_IF_CONTROL(*__chan, __COMMCTL_FLUSH_OUTPUT);
}
 
// Timer support
 
static cyg_handle_t sys_timer_handle;
static cyg_interrupt sys_timer_interrupt;
static cyg_uint64 sys_timer_ticks = 0;
 
#ifdef CYGSEM_REDBOOT_BSP_SYSCALLS_GPROF
 
static unsigned int set_period = CYGNUM_HAL_RTC_PERIOD; // The default
 
typedef void *callback_func( char *pc, char *sp );
static callback_func *timer_callback = 0;
 
#endif // CYGSEM_REDBOOT_BSP_SYSCALLS_GPROF
 
static void
sys_timer_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
// do nothing
}
 
 
static cyg_uint32
sys_timer_isr(cyg_vector_t vector, cyg_addrword_t data)
{
++sys_timer_ticks;
 
#ifdef CYGSEM_REDBOOT_BSP_SYSCALLS_GPROF
HAL_CLOCK_RESET(CYGNUM_HAL_INTERRUPT_RTC, set_period);
#else
HAL_CLOCK_RESET(CYGNUM_HAL_INTERRUPT_RTC, CYGNUM_HAL_RTC_PERIOD);
#endif // CYGSEM_REDBOOT_BSP_SYSCALLS_GPROF
 
cyg_drv_interrupt_acknowledge(CYGNUM_HAL_INTERRUPT_RTC);
 
#ifdef CYGSEM_REDBOOT_BSP_SYSCALLS_GPROF
if ( timer_callback ) {
char *intrpc = (char *)0;
char *intrsp = (char *)0;
 
// There may be a number of ways to get the PC and (optional) SP
// information out of the HAL. Hence this is conditioned. In some
// configurations, a register-set pointer is available as
// (invisible) argument 3 to this ISR call.
 
#ifdef HAL_GET_PROFILE_INFO
HAL_GET_PROFILE_INFO( intrpc, intrsp );
#endif // HAL_GET_PROFILE_INFO available
 
CYGARC_HAL_SAVE_GP();
timer_callback( intrpc, intrsp );
CYGARC_HAL_RESTORE_GP();
}
#endif // CYGSEM_REDBOOT_BSP_SYSCALLS_GPROF
return CYG_ISR_HANDLED;
}
 
 
static void sys_timer_init(void)
{
#ifdef CYGSEM_REDBOOT_BSP_SYSCALLS_GPROF
HAL_CLOCK_INITIALIZE(set_period);
#else
HAL_CLOCK_INITIALIZE(CYGNUM_HAL_RTC_PERIOD);
#endif // CYGSEM_REDBOOT_BSP_SYSCALLS_GPROF
cyg_drv_interrupt_create(
CYGNUM_HAL_INTERRUPT_RTC,
0, // Priority - unused
(CYG_ADDRWORD)0, // Data item passed to ISR & DSR
sys_timer_isr, // ISR
sys_timer_dsr, // DSR
&sys_timer_handle, // handle to intr obj
&sys_timer_interrupt ); // space for int obj
 
cyg_drv_interrupt_attach(sys_timer_handle);
 
cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_RTC);
}
 
 
//
// read -- read bytes from the serial port. Ignore fd, since
// we only have stdin.
static int
sys_read(int fd, char *buf, int nbytes)
{
int i = 0;
 
for (i = 0; i < nbytes; i++) {
*(buf + i) = __getc();
if ((*(buf + i) == '\n') || (*(buf + i) == '\r')) {
(*(buf + i + 1)) = 0;
break;
}
}
return (i);
}
 
 
//
// write -- write bytes to the serial port. Ignore fd, since
// stdout and stderr are the same. Since we have no filesystem,
// open will only return an error.
//
static int
sys_write(int fd, char *buf, int nbytes)
{
#define WBUFSIZE 256
int tosend;
 
tosend = nbytes;
 
while (tosend > 0) {
if (*buf == '\n')
__putc('\r');
__putc(*buf++);
tosend--;
}
__flush();
 
return (nbytes);
}
 
 
//
// open -- open a file descriptor. We don't have a filesystem, so
// we return an error.
//
static int
sys_open (const char *buf, int flags, int mode)
{
return (-NEWLIB_EIO);
}
 
//
// close -- We don't need to do anything, but pretend we did.
//
static int
sys_close(int fd)
{
return (0);
}
 
 
//
// lseek -- Since a serial port is non-seekable, we return an error.
//
static int
sys_lseek(int fd, int offset, int whence)
{
return (-NEWLIB_EIO);
}
 
 
#define NS_PER_TICK (CYGNUM_HAL_RTC_NUMERATOR/CYGNUM_HAL_RTC_DENOMINATOR)
#define TICKS_PER_SEC (1000000000ULL / NS_PER_TICK)
 
// This needs to match newlib HZ which is normally 60.
#define HZ (60ULL)
 
#ifdef CYGSEM_REDBOOT_BSP_SYSCALLS_GPROF
static unsigned int set_freq = TICKS_PER_SEC; // The default
#endif // CYGSEM_REDBOOT_BSP_SYSCALLS_GPROF
static int
sys_times(unsigned long *p)
{
static int inited = 0;
 
if (!inited) {
inited = 1;
sys_timer_init();
}
 
/* target clock runs at CLOCKS_PER_SEC. Convert to HZ */
if (p)
#ifdef CYGSEM_REDBOOT_BSP_SYSCALLS_GPROF
*p = (sys_timer_ticks * HZ) / (cyg_uint64)set_freq;
#else
*p = (sys_timer_ticks * HZ) / TICKS_PER_SEC;
#endif // CYGSEM_REDBOOT_BSP_SYSCALLS_GPROF
 
return 0;
}
 
#ifdef CYGSEM_REDBOOT_BSP_SYSCALLS_GPROF
 
static void sys_profile_call_back( char *func, char **previous_call_back )
{
if ( previous_call_back )
*previous_call_back = (char *)timer_callback;
 
timer_callback = (callback_func *)func;
 
// Ensure the timer is started
(void)sys_times( (unsigned long *)0 );
}
 
static void sys_profile_frequency( int freq, int *previous_freq )
{
// Requested HZ:
// 0 => tell me the current value (no change, IMPLEMENTED HERE)
// - 1 => tell me the slowest (no change)
// - 2 => tell me the default (no change, IMPLEMENTED HERE)
// -nnn => tell me what you would choose for nnn (no change)
// MIN_INT => tell me the fastest (no change)
//
// 1 => tell me the slowest (sets the clock)
// MAX_INT => tell me the fastest (sets the clock)
 
// Ensure the timer is started
(void)sys_times( (unsigned long *)0 );
 
if ( -2 == freq )
freq = TICKS_PER_SEC; // default value
else if ( 0 == freq )
freq = set_freq; // collect current value
else {
int do_set_freq = (freq > 0);
unsigned int period = CYGNUM_HAL_RTC_PERIOD;
 
if ( 0 == (freq ^ -freq) ) // Then it's MIN_INT in local size
freq++; // just so that it will negate correctly
 
// Then set the timer to that fast - or pass on the enquiry
#ifdef HAL_CLOCK_REINITIALIZE
// Give the HAL enough info to do the division sum relative to
// the default setup, in period and TICKS_PER_SEC.
HAL_CLOCK_REINITIALIZE( freq, period, TICKS_PER_SEC );
#else
freq = TICKS_PER_SEC; // the only choice
#endif
if ( do_set_freq ) { // update the global variables
unsigned int orig = set_freq;
set_freq = freq;
set_period = period;
// We must "correct" sys_timer_ticks for the new scale factor.
sys_timer_ticks = sys_timer_ticks * set_freq / orig;
}
}
 
if ( previous_freq ) // Return the current value (new value)
*previous_freq = freq;
}
 
void sys_profile_reset( void )
{
timer_callback = NULL;
// Want to preserve the frequency between runs, for clever GDB users!
// sys_profile_frequency( TICKS_PER_SEC, NULL );
}
 
#endif // CYGSEM_REDBOOT_BSP_SYSCALLS_GPROF
 
//
// Generic syscall handler.
//
// Returns 0 if syscall number is not handled by this
// module, 1 otherwise. This allows applications to
// extend the syscall handler by using exception chaining.
//
CYG_ADDRWORD
__do_syscall(CYG_ADDRWORD func, // syscall function number
CYG_ADDRWORD arg1, CYG_ADDRWORD arg2, // up to four args.
CYG_ADDRWORD arg3, CYG_ADDRWORD arg4,
CYG_ADDRWORD *retval, CYG_ADDRWORD *sig) // syscall return value
{
int err = 0;
*sig = 0;
 
switch (func) {
 
case SYS_open:
{
#ifdef CYGPKG_HAL_GDB_FILEIO // File I/O over the GDB remote protocol
__externC int cyg_hal_gdbfileio_open( const char *name, int flags,
int mode, int *sig );
if (gdb_active)
err = cyg_hal_gdbfileio_open((const char *)arg1, (int)arg2, (int)arg3,
(int *)sig);
else
#endif
err = sys_open((const char *)arg1, (int)arg2, (int)arg3);
break;
}
case SYS_read:
{
#ifdef CYGPKG_HAL_GDB_FILEIO // File I/O over the GDB remote protocol
__externC int cyg_hal_gdbfileio_read( int fd, void *buf, size_t count,
int *sig );
if (gdb_active)
err = cyg_hal_gdbfileio_read((int)arg1, (void *)arg2, (size_t)arg3,
(int *)sig);
else
#endif
err = sys_read((int)arg1, (char *)arg2, (int)arg3);
break;
}
case SYS_write:
{
#ifdef CYGPKG_HAL_GDB_FILEIO // File I/O over the GDB remote protocol
__externC int cyg_hal_gdbfileio_write( int fd, const void *buf,
size_t count, int *sig );
if (gdb_active)
err = cyg_hal_gdbfileio_write((int)arg1, (const void *)arg2,
(size_t)arg3, (int *)sig);
else
#endif
err = sys_write((int)arg1, (char *)arg2, (int)arg3);
break;
}
case SYS_close:
{
#ifdef CYGPKG_HAL_GDB_FILEIO // File I/O over the GDB remote protocol
__externC int cyg_hal_gdbfileio_close( int fd, int *sig );
if (gdb_active)
err = cyg_hal_gdbfileio_close((int)arg1, (int *)sig);
else
#endif
err = sys_close((int)arg1);
break;
}
case SYS_lseek:
{
#ifdef CYGPKG_HAL_GDB_FILEIO // File I/O over the GDB remote protocol
__externC int cyg_hal_gdbfileio_lseek( int fd, long offset,
int whence, int *sig );
if (gdb_active)
err = cyg_hal_gdbfileio_lseek((int)arg1, (long)arg2, (int)arg3,
(int *)sig);
else
#endif
err = sys_lseek((int)arg1, (int)arg2, (int)arg3);
break;
}
case SYS_stat:
{
#ifdef CYGPKG_HAL_GDB_FILEIO // File I/O over the GDB remote protocol
__externC int cyg_hal_gdbfileio_stat( const char *pathname,
void *statbuf, int *sig );
if (gdb_active)
err = cyg_hal_gdbfileio_stat((const char *)arg1, (void *)arg2,
(int *)sig);
else
#endif
err = -NEWLIB_ENOSYS;
break;
}
case SYS_fstat:
{
#ifdef CYGPKG_HAL_GDB_FILEIO // File I/O over the GDB remote protocol
__externC int cyg_hal_gdbfileio_fstat( int fd, void *statbuf,
int *sig );
if (gdb_active)
err = cyg_hal_gdbfileio_fstat((int)arg1, (void *)arg2,
(int *)sig);
else
#endif
{
struct newlib_stat *st = (struct newlib_stat *)arg2;
st->st_mode = NEWLIB_S_IFCHR;
st->st_blksize = 4096;
err = 0;
}
break;
}
case SYS_rename:
{
#ifdef CYGPKG_HAL_GDB_FILEIO // File I/O over the GDB remote protocol
__externC int cyg_hal_gdbfileio_rename( const char *oldpath,
const char *newpath,
int *sig );
if (gdb_active)
err = cyg_hal_gdbfileio_rename((const char *)arg1, (const char *)arg2,
(int *)sig);
else
#endif
err = -NEWLIB_ENOSYS;
break;
}
case SYS_unlink:
{
#ifdef CYGPKG_HAL_GDB_FILEIO // File I/O over the GDB remote protocol
__externC int cyg_hal_gdbfileio_unlink( const char *pathname,
int *sig );
if (gdb_active)
err = cyg_hal_gdbfileio_unlink((const char *)arg1, (int *)sig);
else
#endif
err = -NEWLIB_ENOSYS;
break;
}
case SYS_isatty:
{
#ifdef CYGPKG_HAL_GDB_FILEIO // File I/O over the GDB remote protocol
__externC int cyg_hal_gdbfileio_isatty( int fd, int *sig );
if (gdb_active)
err = cyg_hal_gdbfileio_isatty((int)arg1, (int *)sig);
else
#endif
err = 1;
break;
}
case SYS_system:
{
#ifdef CYGPKG_HAL_GDB_FILEIO // File I/O over the GDB remote protocol
__externC int cyg_hal_gdbfileio_system( const char *command,
int *sig );
if (gdb_active)
err = cyg_hal_gdbfileio_system((const char *)arg1, (int *)sig);
else
#endif
err = -1;
break;
}
case SYS_gettimeofday:
{
#ifdef CYGPKG_HAL_GDB_FILEIO // File I/O over the GDB remote protocol
__externC int cyg_hal_gdbfileio_gettimeofday( void *tv, void *tz,
int *sig );
if (gdb_active)
err = cyg_hal_gdbfileio_gettimeofday((void *)arg1, (void *)arg2,
(int *)sig);
else
#endif
err = 0;
break;
}
case SYS_utime:
// FIXME. Some libglosses depend on this behavior.
err = sys_times((unsigned long *)arg1);
break;
 
case SYS_times:
err = sys_times((unsigned long *)arg1);
break;
 
case SYS_meminfo:
err = 1;
*(unsigned long *)arg1 = (unsigned long)(ram_end-ram_start);
*(unsigned long *)arg2 = (unsigned long)ram_end;
break;
#ifdef CYGSEM_REDBOOT_BSP_SYSCALLS_GPROF
case SYS_timer_call_back:
sys_profile_call_back( (char *)arg1, (char **)arg2 );
break;
 
case SYS_timer_frequency:
sys_profile_frequency( (int)arg1, (unsigned int *)arg2 );
break;
 
case SYS_timer_reset:
sys_profile_reset();
break;
 
#endif // CYGSEM_REDBOOT_BSP_SYSCALLS_GPROF
case __GET_SHARED:
*(__shared_t **)arg1 = &__shared_data;
break;
 
case SYS_exit:
if (gdb_active) {
*sig = SIGTRAP;
err = func;
} else {
CYGACC_CALL_IF_MONITOR_RETURN(arg1);
// never returns
}
break;
 
default:
return 0;
}
 
*retval = err;
return 1;
}
 
#endif
/parse.c
0,0 → 1,402
//==========================================================================
//
// parse.c
//
// RedBoot command line parsing routine
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/hal_cache.h>
#include CYGHWR_MEMORY_LAYOUT_H
#include <cyg/hal/hal_tables.h>
 
// Define table boundaries
extern struct cmd __RedBoot_CMD_TAB__[], __RedBoot_CMD_TAB_END__;
 
//
// Scan through an input line and break it into "arguments". These
// are space delimited strings. Return a structure which points to
// the strings, similar to a Unix program. Multiple commands in the line
// are separated by ; similar to sh. If we find a semi we stop processing the
// line, terminate the current command with a null and return the start
// of the next command in *line. parse() can then be called again to
// process the next command on the line.
// Note: original input is destroyed by replacing the delimiters with
// null ('\0') characters for ease of use.
//
struct cmd *
parse(char **line, int *argc, char **argv)
{
char *cp = *line;
char *pp;
int indx = 0;
int semi = 0;
 
while (*cp) {
// Skip leading spaces
while (*cp && *cp == ' ') cp++;
if (!*cp) {
break; // Line ended with a string of spaces
}
if (*cp == ';') {
*cp = '\0';
semi=1;
break;
}
if (indx < MAX_ARGV) {
argv[indx++] = cp;
} else {
diag_printf("Too many arguments - stopped at: '%s'\n", cp);
}
while (*cp) {
if (*cp == ' ') {
*cp++ = '\0';
break;
} else if (*cp == ';') {
break;
} else if (*cp == '"') {
// Swallow quote, scan till following one
if (argv[indx-1] == cp) {
argv[indx-1] = ++cp;
}
pp = cp;
while (*cp && *cp != '"') {
if (*cp == '\\') {
// Skip over escape - allows for escaped '"'
cp++;
}
// Move string to swallow escapes
*pp++ = *cp++;
}
if (!*cp) {
diag_printf("Unbalanced string!\n");
} else {
if (pp != cp) *pp = '\0';
*cp++ = '\0';
break;
}
} else {
cp++;
}
}
}
if (semi) {
*line = cp + 1;
} else {
*line = cp;
}
*argc = indx;
return cmd_search(__RedBoot_CMD_TAB__, &__RedBoot_CMD_TAB_END__, argv[0]);
}
 
//
// Search through a list of commands
//
struct cmd *
cmd_search(struct cmd *tab, struct cmd *tabend, char *arg)
{
int cmd_len;
struct cmd *cmd, *cmd2;
// Search command table
cmd_len = strlen(arg);
cmd = tab;
while (cmd != tabend) {
if (strncasecmp(arg, cmd->str, cmd_len) == 0) {
if (strlen(cmd->str) > cmd_len) {
// Check for ambiguous commands here
// Note: If there are commands which are not length-unique
// then this check will be invalid. E.g. "du" and "dump"
bool first = true;
cmd2 = tab;
while (cmd2 != tabend) {
if ((cmd != cmd2) &&
(strncasecmp(arg, cmd2->str, cmd_len) == 0)) {
if (first) {
diag_printf("Ambiguous command '%s', choices are: %s",
arg, cmd->str);
first = false;
}
diag_printf(" %s", cmd2->str);
}
cmd2++;
}
if (!first) {
// At least one ambiguity found - fail the lookup
diag_printf("\n");
return (struct cmd *)0;
}
}
return cmd;
}
cmd++;
}
return (struct cmd *)0;
}
 
void
cmd_usage(struct cmd *tab, struct cmd *tabend, char *prefix)
{
struct cmd *cmd;
 
diag_printf("Usage:\n");
for (cmd = tab; cmd != tabend; cmd++) {
diag_printf(" %s%s %s\n", prefix, cmd->str, cmd->usage);
}
}
 
// Option processing
 
// Initialize option table entry (required because these entries
// may have dynamic contents, thus cannot be statically initialized)
//
void
init_opts(struct option_info *opts, char flag, bool takes_arg,
int arg_type, void **arg, bool *arg_set, char *name)
{
opts->flag = flag;
opts->takes_arg = takes_arg;
opts->arg_type = arg_type,
opts->arg = arg;
opts->arg_set = arg_set;
opts->name = name;
}
 
//
// Scan command line arguments (argc/argv), processing options, etc.
//
bool
scan_opts(int argc, char *argv[], int first,
struct option_info *opts, int num_opts,
void **def_arg, int def_arg_type, char *def_descr)
{
bool ret = true;
bool flag_ok;
bool def_arg_set = false;
int i, j;
char c, *s;
struct option_info *opt;
 
if (def_arg && (def_arg_type == OPTION_ARG_TYPE_STR)) {
*def_arg = (char *)0;
}
opt = opts;
for (j = 0; j < num_opts; j++, opt++) {
if (opt->arg_set) {
*opt->arg_set = false;
}
if (!opt->takes_arg) {
switch (opt->arg_type) {
case OPTION_ARG_TYPE_NUM:
*(int *)opt->arg = 0;
break;
case OPTION_ARG_TYPE_FLG:
*(bool *)opt->arg = false;
break;
}
}
}
for (i = first; i < argc; i++) {
if (argv[i][0] == '-') {
c = argv[i][1];
flag_ok = false;
opt = opts;
for (j = 0; j < num_opts; j++, opt++) {
if (c == opt->flag) {
if (opt->arg_set && *opt->arg_set) {
diag_printf("** Error: %s already specified\n", opt->name);
ret = false;
}
if (opt->takes_arg) {
if (argv[i][2] == '=') {
s = &argv[i][3];
} else {
s = argv[i+1];
i++;
}
switch (opt->arg_type) {
case OPTION_ARG_TYPE_NUM:
if (!parse_num(s, (unsigned long *)opt->arg, 0, 0)) {
diag_printf("** Error: invalid number '%s' for %s\n",
s, opt->name);
ret = false;
}
break;
case OPTION_ARG_TYPE_STR:
*opt->arg = s;
break;
}
*opt->arg_set = true;
} else {
switch (opt->arg_type) {
case OPTION_ARG_TYPE_NUM:
*(int *)opt->arg = *(int *)opt->arg + 1;
break;
case OPTION_ARG_TYPE_FLG:
*(bool *)opt->arg = true;
break;
}
}
flag_ok = true;
break;
}
}
if (!flag_ok) {
diag_printf("** Error: invalid flag '%c'\n", c);
ret = false;
}
} else {
if (def_arg) {
if (def_arg_set) {
diag_printf("** Error: %s already specified\n", def_descr);
ret = false;
}
switch (def_arg_type) {
case OPTION_ARG_TYPE_NUM:
if (!parse_num(argv[i], (unsigned long *)def_arg, 0, 0)) {
diag_printf("** Error: invalid number '%s' for %s\n",
argv[i], def_descr);
ret = false;
}
break;
case OPTION_ARG_TYPE_STR:
*def_arg = argv[i];
break;
}
def_arg_set = true;
} else {
diag_printf("** Error: no default/non-flag arguments supported\n");
ret = false;
}
}
}
return ret;
}
 
//
// Parse (scan) a number
//
bool
parse_num(char *s, unsigned long *val, char **es, char *delim)
{
bool first = true;
int radix = 10;
char c;
unsigned long result = 0;
int digit;
 
while (*s == ' ') s++;
while (*s) {
if (first && (s[0] == '0') && (_tolower(s[1]) == 'x')) {
radix = 16;
s += 2;
}
first = false;
c = *s++;
if (_is_hex(c) && ((digit = _from_hex(c)) < radix)) {
// Valid digit
#ifdef CYGPKG_HAL_MIPS
// FIXME: tx49 compiler generates 0x2539018 for MUL which
// isn't any good.
if (16 == radix)
result = result << 4;
else
result = 10 * result;
result += digit;
#else
result = (result * radix) + digit;
#endif
} else {
if (delim != (char *)0) {
// See if this character is one of the delimiters
char *dp = delim;
while (*dp && (c != *dp)) dp++;
if (*dp) break; // Found a good delimiter
}
return false; // Malformatted number
}
}
*val = result;
if (es != (char **)0) {
*es = s;
}
return true;
}
 
bool
parse_bool(char *s, bool *val)
{
while (*s == ' ') s++;
if ((*s == 't') || (*s == 'T')) {
char *p = "rue";
char *P = "RUE";
// check for (partial) rest of the word and no extra including the
// terminating zero. "tru" will match; "truef" will not.
while ( *++s ) {
if ( *p != *s && *P != *s ) return false;
p++; P++;
}
*val = true;
} else
if ((*s == 'f') || (*s == 'F')) {
char *p = "alse";
char *P = "ALSE";
while ( *++s ) {
if ( *p != *s && *P != *s ) return false;
p++; P++;
}
*val = false;
} else {
return false;
}
return true;
}
 
/xyzModem.c
0,0 → 1,588
//==========================================================================
//
// xyzModem.c
//
// RedBoot stream handler for xyzModem protocol
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas, tsmith, Yoshinori Sato
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
#include <xyzModem.h>
 
// Assumption - run xyzModem protocol over the console port
 
// Values magic to the protocol
#define SOH 0x01
#define STX 0x02
#define EOT 0x04
#define ACK 0x06
#define BSP 0x08
#define NAK 0x15
#define CAN 0x18
#define EOF 0x1A // ^Z for DOS officionados
 
#define nUSE_YMODEM_LENGTH
 
// Data & state local to the protocol
static struct {
hal_virtual_comm_table_t* __chan;
unsigned char pkt[1024], *bufp;
unsigned char blk,cblk,crc1,crc2;
unsigned char next_blk; // Expected block
int len, mode, total_retries;
int total_SOH, total_STX, total_CAN;
bool crc_mode, at_eof, tx_ack;
#ifdef USE_YMODEM_LENGTH
unsigned long file_length, read_length;
#endif
} xyz;
 
#define xyzModem_CHAR_TIMEOUT 2000 // 2 seconds
#define xyzModem_MAX_RETRIES 20
#define xyzModem_MAX_RETRIES_WITH_CRC 10
#define xyzModem_CAN_COUNT 3 // Wait for 3 CAN before quitting
 
#ifdef DEBUG
#ifndef USE_SPRINTF
//
// Note: this debug setup only works if the target platform has two serial ports
// available so that the other one (currently only port 1) can be used for debug
// messages.
//
static int
zm_dprintf(char *fmt, ...)
{
int cur_console;
va_list args;
 
va_start(args, fmt);
cur_console = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
CYGACC_CALL_IF_SET_CONSOLE_COMM(1);
diag_vprintf(fmt, args);
CYGACC_CALL_IF_SET_CONSOLE_COMM(cur_console);
}
 
static void
zm_flush(void)
{
}
 
#else
//
// Note: this debug setup works by storing the strings in a fixed buffer
//
static char *zm_out = (char *)0x00380000;
static char *zm_out_start = (char *)0x00380000;
 
static int
zm_dprintf(char *fmt, ...)
{
int len;
va_list args;
 
va_start(args, fmt);
len = diag_vsprintf(zm_out, fmt, args);
zm_out += len;
return len;
}
 
static void
zm_flush(void)
{
char *p = zm_out_start;
while (*p) mon_write_char(*p++);
zm_out = zm_out_start;
}
#endif
 
static void
zm_dump_buf(void *buf, int len)
{
diag_vdump_buf_with_offset(zm_dprintf, buf, len, 0);
}
 
static unsigned char zm_buf[2048];
static unsigned char *zm_bp;
 
static void
zm_new(void)
{
zm_bp = zm_buf;
}
 
static void
zm_save(unsigned char c)
{
*zm_bp++ = c;
}
 
static void
zm_dump(int line)
{
zm_dprintf("Packet at line: %d\n", line);
zm_dump_buf(zm_buf, zm_bp-zm_buf);
}
 
#define ZM_DEBUG(x) x
#else
#define ZM_DEBUG(x)
#endif
 
// Wait for the line to go idle
static void
xyzModem_flush(void)
{
int res;
char c;
while (true) {
res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c);
if (!res) return;
}
}
 
static int
xyzModem_get_hdr(void)
{
char c;
int res;
bool hdr_found = false;
int i, can_total, hdr_chars;
unsigned short cksum;
 
ZM_DEBUG(zm_new());
// Find the start of a header
can_total = 0;
hdr_chars = 0;
 
if (xyz.tx_ack) {
CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
xyz.tx_ack = false;
}
while (!hdr_found) {
res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c);
ZM_DEBUG(zm_save(c));
if (res) {
hdr_chars++;
switch (c) {
case SOH:
xyz.total_SOH++;
case STX:
if (c == STX) xyz.total_STX++;
hdr_found = true;
break;
case CAN:
xyz.total_CAN++;
ZM_DEBUG(zm_dump(__LINE__));
if (++can_total == xyzModem_CAN_COUNT) {
return xyzModem_cancel;
} else {
// Wait for multiple CAN to avoid early quits
break;
}
case EOT:
// EOT only supported if no noise
if (hdr_chars == 1) {
CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
ZM_DEBUG(zm_dprintf("ACK on EOT #%d\n", __LINE__));
ZM_DEBUG(zm_dump(__LINE__));
return xyzModem_eof;
}
default:
// Ignore, waiting for start of header
;
}
} else {
// Data stream timed out
xyzModem_flush(); // Toss any current input
ZM_DEBUG(zm_dump(__LINE__));
CYGACC_CALL_IF_DELAY_US((cyg_int32)250000);
return xyzModem_timeout;
}
}
 
// Header found, now read the data
res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.blk);
ZM_DEBUG(zm_save(xyz.blk));
if (!res) {
ZM_DEBUG(zm_dump(__LINE__));
return xyzModem_timeout;
}
res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.cblk);
ZM_DEBUG(zm_save(xyz.cblk));
if (!res) {
ZM_DEBUG(zm_dump(__LINE__));
return xyzModem_timeout;
}
xyz.len = (c == SOH) ? 128 : 1024;
xyz.bufp = xyz.pkt;
for (i = 0; i < xyz.len; i++) {
res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &c);
ZM_DEBUG(zm_save(c));
if (res) {
xyz.pkt[i] = c;
} else {
ZM_DEBUG(zm_dump(__LINE__));
return xyzModem_timeout;
}
}
res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.crc1);
ZM_DEBUG(zm_save(xyz.crc1));
if (!res) {
ZM_DEBUG(zm_dump(__LINE__));
return xyzModem_timeout;
}
if (xyz.crc_mode) {
res = CYGACC_COMM_IF_GETC_TIMEOUT(*xyz.__chan, &xyz.crc2);
ZM_DEBUG(zm_save(xyz.crc2));
if (!res) {
ZM_DEBUG(zm_dump(__LINE__));
return xyzModem_timeout;
}
}
ZM_DEBUG(zm_dump(__LINE__));
// Validate the message
if ((xyz.blk ^ xyz.cblk) != (unsigned char)0xFF) {
ZM_DEBUG(zm_dprintf("Framing error - blk: %x/%x/%x\n", xyz.blk, xyz.cblk, (xyz.blk ^ xyz.cblk)));
ZM_DEBUG(zm_dump_buf(xyz.pkt, xyz.len));
xyzModem_flush();
return xyzModem_frame;
}
// Verify checksum/CRC
if (xyz.crc_mode) {
cksum = cyg_crc16(xyz.pkt, xyz.len);
if (cksum != ((xyz.crc1 << 8) | xyz.crc2)) {
ZM_DEBUG(zm_dprintf("CRC error - recvd: %02x%02x, computed: %x\n",
xyz.crc1, xyz.crc2, cksum & 0xFFFF));
return xyzModem_cksum;
}
} else {
cksum = 0;
for (i = 0; i < xyz.len; i++) {
cksum += xyz.pkt[i];
}
if (xyz.crc1 != (cksum & 0xFF)) {
ZM_DEBUG(zm_dprintf("Checksum error - recvd: %x, computed: %x\n", xyz.crc1, cksum & 0xFF));
return xyzModem_cksum;
}
}
// If we get here, the message passes [structural] muster
return 0;
}
 
int
xyzModem_stream_open(connection_info_t *info, int *err)
{
int console_chan, stat;
int retries = xyzModem_MAX_RETRIES;
int crc_retries = xyzModem_MAX_RETRIES_WITH_CRC;
 
// ZM_DEBUG(zm_out = zm_out_start);
#ifdef xyzModem_zmodem
if (info->mode == xyzModem_zmodem) {
*err = xyzModem_noZmodem;
return -1;
}
#endif
 
// Set up the I/O channel. Note: this allows for using a different port in the future
console_chan = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
if (info->chan >= 0) {
CYGACC_CALL_IF_SET_CONSOLE_COMM(info->chan);
} else {
CYGACC_CALL_IF_SET_CONSOLE_COMM(console_chan);
}
xyz.__chan = CYGACC_CALL_IF_CONSOLE_PROCS();
CYGACC_CALL_IF_SET_CONSOLE_COMM(console_chan);
CYGACC_COMM_IF_CONTROL(*xyz.__chan, __COMMCTL_SET_TIMEOUT, xyzModem_CHAR_TIMEOUT);
xyz.len = 0;
xyz.crc_mode = true;
xyz.at_eof = false;
xyz.tx_ack = false;
xyz.mode = info->mode;
xyz.total_retries = 0;
xyz.total_SOH = 0;
xyz.total_STX = 0;
xyz.total_CAN = 0;
#ifdef USE_YMODEM_LENGTH
xyz.read_length = 0;
xyz.file_length = 0;
#endif
 
while (retries-- > 0) {
stat = xyzModem_get_hdr();
if (stat == 0) {
// Y-modem file information header
if (xyz.blk == 0) {
#ifdef USE_YMODEM_LENGTH
// skip filename
while (*xyz.bufp++);
// get the length
parse_num(xyz.bufp, &xyz.file_length, NULL, " ");
#endif
// The rest of the file name data block quietly discarded
xyz.tx_ack = true;
}
xyz.next_blk = 1;
xyz.len = 0;
return 0;
} else
if (stat == xyzModem_timeout) {
if (--crc_retries <= 0) xyz.crc_mode = false;
CYGACC_CALL_IF_DELAY_US(5*100000); // Extra delay for startup
CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
xyz.total_retries++;
ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__));
}
if (stat == xyzModem_cancel) {
break;
}
}
*err = stat;
ZM_DEBUG(zm_flush());
return -1;
}
 
int
xyzModem_stream_read(char *buf, int size, int *err)
{
int stat, total, len;
int retries;
 
total = 0;
stat = xyzModem_cancel;
// Try and get 'size' bytes into the buffer
while (!xyz.at_eof && (size > 0)) {
if (xyz.len == 0) {
retries = xyzModem_MAX_RETRIES;
while (retries-- > 0) {
stat = xyzModem_get_hdr();
if (stat == 0) {
if (xyz.blk == xyz.next_blk) {
xyz.tx_ack = true;
ZM_DEBUG(zm_dprintf("ACK block %d (%d)\n", xyz.blk, __LINE__));
xyz.next_blk = (xyz.next_blk + 1) & 0xFF;
// Data blocks can be padded with ^Z (EOF) characters
// This code tries to detect and remove them
#ifdef xyzModem_zmodem
if (xyz.mode != xyzModem_zmodem) {
#else
if (1) {
#endif
if ((xyz.bufp[xyz.len-1] == EOF) &&
(xyz.bufp[xyz.len-2] == EOF) &&
(xyz.bufp[xyz.len-3] == EOF)) {
while (xyz.len && (xyz.bufp[xyz.len-1] == EOF)) {
xyz.len--;
}
}
}
 
#ifdef USE_YMODEM_LENGTH
// See if accumulated length exceeds that of the file.
// If so, reduce size (i.e., cut out pad bytes)
// Only do this for Y-modem (and Z-modem should it ever
// be supported since it can fall back to Y-modem mode).
if (xyz.mode != xyzModem_xmodem && 0 != xyz.file_length) {
xyz.read_length += xyz.len;
if (xyz.read_length > xyz.file_length) {
xyz.len -= (xyz.read_length - xyz.file_length);
}
}
#endif
break;
} else if (xyz.blk == ((xyz.next_blk - 1) & 0xFF)) {
// Just re-ACK this so sender will get on with it
CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
continue; // Need new header
} else {
stat = xyzModem_sequence;
}
}
if (stat == xyzModem_cancel) {
break;
}
if (stat == xyzModem_eof) {
CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
ZM_DEBUG(zm_dprintf("ACK (%d)\n", __LINE__));
if (xyz.mode == xyzModem_ymodem) {
CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
xyz.total_retries++;
ZM_DEBUG(zm_dprintf("Reading Final Header\n"));
stat = xyzModem_get_hdr();
CYGACC_COMM_IF_PUTC(*xyz.__chan, ACK);
ZM_DEBUG(zm_dprintf("FINAL ACK (%d)\n", __LINE__));
}
xyz.at_eof = true;
break;
}
CYGACC_COMM_IF_PUTC(*xyz.__chan, (xyz.crc_mode ? 'C' : NAK));
xyz.total_retries++;
ZM_DEBUG(zm_dprintf("NAK (%d)\n", __LINE__));
}
if (stat < 0) {
*err = stat;
xyz.len = -1;
return total;
}
}
// Don't "read" data from the EOF protocol package
if (!xyz.at_eof) {
len = xyz.len;
if (size < len) len = size;
memcpy(buf, xyz.bufp, len);
size -= len;
buf += len;
total += len;
xyz.len -= len;
xyz.bufp += len;
}
}
return total;
}
 
void
xyzModem_stream_close(int *err)
{
diag_printf("xyzModem - %s mode, %d(SOH)/%d(STX)/%d(CAN) packets, %d retries\n",
xyz.crc_mode ? "CRC" : "Cksum",
xyz.total_SOH, xyz.total_STX, xyz.total_CAN,
xyz.total_retries);
// ZM_DEBUG(zm_flush());
}
 
// Need to be able to clean out the input buffer, so have to take the
// getc
void xyzModem_stream_terminate(bool abort, int (*getc)(void))
{
int c;
 
if (abort) {
ZM_DEBUG(zm_dprintf("!!!! TRANSFER ABORT !!!!\n"));
switch (xyz.mode) {
case xyzModem_xmodem:
case xyzModem_ymodem:
// The X/YMODEM Spec seems to suggest that multiple CAN followed by an equal
// number of Backspaces is a friendly way to get the other end to abort.
CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN);
CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN);
CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN);
CYGACC_COMM_IF_PUTC(*xyz.__chan,CAN);
CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP);
CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP);
CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP);
CYGACC_COMM_IF_PUTC(*xyz.__chan,BSP);
// Now consume the rest of what's waiting on the line.
ZM_DEBUG(zm_dprintf("Flushing serial line.\n"));
xyzModem_flush();
xyz.at_eof = true;
break;
#ifdef xyzModem_zmodem
case xyzModem_zmodem:
// Might support it some day I suppose.
#endif
break;
}
} else {
ZM_DEBUG(zm_dprintf("Engaging cleanup mode...\n"));
// Consume any trailing crap left in the inbuffer from
// previous recieved blocks. Since very few files are an exact multiple
// of the transfer block size, there will almost always be some gunk here.
// If we don't eat it now, RedBoot will think the user typed it.
ZM_DEBUG(zm_dprintf("Trailing gunk:\n"));
while ((c = (*getc)()) > -1) ;
ZM_DEBUG(zm_dprintf("\n"));
// Make a small delay to give terminal programs like minicom
// time to get control again after their file transfer program
// exits.
CYGACC_CALL_IF_DELAY_US((cyg_int32)250000);
}
}
 
char *
xyzModem_error(int err)
{
switch (err) {
case xyzModem_access:
return "Can't access file";
break;
case xyzModem_noZmodem:
return "Sorry, zModem not available yet";
break;
case xyzModem_timeout:
return "Timed out";
break;
case xyzModem_eof:
return "End of file";
break;
case xyzModem_cancel:
return "Cancelled";
break;
case xyzModem_frame:
return "Invalid framing";
break;
case xyzModem_cksum:
return "CRC/checksum error";
break;
case xyzModem_sequence:
return "Block sequence error";
break;
default:
return "Unknown error";
break;
}
}
 
//
// RedBoot interface
//
GETC_IO_FUNCS(xyzModem_io, xyzModem_stream_open, xyzModem_stream_close,
xyzModem_stream_terminate, xyzModem_stream_read, xyzModem_error);
RedBoot_load(xmodem, xyzModem_io, false, false, xyzModem_xmodem);
RedBoot_load(ymodem, xyzModem_io, false, false, xyzModem_ymodem);
/ticks.c
0,0 → 1,70
//==========================================================================
//
// ticks.c
//
// Stand-alone timer support for RedBoot
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include "redboot.h"
 
static unsigned long ticks = 0;
 
unsigned long
do_ms_tick(void)
{
CYGACC_CALL_IF_DELAY_US(1000); // Wait for 1ms
return ++ticks;
}
 
unsigned long
get_ms_ticks(void)
{
return ticks;
}
/load.c
0,0 → 1,767
//==========================================================================
//
// load.c
//
// RedBoot file/image loader
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas, tsmith
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
#include <xyzModem.h>
#include <elf.h>
#ifdef CYGPKG_REDBOOT_DISK
#include <fs/disk.h>
#endif
#ifdef CYGPKG_REDBOOT_NETWORKING
#include <net/tftp_support.h>
#ifdef CYGSEM_REDBOOT_NET_HTTP_DOWNLOAD
#include <net/http.h>
#endif
#endif
 
static char usage[] = "[-r] [-v] "
#ifdef CYGPKG_COMPRESS_ZLIB
"[-d] "
#endif
"[-h <host>] [-m <varies>] "
#if CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS > 1
"[-c <channel_number>] "
#endif
"\n [-b <base_address>] <file_name>";
 
// Exported CLI function
RedBoot_cmd("load",
"Load a file",
usage,
do_load
);
 
//
// Stream I/O support
//
 
// Table describing the various I/O methods
CYG_HAL_TABLE_BEGIN( __RedBoot_LOAD_TAB__, RedBoot_load );
CYG_HAL_TABLE_END( __RedBoot_LOAD_TAB_END__, RedBoot_load );
extern struct load_io_entry __RedBoot_LOAD_TAB__[], __RedBoot_LOAD_TAB_END__;
 
// Buffers, data used by redboot_getc
#define BUF_SIZE 256
struct {
getc_io_funcs_t *io;
int (*fun)(char *, int len, int *err);
unsigned char buf[BUF_SIZE];
unsigned char *bufp;
int avail, len, err;
int verbose, decompress, tick;
#ifdef CYGPKG_COMPRESS_ZLIB
int (*raw_fun)(char *, int len, int *err);
_pipe_t load_pipe;
unsigned char _buffer[CYGNUM_REDBOOT_LOAD_ZLIB_BUFFER];
#endif
} getc_info;
 
typedef int (*getc_t)(void);
 
//
// Read the next data byte from the stream.
// Returns:
// >= 0 - actual data
// -1 - error or EOF, status in getc_info.err
//
static int
redboot_getc(void)
{
static char spin[] = "|/-\\|-";
if (getc_info.avail < 0) {
return -1;
}
if (getc_info.avail == 0) {
if (getc_info.verbose) {
diag_printf("%c\b", spin[getc_info.tick++]);
if (getc_info.tick >= sizeof(spin)) {
getc_info.tick = 0;
}
}
if (getc_info.len < BUF_SIZE) {
// No more data available
if (getc_info.verbose) diag_printf("\n");
return -1;
}
getc_info.bufp = getc_info.buf;
getc_info.len = (*getc_info.fun)(getc_info.bufp, BUF_SIZE, &getc_info.err);
if ((getc_info.avail = getc_info.len) <= 0) {
if (getc_info.verbose) diag_printf("\n");
return -1;
}
}
getc_info.avail--;
return *getc_info.bufp++;
}
 
#ifdef CYGPKG_COMPRESS_ZLIB
//
// Called to fetch a new chunk of data and decompress it
//
static int
_decompress_stream(char *buf, int len, int *err)
{
_pipe_t* p = &getc_info.load_pipe;
int res, total;
 
total = 0;
while (len > 0) {
if (p->in_avail == 0) {
p->in_buf = &getc_info._buffer[0];
res = (*getc_info.raw_fun)(p->in_buf, CYGNUM_REDBOOT_LOAD_ZLIB_BUFFER,
&getc_info.err);
if ((p->in_avail = res) <= 0) {
// No more data
return total;
}
}
p->out_buf = buf;
p->out_size = 0;
p->out_max = len;
res = (*_dc_inflate)(p);
if (res != 0) {
*err = res;
return total;
}
len -= p->out_size;
buf += p->out_size;
total += p->out_size;
}
return total;
}
#endif
 
static int
redboot_getc_init(connection_info_t *info, getc_io_funcs_t *funcs,
int verbose, int decompress)
{
int res;
 
res = (funcs->open)(info, &getc_info.err);
if (res < 0) {
diag_printf("Can't load '%s': %s\n", info->filename, (funcs->error)(getc_info.err));
return res;
}
getc_info.io = funcs;
getc_info.fun = funcs->read;
getc_info.avail = 0;
getc_info.len = BUF_SIZE;
getc_info.verbose = verbose;
getc_info.decompress = decompress;
getc_info.tick = 0;
#ifdef CYGPKG_COMPRESS_ZLIB
if (decompress) {
_pipe_t* p = &getc_info.load_pipe;
p->out_buf = &getc_info.buf[0];
p->out_size = 0;
p->in_avail = 0;
getc_info.raw_fun = getc_info.fun;
getc_info.fun = _decompress_stream;
getc_info.err = (*_dc_init)(p);
if (0 != getc_info.err && p->msg) {
diag_printf("open decompression error: %s\n", p->msg);
}
}
#endif
return 0;
}
 
static void
redboot_getc_rewind(void)
{
getc_info.bufp = getc_info.buf;
getc_info.avail = getc_info.len;
}
 
static void
redboot_getc_terminate(bool abort)
{
if (getc_info.io->terminate) {
(getc_info.io->terminate)(abort, redboot_getc);
}
}
 
static void
redboot_getc_close(void)
{
(getc_info.io->close)(&getc_info.err);
#ifdef CYGPKG_COMPRESS_ZLIB
if (getc_info.decompress) {
_pipe_t* p = &getc_info.load_pipe;
int err = getc_info.err;
if (0 != err && p->msg) {
diag_printf("decompression error: %s\n", p->msg);
}
err = (*_dc_close)(p, getc_info.err);
}
#endif
}
 
#ifdef CYGSEM_REDBOOT_ELF
//
// Support function - used to read bytes into a buffer
// Returns the number of bytes read (stops short on errors)
//
static int
_read(int (*getc)(void), unsigned char *buf, int len)
{
int total = 0;
int ch;
while (len-- > 0) {
ch = (*getc)();
if (ch < 0) {
// EOF or error
break;
}
*buf++ = ch;
total++;
}
return total;
}
#endif
 
//
// Load an ELF [binary] image
//
static unsigned long
load_elf_image(getc_t getc, unsigned long base)
{
#ifdef CYGSEM_REDBOOT_ELF
Elf32_Ehdr ehdr;
#define MAX_PHDR 8
Elf32_Phdr phdr[MAX_PHDR];
unsigned long offset = 0;
int phx, len, ch;
unsigned char *addr;
unsigned long addr_offset = 0;
unsigned long highest_address = 0;
unsigned long lowest_address = 0xFFFFFFFF;
unsigned char *SHORT_DATA = "Short data reading ELF file";
 
// Read the header
if (_read(getc, (unsigned char *)&ehdr, sizeof(ehdr)) != sizeof(ehdr)) {
diag_printf("Can't read ELF header\n");
return 0;
}
offset += sizeof(ehdr);
#if 0 // DEBUG
diag_printf("Type: %d, Machine: %d, Version: %d, Entry: %p, PHoff: %p/%d/%d, SHoff: %p/%d/%d\n",
ehdr.e_type, ehdr.e_machine, ehdr.e_version, ehdr.e_entry,
ehdr.e_phoff, ehdr.e_phentsize, ehdr.e_phnum,
ehdr.e_shoff, ehdr.e_shentsize, ehdr.e_shnum);
#endif
if (ehdr.e_type != ET_EXEC) {
diag_printf("Only absolute ELF images supported\n");
return 0;
}
if (ehdr.e_phnum > MAX_PHDR) {
diag_printf("Too many program headers\n");
return 0;
}
while (offset < ehdr.e_phoff) {
if ((*getc)() < 0) {
diag_printf(SHORT_DATA);
return 0;
}
offset++;
}
for (phx = 0; phx < ehdr.e_phnum; phx++) {
if (_read(getc, (unsigned char *)&phdr[phx], sizeof(phdr[0])) != sizeof(phdr[0])) {
diag_printf("Can't read ELF program header\n");
return 0;
}
#if 0 // DEBUG
diag_printf("Program header: type: %d, off: %p, va: %p, pa: %p, len: %d/%d, flags: %d\n",
phdr[phx].p_type, phdr[phx].p_offset, phdr[phx].p_vaddr, phdr[phx].p_paddr,
phdr[phx].p_filesz, phdr[phx].p_memsz, phdr[phx].p_flags);
#endif
offset += sizeof(phdr[0]);
}
if (base) {
// Set address offset based on lowest address in file.
addr_offset = 0xFFFFFFFF;
for (phx = 0; phx < ehdr.e_phnum; phx++) {
if (phdr[phx].p_vaddr < addr_offset) {
addr_offset = phdr[phx].p_vaddr;
}
}
addr_offset = (unsigned long)base - addr_offset;
} else {
addr_offset = 0;
}
for (phx = 0; phx < ehdr.e_phnum; phx++) {
if (phdr[phx].p_type == PT_LOAD) {
// Loadable segment
addr = (unsigned char *)phdr[phx].p_vaddr;
len = phdr[phx].p_filesz;
if ((unsigned long)addr < lowest_address) {
lowest_address = (unsigned long)addr;
}
addr += addr_offset;
if (offset > phdr[phx].p_offset) {
if ((phdr[phx].p_offset + len) < offset) {
diag_printf("Can't load ELF file - program headers out of order\n");
return 0;
}
addr += offset - phdr[phx].p_offset;
} else {
while (offset < phdr[phx].p_offset) {
if ((*getc)() < 0) {
diag_printf(SHORT_DATA);
return 0;
}
offset++;
}
}
// Copy data into memory
while (len-- > 0) {
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
if ((addr < user_ram_start) || (addr > user_ram_end)) {
redboot_getc_terminate(true);
diag_printf("*** Abort! Attempt to load ELF data to address: %p which is not in RAM\n", (void*)addr);
return 0;
}
#endif
if ((ch = (*getc)()) < 0) {
diag_printf(SHORT_DATA);
return 0;
}
*addr++ = ch;
offset++;
if ((unsigned long)(addr-addr_offset) > highest_address) {
highest_address = (unsigned long)(addr - addr_offset);
}
}
}
}
// Save load base/top and entry
if (base) {
load_address = base;
load_address_end = base + (highest_address - lowest_address);
entry_address = base + (ehdr.e_entry - lowest_address);
} else {
load_address = lowest_address;
load_address_end = highest_address;
entry_address = ehdr.e_entry;
}
 
redboot_getc_terminate(false);
if (addr_offset) diag_printf("Address offset = %p\n", (void *)addr_offset);
diag_printf("Entry point: %p, address range: %p-%p\n",
(void*)entry_address, (void *)load_address, (void *)load_address_end);
return 1;
#else // CYGSEM_REDBOOT_ELF
diag_printf("Loading ELF images not supported\n");
return 0;
#endif // CYGSEM_REDBOOT_ELF
}
 
 
//
// Scan a string of hex bytes and update the checksum
//
static long
_hex2(int (*getc)(void), int len, long *sum)
{
int val, byte;
char c1, c2;
 
val = 0;
while (len-- > 0) {
c1 = (*getc)();
c2 = (*getc)();
if (_is_hex(c1) && _is_hex(c2)) {
val <<= 8;
byte = (_from_hex(c1)<<4) | _from_hex(c2);
val |= byte;
if (sum) {
*sum += byte;
}
} else {
return (-1);
}
}
return (val);
}
 
//
// Process a set of S-records, loading the contents into memory.
// Note: if a "base" value is provided, the data will be relocated
// relative to that location. Of course, this can only work for
// the first section of the data, so if there are non-contiguous
// pieces of data, they will end up relocated in the same fashion.
// Because of this, "base" probably only makes sense for a set of
// data which has only one section, e.g. a ROM image.
//
static unsigned long
load_srec_image(getc_t getc, unsigned long base)
{
int c;
long offset = 0, count, sum, val, cksum;
unsigned char *addr, *base_addr;
char type;
bool first_addr = true;
unsigned long addr_offset = 0;
unsigned long highest_address = 0;
unsigned long lowest_address = 0xFFFFFFFF;
 
while ((c = (*getc)()) > 0) {
// Start of line
if (c != 'S') {
redboot_getc_terminate(true);
diag_printf("Invalid S-record at offset %p, input: %c\n",
(void *)offset, c);
return 0;
}
type = (*getc)();
offset += 2;
sum = 0;
if ((count = _hex2(getc, 1, &sum)) < 0) {
redboot_getc_terminate(true);
diag_printf("Bad S-record count at offset %p\n", (void *)offset);
return 0;
}
offset += 1;
switch (type) {
case '0':
break;
case '1':
case '2':
case '3':
base_addr = addr = (unsigned char *)_hex2(getc, (type-'1'+2), &sum);
offset += (type-'1'+2);
if (first_addr) {
if (base) {
addr_offset = (unsigned long)base - (unsigned long)addr;
} else {
addr_offset = 0;
}
first_addr = false;
}
addr += addr_offset;
if ((unsigned long)(addr-addr_offset) < lowest_address) {
lowest_address = (unsigned long)(addr - addr_offset);
}
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
if ((addr < user_ram_start) || (addr > user_ram_end)) {
// Only if there is no need to stop the download before printing
// output can we ask confirmation questions.
redboot_getc_terminate(true);
diag_printf("*** Abort! Attempt to load S-record to address: %p, which is not in RAM\n",(void*)addr);
return 0;
}
#endif
count -= ((type-'1'+2)+1);
offset += count;
while (count-- > 0) {
val = _hex2(getc, 1, &sum);
*addr++ = val;
}
cksum = _hex2(getc, 1, 0);
offset += 1;
sum = sum & 0xFF;
cksum = (~cksum & 0xFF);
if (cksum != sum) {
redboot_getc_terminate(true);
diag_printf("*** Warning! Checksum failure - Addr: %lx, %02lX <> %02lX\n",
(unsigned long)base_addr, sum, cksum);
return 0;
}
if ((unsigned long)(addr-addr_offset) > highest_address) {
highest_address = (unsigned long)(addr - addr_offset);
}
break;
case '7':
case '8':
case '9':
addr = (unsigned char *)_hex2(getc, ('9'-type+2), &sum);
offset += ('9'-type+2);
// Save load base/top, entry address
if (base) {
load_address = base;
load_address_end = base + (highest_address - lowest_address);
entry_address = (unsigned long)(base + (addr - lowest_address));
} else {
load_address = lowest_address;
load_address_end = highest_address;
entry_address = (unsigned long)addr;
}
redboot_getc_terminate(false);
if (addr_offset) diag_printf("Address offset = %p\n", (void *)addr_offset);
diag_printf("Entry point: %p, address range: %p-%p\n",
(void*)entry_address, (void *)load_address, (void *)load_address_end);
 
return load_address_end;
default:
redboot_getc_terminate(true);
diag_printf("Invalid S-record at offset 0x%lx, type: %x\n",
(unsigned long)offset, type);
return 0;
}
while ((c = (*getc)()) != '\n') offset++;
}
return 0;
}
 
//
// 'load' CLI command processing
// -b - specify a load [base] address
// -m - specify an I/O stream/method
// -c - Alternate serial I/O channel
#ifdef CYGPKG_COMPRESS_ZLIB
// -d - Decompress data [packed via 'zlib']
#endif
//
void
do_load(int argc, char *argv[])
{
int res, num_options;
int i, err;
bool verbose, raw;
bool base_addr_set, mode_str_set;
char *mode_str;
#ifdef CYGPKG_REDBOOT_NETWORKING
struct sockaddr_in host;
bool hostname_set;
char *hostname;
#endif
bool decompress = false;
int chan = -1;
#if CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS > 1
bool chan_set;
#endif
unsigned long base = 0;
unsigned long end = 0;
char type[4];
char *filename = 0;
struct option_info opts[7];
connection_info_t info;
getc_io_funcs_t *io;
struct load_io_entry *io_tab;
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
bool spillover_ok = false;
#endif
 
#ifdef CYGPKG_REDBOOT_NETWORKING
memset((char *)&host, 0, sizeof(host));
host.sin_len = sizeof(host);
host.sin_family = AF_INET;
host.sin_addr = my_bootp_info.bp_siaddr;
host.sin_port = 0;
#endif
 
init_opts(&opts[0], 'v', false, OPTION_ARG_TYPE_FLG,
(void **)&verbose, 0, "verbose");
init_opts(&opts[1], 'r', false, OPTION_ARG_TYPE_FLG,
(void **)&raw, 0, "load raw data");
init_opts(&opts[2], 'b', true, OPTION_ARG_TYPE_NUM,
(void **)&base, (bool *)&base_addr_set, "load address");
init_opts(&opts[3], 'm', true, OPTION_ARG_TYPE_STR,
(void **)&mode_str, (bool *)&mode_str_set, "download mode (TFTP, xyzMODEM, or disk)");
num_options = 4;
#if CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS > 1
init_opts(&opts[num_options], 'c', true, OPTION_ARG_TYPE_NUM,
(void **)&chan, (bool *)&chan_set, "I/O channel");
num_options++;
#endif
#ifdef CYGPKG_REDBOOT_NETWORKING
init_opts(&opts[num_options], 'h', true, OPTION_ARG_TYPE_STR,
(void **)&hostname, (bool *)&hostname_set, "host name or IP address");
num_options++;
#endif
#ifdef CYGPKG_COMPRESS_ZLIB
init_opts(&opts[num_options], 'd', false, OPTION_ARG_TYPE_FLG,
(void **)&decompress, 0, "decompress");
num_options++;
#endif
 
if (!scan_opts(argc, argv, 1, opts, num_options,
(void *)&filename, OPTION_ARG_TYPE_STR, "file name")) {
return;
}
#ifdef CYGPKG_REDBOOT_NETWORKING
if (hostname_set) {
ip_route_t rt;
if (!_gethostbyname(hostname, (in_addr_t *)&host)) {
diag_printf("Invalid host: %s\n", hostname);
return;
}
/* check that the host can be accessed */
if (__arp_lookup((ip_addr_t *)&host.sin_addr, &rt) < 0) {
diag_printf("Unable to reach host %s (%s)\n",
hostname, inet_ntoa((in_addr_t *)&host));
return;
}
}
#endif
if (chan >= CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS) {
diag_printf("Invalid I/O channel: %d\n", chan);
return;
}
if (mode_str_set) {
io = (getc_io_funcs_t *)NULL;
for (io_tab = __RedBoot_LOAD_TAB__;
io_tab != &__RedBoot_LOAD_TAB_END__; io_tab++) {
if (strncasecmp(&mode_str[0], io_tab->name, strlen(&mode_str[0])) == 0) {
io = io_tab->funcs;
break;
}
}
if (!io) {
diag_printf("Invalid 'mode': %s. Valid modes are:", mode_str);
for (io_tab = __RedBoot_LOAD_TAB__;
io_tab != &__RedBoot_LOAD_TAB_END__; io_tab++) {
diag_printf(" %s", io_tab->name);
}
diag_printf("\n");
}
if (!io) {
return;
}
verbose &= io_tab->can_verbose;
if (io_tab->need_filename && !filename) {
diag_printf("File name required\n");
diag_printf("usage: load %s\n", usage);
return;
}
} else {
io_tab = (struct load_io_entry *)NULL; // Default
#ifdef CYGPKG_REDBOOT_NETWORKING
io = &tftp_io;
#else
io = &xyzModem_io;
verbose = false;
#endif
}
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
if (base_addr_set &&
((base < (unsigned long)user_ram_start) ||
(base > (unsigned long)user_ram_end))) {
if (!verify_action("Specified address (%p) is not believed to be in RAM", (void*)base))
return;
spillover_ok = true;
}
#endif
if (raw && !base_addr_set) {
diag_printf("Raw load requires a memory address\n");
return;
}
info.filename = filename;
info.chan = chan;
info.mode = io_tab ? io_tab->mode : 0;
#ifdef CYGPKG_REDBOOT_NETWORKING
info.server = &host;
#endif
res = redboot_getc_init(&info, io, verbose, decompress);
if (res < 0) {
return;
}
 
// Stream open, process the data
if (raw) {
unsigned char *mp = (unsigned char *)base;
err = 0;
while ((res = redboot_getc()) >= 0) {
#ifdef CYGSEM_REDBOOT_VALIDATE_USER_RAM_LOADS
if (mp >= user_ram_end && !spillover_ok) {
// Only if there is no need to stop the download
// before printing output can we ask confirmation
// questions.
redboot_getc_terminate(true);
diag_printf("*** Abort! RAW data spills over limit of user RAM at %p\n",(void*)mp);
err = -1;
break;
}
#endif
*mp++ = res;
}
end = (unsigned long) mp;
 
// Save load base/top
load_address = base;
load_address_end = end;
entry_address = base; // best guess
 
redboot_getc_terminate(false);
if (0 == err)
diag_printf("Raw file loaded %p-%p, assumed entry at %p\n",
(void *)base, (void *)(end - 1), (void*)base);
} else {
// Read initial header - to determine file [image] type
for (i = 0; i < sizeof(type); i++) {
if ((res = redboot_getc()) < 0) {
err = getc_info.err;
break;
}
type[i] = res;
}
if (res >= 0) {
redboot_getc_rewind(); // Restore header to stream
// Treat data as some sort of executable image
if (strncmp(&type[1], "ELF", 3) == 0) {
end = load_elf_image(redboot_getc, base);
} else if ((type[0] == 'S') &&
((type[1] >= '0') && (type[1] <= '9'))) {
end = load_srec_image(redboot_getc, base);
} else {
redboot_getc_terminate(true);
diag_printf("Unrecognized image type: 0x%lx\n", *(unsigned long *)type);
}
}
}
 
redboot_getc_close(); // Clean up
return;
}
/xyzModem.h
0,0 → 1,84
//==========================================================================
//
// xyzModem.h
//
// RedBoot stream handler for xyzModem protocol
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#ifndef _XYZMODEM_H_
#define _XYZMODEM_H_
 
#define xyzModem_xmodem 1
#define xyzModem_ymodem 2
// Don't define this until the protocol support is in place
//#define xyzModem_zmodem 3
 
#define xyzModem_access -1
#define xyzModem_noZmodem -2
#define xyzModem_timeout -3
#define xyzModem_eof -4
#define xyzModem_cancel -5
#define xyzModem_frame -6
#define xyzModem_cksum -7
#define xyzModem_sequence -8
 
#define xyzModem_close 1
#define xyzModem_abort 2
 
int xyzModem_stream_open(connection_info_t *info, int *err);
void xyzModem_stream_close(int *err);
void xyzModem_stream_terminate(int method, int (*getc)(void));
int xyzModem_stream_read(char *buf, int size, int *err);
char *xyzModem_error(int err);
 
extern getc_io_funcs_t xyzModem_io;
 
#endif // _XYZMODEM_H_
/cksum.c
0,0 → 1,96
//==========================================================================
//
// cksum.c
//
// RedBoot POSIX CRC calculation/command
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2001-01-31
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
 
// Compute a CRC, using the POSIX 1003 definition
 
RedBoot_cmd("cksum",
"Compute a 32bit checksum [POSIX algorithm] for a range of memory",
"-b <location> -l <length>",
do_cksum
);
 
void
do_cksum(int argc, char *argv[])
{
struct option_info opts[2];
unsigned long base, len, crc;
bool base_set, len_set;
 
init_opts(&opts[0], 'b', true, OPTION_ARG_TYPE_NUM,
(void **)&base, (bool *)&base_set, "base address");
init_opts(&opts[1], 'l', true, OPTION_ARG_TYPE_NUM,
(void **)&len, (bool *)&len_set, "length");
if (!scan_opts(argc, argv, 1, opts, 2, 0, 0, "")) {
return;
}
if (!base_set || !len_set) {
if (load_address >= (CYG_ADDRESS)ram_start &&
load_address_end < (CYG_ADDRESS)ram_end &&
load_address < load_address_end) {
base = load_address;
len = load_address_end - load_address;
diag_printf("Computing cksum for area %p-%p\n",
base, load_address_end);
} else {
diag_printf("usage: cksum -b <addr> -l <length>\n");
return;
}
}
crc = cyg_posix_crc32((unsigned char *)base, len);
diag_printf("POSIX cksum = %lu %lu (0x%08lx 0x%08lx)\n", crc, len, crc, len);
}
 
/main.c
0,0 → 1,626
//==========================================================================
//
// main.c
//
// RedBoot main routine
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas, tkoeller
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#define DEFINE_VARS
#include <redboot.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/hal_if.h>
#include <cyg/hal/hal_cache.h>
#include CYGHWR_MEMORY_LAYOUT_H
 
#include <cyg/hal/hal_tables.h>
 
#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
#ifdef CYGBLD_HAL_PLATFORM_STUB_H
#include CYGBLD_HAL_PLATFORM_STUB_H
#else
#include <cyg/hal/plf_stub.h>
#endif
// GDB interfaces
extern void breakpoint(void);
#endif
 
// Builtin Self Test (BIST)
externC void bist(void);
 
// Return path for code run from a go command
static void return_to_redboot(int status);
 
 
// CLI command processing (defined in this file)
RedBoot_cmd("version",
"Display RedBoot version information",
"",
do_version
);
RedBoot_cmd("help",
"Help about help?",
"[<topic>]",
do_help
);
RedBoot_cmd("go",
"Execute code at a location",
"[-w <timeout>] [entry]",
do_go
);
#ifdef HAL_PLATFORM_RESET
RedBoot_cmd("reset",
"Reset the system",
"",
do_reset
);
#endif
#ifdef CYGSEM_REDBOOT_VARIABLE_BAUD_RATE
RedBoot_cmd("baudrate",
"Set/Query the system console baud rate",
"[-b <rate>]",
do_baud_rate
);
#endif
 
// Define table boundaries
CYG_HAL_TABLE_BEGIN( __RedBoot_INIT_TAB__, RedBoot_inits );
CYG_HAL_TABLE_END( __RedBoot_INIT_TAB_END__, RedBoot_inits );
extern struct init_tab_entry __RedBoot_INIT_TAB__[], __RedBoot_INIT_TAB_END__;
 
CYG_HAL_TABLE_BEGIN( __RedBoot_CMD_TAB__, RedBoot_commands );
CYG_HAL_TABLE_END( __RedBoot_CMD_TAB_END__, RedBoot_commands );
extern struct cmd __RedBoot_CMD_TAB__[], __RedBoot_CMD_TAB_END__;
 
CYG_HAL_TABLE_BEGIN( __RedBoot_IDLE_TAB__, RedBoot_idle );
CYG_HAL_TABLE_END( __RedBoot_IDLE_TAB_END__, RedBoot_idle );
extern struct idle_tab_entry __RedBoot_IDLE_TAB__[], __RedBoot_IDLE_TAB_END__;
 
#ifdef HAL_ARCH_PROGRAM_NEW_STACK
extern void HAL_ARCH_PROGRAM_NEW_STACK(void *fun);
#endif
 
 
void
do_version(int argc, char *argv[])
{
#ifdef CYGPKG_IO_FLASH
externC void _flash_info(void);
#endif
char *version = CYGACC_CALL_IF_MONITOR_VERSION();
 
diag_printf(version);
#ifdef HAL_PLATFORM_CPU
diag_printf("Platform: %s (%s) %s\n", HAL_PLATFORM_BOARD, HAL_PLATFORM_CPU, HAL_PLATFORM_EXTRA);
#endif
diag_printf("Copyright (C) 2000, 2001, 2002, Red Hat, Inc.\n\n");
diag_printf("RAM: %p-%p, %p-%p available\n",
(void*)ram_start, (void*)ram_end,
(void*)user_ram_start, (void *)user_ram_end);
#ifdef CYGPKG_IO_FLASH
_flash_info();
#endif
}
 
void
do_idle(bool is_idle)
{
struct idle_tab_entry *idle_entry;
 
for (idle_entry = __RedBoot_IDLE_TAB__;
idle_entry != &__RedBoot_IDLE_TAB_END__; idle_entry++) {
(*idle_entry->fun)(is_idle);
}
}
 
// Wrapper used by diag_printf()
static void
_mon_write_char(char c, void **param)
{
if (c == '\n') {
mon_write_char('\r');
}
mon_write_char(c);
}
 
//
// This is the main entry point for RedBoot
//
void
cyg_start(void)
{
int res = 0;
bool prompt = true;
static char line[CYGPKG_REDBOOT_MAX_CMD_LINE];
char *command;
struct cmd *cmd;
int cur;
struct init_tab_entry *init_entry;
extern char RedBoot_version[];
 
// Export version information
CYGACC_CALL_IF_MONITOR_VERSION_SET(RedBoot_version);
 
CYGACC_CALL_IF_MONITOR_RETURN_SET(return_to_redboot);
 
// Make sure the channels are properly initialized.
diag_init_putc(_mon_write_char);
hal_if_diag_init();
 
// Force console to output raw text - but remember the old setting
// so it can be restored if interaction with a debugger is
// required.
cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_HAL_VIRTUAL_VECTOR_DEBUG_CHANNEL);
#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
console_selected = false;
#endif
console_echo = true;
CYGACC_CALL_IF_DELAY_US((cyg_int32)2*100000);
 
ram_start = (unsigned char *)CYGMEM_REGION_ram;
ram_end = (unsigned char *)(CYGMEM_REGION_ram+CYGMEM_REGION_ram_SIZE);
#ifdef HAL_MEM_REAL_REGION_TOP
{
unsigned char *ram_end_tmp = ram_end;
ram_end = HAL_MEM_REAL_REGION_TOP( ram_end_tmp );
}
#endif
#ifdef CYGMEM_SECTION_heap1
workspace_start = (unsigned char *)CYGMEM_SECTION_heap1;
workspace_end = (unsigned char *)(CYGMEM_SECTION_heap1+CYGMEM_SECTION_heap1_SIZE);
workspace_size = CYGMEM_SECTION_heap1_SIZE;
#else
workspace_start = (unsigned char *)CYGMEM_REGION_ram;
workspace_end = (unsigned char *)(CYGMEM_REGION_ram+CYGMEM_REGION_ram_SIZE);
workspace_size = CYGMEM_REGION_ram_SIZE;
#endif
 
if ( ram_end < workspace_end ) {
// when *less* SDRAM is installed than the possible maximum,
// but the heap1 region remains greater...
workspace_end = ram_end;
workspace_size = workspace_end - workspace_start;
}
 
bist();
 
#ifdef CYGOPT_REDBOOT_FIS_ZLIB_COMMON_BUFFER
fis_zlib_common_buffer =
workspace_end -= CYGNUM_REDBOOT_FIS_ZLIB_COMMON_BUFFER_SIZE;
#endif
 
for (init_entry = __RedBoot_INIT_TAB__; init_entry != &__RedBoot_INIT_TAB_END__; init_entry++) {
(*init_entry->fun)();
}
 
user_ram_start = workspace_start;
user_ram_end = workspace_end;
 
do_version(0,0);
 
#ifdef CYGFUN_REDBOOT_BOOT_SCRIPT
# ifdef CYGDAT_REDBOOT_DEFAULT_BOOT_SCRIPT
if (!script) {
script = CYGDAT_REDBOOT_DEFAULT_BOOT_SCRIPT;
# ifndef CYGSEM_REDBOOT_FLASH_CONFIG
script_timeout = CYGNUM_REDBOOT_BOOT_SCRIPT_DEFAULT_TIMEOUT;
# endif
}
# endif
if (script) {
// Give the guy a chance to abort any boot script
unsigned char *hold_script = script;
int script_timeout_ms = script_timeout * CYGNUM_REDBOOT_BOOT_SCRIPT_TIMEOUT_RESOLUTION;
diag_printf("== Executing boot script in %d.%03d seconds - enter ^C to abort\n",
script_timeout_ms/1000, script_timeout_ms%1000);
script = (unsigned char *)0;
res = _GETS_CTRLC; // Treat 0 timeout as ^C
while (script_timeout_ms >= CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT) {
res = _rb_gets(line, sizeof(line), CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT);
if (res >= _GETS_OK) {
diag_printf("== Executing boot script in %d.%03d seconds - enter ^C to abort\n",
script_timeout_ms/1000, script_timeout_ms%1000);
continue; // Ignore anything but ^C
}
if (res != _GETS_TIMEOUT) break;
script_timeout_ms -= CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT;
}
if (res == _GETS_CTRLC) {
script = (unsigned char *)0; // Disable script
} else {
script = hold_script; // Re-enable script
}
}
#endif
 
while (true) {
if (prompt) {
diag_printf("RedBoot> ");
prompt = false;
}
#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
cmd_history = true; // Enable history collection
#endif
res = _rb_gets(line, sizeof(line), CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT);
#if CYGNUM_REDBOOT_CMD_LINE_EDITING != 0
cmd_history = false; // Enable history collection
#endif
if (res == _GETS_TIMEOUT) {
// No input arrived
} else {
#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
if (res == _GETS_GDB) {
int dbgchan;
hal_virtual_comm_table_t *__chan;
int i;
// Special case of '$' - need to start GDB protocol
gdb_active = true;
// Mask interrupts on all channels
for (i = 0; i < CYGNUM_HAL_VIRTUAL_VECTOR_NUM_CHANNELS; i++) {
CYGACC_CALL_IF_SET_CONSOLE_COMM(i);
__chan = CYGACC_CALL_IF_CONSOLE_PROCS();
CYGACC_COMM_IF_CONTROL( *__chan, __COMMCTL_IRQ_DISABLE );
}
CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);
#ifdef HAL_ARCH_PROGRAM_NEW_STACK
HAL_ARCH_PROGRAM_NEW_STACK(breakpoint);
#else
breakpoint(); // Get GDB stubs started, with a proper environment, etc.
#endif
dbgchan = CYGACC_CALL_IF_SET_DEBUG_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
CYGACC_CALL_IF_SET_CONSOLE_COMM(dbgchan);
} else
#endif // CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
{
expand_aliases(line, sizeof(line));
command = (char *)&line;
if ((*command == '#') || (*command == '=')) {
// Special cases
if (*command == '=') {
// Print line on console
diag_printf("%s\n", &line[2]);
}
} else {
while (strlen(command) > 0) {
if ((cmd = parse(&command, &argc, &argv[0])) != (struct cmd *)0) {
(cmd->fun)(argc, argv);
} else {
diag_printf("** Error: Illegal command: \"%s\"\n", argv[0]);
}
}
}
prompt = true;
}
}
}
}
 
void
show_help(struct cmd *cmd, struct cmd *cmd_end, char *which, char *pre)
{
bool show;
int len = 0;
 
if (which) {
len = strlen(which);
}
while (cmd != cmd_end) {
show = true;
if (which && (strncasecmp(which, cmd->str, len) != 0)) {
show = false;
}
if (show) {
diag_printf("%s\n %s %s %s\n", cmd->help, pre, cmd->str, cmd->usage);
if ((cmd->sub_cmds != (struct cmd *)0) && (which != (char *)0)) {
show_help(cmd->sub_cmds, cmd->sub_cmds_end, 0, cmd->str);
}
}
cmd++;
}
}
 
void
do_help(int argc, char *argv[])
{
struct cmd *cmd;
char *which = (char *)0;
 
if (!scan_opts(argc, argv, 1, 0, 0, (void **)&which, OPTION_ARG_TYPE_STR, "<topic>")) {
diag_printf("Invalid argument\n");
return;
}
cmd = __RedBoot_CMD_TAB__;
show_help(cmd, &__RedBoot_CMD_TAB_END__, which, "");
return;
}
 
static void * go_saved_context;
static int go_return_status;
 
static void
go_trampoline(unsigned long entry)
{
typedef void code_fun(void);
code_fun *fun = (code_fun *)entry;
unsigned long oldints;
 
HAL_DISABLE_INTERRUPTS(oldints);
 
#ifdef HAL_ARCH_PROGRAM_NEW_STACK
HAL_ARCH_PROGRAM_NEW_STACK(fun);
#else
(*fun)();
#endif
}
 
static void
return_to_redboot(int status)
{
CYGARC_HAL_SAVE_GP();
 
go_return_status = status;
HAL_THREAD_LOAD_CONTEXT(&go_saved_context);
// never returns
 
// need this to balance above CYGARC_HAL_SAVE_GP on
// some platforms. It will never run, though.
CYGARC_HAL_RESTORE_GP();
}
 
void
do_go(int argc, char *argv[])
{
unsigned long entry;
unsigned long oldints;
bool wait_time_set;
int wait_time, res;
bool cache_enabled = false;
struct option_info opts[2];
char line[8];
hal_virtual_comm_table_t *__chan = CYGACC_CALL_IF_CONSOLE_PROCS();
 
entry = entry_address; // Default from last 'load' operation
init_opts(&opts[0], 'w', true, OPTION_ARG_TYPE_NUM,
(void **)&wait_time, (bool *)&wait_time_set, "wait timeout");
init_opts(&opts[1], 'c', false, OPTION_ARG_TYPE_FLG,
(void **)&cache_enabled, (bool *)0, "go with caches enabled");
if (!scan_opts(argc, argv, 1, opts, 2, (void *)&entry, OPTION_ARG_TYPE_NUM, "starting address"))
{
return;
}
if (wait_time_set) {
int script_timeout_ms = wait_time * 1000;
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
unsigned char *hold_script = script;
script = (unsigned char *)0;
#endif
diag_printf("About to start execution at %p - abort with ^C within %d seconds\n",
(void *)entry, wait_time);
while (script_timeout_ms >= CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT) {
res = _rb_gets(line, sizeof(line), CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT);
if (res == _GETS_CTRLC) {
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
script = hold_script; // Re-enable script
#endif
return;
}
script_timeout_ms -= CYGNUM_REDBOOT_CLI_IDLE_TIMEOUT;
}
}
CYGACC_COMM_IF_CONTROL(*__chan, __COMMCTL_ENABLE_LINE_FLUSH);
 
HAL_DISABLE_INTERRUPTS(oldints);
HAL_DCACHE_SYNC();
if (!cache_enabled) {
HAL_ICACHE_DISABLE();
HAL_DCACHE_DISABLE();
HAL_DCACHE_SYNC();
}
HAL_ICACHE_INVALIDATE_ALL();
HAL_DCACHE_INVALIDATE_ALL();
 
// set up a temporary context that will take us to the trampoline
HAL_THREAD_INIT_CONTEXT((CYG_ADDRESS)workspace_end, entry, go_trampoline, 0);
 
// switch context to trampoline
HAL_THREAD_SWITCH_CONTEXT(&go_saved_context, &workspace_end);
 
// we get back here by way of return_to_redboot()
 
// undo the changes we made before switching context
if (!cache_enabled) {
HAL_ICACHE_ENABLE();
HAL_DCACHE_ENABLE();
}
 
CYGACC_COMM_IF_CONTROL(*__chan, __COMMCTL_DISABLE_LINE_FLUSH);
 
HAL_RESTORE_INTERRUPTS(oldints);
 
diag_printf("\nProgram completed with status %d\n", go_return_status);
}
 
#ifdef HAL_PLATFORM_RESET
void
do_reset(int argc, char *argv[])
{
diag_printf("... Resetting.");
CYGACC_CALL_IF_DELAY_US(2*100000);
diag_printf("\n");
CYGACC_CALL_IF_RESET();
diag_printf("!! oops, RESET not working on this platform\n");
}
#endif
 
#ifdef CYGSEM_REDBOOT_VARIABLE_BAUD_RATE
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
#include <flash_config.h>
#endif
 
static int
set_comm_baud_rate(hal_virtual_comm_table_t *chan, int rate)
{
int current_rate;
 
current_rate = CYGACC_COMM_IF_CONTROL(*chan, __COMMCTL_SETBAUD, rate);
if (rate != current_rate)
return CYGACC_COMM_IF_CONTROL(*chan, __COMMCTL_SETBAUD, rate);
 
return 0;
}
 
int
set_console_baud_rate(int rate)
{
int ret;
#ifdef CYGPKG_REDBOOT_ANY_CONSOLE
if (!console_selected) {
int cur = CYGACC_CALL_IF_SET_CONSOLE_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
int i;
// Set baud for all channels
for (i = 0; i < CYGNUM_HAL_VIRTUAL_VECTOR_COMM_CHANNELS; i++) {
CYGACC_CALL_IF_SET_CONSOLE_COMM(i);
ret = set_comm_baud_rate(CYGACC_CALL_IF_CONSOLE_PROCS(), rate);
if (ret < 0)
break;
}
CYGACC_CALL_IF_SET_CONSOLE_COMM(cur);
} else
#endif
ret = set_comm_baud_rate(CYGACC_CALL_IF_CONSOLE_PROCS(), rate);
 
if (ret < 0)
diag_printf("Setting console baud rate to %d failed\n", rate);
 
return ret;
}
 
static void
_sleep(int ms)
{
int i;
for (i = 0; i < ms; i++) {
CYGACC_CALL_IF_DELAY_US((cyg_int32)1000);
}
}
 
void
do_baud_rate(int argc, char *argv[])
{
int new_rate, ret, old_rate;
bool new_rate_set;
hal_virtual_comm_table_t *__chan;
struct option_info opts[1];
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
struct config_option opt;
#endif
 
init_opts(&opts[0], 'b', true, OPTION_ARG_TYPE_NUM,
(void **)&new_rate, (bool *)&new_rate_set, "new baud rate");
if (!scan_opts(argc, argv, 1, opts, 1, 0, 0, "")) {
return;
}
__chan = CYGACC_CALL_IF_CONSOLE_PROCS();
if (new_rate_set) {
diag_printf("Baud rate will be changed to %d - update your settings\n", new_rate);
_sleep(500); // Give serial time to flush
old_rate = CYGACC_COMM_IF_CONTROL(*__chan, __COMMCTL_GETBAUD);
ret = set_console_baud_rate(new_rate);
if (ret < 0) {
if (old_rate > 0) {
// Try to restore
set_console_baud_rate(old_rate);
_sleep(500); // Give serial time to flush
diag_printf("\nret = %d\n", ret);
}
return; // Couldn't set the desired rate
}
old_rate = ret;
// Make sure this new rate works or back off to previous value
// Sleep for a few seconds, then prompt to see if it works
_sleep(3000); // Give serial time to flush
if (!verify_action_with_timeout(5000, "Baud rate changed to %d", new_rate)) {
_sleep(500); // Give serial time to flush
set_console_baud_rate(old_rate);
_sleep(500); // Give serial time to flush
return;
}
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
opt.type = CONFIG_INT;
opt.enable = (char *)0;
opt.enable_sense = 1;
opt.key = "console_baud_rate";
opt.dflt = new_rate;
flash_add_config(&opt, true);
#endif
} else {
ret = CYGACC_COMM_IF_CONTROL(*__chan, __COMMCTL_GETBAUD);
diag_printf("Baud rate = ");
if (ret <= 0) {
diag_printf("unknown\n");
} else {
diag_printf("%d\n", ret);
}
}
}
#endif
 
//
// [Null] Builtin [Power On] Self Test
//
void bist(void) CYGBLD_ATTRIB_WEAK;
 
void
bist(void)
{
}
/version.c
0,0 → 1,97
//==========================================================================
//
// version.c
//
// RedBoot version "string"
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-12-13
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <pkgconf/redboot.h>
#include <cyg/hal/hal_arch.h>
 
//
// These strings are used for informational purposes only. If the end
// user wishes to replace them, simply redefine them in a platform specific
// file, e.g. in a local file used to provide additional local RedBoot
// customization. Examples of such are platforms which define a special
// 'exec' command used to start a Linux kernel.
//
// CAUTION! Since this file is compiled using very special rules, it is not
// possible to replace it with a local version contained in the "build" tree.
// Replacing the information exported herein is accomplished via the techniques
// outlined above.
//
 
// Do not change the following two lines at all. They are fiddled by release
// scripts.
#define _CERTIFICATE Non-certified
//#define CYGDAT_REDBOOT_CUSTOM_VERSION current
 
#if defined(CYGDAT_REDBOOT_CUSTOM_VERSION)
#define _REDBOOT_VERSION CYGDAT_REDBOOT_CUSTOM_VERSION
#elif defined(CYGPKG_REDBOOT_current)
#define _REDBOOT_VERSION UNKNOWN
#else
#define _REDBOOT_VERSION CYGPKG_REDBOOT
#endif
 
#define __s(x) #x
#define _s(x) __s(x)
 
char RedBoot_version[] CYGBLD_ATTRIB_WEAK =
"\nRedBoot(tm) bootstrap and debug environment [" _s(CYG_HAL_STARTUP) "]"
"\n" _s(_CERTIFICATE) " release, version " _s(_REDBOOT_VERSION)
" - built " __TIME__ ", " __DATE__ "\n\n";
 
// Override default GDB stubs 'info'
// Note: this can still be a "weak" symbol since it will occur in the .o
// file explicitly mentioned in the link command. User programs will
// still be allowed to override it.
char GDB_stubs_version[] CYGBLD_ATTRIB_WEAK =
"eCos GDB stubs [via RedBoot] - built " __DATE__ " / " __TIME__;
/misc_funs.c
0,0 → 1,81
//==========================================================================
//
// misc_funs.c
//
// Miscellaneous [library] functions for RedBoot
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2000-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
 
int
strcasecmp(const char *s1, const char *s2)
{
char c1, c2;
while ((c1 = _tolower(*s1++)) == (c2 = _tolower(*s2++)))
if (c1 == 0)
return (0);
return ((unsigned char)c1 - (unsigned char)c2);
}
 
int
strncasecmp(const char *s1, const char *s2, size_t len)
{
char c1, c2;
 
if (len == 0)
return 0;
do {
if ((c1 = _tolower(*s1++)) != (c2 = _tolower(*s2++)))
return ((unsigned char)c1 - (unsigned char)c2);
if (c1 == 0)
break;
} while (--len != 0);
return 0;
}
/caches.c
0,0 → 1,101
//==========================================================================
//
// caches.c
//
// RedBoot cache control functions
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2002-08-06
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/hal_cache.h>
 
RedBoot_cmd("cache",
"Manage machine caches",
"[ON | OFF]",
do_caches
);
 
void
do_caches(int argc, char *argv[])
{
unsigned long oldints;
int dcache_on=0, icache_on=0;
 
if (argc == 2) {
if (strcasecmp(argv[1], "on") == 0) {
HAL_DISABLE_INTERRUPTS(oldints);
HAL_ICACHE_ENABLE();
HAL_DCACHE_ENABLE();
HAL_RESTORE_INTERRUPTS(oldints);
} else if (strcasecmp(argv[1], "off") == 0) {
HAL_DISABLE_INTERRUPTS(oldints);
HAL_DCACHE_SYNC();
HAL_ICACHE_DISABLE();
HAL_DCACHE_DISABLE();
HAL_DCACHE_SYNC();
HAL_ICACHE_INVALIDATE_ALL();
HAL_DCACHE_INVALIDATE_ALL();
HAL_RESTORE_INTERRUPTS(oldints);
} else {
diag_printf("Invalid cache mode: %s\n", argv[1]);
}
} else {
#ifdef HAL_DCACHE_IS_ENABLED
HAL_DCACHE_IS_ENABLED(dcache_on);
#endif
#ifdef HAL_ICACHE_IS_ENABLED
HAL_ICACHE_IS_ENABLED(icache_on);
#endif
diag_printf("Data cache: %s, Instruction cache: %s\n",
dcache_on?"On":"Off", icache_on?"On":"Off");
}
}
/fs/e2fs.c
0,0 → 1,613
//==========================================================================
//
// e2fs.c
//
// RedBoot support for second extended filesystem
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2003 Gary Thomas <gary@mind.be>
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): msalter
// Contributors: msalter
// Date: 2001-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
#include <fs/disk.h>
#include <fs/e2fs.h>
 
#define DEBUG_E2FS 0
 
#if DEBUG_E2FS > 4
static void dump_sb(struct e2fs_super_block *s);
static void dump_inode(struct e2fs_inode *i);
#endif
 
static void *e2fs_open(partition_t *p, const char *path);
static int e2fs_read(void *fp, char *buf, cyg_uint32 nbytes);
 
// This structure is the only thing exported by this module.
// These filesystem function pointers are attached to disk
// partitions in the generic disk handling code.
//
fs_funs_t redboot_e2fs_funs = {
e2fs_open,
e2fs_read
};
 
// A single block buffer to be shared carefully.
static cyg_uint32 blockbuf[E2FS_MAX_BLOCK_SIZE/sizeof(cyg_uint32)];
 
#define __READ_BLOCK(n) \
PARTITION_READ(e2fs->part, E2FS_BLOCK_TO_SECTOR(e2fs, (n)), \
blockbuf, e2fs->blocksize/SECTOR_SIZE)
 
// Get a group descriptor. Returns non-zero for success.
//
static int
e2fs_get_gdesc(e2fs_desc_t *e2fs, cyg_uint32 group_nr, e2fs_group_t *gdesc)
{
cyg_uint32 sec_nr;
 
if (group_nr < e2fs->gdesc_first ||
group_nr >= (e2fs->gdesc_first + E2FS_GDESC_CACHE_SIZE)) {
 
// cache miss
sec_nr = E2FS_BLOCK_TO_SECTOR(e2fs, e2fs->gdesc_block);
sec_nr += (group_nr / E2FS_GDESC_PER_SECTOR);
 
#if DEBUG_E2FS > 2
diag_printf("%s: group[%d] cache miss, sec_nr[%d]\n",
__FUNCTION__, group_nr, sec_nr);
#endif
if (!PARTITION_READ(e2fs->part, sec_nr, (cyg_uint32 *)e2fs->gdesc_cache,
sizeof(e2fs->gdesc_cache)/SECTOR_SIZE))
return 0;
 
e2fs->gdesc_first = (group_nr / E2FS_GDESC_CACHE_SIZE) * E2FS_GDESC_CACHE_SIZE;
}
*gdesc = e2fs->gdesc_cache[group_nr - e2fs->gdesc_first];
 
return 1;
}
 
// Read the requested inode from disk. Return non-zero if successful
//
static int
e2fs_get_inode(e2fs_desc_t *e2fs, int ino, e2fs_inode_t *ip)
{
cyg_uint32 offset, sec_nr, buf[SECTOR_SIZE/sizeof(cyg_uint32)];
e2fs_group_t gdesc;
 
// get descriptor for group which this inode belongs to
if (!e2fs_get_gdesc(e2fs, (ino - 1) / e2fs->inodes_per_group, &gdesc))
return 0;
if (gdesc.inode_table == 0)
return 0;
 
// byte offset within group inode table
offset = ((ino - 1) % e2fs->inodes_per_group) * sizeof(struct e2fs_inode);
 
// figure out which sector holds the inode
sec_nr = E2FS_BLOCK_TO_SECTOR(e2fs, SWAB_LE32(gdesc.inode_table));
sec_nr += offset / SECTOR_SIZE;
 
// and the offset within that sector.
offset %= SECTOR_SIZE;
 
#if DEBUG_E2FS > 0x08
diag_printf("%s: ino[%d], sec_nr[%d] offset[%d]\n", __FUNCTION__,
ino, sec_nr, offset);
#endif
 
if (!PARTITION_READ(e2fs->part, sec_nr, buf, 1))
return 0;
 
*ip = *(e2fs_inode_t *)((char *)buf + offset);
 
#if DEBUG_E2FS > 0
diag_printf("%s: inode size[%d]\n", __FUNCTION__, SWAB_LE32(ip->size));
#endif
 
return 1;
}
 
// Mount an e2fs filesystem on the given partition.
// Return 0 if successful.
//
static int
e2fs_mount(partition_t *part, e2fs_desc_t *e2fs)
{
int sb_block = 1;
cyg_uint32 sb_buf[E2FS_MIN_BLOCK_SIZE/sizeof(cyg_uint32)];
struct e2fs_super_block *sb = (struct e2fs_super_block *)sb_buf;
 
e2fs->part = part;
 
if (!PARTITION_READ(part, sb_block*(E2FS_MIN_BLOCK_SIZE/SECTOR_SIZE),
(cyg_uint32 *)sb, E2FS_MIN_BLOCK_SIZE/SECTOR_SIZE))
return -1;
 
if (SWAB_LE16(sb->magic) != E2FS_SUPER_MAGIC) {
diag_printf("ext2_mount: bad magic 0x%x\n", SWAB_LE16(sb->magic));
return -1;
}
 
// save some stuff for easy access
e2fs->blocksize = E2FS_BLOCK_SIZE(sb);
e2fs->nr_ind_blocks = (e2fs)->blocksize / sizeof(cyg_uint32);
e2fs->nr_dind_blocks = e2fs->nr_ind_blocks * ((e2fs)->blocksize / sizeof(cyg_uint32));
e2fs->nr_tind_blocks = e2fs->nr_dind_blocks * ((e2fs)->blocksize / sizeof(cyg_uint32));
e2fs->blocks_per_group = SWAB_LE32(sb->blocks_per_group);
e2fs->ngroups = (SWAB_LE32(sb->blocks_count) + e2fs->blocks_per_group - 1) /
e2fs->blocks_per_group;
e2fs->inodes_per_group = SWAB_LE32(sb->inodes_per_group);
 
// Find the group descriptors which follow superblock
e2fs->gdesc_block = ((sb_block * E2FS_MIN_BLOCK_SIZE) / e2fs->blocksize) + 1;
e2fs->gdesc_first = 0; // cache group 0 initially
 
if (!PARTITION_READ(part, E2FS_BLOCK_TO_SECTOR(e2fs,e2fs->gdesc_block),
(cyg_uint32 *)e2fs->gdesc_cache, 1))
return -1;
 
#if DEBUG_E2FS > 1
diag_printf("E2FS superblock:\n");
diag_printf(" [%d] inodes\n", SWAB_LE32(sb->inodes_count));
diag_printf(" [%d] blocks\n", SWAB_LE32(sb->blocks_count));
diag_printf(" [%d] blocksize\n", e2fs->blocksize);
diag_printf(" [%d] blocks per group\n", e2fs->blocks_per_group);
diag_printf(" [%d] ngroups\n", e2fs->ngroups);
#endif
 
#if DEBUG_E2FS > 4
dump_sb(sb);
#endif
 
return 0;
}
 
// Convert a block index into inode data into a block_nr.
// If successful, store block number in pblknr and return non-zero.
//
// NB: This needs some block/sector caching to be speedier. But
// that takes memory and speed is not too bad now for files
// small enough to avoid double and triple indirection.
//
static int
e2fs_inode_block(e2fs_desc_t *e2fs, e2fs_inode_t *inode,
cyg_uint32 bindex, cyg_uint32 *pblknr)
{
if (bindex < E2FS_NR_DIR_BLOCKS) {
*pblknr = SWAB_LE32(inode->block[bindex]);
return 1;
}
bindex -= E2FS_NR_DIR_BLOCKS;
 
if (bindex < e2fs->nr_ind_blocks) {
// Indirect block
if (!__READ_BLOCK(SWAB_LE32(inode->block[E2FS_IND_BLOCK])))
return 0;
*pblknr = SWAB_LE32(blockbuf[bindex]);
return 1;
}
bindex -= e2fs->nr_ind_blocks;
 
if (bindex < e2fs->nr_dind_blocks) {
// Double indirect block
if (!__READ_BLOCK(SWAB_LE32(inode->block[E2FS_DIND_BLOCK])))
return 0;
if (!__READ_BLOCK(SWAB_LE32(blockbuf[bindex / e2fs->nr_ind_blocks])))
return 0;
*pblknr = SWAB_LE32(blockbuf[bindex % e2fs->nr_ind_blocks]);
return 1;
}
bindex -= e2fs->nr_dind_blocks;
 
// Triple indirect block
if (!__READ_BLOCK(SWAB_LE32(inode->block[E2FS_TIND_BLOCK])))
return 0;
if (!__READ_BLOCK(SWAB_LE32(blockbuf[bindex / e2fs->nr_dind_blocks])))
return 0;
bindex %= e2fs->nr_dind_blocks;
if (!__READ_BLOCK(SWAB_LE32(blockbuf[bindex / e2fs->nr_ind_blocks])))
return 0;
*pblknr = SWAB_LE32(blockbuf[bindex % e2fs->nr_ind_blocks]);
return 1;
}
 
 
// search a single directory block in memory looking for an
// entry with the given name. Return pointer to entry if
// found, NULL if not.
//
static e2fs_dir_entry_t *
search_dir_block(e2fs_desc_t *e2fs, cyg_uint32 *blkbuf,
const char *name, int namelen)
{
e2fs_dir_entry_t *dir;
cyg_uint16 reclen, len;
cyg_uint32 offset;
 
#if DEBUG_E2FS > 0
diag_dump_buf(blkbuf, e2fs->blocksize);
#endif
offset = 0;
while (offset < e2fs->blocksize) {
dir = (e2fs_dir_entry_t *)((char *)blkbuf + offset);
reclen = SWAB_LE16(dir->reclen);
offset += reclen;
len = dir->namelen;
 
// terminate on anything which doesn't make sense
if (reclen < 8 || (len + 8) > reclen || offset > (e2fs->blocksize + 1))
return NULL;
 
if (dir->inode && len == namelen && !strncmp(dir->name, name, len))
return dir;
}
return NULL;
}
 
 
// Look in the given directory for an entry with the given name.
// If found, return a pointer to that entry. Return NULL if not
// found.
//
static e2fs_dir_entry_t *
e2fs_dir_lookup(e2fs_desc_t *e2fs, cyg_uint32 dir_ino,
const char *name, int namelen)
{
e2fs_inode_t inode;
e2fs_dir_entry_t *dir;
cyg_uint32 nblocks, last_block_size, i, block_nr, nbytes;
 
#if DEBUG_E2FS > 0
diag_printf("%s: looking for %s [%d] in ino[%d]\n",
__FUNCTION__, name, namelen, dir_ino);
#endif
 
if (!e2fs_get_inode(e2fs, dir_ino, &inode)) {
#if DEBUG_E2FS > 0
diag_printf("%s: e2fs_get_inode [%d] failed\n", __FUNCTION__, dir_ino);
#endif
return NULL;
}
 
nbytes = SWAB_LE32(inode.size);
nblocks = (nbytes + e2fs->blocksize - 1) / e2fs->blocksize;
 
last_block_size = nbytes % e2fs->blocksize;
if (last_block_size == 0)
last_block_size = e2fs->blocksize;
 
for (i = 0; i < nblocks; i++) {
if (!e2fs_inode_block(e2fs, &inode, i, &block_nr))
return NULL;
 
if (block_nr) {
if (!__READ_BLOCK(block_nr))
return NULL;
} else
memset(blockbuf, 0, e2fs->blocksize);
 
dir = search_dir_block(e2fs, blockbuf, name, namelen);
 
if (dir != NULL)
return dir;
}
return NULL;
}
 
typedef struct ino_info {
cyg_uint32 ino;
cyg_uint32 parent_ino;
cyg_uint8 filetype;
} ino_info_t;
 
static int e2fs_inode_lookup(e2fs_desc_t *e2fs, cyg_uint32 dir_ino,
const char *pathname, ino_info_t *info);
 
// Starting from the given directory, find the inode number, filetype, and
// parent inode for the file pointed to by the given symbolic link inode.
// If successful, fills out ino_info_t and return true.
//
static int
e2fs_follow_symlink(e2fs_desc_t *e2fs, cyg_uint32 dir_ino, cyg_uint32 sym_ino, ino_info_t *info)
{
#define MAX_SYMLINK_NAME 255
char symlink[MAX_SYMLINK_NAME+1];
int pathlen;
cyg_uint32 block_nr;
e2fs_inode_t inode;
 
if (!e2fs_get_inode(e2fs, sym_ino, &inode)) {
#if DEBUG_E2FS > 0
diag_printf("%s: e2fs_get_inode [%d] failed\n", __FUNCTION__, sym_ino);
#endif
return 0;
}
 
pathlen = SWAB_LE32(inode.size);
if (pathlen > MAX_SYMLINK_NAME)
return 0;
 
if (inode.blocks) {
if (!e2fs_inode_block(e2fs, &inode, 0, &block_nr))
return 0;
if (block_nr) {
if (!PARTITION_READ(e2fs->part, E2FS_BLOCK_TO_SECTOR(e2fs, block_nr),
blockbuf, e2fs->blocksize/SECTOR_SIZE))
return 0;
memcpy(symlink, blockbuf, pathlen);
} else
return 0;
} else {
// small enough path to fit in inode struct
memcpy(symlink, (char *)&inode.block[0], pathlen);
}
symlink[pathlen] = 0;
 
return e2fs_inode_lookup(e2fs, dir_ino, symlink, info);
}
 
 
// Starting from the given directory, find the inode number, filetype, and
// parent inode for the given file pathname.
// If successful, fills out ino_info_t and return true.
//
static int
e2fs_inode_lookup(e2fs_desc_t *e2fs, cyg_uint32 dir_ino, const char *pathname, ino_info_t *info)
{
int len, pathlen;
const char *p;
e2fs_dir_entry_t *dir = NULL;
if (!pathname || (pathlen = strlen(pathname)) == 0)
return 0;
 
if (*pathname == '/') {
if (--pathlen == 0) {
info->ino = info->parent_ino = E2FS_ROOT_INO;
info->filetype = E2FS_FTYPE_DIR;
return 1;
}
++pathname;
dir_ino = E2FS_ROOT_INO;
}
 
while (pathlen) {
// find next delimiter in path.
for (p = pathname, len = 0; len < pathlen; len++, p++) {
// skip delimiter if found.
if (*p == '/') {
++p;
--pathlen;
break;
}
}
dir = e2fs_dir_lookup(e2fs, dir_ino, pathname, len);
if (dir == NULL)
return 0;
 
pathlen -= len;
pathname = p;
 
switch (dir->filetype) {
case E2FS_FTYPE_SYMLINK:
// follow the symbolic link (this will cause recursion)
if (!e2fs_follow_symlink(e2fs, dir_ino, SWAB_LE32(dir->inode), info))
return 0;
if (pathlen == 0)
return 1;
// must be a dir if we want to continue
if (info->filetype != E2FS_FTYPE_DIR)
return 0;
dir_ino = info->ino;
break;
 
case E2FS_FTYPE_DIR:
if (pathlen)
dir_ino = SWAB_LE32(dir->inode);
break;
 
case E2FS_FTYPE_REG_FILE:
if (pathlen)
return 0; // regular file embedded in middle of path
break;
 
case E2FS_FTYPE_UNKNOWN:
case E2FS_FTYPE_CHRDEV:
case E2FS_FTYPE_BLKDEV:
case E2FS_FTYPE_FIFO:
case E2FS_FTYPE_SOCK:
default:
return 0;
}
}
info->ino = SWAB_LE32(dir->inode);
info->parent_ino = dir_ino;
info->filetype = dir->filetype;
return 1;
}
 
struct read_info {
e2fs_desc_t e2fs_desc;
e2fs_inode_t inode;
cyg_uint32 fsize;
cyg_uint32 fpos;
};
 
static void *
e2fs_open(partition_t *p, const char *filepath)
{
static struct read_info rinfo;
ino_info_t ino_info;
 
// mount partition
if (e2fs_mount(p, &rinfo.e2fs_desc) != 0) {
diag_printf("mount failed.\n");
return NULL;
}
 
// find file inode
if (!e2fs_inode_lookup(&rinfo.e2fs_desc, E2FS_ROOT_INO, filepath, &ino_info)) {
diag_printf("%s: e2fs_inode_lookup failed\n", __FUNCTION__);
return NULL;
}
 
// read inode
if (!e2fs_get_inode(&rinfo.e2fs_desc, ino_info.ino, &rinfo.inode)) {
diag_printf("%s: e2fs_get_inode failed for ino[%d]\n", __FUNCTION__, ino_info.ino);
return NULL;
}
 
rinfo.fsize = SWAB_LE32(rinfo.inode.size);
rinfo.fpos = 0;
 
return &rinfo;
}
 
static int
e2fs_read(void *fp, char *buf, cyg_uint32 nbytes)
{
struct read_info *info = fp;
e2fs_desc_t *e2fs;
cyg_uint32 nread = 0, rem, block_nr, bindex, to_read;
 
if ((info->fpos + nbytes) > info->fsize)
nbytes = info->fsize - info->fpos;
 
e2fs = &info->e2fs_desc;
 
// see if we need to copy leftover data from last read call
rem = e2fs->blocksize - (info->fpos % e2fs->blocksize);
if (rem != e2fs->blocksize) {
char *p = (char *)blockbuf + e2fs->blocksize - rem;
 
if (rem > nbytes)
rem = nbytes;
 
memcpy(buf, p, rem);
 
nread += rem;
buf += rem;
info->fpos += rem;
}
// now loop through blocks if we're not done
bindex = info->fpos / e2fs->blocksize;
while (nread < nbytes) {
if (!e2fs_inode_block(e2fs, &info->inode, bindex, &block_nr))
return -1;
 
if (block_nr) {
if (!PARTITION_READ(e2fs->part, E2FS_BLOCK_TO_SECTOR(e2fs, block_nr),
blockbuf, e2fs->blocksize/SECTOR_SIZE))
return 0;
} else
memset(blockbuf, 0, e2fs->blocksize);
to_read = nbytes - nread;
if (to_read > e2fs->blocksize)
to_read = e2fs->blocksize;
 
memcpy(buf, blockbuf, to_read);
 
nread += to_read;
buf += to_read;
info->fpos += to_read;
++bindex;
}
 
return nread;
}
 
#if DEBUG_E2FS > 4
static void dump_sb(struct e2fs_super_block *s)
{
diag_printf("inode_count: %d\n", SWAB_LE32(s->inodes_count));
diag_printf("blocks_count: %d\n", SWAB_LE32(s->blocks_count));
diag_printf("r_blocks_count: %d\n", SWAB_LE32(s->r_blocks_count));
diag_printf("free_blocks_count: %d\n", SWAB_LE32(s->free_blocks_count));
diag_printf("free_inodes_count: %d\n", SWAB_LE32(s->free_inodes_count));
diag_printf("first_data_block: %d\n", SWAB_LE32(s->first_data_block));
diag_printf("log_block_size: %d\n", SWAB_LE32(s->log_block_size));
diag_printf("log_frag_size: %d\n", SWAB_LE32(s->log_frag_size));
diag_printf("blocks_per_group: %d\n", SWAB_LE32(s->blocks_per_group));
diag_printf("frags_per_group: %d\n", SWAB_LE32(s->frags_per_group));
diag_printf("inodes_per_group: %d\n", SWAB_LE32(s->inodes_per_group));
diag_printf("mnt_count: %d\n", SWAB_LE16(s->mnt_count));
diag_printf("max_mnt_count: %d\n", SWAB_LE16(s->max_mnt_count));
diag_printf("magic: %d\n", SWAB_LE16(s->magic));
diag_printf("state: %d\n", SWAB_LE16(s->state));
diag_printf("errors: %d\n", SWAB_LE16(s->errors));
diag_printf("minor_rev_level: %d\n", SWAB_LE16(s->minor_rev_level));
diag_printf("lastcheck: %d\n", SWAB_LE32(s->lastcheck));
diag_printf("checkinterval: %d\n", SWAB_LE32(s->checkinterval));
diag_printf("creator_os: %d\n", SWAB_LE32(s->creator_os));
diag_printf("rev_level: %d\n", SWAB_LE32(s->rev_level));
}
 
static void dump_inode(struct e2fs_inode *i)
{
int j, n;
 
diag_printf("mode: %o\n", SWAB_LE16(i->mode));
diag_printf("uid: %o\n", SWAB_LE16(i->uid));
diag_printf("size: %d\n", SWAB_LE32(i->size));
diag_printf("gid: %o\n", SWAB_LE16(i->gid));
diag_printf("links: %d\n", SWAB_LE16(i->links_count));
diag_printf("blocks: %d\n", SWAB_LE32(i->blocks));
 
n = i->blocks;
if (n > E2FS_N_BLOCKS)
n = E2FS_N_BLOCKS;
 
for (j = 0; j < n; j++)
diag_printf(" block: %d\n", SWAB_LE32(i->block[j]));
}
#endif
 
 
/fs/ide.c
0,0 → 1,490
//==========================================================================
//
// ide.c
//
// RedBoot IDE support
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2003 Gary Thomas <gary@mind.be>
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): msalter
// Contributors: msalter
// Date: 2001-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
#include <cyg/hal/hal_io.h>
#include <fs/disk.h>
#include <fs/ide.h>
 
static int ide_read(struct disk *d,
cyg_uint32 start_sector,
cyg_uint32 *buf,
cyg_uint8 nr_sectors);
 
static disk_funs_t ide_funs = { ide_read };
 
static struct ide_priv ide_privs[HAL_IDE_NUM_CONTROLLERS * 2];
 
static inline void
__wait_for_ready(int ctlr)
{
cyg_uint8 status;
do {
HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status);
} while (status & (IDE_STAT_BSY | IDE_STAT_DRQ));
}
 
static inline int
__wait_for_drq(int ctlr)
{
cyg_uint8 status;
 
CYGACC_CALL_IF_DELAY_US(10);
do {
HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status);
if (status & IDE_STAT_DRQ)
return 1;
} while (status & IDE_STAT_BSY);
 
return 0;
}
 
static int
ide_reset(int ctlr)
{
cyg_uint8 status;
int delay;
 
HAL_IDE_WRITE_CONTROL(ctlr, 6); // polled mode, reset asserted
CYGACC_CALL_IF_DELAY_US(5000);
HAL_IDE_WRITE_CONTROL(ctlr, 2); // polled mode, reset cleared
CYGACC_CALL_IF_DELAY_US((cyg_uint32)50000);
 
// wait 30 seconds max for not busy
for (delay = 0; delay < 300; ++delay) {
CYGACC_CALL_IF_DELAY_US((cyg_uint32)100000);
HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status);
// bail out early on bogus status
if ((status & (IDE_STAT_BSY|IDE_STAT_DRDY)) == (IDE_STAT_BSY|IDE_STAT_DRDY))
break;
if (!(status & IDE_STAT_BSY))
return 1;
}
return 0;
}
 
static int
ide_ident(int ctlr, int dev, int is_packet_dev, cyg_uint16 *buf)
{
int i;
 
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_DEVICE, dev << 4);
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COMMAND, is_packet_dev ? 0xA1 : 0xEC);
CYGACC_CALL_IF_DELAY_US((cyg_uint32)50000);
 
if (!__wait_for_drq(ctlr))
return 0;
 
for (i = 0; i < (SECTOR_SIZE / sizeof(cyg_uint16)); i++, buf++)
HAL_IDE_READ_UINT16(ctlr, IDE_REG_DATA, *buf);
 
return 1;
}
 
static int
ide_read_sectors(int ctlr, int dev, cyg_uint32 start, cyg_uint8 count, cyg_uint16 *buf)
{
int i, j;
cyg_uint16 *p;
 
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COUNT, count);
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBALOW, start & 0xff);
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBAMID, (start >> 8) & 0xff);
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBAHI, (start >> 16) & 0xff);
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_DEVICE,
((start >> 24) & 0xf) | (dev << 4) | 0x40);
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COMMAND, 0x20);
 
for(p = buf, i = 0; i < count; i++) {
 
if (!__wait_for_drq(ctlr)) {
diag_printf("%s: NO DRQ for ide%d, device %d.\n",
__FUNCTION__, ctlr, dev);
return 0;
}
 
for (j = 0; j < (SECTOR_SIZE / sizeof(cyg_uint16)); j++, p++)
HAL_IDE_READ_UINT16(ctlr, IDE_REG_DATA, *p);
}
return 1;
}
 
// max number of sectors to xfer during a single packet command
#define MAX_CD_XFER 16
 
static inline int
send_packet_command(int ctlr, int dev, cyg_uint16 len, cyg_uint16 *pkt, int pktlen)
{
int i;
cyg_uint8 status, reason;
 
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_FEATURES, 0);
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COUNT, 0);
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBALOW, 0);
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBAMID, len & 0xff);
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBAHI, (len >> 8) & 0xff);
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_DEVICE, dev << 4);
HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COMMAND, 0xA0);
 
if (!__wait_for_drq(ctlr)) {
diag_printf("%s: NO DRQ for ide%d, device %d.\n",
__FUNCTION__, ctlr, dev);
return 0;
}
 
// send packet
for (i = 0; i < (pktlen/sizeof(cyg_uint16)); i++)
HAL_IDE_WRITE_UINT16(ctlr, IDE_REG_DATA, pkt[i]);
 
// wait for not busy transferring packet
do {
HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status);
HAL_IDE_READ_UINT8(ctlr, IDE_REG_REASON, reason);
 
if ((status & (IDE_STAT_BSY | IDE_STAT_DRQ)) == IDE_STAT_DRQ)
if (reason & IDE_REASON_COD)
continue; // still wanting packet data (should timeout here)
 
} while (status & IDE_STAT_BSY);
 
return 1;
}
 
#define READ_COUNT(x) \
{ unsigned char tmp; \
HAL_IDE_READ_UINT8(ctlr, IDE_REG_LBAMID, tmp); \
(x) = tmp; \
HAL_IDE_READ_UINT8(ctlr, IDE_REG_LBAHI, tmp); \
(x) = cdcount |= (tmp << 8); \
}
 
 
// Read the sense data
static int
request_sense(int ctlr, int dev, cyg_uint16 count, cyg_uint16 *buf)
{
int i;
cyg_uint16 cdcount, pkt[6];
unsigned char status, *cpkt = (unsigned char *)pkt;
 
 
// Fill in REQUEST SENSE packet command block
memset(cpkt, 0, sizeof(pkt));
cpkt[0] = 0x03;
cpkt[4] = 254; // allocation length
if (!send_packet_command(ctlr, dev, count, pkt, sizeof(pkt)))
return 0;
 
HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status);
if (!(status & IDE_STAT_DRQ)) {
if (status & IDE_STAT_SERVICE) {
unsigned char reason;
HAL_IDE_READ_UINT8(ctlr, IDE_REG_REASON, reason);
diag_printf("%s: SERVICE request for ide%d, device %d, status[%02x], reason[%02x].\n",
__FUNCTION__, ctlr, dev, status, reason);
}
return 0;
}
 
READ_COUNT(cdcount);
if (cdcount != count)
diag_printf("%s: ide%d, dev%d: his cnt[%d] our count[%d].\n",
__FUNCTION__, ctlr, dev, cdcount, count);
 
for(i = 0; i < (cdcount / sizeof(*buf)); i++, buf++)
HAL_IDE_READ_UINT16(ctlr, IDE_REG_DATA, *buf);
 
// wait for not busy transferring data
do {
HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status);
} while ((status & (IDE_STAT_BSY | IDE_STAT_DRQ)) == IDE_STAT_DRQ);
 
return cdcount;
}
 
// Interpret the sense data
static int
handle_sense(int ctlr, int dev, cyg_uint8 count, cyg_uint16 *buf)
{
#if 0
unsigned char *p = (char *)buf;
 
diag_printf("%s: %d bytes:\n", __FUNCTION__, count);
diag_printf("sense key[%02x] additional sense[%02x]\n",
p[2], p[12]);
#endif
return 1;
}
 
static int
do_packet_read(int ctlr, int dev, cyg_uint32 start, cyg_uint8 count, cyg_uint16 *buf)
{
int i, retry_cnt;
cyg_uint16 cdcount, pkt[6], sense[127];
unsigned char status, *cpkt = (unsigned char *)pkt;
 
// get count number of whole cdrom sectors
while (count) {
 
retry_cnt = 3;
 
i = (count > MAX_CD_XFER) ? MAX_CD_XFER : count;
 
retry:
// Fill in READ(10) packet command block
memset(cpkt, 0, sizeof(pkt));
cpkt[0] = 0x28; // READ(10)
cpkt[2] = (start >> 24) & 0xff;
cpkt[3] = (start >> 16) & 0xff;
cpkt[4] = (start >> 8) & 0xff;
cpkt[5] = (start >> 0) & 0xff;
cpkt[7] = (i >> 8) & 0xff;
cpkt[8] = i & 0xff;
if (!send_packet_command(ctlr, dev, i * CDROM_SECTOR_SIZE,
pkt, sizeof(pkt)))
return 0;
 
HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status);
if (!(status & IDE_STAT_DRQ)) {
if (status & IDE_STAT_SERVICE) {
unsigned char reason;
int sense_count;
HAL_IDE_READ_UINT8(ctlr, IDE_REG_REASON, reason);
#if 1
diag_printf("%s: SERVICE request for ide%d, device %d, status[%02x], reason[%02x].\n",
__FUNCTION__, ctlr, dev, status, reason);
#endif
sense_count = request_sense(ctlr, dev, sizeof(sense), sense);
if (sense_count) {
handle_sense(ctlr, dev, sense_count, sense);
if (retry_cnt--)
goto retry;
}
}
return 0;
}
 
count -= i;
start += i;
 
READ_COUNT(cdcount);
if (cdcount != (i * CDROM_SECTOR_SIZE))
diag_printf("%s: ide%d, dev%d: his cnt[%d] our count[%d].\n",
__FUNCTION__, ctlr, dev,
cdcount, i * CDROM_SECTOR_SIZE);
 
for(i = 0; i < (cdcount / sizeof(*buf)); i++, buf++)
HAL_IDE_READ_UINT16(ctlr, IDE_REG_DATA, *buf);
 
// wait for not busy transferring data
do {
HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status);
} while ((status & (IDE_STAT_BSY | IDE_STAT_DRQ)) == IDE_STAT_DRQ);
}
return 1;
}
 
 
static int
ide_packet_read_sectors(int ctlr, int dev, cyg_uint32 start, cyg_uint8 count, cyg_uint16 *buf)
{
int i, extra;
cyg_uint32 cdstart;
static cyg_uint16 cdsec_buf[CDROM_SECTOR_SIZE/sizeof(cyg_uint16)];
 
cdstart = (start + SECTORS_PER_CDROM_SECTOR-1) / SECTORS_PER_CDROM_SECTOR;
// align to cdrom sector boundary.
if (start % SECTORS_PER_CDROM_SECTOR) {
if (!ide_packet_read_sectors(ctlr, dev,
cdstart * SECTORS_PER_CDROM_SECTOR,
SECTORS_PER_CDROM_SECTOR, cdsec_buf))
return 0;
 
i = SECTORS_PER_CDROM_SECTOR - (start % SECTORS_PER_CDROM_SECTOR);
if (i > count)
i = count;
memcpy(buf, cdsec_buf + ((start % CDROM_SECTOR_SIZE) * SECTOR_SIZE),
i * SECTOR_SIZE);
 
count -= i;
buf += (i * SECTOR_SIZE) / sizeof(*buf);
++cdstart;
}
 
extra = count % SECTORS_PER_CDROM_SECTOR;
count /= SECTORS_PER_CDROM_SECTOR;
 
if (count) {
if (!do_packet_read(ctlr, dev, cdstart, count, buf))
return 0;
buf += count * SECTORS_PER_CDROM_SECTOR * SECTOR_SIZE;
}
 
if (extra) {
// read cdrom sector
if (!ide_packet_read_sectors(ctlr, dev,
cdstart * SECTORS_PER_CDROM_SECTOR,
extra, cdsec_buf))
return 0;
memcpy(buf, cdsec_buf, extra * SECTOR_SIZE);
}
 
return 1;
}
 
static int
ide_read(struct disk *d,
cyg_uint32 start_sec, cyg_uint32 *buf, cyg_uint8 nr_secs)
{
struct ide_priv *p = (struct ide_priv *)(d->private);
 
if (p->flags & IDE_DEV_PACKET)
return ide_packet_read_sectors(p->controller, p->drive,
start_sec, nr_secs, (cyg_uint16 *)buf);
 
return ide_read_sectors(p->controller, p->drive,
start_sec, nr_secs, (cyg_uint16 *)buf);
}
 
 
static void
ide_init(void)
{
cyg_uint32 buf[SECTOR_SIZE/sizeof(cyg_uint32)], u32;
cyg_uint16 u16;
cyg_uint8 u8;
int i, j;
disk_t disk;
struct ide_priv *priv;
 
#define DEV_INIT_VAL ((j << 4) | 0xA0)
 
HAL_IDE_INIT();
 
CYGACC_CALL_IF_DELAY_US(5);
 
priv = ide_privs;
for (i = 0; i < HAL_IDE_NUM_CONTROLLERS; i++) {
 
// soft reset the devices on this controller
if (!ide_reset(i))
continue;
 
// 2 devices per controller
for (j = 0; j < 2; j++, priv++) {
 
priv->controller = i;
priv->drive = j;
priv->flags = 0;
// This is reminiscent of a memory test. We write a value
// to a certain location (device register), then write a
// different value somewhere else so that the first value
// is not hanging on the bus, then we read back the first
// value to see if the write was succesful.
//
HAL_IDE_WRITE_UINT8(i, IDE_REG_DEVICE, DEV_INIT_VAL);
HAL_IDE_WRITE_UINT8(i, IDE_REG_FEATURES, 0);
CYGACC_CALL_IF_DELAY_US(50000);
HAL_IDE_READ_UINT8(i, IDE_REG_DEVICE, u8);
if (u8 != DEV_INIT_VAL) {
diag_printf("IDE failed to identify unit %d - wrote: %x, read: %x\n",
i, DEV_INIT_VAL, u8);
continue;
}
 
// device present
priv->flags |= IDE_DEV_PRESENT;
 
if (ide_ident(i, j, 0, (cyg_uint16 *)buf) <= 0) {
if (ide_ident(i, j, 1, (cyg_uint16 *)buf) <= 0) {
priv->flags = 0;
continue; // can't identify device
} else {
u16 = *(cyg_uint16 *)((char *)buf + IDE_DEVID_GENCONFIG);
if (((u16 >> 8) & 0x1f) != 5) {
diag_printf("Non-CDROM ATAPI device #%d - skipped\n", i);
continue;
}
priv->flags |= IDE_DEV_PACKET;
}
}
memset(&disk, 0, sizeof(disk));
disk.funs = &ide_funs;
disk.private = priv;
 
disk.kind = DISK_IDE_HD; // until proven otherwise
 
if (priv->flags & IDE_DEV_PACKET) {
u16 = *(cyg_uint16 *)((char *)buf + IDE_DEVID_GENCONFIG);
if (((u16 >> 8) & 0x1f) == 5)
disk.kind = DISK_IDE_CDROM;
} else {
u32 = *(cyg_uint32 *)((char *)buf + IDE_DEVID_LBA_CAPACITY);
disk.nr_sectors = u32;
}
 
if (!disk_register(&disk))
return;
}
}
}
 
RedBoot_init(ide_init, RedBoot_INIT_FIRST);
 
/fs/disk.c
0,0 → 1,435
//==========================================================================
//
// disk.c
//
// RedBoot disk support
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): msalter
// Contributors: msalter
// Date: 2001-07-14
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
#include <fs/disk.h>
 
#ifdef CYGSEM_REDBOOT_DISK_EXT2FS
#include <fs/e2fs.h>
#endif
#ifdef CYGSEM_REDBOOT_DISK_ISO9660
#include <fs/iso9660fs.h>
#endif
 
static void do_disks(int argc, char *argv[]);
 
RedBoot_cmd("disks",
"Display disks/partitions.",
"",
do_disks
);
 
static disk_t disk_table[CYGNUM_REDBOOT_MAX_DISKS];
static int disk_count = 0;
 
static int
find_dos_partitions(disk_t *d, cyg_uint8 *mbr)
{
cyg_uint32 s, n, tmp;
struct mbr_partition *p;
int i, found = 0;
 
p = (struct mbr_partition *)(mbr + MBR_PTABLE_OFFSET);
 
// Look for primary partitions
for (i = 0; i < 4 && i < CYGNUM_REDBOOT_MAX_PARTITIONS; i++) {
 
// Have to use memcpy because of alignment
memcpy(&tmp, p->start_sect, 4);
s = SWAB_LE32(tmp);
memcpy(&tmp, p->nr_sects, 4);
n = SWAB_LE32(tmp);
 
if (s && n) {
++found;
d->partitions[i].disk = d;
d->partitions[i].start_sector = s;
d->partitions[i].nr_sectors = n;
d->partitions[i].systype = p->sys_ind;
d->partitions[i].bootflag = p->boot_ind;
}
p++;
}
 
#if CYGNUM_REDBOOT_MAX_PARTITIONS > 4
{
cyg_uint32 buf[SECTOR_SIZE/sizeof(cyg_uint32)], xoffset;
cyg_uint16 magic;
int nextp;
 
// Go back through and find extended partitions
for (i = 0, nextp = 4; i < 4 && nextp < CYGNUM_REDBOOT_MAX_PARTITIONS; i++) {
if (d->partitions[i].systype == SYSTYPE_EXTENDED) {
// sector offsets in partition tables are relative to start
// of extended partition.
xoffset = d->partitions[i].start_sector;
for ( ; nextp < CYGNUM_REDBOOT_MAX_PARTITIONS; ++nextp) {
 
// read partition boot record (same format as mbr except
// there should only be 2 entries max: a normal partition
// and another extended partition
if (DISK_READ(d, xoffset, buf, 1) <= 0)
break;
 
magic = *(cyg_uint16 *)((char *)buf + MBR_MAGIC_OFFSET);
if (SWAB_LE16(magic) != MBR_MAGIC)
break;
 
p = (struct mbr_partition *)((char *)buf + MBR_PTABLE_OFFSET);
 
// Have to use memcpy because of alignment
memcpy(&tmp, p->start_sect, 4);
s = SWAB_LE32(tmp);
memcpy(&tmp, p->nr_sects, 4);
n = SWAB_LE32(tmp);
 
if (s && n) {
++found;
d->partitions[nextp].disk = d;
d->partitions[nextp].start_sector = s + xoffset;
d->partitions[nextp].nr_sectors = n;
d->partitions[nextp].systype = p->sys_ind;
d->partitions[nextp].bootflag = p->boot_ind;
}
++p;
 
memcpy(&tmp, p->start_sect, 4);
s = SWAB_LE32(tmp);
memcpy(&tmp, p->nr_sects, 4);
n = SWAB_LE32(tmp);
 
// more extended partitions?
if (p->sys_ind != SYSTYPE_EXTENDED || !s || !n)
break;
 
xoffset += s;
}
}
}
}
#endif
return found;
}
 
 
// Find partitions on given disk.
// Return number of partitions found
static int
find_partitions(disk_t *d)
{
cyg_uint32 buf[SECTOR_SIZE/sizeof(cyg_uint32)];
cyg_uint16 magic;
partition_t *p;
int i, found = 0;
 
 
if (d->kind == DISK_IDE_CDROM) {
#ifdef CYGSEM_REDBOOT_DISK_ISO9660
// no partition table, so fake it
p = d->partitions;
p->disk = d;
p->start_sector = 0;
p->nr_sectors = d->nr_sectors;
p->funs = &redboot_iso9660fs_funs;
return 1;
#else
return 0;
#endif
}
 
// read Master Boot Record
if (DISK_READ(d, 0, buf, 1) <= 0)
return 0;
 
// Check for DOS MBR
magic = *(cyg_uint16 *)((char *)buf + MBR_MAGIC_OFFSET);
if (SWAB_LE16(magic) == MBR_MAGIC) {
found = find_dos_partitions(d, (cyg_uint8 *)buf);
} else {
// Might want to handle other MBR types, here...
}
 
// Now go through all partitions and install the correct
// funcs for supported filesystems.
for (i = 0, p = d->partitions; i < CYGNUM_REDBOOT_MAX_PARTITIONS; i++, p++) {
switch (p->systype) {
#ifdef CYGSEM_REDBOOT_DISK_EXT2FS
case SYSTYPE_LINUX:
p->funs = &redboot_e2fs_funs;
break;
#endif
#ifdef CYGSEM_REDBOOT_DISK_FAT16
case SYSTYPE_FAT16:
p->funs = &redboot_fat16_funs;
break;
#endif
#ifdef CYGSEM_REDBOOT_DISK_FAT32
case SYSTYPE_FAT32:
p->funs = &redboot_fat32_funs;
break;
#endif
default:
break; // ignore unsupported filesystems
}
}
 
return found;
}
 
// Add a disk to the disk table.
// Return zero if no more room in table.
externC int
disk_register(disk_t *d)
{
int i;
 
// make sure we have room for it
if (disk_count >= CYGNUM_REDBOOT_MAX_DISKS)
return 0;
 
// Set the index
d->index = 0;
for (i = 0; i < disk_count; i++)
if (disk_table[i].kind == d->kind)
d->index++;
 
// put it in the table
disk_table[disk_count] = *d;
 
// fill in partition info
find_partitions(&disk_table[disk_count++]);
 
return 1;
}
 
// Convert a filename in the form <partition_name>:<filename> into
// a partition and path.
//
static int
disk_parse_filename(const char *name, partition_t **part, const char **path)
{
int i, kind, index, pindex;
 
kind = index = pindex = 0;
 
if (name[0] == 'h' && name[1] == 'd') {
// IDE hard drives
kind = DISK_IDE_HD;
if (name[2] < 'a' || name[2] > 'z')
return 0;
index = name[2] - 'a';
if (name[3] < '1' || name[3] >= ('1' + CYGNUM_REDBOOT_MAX_PARTITIONS))
return 0;
pindex = name[3] - '1';
if (name[4] != ':')
return 0;
*path = &name[5];
}
#ifdef CYGSEM_REDBOOT_DISK_ISO9660
else if (name[0] == 'c' && name[1] == 'd') {
// CD drives
kind = DISK_IDE_CDROM;
if (name[2] < '0' || name[2] > '9')
return 0;
index = name[2] - '0';
if (name[3] != ':')
return 0;
*path = &name[4];
}
#endif
 
if (kind) {
for (i = 0; i < CYGNUM_REDBOOT_MAX_DISKS; i++) {
if (disk_table[i].kind == kind && disk_table[i].index == index) {
*part = &disk_table[i].partitions[pindex];
return 1;
}
}
}
return 0;
}
 
static const struct {
int kind;
const char *str;
} systype_names[] = {
{ SYSTYPE_FAT12, "FAT12" },
{ SYSTYPE_FAT16_32M, "FAT16 <32M" },
{ SYSTYPE_FAT16, "FAT16" },
{ SYSTYPE_EXTENDED, "Extended" },
{ SYSTYPE_LINUX_SWAP, "Linux Swap" },
{ SYSTYPE_LINUX, "Linux" }
};
 
static const char *
systype_name(int systype)
{
int i;
for (i = 0; i < sizeof(systype_names)/sizeof(systype_names[0]); i++)
if (systype_names[i].kind == systype)
return systype_names[i].str;
return "Unknown";
}
 
// List disk partitions
static void
do_disks(int argc, char *argv[])
{
int i, j;
disk_t *d;
partition_t *p;
char name[16];
 
for (i = 0, d = disk_table; i < disk_count; i++, d++) {
switch (d->kind) {
case DISK_IDE_HD:
for (j = 0, p = d->partitions;
j < CYGNUM_REDBOOT_MAX_PARTITIONS;
j++, p++) {
if (p->systype) {
diag_sprintf(name, "hd%c%d", 'a' + d->index, j+1);
diag_printf("%-8s %s\n", name, systype_name(p->systype));
}
}
break;
case DISK_IDE_CDROM:
diag_sprintf(name, "cd%d", d->index);
diag_printf("%-8s ISO9660\n", name);
break;
}
}
}
 
static void *fileptr;
static partition_t *file_part;
 
externC int
disk_stream_open(connection_info_t *info, int *err)
{
const char *filepath;
char *filename = info->filename;
 
// The filename is in <disk>:<path> format.
// Convert to a partition and path.
if (!disk_parse_filename(filename, &file_part, &filepath)) {
*err = diskerr_badname;
return -1;
}
 
if (file_part->disk->kind != DISK_IDE_CDROM && file_part->systype == 0) {
*err = diskerr_partition;
return -1;
}
 
if (file_part->funs == (fs_funs_t *)0) {
*err = diskerr_partition;
return -1;
}
 
fileptr = (file_part->funs->open)(file_part, filepath);
if (fileptr == NULL) {
*err = diskerr_open;
return -1;
}
return 0;
}
 
externC int
disk_stream_read(char *buf, int size, int *err)
{
int nread;
 
if ((nread = (file_part->funs->read)(fileptr, buf, size)) < 0) {
*err = diskerr_read;
return -1;
}
return nread;
}
 
externC void
disk_stream_close(int *err)
{
fileptr = NULL;
}
 
externC char *
disk_error(int err)
{
switch (err) {
case diskerr_badname:
return "Bad filename";
break;
case diskerr_partition:
return "Unsupported filesystem";
break;
case diskerr_open:
return "Can't open file";
break;
case diskerr_read:
return "Can't read file";
break;
default:
return "Unknown error";
break;
}
}
 
//
// RedBoot interface
//
GETC_IO_FUNCS(disk_io, disk_stream_open, disk_stream_close,
0, disk_stream_read, disk_error);
RedBoot_load(disk, disk_io, true, true, 0);
/alias.c
0,0 → 1,173
//==========================================================================
//
// alias.c
//
// RedBoot - alias support
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas, jskov
// Date: 2002-05-15
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
 
#ifdef CYGNUM_REDBOOT_FLASH_STRING_SIZE
# define MAX_STRING_LENGTH CYGNUM_REDBOOT_FLASH_STRING_SIZE
#else
# define MAX_STRING_LENGTH 32
#endif
 
// Lookup an alias. First try plain string aliases. If that fails try
// other types so allowing access to all configured values. This allows
// for alias (macro) expansion of normal 'fconfig' data, such as the
// board IP address.
static char *
lookup_alias(char *alias, char *alias_buf)
{
if (0 == strcasecmp("FREEMEMLO", alias)) {
diag_sprintf(alias_buf,"%p", ((CYG_ADDRWORD)user_ram_start + 0x03ff) & ~0x03ff);
return alias_buf;
} else if (0 == strcasecmp("FREEMEMHI", alias)) {
diag_sprintf(alias_buf,"%p", ((CYG_ADDRWORD)user_ram_end) & ~0x03ff);
return alias_buf;
}
 
#ifdef CYGSEM_REDBOOT_FLASH_ALIASES
return flash_lookup_alias(alias, alias_buf);
#else
return NULL;
#endif
}
 
// Expand aliases, this is recursive. ie if one alias contains other
// aliases, these will also be expanded from the insertion point
// onwards.
//
// If 'iter' is zero, then quoted strings are not expanded
//
static bool
_expand_aliases(char *line, int len, int iter)
{
char *lp = line;
char *ms, *me, *ep;
char *alias;
char c;
int offset, line_len, alias_len;
char alias_buf[MAX_STRING_LENGTH];
bool macro_found = false;
 
if ((line_len = strlen(line)) != 0) {
while (*lp) {
c = *lp++;
if ((c == '%') && (*lp == '{')) {
// Found a macro/alias to expand
ms = lp+1;
lp += 2;
while (*lp && (*lp != '}')) lp++;
if (!*lp) {
diag_printf("Invalid macro/alias '%s'\n", ms);
line[0] = '\0'; // Destroy line
return false;
}
me = lp;
*me = '\0';
lp++;
if ((alias = lookup_alias(ms,alias_buf)) != (char *)NULL) {
alias_len = strlen(alias);
// See if there is room in the line to expand this macro/alias
if ((line_len+alias_len) < len) {
// Make a hole by moving data within the line
offset = alias_len-strlen(ms)-2; // Number of bytes being inserted
ep = &lp[strlen(lp)-1];
if (offset > 1) {
ep[offset] = '\0';
while (ep != (lp-1)) {
ep[offset-1] = *ep;
ep--;
}
} else {
if (offset <=0) {
while ((lp-1) != ep) {
lp[offset-1] = *lp;
lp++;
}
lp[offset-1]='\0';
}
}
// Insert the macro/alias data
lp = ms-2;
while (*alias) {
if ((alias[0] == '%') && (alias[1] == '{')) macro_found = true;
*lp++ = *alias++;
}
line_len = strlen(line);
lp = lp - alias_len;
} else {
diag_printf("No room to expand '%s'\n", ms);
line[0] = '\0'; // Destroy line
return false;
}
} else {
diag_printf("Alias '%s' not defined\n", ms);
*me = '|';
}
} else if ((c == '"') && (iter == 0)) {
// Skip quoted strings
while (*lp && (*lp != '"')) lp++;
}
}
}
return macro_found;
}
 
void
expand_aliases(char *line, int len)
{
int iter = 0;
 
while (_expand_aliases(line, len, iter++)) {
}
}
/mcmp.c
0,0 → 1,131
//==========================================================================
//
// mcmp.c
//
// RedBoot memory compare (mcmp) routine
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002 Gary Thomas
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2002-08-06
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
 
RedBoot_cmd("mcmp",
"Compare two blocks of memory",
"-s <location> -d <location> -l <length> [-1|-2|-4]",
do_mcmp
);
 
void
do_mcmp(int argc, char *argv[])
{
// Fill a region of memory with a pattern
struct option_info opts[6];
unsigned long src_base, dst_base;
long len;
bool src_base_set, dst_base_set, len_set;
bool set_32bit, set_16bit, set_8bit;
 
init_opts(&opts[0], 's', true, OPTION_ARG_TYPE_NUM,
(void **)&src_base, (bool *)&src_base_set, "base address");
init_opts(&opts[1], 'l', true, OPTION_ARG_TYPE_NUM,
(void **)&len, (bool *)&len_set, "length");
init_opts(&opts[2], 'd', true, OPTION_ARG_TYPE_NUM,
(void **)&dst_base, (bool *)&dst_base_set, "base address");
init_opts(&opts[3], '4', false, OPTION_ARG_TYPE_FLG,
(void *)&set_32bit, (bool *)0, "fill 32 bit units");
init_opts(&opts[4], '2', false, OPTION_ARG_TYPE_FLG,
(void **)&set_16bit, (bool *)0, "fill 16 bit units");
init_opts(&opts[5], '1', false, OPTION_ARG_TYPE_FLG,
(void **)&set_8bit, (bool *)0, "fill 8 bit units");
if (!scan_opts(argc, argv, 1, opts, 6, 0, 0, "")) {
return;
}
if (!src_base_set || !dst_base_set || !len_set) {
diag_printf("usage: mcmp -s <addr> -d <addr> -l <length> [-1|-2|-4]\n");
return;
}
// No checks here
if (set_8bit) {
// Compare 8 bits at a time
while ((len -= sizeof(cyg_uint8)) >= 0) {
if (*((cyg_uint8 *)src_base)++ != *((cyg_uint8 *)dst_base)++) {
((cyg_uint8 *)src_base)--;
((cyg_uint8 *)dst_base)--;
diag_printf("Buffers don't match - %p=0x%02x, %p=0x%02x\n",
src_base, *((cyg_uint8 *)src_base),
dst_base, *((cyg_uint8 *)dst_base));
return;
}
}
} else if (set_16bit) {
// Compare 16 bits at a time
while ((len -= sizeof(cyg_uint16)) >= 0) {
if (*((cyg_uint16 *)src_base)++ != *((cyg_uint16 *)dst_base)++) {
((cyg_uint16 *)src_base)--;
((cyg_uint16 *)dst_base)--;
diag_printf("Buffers don't match - %p=0x%04x, %p=0x%04x\n",
src_base, *((cyg_uint16 *)src_base),
dst_base, *((cyg_uint16 *)dst_base));
return;
}
}
} else {
// Default - 32 bits
while ((len -= sizeof(cyg_uint32)) >= 0) {
if (*((cyg_uint32 *)src_base)++ != *((cyg_uint32 *)dst_base)++) {
((cyg_uint32 *)src_base)--;
((cyg_uint32 *)dst_base)--;
diag_printf("Buffers don't match - %p=0x%08x, %p=0x%08x\n",
src_base, *((cyg_uint32 *)src_base),
dst_base, *((cyg_uint32 *)dst_base));
return;
}
}
}
}
/flash.c
0,0 → 1,2122
//==========================================================================
//
// flash.c
//
// RedBoot - FLASH memory support
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas, tkoeller
// Date: 2000-07-28
// Purpose:
// Description:
//
// This code is part of RedBoot (tm).
//
//####DESCRIPTIONEND####
//
//==========================================================================
 
#include <redboot.h>
#include <cyg/io/flash.h>
#include <fis.h>
#include <sib.h>
 
#ifdef CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
// Note horrid intertwining of functions, to save precious FLASH
#endif
 
// Round a quantity up
#define _rup(n,s) ((((n)+(s-1))/s)*s)
 
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
#include <flash_config.h>
 
// Configuration data, saved in FLASH, used to set/update RedBoot
// normal "configuration" data items.
static struct _config {
unsigned long len;
unsigned long key1;
unsigned char config_data[MAX_CONFIG_DATA-(4*4)];
unsigned long key2;
unsigned long cksum;
} *config, *backup_config;
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
static struct _config *readonly_config;
#endif
#endif
 
#ifdef CYGOPT_REDBOOT_FIS
// Image management functions
local_cmd_entry("init",
"Initialize FLASH Image System [FIS]",
"[-f]",
fis_init,
FIS_cmds
);
#ifdef CYGSEM_REDBOOT_FIS_CRC_CHECK
# define FIS_LIST_OPTS "[-c] [-d]"
#else
# define FIS_LIST_OPTS "[-d]"
#endif
local_cmd_entry("list",
"Display contents of FLASH Image System [FIS]",
FIS_LIST_OPTS,
fis_list,
FIS_cmds
);
local_cmd_entry("free",
"Display free [available] locations within FLASH Image System [FIS]",
"",
fis_free,
FIS_cmds
);
local_cmd_entry("delete",
"Delete an image from FLASH Image System [FIS]",
"name",
fis_delete,
FIS_cmds
);
 
static char fis_load_usage[] =
#ifdef CYGPKG_COMPRESS_ZLIB
"[-d] "
#endif
"[-b <memory_load_address>] [-c] name";
 
local_cmd_entry("load",
"Load image from FLASH Image System [FIS] into RAM",
fis_load_usage,
fis_load,
FIS_cmds
);
local_cmd_entry("create",
"Create an image",
"-b <mem_base> -l <image_length> [-s <data_length>]\n"
" [-f <flash_addr>] [-e <entry_point>] [-r <ram_addr>] [-n] <name>",
fis_create,
FIS_cmds
);
#endif
 
// Raw flash access functions
local_cmd_entry("erase",
"Erase FLASH contents",
"-f <flash_addr> -l <length>",
fis_erase,
FIS_cmds
);
#ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING
local_cmd_entry("lock",
"LOCK FLASH contents",
"[-f <flash_addr> -l <length>] [name]",
fis_lock,
FIS_cmds
);
local_cmd_entry("unlock",
"UNLOCK FLASH contents",
"[-f <flash_addr> -l <length>] [name]",
fis_unlock,
FIS_cmds
);
#endif
local_cmd_entry("write",
"Write raw data directly to FLASH",
"-f <flash_addr> -b <mem_base> -l <image_length>",
fis_write,
FIS_cmds
);
 
// Define table boundaries
CYG_HAL_TABLE_BEGIN( __FIS_cmds_TAB__, FIS_cmds);
CYG_HAL_TABLE_END( __FIS_cmds_TAB_END__, FIS_cmds);
 
extern struct cmd __FIS_cmds_TAB__[], __FIS_cmds_TAB_END__;
 
// CLI function
static cmd_fun do_fis;
RedBoot_nested_cmd("fis",
"Manage FLASH images",
"{cmds}",
do_fis,
__FIS_cmds_TAB__, &__FIS_cmds_TAB_END__
);
 
// Local data used by these routines
static void *flash_start, *flash_end;
static int flash_block_size, flash_num_blocks;
#ifdef CYGOPT_REDBOOT_FIS
static void *fis_work_block;
static void *fis_addr;
static int fisdir_size; // Size of FIS directory.
#endif
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
static void *cfg_base; // Location in Flash of config data
static int cfg_size; // Length of config data - rounded to Flash block size
// Prototypes for local functions
static unsigned char *flash_lookup_config(char *key);
#endif
 
static void
fis_usage(char *why)
{
diag_printf("*** invalid 'fis' command: %s\n", why);
cmd_usage(__FIS_cmds_TAB__, &__FIS_cmds_TAB_END__, "fis ");
}
 
static void
_show_invalid_flash_address(CYG_ADDRESS flash_addr, int stat)
{
diag_printf("Invalid FLASH address %p: %s\n", (void *)flash_addr, flash_errmsg(stat));
diag_printf(" valid range is %p-%p\n", (void *)flash_start, (void *)flash_end);
}
 
#ifdef CYGOPT_REDBOOT_FIS
struct fis_image_desc *
fis_lookup(char *name, int *num)
{
int i;
struct fis_image_desc *img;
 
img = (struct fis_image_desc *)fis_work_block;
for (i = 0; i < fisdir_size/sizeof(*img); i++, img++) {
if ((img->name[0] != (unsigned char)0xFF) &&
(strcasecmp(name, img->name) == 0)) {
if (num) *num = i;
return img;
}
}
return (struct fis_image_desc *)0;
}
 
static void
fis_update_directory(void)
{
int stat;
void *err_addr;
 
#ifdef CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
memcpy((char *)fis_work_block+fisdir_size, config, cfg_size);
#endif
#ifdef CYGSEM_REDBOOT_FLASH_LOCK_SPECIAL
// Ensure [quietly] that the directory is unlocked before trying to update
flash_unlock((void *)fis_addr, flash_block_size, (void **)&err_addr);
#endif
if ((stat = flash_erase(fis_addr, flash_block_size, (void **)&err_addr)) != 0) {
diag_printf("Error erasing FIS directory at %p: %s\n", err_addr, flash_errmsg(stat));
} else {
if ((stat = flash_program(fis_addr, fis_work_block, flash_block_size,
(void **)&err_addr)) != 0) {
diag_printf("Error writing FIS directory at %p: %s\n",
err_addr, flash_errmsg(stat));
}
}
#ifdef CYGSEM_REDBOOT_FLASH_LOCK_SPECIAL
// Ensure [quietly] that the directory is locked after the update
flash_lock((void *)fis_addr, flash_block_size, (void **)&err_addr);
#endif
}
 
static void
fis_init(int argc, char *argv[])
{
int stat;
struct fis_image_desc *img;
void *err_addr;
bool full_init = false;
struct option_info opts[1];
CYG_ADDRESS redboot_flash_start;
unsigned long redboot_image_size;
 
init_opts(&opts[0], 'f', false, OPTION_ARG_TYPE_FLG,
(void **)&full_init, (bool *)0, "full initialization, erases all of flash");
if (!scan_opts(argc, argv, 2, opts, 1, 0, 0, ""))
{
return;
}
 
if (!verify_action("About to initialize [format] FLASH image system")) {
diag_printf("** Aborted\n");
return;
}
diag_printf("*** Initialize FLASH Image System\n");
 
#define MIN_REDBOOT_IMAGE_SIZE CYGBLD_REDBOOT_MIN_IMAGE_SIZE
redboot_image_size = flash_block_size > MIN_REDBOOT_IMAGE_SIZE ?
flash_block_size : MIN_REDBOOT_IMAGE_SIZE;
 
// Create a pseudo image for RedBoot
img = (struct fis_image_desc *)fis_work_block;
memset(img, 0xFF, fisdir_size); // Start with erased data
#ifdef CYGOPT_REDBOOT_FIS_RESERVED_BASE
memset(img, 0, sizeof(*img));
strcpy(img->name, "(reserved)");
img->flash_base = (CYG_ADDRESS)flash_start;
img->mem_base = (CYG_ADDRESS)flash_start;
img->size = CYGNUM_REDBOOT_FLASH_RESERVED_BASE;
img++;
#endif
redboot_flash_start = (CYG_ADDRESS)flash_start + CYGBLD_REDBOOT_FLASH_BOOT_OFFSET;
#ifdef CYGOPT_REDBOOT_FIS_REDBOOT
memset(img, 0, sizeof(*img));
strcpy(img->name, "RedBoot");
img->flash_base = redboot_flash_start;
img->mem_base = redboot_flash_start;
img->size = redboot_image_size;
img++;
redboot_flash_start += redboot_image_size;
#endif
#ifdef CYGOPT_REDBOOT_FIS_REDBOOT_POST
#ifdef CYGNUM_REDBOOT_FIS_REDBOOT_POST_OFFSET
// Take care to place the POST entry at the right offset:
redboot_flash_start = (CYG_ADDRESS)flash_start + CYGNUM_REDBOOT_FIS_REDBOOT_POST_OFFSET;
#endif
memset(img, 0, sizeof(*img));
strcpy(img->name, "RedBoot[post]");
img->flash_base = redboot_flash_start;
img->mem_base = redboot_flash_start;
img->size = redboot_image_size;
img++;
redboot_flash_start += redboot_image_size;
#endif
#ifdef CYGOPT_REDBOOT_FIS_REDBOOT_BACKUP
// And a backup image
memset(img, 0, sizeof(*img));
strcpy(img->name, "RedBoot[backup]");
img->flash_base = redboot_flash_start;
img->mem_base = redboot_flash_start;
img->size = redboot_image_size;
img++;
redboot_flash_start += redboot_image_size;
#endif
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
// And a descriptor for the configuration data
memset(img, 0, sizeof(*img));
strcpy(img->name, "RedBoot config");
img->flash_base = (CYG_ADDRESS)cfg_base;
img->mem_base = (CYG_ADDRESS)cfg_base;
img->size = cfg_size;
img++;
#endif
// And a descriptor for the descriptor table itself
memset(img, 0, sizeof(*img));
strcpy(img->name, "FIS directory");
img->flash_base = (CYG_ADDRESS)fis_addr;
img->mem_base = (CYG_ADDRESS)fis_addr;
img->size = fisdir_size;
img++;
 
#ifdef CYGOPT_REDBOOT_FIS_DIRECTORY_ARM_SIB_ID
// FIS gets the size of a full block - note, this should be changed
// if support is added for multi-block FIS structures.
img = (struct fis_image_desc *)((CYG_ADDRESS)fis_work_block + fisdir_size);
// Add a footer so the FIS will be recognized by the ARM Boot
// Monitor as a reserved area.
{
tFooter* footer_p = (tFooter*)((CYG_ADDRESS)img - sizeof(tFooter));
cyg_uint32 check = 0;
cyg_uint32 *check_ptr = (cyg_uint32 *)footer_p;
cyg_int32 count = (sizeof(tFooter) - 4) >> 2;
 
// Prepare footer. Try to protect all but the reserved space
// and the first RedBoot image (which is expected to be
// bootable), but fall back to just protecting the FIS if it's
// not at the default position in the flash.
#if defined(CYGOPT_REDBOOT_FIS_RESERVED_BASE) && (-1 == CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK)
footer_p->blockBase = (char*)_ADDR_REDBOOT_TO_ARM(flash_start);
footer_p->blockBase += CYGNUM_REDBOOT_FLASH_RESERVED_BASE + redboot_image_size;
#else
footer_p->blockBase = (char*)_ADDR_REDBOOT_TO_ARM(fis_work_block);
#endif
footer_p->infoBase = NULL;
footer_p->signature = FLASH_FOOTER_SIGNATURE;
footer_p->type = TYPE_REDHAT_REDBOOT;
 
// and compute its checksum
for ( ; count > 0; count--) {
if (*check_ptr > ~check)
check++;
check += *check_ptr++;
}
footer_p->checksum = ~check;
}
#endif
 
// Do this after creating the initialized table because that inherently
// calculates where the high water mark of default RedBoot images is.
 
if (full_init) {
unsigned long erase_size;
CYG_ADDRESS erase_start;
// Erase everything except default RedBoot images, fis block,
// and config block.
// First deal with the possible first part, before RedBoot images:
#if (CYGBLD_REDBOOT_FLASH_BOOT_OFFSET > CYGNUM_REDBOOT_FLASH_RESERVED_BASE)
erase_start = (CYG_ADDRESS)flash_start + CYGNUM_REDBOOT_FLASH_RESERVED_BASE;
erase_size = (CYG_ADDRESS)flash_start + CYGBLD_REDBOOT_FLASH_BOOT_OFFSET;
if ( erase_size > erase_start ) {
erase_size -= erase_start;
if ((stat = flash_erase((void *)erase_start, erase_size,
(void **)&err_addr)) != 0) {
diag_printf(" initialization failed at %p: %s\n",
err_addr, flash_errmsg(stat));
}
}
#endif
// second deal with the larger part in the main:
erase_start = redboot_flash_start; // high water of created images
// Now the empty bits between the end of Redboot and the cfg and dir
// blocks.
#if defined(CYGSEM_REDBOOT_FLASH_CONFIG) && !defined(CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG)
if (fis_addr > cfg_base) {
erase_size = (CYG_ADDRESS)cfg_base - erase_start; // the gap between HWM and config data
} else {
erase_size = (CYG_ADDRESS)fis_addr - erase_start; // the gap between HWM and fis data
}
if ((stat = flash_erase((void *)erase_start, erase_size,
(void **)&err_addr)) != 0) {
diag_printf(" initialization failed %p: %s\n",
err_addr, flash_errmsg(stat));
}
erase_start += (erase_size + flash_block_size);
if (fis_addr > cfg_base) {
erase_size = (CYG_ADDRESS)fis_addr - erase_start; // the gap between config and fis data
} else {
erase_size = (CYG_ADDRESS)cfg_base - erase_start; // the gap between fis and config data
}
if ((stat = flash_erase((void *)erase_start, erase_size,
(void **)&err_addr)) != 0) {
diag_printf(" initialization failed %p: %s\n",
err_addr, flash_errmsg(stat));
}
erase_start += (erase_size + flash_block_size);
#else // !CYGSEM_REDBOOT_FLASH_CONFIG
erase_size = (CYG_ADDRESS)fis_addr - erase_start; // the gap between HWM and fis data
if ((stat = flash_erase((void *)erase_start, erase_size,
(void **)&err_addr)) != 0) {
diag_printf(" initialization failed %p: %s\n",
err_addr, flash_errmsg(stat));
}
erase_start += (erase_size + flash_block_size);
#endif
// Lastly, anything at the end
erase_size = ((CYG_ADDRESS)flash_end - erase_start) + 1;
if ((stat = flash_erase((void *)erase_start, erase_size,
(void **)&err_addr)) != 0) {
diag_printf(" initialization failed at %p: %s\n",
err_addr, flash_errmsg(stat));
}
} else {
diag_printf(" Warning: device contents not erased, some blocks may not be usable\n");
}
 
fis_update_directory();
}
 
static void
fis_list(int argc, char *argv[])
{
struct fis_image_desc *img;
int i;
bool show_cksums = false;
bool show_datalen = false;
struct option_info opts[2];
 
#ifdef CYGHWR_REDBOOT_ARM_FLASH_SIB
// FIXME: this is somewhat half-baked
extern void arm_fis_list(void);
arm_fis_list();
return;
#endif
 
init_opts(&opts[0], 'd', false, OPTION_ARG_TYPE_FLG,
(void **)&show_datalen, (bool *)0, "display data length");
#ifdef CYGSEM_REDBOOT_FIS_CRC_CHECK
init_opts(&opts[1], 'c', false, OPTION_ARG_TYPE_FLG,
(void **)&show_cksums, (bool *)0, "display checksums");
i = 2;
#else
i = 1;
#endif
if (!scan_opts(argc, argv, 2, opts, i, 0, 0, ""))
{
return;
}
img = (struct fis_image_desc *) fis_addr;
// Let diag_printf do the formatting in both cases, rather than counting
// cols by hand....
diag_printf("%-16s %-10s %-10s %-10s %-s\n",
"Name","FLASH addr",
show_cksums ? "Checksum" : "Mem addr",
show_datalen ? "Datalen" : "Length",
"Entry point" );
for (i = 0; i < fisdir_size/sizeof(*img); i++, img++) {
if (img->name[0] != (unsigned char)0xFF) {
diag_printf("%-16s 0x%08lX 0x%08lX 0x%08lX 0x%08lX\n", img->name,
img->flash_base,
#ifdef CYGSEM_REDBOOT_FIS_CRC_CHECK
show_cksums ? img->file_cksum : img->mem_base,
show_datalen ? img->data_length : img->size,
#else
img->mem_base,
img->size,
#endif
img->entry_point);
}
}
}
 
static void
fis_free(int argc, char *argv[])
{
unsigned long *fis_ptr, *fis_end;
unsigned long *area_start;
 
// Do not search the area reserved for pre-RedBoot systems:
fis_ptr = (unsigned long *)((CYG_ADDRESS)flash_start + CYGNUM_REDBOOT_FLASH_RESERVED_BASE);
fis_end = (unsigned long *)(CYG_ADDRESS)flash_end;
area_start = fis_ptr;
while (fis_ptr < fis_end) {
if (*fis_ptr != (unsigned long)0xFFFFFFFF) {
if (area_start != fis_ptr) {
// Assume that this is something
diag_printf(" 0x%08lX .. 0x%08lX\n",
(CYG_ADDRESS)area_start, (CYG_ADDRESS)fis_ptr);
}
// Find next blank block
area_start = fis_ptr;
while (area_start < fis_end) {
if (*area_start == (unsigned long)0xFFFFFFFF) {
break;
}
area_start += flash_block_size / sizeof(CYG_ADDRESS);
}
fis_ptr = area_start;
} else {
fis_ptr += flash_block_size / sizeof(CYG_ADDRESS);
}
}
if (area_start != fis_ptr) {
diag_printf(" 0x%08lX .. 0x%08lX\n",
(CYG_ADDRESS)area_start, (CYG_ADDRESS)fis_ptr);
}
}
 
// Find the first unused area of flash which is long enough
static bool
fis_find_free(CYG_ADDRESS *addr, unsigned long length)
{
unsigned long *fis_ptr, *fis_end;
unsigned long *area_start;
 
// Do not search the area reserved for pre-RedBoot systems:
fis_ptr = (unsigned long *)((CYG_ADDRESS)flash_start + CYGNUM_REDBOOT_FLASH_RESERVED_BASE);
fis_end = (unsigned long *)(CYG_ADDRESS)flash_end;
area_start = fis_ptr;
while (fis_ptr < fis_end) {
if (*fis_ptr != (unsigned long)0xFFFFFFFF) {
if (area_start != fis_ptr) {
// Assume that this is something
if ((fis_ptr-area_start) >= (length/sizeof(unsigned))) {
*addr = (CYG_ADDRESS)area_start;
return true;
}
}
// Find next blank block
area_start = fis_ptr;
while (area_start < fis_end) {
if (*area_start == (unsigned long)0xFFFFFFFF) {
break;
}
area_start += flash_block_size / sizeof(CYG_ADDRESS);
}
fis_ptr = area_start;
} else {
fis_ptr += flash_block_size / sizeof(CYG_ADDRESS);
}
}
if (area_start != fis_ptr) {
if ((fis_ptr-area_start) >= (length/sizeof(unsigned))) {
*addr = (CYG_ADDRESS)area_start;
return true;
}
}
return false;
}
 
static void
fis_create(int argc, char *argv[])
{
int i, stat;
unsigned long length, img_size;
CYG_ADDRESS mem_addr, exec_addr, flash_addr, entry_addr;
char *name;
bool mem_addr_set = false;
bool exec_addr_set = false;
bool entry_addr_set = false;
bool flash_addr_set = false;
bool length_set = false;
bool img_size_set = false;
bool no_copy = false;
void *err_addr;
struct fis_image_desc *img = NULL;
bool defaults_assumed;
struct option_info opts[7];
bool prog_ok = true;
 
init_opts(&opts[0], 'b', true, OPTION_ARG_TYPE_NUM,
(void **)&mem_addr, (bool *)&mem_addr_set, "memory base address");
init_opts(&opts[1], 'r', true, OPTION_ARG_TYPE_NUM,
(void **)&exec_addr, (bool *)&exec_addr_set, "ram base address");
init_opts(&opts[2], 'e', true, OPTION_ARG_TYPE_NUM,
(void **)&entry_addr, (bool *)&entry_addr_set, "entry point address");
init_opts(&opts[3], 'f', true, OPTION_ARG_TYPE_NUM,
(void **)&flash_addr, (bool *)&flash_addr_set, "FLASH memory base address");
init_opts(&opts[4], 'l', true, OPTION_ARG_TYPE_NUM,
(void **)&length, (bool *)&length_set, "image length [in FLASH]");
init_opts(&opts[5], 's', true, OPTION_ARG_TYPE_NUM,
(void **)&img_size, (bool *)&img_size_set, "image size [actual data]");
init_opts(&opts[6], 'n', false, OPTION_ARG_TYPE_FLG,
(void **)&no_copy, (bool *)0, "don't copy from RAM to FLASH, just update directory");
if (!scan_opts(argc, argv, 2, opts, 7, (void *)&name, OPTION_ARG_TYPE_STR, "file name"))
{
fis_usage("invalid arguments");
return;
}
 
memcpy(fis_work_block, fis_addr, fisdir_size);
defaults_assumed = false;
if (name) {
// Search existing files to acquire defaults for params not specified:
img = fis_lookup(name, NULL);
if (img) {
// Found it, so get image size from there
if (!length_set) {
length_set = true;
length = img->size;
defaults_assumed = true;
}
}
}
if (!mem_addr_set && (load_address >= (CYG_ADDRESS)ram_start) &&
(load_address_end) < (CYG_ADDRESS)ram_end) {
mem_addr = load_address;
mem_addr_set = true;
defaults_assumed = true;
// Get entry address from loader, unless overridden
if (!entry_addr_set)
entry_addr = entry_address;
if (!length_set) {
length = load_address_end - load_address;
length_set = true;
} else if (defaults_assumed && !img_size_set) {
/* We got length from the FIS table, so the size of the
actual loaded image becomes img_size */
img_size = load_address_end - load_address;
img_size_set = true;
}
}
// Get the remaining fall-back values from the fis
if (img) {
if (!exec_addr_set) {
// Preserve "normal" behaviour
exec_addr_set = true;
exec_addr = flash_addr_set ? flash_addr : mem_addr;
}
if (!flash_addr_set) {
flash_addr_set = true;
flash_addr = img->flash_base;
defaults_assumed = true;
}
}
 
if ((!no_copy && !mem_addr_set) || (no_copy && !flash_addr_set) ||
!length_set || !name) {
fis_usage("required parameter missing");
return;
}
if (!img_size_set) {
img_size = length;
}
// 'length' is size of FLASH image, 'img_size' is actual data size
// Round up length to FLASH block size
#ifndef CYGPKG_HAL_MIPS // FIXME: compiler is b0rken
length = ((length + flash_block_size - 1) / flash_block_size) * flash_block_size;
if (length < img_size) {
diag_printf("Invalid FLASH image size/length combination\n");
return;
}
#endif
if (flash_addr_set &&
((stat = flash_verify_addr((void *)flash_addr)) ||
(stat = flash_verify_addr((void *)(flash_addr+img_size-1))))) {
_show_invalid_flash_address(flash_addr, stat);
return;
}
if (flash_addr_set && flash_addr & (flash_block_size-1)) {
diag_printf("Invalid FLASH address: %p\n", (void *)flash_addr);
diag_printf(" must be 0x%x aligned\n", flash_block_size);
return;
}
if (strlen(name) >= sizeof(img->name)) {
diag_printf("Name is too long, must be less than %d chars\n", (int)sizeof(img->name));
return;
}
if (!no_copy) {
if ((mem_addr < (CYG_ADDRESS)ram_start) ||
((mem_addr+img_size) >= (CYG_ADDRESS)ram_end)) {
diag_printf("** WARNING: RAM address: %p may be invalid\n", (void *)mem_addr);
diag_printf(" valid range is %p-%p\n", (void *)ram_start, (void *)ram_end);
}
if (!flash_addr_set && !fis_find_free(&flash_addr, length)) {
diag_printf("Can't locate %lx(%ld) bytes free in FLASH\n", length, length);
return;
}
}
// First, see if the image by this name has agreable properties
if (img) {
if (flash_addr_set && (img->flash_base != flash_addr)) {
diag_printf("Image found, but flash address (%p)\n"
" is incorrect (present image location %p)\n",
flash_addr, img->flash_base);
return;
}
if (img->size != length) {
diag_printf("Image found, but length (0x%lx, necessitating image size 0x%lx)\n"
" is incorrect (present image size 0x%lx)\n",
img_size, length, img->size);
return;
}
if (!verify_action("An image named '%s' exists", name)) {
return;
} else {
if (defaults_assumed) {
if (no_copy &&
!verify_action("* CAUTION * about to program '%s'\n at %p..%p from %p",
name, (void *)flash_addr, (void *)(flash_addr+img_size-1),
(void *)mem_addr)) {
return; // The guy gave up
}
}
}
} else {
// If not image by that name, try and find an empty slot
img = (struct fis_image_desc *)fis_work_block;
for (i = 0; i < fisdir_size/sizeof(*img); i++, img++) {
if (img->name[0] == (unsigned char)0xFF) {
break;
}
}
}
if (!no_copy) {
// Safety check - make sure the address range is not within the code we're running
if (flash_code_overlaps((void *)flash_addr, (void *)(flash_addr+img_size-1))) {
diag_printf("Can't program this region - contains code in use!\n");
return;
}
if (prog_ok) {
// Erase area to be programmed
if ((stat = flash_erase((void *)flash_addr, length, (void **)&err_addr)) != 0) {
diag_printf("Can't erase region at %p: %s\n", err_addr, flash_errmsg(stat));
prog_ok = false;
}
}
if (prog_ok) {
// Now program it
if ((stat = flash_program((void *)flash_addr, (void *)mem_addr, img_size, (void **)&err_addr)) != 0) {
diag_printf("Can't program region at %p: %s\n", err_addr, flash_errmsg(stat));
prog_ok = false;
}
}
}
if (prog_ok) {
// Update directory
memset(img, 0, sizeof(*img));
strcpy(img->name, name);
img->flash_base = flash_addr;
img->mem_base = exec_addr_set ? exec_addr : (flash_addr_set ? flash_addr : mem_addr);
img->entry_point = entry_addr_set ? entry_addr : (CYG_ADDRESS)entry_address; // Hope it's been set
img->size = length;
img->data_length = img_size;
#ifdef CYGSEM_REDBOOT_FIS_CRC_CHECK
img->file_cksum = cyg_crc32((unsigned char *)flash_addr, img_size);
#endif
fis_update_directory();
}
}
 
extern void arm_fis_delete(char *);
static void
fis_delete(int argc, char *argv[])
{
char *name;
int num_reserved, i, stat;
void *err_addr;
struct fis_image_desc *img;
 
if (!scan_opts(argc, argv, 2, 0, 0, (void **)&name, OPTION_ARG_TYPE_STR, "image name"))
{
fis_usage("invalid arguments");
return;
}
#ifdef CYGHWR_REDBOOT_ARM_FLASH_SIB
// FIXME: this is somewhat half-baked
arm_fis_delete(name);
return;
#endif
img = (struct fis_image_desc *)fis_work_block;
num_reserved = 0;
#ifdef CYGOPT_REDBOOT_FIS_RESERVED_BASE
num_reserved++;
#endif
#ifdef CYGOPT_REDBOOT_FIS_REDBOOT
num_reserved++;
#endif
#ifdef CYGOPT_REDBOOT_FIS_REDBOOT_BACKUP
num_reserved++;
#endif
#ifdef CYGOPT_REDBOOT_FIS_REDBOOT_POST
num_reserved++;
#endif
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
num_reserved++;
#endif
#if 1 // And the descriptor for the descriptor table itself
num_reserved++;
#endif
 
memcpy(fis_work_block, fis_addr, fisdir_size);
img = fis_lookup(name, &i);
if (img) {
if (i < num_reserved) {
diag_printf("Sorry, '%s' is a reserved image and cannot be deleted\n", img->name);
return;
}
if (!verify_action("Delete image '%s'", name)) {
return;
}
} else {
diag_printf("No image '%s' found\n", name);
return;
}
// Erase Data blocks (free space)
if ((stat = flash_erase((void *)img->flash_base, img->size, (void **)&err_addr)) != 0) {
diag_printf("Error erasing at %p: %s\n", err_addr, flash_errmsg(stat));
} else {
img->name[0] = (unsigned char)0xFF;
fis_update_directory();
}
}
 
static void
fis_load(int argc, char *argv[])
{
char *name;
struct fis_image_desc *img;
CYG_ADDRESS mem_addr;
bool mem_addr_set = false;
bool show_cksum = false;
struct option_info opts[3];
#ifdef CYGPKG_REDBOOT_FIS_CRC_CHECK
unsigned long cksum;
#endif
int num_options;
#ifdef CYGPKG_COMPRESS_ZLIB
bool decompress = false;
#endif
 
init_opts(&opts[0], 'b', true, OPTION_ARG_TYPE_NUM,
(void **)&mem_addr, (bool *)&mem_addr_set, "memory [load] base address");
init_opts(&opts[1], 'c', false, OPTION_ARG_TYPE_FLG,
(void **)&show_cksum, (bool *)0, "display checksum");
num_options = 2;
#ifdef CYGPKG_COMPRESS_ZLIB
init_opts(&opts[num_options], 'd', false, OPTION_ARG_TYPE_FLG,
(void **)&decompress, 0, "decompress");
num_options++;
#endif
 
if (!scan_opts(argc, argv, 2, opts, num_options, (void **)&name, OPTION_ARG_TYPE_STR, "image name"))
{
fis_usage("invalid arguments");
return;
}
memcpy(fis_work_block, fis_addr, fisdir_size);
if ((img = fis_lookup(name, NULL)) == (struct fis_image_desc *)0) {
diag_printf("No image '%s' found\n", name);
return;
}
if (!mem_addr_set) {
mem_addr = img->mem_base;
}
// Load image from FLASH into RAM
if ((mem_addr < (CYG_ADDRESS)user_ram_start) ||
((mem_addr+img->data_length) >= (CYG_ADDRESS)user_ram_end)) {
diag_printf("Not a loadable image\n");
return;
}
#ifdef CYGPKG_COMPRESS_ZLIB
if (decompress) {
int err;
_pipe_t fis_load_pipe;
_pipe_t* p = &fis_load_pipe;
p->out_buf = (unsigned char*) mem_addr;
p->out_max = p->out_size = -1;
p->in_buf = (unsigned char*) img->flash_base;
p->in_avail = img->data_length;
 
err = (*_dc_init)(p);
 
if (0 == err)
err = (*_dc_inflate)(p);
 
// Free used resources, do final translation of
// error value.
err = (*_dc_close)(p, err);
 
if (0 != err && p->msg) {
diag_printf("decompression error: %s\n", p->msg);
} else {
diag_printf("Image loaded from %p-%p\n", (unsigned char *)mem_addr, p->out_buf);
}
 
// Set load address/top
load_address = mem_addr;
load_address_end = (unsigned long)p->out_buf;
 
// Reload fis directory
memcpy(fis_work_block, fis_addr, fisdir_size);
} else // dangling block
#endif
{
memcpy((void *)mem_addr, (void *)img->flash_base, img->data_length);
 
// Set load address/top
load_address = mem_addr;
load_address_end = mem_addr + img->data_length;
}
entry_address = (unsigned long)img->entry_point;
 
#ifdef CYGPKG_REDBOOT_FIS_CRC_CHECK
cksum = cyg_crc32((unsigned char *)mem_addr, img->data_length);
if (show_cksum) {
diag_printf("Checksum: 0x%08lx\n", cksum);
}
// When decompressing, leave CRC checking to decompressor
if (!decompress && img->file_cksum) {
if (cksum != img->file_cksum) {
diag_printf("** Warning - checksum failure. stored: 0x%08lx, computed: 0x%08lx\n",
img->file_cksum, cksum);
}
}
#endif
}
#endif // CYGOPT_REDBOOT_FIS
 
static void
fis_write(int argc, char *argv[])
{
int stat;
unsigned long length;
CYG_ADDRESS mem_addr, flash_addr;
bool mem_addr_set = false;
bool flash_addr_set = false;
bool length_set = false;
void *err_addr;
struct option_info opts[3];
bool prog_ok;
 
init_opts(&opts[0], 'b', true, OPTION_ARG_TYPE_NUM,
(void **)&mem_addr, (bool *)&mem_addr_set, "memory base address");
init_opts(&opts[1], 'f', true, OPTION_ARG_TYPE_NUM,
(void **)&flash_addr, (bool *)&flash_addr_set, "FLASH memory base address");
init_opts(&opts[2], 'l', true, OPTION_ARG_TYPE_NUM,
(void **)&length, (bool *)&length_set, "image length [in FLASH]");
if (!scan_opts(argc, argv, 2, opts, 3, 0, 0, 0))
{
fis_usage("invalid arguments");
return;
}
 
if (!mem_addr_set || !flash_addr_set || !length_set) {
fis_usage("required parameter missing");
return;
}
 
// Round up length to FLASH block size
#ifndef CYGPKG_HAL_MIPS // FIXME: compiler is b0rken
length = ((length + flash_block_size - 1) / flash_block_size) * flash_block_size;
#endif
if (flash_addr_set &&
((stat = flash_verify_addr((void *)flash_addr)) ||
(stat = flash_verify_addr((void *)(flash_addr+length-1))))) {
_show_invalid_flash_address(flash_addr, stat);
return;
}
if (flash_addr_set && flash_addr & (flash_block_size-1)) {
diag_printf("Invalid FLASH address: %p\n", (void *)flash_addr);
diag_printf(" must be 0x%x aligned\n", flash_block_size);
return;
}
if ((mem_addr < (CYG_ADDRESS)ram_start) ||
((mem_addr+length) >= (CYG_ADDRESS)ram_end)) {
diag_printf("** WARNING: RAM address: %p may be invalid\n", (void *)mem_addr);
diag_printf(" valid range is %p-%p\n", (void *)ram_start, (void *)ram_end);
}
// Safety check - make sure the address range is not within the code we're running
if (flash_code_overlaps((void *)flash_addr, (void *)(flash_addr+length-1))) {
diag_printf("Can't program this region - contains code in use!\n");
return;
}
if (!verify_action("* CAUTION * about to program FLASH\n at %p..%p from %p",
(void *)flash_addr, (void *)(flash_addr+length-1),
(void *)mem_addr)) {
return; // The guy gave up
}
prog_ok = true;
if (prog_ok) {
// Erase area to be programmed
if ((stat = flash_erase((void *)flash_addr, length, (void **)&err_addr)) != 0) {
diag_printf("Can't erase region at %p: %s\n", err_addr, flash_errmsg(stat));
prog_ok = false;
}
}
if (prog_ok) {
// Now program it
if ((stat = flash_program((void *)flash_addr, (void *)mem_addr, length, (void **)&err_addr)) != 0) {
diag_printf("Can't program region at %p: %s\n", err_addr, flash_errmsg(stat));
prog_ok = false;
}
}
}
 
static void
fis_erase(int argc, char *argv[])
{
int stat;
unsigned long length;
CYG_ADDRESS flash_addr;
bool flash_addr_set = false;
bool length_set = false;
void *err_addr;
struct option_info opts[2];
 
init_opts(&opts[0], 'f', true, OPTION_ARG_TYPE_NUM,
(void **)&flash_addr, (bool *)&flash_addr_set, "FLASH memory base address");
init_opts(&opts[1], 'l', true, OPTION_ARG_TYPE_NUM,
(void **)&length, (bool *)&length_set, "length");
if (!scan_opts(argc, argv, 2, opts, 2, (void **)0, 0, ""))
{
fis_usage("invalid arguments");
return;
}
 
if (!flash_addr_set || !length_set) {
fis_usage("missing argument");
return;
}
if (flash_addr_set &&
((stat = flash_verify_addr((void *)flash_addr)) ||
(stat = flash_verify_addr((void *)(flash_addr+length-1))))) {
_show_invalid_flash_address(flash_addr, stat);
return;
}
if (flash_addr_set && flash_addr & (flash_block_size-1)) {
diag_printf("Invalid FLASH address: %p\n", (void *)flash_addr);
diag_printf(" must be 0x%x aligned\n", flash_block_size);
return;
}
// Safety check - make sure the address range is not within the code we're running
if (flash_code_overlaps((void *)flash_addr, (void *)(flash_addr+length-1))) {
diag_printf("Can't erase this region - contains code in use!\n");
return;
}
if ((stat = flash_erase((void *)flash_addr, length, (void **)&err_addr)) != 0) {
diag_printf("Error erasing at %p: %s\n", err_addr, flash_errmsg(stat));
}
}
 
#ifdef CYGHWR_IO_FLASH_BLOCK_LOCKING
 
static void
fis_lock(int argc, char *argv[])
{
char *name;
int stat;
unsigned long length;
CYG_ADDRESS flash_addr;
bool flash_addr_set = false;
bool length_set = false;
void *err_addr;
struct option_info opts[2];
 
init_opts(&opts[0], 'f', true, OPTION_ARG_TYPE_NUM,
(void **)&flash_addr, (bool *)&flash_addr_set, "FLASH memory base address");
init_opts(&opts[1], 'l', true, OPTION_ARG_TYPE_NUM,
(void **)&length, (bool *)&length_set, "length");
if (!scan_opts(argc, argv, 2, opts, 2, (void **)&name, OPTION_ARG_TYPE_STR, "image name"))
{
fis_usage("invalid arguments");
return;
}
 
/* Get parameters from image if specified */
if (name) {
struct fis_image_desc *img;
memcpy(fis_work_block, fis_addr, fisdir_size);
if ((img = fis_lookup(name, NULL)) == (struct fis_image_desc *)0) {
diag_printf("No image '%s' found\n", name);
return;
}
 
flash_addr = img->flash_base;
length = img->size;
} else if (!flash_addr_set || !length_set) {
fis_usage("missing argument");
return;
}
if (flash_addr_set &&
((stat = flash_verify_addr((void *)flash_addr)) ||
(stat = flash_verify_addr((void *)(flash_addr+length-1))))) {
_show_invalid_flash_address(flash_addr, stat);
return;
}
if ((stat = flash_lock((void *)flash_addr, length, (void **)&err_addr)) != 0) {
diag_printf("Error locking at %p: %s\n", err_addr, flash_errmsg(stat));
}
}
 
static void
fis_unlock(int argc, char *argv[])
{
char *name;
int stat;
unsigned long length;
CYG_ADDRESS flash_addr;
bool flash_addr_set = false;
bool length_set = false;
void *err_addr;
struct option_info opts[2];
 
init_opts(&opts[0], 'f', true, OPTION_ARG_TYPE_NUM,
(void **)&flash_addr, (bool *)&flash_addr_set, "FLASH memory base address");
init_opts(&opts[1], 'l', true, OPTION_ARG_TYPE_NUM,
(void **)&length, (bool *)&length_set, "length");
if (!scan_opts(argc, argv, 2, opts, 2, (void **)&name, OPTION_ARG_TYPE_STR, "image name"))
{
fis_usage("invalid arguments");
return;
}
 
if (name) {
struct fis_image_desc *img;
memcpy(fis_work_block, fis_addr, fisdir_size);
if ((img = fis_lookup(name, NULL)) == (struct fis_image_desc *)0) {
diag_printf("No image '%s' found\n", name);
return;
}
 
flash_addr = img->flash_base;
length = img->size;
} else if (!flash_addr_set || !length_set) {
fis_usage("missing argument");
return;
}
if (flash_addr_set &&
((stat = flash_verify_addr((void *)flash_addr)) ||
(stat = flash_verify_addr((void *)(flash_addr+length-1))))) {
_show_invalid_flash_address(flash_addr, stat);
return;
}
 
if ((stat = flash_unlock((void *)flash_addr, length, (void **)&err_addr)) != 0) {
diag_printf("Error unlocking at %p: %s\n", err_addr, flash_errmsg(stat));
}
}
#endif
 
// This is set non-zero if the FLASH subsystem has successfully been initialized
static int __flash_init = 0;
 
void
_flash_info(void)
{
if (!__flash_init) return;
diag_printf("FLASH: %p - %p, %d blocks of %p bytes each.\n",
flash_start, (CYG_ADDRWORD)flash_end + 1, flash_num_blocks, (void *)flash_block_size);
}
 
static bool
do_flash_init(void)
{
int stat;
 
if (!__flash_init) {
if ((stat = flash_init((void *)(workspace_end-FLASH_MIN_WORKSPACE),
FLASH_MIN_WORKSPACE, diag_printf)) != 0) {
diag_printf("FLASH: driver init failed: %s\n", flash_errmsg(stat));
return false;
}
flash_get_limits((void *)0, (void **)&flash_start, (void **)&flash_end);
// Keep 'end' address as last valid location, to avoid wrap around problems
flash_end = (void *)((CYG_ADDRESS)flash_end - 1);
flash_get_block_info(&flash_block_size, &flash_num_blocks);
workspace_end = (unsigned char *)(workspace_end-FLASH_MIN_WORKSPACE);
#ifdef CYGOPT_REDBOOT_FIS
# ifdef CYGOPT_REDBOOT_FIS_ZLIB_COMMON_BUFFER
fis_work_block = fis_zlib_common_buffer;
if(CYGNUM_REDBOOT_FIS_ZLIB_COMMON_BUFFER_SIZE < flash_block_size) {
diag_printf("FLASH: common buffer too small\n");
workspace_end += FLASH_MIN_WORKSPACE;
return false;
}
# else
workspace_end = (unsigned char *)(workspace_end-flash_block_size);
fis_work_block = workspace_end;
# endif
fisdir_size = flash_block_size;
if (CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK < 0) {
fis_addr = (void *)((CYG_ADDRESS)flash_end + 1 +
(CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK*flash_block_size));
} else {
fis_addr = (void *)((CYG_ADDRESS)flash_start +
(CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK*flash_block_size));
}
#endif
__flash_init = 1;
}
return true;
}
 
// Wrapper to avoid compiler warnings
static void
_do_flash_init(void)
{
do_flash_init();
}
 
#ifndef CYGOPT_REDBOOT_FLASH_CONFIG
RedBoot_init(_do_flash_init, RedBoot_INIT_FIRST);
#endif
 
static void
do_fis(int argc, char *argv[])
{
struct cmd *cmd;
 
if (argc < 2) {
fis_usage("too few arguments");
return;
}
if (!do_flash_init()) {
diag_printf("Sorry, no FLASH memory is available\n");
return;
}
if ((cmd = cmd_search(__FIS_cmds_TAB__, &__FIS_cmds_TAB_END__,
argv[1])) != (struct cmd *)0) {
(cmd->fun)(argc, argv);
return;
}
fis_usage("unrecognized command");
}
 
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
 
static bool config_ok;
 
#define CONFIG_KEY1 0x0BADFACE
#define CONFIG_KEY2 0xDEADDEAD
 
#define CONFIG_DONE 0
#define CONFIG_ABORT -1
#define CONFIG_CHANGED 1
#define CONFIG_OK 2
#define CONFIG_BACK 3
#define CONFIG_BAD 4
 
// Note: the following options are related. If 'boot_script' is false, then
// the other values are used in the configuration. Because of the way
// that configuration tables are generated, they should have names which
// are related. The configuration options will show up lexicographically
// ordered, thus the peculiar naming.
RedBoot_config_option("Run script at boot",
boot_script,
ALWAYS_ENABLED, true,
CONFIG_BOOL,
false
);
RedBoot_config_option("Boot script",
boot_script_data,
"boot_script", true,
CONFIG_SCRIPT,
""
);
// Some preprocessor magic for building the [constant] prompt string
#define __cat(s1,c2,s3) s1 #c2 s3
#define _cat(s1,c2,s3) __cat(s1,c2,s3)
RedBoot_config_option(_cat("Boot script timeout (",
CYGNUM_REDBOOT_BOOT_SCRIPT_TIMEOUT_RESOLUTION,
"ms resolution)"),
boot_script_timeout,
"boot_script", true,
CONFIG_INT,
0
);
#undef __cat
#undef _cat
 
#ifdef CYGSEM_REDBOOT_VARIABLE_BAUD_RATE
RedBoot_config_option("Console baud rate",
console_baud_rate,
ALWAYS_ENABLED, true,
CONFIG_INT,
CYGNUM_HAL_VIRTUAL_VECTOR_CONSOLE_CHANNEL_BAUD
);
#endif
 
CYG_HAL_TABLE_BEGIN( __CONFIG_options_TAB__, RedBoot_config_options);
CYG_HAL_TABLE_END( __CONFIG_options_TAB_END__, RedBoot_config_options);
 
extern struct config_option __CONFIG_options_TAB__[], __CONFIG_options_TAB_END__[];
 
//
// Layout of config data
// Each data item is variable length, with the name, type and dependencies
// encoded into the object.
// offset contents
// 0 data type
// 1 length of name (N)
// 2 enable sense
// 3 length of enable key (M)
// 4 key name
// N+4 enable key
// M+N+4 data value
//
 
#define CONFIG_OBJECT_TYPE(dp) (dp)[0]
#define CONFIG_OBJECT_KEYLEN(dp) (dp)[1]
#define CONFIG_OBJECT_ENABLE_SENSE(dp) (dp)[2]
#define CONFIG_OBJECT_ENABLE_KEYLEN(dp) (dp)[3]
#define CONFIG_OBJECT_KEY(dp) ((dp)+4)
#define CONFIG_OBJECT_ENABLE_KEY(dp) ((dp)+4+CONFIG_OBJECT_KEYLEN(dp))
#define CONFIG_OBJECT_VALUE(dp) ((dp)+4+CONFIG_OBJECT_KEYLEN(dp)+CONFIG_OBJECT_ENABLE_KEYLEN(dp))
 
#define LIST_OPT_LIST_ONLY (1)
#define LIST_OPT_NICKNAMES (2)
#define LIST_OPT_FULLNAMES (4)
#define LIST_OPT_DUMBTERM (8)
 
static void config_init(void);
 
static int
get_config(unsigned char *dp, char *title, int list_opt, char *newvalue )
{
char line[256], hold_line[256], *sp, *lp;
int ret;
bool hold_bool_val, new_bool_val, enable;
unsigned long hold_int_val, new_int_val;
#ifdef CYGPKG_REDBOOT_NETWORKING
in_addr_t hold_ip_val, new_ip_val;
enet_addr_t hold_esa_val;
int esa_ptr;
char *esp;
#endif
void *val_ptr;
int type;
 
if (CONFIG_OBJECT_ENABLE_KEYLEN(dp)) {
flash_get_config(CONFIG_OBJECT_ENABLE_KEY(dp), &enable, CONFIG_BOOL);
if (((bool)CONFIG_OBJECT_ENABLE_SENSE(dp) && !enable) ||
(!(bool)CONFIG_OBJECT_ENABLE_SENSE(dp) && enable)) {
return CONFIG_OK; // Disabled field
}
}
lp = line; *lp = '\0';
val_ptr = (void *)CONFIG_OBJECT_VALUE(dp);
if (LIST_OPT_NICKNAMES & list_opt)
diag_printf("%s: ", CONFIG_OBJECT_KEY(dp));
if (LIST_OPT_FULLNAMES & list_opt) {
if (title != (char *)NULL) {
diag_printf("%s: ", title);
} else {
diag_printf("%s: ", CONFIG_OBJECT_KEY(dp));
}
}
switch (type = CONFIG_OBJECT_TYPE(dp)) {
case CONFIG_BOOL:
memcpy(&hold_bool_val, val_ptr, sizeof(bool));
lp += diag_sprintf(lp, "%s", hold_bool_val ? "true" : "false");
break;
case CONFIG_INT:
memcpy(&hold_int_val, val_ptr, sizeof(unsigned long));
lp += diag_sprintf(lp, "%ld", hold_int_val);
break;
#ifdef CYGPKG_REDBOOT_NETWORKING
case CONFIG_IP:
lp += diag_sprintf(lp, "%s", inet_ntoa((in_addr_t *)val_ptr));
if (0 == strcmp("0.0.0.0", line) && !(LIST_OPT_LIST_ONLY & list_opt)) {
// then we have a deeply unhelpful starting text - kill it off
// (unless we are just listing all values)
lp = line; *lp = '\0';
}
break;
case CONFIG_ESA:
for (esa_ptr = 0; esa_ptr < sizeof(enet_addr_t); esa_ptr++) {
lp += diag_sprintf(lp, "0x%02X", ((unsigned char *)val_ptr)[esa_ptr]);
if (esa_ptr < (sizeof(enet_addr_t)-1)) lp += diag_sprintf(lp, ":");
}
break;
#endif
case CONFIG_STRING:
lp += diag_sprintf(lp, "%s", (unsigned char *)val_ptr);
break;
case CONFIG_SCRIPT:
diag_printf("\n");
sp = lp = (unsigned char *)val_ptr;
while (*sp) {
while (*lp != '\n') lp++;
*lp = '\0';
diag_printf(".. %s\n", sp);
*lp++ = '\n';
sp = lp;
}
break;
}
if (LIST_OPT_LIST_ONLY & list_opt) {
diag_printf("%s\n", line);
return CONFIG_OK;
}
if (type != CONFIG_SCRIPT) {
if (NULL != newvalue) {
ret = strlen(newvalue);
if (ret > sizeof(line))
return CONFIG_BAD;
strcpy(hold_line, line); // Hold the old value for comparison
strcpy(line, newvalue);
diag_printf("Setting to %s\n", newvalue);
} else {
// read from terminal
strcpy(hold_line, line);
if (LIST_OPT_DUMBTERM & list_opt) {
diag_printf( (CONFIG_STRING == type ?
"%s > " :
"%s ? " ), line);
*line = '\0';
}
ret = _rb_gets_preloaded(line, sizeof(line), 0);
}
if (ret < 0) return CONFIG_ABORT;
// empty input - leave value untouched (else DNS goes away for a
// minute to try to look it up) but we must accept empty value for strings.
if (0 == line[0] && CONFIG_STRING != type) return CONFIG_OK;
if (strcmp(line, hold_line) == 0) return CONFIG_OK; // Just a CR - leave value untouched
lp = &line[strlen(line)-1];
if (*lp == '.') return CONFIG_DONE;
if (*lp == '^') return CONFIG_BACK;
}
switch (type) {
case CONFIG_BOOL:
memcpy(&hold_bool_val, val_ptr, sizeof(bool));
if (!parse_bool(line, &new_bool_val)) {
return CONFIG_BAD;
}
if (hold_bool_val != new_bool_val) {
memcpy(val_ptr, &new_bool_val, sizeof(bool));
return CONFIG_CHANGED;
} else {
return CONFIG_OK;
}
break;
case CONFIG_INT:
memcpy(&hold_int_val, val_ptr, sizeof(unsigned long));
if (!parse_num(line, &new_int_val, 0, 0)) {
return CONFIG_BAD;
}
if (hold_int_val != new_int_val) {
memcpy(val_ptr, &new_int_val, sizeof(unsigned long));
return CONFIG_CHANGED;
} else {
return CONFIG_OK;
}
break;
#ifdef CYGPKG_REDBOOT_NETWORKING
case CONFIG_IP:
memcpy(&hold_ip_val.s_addr, &((in_addr_t *)val_ptr)->s_addr, sizeof(in_addr_t));
if (!_gethostbyname(line, &new_ip_val)) {
return CONFIG_BAD;
}
if (hold_ip_val.s_addr != new_ip_val.s_addr) {
memcpy(val_ptr, &new_ip_val, sizeof(in_addr_t));
return CONFIG_CHANGED;
} else {
return CONFIG_OK;
}
break;
case CONFIG_ESA:
memcpy(&hold_esa_val, val_ptr, sizeof(enet_addr_t));
esp = line;
for (esa_ptr = 0; esa_ptr < sizeof(enet_addr_t); esa_ptr++) {
unsigned long esa_byte;
if (!parse_num(esp, &esa_byte, &esp, ":")) {
memcpy(val_ptr, &hold_esa_val, sizeof(enet_addr_t));
return CONFIG_BAD;
}
((unsigned char *)val_ptr)[esa_ptr] = esa_byte;
}
return CONFIG_CHANGED;
break;
#endif
case CONFIG_SCRIPT:
// Assume it always changes
sp = (unsigned char *)val_ptr;
diag_printf("Enter script, terminate with empty line\n");
while (true) {
*sp = '\0';
diag_printf(">> ");
ret = _rb_gets(line, sizeof(line), 0);
if (ret < 0) return CONFIG_ABORT;
if (strlen(line) == 0) break;
lp = line;
while (*lp) {
*sp++ = *lp++;
}
*sp++ = '\n';
}
break;
case CONFIG_STRING:
if (strlen(line) >= MAX_STRING_LENGTH) {
diag_printf("Sorry, value is too long\n");
return CONFIG_BAD;
}
strcpy((unsigned char *)val_ptr, line);
break;
}
return CONFIG_CHANGED;
}
 
//
// Manage configuration information with the FLASH
//
 
static int
config_length(int type)
{
switch (type) {
case CONFIG_BOOL:
return sizeof(bool);
case CONFIG_INT:
return sizeof(unsigned long);
#ifdef CYGPKG_REDBOOT_NETWORKING
case CONFIG_IP:
return sizeof(in_addr_t);
case CONFIG_ESA:
// Would like this to be sizeof(enet_addr_t), but that causes much
// pain since it fouls the alignment of data which follows.
return 8;
#endif
case CONFIG_STRING:
return MAX_STRING_LENGTH;
case CONFIG_SCRIPT:
return MAX_SCRIPT_LENGTH;
default:
return 0;
}
}
 
static cmd_fun do_flash_config;
RedBoot_cmd("fconfig",
"Manage configuration kept in FLASH memory",
"[-i] [-l] [-n] [-f] [-d] | [-d] nickname [value]",
do_flash_config
);
 
static void
do_flash_config(int argc, char *argv[])
{
bool need_update = false;
struct config_option *optend = __CONFIG_options_TAB_END__;
struct config_option *opt = __CONFIG_options_TAB__;
struct option_info opts[5];
bool list_only;
bool nicknames;
bool fullnames;
bool dumbterminal;
int list_opt = 0;
unsigned char *dp;
int len, ret;
char *title;
char *onlyone = NULL;
char *onevalue = NULL;
bool doneone = false;
bool init = false;
 
if (!__flash_init) {
diag_printf("Sorry, no FLASH memory is available\n");
return;
}
memcpy(backup_config, config, sizeof(struct _config));
script = (unsigned char *)0;
 
init_opts(&opts[0], 'l', false, OPTION_ARG_TYPE_FLG,
(void **)&list_only, (bool *)0, "list configuration only");
init_opts(&opts[1], 'n', false, OPTION_ARG_TYPE_FLG,
(void **)&nicknames, (bool *)0, "show nicknames");
init_opts(&opts[2], 'f', false, OPTION_ARG_TYPE_FLG,
(void **)&fullnames, (bool *)0, "show full names");
init_opts(&opts[3], 'i', false, OPTION_ARG_TYPE_FLG,
(void **)&init, (bool *)0, "initialize configuration database");
init_opts(&opts[4], 'd', false, OPTION_ARG_TYPE_FLG,
(void **)&dumbterminal, (bool *)0, "dumb terminal: no clever edits");
 
// First look to see if we are setting or getting a single option
// by just quoting its nickname
if ( (2 == argc && '-' != argv[1][0]) ||
(3 == argc && '-' != argv[1][0] && '-' != argv[2][0])) {
// then the command was "fconfig foo [value]"
onlyone = argv[1];
onevalue = (3 == argc) ? argv[2] : NULL;
list_opt = LIST_OPT_NICKNAMES;
}
// Next see if we are setting or getting a single option with a dumb
// terminal invoked, ie. no line editing.
else if (3 == argc &&
'-' == argv[1][0] && 'd' == argv[1][1] && 0 == argv[1][2] &&
'-' != argv[2][0]) {
// then the command was "fconfig -d foo"
onlyone = argv[2];
onevalue = NULL;
list_opt = LIST_OPT_NICKNAMES | LIST_OPT_DUMBTERM;
}
else {
if (!scan_opts(argc, argv, 1, opts, 5, 0, 0, ""))
return;
list_opt |= list_only ? LIST_OPT_LIST_ONLY : 0;
list_opt |= nicknames ? LIST_OPT_NICKNAMES : LIST_OPT_FULLNAMES;
list_opt |= fullnames ? LIST_OPT_FULLNAMES : 0;
list_opt |= dumbterminal ? LIST_OPT_DUMBTERM : 0;
}
 
if (init && verify_action("Initialize non-volatile configuration")) {
config_init();
need_update = true;
}
 
dp = &config->config_data[0];
while (dp < &config->config_data[sizeof(config->config_data)]) {
if (CONFIG_OBJECT_TYPE(dp) == CONFIG_EMPTY) {
break;
}
len = 4 + CONFIG_OBJECT_KEYLEN(dp) + CONFIG_OBJECT_ENABLE_KEYLEN(dp) +
config_length(CONFIG_OBJECT_TYPE(dp));
// Provide a title for well known [i.e. builtin] objects
title = (char *)NULL;
opt = __CONFIG_options_TAB__;
while (opt != optend) {
if (strcmp(opt->key, CONFIG_OBJECT_KEY(dp)) == 0) {
title = opt->title;
break;
}
opt++;
}
if ( onlyone && 0 != strcmp(CONFIG_OBJECT_KEY(dp), onlyone) )
ret = CONFIG_OK; // skip this entry
else {
doneone = true;
ret = get_config(dp, title, list_opt, onevalue); // do this opt
}
switch (ret) {
case CONFIG_DONE:
goto done;
case CONFIG_ABORT:
memcpy(config, backup_config, sizeof(struct _config));
return;
case CONFIG_CHANGED:
need_update = true;
case CONFIG_OK:
dp += len;
break;
case CONFIG_BACK:
dp = &config->config_data[0];
continue;
case CONFIG_BAD:
// Nothing - make him do it again
diag_printf ("** invalid entry\n");
onevalue = NULL; // request a good value be typed in - or abort/whatever
}
}
 
done:
if (NULL != onlyone && !doneone) {
#ifdef CYGSEM_REDBOOT_ALLOW_DYNAMIC_FLASH_CONFIG_DATA
if (verify_action("** entry '%s' not found - add", onlyone)) {
struct config_option opt;
diag_printf("Trying to add value\n");
}
#else
diag_printf("** entry '%s' not found", onlyone);
#endif
}
if (!need_update)
return;
flash_write_config();
}
 
 
#ifdef CYGSEM_REDBOOT_FLASH_ALIASES
static cmd_fun do_alias;
RedBoot_cmd("alias",
"Manage aliases kept in FLASH memory",
"name [value]",
do_alias
);
 
static void
make_alias(char *alias, char *name)
{
diag_sprintf(alias, "alias/%s", name);
}
 
static void
do_alias(int argc, char *argv[])
{
char name[80];
char *val;
struct config_option opt;
 
switch (argc) {
case 2:
make_alias(name, argv[1]);
if (flash_get_config(name, &val, CONFIG_STRING)) {
diag_printf("'%s' = '%s'\n", argv[1], val);
} else {
diag_printf("'%s' not found\n", argv[1]);
}
break;
case 3:
if (strlen(argv[2]) >= MAX_STRING_LENGTH) {
diag_printf("Sorry, value is too long\n");
break;
}
make_alias(name, argv[1]);
opt.type = CONFIG_STRING;
opt.enable = (char *)0;
opt.enable_sense = 1;
opt.key = name;
opt.dflt = (CYG_ADDRESS)argv[2];
flash_add_config(&opt, true);
break;
default:
diag_printf("usage: alias name [value]\n");
}
}
 
// Lookup an alias. First try plain string aliases. If that fails try
// other types so allowing access to all configured values. This allows
// for alias (macro) expansion of normal 'fconfig' data, such as the
// board IP address.
char *
flash_lookup_alias(char *alias, char *alias_buf)
{
char name[80];
char *val;
unsigned char * dp;
void *val_ptr;
int type;
bool hold_bool_val;
long hold_long_val;
#ifdef CYGPKG_REDBOOT_NETWORKING
int esa_ptr;
#endif
 
make_alias(name, alias);
if (flash_get_config(name, &val, CONFIG_STRING)) {
return val;
} else {
dp = flash_lookup_config(alias);
if (dp) {
val_ptr = (void *)CONFIG_OBJECT_VALUE(dp);
switch (type = CONFIG_OBJECT_TYPE(dp)) {
case CONFIG_BOOL:
memcpy(&hold_bool_val, val_ptr, sizeof(bool));
diag_sprintf(alias_buf, "%s", hold_bool_val ? "true" : "false");
break;
case CONFIG_INT:
memcpy(&hold_long_val, val_ptr, sizeof(unsigned long));
diag_sprintf(alias_buf,"%ld", hold_long_val);
break;
#ifdef CYGPKG_REDBOOT_NETWORKING
case CONFIG_IP:
diag_sprintf(alias_buf,"%s", inet_ntoa((in_addr_t *)val_ptr));
break;
case CONFIG_ESA:
for (esa_ptr = 0; esa_ptr < sizeof(enet_addr_t); esa_ptr++) {
diag_sprintf(alias_buf+(3*esa_ptr), "0x%02X", ((unsigned char *)val_ptr)[esa_ptr]);
if (esa_ptr < (sizeof(enet_addr_t)-1)) diag_printf(":");
}
break;
#endif
case CONFIG_SCRIPT:
return (char *) val_ptr;
break;
default:
return (char *)NULL;
}
return alias_buf;
}
return (char *)NULL;
}
}
 
#endif // CYGSEM_REDBOOT_FLASH_ALIASES
 
//
// Write the in-memory copy of the configuration data to the flash device.
//
void
flash_write_config(void)
{
#ifndef CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
int stat;
void *err_addr;
#endif
 
config->len = sizeof(struct _config);
config->key1 = CONFIG_KEY1;
config->key2 = CONFIG_KEY2;
config->cksum = cyg_crc32((unsigned char *)config, sizeof(struct _config)-sizeof(config->cksum));
if (verify_action("Update RedBoot non-volatile configuration")) {
#ifdef CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
memcpy(fis_work_block, fis_addr, fisdir_size);
fis_update_directory();
#else // CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
#ifdef CYGSEM_REDBOOT_FLASH_LOCK_SPECIAL
// Insure [quietly] that the config page is unlocked before trying to update
flash_unlock((void *)cfg_base, cfg_size, (void **)&err_addr);
#endif
if ((stat = flash_erase(cfg_base, cfg_size, (void **)&err_addr)) != 0) {
diag_printf(" initialization failed at %p: %s\n", err_addr, flash_errmsg(stat));
} else {
if ((stat = flash_program(cfg_base, (void *)config, sizeof(struct _config),
(void **)&err_addr)) != 0) {
diag_printf("Error writing config data at %p: %s\n",
err_addr, flash_errmsg(stat));
}
}
#ifdef CYGSEM_REDBOOT_FLASH_LOCK_SPECIAL
// Insure [quietly] that the config data is locked after the update
flash_lock((void *)cfg_base, cfg_size, (void **)&err_addr);
#endif
#endif
}
}
 
//
// Find the configuration entry for a particular key
//
static unsigned char *
flash_lookup_config(char *key)
{
unsigned char *dp;
int len;
 
if (!config_ok) return (unsigned char *)NULL;
 
dp = &config->config_data[0];
while (dp < &config->config_data[sizeof(config->config_data)]) {
len = 4 + CONFIG_OBJECT_KEYLEN(dp) + CONFIG_OBJECT_ENABLE_KEYLEN(dp) +
config_length(CONFIG_OBJECT_TYPE(dp));
if (strcmp(key, CONFIG_OBJECT_KEY(dp)) == 0) {
return dp;
}
dp += len;
}
// diag_printf("Can't find config data for '%s'\n", key);
return false;
}
 
//
// Retrieve a data object from the data base (in memory copy)
//
bool
flash_get_config(char *key, void *val, int type)
{
unsigned char *dp;
void *val_ptr;
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
struct _config *save_config = 0;
#endif
 
if (!config_ok) return false;
 
if ((dp = flash_lookup_config(key)) != (unsigned char *)NULL) {
if (CONFIG_OBJECT_TYPE(dp) == type) {
val_ptr = (void *)CONFIG_OBJECT_VALUE(dp);
switch (type) {
// Note: the data may be unaligned in the configuration data
case CONFIG_BOOL:
memcpy(val, val_ptr, sizeof(bool));
break;
case CONFIG_INT:
memcpy(val, val_ptr, sizeof(unsigned long));
break;
#ifdef CYGPKG_REDBOOT_NETWORKING
case CONFIG_IP:
memcpy(val, val_ptr, sizeof(in_addr_t));
break;
case CONFIG_ESA:
memcpy(val, val_ptr, sizeof(enet_addr_t));
break;
#endif
case CONFIG_STRING:
case CONFIG_SCRIPT:
// Just return a pointer to the script/line
*(unsigned char **)val = (unsigned char *)val_ptr;
break;
}
} else {
diag_printf("Request for config value '%s' - wrong type\n", key);
}
return true;
}
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
// Did not find key. Is configuration data valid?
// Check to see if the config data is valid, if not, revert to
// readonly mode, by setting config to readonly_config. We
// will set it back before we leave this function.
if ( (config != readonly_config) && ((cyg_crc32((unsigned char *)config,
sizeof(struct _config)-sizeof(config->cksum)) != config->cksum) ||
(config->key1 != CONFIG_KEY1)|| (config->key2 != CONFIG_KEY2))) {
save_config = config;
config = readonly_config;
if ((cyg_crc32((unsigned char *)config,
sizeof(struct _config)-sizeof(config->cksum)) != config->cksum) ||
(config->key1 != CONFIG_KEY1)|| (config->key2 != CONFIG_KEY2)) {
diag_printf("FLASH configuration checksum error or invalid key\n");
config = save_config;
return false;
}
else{
diag_printf("Getting config information in READONLY mode\n");
return flash_get_config(key, val, type);
}
}
#endif
return false;
}
 
//
// Copy data into the config area
//
static void
flash_config_insert_value(unsigned char *dp, struct config_option *opt)
{
switch (opt->type) {
// Note: the data may be unaligned in the configuration data
case CONFIG_BOOL:
memcpy(dp, (void *)&opt->dflt, sizeof(bool));
break;
case CONFIG_INT:
memcpy(dp, (void *)&opt->dflt, sizeof(unsigned long));
break;
#ifdef CYGPKG_REDBOOT_NETWORKING
case CONFIG_IP:
memcpy(dp, (void *)&opt->dflt, sizeof(in_addr_t));
break;
case CONFIG_ESA:
memcpy(dp, (void *)&opt->dflt, sizeof(enet_addr_t));
break;
#endif
case CONFIG_STRING:
memcpy(dp, (void *)opt->dflt, config_length(CONFIG_STRING));
break;
case CONFIG_SCRIPT:
break;
}
}
 
//
// Add a new option to the database
//
bool
flash_add_config(struct config_option *opt, bool update)
{
unsigned char *dp, *kp;
int len, elen, size;
 
// If data item is already present, just update it
// Note: only the data value can be thusly changed
if ((dp = flash_lookup_config(opt->key)) != (unsigned char *)NULL) {
flash_config_insert_value(CONFIG_OBJECT_VALUE(dp), opt);
if (update) {
flash_write_config();
}
return true;
}
// Add the data item
dp = &config->config_data[0];
size = 0;
while (size < sizeof(config->config_data)) {
if (CONFIG_OBJECT_TYPE(dp) == CONFIG_EMPTY) {
kp = opt->key;
len = strlen(kp) + 1;
size += len + 2 + 2 + config_length(opt->type);
if (opt->enable) {
elen = strlen(opt->enable) + 1;
size += elen;
} else {
elen = 0;
}
if (size > sizeof(config->config_data)) {
break;
}
CONFIG_OBJECT_TYPE(dp) = opt->type;
CONFIG_OBJECT_KEYLEN(dp) = len;
CONFIG_OBJECT_ENABLE_SENSE(dp) = opt->enable_sense;
CONFIG_OBJECT_ENABLE_KEYLEN(dp) = elen;
dp = CONFIG_OBJECT_KEY(dp);
while (*kp) *dp++ += *kp++;
*dp++ = '\0';
if (elen) {
kp = opt->enable;
while (*kp) *dp++ += *kp++;
*dp++ = '\0';
}
flash_config_insert_value(dp, opt);
if (update) {
flash_write_config();
}
return true;
} else {
len = 4 + CONFIG_OBJECT_KEYLEN(dp) + CONFIG_OBJECT_ENABLE_KEYLEN(dp) +
config_length(CONFIG_OBJECT_TYPE(dp));
dp += len;
size += len;
}
}
diag_printf("No space to add '%s'\n", opt->key);
return false;
}
 
//
// Reset/initialize configuration data - used only when starting from scratch
//
static void
config_init(void)
{
// Well known option strings
struct config_option *optend = __CONFIG_options_TAB_END__;
struct config_option *opt = __CONFIG_options_TAB__;
 
memset(config, 0, sizeof(struct _config));
while (opt != optend) {
if (!flash_add_config(opt, false)) {
return;
}
opt++;
}
config_ok = true;
}
 
//
// Attempt to get configuration information from the FLASH.
// If available (i.e. good checksum, etc), initialize "known"
// values for later use.
//
static void
load_flash_config(void)
{
bool use_boot_script;
 
config_ok = false;
script = (unsigned char *)0;
if (!do_flash_init()) return;
config = (struct _config *)(workspace_end-sizeof(struct _config));
backup_config = (struct _config *)((CYG_ADDRESS)config-sizeof(struct _config));
workspace_end = (unsigned char *)backup_config;
cfg_size = (flash_block_size > sizeof(struct _config)) ?
sizeof(struct _config) :
_rup(sizeof(struct _config), flash_block_size);
#ifdef CYGSEM_REDBOOT_FLASH_COMBINED_FIS_AND_CONFIG
cfg_size = _rup(cfg_size, sizeof(struct fis_image_desc));
if ((flash_block_size-cfg_size) < 8*sizeof(struct fis_image_desc)) {
// Too bad this can't be checked at compile/build time
diag_printf("Sorry, FLASH config exceeds available space in FIS directory\n");
return;
}
fisdir_size = flash_block_size - cfg_size;
cfg_base = (void *)(((CYG_ADDRESS)fis_addr + flash_block_size) - cfg_size);
#else
if (CYGNUM_REDBOOT_FLASH_CONFIG_BLOCK < 0) {
cfg_base = (void *)((CYG_ADDRESS)flash_end + 1 -
_rup(_rup((-CYGNUM_REDBOOT_FLASH_CONFIG_BLOCK*flash_block_size), cfg_size), flash_block_size));
} else {
cfg_base = (void *)((CYG_ADDRESS)flash_start +
_rup(_rup((CYGNUM_REDBOOT_FLASH_CONFIG_BLOCK*flash_block_size), cfg_size), flash_block_size));
}
#endif
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG_READONLY_FALLBACK
readonly_config = cfg_base;
#endif
memcpy(config, cfg_base, sizeof(struct _config));
if ((cyg_crc32((unsigned char *)config,
sizeof(struct _config)-sizeof(config->cksum)) != config->cksum) ||
(config->key1 != CONFIG_KEY1)|| (config->key2 != CONFIG_KEY2)) {
diag_printf("FLASH configuration checksum error or invalid key\n");
config_init();
return;
}
config_ok = true;
flash_get_config("boot_script", &use_boot_script, CONFIG_BOOL);
if (use_boot_script) {
flash_get_config("boot_script_data", &script, CONFIG_SCRIPT);
flash_get_config("boot_script_timeout", &script_timeout, CONFIG_INT);
}
#ifdef CYGSEM_REDBOOT_VARIABLE_BAUD_RATE
if (flash_get_config("console_baud_rate", &console_baud_rate, CONFIG_INT)) {
extern int set_console_baud_rate(int);
set_console_baud_rate(console_baud_rate);
}
#endif
}
 
RedBoot_init(load_flash_config, RedBoot_INIT_FIRST);
 
#endif // CYGSEM_REDBOOT_FLASH_CONFIG
 
// EOF flash.c

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.