/** @file * @brief DSA related functions */ /* * Copyright (c) 2018 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include LOG_MODULE_REGISTER(net_dsa, CONFIG_NET_DSA_LOG_LEVEL); #include #include #include #include #include #include /* * 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); }