Implementation of Sylix OS Network Card Driver

Keywords: network IE Mac Programming

1. Development environment

Operating System: Sylix OS Programming environment: RealEvo-IDE3.1 Hardware Platform: IMX6Q Experimental Box

2. Technical Realization

The transceiver function driven by network card is realized by managing transceiver descriptors. Therefore, it is necessary to initialize the descriptor when the MAC is initialized. Initialization content varies from CPU to CPU. When the descriptors are initialized, they can be used to send and receive network messages.

Implementation of ** 2.1 Network Sender Function

The network-driven sending function is implemented by the enetCoreTx function, as shown in Listing 2-1.

> Program Listing 2-1 Sender Function

   /***************************************************************************************************
** Function name: enetCoreTx
** Function Description: Network Sender Function
** Input: pNetDev: Network Structure
**           pbuf    :  lwip Core buff
** Output: Error Number
** Global variables:
** Call module:
***************************************************************************************************/
static INT enetCoreTx (struct netdev *pNetDev, struct pbuf *pbuf)
{
    ENET     *pEnet;
    addr_t    atBase;
    UINT16    usStatus;
    UINT16    usLen;
    BUFD     *pbufd;
    INT       iLinkUp;
    INTREG    iregFlag;

    pEnet  = pNetDev->priv;
    atBase = pEnet->ENET_atIobase;

    /*
     * If the network is disconnected and an error is returned, flags is read-only in the user program and is set to
     * link_down link_up Settings in functions
     */
    netdev_get_linkup(pNetDev, &iLinkUp);
    if(!iLinkUp) {
        return  (PX_ERROR);
    }
    KN_SMP_WMB();
    LW_SPIN_LOCK_QUICK(&pEnet->ENET_slLock, &iregFlag);

    pbufd = pEnet->ENET_pbufdCurTxbd;
    usStatus = pbufd->BUFD_usStatus;
    if (usStatus & ENET_BD_TX_READY) {
        LW_SPIN_UNLOCK_IRQ(&pEnet->ENET_slLock, iregFlag);
        printk("Send queue full!!\n");
        return  (PX_ERROR);
    }
    KN_SMP_WMB();
    usStatus &= ~ENET_BD_TX_STATS;

    usLen = pbuf->tot_len;

    usStatus |= ENET_BD_TX_TC | ENET_BD_TX_LAST | ENET_BD_TX_READY | ENET_BD_TX_TO2;

    pbuf_copy_partial(pbuf, (PVOID)pbufd->BUFD_uiBufAddr, usLen, 0);
    KN_SMP_WMB();
    pbufd->BUFD_usDataLen = usLen;
    pbufd->BUFD_usStatus  = usStatus;

    writel(ENET_TDAR_TX_ACTIVE, atBase + HW_ENET_MAC_TDAR);           /*  Enable enet controller to send, original location*/

    /*
     *  If this is the last send descriptor, return to the beginning
     */
    if (usStatus & ENET_BD_TX_WRAP) {
        pbufd = pEnet->ENET_pbufdTxbdBase;
    } else {
        pbufd++;
    }

    if (pbufd == pEnet->ENET_pbufdTxDirty) {
        pEnet->ENET_iFull = 1;
    }

    pEnet->ENET_pbufdCurTxbd = pbufd;

    netdev_statinfo_total_add(pNetDev, LINK_OUTPUT, usLen);

    if (((UINT8 *)pbuf->payload)[0] & 1) {
        netdev_statinfo_mcasts_inc(pNetDev, LINK_OUTPUT);   /*   Statistics of the number of broadcast packets sent       */
    } else {
        netdev_statinfo_ucasts_inc(pNetDev, LINK_OUTPUT);   /*   Statistics on the number of unicast packets sent       */
    }

    LW_SPIN_UNLOCK_QUICK(&pEnet->ENET_slLock, iregFlag);

    return  (ERROR_NONE);
}

In the enetCoreTx function, the current connection status of the network is detected first, and only when the connection is successful can the sending operation be carried out. When the network is in a successful connection state, it will determine whether the current descriptor is available. Once the sending descriptor is operational, the descriptor is populated. The contents of filling include message length, message address, descriptor status and so on. This also needs to be handled according to different descriptor definitions.

Once the descriptor filling is completed, the MAC can be started for sending operations.

After sending, if the descriptor is in the form of a linked list, it is necessary to determine whether the end of the list is reached after the current sending is completed. If so, it is necessary to select the descriptor node of the head of the linked list as the descriptor for the next sending.

After completing the above operations, we use the netdev_statinfo_total_add, netdev_statinfo_mcasts_inc and netdev_statinfo_ucasts_inc functions provided by our system to carry out the statistics of sending messages.

2.2 Realization of Network Receiving Function The function ** enetCoreRecv, which handles receiving tasks, needs to be called by interruption. The interrupt service function is shown in Listing 2-2.

> Interrupt service functions in program listing 2-2

/***************************************************************************************************
** Function name: enetIsr
** Function description: Ethernet interrupt response function
** Input: pvArg: interrupt parameter
**           uiVector:  Interrupt vector number
** Output: interrupt return value
** Global variables:
** Call module:
***************************************************************************************************/
static irqreturn_t enetIsr (PVOID  pvArg, UINT32  uiVector)
{
    struct netdev  *pNetDev = (struct netdev *)pvArg;
    ENET           *pEnet;
    INT             ie = 0;
    addr_t          atBase;

    pEnet = pNetDev->priv;
    atBase = pEnet->ENET_atIobase;

    ie = readl(atBase + HW_ENET_MAC_EIR);
    writel(ie, atBase + HW_ENET_MAC_EIR);
    KN_SMP_WMB();

    if (ie & ENET_EIR_TXF) {                                        /*  Sending has been completed              */
        enetCoreSendComplete(pNetDev);
        return (LW_IRQ_HANDLED);
    }

    if (ie & ENET_EIR_RXF) {                                        /*  Receiving has been completed              */
        netdev_notify(pNetDev, LINK_INPUT, 1);
        writel(ENET_EIR_TXF, atBase + HW_ENET_MAC_EIMR);            /* Turn off receiving interrupt                */
    }

    if (ie & ENET_EIR_MII) {
       /*
        *  MII Read and write events, because this driver does not use MII interruption, so this part of the code has no practical use, debugging use
        */
    }

    return  (LW_IRQ_HANDLED);
}

The interrupt service will determine what triggers the interruption. If the receiving is completed, the notification function netdev_notify of the system is called to notify the protocol stack that the network message has been received and needs to be processed. If the third parameter of netdev_notify function is 1, the receiving processing function enetCoreRecv is added to the network processing queue to process the received message. The enetCoreRecv function is shown in Listing 2-3.

> Program Listing 2-3 Accepts Processing Functions

/**************************************************************************************************
** Function name: enetCoreRecv
** Function Description: Network Receiver Function
** Input: pNetDev: Network Structure
** Output: length of reception
** Global variables:
** Call module:
***************************************************************************************************/
static VOID  enetCoreRecv (struct netdev  *pNetDev, INT (*input)(struct netdev *, struct pbuf *))
{
    ENET         *pEnet;
    struct pbuf  *pBuf;
    UINT8        *ucFrame;
    BUFD         *pBufd;
    addr_t        atBase;
    UINT16        usStatus;
    UINT16        usLen = 0;

    pEnet  = pNetDev->priv;
    atBase = pEnet->ENET_atIobase;
    KN_SMP_WMB();

    pBufd = pEnet->ENET_pbufdCurRxbd;
    while (!((usStatus = pBufd->BUFD_usStatus) & ENET_BD_RX_EMPTY)) {

        if (usStatus & ENET_BD_RX_LG) {
#if LINK_STATS
            netdev_linkinfo_lenerr_inc(pNetDev);
#endif
        }

        if (usStatus & ENET_BD_RX_CR) {
#if LINK_STATS
            netdev_linkinfo_chkerr_inc(pNetDev);
#endif
        }

        if (usStatus & ENET_BD_RX_OV) {
#if LINK_STATS
            netdev_linkinfo_memerr_inc(pNetDev);
#endif
        }

        if (usStatus & ENET_BD_RX_TR) {
#if LINK_STATS
            netdev_linkinfo_err_inc(pNetDev);
            netdev_linkinfo_memerr_inc(pNetDev);
#endif
            goto rx_done;
        }
        usLen = pBufd->BUFD_usDataLen;
        ucFrame = (UINT8 *)pBufd->BUFD_uiBufAddr;                  /*  Get the received frame                */
        usLen -= 4;                                               /*  Remove FCS                    */

        pBuf = netdev_pbuf_alloc(usLen);
        if (!pBuf) {
#if LINK_STATS
            netdev_linkinfo_memerr_inc(pNetDev);
            netdev_linkinfo_drop_inc(pNetDev);
#endif
            netdev_statinfo_discards_inc(pNetDev, LINK_INPUT);
        } else {
            pbuf_take(pBuf, ucFrame, (UINT16)usLen);
            KN_SMP_WMB();

            if (input(pNetDev, pBuf)) {                             /*  Submit data to protocol stack            */
                netdev_pbuf_free(pBuf);
                netdev_statinfo_discards_inc(pNetDev, LINK_INPUT);

            } else {

#if LINK_STATS
                netdev_linkinfo_recv_inc(pNetDev);
#endif
                netdev_statinfo_total_add(pNetDev, LINK_INPUT, usLen);  /* Statistical Send Data Degree           */
                if (((UINT8 *)pBuf->payload)[0] & 1) {
                    netdev_statinfo_mcasts_inc(pNetDev, LINK_INPUT);    /* Statistical Number of Broadcast Packets Sent       */
                } else {
                    netdev_statinfo_ucasts_inc(pNetDev, LINK_INPUT);    /* Statistics on the number of unicast packets sent       */
                }
            }
        }
rx_done:
        usStatus &= ~ENET_BD_RX_STATS;
        usStatus |= ENET_BD_RX_EMPTY;
        pBufd->BUFD_usStatus = usStatus;

        if (usStatus & ENET_BD_RX_WRAP) {
            pBufd = pEnet->ENET_pbufdRxbdBase;
        } else {
            pBufd++;
        }
        KN_SMP_WMB();
        writel(ENET_RDAR_RX_ACTIVE, atBase + HW_ENET_MAC_RDAR);     /*  Enabling enet controller to receive        */
    }
    pEnet->ENET_pbufdCurRxbd = pBufd;
    writel(ENET_DEFAULT_INTE, atBase + HW_ENET_MAC_EIMR);
}

In the enetCoreRecv function, a loop is processed on condition that the current receiving descriptor receives the message correctly.

In the while loop, the length of the received message and the address where the message is stored are first obtained through the descriptor. After knowing where the message is stored, the message can be copied to pbuf through the pbuf_take function. Once the copy is successful, pbuf can be submitted to the protocol stack through the second parameter input of enetCoreRecv.

After successful submission, you can call netdev_linkinfo_recv_inc, netdev_statinfo_total_add, netdev_statinfo_mcasts_inc, netdev_statinfo_ucasts_inc, just like the sending function, to perform statistical operations of receiving messages.

Finally, we need to update or return the descriptor so that it can be used to receive messages again.

3. References

nothing

Posted by nonaguy on Sun, 14 Apr 2019 23:03:32 -0700