Commit d5f620e9 authored by Daniele Lacamera's avatar Daniele Lacamera

Inclusion of IPv6 modules - work in progress

parent a349e291
......@@ -35,8 +35,8 @@ OLSR?=1
SLAACV4?=1
#IPv6 related
IPV6?=0
ICMP6?=0
IPV6?=1
ICMP6?=1
CFLAGS=-Iinclude -Imodules -Wall -Wdeclaration-after-statement -W -Wextra -Wshadow -Wcast-qual -Wwrite-strings -Wmissing-field-initializers $(EXTRA_CFLAGS)
# extra flags recommanded by TIOBE TICS framework to score an A on compiler warnings
......@@ -213,11 +213,13 @@ posix: all $(POSIX_OBJ)
TEST_ELF= test/picoapp.elf
TEST6_ELF= test/picoapp6.elf
test: posix $(TEST_ELF) $(TEST_OBJ)
@mkdir -p $(PREFIX)/test/
@rm test/*.o
@mv test/*.elf $(PREFIX)/test
@cp $(PREFIX)/$(TEST_ELF) $(PREFIX)/$(TEST6_ELF)
tst: test
......
This diff is collapsed.
/*********************************************************************
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
See LICENSE and COPYING for usage.
.
*********************************************************************/
#ifndef _INCLUDE_PICO_ICMP6
#define _INCLUDE_PICO_ICMP6
#include "pico_addressing.h"
#include "pico_protocol.h"
/* ICMP header sizes */
#define PICO_ICMP6HDR_DRY_SIZE 4
#define PICO_ICMP6HDR_ECHO_REQUEST_SIZE 8
#define PICO_ICMP6HDR_DEST_UNREACH_SIZE 8
#define PICO_ICMP6HDR_TIME_XCEEDED_SIZE 8
#define PICO_ICMP6HDR_NEIGH_SOL_SIZE 24
#define PICO_ICMP6HDR_NEIGH_ADV_SIZE 24
#define PICO_ICMP6HDR_ROUTER_SOL_SIZE 8
#define PICO_ICMP6HDR_ROUTER_ADV_SIZE 16
#define PICO_ICMP6HDR_REDIRECT_SIZE 40
/* ICMP types */
#define PICO_ICMP6_DEST_UNREACH 1
#define PICO_ICMP6_PKT_TOO_BIG 2
#define PICO_ICMP6_TIME_EXCEEDED 3
#define PICO_ICMP6_PARAM_PROBLEM 4
#define PICO_ICMP6_ECHO_REQUEST 128
#define PICO_ICMP6_ECHO_REPLY 129
#define PICO_ICMP6_ROUTER_SOL 133
#define PICO_ICMP6_ROUTER_ADV 134
#define PICO_ICMP6_NEIGH_SOL 135
#define PICO_ICMP6_NEIGH_ADV 136
#define PICO_ICMP6_REDIRECT 137
/* destination unreachable codes */
#define PICO_ICMP6_UNREACH_NOROUTE 0
#define PICO_ICMP6_UNREACH_ADMIN 1
#define PICO_ICMP6_UNREACH_SRCSCOPE 2
#define PICO_ICMP6_UNREACH_ADDR 3
#define PICO_ICMP6_UNREACH_PORT 4
#define PICO_ICMP6_UNREACH_SRCFILTER 5
#define PICO_ICMP6_UNREACH_REJROUTE 6
/* time exceeded codes */
#define PICO_ICMP6_TIMXCEED_INTRANS 0
#define PICO_ICMP6_TIMXCEED_REASS 1
/* parameter problem codes */
#define PICO_ICMP6_PARAMPROB_HDRFIELD 0
#define PICO_ICMP6_PARAMPROB_NXTHDR 1
#define PICO_ICMP6_PARAMPROB_IPV6OPT 2
/* ping error codes */
#define PICO_PING6_ERR_REPLIED 0
#define PICO_PING6_ERR_TIMEOUT 1
#define PICO_PING6_ERR_UNREACH 2
#define PICO_PING6_ERR_PENDING 0xFFFF
/* custom defines */
#define PICO_ICMP6_ND_UNICAST 0
#define PICO_ICMP6_ND_ANYCAST 1
#define PICO_ICMP6_ND_SOLICITED 2
#define PICO_ICMP6_ND_DAD 3
#define PICO_ICMP6_MAX_RTR_SOL_DELAY 1000
#define PICO_SIZE_ICMP6HDR ((sizeof(struct pico_icmp6_hdr)))
extern struct pico_protocol pico_proto_icmp6;
struct __attribute__((packed)) pico_icmp6_hdr {
uint8_t type;
uint8_t code;
uint16_t crc;
union {
/* error messages */
union {
struct {
uint32_t unused;
uint8_t data[0];
} dest_unreach;
struct {
uint32_t mtu;
uint8_t data[0];
} pkt_too_big;
struct {
uint32_t unused;
uint8_t data[0];
} time_exceeded;
struct {
uint32_t ptr;
uint8_t data[0];
} param_problem;
} err;
/* informational messages */
union {
struct {
uint16_t id;
uint16_t seq;
uint8_t data[0];
} echo_request;
struct {
uint16_t id;
uint16_t seq;
uint8_t data[0];
} echo_reply;
struct {
uint32_t unused;
uint8_t options[0];
} router_sol;
struct {
uint8_t hop;
uint8_t mor;
uint16_t life_time;
uint32_t reachable_time;
uint32_t retrans_time;
uint8_t options[0];
} router_adv;
struct {
uint32_t unused;
struct pico_ip6 target;
uint8_t options[0];
} neigh_sol;
struct {
uint32_t rsor;
struct pico_ip6 target;
uint8_t options[0];
} neigh_adv;
struct {
uint32_t reserved;
struct pico_ip6 target;
struct pico_ip6 dest;
uint8_t options[0];
} redirect;
} info;
} msg;
};
struct __attribute__((packed)) pico_icmp6_opt_lladdr
{
uint8_t type;
uint8_t len;
union {
struct pico_eth mac;
} addr;
};
struct __attribute__((packed)) pico_icmp6_opt_prefix
{
uint8_t type;
uint8_t len;
uint8_t prefix_len;
uint8_t res : 6;
uint8_t aac : 1;
uint8_t onlink : 1;
uint32_t val_lifetime;
uint32_t pref_lifetime;
uint32_t reserved;
struct pico_ip6 prefix;
};
struct __attribute__((packed)) pico_icmp6_opt_mtu
{
uint8_t type;
uint8_t len;
uint16_t res;
uint32_t mtu;
};
struct __attribute__((packed)) pico_icmp6_opt_redirect
{
uint8_t type;
uint8_t len;
uint16_t res0;
uint32_t res1;
uint8_t data[0];
};
struct __attribute__((packed)) pico_icmp6_opt_na
{
uint8_t type;
uint8_t len;
uint8_t options[0];
};
struct pico_icmp6_stats
{
unsigned long size;
unsigned long seq;
pico_time time;
unsigned long ttl;
int err;
struct pico_ip6 dst;
};
int pico_icmp6_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp6_stats *));
int pico_icmp6_neighbor_solicitation(struct pico_device *dev, struct pico_ip6 *dst, uint8_t type);
int pico_icmp6_neighbor_advertisement(struct pico_frame *f, struct pico_ip6 *target);
int pico_icmp6_router_solicitation(struct pico_device *dev, struct pico_ip6 *src);
int pico_icmp6_port_unreachable(struct pico_frame *f);
int pico_icmp6_proto_unreachable(struct pico_frame *f);
int pico_icmp6_dest_unreachable(struct pico_frame *f);
int pico_icmp6_ttl_expired(struct pico_frame *f);
int pico_icmp6_packet_filtered(struct pico_frame *f);
uint16_t pico_icmp6_checksum(struct pico_frame *f);
#endif
This diff is collapsed.
This diff is collapsed.
/*********************************************************************
PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved.
See LICENSE and COPYING for usage.
*********************************************************************/
#ifndef _INCLUDE_PICO_ND
#define _INCLUDE_PICO_ND
#include "pico_frame.h"
/* RFC constants */
#define PICO_ND_REACHABLE_TIME 30000 /* msec */
#define PICO_ND_RETRANS_TIMER 1000 /* msec */
struct pico_nd_hostvars {
uint32_t mtu;
uint8_t hoplimit;
uint32_t basetime;
uint32_t reachabletime;
uint32_t retranstime;
};
void pico_nd_init(void);
struct pico_eth *pico_nd_get(struct pico_frame *f);
int pico_nd_neigh_sol_recv(struct pico_frame *f);
int pico_nd_neigh_adv_recv(struct pico_frame *f);
int pico_nd_router_sol_recv(struct pico_frame *f);
int pico_nd_router_adv_recv(struct pico_frame *f);
int pico_nd_redirect_recv(struct pico_frame *f);
#endif
......@@ -8,7 +8,7 @@ int pico_socket_udp_deliver(struct pico_sockport *sp, struct pico_frame *f);
#ifdef PICO_SUPPORT_UDP
# define pico_socket_udp_recv(s, buf, len, addr, port) pico_udp_recv(s, buf, len, addr, port)
#else
# define pico_socket_udp-recv(...) (0)
# define pico_socket_udp_recv(...) (0)
#endif
......
OPTIONS+=-DPICO_SUPPORT_ICMP6
MOD_OBJ+=modules/pico_icmp6.o
ifneq ($(PING),0)
OPTIONS+=-DPICO_SUPPORT_PING
endif
OPTIONS+=-DPICO_SUPPORT_IPV6
MOD_OBJ+=modules/pico_ipv6.o modules/pico_ipv6_nd.o
......@@ -13,6 +13,8 @@
#include "pico_stack.h"
#include "pico_protocol.h"
#include "pico_tree.h"
#include "pico_ipv6.h"
#include "pico_icmp6.h"
struct pico_devices_rr_info {
struct pico_tree_node *node_in, *node_out;
......@@ -38,6 +40,11 @@ PICO_TREE_DECLARE(Device_tree, pico_dev_cmp);
int pico_device_init(struct pico_device *dev, const char *name, uint8_t *mac)
{
#ifdef PICO_SUPPORT_IPV6
struct pico_ip6 linklocal = {{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xff, 0xfe, 0xaa, 0xaa, 0xaa}};
struct pico_ip6 netmask6 = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
#endif
uint32_t len = (uint32_t)strlen(name);
if(len > MAX_DEVICE_NAME)
len = MAX_DEVICE_NAME;
......@@ -45,22 +52,78 @@ int pico_device_init(struct pico_device *dev, const char *name, uint8_t *mac)
memcpy(dev->name, name, len);
dev->hash = pico_hash(dev->name, len);
pico_tree_insert(&Device_tree, dev);
Devices_rr_info.node_in = NULL;
Devices_rr_info.node_out = NULL;
dev->q_in = pico_zalloc(sizeof(struct pico_queue));
dev->q_out = pico_zalloc(sizeof(struct pico_queue));
if (!dev->q_in || !dev->q_out)
return -1;
pico_tree_insert(&Device_tree, dev);
if (mac) {
dev->eth = pico_zalloc(sizeof(struct pico_ethdev));
if (dev->eth)
if (dev->eth) {
memcpy(dev->eth->mac.addr, mac, PICO_SIZE_ETH);
#ifdef PICO_SUPPORT_IPV6
/* modified EUI-64 + invert universal/local bit */
linklocal.addr[8] = (mac[0] ^ 0x02);
linklocal.addr[9] = mac[1];
linklocal.addr[10] = mac[2];
linklocal.addr[13] = mac[3];
linklocal.addr[14] = mac[4];
linklocal.addr[15] = mac[5];
if (pico_ipv6_link_add(dev, linklocal, netmask6)) {
pico_free(dev->q_in);
pico_free(dev->q_out);
pico_free(dev->eth);
return -1;
}
#endif
}
} else {
dev->eth = NULL;
#ifdef PICO_SUPPORT_IPV6
if (strcmp(dev->name, "loop")) {
do {
/* privacy extension + unset universal/local and individual/group bit */
len = pico_rand();
linklocal.addr[8] = (uint8_t)((len & 0xff) & (uint8_t)(~0x03));
linklocal.addr[9] = (uint8_t)(len >> 8);
linklocal.addr[10] = (uint8_t)(len >> 16);
linklocal.addr[11] = (uint8_t)(len >> 24);
len = pico_rand();
linklocal.addr[12] = (uint8_t)len;
linklocal.addr[13] = (uint8_t)(len >> 8);
linklocal.addr[14] = (uint8_t)(len >> 16);
linklocal.addr[15] = (uint8_t)(len >> 24);
pico_rand_feed(dev->hash);
} while (pico_ipv6_link_get(&linklocal));
if (pico_ipv6_link_add(dev, linklocal, netmask6)) {
pico_free(dev->q_in);
pico_free(dev->q_out);
return -1;
}
}
#endif
}
if (!dev->q_in || !dev->q_out || (mac && !dev->eth))
return -1;
#ifdef PICO_SUPPORT_IPV6
if (dev->eth)
{
dev->hostvars.mtu = PICO_ETH_MTU;
dev->hostvars.basetime = PICO_ND_REACHABLE_TIME;
/* RFC 4861 $6.3.2 value between 0.5 and 1.5 times basetime */
dev->hostvars.reachabletime = ((5 + (pico_rand() % 10)) * PICO_ND_REACHABLE_TIME) / 10;
dev->hostvars.retranstime = PICO_ND_RETRANS_TIMER;
pico_icmp6_router_solicitation(dev, &linklocal);
}
dev->hostvars.hoplimit = PICO_IPV6_DEFAULT_HOP;
#endif
return 0;
}
......
......@@ -194,6 +194,8 @@ struct pico_sockport *pico_get_sockport(uint16_t proto, uint16_t port)
else return NULL;
}
#ifdef PICO_SUPPORT_IPV4
static int pico_port_in_use_by_nat(uint16_t proto, uint16_t port)
{
int ret = 0;
......@@ -209,7 +211,7 @@ static int pico_port_in_use_by_nat(uint16_t proto, uint16_t port)
return ret;
}
static int pico_port_in_use_with_this_address(struct pico_sockport *sp, struct pico_ip4 ip)
static int pico_port_in_use_with_this_ipv4_address(struct pico_sockport *sp, struct pico_ip4 ip)
{
if (sp) {
struct pico_ip4 *s_local;
......@@ -225,11 +227,11 @@ static int pico_port_in_use_with_this_address(struct pico_sockport *sp, struct p
}
}
}
return 0;
}
static int pico_port_in_use(struct pico_sockport *sp, void *addr)
static int pico_port_in_use_ipv4(struct pico_sockport *sp, void *addr)
{
struct pico_ip4 ip;
/* IPv4 */
......@@ -247,18 +249,69 @@ static int pico_port_in_use(struct pico_sockport *sp, void *addr)
}
}
return pico_port_in_use_with_this_address(sp, ip);
return pico_port_in_use_with_this_ipv4_address(sp, ip);
}
#endif
#ifdef PICO_SUPPORT_IPV6
static int pico_port_in_use_with_this_ipv6_address(struct pico_sockport *sp, struct pico_ip6 ip)
{
if (sp) {
struct pico_ip6 *s_local;
struct pico_tree_node *idx;
struct pico_socket *s;
pico_tree_foreach(idx, &sp->socks) {
s = idx->keyValue;
if (s->net == &pico_proto_ipv6) {
s_local = (struct pico_ip6*) &s->local_addr;
if ((pico_ipv6_is_unspecified(s_local->addr)) || (!memcmp(s_local->addr, ip.addr, PICO_SIZE_IP6))) {
return 1;
}
}
}
}
return 0;
}
static int pico_port_in_use_ipv6(struct pico_sockport *sp, void *addr)
{
struct pico_ip6 ip;
/* IPv6 */
if (addr)
memcpy(&ip.addr,((struct pico_ip6 *)addr)->addr, sizeof(struct pico_ip6));
else
memcpy(&ip.addr, PICO_IP6_ANY, sizeof(struct pico_ip6));
if (memcmp(ip.addr, PICO_IP6_ANY, sizeof(struct pico_ip6)) == 0) {
if (!sp)
return 0;
else {
dbg("In use, and asked for ANY\n");
return 1;
}
}
return pico_port_in_use_with_this_ipv6_address(sp, ip);
}
#endif
static int pico_generic_port_in_use(uint16_t proto, uint16_t port, struct pico_sockport *sp, void *addr)
{
#ifdef PICO_SUPPORT_IPV4
if (pico_port_in_use_by_nat(proto, port)) {
return 1;
}
if (pico_port_in_use(sp, addr)) {
if (pico_port_in_use_ipv4(sp, addr)) {
return 1;
}
#endif
#ifdef PICO_SUPPORT_IPV6
if (pico_port_in_use_ipv6(sp, addr)) {
return 1;
}
#endif
return 0;
}
......@@ -266,17 +319,9 @@ static int pico_generic_port_in_use(uint16_t proto, uint16_t port, struct pico_s
int pico_is_port_free(uint16_t proto, uint16_t port, void *addr, void *net)
{
struct pico_sockport *sp;
(void) net;
sp = pico_get_sockport(proto, port);
if (!net)
net = &pico_proto_ipv4;
/** IPv6 (wip) ***/
if (net != &pico_proto_ipv4) {
dbg("IPV6!!!!!\n");
return (!sp);
}
if (pico_generic_port_in_use(proto, port, sp, addr))
return 0;
......@@ -774,8 +819,8 @@ static void *pico_socket_sendto_get_ip6_src(struct pico_socket *s, struct pico_i
return NULL;
}
/* TODO:compare against IP6ADDR_ANY just like the ip4 version above */
memcpy(&s->local_addr, src6, PICO_SIZE_IP6);
if (!pico_ipv6_is_unspecified(src6->addr))
s->local_addr.ip6 = *src6;
}
#else
......@@ -1261,7 +1306,6 @@ int pico_socket_recv(struct pico_socket *s, void *buf, int len)
return pico_socket_recvfrom(s, buf, len, NULL, NULL);
}
int pico_socket_bind(struct pico_socket *s, void *local_addr, uint16_t *port)
{
if (!s || !local_addr || !port) {
......@@ -1269,7 +1313,8 @@ int pico_socket_bind(struct pico_socket *s, void *local_addr, uint16_t *port)
return -1;
}
if (!is_sock_ipv6(s)) {
if (IS_SOCK_IPV4(s)) {
#ifdef PICO_SUPPORT_IPV4
struct pico_ip4 *ip = (struct pico_ip4 *)local_addr;
if (ip->addr != PICO_IPV4_INADDR_ANY) {
if (!pico_ipv4_link_find(local_addr)) {
......@@ -1277,8 +1322,22 @@ int pico_socket_bind(struct pico_socket *s, void *local_addr, uint16_t *port)
return -1;
}
}
#endif
} else if (IS_SOCK_IPV6(s)) {
#ifdef PICO_SUPPORT_IPV6
struct pico_ip6 *ip = (struct pico_ip6 *)local_addr;
if (!pico_ipv6_is_unspecified(ip->addr)) {
if (!pico_ipv6_link_find(local_addr)) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
}
#endif
} else {
/*... IPv6 */
pico_err = PICO_ERR_EINVAL;
return -1;
}
......@@ -1298,23 +1357,25 @@ int pico_socket_bind(struct pico_socket *s, void *local_addr, uint16_t *port)
s->local_port = *port;
if (is_sock_ipv6(s)) {
struct pico_ip6 *ip = (struct pico_ip6 *) local_addr;
memcpy(s->local_addr.ip6.addr, ip, PICO_SIZE_IP6);
/* XXX: port ipv4 functionality to ipv6 */
/* Check for port already in use */
if (pico_is_port_free(PROTO(s), *port, &local_addr, s->net)) {
pico_err = PICO_ERR_EADDRINUSE;
return -1;
}
} else if (is_sock_ipv4(s)) {
struct pico_ip4 *ip = (struct pico_ip4 *) local_addr;
s->local_addr.ip4.addr = ip->addr;
if (IS_SOCK_IPV4(s)) {
#ifdef PICO_SUPPORT_IPV4
struct pico_ip4 *ip = (struct pico_ip4 *)local_addr;
s->local_addr.ip4 = *ip;
#endif
} else if (IS_SOCK_IPV6(s)) {
#ifdef PICO_SUPPORT_IPV6
struct pico_ip6 *ip = (struct pico_ip6 *)local_addr;
s->local_addr.ip6 = *ip;
#endif
} else {
pico_err = PICO_ERR_EINVAL;
return -1;
}
return pico_socket_alter_state(s, PICO_SOCKET_STATE_BOUND, 0, 0);
}
int pico_socket_connect(struct pico_socket *s, const void *remote_addr, uint16_t remote_port)
{
int ret = -1;
......@@ -1334,19 +1395,37 @@ int pico_socket_connect(struct pico_socket *s, const void *remote_addr, uint16_t
}
}
if (is_sock_ipv6(s)) {
const struct pico_ip6 *ip = (const struct pico_ip6 *) remote_addr;
memcpy(s->remote_addr.ip6.addr, ip, PICO_SIZE_IP6);
} else if (is_sock_ipv4(s)) {
const struct pico_ip4 *local, *ip = (const struct pico_ip4 *) remote_addr;
s->remote_addr.ip4.addr = ip->addr;
if (IS_SOCK_IPV4(s)) {
#ifdef PICO_SUPPORT_IPV4
struct pico_ip4 *local = NULL;
const struct pico_ip4 *ip = (const struct pico_ip4 *)remote_addr;
s->remote_addr.ip4 = *ip;
local = pico_ipv4_source_find(ip);
if (local) {
s->local_addr.ip4.addr = local->addr;
s->local_addr.ip4 = *local;
} else {
pico_err = PICO_ERR_EHOSTUNREACH;
return -1;
}
#endif
} else if (IS_SOCK_IPV6(s)) {
#ifdef PICO_SUPPORT_IPV6
struct pico_ip6 *local = NULL;
const struct pico_ip6 *ip = (const struct pico_ip6 *)remote_addr;
s->remote_addr.ip6 = *ip;
local = pico_ipv6_source_find(ip);
if (local) {
s->local_addr.ip6 = *local;
} else {
pico_err = PICO_ERR_EHOSTUNREACH;
return -1;
}
#endif
} else {
pico_err = PICO_ERR_EINVAL;
return -1;
}
pico_socket_alter_state(s, PICO_SOCKET_STATE_BOUND, 0, 0);
......@@ -1376,6 +1455,7 @@ int pico_socket_connect(struct pico_socket *s, const void *remote_addr, uint16_t
return ret;
}
#ifdef PICO_SUPPORT_TCP
int pico_socket_listen(struct pico_socket *s, int backlog)
......
......@@ -145,6 +145,13 @@ int32_t pico_transport_receive(struct pico_frame *f, uint8_t proto)
break;
#endif
#ifdef PICO_SUPPORT_ICMP6
case PICO_PROTO_ICMP6:
ret = pico_enqueue(pico_proto_icmp6.q_in, f);
break;
#endif
#ifdef PICO_SUPPORT_IGMP
case PICO_PROTO_IGMP:
ret = pico_enqueue(pico_proto_igmp.q_in, f);
......@@ -841,6 +848,10 @@ int pico_stack_init(void)
pico_protocol_init(&pico_proto_icmp4);
#endif
#ifdef PICO_SUPPORT_ICMP6
pico_protocol_init(&pico_proto_icmp6);
#endif
#ifdef PICO_SUPPORT_IGMP
pico_protocol_init(&pico_proto_igmp);
#endif
......
......@@ -5,6 +5,23 @@ rm -f /tmp/pico-mem-report-*
sleep 2
ulimit -c unlimited
echo "IPV6 tests!"
echo "PING6 LOCALHOST"
./build/test/picoapp6.elf --loop -a ping,::1, || exit 1
echo "PING6 TEST"
(./build/test/picoapp6.elf --vde pic0,/tmp/pic0.ctl,aaaa::1,ffff::,) &
./build/test/picoapp6.elf --vde pic0,/tmp/pic0.ctl,aaaa::2,ffff::, -a ping,aaaa::1, || exit 1
killall picoapp6.elf
echo
echo
echo
echo
echo
echo "PING LOCALHOST"
./build/test/picoapp.elf --loop -a ping:127.0.0.1: || exit 1
......@@ -69,6 +86,13 @@ echo "SLAACV4 TEST"
killall picoapp.elf
MAXMEM=`cat /tmp/pico-mem-report-* | sort -r -n |head -1`
echo
echo
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment