#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

//#   ifdef _DEBUG
#   if 1
/* Non ANSI mapping */
#    define vsnprintf _vsnprintf
/* Buffer size for msvc implementation because it outputs to DebugOutput */
unsigned int aic3262_debug = 0;
#define DPRINTF_BUF_SZ  1024
#define AIC3262_DEBUG_ERROR     (1<< 0)

#undef CFW_FW_IF_ID
#define CFW_FW_IF_ID (0x3FA6D547)

#define CFW_FW_MAGIC (0xC0D1F1ED)
#ifndef CFW_MAX_ID
#    define CFW_MAX_ID          (64)	/**<Max length of string identifies*/
#    define CFW_MAX_VARS       (256)	/**<Number of "variables" alive at the*/
/**<same time in an acx file*/
#endif

/**
 * Transition Sequence Identifier
 *
 */
enum cfw_transition_t {
    CFW_TRN_INIT,
    CFW_TRN_RESUME,
    CFW_TRN_NEUTRAL,
    CFW_TRN_A_MUTE,
    CFW_TRN_D_MUTE,
    CFW_TRN_AD_MUTE,
    CFW_TRN_A_UNMUTE,
    CFW_TRN_D_UNMUTE,
    CFW_TRN_AD_UNMUTE,
    CFW_TRN_SUSPEND,
    CFW_TRN_EXIT,
    CFW_TRN_N
};

/**
 * CFW Block Type
 *
 * Block identifier
 *
 */
#pragma pack(push, 1)
enum  cfw_block_t {
    CFW_BLOCK_SYSTEM_PRE,
        CFW_BLOCK_A_INST,
        CFW_BLOCK_A_A_COEF,
        CFW_BLOCK_A_B_COEF,
        CFW_BLOCK_A_F_COEF,
        CFW_BLOCK_D_INST,
        CFW_BLOCK_D_A1_COEF,
        CFW_BLOCK_D_B1_COEF,
        CFW_BLOCK_D_A2_COEF,
        CFW_BLOCK_D_B2_COEF,
        CFW_BLOCK_D_F_COEF,
        CFW_BLOCK_SYSTEM_POST,
        CFW_BLOCK_N,
        CFW_BLOCK_INVALID,
};
/**
 * CFW Command
 *
 * Can be a either a
 *      -# single register write, or
 *      -# command
 *
 */
 /**
 * CFW Command
 * These commands do not appear in the register
 * set of the device.
 */
enum cfw_cmd_id {
    CFW_CMD_NOP = 0x80,
    CFW_CMD_DELAY,
    CFW_CMD_UPDTBITS,
    CFW_CMD_WAITBITS,
    CFW_CMD_LOCK,
    CFW_CMD_BURST,
    CFW_CMD_RBURST,
    CFW_CMD_LOAD_VAR_IM,
    CFW_CMD_LOAD_VAR_ID,
    CFW_CMD_STORE_VAR,
    CFW_CMD_COND,
    CFW_CMD_BRANCH,
    CFW_CMD_BRANCH_IM,
    CFW_CMD_BRANCH_ID,
    CFW_CMD_PRINT,
    CFW_CMD_OP_ADD = 0xC0,
    CFW_CMD_OP_SUB,
    CFW_CMD_OP_MUL,
    CFW_CMD_OP_DIV,
    CFW_CMD_OP_AND,
    CFW_CMD_OP_OR,
    CFW_CMD_OP_SHL,
    CFW_CMD_OP_SHR,
    CFW_CMD_OP_RR,
    CFW_CMD_OP_XOR,
    CFW_CMD_OP_NOT,
    CFW_CMD_OP_LNOT,
};

/**
 * Device Family Identifier
 *
 */
enum cfw_dfamily {
    AIC_DFM_TYPE_A,
        AIC_DFM_TYPE_B,
        AIC_DFM_TYPE_C,
        AIC_DFM_TYPE_HC
};

/**
 * Device Identifier
 *
 */
enum cfw_device {
    AIC_DEV_DAC3120,
        AIC_DEV_DAC3100,
        AIC_DEV_AIC3120,
        AIC_DEV_AIC3100,
        AIC_DEV_AIC3110,
        AIC_DEV_AIC3111,
        AIC_DEV_AIC36,

        AIC_DEV_AIC3206,
        AIC_DEV_AIC3204,
        AIC_DEV_AIC3254,
        AIC_DEV_AIC3256,
        AIC_DEV_AIC3253,

        AIC_DEV_AIC3212,
        AIC_DEV_AIC3262,
        AIC_DEV_AIC3017,
        AIC_DEV_AIC3008,
        AIC_DEV_AIC3213,
        AIC_DEV_AIC3263,
        AIC_DEV_AIC3266,
        AIC_DEV_AIC3268,
        AIC_DEV_AIC3285,

        AIC_DEV_PCM51X,
        AIC_NDEV
};
#pragma pack(pop)
static __inline void DPRINTF(int level, char const* const fmt, ...)
{
    if (aic3262_debug & level) {
        va_list args;
        char buf[DPRINTF_BUF_SZ];
        va_start(args, fmt);
        vsprintf(buf, fmt, args);
        va_end(args);
        OutputDebugStringA(buf);
        fprintf(stderr, "%s", buf);
    }
}
#    else
static __inline void DPRINTF(int level, char const* const fmt, ...) {}
#define AIC3262_DEBUG_ERROR     (0)
#    endif

struct firmware {
    int size;
    unsigned char* data;
};

/**
* CFW Delay
* Used for the cmd command delay
* Has one parameter of delay time in ms
*/
struct cfw_cmd_delay {
    unsigned short delay;
    unsigned char/*enum cfw_cmd_id*/ cid;
    unsigned char delay_fine;
};

/**
* CFW Lock
* Take codec mutex to avoid clashing with DAPM operations
*/
struct cfw_cmd_lock {
    unsigned short lock;
    unsigned char/*enum cfw_cmd_id*/ cid;
    unsigned char unused;
};


/**
 * CFW  UPDTBITS, WAITBITS, CHKBITS
 * Both these cmd commands have same arguments
 * cid will be used to specify which command it is
 * has parameters of book, page, offset and mask
 */
struct cfw_cmd_bitop {
    unsigned short unused1;
    unsigned char/*enum cfw_cmd_id*/ cid;
    unsigned char mask;
};

/**
 * CFW  CMD Burst header
 * Burst writes inside command array
 * Followed by burst address, first byte
 */
struct cfw_cmd_bhdr {
    unsigned short len;
    unsigned char/*enum cfw_cmd_id*/ cid;
    unsigned char unused;
};

/**
 * CFW  CMD Burst
 * Burst writes inside command array
 * Followed by data to the extent indicated in previous len
 * Can be safely cast to cfw_burst
 */
struct cfw_cmd_burst {
    unsigned char book;
    unsigned char page;
    unsigned char offset;
    unsigned char data[1];
};

/**
 * CFW Register
 *
 * A single reg write
 *
 */
union cfw_register {
    struct {
        unsigned char book;
        unsigned char page;
        unsigned char offset;
        unsigned char data;
    };
    unsigned int bpod;
};

/**
 * CFW  CMD Scratch register
 * For load
 *  if (svar != dvar)
 *      dvar = setbits(svar, mask) // Ignore reg
 *  else
 *      dvar = setbits(reg, mask)
 * For store
 *  if (svar != dvar)
 *      reg = setbits(svar,  dvar)
 *  else
 *      reg =  setbits(svar, mask)
 *
 */
struct cfw_cmd_ldst {
    unsigned char dvar;
    unsigned char svar;
    unsigned char/*enum cfw_cmd_id*/ cid;
    unsigned char mask;
};

/**
 * CFW  CMD Conditional
 * May only precede branch. Followed by nmatch+1 jump
 * instructions
 *   cond = svar&mask
 * At each of the following nmatch+1 branch command
 *   if (cond == match)
 *       take the branch
 */
struct cfw_cmd_cond {
    unsigned char svar;
    unsigned char nmatch;
    unsigned char/*enum cfw_cmd_id*/ cid;
    unsigned char mask;
};

/**
 * CFW  CMD Goto
 * For branch, break, continue and stop
 */
struct cfw_cmd_branch {
    unsigned short address;
    unsigned char/*enum cfw_cmd_id*/ cid;
    unsigned char match;
};

/**
 * CFW  Debug print
 * For diagnostics
 */
struct cfw_cmd_print {
    unsigned char fmtlen;
    unsigned char nargs;
    unsigned char/*enum cfw_cmd_id*/ cid;
    char fmt[1];
};

/**
 * CFW  Arithmetic and logical operations
 *  Bit 5 indicates if op1 is indirect
 *  Bit 6 indicates if op2 is indirect
 */
struct cfw_cmd_op {
    unsigned char op1;
    unsigned char op2;
    unsigned char/*enum cfw_cmd_id*/ cid;
    unsigned char dst;
};

union cfw_cmd {
    struct {
        unsigned short unused1;
        unsigned char/*enum cfw_cmd_id*/ cid;
        unsigned char unused2;
    };
    union cfw_register reg;
    struct cfw_cmd_delay delay;
    struct cfw_cmd_lock lock;
    struct cfw_cmd_bitop bitop;
    struct cfw_cmd_bhdr bhdr;
    struct cfw_cmd_burst burst;
    struct cfw_cmd_ldst ldst;
    struct cfw_cmd_cond cond;
    struct cfw_cmd_branch branch;
    struct cfw_cmd_print print;
    unsigned char     print_arg[4];
    struct cfw_cmd_op op;
};

/**
 * CFW Block
 *
 * A block of logically grouped sequences/commands/cmd-commands
 *
 */
struct cfw_block {
    enum cfw_block_t type;
    int ncmds;
    union cfw_cmd cmd[];
};

struct cfw_transition {
    char name[CFW_MAX_ID];	/**< Name of the transition*/
    char* desc;		/**< User string*/
    struct cfw_block* block;
};
/**
 * CFW PLL
 *
 * PLL configuration sequence and match critirea
 */
struct cfw_pll {
    char name[CFW_MAX_ID];	/**< Name of the PLL sequence*/
    char* desc;		/**< User string*/
    struct cfw_block* seq;
};

/**
 * CFW Image
 *
 * A downloadable image
 */
struct cfw_image {
    char name[CFW_MAX_ID];	/**< Name of the pfw/overlay/configuration*/
    char* desc;		/**< User string*/
    int mute_flags;
    struct cfw_block* block[CFW_BLOCK_N];
};

/**
 * CFW Control
 *
 * Run-time control for a process flow
 */
struct cfw_control {
    char name[CFW_MAX_ID];	/**< Control identifier*/
    char* desc;		/**< User string*/
    int mute_flags;

    int min;		/**< Min value of control (*100)*/
    int max;		/**< Max  value of control (*100)*/
    int step;		/**< Control step size (*100)*/

    int imax;		/**< Max index into controls array*/
    int ireset;		/**< Reset control to defaults*/
    int icur;		/**< Last value set*/
    struct cfw_block** output;	/**< Array of sequences to send*/
};

/**
 * Process flow
 *
 * Complete description of a process flow
 */
struct cfw_pfw {
    char name[CFW_MAX_ID];	/**< Name of the process flow*/
    char* desc;		/**< User string*/
    unsigned int version;
    unsigned char prb_a;
    unsigned char prb_d;
    int novly;		/**< Number of overlays (1 or more)*/
    int ncfg;		/**< Number of configurations (0 or more)*/
    int nctrl;		/**< Number of run-time controls*/
    struct cfw_image* base;	/**< Base sequence*/
    struct cfw_image** ovly_cfg;	/**< Overlay and cfg*/
                    /**< patches (if any)*/
    struct cfw_control** ctrl;	/**< Array of run-time controls*/
};

/**
 * Device audio mode
 *
 * Link operating modes to process flows,
 * configurations and sequences
 *
 */
struct cfw_mode {
    char name[CFW_MAX_ID];
    char* desc;		/**< User string*/
    unsigned int flags;
    unsigned char pfw;
    unsigned char ovly;
    unsigned char cfg;
    unsigned char pll;
    struct cfw_block* entry;
    struct cfw_block* exit;
};

struct cfw_asoc_toc_entry {
    char etext[CFW_MAX_ID];
    int mode;
    int cfg;
};

struct cfw_asoc_toc {
    int nentries;
    struct cfw_asoc_toc_entry entry[];
};

/**
 * CFW Project
 *
 * Top level structure describing the CFW project
 */
struct cfw_project {
    unsigned int magic;		/**< magic number for identifying F/W file*/
    unsigned int if_id;		/**< Interface match code */
    unsigned int size;		/**< Total size of the firmware (including this header)*/
    unsigned int cksum;		/**< CRC32 of the pickled firmware */
    unsigned int version;		/**< Firmware version (from CFD file)*/
    unsigned int tstamp;		/**< Time stamp of firmware build (epoch seconds)*/
    char name[CFW_MAX_ID];	/**< Project name*/
    char* desc;		/**< User string*/
    unsigned char/*enum cfw_dfamily*/ dfamily;	/**< Device family*/
    unsigned char/*enum cfw_device*/ device;	/**< Device identifier*/
    unsigned int flags;		/**< CFW flags*/

    struct cfw_transition** transition;	/**< Transition sequences*/

    unsigned short npll;		/**< Number of PLL settings*/
    struct cfw_pll** pll;	/**< PLL settings*/

    unsigned short npfw;		/**< Number of process flows*/
    struct cfw_pfw** pfw;	/**< Process flows*/

    unsigned short nmode;		/**< Number of operating modes*/
    struct cfw_mode** mode;	/**< Modes*/

    struct cfw_asoc_toc* asoc_toc;	/**< list of amixer controls*/
};

struct cfw_state {
    struct cfw_project* pjt;
    int cur_mode_id;
    int cur_pll;
    int cur_mode;
    int cur_pfw;
    int cur_ovly;
    int cur_cfg;
    int is_open;
};

struct aic3262_priv {
    struct cfw_state cfw_ps;
};

/*****************************************************************************
 *               Usage function
 ****************************************************************************/

static void usage(char *cmdline)
{

    fprintf(stderr, "Usage : %s [OPTIONS]\n", cmdline);
    fprintf(stderr, "Options :\n");
    fprintf(stderr, " -i string               : input filename\n");
    fprintf(stderr, " -debug                  : debug mode\n");
    fprintf(stderr, " -help                   : help message\n");
}

void release_firmware(struct firmware* fw)
{
    if (fw) {
        if (fw->data) free(fw->data);
        free(fw);
    }
}

static unsigned int crc32(unsigned int* pdata, int n)
{
    unsigned int crc = 0, i, crc_poly = 0x04C11DB7;	/* CRC - 32 */
    unsigned int msb;
    unsigned int residue_value = 0;
    int bits;

    for (i = 0; i < (n >> 2); i++) {
        bits = 32;
        while (--bits >= 0) {
            msb = crc & 0x80000000;
            crc = (crc << 1) ^ ((*pdata >> bits) & 1);
            if (msb)
                crc = crc ^ crc_poly;
        }
        pdata++;
    }

    switch (n & 3) {
    case 0:
        break;
    case 1:
        residue_value = (*pdata & 0xFF);
        bits = 8;
        break;
    case 2:
        residue_value = (*pdata & 0xFFFF);
        bits = 16;
        break;
    case 3:
        residue_value = (*pdata & 0xFFFFFF);
        bits = 24;
        break;
    }

    if (n & 3) {
        while (--bits >= 0) {
            msb = crc & 0x80000000;
            crc = (crc << 1) ^ ((residue_value >> bits) & 1);
            if (msb)
                crc = crc ^ crc_poly;
        }
    }
    return crc;
}

static int crc_chk(void* p, int n)
{
    struct cfw_project* pjt = (struct cfw_project*)p;
    unsigned int crc = pjt->cksum, crc_comp;

    pjt->cksum = 0;
    fprintf(stdout, "Entering crc %d\n", n);
    crc_comp = crc32((unsigned int *)p, n);
    if (crc_comp != crc) {
        fprintf(stderr, "CRC mismatch 0x%08X != 0x%08X\n", crc, crc_comp);
        return 0;
    }
    fprintf(stdout, "CRC pass\n");
    pjt->cksum = crc;
    return 1;
}

static inline void* aic3xxx_cfw_ndx2ptr(void* p, unsigned char* base)
{
    return &base[(int)p];
}

static inline char* aic3xxx_cfw_desc(void* p, unsigned char* base)
{
    if (p)
        return (char *)aic3xxx_cfw_ndx2ptr(p, base);
    return NULL;
}

static void aic3xxx_cfw_unpickle_image(struct cfw_image* im, void* p)
{
    int i;

    im->desc = aic3xxx_cfw_desc(im->desc, (unsigned char*)p);
    for (i = 0; i < CFW_BLOCK_N; ++i)
        if (im->block[i])
            im->block[i] = (struct cfw_block*)aic3xxx_cfw_ndx2ptr(im->block[i], (unsigned char*)p);
}

static void aic3xxx_cfw_unpickle_control(struct cfw_control* ct, void* p)
{
    int i;

    ct->output = (struct cfw_block**)aic3xxx_cfw_ndx2ptr(ct->output, (unsigned char*)p);
    ct->desc = aic3xxx_cfw_desc(ct->desc, (unsigned char *)p);
    for (i = 0; i <= ct->imax; ++i)
        ct->output[i] = (struct cfw_block *)aic3xxx_cfw_ndx2ptr(ct->output[i], 
            (unsigned char*)p);
}

static struct cfw_project* aic3xxx_cfw_unpickle(void* p, int n)
{
    struct cfw_project* pjt = (struct cfw_project*)p;
    void **q = NULL;
    int i, j;

    if (pjt->magic != CFW_FW_MAGIC || pjt->size != n ||
        pjt->if_id != CFW_FW_IF_ID || !crc_chk(p, n)) {
        fprintf(stderr, "Version mismatch: unable to load firmware\n");
        return NULL;
    }
    fprintf(stdout, "Loaded firmware inside unpickle\n");

    pjt->desc = aic3xxx_cfw_desc(pjt->desc, (unsigned char *)p);
    pjt->transition = (struct cfw_transition **)aic3xxx_cfw_ndx2ptr(pjt->transition, (unsigned char*)p);

    fprintf(stdout, "dfamily = %u, device = %u\n", pjt->dfamily, pjt->device);

    for (i = 0; i < CFW_TRN_N; i++) {
        if (!pjt->transition[i])
            continue;
        pjt->transition[i] = (struct cfw_transition *)aic3xxx_cfw_ndx2ptr(pjt->transition[i], 
            (unsigned char*)p);
        pjt->transition[i]->desc = aic3xxx_cfw_desc(pjt->transition[i]->desc, 
            (unsigned char*)p);
        pjt->transition[i]->block = (struct cfw_block*)aic3xxx_cfw_ndx2ptr(
            pjt->transition[i]->block, (unsigned char*)p);
    }
    pjt->pll = (struct cfw_pll**)aic3xxx_cfw_ndx2ptr(pjt->pll, (unsigned char*)p);
    for (i = 0; i < pjt->npll; i++) {
        pjt->pll[i] = (struct cfw_pll *)aic3xxx_cfw_ndx2ptr(pjt->pll[i], (unsigned char*)p);
        pjt->pll[i]->desc = aic3xxx_cfw_desc(pjt->pll[i]->desc, (unsigned char*)p);
        pjt->pll[i]->seq = (struct cfw_block*)aic3xxx_cfw_ndx2ptr(pjt->pll[i]->seq, (unsigned char*)p);
    }

    pjt->pfw = (struct cfw_pfw **)aic3xxx_cfw_ndx2ptr(pjt->pfw, (unsigned char*)p);
    for (i = 0; i < pjt->npfw; i++) {
        fprintf(stdout, "loading pfw %d\n", i);
        pjt->pfw[i] = (struct cfw_pfw*)aic3xxx_cfw_ndx2ptr(pjt->pfw[i], (unsigned char*)p);
        pjt->pfw[i]->desc = aic3xxx_cfw_desc(pjt->pfw[i]->desc, (unsigned char*)p);
        if (pjt->pfw[i]->base) {
            pjt->pfw[i]->base = (struct cfw_image*)aic3xxx_cfw_ndx2ptr(
                pjt->pfw[i]->base, (unsigned char*)p);
            aic3xxx_cfw_unpickle_image(pjt->pfw[i]->base, p);
        }
        pjt->pfw[i]->ovly_cfg = (struct cfw_image** )aic3xxx_cfw_ndx2ptr(
            pjt->pfw[i]->ovly_cfg, (unsigned char*)p);
        for (j = 0; j < pjt->pfw[i]->novly * pjt->pfw[i]->ncfg; ++j) {
            pjt->pfw[i]->ovly_cfg[j] = (struct cfw_image*)aic3xxx_cfw_ndx2ptr(
                pjt->pfw[i]->ovly_cfg[j], (unsigned char*)p);
            aic3xxx_cfw_unpickle_image(pjt->pfw[i]->ovly_cfg[j], p);
        }
        if (pjt->pfw[i]->nctrl)
            pjt->pfw[i]->ctrl = (struct cfw_control**)aic3xxx_cfw_ndx2ptr(
                pjt->pfw[i]->ctrl, (unsigned char*)p);
        for (j = 0; j < pjt->pfw[i]->nctrl; ++j) {
            pjt->pfw[i]->ctrl[j] = (struct cfw_control*)aic3xxx_cfw_ndx2ptr(
                pjt->pfw[i]->ctrl[j], (unsigned char*)p);
            aic3xxx_cfw_unpickle_control(pjt->pfw[i]->ctrl[j], p);
        }
    }

    fprintf(stdout, "loaded pfw's\n");
    pjt->mode = (struct cfw_mode**)aic3xxx_cfw_ndx2ptr(pjt->mode, (unsigned char*)p);
    for (i = 0; i < pjt->nmode; i++) {
        pjt->mode[i] = (struct cfw_mode *)aic3xxx_cfw_ndx2ptr(pjt->mode[i], (unsigned char*)p);
        pjt->mode[i]->desc = aic3xxx_cfw_desc(pjt->mode[i]->desc, (unsigned char*)p);
        if (pjt->mode[i]->entry)
            pjt->mode[i]->entry = (struct cfw_block *)aic3xxx_cfw_ndx2ptr(
                pjt->mode[i]->entry, (unsigned char*)p);
        if (pjt->mode[i]->exit)
            pjt->mode[i]->exit = (struct cfw_block*)aic3xxx_cfw_ndx2ptr(
                pjt->mode[i]->exit, (unsigned char*)p);
    }
    if (pjt->asoc_toc)
        pjt->asoc_toc = (struct cfw_asoc_toc*) aic3xxx_cfw_ndx2ptr(pjt->asoc_toc, (unsigned char*)p);
    else {
        fprintf(stderr, "asoc_toc not defined.  FW version mismatch?");
        return NULL;
    }
    fprintf(stdout, "loaded modes");
    return pjt;
}

int aic3xxx_cfw_reload(struct cfw_state* ps, void* pcfw, int n)
{
    ps->pjt = aic3xxx_cfw_unpickle(pcfw, n);
    ps->cur_mode_id =
        ps->cur_mode = ps->cur_pll = ps->cur_pfw =
        ps->cur_ovly = ps->cur_cfg = -1;
    if (ps->pjt == NULL)
        return -1;
    return 0;
}

static int aic3262_load_container(const struct firmware* fw, struct aic3262_priv* private_ds)
{
    int ret = 0;

    ret = aic3xxx_cfw_reload(&private_ds->cfw_ps, (void*)fw->data,
        fw->size);
    if (ret < 0) { /* reload failed */
        fprintf(stderr, "Firmware binary load failed\n");
        goto out;
    }

out:
    return 0;
}

int main(int argc, char* argv[])
{
    int conf_no = -1, i = 0, rc = 0;
    unsigned char block_type = -1;
    char *ARG_INPUTFILE = NULL;
    FILE* fr = NULL;
    struct firmware* fw = NULL;
    struct aic3262_priv* private_ds = NULL;
    char* cmd = strrchr(argv[0], '\\');
    cmd = (NULL == cmd) ? argv[0] : (cmd + 1);
    for (i = 1; i < argc; i++) {
        if (strcmp("-i", argv[i]) == 0 && i < argc - 1) {
            i++;
            ARG_INPUTFILE = argv[i];
        } else if (strcmp("-debug", argv[i]) == 0) {
#if defined(_DEBUG)
            aic3262_debug = 1;
#endif
        } else if (strcmp("-help", argv[i]) == 0) {
            usage(cmd);
            goto EXIT;
        } else {
            usage(cmd);
            goto EXIT;
        }
    }
    if (NULL == ARG_INPUTFILE) {
        usage(cmd);
        goto EXIT;
    }
    fr = fopen(ARG_INPUTFILE, "rb");
    if (NULL == fr) {
        printf("can't open bin file!\n");
        goto EXIT;
    }
    fw = (struct firmware*) calloc(1, sizeof(struct firmware));
    if (NULL == fw) {
        printf("FW memory failed!\n");
        goto EXIT;
    }
    fseek(fr, 0L, SEEK_END);
    fw->size = ftell(fr);
    fseek(fr, 0L, SEEK_SET);

    if (fw->size <= 0) {
        goto EXIT;
    }

    fw->data = (unsigned char*)calloc(1, fw->size);
    if (NULL == fw->data) {
        printf("%s:%u:Memory alloc failed!\n", __func__, __LINE__);
        goto EXIT;
    }

    rc = fread(fw->data, sizeof(unsigned char), fw->size, fr);
    if (rc <= 0) {
        printf("File read error\n");
        goto EXIT;
    }

    private_ds = (struct aic3262_priv*)calloc(1, sizeof(struct aic3262_priv));
    if (!private_ds) {
        printf("%s:%u:Memory alloc failed!\n", __func__, __LINE__);
        goto EXIT;
    }

    rc = aic3262_load_container(fw, private_ds);
    printf("%s Bin loading requested: %d\n", __func__, rc);

EXIT:
    if (fr) fclose(fr);

    if (private_ds) {
        release_firmware(fw);
        free(private_ds);
    }
    return 0;
}