AR4100 Flash Agent Source Code

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

AR4100 Flash Agent Source Code

Jump to solution
1,950 Views
kcameron
Contributor III

Hi everyone,

I've searched around and have not found any posts that are up to date in regards to this problem.

I've implemented the AR4100 WiFi chip into a design that interfaces to it with a K60 processor. In order for the AR4100 to run properly it needs it's SPI flash memory chip to be programmed with a MAC address and other configuration information. Freescale provides a zip file entitled "MFG_Files&Docs" as part of their patch "TWR_WIFI_AR4100_MFG_FILES_PATCH" available on the TWR-WIFI-AR4100 page  that contains an app note entitled "80-Y2220-4_A_Programming_AR4100_SIP_on_the_Freescale_Tower_AppNote.pdf" to describe how to program this flash chip. They also provide a pre-compiled binary ("flash_agent.afx") that is built to run on the K60N512 and an .EXE file ("host.exe") that runs on a PC command line to send the MAC address over through a serial port. I've been able to run the pre-compiled binary and the .EXE file to program the flash chip successfully on a TWR-K60N512 for my run of prototype boards.

The problem I am having is that I need to program the flash chips before soldering them onto my custom PCB, and the "programmer" I have made using the tower  boards is not practical for manufacturing. What I really need is to be able to modify the flash agent firmware so it will run on my custom PCB or on a custom programmer PCB so that I can streamline this process for higher volume manufacturing.

Also, the K60N512 is now obsolete and has been replaced by the K60DN512, on which the flash agent firmware does not run on.

In the App note I described above, it mentions that the source code for the flash agent firmware is available, but I can't for the life of me find it anywhere. I've found other posts that mention you can obtain the source code from the patch ("TWR_WIFI_AR4100_MFG_FILES_PATCH"), but it seems to have been removed in recent versions of this patch.

Can anyone point me in the right direction here as to where I can find the flash agent source code? Any other ideas that might be helpful?

Thanks in advance,

Kyle

Labels (1)
1 Solution
1,212 Views
kcameron
Contributor III

Well, no thanks to Freescale, I was able to find the source code and get it working. Another user posted a Dropbox link that contains the source code they downloaded before it was taken offline.

This is the thread:

https://community.freescale.com/message/337729#337729

And this is the Dropbox link:

https://www.dropbox.com/s/sa766llgi6fwxfn/TWR_WIFI_AR4100_MQX3_8_0_Patch.exe

I was able to add this code to my firmware on my custom PCB to allow it to interface with the host.exe program provided in the "MFG_Files&Docs" over a serial port. I then recorded the communications from the host.exe program to my board and obtained the information I needed to allow me to flash the SPI memory chip from within my code using a .bin file I created with the flash image and saved to my on board SD card. If anyone is interested I can share my source code for doing this or help you implement your own solution.

View solution in original post

0 Kudos
11 Replies
1,212 Views
RadekS
NXP Employee
NXP Employee

Unfortunately,

main part of your question should be addressed to Atheros (Qualcomm). flash_agent.afx and host.exe aren’t Freescale products and they were delivered by Atheros.

About second part of your question = legacy TWR-K60N512)

We already prepared new version of TWR_WIFI_AR4100 MQX patch for MQX4.1 which will support “all” Kinetis boards include boards without ENET module. “all” here means all Kinetis boards with more than 64kB RAM.

Unfortunately this patch wasn’t still officially released and currently it is in hand of marketing and lawyers…   

And this doesn't solve issue with Flash Agent...


Have a great day,
RadekS

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos
1,212 Views
ThomNet
Contributor III

Hi Radek S,

is there anything new on your comment:

We already prepared new version of TWR_WIFI_AR4100 MQX patch for MQX4.1 which will support “all” Kinetis boards include boards without ENET module. “all” here means all Kinetis boards with more than 64kB RAM.

Unfortunately this patch wasn’t still officially released and currently it is in hand of marketing and lawyers…  


Can you tell, when the new patch will be available?


Thanks


Thomas

0 Kudos
1,212 Views
RadekS
NXP Employee
NXP Employee

According last news, unfortunately an agreement between Freescale and Atheros was not concluded.

Therefore this patch will be not officially released.

I am sorry.

0 Kudos
1,212 Views
ThomNet
Contributor III

Hi,

thanks for your answer. Does this mean, it is postponed or is it grounded?

Thanks again

Thomas

0 Kudos
1,212 Views
RadekS
NXP Employee
NXP Employee

Rather grounded, but I am not directly involved in this area, therefore we never know

0 Kudos
1,212 Views
ThomNet
Contributor III

HI,

no good news, but thanks again.

Keep me please up to date, if somethings changing.

Greetings from Berlin

Thomas

0 Kudos
1,212 Views
kcameron
Contributor III

Hi RadekS,

Thank you for your response.

Can you please provide me with who I need to contact from Qualcomm to obtain the source code?

Thanks, and you have a great day too.

Kyle

0 Kudos
1,213 Views
kcameron
Contributor III

Well, no thanks to Freescale, I was able to find the source code and get it working. Another user posted a Dropbox link that contains the source code they downloaded before it was taken offline.

This is the thread:

https://community.freescale.com/message/337729#337729

And this is the Dropbox link:

https://www.dropbox.com/s/sa766llgi6fwxfn/TWR_WIFI_AR4100_MQX3_8_0_Patch.exe

I was able to add this code to my firmware on my custom PCB to allow it to interface with the host.exe program provided in the "MFG_Files&Docs" over a serial port. I then recorded the communications from the host.exe program to my board and obtained the information I needed to allow me to flash the SPI memory chip from within my code using a .bin file I created with the flash image and saved to my on board SD card. If anyone is interested I can share my source code for doing this or help you implement your own solution.

0 Kudos
1,212 Views
kcameron
Contributor III

This is going to be a very long post, but I'll try to describe in detail how I did it.

I used the source above to add the code that communicates to host.exe to my own project. This allowed me to snoop on the communications to host.exe in order to determine the information I needed to do the flashing on my own. It turns out this involves sending commands to the AR4100 that contain an address, some data and a length that tells the AR4100 to flash it's SPI memory. Once the memory is flashed, an execute command is needed that tells the AR4100 to reprogram it's firmware.

After the AR4100 is programmed, there is another command that lets you program the MAC address.

I've designed the flashing process as follows.

  1. When my application boots, it looks for a location in SRAM to see if it is FF (which it will be after a fresh install of your application).
  2. If it sees FF, it boots the AR4100 in BMI mode which is needed in order to send the commands to flash the SPI memory on the AR4100
  3. After booting into BMI mode, I look for the presence of a .bin file I created (attached) that is a modified version of the "flashotp.bin" file Freescale provides.
  4. If the .bin file exists, it proceeds to read from it and send the data it contains to the SPI memory on the AR4100 via the flash commands
  5. After the programming is complete, it sends the execute command
  6. After the execute completes, the location in SRAM is cleared and the processor is rebooted.
  7. After booting into normal mode, the MAC address is programmed by sending the appropriate command to the AR4100

All of the commands I'm talking about are sent using functions I had to define in RTCS.c, which I have modified to suit my needs.

Now, to handle booting into BMI mode or normal mode, modify your "initialize_networking()" function in RTCS.c to look like the following. The key things to note are lines 32-41 where I decide how to boot the AR4100, then lines 57-65 where I either setup RTCS normally, or call the "handle_wifi_flash()" function. You'll need to add the attached file entitled "atheros_wifi_init.c" to your application's source directory.

/*FUNCTION*--------------------------------------------------------------------

*

* Function Name  : initialize_networking()

* Returned Value  : void

* Comments        : Sets up the TCP/IP stack, Wi-Fi device, Telnet and FTP

*END*------------------------------------------------------------------------*/

void initialize_networking(boolean mode)

{

    int_32 error = RTCS_ERROR;

    _enet_address IPCFG_default_enet_address = BSP_DEFAULT_ENET_OUI;

    uint_32 IPCFG_default_enet_device = BSP_DEFAULT_ENET_DEVICE;

    _ip_address IPCFG_default_ip_address = DEFAULT_IP_ADDRESS;

#if RTCS_MINIMUM_FOOTPRINT

    /* runtime RTCS configuration for devices with small RAM, for others the default BSP setting is used */

    _RTCSPCB_init          =  4;

    _RTCSPCB_grow          =  2;

    _RTCSPCB_max          =  6;                              

    _RTCS_msgpool_init    =  4;

    _RTCS_msgpool_grow    =  2;

    _RTCS_msgpool_max      =  6;                              

    _RTCS_socket_part_init =  4;

    _RTCS_socket_part_grow =  2;

    _RTCS_socket_part_max  =  6;                              

    _RTCSTASK_stacksize    = 1900; // TODO: (KDC) this was 3000

    _RTCSTASK_priority    = 6;

#endif

    error = (int_32)RTCS_create();

    if (error == RTCS_OK)

    {

        if (mode == NORMAL_MODE)

        {

            // Set up the driver to boot the AR4100 normally

            atheros_driver_init();

        }

        else

        {

            // Set up the driver to boot the AR4100 in BMI mode (used for programming the firmware)

            atheros_driver_init_bmi();

        }

        ENET_get_mac_address(IPCFG_default_enet_device,IPCFG_default_ip_address, IPCFG_default_enet_address);

        error = (int_32)ipcfg_init_device_wifi(IPCFG_default_enet_device, IPCFG_default_enet_address);

        /* ENET_initialize is already called from within ipcfg_init_device.  We call it again

        * so that we can get a handle to the driver allowing this code to make direct driver

        * calls as needed. */

        error = (int_32)ENET_initialize_wifi(IPCFG_default_enet_device,IPCFG_default_enet_address,0,&handle);

        if (error != ENETERR_INITIALIZED_DEVICE)

        {

            debug_printf("Failed to initialize Wi-Fi...\n");

            SPIN_FOREVER;

        }

        if (mode == NORMAL_MODE)

        {

            // Set things up like you normally would here

        }

        else

        {

            // Now that we're in BMI mode and the driver is initialized, program the SPI flash for the AR4100

            handle_wifi_flash();

        }

    }

    else

    { // if the rtcs doesn't create

        printf("\nRTCS_Create failed !\n");

        _task_block();

    }

}

This is the implementation of the handle_wifi_flash() function. I'm assuming you have an SD card to save "wifi_flash_img.bin" to - if you don't have an SD card you'll have to figure something else out.

#define AR4100_FLASH_FILENAME "wifi_flash_img.bin"

#define WIFI_FLASH_START_ADDR 0x1234

#define FLASH_BUFF_SZ  100

#define WIFI_FLASH_LOAD_ADDR 0x946E00

/*FUNCTION*-------------------------------------------------------------

*

* Function Name  : handle_wifi_flash()

* Returned Value  : This function never returns.

* Comments        : Programs the SPI flash memory for the AR4100

*

*END*-----------------------------------------------------------------*/

void handle_wifi_flash(void)

{

    ATH_PROGRAM_FLASH_STRUCT flashMsg;

    ATH_IOCTL_PARAM_STRUCT  inout_param;

    uint_32      error = A_ERROR;

    LWGPIO_STRUCT led;

    boolean      led_blink = FALSE;

    MQX_FILE_PTR  file_handle;

    char          file_name[FILENAME_SIZE + 1]  = MAIN_ROOT_FILE_PATH;

    uint_8        buffer[FLASH_BUFF_SZ];

    printf("-------- AR4100 Flash Start\n");

    // Let the SD card task startup so we'll be able to access the file system

    _time_delay(2000);

    printf("Opening flash file on SD card ... ");

    // Open image file on SD card

    strncat((char *)file_name, AR4100_FLASH_FILENAME, strlen(AR4100_FLASH_FILENAME));

    file_handle = fopen(file_name, "r");

    if(NULL == file_handle)

    {

        printf("Could not open flash file.\n");

        return;

    }

    printf("Done!\n");

    printf("Sending flash commands to AR4100. The LED will blink for each command ... ");

    // Set the initial address

    flashMsg.load_addr = WIFI_FLASH_START_ADDR;

    // Point the buffer pointer at the buffer we'll be using to read in data

    flashMsg.buffer = buffer;

    // Read the first <length> bytes of the image file

    flashMsg.length = (A_UINT16)read(file_handle, buffer, FLASH_BUFF_SZ);

    //send flash command

    inout_param.cmd_id = ATH_PROGRAM_FLASH;

    inout_param.data = &flashMsg;

    inout_param.length = sizeof(flashMsg);

    error = handle_ioctl(&inout_param);

    if(A_ERROR == error)

    {

        printf("Flash command failed.\n");

        _task_block();

    }

    _time_delay(10);

    while(flashMsg.length == FLASH_BUFF_SZ)

    {

        // Increment the address

        flashMsg.load_addr += flashMsg.length;

        // Read the next <length> bytes of data from the file

        //Note: Might reach the end of the file, in which case the read length will be less than the requested read length

        flashMsg.length = (A_UINT16)read(file_handle, buffer, FLASH_BUFF_SZ);

        // Set the length again in case it changed

        inout_param.length = sizeof(flashMsg);

        // Send flash command

        error = handle_ioctl(&inout_param);

        if(A_ERROR == error)

        {

            printf("Flash command failed.\n");

            _task_block();

        }

        _time_delay(10);

    }

    // Close the file now that we're done with it

    fclose(file_handle);

    printf("Done!\n");

    // Tell AR4100 to execute from the location we programmed.

    flashMsg.load_addr = WIFI_FLASH_LOAD_ADDR;

    flashMsg.length = 0;

    flashMsg.result = WIFI_FLASH_OPTIONS;

    flashMsg.buffer = NULL;

    inout_param.cmd_id = ATH_EXECUTE_FLASH;

    inout_param.data = &flashMsg;

    inout_param.length = sizeof(flashMsg);

    printf("Sending execute command to AR4100. This will take ~30 seconds to complete ... ");

    error = handle_ioctl(&inout_param);

    if(A_ERROR == error)

    {

        printf("Execute command failed.\n");

        _task_block();

    }

    printf("Done!\n");

    printf("Rebooting into normal mode.\n");

    // Clear the flash in SRAM here!

    _time_delay(2000);

    // Reset the processor here!

}

Then, this is how I program the MAC address once I've flashed wifi.

/*FUNCTION*-------------------------------------------------------------

*

* Function Name  : set_wifi_mac_address()

* Returned Value  : A_OK if wifi MAC set successfully else ERROR CODE

* Comments        : Sets the Wi-Fi MAC using the lower level Atheros API

*

*END*-----------------------------------------------------------------*/

uint_32 set_wifi_mac_address(uint_8* address)

{

    ATH_IOCTL_PARAM_STRUCT inout_param;

    ATH_PROGRAM_MAC_ADDR_PARAM  mac_addr_struct;

    uint_32 error = A_ERROR;

    uint_8 current_mac[6];

    //Check to see if the value we are trying to program is already programmed.

    get_wifi_mac_address(current_mac);

    if (address[0] == current_mac[0] &&

      address[1] == current_mac[1] &&

      address[2] == current_mac[2] &&

      address[3] == current_mac[3] &&

      address[4] == current_mac[4] &&

      address[5] == current_mac[5])

    {

        printf("MAC address already programmed.\n");

        return A_OK;

    }

    else

    {

        mac_addr_struct.addr[0] = address[0];

        mac_addr_struct.addr[1] = address[1];

        mac_addr_struct.addr[2] = address[2];

        mac_addr_struct.addr[3] = address[3];

        mac_addr_struct.addr[4] = address[4];

        mac_addr_struct.addr[5] = address[5];

        inout_param.cmd_id = ATH_PROGRAM_MAC_ADDR;

        inout_param.data = &mac_addr_struct;

        inout_param.length = sizeof(mac_addr_struct);

        if (is_driver_initialized() != A_OK)

        {

            return A_ERROR;

        }

        error = handle_ioctl(&inout_param);

        if (error == A_OK)

        {

            uint_32 read_value = SCB_AIRCR;

            // Bring Wi-Fi down

            set_wifi_chip_state(WIFI_DOWN);

            // Wait for the AR4100 and driver to go down

            _time_delay(100);

            // Reset K60

            read_value &= ~SCB_AIRCR_VECTKEY_MASK;

            read_value |= SCB_AIRCR_VECTKEY(0x05FA);

            read_value |= SCB_AIRCR_SYSRESETREQ_MASK;

            _int_disable();

            SCB_AIRCR = read_value;

            while (1){}

        }

    }

    return error;

}

You'll need these functions also - they're called within the functions I've got above.

typedef enum

{

    WIFI_DOWN,

    WIFI_UP

} Wifi_Up_Down_State_t;

/*FUNCTION*-------------------------------------------------------------

*

* Function Name  : wifi_chip_state()

* Returned Value  : A_OK if wifi chip state set successfully else A_ERROR

* Comments        : Special command to bring driver + wifi device up or down without

*                  officially shutting down the driver. This allows a task to shutdown the chip

*                  but prevents other tasks from having invalid enet_ptr's.

*

*END*-----------------------------------------------------------------*/

static uint_32 set_wifi_chip_state(Wifi_Up_Down_State_t desired_up_Down_state)

{

    static LWGPIO_STRUCT  wifi_chip_pwd_pin;

    static boolean        wifi_chip_pwd_pin__is_initialised = FALSE;

    ATH_IOCTL_PARAM_STRUCT inout_param;

    uint_32                data_val;

    uint_32                error = A_ERROR;

    // initialise the WiFi chip power-down pin, once

    if (wifi_chip_pwd_pin__is_initialised == FALSE)

    {

        if (lwgpio_init(&wifi_chip_pwd_pin, WIFI_CHIP_PWD__GPIO_PIN, LWGPIO_DIR_OUTPUT, LWGPIO_VALUE_NOCHANGE) == FALSE)

        {

            debug_printf("Initialising PWD GPIO as output failed.\n");

            _task_block();

        }

        // switch pin functionality (MUX) to GPIO mode

        lwgpio_set_functionality(&wifi_chip_pwd_pin, WIFI_CHIP_PWD__GPIO_MUX);

        wifi_chip_pwd_pin__is_initialised = TRUE;

    }

    if (desired_up_Down_state == WIFI_DOWN)

    {

        // This is to prevent unhandled interrupts in the TCP/IP task

        ipcfg_unbind(BSP_DEFAULT_ENET_DEVICE);

        data_val = 0;

    }

    else

    {

          data_val = 1;

    }

    inout_param.cmd_id = ATH_CHIP_STATE;

    inout_param.data  = &data_val;

    inout_param.length = sizeof(data_val);

    // Send chip state command to AR4100

    error = handle_ioctl(&inout_param);

    if (error == A_OK)

    {

        if (desired_up_Down_state == WIFI_DOWN)

        {

            // De-assert PWD pin to keep AR4100 down. If this isn't done PWD will be floating after the driver goes down and the wifi power will cycle randomly.

            lwgpio_set_value(&wifi_chip_pwd_pin, LWGPIO_VALUE_LOW);

        }

    }

    else

    {

        // There was an error returned when we tried to send the command to the AR4100

        printf("Wi-Fi chip state could not be set...\n");

    }

    return error;

}

/*FUNCTION*--------------------------------------------------------------------

*

* Function Name  : handle_ioctl()

* Returned Value  : A_OK if mode set successful else A_ERROR or A_EBUSY

* Comments        : Sends a vendor specific command to the Wi-Fi device

*END*------------------------------------------------------------------------*/

static A_STATUS handle_ioctl(ATH_IOCTL_PARAM_STRUCT* inout_param)

{

    A_STATUS status = A_OK;

    uint_32 error = ENET_mediactl(handle, ENET_MEDIACTL_VENDOR_SPECIFIC, inout_param);

    if (ENET_OK != error)

    {

        status = (error == ENETERR_INPROGRESS) ? A_EBUSY : A_ERROR;

    }

    return status;

}

I hope this helps! I spent probably 40 or more hours trying to figure this out, so hopefully I've saved you a bunch of time.

Kyle

1,212 Views
RadekS
NXP Employee
NXP Employee

Thank you very much for your detail description and attached code.


Best Regards,
RadekS

0 Kudos
1,212 Views
huishao2
Contributor IV

Hi RadekS,

That is the thing I asked for.

Thanks!

Hui

0 Kudos