/** @file  uapevent.c
 *
 *  @brief Program to receive events from the driver/firmware of the uAP
 *         driver.
 * 
 * Copyright (C) 2008-2009, Marvell International Ltd. 
 *
 * This software file (the "File") is distributed by Marvell International 
 * Ltd. under the terms of the GNU General Public License Version 2, June 1991 
 * (the "License").  You may use, redistribute and/or modify this File in 
 * accordance with the terms and conditions of the License, a copy of which 
 * is available along with the File in the gpl.txt file or by writing to 
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
 * 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
 *
 * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE 
 * ARE EXPRESSLY DISCLAIMED.  The License provides additional details about 
 * this warranty disclaimer.
 *
 */
/****************************************************************************
Change log:
    03/18/08: Initial creation
****************************************************************************/

/****************************************************************************
        Header files
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <getopt.h>

#include <sys/socket.h>
#include <linux/netlink.h>
#include "uapevent.h"

/****************************************************************************
        Definitions
****************************************************************************/
/** Enable or disable debug outputs */
#define DEBUG   0

/****************************************************************************
        Global variables
****************************************************************************/
/** Termination flag */
int terminate_flag = 0;

/****************************************************************************
        Local functions
****************************************************************************/
/** 
 *  @brief Signal handler
 *
 *  @param sig      Received signal number
 *  @return         N/A
 */
void
sig_handler(int sig)
{
    printf("Stopping application.\n");
#if DEBUG
    printf("Process ID of process killed = %d\n", getpid());
#endif
    terminate_flag = 1;
}

/** 
 *  @brief Dump hex data
 *
 *  @param p        A pointer to data buffer
 *  @param len      The len of data buffer
 *  @param delim    Deliminator character
 *  @return         Hex integer
 */
static void
hexdump(void *p, s32 len, s8 delim)
{
    s32 i;
    u8 *s = p;
    for (i = 0; i < len; i++) {
        if (i != len - 1)
            printf("%02x%c", *s++, delim);
        else
            printf("%02x\n", *s);
        if ((i + 1) % 16 == 0)
            printf("\n");
    }
}

/** 
 *  @brief Prints a MAC address in colon separated form from raw data
 *
 *  @param raw      A pointer to the hex data buffer
 *  @return         N/A
 */
void
print_mac(u8 * raw)
{
    printf("%02x:%02x:%02x:%02x:%02x:%02x", (unsigned int) raw[0],
           (unsigned int) raw[1], (unsigned int) raw[2], (unsigned int) raw[3],
           (unsigned int) raw[4], (unsigned int) raw[5]);
    return;
}

/** 
 *  @brief Print usage information
 *
 *  @return         N/A
 */
void
print_usage(void)
{
    printf("\n");
    printf("Usage : uapevent.exe [-v] [-h]\n");
    printf("    -v               : Print version information\n");
    printf("    -h               : Print help information\n");
    printf("\n");
}

/** 
 *  @brief Parse and print STA deauthentication event data
 *
 *  @param buffer   Pointer to received event buffer
 *  @param size     Length of the received event data
 *  @return         N/A
 */
void
print_event_sta_deauth(u8 * buffer, u16 size)
{
    EVENTBUF_STA_DEAUTH *event_body = NULL;

    if (size < sizeof(EVENTBUF_STA_DEAUTH)) {
        printf("ERR:Event buffer too small!\n");
        return;
    }
    event_body = (EVENTBUF_STA_DEAUTH *) buffer;
    event_body->ReasonCode = uap_le16_to_cpu(event_body->ReasonCode);
    printf("EVENT: STA_DEAUTH\n");
    printf("Deauthenticated STA MAC: ");
    print_mac(event_body->StaMacAddress);
    printf("\nReason: ");
    switch (event_body->ReasonCode) {
    case 1:
        printf("Unspecified reason.\n");
        break;
    case 2:
        printf("Previous authentication no longer valid.\n");
        break;
    case 3:
        printf("Deauthenticated because sending STA is leaving IBSS or ESS.\n");
        break;
    case 4:
        printf("Disassociated due to inactivity.\n");
        break;
    case 5:
        printf
            ("Disassociated because AP is unable to handle all currently associated STAs.\n");
        break;
    case 6:
        printf("Class 2 frame received from nonauthenticated STA.\n");
        break;
    case 7:
        printf("Class 3 frame received from nonassociated STA.\n");
        break;
    case 8:
        printf("Disassociated because sending STA is leaving BSS.\n");
        break;
    case 9:
        printf
            ("STA requesting (re)association is not authenticated with responding STA.\n");
        break;
    case 10:
        printf
            ("Disassociated because the information in the Power Capability element is unacceptable.\n");
        break;
    case 11:
        printf
            ("Disassociated because the information in the Supported Channels element is unacceptable.\n");
        break;
    case 13:
        printf("Invalid information element.\n");
        break;
    case 14:
        printf("Message integrity code (MIC) failure.\n");
        break;
    case 15:
        printf("4-Way Handshake timeout.\n");
        break;
    case 16:
        printf("Group Key Handshake timeout.\n");
        break;
    case 17:
        printf("Information element in 4-Way Handshake different from\n");
        printf("   (Re)Association Request/Probe Response/Beacon frame.\n");
        break;
    case 18:
        printf("Invalid group cipher.\n");
        break;
    case 19:
        printf("Invalid pairwise cipher.\n");
        break;
    case 20:
        printf("Invalid AKMP.\n");
        break;
    case 21:
        printf("Unsupported RSN information element version.\n");
        break;
    case 22:
        printf("Invalid RSN information element capabilities.\n");
        break;
    case 23:
        printf("IEEE 802.1X authentication failed.\n");
        break;
    case 24:
        printf("Cipher suite rejected because of the security policy.\n");
        break;
    case 32:
        printf("Disassociated for unspecified, QoS-related reason.\n");
        break;
    case 33:
        printf
            ("Disassociated because QoS AP lacks sufficient bandwidth for this QoS STA.\n");
        break;
    case 34:
        printf
            ("Disassociated because excessive number of frames need to be acknowledged\n");
        printf
            (" but are not acknowledged due to AP transmissions and or poor channel conditions.\n");
        break;
    case 35:
        printf
            ("Disassociated because STA is transmitting outside the limits of its TXOPs.\n");
        break;
    case 36:
        printf
            ("Requested from peer STA as the STA is leaving the BSS or resetting.\n");
        break;
    case 37:
        printf
            ("Requested from peer STA as it does not want to use the mechanism.\n");
        break;
    case 38:
        printf("Requested from peer STA as the STA received frames using");
        printf("   the mechanism for which a setup is required.\n");
        break;
    case 39:
        printf("Requested from peer STA due to timeout.\n");
        break;
    case 45:
        printf("Peer STA does not support the requested cipher suite.\n");
        break;
    default:
        printf("Reserved or Unspecified\n");
        break;
    }
    return;
}

/** 
 *  @brief Prints mgmt frame
 *
 *  @param mgmt_tlv A pointer to mgmt_tlv
 *  @param tlv_len  Length of tlv payload
 *  @return         N/A
 */
void
print_mgmt_frame(MrvlIETypes_MgmtFrameSet_t * mgmt_tlv, int tlv_len)
{
    IEEEtypes_AssocRqst_t *assoc_req = NULL;
    IEEEtypes_ReAssocRqst_t *reassoc_req = NULL;
    IEEEtypes_AssocRsp_t *assoc_resp = NULL;
    u16 frmctl = 0;
    printf("\nMgmt Frame:\n");
    memcpy(&frmctl, &mgmt_tlv->FrameControl, sizeof(u16));
    printf("FrameControl: 0x%x\n", frmctl);
    if (mgmt_tlv->FrameControl.Type != 0) {
        printf("Frame type=%d subtype=%d:\n", mgmt_tlv->FrameControl.Type,
               mgmt_tlv->FrameControl.Subtype);
        hexdump(mgmt_tlv->FrameContents, tlv_len - sizeof(u16), ' ');
        return;
    }
    switch (mgmt_tlv->FrameControl.Subtype) {
    case SUBTYPE_ASSOC_REQUEST:
        printf("Assoc Request:\n");
        assoc_req = (IEEEtypes_AssocRqst_t *) mgmt_tlv->FrameContents;
        printf("CapInfo: 0x%x  ListenInterval: 0x%x \n",
               uap_le16_to_cpu(assoc_req->CapInfo),
               uap_le16_to_cpu(assoc_req->ListenInterval));
        printf("AssocReqIE:\n");
        hexdump(assoc_req->IEBuffer,
                tlv_len - sizeof(IEEEtypes_AssocRqst_t) -
                sizeof(IEEEtypes_FrameCtl_t), ' ');
        break;
    case SUBTYPE_REASSOC_REQUEST:
        printf("ReAssoc Request:\n");
        reassoc_req = (IEEEtypes_ReAssocRqst_t *) mgmt_tlv->FrameContents;
        printf("CapInfo: 0x%x  ListenInterval: 0x%x \n",
               uap_le16_to_cpu(reassoc_req->CapInfo),
               uap_le16_to_cpu(reassoc_req->ListenInterval));
        printf("Current AP address: ");
        print_mac(reassoc_req->CurrentApAddr);
        printf("\nReAssocReqIE:\n");
        hexdump(reassoc_req->IEBuffer,
                tlv_len - sizeof(IEEEtypes_ReAssocRqst_t) -
                sizeof(IEEEtypes_FrameCtl_t), ' ');
        break;
    case SUBTYPE_ASSOC_RESPONSE:
    case SUBTYPE_REASSOC_RESPONSE:
        if (mgmt_tlv->FrameControl.Subtype == SUBTYPE_ASSOC_RESPONSE)
            printf("Assoc Response:\n");
        else
            printf("ReAssoc Response:\n");
        assoc_resp = (IEEEtypes_AssocRsp_t *) mgmt_tlv->FrameContents;
        printf("CapInfo: 0x%x  StatusCode: %d  AID: 0x%x \n",
               uap_le16_to_cpu(assoc_resp->CapInfo),
               (int) (uap_le16_to_cpu(assoc_resp->StatusCode)),
               uap_le16_to_cpu(assoc_resp->AId) & 0x3fff);
        break;
    default:
        printf("Frame subtype = %d:\n", mgmt_tlv->FrameControl.Subtype);
        hexdump(mgmt_tlv->FrameContents, tlv_len - sizeof(u16), ' ');
        break;
    }
    return;
}

/** 
 *  @brief Parse and print STA associate event data
 *
 *  @param buffer   Pointer to received buffer
 *  @param size     Length of the received event data
 *  @return         N/A
 */
void
print_event_sta_assoc(u8 * buffer, u16 size)
{
    int tlvBufLeft = size;
    u16 tlvType, tlvLen;
    tlvbuf_header *tlv = NULL;
    MrvlIEtypes_WapiInfoSet_t *wapi_tlv = NULL;
    MrvlIETypes_MgmtFrameSet_t *mgmt_tlv = NULL;
    EVENTBUF_STA_ASSOC *event_body = NULL;
    if (size < sizeof(EVENTBUF_STA_ASSOC)) {
        printf("ERR:Event buffer too small!\n");
        return;
    }
    event_body = (EVENTBUF_STA_ASSOC *) buffer;
    printf("EVENT: STA_ASSOCIATE\n");
    printf("Associated STA MAC: ");
    print_mac(event_body->StaMacAddress);
    printf("\n");
    tlvBufLeft = size - sizeof(EVENTBUF_STA_ASSOC);
    if (tlvBufLeft < (int) sizeof(tlvbuf_header))
        return;
    tlv = (tlvbuf_header *) (buffer + sizeof(EVENTBUF_STA_ASSOC));

    while (tlvBufLeft >= (int) sizeof(tlvbuf_header)) {
        tlvType = uap_le16_to_cpu(tlv->type);
        tlvLen = uap_le16_to_cpu(tlv->len);
        if ((sizeof(tlvbuf_header) + tlvLen) > tlvBufLeft) {
            printf("wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", tlvLen, tlvBufLeft);
            break;
        }
        switch (tlvType) {
        case MRVL_WAPI_INFO_TLV_ID:
            wapi_tlv = (MrvlIEtypes_WapiInfoSet_t *) tlv;
            printf("WAPI Multicast PN:\n");
            hexdump(wapi_tlv->MulticastPN, tlvLen, ' ');
            break;
        case MRVL_MGMT_FRAME_TLV_ID:
            mgmt_tlv = (MrvlIETypes_MgmtFrameSet_t *) tlv;
            print_mgmt_frame(mgmt_tlv, tlvLen);
            break;
        default:
            printf("unknown tlv: %d\n", tlvType);
            break;
        }
        tlvBufLeft -= (sizeof(tlvbuf_header) + tlvLen);
        tlv = (tlvbuf_header *) ((u8 *) tlv + tlvLen + sizeof(tlvbuf_header));
    }
    return;
}

/** 
 *  @brief Parse and print BSS start event data
 *
 *  @param buffer   Pointer to received buffer
 *  @param size     Length of the received event data
 *  @return         N/A
 */
void
print_event_bss_start(u8 * buffer, u16 size)
{
    EVENTBUF_BSS_START *event_body = NULL;

    if (size < sizeof(EVENTBUF_BSS_START)) {
        printf("ERR:Event buffer too small!\n");
        return;
    }
    event_body = (EVENTBUF_BSS_START *) buffer;
    printf("EVENT: BSS_START ");
    printf("BSS MAC: ");
    print_mac(event_body->apMacAddress);
    printf("\n");
    return;
}

/** 
 *  @brief Prints station reject state
 *
 *  @param state      fail state
 *  @return         N/A
 */
void
print_reject_state(u8 state)
{
    switch (state) {
    case REJECT_STATE_FAIL_EAPOL_2:
        printf("Reject state: FAIL_EAPOL_2\n");
        break;
    case REJECT_STATE_FAIL_EAPOL_4:
        printf("Reject state: FAIL_EAPOL_4:\n");
        break;
    case REJECT_STATE_FAIL_EAPOL_GROUP_2:
        printf("Reject state: FAIL_EAPOL_GROUP_2\n");
        break;
    default:
        printf("ERR: unknown reject state %d\n", state);
        break;
    }
    return;
}

/** 
 *  @brief Prints station reject reason
 *
 *  @param reason      reason code
 *  @return         N/A
 */
void
print_reject_reason(u16 reason)
{
    switch (reason) {
    case IEEEtypes_REASON_INVALID_IE:
        printf("Reject reason: Invalid IE\n");
        break;
    case IEEEtypes_REASON_MIC_FAILURE:
        printf("Reject reason: Mic Failure\n");
        break;
    default:
        printf("Reject reason: %d\n", reason);
        break;
    }
    return;
}

/** 
 *  @brief Prints EAPOL state
 *
 *  @param state    eapol state
 *  @return         N/A
 */
void
print_eapol_state(u8 state)
{
    switch (state) {
    case EAPOL_START:
        printf("Eapol state: EAPOL_START\n");
        break;
    case EAPOL_WAIT_PWK2:
        printf("Eapol state: EAPOL_WAIT_PWK2\n");
        break;
    case EAPOL_WAIT_PWK4:
        printf("Eapol state: EAPOL_WAIT_PWK4\n");
        break;
    case EAPOL_WAIT_GTK2:
        printf("Eapol state: EAPOL_WAIT_GTK2\n");
        break;
    case EAPOL_END:
        printf("Eapol state: EAPOL_END\n");
        break;
    default:
        printf("ERR: unknow eapol state%d\n", state);
        break;
    }
    return;
}

/** 
 *  @brief Parse and print debug event data
 *
 *  @param buffer   Pointer to received buffer
 *  @param size     Length of the received event data
 *  @return         N/A
 */
void
print_event_debug(u8 * buffer, u16 size)
{
    EVENTBUF_DEBUG *event_body = NULL;
    if (size < sizeof(EVENTBUF_DEBUG)) {
        printf("ERR:Event buffer too small!\n");
        return;
    }
    event_body = (EVENTBUF_DEBUG *) buffer;
    printf("Debug Event Type: %s\n",
           (event_body->debugtype == 0) ? "EVENT" : "INFO");
    printf("%s log:\n",
           (uap_le32_to_cpu(event_body->debugIdMajor) ==
            DEBUG_ID_MAJ_AUTHENTICATOR) ? "Authenticator" : "Assoc_agent");
    if (uap_le32_to_cpu(event_body->debugIdMajor) == DEBUG_ID_MAJ_AUTHENTICATOR) {
        switch (uap_le32_to_cpu(event_body->debugIdMinor)) {
        case DEBUG_MAJ_AUTH_MIN_PWK1:
            printf("EAPOL Key message 1 (PWK):\n");
            hexdump((u8 *) & event_body->info.eapol_pwkMsg,
                    sizeof(EAPOL_KeyMsg_Debug_t), ' ');
            break;
        case DEBUG_MAJ_AUTH_MIN_PWK2:
            printf("EAPOL Key message 2 (PWK):\n");
            hexdump((u8 *) & event_body->info.eapol_pwkMsg,
                    sizeof(EAPOL_KeyMsg_Debug_t), ' ');
            break;
        case DEBUG_MAJ_AUTH_MIN_PWK3:
            printf("EAPOL Key message 3 (PWK):\n");
            hexdump((u8 *) & event_body->info.eapol_pwkMsg,
                    sizeof(EAPOL_KeyMsg_Debug_t), ' ');
            break;
        case DEBUG_MAJ_AUTH_MIN_PWK4:
            printf("EAPOL Key message 4: (PWK)\n");
            hexdump((u8 *) & event_body->info.eapol_pwkMsg,
                    sizeof(EAPOL_KeyMsg_Debug_t), ' ');
            break;
        case DEBUG_MAJ_AUTH_MIN_GWK1:
            printf("EAPOL Key message 1: (GWK)\n");
            hexdump((u8 *) & event_body->info.eapol_pwkMsg,
                    sizeof(EAPOL_KeyMsg_Debug_t), ' ');
            break;
        case DEBUG_MAJ_AUTH_MIN_GWK2:
            printf("EAPOL Key message 2: (GWK)\n");
            hexdump((u8 *) & event_body->info.eapol_pwkMsg,
                    sizeof(EAPOL_KeyMsg_Debug_t), ' ');
            break;
        case DEBUG_MAJ_AUTH_MIN_STA_REJ:
            printf("Reject STA MAC: ");
            print_mac(event_body->info.sta_reject.staMacAddr);
            printf("\n");
            print_reject_state(event_body->info.sta_reject.reject_state);
            print_reject_reason(uap_le16_to_cpu
                                (event_body->info.sta_reject.reject_reason));
            break;
        case DEBUG_MAJ_AUTH_MIN_EAPOL_TR:
            printf("STA MAC: ");
            print_mac(event_body->info.eapol_state.staMacAddr);
            printf("\n");
            print_eapol_state(event_body->info.eapol_state.eapolState);
            break;
        default:
            printf("ERR: unknow debugIdMinor: %d\n",
                   (int) uap_le32_to_cpu(event_body->debugIdMinor));
            hexdump(buffer, size, ' ');
            return;
        }
    } else if (uap_le32_to_cpu(event_body->debugIdMajor) ==
               DEBUG_ID_MAJ_ASSOC_AGENT) {
        switch (uap_le32_to_cpu(event_body->debugIdMinor)) {
        case DEBUG_ID_MAJ_ASSOC_MIN_WPA_IE:
            printf("STA MAC: ");
            print_mac(event_body->info.wpaIe.staMacAddr);
            printf("\n");
            printf("wpa ie:\n");
            hexdump(event_body->info.wpaIe.wpa_ie, MAX_WPA_IE_LEN, ' ');
            break;
        case DEBUG_ID_MAJ_ASSOC_MIN_STA_REJ:
            printf("Reject STA MAC: ");
            print_mac(event_body->info.sta_reject.staMacAddr);
            printf("\n");
            print_reject_state(event_body->info.sta_reject.reject_state);
            print_reject_reason(uap_le16_to_cpu
                                (event_body->info.sta_reject.reject_reason));
            break;
        default:
            printf("ERR: unknow debugIdMinor: %d\n",
                   (int) uap_le32_to_cpu(event_body->debugIdMinor));
            hexdump(buffer, size, ' ');
            return;
        }
    }
    return;
}

/** 
 *  @brief Parse and print received event information
 *
 *  @param event    Pointer to received event
 *  @param size     Length of the received event
 *  @return         N/A
 */
void
print_event(EVENTHEADER * event, u16 size)
{
    u32 event_id = event->EventId & EVENT_ID_MASK;
    switch (event_id) {
    case MICRO_AP_EV_ID_STA_DEAUTH:
        print_event_sta_deauth(event->EventData, size - EVENT_ID_LEN);
        break;
    case MICRO_AP_EV_ID_STA_ASSOC:
        print_event_sta_assoc(event->EventData, size - EVENT_ID_LEN);
        break;
    case MICRO_AP_EV_ID_BSS_START:
        print_event_bss_start(event->EventData, size - EVENT_ID_LEN);
        break;
    case MICRO_AP_EV_ID_DEBUG:
        print_event_debug(event->EventData, size - EVENT_ID_LEN);
        break;
    case MICRO_AP_EV_BSS_IDLE:
        printf("EVENT: BSS_IDLE\n");
        break;
    case MICRO_AP_EV_BSS_ACTIVE:
        printf("EVENT: BSS_ACTIVE\n");
        break;
    default:
        printf("ERR:Undefined event type (%X). Dumping event buffer:\n",
               (unsigned int) event_id);
        hexdump((void *) event, size, ' ');
        break;
    }
    return;
}

/** 
 *  @brief Read event data from netlink socket
 *
 *  @param sk_fd    Netlink socket handler
 *  @param buffer   Pointer to the data buffer
 *  @param nlh      Pointer to netlink message header
 *  @param msg      Pointer to message header
 *  @return         Number of bytes read or UAP_FAILURE
 */
int
read_event_netlink_socket(int sk_fd, unsigned char *buffer,
                          struct nlmsghdr *nlh, struct msghdr *msg)
{
    int count = -1;
    count = recvmsg(sk_fd, msg, 0);
#if DEBUG
    printf("DBG:Waiting for message from NETLINK.\n");
#endif
    if (count < 0) {
        printf("ERR:NETLINK read failed!\n");
        terminate_flag++;
        return UAP_FAILURE;
    }
#if DEBUG
    printf("DBG:Received message payload (%d)\n", count);
#endif
    if (count > NLMSG_SPACE(NL_MAX_PAYLOAD)) {
        printf("ERR:Buffer overflow!\n");
        return UAP_FAILURE;
    }
    bzero(buffer, NL_MAX_PAYLOAD);
    memcpy(buffer, NLMSG_DATA(nlh), count - NLMSG_HDRLEN);
#if DEBUG
    hexdump(buffer, count - NLMSG_HDRLEN, ' ');
#endif
    return count - NLMSG_HDRLEN;
}

/** 
 *  @brief Configure and read event data from netlink socket
 *
 *  @param sk_fd    Netlink socket handler
 *  @param buffer   Pointer to the data buffer
 *  @param timeout  Socket listen timeout value
 *  @param nlh      Pointer to netlink message header
 *  @param msg      Pointer to message header
 *  @return         Number of bytes read or UAP_FAILURE
 */
int
read_event(int sk_fd, unsigned char *buffer, int timeout, struct nlmsghdr *nlh,
           struct msghdr *msg)
{
    struct timeval tv;
    fd_set rfds;
    int ret = UAP_FAILURE;

    /* Setup read fds */
    FD_ZERO(&rfds);
    FD_SET(sk_fd, &rfds);

    /* Initialize timeout value */
    if (timeout != 0)
        tv.tv_sec = timeout;
    else
        tv.tv_sec = UAP_RECV_WAIT_DEFAULT;
    tv.tv_usec = 0;

    /* Wait for reply */
    ret = select(sk_fd + 1, &rfds, NULL, NULL, &tv);
    if (ret == -1) {
        /* Error */
        terminate_flag++;
        return UAP_FAILURE;
    } else if (!ret) {
        /* Timeout. Try again */
        return UAP_FAILURE;
    }
    if (!FD_ISSET(sk_fd, &rfds)) {
        /* Unexpected error. Try again */
        return UAP_FAILURE;
    }

    /* Success */
    ret = read_event_netlink_socket(sk_fd, buffer, nlh, msg);
    return ret;
}

/* Command line options */
static const struct option long_opts[] = {
    {"help", no_argument, NULL, 'h'},
    {"version", no_argument, NULL, 'v'},
    {NULL, 0, NULL, 0}
};

/****************************************************************************
        Global functions
****************************************************************************/
/** 
 *  @brief The main function
 *  
 *  @param argc     Number of arguments
 *  @param argv     Pointer to the arguments
 *  @return         0 or 1
 */
int
main(int argc, char *argv[])
{
    int opt;
    int nl_sk = 0;
    struct nlmsghdr *nlh = NULL;
    struct sockaddr_nl src_addr, dest_addr;
    struct msghdr msg;
    struct iovec iov;
    unsigned char *buffer = NULL;
    struct timeval current_time;
    struct tm *timeinfo;
    int num_events = 0;
    EVENTHEADER *event = NULL;
    int ret = UAP_FAILURE;

    /* Check command line options */
    while ((opt = getopt_long(argc, argv, "hvt", long_opts, NULL)) > 0) {
        switch (opt) {
        case 'h':
            print_usage();
            return 0;
        case 'v':
            printf("uapevent version : %s\n", UAP_VERSION);
            return 0;
            break;
        default:
            print_usage();
            return 1;
        }
    }
    if (optind < argc) {
        fputs("Too many arguments.\n", stderr);
        print_usage();
        return 1;
    }

    /* Open netlink socket */
    nl_sk = socket(PF_NETLINK, SOCK_RAW, NETLINK_MARVELL);
    if (nl_sk < 0) {
        printf("ERR:Could not open netlink socket.\n");
        ret = UAP_FAILURE;
        goto done;
    }

    /* Set source address */
    bzero((char *) &src_addr, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = getpid(); /* Our PID */
    src_addr.nl_groups = NL_MULTICAST_GROUP;

    /* Bind socket with source address */
    if (bind(nl_sk, (struct sockaddr *) &src_addr, sizeof(src_addr)) < 0) {
        printf("ERR:Could not bind socket!\n");
        ret = UAP_FAILURE;
        goto done;
    }

    /* Set destination address */
    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0;       /* Kernel */
    dest_addr.nl_groups = NL_MULTICAST_GROUP;

    /* Initialize netlink header */
    nlh = (struct nlmsghdr *) malloc(NLMSG_SPACE(NL_MAX_PAYLOAD));
    if (!nlh) {
        printf("ERR: Could not alloc buffer\n");
        ret = UAP_FAILURE;
        goto done;
    }
    memset(nlh, 0, NLMSG_SPACE(NL_MAX_PAYLOAD));

    /* Initialize I/O vector */
    iov.iov_base = (void *) nlh;
    iov.iov_len = NLMSG_SPACE(NL_MAX_PAYLOAD);

    /* Initialize message header */
    memset(&msg, 0, sizeof(struct msghdr));
    msg.msg_name = (void *) &dest_addr;
    msg.msg_namelen = sizeof(dest_addr);
    msg.msg_iov = &iov;
    msg.msg_iovlen = 1;

    /* Initialize receive buffer */
    buffer = malloc(NL_MAX_PAYLOAD);
    if (!buffer) {
        printf("ERR: Could not alloc buffer\n");
        ret = UAP_FAILURE;
        goto done;
    }
    bzero(buffer, sizeof(buffer));

    gettimeofday(&current_time, NULL);

    printf("\n");
    printf("*********************************************\n");
    if ((timeinfo = localtime(&(current_time.tv_sec))))
        printf("uapevent start time : %s", asctime(timeinfo));
    printf("                      %u usecs\n",
           (unsigned int) current_time.tv_usec);
    printf("*********************************************\n");

    signal(SIGTERM, sig_handler);
    signal(SIGINT, sig_handler);
    signal(SIGALRM, sig_handler);
    while (1) {
        if (terminate_flag) {
            printf("Stopping!\n");
            break;
        }
        ret = read_event(nl_sk, buffer, 0, nlh, &msg);

        /* No result. Loop again */
        if (ret == UAP_FAILURE) {
            continue;
        }
        if (ret == 0) {
            /* Zero bytes received */
            printf("ERR:Received zero bytes!\n");
            continue;
        }
        num_events++;
        gettimeofday(&current_time, NULL);
        printf("\n");
        printf("============================================\n");
        printf("Received event");
        if ((timeinfo = localtime(&(current_time.tv_sec))))
            printf(": %s", asctime(timeinfo));
        printf("                     %u usecs\n",
               (unsigned int) current_time.tv_usec);
        printf("============================================\n");
        event = (EVENTHEADER *) buffer;
        event->EventId = uap_le32_to_cpu(event->EventId);
#if DEBUG
        printf("DBG:Received buffer =\n");
        hexdump(buffer, ret, ' ');
#endif
        print_event(event, ret);
        fflush(stdout);
    }
    gettimeofday(&current_time, NULL);
    printf("\n");
    printf("********************************************\n");
    if ((timeinfo = localtime(&(current_time.tv_sec))))
        printf("uapevent end time  : %s", asctime(timeinfo));
    printf("                     %u usecs\n",
           (unsigned int) current_time.tv_usec);
    printf("Total events       : %u\n", num_events);
    printf("********************************************\n");
  done:
    if (buffer)
        free(buffer);
    if (nl_sk)
        close(nl_sk);
    if (nlh)
        free(nlh);
    return 0;
}
