...
The DMA controller is initialized and the hardware is informed of the address of descriptor list.
Panel | ||
---|---|---|
| ||
Panel | ||
---|---|---|
| ||
(1) The result of |
Panel | ||
---|---|---|
| ||
Panel | ||
---|---|---|
| ||
(1) µC/TCP-IP allocates a list of descriptors based on the network device driver configuration and sets each address field to point to the start address of a receive buffer. |
Panel | ||
---|---|---|
| ||
Panel | ||
---|---|---|
| ||
(1) The network device driver initializes three pointers. One to track the current descriptor which is expected to contain the next received frame (2) A second pointer to remember the descriptor list boundaries. (3) Finally, the DMA controller is initialized and hardware is informed of the descriptor list starting address. |
Reception
Panel | ||
---|---|---|
| ||
Panel | ||
---|---|---|
| ||
(1) With each new received frame, the network device driver increments (2) The hardware applies the same logic to an internal descriptor pointer. |
When a received frame is processed, the driver gets a pointer to a new data buffer and updates the current descriptor address field. The previous buffer address is passed to the protocol stack for processing. If a buffer pointer cannot be obtained, the existing pointer remains in place and the frame is dropped.
...
You also have to initialize each descriptor. You must initialize descriptor fields according to the controller documentation. Note that the descriptor must be configured to be owned by the DMA and not the software. Here is the pseudo code of the descriptor ring initialization:
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
pdesc = (DEV_DESC *)pdev_data->RxBufDescPtrStart; (1)
pdev_data->RxBufDescPtrCur = (DEV_DESC *)pdesc; (2)
pdev_data->RxBufDescPtrEnd = (DEV_DESC *)pdesc + (pdev_cfg->RxDescNbr - 1); (3)
for (i = 0; i < pdev_cfg->RxDescNbr; i++) { (4)
pdesc->Field = value; (5)
pdesc->Status = ETH_DMA_RX_DESC_OWN; (6)
pdesc->Buf = NetBuf_GetDataPtr((NET_IF *)pif, (7)
(NET_TRANSACTION)NET_TRANSACTION_RX,
(NET_BUF_SIZE )NET_IF_ETHER_FRAME_MAX_SIZE,
(NET_BUF_SIZE )NET_IF_IX_RX,
(NET_BUF_SIZE *)0,
(NET_BUF_SIZE *)0,
(NET_TYPE *)0,
(NET_ERR *)perr);
if (*perr != NET_BUF_ERR_NONE) { (8)
return;
}
pdesc->Next = (DEV_DESC *)(pdesc + 1); (9)
pdesc++; (10)
} |
Panel | ||
---|---|---|
| ||
(1) Initialize the descriptor pointer to the first Rx buffer descriptor of (2) Initialize current descriptor pointer of (3) Initialize last descriptor pointer of (4) Repeat for each descriptor defined in (5) Initialize the description fields to their initial value as defined by the DMA Descriptor's documentation in the device data sheet. There might be more than a single field to define depending of the specifications of the DMA used (a field describing the size of the associated data buffer might be present and required to be initialized to the length of the requested buffer area below.) (6) Initialize the status bit of the descriptor to specify that it is owned by the DMA engine (not by the software). (7) Call (8) If an error occurred during the allocation of a buffer area, return, as it might mean that there is an issue with the values declared in (9) Initialize the next descriptor location of the current descriptor to the next descriptor using pointer arithmetic. (10) Increment the descriptor using pointer arithmetic. |
Once the Rx descriptor ring is ready, you have to configure controller registers to enable the controller reception. Controller's interrupt generation should be enabled for the following events: reception of a packet with and without errors and completed transmission of a packet with and without errors.
...
NetDev_Rx()
is called by core once a NetOS_IF_RxTaskSignal()
call has been made to recover the received packet. If data received is valid, this function must replace the buffer of the current descriptor with a free buffer. Also, the current descriptor must be restarted (owned by the DMA) to be able to receive again. RxBufDescPtrCur
must be moved to point on the next descriptor. The sub-function NetDev_RxDescPtrCurInc()
is called to restart the current descriptor and to move the pointer to the next descriptor. If an error has occurred, you have to set data and length pointers to 0 and return an error. If there is no free Rx buffer available, the packet must be discarded by leaving the current data buffer assigned to the DMA, increment the current descriptor and return an error to the µC/TCP-IP module. Here is some pseudo code of NetDev_Rx()
:
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
static void NetDev_Rx (NET_IF *pif,
CPU_INT08U **p_data,
CPU_INT16U *size,
NET_ERR *perr)
{
pdesc = (DEV_DESC *)pdev_data->RxBufDescPtrStart; (1)
if (pdesc owned by DMA) { (2)
*perr = NET_DEV_ERR_RX;
*size = 0;
*p_data = (CPU_INT08U *)0;
return;
}
if (is Rx error) { (3)
if needed, restart DMA;
if needed, NetDev_RxDescPtrCurInc();
*perr = NET_DEV_ERR_RX;
*size = 0;
*p_data = (CPU_INT08U *)0;
return;
}
if (is Data length valid) { (4)
if needed, NetDev_RxDescPtrCurInc();
*perr = NET_DEV_ERR_INVALID_SIZE;
*size = 0;
*p_data = (CPU_INT08U *)0;
return;
}
pbuf_new = NetBuf_GetDataPtr(..., perr); (5)
if (*perr != NET_BUF_ERR_NONE) {
NetDev_RxDescPtrCurInc();
*size = 0;
*p_data = (CPU_INT08U *)0;
return;
}
*size = pdesc->Length; (6)
*p_data = (CPU_INT08U *)pdesc->Buf; (7)
pdesc->Buf = pbuf_new; (8)
NetDev_RxDescPtrCurInc(); (9)
*perr = NET_DEV_ERR_NONE; (10)
} |
Panel | ||
---|---|---|
| ||
(1) Obtain pointer to the next ready descriptor. (2) If this descriptor is owned by the DMA (e.g., the DMA is currently receiving data or hasn't started receiving data yet). The descriptor has to be owned by the software to be processed. If owned by the DMA, set (3) If a reception error is reported in the descriptor set (4) If the frame length is either run or overrun set (5) Once every error has been handled, acquire a new data buffer to replace the one we're about to take from the descriptor. If no buffers are available set (6) Set (7) Set (8) Set the value of the descriptor’s data buffer to the newly allocated data area. (9) Increment the current descriptor to the next descriptor. (10) Set |
The following is the pseudo code for NetDev_RxDescPtrCurInc()
:
Code Block | ||||||||
---|---|---|---|---|---|---|---|---|
| ||||||||
pdesc = pdev_data->RxBufDescPtrCur; (1)
pdev_data->RxBufDescPtrCur = pdesc→Next; (2) |
Panel | ||
---|---|---|
| ||
(1) Get current (2) Set |
Stopping the Reception of Packets
NetDev_Stop()
is called to shutdown a network interface hardware by disabling the receiver and transmitter, disabling receive and transmit interrupts, free all receive descriptors and deallocate all transmit buffers. When the interface is stopped, you must deallocate the DMA descriptor ring. To do that, a sub-function is called NetDev_RxDescFreeAll()
where each descriptor’s buffer is freed and the DMA controller control is disabled:
Code Block |
---|
pdesc = pdev_data->RxBufDescPtrStart; (1)
for (i = 0; i < pdev_cfg->RxDescNbr; i++) { (2)
pdesc_data = (CPU_INT08U *)(pdesc->Addr); (3)
NetBuf_FreeBufDataAreaRx(pif->Nbr, pdesc_data); (4)
pdesc->Status = Not owned by the controller (5)
pdesc++; (6)
} |
Panel | ||
---|---|---|
| ||
(1) Get (2) For each descriptor defined in (3) Get the address of the descriptor's buffer. (4) Deallocate the buffer area. (5) Set the status of the descriptor to be owned by the software (to disable reception on that descriptor). (6) Increment the current descriptor using pointer arithmetic. |