From b50dabb9fd36fd19bacf46033da40872a2bb9d0a Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Thu, 27 Feb 2020 00:14:24 +0200 Subject: [PATCH] drivers: eth: enc28j60: Add VLAN support Add Virtual LAN support to enc28j60 Ethernet driver. Signed-off-by: Jukka Rissanen --- drivers/ethernet/eth_enc28j60.c | 83 +++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 14 deletions(-) diff --git a/drivers/ethernet/eth_enc28j60.c b/drivers/ethernet/eth_enc28j60.c index adc7939b083..d2eb057e877 100644 --- a/drivers/ethernet/eth_enc28j60.c +++ b/drivers/ethernet/eth_enc28j60.c @@ -410,6 +410,25 @@ static void eth_enc28j60_init_phy(struct device *dev) } } +static struct net_if *get_iface(struct eth_enc28j60_runtime *ctx, + u16_t vlan_tag) +{ +#if defined(CONFIG_NET_VLAN) + struct net_if *iface; + + iface = net_eth_get_vlan_iface(ctx->iface, vlan_tag); + if (!iface) { + return ctx->iface; + } + + return iface; +#else + ARG_UNUSED(vlan_tag); + + return ctx->iface; +#endif +} + static int eth_enc28j60_tx(struct device *dev, struct net_pkt *pkt) { struct eth_enc28j60_runtime *context = dev->driver_data; @@ -483,7 +502,7 @@ static int eth_enc28j60_tx(struct device *dev, struct net_pkt *pkt) return 0; } -static int eth_enc28j60_rx(struct device *dev) +static int eth_enc28j60_rx(struct device *dev, u16_t *vlan_tag) { const struct eth_enc28j60_config *config = dev->config->config_info; struct eth_enc28j60_runtime *context = dev->driver_data; @@ -541,12 +560,13 @@ static int eth_enc28j60_rx(struct device *dev) lengthfr = frm_len; /* Get the frame from the buffer */ - pkt = net_pkt_rx_alloc_with_buffer(context->iface, frm_len, - AF_UNSPEC, 0, - config->timeout); + pkt = net_pkt_rx_alloc_with_buffer( + get_iface(context, *vlan_tag), frm_len, + AF_UNSPEC, 0, config->timeout); if (!pkt) { LOG_ERR("Could not allocate rx buffer"); - eth_stats_update_errors_rx(context->iface); + eth_stats_update_errors_rx(get_iface(context, + *vlan_tag)); goto done; } @@ -587,9 +607,32 @@ static int eth_enc28j60_rx(struct device *dev) eth_enc28j60_read_mem(dev, NULL, 1); } +#if defined(CONFIG_NET_VLAN) + struct net_eth_hdr *hdr = NET_ETH_HDR(pkt); + + if (ntohs(hdr->type) == NET_ETH_PTYPE_VLAN) { + struct net_eth_vlan_hdr *hdr_vlan = + (struct net_eth_vlan_hdr *)NET_ETH_HDR(pkt); + + net_pkt_set_vlan_tci(pkt, ntohs(hdr_vlan->vlan.tci)); + *vlan_tag = net_pkt_vlan_tag(pkt); + +#if CONFIG_NET_TC_RX_COUNT > 1 + enum net_priority prio; + + prio = net_vlan2priority(net_pkt_vlan_priority(pkt)); + net_pkt_set_priority(pkt, prio); +#endif + } else { + net_pkt_set_iface(pkt, context->iface); + } +#else /* CONFIG_NET_VLAN */ + net_pkt_set_iface(pkt, context->iface); +#endif /* CONFIG_NET_VLAN */ + /* Feed buffer frame to IP stack */ LOG_DBG("Received packet of length %u", lengthfr); - if (net_recv_data(context->iface, pkt) < 0) { + if (net_recv_data(net_pkt_iface(pkt), pkt) < 0) { net_pkt_unref(pkt); } done: @@ -615,6 +658,7 @@ done: static void eth_enc28j60_rx_thread(struct device *dev) { struct eth_enc28j60_runtime *context = dev->driver_data; + u16_t vlan_tag = NET_VLAN_TAG_UNSPEC; u8_t int_stat; while (true) { @@ -622,7 +666,7 @@ static void eth_enc28j60_rx_thread(struct device *dev) eth_enc28j60_read_reg(dev, ENC28J60_REG_EIR, &int_stat); if (int_stat & ENC28J60_BIT_EIR_PKTIF) { - eth_enc28j60_rx(dev); + eth_enc28j60_rx(dev, &vlan_tag); /* Clear rx interruption flag */ eth_enc28j60_clear_eth_reg(dev, ENC28J60_REG_EIR, ENC28J60_BIT_EIR_PKTIF @@ -635,7 +679,11 @@ static enum ethernet_hw_caps eth_enc28j60_get_capabilities(struct device *dev) { ARG_UNUSED(dev); - return ETHERNET_LINK_10BASE_T; + return ETHERNET_LINK_10BASE_T +#if defined(CONFIG_NET_VLAN) + | ETHERNET_HW_VLAN +#endif + ; } static void eth_enc28j60_iface_init(struct net_if *iface) @@ -646,7 +694,15 @@ static void eth_enc28j60_iface_init(struct net_if *iface) net_if_set_link_addr(iface, context->mac_address, sizeof(context->mac_address), NET_LINK_ETHERNET); - context->iface = iface; + + /* For VLAN, this value is only used to get the correct L2 driver. + * The iface pointer in context should contain the main interface + * if the VLANs are enabled. + */ + if (context->iface == NULL) { + context->iface = iface; + } + ethernet_init(iface); } @@ -773,10 +829,9 @@ static const struct eth_enc28j60_config eth_enc28j60_0_config = { .timeout = CONFIG_ETH_ENC28J60_TIMEOUT, }; -NET_DEVICE_INIT(enc28j60_0, DT_INST_0_MICROCHIP_ENC28J60_LABEL, - eth_enc28j60_init, ð_enc28j60_0_runtime, - ð_enc28j60_0_config, CONFIG_ETH_INIT_PRIORITY, &api_funcs, - ETHERNET_L2, NET_L2_GET_CTX_TYPE(ETHERNET_L2), - NET_ETH_MTU); +ETH_NET_DEVICE_INIT(enc28j60_0, DT_INST_0_MICROCHIP_ENC28J60_LABEL, + eth_enc28j60_init, ð_enc28j60_0_runtime, + ð_enc28j60_0_config, CONFIG_ETH_INIT_PRIORITY, + &api_funcs, NET_ETH_MTU); #endif /* CONFIG_ETH_ENC28J60_0 */