W3150A+ UDP receive issue

Hi dkay,

The random register read/write operations work normally.

My original code was this:

/**************************************************************************//**
 * @brief
 *   Get received data from a UDP socket.
 * @param[in] socket
 *   Number of socket (0..3).
 * @param[out] recvdata
 *   Address of the buffer which receives the data.
 * @param[in] recvmax
 *   Maximum number of bytes to read (practically the destination buffer
 *   size). If the amount of data is more than this parameter, the first
 *   recvmax bytes will be copied into the buffer, the others will be
 *   discarded. Set to zero to disable this feature.
 * @param[out] info
 *   Points to a receive info structure which will be filled up with
 *   information about the action.
 * @return
 *   TRUE on success, otherwise false.
 *****************************************************************************/
bool W3150AP_udpRecv(uint8_t socket, uint8_t *recvdata, size_t recvmax, W3150AP_udpRecvInfo_t *info)
{
  uint16_t sbaddr = getSocketRegBaseAddress(socket);
  uint16_t recvlen, rx_rd, start, data_ahead;
  uint8_t data[UDP_HEADER_SIZE];

  /* check for errors */
  if (rxMemory[socket].size == 0) return false;   // socket is unusable
  SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RSR, data, 2);   // read received data size
  recvlen = (data[0] << 8) + data[1];   // (big-endian)
  if (recvlen == 0 || recvlen < UDP_HEADER_SIZE) return false;   // no data or error

  /* initialize */
  SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2);   // read read pointer
  rx_rd = (data[0] << 8) + data[1];   // (big-endian)
  start = rxMemory[socket].base + rx_rd % rxMemory[socket].size;   // compute starting address

  /* read header (circular read from the Rx buffer) */
  /*   the 8-byte header structure:
   *     offset 0..3:  source IP address
   *     offset 4..5:  source port number
   *     offset 6..7:  data size (in bytes)
   */
  data_ahead = rxMemory[socket].size - rx_rd % rxMemory[socket].size;   // compute amount of (possible) data ahead
  if (data_ahead < UDP_HEADER_SIZE) {   // read in 2 steps
    SPI_Read(&W3150AP_SpiCfg, start, data, data_ahead);   // read the first part of the header from the data ahead the read pointer
    SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, data + data_ahead, UDP_HEADER_SIZE - data_ahead);   // read the remaining part of header restarting from the base address
  } else {   // read in 1 step
    SPI_Read(&W3150AP_SpiCfg, start, data, UDP_HEADER_SIZE);   // read all
  }
  start = rxMemory[socket].base + (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size;   // update starting address

  /* process header */
  memcpy(info->srcip, data, 4);   // source IP address
  info->srcport = (data[4] << 8) + data[5];   // source port number (big-endian)
  data_ahead = (data[6] << 8) + data[7];   // data length (big-endian), data_ahead re-used
  info->rxlen = data_ahead;
  info->herror = recvlen - (UDP_HEADER_SIZE + data_ahead);

  /* read data (circular read from the Rx buffer) */
  if (recvmax != 0)   // determine the actual number of data to read
    info->rxlen_act = (recvlen - UDP_HEADER_SIZE > recvmax) ? recvmax : recvlen - UDP_HEADER_SIZE;
    else
    info->rxlen_act = recvlen - UDP_HEADER_SIZE;
  data_ahead = rxMemory[socket].size - (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size;   // compute amount of (possible) data ahead
  if (data_ahead < info->rxlen_act) {   // read in 2 steps
    SPI_Read(&W3150AP_SpiCfg, start, recvdata, data_ahead);   // read the first part of data from the data ahead the read pointer
    SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, recvdata + data_ahead, info->rxlen_act - data_ahead);   // read the remaining part of data restarting from the base address
  } else {   // read in 1 step
    SPI_Read(&W3150AP_SpiCfg, start, recvdata, info->rxlen_act);   // read all
  }

  /* finalize and indicate that Rx is processed */
  rx_rd += recvlen;
  data[0] = rx_rd >> 8;
  data[1] = rx_rd & 0xFF;
  SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2);   // re-write read pointer
  data[0] = SCR_RECV;    // receive processed indicator command
  SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_CR, data, 1);

  return true;
} /* W3150AP_udpRecv */

Before reading your message, I implemented a version that uses the UDP header size parameter (as the datasheet suggests) if the RX_RSR value is smaller than it. This works fine.

bool W3150AP_udpRecv(uint8_t socket, uint8_t *recvdata, size_t recvmax, W3150AP_udpRecvInfo_t *info)
{
  uint16_t sbaddr = getSocketRegBaseAddress(socket);
  uint16_t recvlen, rx_rd, start, data_ahead;
  uint8_t data[UDP_HEADER_SIZE];

  /* check for errors */
  if (rxMemory[socket].size == 0) return false;   // socket is unusable
  SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RSR, data, 2);   // read received data size
  recvlen = (data[0] << 8) + data[1];   // (big-endian)
  if (recvlen == 0 || recvlen < UDP_HEADER_SIZE) return false;   // no data or error

  /* initialize */
  SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2);   // read read pointer
  rx_rd = (data[0] << 8) + data[1];   // (big-endian)
  start = rxMemory[socket].base + rx_rd % rxMemory[socket].size;   // compute starting address

  /* read header (circular read from the Rx buffer) */
  /*   the 8-byte header structure:
   *     offset 0..3:  source IP address
   *     offset 4..5:  source port number
   *     offset 6..7:  data size (in bytes)
   */
  data_ahead = rxMemory[socket].size - rx_rd % rxMemory[socket].size;   // compute amount of (possible) data ahead
  if (data_ahead < UDP_HEADER_SIZE) {   // read in 2 steps
    SPI_Read(&W3150AP_SpiCfg, start, data, data_ahead);   // read the first part of the header from the data ahead the read pointer
    SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, data + data_ahead, UDP_HEADER_SIZE - data_ahead);   // read the remaining part of header restarting from the base address
  } else {   // read in 1 step
    SPI_Read(&W3150AP_SpiCfg, start, data, UDP_HEADER_SIZE);   // read all
  }
  start = rxMemory[socket].base + (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size;   // update starting address

  /* process header */
  memcpy(info->srcip, data, 4);   // source IP address
  info->srcport = (data[4] << 8) + data[5];   // source port number (big-endian)
  data_ahead = (data[6] << 8) + data[7];   // data length (big-endian), data_ahead re-used
  info->rxlen = data_ahead;
  info->herror = recvlen - (UDP_HEADER_SIZE + data_ahead);

  /* read data (circular read from the Rx buffer) */
  if (info->herror < 0) {    // XXX workaround for a possible bug: sometimes RX_RSR is smaller than the size encoded in the UDP header
    if (recvmax != 0)   // determine the actual number of data to read
      info->rxlen_act = (info->rxlen > recvmax) ? recvmax : info->rxlen;
      else
      info->rxlen_act = info->rxlen;
  } else {   // normal operation
    if (recvmax != 0)   // determine the actual number of data to read
      info->rxlen_act = (recvlen - UDP_HEADER_SIZE > recvmax) ? recvmax : recvlen - UDP_HEADER_SIZE;
      else
      info->rxlen_act = recvlen - UDP_HEADER_SIZE;
  }
  data_ahead = rxMemory[socket].size - (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size;   // compute amount of (possible) data ahead
  if (data_ahead < info->rxlen_act) {   // read in 2 steps
    SPI_Read(&W3150AP_SpiCfg, start, recvdata, data_ahead);   // read the first part of data from the data ahead the read pointer
    SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, recvdata + data_ahead, info->rxlen_act - data_ahead);   // read the remaining part of data restarting from the base address
  } else {   // read in 1 step
    SPI_Read(&W3150AP_SpiCfg, start, recvdata, info->rxlen_act);   // read all
  }

  /* finalize and indicate that Rx is processed */
  if (info->herror < 0)   // XXX workaround for a possible bug: sometimes RX_RSR is smaller than the size encoded in the UDP header
    rx_rd += info->rxlen_act + UDP_HEADER_SIZE;
    else   // normal operation
    rx_rd += recvlen;
  data[0] = rx_rd >> 8;
  data[1] = rx_rd & 0xFF;
  SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2);   // re-write read pointer
  data[0] = SCR_RECV;    // receive processed indicator command
  SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_CR, data, 1);

  return true;
} /* W3150AP_udpRecv */

Then I implemented the multiple RX_RSR reading suggested by you, and it also works well.

bool W3150AP_udpRecv(uint8_t socket, uint8_t *recvdata, size_t recvmax, W3150AP_udpRecvInfo_t *info)
{
  uint16_t sbaddr = getSocketRegBaseAddress(socket);
  uint16_t recvlen, rx_rd, start, data_ahead;
  uint8_t data[UDP_HEADER_SIZE];

  /* check for errors */
  if (rxMemory[socket].size == 0) return false;   // socket is unusable
  {
    uint16_t x1, x2;
    SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RSR, (uint8_t*) &x1, 2);
    do {
      x2 = x1;
      SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RSR, (uint8_t*) &x1, 2);
    } while (x2 != x1);
    recvlen = (((uint8_t*) &x1)[0] << 8) + ((uint8_t*) &x1)[1];
  }
  if (recvlen == 0 || recvlen < UDP_HEADER_SIZE) return false;   // no data or error

  /* initialize */
  SPI_Read(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2);   // read read pointer
  rx_rd = (data[0] << 8) + data[1];   // (big-endian)
  start = rxMemory[socket].base + rx_rd % rxMemory[socket].size;   // compute starting address

  /* read header (circular read from the Rx buffer) */
  /*   the 8-byte header structure:
   *     offset 0..3:  source IP address
   *     offset 4..5:  source port number
   *     offset 6..7:  data size (in bytes)
   */
  data_ahead = rxMemory[socket].size - rx_rd % rxMemory[socket].size;   // compute amount of (possible) data ahead
  if (data_ahead < UDP_HEADER_SIZE) {   // read in 2 steps
    SPI_Read(&W3150AP_SpiCfg, start, data, data_ahead);   // read the first part of the header from the data ahead the read pointer
    SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, data + data_ahead, UDP_HEADER_SIZE - data_ahead);   // read the remaining part of header restarting from the base address
  } else {   // read in 1 step
    SPI_Read(&W3150AP_SpiCfg, start, data, UDP_HEADER_SIZE);   // read all
  }
  start = rxMemory[socket].base + (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size;   // update starting address

  /* process header */
  memcpy(info->srcip, data, 4);   // source IP address
  info->srcport = (data[4] << 8) + data[5];   // source port number (big-endian)
  data_ahead = (data[6] << 8) + data[7];   // data length (big-endian), data_ahead re-used
  info->rxlen = data_ahead;
  info->herror = recvlen - (UDP_HEADER_SIZE + data_ahead);

  /* read data (circular read from the Rx buffer) */
  if (recvmax != 0)   // determine the actual number of data to read
    info->rxlen_act = (recvlen - UDP_HEADER_SIZE > recvmax) ? recvmax : recvlen - UDP_HEADER_SIZE;
    else
    info->rxlen_act = recvlen - UDP_HEADER_SIZE;
  data_ahead = rxMemory[socket].size - (rx_rd + UDP_HEADER_SIZE) % rxMemory[socket].size;   // compute amount of (possible) data ahead
  if (data_ahead < info->rxlen_act) {   // read in 2 steps
    SPI_Read(&W3150AP_SpiCfg, start, recvdata, data_ahead);   // read the first part of data from the data ahead the read pointer
    SPI_Read(&W3150AP_SpiCfg, rxMemory[socket].base, recvdata + data_ahead, info->rxlen_act - data_ahead);   // read the remaining part of data restarting from the base address
  } else {   // read in 1 step
    SPI_Read(&W3150AP_SpiCfg, start, recvdata, info->rxlen_act);   // read all
  }

  /* finalize and indicate that Rx is processed */
  rx_rd += recvlen;
  data[0] = rx_rd >> 8;
  data[1] = rx_rd & 0xFF;
  SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_RX_RD, data, 2);   // re-write read pointer
  data[0] = SCR_RECV;    // receive processed indicator command
  SPI_Write(&W3150AP_SpiCfg, sbaddr + ROFFS_SX_CR, data, 1);

  return true;
} /* W3150AP_udpRecv */

So my problem is solved, thank you :slightly_smiling_face: But I am wondering what is the reason of this thing that RX_RSR sometimes contains too small number…

If you are interested, I could send the whole driver. It only supports UDP, but it is much less complicated than the example driver provided by WIZnet.

Best wishes.