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 *)ð_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, ð_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, ð_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(ð_hdr.destination, dest, sizeof(enet_addr_t)); |
memcpy(ð_hdr.source, __local_enet_addr, sizeof(enet_addr_t)); |
eth_hdr.type = htons(eth_type); |
|
eth_drv_write((char *)ð_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 |