beaker1969 wrote on Tuesday, June 09, 2015:
Hi
I have been using the FreeRTOS_TCP in out latest product using the the STM32F429. In the Network address configuration stage i have needed the device to use a Link Layer Address if the DHCP fails. I hope that this will be implemented fully in a future release but in the meantime i have implemented a very simple version of it in my code. It is based on the description here http://www.zeroconf.org/QDIPv4LL.html
i have had to modify FreeRTOS_ARP.c and .h in order for checking if the IP address i wish to use is already in use on the network.
i know this is not a full implementation but it works good enough for what i need. I hope this helps anyone who may want to expand it.
in FreeRTOSIPConfig.h
i added
#define ipconfigDHCP_Link_Local_Addressing 1 // added by AW - new for Link Local Addressing.
The main work is done in the FreeRTOS_DHCP.c
in FreeRTOS_DHCP.c i modified the following
i added 3 new DHCP states.
typedef enum
{
eWaitingSendFirstDiscover = 0, /* Initial state. Send a discover the first time it is called, and reset all timers. */
eWaitingOffer, /* Either resend the discover, or, if the offer is forthcoming, send a request. */
eWaitingAcknowledge, /* Either resend the request. */
eLeasedAddress, /* Resend the request at the appropriate time to renew the lease. */
eNotUsingLeasedAddress, /* DHCP failed, and a default IP address is being used. */
eInitDummyWaitingAcknowledge, /* added by AW - init dummy acknowledge - this may be called many times if link local address is already being used */
eWaitingDummyAcknowledge, /* added by AW - for Link Local addressing */
eDummyAcknowledgeArpTest /* added by AW - for Link Local addressing */
} eDHCPState_t;
i added default Link Layer addressing address and subnet mask.
unsigned char LLAIPAddress[4] = {169,254,221,196};
unsigned char LLASubnetMask[4] = {255,255,0,0};
in vDHCPProcess i modified the following to the switch statement.
case eWaitingOffer :
/* Look for offers coming in. */
if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_OFFER ) == pdPASS )
{
/* An offer has been made, generate the request. */
xDHCPData.xDHCPTxTime = xTaskGetTickCount();
xDHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;
prvSendDHCPRequest( );
xDHCPData.eDHCPState = eWaitingAcknowledge;
}
else
{
/* Is it time to send another Discover? */
if( ( xTaskGetTickCount() - xDHCPData.xDHCPTxTime ) > xDHCPData.xDHCPTxPeriod )
{
/* Increase the time period, and if it has not got to the
point of giving up - send another discovery. */
xDHCPData.xDHCPTxPeriod <<= 1;
//temp = ipconfigMAXIMUM_DISCOVER_TX_PERIOD;
//if( xDHCPData.xDHCPTxPeriod <= ipconfigMAXIMUM_DISCOVER_TX_PERIOD) // original.
if( xDHCPData.xDHCPTxPeriod <= (TickType_t)ipconfigMAXIMUM_DISCOVER_TX_PERIOD && xNetworkAddressing.ulDefaultIPAddress == 0x00 ) //new version
{
xDHCPData.ulTransactionId++;
xDHCPData.xDHCPTxTime = xTaskGetTickCount();
xDHCPData.xUseBroadcast = !xDHCPData.xUseBroadcast;
prvSendDHCPDiscover( );
FreeRTOS_debug_printf( ( "vDHCPProcess: timeout %lu ticks\n",
xDHCPData.xDHCPTxPeriod ) );
}
else
{
FreeRTOS_debug_printf( ( "vDHCPProcess: giving up %lu > %lu ticks\n",
xDHCPData.xDHCPTxPeriod, ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) );
// only use fake ack if default IP address = 0x00 and link local addressing is used.
if(ipconfigDHCP_Link_Local_Addressing == 0x01 && xNetworkAddressing.ulDefaultIPAddress == 0x00)
{
//do a fake ACK
xDHCPData.eDHCPState = eInitDummyWaitingAcknowledge;
}
else
{
/* Revert to static IP address. */
taskENTER_CRITICAL();
{
*ipLOCAL_IP_ADDRESS_POINTER = xNetworkAddressing.ulDefaultIPAddress;
iptraceDHCP_REQUESTS_FAILED_USING_DEFAULT_IP_ADDRESS( xNetworkAddressing.ulDefaultIPAddress );
}
taskEXIT_CRITICAL();
xDHCPData.eDHCPState = eNotUsingLeasedAddress;
vIPSetDHCPTimerEnableState( pdFALSE );
/* DHCP failed, the default configured IP-address will be used
Now call vIPNetworkUpCalls() to send the network-up event, start Nabto
and start the ARP timer*/
vIPNetworkUpCalls( );
/* Close socket to ensure packets don't queue on it. */
FreeRTOS_closesocket( xDHCPData.xDHCPSocket );
xDHCPData.xDHCPSocket = NULL;
}
}
}
}
break;
i added the following case’s to the switch statement.
case eInitDummyWaitingAcknowledge:
//do a fake ACK
xDHCPData.xDHCPTxTime = xTaskGetTickCount();
xDHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;
xDHCPData.eDHCPState = eWaitingDummyAcknowledge;
//prepare xDHCPData with data to test
xDHCPData.ulOfferedIPAddress = FreeRTOS_inet_addr_quick( LLAIPAddress[ 0 ], LLAIPAddress[ 1 ], LLAIPAddress[ 2 ], LLAIPAddress[ 3 ] );
xDHCPData.ulLeaseTime = dhcpDEFAULT_LEASE_TIME; // don't care about lease time. just put anything.
xNetworkAddressing.ulNetMask = FreeRTOS_inet_addr_quick( LLASubnetMask[ 0 ], LLASubnetMask[ 1 ], LLASubnetMask[ 2 ], LLASubnetMask[ 3 ] );
flg_got_dummy_dhcp_ack = 0x01;
break;
case eWaitingDummyAcknowledge :
FreeRTOS_debug_printf( ( "vDHCPProcess: dummy acked %lxip\n", FreeRTOS_ntohl( xDHCPData.ulOfferedIPAddress ) ) );
/* DHCP completed. The IP address can now be used, and the
timer set to the lease timeout time. */
*ipLOCAL_IP_ADDRESS_POINTER = xDHCPData.ulOfferedIPAddress;
/* Setting the 'local' broadcast address, something like 192.168.1.255' */
xNetworkAddressing.ulBroadcastAddress = ( xDHCPData.ulOfferedIPAddress & xNetworkAddressing.ulNetMask ) | ~xNetworkAddressing.ulNetMask;
/* Close socket to ensure packets don't queue on it. not needed anymore as DHCP failed. but still need timer for ARP testing. */
FreeRTOS_closesocket( xDHCPData.xDHCPSocket );
xDHCPData.xDHCPSocket = NULL;
xDHCPData.xDHCPTxPeriod = 3000 + ((unsigned char)get_random_number() & 0x3ff) / portTICK_PERIOD_MS; // do ARP test every (3 + 0-1024mS) seconds.
xDHCPData.eDHCPState = eDummyAcknowledgeArpTest; // setting IP address manually so set to not using leased address mode.
flg_arp_clash = 0x00; //reset flag that shows if have ARP clash.
vARPSendGratuitous();
break;
case eDummyAcknowledgeArpTest:
if( ( xTaskGetTickCount() - xDHCPData.xDHCPTxTime ) > xDHCPData.xDHCPTxPeriod )
{
if(flg_arp_clash == 0x00)
{
//ARP OK. proceed.
iptraceDHCP_SUCCEDEED( xDHCPData.ulOfferedIPAddress );
/* DHCP succe, the default configured IP-address will be used
Now call vIPNetworkUpCalls() to send the network-up event, start Nabto
and start the ARP timer*/
vIPNetworkUpCalls( );
}
else
{
//ARP clashed - try another IP address
LLAIPAddress[ 2 ] = (unsigned char)(get_random_number() & 0xff); //get value 0-255. for ip address 3rd byte of IP address to try.
LLAIPAddress[ 3 ] = (unsigned char)(get_random_number() & 0xff); //get value 0-255. for ip address 4th byte of IP address to try.
xDHCPData.eDHCPState = eInitDummyWaitingAcknowledge;
}
}
break;
in FreeRTOS_ARP.c
i added
volatile unsigned char flg_arp_clash = 0x00; //added by AW. for use in DHCP for checking of Link Layer Addressing of IP address assigned.
i modified the ipARP_REPLY case statement in eARPProcessPacket routine.
case ipARP_REPLY :
vARPClash(pxARPHeader); //added by AW.
iptracePROCESSING_RECEIVED_ARP_REPLY( pxARPHeader->ulTargetProtocolAddress );
vARPRefreshCacheEntry( &( pxARPHeader->xSenderHardwareAddress ), pxARPHeader->ulSenderProtocolAddress );
break;
i added the following routine
static void vARPClash(xARPHeader_t *pxARPHeader )
{
//process received ARP frame to see if there is a clash.
if(pxARPHeader->ulSenderProtocolAddress == *ipLOCAL_IP_ADDRESS_POINTER)
{
extern void vApplicationARPClashHook(void);
flg_arp_clash = 0x01;
}
}
in FreeRTOS_ARP.h
extern volatile unsigned char flg_arp_clash; //added by AW. for use in DHCP for checking of Link Layer Addressing of IP address assigned.
the random number generator built into the STM32f429
uint32_t get_random_number(void)
{
//use HAL system to get random number.
uint32_t ret_value;
if(HAL_RNG_GenerateRandomNumber(&hrng, &ret_value) == HAL_OK)
{
return ret_value;
}
else
{
return 0;
}
}