| /* |
| * Copyright (c) 2009 Mindspeed Technologies, Inc. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version 2 |
| * of the License, or (at your option) any later version. |
| |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| * |
| * |
| */ |
| |
| #include "module_bridge.h" |
| #include "fpp_globals.h" |
| #include "modules.h" |
| #include "events.h" |
| #include "module_Rx.h" |
| #include "gemac.h" |
| #include "fpool.h" |
| #include "fpp.h" |
| #include "layer2.h" |
| #include "module_hidrv.h" |
| #include "module_timer.h" |
| #include "module_expt.h" |
| #include "module_ethernet.h" |
| #include "module_ipv4.h" |
| #include "module_ipv6.h" |
| #include "module_stat.h" |
| #include "system.h" |
| #include "fe.h" |
| #include "channels.h" |
| #include "module_qm.h" |
| #include "module_wifi.h" |
| |
| #if defined(COMCERTO_2000) |
| #include "control_bridge.h" |
| #include "control_common.h" |
| PVOID CLASS_DMEM_SH2 (bridge_cache)[NUM_BT_ENTRIES]; |
| PVOID CLASS_PE_LMEM_SH2 (bridge_l3_cache)[NUM_BT_L3_ENTRIES]; |
| |
| static struct _tbridge_enable L2bridge_enabled[MAX_PHY_PORTS]; |
| |
| struct dlist_head hw_bridge_active_list[NUM_BT_ENTRIES]; |
| struct dlist_head hw_bridge_l3_active_list[NUM_BT_L3_ENTRIES]; |
| struct dlist_head hw_bridge_removal_list; |
| struct dlist_head hw_bridge_l3_removal_list; |
| |
| struct dma_pool *bridge_dma_pool; |
| |
| extern TIMER_ENTRY bridge_delayed_removal_timer; |
| |
| #ifdef CFG_BR_MANUAL |
| static U32 CLASS_DMEM_SH(L2Bridge_mode); |
| #else |
| static U32 L2Bridge_mode; |
| #endif |
| |
| U32 L2Bridge_entries; |
| U32 L2Bridge_bin_number; |
| U32 L2Bridge_l3_bin_number; |
| U32 L2Bridge_timeout; |
| #endif /* COMCERTO_2000 */ |
| |
| int rx_Get_Next_Hash_BridgeEntry(PL2BridgeQueryEntryResponse pBridgeCmd , int reset_action); |
| int rx_Get_Next_Hash_L2FlowEntry(PL2BridgeL2FlowEntryCommand pL2FlowCmd, int reset_action); |
| |
| extern TIMER_ENTRY bridge_timer; |
| |
| |
| #if !defined(COMCERTO_2000) |
| static PVOID l2bridge_alloc(void) |
| { |
| return Heap_Alloc_ARAM(sizeof(L2Bridge_entry)); |
| } |
| |
| static void l2bridge_free(PL2Bridge_entry pL2Bridge) |
| { |
| Heap_Free((PVOID) pL2Bridge); |
| } |
| |
| static int l2bridge_add(PL2Bridge_entry pL2Bridge, U32 hash) |
| { |
| /* Add software entry to local hash */ |
| slist_add(&bridge_cache[hash], &pL2Bridge->list); |
| |
| L2Bridge_entries++; |
| |
| return 0; |
| } |
| |
| static void l2bridge_remove(PL2Bridge_entry pL2Bridge, U32 hash) |
| { |
| slist_remove(&bridge_cache[hash], &pL2Bridge->list); |
| |
| l2bridge_free(pL2Bridge); |
| |
| L2Bridge_entries--; |
| } |
| |
| static void l2bridge_enable(U16 port_id, U16 flag) |
| { |
| L2bridge_enabled[port_id].enabled = flag; |
| } |
| |
| static PVOID l2flow_alloc(void) |
| { |
| return Heap_Alloc_ARAM(sizeof(L2Flow_entry)); |
| } |
| |
| static void l2flow_free(PL2Flow_entry pL2Flow) |
| { |
| Heap_Free((PVOID) pL2Flow); |
| } |
| |
| static int l2flow_add(PL2Flow_entry pL2Flow, U32 hash) |
| { |
| /* Add software entry to local hash */ |
| slist_add(&bridge_cache[hash], &pL2Flow->list); |
| |
| L2Bridge_entries++; |
| |
| return 0; |
| } |
| |
| static void l2flow_remove(PL2Flow_entry pL2Flow, U32 hash) |
| { |
| slist_remove(&bridge_cache[hash], &pL2Flow->list); |
| |
| l2flow_free(pL2Bridge); |
| |
| L2Bridge_entries--; |
| } |
| #else // defined(COMCERTO_2000) |
| |
| static void hw_l2_set_mode(U32 mode) |
| { |
| #ifdef CFG_BR_MANUAL |
| int id; |
| |
| for(id = CLASS0_ID; id <= CLASS_MAX_ID; id++) |
| pe_dmem_writel(id, cpu_to_be32(mode), virt_to_class_dmem(&L2Bridge_mode)); |
| #endif |
| } |
| |
| /** Update PE's internal memory bridge cache tables with the HW entry's DDR address |
| * |
| * @entry_ddr DDR physical address of the hw socket entry |
| * |
| */ |
| static void hw_l2bridge_add_to_class(U32 host_addr, U32 hash) |
| { |
| U32 *pe_addr = (U32*)&class_bridge_cache[hash]; |
| int id; |
| |
| for(id = CLASS0_ID; id <= CLASS_MAX_ID; id++) |
| pe_dmem_writel(id, host_addr, virt_to_class_dmem(pe_addr)); |
| } |
| |
| |
| static void hw_l2bridge_schedule_remove(struct _thw_L2Bridge_entry *hw_bridge) |
| { |
| hw_bridge->removal_time = jiffies + 2; |
| dlist_add(&hw_bridge_removal_list, &hw_bridge->list); |
| } |
| |
| /** Processes hardware socket delayed removal list. |
| * Free all hardware socket in the removal list that have reached their removal time. |
| * |
| * |
| */ |
| static void hw_l2bridge_delayed_remove(void) |
| { |
| struct dlist_head *entry; |
| struct _thw_L2Bridge_entry *hw_bridge; |
| |
| dlist_for_each_safe(hw_bridge, entry, &hw_bridge_removal_list, list) |
| { |
| if (!time_after(jiffies, hw_bridge->removal_time)) |
| continue; |
| |
| dlist_remove(&hw_bridge->list); |
| |
| dma_pool_free(bridge_dma_pool, hw_bridge, be32_to_cpu(hw_bridge->dma_addr)); |
| } |
| } |
| |
| static void hw_l2flow_add_to_class(U32 host_addr, U32 hash) |
| { |
| U32 *pe_addr = (U32*)&class_bridge_cache[hash]; |
| int id; |
| |
| for(id = CLASS0_ID; id <= CLASS_MAX_ID; id++) |
| pe_dmem_writel(id, host_addr, virt_to_class_dmem(pe_addr)); |
| } |
| |
| |
| static void hw_l2flow_schedule_remove(struct _thw_L2Flow_entry *hw_l2flow) |
| { |
| hw_l2flow->removal_time = jiffies + 2; |
| dlist_add(&hw_bridge_removal_list, &hw_l2flow->list); |
| } |
| |
| static void hw_l2flow_delayed_remove(void) |
| { |
| struct dlist_head *entry; |
| struct _thw_L2Flow_entry *hw_l2flow; |
| |
| dlist_for_each_safe(hw_l2flow, entry, &hw_bridge_removal_list, list) |
| { |
| if (!time_after(jiffies, hw_l2flow->removal_time)) |
| continue; |
| |
| dlist_remove(&hw_l2flow->list); |
| |
| dma_pool_free(bridge_dma_pool, hw_l2flow, be32_to_cpu(hw_l2flow->dma_addr)); |
| } |
| } |
| |
| U32 hw_l2flow_get_active(struct _thw_L2Flow_entry *hw_l2flow) |
| { |
| return be32_to_cpu(readl(&hw_l2flow->active)); |
| } |
| |
| void hw_l2flow_set_active(struct _thw_L2Flow_entry *hw_l2flow, U32 active) |
| { |
| writel(cpu_to_be32(active), &hw_l2flow->active); |
| } |
| |
| static void hw_l3flow_add_to_class(U32 host_addr, U32 hash) |
| { |
| U32 *pe_addr = (U32*)&class_bridge_l3_cache[hash]; |
| |
| class_bus_writel(host_addr, virt_to_class_pe_lmem(pe_addr)); |
| } |
| |
| static void hw_l3flow_schedule_remove(struct _thw_L3Flow_entry *hw_l3flow) |
| { |
| hw_l3flow->removal_time = jiffies + 2; |
| dlist_add(&hw_bridge_l3_removal_list, &hw_l3flow->list); |
| } |
| |
| static void hw_l3flow_delayed_remove(void) |
| { |
| struct dlist_head *entry; |
| struct _thw_L3Flow_entry *hw_l3flow; |
| |
| dlist_for_each_safe(hw_l3flow, entry, &hw_bridge_l3_removal_list, list) |
| { |
| if (!time_after(jiffies, hw_l3flow->removal_time)) |
| continue; |
| |
| dlist_remove(&hw_l3flow->list); |
| |
| dma_pool_free(bridge_dma_pool, hw_l3flow, be32_to_cpu(hw_l3flow->dma_addr)); |
| } |
| } |
| |
| U32 hw_l3flow_get_active(struct _thw_L3Flow_entry *hw_l3flow) |
| { |
| return be32_to_cpu(readl(&hw_l3flow->active)); |
| } |
| |
| void hw_l3flow_set_active(struct _thw_L3Flow_entry *hw_l3flow, U32 active) |
| { |
| writel(cpu_to_be32(active), &hw_l3flow->active); |
| } |
| |
| static PVOID l2bridge_alloc(void) |
| { |
| return pfe_kzalloc(sizeof(L2Bridge_entry), GFP_KERNEL); |
| } |
| |
| static void l2bridge_free(PL2Bridge_entry pL2Bridge) |
| { |
| pfe_kfree((PVOID)pL2Bridge); |
| } |
| |
| static int l2bridge_add(PL2Bridge_entry pL2Bridge, U32 hash) |
| { |
| struct _thw_L2Bridge_entry *hw_l2bridge; |
| struct _thw_L2Bridge_entry *hw_l2bridge_first; |
| dma_addr_t dma_addr; |
| int rc = NO_ERR; |
| |
| /* Allocate hardware entry */ |
| hw_l2bridge = dma_pool_alloc(bridge_dma_pool, GFP_ATOMIC, &dma_addr); |
| if (!hw_l2bridge) |
| { |
| printk(KERN_ERR "%s: dma alloc failed\n", __func__); |
| rc = ERR_NOT_ENOUGH_MEMORY; |
| goto err; |
| } |
| |
| memset(hw_l2bridge, 0, sizeof(*hw_l2bridge)); |
| hw_l2bridge->dma_addr = cpu_to_be32(dma_addr); |
| |
| /* Link software conntrack to hardware conntrack */ |
| pL2Bridge->hw_l2bridge = hw_l2bridge; |
| hw_l2bridge->sw_l2bridge = pL2Bridge; |
| |
| pL2Bridge->hash = hash; |
| |
| /* add hw entry to active list and update next pointer */ |
| if(!dlist_empty(&hw_bridge_active_list[pL2Bridge->hash])) |
| { |
| /* list is not empty, and we'll be added at head, so current first will become our next pointer */ |
| hw_l2bridge_first = container_of(dlist_first(&hw_bridge_active_list[pL2Bridge->hash]), typeof(struct _thw_L2Bridge_entry), list); |
| hw_entry_set_field(&hw_l2bridge->next, hw_entry_get_field(&hw_l2bridge_first->dma_addr)); |
| } |
| else |
| { |
| /* entry is empty, so we'll be the first and only one entry */ |
| hw_entry_set_field(&hw_l2bridge->next, 0); |
| } |
| |
| dlist_add(&hw_bridge_active_list[pL2Bridge->hash], &hw_l2bridge->list); |
| |
| /* reflect changes to hardware flow */ |
| hw_l2bridge->valid = pL2Bridge->valid; |
| hw_l2bridge->hash = pL2Bridge->hash; |
| |
| memcpy(hw_l2bridge->da, pL2Bridge->da, 6); |
| memcpy(hw_l2bridge->sa, pL2Bridge->sa, 6); |
| |
| hw_l2bridge->ethertype = pL2Bridge->ethertype; /* already set in BE by upper function*/ |
| hw_l2bridge->input_vlan = pL2Bridge->input_vlan; |
| hw_l2bridge->output_vlan = pL2Bridge->output_vlan; |
| hw_l2bridge->session_id = pL2Bridge->session_id; |
| hw_l2bridge->pkt_priority = pL2Bridge->pkt_priority; |
| hw_l2bridge->vlan_priority = pL2Bridge->vlan_priority; |
| hw_l2bridge->vlan_priority_mark = pL2Bridge->vlan_priority_mark; |
| hw_l2bridge->sa_wc = pL2Bridge->sa_wc; |
| hw_l2bridge->qmod = pL2Bridge->qmod; |
| |
| hw_l2bridge->output_itf = (void *)cpu_to_be32(virt_to_class(pL2Bridge->output_itf)); |
| |
| /* Update PE's internal memory bridge cache tables with the HW entry's DDR address */ |
| hw_l2bridge_add_to_class(hw_l2bridge->dma_addr, hash); |
| |
| /* Add software entry to local hash */ |
| slist_add(&bridge_cache[hash], &pL2Bridge->list); |
| |
| L2Bridge_entries++; |
| |
| return NO_ERR; |
| |
| err: |
| return rc; |
| |
| } |
| |
| static void l2bridge_remove(PL2Bridge_entry pL2Bridge, U32 hash) |
| { |
| struct _thw_L2Bridge_entry *hw_l2bridge; |
| struct _thw_L2Bridge_entry *hw_l2bridge_prev; |
| |
| /* Check if there is a hardware flow */ |
| if ((hw_l2bridge = pL2Bridge->hw_l2bridge)) |
| { |
| /* Detach from software socket */ |
| pL2Bridge->hw_l2bridge = NULL; |
| |
| /* if the removed entry is first in hash slot then only PE dmem hash need to be updated */ |
| if (&hw_l2bridge->list == dlist_first(&hw_bridge_active_list[hash])) |
| { |
| hw_l2bridge_add_to_class(hw_entry_get_field(&hw_l2bridge->next), hash); |
| } |
| else |
| { |
| hw_l2bridge_prev = container_of(hw_l2bridge->list.prev, typeof(struct _thw_L2Bridge_entry), list); |
| hw_entry_set_field(&hw_l2bridge_prev->next, hw_entry_get_field(&hw_l2bridge->next)); |
| } |
| |
| dlist_remove(&hw_l2bridge->list); |
| |
| hw_l2bridge_schedule_remove(hw_l2bridge); |
| } |
| |
| slist_remove(&bridge_cache[hash], &pL2Bridge->list); |
| |
| l2bridge_free(pL2Bridge); |
| |
| L2Bridge_entries--; |
| } |
| |
| static void l2bridge_enable(U16 port_id, U16 flag) |
| { |
| #ifdef CFG_BR_MANUAL |
| int id; |
| struct physical_port *port = phy_port_get(port_id); |
| |
| if(flag) |
| port->flags |= L2_BRIDGE_ENABLED; |
| else |
| port->flags &= ~L2_BRIDGE_ENABLED; |
| |
| if( port_id < MAX_PHY_PORTS_FAST) { |
| /* update the DMEM in class-pe */ |
| for (id = CLASS0_ID; id <= CLASS_MAX_ID; id++) |
| { |
| pe_dmem_writeb(id, port->flags, virt_to_class_dmem((void*)&port->flags)); |
| } |
| }else { |
| /* update in PE_LMEM */ |
| class_bus_writeb(port->flags, virt_to_class_pe_lmem(&port->flags)); |
| } |
| #endif |
| |
| L2bridge_enabled[port_id].enabled = flag; |
| } |
| |
| static PVOID l2flow_alloc(void) |
| { |
| return pfe_kzalloc(sizeof(L2Flow_entry), GFP_KERNEL); |
| } |
| |
| static void l2flow_free(PL2Flow_entry pL2Flow) |
| { |
| pfe_kfree((PVOID)pL2Flow); |
| } |
| |
| static int l2flow_add(PL2Flow_entry pL2Flow, U32 hash) |
| { |
| struct _thw_L2Flow_entry *hw_l2flow; |
| struct _thw_L2Flow_entry *hw_l2flow_first; |
| dma_addr_t dma_addr; |
| int rc = NO_ERR; |
| |
| /* Allocate hardware entry */ |
| hw_l2flow = dma_pool_alloc(bridge_dma_pool, GFP_ATOMIC, &dma_addr); |
| if (!hw_l2flow) |
| { |
| printk(KERN_ERR "%s: dma alloc failed\n", __func__); |
| rc = ERR_NOT_ENOUGH_MEMORY; |
| goto err; |
| } |
| |
| memset(hw_l2flow, 0, sizeof(*hw_l2flow)); |
| hw_l2flow->dma_addr = cpu_to_be32(dma_addr); |
| |
| /* Link software conntrack to hardware conntrack */ |
| pL2Flow->hw_l2flow = hw_l2flow; |
| hw_l2flow->sw_l2flow = pL2Flow; |
| |
| /* add hw entry to active list and update next pointer */ |
| if(!dlist_empty(&hw_bridge_active_list[hash])) |
| { |
| /* list is not empty, and we'll be added at head, so current first will become our next pointer */ |
| hw_l2flow_first = container_of(dlist_first(&hw_bridge_active_list[hash]), typeof(struct _thw_L2Flow_entry), list); |
| hw_entry_set_field(&hw_l2flow->next, hw_entry_get_field(&hw_l2flow_first->dma_addr)); |
| } |
| else |
| { |
| /* entry is empty, so we'll be the first and only one entry */ |
| hw_entry_set_field(&hw_l2flow->next, 0); |
| } |
| |
| dlist_add(&hw_bridge_active_list[hash], &hw_l2flow->list); |
| |
| /* reflect changes to hardware flow */ |
| memcpy(hw_l2flow->l2flow.da, pL2Flow->l2flow.da, 6); |
| memcpy(hw_l2flow->l2flow.sa, pL2Flow->l2flow.sa, 6); |
| hw_l2flow->l2flow.ethertype = pL2Flow->l2flow.ethertype; /* already set in BE by upper function*/ |
| hw_l2flow->l2flow.session_id = pL2Flow->l2flow.session_id; |
| hw_l2flow->l2flow.vlan_tag= pL2Flow->l2flow.vlan_tag; |
| |
| hw_l2flow->mark = cpu_to_be16(pL2Flow->mark); |
| hw_l2flow->input_ifindex = cpu_to_be16(pL2Flow->input_ifindex); |
| hw_l2flow->output_if = (struct itf *) cpu_to_be32(virt_to_class(pL2Flow->output_if)); |
| hw_l2flow->nb_l3_ref = cpu_to_be16(pL2Flow->nb_l3_ref); |
| |
| /* Update PE's internal memory bridge cache tables with the HW entry's DDR address */ |
| hw_l2flow_add_to_class(hw_l2flow->dma_addr, hash); |
| |
| /* Add software entry to local hash */ |
| slist_add(&bridge_cache[hash], &pL2Flow->list); |
| |
| L2Bridge_entries++; |
| |
| return NO_ERR; |
| |
| err: |
| l2flow_free(pL2Flow); |
| return rc; |
| |
| } |
| |
| static void l2flow_update(PL2Flow_entry pL2Flow) |
| { |
| struct _thw_L2Flow_entry *hw_l2flow; |
| |
| if ((hw_l2flow = pL2Flow->hw_l2flow) == NULL) |
| return; |
| |
| hw_entry_set_flags(&hw_l2flow->flags, L2FLOW_UPDATING); |
| |
| hw_l2flow->input_ifindex = cpu_to_be16(pL2Flow->input_ifindex); |
| hw_l2flow->output_if = (struct itf *) cpu_to_be32(virt_to_class(pL2Flow->output_if)); |
| |
| hw_entry_set_flags(&hw_l2flow->flags, 0); |
| } |
| |
| static void l2flow_remove(PL2Flow_entry pL2Flow, U32 hash) |
| { |
| struct _thw_L2Flow_entry *hw_l2flow; |
| struct _thw_L2Flow_entry *hw_l2flow_prev; |
| |
| /* Check if there is a hardware flow */ |
| if ((hw_l2flow = pL2Flow->hw_l2flow)) |
| { |
| /* Detach from software socket */ |
| pL2Flow->hw_l2flow = NULL; |
| |
| /* if the removed entry is first in hash slot then only PE dmem hash need to be updated */ |
| if (&hw_l2flow->list == dlist_first(&hw_bridge_active_list[hash])) |
| { |
| hw_l2bridge_add_to_class(hw_entry_get_field(&hw_l2flow->next), hash); |
| } |
| else |
| { |
| hw_l2flow_prev = container_of(hw_l2flow->list.prev, typeof(struct _thw_L2Flow_entry), list); |
| hw_entry_set_field(&hw_l2flow_prev->next, hw_entry_get_field(&hw_l2flow->next)); |
| } |
| |
| dlist_remove(&hw_l2flow->list); |
| |
| hw_l2flow_schedule_remove(hw_l2flow); |
| } |
| |
| slist_remove(&bridge_cache[hash], &pL2Flow->list); |
| |
| l2flow_free(pL2Flow); |
| |
| L2Bridge_entries--; |
| } |
| |
| static PVOID l3flow_alloc(void) |
| { |
| return pfe_kzalloc(sizeof(L3Flow_entry), GFP_KERNEL); |
| } |
| |
| static void l3flow_free(PL3Flow_entry pL3Flow) |
| { |
| pfe_kfree((PVOID)pL3Flow); |
| } |
| |
| static int l3flow_add(PL3Flow_entry pL3Flow, U32 hash) |
| { |
| struct _thw_L3Flow_entry *hw_l3flow; |
| struct _thw_L3Flow_entry *hw_l3flow_first; |
| dma_addr_t dma_addr; |
| int rc = NO_ERR; |
| |
| /* Allocate hardware entry */ |
| hw_l3flow = dma_pool_alloc(bridge_dma_pool, GFP_ATOMIC, &dma_addr); |
| if (!hw_l3flow) |
| { |
| printk(KERN_ERR "%s: dma alloc failed\n", __func__); |
| rc = ERR_NOT_ENOUGH_MEMORY; |
| goto err; |
| } |
| |
| memset(hw_l3flow, 0, sizeof(*hw_l3flow)); |
| hw_l3flow->dma_addr = cpu_to_be32(dma_addr); |
| |
| /* Link software conntrack to hardware conntrack */ |
| pL3Flow->hw_l3flow = hw_l3flow; |
| hw_l3flow->sw_l3flow = pL3Flow; |
| |
| //pL2Flow->hash = hash; |
| |
| /* add hw entry to active list and update next pointer */ |
| if(!dlist_empty(&hw_bridge_l3_active_list[hash])) |
| { |
| /* list is not empty, and we'll be added at head, so current first will become our next pointer */ |
| hw_l3flow_first = container_of(dlist_first(&hw_bridge_l3_active_list[hash]), typeof(struct _thw_L3Flow_entry), list); |
| hw_entry_set_field(&hw_l3flow->next, hw_entry_get_field(&hw_l3flow_first->dma_addr)); |
| } |
| else |
| { |
| /* entry is empty, so we'll be the first and only one entry */ |
| hw_entry_set_field(&hw_l3flow->next, 0); |
| } |
| |
| dlist_add(&hw_bridge_l3_active_list[hash], &hw_l3flow->list); |
| |
| /* reflect changes to hardware flow */ |
| memcpy(&hw_l3flow->l3flow, &pL3Flow->l3flow, sizeof(struct L3Flow)); |
| hw_l3flow->mark = cpu_to_be16(pL3Flow->mark); |
| |
| /* Update PE's internal memory bridge cache tables with the HW entry's DDR address */ |
| hw_l3flow_add_to_class(hw_l3flow->dma_addr, hash); |
| |
| /* Add software entry to local hash */ |
| slist_add(&bridge_l3_cache[hash], &pL3Flow->list); |
| |
| return NO_ERR; |
| |
| err: |
| l3flow_free(pL3Flow); |
| return rc; |
| |
| } |
| |
| static void l3flow_remove(PL3Flow_entry pL3Flow, U32 hash) |
| { |
| struct _thw_L3Flow_entry *hw_l3flow; |
| struct _thw_L3Flow_entry *hw_l3flow_prev; |
| |
| /* Check if there is a hardware flow */ |
| if ((hw_l3flow = pL3Flow->hw_l3flow)) |
| { |
| pL3Flow->hw_l3flow = NULL; |
| |
| /* if the removed entry is first in hash slot then only PE lmem hash need to be updated */ |
| if (&hw_l3flow->list == dlist_first(&hw_bridge_l3_active_list[hash])) |
| { |
| hw_l3flow_add_to_class(hw_entry_get_field(&hw_l3flow->next), hash); |
| } |
| else |
| { |
| hw_l3flow_prev = container_of(hw_l3flow->list.prev, typeof(struct _thw_L3Flow_entry), list); |
| hw_entry_set_field(&hw_l3flow_prev->next, hw_entry_get_field(&hw_l3flow->next)); |
| } |
| |
| dlist_remove(&hw_l3flow->list); |
| |
| hw_l3flow_schedule_remove(hw_l3flow); |
| } |
| |
| slist_remove(&bridge_l3_cache[hash], &pL3Flow->list); |
| |
| pL3Flow->l2flow_entry->nb_l3_ref--; |
| |
| l3flow_free(pL3Flow); |
| } |
| |
| #endif // defined(COMCERTO_2000) |
| |
| static PL2Bridge_entry l2bridge_find_entry(U32 hash, U8 *destaddr, U16 ethertype, U16 vlan_id, U16 session_id) |
| { |
| PL2Bridge_entry pEntry; |
| PL2Bridge_entry pL2Bridge = NULL; |
| struct slist_entry *entry; |
| |
| slist_for_each(pEntry, entry, &bridge_cache[hash], list) |
| { |
| if (TESTEQ_L2ENTRY(pEntry, (void*)destaddr, ethertype, vlan_id, session_id)) |
| pL2Bridge = pEntry; |
| } |
| |
| return pL2Bridge; |
| } |
| |
| static PL2Flow_entry l2flow_find_entry(U32 hash, struct L2Flow_tmp *l2flow) |
| { |
| PL2Flow_entry pEntry; |
| struct slist_entry *entry; |
| |
| slist_for_each(pEntry, entry, &bridge_cache[hash], list) |
| { |
| if (TESTEQ_L2FLOW(l2flow, &pEntry->l2flow)) |
| return pEntry; |
| } |
| |
| return NULL; |
| } |
| |
| static PL3Flow_entry l3flow_find_entry(U32 hash, struct L3Flow *l3flow) |
| { |
| PL3Flow_entry pEntry; |
| struct slist_entry *entry; |
| |
| slist_for_each(pEntry, entry, &bridge_l3_cache[hash], list) |
| { |
| if (TESTEQ_L3FLOW(&pEntry->l3flow, l3flow)) |
| return pEntry; |
| } |
| |
| return NULL; |
| } |
| static int M_bridge_expire_l2_flow_entry(PL2Flow_entry l2flow_entry) |
| { |
| L2BridgeL2FlowEntryCommand *message; |
| struct L2Flow_tmp l2flow_tmp; |
| HostMessage *pmsg; |
| U32 hash; |
| |
| // Send indication message |
| pmsg = msg_alloc(); |
| if (!pmsg) |
| goto err; |
| |
| message = (L2BridgeL2FlowEntryCommand *)pmsg->data; |
| |
| // Prepare indication message |
| memset(message, 0 , sizeof(*message)); |
| message->action = ACTION_REMOVED; |
| SFL_memcpy(message->destaddr, l2flow_entry->l2flow.da, 2 * ETHER_ADDR_LEN); |
| message->ethertype = l2flow_entry->l2flow.ethertype; |
| message->vlan_tag = l2flow_entry->l2flow.vlan_tag; |
| message->session_id = l2flow_entry->l2flow.session_id; |
| |
| pmsg->code = CMD_RX_L2BRIDGE_FLOW_ENTRY; |
| pmsg->length = sizeof(*message); |
| |
| if (msg_send(pmsg) < 0) |
| goto err; |
| |
| l2flow_tmp.da_sa = l2flow_entry->l2flow.da; |
| l2flow_tmp.ethertype = l2flow_entry->l2flow.ethertype; |
| l2flow_tmp.vlan_tag = l2flow_entry->l2flow.vlan_tag; |
| l2flow_tmp.session_id = l2flow_entry->l2flow.session_id; |
| |
| hash = HASH_L2FLOW(&l2flow_tmp); |
| |
| l2flow_remove(l2flow_entry, hash); |
| |
| return 0; |
| |
| err: |
| l2flow_entry->status |= L2_BRIDGE_TIMED_OUT; |
| |
| return 1; |
| } |
| |
| |
| static void M_bridge_timer_l2(unsigned int xstart) |
| { |
| PL2Flow_entry l2flow_entry; |
| U32 i; |
| U32 timer; |
| struct slist_entry *entry; |
| |
| for (i = xstart; i < xstart + L2_BRIDGE_TIMER_BINSIZE; i++) { |
| slist_for_each_safe(l2flow_entry, entry, &bridge_cache[i], list) { |
| #if defined(COMCERTO_2000) |
| struct _thw_L2Flow_entry *hw_l2flow; |
| |
| if ((hw_l2flow = l2flow_entry->hw_l2flow) && hw_l2flow_get_active(hw_l2flow)) { |
| l2flow_entry->last_l2flow_timer = ct_timer; |
| hw_l2flow_set_active(hw_l2flow, 0); |
| } |
| #endif |
| timer = ct_timer - l2flow_entry->last_l2flow_timer; |
| |
| if ((!l2flow_entry->nb_l3_ref) && ((timer > L2Bridge_timeout) || (l2flow_entry->status & L2_BRIDGE_TIMED_OUT))) |
| M_bridge_expire_l2_flow_entry(l2flow_entry); |
| |
| } |
| } |
| } |
| |
| static int M_bridge_expire_l3_flow_entry(PL3Flow_entry l3flow_entry) |
| { |
| |
| L2BridgeL2FlowEntryCommand *message; |
| PL2Flow_entry l2flow_entry; |
| HostMessage *pmsg; |
| unsigned int hash; |
| |
| // Send indication message |
| pmsg = msg_alloc(); |
| if (!pmsg) |
| goto err; |
| |
| l2flow_entry = l3flow_entry->l2flow_entry; |
| |
| message = (L2BridgeL2FlowEntryCommand *)pmsg->data; |
| |
| /* Prepare indication message */ |
| memset(message, 0 , sizeof(*message)); |
| message->action = ACTION_REMOVED; |
| |
| /* L2 info */ |
| SFL_memcpy(message->destaddr, l2flow_entry->l2flow.da, 2 * ETHER_ADDR_LEN); |
| message->ethertype = l2flow_entry->l2flow.ethertype; |
| message->vlan_tag = l2flow_entry->l2flow.vlan_tag; |
| message->session_id = l2flow_entry->l2flow.session_id; |
| |
| /* L3-4 info */ |
| SFL_memcpy(message->saddr, l3flow_entry->l3flow.saddr, IPV6_ADDRESS_LENGTH); |
| SFL_memcpy(message->daddr, l3flow_entry->l3flow.daddr, IPV6_ADDRESS_LENGTH); |
| message->proto = l3flow_entry->l3flow.proto; |
| message->sport = l3flow_entry->l3flow.sport; |
| message->dport = l3flow_entry->l3flow.dport; |
| |
| pmsg->code = CMD_RX_L2BRIDGE_FLOW_ENTRY; |
| pmsg->length = sizeof(*message); |
| |
| if (msg_send(pmsg) < 0) |
| goto err; |
| |
| hash = HASH_L3FLOW(&l3flow_entry->l3flow); |
| |
| l3flow_remove(l3flow_entry, hash); |
| |
| if(!l2flow_entry->nb_l3_ref) { |
| struct L2Flow_tmp l2flow_tmp; |
| |
| l2flow_tmp.da_sa = l2flow_entry->l2flow.da; |
| l2flow_tmp.ethertype = l2flow_entry->l2flow.ethertype; |
| l2flow_tmp.vlan_tag = l2flow_entry->l2flow.vlan_tag; |
| l2flow_tmp.session_id = l2flow_entry->l2flow.session_id; |
| hash = HASH_L2FLOW(&l2flow_tmp); |
| l2flow_remove(l2flow_entry, hash); |
| } |
| |
| return 0; |
| |
| err: |
| l3flow_entry->status |= L2_BRIDGE_TIMED_OUT; |
| |
| return 1; |
| } |
| |
| |
| static void M_bridge_timer_l3(unsigned int xstart) |
| { |
| PL3Flow_entry l3flow_entry; |
| U32 i; |
| U32 timer; |
| struct slist_entry *entry; |
| |
| for (i = xstart; i < xstart + L2_BRIDGE_L3_TIMER_BINSIZE; i++){ |
| slist_for_each_safe(l3flow_entry, entry, &bridge_l3_cache[i], list) { |
| #if defined(COMCERTO_2000) |
| struct _thw_L3Flow_entry *hw_l3flow; |
| |
| if ((hw_l3flow = l3flow_entry->hw_l3flow) && hw_l3flow_get_active(hw_l3flow)) { |
| l3flow_entry->last_l3flow_timer = ct_timer; |
| hw_l3flow_set_active(hw_l3flow, 0); |
| } |
| #endif |
| timer = ct_timer - l3flow_entry->last_l3flow_timer; |
| |
| if ((timer > L2Bridge_timeout) || (l3flow_entry->status & L2_BRIDGE_TIMED_OUT)) |
| M_bridge_expire_l3_flow_entry(l3flow_entry); |
| |
| } |
| } |
| } |
| |
| static void M_bridge_timer(void) |
| { |
| unsigned int bin; |
| |
| bin = L2Bridge_bin_number; |
| L2Bridge_bin_number = (L2Bridge_bin_number + 1) & (L2_BRIDGE_TIMER_NUMBINS-1); |
| M_bridge_timer_l2(bin * L2_BRIDGE_TIMER_BINSIZE); |
| |
| bin = L2Bridge_l3_bin_number; |
| L2Bridge_l3_bin_number = (L2Bridge_l3_bin_number + 1) & (L2_BRIDGE_L3_TIMER_NUMBINS-1); |
| M_bridge_timer_l3(bin * L2_BRIDGE_L3_TIMER_BINSIZE); |
| |
| } |
| |
| static int M_bridge_handle_l2flow(U16 *p, U16 Length) |
| { |
| U16 ackstatus = CMD_OK; |
| char has_l3 = 0; |
| char new_l2 = 0; |
| POnifDesc pOnif; |
| POnifDesc pInif; |
| U32 hash, hash_l3 = 0; |
| struct L2Flow_tmp l2flow_tmp; |
| struct L3Flow l3flow_tmp; |
| PL2Flow_entry l2flow_entry; |
| PL3Flow_entry l3flow_entry = NULL; |
| char reset_action = 0; |
| PL2BridgeL2FlowEntryCommand pcmd = (PL2BridgeL2FlowEntryCommand)p; |
| |
| memset(&l2flow_tmp, 0, sizeof(struct L2Flow)); |
| memset(&l3flow_tmp, 0, sizeof(struct L3Flow)); |
| |
| if(Length != sizeof(L2BridgeL2FlowEntryCommand)) |
| return ERR_WRONG_COMMAND_SIZE; |
| |
| l2flow_tmp.da_sa = pcmd->destaddr; |
| l2flow_tmp.ethertype = pcmd->ethertype; |
| l2flow_tmp.session_id = pcmd->session_id; |
| l2flow_tmp.vlan_tag = pcmd->vlan_tag; |
| |
| SFL_memcpy(l3flow_tmp.saddr, pcmd->saddr, IPV6_ADDRESS_LENGTH); |
| SFL_memcpy(l3flow_tmp.daddr, pcmd->daddr, IPV6_ADDRESS_LENGTH); |
| l3flow_tmp.proto = pcmd->proto; |
| l3flow_tmp.sport = pcmd->sport; |
| l3flow_tmp.dport = pcmd->dport; |
| |
| hash = HASH_L2FLOW(&l2flow_tmp); |
| if((l2flow_entry = l2flow_find_entry(hash, &l2flow_tmp)) == NULL) |
| new_l2 = 1; |
| |
| if(l3flow_tmp.proto){ |
| has_l3 = 1; |
| hash_l3 = HASH_L3FLOW(&l3flow_tmp); |
| l3flow_entry = l3flow_find_entry(hash_l3, &l3flow_tmp); |
| } |
| |
| switch(pcmd->action){ |
| case ACTION_REGISTER: |
| if (l3flow_entry || (!has_l3 && l2flow_entry)) { |
| ackstatus = ERR_BRIDGE_ENTRY_ALREADY_EXISTS; |
| break; |
| } |
| if ((pOnif = get_onif_by_name(pcmd->output_name)) == NULL) { |
| ackstatus = ERR_UNKNOWN_INTERFACE; |
| break; |
| } |
| if ((pInif = get_onif_by_name(pcmd->input_name)) == NULL) { |
| ackstatus = ERR_UNKNOWN_INTERFACE; |
| break; |
| } |
| if (new_l2) |
| if ((l2flow_entry = (PL2Flow_entry)l2flow_alloc()) == NULL) { |
| ackstatus = ERR_NOT_ENOUGH_MEMORY; |
| break; |
| } |
| |
| if (has_l3) { |
| if ((l3flow_entry = (PL3Flow_entry)l3flow_alloc()) == NULL) { |
| ackstatus = ERR_NOT_ENOUGH_MEMORY; |
| if (new_l2) |
| l2flow_free(l2flow_entry); |
| break; |
| } |
| |
| memset(l3flow_entry, 0, sizeof(L3Flow_entry)); |
| SFL_memcpy(&l3flow_entry->l3flow, &l3flow_tmp, sizeof(struct L3Flow)); |
| l3flow_entry->l2flow_entry = l2flow_entry; |
| l3flow_entry->last_l3flow_timer = ct_timer; |
| l3flow_entry->mark = pcmd->mark; |
| if ((ackstatus = l3flow_add(l3flow_entry, hash_l3)) != NO_ERR) { |
| if (new_l2) |
| l2flow_free(l2flow_entry); |
| |
| break; |
| } |
| } |
| |
| if(new_l2){ |
| memset(l2flow_entry, 0, sizeof(L2Flow_entry)); |
| SFL_memcpy(l2flow_entry->l2flow.da, l2flow_tmp.da_sa, 2 * ETHER_ADDR_LEN); |
| l2flow_entry->l2flow.ethertype = l2flow_tmp.ethertype; |
| l2flow_entry->l2flow.session_id = l2flow_tmp.session_id; |
| l2flow_entry->l2flow.vlan_tag = l2flow_tmp.vlan_tag; |
| l2flow_entry->input_ifindex = pInif->itf->index; |
| l2flow_entry->output_if = pOnif->itf; |
| l2flow_entry->last_l2flow_timer = ct_timer; |
| l2flow_entry->mark = pcmd->mark; |
| if (has_l3) |
| l2flow_entry->nb_l3_ref = 1; |
| |
| if ((ackstatus = l2flow_add(l2flow_entry, hash)) != NO_ERR) { |
| if (has_l3) |
| l3flow_remove(l3flow_entry, hash_l3); |
| |
| break; |
| } |
| } |
| else { |
| /* We only need this "exact" value in control path, binary value is only needed in data path */ |
| if (has_l3) |
| l2flow_entry->nb_l3_ref++; |
| } |
| |
| break; |
| |
| case ACTION_UPDATE: |
| if (!l2flow_entry) { |
| ackstatus = ERR_BRIDGE_ENTRY_NOT_FOUND; |
| break; |
| } |
| if((pOnif = get_onif_by_name(pcmd->output_name)) == NULL) { |
| ackstatus = ERR_UNKNOWN_INTERFACE; |
| break; |
| } |
| if((pInif = get_onif_by_name(pcmd->input_name)) == NULL) { |
| ackstatus = ERR_UNKNOWN_INTERFACE; |
| break; |
| } |
| l2flow_entry->input_ifindex= pInif->itf->index; |
| l2flow_entry->output_if = pOnif->itf; |
| l2flow_update(l2flow_entry); |
| |
| break; |
| |
| case ACTION_DEREGISTER: |
| if (!l2flow_entry || (has_l3 && !l3flow_entry)){ |
| ackstatus = ERR_BRIDGE_ENTRY_NOT_FOUND; |
| break; |
| } |
| if(has_l3) |
| l3flow_remove(l3flow_entry, hash_l3); |
| |
| if(!l2flow_entry->nb_l3_ref) |
| l2flow_remove(l2flow_entry, hash); |
| |
| break; |
| |
| case ACTION_QUERY: |
| reset_action = 1; |
| /* fallthrough */ |
| case ACTION_QUERY_CONT: |
| ackstatus = rx_Get_Next_Hash_L2FlowEntry(pcmd, reset_action); |
| break; |
| default: |
| ackstatus = ERR_UNKNOWN_ACTION; |
| break; |
| }//End switch |
| |
| return ackstatus; |
| } |
| |
| /* Important: Prior to mode change bridge reset needs to be performed */ |
| static int M_bridge_handle_control(U16 code, U16 *p, U16 Length) |
| { |
| U16 ackstatus = CMD_OK; |
| PL2BridgeControlCommand prsp = (PL2BridgeControlCommand)p; |
| |
| switch (code) { |
| case CMD_RX_L2BRIDGE_FLOW_TIMEOUT: |
| L2Bridge_timeout = prsp->mode_timeout * L2_BRIDGE_TICKS_PER_SECOND; |
| break; |
| |
| case CMD_RX_L2BRIDGE_MODE: |
| #ifdef CFG_BR_MANUAL |
| if (prsp->mode_timeout > L2_BRIDGE_MODE_AUTO) { |
| ackstatus = ERR_WRONG_COMMAND_PARAM; |
| break; |
| } |
| #else |
| if (prsp->mode_timeout != L2_BRIDGE_MODE_AUTO) { |
| ackstatus = ERR_WRONG_COMMAND_PARAM; |
| break; |
| } |
| #endif |
| L2Bridge_mode = prsp->mode_timeout; |
| hw_l2_set_mode(L2Bridge_mode); |
| |
| if (L2Bridge_mode == L2_BRIDGE_MODE_AUTO) |
| timer_add(&bridge_timer, L2_BRIDGE_TIMER_INTERVAL); |
| else |
| timer_del(&bridge_timer); |
| |
| break; |
| |
| default: |
| ackstatus = ERR_UNKNOWN_COMMAND; |
| break; |
| } |
| |
| return ackstatus; |
| } |
| |
| static int M_bridge_handle_reset(void) |
| { |
| U16 ackstatus = CMD_OK; |
| struct slist_entry *entry; |
| int i; |
| |
| if (L2Bridge_mode == L2_BRIDGE_MODE_AUTO) { |
| PL2Flow_entry l2flow_entry; |
| PL3Flow_entry l3flow_entry; |
| |
| for (i = 0; i < NUM_BT_ENTRIES; i++) { |
| slist_for_each_safe(l3flow_entry, entry, bridge_l3_cache, list) |
| l3flow_remove(l3flow_entry, i); |
| |
| slist_for_each_safe(l2flow_entry, entry, bridge_cache, list) |
| l2flow_remove(l2flow_entry, i); |
| } |
| } |
| else { |
| PL2Bridge_entry l2bridge_entry; |
| |
| for (i = 0; i < NUM_BT_ENTRIES; i++) |
| slist_for_each_safe(l2bridge_entry, entry, bridge_cache, list) |
| l2bridge_remove(l2bridge_entry, i); |
| } |
| |
| return ackstatus; |
| } |
| |
| static void M_bridge_delayed_removal_handler(void) |
| { |
| if (L2Bridge_mode == L2_BRIDGE_MODE_AUTO) { |
| hw_l3flow_delayed_remove(); |
| hw_l2flow_delayed_remove(); |
| } |
| else { |
| hw_l2bridge_delayed_remove(); |
| } |
| } |
| |
| U16 M_bridge_cmdproc(U16 cmd_code, U16 cmd_len, U16 *p) |
| { |
| U16 acklen; |
| U16 ackstatus; |
| U16 action; |
| |
| acklen = 2; |
| ackstatus = CMD_OK; |
| |
| /* For legacy support of manual bridging, TO BE REMOVED */ |
| switch (cmd_code) |
| { |
| case CMD_RX_L2BRIDGE_ENABLE: |
| case CMD_RX_L2BRIDGE_ADD: |
| case CMD_RX_L2BRIDGE_REMOVE: |
| case CMD_RX_L2BRIDGE_QUERY_STATUS: |
| case CMD_RX_L2BRIDGE_QUERY_ENTRY: |
| if(L2Bridge_mode == L2_BRIDGE_MODE_AUTO){ |
| ackstatus = ERR_BRIDGE_WRONG_MODE; |
| goto exit; |
| } |
| break; |
| case CMD_RX_L2BRIDGE_FLOW_ENTRY: |
| case CMD_RX_L2BRIDGE_FLOW_TIMEOUT: |
| case CMD_RX_L2BRIDGE_FLOW_RESET: |
| if(L2Bridge_mode == L2_BRIDGE_MODE_MANUAL){ |
| ackstatus = ERR_BRIDGE_WRONG_MODE; |
| goto exit; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| switch (cmd_code) |
| { |
| case CMD_RX_L2BRIDGE_ENABLE: { |
| PL2BridgeEnableCommand pcmd = (PL2BridgeEnableCommand)p; |
| U16 phy_port_id = pcmd->interface; |
| |
| if ( pcmd->interface >= GEM_PORTS ) |
| { |
| POnifDesc pOnif; |
| |
| if((pOnif = get_onif_by_name(pcmd->input_name)) == NULL) { |
| ackstatus = ERR_UNKNOWN_INTERFACE; break; |
| } |
| |
| phy_port_id = itf_get_phys_port(pOnif->itf); |
| |
| /*FIXME : Below code might not required by c1000 also, need to be removed*/ |
| #if defined(CFG_WIFI_OFFLOAD) && defined(COMCERTO_1000) |
| if( !onif_is_wifi( pOnif ) ) { |
| ackstatus = ERR_UNKNOWN_INTERFACE; break; |
| } |
| #endif |
| } |
| |
| if ( (pcmd->interface != 0xffff) && (phy_port_id < MAX_PHY_PORTS) && (L2bridge_enabled[phy_port_id].used) ) |
| l2bridge_enable(phy_port_id, pcmd->enable_flag); |
| |
| break; |
| } |
| case CMD_RX_L2BRIDGE_ADD: { |
| POnifDesc pOnif; |
| U32 hash; |
| U16 ethertype_bigendian; |
| U16 vlanid_bigendian; |
| U16 session_id; |
| U16 input_interface; |
| struct itf *output_itf; |
| PL2Bridge_entry pEntry; |
| PL2BridgeAddEntryCommand pcmd = (PL2BridgeAddEntryCommand)p; |
| ethertype_bigendian = htons(pcmd->ethertype); // NULL if wildcard entry |
| vlanid_bigendian = htons(pcmd->input_vlan); |
| session_id = htons(pcmd->session_id); |
| |
| if(pcmd->input_interface >= GEM_PORTS) { |
| if((pOnif = get_onif_by_name(pcmd->input_name)) == NULL) { |
| ackstatus = ERR_UNKNOWN_INTERFACE; |
| break; |
| } |
| input_interface = itf_get_phys_port(pOnif->itf); |
| } |
| else |
| input_interface = pcmd->input_interface; |
| |
| if(pcmd->output_interface >= GEM_PORTS) { |
| if((pOnif = get_onif_by_name(pcmd->output_name)) == NULL) { |
| ackstatus = ERR_UNKNOWN_INTERFACE; break; |
| } |
| |
| if (pcmd->output_interface == 0xffff) { |
| if (!(pOnif->itf->type & IF_TYPE_TUNNEL)) { |
| ackstatus = ERR_UNKNOWN_INTERFACE; break; |
| } |
| } |
| else { |
| if (!(pOnif->itf->type & IF_TYPE_PHYSICAL)) { |
| ackstatus = ERR_UNKNOWN_INTERFACE; break; |
| } |
| } |
| |
| output_itf = pOnif->itf; |
| } |
| else { |
| struct physical_port *port = phy_port_get(pcmd->output_interface); |
| output_itf = &port->itf; |
| } |
| |
| if (TESTEQ_NULL_MACADDR(pcmd->srcaddr) || (pcmd->ethertype == 0)) { |
| hash = HASH_L2BRIDGE_WC(pcmd->destaddr, input_interface); |
| } |
| else { |
| hash = HASH_L2BRIDGE(pcmd->destaddr, ethertype_bigendian, input_interface); |
| } |
| |
| if((pEntry = l2bridge_find_entry(hash, pcmd->destaddr, ethertype_bigendian, vlanid_bigendian, session_id))) |
| { |
| ackstatus = ERR_BRIDGE_ENTRY_ALREADY_EXISTS; |
| break; |
| } |
| |
| if ((pEntry = (PL2Bridge_entry)l2bridge_alloc()) == NULL) |
| { |
| ackstatus = ERR_NOT_ENOUGH_MEMORY; |
| break; |
| } |
| memset(pEntry, 0, sizeof(L2Bridge_entry)); |
| |
| SFL_memcpy(pEntry->da, pcmd->destaddr, 2 * ETHER_ADDR_LEN); |
| pEntry->ethertype = ethertype_bigendian; |
| pEntry->session_id = session_id; |
| pEntry->pkt_priority = pcmd->pkt_priority == 0x8000 ? 0xFF : pcmd->pkt_priority; |
| if (pcmd->vlan_priority == 0x8000) |
| pEntry->vlan_priority_mark = 1; |
| else |
| pEntry->vlan_priority = pcmd->vlan_priority; |
| |
| pEntry->input_interface = input_interface; |
| pEntry->input_vlan= vlanid_bigendian; |
| pEntry->output_itf = output_itf; |
| pEntry->output_vlan = htons(pcmd->output_vlan); |
| pEntry->qmod = pcmd->qmod; |
| if (TESTEQ_NULL_MACADDR(pcmd->srcaddr)) |
| pEntry->sa_wc = TRUE; |
| |
| l2bridge_add(pEntry, hash); |
| |
| break; |
| } |
| case CMD_RX_L2BRIDGE_REMOVE: { |
| U32 hash; |
| U16 ethertype_bigendian; |
| U16 vlanid_bigendian; |
| U16 session_id; |
| PL2Bridge_entry pEntry; |
| PL2BridgeRemoveEntryCommand pcmd = (PL2BridgeRemoveEntryCommand)p; |
| ethertype_bigendian = htons(pcmd->ethertype); |
| vlanid_bigendian = htons(pcmd->input_vlan); |
| session_id = htons(pcmd->session_id); |
| |
| //This can be served for WiFi and tunnel interfaces |
| if( pcmd->input_interface >= GEM_PORTS ) |
| { |
| POnifDesc pOnif; |
| |
| if((pOnif = get_onif_by_name(pcmd->input_name)) != NULL) { |
| |
| pcmd->input_interface = itf_get_phys_port(pOnif->itf); |
| } |
| } |
| |
| if (TESTEQ_NULL_MACADDR(pcmd->srcaddr) || (pcmd->ethertype == 0)) { |
| hash = HASH_L2BRIDGE_WC(pcmd->destaddr, pcmd->input_interface); |
| } |
| else { |
| hash = HASH_L2BRIDGE(pcmd->destaddr, ethertype_bigendian, pcmd->input_interface); |
| } |
| |
| if((pEntry = l2bridge_find_entry(hash, pcmd->destaddr, ethertype_bigendian, vlanid_bigendian, session_id)) == NULL) |
| { |
| ackstatus = CMD_ERR; |
| break; |
| } |
| |
| l2bridge_remove(pEntry, hash); |
| |
| break; |
| } |
| case CMD_RX_L2BRIDGE_QUERY_STATUS: { |
| |
| static int bridge_interface_index = 0; |
| PL2BridgeQueryStatusResponse prsp = (PL2BridgeQueryStatusResponse)p; |
| |
| while( bridge_interface_index < MAX_PHY_PORTS ) |
| { |
| if( L2bridge_enabled[bridge_interface_index].used ) |
| break; |
| |
| bridge_interface_index++; |
| } |
| |
| if(bridge_interface_index < MAX_PHY_PORTS) |
| { |
| prsp->status = L2bridge_enabled[bridge_interface_index].enabled; |
| SFL_memcpy(prsp->ifname, get_onif_name(L2bridge_enabled[bridge_interface_index].onif_index), INTERFACE_NAME_LENGTH); |
| bridge_interface_index++; |
| prsp->eof = 0; |
| } |
| else /*if (bridge_interface_index == MAX_PHY_PORTS)*/ |
| { |
| prsp->eof = 1; |
| bridge_interface_index = 0; |
| } |
| |
| acklen = sizeof(L2BridgeQueryStatusResponse); |
| |
| |
| /* This function just initializes static variables for next queries*/ |
| rx_Get_Next_Hash_BridgeEntry((PL2BridgeQueryEntryResponse)p, 1); |
| break; |
| } |
| case CMD_RX_L2BRIDGE_QUERY_ENTRY: |
| { |
| |
| int rc; |
| PL2BridgeQueryEntryResponse prsp = (PL2BridgeQueryEntryResponse)p; |
| rc = rx_Get_Next_Hash_BridgeEntry (prsp , 0); |
| if (rc != NO_ERR) |
| prsp->eof = 1; |
| else |
| prsp->eof = 0; |
| acklen = sizeof(L2BridgeQueryEntryResponse); |
| break; |
| } |
| case CMD_RX_L2BRIDGE_FLOW_ENTRY: |
| action = *p; |
| ackstatus = M_bridge_handle_l2flow(p, cmd_len); |
| if(ackstatus == NO_ERR && ((action == ACTION_QUERY) || (action == ACTION_QUERY_CONT))) |
| acklen += sizeof(L2BridgeL2FlowEntryCommand); |
| break; |
| |
| case CMD_RX_L2BRIDGE_FLOW_TIMEOUT: |
| case CMD_RX_L2BRIDGE_MODE: |
| ackstatus = M_bridge_handle_control(cmd_code, p, cmd_len); |
| break; |
| |
| case CMD_RX_L2BRIDGE_FLOW_RESET: |
| ackstatus = M_bridge_handle_reset(); |
| break; |
| |
| default: |
| ackstatus = ERR_UNKNOWN_COMMAND; |
| break; |
| } |
| exit: |
| *p = ackstatus; |
| return acklen; |
| } |
| |
| |
| #if !defined (COMCERTO_2000) |
| int bridge_init(void) |
| { |
| set_event_handler(EVENT_BRIDGE, M_bridge_entry); |
| |
| set_cmd_handler(EVENT_BRIDGE, M_bridge_cmdproc); |
| |
| timer_init(&bridge_timer, M_bridge_timer); |
| #ifdef CFG_BR_MANUAL |
| L2Bridge_mode = L2_BRIDGE_MODE_MANUAL; |
| #else |
| L2Bridge_mode = L2_BRIDGE_MODE_AUTO; |
| #endif |
| L2Bridge_timeout = L2_BRIDGE_DEFAULT_TIMEOUT * L2_BRIDGE_TICKS_PER_SECOND; |
| L2Bridge_bin_number = 0; |
| |
| L2Bridge_l3_bin_number = 0; |
| |
| return 0; |
| } |
| |
| void bridge_exit(void) |
| { |
| } |
| |
| #else /* COMCERTO_2000 */ |
| |
| int bridge_interface_deregister( U16 phy_port_id ) |
| { |
| if( (phy_port_id >= MAX_PHY_PORTS) || !L2bridge_enabled[phy_port_id].used ) |
| return -1; |
| |
| L2bridge_enabled[phy_port_id].enabled = 0; |
| L2bridge_enabled[phy_port_id].used = 0; |
| |
| return 0; |
| } |
| |
| int bridge_interface_register( U8 *name, U16 phy_port_id ) |
| { |
| POnifDesc pOnif; |
| |
| if( (phy_port_id >= MAX_PHY_PORTS) || L2bridge_enabled[phy_port_id].used ) |
| return -1; |
| |
| pOnif = get_onif_by_name(name); |
| if (!pOnif) |
| return -1; |
| |
| L2bridge_enabled[phy_port_id].onif_index = (U16)get_onif_index(pOnif); |
| |
| L2bridge_enabled[phy_port_id].enabled = 0; |
| L2bridge_enabled[phy_port_id].used = 1; |
| |
| return 0; |
| } |
| |
| |
| int bridge_init(void) |
| { |
| int i; |
| struct pfe_ctrl *ctrl = &pfe->ctrl; |
| |
| bridge_dma_pool = ctrl->dma_pool_512; |
| |
| set_cmd_handler(EVENT_BRIDGE, M_bridge_cmdproc); |
| #ifdef CFG_BR_MANUAL |
| L2Bridge_mode = L2_BRIDGE_MODE_MANUAL; |
| #else |
| L2Bridge_mode = L2_BRIDGE_MODE_AUTO; |
| #endif |
| L2Bridge_timeout = L2_BRIDGE_DEFAULT_TIMEOUT * L2_BRIDGE_TICKS_PER_SECOND; |
| L2Bridge_bin_number = 0; |
| L2Bridge_l3_bin_number = 0; |
| |
| for (i = 0; i < NUM_BT_ENTRIES; i++) { |
| slist_head_init(&bridge_cache[i]); |
| dlist_head_init(&hw_bridge_active_list[i]); |
| } |
| |
| for (i = 0; i < NUM_BT_L3_ENTRIES; i++) { |
| slist_head_init(&bridge_l3_cache[i]); |
| dlist_head_init(&hw_bridge_l3_active_list[i]); |
| } |
| |
| dlist_head_init(&hw_bridge_removal_list); |
| dlist_head_init(&hw_bridge_l3_removal_list); |
| |
| timer_init(&bridge_delayed_removal_timer, M_bridge_delayed_removal_handler); |
| timer_add(&bridge_delayed_removal_timer, CT_TIMER_INTERVAL); |
| |
| timer_init(&bridge_timer, M_bridge_timer); |
| |
| return 0; |
| } |
| |
| |
| void bridge_exit(void) |
| { |
| struct dlist_head *entry; |
| struct _thw_L2Bridge_entry *hw_l2bridge; |
| |
| timer_del(&bridge_delayed_removal_timer); |
| timer_del(&bridge_timer); |
| |
| M_bridge_handle_reset(); |
| |
| dlist_for_each_safe(hw_l2bridge, entry, &hw_bridge_removal_list, list) |
| { |
| dlist_remove(&hw_l2bridge->list); |
| dma_pool_free(bridge_dma_pool, hw_l2bridge, be32_to_cpu(hw_l2bridge->dma_addr)); |
| } |
| } |
| #endif /* COMCERTO_2000 */ |
| |