OpenHarmony NetDev 原创 精华

杀手来过
发布于 2022-4-9 09:37
浏览
1收藏

NetDev

WIFI 芯片属于网络设备,自然也要归OpenHarmony的网络框架管理,本文用于了解 网络数据如何在协议栈和网络驱动之间传输。

网络设备的使用需要配合网络协议栈,OpenHarmony的网络协议栈有两种,一种是liteos-a内核使用的lwip协议栈,一种是标准系统linux内核网络协议栈。

本文以lwip协议栈为例来了解。

一、前提

网络数据的通路:(这里的网口驱动是具体wifi芯片的驱动程序,由厂商实现。)

发送数据:应用程序->lwip->网口驱动

接受数据:网口驱动->lwip->应用程序

二、NetDevice

openharmony使用NetDevice结构体来描述所有的网络设备(网口)

typedef struct NetDevice {
    NetIfCategory netifCateg;                 //内核类型
    char name[IFNAMSIZ];                      //设备名称
    NetLinkType LinkLayerType;                //链路层类型:以太网或wifi
    IfType funType;                           //网络端口类型 including AP, STA, and P2P
    uint8_t macAddr[MAC_ADDR_SIZE];           //mac地址
    ....
    struct NetDeviceInterFace *netDeviceIf;   //提供给驱动厂商的接口
    struct NetDevice *owner;                  /**< Network device */
    struct NetDevStats stats;                 /**< Network statistics */
} NetDevice;

其中 NetDeviceInterFace 对象的作用是初始化、打开、关闭一个网络设备等以下功能,必须由驱动开发厂商根据具体的wifi芯片编写的驱动来实现其中的函数:

struct NetDeviceInterFace {
    int32_t (*init)(struct NetDevice *netDev);  //初始化网络设备
    void (*deInit)(struct NetDevice *netDev);   
    int32_t (*open)(struct NetDevice *netDev);  //打开数据链路层
    int32_t (*stop)(struct NetDevice *netDev);  
    NetDevTxResult (*xmit)(struct NetDevice *netDev, NetBuf *netBuff);        //发送数据
    int32_t (*ioctl)(struct NetDevice *netDev, IfReq *req, int32_t cmd);      /**< Used for the control command word. */
    int32_t (*setMacAddr)(struct NetDevice *netDev, void *addr);              //设置mac地址
    struct NetDevStats *(*getStats)(struct NetDevice *netDev);                //获取设备状态
    void (*setNetIfStatus)(struct NetDevice *netDev, NetIfStatus status);     //设置状态
    uint16_t (*selectQueue)(struct NetDevice *netDev, NetBuf *netBuff);       //选择一个队列
    uint32_t (*netifNotify)(struct NetDevice *netDev, NetDevNotify *notify);  /**< Notifies the network port status. */
    int32_t (*changeMtu)(struct NetDevice *netDev, int32_t newMtu);           //改变mtu
    void (*linkStatusChanged)(struct NetDevice *netDev);   	//检查以太网连接状态
    ProcessingResult (*specialEtherTypeProcess)(const struct NetDevice *netDev, NetBuf *buff);
                                                                              /**< Performs private processing without
                                                                               * involving network-layer data.
                                                                               */
};

根据HdfWlanDevice 创建NetDevice:

struct NetDevice *AllocPlatformNetDevice(struct HdfWlanDevice *device)
{
    struct NetDevice *result = NULL;
    char ifName[IFNAMSIZ] = {0};
    struct HdfWifiNetDeviceData *data = NULL;
    data = (struct HdfWifiNetDeviceData *)OsalMemCalloc(sizeof(struct HdfWifiNetDeviceData));
    do {
        //分配网卡编号
        for (i = 0; i < MAX_IF_COUNT; i++) {
            if (((g_allocatedIfMap >> i) & 0x1) != 0x1) {
                id = i;
                break;
            }
        }
		//生成网卡名称:wlan0
        ret = GetPlatformIfName(id, ifName, IFNAMSIZ);
#ifdef _PRE_HDF_LINUX
        result = NetDeviceInit(ifName, strlen(ifName), WIFI_LINK, FULL_OS);
#else
        //获取netdev
        result = NetDeviceInit(ifName, strlen(ifName), WIFI_LINK, LITE_OS);
#endif
    } while (false);
    result->classDriverName = HDF_WIFI_PLATFORM_DRIVER_NAME;
    result->classDriverPriv = data;
    data->netInterfaceId = id;
    SET_BIT(device->netIfMap, id);
    SET_BIT(g_allocatedIfMap, id);
    return result;
}

三、NetBuf

NetBuf用于描述网络数据,以在网络协议栈和网络驱动之间传输数据。

//记录一段网络数据的偏移和长度
struct BufField {
    uint32_t offset;      /**< Offset of the buffer segment */
    uint32_t len;         /**< Length of the buffer segment */
};

typedef struct NetBuf {
    struct DListHead dlist;             //双链表
    struct BufField bufs[MAX_BUF_NUM];  
    uint8_t     *mem;                   //内存地址
    uint32_t    len;                    //内存长度
    uint32_t    dataLen;                //真实数据长度
    void        *dev;                   //接受到该数据的netdev
    uint32_t    qmap;                   /**< Queue mappings of the network data buffer */
    uint8_t     rsv[MAX_NETBUF_RESEVER_SIZE]; /**< Reserved field. For details, see {@link MAX_NETBUF_RESEVER_SIZE}. */
} NetBuf;

四、适配器 NetDeviceImpl

这里有一个问题,就是openharmony使用NetDev来描述网口,用NetBuf来描述网口数据,但是在lwip协议栈中,有他自己的一套机制来描述网口和网络数据。

这里就存在一个适配(adapt)的问题,需要一个适配器(adapter)来适配这两套机制,使得数据能在这两者之间顺利传递。

NetDev ------> netif
NetBuf ------> pbuf

NetDeviceImpl结构体应运而生,该部分的代码在bearpi_micro/drivers/adapter/khdf/liteos/network/src/net_device_adapter.c,net_device_adapter顾名思义就是把net_device适配到lwip的netif。同理netbuf_adapter.c实现的就是把netbuf适配到lwip的pbuf。

struct NetDeviceImpl {
    struct NetDevice *netDevice;	//可通过netDevice->NetDeviceInterFace 调用芯片驱动程序
    struct NetDeviceImplOp *interFace;	//调用lwip的接口
    void *osPrivate;	//指向 lwip的netif对象
};

所以NetDeviceImpl 就是芯片驱动程序 和 lwip协议栈沟通的中介,其成员的作用很明显:

  • NetDevice:可通过netDevice->NetDeviceInterFace 调用芯片驱动程序
  • NetDeviceImplOp:提供给开发者的接口
  • osPrivate:指向 lwip的netif对象

NetDeviceImplOp

NetDeviceImplOp结构体就是提供给开发者的接口,使得网口驱动程序能与lwip协议栈交互。在net_device_adapter.c中的g_liteNdImplOps就是NetDeviceImplOp。我们将通过这个结构体,看lwip协议栈如何收发数据。

struct NetDeviceImplOp {
   	//初始化netdev
    int32_t (*init)(struct NetDeviceImpl *netDevice);
    int32_t (*deInit)(struct NetDeviceImpl *netDevice);
    int32_t (*add)(struct NetDeviceImpl *netDevice);
    int32_t (*delete)(struct NetDeviceImpl *netDevice);
    int32_t (*setStatus)(struct NetDeviceImpl *netDevice, NetIfStatus status);
    int32_t (*setLinkStatus)(struct NetDeviceImpl *netDevice, NetIfLinkStatus status);
    int32_t (*getLinkStatus)(struct NetDeviceImpl *netDevice, NetIfLinkStatus *status);
    int32_t (*receive)(struct NetDeviceImpl *netDevice, NetBuf *buff, ReceiveFlag flag);
    int32_t (*setIpAddr)(struct NetDeviceImpl *netDevice, const IpV4Addr *ipAddr, const IpV4Addr *netMask,
        const IpV4Addr *gw);
    int32_t (*dhcpsStart)(struct NetDeviceImpl *netDevice, char *ip, uint16_t ipNum);
    int32_t (*dhcpsStop)(struct NetDeviceImpl *netDevice);
    int32_t (*dhcpStart)(struct NetDeviceImpl *netDevice);
    int32_t (*dhcpStop)(struct NetDeviceImpl *netDevice);
    int32_t (*dhcpIsBound)(struct NetDeviceImpl *netDevice);
    int32_t (*changeMacAddr)(struct NetDeviceImpl *netDevice);
};

static struct NetDeviceImplOp g_liteNdImplOps = {
    .init = LiteNetDevInit,		//创建netif
    .deInit = LiteNetDevDeInit,
    .add = LiteNetDevAdd,	//创建netif并添加到协议栈
    .delete = LiteNetDevDelete,
    .setStatus = LiteNetDevSetStatus,
    .setLinkStatus = LiteNetDevSetLinkStatus,
    .getLinkStatus = LiteNetDevGetLinkStatus,
    .receive = LiteNetDevReceive,	//将数据上传到协议栈
    .setIpAddr = LiteNetSetIpAddr,	//设置ip
    .dhcpsStart = LiteNetDhcpsStart,	//开始dhcp
    .dhcpsStop = LiteNetDhcpsStop,
    .dhcpStart = LiteNetDhcpStart,
    .dhcpStop = LiteNetDhcpStop,
    .dhcpIsBound = LiteNetDhcpIsBound,
    .changeMacAddr = LiteNetChangeMacAddr,
};

五、netif

要使用lwip协议栈,就需要创建一个netif来描述所使用的wifi 网口。NetDeviceImplOp结构体提供给开发者创建netif的接口:

创建初始化lwip netif的接口:

  • g_liteNdImplOps.init()
  • g_liteNdImplOps.add()
struct NetDeviceAdapterLite {
    struct netif *lwipNetif;
};
//创建netif
static int32_t LiteNetDevInit(struct NetDeviceImpl *netDeviceImpl)
{
    struct NetDeviceAdapterLite *liteNdPri = NULL;

    liteNdPri = (struct NetDeviceAdapterLite *)OsalMemCalloc(sizeof(struct NetDeviceAdapterLite));
    (void)memset_s(liteNdPri, sizeof(struct NetDeviceAdapterLite), 0, sizeof(struct NetDeviceAdapterLite));
    netDeviceImpl->osPrivate = (void *)liteNdPri;
}
//创建netif并添加到协议栈
static int32_t LiteNetDevAdd(struct NetDeviceImpl *netDeviceImpl)
{
    struct NetDeviceAdapterLite *liteNdPri = (struct NetDeviceAdapterLite *)netDeviceImpl->osPrivate;
    struct NetDevice *lwipNd = netDeviceImpl->netDevice;
	//根据lwipNd创建netif
    lwipNf = CreateLwipNetIf(netDeviceImpl, lwipNd创建netif);
    if ((ret = netifapi_netif_add(lwipNf, &ipaddr, &netmask, &gw)) != ERR_OK) {
        return HDF_FAILURE;
    }
    //复制mac地址
    if (memcpy_s(lwipNf->hwaddr, NETIF_MAX_HWADDR_LEN, lwipNd->macAddr, MAC_ADDR_SIZE) != EOK) {
        return HDF_FAILURE;
    }

    liteNdPri->lwipNetif = lwipNf;
    IpV6SpecialProc(lwipNd, lwipNf);
    /* set netif default status */
    netifapi_netif_set_default(lwipNf);
    netif_set_link_callback(lwipNf, LiteNetifLinkChangeCallback);
    return HDF_SUCCESS;
}


创建netif的同时实现了netif的一些函数如drv_send、drv_set_hwaddr,这些函数需要调用芯片驱动。

static struct netif *CreateLwipNetIf(const struct NetDeviceImpl *netDeviceImpl, const struct NetDevice *netDev)
{
    lwipNf = (struct netif *)OsalMemCalloc(sizeof(struct netif));
    (void)memset_s(lwipNf, sizeof(struct netif), 0, sizeof(struct netif));

    /* register netif to lwip */
    lwipNf->state = (void *)netDeviceImpl;
    lwipNf->drv_send = LwipSend;
    lwipNf->drv_set_hwaddr = LwipSetHwaddr;
    lwipNf->link_layer_type = netDev->LinkLayerType;
    lwipNf->hwaddr_len = MAC_ADDR_SIZE;
    lwipNf->drv_config = LwipDrvConfig;

    return lwipNf;
}

网口发送数据到lwip

在网口的驱动程序收到数据后,需要传递给lwip协议栈,这时调用NetDeviceImplOp->reveive()函数来传递数据:

首先把netdev转换成netif,把netbuf转换成pbuf,再调用driverif_input()传递pbuf给lwip协议栈。

static int32_t LiteNetDevReceive(struct NetDeviceImpl *netDeviceImpl, struct NetBuf *buff, ReceiveFlag flag)
{
    //get lwip netif
    struct netif *lwipNf = GetNetIfFromDevImpl(netDeviceImpl);
    ProcessingResult ret = LiteNetDevDataFilter(netDeviceImpl, buff);
    if (ret == PROCESSING_CONTINUE) {
        //call LiteNetDevDataReceive to send pbuf
        return LiteNetDevDataReceive(netDeviceImpl, buff);
    } else if (ret == PROCESSING_COMPLETE) {
        return HDF_SUCCESS;
    } else {
        return HDF_FAILURE;
    }
}

static int32_t LiteNetDevDataReceive(struct NetDeviceImpl *netDeviceImpl, struct NetBuf *buff)
{
    struct netif *lwipNf = GetNetIfFromDevImpl(netDeviceImpl);
    //Conver NetBuf To PBuf
    struct pbuf *pBuff = ConverNetBufToPBuf(buff);
	//post pbuff to lwip
    driverif_input(lwipNf, pBuff);
    NetBufFree(buff);
    return HDF_SUCCESS;
}

driverif_input()是由lwip提供的接口,定义如下:

/*
 * This function should be called by network driver to pass the input packet to LwIP.
 * Before calling this API, driver has to keep the packet in pbuf structure. Driver has to
 * call pbuf_alloc() with type as PBUF_RAM to create pbuf structure. Then driver
 * has to pass the pbuf structure to this API. This will add the pbuf into the TCPIP thread.
 * Once this packet is processed by TCPIP thread, pbuf will be freed. Driver is not required to
 * free the pbuf.
 *
 * @param netif the lwip network interface structure for this driverif
 * @param p packet in pbuf structure format
 */
void driverif_input(struct netif *netif, struct pbuf *p)

网口从lwip接收数据

lwip协议栈的数据会传递到netif结构体,而我们在创建netif结构体时,设置了drv_send()等函数,lwip协议栈就会调用该函数,在这个函数中我们就需要实现netif和netdev,pbuf和netbuf的转换,调用NetDeviceInterFace 里的函数发送数据。

void LwipSend(struct netif *netif, struct pbuf *lwipBuf)
{
    struct NetDeviceImpl *ndImpl = (struct NetDeviceImpl *)netif->state;
    //get netdev
    struct NetDevice *netDev = ndImpl->netDevice;
    struct NetDeviceInterFace *netDevIf = NULL;
	//get netbuf
    struct NetBuf *netBuff = ConverPbuffToNetBuf(netDev, lwipBuf);
	//get NetDeviceInterFace 
    netDevIf = netDev->netDeviceIf;
    //send netbuf
    netDevIf->xmit(netDev, netBuff);
    return;
}

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
2
收藏 1
回复
举报
回复
    相关推荐