blob: e293af4237ac7c6ef4207bef30179974c74cdb2d [file] [log] [blame]
/** @file
* @brief DSA related functions
*/
/*
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(net_dsa, CONFIG_NET_DSA_LOG_LEVEL);
#include <errno.h>
#include <stdlib.h>
#include <zephyr/net/net_core.h>
#include <zephyr/net/ethernet.h>
#include <zephyr/net/net_mgmt.h>
#include <zephyr/net/dsa.h>
/*
* Store, in the ethernet_context for master interface, the original
* eth_tx() function, which will send packet with tag appended.
*/
int dsa_register_master_tx(struct net_if *iface, dsa_send_t fn)
{
struct ethernet_context *ctx = net_if_l2_data(iface);
ctx->dsa_send = fn;
return 0;
}
#ifdef CONFIG_NET_L2_ETHERNET
bool dsa_is_port_master(struct net_if *iface)
{
/* First check if iface points to ETH interface */
if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) {
/* Check its capabilities */
if (net_eth_get_hw_capabilities(iface) &
ETHERNET_DSA_MASTER_PORT) {
return true;
}
}
return false;
}
#else
bool dsa_is_port_master(struct net_if *iface)
{
return false;
}
#endif
/*
* RECEIVE HANDLING CODE - ingress (ETH -> DSA slave ports)
*/
static int dsa_check_iface(struct net_if *iface)
{
if (net_if_l2(iface) != &NET_L2_GET_NAME(ETHERNET)) {
return -ENOENT;
}
if (!((net_eth_get_hw_capabilities(iface) & ETHERNET_DSA_MASTER_PORT) ||
(net_eth_get_hw_capabilities(iface) & ETHERNET_DSA_SLAVE_PORT))) {
return -ESRCH;
}
return 0;
}
int dsa_register_recv_callback(struct net_if *iface, dsa_net_recv_cb_t cb)
{
struct ethernet_context *ctx;
int ret;
ret = dsa_check_iface(iface);
if (ret < 0) {
return ret;
}
if (cb) {
ctx = net_if_l2_data(iface);
ctx->dsa_recv_cb = cb;
}
return 0;
}
struct net_if *dsa_net_recv(struct net_if *iface, struct net_pkt **pkt)
{
const struct ethernet_context *c;
const struct dsa_context *ctx;
struct net_if *iface_sw;
int ret;
if (*pkt == NULL || iface == NULL) {
return NULL;
}
c = net_if_l2_data(iface);
ctx = c->dsa_ctx;
if (ctx == NULL || ctx->dapi == NULL) {
return iface;
}
if (ctx->dapi->dsa_get_iface == NULL) {
NET_ERR("DSA: No callback to set LAN interfaces!");
return iface;
}
iface_sw = ctx->dapi->dsa_get_iface(iface, *pkt);
ret = dsa_check_iface(iface_sw);
if (ret < 0) {
return iface_sw;
}
/*
* Optional code to change the destination interface with some
* custom callback (to e.g. filter/switch packets based on MAC).
*
* The callback shall be only present (and used) for lan1..3, but
* not for the master interface, which shall support all other
* protocols - i.e. UDP. ICMP, TCP.
*/
c = net_if_l2_data(iface_sw);
if (c->dsa_recv_cb) {
if (c->dsa_recv_cb(iface_sw, *pkt)) {
return iface_sw;
}
}
return iface;
}
/*
* TRANSMISSION HANDLING CODE egress (DSA slave ports -> ETH)
*/
int dsa_tx(const struct device *dev, struct net_pkt *pkt)
{
struct net_if *iface_master, *iface;
struct ethernet_context *ctx;
struct dsa_context *context;
iface = net_if_lookup_by_dev(dev);
if (dsa_is_port_master(iface)) {
/*
* The master interface's ethernet_context structure holds
* pointer to its original eth_tx().
* The wrapper (i.e. dsa_tx()) is needed to modify packet -
* it appends tag to it.
*/
ctx = net_if_l2_data(iface);
context = ctx->dsa_ctx;
return ctx->dsa_send(dev,
context->dapi->dsa_xmit_pkt(iface, pkt));
}
context = dev->data;
iface_master = context->iface_master;
if (iface_master == NULL) {
NET_ERR("DSA: No master interface!");
return -ENODEV;
}
/*
* Here packets are send via lan{123} interfaces in user program.
* Those structs' ethernet_api only have .send callback set to point
* to this wrapper function.
*
* Hence, it is necessary to get this callback from master's ethernet
* context structure..
*/
/* Adjust packet for DSA routing and send it via master interface */
ctx = net_if_l2_data(iface_master);
return ctx->dsa_send(net_if_get_device(iface_master),
context->dapi->dsa_xmit_pkt(iface, pkt));
}
struct net_if *dsa_get_slave_port(struct net_if *iface, int slave_num)
{
struct ethernet_context *eth_ctx;
struct dsa_context *dsa_ctx;
eth_ctx = net_if_l2_data(iface);
if (eth_ctx == NULL) {
LOG_ERR("Iface %p context not available!", iface);
return NULL;
}
dsa_ctx = eth_ctx->dsa_ctx;
if (slave_num < 0 || slave_num >= dsa_ctx->num_slave_ports) {
return NULL;
}
return dsa_ctx->iface_slave[slave_num];
}
int dsa_switch_read(struct net_if *iface, uint16_t reg_addr, uint8_t *value)
{
const struct device *dev = net_if_get_device(iface);
struct dsa_context *context = dev->data;
const struct dsa_api *api =
(const struct dsa_api *)context->dapi;
return api->switch_read(dev, reg_addr, value);
}
int dsa_switch_write(struct net_if *iface, uint16_t reg_addr, uint8_t value)
{
const struct device *dev = net_if_get_device(iface);
struct dsa_context *context = dev->data;
const struct dsa_api *api =
(const struct dsa_api *)context->dapi;
return api->switch_write(dev, reg_addr, value);
}
/**
* @brief Write static MAC table entry
*
* @param iface DSA interface
* @param[in] mac MAC address
* @param[in] fw_port The firmware port
* @param[in] tbl_entry_idx Table entry index
* @param[in] flags Flags
*
* @return 0 if successful, negative if error
*/
int dsa_switch_set_mac_table_entry(struct net_if *iface,
const uint8_t *mac,
uint8_t fw_port,
uint16_t tbl_entry_idx,
uint16_t flags)
{
const struct device *dev = net_if_get_device(iface);
struct dsa_context *context = dev->data;
const struct dsa_api *api =
(const struct dsa_api *)context->dapi;
return api->switch_set_mac_table_entry(dev, mac, fw_port,
tbl_entry_idx, flags);
}
/**
* @brief Read static MAC table entry
*
* @param iface DSA interface
* @param buf Buffer to receive MAC address
* @param[in] tbl_entry_idx Table entry index
*
* @return 0 if successful, negative if error
*/
int dsa_switch_get_mac_table_entry(struct net_if *iface,
uint8_t *buf,
uint16_t tbl_entry_idx)
{
const struct device *dev = net_if_get_device(iface);
struct dsa_context *context = dev->data;
const struct dsa_api *api =
(const struct dsa_api *)context->dapi;
return api->switch_get_mac_table_entry(dev, buf, tbl_entry_idx);
}