summaryrefslogblamecommitdiffstats
path: root/c/src/libchip/network/dwmac.c
blob: 20d87dc83b06e92b2a5f9615339769d711e74a5a (plain) (tree)
1
2
3
4
5
6
7
8
9
10
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935









                                                                         
                                                                      
















                                                          
                
                      


                         

                                
                    
      












                                                                           
                                             
                                              
                                             













































                                                                            


                                                                                         

































































































































































                                                                               
                                                                            
 





                                                                                






                                                                     





                                                                                  
 
                                                    





                                                                              






















                                                                               










































































































































































































































































































































































































































































































































































































































                                                                                        
                                                                          









































































































































































































































                                                                                 
                                                  


















































































































































































                                                                            













                                                                        
 


                                                       
         
 



                                                            
 

                                                          
 




                                                                     
 

                           
 



                                                             
 

                                                           
 
                                               
 




                                                                        
 






















                                                              
       
 










                                                                            
              
         
       
 



















                                                                            
 





                                                    
 



                                 
 

                                                               
 


                                                      
 















                                                                     
       
 



                                                                            
 



                                              
 


                                             
 






                                    
 


                                        
 


                                                                 
 


                                                                     
 




                                                  
 







                                                   
 






                                                       
 
                
 






                                                                       
 
















                                                                             
 

                                     
 

                                       
             
 




                                            
           
         
 



                                                      
                





                                                             
         
 
                                

       
                                                            
     





































































































































































































                                                                          
                                







                                           


                                 




                                             
                                  
                                                               


                     
                                  
                                                               
















                                                               
                                




                                            
      









                                                             
                                  
                                                               


                     
                                  
                                                               


                     
                                




                                          


                                 



































































































































                                                                              
                                                     






















































































                                                                         
                                                    


                                                   
                                                     








































































                                                                               






                                                                 







































































































































































                                                                               
/**
 * @file
 *
 * @brief DWMAC 10/100/1000 Network Interface Controller
 *
 * DWMAC 10/100/1000 on-chip Ethernet controllers are a Synopsys IP Core.
 * This is the main mode for the network interface controller.
 */

/*
 * Copyright (c) 2013-2014 embedded brains GmbH.  All rights reserved.
 *
 *  embedded brains GmbH
 *  Dornierstr. 4
 *  82178 Puchheim
 *  Germany
 *  <rtems@embedded-brains.de>
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rtems.org/license/LICENSE.
 */

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <bsp.h>
#include <bsp/fatal.h>
#include <rtems/endian.h>
#include "dwmac-common.h"
#include "dwmac-core.h"

#ifdef BSP_FEATURE_IRQ_EXTENSION
#include <bsp/irq.h>
#endif

/* PHY events which can be combined to an event set.
 * The PHY can create an event for a corresponding status change */
const dwmac_phy_event PHY_EVENT_JABBER                = 0x80;
const dwmac_phy_event PHY_EVENT_RECEIVE_ERROR         = 0x40;
const dwmac_phy_event PHY_EVENT_PAGE_RECEIVE          = 0x20;
const dwmac_phy_event PHY_EVENT_PARALLEL_DETECT_FAULT = 0x10;
const dwmac_phy_event PHY_EVENT_LINK_PARTNER_ACK      = 0x08;
const dwmac_phy_event PHY_EVENT_LINK_DOWN             = 0x04;
const dwmac_phy_event PHY_EVENT_REMOTE_FAULT          = 0x02;
const dwmac_phy_event PHY_EVENT_LINK_UP               = 0x01;

/* Default values for the number of DMA descriptors and mbufs to be used */
#define DWMAC_CONFIG_RX_UNIT_COUNT_DEFAULT 64
#define DWMAC_CONFIG_RX_UNIT_COUNT_MAX INT_MAX
#define DWMAC_CONFIG_TX_UNIT_COUNT_DEFAULT 64
#define DWMAC_CONFIG_TX_UNIT_COUNT_MAX INT_MAX

/* Default values for the DMA configuration */
#define DWMAC_DMA_BUS_MODE_PBL_DEFAULT DWMAC_DMA_CFG_BUS_MODE_BURST_LENGTH_8
#define DWMAC_DMA_BUS_MODE_FB_DEFAULT \
  DWMAC_DMA_CFG_BUS_MODE_BURST_MODE_SINGLE_OR_INCR
#define DWMAC_DMA_BUS_MODE_MIXED_BURSTS_DEFAULT \
  DWMAC_DMA_CFG_BUS_MODE_BURST_NOT_MIXED
#define DWMAC_DMA_AXI_BURST_LENGTH_4_DEFAULT \
  DWMAC_DMA_CFG_AXI_BURST_LENGTH_4_NOT_SUPPORTED
#define DWMAC_DMA_AXI_BURST_LENGTH_8_DEFAULT \
  DWMAC_DMA_CFG_AXI_BURST_LENGTH_8_NOT_SUPPORTED
#define DWMAC_DMA_AXI_BURST_LENGTH_16_DEFAULT \
  DWMAC_DMA_CFG_AXI_BURST_LENGTH_16_NOT_SUPPORTED
#define DWMAC_DMA_AXI_BURST_BOUNDARY_DEFAULT \
  DWMAC_DMA_CFG_AXI_BURST_BOUNDARY_4_KB

/* CSR Frequency Access Defines*/
#define DWCGNAC3504_CSR_F_35M 35000000
#define DWCGNAC3504_CSR_F_60M 60000000
#define DWCGNAC3504_CSR_F_100M 100000000
#define DWCGNAC3504_CSR_F_150M 150000000
#define DWCGNAC3504_CSR_F_250M 250000000
#define DWCGNAC3504_CSR_F_300M 300000000

/* MDC Clock Selection define*/
#define DWCGNAC3504_CSR_60_100M 0x0 /* MDC = clk_scr_i/42 */
#define DWCGNAC3504_CSR_100_150M 0x1 /* MDC = clk_scr_i/62 */
#define DWCGNAC3504_CSR_20_35M 0x2 /* MDC = clk_scr_i/16 */
#define DWCGNAC3504_CSR_35_60M 0x3 /* MDC = clk_scr_i/26 */
#define DWCGNAC3504_CSR_150_250M 0x4 /* MDC = clk_scr_i/102 */
#define DWCGNAC3504_CSR_250_300M 0x5 /* MDC = clk_scr_i/122 */

#define DWMAC_ALIGN( value, num_bytes_to_align_to ) \
  ( ( ( value ) + ( ( typeof( value ) )( num_bytes_to_align_to ) - 1 ) ) \
    & ~( ( typeof( value ) )( num_bytes_to_align_to ) - 1 ) )

#undef DWMAC_DEBUG
#ifdef DWMAC_DEBUG
#define DWMAC_PRINT_DBG( fmt, args ... )  printk( fmt, ## args )
#else
#define DWMAC_PRINT_DBG( fmt, args ... )  do { } while ( 0 )
#endif

#define DWMAC_DMA_THRESHOLD_CONTROL_DEFAULT 64

#define DWMAC_GLOBAL_MBUF_CNT (rtems_bsdnet_config.mbuf_bytecount / sizeof(struct  mbuf))
#define DWMAG_GLOBAL_MCLUST_CNT (rtems_bsdnet_config.mbuf_cluster_bytecount / MCLBYTES)

static int dwmac_if_mdio_busy_wait( const volatile uint32_t *gmii_address )
{
  rtems_interval timeout = rtems_clock_get_ticks_per_second();
  rtems_interval i       = 0;


  while ( ( *gmii_address & MACGRP_GMII_ADDRESS_GMII_BUSY ) != 0
          && i < timeout ) {
    rtems_task_wake_after( 1 );
    ++i;
  }

  return i < timeout ? 0 : EBUSY;
}

static int dwmac_if_mdio_read(
  int       phy,
  void     *arg,
  unsigned  phy_reg,
  uint32_t *val )
{
  int                   eno         = 0;
  dwmac_common_context *self        = (dwmac_common_context *) arg;
  volatile uint32_t    *mii_address = &self->macgrp->gmii_address;
  volatile uint32_t    *mii_data    = &self->macgrp->gmii_data;
  uint32_t              reg_value   = 0;


  if ( phy == -1 ) {
    reg_value = MACGRP_GMII_ADDRESS_PHYSICAL_LAYER_ADDRESS_SET(
      reg_value,
      self->CFG->MDIO_BUS_ADDR
      );
  } else {
    reg_value = MACGRP_GMII_ADDRESS_PHYSICAL_LAYER_ADDRESS_SET(
      reg_value,
      phy
      );
  }

  reg_value = MACGRP_GMII_ADDRESS_GMII_REGISTER_SET(
    reg_value,
    phy_reg
    );
  reg_value &= ~MACGRP_GMII_ADDRESS_GMII_WRITE;

  /* For the 7111 GMAC, we must set BUSY bit  in the MII address register while
   * accessing the PHY registers.
   * Fortunately, it seems this has no drawback for the 7109 MAC. */
  reg_value |= MACGRP_GMII_ADDRESS_GMII_BUSY;
  reg_value  = MACGRP_GMII_ADDRESS_CSR_CLOCK_RANGE_SET(
    reg_value,
    self->csr_clock
    );

  eno = dwmac_if_mdio_busy_wait( mii_address );

  if ( eno == 0 ) {
    *mii_address = reg_value;

    if ( dwmac_if_mdio_busy_wait( mii_address ) ) {
      eno = EBUSY;
    } else {
      /* Read the value from the MII data register */
      *val = MACGRP_GMII_DATA_GMII_DATA_GET( *mii_data );
    }
  }

  return eno;
}

static int dwmac_if_mdio_write(
  int      phy,
  void    *arg,
  unsigned phy_reg,
  uint32_t val )
{
  int                   eno         = 0;
  dwmac_common_context *self        = (dwmac_common_context *) arg;
  volatile uint32_t    *mii_address = &self->macgrp->gmii_address;
  volatile uint32_t    *mii_data    = &self->macgrp->gmii_data;
  uint32_t              reg_value   = *mii_address;


  if ( phy == -1 ) {
    reg_value = MACGRP_GMII_ADDRESS_PHYSICAL_LAYER_ADDRESS_SET(
      reg_value,
      self->CFG->MDIO_BUS_ADDR
      );
  } else {
    reg_value = MACGRP_GMII_ADDRESS_PHYSICAL_LAYER_ADDRESS_SET(
      reg_value,
      phy
      );
  }

  reg_value = MACGRP_GMII_ADDRESS_GMII_REGISTER_SET(
    reg_value,
    phy_reg
    );
  reg_value |= MACGRP_GMII_ADDRESS_GMII_WRITE;
  reg_value |= MACGRP_GMII_ADDRESS_GMII_BUSY;
  reg_value  = MACGRP_GMII_ADDRESS_CSR_CLOCK_RANGE_SET(
    reg_value,
    self->csr_clock
    );

  /* Wait until any existing MII operation is complete */
  eno = dwmac_if_mdio_busy_wait( mii_address );

  if ( eno == 0 ) {
    /* Set the MII address register to write */
    *mii_data    = val;
    *mii_address = reg_value;

    /* Wait until any existing MII operation is complete */
    eno = dwmac_if_mdio_busy_wait( mii_address );
  }

  return eno;
}

static int dwmac_get_phy_info(
  dwmac_common_context *self,
  const uint8_t         mdio_bus_addr,
  uint32_t             *oui,
  uint8_t              *model,
  uint8_t              *revision )
{
  int      eno = 0;
  uint32_t read;


  eno = dwmac_if_mdio_read(
    mdio_bus_addr,
    self,
    2,
    &read );

  if ( eno == 0 ) {
    *oui = 0 | ( read << 6 );
    eno  = dwmac_if_mdio_read(
      mdio_bus_addr,
      self,
      3,
      &read );

    if ( eno == 0 ) {
      *oui     |= ( read & 0xFC00 ) >> 10;
      *model    = (uint8_t) ( ( read & 0x03F0 ) >> 4 );
      *revision = read & 0x000F;
    }
  }

  return eno;
}

static inline void dwmac_enable_irq_rx( dwmac_common_context *self )
{
  dwmac_core_enable_dma_irq_rx( self );
}

static inline void dwmac_enable_irq_tx_default( dwmac_common_context *self )
{
  dwmac_core_enable_dma_irq_tx_default( self );
}

static inline void dwmac_enable_irq_tx_transmitted( dwmac_common_context *self )
{
  dwmac_core_enable_dma_irq_tx_transmitted( self );
}

static inline void dwmac_disable_irq_rx( dwmac_common_context *self )
{
  dwmac_core_disable_dma_irq_rx( self );
}

static inline void dwmac_disable_irq_tx_all( dwmac_common_context *self )
{
  dwmac_core_disable_dma_irq_tx_all( self );
}

static inline void dwmac_disable_irq_tx_transmitted ( dwmac_common_context *self )
{
  dwmac_core_disable_dma_irq_tx_transmitted( self );
}

static void dwmac_control_request_complete( const dwmac_common_context *self )
{
  rtems_status_code sc = rtems_event_transient_send( self->task_id_control );

  assert( sc == RTEMS_SUCCESSFUL );
}

static int dwmac_control_request(
  dwmac_common_context *self,
  rtems_id              task,
  rtems_event_set       event )
{
  int               eno        = 0;
  rtems_status_code sc         = RTEMS_SUCCESSFUL;
  uint32_t          nest_count = 0;


  self->task_id_control = rtems_task_self();

  sc                    = rtems_bsdnet_event_send( task, event );

  eno                   = rtems_status_code_to_errno( sc );

  if ( eno == 0 ) {
    nest_count = rtems_bsdnet_semaphore_release_recursive();
    sc         = rtems_event_transient_receive( RTEMS_WAIT, RTEMS_NO_TIMEOUT );
    eno        = rtems_status_code_to_errno( sc );
    rtems_bsdnet_semaphore_obtain_recursive( nest_count );
    self->task_id_control = 0;
  }

  return eno;
}

static bool dwmac_if_media_status(
  dwmac_common_context *self,
  int                  *media,
  uint8_t               phy_address )
{
  struct ifnet *ifp = &self->arpcom.ac_if;


  *media = (int) IFM_MAKEWORD( 0, 0, 0, phy_address );

  return ( *ifp->if_ioctl )( ifp, SIOCGIFMEDIA, (caddr_t) media ) == 0;
}

#define DWMAC_PRINT_COUNTER( fmt, counter, args ... ) \
  if ( counter != 0 ) { \
    printf( fmt, counter, ## args ); \
  }

/* Print statistics. Get called via ioctl. */
static int dwmac_if_interface_stats( void *arg )
{
  int                   eno      = 0;
  dwmac_common_context *self     = arg;
  volatile macgrp      *macgrp   = self->macgrp;
  int                   media    = 0;
  bool                  media_ok = dwmac_if_media_status(
    self, &media, self->CFG->MDIO_BUS_ADDR );
  uint32_t              oui;
  uint8_t               model;
  uint8_t               revision;
  uint16_t              giant_frame_size = 1518;


  if ( ( MACGRP_MAC_CONFIGURATION_JE & macgrp->mac_configuration ) != 0 ) {
    /* Jumbo Frames are enabled */
    giant_frame_size = 9018;
  }

  if ( media_ok ) {
    rtems_ifmedia2str( media, NULL, 0 );
    printf( "\n" );
    eno = dwmac_get_phy_info(
      self,
      self->CFG->MDIO_BUS_ADDR,
      &oui,
      &model,
      &revision );

    if ( eno == 0 ) {
      printf( "PHY 0x%02x: OUI = 0x%04" PRIX32 ", Model = 0x%02" PRIX8 ", Rev = "
              "0x%02" PRIX8 "\n",
              self->CFG->MDIO_BUS_ADDR,
              oui,
              model,
              revision );
      printf( "PHY status counters:\n" );
      DWMAC_PRINT_COUNTER(
        "%" PRIu32 " link down\n",
        self->stats.phy_status_counts.link_down
        );
      DWMAC_PRINT_COUNTER(
        "%" PRIu32 " link up\n",
        self->stats.phy_status_counts.link_up
        );
    }
  } else {
    printf( "PHY %d communication error\n", self->CFG->MDIO_BUS_ADDR );
  }

  printf( "\nHardware counters:\n" );

  if ( ( self->dmagrp->hw_feature & DMAGRP_HW_FEATURE_MMCSEL ) != 0 ) {
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes transmitted, exclusive of preamble and retried bytes, "
      "in good and bad frames\n",
      macgrp->txoctetcount_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad frames transmitted, exclusive of retried "
      "frames\n",
      macgrp->txframecount_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good broadcast frames transmitted\n",
      macgrp->txbroadcastframes_g
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good multicast frames transmitted\n",
      macgrp->txmulticastframes_g
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad frames transmitted with length 64 bytes, "
      "exclusive of preamble and retried frames\n",
      macgrp->tx64octets_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad frames transmitted with length between 65 and "
      "127 (inclusive) bytes, exclusive of preamble and retried frames\n",
      macgrp->tx65to127octets_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad frames transmitted with length between 128 and "
      "255 (inclusive) bytes, exclusive of preamble and retried frames\n",
      macgrp->tx128to255octets_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad frames transmitted with length between 256 and "
      "511 (inclusive) bytes, exclusive of preamble and retried frames\n",
      macgrp->tx256to511octets_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad frames transmitted with length between 512 and "
      "1,023 (inclusive) bytes, exclusive of preamble and retried frames\n",
      macgrp->tx512to1023octets_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad frames transmitted with length between 1,024 and"
      " maxsize (inclusive) bytes, exclusive of preamble and retried frames\n",
      macgrp->tx1024tomaxoctets_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad unicast frames transmitted\n",
      macgrp->txunicastframes_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad multicast frames transmitted\n",
      macgrp->txmulticastframes_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad broadcast frames transmitted\n",
      macgrp->txbroadcastframes_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " frames aborted due to frame underflow error\n",
      macgrp->txunderflowerror
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " successfully transmitted frames after a single collision in "
      "Half-duplex mode\n",
      macgrp->txsinglecol_g
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " successfully transmitted frames after more than a single"
      " collision in Half-duplex mode\n",
      macgrp->txmulticol_g
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " successfully transmitted frames after a deferral in "
      "Halfduplex mode\n",
      macgrp->txdeferred
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " frames aborted due to late collision error\n",
      macgrp->txlatecol
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " frames aborted due to excessive (16) collision errors\n",
      macgrp->txexesscol
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " frames aborted due to carrier sense error (no carrier or loss"
      " of carrier)\n",
      macgrp->txcarriererr
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes transmitted, exclusive of preamble, in good frames "
      "only\n",
      macgrp->txoctetcnt
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good frames transmitted\n",
      macgrp->txframecount_g
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " frames aborted due to excessive deferral error (deferred for"
      " more than two max-sized frame times)\n",
      macgrp->txexcessdef
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good PAUSE frames transmitted\n",
      macgrp->txpauseframes
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good VLAN frames transmitted, exclusive of retried frames\n",
      macgrp->txvlanframes_g
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad frames received\n",
      macgrp->txoversize_g
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad frames received\n",
      macgrp->rxframecount_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes received, exclusive of preamble, in good and bad "
      "frames\n",
      macgrp->rxoctetcount_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes received, exclusive of preamble, only in good frames\n",
      macgrp->rxoctetcount_g
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good broadcast frames received\n",
      macgrp->rxbroadcastframes_g
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good multicast frames received\n",
      macgrp->rxmulticastframes_g
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " frames received with CRC error\n",
      macgrp->rxcrcerror
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " frames received with alignment (dribble) error. Valid only"
      " in 10/100 mode\n",
      macgrp->rxalignmenterror
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " frames received with runt (<64 bytes and CRC error) error\n",
      macgrp->rxrunterror
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " giant frames received with length (including CRC) greater "
      "than %" PRIu16 " bytes (%" PRIu16 " bytes for VLAN tagged) and with CRC"
      " error\n",
      macgrp->rxjabbererror, giant_frame_size, giant_frame_size + 4
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " frames received with length less than 64 bytes, without any"
      " errors\n",
      macgrp->rxundersize_g
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " frames received with length greater than the maxsize (1,518"
      " or 1,522 for VLAN tagged frames), without errors\n",
      macgrp->rxoversize_g
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad frames received with length 64 bytes, exclusive"
      " of preamble\n",
      macgrp->rx64octets_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad frames received with length between 65 and 127"
      " (inclusive) bytes, exclusive of preamble\n",
      macgrp->rx65to127octets_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad frames received with length between 128 and 255"
      " (inclusive) bytes, exclusive of preamble\n",
      macgrp->rx128to255octets_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad frames received with length between 256 and 511"
      " (inclusive) bytes, exclusive of preamble\n",
      macgrp->rx256to511octets_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad frames received with length between 512 and "
      "1,023 (inclusive) bytes, exclusive of preamble\n",
      macgrp->rx512to1023octets_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad frames received with length between 1,024 and"
      " maxsize (inclusive) bytes, exclusive of preamble and retried frames\n",
      macgrp->rx1024tomaxoctets_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good unicast frames received\n",
      macgrp->rxunicastframes_g
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " frames received with length error (length type field not "
      "equal to frame size), for all frames with valid length field\n",
      macgrp->rxlengtherror
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " frames received with length field not equal to the valid "
      "frame size (greater than 1,500 but less than 1,536)\n",
      macgrp->rxoutofrangetype
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and valid PAUSE frames received\n",
      macgrp->rxpauseframes
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " missed received frames due to FIFO overflow\n",
      macgrp->rxfifooverflow
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good and bad VLAN frames received\n",
      macgrp->rxvlanframes_gb
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " frames received with error due to watchdog timeout error "
      "(frames with a data load larger than 2,048 bytes)\n",
      macgrp->rxwatchdogerror
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " frames received with Receive error or Frame Extension error"
      " on the GMII or MII interface\n",
      macgrp->rxrcverror
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " received good control frames\n",
      macgrp->rxctrlframes_g
      );

    printf( "\n" );

    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good IPv4 datagrams received with the TCP, UDP, or ICMP "
      "payload\n",
      macgrp->rxipv4_gd_frms
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " IPv4 datagrams received with header (checksum, length, or "
      "version mismatch) errors\n",
      macgrp->rxipv4_hdrerr_frms
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " IPv4 datagram frames received that did not have a TCP, UDP, "
      "or ICMP payload processed by the Checksum engine\n",
      macgrp->rxipv4_nopay_frms
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good IPv4 datagrams with fragmentation\n",
      macgrp->rxipv4_frag_frms
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good IPv4 datagrams received that had a UDP payload with "
      "checksum disabled\n",
      macgrp->rxipv4_udsbl_frms
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good IPv6 datagrams received with TCP, UDP, or ICMP "
      "payloads\n",
      macgrp->rxipv6_gd_frms
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " IPv6 datagrams received with header errors (length or "
      "version mismatch)\n",
      macgrp->rxipv6_hdrerr_frms
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " IPv6 datagram frames received that did not have a TCP, UDP,"
      " or ICMP payload. This includes all IPv6 datagrams with fragmentation"
      " or security extension headers\n",
      macgrp->rxipv6_nopay_frms
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good IP datagrams with a good UDP payload. This counter is "
      "not updated when the counter is incremented\n",
      macgrp->rxudp_gd_frms
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good IP datagrams whose UDP payload has a checksum error\n",
      macgrp->rxudp_err_frms
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good IP datagrams with a good TCP payload\n",
      macgrp->rxtcp_gd_frms
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good IP datagrams whose TCP payload has a checksum error\n",
      macgrp->rxtcp_err_frms
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good IP datagrams with a good ICMP payload\n",
      macgrp->rxicmp_gd_frms
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " good IP datagrams whose ICMP payload has a checksum error\n",
      macgrp->rxicmp_err_frms
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes received in good IPv4 datagrams encapsulating TCP, UDP,"
      " or ICMP data\n",
      macgrp->rxipv4_gd_octets
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes received in IPv4 datagrams with header errors (checksum,"
      " length, version mismatch). The value in the Length field of IPv4 "
      "header is used to update this counter\n",
      macgrp->rxipv4_hdrerr_octets
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes received in IPv4 datagrams that did not have a TCP, UDP"
      ", or ICMP payload. The value in the IPv4 headers Length field is used"
      " to update this counter\n",
      macgrp->rxipv4_nopay_octets
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes received in fragmented IPv4 datagrams. The value in the"
      " IPv4 headers Length field is used to update this counter\n",
      macgrp->rxipv4_frag_octets
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes received in a UDP segment that had the UDP checksum "
      "disabled. This counter does not count IP Header bytes\n",
      macgrp->rxipv4_udsbl_octets
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes received in good IPv6 datagrams encapsulating TCP, UDP "
      "or ICMPv6 data\n",
      macgrp->rxipv6_gd_octets
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes received in IPv6 datagrams with header errors (length, "
      "version mismatch). The value in the IPv6 headers Length field is used "
      "to update this counter\n",
      macgrp->rxipv6_hdrerr_octets
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes received in IPv6 datagrams that did not have a TCP, UDP"
      ", or ICMP payload. The value in the IPv6 headers Length field is used "
      "to update this counter\n",
      macgrp->rxipv6_nopay_octets
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes received in a good UDP segment. This counter does not "
      "count IP header bytes\n",
      macgrp->rxudp_gd_octets
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes received in a UDP segment that had checksum errors\n",
      macgrp->rxudp_err_octets
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes received in a good TCP segment\n",
      macgrp->rxtcp_gd_octets
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes received in a TCP segment with checksum errors\n",
      macgrp->rxtcperroctets
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes received in a good ICMP segment\n",
      macgrp->rxicmp_gd_octets
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " bytes received in an ICMP segment with checksum errors\n",
      macgrp->rxicmp_err_octets
      );
  }

  if ( eno == 0 ) {
    printf( "\nInterrupt counts:\n" );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " receive interrputs\n",
                         self->stats.dma_irq_counts.receive
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " transmit interrupts\n",
                         self->stats.dma_irq_counts.transmit
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " tx jabber interrupts\n",
                         self->stats.dma_irq_counts.tx_jabber
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " rx overflow interrupts\n",
                         self->stats.dma_irq_counts.rx_overflow
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " rx early interrupts\n",
                         self->stats.dma_irq_counts.rx_early
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " rx buffer unavailable interrupts\n",
                         self->stats.dma_irq_counts.rx_buf_unav
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " rx process stopped interrupts\n",
                         self->stats.dma_irq_counts.rx_process_stopped
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " rx watchdog interrupts\n",
                         self->stats.dma_irq_counts.rx_watchdog
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " tx early interrupts\n",
                         self->stats.dma_irq_counts.tx_early
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " tx buffer unavailable interrupts\n",
                         self->stats.dma_irq_counts.tx_buf_unav
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " tx process stopped interrupts\n",
                         self->stats.dma_irq_counts.tx_process_stopped
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " tx underflow interrupts\n",
                         self->stats.dma_irq_counts.tx_underflow
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " fatal bus error interrupts\n",
                         self->stats.dma_irq_counts.fatal_bus_error
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " unhandled interrupts\n",
                         self->stats.dma_irq_counts.unhandled
                         );

    printf( "\nRX DMA status counts:\n" );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " CRC errors\n",
                         self->stats.desc_status_counts_rx.crc_error
                         );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " late collision when receing in half duplex mode\n",
      self->stats.desc_status_counts_rx.late_collision
      );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " giant frame or timestamp or checksum error\n",
      self->stats.desc_status_counts_rx.giant_frame
      );

    if ( self->stats.desc_status_counts_rx.watchdog_timeout != 0
         || self->stats.desc_status_counts_rx.receive_error != 0 ) {
      printf( "  IP Header or IP Payload:\n" );
      DWMAC_PRINT_COUNTER( "%" PRIu32 " receive watchdog timeout\n",
                           self->stats.desc_status_counts_rx.watchdog_timeout
                           );
      DWMAC_PRINT_COUNTER( "%" PRIu32 " receive error\n",
                           self->stats.desc_status_counts_rx.receive_error
                           );
    }

    DWMAC_PRINT_COUNTER( "%" PRIu32 " buffer overflows in MTL\n",
                         self->stats.desc_status_counts_rx.overflow_error
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " descriptor buffers too small\n",
                         self->stats.desc_status_counts_rx.descriptor_error
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " length errors\n",
                         self->stats.desc_status_counts_rx.length_error
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " dribble bit errors\n",
                         self->stats.desc_status_counts_rx.dribble_bit_error
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " vlan tags\n",
                         self->stats.desc_status_counts_rx.vlan_tag
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " ethernet frames\n",
                         self->stats.desc_status_counts_rx.ethernet_frames
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " destination address filter failures\n",
                         self->stats.desc_status_counts_rx.dest_addr_fail
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " source addresss filter failures\n",
                         self->stats.desc_status_counts_rx.source_addr_fail
                         );

    printf( "\nTX DMA status counts:\n" );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " jabber time-outs\n",
                         self->stats.desc_status_counts_tx.jabber
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " frame flushes\n",
                         self->stats.desc_status_counts_tx.frame_flushed
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " losses of carrier\n",
                         self->stats.desc_status_counts_tx.losscarrier
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " no carriers\n",
                         self->stats.desc_status_counts_tx.no_carrier
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " excessive collisions\n",
                         self->stats.desc_status_counts_tx.excessive_collisions
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " excessive deferrals\n",
                         self->stats.desc_status_counts_tx.excessive_deferral
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " underfolw errors\n",
                         self->stats.desc_status_counts_tx.underflow
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " IP header error\n",
                         self->stats.desc_status_counts_tx.ip_header_error
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " payload error\n",
                         self->stats.desc_status_counts_rx.source_addr_fail
                         );
    DWMAC_PRINT_COUNTER(
      "%" PRIu32 " MAC defers before transmission because of the presence of carrier\n",
      self->stats.desc_status_counts_tx.deferred
      );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " VLAN frames\n",
                         self->stats.desc_status_counts_tx.vlan
                         );

    printf( "\nRX frame counts:\n" );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " frames with errors\n",
                         self->stats.frame_counts_rx.errors
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " frames dropped\n",
                         self->stats.frame_counts_rx.dropped
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " frames good\n",
                         self->stats.frame_counts_rx.frames_good
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " bytes in good frames\n",
                         self->stats.frame_counts_rx.bytes_good
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " frames good or bad\n",
                         self->stats.frame_counts_rx.frames
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " DMA suspended\n",
                         self->stats.frame_counts_rx.dma_suspended
                         );

    printf( "\nTX frame counts:\n" );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " frames received from network stack\n",
                         self->stats.frame_counts_tx.frames_from_stack
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " frames tranmitted to DMA\n",
                         self->stats.frame_counts_tx.frames_to_dma
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " packets tranmitted to DMA\n",
                         self->stats.frame_counts_tx.packets_to_dma
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " bytes tranmitted to DMA\n",
                         self->stats.frame_counts_tx.bytes_to_dma
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " packet buffers regained from DMA\n",
                         self->stats.frame_counts_tx.packets_tranmitted_by_DMA
                         );
    DWMAC_PRINT_COUNTER( "%" PRIu32 " packet errors\n",
                         self->stats.frame_counts_tx.packet_errors
                         );

    printf( "\n" );

#ifdef RTEMS_DEBUG
    {
      const dwmac_common_desc_ops *DESC_OPS =
        (const dwmac_common_desc_ops *) self->CFG->DESC_OPS->ops;

      ( DESC_OPS->print_rx_desc )(
        self->dma_rx,
        (unsigned int) self->bsd_config->xbuf_count
        );

      ( DESC_OPS->print_tx_desc )(
        self->dma_tx,
        (unsigned int) self->bsd_config->xbuf_count
        );
    }
#endif /* RTEMS_DEBUG */
  }

  return eno;
}

static inline unsigned int dwmac_increment(
  const unsigned int value,
  const unsigned int cycle )
{
  if ( value < cycle ) {
    return value + 1;
  } else {
    return 0;
  }
}

/* Receive task
 * It handles receiving frames */
static void dwmac_task_rx( void *arg )
{
  dwmac_common_context         *self           = arg;
  dwmac_common_rx_frame_counts *counts         = &self->stats.frame_counts_rx;
  const unsigned int            INDEX_MAX      =
    (unsigned int) self->bsd_config->rbuf_count - 1U;
  size_t                        frame_len_last = 0;
  const dwmac_common_desc_ops  *DESC_OPS       =
    (const dwmac_common_desc_ops *) self->CFG->DESC_OPS->ops;
  size_t                        segment_size;


  while ( true ) {
    rtems_event_set   events;
    rtems_status_code sc = rtems_bsdnet_event_receive(
      DWMAC_COMMON_EVENT_TASK_INIT
      | DWMAC_COMMON_EVENT_TASK_STOP
      | DWMAC_COMMON_EVENT_RX_FRAME_RECEIVED,
      RTEMS_EVENT_ANY | RTEMS_WAIT,
      RTEMS_NO_TIMEOUT,
      &events
      );
    assert( sc == RTEMS_SUCCESSFUL );

    /* Stop the task */
    if ( ( events & DWMAC_COMMON_EVENT_TASK_STOP ) != 0 ) {
      dwmac_core_dma_stop_rx( self );
      dwmac_disable_irq_rx( self );

      /* Release all tx mbufs at the risk of data loss */
      ( DESC_OPS->release_rx_bufs )( self );

      dwmac_control_request_complete( self );

      /* Return to events reception without re-enabling the interrupts
       * The task needs a re-initialization to to resume work */
      continue;
    }

    /* Ininitialize / Re-initialize reception handling */
    if ( ( events & DWMAC_COMMON_EVENT_TASK_INIT ) != 0 ) {
      unsigned int index;
      dwmac_core_dma_stop_rx( self );
      ( DESC_OPS->release_rx_bufs )( self );

      for ( index = 0; index <= INDEX_MAX; ++index ) {
        self->mbuf_addr_rx[index] = ( DESC_OPS->alloc_data_buf )( self );
        assert( self->mbuf_addr_rx[index] != NULL );
        ( DESC_OPS->init_rx_desc )( self, index );
      }

      self->idx_rx   = 0;
      frame_len_last = 0;

      /* Clear our interrupt statuses */
      dwmac_core_reset_dma_irq_status_rx( self );

      dwmac_core_dma_start_rx( self );

      dwmac_control_request_complete( self );
      events = events
               & (rtems_event_set) ( ~DWMAC_COMMON_EVENT_RX_FRAME_RECEIVED );
    }

    /* Handle the reception of one or more frames */
    if ( ( events & DWMAC_COMMON_EVENT_RX_FRAME_RECEIVED ) != 0 ) {
      unsigned int idx = self->idx_rx;

      /* Only handle frames for which we own the DMA descriptor */
      while ( ( DESC_OPS->am_i_rx_owner )( self, idx ) ) {
        struct mbuf                 *p_m_new;
        dwmac_common_rx_frame_status status;
        unsigned int                 idx_next = dwmac_increment(
          self->idx_rx, INDEX_MAX );

        /* Assign the next index (with wrap around) */
        self->idx_rx = idx_next;

        /* read the status of the incoming frame */
        status = ( DESC_OPS->rx_status )( self, idx );

        if ( status == DWMAC_COMMON_RX_FRAME_STATUS_DISCARD
             || status == DWMAC_COMMON_RX_FRAME_STATUS_CSUM_NONE ) {
          DWMAC_PRINT_DBG(
            "rx discarded: %02u\n",
            idx
            );
          ++counts->errors;

          /* simply re-initialize the DMA descriptor */
          ( DESC_OPS->init_rx_desc )( self, idx );
        } else if ( status == DWMAC_COMMON_RX_FRAME_STATUS_LLC_SNAP ) {
          DWMAC_PRINT_DBG(
            "rx dropped llc snap: %02u\n",
            idx
            );
          ++counts->dropped;

          /* simply re-initialize the DMA descriptor */
          ( DESC_OPS->init_rx_desc )( self, idx );
        } else {
          /* Before actually handling the valid frame, make sure we get a new
           * mbuf */
          p_m_new = ( DESC_OPS->alloc_data_buf )( self );

          if ( p_m_new != NULL ) {
            bool         is_first_seg;
            bool         is_last_seg;
            struct mbuf *p_m;
            uint32_t     frame_len = ( DESC_OPS->get_rx_frame_len )(
              self, idx );

            /* frame_len is the cummulated size of all segments of a frame.
             * But what we need is the size of the single segment */
            is_first_seg = ( DESC_OPS->is_first_rx_segment )( self, idx );
            is_last_seg  = ( DESC_OPS->is_last_rx_segment )( self, idx );

            if ( is_first_seg ) {
              segment_size = frame_len;
            } else {
              segment_size = frame_len - frame_len_last;
            }

            frame_len_last          = frame_len;

            p_m                     = self->mbuf_addr_rx[idx];
            self->mbuf_addr_rx[idx] = p_m_new;

            /* Initialize the DMA descriptor with the new mbuf */
            ( DESC_OPS->init_rx_desc )( self, idx );

            if ( p_m != NULL ) {
              /* Ethernet header */
              struct ether_header *eh = mtod( p_m, struct ether_header * );
              int                  sz = (int) segment_size;

              rtems_cache_invalidate_multiple_data_lines(
                eh, segment_size );

              /* Discard Ethernet header and CRC */
              if ( is_last_seg ) {
                sz -= ( ETHER_HDR_LEN + ETHER_CRC_LEN );
              } else {
                sz -= ETHER_HDR_LEN;
              }

              /* Update mbuf */
              p_m->m_len        = sz;
              p_m->m_pkthdr.len = sz;
              p_m->m_data       = mtod( p_m, char * ) + ETHER_HDR_LEN;

              DWMAC_COMMON_DSB();

              DWMAC_PRINT_DBG(
                "rx: %02u: %u %s%s\n",
                idx,
                segment_size,
                ( is_first_seg ) ? ( "F" ) : ( "" ),
                ( is_last_seg ) ? ( "L" ) : ( "" )
                );

              ether_input( &self->arpcom.ac_if, eh, p_m );

              ++counts->frames_good;
              counts->bytes_good += (uint32_t) p_m->m_len;
            } else {
              DWMAC_PRINT_DBG( "rx: %s: Inconsistent Rx descriptor chain\n",
                               self->arpcom.ac_if.if_name );
              ++counts->dropped;
            }
          } else {
            /* We have to discard the received frame because we did not get a new
             * mbuf to replace the frame's mbuf. */
            ++counts->dropped;

            /* simply re-initialize the DMA descriptor with the existing mbuf */
            ( DESC_OPS->init_rx_desc )( self, idx );
            DWMAC_PRINT_DBG(
              "rx dropped: %02u\n",
              idx
              );
          }
        }

        if ( ( self->dmagrp->status & DMAGRP_STATUS_RU ) != 0 ) {
          /* The receive DMA is suspended and needs to get restarted */
          dwmac_core_dma_restart_rx( self );
          DWMAC_PRINT_DBG(
            "rx DMA restart: %02u\n",
            idx
            );
          ++counts->dma_suspended;
          self->dmagrp->status = DMAGRP_STATUS_RU;
        }

        idx = idx_next;
        ++counts->frames;
      }

      /* Clear interrupt */
      self->dmagrp->status = DMAGRP_STATUS_RI;
    }

    /* Re-enable the IRQs */
    dwmac_enable_irq_rx( self );
  }
}

static struct mbuf *dwmac_next_fragment(
  struct ifnet *ifp,
  struct mbuf  *m,
  bool         *is_first,
  bool         *is_last,
  size_t       *size ) {
  struct mbuf *n  = NULL;
  int          sz = 0;


  while ( true ) {
    if ( m == NULL ) {
      /* Dequeue first fragment of the next frame */
      IF_DEQUEUE( &ifp->if_snd, m );

      /* Empty queue? */
      if ( m == NULL ) {
        return m;
      }

      /* The sum of all fragments lengths must fit into one
       * ethernet transfer unit */
      assert( ETHER_MAX_LEN >= m->m_pkthdr.len );
      *is_first = true;
    }

    /* Get fragment size */
    sz = m->m_len;

    if ( sz > 0 ) {
      /* Now we have a not empty fragment */
      break;
    } else {
      /* Discard empty fragments */
      m = m_free( m );
    }
  }

  /* Set fragment size */
  *size = (size_t) sz;

  /* Discard empty successive fragments */
  n = m->m_next;

  while ( n != NULL && n->m_len <= 0 ) {
    n = m_free( n );
  }

  m->m_next = n;

  /* Is our fragment the last in the frame? */
  if ( n == NULL ) {
    *is_last = true;
  } else {
    *is_last = false;
  }

  return m;
}

static int dwmac_update_autonegotiation_params( dwmac_common_context *self )
{
  int      eno      = 0;
  uint32_t value    = self->macgrp->mac_configuration;
  int      media    = 0;
  bool     media_ok = dwmac_if_media_status(
    self, &media, self->CFG->MDIO_BUS_ADDR );


  if ( media_ok ) {
    /* only ethernet supported, so far */
    if ( IFM_ETHER == IFM_TYPE( media ) ) {
      if ( IFM_NONE != IFM_SUBTYPE( media ) ) {
        if ( IFM_FDX & media ) {
          /* Enable duplex mode */
          value |= MACGRP_MAC_CONFIGURATION_DM;
        } else {
          /* Disable duplex mode */
          value &= ~MACGRP_MAC_CONFIGURATION_DM;
        }

        switch ( IFM_SUBTYPE( media ) ) {
          case IFM_10_T:

            /* Set RMII/RGMII speed to 10Mbps */
            value &= ~MACGRP_MAC_CONFIGURATION_FES;

            /* MII 10/100 Mbps */
            value |= MACGRP_MAC_CONFIGURATION_PS;
            break;
          case IFM_100_TX:

            /* Set RMII/RGMII speed to 100Mbps */
            value |= MACGRP_MAC_CONFIGURATION_FES;

            /* MII 10/100 Mbps */
            value |= MACGRP_MAC_CONFIGURATION_PS;
            break;
          case IFM_1000_T:

            /* RMII/RGMII speed irrelevant for 1000baseT */
            value &= ~MACGRP_MAC_CONFIGURATION_FES;

            /* GMII 1000 Mbps */
            value &= ~MACGRP_MAC_CONFIGURATION_PS;
            break;
          default:
            eno = ENOTSUP;
            break;
        }
      } else {
        eno = ENOTSUP;
      }
    } else {
      eno = ENOTSUP;
    }
  } else {
    eno = ENOTSUP;
  }

  if ( eno == 0 ) {
    self->macgrp->mac_configuration = value;
  }

  return eno;
}

/* Transmit task
 * It handles transmitting frames */
static void dwmac_task_tx( void *arg )
{
  dwmac_common_context        *self               = arg;
  unsigned int                 idx_transmit       = 0;
  unsigned int                 idx_transmit_first = 0;
  unsigned int                 idx_transmitted    = 0;
  unsigned int                 idx_release        = 0;
  struct mbuf                 *p_m                = NULL;
  bool                         is_first           = false;
  bool                         is_last            = false;
  size_t                       size               = 0;
  const unsigned int           INDEX_MAX          =
    (unsigned int) self->bsd_config->xbuf_count - 1U;
  const dwmac_common_dma_ops  *DMA_OPS            =
    (const dwmac_common_dma_ops *) self->CFG->MAC_OPS->dma;
  const dwmac_common_desc_ops *DESC_OPS           =
    (const dwmac_common_desc_ops *) self->CFG->DESC_OPS->ops;
  const dwmac_callback_cfg    *CALLBACK           = &self->CFG->CALLBACK;


  while ( true ) {
    rtems_event_set   events = 0;
    rtems_status_code sc     = rtems_bsdnet_event_receive(
      DWMAC_COMMON_EVENT_TASK_INIT
      | DWMAC_COMMON_EVENT_TASK_STOP
      | DWMAC_COMMON_EVENT_TX_TRANSMIT_FRAME
      | DWMAC_COMMON_EVENT_TX_FRAME_TRANSMITTED
      | DWMAC_COMMON_EVENT_TX_BUMP_UP_DMA_THRESHOLD
      | DWMAC_COMMON_EVENT_TX_PHY_STATUS_CHANGE,
      RTEMS_EVENT_ANY | RTEMS_WAIT,
      RTEMS_NO_TIMEOUT,
      &events
      );
    assert( sc == RTEMS_SUCCESSFUL );

    while( events != 0 ) {
      /* Handle a status change of the ethernet PHY */
      if ( ( events & DWMAC_COMMON_EVENT_TX_PHY_STATUS_CHANGE ) != 0 ) {
        events &= ~DWMAC_COMMON_EVENT_TX_PHY_STATUS_CHANGE;
        dwmac_common_phy_status_counts *counts     =
          &self->stats.phy_status_counts;
        dwmac_phy_event                 phy_events = 0;
        int                             eno;

        /* Get tripped PHY events */
        eno = CALLBACK->phy_events_get(
          self->arg,
          &phy_events
          );

        if ( eno == 0 ) {
          /* Clear the PHY events */
          eno = CALLBACK->phy_event_clear( self->arg );
        }

        if ( eno == 0 ) {
          if ( ( phy_events & PHY_EVENT_LINK_DOWN ) != 0 ) {
            ++counts->link_down;
          }

          if ( ( phy_events & PHY_EVENT_LINK_UP ) != 0 ) {
            ++counts->link_up;

            /* A link up events means that we have a new connection.
            * Thus the autonegotiation paremeters must get updated */
            (void) dwmac_update_autonegotiation_params( self );
          }
        }

        assert( eno == 0 );
      }

      /* Stop the task */
      if ( ( events & DWMAC_COMMON_EVENT_TASK_STOP ) != 0 ) {
        dwmac_core_dma_stop_tx( self );
        dwmac_disable_irq_tx_all( self );

        /* Release all tx mbufs at the risk of data loss */
        ( DESC_OPS->release_tx_bufs )( self );

        dwmac_control_request_complete( self );

        /* Return to events reception without re-enabling the interrupts
        * The task needs a re-initialization to to resume work */
        events = 0;
        continue;
      }

      /* Ininitialize / Re-initialize transmission handling */
      if ( ( events & DWMAC_COMMON_EVENT_TASK_INIT ) != 0 ) {
        events &= ~DWMAC_COMMON_EVENT_TASK_INIT;
        (void) dwmac_update_autonegotiation_params( self );
        dwmac_core_dma_stop_tx( self );
        ( DESC_OPS->release_tx_bufs )( self );
        idx_transmit       = 0;
        idx_transmit_first = 0;
        idx_transmitted    = 0;
        idx_release        = 0;
        p_m                = NULL;
        is_first           = false;
        is_last            = false;
        size               = 0;
        ( DESC_OPS->init_tx_desc )( self );
        dwmac_core_dma_start_tx( self );
        dwmac_core_dma_restart_tx( self );

        /* Clear our interrupt statuses */
        dwmac_core_reset_dma_irq_status_tx( self );
        dwmac_enable_irq_tx_default( self );

        dwmac_control_request_complete( self );
      }

      /* Try to bump up the dma threshold due to a failure */
      if ( ( events & DWMAC_COMMON_EVENT_TX_BUMP_UP_DMA_THRESHOLD ) != 0 ) {
        events &= ~DWMAC_COMMON_EVENT_TX_BUMP_UP_DMA_THRESHOLD;
        if ( self->dma_threshold_control
            != DWMAC_COMMON_DMA_MODE_STORE_AND_FORWARD
            && self->dma_threshold_control <= 256 ) {
          self->dma_threshold_control += 64;
          ( DMA_OPS->dma_mode )(
            self,
            self->dma_threshold_control,
            DWMAC_COMMON_DMA_MODE_STORE_AND_FORWARD
            );
        }
      }

      /* Handle one or more transmitted frames */
      if ( ( events & DWMAC_COMMON_EVENT_TX_FRAME_TRANSMITTED ) != 0 ) {
        events &= ~DWMAC_COMMON_EVENT_TX_FRAME_TRANSMITTED;
        dwmac_common_tx_frame_counts *counts = &self->stats.frame_counts_tx;
        dwmac_disable_irq_tx_transmitted( self );

        /* Next index to be transmitted */
        unsigned int idx_transmitted_next = dwmac_increment(
          idx_transmitted, INDEX_MAX );

        /* Free consumed fragments */
        if( idx_release != idx_transmitted_next
            && ( DESC_OPS->am_i_tx_owner )( self, idx_release ) ) {
          while ( idx_release != idx_transmitted_next
                  && ( DESC_OPS->am_i_tx_owner )( self, idx_release ) ) {
            /* Status handling per packet */
            if ( ( DESC_OPS->get_tx_ls )( self, idx_release ) ) {
              int status = ( DESC_OPS->tx_status )(
                self, idx_release
                );

              if ( status == 0 ) {
                ++counts->packets_tranmitted_by_DMA;
              } else {
                ++counts->packet_errors;
              }
            }

            DWMAC_PRINT_DBG(
              "tx: release %u\n",
              idx_release
              );

            /* Release the DMA descriptor */
            ( DESC_OPS->release_tx_desc )( self, idx_release );

            /* Release mbuf */
            m_free( self->mbuf_addr_tx[idx_release] );
            self->mbuf_addr_tx[idx_release] = NULL;

            /* Next release index */
            idx_release = dwmac_increment(
              idx_release, INDEX_MAX );
          }
          if ( ( self->arpcom.ac_if.if_flags & IFF_OACTIVE ) != 0 ) {
            /* The last tranmission has been incomplete
            * (for example due to lack of DMA descriptors).
            * Continue it now! */
            events |= DWMAC_COMMON_EVENT_TX_TRANSMIT_FRAME;
          }
        } else {
          /* Clear transmit interrupt status */
          self->dmagrp->status = DMAGRP_STATUS_TI;
          /* Get re-activated by the next interrupt */
          dwmac_enable_irq_tx_transmitted( self );
        }
      }

      /* There are one or more frames to be transmitted. */
      if ( ( events & DWMAC_COMMON_EVENT_TX_TRANSMIT_FRAME ) != 0 ) {
        events &= ~DWMAC_COMMON_EVENT_TX_TRANSMIT_FRAME;
        dwmac_common_tx_frame_counts *counts = &self->stats.frame_counts_tx;

        if ( p_m != NULL ) {
          /* This frame will get re-counted */
          --counts->frames_from_stack;
        }

        while ( true ) {
          unsigned int idx = dwmac_increment(
            idx_transmit, INDEX_MAX );

          p_m = dwmac_next_fragment(
            &self->arpcom.ac_if,
            p_m,
            &is_first,
            &is_last,
            &size
            );

          /* New fragment? */
          if ( p_m != NULL ) {
            ++counts->frames_from_stack;

            /* Queue full? */
            if ( idx == idx_release ) {
              DWMAC_PRINT_DBG( "tx: full queue: 0x%08x\n", p_m );

              /* The queue is full, wait for transmitted interrupt */
              break;
            }

            /* Set the transfer data */
            rtems_cache_flush_multiple_data_lines(
              mtod( p_m, const void * ),
              size
              );

            ( DESC_OPS->prepare_tx_desc )(
              self,
              idx_transmit,
              is_first,
              size,
              mtod( p_m, const void * )
              );
            self->mbuf_addr_tx[idx_transmit] = p_m;

            ++counts->frames_to_dma;
            counts->bytes_to_dma += size;
            DWMAC_PRINT_DBG(
              "tx: %02" PRIu32 ": %u %s%s\n",
              idx_transmit, size,
              ( is_first != false ) ? ( "F" ) : ( "" ),
              ( is_last != false ) ? ( "L" ) : ( "" )

              );

            if ( is_first ) {
              idx_transmit_first = idx_transmit;
              is_first           = false;
            } else {
              /* To avoid race condition */
              ( DESC_OPS->release_tx_ownership )( self, idx_transmit );
            }

            if ( is_last ) {
              /* Interrupt on completition only for the latest fragment */
              ( DESC_OPS->close_tx_desc )( self, idx_transmit );

              /* To avoid race condition */
              ( DESC_OPS->release_tx_ownership )( self, idx_transmit_first );
              idx_transmitted = idx_transmit;

              if ( ( self->dmagrp->status & DMAGRP_STATUS_TU ) != 0 ) {
                /* Re-enable the tranmit DMA */
                dwmac_core_dma_restart_tx( self );
                DWMAC_PRINT_DBG(
                  "tx DMA restart: %02u\n",
                  idx_transmit_first
                  );
              }
            }

            /* Next transmit index */
            idx_transmit = idx;

            if ( is_last ) {
              ++counts->packets_to_dma;
            }

            /* Next fragment of the frame */
            p_m = p_m->m_next;
          } else {
            /* Nothing to transmit */
            break;
          }
        }

        /* No more packets and fragments? */
        if ( p_m == NULL ) {
          /* Interface is now inactive */
          self->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
        } else {
          /* There are more packets pending to be sent,
          * but we have run out of DMA descriptors.
          * We will continue sending once descriptors
          * have been freed due to a transmitted interupt */
          DWMAC_PRINT_DBG( "tx: transmission incomplete\n" );
          events |= DWMAC_COMMON_EVENT_TX_FRAME_TRANSMITTED;
        }

        /* TODO: Add handling */
      }

      DWMAC_PRINT_DBG( "tx: enable transmit interrupts\n" );
    }
  }
}

/* Major driver initialization method. Gets called
 * by the network stack automatically */
static void dwmac_if_interface_init( void *arg )
{
  (void) arg;
}

static void dwmac_if_set_promiscous_mode(
  dwmac_common_context *self,
  const bool            enabled )
{
  if ( enabled ) {
    /* Enable promiscuos mode */
    self->macgrp->mac_frame_filter |= MACGRP_MAC_FRAME_FILTER_PR;
  } else {
    /* Hash filter or perfect match */
    self->macgrp->mac_frame_filter &= ~MACGRP_MAC_FRAME_FILTER_PR;
    self->macgrp->mac_frame_filter &= ~MACGRP_MAC_FRAME_FILTER_HUC;
    self->macgrp->mac_frame_filter |= MACGRP_MAC_FRAME_FILTER_HPF;
  }
}

static int dwmac_create_dma_desc_rings( dwmac_common_context *self )
{
  int                          eno      = 0;
  const dwmac_common_desc_ops *DESC_OPS =
    (const dwmac_common_desc_ops *) self->CFG->DESC_OPS->ops;


  /* create and initialize the Rx/Tx descriptors */
  eno = ( DESC_OPS->create_rx_desc )( self );

  if ( eno == 0 ) {
    eno = ( DESC_OPS->create_tx_desc )( self );
  }

  return eno;
}

static int dwmac_destroy_dma_desc_rings( dwmac_common_context *self )
{
  int                          eno      = 0;
  const dwmac_common_desc_ops *DESC_OPS =
    (const dwmac_common_desc_ops *) self->CFG->DESC_OPS->ops;


  /* Free the Rx/Tx descriptors and the data buffers */
  eno = ( DESC_OPS->destroy_rx_desc )( self );

  if ( eno == 0 ) {
    eno = ( DESC_OPS->destroy_tx_desc )( self );
  }

  return eno;
}

static int dwmac_init_dma_engine( dwmac_common_context *self )
{
  uint32_t                     pbl                  =
    DWMAC_DMA_BUS_MODE_PBL_DEFAULT;
  uint32_t                     fixed_burst          =
    DWMAC_DMA_BUS_MODE_FB_DEFAULT;
  uint32_t                     mixed_burst          =
    DWMAC_DMA_BUS_MODE_MIXED_BURSTS_DEFAULT;
  uint32_t                     burst_len_4_support  =
    DWMAC_DMA_AXI_BURST_LENGTH_4_DEFAULT;
  uint32_t                     burst_len_8_support  =
    DWMAC_DMA_AXI_BURST_LENGTH_8_DEFAULT;
  uint32_t                     burst_len_16_support =
    DWMAC_DMA_AXI_BURST_LENGTH_16_DEFAULT;
  uint32_t                     burst_boundary       =
    DWMAC_DMA_AXI_BURST_BOUNDARY_DEFAULT;
  const dwmac_common_dma_ops  *DMA_OPS              =
    (const dwmac_common_dma_ops *) self->CFG->MAC_OPS->dma;
  const dwmac_common_desc_ops *DESC_OPS             =
    (const dwmac_common_desc_ops *) self->CFG->DESC_OPS->ops;


  /* The DMA configuration is optional */
  if ( self->CFG->DMA_CFG != NULL ) {
    const dwmac_dma_cfg *DMA_CFG = self->CFG->DMA_CFG;
    pbl                  = DMA_CFG->bus_mode_burst_length;
    fixed_burst          = DMA_CFG->bus_mode_burst_mode;
    mixed_burst          = DMA_CFG->bus_mode_burst_mixed;
    burst_len_4_support  = DMA_CFG->axi_burst_length_4_support;
    burst_len_8_support  = DMA_CFG->axi_burst_length_8_support;
    burst_len_16_support = DMA_CFG->axi_burst_length_16_support;
    burst_boundary       = DMA_CFG->axi_burst_boundary;
  }

  return ( DMA_OPS->init )(
    self,
    pbl,
    fixed_burst,
    mixed_burst,
    ( DESC_OPS->use_enhanced_descs )( self ),
    burst_len_4_support,
    burst_len_8_support,
    burst_len_16_support,
    burst_boundary,
    &self->dma_tx[0],
    &self->dma_rx[0]
    );
}

static void dwmac_dma_operation_mode( dwmac_common_context *self )
{
  const dwmac_common_dma_ops *DMA_OPS =
    (const dwmac_common_dma_ops *) self->CFG->MAC_OPS->dma;


  if ( ( self->dmagrp->hw_feature & DMAGRP_HW_FEATURE_TXOESEL ) != 0 ) {
    ( DMA_OPS->dma_mode )(
      self,
      DWMAC_COMMON_DMA_MODE_STORE_AND_FORWARD,
      DWMAC_COMMON_DMA_MODE_STORE_AND_FORWARD
      );
    self->dma_threshold_control = DWMAC_COMMON_DMA_MODE_STORE_AND_FORWARD;
  } else {
    ( DMA_OPS->dma_mode )(
      self,
      self->dma_threshold_control,
      DWMAC_COMMON_DMA_MODE_STORE_AND_FORWARD
      );
  }
}

static void dwmac_mmc_setup( dwmac_common_context *self )
{
  /* Mask MMC irq, counters are managed in HW and registers
   * are not cleared on each READ eventually. */

  /* No MAC Management Counters available */
  assert( ( self->dmagrp->hw_feature & DMAGRP_HW_FEATURE_MMCSEL ) != 0 );

  if ( ( self->dmagrp->hw_feature & DMAGRP_HW_FEATURE_MMCSEL ) != 0 ) {
    self->macgrp->mmc_control = MACGRP_MMC_CONTROL_CNTRST;
  }
}

static int dwmac_if_up_or_down(
  dwmac_common_context *self,
  const bool            up )
{
  int                          eno      = 0;
  rtems_status_code            sc       = RTEMS_SUCCESSFUL;
  struct ifnet                *ifp      = &self->arpcom.ac_if;
  const dwmac_callback_cfg    *CALLBACK = &self->CFG->CALLBACK;
  const dwmac_common_core_ops *CORE_OPS =
    (const dwmac_common_core_ops *) self->CFG->MAC_OPS->core;


  if ( up && self->state == DWMAC_COMMON_STATE_DOWN ) {
    eno = CALLBACK->nic_enable( self->arg );

    if ( eno == 0 ) {
      eno = CALLBACK->phy_enable( self->arg );
    }

    if ( eno == 0 ) {
      eno = CALLBACK->phy_event_enable(
        self->arg,
        PHY_EVENT_LINK_DOWN
        | PHY_EVENT_LINK_UP
        );
    }

    if ( eno == 0 ) {
      eno = dwmac_create_dma_desc_rings( self );
    }

    if ( eno == 0 ) {
      eno = dwmac_init_dma_engine( self );
    }

    if ( eno == 0 ) {
      /* Copy the MAC addr into the HW  */
      ( CORE_OPS->set_umac_addr )( self, self->arpcom.ac_enaddr, 0 );

      eno = ( CALLBACK->bus_setup )( self->arg );
    }

    if ( eno == 0 ) {
      /* Initialize the MAC Core */
      ( CORE_OPS->core_init )( self );

      /* Enable the MAC Rx/Tx */
      dwmac_core_set_mac( self, true );

      /* Set the HW DMA mode and the COE */
      dwmac_dma_operation_mode( self );

      /* Set up mmc counters */
      dwmac_mmc_setup( self );

#ifdef BSP_FEATURE_IRQ_EXTENSION
      /* Install interrupt handler */
      sc = rtems_interrupt_handler_install(
        self->CFG->IRQ_EMAC,
        "Ethernet",
        RTEMS_INTERRUPT_UNIQUE,
        dwmac_core_dma_interrupt,
        self
        );
#else
      sc = RTEMS_NOT_IMPLEMENTED;
#endif
      eno = rtems_status_code_to_errno( sc );
    }

    if ( eno == 0 ) {
      /* Start the ball rolling ... */
      eno = dwmac_control_request(
        self, self->task_id_tx, DWMAC_COMMON_EVENT_TASK_INIT );
    }

    if ( eno == 0 ) {
      eno = dwmac_control_request(
        self, self->task_id_rx, DWMAC_COMMON_EVENT_TASK_INIT );
    }

    if ( eno == 0 ) {
      dwmac_core_dma_start_rx( self );

      /* Start the phy */
      eno = ( CALLBACK->phy_start )( self->arg );
    }

    if ( eno == 0 ) {
      self->state = DWMAC_COMMON_STATE_UP;
    } else {
      ifp->if_flags &= ~IFF_UP;
      (void) dwmac_control_request(
        self, self->task_id_tx, DWMAC_COMMON_EVENT_TASK_STOP );
      (void) dwmac_control_request(
        self, self->task_id_rx, DWMAC_COMMON_EVENT_TASK_STOP );
#ifdef BSP_FEATURE_IRQ_EXTENSION
      (void) rtems_interrupt_handler_remove(
        self->CFG->IRQ_EMAC,
        dwmac_core_dma_interrupt,
        self
        );
#endif
      (void) ( CALLBACK->phy_stop )( self->arg );
      (void) ( CALLBACK->phy_disable )( self->arg );

      /* Disable the MAC Rx/Tx */
      dwmac_core_set_mac( self, false );
      (void) ( CALLBACK->nic_disable )( self->arg );
      (void) dwmac_destroy_dma_desc_rings( self );
    }
  } else if ( !up && self->state == DWMAC_COMMON_STATE_UP ) {
    if ( eno == 0 ) {
      eno = dwmac_control_request(
        self, self->task_id_tx, DWMAC_COMMON_EVENT_TASK_STOP );
    }

    if ( eno == 0 ) {
      eno = dwmac_control_request(
        self, self->task_id_rx, DWMAC_COMMON_EVENT_TASK_STOP );
    }

    if ( eno == 0 ) {
#ifdef BSP_FEATURE_IRQ_EXTENSION
      sc = rtems_interrupt_handler_remove(
        self->CFG->IRQ_EMAC,
        dwmac_core_dma_interrupt,
        self
        );
#else
      sc = RTEMS_NOT_IMPLEMENTED;
#endif
      eno = rtems_status_code_to_errno( sc );
    }

    if ( eno == 0 ) {
      eno = ( CALLBACK->phy_stop )( self->arg );
    }

    if ( eno == 0 ) {
      eno = ( CALLBACK->phy_disable )( self->arg );
    }

    if ( eno == 0 ) {
      /* Disable the MAC Rx/Tx */
      dwmac_core_set_mac( self, false );

      eno = CALLBACK->nic_disable( self->arg );
    }

    if ( eno == 0 ) {
      eno = dwmac_destroy_dma_desc_rings( self );
    }

    if ( eno == 0 ) {
      /* Reset counters to be printed */
      memset( &self->stats, 0, sizeof( dwmac_common_stats ) );

      /* Change state */
      self->state = DWMAC_COMMON_STATE_DOWN;
    }
  }

  return eno;
}

/* Get commands executed by the driver. This method gets called
 * automatically from the network stack */
static int dwmac_if_interface_ioctl(
  struct ifnet   *ifp,
  ioctl_command_t cmd,
  caddr_t         data )
{
  dwmac_common_context *self = ifp->if_softc;
  int                   eno  = 0;


  switch ( cmd ) {
    case SIOCGIFMEDIA:
    case SIOCSIFMEDIA:
      rtems_mii_ioctl( &self->mdio, self, cmd, (int *) data );
      break;
    case SIOCGIFADDR:
    case SIOCSIFADDR:
      ether_ioctl( ifp, cmd, data );
      break;
    case SIOCSIFFLAGS:
      eno = dwmac_if_up_or_down( self, ( ifp->if_flags & IFF_UP ) != 0 );

      if ( eno == 0 && ( ifp->if_flags & IFF_UP ) != 0 ) {
        dwmac_if_set_promiscous_mode( self, ( ifp->if_flags & IFF_PROMISC )
                                      != 0 );
      } else {
        assert( eno == 0 );
      }

      break;
    case SIOCADDMULTI:
    case SIOCDELMULTI:
#ifndef COMMENTED_OUT
      eno = ENOTSUP;
#else /* COMMENTED_OUT */
      {
        struct ifreq                *ifr      = (struct ifreq *) data;
        const dwmac_common_core_ops *CORE_OPS =
          (const dwmac_common_core_ops *) self->CFG->MAC_OPS->core;
        ( CORE_OPS->set_hash_filter )( self, cmd == SIOCADDMULTI, ifr );
      }
#endif /* COMMENTED_OUT */
      break;
    case SIO_RTEMS_SHOW_STATS:
      eno = dwmac_if_interface_stats( self );
      break;
    default:
      eno = EINVAL;
      break;
  }

  return eno;
}

/* Method called by the network stack if there are frames to be transmitted */
static void dwmac_if_interface_start( struct ifnet *ifp )
{
  rtems_status_code     sc;
  dwmac_common_context *self = ifp->if_softc;


  /* Mark that we are transmitting */
  ifp->if_flags |= IFF_OACTIVE;

  if ( self->state == DWMAC_COMMON_STATE_UP ) {
    /* Wake up our task */
    sc = rtems_bsdnet_event_send( self->task_id_tx,
                                  DWMAC_COMMON_EVENT_TX_TRANSMIT_FRAME );
    assert( sc == RTEMS_SUCCESSFUL );
  }
}

static int dwmac_if_clk_csr_set(
  const uint32_t        gmii_clk_rate,
  dwmac_common_context *self )
{
  int eno = 0;


  if ( gmii_clk_rate < DWCGNAC3504_CSR_F_35M ) {
    self->csr_clock = DWCGNAC3504_CSR_20_35M;
  } else if ( ( gmii_clk_rate >= DWCGNAC3504_CSR_F_35M )
              && ( gmii_clk_rate < DWCGNAC3504_CSR_F_60M ) ) {
    self->csr_clock = DWCGNAC3504_CSR_35_60M;
  } else if ( ( gmii_clk_rate >= DWCGNAC3504_CSR_F_60M )
              && ( gmii_clk_rate < DWCGNAC3504_CSR_F_100M ) ) {
    self->csr_clock = DWCGNAC3504_CSR_60_100M;
  } else if ( ( gmii_clk_rate >= DWCGNAC3504_CSR_F_100M )
              && ( gmii_clk_rate < DWCGNAC3504_CSR_F_150M ) ) {
    self->csr_clock = DWCGNAC3504_CSR_100_150M;
  } else if ( ( gmii_clk_rate >= DWCGNAC3504_CSR_F_150M )
              && ( gmii_clk_rate < DWCGNAC3504_CSR_F_250M ) ) {
    self->csr_clock = DWCGNAC3504_CSR_150_250M;
  } else if ( ( gmii_clk_rate >= DWCGNAC3504_CSR_F_250M )
              && ( gmii_clk_rate < DWCGNAC3504_CSR_F_300M ) ) {
    self->csr_clock = DWCGNAC3504_CSR_250_300M;
  } else {
    assert( gmii_clk_rate < DWCGNAC3504_CSR_F_300M );
    eno = EINVAL;
  }

  return eno;
}

static int dwmac_fixup_unit_count(
  const dwmac_common_context *self,
  const int                   count,
  const int                   default_value,
  const int                   max )
{
  int ret = count;


  if ( ret <= 0 ) {
    ret = DWMAC_ALIGN( default_value, self->CFG->L1_CACHE_LINE_SIZE );
  } else if ( ret > max ) {
    ret = DWMAC_ALIGN( max, self->CFG->L1_CACHE_LINE_SIZE );
  }

  return ret;
}

/* Called during initial setup */
static int dwmac_if_attach(
  dwmac_common_context         *self,
  struct rtems_bsdnet_ifconfig *bsd_config,
  const dwmac_cfg              *driver_config,
  void                         *arg )
{
  int           eno                     = 0;
  struct ifnet *ifp                     = &self->arpcom.ac_if;
  char         *unit_name               = NULL;
  int           unit_number             = rtems_bsdnet_parse_driver_name(
    bsd_config,
    &unit_name );
  const dwmac_callback_cfg    *CALLBACK = &driver_config->CALLBACK;
  const dwmac_common_desc_ops *DESC_OPS =
    (const dwmac_common_desc_ops *) driver_config->DESC_OPS->ops;


  assert( self != NULL );
  assert( bsd_config != NULL );
  assert( driver_config != NULL );
  assert( CALLBACK != NULL );

  if ( self == NULL
       || bsd_config == NULL
       || driver_config == NULL
       || CALLBACK == NULL
       ) {
    eno = EINVAL;
  } else {
    assert( CALLBACK->nic_enable != NULL );
    assert( CALLBACK->nic_disable != NULL );
    assert( CALLBACK->phy_enable != NULL );
    assert( CALLBACK->phy_disable != NULL );
    assert( CALLBACK->phy_event_enable != NULL );
    assert( CALLBACK->phy_event_clear != NULL );
    assert( CALLBACK->phy_events_get != NULL );
    assert( CALLBACK->phy_start != NULL );
    assert( CALLBACK->phy_stop != NULL );
    assert( CALLBACK->mem_alloc_nocache != NULL );
    assert( CALLBACK->mem_free_nocache != NULL );
    assert( CALLBACK->bus_setup != NULL );

    if ( CALLBACK->nic_enable == NULL
         || CALLBACK->nic_disable == NULL
         || CALLBACK->phy_enable == NULL
         || CALLBACK->phy_disable == NULL
         || CALLBACK->phy_event_enable == NULL
         || CALLBACK->phy_event_clear == NULL
         || CALLBACK->phy_events_get == NULL
         || CALLBACK->phy_start == NULL
         || CALLBACK->phy_stop == NULL
         || CALLBACK->mem_alloc_nocache == NULL
         || CALLBACK->mem_free_nocache == NULL
         || CALLBACK->bus_setup == NULL
         ) {
      eno = EINVAL;
    }
  }

  if ( eno == 0 ) {
    self->dma_threshold_control = DWMAC_DMA_THRESHOLD_CONTROL_DEFAULT;

    assert( driver_config->addr_gmac_regs != NULL );
    if ( driver_config->addr_gmac_regs != NULL ) {
      self->macgrp = driver_config->addr_gmac_regs;

      assert( driver_config->addr_dma_regs != NULL );
      if ( driver_config->addr_dma_regs != NULL ) {
        self->dmagrp = driver_config->addr_dma_regs;
      } else {
        eno = EINVAL;
      }
    } else {
      eno = EINVAL;
    }
  }

  if ( eno == 0 ) {
    eno = dwmac_if_clk_csr_set(
      driver_config->GMII_CLK_RATE,
      self
      );
  }

  if ( eno == 0 ) {
    assert( 32 >= driver_config->MDIO_BUS_ADDR );

    if ( 32 < driver_config->MDIO_BUS_ADDR ) {
      eno = EINVAL;
    }
  }

  if ( eno == 0 ) {
    self->arg        = arg;        /* Optional argument from upper layer */
    self->state      = DWMAC_COMMON_STATE_DOWN;
    self->bsd_config = bsd_config; /* BSD-Net configuration.
                                      Exists for ll BSD based network divers */

    /* Device control */
    bsd_config->drv_ctrl = NULL;

    /* MDIO */
    self->mdio.mdio_r = dwmac_if_mdio_read; /* read from phy
                                               registers */
    self->mdio.mdio_w = dwmac_if_mdio_write; /* write to phy
                                                registers */

    if ( driver_config->MAC_OPS == &DWMAC_1000_ETHERNET_MAC_OPS ) {
      self->mdio.has_gmii = 1;
    } else {
      self->mdio.has_gmii = 0;

      /* dwmac_dma_ops_100 and dwmac_core_ops_100 is not yet implemented! */
      assert( driver_config->MAC_OPS == &DWMAC_1000_ETHERNET_MAC_OPS );
      eno = ENOTSUP;
    }

    if ( eno == 0 ) {
      self->CFG     = driver_config;
      ifp->if_ioctl = dwmac_if_interface_ioctl; /* Execute commands
                                                   at runtime */
      ifp->if_init  = dwmac_if_interface_init;        /* Initialization
                                                         (called once) */

      /* Receive unit count */
      bsd_config->rbuf_count = dwmac_fixup_unit_count(
        self,
        bsd_config->rbuf_count,
        DWMAC_CONFIG_RX_UNIT_COUNT_DEFAULT,
        DWMAC_CONFIG_RX_UNIT_COUNT_MAX
        );

      /* Transmit unit count */
      bsd_config->xbuf_count = dwmac_fixup_unit_count(
        self,
        bsd_config->xbuf_count,
        DWMAC_CONFIG_TX_UNIT_COUNT_DEFAULT,
        DWMAC_CONFIG_TX_UNIT_COUNT_MAX
        );

      if (
        DWMAC_GLOBAL_MBUF_CNT / 4 < bsd_config->rbuf_count
          || DWMAG_GLOBAL_MCLUST_CNT / 4 < bsd_config->rbuf_count
      ) {
        bsp_fatal( DWMAC_FATAL_TOO_MANY_RBUFS_CONFIGURED );
      }

      /* Copy MAC address */
      memcpy(
        self->arpcom.ac_enaddr,
        bsd_config->hardware_address,
        ETHER_ADDR_LEN
        );

      /* Set interface data */
      ifp->if_softc          = self;
      ifp->if_unit           = (short) unit_number;
      ifp->if_name           = unit_name;

      ifp->if_start          = dwmac_if_interface_start;  /* Start transmitting
                                                             frames */
      ifp->if_output         = ether_output;
      ifp->if_watchdog       = NULL;
      ifp->if_flags          = IFF_BROADCAST | IFF_SIMPLEX;
      ifp->if_snd.ifq_maxlen = ifqmaxlen;
      ifp->if_timer          = 0;
      ifp->if_mtu            =
        bsd_config->mtu > 0 ? (u_long) bsd_config->mtu : ETHERMTU;

      eno = DESC_OPS->validate( self );
      assert( eno == 0 );
    }

    if ( eno == 0 ) {
      /* Create and start the receive task for our driver */
      assert( self->task_id_rx == RTEMS_ID_NONE );
      self->task_id_rx = rtems_bsdnet_newproc(
        "ntrx",
        4096,
        dwmac_task_rx,
        self
        );
      assert( self->task_id_rx != RTEMS_ID_NONE );

      if ( self->task_id_rx == RTEMS_ID_NONE ) {
        eno = ENOMEM;
      }
    }

    if ( eno == 0 ) {
      /* Create and start the transmit task for our driver */
      assert( self->task_id_tx == RTEMS_ID_NONE );
      self->task_id_tx = rtems_bsdnet_newproc(
        "nttx",
        4096,
        dwmac_task_tx,
        self
        );
      assert( self->task_id_tx != RTEMS_ID_NONE );

      if ( self->task_id_tx == RTEMS_ID_NONE ) {
        eno = ENOMEM;
      }
    }
  }

  if ( eno == 0 ) {
    /* Change status */
    ifp->if_flags |= IFF_RUNNING;

    /* Attach the interface */
    if_attach( ifp );
    ether_ifattach( ifp );
  }

  return eno;
}

/* Initial setup method. Part of the driver API
 * Returns the address of the driver context on success and NULL on failure */
void *dwmac_network_if_attach_detach(
  struct rtems_bsdnet_ifconfig *bsd_config,
  const dwmac_cfg              *driver_config,
  void                         *arg,
  int                           attaching )
{
  int                   eno     = 0;
  dwmac_common_context *context = NULL;


  if ( attaching ) {
    context = calloc( 1, sizeof( dwmac_common_context ) );
    assert( context != NULL );

    if ( context != NULL ) {
      eno = dwmac_if_attach(
        context,
        bsd_config,
        driver_config,
        arg
        );

      if ( eno != 0 ) {
        free( context, 0 );
        context = NULL;
      }
    }
  } else {
    /* Detaching is not supported */
    assert( attaching != false );
  }

  return context;
}

int dwmac_if_read_from_phy(
  void          *arg,
  const unsigned phy_reg,
  uint16_t      *val )
{
  int                   eno  = EINVAL;
  dwmac_common_context *self = (dwmac_common_context *) arg;
  uint32_t              value;


  if ( arg != NULL ) {
    eno = dwmac_if_mdio_read(
      self->CFG->MDIO_BUS_ADDR,
      self,
      phy_reg,
      &value );
  }

  if ( eno == 0 ) {
    *val = (uint16_t) value;
  }

  return eno;
}

int dwmac_if_write_to_phy(
  void          *arg,
  const unsigned phy_reg,
  const uint16_t val )
{
  int                   eno  = EINVAL;
  dwmac_common_context *self = (dwmac_common_context *) arg;


  if ( arg != NULL ) {
    eno = dwmac_if_mdio_write(
      self->CFG->MDIO_BUS_ADDR,
      self,
      phy_reg,
      val );
  }

  return eno;
}

int dwmac_if_handle_phy_event( void *arg )
{
  int                   eno;
  rtems_status_code     sc;
  dwmac_common_context *self = (dwmac_common_context *) arg;


  sc = rtems_bsdnet_event_send(
    self->task_id_tx,
    DWMAC_COMMON_EVENT_TX_PHY_STATUS_CHANGE
    );
  eno = rtems_status_code_to_errno( sc );

  return eno;
}