blob: 5279a12961f00172559155ded68122c7c3dfcfbd [file] [log] [blame]
/* net_driver_ethernet.c - Ethernet driver */
/*
* Copyright (c) 2015 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <nanokernel.h>
#include <toolchain.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#if defined(CONFIG_STDOUT_CONSOLE)
#include <stdio.h>
#define PRINT printf
#else
#include <misc/printk.h>
#define PRINT printk
#endif
#include <net/net_core.h>
#include "net_driver_ethernet.h"
#include "contiki/ipv4/uip_arp.h"
static bool opened;
static ethernet_tx_callback tx_cb;
void net_driver_ethernet_register_tx(ethernet_tx_callback cb)
{
tx_cb = cb;
}
static int net_driver_ethernet_open(void)
{
NET_DBG("Initialized Ethernet driver\n");
opened = true;
return 0;
}
bool net_driver_ethernet_is_opened(void)
{
return opened;
}
static int net_driver_ethernet_send(struct net_buf *buf)
{
#ifdef CONFIG_NETWORKING_WITH_IPV6
struct uip_eth_hdr *eth_hdr = (struct uip_eth_hdr *)uip_buf(buf);
#endif
int res;
NET_DBG("Sending %d bytes\n", buf->len);
if (!tx_cb) {
NET_ERR("Ethernet transmit callback is uninitialized.\n");
return -1;
}
#ifdef CONFIG_NETWORKING_WITH_IPV4
/* Note that uip_arp_out overwrites the outgoing packet if it needs to
* send an ARP request. It relies on higher layers to resend the
* original packet if necessary.
*/
uip_arp_out(buf);
#else
memcpy(eth_hdr->dest.addr, ip_buf_ll_dest(buf).u8, UIP_LLADDR_LEN);
memcpy(eth_hdr->src.addr, uip_lladdr.addr, UIP_LLADDR_LEN);
eth_hdr->type = UIP_HTONS(UIP_ETHTYPE_IPV6);
uip_len(buf) += sizeof(struct uip_eth_hdr);
#endif
res = tx_cb(buf);
if (res == 1) {
/* Release the buffer because we sent all the data
* successfully.
*/
ip_buf_unref(buf);
}
return res;
}
void net_driver_ethernet_recv(struct net_buf *buf)
{
#ifdef CONFIG_NETWORKING_WITH_IPV4
struct uip_eth_hdr *eth_hdr = (struct uip_eth_hdr *)uip_buf(buf);
if (eth_hdr->type == uip_htons(UIP_ETHTYPE_ARP)) {
uip_arp_arpin(buf);
/* If uip_arp_arpin needs to send an ARP response, it
* overwrites the contents of buf and updates its
* length variable. Otherwise, it zeroes out the
* length variable.
*/
if (uip_len(buf) == 0) {
ip_buf_unref(buf);
return;
}
if (!tx_cb) {
NET_ERR("Ethernet transmit callback is uninitialized.\n");
ip_buf_unref(buf);
return;
}
if (tx_cb(buf) != 1) {
NET_ERR("Failed to send ARP response.\n");
}
ip_buf_unref(buf);
} else
#endif
if (net_recv(buf) != 0) {
NET_ERR("Unexpected return value from net_recv.\n");
ip_buf_unref(buf);
}
}
static struct net_driver net_driver_ethernet = {
.head_reserve = 0,
.open = net_driver_ethernet_open,
.send = net_driver_ethernet_send,
};
int net_driver_ethernet_init(void)
{
net_register_driver(&net_driver_ethernet);
return 0;
}