1
0
mirror of https://github.com/arduino/Arduino.git synced 2024-12-04 15:24:12 +01:00
Arduino/wifiHD/src/ping.c
2012-05-20 23:54:32 +02:00

341 lines
11 KiB
C

/* This source file is part of the ATMEL AVR-UC3-SoftwareFramework-1.7.0 Release */
/*
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is derived from a part of the lwIP TCP/IP stack.
*
*/
#ifdef PING_CMD
#include "lwip/opt.h"
#include "lwip/mem.h"
#include "lwip/raw.h"
#include "lwip/icmp.h"
#include "lwip/netif.h"
#include "lwip/sys.h"
#include "lwip/sockets.h"
#include "lwip/inet.h"
#include "lwip/inet_chksum.h"
#include "ping.h"
#include "timer.h"
#include "util.h"
#include "getopt.h"
#define PING_ID 0xAFAF
struct ping_info_t {
struct ip_addr destination;
uint32_t deadline; /* -w (in seconds) */
uint32_t interval; /* -i (in ms) */
uint32_t timeout; /* ms */
uint32_t data_size; /* -s */
uint32_t count; /* -c, 0 means continous ping */
uint32_t size;
uint32_t first_tx_tm;
uint32_t last_tx_tm;
uint32_t last_rx_tm;
uint32_t num_tx;
uint32_t num_rx;
uint32_t flags;
uint16_t seq_num;
Bool quiet; /* -q */
ping_complete_cb_t complete_cb;
void *ctx;
#define PING_REPLY (1 << 0)
};
static struct ping_info_t INFO;
/** Prepare a echo ICMP request */
static void ping_prepare_echo(struct icmp_echo_hdr *iecho,
struct ping_info_t* ping_info)
{
int i;
ICMPH_TYPE_SET(iecho,ICMP_ECHO);
ICMPH_CODE_SET(iecho, 0);
iecho->chksum = 0;
iecho->id = PING_ID;
iecho->seqno = htons(++ping_info->seq_num);
iecho->chksum = 0;
/* fill the additional data buffer with some data */
for(i = 0; i < ping_info->data_size; i++) {
((char*)iecho)[sizeof(struct icmp_echo_hdr) + i] = i;
}
iecho->chksum = inet_chksum(iecho, ping_info->size);
}
/* Ping using the raw ip */
static u8_t ping_recv(void *arg, struct raw_pcb *pcb, struct pbuf *p,
struct ip_addr *addr)
{
struct icmp_echo_hdr *iecho;
struct ip_hdr *ip = p->payload;
struct ping_info_t* ping_info = (struct ping_info_t*) arg;
uint32_t us;
if (pbuf_header( p, -PBUF_IP_HLEN)==0) {
iecho = p->payload;
if ((iecho->id == PING_ID) &&
(iecho->seqno == htons(ping_info->seq_num))) {
ping_info->last_rx_tm = timer_get_ms();
ping_info->num_rx++;
us = 1000 *
(ping_info->last_rx_tm - ping_info->last_tx_tm);
if (!ping_info->quiet)
printk("%d bytes from %s: icmp_seq=%d ttl=%d " \
"time=%d.%03d ms\n",
p->tot_len, ip2str(ip->src),
iecho->seqno,
IPH_TTL(ip),
us / 1000, us % 1000);
/* do some ping result processing */
ping_info->flags |= PING_REPLY;
}
}
pbuf_free(p);
return 1; /* eat the event */
}
static void ping_send(struct raw_pcb *raw, struct ping_info_t* ping_info)
{
struct pbuf *p;
struct icmp_echo_hdr *iecho;
if (!(p = pbuf_alloc(PBUF_IP, ping_info->size, PBUF_RAM))) {
return;
}
if ((p->len == p->tot_len) && (p->next == NULL)) {
iecho = p->payload;
ping_prepare_echo(iecho, ping_info);
raw_sendto(raw, p, &ping_info->destination);
if (!ping_info->first_tx_tm)
ping_info->first_tx_tm = timer_get_ms();
ping_info->last_tx_tm = timer_get_ms();
ping_info->num_tx++;
}
pbuf_free(p);
}
void ping_set_callback(ping_complete_cb_t cb, void *ctx) {
INFO.complete_cb = cb;
INFO.ctx = ctx;
}
void ping_stop(uint32_t *tx_cnt, uint32_t *rx_cnt) {
struct ping_info_t *ping_info = &INFO;
*tx_cnt = ping_info->num_tx;
*rx_cnt = ping_info->num_rx;
ping_info->count = ping_info->num_tx;
if ( 0 == ping_info->count ) {
ping_info->count = 1;
}
}
static int init_ping_info(int argc, char* argv[], struct ping_info_t* ping_info)
{
int c;
ping_complete_cb_t cb;
void *ctx;
cb = ping_info->complete_cb;
ctx = ping_info->ctx;
memset(ping_info, 0, sizeof(struct ping_info_t));
ping_info->complete_cb = cb;
ping_info->ctx = ctx;
ping_info->deadline = 0;
ping_info->interval = 1000;
ping_info->timeout = 3000;
ping_info->data_size = 32;
ping_info->count = 3;
ping_info->destination =
netif_default ? netif_default->gw : ip_addr_any;
optind = 1;
while ((c = getopt(argc, argv, "c:i:s:w:q")) != -1) {
switch (c) {
case 'c':
ping_info->count = atoi(optarg);
break;
case 'i':
ping_info->interval = atoi(optarg);
break;
case 's':
ping_info->data_size = atoi(optarg);
break;
case 'q':
ping_info->quiet = TRUE;
break;
case 'w':
ping_info->deadline = atoi(optarg);
break;
}
}
ping_info->size = sizeof(struct icmp_echo_hdr) + ping_info->data_size;
if (optind >= argc)
return -1;
ping_info->destination = str2ip(argv[optind]);
if (!ping_info->destination.addr)
return -1;
ping_info->last_rx_tm = timer_get_ms();
return 0;
}
static void print_stats(struct ping_info_t* ping_info)
{
printk("\n--- %s ping statistics ---\n",
ip2str(ping_info->destination));
printk("%d packets transmitted, %d received, %d%% packet loss, "\
"time %dms\n\n",
ping_info->num_tx, ping_info->num_rx,
100 * (ping_info->num_tx - ping_info->num_rx) /
ping_info->num_tx,
timer_get_ms() - ping_info->first_tx_tm);
}
static void ping_finalize(struct ping_info_t* ping_info) {
print_stats(ping_info);
if (ping_info->complete_cb) {
ping_info->complete_cb(ping_info->num_tx, ping_info->num_rx, ping_info->ctx);
}
}
cmd_state_t cmd_ping(int argc, char* argv[], void* ctx)
{
static enum {
INIT,
PING,
WAIT_REPLY
} state = INIT;
struct ping_info_t *ping_info = &INFO;
static struct raw_pcb *pcb;
switch (state) {
case INIT:
if (init_ping_info(argc, argv, ping_info) != 0) {
printk("Usage: ping [-c count] [-i interval] " \
"[-s packetsize]\n " \
"[-w deadline] [-q] destination\n");
return CMD_DONE;
}
if (!(pcb = raw_new(IP_PROTO_ICMP))) {
printk("could not allocate pcb\n");
state = INIT;
return CMD_DONE;
}
raw_recv(pcb, ping_recv, ping_info);
raw_bind(pcb, IP_ADDR_ANY);
printk("PING %s %d(%d) bytes of data\n",
ip2str(ping_info->destination),
ping_info->data_size,
ping_info->size);
state = PING;
/* fall through */
case PING:
if (!netif_is_up(netif_default)) {
printk("netif is down\n");
raw_remove(pcb);
state = INIT;
return CMD_DONE;
}
if (ping_info->count && ping_info->num_tx == ping_info->count) {
ping_finalize(ping_info);
raw_remove(pcb);
state = INIT;
return CMD_DONE;
}
if (timer_get_ms() < ping_info->last_rx_tm + ping_info->interval) {
return CMD_INPROGRESS;
}
ping_send(pcb, ping_info);
state = WAIT_REPLY;
return CMD_INPROGRESS;
case WAIT_REPLY:
if (ping_info->flags & PING_REPLY) {
ping_info->flags &= (~PING_REPLY);
state = PING;
return CMD_INPROGRESS;
}
if (timer_get_ms() >
ping_info->last_tx_tm + ping_info->timeout) {
if (!ping_info->quiet)
printk("timeout from %s\n",
ip2str(ping_info->destination));
state = PING;
return CMD_INPROGRESS;
}
if (ping_info->deadline &&
timer_get_ms() >
ping_info->first_tx_tm + ping_info->deadline * 1000) {
ping_finalize(ping_info);
raw_remove(pcb);
state = INIT;
return CMD_DONE;
}
return CMD_INPROGRESS;
}
/* unreachable */
Assert(0);
return CMD_DONE;
}
#endif