/*
 * hostapd / IEEE 802.11be EHT
 * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc.
 *
 * This software may be distributed under the terms of the BSD license.
 * See README for more details.
 */

#include "utils/includes.h"
#include "utils/common.h"
#include "common/qca-vendor.h"
#include "hostapd.h"
#include "sta_info.h"
#include "ieee802_11.h"


static u16 ieee80211_eht_ppet_size(u16 ppe_thres_hdr, const u8 *phy_cap_info)
{
	u8 ru;
	u16 sz = 0;

	if ((phy_cap_info[EHT_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
	     EHT_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
		return 0;

	ru = (ppe_thres_hdr &
	      EHT_PPE_THRES_RU_INDEX_MASK) >> EHT_PPE_THRES_RU_INDEX_SHIFT;
	while (ru) {
		if (ru & 0x1)
			sz++;
		ru >>= 1;
	}

	sz = sz * (1 + ((ppe_thres_hdr & EHT_PPE_THRES_NSS_MASK) >>
			EHT_PPE_THRES_NSS_SHIFT));
	sz = (sz * 6) + 9;
	if (sz % 8)
		sz += 8;
	sz /= 8;

	return sz;
}


static u8 ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode, u8 opclass,
				     u8 he_oper_chwidth, const u8 *he_phy_cap,
				     const u8 *eht_phy_cap)
{
	u8 sz = EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
	bool band24, band5, band6;
	u8 he_phy_cap_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;

	switch (he_oper_chwidth) {
	case CONF_OPER_CHWIDTH_80P80MHZ:
		he_phy_cap_chwidth |=
			HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
		/* fall through */
	case CONF_OPER_CHWIDTH_160MHZ:
		he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
		/* fall through */
	case CONF_OPER_CHWIDTH_80MHZ:
	case CONF_OPER_CHWIDTH_USE_HT:
		he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
			HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
		break;
	}

	he_phy_cap_chwidth &= he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];

	band24 = mode == HOSTAPD_MODE_IEEE80211B ||
		mode == HOSTAPD_MODE_IEEE80211G ||
		mode == NUM_HOSTAPD_MODES;
	band5 = mode == HOSTAPD_MODE_IEEE80211A ||
		mode == NUM_HOSTAPD_MODES;
	band6 = is_6ghz_op_class(opclass);

	if (band24 &&
	    (he_phy_cap_chwidth & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0)
		return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;

	if (band5 &&
	    (he_phy_cap_chwidth &
	     (HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
	      HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
	      HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) == 0)
		return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;

	if (band5 &&
	    (he_phy_cap_chwidth &
	     (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
	      HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)))
	    sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;

	if (band6 &&
	    (eht_phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
	     EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK)) {
		sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
		sz += EHT_PHYCAP_MCS_NSS_LEN_160MHZ;
	}

	return sz;
}


size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
				 enum ieee80211_op_mode opmode)
{
	struct hostapd_hw_modes *mode;
	struct eht_capabilities *eht_cap;
	size_t len = 3 + 2 + EHT_PHY_CAPAB_LEN;

	mode = hapd->iface->current_mode;
	if (!mode)
		return 0;

	eht_cap = &mode->eht_capab[opmode];
	if (!eht_cap->eht_supported)
		return 0;

	len += ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
					  hapd->iconf->he_oper_chwidth,
					  mode->he_capab[opmode].phy_cap,
					  eht_cap->phy_cap);
	len += ieee80211_eht_ppet_size(WPA_GET_LE16(&eht_cap->ppet[0]),
				       eht_cap->phy_cap);

	return len;
}


u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
			   enum ieee80211_op_mode opmode)
{
	struct hostapd_hw_modes *mode;
	struct eht_capabilities *eht_cap;
	struct ieee80211_eht_capabilities *cap;
	size_t mcs_nss_len, ppe_thresh_len;
	u8 *pos = eid, *length_pos;

	mode = hapd->iface->current_mode;
	if (!mode)
		return eid;

	eht_cap = &mode->eht_capab[opmode];
	if (!eht_cap->eht_supported)
		return eid;

	*pos++ = WLAN_EID_EXTENSION;
	length_pos = pos++;
	*pos++ = WLAN_EID_EXT_EHT_CAPABILITIES;

	cap = (struct ieee80211_eht_capabilities *) pos;
	os_memset(cap, 0, sizeof(*cap));
	cap->mac_cap = host_to_le16(eht_cap->mac_cap);
	os_memcpy(cap->phy_cap, eht_cap->phy_cap, EHT_PHY_CAPAB_LEN);

	if (!is_6ghz_op_class(hapd->iconf->op_class))
		cap->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &=
			~EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK;
	if (!hapd->iface->conf->eht_phy_capab.su_beamformer)
		cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMER_IDX] &=
			~EHT_PHYCAP_SU_BEAMFORMER;

	if (!hapd->iface->conf->eht_phy_capab.su_beamformee)
		cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMEE_IDX] &=
			~EHT_PHYCAP_SU_BEAMFORMEE;

	if (!hapd->iface->conf->eht_phy_capab.mu_beamformer)
		cap->phy_cap[EHT_PHYCAP_MU_CAPABILITY_IDX] &=
			~EHT_PHYCAP_MU_BEAMFORMER_MASK;

	if (!hapd->iface->conf->eht_phy_capab.non_ofdma_ulmumimo_80mhz)
		cap->phy_cap[EHT_PHYCAP_MU_CAPABILITY_IDX] &=
					~EHT_PHYCAP_NON_OFDMA_UL_MU_MIMO_80MHZ;

	if (!hapd->iface->conf->eht_phy_capab.non_ofdma_ulmumimo_160mhz)
		cap->phy_cap[EHT_PHYCAP_MU_CAPABILITY_IDX] &=
					~EHT_PHYCAP_NON_OFDMA_UL_MU_MIMO_160MHZ;

	if (!hapd->iface->conf->eht_phy_capab.non_ofdma_ulmumimo_320mhz)
		cap->phy_cap[EHT_PHYCAP_MU_CAPABILITY_IDX] &=
					~EHT_PHYCAP_NON_OFDMA_UL_MU_MIMO_320MHZ;

	pos = cap->optional;

	mcs_nss_len = ieee80211_eht_mcs_set_size(mode->mode,
						 hapd->iconf->op_class,
						 hapd->iconf->he_oper_chwidth,
						 mode->he_capab[opmode].phy_cap,
						 eht_cap->phy_cap);
	if (mcs_nss_len) {
		os_memcpy(pos, eht_cap->mcs, mcs_nss_len);
		pos += mcs_nss_len;
	}

	ppe_thresh_len = ieee80211_eht_ppet_size(
				WPA_GET_LE16(&eht_cap->ppet[0]),
				eht_cap->phy_cap);
	if (ppe_thresh_len) {
		os_memcpy(pos, eht_cap->ppet, ppe_thresh_len);
		pos += ppe_thresh_len;
	}

	*length_pos = pos - (eid + 2);
	return pos;
}


u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid,
			       enum ieee80211_op_mode opmode)
{
	struct hostapd_hw_modes *mode;
	struct hostapd_config *conf = hapd->iconf;
	struct ieee80211_eht_operation *oper;
	u8 *pos = eid, seg0 = 0, seg1 = 0, *length_pos;
	struct eht_capabilities *eht_cap;
	enum oper_chan_width chwidth;

	mode = hapd->iface->current_mode;
	if (!mode)
		return eid;

	if (!mode->eht_capab[opmode].eht_supported)
		return eid;

	*pos++ = WLAN_EID_EXTENSION;
	length_pos = pos++;
	*pos++ = WLAN_EID_EXT_EHT_OPERATION;

	oper = (struct ieee80211_eht_operation *) pos;
	oper->oper_params = EHT_OPER_INFO_PRESENT;

	eht_cap = &mode->eht_capab[opmode];
	memcpy(&oper->basic_mcs_nss, &eht_cap->mcs,
	       EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY);

	if (is_6ghz_op_class(conf->op_class))
		chwidth = op_class_to_ch_width(conf->op_class);
	else
		chwidth = conf->eht_oper_chwidth;

	if (hapd->iconf->ru_punct_bitmap && !hapd->iface->ru_punct_supp_bw) {
		hapd->iconf->ru_punct_bitmap = 0;
		wpa_printf(MSG_ERROR,
			   "Driver does not support RU puncturing. Setting bitmap to 0");
	}

	seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf);
	if (!seg0)
		seg0 = hapd->iconf->channel;

	switch (chwidth) {
	case CONF_OPER_CHWIDTH_320MHZ:
		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_320MHZ;
		seg1 = seg0;
		if (hapd->iconf->channel < seg0)
			seg0 -= 16;
		else
			seg0 += 16;
		break;
	case CONF_OPER_CHWIDTH_160MHZ:
		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_160MHZ;
		if (!oper->oper_info.ccfs1) {
			/* CCFS0 points to center channel frequency in config */
			oper->oper_info.ccfs1 = oper->oper_info.ccfs0;
			if (hapd->iconf->channel < oper->oper_info.ccfs0)
				oper->oper_info.ccfs0 = oper->oper_info.ccfs1 - 8;
			else
				oper->oper_info.ccfs0 = oper->oper_info.ccfs1 + 8;
		}

		if (hapd->iconf->ru_punct_bitmap &&
		    hapd->iface->ru_punct_supp_bw == CONF_OPER_CHWIDTH_320MHZ) {
			hapd->iconf->ru_punct_bitmap = 0;
			wpa_printf(MSG_ERROR,
				   "Driver does not support RU puncturing for bandwidths less than 320 MHz. Setting bitmap to 0");
		}
		seg1 = seg0;
		if (hapd->iconf->channel < seg0)
			seg0 -= 8;
		else
			seg0 += 8;
		break;
	case CONF_OPER_CHWIDTH_80MHZ:
		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_80MHZ;
		break;
	case CONF_OPER_CHWIDTH_USE_HT:
		hapd->iconf->ru_punct_bitmap = 0;
		if ((is_6ghz_op_class(hapd->iconf->op_class) &&
		    op_class_to_bandwidth(hapd->iconf->op_class) == 40) ||
		    hapd->iconf->secondary_channel)
			oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_40MHZ;
		else
			oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_20MHZ;

		break;
	default:
		hapd->iconf->ru_punct_bitmap = 0;
		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_20MHZ;
		break;
	}

	oper->oper_info.ccfs0 = seg0;
	oper->oper_info.ccfs1 = seg1;

	if (hapd->iconf->ru_punct_bitmap) {
		oper->oper_params |= EHT_OPER_DISABLED_SUBCHAN_BITMAP_PRESENT;
		oper->oper_info.disabled_chan_bitmap = host_to_le16(hapd->iconf->ru_punct_bitmap);
		pos += DISABLED_SUBCHANNEL_BITMAP_BYTES_SIZE;
	}

	pos += 8;
	*length_pos = pos - (eid + 2);

	return pos;
}

u8 * hostapd_eid_vendor_240mhz(struct hostapd_data *hapd, u8 *eid,
                               enum ieee80211_op_mode opmode)
{
       struct hostapd_hw_modes *mode;
       u8 *pos = eid;
       struct eht_capabilities *eht_cap;
       struct ieee80211_240mhz_vendor_oper *eht_240_cap;
       u8 ccfs0,ccfs1;

       mode = hapd->iface->current_mode;
       if (!mode || is_6ghz_op_class(hapd->iconf->op_class) ||
	    hapd->iconf->eht_oper_chwidth != CONF_OPER_CHWIDTH_320MHZ)
               return eid;

       eht_cap = &mode->eht_capab[opmode];

       if (!eht_cap->eht_supported)
                   return eid;
       ccfs0 = hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf);
       ccfs1 = ccfs0 - 16;

       *pos++ = WLAN_EID_VENDOR_SPECIFIC;
       *pos++ = 6 + /* Element ID, Length, OUI, OUI Type */
	        4 + /* QCN version Attribute size */
		sizeof(struct ieee80211_240mhz_vendor_oper);
       WPA_PUT_BE24(pos, OUI_QCN);
       pos += 3;
       *pos++ = 1; /* QCN_OUI_TYPE */

       /* QCN Version Attribute*/
       *pos++ = 1; /* QCN_ATTRIB_VERSION */
       *pos++ = 2; /* Length */
       *pos++ = 1; /* QCN_VER_ATTR_VER */
       *pos++ = 0; /* QCN_VER_ATTR_SUBVERSION */

       /* QCN Attirbute */
       *pos++ = QCN_ATTRIB_HE_240_MHZ_SUPP; /*QCN_ATTRIB_HE_240_MHZ_SUPP*/
       *pos++ = sizeof(struct ieee80211_240mhz_vendor_oper);

       /* 240Mhz fields */
       eht_240_cap = (struct ieee80211_240mhz_vendor_oper*)pos;
       os_memset(eht_240_cap, 0, sizeof(struct ieee80211_240mhz_vendor_oper));

       eht_240_cap->ccfs1 = ccfs1;
       eht_240_cap->ccfs0 = hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf);
       eht_240_cap->ru_punct_bitmap = hapd->iconf->ru_punct_bitmap;
       eht_240_cap->phy_cap_320mhz = eht_cap->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
                                     EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK;
       eht_240_cap->phy_cap_320mhz |= eht_cap->phy_cap[EHT_PHYCAP_BEAMFORMEE_SS_320MHZ_IDX] &
                                      EHT_PHYCAP_BEAMFORMEE_SS_320MHZ_MASK;
       eht_240_cap->phy_cap_320mhz |= (eht_cap->phy_cap[EHT_PHYCAP_NUM_SOUND_DIM_320MHZ_IDX] &
                                      EHT_PHYCAP_NUM_SOUND_DIM_320MHZ_MASK) |
                                      (eht_cap->phy_cap[EHT_PHYCAP_NUM_SOUND_DIM_320MHZ_IDX_1] &
                                       EHT_PHYCAP_NUM_SOUND_DIM_320MHZ_MASK);
       eht_240_cap->phy_cap_320mhz |= eht_cap->phy_cap[EHT_PHYCAP_NON_OFDMA_UL_MUMIMO_320MHZ_IDX] &
                                      EHT_PHYCAP_NON_OFDMA_UL_MUMIMO_320MHZ_MASK;
       eht_240_cap->phy_cap_320mhz |= eht_cap->phy_cap[EHT_PHYCAP_MU_CAPABILITY_IDX] &
                                       EHT_PHYCAP_MU_BEAMFORMER_MASK;

       memcpy(&eht_240_cap->mcs_map_320mhz, &eht_cap->mcs, EHT_PHYCAP_MCS_NSS_LEN_160MHZ);
       pos += sizeof(struct ieee80211_240mhz_vendor_oper);

       return pos;
}

static bool check_valid_eht_mcs_nss(struct hostapd_data *hapd, const u8 *ap_mcs,
				    const u8 *sta_mcs, u8 mcs_count, u8 map_len)
{
	unsigned int i, j;

	for (i = 0; i < mcs_count; i++) {
		ap_mcs += i * 3;
		sta_mcs += i * 3;

		for (j = 0; j < map_len; j++) {
			if (((ap_mcs[j] >> 4) & 0xFF) == 0)
				continue;

			if ((sta_mcs[j] & 0xFF) == 0)
				continue;

			return true;
		}
	}

	wpa_printf(MSG_DEBUG,
		   "No matching EHT MCS found between AP TX and STA RX");
	return false;
}


static bool check_valid_eht_mcs(struct hostapd_data *hapd,
				const u8 *sta_eht_capab,
				enum ieee80211_op_mode opmode)
{
	struct hostapd_hw_modes *mode;
	const struct ieee80211_eht_capabilities *capab;
	const u8 *ap_mcs, *sta_mcs;
	u8 mcs_count = 1;

	mode = hapd->iface->current_mode;
	if (!mode)
		return true;

	ap_mcs = mode->eht_capab[opmode].mcs;
	capab = (const struct ieee80211_eht_capabilities *) sta_eht_capab;
	sta_mcs = capab->optional;

	if (ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
				       hapd->iconf->he_oper_chwidth,
				       mode->he_capab[opmode].phy_cap,
				       mode->eht_capab[opmode].phy_cap) ==
	    EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY)
		return check_valid_eht_mcs_nss(
			hapd, ap_mcs, sta_mcs, 1,
			EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY);

	switch (hapd->iface->conf->eht_oper_chwidth) {
	case CONF_OPER_CHWIDTH_320MHZ:
		mcs_count++;
		/* fall through */
	case CONF_OPER_CHWIDTH_80P80MHZ:
	case CONF_OPER_CHWIDTH_160MHZ:
		mcs_count++;
		break;
	default:
		break;
	}

	return check_valid_eht_mcs_nss(hapd, ap_mcs, sta_mcs, mcs_count,
				       EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS);
}


static bool ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode,
					   u8 opclass, u8 he_oper_chwidth,
					   const u8 *he_cap, const u8 *eht_cap,
					   size_t len)
{
	const struct ieee80211_he_capabilities *he_capab;
	struct ieee80211_eht_capabilities *cap;
	const u8 *he_phy_cap;
	size_t cap_len;
	u16 ppe_thres_hdr;

	return false;
	he_capab = (const struct ieee80211_he_capabilities *) he_cap;
	he_phy_cap = he_capab->he_phy_capab_info;
	cap = (struct ieee80211_eht_capabilities *) eht_cap;
	cap_len = sizeof(*cap) - sizeof(cap->optional);
	if (len < cap_len)
		return true;

	cap_len += ieee80211_eht_mcs_set_size(mode, opclass, he_oper_chwidth,
					      he_phy_cap, cap->phy_cap);
	if (len < cap_len)
		return true;

	ppe_thres_hdr = len > cap_len + 1 ?
		WPA_GET_LE16(&eht_cap[cap_len]) : 0x01ff;
	cap_len += ieee80211_eht_ppet_size(ppe_thres_hdr, cap->phy_cap);

	return len < cap_len;
}


u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
		       enum ieee80211_op_mode opmode,
		       const u8 *he_capab, size_t he_capab_len,
		       const u8 *eht_capab, size_t eht_capab_len)
{
	struct hostapd_hw_modes *c_mode = hapd->iface->current_mode;
	enum hostapd_hw_mode mode = c_mode ? c_mode->mode : NUM_HOSTAPD_MODES;

	if (!hapd->iconf->ieee80211be || hapd->conf->disable_11be ||
	    !he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN ||
	    !eht_capab ||
	    ieee80211_invalid_eht_cap_size(mode, hapd->iconf->op_class,
					   hapd->iconf->he_oper_chwidth,
					   he_capab, eht_capab,
					   eht_capab_len) ||
	    !check_valid_eht_mcs(hapd, eht_capab, opmode)) {
		sta->flags &= ~WLAN_STA_EHT;
		os_free(sta->eht_capab);
		sta->eht_capab = NULL;
		return WLAN_STATUS_SUCCESS;
	}

	os_free(sta->eht_capab);
	sta->eht_capab = os_memdup(eht_capab, eht_capab_len);
	if (!sta->eht_capab) {
		sta->eht_capab_len = 0;
		return WLAN_STATUS_UNSPECIFIED_FAILURE;
	}

	sta->flags |= WLAN_STA_EHT;
	sta->eht_capab_len = eht_capab_len;

	return WLAN_STATUS_SUCCESS;
}


void hostapd_get_eht_capab(struct hostapd_data *hapd,
			   const struct ieee80211_eht_capabilities *src,
			   struct ieee80211_eht_capabilities *dest,
			   size_t len)
{
	if (!src || !dest)
		return;

	if (len > sizeof(*dest))
		len = sizeof(*dest);
	/* TODO: mask out unsupported features */

	os_memset(dest, 0, sizeof(*dest));
	os_memcpy(dest, src, len);
}

u16 copy_sta_eht_240mhz_capab(struct hostapd_data *hapd, struct sta_info *sta,
                       enum ieee80211_op_mode opmode,
                       const u8 *eht_240mhz_capab, size_t eht_240mhz_capab_len)
{
        if (!eht_240mhz_capab || !hapd->iconf->ieee80211be ||
            hapd->conf->disable_11be) {
                os_free(sta->eht_240mhz_capab);
                sta->eht_240mhz_capab = NULL;
                return WLAN_STATUS_SUCCESS;
        }

        if (!sta->eht_240mhz_capab) {
                sta->eht_240mhz_capab = os_zalloc(eht_240mhz_capab_len);
                if (!sta->eht_240mhz_capab)
                        return WLAN_STATUS_UNSPECIFIED_FAILURE;
        }

        os_memcpy(sta->eht_240mhz_capab, eht_240mhz_capab, eht_240mhz_capab_len);
        sta->eht_240mhz_len = eht_240mhz_capab_len;

        return WLAN_STATUS_SUCCESS;
}


void hostapd_get_eht_240mhz_capab(struct hostapd_data *hapd,
				  const struct ieee80211_240mhz_vendor_oper *src,
				  struct ieee80211_240mhz_vendor_oper *dest,
				  size_t len)
{
	if (!src || !dest)
		return;

	if (len > sizeof(*dest))
		len = sizeof(*dest);

	os_memcpy(dest, src, len);
}
