📄 dev_nm_16esw.c
字号:
BCM_LOG(d,"unknown s-chan command 0x%8.8x\n",cmd);#endif d->schan_cmd_res = 0xFFFFFFFF; }}/* * dev_bcm5605_access() */void *dev_bcm5605_access(cpu_mips_t *cpu,struct vdevice *dev,m_uint32_t offset, u_int op_size,u_int op_type,m_uint64_t *data){ struct nm_16esw_data *d = dev->priv_data; u_int reg; if (op_type == MTS_READ) *data = 0;#if DEBUG_ACCESS if (op_type == MTS_READ) { BCM_LOG(d,"read access to offset=0x%x, pc=0x%llx\n",offset,cpu->pc); } else { BCM_LOG(d,"write access to offset=0x%x, pc=0x%llx, val=0x%llx\n", offset,cpu->pc,*data); }#endif BCM_LOCK(d); switch(offset) { case 0x50: if (op_type == MTS_WRITE) { bcm5600_handle_schan_cmd(d,*data); } else { *data = d->schan_cmd_res; } break; case 0x140: if (op_type == MTS_READ) *data = bcm5600_mii_port_status_bmp(d); break; /* MII input register */ case 0x158: if (op_type == MTS_WRITE) d->mii_input = *data; break; /* MII output register */ case 0x15c: if (op_type == MTS_READ) *data = d->mii_output; break; /* Unknown (related to RX/TX rings ?) */ case 0x104: break; /* TX ring address */ case 0x110: if (op_type == MTS_READ) *data = d->tx_ring_addr; else { d->tx_ring_addr = d->tx_current = *data; d->tx_end_scan = 0;#if DEBUG_TRANSMIT BCM_LOG(d,"tx_ring_addr = 0x%8.8x\n",d->tx_ring_addr);#endif } break; /* RX ring address */ case 0x114: if (op_type == MTS_READ) *data = d->rx_ring_addr; else { d->rx_ring_addr = d->rx_current = *data; d->rx_end_scan = 0;#if DEBUG_RECEIVE BCM_LOG(d,"rx_ring_addr = 0x%8.8x\n",d->rx_ring_addr);#endif } break; /* Interrupt status */ case 0x144: if (op_type == MTS_READ) { *data = 0; /* RX/TX underrun (end of rings reached) */ if (d->tx_end_scan) *data |= BCM5600_INTR_TX_UNDERRUN; if (d->rx_end_scan) *data |= BCM5600_INTR_RX_UNDERRUN; /* RX packet available */ *data |= BCM5600_INTR_RX_AVAIL; /* Link status changed */ if (d->mii_intr) { *data |= BCM5600_INTR_LINKSTAT_MOD; d->mii_intr = FALSE; } } break; /* Interrupt mask */ case 0x148: if (op_type == MTS_READ) *data = d->intr_mask; else d->intr_mask = *data; break; /* Data Words */ case 0x800 ... 0x850: reg = (offset - 0x800) >> 2; if (op_type == MTS_READ) *data = d->dw[reg]; else d->dw[reg] = *data; break;#if DEBUG_UNKNOWN /* Unknown offset */ default: if (op_type == MTS_READ) { BCM_LOG(d,"read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", offset,cpu->pc,op_size); } else { BCM_LOG(d,"write to unknown addr 0x%x, value=0x%llx, " "pc=0x%llx (size=%u)\n",offset,*data,cpu->pc,op_size); }#endif } BCM_UNLOCK(d); return NULL;}/* Show mirroring status */static int bcm5600_mirror_show_status(struct nm_16esw_data *d){ m_uint32_t *port,dst_port; int i; printf("Mirroring status: "); if (!(d->mirror_dst_port & BCM5600_MIRROR_ENABLE)) { printf("disabled.\n\n"); return(FALSE); } printf("enabled. Dest port: "); dst_port = d->mirror_dst_port & BCM5600_MIRROR_PORT_MASK; if (dst_port < 32) printf("%s\n",d->ports[dst_port].name); else printf("none set.\n"); /* Ingress info */ printf(" Ingress Ports: "); for(i=0;i<d->nr_port;i++) { port = bcm5600_table_get_entry(d,d->t_ptable,i); if (port[1] & BCM5600_PTABLE_MI_FLAG) printf("%s ",d->ports[i].name); } printf("\n"); /* Egress info */ printf(" Egress Ports: "); for(i=0;i<d->nr_port;i++) if (d->mirror_egress_ports & (1 << i)) printf("%s ",d->ports[i].name); printf("\n\n"); return(TRUE);}/* Mirror a packet */static int bcm5600_mirror_pkt(struct nm_16esw_data *d,struct bcm5600_pkt *p, int reason){ u_int mport; if (!(d->mirror_dst_port & BCM5600_MIRROR_ENABLE)) return(FALSE);#if DEBUG_MIRROR if (reason == 0) { BCM_LOG(d,"mirroring packet on ingress port %s\n", d->ports[p->ingress_port]); } else { BCM_LOG(d,"mirroring packet on egress port (input port %s)\n", d->ports[p->ingress_port]); } mem_dump(d->vm->log_fd,pkt,pkt_len);#endif mport = d->mirror_dst_port & BCM5600_MIRROR_PORT_MASK; if (mport < 32) netio_send(d->ports[mport].nio,p->pkt,p->pkt_len); return(TRUE);}/* Put a packet into the RX ring (tag it if necessary) */static int bcm5600_send_pkt_to_cpu(struct nm_16esw_data *d, struct bcm5600_pkt *p){ m_uint32_t pkt_addr,pkt_len,dot1q_data; /* If the packet was already sent to CPU, don't send it again */ if (p->sent_to_cpu) return(FALSE); pkt_addr = p->rdes[0]; pkt_len = p->pkt_len; if (p->orig_vlan != -1) { /* 802.1Q packet: copy it directly */ physmem_copy_to_vm(d->vm,p->pkt,pkt_addr,pkt_len); } else { /* untagged packet: copy the dst and src addresses first */ physmem_copy_to_vm(d->vm,p->pkt,pkt_addr,N_ETH_HLEN - 2); /* add the 802.1Q protocol field (0x8100) + VLAN info */ dot1q_data = (N_ETH_PROTO_DOT1Q << 16) | p->real_vlan; physmem_copy_u32_to_vm(d->vm,pkt_addr+N_ETH_HLEN-2,dot1q_data); /* copy the payload */ physmem_copy_to_vm(d->vm,p->pkt+N_ETH_HLEN-2, pkt_addr+sizeof(n_eth_dot1q_hdr_t), pkt_len - (N_ETH_HLEN - 2)); pkt_len += 4; } physmem_copy_u32_to_vm(d->vm,d->rx_current+0x14,0x40000000 + (pkt_len+4)); physmem_copy_u32_to_vm(d->vm,d->rx_current+0x18,0x100 + p->ingress_port); p->sent_to_cpu = TRUE;#if DEBUG_RECEIVE BCM_LOG(d,"sending packet to CPU (orig_vlan=%d).\n",p->orig_vlan);#endif return(TRUE);}/* Source MAC address learning */static int bcm5600_src_mac_learning(struct nm_16esw_data *d, struct bcm5600_pkt *p){ n_eth_hdr_t *eth_hdr = (n_eth_hdr_t *)p->pkt; n_eth_addr_t *src_mac = ð_hdr->saddr; m_uint32_t *arl_entry,*src_port,*trunk; u_int trunk_id,old_ingress_port; int src_mac_index; trunk = NULL; trunk_id = 0; /* Skip multicast sources */ if (eth_addr_is_mcast(src_mac)) return(FALSE); src_port = bcm5600_table_get_entry(d,d->t_ptable,p->ingress_port); assert(src_port != NULL); /* * The packet comes from a trunk port. Prevent sending the packet * to the other ports of the trunk. */ if (src_port[0] & BCM5600_PTABLE_TRUNK_FLAG) { trunk_id = src_port[0] & BCM5600_PTABLE_TGID_MASK; trunk_id >>= BCM5600_PTABLE_TGID_SHIFT; trunk = bcm5600_table_get_entry(d,d->t_tbmap,trunk_id); assert(trunk != NULL); p->egress_filter_bitmap |= trunk[0] & BCM5600_TBMAP_MASK; } /* Source MAC address learning */ src_mac_index = bcm5600_arl_lookup(d,src_mac,p->real_vlan); if (src_mac_index != -1) { arl_entry = bcm5600_table_get_entry(d,d->t_arl,src_mac_index); assert(arl_entry != NULL); old_ingress_port = arl_entry[2] & BCM5600_ARL_PORT_MASK; old_ingress_port >>= BCM5600_ARL_PORT_SHIFT; if (old_ingress_port != p->ingress_port) { /* * Determine if we have a station movement. * If we have a trunk, check if the old ingress port is member * of this trunk, in this case this is not a movement. */ if (trunk != NULL) { if (trunk[0] & (1 << old_ingress_port)) arl_entry[2] |= BCM5600_ARL_HIT_FLAG; else arl_entry[2] &= ~BCM5600_ARL_HIT_FLAG; } else { arl_entry[2] &= ~(BCM5600_ARL_TRUNK_FLAG|BCM5600_ARL_HIT_FLAG); arl_entry[2] &= ~BCM5600_ARL_TGID_MASK; } /* Change the ingress port */ arl_entry[2] &= ~BCM5600_ARL_PORT_MASK; arl_entry[2] |= p->ingress_port << BCM5600_ARL_PORT_SHIFT; return(TRUE); } arl_entry[2] |= BCM5600_ARL_HIT_FLAG; return(TRUE); }#if DEBUG_FORWARD BCM_LOG(d,"source MAC address unknown, learning it.\n");#endif /* Add the new learned MAC address */ src_mac_index = bcm5600_find_free_arl_entry(d); if (src_mac_index == -1) { BCM_LOG(d,"no free entries in ARL table!\n"); return(FALSE); } arl_entry = bcm5600_table_get_entry(d,d->t_arl,src_mac_index); assert(arl_entry != NULL); /* Fill the new ARL entry */ arl_entry[0] = src_mac->eth_addr_byte[2] << 24; arl_entry[0] |= src_mac->eth_addr_byte[3] << 16; arl_entry[0] |= src_mac->eth_addr_byte[4] << 8; arl_entry[0] |= src_mac->eth_addr_byte[5]; arl_entry[1] = src_mac->eth_addr_byte[0] << 8; arl_entry[1] |= src_mac->eth_addr_byte[1]; arl_entry[1] |= p->real_vlan << BCM5600_ARL_VLAN_TAG_SHIFT; arl_entry[2] = BCM5600_ARL_HIT_FLAG; arl_entry[2] |= p->ingress_port << BCM5600_ARL_PORT_SHIFT; if (trunk != NULL) { arl_entry[2] |= BCM5600_ARL_TRUNK_FLAG; arl_entry[2] |= (trunk_id << BCM5600_ARL_TGID_SHIFT); } d->arl_cnt[0]++; return(TRUE);}/* Select an egress port the specified trunk */static int bcm5600_trunk_egress_port(struct nm_16esw_data *d, struct bcm5600_pkt *p, u_int trunk_id){ n_eth_hdr_t *eth_hdr = (n_eth_hdr_t *)p->pkt; struct bcm5600_tg_info *tgi; m_uint32_t *ttr_entry; u_int i,nr_links; u_int hash,port_id; ttr_entry = bcm5600_table_get_entry(d,d->t_ttr,trunk_id); assert(ttr_entry != NULL); nr_links = ttr_entry[1] & BCM5600_TTR_TG_SIZE_MASK; nr_links >>= BCM5600_TTR_TG_SIZE_SHIFT;#if 0 /* Hash on source and destination MAC addresses */ for(i=0,hash=0;i<N_ETH_ALEN;i++) { hash ^= eth_hdr->saddr.eth_addr_byte[i]; hash ^= eth_hdr->daddr.eth_addr_byte[i]; } hash ^= (hash >> 4); port_id = hash % nr_links; /* Maximum of 8 ports per trunk */ assert(hash < BCM5600_MAX_PORTS_PER_TRUNK);#else port_id = d->trunk_last_egress_port[trunk_id] + 1; port_id %= nr_links; #endif /* Save the latest port used for this trunk */ d->trunk_last_egress_port[trunk_id] = port_id; /* Select the egress port */ tgi = &tg_info[port_id]; return((ttr_entry[tgi->index] & tgi->mask) >> tgi->shift);}/* Destination address lookup (take the forwarding decision) */static int bcm5600_dst_mac_lookup(struct nm_16esw_data *d, struct bcm5600_pkt *p){ n_eth_hdr_t *eth_hdr = (n_eth_hdr_t *)p->pkt; n_eth_addr_t *dst_mac = ð_hdr->daddr; struct bcm5600_table *arl_table; m_uint32_t *arl_entry; u_int egress_port; u_int trunk_id; int dst_mac_index; int is_mcast; /* Select the appropriate ARL table and do the lookup on dst MAC + VLAN */ if (eth_addr_is_mcast(dst_mac)) { is_mcast = TRUE; arl_table = d->t_marl; dst_mac_index = bcm5600_marl_lookup(d,dst_mac,p->real_vlan); } else { is_mcast = FALSE; arl_table = d->t_arl; dst_mac_index = bcm5600_arl_lookup(d,dst_mac,p->real_vlan); } /* * Destination Lookup Failure (DLF). * * Use the VLAN bitmap to compute the Egress port bitmap. * Remove the ingress port from it. */ if (dst_mac_index == -1) {#if DEBUG_FORWARD BCM_LOG(d,"Destination MAC address unknown, flooding.\n");#endif p->egress_bitmap = p->vlan_entry[1] & BCM5600_VTABLE_PORT_BMAP_MASK; /* Add the CPU to the egress ports */ p->egress_bitmap |= 1 << d->cpu_port; p->egress_ut_bitmap = p->vlan_entry[2]; p->egress_ut_bitmap &= BCM5600_VTABLE_UT_PORT_BMAP_MASK; return(TRUE); } /* The MAC address was found in the ARL/MARL table */ arl_entry = bcm5600_table_get_entry(d,arl_table,dst_mac_index); assert(arl_entry != NULL); /* If the CPU bit is set, send a copy of the packet to the CPU */ if (arl_entry[1] & BCM5600_ARL_CPU_FLAG) bcm5600_send_pkt_to_cpu(d,p); if (!is_mcast) { /* Unicast: send the packet to the port or trunk found in ARL table */ if (arl_entry[2] & BCM5600_ARL_TRUNK_FLAG) { trunk_id = arl_entry[2] & BCM5600_ARL_TGID_MASK; trunk_id >>= BCM5600_ARL_TGID_SHIFT; /* Select an output port for this trunk */ egress_port = bcm5600_trunk_egress_port(d,p,trunk_id);#if DEBUG_FORWARD BCM_LOG(d,"Sending packet to trunk port %u, egress port %u\n", trunk_id,egress_port);#endif } else { egress_port = arl_entry[2] & BCM5600_ARL_PORT_MASK; egress_port >>= BCM5600_ARL_PORT_SHIFT;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -