|
|
1.1 ! root 1: /* ! 2: * Cisco C3600 simulation platform. ! 3: * Copyright (c) 2006 Christophe Fillot ([email protected]) ! 4: * ! 5: * NM-16ESW ethernet switch module (experimental!) ! 6: * ! 7: * It's an attempt of proof of concept, so not optimized at all at this time. ! 8: * Only L2 switching will be managed (no L3 at all). ! 9: * ! 10: * To do next: QoS features (CoS/DSCP handling). ! 11: */ ! 12: ! 13: #include <stdio.h> ! 14: #include <stdlib.h> ! 15: #include <string.h> ! 16: #include <stdarg.h> ! 17: #include <unistd.h> ! 18: #include <time.h> ! 19: #include <errno.h> ! 20: #include <assert.h> ! 21: ! 22: #include "utils.h" ! 23: #include "timer.h" ! 24: #include "net.h" ! 25: #include "net_io.h" ! 26: #include "ptask.h" ! 27: #include "dev_nm_16esw.h" ! 28: ! 29: /* Debugging flags */ ! 30: #define DEBUG_ACCESS 0 ! 31: #define DEBUG_UNKNOWN 0 ! 32: #define DEBUG_MII 0 ! 33: #define DEBUG_MEM 0 ! 34: #define DEBUG_REG 1 ! 35: #define DEBUG_TRANSMIT 0 ! 36: #define DEBUG_RECEIVE 0 ! 37: #define DEBUG_FORWARD 0 ! 38: #define DEBUG_MIRROR 0 ! 39: #define DEBUG_ARL 0 ! 40: ! 41: /* Invalid VLAN value */ ! 42: #define VLAN_INVALID 0xFFF ! 43: ! 44: /* Maximum packet size */ ! 45: #define BCM5600_MAX_PKT_SIZE 2048 ! 46: ! 47: /* PCI vendor/product codes */ ! 48: #define BCM5605_PCI_VENDOR_ID 0x14e4 ! 49: #define BCM5605_PCI_PRODUCT_ID 0x5605 ! 50: ! 51: /* "S-channel" commands */ ! 52: #define BCM5600_SCHAN_CMD_LINKSCAN 0x13 ! 53: #define BCM5600_SCHAN_CMD_EXEC 0x80 ! 54: #define BCM5600_SCHAN_CMD_READ_MII 0x90 ! 55: #define BCM5600_SCHAN_CMD_WRITE_MII 0x91 ! 56: ! 57: /* Opcodes */ ! 58: #define BCM5600_OP_BP_WARN_STATUS 0x01 ! 59: #define BCM5600_OP_BP_DISCARD_STATUS 0x02 ! 60: #define BCM5600_OP_COS_QSTAT_NOTIFY 0x03 ! 61: #define BCM5600_OP_HOL_STAT_NOTIFY 0x04 ! 62: #define BCM5600_OP_GBP_FULL_NOTIFY 0x05 ! 63: #define BCM5600_OP_GBP_AVAIL_NOTIFY 0x06 ! 64: #define BCM5600_OP_READ_MEM_CMD 0x07 ! 65: #define BCM5600_OP_READ_MEM_ACK 0x08 ! 66: #define BCM5600_OP_WRITE_MEM_CMD 0x09 ! 67: #define BCM5600_OP_WRITE_MEM_ACK 0x0A ! 68: #define BCM5600_OP_READ_REG_CMD 0x0B ! 69: #define BCM5600_OP_READ_REG_ACK 0x0C ! 70: #define BCM5600_OP_WRITE_REG_CMD 0x0D ! 71: #define BCM5600_OP_WRITE_REG_ACK 0x0E ! 72: #define BCM5600_OP_ARL_INSERT_CMD 0x0F ! 73: #define BCM5600_OP_ARL_INSERT_DONE 0x10 ! 74: #define BCM5600_OP_ARL_DELETE_CMD 0x11 ! 75: #define BCM5600_OP_ARL_DELETE_DONE 0x12 ! 76: #define BCM5600_OP_LINKSTAT_NOTIFY 0x13 ! 77: #define BCM5600_OP_MEM_FAIL_NOTIFY 0x14 ! 78: #define BCM5600_OP_INIT_CFAP 0x15 ! 79: #define BCM5600_OP_INIT_SFAP 0x16 ! 80: #define BCM5600_OP_ENTER_DEBUG_MODE 0x17 ! 81: #define BCM5600_OP_EXIT_DEBUG_MODE 0x18 ! 82: #define BCM5600_OP_ARL_LOOKUP_CMD 0x19 ! 83: ! 84: /* Command details */ ! 85: #define BCM5600_CMD_OP_MASK 0xFC000000 ! 86: #define BCM5600_CMD_OP_SHIFT 26 ! 87: #define BCM5600_CMD_DST_MASK 0x03F00000 ! 88: #define BCM5600_CMD_DST_SHIFT 20 ! 89: #define BCM5600_CMD_SRC_MASK 0x000FC000 ! 90: #define BCM5600_CMD_SRC_SHIFT 14 ! 91: #define BCM5600_CMD_LEN_MASK 0x00003F80 ! 92: #define BCM5600_CMD_LEN_SHIFT 7 ! 93: #define BCM5600_CMD_EBIT_MASK 0x00000040 ! 94: #define BCM5600_CMD_EBIT_SHIFT 6 ! 95: #define BCM5600_CMD_ECODE_MASK 0x00000030 ! 96: #define BCM5600_CMD_ECODE_SHIFT 4 ! 97: #define BCM5600_CMD_COS_MASK 0x0000000E ! 98: #define BCM5600_CMD_COS_SHIFT 1 ! 99: #define BCM5600_CMD_CPU_MASK 0x00000001 ! 100: #define BCM5600_CMD_CPU_SHIFT 0 ! 101: ! 102: /* Memory zones */ ! 103: #define BCM5600_ADDR_ARLCNT0 0x01000000 ! 104: #define BCM5600_ADDR_ARLCNT1 0x01100000 ! 105: #define BCM5600_ADDR_ARLCNT2 0x01200000 ! 106: #define BCM5600_ADDR_ARL0 0x02000000 ! 107: #define BCM5600_ADDR_ARL1 0x02100000 ! 108: #define BCM5600_ADDR_ARL2 0x02200000 ! 109: #define BCM5600_ADDR_PTABLE0 0x03000000 ! 110: #define BCM5600_ADDR_PTABLE1 0x03100000 ! 111: #define BCM5600_ADDR_PTABLE2 0x03200000 ! 112: #define BCM5600_ADDR_VTABLE0 0x05000000 ! 113: #define BCM5600_ADDR_VTABLE1 0x05100000 ! 114: #define BCM5600_ADDR_VTABLE2 0x05200000 ! 115: #define BCM5600_ADDR_TTR0 0x06000000 ! 116: #define BCM5600_ADDR_TBMAP0 0x06010000 ! 117: #define BCM5600_ADDR_TTR1 0x06100000 ! 118: #define BCM5600_ADDR_TBMAP1 0x06110000 ! 119: #define BCM5600_ADDR_TTR2 0x06200000 ! 120: #define BCM5600_ADDR_TBMAP2 0x06210000 ! 121: #define BCM5600_ADDR_IMASK0 0x07000000 ! 122: #define BCM5600_ADDR_IRULE0 0x07020000 ! 123: #define BCM5600_ADDR_IMASK1 0x07100000 ! 124: #define BCM5600_ADDR_IRULE1 0x07120000 ! 125: #define BCM5600_ADDR_IMASK2 0x07200000 ! 126: #define BCM5600_ADDR_IRULE2 0x07220000 ! 127: #define BCM5600_ADDR_GIMASK 0x07300000 ! 128: #define BCM5600_ADDR_GIRULE 0x07320000 ! 129: #define BCM5600_ADDR_MARL0 0x08000000 ! 130: #define BCM5600_ADDR_MARL1 0x08100000 ! 131: #define BCM5600_ADDR_MARL2 0x08200000 ! 132: #define BCM5600_ADDR_L3 0x09000000 ! 133: #define BCM5600_ADDR_DEFIP 0x09010000 ! 134: #define BCM5600_ADDR_L3INTF 0x09020000 ! 135: #define BCM5600_ADDR_IPMC 0x09030000 ! 136: #define BCM5600_ADDR_CBPHDR 0x0A600000 ! 137: #define BCM5600_ADDR_CAB0 0x0A610000 ! 138: #define BCM5600_ADDR_CAB1 0x0A620000 ! 139: #define BCM5600_ADDR_CAB2 0x0A630000 ! 140: #define BCM5600_ADDR_CAB3 0x0A640000 ! 141: #define BCM5600_ADDR_CCP 0x0A650000 ! 142: #define BCM5600_ADDR_PPP 0x0A660000 ! 143: #define BCM5600_ADDR_CFAP 0x0A670000 ! 144: #define BCM5600_ADDR_SFAP 0x0A680000 ! 145: #define BCM5600_ADDR_CBPDATA0 0x0A6A0000 ! 146: #define BCM5600_ADDR_CBPDATA1 0x0A6B0000 ! 147: #define BCM5600_ADDR_CBPDATA2 0x0A6C0000 ! 148: #define BCM5600_ADDR_CBPDATA3 0x0A6D0000 ! 149: #define BCM5600_ADDR_PID 0x0A900000 ! 150: #define BCM5600_ADDR_XQ_BASE 0x0B600000 ! 151: #define BCM5600_ADDR_GBP 0x12000000 ! 152: ! 153: /* Number of "Data Words" */ ! 154: #define BCM5600_DW_MAX 32 ! 155: ! 156: /* === VTABLE definitions === */ ! 157: /* Word 0 */ ! 158: #define BCM5600_VTABLE_VLAN_TAG_MASK 0x00000FFF ! 159: ! 160: /* Word 1: Port bitmap */ ! 161: #define BCM5600_VTABLE_PORT_BMAP_MASK 0x1FFFFFFF ! 162: ! 163: /* Word 2: Untagged port bitmap */ ! 164: #define BCM5600_VTABLE_UT_PORT_BMAP_MASK 0x1FFFFFFF ! 165: ! 166: /* Word 3: Module bitmap */ ! 167: #define BCM5600_VTABLE_MOD_BMAP_MASK 0xFFFFFFFF ! 168: ! 169: /* === PTABLE definitions === */ ! 170: /* Word 0 */ ! 171: #define BCM5600_PTABLE_VLAN_TAG_MASK 0x00000FFF ! 172: #define BCM5600_PTABLE_SP_ST_MASK 0x00003000 ! 173: #define BCM5600_PTABLE_SP_ST_SHIFT 12 ! 174: #define BCM5600_PTABLE_PRT_DIS_MASK 0x000FC000 ! 175: #define BCM5600_PTABLE_PRT_DIS_SHIFT 14 ! 176: #define BCM5600_PTABLE_JUMBO_FLAG 0x00100000 ! 177: #define BCM5600_PTABLE_RTAG_MASK 0x00E00000 ! 178: #define BCM5600_PTABLE_RTAG_SHIFT 21 ! 179: #define BCM5600_PTABLE_TGID_MASK 0x07000000 ! 180: #define BCM5600_PTABLE_TGID_SHIFT 24 ! 181: #define BCM5600_PTABLE_TRUNK_FLAG 0x08000000 ! 182: #define BCM5600_PTABLE_CPU_FLAG 0x10000000 ! 183: #define BCM5600_PTABLE_PTYPE_MASK 0x60000000 ! 184: #define BCM5600_PTABLE_PTYPE_SHIFT 29 ! 185: #define BCM5600_PTABLE_BPDU_FLAG 0x80000000 ! 186: ! 187: /* Word 1 */ ! 188: #define BCM5600_PTABLE_PORT_BMAP_MASK 0x1FFFFFFF ! 189: #define BCM5600_PTABLE_MI_FLAG 0x20000000 ! 190: #define BCM5600_PTABLE_CML_MASK 0xC0000000 ! 191: #define BCM5600_PTABLE_CML_SHIFT 30 ! 192: ! 193: /* Word 2 */ ! 194: #define BCM5600_PTABLE_UT_PORT_BMAP_MASK 0x1FFFFFFF ! 195: ! 196: /* Word 4 */ ! 197: #define BCM5600_PTABLE_DSCP_MASK 0x0000003F ! 198: #define BCM5600_PTABLE_DSCP_SHIFT 0 ! 199: #define BCM5600_PTABLE_DSE_MODE_MASK 0x000000C0 ! 200: #define BCM5600_PTABLE_DSE_MODE_SHIFT 6 ! 201: #define BCM5600_PTABLE_RPE_FLAG 0x00000100 ! 202: #define BCM5600_PTABLE_PRI_MASK 0x00000E00 ! 203: #define BCM5600_PTABLE_PRI_SHIFT 9 ! 204: #define BCM5600_PTABLE_L3_DIS_FLAG 0x00001000 ! 205: ! 206: ! 207: /* === ARL (Addess Resolution Logic) definitions === */ ! 208: /* Word 0: MAC address LSB */ ! 209: ! 210: /* Word 1 */ ! 211: #define BCM5600_ARL_MAC_MSB_MASK 0x0000FFFF ! 212: #define BCM5600_ARL_VLAN_TAG_MASK 0x0FFF0000 ! 213: #define BCM5600_ARL_VLAN_TAG_SHIFT 16 ! 214: #define BCM5600_ARL_COS_DST_MASK 0x70000000 ! 215: #define BCM5600_ARL_COS_DST_SHIFT 28 ! 216: #define BCM5600_ARL_CPU_FLAG 0x80000000 ! 217: ! 218: /* Word 2 */ ! 219: #define BCM5600_ARL_L3_FLAG 0x00000001 ! 220: #define BCM5600_ARL_SD_DIS_MASK 0x00000006 ! 221: #define BCM5600_ARL_SD_DIS_SHIFT 1 ! 222: #define BCM5600_ARL_ST_FLAG 0x00000008 ! 223: #define BCM5600_ARL_HIT_FLAG 0x00000010 ! 224: #define BCM5600_ARL_COS_SRC_MASK 0x000000E0 ! 225: #define BCM5600_ARL_COS_SRC_SHIFT 5 ! 226: #define BCM5600_ARL_TRUNK_FLAG 0x00000100 ! 227: #define BCM5600_ARL_TGID_MASK 0x00000E00 ! 228: #define BCM5600_ARL_TGID_SHIFT 9 ! 229: #define BCM5600_ARL_RTAG_MASK 0x00007000 ! 230: #define BCM5600_ARL_RTAG_SHIFT 12 ! 231: #define BCM5600_ARL_PORT_MASK 0x001F8000 ! 232: #define BCM5600_ARL_PORT_SHIFT 15 ! 233: #define BCM5600_ARL_SCP_FLAG 0x00200000 ! 234: #define BCM5600_ARL_MOD_ID_MASK 0x07C00000 ! 235: #define BCM5600_ARL_MOD_ID_SHIFT 22 ! 236: ! 237: /* === Multicast ARL definitions === */ ! 238: /* Word 0: MAC address LSB */ ! 239: ! 240: /* Word 1 */ ! 241: #define BCM5600_MARL_MAC_MSB_MASK 0x0000FFFF ! 242: #define BCM5600_MARL_VLAN_TAG_MASK 0x0FFF0000 ! 243: #define BCM5600_MARL_VLAN_TAG_SHIFT 16 ! 244: #define BCM5600_MARL_COS_DST_MASK 0x70000000 ! 245: #define BCM5600_MARL_COS_DST_SHIFT 28 ! 246: ! 247: /* Word 2 */ ! 248: #define BCM5600_MARL_PORT_BMAP_MASK 0x1FFFFFFF ! 249: ! 250: /* Word 3 */ ! 251: #define BCM5600_MARL_UT_PORT_BMAP_MASK 0x1FFFFFFF ! 252: ! 253: /* Word 4 */ ! 254: #define BCM5600_MARL_MOD_BMAP_MASK 0xFFFFFFFF ! 255: ! 256: /* === Trunk bitmap === */ ! 257: #define BCM5600_TBMAP_MASK 0x0FFFFFFF ! 258: ! 259: /* === Trunk table === */ ! 260: /* Word 0 */ ! 261: #define BCM5600_TTR_TP0_MASK 0x0000003F ! 262: #define BCM5600_TTR_TP0_SHIFT 0 ! 263: #define BCM5600_TTR_TP1_MASK 0x00000FC0 ! 264: #define BCM5600_TTR_TP1_SHIFT 6 ! 265: #define BCM5600_TTR_TP2_MASK 0x0003F000 ! 266: #define BCM5600_TTR_TP2_SHIFT 12 ! 267: #define BCM5600_TTR_TP3_MASK 0x00FC0000 ! 268: #define BCM5600_TTR_TP3_SHIFT 18 ! 269: #define BCM5600_TTR_TP4_MASK 0x3F000000 ! 270: #define BCM5600_TTR_TP4_SHIFT 24 ! 271: ! 272: /* Word 1 */ ! 273: #define BCM5600_TTR_TP5_MASK 0x0000003F ! 274: #define BCM5600_TTR_TP5_SHIFT 0 ! 275: #define BCM5600_TTR_TP6_MASK 0x00000FC0 ! 276: #define BCM5600_TTR_TP6_SHIFT 6 ! 277: #define BCM5600_TTR_TP7_MASK 0x0003F000 ! 278: #define BCM5600_TTR_TP7_SHIFT 12 ! 279: ! 280: #define BCM5600_TTR_TG_SIZE_MASK 0x003C0000 ! 281: #define BCM5600_TTR_TG_SIZE_SHIFT 18 ! 282: ! 283: /* Trunks (port aggregation) */ ! 284: #define BCM5600_MAX_TRUNKS 6 ! 285: #define BCM5600_MAX_PORTS_PER_TRUNK 8 ! 286: ! 287: /* ======================================================================= */ ! 288: ! 289: /* Transmit descriptor size */ ! 290: #define BCM5600_TXD_SIZE 32 ! 291: #define BCM5600_TXD_RING_CONT 0x80000000 /* ring is continuing */ ! 292: #define BCM5600_TXD_UNKNOWN 0x04000000 /* valid packet (?) */ ! 293: #define BCM5600_TXD_NEOP 0x00040000 /* end of packet if not set */ ! 294: ! 295: /* Receive descriptor size */ ! 296: #define BCM5600_RXD_SIZE 32 ! 297: #define BCM5600_RXD_RING_CONT 0x80000000 /* ring is continuing */ ! 298: #define BCM5600_RXD_UNKNOWN 0x00040000 /* unknown */ ! 299: ! 300: /* Interrupt sources */ ! 301: #define BCM5600_INTR_STAT_ITER_DONE 0x00100000 /* Unknown */ ! 302: #define BCM5600_INTR_RX_UNDERRUN 0x00000400 /* RX ring underrun */ ! 303: #define BCM5600_INTR_RX_AVAIL 0x00000200 /* packet available */ ! 304: #define BCM5600_INTR_TX_UNDERRUN 0x00000100 /* TX ring underrun */ ! 305: #define BCM5600_INTR_LINKSTAT_MOD 0x00000010 /* Link status modified */ ! 306: ! 307: /* ======================================================================= */ ! 308: ! 309: /* Port Mirroring */ ! 310: #define BCM5600_MIRROR_ENABLE 0x40 ! 311: #define BCM5600_MIRROR_PORT_MASK 0x3F ! 312: ! 313: /* ======================================================================= */ ! 314: ! 315: #define BCM5600_REG_HASH_SIZE 8192 ! 316: ! 317: /* BCM5600 register */ ! 318: struct bcm5600_reg { ! 319: m_uint32_t addr; ! 320: m_uint32_t value; ! 321: struct bcm5600_reg *next; ! 322: }; ! 323: ! 324: /* BCM5600 table */ ! 325: struct bcm5600_table { ! 326: char *name; ! 327: long offset; ! 328: m_uint32_t addr; ! 329: u_int min_index,max_index; ! 330: u_int nr_words; ! 331: }; ! 332: ! 333: /* BCM5600 in-transit packet */ ! 334: struct bcm5600_pkt { ! 335: /* Received packet data */ ! 336: u_char *pkt; ! 337: ssize_t pkt_len; ! 338: ! 339: /* Rewritten packet (802.1Q tag pushed or poped) */ ! 340: u_char *rewr_pkt; ! 341: int rewrite_done; ! 342: ! 343: /* Original VLAN (-1 for untagged packet) and Real VLAN */ ! 344: int orig_vlan,real_vlan; ! 345: ! 346: /* VLAN entry */ ! 347: m_uint32_t *vlan_entry; ! 348: ! 349: /* Ingress Port and Egress Port bitmap */ ! 350: u_int ingress_port,egress_bitmap,egress_ut_bitmap; ! 351: u_int egress_filter_bitmap; ! 352: ! 353: /* RX descriptor */ ! 354: m_uint32_t rdes[4]; ! 355: ! 356: /* Packet sent to CPU */ ! 357: u_int sent_to_cpu; ! 358: }; ! 359: ! 360: /* BCM5600 physical port */ ! 361: struct bcm5600_port { ! 362: netio_desc_t *nio; ! 363: u_int id; ! 364: char name[32]; ! 365: }; ! 366: ! 367: /* NM-16ESW private data */ ! 368: struct nm_16esw_data { ! 369: char *name; ! 370: u_int nr_port; ! 371: ! 372: vm_instance_t *vm; ! 373: struct vdevice *dev; ! 374: struct pci_device *pci_dev; ! 375: ! 376: pthread_mutex_t lock; ! 377: ! 378: /* Ager task */ ! 379: timer_id ager_tid; ! 380: ! 381: /* S-channel command and command result */ ! 382: m_uint32_t schan_cmd,schan_cmd_res; ! 383: ! 384: /* Data Words */ ! 385: m_uint32_t dw[BCM5600_DW_MAX]; ! 386: ! 387: /* Interrupt mask */ ! 388: m_uint32_t intr_mask; ! 389: ! 390: /* MII registers */ ! 391: m_uint16_t mii_regs[64][32]; ! 392: m_uint32_t mii_input,mii_output; ! 393: u_int mii_intr; ! 394: ! 395: /* RX/TX rings addresses */ ! 396: m_uint32_t rx_ring_addr,tx_ring_addr; ! 397: m_uint32_t tx_current,tx_end_scan; ! 398: m_uint32_t rx_current,rx_end_scan; ! 399: ! 400: /* TX ring scanner task id */ ! 401: ptask_id_t tx_tid; ! 402: ! 403: /* TX buffer */ ! 404: u_char tx_buffer[BCM5600_MAX_PKT_SIZE]; ! 405: u_int tx_bufsize; ! 406: ! 407: /* Port Mirroring */ ! 408: u_int mirror_dst_port; ! 409: u_int mirror_egress_ports; ! 410: ! 411: /* Registers hash table */ ! 412: struct bcm5600_reg *reg_hash_table[BCM5600_REG_HASH_SIZE]; ! 413: ! 414: /* Most used tables... */ ! 415: struct bcm5600_table *t_ptable,*t_vtable; ! 416: struct bcm5600_table *t_arl,*t_marl; ! 417: struct bcm5600_table *t_tbmap,*t_ttr; ! 418: ! 419: /* Ports (only 16 are "real" and usable) */ ! 420: struct bcm5600_port ports[32]; ! 421: ! 422: /* CPU port */ ! 423: u_int cpu_port; ! 424: ! 425: /* Current egress port of all trunks */ ! 426: u_int trunk_last_egress_port[BCM5600_MAX_TRUNKS]; ! 427: ! 428: /* ARL count table */ ! 429: m_uint32_t *arl_cnt; ! 430: ! 431: /* ARL (Address Resolution Logic) Table */ ! 432: m_uint32_t *arl_table; ! 433: ! 434: /* Multicast ARL Table */ ! 435: m_uint32_t *marl_table; ! 436: ! 437: /* VTABLE (VLAN Table) */ ! 438: m_uint32_t *vtable; ! 439: ! 440: /* Trunks */ ! 441: m_uint32_t *ttr,*tbmap; ! 442: ! 443: /* PTABLE (Port Table) */ ! 444: m_uint32_t *ptable; ! 445: }; ! 446: ! 447: /* NM-16ESW Port physical port mapping table (Cisco => BCM) */ ! 448: static int nm16esw_port_mapping[] = { ! 449: 2, 0, 6, 4, 10, 8, 14, 12, 3, 1, 7, 5, 11, 9, 15, 13, ! 450: }; ! 451: ! 452: /* Log a BCM message */ ! 453: #define BCM_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) ! 454: ! 455: /* Lock/Unlock primitives */ ! 456: #define BCM_LOCK(d) pthread_mutex_lock(&(d)->lock) ! 457: #define BCM_UNLOCK(d) pthread_mutex_unlock(&(d)->lock) ! 458: ! 459: /* Trunk group info */ ! 460: struct bcm5600_tg_info { ! 461: u_int index,mask,shift; ! 462: }; ! 463: ! 464: static struct bcm5600_tg_info tg_info[8] = { ! 465: { 0, BCM5600_TTR_TP0_MASK, BCM5600_TTR_TP0_SHIFT }, ! 466: { 0, BCM5600_TTR_TP1_MASK, BCM5600_TTR_TP1_SHIFT }, ! 467: { 0, BCM5600_TTR_TP2_MASK, BCM5600_TTR_TP2_SHIFT }, ! 468: { 0, BCM5600_TTR_TP3_MASK, BCM5600_TTR_TP3_SHIFT }, ! 469: { 0, BCM5600_TTR_TP4_MASK, BCM5600_TTR_TP4_SHIFT }, ! 470: { 1, BCM5600_TTR_TP5_MASK, BCM5600_TTR_TP5_SHIFT }, ! 471: { 1, BCM5600_TTR_TP6_MASK, BCM5600_TTR_TP6_SHIFT }, ! 472: { 1, BCM5600_TTR_TP7_MASK, BCM5600_TTR_TP7_SHIFT }, ! 473: }; ! 474: ! 475: /* Return port status (up or down), based on the MII register */ ! 476: static int bcm5600_mii_port_status(struct nm_16esw_data *d,u_int port) ! 477: { ! 478: u_int mii_ctrl; ! 479: ! 480: mii_ctrl = d->mii_regs[port][0x00]; ! 481: ! 482: /* Isolate bit */ ! 483: return(!(mii_ctrl & 0x400)); ! 484: } ! 485: ! 486: /* Build port status bitmap */ ! 487: static m_uint32_t bcm5600_mii_port_status_bmp(struct nm_16esw_data *d) ! 488: { ! 489: m_uint32_t bmp; ! 490: int i; ! 491: ! 492: for(i=0,bmp=0;i<d->nr_port;i++) ! 493: if (bcm5600_mii_port_status(d,i)) ! 494: bmp |= 1 << i; ! 495: ! 496: return(bmp); ! 497: } ! 498: ! 499: /* Read a MII register */ ! 500: static void bcm5600_mii_read(struct nm_16esw_data *d) ! 501: { ! 502: m_uint8_t port,reg; ! 503: ! 504: port = (d->mii_input >> 16) & 0xFF; ! 505: reg = (d->mii_input >> 24) & 0xFF; ! 506: ! 507: if ((port < 32) && (reg < 32)) { ! 508: d->mii_output = d->mii_regs[port][reg]; ! 509: ! 510: switch(reg) { ! 511: case 0x00: ! 512: d->mii_output &= ~0x8200; ! 513: break; ! 514: case 0x01: ! 515: if (d->ports[port].nio && bcm5600_mii_port_status(d,port)) ! 516: d->mii_output = 0x782C; ! 517: else ! 518: d->mii_output = 0; ! 519: break; ! 520: case 0x02: ! 521: d->mii_output = 0x40; ! 522: break; ! 523: case 0x03: ! 524: d->mii_output = 0x61d4; ! 525: break; ! 526: case 0x04: ! 527: d->mii_output = 0x1E1; ! 528: break; ! 529: case 0x05: ! 530: d->mii_output = 0x41E1; ! 531: break; ! 532: default: ! 533: d->mii_output = 0; ! 534: } ! 535: } ! 536: } ! 537: ! 538: /* Write a MII register */ ! 539: static void bcm5600_mii_write(struct nm_16esw_data *d) ! 540: { ! 541: m_uint8_t port,reg; ! 542: m_uint16_t isolation; ! 543: ! 544: port = (d->mii_input >> 16) & 0xFF; ! 545: reg = (d->mii_input >> 24) & 0xFF; ! 546: ! 547: if ((port < 32) && (reg < 32)) ! 548: { ! 549: #if DEBUG_MII ! 550: BCM_LOG(d,"MII: port 0x%4.4x, reg 0x%2.2x: writing 0x%4.4x\n", ! 551: port,reg,d->mii_input & 0xFFFF); ! 552: #endif ! 553: ! 554: /* Check if PHY isolation status is changing */ ! 555: if (reg == 0) { ! 556: isolation = (d->mii_input ^ d->mii_regs[port][reg]) & 0x400; ! 557: ! 558: if (isolation) { ! 559: #if DEBUG_MII ! 560: BCM_LOG(d,"MII: port 0x%4.4x: generating IRQ\n",port); ! 561: #endif ! 562: d->mii_intr = TRUE; ! 563: pci_dev_trigger_irq(d->vm,d->pci_dev); ! 564: } ! 565: } ! 566: ! 567: d->mii_regs[port][reg] = d->mii_input & 0xFFFF; ! 568: } ! 569: } ! 570: ! 571: /* Hash function for register */ ! 572: static u_int bcm5600_reg_get_hash(m_uint32_t addr) ! 573: { ! 574: return((addr ^ (addr >> 16)) & (BCM5600_REG_HASH_SIZE - 1)); ! 575: } ! 576: ! 577: /* Find a register entry */ ! 578: static struct bcm5600_reg *bcm5600_reg_find(struct nm_16esw_data *d, ! 579: m_uint32_t addr) ! 580: { ! 581: struct bcm5600_reg *reg; ! 582: u_int h_index; ! 583: ! 584: h_index = bcm5600_reg_get_hash(addr); ! 585: for(reg=d->reg_hash_table[h_index];reg;reg=reg->next) ! 586: if (reg->addr == addr) ! 587: return reg; ! 588: ! 589: return NULL; ! 590: } ! 591: ! 592: /* Read a register */ ! 593: static m_uint32_t bcm5600_reg_read(struct nm_16esw_data *d,m_uint32_t addr) ! 594: { ! 595: struct bcm5600_reg *reg; ! 596: ! 597: if (!(reg = bcm5600_reg_find(d,addr))) ! 598: return(0); ! 599: ! 600: return(reg->value); ! 601: } ! 602: ! 603: /* Write a register */ ! 604: static int bcm5600_reg_write(struct nm_16esw_data *d,m_uint32_t addr, ! 605: m_uint32_t value) ! 606: { ! 607: struct bcm5600_reg *reg; ! 608: u_int h_index; ! 609: ! 610: if ((reg = bcm5600_reg_find(d,addr))) { ! 611: reg->value = value; ! 612: return(0); ! 613: } ! 614: ! 615: /* create a new register */ ! 616: if (!(reg = malloc(sizeof(*reg)))) ! 617: return(-1); ! 618: ! 619: reg->addr = addr; ! 620: reg->value = value; ! 621: ! 622: /* insert new register in hash table */ ! 623: h_index = bcm5600_reg_get_hash(addr); ! 624: reg->next = d->reg_hash_table[h_index]; ! 625: d->reg_hash_table[h_index] = reg; ! 626: ! 627: return(0); ! 628: } ! 629: ! 630: /* Register special handling */ ! 631: static void bcm5600_reg_write_special(struct nm_16esw_data *d, ! 632: m_uint32_t addr,m_uint32_t value) ! 633: { ! 634: switch(addr) { ! 635: case 0x80006: ! 636: d->mirror_dst_port = value; ! 637: break; ! 638: ! 639: case 0x8000d: ! 640: d->mirror_egress_ports = value; ! 641: break; ! 642: ! 643: case 0x80009: ! 644: /* age timer */ ! 645: break; ! 646: } ! 647: } ! 648: ! 649: /* Free memory used to store register info */ ! 650: static void bcm5600_reg_free(struct nm_16esw_data *d) ! 651: { ! 652: struct bcm5600_reg *reg,*next; ! 653: int i; ! 654: ! 655: for(i=0;i<BCM5600_REG_HASH_SIZE;i++) ! 656: for(reg=d->reg_hash_table[i];reg;reg=next) { ! 657: next = reg->next; ! 658: free(reg); ! 659: } ! 660: } ! 661: ! 662: /* Dump all known registers */ ! 663: static void bcm5600_reg_dump(struct nm_16esw_data *d,int show_null) ! 664: { ! 665: struct bcm5600_reg *reg; ! 666: int i; ! 667: ! 668: printf("%s: dumping registers:\n",d->name); ! 669: ! 670: for(i=0;i<BCM5600_REG_HASH_SIZE;i++) ! 671: for(reg=d->reg_hash_table[i];reg;reg=reg->next) { ! 672: if (reg->value || show_null) ! 673: printf(" 0x%8.8x: 0x%8.8x\n",reg->addr,reg->value); ! 674: } ! 675: } ! 676: ! 677: /* Fill a string buffer with all ports of the specified bitmap */ ! 678: static char *bcm5600_port_bitmap_str(struct nm_16esw_data *d, ! 679: char *buffer,m_uint32_t bitmap) ! 680: { ! 681: char *ptr = buffer; ! 682: int i; ! 683: ! 684: *ptr = 0; ! 685: ! 686: for(i=0;i<d->nr_port;i++) ! 687: if (bitmap & (1 << i)) { ! 688: ptr += sprintf(ptr,"%s ",d->ports[i].name); ! 689: } ! 690: ! 691: return buffer; ! 692: } ! 693: ! 694: /* BCM5600 tables */ ! 695: #define BCM_OFFSET(x) (OFFSET(struct nm_16esw_data,x)) ! 696: ! 697: static struct bcm5600_table bcm5600_tables[] = { ! 698: /* ARL tables */ ! 699: { "arlcnt0", BCM_OFFSET(arl_cnt), BCM5600_ADDR_ARLCNT0, 0, 0, 1 }, ! 700: { "arlcnt1", BCM_OFFSET(arl_cnt), BCM5600_ADDR_ARLCNT1, 0, 0, 1 }, ! 701: { "arlcnt2", BCM_OFFSET(arl_cnt), BCM5600_ADDR_ARLCNT2, 0, 0, 1 }, ! 702: ! 703: /* ARL tables */ ! 704: { "arl0", BCM_OFFSET(arl_table), BCM5600_ADDR_ARL0, 0, 8191, 3 }, ! 705: { "arl1", BCM_OFFSET(arl_table), BCM5600_ADDR_ARL1, 0, 8191, 3 }, ! 706: { "arl2", BCM_OFFSET(arl_table), BCM5600_ADDR_ARL2, 0, 8191, 3 }, ! 707: ! 708: /* Multicast ARL tables */ ! 709: { "marl0", BCM_OFFSET(marl_table), BCM5600_ADDR_MARL0, 1, 255, 5 }, ! 710: { "marl1", BCM_OFFSET(marl_table), BCM5600_ADDR_MARL1, 1, 255, 5 }, ! 711: { "marl2", BCM_OFFSET(marl_table), BCM5600_ADDR_MARL2, 1, 255, 5 }, ! 712: ! 713: /* PTABLE - Physical Ports */ ! 714: { "ptable0", BCM_OFFSET(ptable), BCM5600_ADDR_PTABLE0, 0, 31, 6 }, ! 715: { "ptable1", BCM_OFFSET(ptable), BCM5600_ADDR_PTABLE1, 0, 31, 6 }, ! 716: { "ptable2", BCM_OFFSET(ptable), BCM5600_ADDR_PTABLE2, 0, 31, 6 }, ! 717: ! 718: /* VTABLE - VLANs */ ! 719: { "vtable0", BCM_OFFSET(vtable), BCM5600_ADDR_VTABLE0, 1, 255, 4 }, ! 720: { "vtable1", BCM_OFFSET(vtable), BCM5600_ADDR_VTABLE1, 1, 255, 4 }, ! 721: { "vtable2", BCM_OFFSET(vtable), BCM5600_ADDR_VTABLE2, 1, 255, 4 }, ! 722: ! 723: /* TTR */ ! 724: { "ttr0", BCM_OFFSET(ttr), BCM5600_ADDR_TTR0, 0, 5, 3 }, ! 725: { "ttr1", BCM_OFFSET(ttr), BCM5600_ADDR_TTR1, 0, 5, 3 }, ! 726: { "ttr2", BCM_OFFSET(ttr), BCM5600_ADDR_TTR2, 0, 5, 3 }, ! 727: ! 728: /* TBMAP */ ! 729: { "tbmap0", BCM_OFFSET(tbmap), BCM5600_ADDR_TBMAP0, 0, 5, 1 }, ! 730: { "tbmap1", BCM_OFFSET(tbmap), BCM5600_ADDR_TBMAP1, 0, 5, 1 }, ! 731: { "tbmap2", BCM_OFFSET(tbmap), BCM5600_ADDR_TBMAP2, 0, 5, 1 }, ! 732: ! 733: { NULL, -1, 0, 0, 0 }, ! 734: }; ! 735: ! 736: /* Get table size (in number of words) */ ! 737: static inline u_int bcm5600_table_get_size(struct bcm5600_table *table) ! 738: { ! 739: return(table->nr_words * (table->max_index + 1)); ! 740: } ! 741: ! 742: /* Create automatically tables */ ! 743: static int bcm5600_table_create(struct nm_16esw_data *d) ! 744: { ! 745: struct bcm5600_table *table; ! 746: m_uint32_t *array; ! 747: size_t nr_words; ! 748: int i; ! 749: ! 750: for(i=0;bcm5600_tables[i].name;i++) ! 751: { ! 752: table = &bcm5600_tables[i]; ! 753: nr_words = bcm5600_table_get_size(table); ! 754: ! 755: if (!(array = calloc(nr_words,sizeof(m_uint32_t)))) { ! 756: fprintf(stderr,"BCM5600: unable to create table '%s'\n",table->name); ! 757: return(-1); ! 758: } ! 759: ! 760: *(PTR_ADJUST(m_uint32_t **,d,table->offset)) = array; ! 761: } ! 762: ! 763: return(0); ! 764: } ! 765: ! 766: /* Free tables */ ! 767: static void bcm5600_table_free(struct nm_16esw_data *d) ! 768: { ! 769: struct bcm5600_table *table; ! 770: m_uint32_t **array; ! 771: int i; ! 772: ! 773: for(i=0;bcm5600_tables[i].name;i++) { ! 774: table = &bcm5600_tables[i]; ! 775: array = (PTR_ADJUST(m_uint32_t **,d,table->offset)); ! 776: free(*array); ! 777: ! 778: /* avoid freeing the same table multiple times */ ! 779: *array = NULL; ! 780: } ! 781: } ! 782: ! 783: /* Find a table given its address */ ! 784: static struct bcm5600_table *bcm5600_table_find(struct nm_16esw_data *d, ! 785: m_uint32_t addr) ! 786: { ! 787: int i; ! 788: ! 789: for(i=0;bcm5600_tables[i].name;i++) ! 790: if (bcm5600_tables[i].addr == addr) ! 791: return(&bcm5600_tables[i]); ! 792: ! 793: #if DEBUG_UNKNOWN ! 794: BCM_LOG(d,"unknown table at address 0x%8.8x\n",addr); ! 795: #endif ! 796: return NULL; ! 797: } ! 798: ! 799: /* Get a table entry */ ! 800: static inline m_uint32_t *bcm5600_table_get_entry(struct nm_16esw_data *d, ! 801: struct bcm5600_table *table, ! 802: m_uint32_t index) ! 803: { ! 804: m_uint32_t *array; ! 805: ! 806: if ((index < table->min_index) || (index > table->max_index)) ! 807: return NULL; ! 808: ! 809: array = *(PTR_ADJUST(m_uint32_t **,d,table->offset)); ! 810: return(&array[index*table->nr_words]); ! 811: } ! 812: ! 813: /* Read a table entry */ ! 814: static int bcm5600_table_read_entry(struct nm_16esw_data *d) ! 815: { ! 816: struct bcm5600_table *table; ! 817: m_uint32_t addr,index,*entry; ! 818: int i; ! 819: ! 820: addr = d->dw[1] & 0xFFFF0000; ! 821: index = d->dw[1] & 0x0000FFFF; ! 822: ! 823: if (!(table = bcm5600_table_find(d,addr))) { ! 824: #if DEBUG_UNKNOWN ! 825: BCM_LOG(d,"unknown mem address at address 0x%8.8x\n",d->dw[1]); ! 826: #endif ! 827: return(-1); ! 828: } ! 829: ! 830: if (!(entry = bcm5600_table_get_entry(d,table,index))) ! 831: return(-1); ! 832: ! 833: #if DEBUG_MEM ! 834: BCM_LOG(d,"READ_MEM: addr=0x%8.8x (table %s)\n",d->dw[1],table->name); ! 835: #endif ! 836: ! 837: for(i=0;i<table->nr_words;i++) ! 838: d->dw[i+1] = entry[i]; ! 839: ! 840: return(0); ! 841: } ! 842: ! 843: /* Write a table entry */ ! 844: static int bcm5600_table_write_entry(struct nm_16esw_data *d) ! 845: { ! 846: struct bcm5600_table *table; ! 847: m_uint32_t addr,index,*entry; ! 848: int i; ! 849: ! 850: addr = d->dw[1] & 0xFFFF0000; ! 851: index = d->dw[1] & 0x0000FFFF; ! 852: ! 853: if (!(table = bcm5600_table_find(d,addr))) ! 854: return(-1); ! 855: ! 856: if (!(entry = bcm5600_table_get_entry(d,table,index))) ! 857: return(-1); ! 858: ! 859: for(i=0;i<table->nr_words;i++) ! 860: entry[i] = d->dw[i+2]; ! 861: ! 862: #if DEBUG_MEM ! 863: { ! 864: char buffer[512],*ptr = buffer; ! 865: ! 866: for(i=0;i<table->nr_words;i++) ! 867: ptr += sprintf(ptr,"data[%d]=0x%8.8x ",i,entry[i]); ! 868: ! 869: BCM_LOG(d,"WRITE_MEM: addr=0x%8.8x (table %s) %s\n", ! 870: d->dw[1],table->name,buffer); ! 871: } ! 872: #endif ! 873: return(0); ! 874: } ! 875: ! 876: /* Dump a table (for debugging) */ ! 877: static int bcm5600_table_dump(struct nm_16esw_data *d,m_uint32_t addr) ! 878: { ! 879: struct bcm5600_table *table; ! 880: m_uint32_t *entry; ! 881: int i,j; ! 882: ! 883: if (!(table = bcm5600_table_find(d,addr))) ! 884: return(-1); ! 885: ! 886: printf("%s: dumping table \"%s\":\n",d->name,table->name); ! 887: ! 888: for(i=table->min_index;i<=table->max_index;i++) { ! 889: if (!(entry = bcm5600_table_get_entry(d,table,i))) ! 890: break; ! 891: ! 892: printf(" %4d:",i); ! 893: ! 894: for(j=0;j<table->nr_words;j++) ! 895: printf("0x%8.8x ",entry[j]); ! 896: ! 897: printf("\n"); ! 898: } ! 899: ! 900: printf("\n"); ! 901: return(0); ! 902: } ! 903: ! 904: /* Dump the VLAN table */ ! 905: static int bcm5600_dump_vtable(struct nm_16esw_data *d) ! 906: { ! 907: struct bcm5600_table *table; ! 908: struct bcm5600_port *port; ! 909: m_uint32_t *entry,tbmp,ubmp; ! 910: u_int vlan; ! 911: int i,j; ! 912: ! 913: if (!(table = bcm5600_table_find(d,BCM5600_ADDR_VTABLE0))) ! 914: return(-1); ! 915: ! 916: printf("%s: dumping VLAN table:\n",d->name); ! 917: ! 918: for(i=table->min_index;i<=table->max_index;i++) { ! 919: if (!(entry = bcm5600_table_get_entry(d,table,i))) ! 920: break; ! 921: ! 922: /* Extract the VLAN info */ ! 923: vlan = entry[0] & BCM5600_VTABLE_VLAN_TAG_MASK; ! 924: ! 925: if (vlan == VLAN_INVALID) ! 926: continue; ! 927: ! 928: printf(" VLAN %4u: ",vlan); ! 929: ! 930: for(j=0;j<d->nr_port;j++) { ! 931: tbmp = entry[1] & (1 << j); ! 932: ubmp = entry[2] & (1 << j); ! 933: ! 934: if (tbmp || ubmp) { ! 935: port = &d->ports[j]; ! 936: ! 937: printf("%s (",port->name); ! 938: ! 939: if (tbmp) ! 940: printf("T%s",ubmp ? "/" : ") "); ! 941: ! 942: if (ubmp) ! 943: printf("UT) "); ! 944: } ! 945: } ! 946: ! 947: printf("\n"); ! 948: } ! 949: ! 950: printf("\n"); ! 951: return(0); ! 952: } ! 953: ! 954: /* Dump the "trunk" ports */ ! 955: static int bcm5600_dump_trunks(struct nm_16esw_data *d) ! 956: { ! 957: struct bcm5600_table *table; ! 958: struct bcm5600_port *port; ! 959: m_uint32_t *entry; ! 960: int i,j; ! 961: ! 962: if (!(table = bcm5600_table_find(d,BCM5600_ADDR_TBMAP0))) ! 963: return(-1); ! 964: ! 965: printf("%s: trunk ports:\n",d->name); ! 966: ! 967: for(i=table->min_index;i<=table->max_index;i++) { ! 968: if (!(entry = bcm5600_table_get_entry(d,table,i))) ! 969: break; ! 970: ! 971: if (!entry[0]) ! 972: continue; ! 973: ! 974: printf(" Trunk %d: ",i); ! 975: ! 976: for(j=0;j<d->nr_port;j++) { ! 977: if (entry[0] & (1 << j)) { ! 978: port = &d->ports[j]; ! 979: printf("%s ",port->name); ! 980: } ! 981: } ! 982: ! 983: printf("\n"); ! 984: } ! 985: ! 986: printf("\n"); ! 987: return(0); ! 988: } ! 989: ! 990: /* Dump the physical port info */ ! 991: static int bcm5600_dump_ports(struct nm_16esw_data *d) ! 992: { ! 993: struct bcm5600_table *table; ! 994: struct bcm5600_port *port; ! 995: m_uint32_t *entry; ! 996: u_int vlan,tgid; ! 997: int i; ! 998: ! 999: if (!(table = bcm5600_table_find(d,BCM5600_ADDR_PTABLE0))) ! 1000: return(-1); ! 1001: ! 1002: printf("%s: physical ports:\n",d->name); ! 1003: ! 1004: for(i=0;i<d->nr_port;i++) { ! 1005: if (!(entry = bcm5600_table_get_entry(d,table,i))) ! 1006: break; ! 1007: ! 1008: port = &d->ports[i]; ! 1009: vlan = entry[0] & BCM5600_PTABLE_VLAN_TAG_MASK; ! 1010: ! 1011: printf(" %-10s: VLAN %u",port->name,vlan); ! 1012: ! 1013: if (entry[0] & BCM5600_PTABLE_TRUNK_FLAG) { ! 1014: tgid = entry[0] & BCM5600_PTABLE_TGID_MASK; ! 1015: tgid >>= BCM5600_PTABLE_TGID_SHIFT; ! 1016: ! 1017: printf(", Trunk Group %u ",tgid); ! 1018: } ! 1019: ! 1020: printf("\n"); ! 1021: } ! 1022: ! 1023: printf("\n"); ! 1024: return(0); ! 1025: } ! 1026: ! 1027: /* Dump the physical port bitmaps */ ! 1028: static int bcm5600_dump_port_bitmaps(struct nm_16esw_data *d) ! 1029: { ! 1030: struct bcm5600_table *table; ! 1031: struct bcm5600_port *port; ! 1032: m_uint32_t *entry,tbmp,ubmp; ! 1033: int i,j; ! 1034: ! 1035: if (!(table = bcm5600_table_find(d,BCM5600_ADDR_PTABLE0))) ! 1036: return(-1); ! 1037: ! 1038: printf("%s: dumping bitmaps of the port table:\n",d->name); ! 1039: ! 1040: for(i=0;i<d->nr_port;i++) { ! 1041: if (!(entry = bcm5600_table_get_entry(d,table,i))) ! 1042: break; ! 1043: ! 1044: port = &d->ports[i]; ! 1045: ! 1046: printf(" %-10s: ",port->name); ! 1047: ! 1048: for(j=0;j<d->nr_port;j++) { ! 1049: tbmp = entry[1] & (1 << j); ! 1050: ubmp = entry[2] & (1 << j); ! 1051: ! 1052: if (tbmp || ubmp) { ! 1053: printf("%s (",d->ports[j].name); ! 1054: ! 1055: if (tbmp) ! 1056: printf("T%s",ubmp ? "/" : ") "); ! 1057: ! 1058: if (ubmp) ! 1059: printf("UT) "); ! 1060: } ! 1061: } ! 1062: ! 1063: printf("\n"); ! 1064: } ! 1065: ! 1066: printf("\n"); ! 1067: return(0); ! 1068: } ! 1069: ! 1070: /* Dump main tables */ ! 1071: static void bcm5600_dump_main_tables(struct nm_16esw_data *d) ! 1072: { ! 1073: bcm5600_dump_ports(d); ! 1074: bcm5600_dump_port_bitmaps(d); ! 1075: bcm5600_dump_vtable(d); ! 1076: bcm5600_dump_trunks(d); ! 1077: } ! 1078: ! 1079: /* Find a free ARL entry */ ! 1080: static int bcm5600_find_free_arl_entry(struct nm_16esw_data *d) ! 1081: { ! 1082: struct bcm5600_table *table = d->t_arl; ! 1083: ! 1084: if (d->arl_cnt[0] == table->max_index) ! 1085: return(-1); ! 1086: ! 1087: return(d->arl_cnt[0] - 1); ! 1088: } ! 1089: ! 1090: /* ARL Lookup. TODO: this must be optimized in the future. */ ! 1091: static inline int bcm5600_gen_arl_lookup(struct nm_16esw_data *d, ! 1092: struct bcm5600_table *table, ! 1093: u_int index_start,u_int index_end, ! 1094: n_eth_addr_t *mac_addr, ! 1095: u_int vlan) ! 1096: { ! 1097: m_uint32_t *entry,tmp[2],mask; ! 1098: int i; ! 1099: ! 1100: tmp[0] = mac_addr->eth_addr_byte[2] << 24; ! 1101: tmp[0] |= mac_addr->eth_addr_byte[3] << 16; ! 1102: tmp[0] |= mac_addr->eth_addr_byte[4] << 8; ! 1103: tmp[0] |= mac_addr->eth_addr_byte[5]; ! 1104: ! 1105: tmp[1] = (mac_addr->eth_addr_byte[0] << 8) | mac_addr->eth_addr_byte[1]; ! 1106: tmp[1] |= vlan << BCM5600_ARL_VLAN_TAG_SHIFT; ! 1107: ! 1108: mask = BCM5600_ARL_VLAN_TAG_MASK | BCM5600_ARL_MAC_MSB_MASK; ! 1109: ! 1110: for(i=index_start;i<index_end;i++) { ! 1111: entry = bcm5600_table_get_entry(d,table,i); ! 1112: ! 1113: if ((entry[0] == tmp[0]) && ((entry[1] & mask) == tmp[1])) ! 1114: return(i); ! 1115: } ! 1116: ! 1117: return(-1); ! 1118: } ! 1119: ! 1120: /* ARL Lookup */ ! 1121: static inline int bcm5600_arl_lookup(struct nm_16esw_data *d, ! 1122: n_eth_addr_t *mac_addr, ! 1123: u_int vlan) ! 1124: { ! 1125: struct bcm5600_table *table = d->t_arl; ! 1126: return(bcm5600_gen_arl_lookup(d,table,1,d->arl_cnt[0]-1,mac_addr,vlan)); ! 1127: } ! 1128: ! 1129: /* MARL Lookup */ ! 1130: static inline int bcm5600_marl_lookup(struct nm_16esw_data *d, ! 1131: n_eth_addr_t *mac_addr, ! 1132: u_int vlan) ! 1133: { ! 1134: struct bcm5600_table *table = d->t_marl; ! 1135: return(bcm5600_gen_arl_lookup(d,table,table->min_index,table->max_index+1, ! 1136: mac_addr,vlan)); ! 1137: } ! 1138: ! 1139: /* Invalidate an ARL entry */ ! 1140: static void bcm5600_invalidate_arl_entry(m_uint32_t *entry) ! 1141: { ! 1142: entry[0] = entry[1] = entry[2] = 0; ! 1143: } ! 1144: ! 1145: /* Insert an entry into the ARL table */ ! 1146: static int bcm5600_insert_arl_entry(struct nm_16esw_data *d) ! 1147: { ! 1148: struct bcm5600_table *table = d->t_arl; ! 1149: m_uint32_t *entry,mask; ! 1150: int i,index; ! 1151: ! 1152: mask = BCM5600_ARL_VLAN_TAG_MASK | BCM5600_ARL_MAC_MSB_MASK; ! 1153: ! 1154: for(i=0;i<d->arl_cnt[0]-1;i++) { ! 1155: entry = bcm5600_table_get_entry(d,table,i); ! 1156: ! 1157: /* If entry already exists, just modify it */ ! 1158: if ((entry[0] == d->dw[1]) && ((entry[1] & mask) == (d->dw[2] & mask))) { ! 1159: entry[0] = d->dw[1]; ! 1160: entry[1] = d->dw[2]; ! 1161: entry[2] = d->dw[3]; ! 1162: d->dw[1] = i; ! 1163: return(0); ! 1164: } ! 1165: } ! 1166: ! 1167: index = d->arl_cnt[0] - 1; ! 1168: ! 1169: entry = bcm5600_table_get_entry(d,table,index); ! 1170: entry[0] = d->dw[1]; ! 1171: entry[1] = d->dw[2]; ! 1172: entry[2] = d->dw[3]; ! 1173: d->dw[1] = index; ! 1174: ! 1175: d->arl_cnt[0]++; ! 1176: return(0); ! 1177: } ! 1178: ! 1179: /* Delete an entry from the ARL table */ ! 1180: static int bcm5600_delete_arl_entry(struct nm_16esw_data *d) ! 1181: { ! 1182: struct bcm5600_table *table; ! 1183: m_uint32_t *entry,*last_entry,mac_msb; ! 1184: u_int cvlan,vlan; ! 1185: int i; ! 1186: ! 1187: if (!(table = bcm5600_table_find(d,BCM5600_ADDR_ARL0))) ! 1188: return(-1); ! 1189: ! 1190: vlan = d->dw[2] & BCM5600_ARL_VLAN_TAG_MASK; ! 1191: vlan >>= BCM5600_ARL_VLAN_TAG_SHIFT; ! 1192: ! 1193: mac_msb = d->dw[2] & BCM5600_ARL_MAC_MSB_MASK; ! 1194: ! 1195: for(i=table->min_index;i<=table->max_index;i++) { ! 1196: entry = bcm5600_table_get_entry(d,table,i); ! 1197: ! 1198: /* compare VLANs and MAC addresses */ ! 1199: cvlan = (entry[1] & BCM5600_ARL_VLAN_TAG_MASK); ! 1200: cvlan >>= BCM5600_ARL_VLAN_TAG_SHIFT; ! 1201: ! 1202: if ((cvlan == vlan) && (entry[0] == d->dw[1]) && ! 1203: ((entry[1] & BCM5600_ARL_MAC_MSB_MASK) == mac_msb)) ! 1204: { ! 1205: d->dw[1] = i; ! 1206: ! 1207: last_entry = bcm5600_table_get_entry(d,d->t_arl,d->arl_cnt[0]-2); ! 1208: ! 1209: entry[0] = last_entry[0]; ! 1210: entry[1] = last_entry[1]; ! 1211: entry[2] = last_entry[2]; ! 1212: ! 1213: d->arl_cnt[0]--; ! 1214: return(i); ! 1215: } ! 1216: } ! 1217: ! 1218: return(0); ! 1219: } ! 1220: ! 1221: /* Reset the ARL tables */ ! 1222: static int bcm5600_reset_arl(struct nm_16esw_data *d) ! 1223: { ! 1224: struct bcm5600_table *table; ! 1225: m_uint32_t *entry; ! 1226: int i; ! 1227: ! 1228: if (!(table = bcm5600_table_find(d,BCM5600_ADDR_ARL0))) ! 1229: return(-1); ! 1230: ! 1231: for(i=table->min_index;i<=table->max_index;i++) { ! 1232: entry = bcm5600_table_get_entry(d,table,i); ! 1233: bcm5600_invalidate_arl_entry(entry); ! 1234: } ! 1235: ! 1236: return(0); ! 1237: } ! 1238: ! 1239: /* MAC Address Ager */ ! 1240: static int bcm5600_arl_ager(struct nm_16esw_data *d) ! 1241: { ! 1242: m_uint32_t *entry,*last_entry; ! 1243: int i; ! 1244: ! 1245: BCM_LOCK(d); ! 1246: ! 1247: for(i=1;i<d->arl_cnt[0]-1;i++) { ! 1248: entry = bcm5600_table_get_entry(d,d->t_arl,i); ! 1249: assert(entry); ! 1250: ! 1251: if (entry[2] & BCM5600_ARL_ST_FLAG) ! 1252: continue; ! 1253: ! 1254: /* The entry has expired, purge it */ ! 1255: if (!(entry[2] & BCM5600_ARL_HIT_FLAG)) { ! 1256: last_entry = bcm5600_table_get_entry(d,d->t_arl,d->arl_cnt[0]-2); ! 1257: ! 1258: entry[0] = last_entry[0]; ! 1259: entry[1] = last_entry[1]; ! 1260: entry[2] = last_entry[2]; ! 1261: ! 1262: d->arl_cnt[0]--; ! 1263: i--; ! 1264: } else { ! 1265: entry[2] &= ~BCM5600_ARL_HIT_FLAG; ! 1266: } ! 1267: } ! 1268: ! 1269: BCM_UNLOCK(d); ! 1270: return(TRUE); ! 1271: } ! 1272: ! 1273: /* Get the VTABLE entry matching the specified VLAN */ ! 1274: static m_uint32_t *bcm5600_vtable_get_entry_by_vlan(struct nm_16esw_data *d, ! 1275: u_int vlan) ! 1276: { ! 1277: struct bcm5600_table *table = d->t_vtable; ! 1278: m_uint32_t *entry; ! 1279: int i; ! 1280: ! 1281: for(i=table->min_index;i<=table->max_index;i++) { ! 1282: if (!(entry = bcm5600_table_get_entry(d,table,i))) ! 1283: break; ! 1284: ! 1285: if ((entry[0] & BCM5600_VTABLE_VLAN_TAG_MASK) == vlan) ! 1286: return entry; ! 1287: } ! 1288: ! 1289: return NULL; ! 1290: } ! 1291: ! 1292: /* Read memory command */ ! 1293: static void bcm5600_handle_read_mem_cmd(struct nm_16esw_data *d) ! 1294: { ! 1295: int i; ! 1296: ! 1297: if (bcm5600_table_read_entry(d) != 0) { ! 1298: for(i=1;i<BCM5600_DW_MAX;i++) ! 1299: d->dw[i] = 0; ! 1300: } ! 1301: ! 1302: d->dw[0] = BCM5600_OP_READ_MEM_ACK << BCM5600_CMD_OP_SHIFT; ! 1303: } ! 1304: ! 1305: /* Write memory command */ ! 1306: static void bcm5600_handle_write_mem_cmd(struct nm_16esw_data *d) ! 1307: { ! 1308: bcm5600_table_write_entry(d); ! 1309: d->dw[0] = BCM5600_OP_WRITE_MEM_ACK << BCM5600_CMD_OP_SHIFT; ! 1310: } ! 1311: ! 1312: /* Handle a "general" command */ ! 1313: static void bcm5600_handle_gen_cmd(struct nm_16esw_data *d) ! 1314: { ! 1315: m_uint32_t op,src,dst,len; ! 1316: ! 1317: /* Extract the opcode */ ! 1318: op = (d->dw[0] & BCM5600_CMD_OP_MASK) >> BCM5600_CMD_OP_SHIFT; ! 1319: src = (d->dw[0] & BCM5600_CMD_SRC_MASK) >> BCM5600_CMD_SRC_SHIFT; ! 1320: dst = (d->dw[0] & BCM5600_CMD_DST_MASK) >> BCM5600_CMD_DST_SHIFT; ! 1321: len = (d->dw[0] & BCM5600_CMD_LEN_MASK) >> BCM5600_CMD_LEN_SHIFT; ! 1322: ! 1323: #if DEBUG_ACCESS ! 1324: BCM_LOG(d,"gen_cmd: opcode 0x%2.2x [src=0x%2.2x,dst=0x%2.2x,len=0x%2.2x] " ! 1325: "(dw[0]=0x%8.8x, dw[1]=0x%8.8x, dw[2]=0x%8.8x, dw[3]=0x%8.8x)\n", ! 1326: op,src,dst,len,d->dw[0],d->dw[1],d->dw[2],d->dw[3]); ! 1327: #endif ! 1328: ! 1329: switch(op) { ! 1330: case BCM5600_OP_READ_MEM_CMD: ! 1331: bcm5600_handle_read_mem_cmd(d); ! 1332: break; ! 1333: ! 1334: case BCM5600_OP_WRITE_MEM_CMD: ! 1335: bcm5600_handle_write_mem_cmd(d); ! 1336: break; ! 1337: ! 1338: case BCM5600_OP_READ_REG_CMD: ! 1339: d->dw[0] = BCM5600_OP_READ_REG_ACK << BCM5600_CMD_OP_SHIFT; ! 1340: #if DEBUG_REG ! 1341: BCM_LOG(d,"READ_REG: reg_addr=0x%8.8x\n",d->dw[1]); ! 1342: #endif ! 1343: d->dw[1] = bcm5600_reg_read(d,d->dw[1]); ! 1344: break; ! 1345: ! 1346: case BCM5600_OP_WRITE_REG_CMD: ! 1347: d->dw[0] = BCM5600_OP_WRITE_REG_ACK << BCM5600_CMD_OP_SHIFT; ! 1348: #if DEBUG_REG ! 1349: BCM_LOG(d,"WRITE_REG: reg_addr=0x%8.8x val=0x%8.8x\n", ! 1350: d->dw[1],d->dw[2]); ! 1351: #endif ! 1352: bcm5600_reg_write(d,d->dw[1],d->dw[2]); ! 1353: bcm5600_reg_write_special(d,d->dw[1],d->dw[2]); ! 1354: break; ! 1355: ! 1356: case BCM5600_OP_ARL_INSERT_CMD: ! 1357: d->dw[0] = BCM5600_OP_ARL_INSERT_DONE << BCM5600_CMD_OP_SHIFT; ! 1358: ! 1359: #if DEBUG_ARL ! 1360: BCM_LOG(d,"ARL_INSERT_CMD " ! 1361: "(dw[1]=0x%8.8x,dw[2]=0x%8.8x,dw[3]=0x%8.8x)\n", ! 1362: d->dw[1],d->dw[2],d->dw[3]); ! 1363: #endif ! 1364: bcm5600_insert_arl_entry(d); ! 1365: break; ! 1366: ! 1367: case BCM5600_OP_ARL_DELETE_CMD: ! 1368: d->dw[0] = BCM5600_OP_ARL_DELETE_DONE << BCM5600_CMD_OP_SHIFT; ! 1369: ! 1370: #if DEBUG_ARL ! 1371: BCM_LOG(d,"ARL_DELETE_CMD (dw[1]=0x%8.8x,dw[2]=0x%8.8x)\n", ! 1372: d->dw[1],d->dw[2]); ! 1373: #endif ! 1374: bcm5600_delete_arl_entry(d); ! 1375: break; ! 1376: ! 1377: case BCM5600_OP_ARL_LOOKUP_CMD: ! 1378: d->dw[0] = BCM5600_OP_READ_MEM_ACK << BCM5600_CMD_OP_SHIFT; ! 1379: break; ! 1380: ! 1381: default: ! 1382: BCM_LOG(d,"unknown opcode 0x%8.8x (cmd=0x%8.8x)\n",op,d->dw[0]); ! 1383: } ! 1384: } ! 1385: ! 1386: /* Handle a s-channel command */ ! 1387: static void bcm5600_handle_schan_cmd(struct nm_16esw_data *d,m_uint32_t cmd) ! 1388: { ! 1389: d->schan_cmd = cmd; ! 1390: ! 1391: #if DEBUG_ACCESS ! 1392: BCM_LOG(d,"s-chan command 0x%8.8x\n",cmd); ! 1393: #endif ! 1394: ! 1395: switch(cmd) { ! 1396: case BCM5600_SCHAN_CMD_EXEC: ! 1397: bcm5600_handle_gen_cmd(d); ! 1398: d->schan_cmd_res = 0xFFFFFFFF; ! 1399: break; ! 1400: ! 1401: case BCM5600_SCHAN_CMD_READ_MII: ! 1402: bcm5600_mii_read(d); ! 1403: d->schan_cmd_res = 0xFFFFFFFF; ! 1404: break; ! 1405: ! 1406: case BCM5600_SCHAN_CMD_WRITE_MII: ! 1407: bcm5600_mii_write(d); ! 1408: d->schan_cmd_res = 0xFFFFFFFF; ! 1409: break; ! 1410: ! 1411: case BCM5600_SCHAN_CMD_LINKSCAN: ! 1412: d->schan_cmd_res = 0x0; ! 1413: break; ! 1414: ! 1415: default: ! 1416: #if DEBUG_UNKNOWN ! 1417: BCM_LOG(d,"unknown s-chan command 0x%8.8x\n",cmd); ! 1418: #endif ! 1419: d->schan_cmd_res = 0xFFFFFFFF; ! 1420: } ! 1421: } ! 1422: ! 1423: /* ! 1424: * dev_bcm5605_access() ! 1425: */ ! 1426: void *dev_bcm5605_access(cpu_mips_t *cpu,struct vdevice *dev,m_uint32_t offset, ! 1427: u_int op_size,u_int op_type,m_uint64_t *data) ! 1428: { ! 1429: struct nm_16esw_data *d = dev->priv_data; ! 1430: u_int reg; ! 1431: ! 1432: if (op_type == MTS_READ) ! 1433: *data = 0; ! 1434: ! 1435: #if DEBUG_ACCESS ! 1436: if (op_type == MTS_READ) { ! 1437: BCM_LOG(d,"read access to offset=0x%x, pc=0x%llx\n",offset,cpu->pc); ! 1438: } else { ! 1439: BCM_LOG(d,"write access to offset=0x%x, pc=0x%llx, val=0x%llx\n", ! 1440: offset,cpu->pc,*data); ! 1441: } ! 1442: #endif ! 1443: ! 1444: BCM_LOCK(d); ! 1445: ! 1446: switch(offset) { ! 1447: case 0x50: ! 1448: if (op_type == MTS_WRITE) { ! 1449: bcm5600_handle_schan_cmd(d,*data); ! 1450: } else { ! 1451: *data = d->schan_cmd_res; ! 1452: } ! 1453: break; ! 1454: ! 1455: case 0x140: ! 1456: if (op_type == MTS_READ) ! 1457: *data = bcm5600_mii_port_status_bmp(d); ! 1458: break; ! 1459: ! 1460: /* MII input register */ ! 1461: case 0x158: ! 1462: if (op_type == MTS_WRITE) ! 1463: d->mii_input = *data; ! 1464: break; ! 1465: ! 1466: /* MII output register */ ! 1467: case 0x15c: ! 1468: if (op_type == MTS_READ) ! 1469: *data = d->mii_output; ! 1470: break; ! 1471: ! 1472: /* Unknown (related to RX/TX rings ?) */ ! 1473: case 0x104: ! 1474: break; ! 1475: ! 1476: /* TX ring address */ ! 1477: case 0x110: ! 1478: if (op_type == MTS_READ) ! 1479: *data = d->tx_ring_addr; ! 1480: else { ! 1481: d->tx_ring_addr = d->tx_current = *data; ! 1482: d->tx_end_scan = 0; ! 1483: #if DEBUG_TRANSMIT ! 1484: BCM_LOG(d,"tx_ring_addr = 0x%8.8x\n",d->tx_ring_addr); ! 1485: #endif ! 1486: } ! 1487: break; ! 1488: ! 1489: /* RX ring address */ ! 1490: case 0x114: ! 1491: if (op_type == MTS_READ) ! 1492: *data = d->rx_ring_addr; ! 1493: else { ! 1494: d->rx_ring_addr = d->rx_current = *data; ! 1495: d->rx_end_scan = 0; ! 1496: #if DEBUG_RECEIVE ! 1497: BCM_LOG(d,"rx_ring_addr = 0x%8.8x\n",d->rx_ring_addr); ! 1498: #endif ! 1499: } ! 1500: break; ! 1501: ! 1502: /* Interrupt status */ ! 1503: case 0x144: ! 1504: if (op_type == MTS_READ) { ! 1505: *data = 0; ! 1506: ! 1507: /* RX/TX underrun (end of rings reached) */ ! 1508: if (d->tx_end_scan) ! 1509: *data |= BCM5600_INTR_TX_UNDERRUN; ! 1510: ! 1511: if (d->rx_end_scan) ! 1512: *data |= BCM5600_INTR_RX_UNDERRUN; ! 1513: ! 1514: /* RX packet available */ ! 1515: *data |= BCM5600_INTR_RX_AVAIL; ! 1516: ! 1517: /* Link status changed */ ! 1518: if (d->mii_intr) { ! 1519: *data |= BCM5600_INTR_LINKSTAT_MOD; ! 1520: d->mii_intr = FALSE; ! 1521: } ! 1522: } ! 1523: break; ! 1524: ! 1525: /* Interrupt mask */ ! 1526: case 0x148: ! 1527: if (op_type == MTS_READ) ! 1528: *data = d->intr_mask; ! 1529: else ! 1530: d->intr_mask = *data; ! 1531: break; ! 1532: ! 1533: /* Data Words */ ! 1534: case 0x800 ... 0x850: ! 1535: reg = (offset - 0x800) >> 2; ! 1536: ! 1537: if (op_type == MTS_READ) ! 1538: *data = d->dw[reg]; ! 1539: else ! 1540: d->dw[reg] = *data; ! 1541: break; ! 1542: ! 1543: #if DEBUG_UNKNOWN ! 1544: /* Unknown offset */ ! 1545: default: ! 1546: if (op_type == MTS_READ) { ! 1547: BCM_LOG(d,"read from unknown addr 0x%x, pc=0x%llx (size=%u)\n", ! 1548: offset,cpu->pc,op_size); ! 1549: } else { ! 1550: BCM_LOG(d,"write to unknown addr 0x%x, value=0x%llx, " ! 1551: "pc=0x%llx (size=%u)\n",offset,*data,cpu->pc,op_size); ! 1552: } ! 1553: #endif ! 1554: } ! 1555: ! 1556: BCM_UNLOCK(d); ! 1557: return NULL; ! 1558: } ! 1559: ! 1560: /* Show mirroring status */ ! 1561: static int bcm5600_mirror_show_status(struct nm_16esw_data *d) ! 1562: { ! 1563: m_uint32_t *port,dst_port; ! 1564: int i; ! 1565: ! 1566: printf("Mirroring status: "); ! 1567: ! 1568: if (!(d->mirror_dst_port & BCM5600_MIRROR_ENABLE)) { ! 1569: printf("disabled.\n\n"); ! 1570: return(FALSE); ! 1571: } ! 1572: ! 1573: printf("enabled. Dest port: "); ! 1574: ! 1575: dst_port = d->mirror_dst_port & BCM5600_MIRROR_PORT_MASK; ! 1576: ! 1577: if (dst_port < 32) ! 1578: printf("%s\n",d->ports[dst_port].name); ! 1579: else ! 1580: printf("none set.\n"); ! 1581: ! 1582: /* Ingress info */ ! 1583: printf(" Ingress Ports: "); ! 1584: ! 1585: for(i=0;i<d->nr_port;i++) { ! 1586: port = bcm5600_table_get_entry(d,d->t_ptable,i); ! 1587: if (port[1] & BCM5600_PTABLE_MI_FLAG) ! 1588: printf("%s ",d->ports[i].name); ! 1589: } ! 1590: ! 1591: printf("\n"); ! 1592: ! 1593: /* Egress info */ ! 1594: printf(" Egress Ports: "); ! 1595: ! 1596: for(i=0;i<d->nr_port;i++) ! 1597: if (d->mirror_egress_ports & (1 << i)) ! 1598: printf("%s ",d->ports[i].name); ! 1599: ! 1600: printf("\n\n"); ! 1601: return(TRUE); ! 1602: } ! 1603: ! 1604: /* Mirror a packet */ ! 1605: static int bcm5600_mirror_pkt(struct nm_16esw_data *d,struct bcm5600_pkt *p, ! 1606: int reason) ! 1607: { ! 1608: u_int mport; ! 1609: ! 1610: if (!(d->mirror_dst_port & BCM5600_MIRROR_ENABLE)) ! 1611: return(FALSE); ! 1612: ! 1613: #if DEBUG_MIRROR ! 1614: if (reason == 0) { ! 1615: BCM_LOG(d,"mirroring packet on ingress port %s\n", ! 1616: d->ports[p->ingress_port]); ! 1617: } else { ! 1618: BCM_LOG(d,"mirroring packet on egress port (input port %s)\n", ! 1619: d->ports[p->ingress_port]); ! 1620: } ! 1621: mem_dump(d->vm->log_fd,pkt,pkt_len); ! 1622: #endif ! 1623: ! 1624: mport = d->mirror_dst_port & BCM5600_MIRROR_PORT_MASK; ! 1625: if (mport < 32) ! 1626: netio_send(d->ports[mport].nio,p->pkt,p->pkt_len); ! 1627: return(TRUE); ! 1628: } ! 1629: ! 1630: /* Put a packet into the RX ring (tag it if necessary) */ ! 1631: static int bcm5600_send_pkt_to_cpu(struct nm_16esw_data *d, ! 1632: struct bcm5600_pkt *p) ! 1633: { ! 1634: m_uint32_t pkt_addr,pkt_len,dot1q_data; ! 1635: ! 1636: /* If the packet was already sent to CPU, don't send it again */ ! 1637: if (p->sent_to_cpu) ! 1638: return(FALSE); ! 1639: ! 1640: pkt_addr = p->rdes[0]; ! 1641: pkt_len = p->pkt_len; ! 1642: ! 1643: if (p->orig_vlan != -1) { ! 1644: /* 802.1Q packet: copy it directly */ ! 1645: physmem_copy_to_vm(d->vm,p->pkt,pkt_addr,pkt_len); ! 1646: } else { ! 1647: /* untagged packet: copy the dst and src addresses first */ ! 1648: physmem_copy_to_vm(d->vm,p->pkt,pkt_addr,N_ETH_HLEN - 2); ! 1649: ! 1650: /* add the 802.1Q protocol field (0x8100) + VLAN info */ ! 1651: dot1q_data = (N_ETH_PROTO_DOT1Q << 16) | p->real_vlan; ! 1652: physmem_copy_u32_to_vm(d->vm,pkt_addr+N_ETH_HLEN-2,dot1q_data); ! 1653: ! 1654: /* copy the payload */ ! 1655: physmem_copy_to_vm(d->vm,p->pkt+N_ETH_HLEN-2, ! 1656: pkt_addr+sizeof(n_eth_dot1q_hdr_t), ! 1657: pkt_len - (N_ETH_HLEN - 2)); ! 1658: pkt_len += 4; ! 1659: } ! 1660: ! 1661: physmem_copy_u32_to_vm(d->vm,d->rx_current+0x14,0x40000000 + (pkt_len+4)); ! 1662: physmem_copy_u32_to_vm(d->vm,d->rx_current+0x18,0x100 + p->ingress_port); ! 1663: p->sent_to_cpu = TRUE; ! 1664: ! 1665: #if DEBUG_RECEIVE ! 1666: BCM_LOG(d,"sending packet to CPU (orig_vlan=%d).\n",p->orig_vlan); ! 1667: #endif ! 1668: return(TRUE); ! 1669: } ! 1670: ! 1671: /* Source MAC address learning */ ! 1672: static int bcm5600_src_mac_learning(struct nm_16esw_data *d, ! 1673: struct bcm5600_pkt *p) ! 1674: { ! 1675: n_eth_hdr_t *eth_hdr = (n_eth_hdr_t *)p->pkt; ! 1676: n_eth_addr_t *src_mac = ð_hdr->saddr; ! 1677: m_uint32_t *arl_entry,*src_port,*trunk; ! 1678: u_int trunk_id,old_ingress_port; ! 1679: int src_mac_index; ! 1680: ! 1681: trunk = NULL; ! 1682: trunk_id = 0; ! 1683: ! 1684: /* Skip multicast sources */ ! 1685: if (eth_addr_is_mcast(src_mac)) ! 1686: return(FALSE); ! 1687: ! 1688: src_port = bcm5600_table_get_entry(d,d->t_ptable,p->ingress_port); ! 1689: assert(src_port != NULL); ! 1690: ! 1691: /* ! 1692: * The packet comes from a trunk port. Prevent sending the packet ! 1693: * to the other ports of the trunk. ! 1694: */ ! 1695: if (src_port[0] & BCM5600_PTABLE_TRUNK_FLAG) { ! 1696: trunk_id = src_port[0] & BCM5600_PTABLE_TGID_MASK; ! 1697: trunk_id >>= BCM5600_PTABLE_TGID_SHIFT; ! 1698: ! 1699: trunk = bcm5600_table_get_entry(d,d->t_tbmap,trunk_id); ! 1700: assert(trunk != NULL); ! 1701: ! 1702: p->egress_filter_bitmap |= trunk[0] & BCM5600_TBMAP_MASK; ! 1703: } ! 1704: ! 1705: /* Source MAC address learning */ ! 1706: src_mac_index = bcm5600_arl_lookup(d,src_mac,p->real_vlan); ! 1707: ! 1708: if (src_mac_index != -1) { ! 1709: arl_entry = bcm5600_table_get_entry(d,d->t_arl,src_mac_index); ! 1710: assert(arl_entry != NULL); ! 1711: ! 1712: old_ingress_port = arl_entry[2] & BCM5600_ARL_PORT_MASK; ! 1713: old_ingress_port >>= BCM5600_ARL_PORT_SHIFT; ! 1714: ! 1715: if (old_ingress_port != p->ingress_port) ! 1716: { ! 1717: /* ! 1718: * Determine if we have a station movement. ! 1719: * If we have a trunk, check if the old ingress port is member ! 1720: * of this trunk, in this case this is not a movement. ! 1721: */ ! 1722: if (trunk != NULL) { ! 1723: if (trunk[0] & (1 << old_ingress_port)) ! 1724: arl_entry[2] |= BCM5600_ARL_HIT_FLAG; ! 1725: else ! 1726: arl_entry[2] &= ~BCM5600_ARL_HIT_FLAG; ! 1727: } else { ! 1728: arl_entry[2] &= ~(BCM5600_ARL_TRUNK_FLAG|BCM5600_ARL_HIT_FLAG); ! 1729: arl_entry[2] &= ~BCM5600_ARL_TGID_MASK; ! 1730: } ! 1731: ! 1732: /* Change the ingress port */ ! 1733: arl_entry[2] &= ~BCM5600_ARL_PORT_MASK; ! 1734: arl_entry[2] |= p->ingress_port << BCM5600_ARL_PORT_SHIFT; ! 1735: return(TRUE); ! 1736: } ! 1737: ! 1738: arl_entry[2] |= BCM5600_ARL_HIT_FLAG; ! 1739: return(TRUE); ! 1740: } ! 1741: ! 1742: #if DEBUG_FORWARD ! 1743: BCM_LOG(d,"source MAC address unknown, learning it.\n"); ! 1744: #endif ! 1745: ! 1746: /* Add the new learned MAC address */ ! 1747: src_mac_index = bcm5600_find_free_arl_entry(d); ! 1748: ! 1749: if (src_mac_index == -1) { ! 1750: BCM_LOG(d,"no free entries in ARL table!\n"); ! 1751: return(FALSE); ! 1752: } ! 1753: ! 1754: arl_entry = bcm5600_table_get_entry(d,d->t_arl,src_mac_index); ! 1755: assert(arl_entry != NULL); ! 1756: ! 1757: /* Fill the new ARL entry */ ! 1758: arl_entry[0] = src_mac->eth_addr_byte[2] << 24; ! 1759: arl_entry[0] |= src_mac->eth_addr_byte[3] << 16; ! 1760: arl_entry[0] |= src_mac->eth_addr_byte[4] << 8; ! 1761: arl_entry[0] |= src_mac->eth_addr_byte[5]; ! 1762: ! 1763: arl_entry[1] = src_mac->eth_addr_byte[0] << 8; ! 1764: arl_entry[1] |= src_mac->eth_addr_byte[1]; ! 1765: arl_entry[1] |= p->real_vlan << BCM5600_ARL_VLAN_TAG_SHIFT; ! 1766: ! 1767: arl_entry[2] = BCM5600_ARL_HIT_FLAG; ! 1768: arl_entry[2] |= p->ingress_port << BCM5600_ARL_PORT_SHIFT; ! 1769: ! 1770: if (trunk != NULL) { ! 1771: arl_entry[2] |= BCM5600_ARL_TRUNK_FLAG; ! 1772: arl_entry[2] |= (trunk_id << BCM5600_ARL_TGID_SHIFT); ! 1773: } ! 1774: ! 1775: d->arl_cnt[0]++; ! 1776: return(TRUE); ! 1777: } ! 1778: ! 1779: /* Select an egress port the specified trunk */ ! 1780: static int bcm5600_trunk_egress_port(struct nm_16esw_data *d, ! 1781: struct bcm5600_pkt *p, ! 1782: u_int trunk_id) ! 1783: { ! 1784: n_eth_hdr_t *eth_hdr = (n_eth_hdr_t *)p->pkt; ! 1785: struct bcm5600_tg_info *tgi; ! 1786: m_uint32_t *ttr_entry; ! 1787: u_int i,nr_links; ! 1788: u_int hash,port_id; ! 1789: ! 1790: ttr_entry = bcm5600_table_get_entry(d,d->t_ttr,trunk_id); ! 1791: assert(ttr_entry != NULL); ! 1792: ! 1793: nr_links = ttr_entry[1] & BCM5600_TTR_TG_SIZE_MASK; ! 1794: nr_links >>= BCM5600_TTR_TG_SIZE_SHIFT; ! 1795: ! 1796: #if 0 ! 1797: /* Hash on source and destination MAC addresses */ ! 1798: for(i=0,hash=0;i<N_ETH_ALEN;i++) { ! 1799: hash ^= eth_hdr->saddr.eth_addr_byte[i]; ! 1800: hash ^= eth_hdr->daddr.eth_addr_byte[i]; ! 1801: } ! 1802: ! 1803: hash ^= (hash >> 4); ! 1804: port_id = hash % nr_links; ! 1805: ! 1806: /* Maximum of 8 ports per trunk */ ! 1807: assert(hash < BCM5600_MAX_PORTS_PER_TRUNK); ! 1808: #else ! 1809: port_id = d->trunk_last_egress_port[trunk_id] + 1; ! 1810: port_id %= nr_links; ! 1811: #endif ! 1812: ! 1813: /* Save the latest port used for this trunk */ ! 1814: d->trunk_last_egress_port[trunk_id] = port_id; ! 1815: ! 1816: /* Select the egress port */ ! 1817: tgi = &tg_info[port_id]; ! 1818: return((ttr_entry[tgi->index] & tgi->mask) >> tgi->shift); ! 1819: } ! 1820: ! 1821: /* Destination address lookup (take the forwarding decision) */ ! 1822: static int bcm5600_dst_mac_lookup(struct nm_16esw_data *d, ! 1823: struct bcm5600_pkt *p) ! 1824: { ! 1825: n_eth_hdr_t *eth_hdr = (n_eth_hdr_t *)p->pkt; ! 1826: n_eth_addr_t *dst_mac = ð_hdr->daddr; ! 1827: struct bcm5600_table *arl_table; ! 1828: m_uint32_t *arl_entry; ! 1829: u_int egress_port; ! 1830: u_int trunk_id; ! 1831: int dst_mac_index; ! 1832: int is_mcast; ! 1833: ! 1834: /* Select the appropriate ARL table and do the lookup on dst MAC + VLAN */ ! 1835: if (eth_addr_is_mcast(dst_mac)) { ! 1836: is_mcast = TRUE; ! 1837: arl_table = d->t_marl; ! 1838: dst_mac_index = bcm5600_marl_lookup(d,dst_mac,p->real_vlan); ! 1839: } else { ! 1840: is_mcast = FALSE; ! 1841: arl_table = d->t_arl; ! 1842: dst_mac_index = bcm5600_arl_lookup(d,dst_mac,p->real_vlan); ! 1843: } ! 1844: ! 1845: /* ! 1846: * Destination Lookup Failure (DLF). ! 1847: * ! 1848: * Use the VLAN bitmap to compute the Egress port bitmap. ! 1849: * Remove the ingress port from it. ! 1850: */ ! 1851: if (dst_mac_index == -1) { ! 1852: #if DEBUG_FORWARD ! 1853: BCM_LOG(d,"Destination MAC address unknown, flooding.\n"); ! 1854: #endif ! 1855: p->egress_bitmap = p->vlan_entry[1] & BCM5600_VTABLE_PORT_BMAP_MASK; ! 1856: ! 1857: /* Add the CPU to the egress ports */ ! 1858: p->egress_bitmap |= 1 << d->cpu_port; ! 1859: ! 1860: p->egress_ut_bitmap = p->vlan_entry[2]; ! 1861: p->egress_ut_bitmap &= BCM5600_VTABLE_UT_PORT_BMAP_MASK; ! 1862: return(TRUE); ! 1863: } ! 1864: ! 1865: /* The MAC address was found in the ARL/MARL table */ ! 1866: arl_entry = bcm5600_table_get_entry(d,arl_table,dst_mac_index); ! 1867: assert(arl_entry != NULL); ! 1868: ! 1869: /* If the CPU bit is set, send a copy of the packet to the CPU */ ! 1870: if (arl_entry[1] & BCM5600_ARL_CPU_FLAG) ! 1871: bcm5600_send_pkt_to_cpu(d,p); ! 1872: ! 1873: if (!is_mcast) { ! 1874: /* Unicast: send the packet to the port or trunk found in ARL table */ ! 1875: if (arl_entry[2] & BCM5600_ARL_TRUNK_FLAG) { ! 1876: trunk_id = arl_entry[2] & BCM5600_ARL_TGID_MASK; ! 1877: trunk_id >>= BCM5600_ARL_TGID_SHIFT; ! 1878: ! 1879: /* Select an output port for this trunk */ ! 1880: egress_port = bcm5600_trunk_egress_port(d,p,trunk_id); ! 1881: ! 1882: #if DEBUG_FORWARD ! 1883: BCM_LOG(d,"Sending packet to trunk port %u, egress port %u\n", ! 1884: trunk_id,egress_port); ! 1885: #endif ! 1886: } else { ! 1887: egress_port = arl_entry[2] & BCM5600_ARL_PORT_MASK; ! 1888: egress_port >>= BCM5600_ARL_PORT_SHIFT; ! 1889: } ! 1890: ! 1891: p->egress_bitmap = 1 << egress_port; ! 1892: p->egress_ut_bitmap = p->vlan_entry[2] & ! 1893: BCM5600_VTABLE_UT_PORT_BMAP_MASK; ! 1894: } else { ! 1895: /* Multicast: send the packet to the egress ports found in MARL table */ ! 1896: p->egress_bitmap = arl_entry[2] & BCM5600_MARL_PORT_BMAP_MASK; ! 1897: p->egress_ut_bitmap = arl_entry[3] & BCM5600_MARL_UT_PORT_BMAP_MASK; ! 1898: } ! 1899: ! 1900: #if DEBUG_FORWARD ! 1901: { ! 1902: char buffer[1024]; ! 1903: ! 1904: BCM_LOG(d,"bitmap: 0x%8.8x, filter: 0x%8.8x\n", ! 1905: p->egress_bitmap,p->egress_filter_bitmap); ! 1906: ! 1907: bcm5600_port_bitmap_str(d,buffer,p->egress_bitmap); ! 1908: ! 1909: /* without egress port filtering */ ! 1910: if (*buffer) ! 1911: BCM_LOG(d,"forwarding to egress port list w/o filter: %s\n",buffer); ! 1912: else ! 1913: BCM_LOG(d,"w/o filter: empty egress port list.\n"); ! 1914: ! 1915: /* with egress port filtering */ ! 1916: bcm5600_port_bitmap_str(d,buffer, ! 1917: p->egress_bitmap & ~p->egress_filter_bitmap); ! 1918: ! 1919: if (*buffer) ! 1920: BCM_LOG(d,"forwarding to egress port list w/ filter: %s\n",buffer); ! 1921: } ! 1922: #endif ! 1923: ! 1924: return(p->egress_bitmap != 0); ! 1925: } ! 1926: ! 1927: /* Prototype for a packet sending function */ ! 1928: typedef void (*bcm5600_send_pkt_t)(struct nm_16esw_data *d, ! 1929: struct bcm5600_pkt *p, ! 1930: netio_desc_t *nio); ! 1931: ! 1932: /* Directly forward a packet (not rewritten) */ ! 1933: static void bcm5600_send_pkt_direct(struct nm_16esw_data *d, ! 1934: struct bcm5600_pkt *p, ! 1935: netio_desc_t *nio) ! 1936: { ! 1937: netio_send(nio,p->pkt,p->pkt_len); ! 1938: } ! 1939: ! 1940: /* Send a packet with a 802.1Q tag */ ! 1941: static void bcm5600_send_pkt_push_dot1q(struct nm_16esw_data *d, ! 1942: struct bcm5600_pkt *p, ! 1943: netio_desc_t *nio) ! 1944: { ! 1945: n_eth_dot1q_hdr_t *hdr; ! 1946: ! 1947: if (!p->rewrite_done) { ! 1948: memcpy(p->rewr_pkt,p->pkt,(N_ETH_HLEN - 2)); ! 1949: ! 1950: hdr = (n_eth_dot1q_hdr_t *)p->rewr_pkt; ! 1951: hdr->type = htons(N_ETH_PROTO_DOT1Q); ! 1952: hdr->vlan_id = htons(p->real_vlan); ! 1953: ! 1954: memcpy(p->rewr_pkt + sizeof(n_eth_dot1q_hdr_t), ! 1955: p->pkt + (N_ETH_HLEN - 2), ! 1956: p->pkt_len - (N_ETH_HLEN - 2)); ! 1957: ! 1958: p->rewrite_done = TRUE; ! 1959: } ! 1960: ! 1961: netio_send(nio,p->rewr_pkt,p->pkt_len+4); ! 1962: } ! 1963: ! 1964: /* Send a packet deleting its 802.1Q tag */ ! 1965: static void bcm5600_send_pkt_pop_dot1q(struct nm_16esw_data *d, ! 1966: struct bcm5600_pkt *p, ! 1967: netio_desc_t *nio) ! 1968: { ! 1969: if (!p->rewrite_done) { ! 1970: memcpy(p->rewr_pkt,p->pkt,(N_ETH_HLEN - 2)); ! 1971: ! 1972: memcpy(p->rewr_pkt + (N_ETH_HLEN - 2), ! 1973: p->pkt + sizeof(n_eth_dot1q_hdr_t), ! 1974: p->pkt_len - sizeof(n_eth_dot1q_hdr_t)); ! 1975: ! 1976: p->rewrite_done = TRUE; ! 1977: } ! 1978: ! 1979: netio_send(nio,p->rewr_pkt,p->pkt_len-4); ! 1980: } ! 1981: ! 1982: /* Forward a packet on physical ports (egress bitmap must be defined) */ ! 1983: static int bcm5600_forward_pkt(struct nm_16esw_data *d,struct bcm5600_pkt *p) ! 1984: { ! 1985: u_char rewr_pkt[BCM5600_MAX_PKT_SIZE]; ! 1986: bcm5600_send_pkt_t send_pkt; ! 1987: u_int egress_untagged,trunk_id; ! 1988: m_uint32_t *dst_port,*trunk; ! 1989: int i; ! 1990: ! 1991: p->egress_bitmap &= ~p->egress_filter_bitmap; ! 1992: ! 1993: if (!p->egress_bitmap) ! 1994: return(FALSE); ! 1995: ! 1996: /* Process egress mirroring (if enabled) */ ! 1997: if (p->egress_bitmap & d->mirror_egress_ports) ! 1998: bcm5600_mirror_pkt(d,p,1); ! 1999: ! 2000: /* No rewrite done at this time */ ! 2001: p->rewr_pkt = rewr_pkt; ! 2002: p->rewrite_done = FALSE; ! 2003: ! 2004: /* Forward to CPU port ? */ ! 2005: if (p->egress_bitmap & (1 << d->cpu_port)) ! 2006: bcm5600_send_pkt_to_cpu(d,p); ! 2007: ! 2008: for(i=0;i<d->nr_port;i++) { ! 2009: if (!(p->egress_bitmap & (1 << i))) ! 2010: continue; ! 2011: ! 2012: /* ! 2013: * If this port is a member of a trunk, remove all other ports to avoid ! 2014: * duplicate frames (typically, when a dest MAC address is unknown ! 2015: * or for a broadcast/multicast). ! 2016: */ ! 2017: dst_port = bcm5600_table_get_entry(d,d->t_ptable,i); ! 2018: assert(dst_port != NULL); ! 2019: ! 2020: if (dst_port[0] & BCM5600_PTABLE_TRUNK_FLAG) { ! 2021: trunk_id = dst_port[0] & BCM5600_PTABLE_TGID_MASK; ! 2022: trunk_id >>= BCM5600_PTABLE_TGID_SHIFT; ! 2023: ! 2024: trunk = bcm5600_table_get_entry(d,d->t_tbmap,trunk_id); ! 2025: assert(trunk != NULL); ! 2026: ! 2027: p->egress_bitmap &= ~trunk[0]; ! 2028: } ! 2029: ! 2030: /* select the appropriate output vector */ ! 2031: if (p->orig_vlan == 0) ! 2032: send_pkt = bcm5600_send_pkt_direct; ! 2033: else { ! 2034: egress_untagged = p->egress_ut_bitmap & (1 << i); ! 2035: ! 2036: if (p->orig_vlan == -1) { ! 2037: /* Untagged packet */ ! 2038: if (egress_untagged) ! 2039: send_pkt = bcm5600_send_pkt_direct; ! 2040: else ! 2041: send_pkt = bcm5600_send_pkt_push_dot1q; ! 2042: } else { ! 2043: /* Tagged packet */ ! 2044: if (egress_untagged) ! 2045: send_pkt = bcm5600_send_pkt_pop_dot1q; ! 2046: else ! 2047: send_pkt = bcm5600_send_pkt_direct; ! 2048: } ! 2049: } ! 2050: ! 2051: #if DEBUG_FORWARD > 1 ! 2052: BCM_LOG(d,"forwarding on port %s (vector=%p)\n", ! 2053: d->ports[i].name,send_pkt); ! 2054: #endif ! 2055: send_pkt(d,p,d->ports[i].nio); ! 2056: } ! 2057: ! 2058: return(TRUE); ! 2059: } ! 2060: ! 2061: /* Handle a received packet */ ! 2062: static int bcm5600_handle_rx_pkt(struct nm_16esw_data *d,struct bcm5600_pkt *p) ! 2063: { ! 2064: m_uint32_t *port_entry; ! 2065: n_eth_dot1q_hdr_t *eth_hdr; ! 2066: u_int discard; ! 2067: ! 2068: /* No egress port at this time */ ! 2069: p->egress_bitmap = 0; ! 2070: ! 2071: /* Never send back frames to the source port */ ! 2072: p->egress_filter_bitmap = 1 << p->ingress_port; ! 2073: ! 2074: if (!(port_entry = bcm5600_table_get_entry(d,d->t_ptable,p->ingress_port))) ! 2075: return(FALSE); ! 2076: ! 2077: /* Analyze the Ethernet header */ ! 2078: eth_hdr = (n_eth_dot1q_hdr_t *)p->pkt; ! 2079: ! 2080: /* Check for the reserved addresses (BPDU for spanning-tree) */ ! 2081: if (!memcmp(ð_hdr->daddr,"\x01\x80\xc2\x00\x00",5) || ! 2082: !memcmp(ð_hdr->daddr,"\x01\x00\x0c\xcc\xcc\xcd",6)) ! 2083: { ! 2084: #if DEBUG_RECEIVE ! 2085: BCM_LOG(d,"Received a BPDU packet:\n"); ! 2086: mem_dump(d->vm->log_fd,p->pkt,p->pkt_len); ! 2087: #endif ! 2088: p->orig_vlan = 0; ! 2089: p->egress_bitmap |= 1 << d->cpu_port; ! 2090: return(bcm5600_forward_pkt(d,p)); ! 2091: } ! 2092: ! 2093: /* Discard packet ? */ ! 2094: discard = port_entry[0] & BCM5600_PTABLE_PRT_DIS_MASK; ! 2095: discard >>= BCM5600_PTABLE_PRT_DIS_SHIFT; ! 2096: ! 2097: if (discard) { ! 2098: if (discard != 0x20) { ! 2099: printf("\n\n\n" ! 2100: "-----------------------------------------------------------" ! 2101: "---------------------------------\n" ! 2102: "Unspported feature: please post your current configuration " ! 2103: "on http://www.ipflow.utc.fr/blog/\n" ! 2104: "-----------------------------------------------------------" ! 2105: "---------------------------------\n"); ! 2106: } ! 2107: ! 2108: /* Drop the packet */ ! 2109: return(FALSE); ! 2110: } ! 2111: ! 2112: /* Mirroring on Ingress ? */ ! 2113: if (port_entry[1] & BCM5600_PTABLE_MI_FLAG) ! 2114: bcm5600_mirror_pkt(d,p,0); ! 2115: ! 2116: /* Determine VLAN */ ! 2117: if (ntohs(eth_hdr->type) != N_ETH_PROTO_DOT1Q) { ! 2118: p->orig_vlan = -1; ! 2119: p->real_vlan = port_entry[0] & BCM5600_PTABLE_VLAN_TAG_MASK; ! 2120: ! 2121: if (!(p->vlan_entry = bcm5600_vtable_get_entry_by_vlan(d,p->real_vlan))) ! 2122: return(FALSE); ! 2123: ! 2124: /* TODO: 802.1p/CoS remarking */ ! 2125: if (port_entry[4] & BCM5600_PTABLE_RPE_FLAG) { ! 2126: } ! 2127: } else { ! 2128: p->orig_vlan = p->real_vlan = ntohs(eth_hdr->vlan_id) & 0xFFF; ! 2129: ! 2130: /* Check that this VLAN exists */ ! 2131: if (!(p->vlan_entry = bcm5600_vtable_get_entry_by_vlan(d,p->real_vlan))) ! 2132: return(FALSE); ! 2133: ! 2134: /* Check that this port is a member of this VLAN */ ! 2135: if (!(p->vlan_entry[1] & (1 << p->ingress_port))) ! 2136: return(FALSE); ! 2137: } ! 2138: ! 2139: #if DEBUG_RECEIVE ! 2140: BCM_LOG(d,"%s: received a packet on VLAN %u\n", ! 2141: d->ports[p->ingress_port].name,p->real_vlan); ! 2142: #endif ! 2143: ! 2144: /* Source MAC address learning */ ! 2145: if (!bcm5600_src_mac_learning(d,p)) ! 2146: return(FALSE); ! 2147: ! 2148: /* Take forwarding decision based on destination MAC address */ ! 2149: if (!bcm5600_dst_mac_lookup(d,p)) ! 2150: return(FALSE); ! 2151: ! 2152: /* Send the packet to the egress ports */ ! 2153: return(bcm5600_forward_pkt(d,p)); ! 2154: } ! 2155: ! 2156: /* Handle a packet to transmit */ ! 2157: static int bcm5600_handle_tx_pkt(struct nm_16esw_data *d, ! 2158: struct bcm5600_pkt *p, ! 2159: u_int egress_bitmap) ! 2160: { ! 2161: n_eth_dot1q_hdr_t *eth_hdr; ! 2162: ! 2163: /* Never send back frames to the source port */ ! 2164: p->egress_filter_bitmap = 1 << p->ingress_port; ! 2165: ! 2166: /* We take the complete forwarding decision if bit 23 is set */ ! 2167: if (egress_bitmap & (1 << 23)) { ! 2168: /* No egress port at this time */ ! 2169: p->egress_bitmap = 0; ! 2170: ! 2171: /* The packet must be tagged so that we can determine the VLAN */ ! 2172: eth_hdr = (n_eth_dot1q_hdr_t *)p->pkt; ! 2173: ! 2174: if (ntohs(eth_hdr->type) != N_ETH_PROTO_DOT1Q) { ! 2175: BCM_LOG(d,"bcm5600_handle_tx_pkt: untagged packet ?\n"); ! 2176: return(FALSE); ! 2177: } ! 2178: ! 2179: /* Find the appropriate, check it exists (just in case) */ ! 2180: p->orig_vlan = p->real_vlan = ntohs(eth_hdr->vlan_id) & 0xFFF; ! 2181: ! 2182: if (!(p->vlan_entry = bcm5600_vtable_get_entry_by_vlan(d,p->real_vlan))) ! 2183: return(FALSE); ! 2184: ! 2185: #if DEBUG_TRANSMIT ! 2186: BCM_LOG(d,"Transmitting a packet from TX ring to VLAN %u\n", ! 2187: p->real_vlan); ! 2188: #endif ! 2189: ! 2190: /* Take forwarding decision based on destination MAC address */ ! 2191: if (!bcm5600_dst_mac_lookup(d,p)) ! 2192: return(FALSE); ! 2193: } else { ! 2194: #if DEBUG_TRANSMIT ! 2195: BCM_LOG(d,"Transmitting natively a packet from TX ring.\n"); ! 2196: #endif ! 2197: /* The egress ports are specified, send the packet natively */ ! 2198: p->orig_vlan = 0; ! 2199: p->egress_bitmap = egress_bitmap; ! 2200: } ! 2201: ! 2202: /* Send the packet to the egress ports */ ! 2203: return(bcm5600_forward_pkt(d,p)); ! 2204: } ! 2205: ! 2206: /* Handle the TX ring */ ! 2207: static int dev_bcm5600_handle_txring(struct nm_16esw_data *d) ! 2208: { ! 2209: struct bcm5600_pkt pkt_data; ! 2210: m_uint32_t tdes[4],txd_len; ! 2211: ! 2212: BCM_LOCK(d); ! 2213: ! 2214: if (!d->tx_current || d->tx_end_scan) { ! 2215: BCM_UNLOCK(d); ! 2216: return(FALSE); ! 2217: } ! 2218: ! 2219: /* Read the current TX descriptor */ ! 2220: physmem_copy_from_vm(d->vm,tdes,d->tx_current,4*sizeof(m_uint32_t)); ! 2221: tdes[0] = vmtoh32(tdes[0]); ! 2222: tdes[1] = vmtoh32(tdes[1]); ! 2223: tdes[2] = vmtoh32(tdes[2]); ! 2224: tdes[3] = vmtoh32(tdes[3]); ! 2225: ! 2226: #if DEBUG_TRANSMIT ! 2227: BCM_LOG(d,"=== TRANSMIT PATH ===\n"); ! 2228: ! 2229: BCM_LOG(d,"tx_current=0x%8.8x, " ! 2230: "tdes[0]=0x%8.8x, tdes[1]=0x%8.8x, tdes[2]=0x%8.8x\n", ! 2231: d->tx_current,tdes[0],tdes[1],tdes[2]); ! 2232: #endif ! 2233: ! 2234: /* Get the buffer size */ ! 2235: txd_len = tdes[1] & 0x7FF; ! 2236: ! 2237: /* Check buffer size */ ! 2238: if ((d->tx_bufsize + txd_len) >= sizeof(d->tx_buffer)) ! 2239: goto done; ! 2240: ! 2241: /* Copy the packet from memory */ ! 2242: physmem_copy_from_vm(d->vm,d->tx_buffer+d->tx_bufsize,tdes[0],txd_len); ! 2243: d->tx_bufsize += txd_len; ! 2244: ! 2245: /* Packet not complete: handle it later */ ! 2246: if (tdes[1] & BCM5600_TXD_NEOP) ! 2247: goto done; ! 2248: ! 2249: #if DEBUG_TRANSMIT ! 2250: mem_dump(d->vm->log_fd,d->tx_buffer,d->tx_bufsize); ! 2251: #endif ! 2252: ! 2253: /* Transmit the packet */ ! 2254: pkt_data.ingress_port = d->cpu_port; ! 2255: pkt_data.pkt = d->tx_buffer; ! 2256: pkt_data.pkt_len = d->tx_bufsize - 4; ! 2257: pkt_data.sent_to_cpu = TRUE; ! 2258: bcm5600_handle_tx_pkt(d,&pkt_data,tdes[2]); ! 2259: ! 2260: /* Reset the TX buffer (packet fully transmitted) */ ! 2261: d->tx_bufsize = 0; ! 2262: ! 2263: done: ! 2264: /* We have reached end of ring: trigger the TX underrun interrupt */ ! 2265: if (!(tdes[1] & BCM5600_TXD_RING_CONT)) { ! 2266: d->tx_end_scan = 1; ! 2267: pci_dev_trigger_irq(d->vm,d->pci_dev); ! 2268: BCM_UNLOCK(d); ! 2269: return(TRUE); ! 2270: } ! 2271: ! 2272: /* Go to the next descriptor */ ! 2273: d->tx_current += BCM5600_TXD_SIZE; ! 2274: BCM_UNLOCK(d); ! 2275: return(TRUE); ! 2276: } ! 2277: ! 2278: /* Handle the RX ring */ ! 2279: static int dev_bcm5600_handle_rxring(netio_desc_t *nio, ! 2280: u_char *pkt,ssize_t pkt_len, ! 2281: struct nm_16esw_data *d, ! 2282: struct bcm5600_port *port) ! 2283: { ! 2284: struct bcm5600_pkt pkt_data; ! 2285: m_uint32_t rxd_len; ! 2286: ! 2287: #if DEBUG_RECEIVE ! 2288: BCM_LOG(d,"=== RECEIVE PATH ===\n"); ! 2289: ! 2290: BCM_LOG(d,"%s: received a packet of %ld bytes.\n", ! 2291: port->name,(u_long)pkt_len); ! 2292: mem_dump(d->vm->log_fd,pkt,pkt_len); ! 2293: #endif ! 2294: ! 2295: BCM_LOCK(d); ! 2296: ! 2297: if (!d->rx_current || d->rx_end_scan) { ! 2298: BCM_UNLOCK(d); ! 2299: return(FALSE); ! 2300: } ! 2301: ! 2302: /* Read the current TX descriptor */ ! 2303: physmem_copy_from_vm(d->vm,pkt_data.rdes,d->rx_current, ! 2304: (4 * sizeof(m_uint32_t))); ! 2305: ! 2306: pkt_data.rdes[0] = vmtoh32(pkt_data.rdes[0]); ! 2307: pkt_data.rdes[1] = vmtoh32(pkt_data.rdes[1]); ! 2308: pkt_data.rdes[2] = vmtoh32(pkt_data.rdes[2]); ! 2309: pkt_data.rdes[3] = vmtoh32(pkt_data.rdes[3]); ! 2310: ! 2311: #if DEBUG_RECEIVE ! 2312: BCM_LOG(d,"rx_current=0x%8.8x, " ! 2313: "rdes[0]=0x%8.8x, rdes[1]=0x%8.8x, rdes[2]=0x%8.8x\n", ! 2314: d->rx_current,pkt_data.rdes[0],pkt_data.rdes[1],pkt_data.rdes[2]); ! 2315: #endif ! 2316: ! 2317: /* Get the buffer size */ ! 2318: rxd_len = pkt_data.rdes[1] & 0x7FF; ! 2319: ! 2320: if (pkt_len > rxd_len) { ! 2321: BCM_UNLOCK(d); ! 2322: return(FALSE); ! 2323: } ! 2324: ! 2325: /* Fill the packet info */ ! 2326: pkt_data.ingress_port = port->id; ! 2327: pkt_data.pkt = pkt; ! 2328: pkt_data.pkt_len = pkt_len; ! 2329: pkt_data.sent_to_cpu = FALSE; ! 2330: ! 2331: /* Handle the packet */ ! 2332: bcm5600_handle_rx_pkt(d,&pkt_data); ! 2333: ! 2334: /* Signal only an interrupt when a packet has been sent to the CPU */ ! 2335: if (pkt_data.sent_to_cpu) { ! 2336: /* We have reached end of ring: trigger the RX underrun interrupt */ ! 2337: if (!(pkt_data.rdes[1] & BCM5600_RXD_RING_CONT)) { ! 2338: d->rx_end_scan = 1; ! 2339: pci_dev_trigger_irq(d->vm,d->pci_dev); ! 2340: BCM_UNLOCK(d); ! 2341: return(TRUE); ! 2342: } ! 2343: ! 2344: /* A packet was received */ ! 2345: pci_dev_trigger_irq(d->vm,d->pci_dev); ! 2346: ! 2347: /* Go to the next descriptor */ ! 2348: d->rx_current += BCM5600_RXD_SIZE; ! 2349: } ! 2350: ! 2351: BCM_UNLOCK(d); ! 2352: return(TRUE); ! 2353: } ! 2354: ! 2355: /* pci_bcm5605_read() */ ! 2356: static m_uint32_t pci_bcm5605_read(cpu_mips_t *cpu,struct pci_device *dev, ! 2357: int reg) ! 2358: { ! 2359: struct nm_16esw_data *d = dev->priv_data; ! 2360: ! 2361: switch(reg) { ! 2362: case PCI_REG_BAR0: ! 2363: return(d->dev->phys_addr); ! 2364: default: ! 2365: return(0); ! 2366: } ! 2367: } ! 2368: ! 2369: /* pci_bcm5605_write() */ ! 2370: static void pci_bcm5605_write(cpu_mips_t *cpu,struct pci_device *dev, ! 2371: int reg,m_uint32_t value) ! 2372: { ! 2373: struct nm_16esw_data *d = dev->priv_data; ! 2374: ! 2375: switch(reg) { ! 2376: case PCI_REG_BAR0: ! 2377: vm_map_device(cpu->vm,d->dev,(m_uint64_t)value); ! 2378: BCM_LOG(d,"BCM5600 registers are mapped at 0x%x\n",value); ! 2379: break; ! 2380: } ! 2381: } ! 2382: ! 2383: /* Rewrite the base MAC address */ ! 2384: int dev_nm_16esw_burn_mac_addr(vm_instance_t *vm,u_int nm_bay, ! 2385: struct cisco_eeprom *eeprom) ! 2386: { ! 2387: m_uint8_t eeprom_ver; ! 2388: size_t offset; ! 2389: n_eth_addr_t addr; ! 2390: m_uint16_t pid; ! 2391: ! 2392: pid = (m_uint16_t)getpid(); ! 2393: ! 2394: /* Generate automatically the MAC address */ ! 2395: addr.eth_addr_byte[0] = vm_get_mac_addr_msb(vm); ! 2396: addr.eth_addr_byte[1] = vm->instance_id & 0xFF; ! 2397: addr.eth_addr_byte[2] = pid >> 8; ! 2398: addr.eth_addr_byte[3] = pid & 0xFF; ! 2399: addr.eth_addr_byte[4] = 0xF0 + nm_bay; ! 2400: addr.eth_addr_byte[5] = 0x00; ! 2401: ! 2402: /* Read EEPROM format version */ ! 2403: cisco_eeprom_get_byte(eeprom,0,&eeprom_ver); ! 2404: ! 2405: if (eeprom_ver != 4) ! 2406: return(-1); ! 2407: ! 2408: if (cisco_eeprom_v4_find_field(eeprom,0xCF,&offset) == -1) ! 2409: return(-1); ! 2410: ! 2411: cisco_eeprom_set_region(eeprom,offset,addr.eth_addr_byte,6); ! 2412: return(0); ! 2413: } ! 2414: ! 2415: /* Initialize a NM-16ESW module */ ! 2416: struct nm_16esw_data * ! 2417: dev_nm_16esw_init(vm_instance_t *vm,char *name,u_int nm_bay, ! 2418: struct pci_bus *pci_bus,int pci_device,int irq) ! 2419: { ! 2420: struct nm_16esw_data *data; ! 2421: struct bcm5600_port *port; ! 2422: struct vdevice *dev; ! 2423: int i,port_id; ! 2424: ! 2425: /* Allocate the private data structure */ ! 2426: if (!(data = malloc(sizeof(*data)))) { ! 2427: fprintf(stderr,"%s: out of memory\n",name); ! 2428: return NULL; ! 2429: } ! 2430: ! 2431: memset(data,0,sizeof(*data)); ! 2432: pthread_mutex_init(&data->lock,NULL); ! 2433: data->name = name; ! 2434: data->nr_port = 16; ! 2435: data->vm = vm; ! 2436: ! 2437: /* Create the BCM5600 tables */ ! 2438: if (bcm5600_table_create(data) == -1) ! 2439: return NULL; ! 2440: ! 2441: /* Clear the various tables */ ! 2442: bcm5600_reset_arl(data); ! 2443: data->arl_cnt[0] = 1; ! 2444: data->t_ptable = bcm5600_table_find(data,BCM5600_ADDR_PTABLE0); ! 2445: data->t_vtable = bcm5600_table_find(data,BCM5600_ADDR_VTABLE0); ! 2446: data->t_arl = bcm5600_table_find(data,BCM5600_ADDR_ARL0); ! 2447: data->t_marl = bcm5600_table_find(data,BCM5600_ADDR_MARL0); ! 2448: data->t_tbmap = bcm5600_table_find(data,BCM5600_ADDR_TBMAP0); ! 2449: data->t_ttr = bcm5600_table_find(data,BCM5600_ADDR_TTR0); ! 2450: ! 2451: /* Initialize ports */ ! 2452: data->cpu_port = 27; ! 2453: ! 2454: for(i=0;i<data->nr_port;i++) { ! 2455: port_id = nm16esw_port_mapping[i]; ! 2456: ! 2457: port = &data->ports[port_id]; ! 2458: port->id = port_id; ! 2459: snprintf(port->name,sizeof(port->name),"Fa%u/%d",nm_bay,i); ! 2460: } ! 2461: ! 2462: /* Create the BCM5605 PCI device */ ! 2463: data->pci_dev = pci_dev_add(pci_bus,name, ! 2464: BCM5605_PCI_VENDOR_ID,BCM5605_PCI_PRODUCT_ID, ! 2465: pci_device,0,irq,data, ! 2466: NULL,pci_bcm5605_read,pci_bcm5605_write); ! 2467: ! 2468: if (!data->pci_dev) { ! 2469: fprintf(stderr,"%s: unable to create PCI device.\n",name); ! 2470: return NULL; ! 2471: } ! 2472: ! 2473: /* Create the BCM5605 device itself */ ! 2474: if (!(dev = dev_create(name))) { ! 2475: fprintf(stderr,"%s: unable to create device.\n",name); ! 2476: return NULL; ! 2477: } ! 2478: ! 2479: dev->phys_addr = 0; ! 2480: dev->phys_len = 0x200000; ! 2481: dev->handler = dev_bcm5605_access; ! 2482: ! 2483: /* Store device info */ ! 2484: dev->priv_data = data; ! 2485: data->dev = dev; ! 2486: ! 2487: /* Create the TX ring scanner */ ! 2488: data->tx_tid = ptask_add((ptask_callback)dev_bcm5600_handle_txring, ! 2489: data,NULL); ! 2490: ! 2491: /* Start the MAC address ager */ ! 2492: data->ager_tid = timer_create_entry(15000,FALSE,10, ! 2493: (timer_proc)bcm5600_arl_ager,data); ! 2494: return data; ! 2495: } ! 2496: ! 2497: /* Remove a NM-16ESW from the specified slot */ ! 2498: int dev_nm_16esw_remove(struct nm_16esw_data *data) ! 2499: { ! 2500: /* Stop the Ager */ ! 2501: timer_remove(data->ager_tid); ! 2502: ! 2503: /* Stop the TX ring task */ ! 2504: ptask_remove(data->tx_tid); ! 2505: ! 2506: /* Remove device + PCI stuff */ ! 2507: pci_dev_remove(data->pci_dev); ! 2508: vm_unbind_device(data->vm,data->dev); ! 2509: cpu_group_rebuild_mts(data->vm->cpu_group); ! 2510: free(data->dev); ! 2511: ! 2512: /* Free all tables and registers */ ! 2513: bcm5600_table_free(data); ! 2514: bcm5600_reg_free(data); ! 2515: free(data); ! 2516: return(0); ! 2517: } ! 2518: ! 2519: /* Bind a Network IO descriptor */ ! 2520: int dev_nm_16esw_set_nio(struct nm_16esw_data *d,u_int port_id, ! 2521: netio_desc_t *nio) ! 2522: { ! 2523: struct bcm5600_port *port; ! 2524: ! 2525: if (!d || (port_id >= d->nr_port)) ! 2526: return(-1); ! 2527: ! 2528: /* define the new NIO */ ! 2529: port = &d->ports[nm16esw_port_mapping[port_id]]; ! 2530: port->nio = nio; ! 2531: netio_rxl_add(nio,(netio_rx_handler_t)dev_bcm5600_handle_rxring,d,port); ! 2532: return(0); ! 2533: } ! 2534: ! 2535: /* Unbind a Network IO descriptor */ ! 2536: int dev_nm_16esw_unset_nio(struct nm_16esw_data *d,u_int port_id) ! 2537: { ! 2538: struct bcm5600_port *port; ! 2539: ! 2540: if (!d || (port_id >= d->nr_port)) ! 2541: return(-1); ! 2542: ! 2543: port = &d->ports[nm16esw_port_mapping[port_id]]; ! 2544: ! 2545: if (port->nio) { ! 2546: netio_rxl_remove(port->nio); ! 2547: port->nio = NULL; ! 2548: } ! 2549: ! 2550: return(0); ! 2551: } ! 2552: ! 2553: /* Show debugging information */ ! 2554: int dev_nm_16esw_show_info(struct nm_16esw_data *d) ! 2555: { ! 2556: BCM_LOCK(d); ! 2557: printf("ARL count = %u\n\n",d->arl_cnt[0]); ! 2558: bcm5600_dump_main_tables(d); ! 2559: bcm5600_mirror_show_status(d); ! 2560: bcm5600_reg_dump(d,FALSE); ! 2561: BCM_UNLOCK(d); ! 2562: return(0); ! 2563: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.