/*
 * Copyright (c) 2014, 2016-2017 The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 *
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all
 * copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * This file was originally distributed by Qualcomm Atheros, Inc.
 * under proprietary terms before Copyright ownership was assigned
 * to the Linux Foundation.
 */

#include "ath_dfs_structs.h"
#include "_ieee80211_common.h"
#include <vos_lock.h>
#define IEEE80211_CHAN_MAX      255

/* channel attributes */

/* Turbo channel */
#define IEEE80211_CHAN_TURBO            0x00000010
/* CCK channel */
#define IEEE80211_CHAN_CCK              0x00000020
/* OFDM channel */
#define IEEE80211_CHAN_OFDM             0x00000040
/* 2 GHz spectrum channel. */
#define IEEE80211_CHAN_2GHZ             0x00000080
/* 5 GHz spectrum channel */
#define IEEE80211_CHAN_5GHZ             0x00000100
/* Only passive scan allowed */
#define IEEE80211_CHAN_PASSIVE          0x00000200
/* Dynamic CCK-OFDM channel */
#define IEEE80211_CHAN_DYN              0x00000400
/* GFSK channel (FHSS PHY) */
#define IEEE80211_CHAN_GFSK             0x00000800
/* Radar found on channel */
#define IEEE80211_CHAN_RADAR            0x00001000
/* 11a static turbo channel only */
#define IEEE80211_CHAN_STURBO           0x00002000
/* Half rate channel */
#define IEEE80211_CHAN_HALF             0x00004000
/* Quarter rate channel */
#define IEEE80211_CHAN_QUARTER          0x00008000
/* HT 20 channel */
#define IEEE80211_CHAN_HT20             0x00010000
/* HT 40 with extension channel above */
#define IEEE80211_CHAN_HT40PLUS         0x00020000
/* HT 40 with extension channel below */
#define IEEE80211_CHAN_HT40MINUS        0x00040000
/* HT 40 Intolerant */
#define IEEE80211_CHAN_HT40INTOL        0x00080000
/* VHT 20 channel */
#define IEEE80211_CHAN_VHT20            0x00100000
/* VHT 40 with extension channel above */
#define IEEE80211_CHAN_VHT40PLUS        0x00200000
/* VHT 40 with extension channel below */
#define IEEE80211_CHAN_VHT40MINUS       0x00400000
/* VHT 80 channel */
#define IEEE80211_CHAN_VHT80            0x00800000

/* token for ``any channel'' */
#define IEEE80211_CHAN_ANY      (-1)
#define IEEE80211_CHAN_ANYC \
        ((struct ieee80211_channel *) IEEE80211_CHAN_ANY)


#define IEEE80211_IS_CHAN_11N_HT40MINUS(_c) \
    (((_c)->ic_flags & IEEE80211_CHAN_HT40MINUS) != 0)
#define IEEE80211_IS_CHAN_11N_HT40PLUS(_c) \
    (((_c)->ic_flags & IEEE80211_CHAN_HT40PLUS) != 0)
#define IEEE80211_CHAN_11AC_VHT80 \
    (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_VHT80)


#define IEEE80211_IS_CHAN_11AC_VHT80(_c) \
    (((_c)->ic_flags & IEEE80211_CHAN_11AC_VHT80) == \
                                    IEEE80211_CHAN_11AC_VHT80)
#define CHANNEL_108G \
    (IEEE80211_CHAN_2GHZ|IEEE80211_CHAN_OFDM|IEEE80211_CHAN_TURBO)

/*
 * Software use: channel interference
 * used for as AR as well as RADAR
 * interference detection
*/
#define CHANNEL_INTERFERENCE    0x01
/* In case of VHT160, we can have 8 20Mhz channels */
#define IEE80211_MAX_20M_SUB_CH 8

struct ieee80211_channel
{
    u_int32_t       ic_freq;        /* setting in Mhz */
    u_int32_t       ic_flags;       /* see below */
    u_int8_t        ic_flagext;     /* see below */
    u_int8_t        ic_ieee;        /* IEEE channel number */

    /* maximum regulatory tx power in dBm */
    int8_t          ic_maxregpower;

    int8_t          ic_maxpower;    /* maximum tx power in dBm */
    int8_t          ic_minpower;    /* minimum tx power in dBm */
    u_int8_t        ic_regClassId;  /* regClassId of this channel */
    u_int8_t        ic_antennamax;  /* antenna gain max from regulatory */
    u_int32_t       ic_vhtop_ch_freq_seg1; /* Channel Center frequency */

    /* Channel Center frequency applicable*/
    u_int32_t       ic_vhtop_ch_freq_seg2;

    /*
     * spectral separation between pri channel
     * and the center frequency in MHz
     */
    int             ic_pri_freq_center_freq_mhz_separation;
};

struct ieee80211_channel_list
{
    int                         cl_nchans;
    struct ieee80211_channel    *cl_channels[IEE80211_MAX_20M_SUB_CH];
};

struct ieee80211_dfs_state
{
    int                            nol_event[IEEE80211_CHAN_MAX];
    os_timer_t                     nol_timer; /* NOL list processing */
    os_timer_t                     cac_timer; /* CAC timer */
    int                            cureps;    /* current events/second */
    const struct ieee80211_channel *lastchan; /* chan w/ last radar event */
    struct ieee80211_channel       *newchan;  /* chan selected next */
    /* overridden cac timeout */
    int                            cac_timeout_override;
    u_int8_t                       enable:1,
                                   cac_timer_running:1,
                                   ignore_dfs:1,
                                   ignore_cac:1;
};

typedef struct ieee80211com
{
    void (*ic_start_csa)(struct ieee80211com *ic,u_int8_t ieeeChan);
    void (*ic_get_ext_chan_info)(struct ieee80211com *ic,
                                        struct ieee80211_channel_list *chan);
    enum ieee80211_opmode ic_opmode;  /* operation mode */
    struct ieee80211_channel *(*ic_find_channel)(struct ieee80211com *ic,
                                                 int freq, u_int32_t flags);
    u_int64_t (*ic_get_TSF64)(struct ieee80211com *ic);
    unsigned int (*ic_ieee2mhz)(u_int chan, u_int flags);
    struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX+1];
    /* Number of Channels according to the Regulatory domain channels */
    int ic_nchans;
    struct ieee80211_channel *ic_curchan;   /* current channel */
    u_int8_t ic_isdfsregdomain; /* operating in DFS domain ? */
    int (*ic_get_dfsdomain)(struct ieee80211com *);
    u_int16_t (*ic_dfs_usenol)(struct ieee80211com *ic);
    u_int16_t (*ic_dfs_isdfsregdomain)(struct ieee80211com *ic);
    int (*ic_dfs_attached)(struct ieee80211com *ic);
    void *ic_dfs;
    struct ieee80211_dfs_state ic_dfs_state;
    int (*ic_dfs_attach)(struct ieee80211com *ic,
                                       void *pCap, void *radar_info);
    int (*ic_dfs_detach)(struct ieee80211com *ic);
    int (*ic_dfs_enable)(struct ieee80211com *ic, int *is_fastclk, void *);
    int (*ic_dfs_disable)(struct ieee80211com *ic);
    int (*ic_get_ext_busy)(struct ieee80211com *ic);
    int (*ic_get_mib_cycle_counts_pct)(struct ieee80211com *ic,
          u_int32_t *rxc_pcnt, u_int32_t *rxf_pcnt, u_int32_t *txf_pcnt);
    int (*ic_dfs_get_thresholds)(struct ieee80211com *ic,void *pe);

    int (*ic_dfs_debug)(struct ieee80211com *ic, int type, void *data);
    /*
     * Update the channel list with the current set of DFS
     * NOL entries.
     *
     * + 'cmd' indicates what to do; for now it should just
     *   be DFS_NOL_CLIST_CMD_UPDATE which will update all
     *   channels, given the _entire_ NOL. (Rather than
     *   the earlier behaviour with clist_update, which
     *   was to either add or remove a set of channel
     *   entries.)
     */
    void (*ic_dfs_clist_update)(struct ieee80211com *ic, int cmd,
                                struct dfs_nol_chan_entry *, int nentries);
    void (*ic_dfs_notify_radar)(struct ieee80211com *ic,
                                   struct ieee80211_channel *chan);
    void (*ic_dfs_unmark_radar)(struct ieee80211com *ic,
                                   struct ieee80211_channel *chan);
    int (*ic_dfs_control)(struct ieee80211com *ic,
                          u_int id, void *indata, u_int32_t insize,
                          void *outdata, u_int32_t *outsize);
    void (*ic_update_dfs_cac_block_tx)(bool cac_block_tx);
    HAL_DFS_DOMAIN current_dfs_regdomain;
    u_int8_t vdev_id;
    u_int8_t last_radar_found_chan;
    int32_t dfs_pri_multiplier;
    adf_os_spinlock_t chan_lock;
    bool disable_phy_err_processing;
} IEEE80211COM, *PIEEE80211COM;

/*
 * Convert channel to frequency value.
 */
static INLINE u_int
ieee80211_chan2freq(struct ieee80211com *ic,
                                 const struct ieee80211_channel *c)
{
    if (c == NULL)
    {
        return 0;
    }
    return (c == IEEE80211_CHAN_ANYC ?  IEEE80211_CHAN_ANY : c->ic_freq);
}
