* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "hdf_netbuf.h"
#include "los_memory.h"
#include "lwip/prot/ethernet.h"
#include "net_device.h"
#include "osal.h"
#define HDF_LOG_TAG NetBuf
#define MAX_CONVERSION_LEN 65535
* @brief Initializes a network data buffer queue.
*
* @param q Indicates the pointer to the network data buffer queue.
*
* @since 1.0
*/
void NetBufQueueInit(NetBufQueue *q)
{
if (q == NULL) {
return;
}
DListHeadInit(&q->dlist);
LOS_SpinInit(&q->lock);
q->size = 0;
}
* @brief Obtains the size of a network data buffer queue.
*
* @param q Indicates the pointer to the network data buffer queue.
*
* @return Returns the size of the network data buffer queue.
*
* @since 1.0
*/
uint32_t NetBufQueueSize(const NetBufQueue *q)
{
if (q == NULL) {
return 0;
}
return q->size;
}
* @brief Checks whether the network data buffer queue is empty.
*
* @param q Indicates the pointer to the network data buffer queue.
*
* @return Returns <b>true</b> if the queue is empty; returns <b>false</b> otherwise.
*
* @since 1.0
*/
bool NetBufQueueIsEmpty(const NetBufQueue *q)
{
if (q == NULL) {
return true;
}
return DListIsEmpty(&q->dlist);
}
* Net buffer is a continuous memory which consists of three parts: head buffer, data buffer and tail buffer.
*
* -----------------------------------------------------
* | | | | | | | | | | | | | | | | | | | | | | | | | | |
* ^ ^ ^ ^
* | | | |
* |<--head buff-->|<----data buff---->|<--tail buff-->|
* |<----data len----->|
* |<---------------------len------------------------->|
*/
* Enqueue the new net buffer to the tail of buffer queue.
*
* @param : q The pointer to the net buffer queue
* nb The new net buffer
* @return : void
*/
void NetBufQueueEnqueue(NetBufQueue *q, NetBuf *nb)
{
if (q == NULL || nb == NULL) {
return;
}
uint32_t intSave;
LOS_SpinLockSave(&q->lock, &intSave);
DListInsertTail(&nb->dlist, &q->dlist);
q->size++;
LOS_SpinUnlockRestore(&q->lock, intSave);
}
* Enqueue the new net buffer to the head of buffer queue.
*
* @param : q The pointer to the net buffer queue
* nb The new net buffer
* @return : void
*/
void NetBufQueueEnqueueHead(NetBufQueue *q, NetBuf *nb)
{
if (q == NULL || nb == NULL) {
return;
}
uint32_t intSave;
LOS_SpinLockSave(&q->lock, &intSave);
DListInsertHead(&nb->dlist, &q->dlist);
q->size++;
LOS_SpinUnlockRestore(&q->lock, intSave);
}
* Dequeue the first net buffer from net buffer queue.
*
* @param : q The pointer to the net buffer queue
* @return : The first net buffer in net buffer queue
*/
NetBuf *NetBufQueueDequeue(NetBufQueue *q)
{
NetBuf *nb = NULL;
if (q == NULL) {
return nb;
}
uint32_t intSave;
LOS_SpinLockSave(&q->lock, &intSave);
if (!DListIsEmpty(&q->dlist)) {
nb = DLIST_FIRST_ENTRY(&q->dlist, NetBuf, dlist);
DListRemove(&nb->dlist);
q->size--;
}
LOS_SpinUnlockRestore(&q->lock, intSave);
return nb;
}
* Dequeue the last net buffer from net buffer queue.
*
* @param : q The pointer to the net buffer queue
* @return : The last net buffer in net buffer queue
*/
NetBuf *NetBufQueueDequeueTail(NetBufQueue *q)
{
NetBuf *nb = NULL;
if (q == NULL) {
return nb;
}
uint32_t intSave;
LOS_SpinLockSave(&q->lock, &intSave);
if (!DListIsEmpty(&q->dlist)) {
nb = DLIST_LAST_ENTRY(&q->dlist, NetBuf, dlist);
DListRemove(&nb->dlist);
q->size--;
}
LOS_SpinUnlockRestore(&q->lock, intSave);
return nb;
}
* @brief Obtains the network data buffer from the header of a queue, without deleting it from the queue.
*
* @param q Indicates the pointer to the network data buffer queue.
*
* @return Returns the pointer to the first network data buffer if the queue is not empty;
* returns <b>NULL</b> otherwise.
*
* @since 1.0
*/
NetBuf *NetBufQueueAtHead(const NetBufQueue *q)
{
if (q == NULL) {
return NULL;
}
return (DListIsEmpty(&q->dlist)) ? NULL : DLIST_FIRST_ENTRY(&q->dlist, NetBuf, dlist);
}
* @brief Obtains the network data buffer from the tail of a queue, without deleting it from the queue.
*
* @param q Indicates the pointer to the network data buffer queue.
*
* @return Returns the pointer to the last network data buffer if the queue is not empty;
* returns <b>NULL</b> otherwise.
*
* @since 1.0
*/
NetBuf *NetBufQueueAtTail(const NetBufQueue *q)
{
if (q == NULL) {
return NULL;
}
return (DListIsEmpty(&q->dlist)) ? NULL : DLIST_LAST_ENTRY(&q->dlist, NetBuf, dlist);
}
* Clear the net buffer queue and free all net buffer in net buffer queue.
*
* @param : q The pointer to the net buffer queue
* @return : void
*/
void NetBufQueueClear(NetBufQueue *q)
{
NetBuf *nb = NULL;
if (q == NULL) {
return;
}
while ((nb = NetBufQueueDequeue(q)) != NULL) {
NetBufFree(nb);
}
}
* Merge two net buffer queues.
*
* @param : q The pointer to the net buffer queue for added.
* add The pointer to the net buffer queue to add.
* @return : void
*/
void NetBufQueueConcat(NetBufQueue *q, NetBufQueue *add)
{
if (q == NULL) {
return;
}
if (!NetBufQueueIsEmpty(add)) {
DListMerge(&add->dlist, &q->dlist);
q->size += add->size;
add->size = 0;
}
}
* Alloc a net buffer.
*
* @param : size The net buffer size
* @return : A new net buffer or NULL on fail
*/
NetBuf *NetBufAlloc(uint32_t size)
{
NetBuf *nb = NULL;
uint8_t *data = NULL;
#if CACHE_ALIGNED_SIZE
nb = (NetBuf *)LOS_MemAllocAlign(m_aucSysMem0, sizeof(*nb), CACHE_ALIGNED_SIZE);
#else
nb = (NetBuf *)LOS_MemAlloc(m_aucSysMem0, sizeof(*nb));
#endif
if (nb == NULL) {
HDF_LOGE("%s alloc net buf fail", __func__);
return NULL;
}
#if CACHE_ALIGNED_SIZE
data = (uint8_t *)LOS_MemAllocAlign(m_aucSysMem0, size, CACHE_ALIGNED_SIZE);
#else
data = (uint8_t *)LOS_MemAlloc(m_aucSysMem0, size);
#endif
if (data == NULL) {
HDF_LOGE("%s alloc data fail, size:%u", __func__, size);
LOS_MemFree(m_aucSysMem0, nb);
return NULL;
}
(void)memset_s(nb, sizeof(NetBuf), 0, sizeof(NetBuf));
(void)memset_s(data, size, 0, size);
nb->mem = data;
nb->len = size;
nb->dataLen = 0;
nb->bufs[E_HEAD_BUF].offset = 0;
nb->bufs[E_HEAD_BUF].len = 0;
nb->bufs[E_DATA_BUF].offset = 0;
nb->bufs[E_DATA_BUF].len = 0;
nb->bufs[E_TAIL_BUF].offset = 0;
nb->bufs[E_TAIL_BUF].len = size;
return nb;
}
* Free a net buffer.
*
* @param : nb A net buffer
* @return : void
*/
void NetBufFree(NetBuf *nb)
{
if (nb == NULL) {
return;
}
if (nb->mem != NULL) {
LOS_MemFree(m_aucSysMem0, nb->mem);
nb->mem = NULL;
}
LOS_MemFree(m_aucSysMem0, nb);
}
* Pop head room and add to data buffer
*
* @param : nb A net buffer
* len The length of remove data
* @return : The new start address of net buffer data on success or NULL on fail.
*/
void *NetBufPush(NetBuf *nb, uint32_t id, uint32_t len)
{
struct BufField *headBuf = NULL;
struct BufField *dataBuf = NULL;
struct BufField *tailBuf = NULL;
if (nb == NULL) {
return NULL;
}
headBuf = &nb->bufs[E_HEAD_BUF];
dataBuf = &nb->bufs[E_DATA_BUF];
tailBuf = &nb->bufs[E_TAIL_BUF];
switch (id) {
case E_HEAD_BUF:
if (dataBuf->len < len || nb->dataLen < len) {
HDF_LOGE("%s fail, datasize[%u], dataroom[%u], len[%u]", __func__, nb->dataLen, dataBuf->len, len);
return NULL;
}
headBuf->len += len;
dataBuf->offset += len;
dataBuf->len -= len;
nb->dataLen -= len;
break;
case E_DATA_BUF:
if (tailBuf->len < len) {
HDF_LOGE("%s fail, tailroom[%u], len[%u]", __func__, tailBuf->len, len);
return NULL;
}
nb->dataLen += len;
dataBuf->len += len;
tailBuf->offset += len;
tailBuf->len -= len;
break;
case E_TAIL_BUF:
if (dataBuf->len < len || nb->dataLen < len || tailBuf->offset < len) {
HDF_LOGE("%s fail, datasize[%u], netbuflen[%u], tailbufOffset[%u], len[%u]", __func__,
dataBuf->len, nb->dataLen, tailBuf->offset, len);
return NULL;
}
dataBuf->len -= len;
nb->dataLen -= len;
tailBuf->offset -= len;
tailBuf->len += len;
break;
default:
break;
}
return (nb->mem + dataBuf->offset);
}
* reduce data buffer and push to head room.
*
* @param : nb A net buffer
* len The length of add data
* @return : The new start address of net buffer data on success or NULL on fail.
*/
void *NetBufPop(NetBuf *nb, uint32_t id, uint32_t len)
{
struct BufField *headBuf = NULL;
struct BufField *dataBuf = NULL;
struct BufField *tailBuf = NULL;
if (nb == NULL) {
return NULL;
}
headBuf = &nb->bufs[E_HEAD_BUF];
dataBuf = &nb->bufs[E_DATA_BUF];
tailBuf = &nb->bufs[E_TAIL_BUF];
switch (id) {
case E_HEAD_BUF:
if (headBuf->len < len || dataBuf->offset < len) {
HDF_LOGE("%s fail, headroom[%u], len[%u]", __func__, headBuf->len, len);
return NULL;
}
headBuf->len -= len;
dataBuf->offset -= len;
dataBuf->len += len;
nb->dataLen += len;
break;
case E_DATA_BUF:
if (nb->dataLen < len || dataBuf->len < len) {
HDF_LOGE("%s fail, datasize[%u], dataroom[%u], len[%u]",
__func__, nb->dataLen, dataBuf->len, len);
return NULL;
}
headBuf->len += len;
dataBuf->offset += len;
dataBuf->len -= len;
nb->dataLen -= len;
break;
case E_TAIL_BUF:
if (tailBuf->len < len) {
HDF_LOGE("%s fail, tailroom[%u], len[%u]", __func__, tailBuf->len, len);
return NULL;
}
tailBuf->len -= len;
tailBuf->offset += len;
dataBuf->len += len;
nb->dataLen += len;
break;
default:
break;
}
return (nb->mem + dataBuf->offset);
}
* @brief Obtains the address of a specified buffer segment in a network data buffer.
*
* @param nb Indicates the pointer to the network data buffer.
* @param id Indicates the buffer segment ID.
*
* @return Returns the address of the specified buffer segment if the operation is successful;
* returns <b>NULL</b> if the buffer segment ID is invalid.
*
* @since 1.0
*/
uint8_t *NetBufGetAddress(const NetBuf *nb, uint32_t id)
{
if (nb == NULL) {
return NULL;
}
if (id < MAX_BUF_NUM) {
return (nb->mem + nb->bufs[id].offset);
}
return NULL;
}
* @brief Obtains the size of a specified buffer segment space in a network data buffer.
*
* @param nb Indicates the pointer to the network data buffer.
* @param id Indicates the buffer segment ID.
*
* @return Returns the size of the specified buffer segment space if the operation is successful;
* returns <b>NULL</b> if the buffer segment ID is invalid.
*
* @since 1.0
*/
uint32_t NetBufGetRoom(const NetBuf *nb, uint32_t id)
{
if (nb == NULL) {
return 0;
}
if (id < MAX_BUF_NUM) {
return nb->bufs[id].len;
}
return 0;
}
* @brief Obtains the actual data length of the data segment of a network data buffer.
*
* @param nb Indicates the pointer to the network data buffer.
*
* @return Returns the actual data length of the data segment.
*
* @since 1.0
*/
uint32_t NetBufGetDataLen(const NetBuf *nb)
{
if (nb == NULL) {
return 0;
}
return nb->dataLen;
}
* Expand a net buffer
*
* @param : nb A net buffer
* head The reserved size at head room
* tail The reserved size at tail room
* @return : 0 for success and others for failure
*/
int32_t NetBufResizeRoom(NetBuf *nb, uint32_t head, uint32_t tail)
{
uint8_t *data = NULL;
uint32_t size;
uint32_t offset;
if (nb == NULL) {
return HDF_ERR_INVALID_PARAM;
}
size = head + nb->len + tail;
#if CACHE_ALIGNED_SIZE
data = (uint8_t *)LOS_MemAllocAlign(m_aucSysMem0, size, CACHE_ALIGNED_SIZE);
#else
data = (uint8_t *)LOS_MemAlloc(m_aucSysMem0, size, CACHE_ALIGNED_SIZE);
#endif
if (data == NULL) {
HDF_LOGE("%s mem alloc fail, size:%u", __func__, size);
return HDF_FAILURE;
}
(void)memset_s(data, size, 0, size);
if (memcpy_s(data + head, size - head, nb->mem, nb->bufs[E_TAIL_BUF].offset) != EOK) {
HDF_LOGE("%s memcopy failed", __func__);
return HDF_FAILURE;
}
offset = nb->bufs[E_HEAD_BUF].len;
LOS_MemFree(m_aucSysMem0, nb->mem);
nb->mem = data;
nb->len = size;
nb->bufs[E_HEAD_BUF].offset = 0;
nb->bufs[E_HEAD_BUF].len = offset + head;
nb->bufs[E_DATA_BUF].offset = offset + head;
nb->bufs[E_DATA_BUF].len = nb->dataLen;
nb->bufs[E_TAIL_BUF].offset += head;
nb->bufs[E_TAIL_BUF].len += tail;
return HDF_SUCCESS;
}
* Concat a net buffer to another net buffer and free the net buffer for concatted.
*
* @param : nb A net buffer
* cnb The concat net buffer
* @return : 0 for success and others for failure
*/
int32_t NetBufConcat(NetBuf *nb, NetBuf *cnb)
{
uint32_t tailroom;
if (nb == NULL || cnb == NULL) {
return HDF_ERR_INVALID_PARAM;
}
tailroom = NetBufGetRoom(nb, E_TAIL_BUF);
if (tailroom < cnb->dataLen) {
HDF_LOGE("%s can not concat, tailroom[%u], len[%u]", __func__, tailroom, cnb->len);
return HDF_FAILURE;
}
if (nb->mem != NULL) {
uint8_t *src = cnb->mem + cnb->bufs[E_DATA_BUF].offset;
uint8_t *dst = nb->mem + nb->bufs[E_TAIL_BUF].offset;
if (memcpy_s(dst, tailroom, src, cnb->dataLen) != EOK) {
return HDF_FAILURE;
}
NetBufPush(nb, E_DATA_BUF, cnb->dataLen);
}
NetBufFree(cnb);
return HDF_SUCCESS;
}
* Convert net buffer to LWIP pbuf.
*
* @param : nb The net buffer
* @return : pbuf for lwip
*/
struct pbuf *NetBuf2Pbuf(const NetBuf *nb)
{
struct pbuf *p = NULL;
struct eth_hdr *hdr = NULL;
uint32_t len = NetBufGetDataLen(nb);
if ((len + ETH_PAD_SIZE) > MAX_CONVERSION_LEN) {
HDF_LOGE("%s netbuf len exceeds the maximum length of the pbuf!", __func__);
return NULL;
}
p = pbuf_alloc(PBUF_RAW, (uint16_t)(len + ETH_PAD_SIZE), PBUF_RAM);
if (p == NULL) {
HDF_LOGE("%s pbuf_alloc failed! len = %d", __func__, len);
return NULL;
}
hdr = (struct eth_hdr *)p->payload;
if (memcpy_s(&hdr->dest, len, NetBufGetAddress(nb, E_DATA_BUF), len) != EOK) {
pbuf_free(p);
HDF_LOGE("%s memcpy err!", __func__);
return NULL;
}
return p;
}
* Convert LWIP pbuf to net buffer.
*
* @param : netdev The net device object
* lwip_buf The LWIP net buffer
* @return : The net buffer
*/
NetBuf *Pbuf2NetBuf(const NetDevice *netdev, struct pbuf *lwipBuf)
{
NetBuf *nb = NULL;
struct pbuf *tmp = NULL;
uint32_t index = 0;
uint32_t offset = 0;
if (lwipBuf == NULL) {
return NULL;
}
nb = NetBufDevAlloc(netdev, lwipBuf->tot_len);
if (nb == NULL) {
HDF_LOGE("%s alloc net buffer failed! len = %d", __func__, lwipBuf->tot_len);
return NULL;
}
offset = nb->bufs[E_DATA_BUF].offset;
for (tmp = lwipBuf; tmp != NULL; tmp = tmp->next, index++) {
if (tmp->len == 0 || tmp->payload == NULL) {
continue;
}
if (NetBufGetRoom(nb, E_TAIL_BUF) < tmp->len) {
HDF_LOGE("%s fail, tailroom[%d], len[%d], idx[%d]",
__func__, NetBufGetRoom(nb, E_TAIL_BUF), tmp->len, index);
NetBufFree(nb);
return NULL;
}
if (memcpy_s(nb->mem + offset, tmp->len, tmp->payload, tmp->len) != EOK) {
HDF_LOGE("%s memcpy_s err!", __func__);
NetBufFree(nb);
return NULL;
}
NetBufPush(nb, E_DATA_BUF, tmp->len);
offset += tmp->len;
}
return nb;
}