openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

bcm-net.c (20548B)


      1 /*
      2  * 
      3  * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file.
      4  * 
      5  * Copyright 2007-2019 Broadcom Inc. All rights reserved.
      6  */
      7 
      8 /*
      9  * This version of the BCM Linux network driver is written
     10  * to run on top of the linux-bcm-core module.
     11  *
     12  * All TX packets are tagged and all RX packets are assumed to
     13  * include a tag (which is stripped unconditionally.)
     14  *
     15  * It is possible to segment the switch ports into multiple network
     16  * devices by specifying port masks for each network device.
     17  *
     18  * For a list of supported module parameters, please see below.
     19  */
     20 
     21 #include <gmodule.h> /* Must be included first */
     22 #include <linux/netdevice.h>
     23 #include <linux/etherdevice.h>
     24 #include <linux/random.h>
     25 #include <sal/core/dpc.h>
     26 #include <sal/appl/sal.h>
     27 
     28 #include <soc/types.h>
     29 #include <soc/enet.h>
     30 #include <soc/drv.h>
     31 
     32 #include <bcm/error.h>
     33 #include <bcm/tx.h>
     34 #include <bcm/rx.h>
     35 #include <bcm/l2.h>
     36 #include <bcm/stat.h>
     37 #include <bcm/port.h>
     38 
     39 #include <ibde.h>
     40 #include <linux-bde.h>
     41 #include <bcm-core.h>
     42 
     43 
     44 MODULE_AUTHOR("Broadcom Corporation");
     45 MODULE_DESCRIPTION("Network Device Driver for BCM Core devices");
     46 MODULE_LICENSE("Proprietary");
     47 
     48 static int unit;
     49 LKM_MOD_PARAM(unit, "i", int, 0);
     50 MODULE_PARM_DESC(unit,
     51 "SOC unit to operate on (default 0)");
     52 
     53 static int vlan = 1;
     54 LKM_MOD_PARAM(vlan, "i", int, 0);
     55 MODULE_PARM_DESC(vlan,
     56 "VLAN ID used for transmission (default 1)");
     57 
     58 static char *mac_addr = NULL;
     59 LKM_MOD_PARAM(mac_addr, "s", charp, 0);
     60 MODULE_PARM_DESC(mac_addr,
     61 "Ethernet MAC address (default 02:10:18:xx:xx:xx)");
     62 
     63 static int tx_buffers = 32;
     64 LKM_MOD_PARAM(tx_buffers, "i", int, 0);
     65 MODULE_PARM_DESC(tx_buffers,
     66 "Number of preallocated TX packet buffers (default 32)");
     67 
     68 
     69 /* Module Information */
     70 #define MODULE_MAJOR 123
     71 #define MODULE_NAME "linux-bcm-net"
     72 
     73 /* Compatibility */
     74 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,27)
     75 static inline void *netdev_priv(struct net_device *dev)
     76 {
     77         return dev->priv;
     78 }
     79 #endif /* KERNEL_VERSION(2,4,27) */
     80 
     81 /*
     82  * Currently we cannot send packets from interrupt context because
     83  * bcm_l2_addr_get will try to sleep while doing S-Channel messaging.
     84  * To ensure we only send packets from process context, we pass all
     85  * packets to an internal thread, which handles the actual packet
     86  * transmissions.
     87  */
     88 #define BNET_TX_THREAD
     89 
     90 
     91 /* Local packet info */
     92 typedef struct pkt_info_s {
     93     struct list_head list;
     94 
     95     unsigned char *pkt;
     96     bcm_pkt_t bcm_pkt;
     97 
     98     int count;
     99 } pkt_info_t;
    100 
    101 typedef struct {
    102     uint32_t pmask;
    103     struct net_device *ndev;
    104     struct net_device_stats stats; 
    105     pbmp_t pbmp;
    106 } bnet_dev_t;
    107 
    108 /* Default random MAC address has Broadcom OUI with local admin bit set */
    109 static sal_mac_addr_t bnet_dev_mac = { 0x02, 0x10, 0x18, 0x00, 0x00, 0x00 };
    110 
    111 static spinlock_t packet_lock;
    112 
    113 /* 
    114  * Driver net device
    115  * 
    116  * By default a separate network device will be assigned to port 0
    117  * while the remaining ports will all belong to another network
    118  * device.
    119  */
    120 static bnet_dev_t bnet_dev[] = {
    121     { 0x00000001 },     /* Port bitmask for 1st interface (ethX) */
    122     { 0xFFFFFFFE },     /* Port bitmask for 2nd interface (ethX+1) */
    123     { 0 }               /* Last entry must be zero */
    124 };
    125 
    126 /* Driver Proc Entry root */
    127 static struct proc_dir_entry *bnet_proc_root = NULL;
    128 
    129 /* Driver Proc Entry switch stats root */
    130 static struct proc_dir_entry *stats_root = NULL;
    131 
    132 struct bnet_priv_t {
    133     sal_sem_t tx_sem;
    134     volatile int tx_thread_running;
    135     struct sk_buff_head tx_queue;
    136     bnet_dev_t *bdev;
    137 };
    138 
    139 extern ibde_t *bde;
    140 
    141 LIST_HEAD(_free_list);
    142 
    143 static void
    144 bnet_init_packets(void)
    145 {
    146     int i;
    147 
    148     spin_lock_init(&packet_lock);
    149 
    150     if (tx_buffers < 0 || tx_buffers > 4096) {
    151 	tx_buffers = 32;
    152     }	
    153 
    154     for (i = 0; i < tx_buffers; i++) {
    155 	pkt_info_t *pkt = (pkt_info_t*)kmalloc(sizeof(*pkt), GFP_KERNEL);
    156 	memset(pkt, 0, sizeof(*pkt));
    157 	pkt->pkt = (unsigned char*) bde->salloc(0, 1600, "");
    158 	list_add(&pkt->list, &_free_list);
    159     }
    160 }
    161 
    162 static pkt_info_t *
    163 bnet_alloc_packet(void)
    164 {
    165     pkt_info_t *rc = NULL;
    166     unsigned long flags;
    167 
    168     spin_lock_irqsave(&packet_lock, flags);
    169     if (!list_empty(&_free_list)) {
    170 	rc = list_entry(_free_list.next, pkt_info_t, list);
    171 	list_del(&rc->list);
    172 	rc->count++;
    173     }
    174     spin_unlock_irqrestore(&packet_lock, flags);
    175     return rc;
    176 }
    177 
    178 static void
    179 bnet_free_packet(pkt_info_t *pkt)
    180 {
    181     unsigned long flags;
    182 
    183     spin_lock_irqsave(&packet_lock, flags);
    184     list_add(&pkt->list, &_free_list);
    185     spin_unlock_irqrestore(&packet_lock, flags);
    186 }
    187 
    188 static void
    189 bnet_cleanup_packets(void)
    190 {
    191     while(!list_empty(&_free_list)) {
    192 	pkt_info_t *p;
    193 	p = list_entry(_free_list.next, pkt_info_t, list);	
    194 	list_del(&p->list);
    195 	bde->sfree(0, p->pkt);
    196 	kfree(p);
    197     }
    198 }
    199 
    200 /* 
    201  * Network Interface 
    202  */
    203 static void bnet_init_bdev_pbmp(bnet_dev_t *bdev)
    204 {
    205     int i;
    206     pbmp_t pbmp_all;
    207 
    208     BCM_PBMP_ASSIGN(pbmp_all, PBMP_PORT_ALL(unit));
    209     BCM_PBMP_CLEAR(bdev->pbmp);
    210     for (i = 0; i < 8*sizeof(bdev->pmask); i++) {
    211         if (((1 << i) & bdev->pmask) && BCM_PBMP_MEMBER(pbmp_all, i)) {
    212             BCM_PBMP_PORT_ADD(bdev->pbmp, i);
    213         }
    214     }
    215 }
    216 
    217 static bnet_dev_t *bnet_bdev_from_port(int port)
    218 {
    219     int i;
    220 
    221     for (i = 0; bnet_dev[i].ndev; i++) {
    222         if ((1 << port) & bnet_dev[i].pmask) {
    223             return &bnet_dev[i];
    224         }
    225     }
    226 
    227     return NULL;
    228 }
    229 
    230 static bnet_dev_t *bnet_bdev_from_ndev(struct net_device *dev)
    231 {
    232     return ((struct bnet_priv_t *)netdev_priv(dev))->bdev;
    233 }
    234 
    235 static int 
    236 bnet_open(struct net_device *dev)
    237 {
    238     netif_start_queue(dev);
    239     return 0;
    240 }
    241 
    242 static int
    243 bnet_stop(struct net_device *dev)
    244 {
    245     netif_stop_queue(dev);
    246     return 0;
    247 }	
    248 
    249 /* 
    250  * Network Device Statistics. 
    251  * Cleared at init time.  
    252  */
    253 static struct net_device_stats *
    254 bnet_get_stats(struct net_device *dev)
    255 {
    256     bnet_dev_t *bdev = bnet_bdev_from_ndev(dev);
    257 
    258     if (bdev == NULL) {
    259         /* Unknown device */
    260         return NULL;
    261     }
    262 
    263     return &bdev->stats;
    264 }
    265 
    266 /* fake multicast ability */
    267 static void
    268 bnet_set_multicast_list(struct net_device *dev)
    269 {
    270 }
    271 
    272 /* bcmx_pkt_cb_f:  cookie param must be ignored (rx only) */
    273 static void 
    274 bnet_tx_done(int ignored, bcm_pkt_t *packet, void *cookie)
    275 {
    276     bnet_free_packet((pkt_info_t*)packet->cookie);
    277 }	
    278 
    279 static int 
    280 bnet_tx_deferred(struct sk_buff *skb, struct net_device *dev)
    281 {
    282     pkt_info_t *tx;
    283     bcm_l2_addr_t l2addr;
    284     pbmp_t pbmp;
    285     int rv;
    286     bnet_dev_t *bdev = bnet_bdev_from_ndev(dev);
    287 
    288     if (bdev == NULL) {
    289         /* Unknown device */
    290         return -1;
    291     }
    292 
    293     bdev->stats.tx_packets++;
    294     bdev->stats.tx_bytes += skb->len+4;
    295     tx = bnet_alloc_packet();
    296 
    297     if (tx) {
    298         bcm_pkt_t *pkt = &tx->bcm_pkt;
    299 
    300         memcpy(tx->pkt, skb->data, 12);
    301         tx->pkt[12] = 0x81;
    302         tx->pkt[13] = 0x00;
    303         tx->pkt[14] = (vlan >> 8) & 0xF;
    304         tx->pkt[15] = vlan & 0xFF;
    305         memcpy(tx->pkt+16, skb->data+12, skb->len-12);   
    306 
    307         /* Set up bcm packet */
    308         pkt->pkt_data = &pkt->_pkt_data;
    309         pkt->blk_count = 1;
    310         pkt->_pkt_data.data = (uint8 *)tx->pkt;
    311         pkt->_pkt_data.len = skb->len + 4;
    312         pkt->flags = BCM_TX_CRC_APPEND;
    313         pkt->call_back = bnet_tx_done;
    314         pkt->cookie = (void *)tx;
    315 
    316         rv = BCM_E_NOT_FOUND;
    317         if (!in_interrupt() && (tx->pkt[0] & 0x1) == 0) {
    318             bcm_l2_addr_t_init(&l2addr, tx->pkt, vlan);
    319             rv = bcm_l2_addr_get(unit, tx->pkt, vlan, &l2addr);
    320         }
    321         if (rv == BCM_E_NOT_FOUND) {
    322             BCM_PBMP_ASSIGN(pbmp, bdev->pbmp);
    323         } else {
    324             BCM_PBMP_PORT_SET(pbmp, l2addr.port);
    325         }
    326         BCM_PBMP_ASSIGN(pkt->tx_pbmp, pbmp);
    327         BCM_PBMP_ASSIGN(pkt->tx_upbmp, pbmp);
    328 
    329         if (bcm_tx(unit, &tx->bcm_pkt, NULL) < 0) {
    330             bdev->stats.tx_errors++;
    331             bnet_free_packet(tx);
    332         }
    333     }
    334     else {
    335         bdev->stats.tx_dropped++;
    336     }
    337 
    338     kfree_skb(skb);
    339     return 0;
    340 }
    341 
    342 /*
    343  * Currently we cannot send packets from interrupt context because
    344  * bcm_l2_addr_get will try to sleep while doing S-Channel messaging.
    345  * To avoid out of order transmissions, all packets are sent through
    346  * the queue, even if we are called from process context.
    347  */
    348 static int 
    349 bnet_tx(struct sk_buff *skb, struct net_device *dev)
    350 {
    351 #ifdef BNET_TX_THREAD
    352     struct bnet_priv_t *pd = (struct bnet_priv_t *)netdev_priv(bnet_dev[0].ndev);
    353 
    354     skb_queue_tail(&pd->tx_queue, skb);
    355     sal_sem_give(pd->tx_sem);
    356 #else
    357     bnet_tx_deferred(skb, dev);
    358 #endif
    359     return 0;
    360 }
    361 
    362 #ifdef BNET_TX_THREAD
    363 static void
    364 bnet_tx_thread(void *p)
    365 {
    366     struct bnet_priv_t *pd = (struct bnet_priv_t *)netdev_priv(bnet_dev[0].ndev);
    367     struct sk_buff *skb;
    368 
    369 #ifndef CONFIG_TIMESYS
    370     current->rt_priority = 50;
    371     current->policy = SCHED_RR;
    372 #endif
    373 
    374     while (pd->tx_thread_running) {
    375         while ((skb = skb_dequeue(&pd->tx_queue)) != NULL) {
    376             bnet_tx_deferred(skb, skb->dev);
    377         }
    378         sal_sem_take(pd->tx_sem, sal_sem_FOREVER);
    379     }
    380 }
    381 
    382 static int 
    383 bnet_tx_init(struct net_device *dev)
    384 {
    385     struct bnet_priv_t *pd = (struct bnet_priv_t *)netdev_priv(bnet_dev[0].ndev);
    386 
    387     pd->tx_sem = sal_sem_create("bnetTX", 0, 0);
    388     if (!pd->tx_sem) {
    389         gprintk("Error creating thread semaphore.\n");
    390         return -1;
    391     }
    392     skb_queue_head_init(&pd->tx_queue);
    393     pd->tx_thread_running = 1;
    394     sal_thread_create("bnetTX", 0x1000, 50, bnet_tx_thread, dev);
    395     return 0;
    396 }
    397 
    398 static int 
    399 bnet_tx_cleanup(struct net_device *dev)
    400 {
    401     struct bnet_priv_t *pd = (struct bnet_priv_t *)netdev_priv(bnet_dev[0].ndev);
    402     struct sk_buff *skb;
    403 
    404     pd->tx_thread_running = 0;
    405     sal_sem_give(pd->tx_sem);
    406     sal_usleep(500000);
    407     sal_sem_destroy(pd->tx_sem);
    408     while ((skb = skb_dequeue(&pd->tx_queue)) != NULL) {
    409         kfree_skb(skb);
    410     }
    411     return 0;
    412 }
    413 #endif
    414 
    415 static void
    416 bnet_rx_deferred(void *packet_int, int src_port,
    417                  int reason, int pkt_size)
    418 {
    419     struct sk_buff *skb;    
    420     unsigned char *pkt = (unsigned char*) packet_int;
    421     int i;
    422     int rx_vlan;
    423     bnet_dev_t *bdev = bnet_bdev_from_port(src_port);
    424 
    425     if (bdev == NULL) {
    426         /* Unknown port */
    427         return;
    428     }
    429 
    430     bdev->stats.rx_packets++;
    431     bdev->stats.rx_bytes += pkt_size; 
    432 
    433     if (ENET_MACADDR_MULTICAST(pkt)) {
    434 	bdev->stats.multicast++;
    435     }
    436 
    437     rx_vlan = ((pkt[14] & 0x0F) << 8) + pkt[15];
    438 
    439     if (rx_vlan == vlan && (skb = dev_alloc_skb(pkt_size + 2)) != NULL) {
    440         skb->dev = bdev->ndev;
    441         skb_reserve(skb, 2);	/* 16 byte align the IP fields. */
    442 
    443         /* Drop the VLAN tag */
    444         for (i = 11; i >= 0; i--) {
    445             pkt[i+4] = pkt[i];
    446 	}
    447 #if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24))
    448         skb_copy_to_linear_data(skb, pkt+4, pkt_size-4);
    449 #else
    450         eth_copy_and_sum(skb, pkt+4, pkt_size-4, 0);
    451 #endif
    452         skb_put(skb, pkt_size);
    453 
    454         skb->protocol = eth_type_trans(skb, skb->dev);
    455         netif_rx (skb);
    456     }
    457     else {
    458         bdev->stats.rx_dropped++;
    459     }
    460 }
    461 
    462 static bcm_rx_t
    463 bnet_rx(int ignored, bcm_pkt_t *pkt, void *cookie)
    464 {
    465     COMPILER_REFERENCE(ignored);
    466 
    467     bnet_rx_deferred(pkt->pkt_data->data, pkt->rx_port, 0, pkt->pkt_len);
    468     return BCM_RX_HANDLED;
    469 }	
    470 
    471 
    472 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29))
    473 static const struct net_device_ops bkn_netdev_ops = {
    474 	.ndo_open		= bnet_open,
    475 	.ndo_stop		= bnet_stop,
    476 	.ndo_start_xmit		= bnet_tx,
    477 	.ndo_get_stats		= bnet_get_stats,
    478 	.ndo_validate_addr	= eth_validate_addr,
    479 	.ndo_set_rx_mode	= bnet_set_multicast_list,
    480 	.ndo_set_mac_address	= NULL,
    481 	.ndo_do_ioctl		= NULL,
    482 	.ndo_tx_timeout		= NULL,
    483 	.ndo_change_mtu		= NULL,
    484 };
    485 #endif
    486 
    487 static struct net_device *
    488 bnet_init(sal_mac_addr_t mac, bnet_dev_t *bnet_dev)
    489 {
    490     struct net_device *dev;
    491     bcm_l2_addr_t l2addr;
    492 
    493     /* 
    494      * Create the kernel ethernet device. 
    495      */
    496     dev = alloc_etherdev(sizeof(struct bnet_priv_t));
    497 #ifdef SET_MODULE_OWNER
    498     SET_MODULE_OWNER(dev);
    499 #endif
    500 
    501     if (dev == NULL) {
    502         gprintk("Error initializing ethernet device.\n");
    503         return NULL;
    504     }
    505 
    506     ((struct bnet_priv_t *)netdev_priv(dev))->bdev = bnet_dev;
    507 
    508     /* Set the device MAC address */
    509     memcpy(dev->dev_addr, mac, 6);
    510 
    511     /* Device information -- not available right now */
    512     dev->irq = 0;
    513     dev->base_addr = 0;
    514 
    515     /* Device vectors */
    516 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29))
    517     dev->netdev_ops = &bkn_netdev_ops;
    518 #else
    519     dev->open = bnet_open;
    520     dev->hard_start_xmit = bnet_tx;
    521     dev->stop = bnet_stop;
    522     dev->set_multicast_list = bnet_set_multicast_list;
    523     dev->do_ioctl = NULL;
    524     dev->get_stats = bnet_get_stats;
    525 #endif
    526 
    527     /* Register the kernel ethernet device */
    528     if (register_netdev(dev)) {
    529         gprintk("Error registering ethernet device.\n");
    530         _free_netdev(dev);
    531         return NULL;
    532     }
    533 
    534     /* Add ARL entry */
    535     bcm_l2_addr_t_init(&l2addr, mac, vlan);
    536     l2addr.flags = BCM_L2_STATIC;	
    537     l2addr.port = CMIC_PORT(unit);
    538     bcm_l2_addr_add(unit, &l2addr);
    539 
    540     return dev;
    541 }
    542 
    543 static int
    544 bnet_destroy(struct net_device *dev)
    545 {
    546     /* Delete ARL entry */
    547     bcm_l2_addr_delete(unit, dev->dev_addr, vlan);
    548 
    549     unregister_netdev(dev);
    550     _free_netdev(dev);
    551 
    552     return 0;
    553 }
    554 
    555 #define STAT(p, s) { \
    556     int _rv; \
    557     uint64 _v; \
    558     _rv = bcm_stat_get(unit, (p), (s), &_v); \
    559     if (_rv >= 0) { \
    560         seq_printf(m, "%s=%d\n", #s, COMPILER_64_LO(_v)); \
    561     } \
    562 }
    563 
    564 /*
    565  * Port Statistics Proc Entries
    566  */
    567 static int 
    568 bnet_proc_stat_show(struct seq_file *m, void *v)
    569 {
    570     bcm_port_t port = (bcm_port_t)PTR_TO_INT(m->private);
    571 
    572     /*** RFC 1213 ***/
    573 
    574     STAT(port, snmpIfInOctets); 
    575     STAT(port, snmpIfInUcastPkts);
    576     STAT(port, snmpIfInNUcastPkts);
    577     STAT(port, snmpIfInDiscards);
    578     STAT(port, snmpIfInErrors);
    579     STAT(port, snmpIfInUnknownProtos);
    580     STAT(port, snmpIfOutOctets);
    581     STAT(port, snmpIfOutUcastPkts);
    582     STAT(port, snmpIfOutNUcastPkts);
    583     STAT(port, snmpIfOutDiscards);
    584     STAT(port, snmpIfOutErrors);
    585     STAT(port, snmpIpForwDatagrams);
    586     STAT(port, snmpIpInReceives);
    587     STAT(port, snmpIpInDiscards);
    588 
    589     /*** RFC 1493 ***/
    590 
    591     STAT(port, snmpDot1dBasePortDelayExceededDiscards);
    592     STAT(port, snmpDot1dBasePortMtuExceededDiscards);
    593     STAT(port, snmpDot1dTpPortInFrames);
    594     STAT(port, snmpDot1dTpPortOutFrames);
    595     STAT(port, snmpDot1dPortInDiscards);
    596 
    597     /*** RFC 1757 (EtherStat) ***/
    598 
    599     STAT(port, snmpEtherStatsDropEvents);
    600     STAT(port, snmpEtherStatsMulticastPkts);
    601     STAT(port, snmpEtherStatsBroadcastPkts);
    602     STAT(port, snmpEtherStatsUndersizePkts);
    603     STAT(port, snmpEtherStatsFragments);
    604     STAT(port, snmpEtherStatsPkts64Octets);
    605     STAT(port, snmpEtherStatsPkts65to127Octets);
    606     STAT(port, snmpEtherStatsPkts128to255Octets);
    607     STAT(port, snmpEtherStatsPkts256to511Octets);
    608     STAT(port, snmpEtherStatsPkts512to1023Octets);
    609     STAT(port, snmpEtherStatsPkts1024to1518Octets);
    610     STAT(port, snmpEtherStatsOversizePkts);
    611     STAT(port, snmpEtherStatsJabbers);
    612     STAT(port, snmpEtherStatsOctets);
    613     STAT(port, snmpEtherStatsPkts);
    614     STAT(port, snmpEtherStatsCollisions);
    615     STAT(port, snmpEtherStatsCRCAlignErrors);
    616     STAT(port, snmpEtherStatsTXNoErrors);
    617     STAT(port, snmpEtherStatsRXNoErrors);
    618 
    619     /*** RFC 2665 ***/
    620 
    621     STAT(port, snmpDot3StatsAlignmentErrors);
    622     STAT(port, snmpDot3StatsFCSErrors);
    623     STAT(port, snmpDot3StatsSingleCollisionFrames);
    624     STAT(port, snmpDot3StatsMultipleCollisionFrames);
    625     STAT(port, snmpDot3StatsSQETTestErrors);
    626     STAT(port, snmpDot3StatsDeferredTransmissions);
    627     STAT(port, snmpDot3StatsLateCollisions);
    628     STAT(port, snmpDot3StatsExcessiveCollisions);
    629     STAT(port, snmpDot3StatsInternalMacTransmitErrors);
    630     STAT(port, snmpDot3StatsCarrierSenseErrors);
    631     STAT(port, snmpDot3StatsFrameTooLongs);
    632     STAT(port, snmpDot3StatsInternalMacReceiveErrors);
    633     STAT(port, snmpDot3StatsSymbolErrors);
    634 
    635     /*** RFC 2233 ***/
    636 
    637     STAT(port, snmpIfHCInOctets);
    638     STAT(port, snmpIfHCInUcastPkts);
    639     STAT(port, snmpIfHCInMulticastPkts);
    640     STAT(port, snmpIfHCInBroadcastPkts);
    641     STAT(port, snmpIfHCOutOctets);
    642     STAT(port, snmpIfHCOutUcastPkts);
    643     STAT(port, snmpIfHCOutMulticastPkts);
    644     STAT(port, snmpIfHCOutBroadcastPckts);
    645 
    646     /*** Additional Broadcom stats ***/
    647 
    648     STAT(port, snmpBcmIPMCBridgedPckts);
    649     STAT(port, snmpBcmIPMCRoutedPckts);
    650     STAT(port, snmpBcmIPMCInDroppedPckts);
    651     STAT(port, snmpBcmIPMCOutDroppedPckts);
    652 
    653     return 0;
    654 }
    655 
    656 static int bnet_proc_stat_open(struct inode * inode, struct file * file) {
    657     return single_open(file, bnet_proc_stat_show, PROC_PDE_DATA(inode));
    658 
    659 }
    660 struct file_operations bnet_proc_stat_fops = {
    661     owner:      THIS_MODULE,
    662     open:       bnet_proc_stat_open,
    663     read:       seq_read,
    664     llseek:     seq_lseek,
    665     release:    single_release,
    666 };
    667 
    668 static int
    669 bnet_proc_init(void)
    670 {
    671     char scratch[64];
    672     pbmp_t pbm;
    673     bcm_port_t port;
    674     struct proc_dir_entry* entry;
    675 
    676     stats_root = proc_mkdir("stats", bnet_proc_root); 
    677 
    678     BCM_PBMP_ASSIGN(pbm, PBMP_PORT_ALL(unit));
    679     PBMP_ITER(pbm, port) {
    680         sprintf(scratch, "port%d", port);
    681         PROC_CREATE_DATA(entry, scratch, 0, stats_root, &bnet_proc_stat_fops,
    682                          INT_TO_PTR(port));
    683         if (entry == NULL) {
    684             return -1;
    685         }
    686     }
    687 
    688     return 0;  
    689 }
    690 
    691 static int
    692 bnet_proc_cleanup(void)
    693 {
    694     char scratch[64];
    695     pbmp_t pbm;
    696     bcm_port_t port;
    697 
    698     BCM_PBMP_ASSIGN(pbm, PBMP_PORT_ALL(unit));
    699     PBMP_ITER(pbm, port) {
    700         sprintf(scratch, "port%d", port); 
    701         remove_proc_entry(scratch, stats_root); 
    702     }
    703 
    704     remove_proc_entry("stats", bnet_proc_root); 
    705     return 0;
    706 }
    707 
    708 /*
    709  * Generic module functions
    710  */
    711 
    712 static int
    713 _pprint(void)
    714 {	
    715     struct list_head *list;
    716     int i=0;
    717 
    718     pprintf("Broadcom BCM Core Linux Network Driver\n");
    719     pprintf("SOC Unit: %d\n", unit);
    720     pprintf("VLAN ID: %d\n", vlan);
    721     pprintf("Packet Buffer Usage Statistics (%d buffers):\n", tx_buffers);
    722     list_for_each(list, &_free_list) {
    723 	pkt_info_t *pkt = (pkt_info_t*)list;
    724 	pprintf("%3d: %.8d\n", i++, pkt->count);
    725     }
    726 
    727     /* put some goodies here */
    728     return 0;
    729 }
    730 
    731 static int
    732 _init(void)
    733 {
    734     char scratch[64];
    735     int i;
    736     struct net_device *dev;
    737 
    738     if (!soc_attached(unit)) {
    739         gprintk("Error: SOC device not attached.\n");
    740         return -1;
    741     }
    742 
    743     /* Initialize ether device processing */
    744     bnet_init_packets();
    745 
    746     /* Randomize Lower 3 bytes of the MAC address (TESTING ONLY) */
    747     get_random_bytes(&bnet_dev_mac[3], 3);
    748 
    749     /* Check for user-supplied MAC address (recommended) */
    750     if (mac_addr != NULL && strlen(mac_addr) == 17) {
    751         for (i = 0; i < 6; i++) {
    752             bnet_dev_mac[i] = simple_strtoul(&mac_addr[i*3], NULL, 16) & 0xFF;
    753         }
    754         /* Do not allow multicast address */
    755         bnet_dev_mac[0] &= ~0x01;
    756     }
    757 
    758     /* Initialize BCM driver */
    759     system_init(unit);
    760 
    761     bnet_init_bdev_pbmp(&bnet_dev[0]);
    762     if ((dev = bnet_init(bnet_dev_mac, &bnet_dev[0])) == NULL) {
    763         return -1;
    764     }
    765     bnet_dev[0].ndev = dev;
    766 
    767     /* Create virtual devices as needed */
    768     for (i = 1; bnet_dev[i].pmask; i++) {
    769         bnet_dev_mac[5]++;
    770         bnet_init_bdev_pbmp(&bnet_dev[i]);
    771         if ((dev = bnet_init(bnet_dev_mac, &bnet_dev[i])) == NULL) {
    772             return -1;
    773         }
    774         bnet_dev[i].ndev = dev;
    775     }
    776 
    777     bcm_rx_register(unit, "bcm-net", bnet_rx, BCM_RX_PRIO_MAX, NULL, 
    778                     BCM_RCO_F_ALL_COS | BCM_RCO_F_INTR);
    779     bcm_rx_start(unit, NULL);
    780 
    781     /* Initialize TX */
    782 #ifdef BNET_TX_THREAD
    783     bnet_tx_init(bnet_dev[0].ndev);
    784 #endif
    785 
    786     /* Initialize proc files */
    787     proc_mkdir("bcm", NULL);
    788     sprintf(scratch, "bcm/%s", bnet_dev[0].ndev->name); 
    789     bnet_proc_root = proc_mkdir(scratch, NULL);
    790 
    791     bnet_proc_init();
    792     return 0;
    793 }
    794 
    795 static int
    796 _cleanup(void)
    797 {
    798     char scratch[64];
    799     int i;
    800 
    801     bcm_rx_stop(unit, NULL);
    802     bcm_rx_unregister(unit, bnet_rx, BCM_RX_PRIO_MAX);
    803     bnet_cleanup_packets();
    804 #ifdef BNET_TX_THREAD
    805     bnet_tx_cleanup(bnet_dev[0].ndev);
    806 #endif
    807 
    808     bnet_proc_cleanup();
    809     sprintf(scratch, "bcm/%s", bnet_dev[0].ndev->name);
    810     remove_proc_entry(scratch, NULL);
    811     remove_proc_entry("bcm", NULL);
    812 
    813     for (i = 0; bnet_dev[i].ndev; i++) {
    814         bnet_destroy(bnet_dev[i].ndev);
    815     }
    816 
    817     return 0;
    818 }	
    819 
    820 static gmodule_t _gmodule = {
    821     name: MODULE_NAME, 
    822     major: MODULE_MAJOR, 
    823     init: _init,
    824     cleanup: _cleanup, 
    825     pprint: _pprint, 
    826     ioctl: NULL,
    827     open: NULL, 
    828     close: NULL, 
    829 }; 
    830 
    831 gmodule_t*
    832 gmodule_get(void)
    833 {
    834     EXPORT_NO_SYMBOLS;
    835     return &_gmodule;
    836 }