From 8d8c8e710aca29b1519fbc6c7b87f1d2fd20b345 Mon Sep 17 00:00:00 2001 From: Rathees kumar Chinannan Date: Wed, 30 Dec 2020 13:49:59 +0530 Subject: [PATCH] qca-wifi: Componentize QWRAP code Remove dependency of qwrap on VAP layer and radio(ic) layer data structures and move QWRAP DP code from OSIF to Component DEV. Change-Id: I0fc975ef4cbac52e1e7fd8a87db3de8f97056ac0 CRs-Fixed: 2832334 --- dp_wrap/inc/dp_wrap.h | 392 ++++++++++++++++ dp_wrap/inc/dp_wrap_struct.h | 87 ++++ dp_wrap/src/dp_wrap.c | 837 +++++++++++++++++++++++++++++++++++ dp_wrap/src/dp_wrap_mat.c | 590 ++++++++++++++++++++++++ 4 files changed, 1906 insertions(+) create mode 100644 dp_wrap/inc/dp_wrap.h create mode 100644 dp_wrap/inc/dp_wrap_struct.h create mode 100644 dp_wrap/src/dp_wrap.c create mode 100644 dp_wrap/src/dp_wrap_mat.c diff --git a/dp_wrap/inc/dp_wrap.h b/dp_wrap/inc/dp_wrap.h new file mode 100644 index 0000000000..91938fe9e4 --- /dev/null +++ b/dp_wrap/inc/dp_wrap.h @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2020-2021 The Linux Foundation. All rights reserved. + * + * 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. + */ + +#ifndef _DP_WRAP_H_ +#define _DP_WRAP_H__ + +#if ATH_SUPPORT_WRAP +#if !WLAN_QWRAP_LEGACY + +#include "dp_wrap_struct.h" +#include +#include +#include + +/** + * dp_wrap_get_pdev_handle() - get wrap handle from pdev handle + * + * @pdev: handle to the objmgr pdev + * Return: handle to wrap_pdev + */ +static inline +dp_pdev_wrap_t *dp_wrap_get_pdev_handle(struct wlan_objmgr_pdev *pdev) +{ + ol_txrx_soc_handle soc; + dp_txrx_pdev_handle_t *dp_hdl; + + soc = wlan_psoc_get_dp_handle(wlan_pdev_get_psoc(pdev)); + + if (qdf_unlikely(!soc)) { + qwrap_err(" soc handle is NULL"); + return NULL; + } + dp_hdl = (dp_txrx_pdev_handle_t *)cdp_pdev_get_dp_txrx_handle(soc, + wlan_objmgr_pdev_get_pdev_id(pdev)); + + if (qdf_unlikely(!dp_hdl)) { + qwrap_err(" dp txrx handle is NULL"); + return NULL; + } + return &dp_hdl->wrap_pdev_hdl; +} + +/** + * dp_wrap_get_vdev_handle() - get wrap handle from vdev handle + * + * @vdev: handle to the objmgr vdev + * Return: handle to wrap_vdev + */ +static inline +dp_vdev_wrap_t *dp_wrap_get_vdev_handle(struct wlan_objmgr_vdev *vdev) +{ + ol_txrx_soc_handle soc; + dp_vdev_txrx_handle_t *dp_hdl; + + soc = wlan_psoc_get_dp_handle( + wlan_pdev_get_psoc(wlan_vdev_get_pdev(vdev))); + + if (qdf_unlikely(!soc)) { + qwrap_err(" soc handle is NULL"); + return NULL; + } + + dp_hdl = (dp_vdev_txrx_handle_t *)cdp_vdev_get_dp_ext_txrx_handle(soc, + wlan_vdev_get_id(vdev)); + + if (qdf_unlikely(!dp_hdl)) { + qwrap_err(" dp txrx handle is NULL"); + return NULL; + } + + return &dp_hdl->wrap_vdev_hdl; +} + +/** + * dp_wrap_get_mpsta_vdev() - get mpsta vdev + * + * @pdev: handle to pdev obj mgr + * Return: mpsta vdev object + */ + +static +struct wlan_objmgr_vdev *dp_wrap_get_mpsta_vdev(struct wlan_objmgr_pdev *pdev) +{ + struct dp_wrap_pdev *wrap_pdev; + + if (!pdev) + return NULL; + + wrap_pdev = dp_wrap_get_pdev_handle(pdev); + if (!wrap_pdev) + return NULL; + return wrap_pdev->mpsta_vdev; +} + +/** + * dp_wrap_get_vdev() - get mpsta vdev + * + * @pdev: handle to pdev obj mgr + * Return: wrap vdev object + */ + +static +struct wlan_objmgr_vdev *dp_wrap_get_vdev(struct wlan_objmgr_pdev *pdev) +{ + struct dp_wrap_pdev *wrap_pdev; + + if (!pdev) + return NULL; + + wrap_pdev = dp_wrap_get_pdev_handle(pdev); + if (!wrap_pdev) + return NULL; + return wrap_pdev->wrap_vdev; +} + +/** + * dp_wrap_vdev_is_mat_set() - Indicates whether mat translation is set + * + * @vdev: handle to the objmgr vdev. + * Return: true if mat is set, false otherwise + */ +static inline bool dp_wrap_vdev_is_mat_set(struct wlan_objmgr_vdev *vdev) +{ + if (vdev && wlan_vdev_mlme_feat_ext_cap_get(vdev, WLAN_VDEV_FEXT_MAT)) + return true; + else + return false; +} + +/** + * dp_wrap_vdev_is_wired_psta() - Indicates whether vdev is wired psta. + * + * @vdev: handle to the objmgr vdev. + * Return: true if wired psta, false otherwise. + */ +static inline bool dp_wrap_vdev_is_wired_psta(struct wlan_objmgr_vdev *vdev) +{ + if (vdev && wlan_vdev_mlme_feat_ext_cap_get( + vdev, WLAN_VDEV_FEXT_WIRED_PSTA)) + return true; + else + return false; +} + +/** + * dp_wrap_vdev_is_psta() - Indicates whether vdev is of type PSTA. + * + * @vdev: handle to the objmgr vdev. + * Return: True if psta, false otherwise + */ +static inline bool dp_wrap_vdev_is_psta(struct wlan_objmgr_vdev *vdev) +{ + if (vdev && wlan_vdev_mlme_feat_ext_cap_get(vdev, WLAN_VDEV_FEXT_PSTA)) + return true; + else + return false; +} + +/** + * dp_wrap_vdev_is_mpsta() - Indicates whether vdev is of type MPSTA. + * + * @vdev: handle to the objmgr vdev. + * Return: True if mpsta, false otherwise. + */ +static inline bool dp_wrap_vdev_is_mpsta(struct wlan_objmgr_vdev *vdev) +{ + if (vdev && wlan_vdev_mlme_feat_ext_cap_get( + vdev, WLAN_VDEV_FEXT_MPSTA)) + return true; + else + return false; +} + +/** + * dp_wrap_vdev_is_wrap() - Indicates whether vdev is of type WRAP. + * + * @vdev: handle to the objmgr vdev. + * Return: True if wrap, false otherwise. + */ +static bool dp_wrap_vdev_is_wrap(struct wlan_objmgr_vdev *vdev) +{ + if (vdev && wlan_vdev_mlme_feat_ext_cap_get(vdev, WLAN_VDEV_FEXT_WRAP)) + return true; + else + return false; +} + +/** + * dp_wrap_vdev_get_nwrapvaps() - Get number of wrap vaps + * + * @pdev: handle to the objmgr pdev. + * Return: wrap vaps count + */ +static int dp_wrap_vdev_get_nwrapvaps(struct wlan_objmgr_pdev *pdev) +{ + struct dp_wrap_pdev *wpdev; + + if (pdev) { + wpdev = dp_wrap_get_pdev_handle(pdev); + if (!wpdev) { + qwrap_err(" wpdev is NULL"); + return 0; + } + return wpdev->nwrapvaps; + } + return 0; +} + +/** + * dp_wrap_vdev_get_npstavaps() - Get number of psta vaps + * + * @pdev: handle to the objmgr pdev. + * Return: psta vaps count + */ +static int dp_wrap_vdev_get_npstavaps(struct wlan_objmgr_pdev *pdev) +{ + struct dp_wrap_pdev *wpdev; + + if (pdev) { + wpdev = dp_wrap_get_pdev_handle(pdev); + if (!wpdev) { + qwrap_err(" wpdev is NULL"); + return 0; + } + return wpdev->npstavaps; + } + return 0; +} + +/** + * dp_wrap_pdev_set_isolation() - set isolation + * + * @pdev: handle to the objmgr pdev. + * @isolation: handle to set isolation param + * Return: void + */ +static void +dp_wrap_pdev_set_isolation(struct wlan_objmgr_pdev *pdev, int isolation) +{ + struct dp_wrap_pdev *wpdev; + + if (pdev) { + wpdev = dp_wrap_get_pdev_handle(pdev); + if (!wpdev) { + qwrap_err(" wpdev is NULL"); + return; + } + wpdev->wp_isolation = isolation; + } +} + +/** + * dp_wrap_pdev_get_isolation() - Indicates whether isolation is set + * + * @pdev: handle to the objmgr pdev. + * Return: isolation value + */ +static int dp_wrap_pdev_get_isolation(struct wlan_objmgr_pdev *pdev) +{ + struct dp_wrap_pdev *wpdev; + + if (pdev) { + wpdev = dp_wrap_get_pdev_handle(pdev); + if (!wpdev) { + qwrap_err(" wpdev is NULL"); + return 0; + } + return wpdev->wp_isolation; + } + return 0; +} + +/** + * dp_wrap_vdev_get_oma() - Get OMA address of PSTA + * + * @pdev: handle to the objmgr vdev. + * Return: PSTA OMA address + */ +static u_int8_t *dp_wrap_vdev_get_oma(struct wlan_objmgr_vdev *vdev) +{ + struct dp_wrap_vdev *wvdev; + + if (vdev) { + wvdev = dp_wrap_get_vdev_handle(vdev); + if (!wvdev) { + qwrap_err(" wvdev is NULL"); + return NULL; + } + return &wvdev->wrap_dev_oma[0]; + } + return NULL; +} + +/** + * dp_wrap_vdev_get_vma() - Get VMA address of PSTA + * + * @pdev: handle to the objmgr vdev. + * Return: PSTA VMA address + */ +static u_int8_t *dp_wrap_vdev_get_vma(struct wlan_objmgr_vdev *vdev) +{ + struct dp_wrap_vdev *wvdev; + + if (vdev) { + wvdev = dp_wrap_get_vdev_handle(vdev); + if (!wvdev) { + qwrap_err(" wvdev is NULL"); + return NULL; + } + return &wvdev->wrap_dev_vma[0]; + } + return NULL; +} + +/** + * @brief Find vdev object based on MAC address. + * + * @param pdev Ptr to pdev obj mgr. + * @param mac MAC address to look up. + * + * @return vdev objmgr on success + * @return NULL on failure + */ + +static inline struct wlan_objmgr_vdev *dp_wrap_vdev_vma_find( + struct wlan_objmgr_pdev *pdev, unsigned char *mac) +{ + dp_vdev_wrap_t *wrap_vdev; + int hash; + rwlock_state_t lock_state; + struct dp_wrap_pdev *wrap_pdev; + struct dp_wrap_devt *wdt; + + if (!pdev) + return NULL; + wrap_pdev = dp_wrap_get_pdev_handle(pdev); + if (!wrap_pdev) + return NULL; + wdt = &wrap_pdev->wp_devt; + hash = WRAP_DEV_HASH(mac); + OS_RWLOCK_READ_LOCK(&wdt->wdt_lock, &lock_state); + LIST_FOREACH(wrap_vdev, &wdt->wdt_hash_vma[hash], wrap_dev_hash_vma) { + if (qdf_is_macaddr_equal((struct qdf_mac_addr *) + wrap_vdev->wrap_dev_vma, (struct qdf_mac_addr *)mac)) { + OS_RWLOCK_READ_UNLOCK(&wdt->wdt_lock, &lock_state); + return wrap_vdev->vdev; + } + } + OS_RWLOCK_READ_UNLOCK(&wdt->wdt_lock, &lock_state); + + return NULL; +} + +int dp_wrap_attach(struct wlan_objmgr_pdev *pdev); +int dp_wrap_detach(struct wlan_objmgr_pdev *pdev); +void dp_wrap_vdev_attach(struct wlan_objmgr_vdev *vdev); +void dp_wrap_vdev_detach(struct wlan_objmgr_vdev *vdev); +int dp_wrap_dev_add(struct wlan_objmgr_vdev *vdev); +void dp_wrap_vdev_set_psta(struct wlan_objmgr_vdev *vdev); +void dp_wrap_vdev_set_mpsta(struct wlan_objmgr_vdev *vdev); +void dp_wrap_vdev_clear_psta(struct wlan_objmgr_vdev *vdev); +void dp_wrap_vdev_set_wrap(struct wlan_objmgr_vdev *vdev); +void dp_wrap_vdev_clear_wrap(struct wlan_objmgr_vdev *vdev); +void dp_wrap_dev_remove(struct wlan_objmgr_vdev *vdev); +void dp_wrap_dev_remove_vma(struct wlan_objmgr_vdev *vdev); +int dp_wrap_rx_process(struct net_device **dev, struct wlan_objmgr_vdev *vdev, + struct sk_buff *skb, int *nwifi); +int dp_wrap_tx_process(struct net_device **dev, struct wlan_objmgr_vdev *vdev, + struct sk_buff **skb); +int dp_wrap_mat_tx(struct dp_wrap_vdev *wvdev, wbuf_t buf); +int dp_wrap_mat_rx(struct dp_wrap_vdev *wvdev, wbuf_t buf); +void dp_wrap_vdev_set_netdev(struct wlan_objmgr_vdev *vdev, + struct net_device *dev); +void dp_wrap_register_xmit_handler(struct wlan_objmgr_vdev *vdev, + void (*wlan_vdev_xmit_queue) + (struct net_device *dev, wbuf_t wbuf)); +#endif +#endif +#endif diff --git a/dp_wrap/inc/dp_wrap_struct.h b/dp_wrap/inc/dp_wrap_struct.h new file mode 100644 index 0000000000..1dabd9eac2 --- /dev/null +++ b/dp_wrap/inc/dp_wrap_struct.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2020-2021 The Linux Foundation. All rights reserved. + * + * 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. + */ + +#ifndef _DP_WRAP_STRUCT_H_ +#define _DP_WRAP_STRUCT_H_ + +#if ATH_SUPPORT_WRAP +#if !WLAN_QWRAP_LEGACY + +#include + +#define WRAP_DEV_HASHSIZE 32 /*dev hash table size*/ + +/*device table simple hash function*/ +#define WRAP_DEV_HASH(addr) \ + (((const u_int8_t *)(addr))[QDF_MAC_ADDR_SIZE - 1] % WRAP_DEV_HASHSIZE) + +#define qwrap_err(params...) QDF_TRACE_ERROR(QDF_MODULE_ID_WRAP, ## params) +#define qwrap_warn(params...) QDF_TRACE_WARN(QDF_MODULE_ID_WRAP, ## params) +#define qwrap_info(params...) QDF_TRACE_INFO(QDF_MODULE_ID_WRAP, ## params) +#define qwrap_debug(params...) QDF_TRACE_DEBUG(QDF_MODULE_ID_WRAP, ## params) +#define qwrap_trace(params...) QDF_TRACE_DEBUG(QDF_MODULE_ID_WRAP, ## params) + +#define WRAP_ISOLATION_DEFVAL 0 + +typedef rwlock_t wrap_devt_lock_t; + +/*wrap device table*/ +typedef struct dp_wrap_devt { + struct dp_wrap_pdev *wrap_pdev; + /*back ptr to wrap pdev*/ + wrap_devt_lock_t wdt_lock; + /*lock for dev table*/ + TAILQ_HEAD(, dp_wrap_vdev) wdt_dev; /*head for device list*/ + ATH_LIST_HEAD(, dp_wrap_vdev) wdt_hash[WRAP_DEV_HASHSIZE]; + /*head for device hash*/ + TAILQ_HEAD(, dp_wrap_vdev) wdt_dev_vma; /*head for device list*/ + ATH_LIST_HEAD(, dp_wrap_vdev) wdt_hash_vma[WRAP_DEV_HASHSIZE]; + /*head for device hash*/ +} dp_wrap_devt_t; + +/*wrap pdev struct*/ +typedef struct dp_wrap_pdev { + struct dp_wrap_devt wp_devt; /*wrap device table*/ + u_int8_t wp_isolation; + int wp_use_cnt; /*wrap pdev use cnt*/ + struct wlan_objmgr_vdev *mpsta_vdev; + struct wlan_objmgr_vdev *wrap_vdev; + struct net_device *mpsta_dev; + u_int8_t nwrapvaps; /* Number of active WRAP APs */ + u_int8_t npstavaps; +} dp_pdev_wrap_t; + +/*wrap vdev struct*/ +typedef struct dp_wrap_vdev { + struct dp_wrap_pdev *wrap_pdev; /*back ptr to wrap pdev*/ + struct net_device *dev; + struct wlan_objmgr_vdev *vdev; + bool is_wrap; + bool is_mpsta; + bool is_psta; + bool is_wired_psta; + bool mat_enabled; + unsigned char wrap_dev_oma[ETH_ALEN]; /* dev oma mac address */ + unsigned char wrap_dev_vma[ETH_ALEN]; /* dev vma mac address */ + TAILQ_ENTRY(dp_wrap_vdev) wrap_dev_list; /*wrap oma dev list entry*/ + LIST_ENTRY(dp_wrap_vdev) wrap_dev_hash; /*wrap oma hash list entry*/ + TAILQ_ENTRY(dp_wrap_vdev) wrap_dev_list_vma; /*wrap vma dev list*/ + LIST_ENTRY(dp_wrap_vdev) wrap_dev_hash_vma; /*wrap vma hash list */ + void (*wlan_vdev_xmit_queue)(struct net_device *dev, wbuf_t wbuf); +} dp_vdev_wrap_t; +#endif +#endif +#endif diff --git a/dp_wrap/src/dp_wrap.c b/dp_wrap/src/dp_wrap.c new file mode 100644 index 0000000000..2d5a8d7727 --- /dev/null +++ b/dp_wrap/src/dp_wrap.c @@ -0,0 +1,837 @@ +/* + * Copyright (c) 2020-2021 The Linux Foundation. All rights reserved. + * + * 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. + */ + +#if ATH_SUPPORT_WRAP +#if !WLAN_QWRAP_LEGACY +#include +#include +#include +#include +#include + +#include "dp_wrap.h" + +#include +#ifdef QCA_NSS_WIFI_OFFLOAD_SUPPORT +#include "osif_nss_wifiol_if.h" +#include "osif_nss_wifiol_vdev_if.h" +#endif +extern void transcap_nwifi_to_8023(qdf_nbuf_t msdu); + +/** + * @brief Find wrap vdev object based on MAC address. + * + * @param wdt Ptr to the wrap device table. + * @param mac MAC address to look up. + * + * @return wrap_vdev on success + * @return NULL on failure + */ +dp_vdev_wrap_t *dp_wrap_wdev_find(struct dp_wrap_devt *wdt, unsigned char *mac) +{ + dp_vdev_wrap_t *wrap_vdev; + int hash; + rwlock_state_t lock_state; + + hash = WRAP_DEV_HASH(mac); + OS_RWLOCK_READ_LOCK(&wdt->wdt_lock, &lock_state); + LIST_FOREACH(wrap_vdev, &wdt->wdt_hash[hash], wrap_dev_hash) { + if (qdf_is_macaddr_equal((struct qdf_mac_addr *) + wrap_vdev->wrap_dev_oma, (struct qdf_mac_addr *)mac)) { + OS_RWLOCK_READ_UNLOCK(&wdt->wdt_lock, &lock_state); + return wrap_vdev; + } + } + OS_RWLOCK_READ_UNLOCK(&wdt->wdt_lock, &lock_state); + return NULL; +} + +/** + * @brief Find wrap vdev object based on MAC address. + * + * @param wdt Ptr to the wrap device table. + * @param mac MAC address to look up. + * + * @return wrap_vdev on success + * @return NULL on failure + */ + +dp_vdev_wrap_t *dp_wrap_wdev_vma_find(struct dp_wrap_devt *wdt, unsigned char *mac) +{ + dp_vdev_wrap_t *wrap_vdev; + int hash; + rwlock_state_t lock_state; + + hash = WRAP_DEV_HASH(mac); + OS_RWLOCK_READ_LOCK(&wdt->wdt_lock, &lock_state); + LIST_FOREACH(wrap_vdev, &wdt->wdt_hash_vma[hash], wrap_dev_hash_vma) { + if (qdf_is_macaddr_equal((struct qdf_mac_addr *) + wrap_vdev->wrap_dev_vma, (struct qdf_mac_addr *)mac)) { + OS_RWLOCK_READ_UNLOCK(&wdt->wdt_lock, &lock_state); + return wrap_vdev; + } + } + OS_RWLOCK_READ_UNLOCK(&wdt->wdt_lock, &lock_state); + return NULL; +} + +/** + * @brief Add wrap vdev object to the device table, also + * registers bridge hooks if this the first object. + * + * @param vdev objmgr Pointer to wrap_vdev to add. + * + * @return 0 on success + * @return -ve on failure + */ +int dp_wrap_dev_add(struct wlan_objmgr_vdev *vdev) +{ + int hash, hash_vma; + struct dp_wrap_pdev *wrap_pdev; + struct dp_wrap_vdev *wrap_vdev; + struct dp_wrap_devt *wdt; + rwlock_state_t lock_state; + + wrap_pdev = dp_wrap_get_pdev_handle(wlan_vdev_get_pdev(vdev)); + if (!wrap_pdev) { + qwrap_err(" wrap_pdev is NULL"); + return -EINVAL; + } + wrap_vdev = dp_wrap_get_vdev_handle(vdev); + if (!wrap_vdev) { + qwrap_err(" wrap_vdev is NULL"); + return -EINVAL; + } + wdt = &wrap_pdev->wp_devt; + hash = WRAP_DEV_HASH(wrap_vdev->wrap_dev_oma); + hash_vma = WRAP_DEV_HASH(wrap_vdev->wrap_dev_vma); + OS_RWLOCK_WRITE_LOCK_BH(&wdt->wdt_lock, &lock_state); + LIST_INSERT_HEAD(&wdt->wdt_hash[hash], wrap_vdev, wrap_dev_hash); + TAILQ_INSERT_TAIL(&wdt->wdt_dev, wrap_vdev, wrap_dev_list); + LIST_INSERT_HEAD(&wdt->wdt_hash_vma[hash_vma], wrap_vdev, + wrap_dev_hash_vma); + TAILQ_INSERT_TAIL(&wdt->wdt_dev_vma, wrap_vdev, wrap_dev_list_vma); + OS_RWLOCK_WRITE_UNLOCK_BH(&wdt->wdt_lock, &lock_state); + qwrap_info("Added vdev:%d to the list mat. pdev_id:%d", + vdev->vdev_objmgr.vdev_id, + wlan_objmgr_pdev_get_pdev_id( + wlan_vdev_get_pdev(vdev))); + return 0; +} + +/** + * @brief Delete wrap dev object from the device table, + * based on OMA address. + * + * @param vdev objmgr Pointer to wrap_vdev to delete. + * + * @return void + */ +void dp_wrap_dev_remove(struct wlan_objmgr_vdev *vdev) +{ + int hash; + struct dp_wrap_vdev *wrap_vdev, *wvdev, *temp; + struct dp_wrap_pdev *wrap_pdev; + struct dp_wrap_devt *wdt; + rwlock_state_t lock_state; + + wrap_pdev = dp_wrap_get_pdev_handle(wlan_vdev_get_pdev(vdev)); + if (!wrap_pdev) { + qwrap_err(" wrap_pdev is NULL"); + return; + } + wrap_vdev = dp_wrap_get_vdev_handle(vdev); + if (!wrap_vdev) { + qwrap_err(" wrap_vdev is NULL"); + return; + } + wdt = &wrap_pdev->wp_devt; + hash = WRAP_DEV_HASH(wrap_vdev->wrap_dev_oma); + OS_RWLOCK_WRITE_LOCK_BH(&wdt->wdt_lock, &lock_state); + LIST_FOREACH_SAFE(wvdev, &wdt->wdt_hash[hash], wrap_dev_hash, temp) { + if ((wvdev == wrap_vdev) && + qdf_is_macaddr_equal((struct qdf_mac_addr *) + wvdev->wrap_dev_oma, + (struct qdf_mac_addr *)wrap_vdev->wrap_dev_oma)) { + LIST_REMOVE(wvdev, wrap_dev_hash); + TAILQ_REMOVE(&wdt->wdt_dev, wvdev, wrap_dev_list); + OS_RWLOCK_WRITE_UNLOCK_BH(&wdt->wdt_lock, &lock_state); + qwrap_info("Removed vdev:%d from OMA list. pdev_id:%d", + vdev->vdev_objmgr.vdev_id, + wlan_objmgr_pdev_get_pdev_id( + wlan_vdev_get_pdev(vdev))); + return; + } + } + OS_RWLOCK_WRITE_UNLOCK_BH(&wdt->wdt_lock, &lock_state); +} + +/** + * @brief Delete wrap dev object from the device table, + * based on VMA address. + * + * @param vdev objmgr Pointer to wrap_vdev to delete. + * + * @return void + */ +void dp_wrap_dev_remove_vma(struct wlan_objmgr_vdev *vdev) +{ + int hash; + struct dp_wrap_vdev *wrap_vdev, *wvdev, *temp; + struct dp_wrap_pdev *wrap_pdev; + struct dp_wrap_devt *wdt; + rwlock_state_t lock_state; + + wrap_pdev = dp_wrap_get_pdev_handle(wlan_vdev_get_pdev(vdev)); + if (!wrap_pdev) { + qwrap_err(" wrap_pdev is NULL"); + return; + } + wrap_vdev = dp_wrap_get_vdev_handle(vdev); + if (!wrap_vdev) { + qwrap_err(" wrap_vdev is NULL"); + return; + } + wdt = &wrap_pdev->wp_devt; + hash = WRAP_DEV_HASH(wrap_vdev->wrap_dev_vma); + OS_RWLOCK_WRITE_LOCK_BH(&wdt->wdt_lock, &lock_state); + LIST_FOREACH_SAFE(wvdev, &wdt->wdt_hash_vma[hash], + wrap_dev_hash_vma, temp) { + if ((wvdev == wrap_vdev) && + qdf_is_macaddr_equal((struct qdf_mac_addr *) + wvdev->wrap_dev_vma, + (struct qdf_mac_addr *)wrap_vdev->wrap_dev_vma)) { + LIST_REMOVE(wvdev, wrap_dev_hash_vma); + TAILQ_REMOVE(&wdt->wdt_dev_vma, wvdev, + wrap_dev_list_vma); + OS_RWLOCK_WRITE_UNLOCK_BH(&wdt->wdt_lock, &lock_state); + qwrap_info("Removed vdev:%d from vma list. pdev_id:%d", + vdev->vdev_objmgr.vdev_id, + wlan_objmgr_pdev_get_pdev_id( + wlan_vdev_get_pdev(vdev))); + return; + } + } + OS_RWLOCK_WRITE_UNLOCK_BH(&wdt->wdt_lock, &lock_state); +} + +/** + * @brief WRAP device table attach + * + * @param pdev objmgr Pointer. + * @param wdt Ptr to wrap device table. + * + * @return void + */ +static void dp_wrap_devt_init(struct dp_wrap_pdev *wpdev, struct dp_wrap_devt *wdt) +{ + int i; + + OS_RWLOCK_INIT(&wdt->wdt_lock); + TAILQ_INIT(&wdt->wdt_dev); + TAILQ_INIT(&wdt->wdt_dev_vma); + for (i = 0; i < WRAP_DEV_HASHSIZE; i++) { + LIST_INIT(&wdt->wdt_hash[i]); + LIST_INIT(&wdt->wdt_hash_vma[i]); + } + wdt->wrap_pdev = wpdev; +} + +/** + * @brief wrap device table detach + * + * @param pdev objmgr Pointer. + * + * @return + */ +static void dp_wrap_devt_detach(struct dp_wrap_pdev *wpdev) +{ + struct dp_wrap_devt *wdt = &wpdev->wp_devt; + + OS_RWLOCK_DESTROY(&wdt->wdt_lock); + wdt->wrap_pdev = NULL; +} + +/** + * @brief wrap attach + * + * @param pdev objmgr Pointer. + * + * @return 0 on success + * @return -ve on failure + */ +int dp_wrap_attach(struct wlan_objmgr_pdev *pdev) +{ + int ret = 0; + struct dp_wrap_pdev *wrap_pdev = dp_wrap_get_pdev_handle(pdev); + + if (!wrap_pdev) { + qwrap_err(" wrap_pdev is NULL"); + return -EINVAL; + } + + OS_MEMZERO(wrap_pdev, sizeof(struct dp_wrap_pdev)); + wrap_pdev->wp_use_cnt++; + dp_wrap_devt_init(wrap_pdev, &wrap_pdev->wp_devt); + wrap_pdev->wp_isolation = WRAP_ISOLATION_DEFVAL; + qwrap_info("Wrap Attached: Wrap_pdev =%pK &wrap_pdev->wp_devt=%pK", + wrap_pdev, &wrap_pdev->wp_devt); + return ret; +} +qdf_export_symbol(dp_wrap_attach); + +/** + * @brief wrap vdev attach + * + * @param vdev objmgr Pointer. + * + * @return void + */ +void dp_wrap_vdev_attach(struct wlan_objmgr_vdev *vdev) +{ + if (dp_wrap_vdev_is_psta(vdev)) { + dp_wrap_vdev_set_psta(vdev); + if (dp_wrap_vdev_is_mpsta(vdev)) + dp_wrap_vdev_set_mpsta(vdev); + } + if (dp_wrap_vdev_is_wrap(vdev)) + dp_wrap_vdev_set_wrap(vdev); +} + +/** + * @brief wrap vdev detach + * + * @param vdev objmgr Pointer. + * + * @return void + */ +void dp_wrap_vdev_detach(struct wlan_objmgr_vdev *vdev) +{ + if (dp_wrap_vdev_is_psta(vdev)) + dp_wrap_vdev_clear_psta(vdev); + if (dp_wrap_vdev_is_wrap(vdev)) + dp_wrap_vdev_clear_wrap(vdev); +} + +/** + * @brief wrap detach + * + * @param pdev objmgr Pointer. + * + * @return 0 on success + * @return -ve on failure + */ +int dp_wrap_detach(struct wlan_objmgr_pdev *pdev) +{ + int ret = 0; + struct dp_wrap_pdev *wrap_pdev = dp_wrap_get_pdev_handle(pdev); + + if (!wrap_pdev) { + qwrap_err(" wrap_pdev is NULL"); + return -EINVAL; + } + wrap_pdev->wp_use_cnt--; + if (wrap_pdev->wp_use_cnt == 0) { + dp_wrap_devt_detach(wrap_pdev); + qwrap_info("qca wrap detached\n"); + } + return ret; +} +qdf_export_symbol(dp_wrap_detach); + +/** + * dp_wrap_vdev_set_psta() - Set psta flag + * + * @vdev: handle to the objmgr vdev. + * Return: void + */ +void dp_wrap_vdev_set_psta(struct wlan_objmgr_vdev *vdev) +{ + struct dp_wrap_vdev *wvdev; + struct dp_wrap_pdev *wpdev; + struct wlan_objmgr_pdev *pdev; + + if (vdev) { + pdev = vdev->vdev_objmgr.wlan_pdev; + if (!pdev) { + qwrap_err(" pdev is NULL"); + return; + } + wpdev = dp_wrap_get_pdev_handle(pdev); + if (!wpdev) { + qwrap_err(" wrap_pdev is NULL"); + return; + } + wvdev = dp_wrap_get_vdev_handle(vdev); + if (!wvdev) { + qwrap_err(" wrap_vdev is NULL"); + return; + } + OS_MEMZERO(wvdev, sizeof(struct dp_wrap_vdev)); + wvdev->wrap_pdev = wpdev; + wvdev->is_psta = 1; + wvdev->vdev = vdev; + wpdev->npstavaps++; + if (dp_wrap_vdev_is_wired_psta(vdev)) + wvdev->is_wired_psta = 1; + if (dp_wrap_vdev_is_mat_set(vdev)) { + wvdev->mat_enabled = 1; + WLAN_ADDR_COPY(wvdev->wrap_dev_oma, + vdev->vdev_mlme.mataddr); + } else { + WLAN_ADDR_COPY(wvdev->wrap_dev_oma, + vdev->vdev_mlme.macaddr); + } + WLAN_ADDR_COPY(wvdev->wrap_dev_vma, vdev->vdev_mlme.macaddr); + qwrap_info("set PSTA flags for vdev_id:%d pdev_id:%d OMA addr:" + QDF_MAC_ADDR_FMT "Vma addr:" QDF_MAC_ADDR_FMT, + vdev->vdev_objmgr.vdev_id, + wlan_objmgr_pdev_get_pdev_id(pdev), + QDF_MAC_ADDR_REF(wvdev->wrap_dev_oma), + QDF_MAC_ADDR_REF(wvdev->wrap_dev_vma)); + } +} + +/** + * dp_wrap_vdev_clear_psta() - clear psta flag + * + * @vdev: handle to the objmgr vdev. + * Return: void + */ +void dp_wrap_vdev_clear_psta(struct wlan_objmgr_vdev *vdev) +{ + struct dp_wrap_pdev *wpdev; + struct dp_wrap_vdev *wvdev; + + if (vdev) { + wvdev = dp_wrap_get_vdev_handle(vdev); + if (!wvdev) { + qwrap_err(" wrap_vdev is NULL"); + return; + } + wpdev = wvdev->wrap_pdev; + if (!wpdev) { + qwrap_err(" wrap_pdev is NULL"); + return; + } + wpdev->npstavaps--; + if (wvdev->is_mpsta) { + wpdev->mpsta_vdev = NULL; + wpdev->mpsta_dev = NULL; + } + qwrap_info("clear PSTA flag vdev_id:%d pdev_id:%d mpsta:%d", + vdev->vdev_objmgr.vdev_id, + wlan_objmgr_pdev_get_pdev_id(wlan_vdev_get_pdev + (vdev)), wvdev->is_mpsta); + } +} + +/** + * dp_wrap_vdev_set_mpsta() - Set mpsta flag + * + * @vdev: handle to the objmgr vdev. + * Return: void + */ +void dp_wrap_vdev_set_mpsta(struct wlan_objmgr_vdev *vdev) +{ + struct dp_wrap_vdev *wvdev; + struct dp_wrap_pdev *wpdev; + struct wlan_objmgr_pdev *pdev; + + if (vdev) { + pdev = vdev->vdev_objmgr.wlan_pdev; + if (!pdev) { + qwrap_err(" pdev is NULL"); + return; + } + wpdev = dp_wrap_get_pdev_handle(pdev); + if (!wpdev) { + qwrap_err(" wrap_pdev is NULL"); + return; + } + wvdev = dp_wrap_get_vdev_handle(vdev); + if (!wvdev) { + qwrap_err(" wrap_vdev is NULL"); + return; + } + wvdev->is_mpsta = 1; + wpdev->mpsta_vdev = vdev; + qwrap_info("set MPSTA flags vdev_id:%d pdev_id:%d OMA addr:" + QDF_MAC_ADDR_FMT "Vma addr:" QDF_MAC_ADDR_FMT, + vdev->vdev_objmgr.vdev_id, + wlan_objmgr_pdev_get_pdev_id(pdev), + QDF_MAC_ADDR_REF(wvdev->wrap_dev_oma), + QDF_MAC_ADDR_REF(wvdev->wrap_dev_vma)); + } +} + +/** + * dp_wrap_vdev_set_wrap() - set wrap flag + * + * @vdev: handle to the objmgr vdev. + * Return: void + */ +void dp_wrap_vdev_set_wrap(struct wlan_objmgr_vdev *vdev) +{ + struct dp_wrap_vdev *wvdev; + struct dp_wrap_pdev *wpdev; + struct wlan_objmgr_pdev *pdev; + + if (vdev) { + pdev = vdev->vdev_objmgr.wlan_pdev; + if (!pdev) { + qwrap_err(" pdev is NULL"); + return; + } + wpdev = dp_wrap_get_pdev_handle(pdev); + if (!wpdev) { + qwrap_err(" wrap_pdev is NULL"); + return; + } + wvdev = dp_wrap_get_vdev_handle(vdev); + if (!wvdev) { + qwrap_err(" wrap_vdev is NULL"); + return; + } + OS_MEMZERO(wvdev, sizeof(struct dp_wrap_vdev)); + wvdev->wrap_pdev = wpdev; + wvdev->is_wrap = 1; + wvdev->vdev = vdev; + wpdev->wrap_vdev = vdev; + wpdev->nwrapvaps++; + qwrap_info("set WRAP flags for vdev_id:%d pdev_id:%d OMA addr:" + QDF_MAC_ADDR_FMT "Vma addr:" QDF_MAC_ADDR_FMT, + vdev->vdev_objmgr.vdev_id, + wlan_objmgr_pdev_get_pdev_id(pdev), + QDF_MAC_ADDR_REF(wvdev->wrap_dev_oma), + QDF_MAC_ADDR_REF(wvdev->wrap_dev_vma)); + } +} + +/** + * dp_wrap_vdev_clear_wrap() - clear wrap flag + * + * @vdev: handle to the objmgr vdev. + * Return: void + */ +void dp_wrap_vdev_clear_wrap(struct wlan_objmgr_vdev *vdev) +{ + struct wlan_objmgr_pdev *pdev; + struct dp_wrap_pdev *wpdev; + + if (vdev) { + pdev = vdev->vdev_objmgr.wlan_pdev; + if (!pdev) { + qwrap_err(" pdev is NULL"); + return; + } + wpdev = dp_wrap_get_pdev_handle(pdev); + if (!wpdev) { + qwrap_err(" wpdev is NULL"); + return; + } + wpdev->wrap_vdev = NULL; + wpdev->nwrapvaps--; + qwrap_info("clear WRAP flags for vdev_id:%d pdev_id:%d", + vdev->vdev_objmgr.vdev_id, + wlan_objmgr_pdev_get_pdev_id(pdev)); + } +} + +static inline int +dp_wrap_tx_bridge(struct wlan_objmgr_vdev *vdev, struct dp_wrap_vdev **wvdev, + struct sk_buff **skb) { + /* Assuming native wifi or raw mode is not + * enabled in beeliner, to be revisted later + */ + struct ether_header *eh = (struct ether_header *)((*skb)->data); + struct dp_wrap_pdev *wpdev; + struct dp_wrap_vdev *wrap_vdev; + + /* Mpsta vap here, find the correct tx vap from the wrap + * common based on src address + */ + wpdev = dp_wrap_get_pdev_handle(wlan_vdev_get_pdev(vdev)); + if (qdf_unlikely(!wpdev)) + return 1; + wrap_vdev = dp_wrap_wdev_find(&wpdev->wp_devt, eh->ether_shost); + if (qdf_unlikely(!wrap_vdev)) + return 1; + *wvdev = wrap_vdev; + if (wrap_vdev) { + if (qdf_unlikely((IEEE80211_IS_MULTICAST(eh->ether_dhost) || + IEEE80211_IS_BROADCAST(eh->ether_dhost)))) { + *skb = qdf_nbuf_unshare(*skb); + } + return 0; + } else { + /* When proxysta is not created, drop the packet. Donot send + * packet on mainproxysta. Return 1 here to drop the packet + * when psta is not yet created. + */ + qwrap_err("Drop pkt, PSTA is not created src mac:" + QDF_MAC_ADDR_FMT "vdev_id:%d", QDF_MAC_ADDR_REF + (eh->ether_shost), vdev->vdev_objmgr.vdev_id); + return 1; + } +} + +int dp_wrap_tx_process(struct net_device **dev, struct wlan_objmgr_vdev *vdev, + struct sk_buff **skb) +{ + struct dp_wrap_vdev *wvdev = NULL; + struct ether_header *eh; + + if (qdf_unlikely(dp_wrap_vdev_is_mpsta(vdev))) { + if (dp_wrap_tx_bridge(vdev, &wvdev, skb)) + return 1; + if (*(skb) == NULL) { + qwrap_err("Drop pkt, SKB is null dev_id:%d", + vdev->vdev_objmgr.vdev_id); + return 1; + } + eh = (struct ether_header *)((*skb)->data); + if (qdf_likely(dp_wrap_vdev_is_psta(wvdev->vdev))) { + if (dp_wrap_mat_tx(wvdev, (wbuf_t)*skb)) { + qwrap_err("Drop pkt,MAT error:"QDF_MAC_ADDR_FMT + "vdev_id:%d", QDF_MAC_ADDR_REF( + eh->ether_shost), + vdev->vdev_objmgr.vdev_id); + return 1; + } + } + *dev = wvdev->dev; + vdev = wvdev->vdev; + } + if (wlan_vdev_is_up(vdev) != QDF_STATUS_SUCCESS) { + qwrap_err("Drop pkt, vdev is not up:"QDF_MAC_ADDR_FMT + "vdevid:%d", QDF_MAC_ADDR_REF(eh->ether_shost), + vdev->vdev_objmgr.vdev_id); + return 1; + } + return 0; +} +qdf_export_symbol(dp_wrap_tx_process); + +static inline +int dp_wrap_rx_bridge(struct wlan_objmgr_vdev *vdev, struct net_device **dev, + struct dp_wrap_vdev *wvdev, struct sk_buff *skb) +{ + /* Assuming native wifi or raw mode is not + * enabled in beeliner, to be revisted later + */ + struct ether_header *eh = (struct ether_header *)skb->data; + struct dp_wrap_pdev *wpdev = wvdev->wrap_pdev; + int isolation = wpdev->wp_isolation; + int ret = 0; + struct dp_wrap_vdev *mpsta_wvdev, *wrap_wvdev, *t_wvdev; + struct wlan_objmgr_vdev *mpsta_vdev, *wrap_vdev; + + mpsta_vdev = wpdev->mpsta_vdev; + wrap_vdev = wpdev->wrap_vdev; + if (isolation == 0) { + /* Isolatio mode,Wired and wireless clients connected + * to Qwrap Ap can talk through Qwrap bridge + */ + if ((wvdev->is_mpsta == 0) && (wvdev->is_psta) && + (mpsta_vdev)) { + if (qdf_likely( + !(eh->ether_type == htons(ETHERTYPE_PAE)))) { + /*get mpsta vap , for qwrap bridge learning + * is always through main proxy sta + */ + skb->dev = wpdev->mpsta_dev; + *dev = skb->dev; + } + ret = 0; + } + } else { + /* isolation mode enabled. Wired and wireless client + * connected to Qwrap AP can talk through root AP + */ + if (wvdev->is_psta && !wvdev->is_mpsta && + wvdev->is_wired_psta) { + /* Packets recevied through wired psta vap */ + if (qdf_likely(!(eh->ether_type == htons(ETHERTYPE_PAE) + )) && (mpsta_vdev)) { + /* rx packets from wired psta should go through + * bridge.Here qwrap bridge learning for wired + * proxy clients is always through mpsta + */ + skb->dev = wpdev->mpsta_dev; + *dev = skb->dev; + ret = 0; + } + } else if (wvdev->is_psta && !wvdev->is_mpsta && + !wvdev->is_wired_psta && + !(eh->ether_type == htons(ETHERTYPE_PAE)) && + (wrap_vdev)) { + /* rx unicast from wireless proxy, client + * should always xmit through wrap AP vap + */ + wrap_wvdev = dp_wrap_get_vdev_handle(wrap_vdev); + wrap_wvdev->wlan_vdev_xmit_queue(wrap_wvdev->dev, skb); + ret = 1; + } else if ((wvdev->is_wrap && + !(eh->ether_type == htons(ETHERTYPE_PAE))) && + (mpsta_vdev)) { + /* rx from wrap AP , since wrap not connected to + * in isolation , should always xmit through + * main proxy vap + */ + mpsta_wvdev = dp_wrap_get_vdev_handle(mpsta_vdev); + mpsta_wvdev->wlan_vdev_xmit_queue( + mpsta_wvdev->dev, skb); + ret = 1; + } else if (wvdev->is_mpsta && + IEEE80211_IS_MULTICAST(eh->ether_dhost) && + (mpsta_vdev) && (wrap_vdev)) { + /*check oma or vma, for MPSTA both are same*/ + mpsta_wvdev = dp_wrap_get_vdev_handle(mpsta_vdev); + if ((OS_MEMCMP(mpsta_wvdev->wrap_dev_oma, + eh->ether_shost, 6) == 0)) { + /* Multicast orginated from mpsta/bridge + * should always xmit through wrap AP vap + */ + wrap_wvdev = dp_wrap_get_vdev_handle( + wrap_vdev); + wrap_wvdev->wlan_vdev_xmit_queue( + wrap_wvdev->dev, skb); + ret = 1; + } else { + t_wvdev = dp_wrap_wdev_vma_find( + &wpdev->wp_devt, + eh->ether_shost); + if (t_wvdev) { + if (t_wvdev->is_wired_psta) { + /*Multicast received from wired + *clients , forward to wrap AP + */ + wrap_wvdev = + dp_wrap_get_vdev_handle( + wrap_vdev); + wrap_wvdev-> + wlan_vdev_xmit_queue + (wrap_wvdev->dev, skb); + ret = 1; + } else { + /*Multicast received from + *wireless client,fwd to bridge + */ + skb->dev = wpdev->mpsta_dev; + *dev = skb->dev; + ret = 0; + } + } else { + qdf_nbuf_t copy; + + copy = qdf_nbuf_copy(skb); + /*Multicast received from client behind + *root side forward to both wrap and + *bridge side + */ + if (copy) { + wrap_wvdev = + dp_wrap_get_vdev_handle( + wrap_vdev); + wrap_wvdev-> + wlan_vdev_xmit_queue( + wrap_wvdev->dev, copy); + } else { + qdf_err("Wrap buf cpy fail"); + } + skb->dev = wpdev->mpsta_dev; + *dev = skb->dev; + ret = 0; + } + } + } + } + return ret; +} + +int dp_wrap_rx_process(struct net_device **dev, struct wlan_objmgr_vdev *vdev, + struct sk_buff *skb, int *nwifi) +{ + struct dp_wrap_vdev *wvdev; + int rv = 0; + + wvdev = dp_wrap_get_vdev_handle(vdev); + + if (qdf_unlikely(dp_wrap_vdev_is_psta(vdev) || dp_wrap_vdev_is_wrap(vdev))) { + if (*nwifi) { + transcap_nwifi_to_8023(skb); + *nwifi = 0; + } + dp_wrap_mat_rx(wvdev, (wbuf_t)skb); + rv = dp_wrap_rx_bridge(vdev, dev, wvdev, skb); + } + return rv; +} +qdf_export_symbol(dp_wrap_rx_process); + +void +dp_wrap_vdev_set_netdev(struct wlan_objmgr_vdev *vdev, struct net_device *dev) +{ + struct dp_wrap_vdev *wvdev; + struct dp_wrap_pdev *wpdev; + struct wlan_objmgr_pdev *pdev; + + if (vdev) { + wvdev = dp_wrap_get_vdev_handle(vdev); + if (!wvdev) { + qwrap_err("wrap_vdev is NULL"); + return; + } + wvdev->dev = dev; + if (dp_wrap_vdev_is_mpsta(vdev)) { + pdev = vdev->vdev_objmgr.wlan_pdev; + if (!pdev) { + qwrap_err("pdev is NULL"); + return; + } + wpdev = dp_wrap_get_pdev_handle(pdev); + if (!wpdev) { + qwrap_err(" wpdev is NULL"); + return; + } + wpdev->mpsta_dev = dev; + } + } +} + +/** + * dp_wrap_register_xmit_handler() - Register xmit handler in wrap_vdev object + * + * @vdev: handle to the objmgr vdev + * @func: xmit handler function to register + * Return: void + */ +void dp_wrap_register_xmit_handler(struct wlan_objmgr_vdev *vdev, + void (*wlan_vdev_xmit_queue) + (struct net_device *dev, wbuf_t wbuf)) +{ + struct dp_wrap_vdev *wrap_vdev; + + wrap_vdev = dp_wrap_get_vdev_handle(vdev); + + if (wrap_vdev) + wrap_vdev->wlan_vdev_xmit_queue = wlan_vdev_xmit_queue; + else + qwrap_err("Error in registering xmit queue"); +} +#endif +#endif diff --git a/dp_wrap/src/dp_wrap_mat.c b/dp_wrap/src/dp_wrap_mat.c new file mode 100644 index 0000000000..142b0cf754 --- /dev/null +++ b/dp_wrap/src/dp_wrap_mat.c @@ -0,0 +1,590 @@ +/* + * Copyright (c) 2020-2021 The Linux Foundation. All rights reserved. + * + * 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. + */ + +#if ATH_SUPPORT_WRAP +#if !WLAN_QWRAP_LEGACY +/*WRAP includes for MAT*/ +#include +#include +#include +#include +#include +#include "asf_print.h" /* asf_print_setup */ +#include "qdf_mem.h" /* qdf_mem_malloc,free */ +#include "qdf_lock.h" /* qdf_spinlock_* */ +#include "qdf_types.h" /* qdf_vprint */ +#include "dp_wrap.h" +#include "ol_if_athvar.h" + +extern char *arps[]; + +extern void +compute_udp_checksum(qdf_net_iphdr_t *p_iph, unsigned short *ip_payload); + +static u16 +checksum(u16 protocol, u16 len, u8 src_addr[], u8 dest_addr[], + u16 addrleninbytes, u8 *buff) +{ + u16 pad = 0; + u16 word16; + u32 sum = 0; + int i; + + /* Find out if the length of data is even or odd number. If odd, + * add a padding byte = 0 at the end of packet + */ + if (len & 1) { + /* Take care of the last byte by itself. */ + len -= 1; + pad = 1; + } + + /* make 16 bit words out of every two adjacent 8 bit words and + * calculate the sum of all 16 bit words + */ + for (i = 0; i < len; i = i + 2) { + word16 = buff[i]; + word16 = (word16 << 8) + buff[i + 1]; + sum = sum + (u32)word16; + } + + if (pad) { + /* Get the last byte */ + word16 = buff[len]; + word16 <<= 8; + sum = sum + (u32)word16; + } + + /* add the UDP pseudo header which contains the IP source and + * destination addresses. + */ + for (i = 0; i < addrleninbytes; i = i + 2) { + word16 = src_addr[i]; + word16 = (word16 << 8) + src_addr[i + 1]; + sum = sum + (u32)word16; + } + + for (i = 0; i < addrleninbytes; i = i + 2) { + word16 = dest_addr[i]; + word16 = (word16 << 8) + dest_addr[i + 1]; + sum = sum + (u32)word16; + } + + /* the protocol number and the length of the s packet */ + sum = sum + (u32)protocol + (u32)(len + pad); + + /* keep only the last 16 bits of the 32 bit calculated sum and + * add back the carries + */ + while (sum >> 16) { + sum = (sum & 0xFFFF) + (sum >> 16); + } + + /* Take the one's complement of sum */ + sum = ~sum; + + return (u16)sum; +} + +struct eth_arphdr { + unsigned short ar_hrd, /* format of hardware address */ + ar_pro; /* format of protocol address */ + unsigned char ar_hln, /* length of hardware address */ + ar_pln; /* length of protocol address */ + unsigned short ar_op; /* ARP opcode (command) */ + unsigned char ar_sha[ETH_ALEN], /* sender hardware address */ + ar_sip[4], /* sender IP address */ + ar_tha[ETH_ALEN], /* target hardware address */ + ar_tip[4]; /* target IP address */ +} __attribute__((__packed__)); + +struct dhcp_option { + u_char type; + u_char len; + u_char value[0]; /* option value*/ +} __attribute__((__packed__)); + +struct dhcp_packet { + u_char op; /* packet opcode type */ + u_char htype; /* hardware addr type */ + u_char hlen; /* hardware addr length */ + u_char hops; /* gateway hops */ + u32 xid; /* transaction ID */ + u16 secs; /* seconds since boot began */ + u16 flags; /* flags */ + struct in_addr ciaddr; /* client IP address */ + struct in_addr yiaddr; /* 'your' IP address */ + struct in_addr siaddr; /* server IP address */ + struct in_addr giaddr; /* gateway IP address */ + u_char chaddr[16]; /* client hardware address */ + u_char sname[64]; /* server host name */ + u_char file[128]; /* boot file name */ + u_char magic_cookie[4]; /* magic cookie */ + u_char options[0]; /* variable-length options field */ +} __attribute__((__packed__)); + +struct eth_icmp6_lladdr { + unsigned char type; + unsigned char len; + unsigned char addr[6]; /* hardware address */ +} __attribute__((__packed__)); + +typedef struct eth_icmp6_lladdr eth_icmp6_lladdr_t; +/* Option: (53) DHCP Message Type */ +#define OPTION_DHCP_MSG_TYPE 0x35 + /* DHCP: Discover (1) */ +#define DHCP_DISOVER 1 +/*Option: (50) Requested IP Address */ +#define OPTION_DHCP_REQUESTED_IP_ADDR 0x32 + +/** + * @brief WRAP MAT function for transmit. + * + * @param wrap_vdev handle. + * @param buf + * + * @return 0 On success. + * @return -ve On failure. + */ +int dp_wrap_mat_tx(struct dp_wrap_vdev *wvdev, wbuf_t buf) +{ + struct ether_header *eh; + u16 ether_type; + int contig_len = sizeof(struct ether_header); + int pktlen = wbuf_get_pktlen(buf); + u8 *src_mac, *des_mac, *p, ismcast; + u8 *arp_smac = NULL; + u8 *arp_dmac = NULL; + struct eth_arphdr *parp = NULL; + + if (!wvdev->mat_enabled) + return 0; + + if (pktlen < contig_len) + return -EINVAL; + + eh = (struct ether_header *)(wbuf_header(buf)); + p = (u8 *)(eh + 1); + + ether_type = eh->ether_type; + src_mac = eh->ether_shost; + des_mac = eh->ether_dhost; + ismcast = IEEE80211_IS_MULTICAST(des_mac); + + qwrap_trace("src mac:" QDF_MAC_ADDR_FMT " dst mac: " + " "QDF_MAC_ADDR_FMT " " + "ether type: %d", QDF_MAC_ADDR_REF(src_mac), + QDF_MAC_ADDR_REF(des_mac), ether_type); + + if (ether_type == htons(ETH_P_PAE)) + return 0; + + if (ether_type == htons(ETHERTYPE_ARP)) { + parp = (struct eth_arphdr *)p; + contig_len += sizeof(struct eth_arphdr); + + if (pktlen < contig_len) + return -EINVAL; + + if (parp->ar_hln == ETH_ALEN && + parp->ar_pro == htons(ETH_P_IP)) { + arp_smac = parp->ar_sha; + arp_dmac = parp->ar_tha; + } else { + parp = NULL; + } + } + + if (parp) { + if (parp->ar_op == htons(ARPOP_REQUEST) || + parp->ar_op == htons(ARPOP_REPLY)) { + IEEE80211_ADDR_COPY(arp_smac, wvdev->wrap_dev_vma); + qwrap_debug("ARP %s\t" QDF_IPV4_ADDR_STR "\t" + " " QDF_MAC_ADDR_FMT "\t" + " " QDF_IPV4_ADDR_STR "\t" + " " QDF_MAC_ADDR_FMT "\n", + arps[qdf_ntohs(parp->ar_op)], + QDF_IPV4_ADDR_ARRAY(parp->ar_sip), + QDF_MAC_ADDR_REF(parp->ar_sha), + QDF_IPV4_ADDR_ARRAY(parp->ar_tip), + QDF_MAC_ADDR_REF(parp->ar_tha)); + } + } else if (ether_type == htons(ETHERTYPE_IP)) { + struct iphdr *p_ip = (struct iphdr *)(p); + qdf_net_udphdr_t *p_udp; + s16 ip_hlen = 0; + u16 udplen = 0; + + contig_len += sizeof(struct iphdr); + if (pktlen < contig_len) + return -EINVAL; + + ip_hlen = p_ip->ihl * 4; + p_udp = (qdf_net_udphdr_t *)(((u8 *)p_ip) + ip_hlen); + + /* If Proto is UDP */ + if (p_ip->protocol == IPPROTO_UDP) { + contig_len += sizeof(struct udphdr); + if (pktlen < contig_len) + return -EINVAL; + udplen = p_ip->tot_len - (p_ip->ihl * 4); + } + /* + * DHCP request UDP Client SP = 68 (bootpc), DP = 67 (bootps). + */ + if ((p_ip->protocol == IPPROTO_UDP) && + (p_udp->dst_port == htons(67))) { + struct dhcp_packet *p_dhcp; + struct dhcp_option *option; + u8 *value; + u8 dhcpDicover = 0; + + p_dhcp = (struct dhcp_packet *)(((u8 *)p_udp) + + sizeof(struct udphdr)); + option = (struct dhcp_option *)p_dhcp->options; + + contig_len += sizeof(struct dhcp_packet); + if (pktlen < contig_len) + return -EINVAL; + + qwrap_debug("src mac:" QDF_MAC_ADDR_FMT " " + "dst mac:" QDF_MAC_ADDR_FMT " " + "ether type: %d", + QDF_MAC_ADDR_REF(src_mac), + QDF_MAC_ADDR_REF(des_mac), + ether_type); + qwrap_debug("DHCP: sport %d dport %d len %d" + "chaddr:" QDF_MAC_ADDR_FMT " " + "opcode: %d", + p_udp->src_port, p_udp->dst_port, + udplen, + QDF_MAC_ADDR_REF(p_dhcp->chaddr), + p_dhcp->op); + + if (p_dhcp->magic_cookie[0] == 0x63 && + p_dhcp->magic_cookie[1] == 0x82 && + p_dhcp->magic_cookie[2] == 0x53 && + p_dhcp->magic_cookie[3] == 0x63) { + if (p_dhcp->op == 1) { + /* dhcp REQ or DISCOVER*/ +#ifdef BIG_ENDIAN_HOST + if ((p_dhcp->flags & 0x8000) == 0) { + p_dhcp->flags |= 0x8000; +#else + if ((p_dhcp->flags & 0x0080) == 0) { + p_dhcp->flags |= 0x0080; +#endif + + compute_udp_checksum((qdf_net_iphdr_t *)p_ip, (unsigned short *)p_udp); + } + } + } /* Magic cookie for DHCP*/ + + while (option->type != 0xFF) { + /*Not an end option*/ + + contig_len += (option->len + 2); + if (pktlen < contig_len) + return -EINVAL; + + value = option->value; + if (option->type == OPTION_DHCP_MSG_TYPE) { + if (value[0] == DHCP_DISOVER) { + dhcpDicover = 1; + } + option = (struct dhcp_option *)(value + + option->len); + + } else if (option->type == + OPTION_DHCP_REQUESTED_IP_ADDR && + dhcpDicover == 1) { + u32 *addr = (u32 *)value; + + if (*addr != 0) { + memset(value, 0, option->len); + compute_udp_checksum((qdf_net_iphdr_t *)p_ip, (unsigned short *)p_udp); + } + qwrap_debug("DHCP Disover with" + "IP Addr:" + " " QDF_IPV4_ADDR_STR " " + "from caddr: " + " " QDF_MAC_ADDR_FMT " ", + QDF_IPV4_ADDR_ARRAY(value), + QDF_MAC_ADDR_REF(p_dhcp->chaddr)); + + break; + } else { + option = (struct dhcp_option *)(value + + option->len); + } + } + } + } else if (ether_type == htons(ETHERTYPE_IPV6)) { + struct ipv6hdr *p_ip6 = (struct ipv6hdr *)(p); + eth_icmp6_lladdr_t *phwaddr; + int change_packet = 1; + + contig_len += sizeof(struct ipv6hdr); + if (pktlen < contig_len) + return -EINVAL; + + if (p_ip6->nexthdr == IPPROTO_ICMPV6) { + struct icmp6hdr *p_icmp6; + u16 icmp6len = ntohs(p_ip6->payload_len); + p_icmp6 = (struct icmp6hdr *)(p_ip6 + 1); + + contig_len += sizeof(struct icmp6hdr); + if (pktlen < contig_len) + return -EINVAL; + /* + * It seems that we only have to modify IPv6 packets + * sent by a Proxy STA. Both the solicitation and + * adevertisement packets have the STA's OMA. + * Flip that to the VMA. + */ + switch (p_icmp6->icmp6_type) { + case NDISC_NEIGHBOUR_SOLICITATION: + case NDISC_NEIGHBOUR_ADVERTISEMENT: + { + contig_len += 16 + sizeof(eth_icmp6_lladdr_t); + if (pktlen < contig_len) + return -EINVAL; + + phwaddr = (eth_icmp6_lladdr_t *) + ((u8 *)(p_icmp6 + 1) + 16); + IEEE80211_ADDR_COPY(phwaddr->addr, + wvdev->wrap_dev_vma); + p_icmp6->icmp6_cksum = 0; + p_icmp6->icmp6_cksum = htons(checksum( + (u16)IPPROTO_ICMPV6, + icmp6len, + p_ip6->saddr.s6_addr, + p_ip6->daddr.s6_addr, + 16 /* IPv6 has 32 byte addresses */, + (u8 *)p_icmp6)); + break; + } + case NDISC_ROUTER_SOLICITATION: + { + contig_len += sizeof(eth_icmp6_lladdr_t); + if (pktlen < contig_len) + return -EINVAL; + + /*replace the HW address with the VMA*/ + phwaddr = (eth_icmp6_lladdr_t *) + ((u8 *)(p_icmp6 + 1)); + break; + } + default: + change_packet = 0; + break; + } + + if (change_packet) { + IEEE80211_ADDR_COPY(phwaddr->addr, + wvdev->wrap_dev_vma); + p_icmp6->icmp6_cksum = 0; + p_icmp6->icmp6_cksum = htons(checksum((u16)IPPROTO_ICMPV6, + icmp6len, + p_ip6->saddr.s6_addr, + p_ip6->daddr.s6_addr, + /* IPv6 has 32 byte addresses */ + 16, + (u8 *)p_icmp6)); + qwrap_debug("IPV6 type %d\t sip:" + QDF_IPV6_ADDR_STR " \t hwaddr:" + QDF_MAC_ADDR_FMT "\t dip:" + QDF_IPV6_ADDR_STR "\t\n", + p_icmp6->icmp6_type, + QDF_IPV6_ADDR_ARRAY(p_ip6->saddr.s6_addr), + QDF_MAC_ADDR_REF(phwaddr->addr), + QDF_IPV6_ADDR_ARRAY(p_ip6->daddr.s6_addr)); + } + } + } + IEEE80211_ADDR_COPY(src_mac, wvdev->wrap_dev_vma); + return 0; +} + +/** + * @brief WRAP MAT on receive path. + * + * @param wrap_vdev handle. + * @param buf + * + * @return 0 On success. + * @return -ve On failure. + */ +int dp_wrap_mat_rx(struct dp_wrap_vdev *wvdev, wbuf_t buf) +{ + struct ether_header *eh; + u16 ether_type; + int contig_len = sizeof(struct ether_header); + int pktlen = wbuf_get_pktlen(buf); + u8 *src_mac, *des_mac, *p, ismcast; + u8 *arp_smac = NULL; + u8 *arp_dmac = NULL; + struct eth_arphdr *parp = NULL; + + eh = (struct ether_header *)(wbuf_header(buf)); + p = (u8 *)(eh + 1); + + if (!wvdev->is_wrap && !wvdev->is_psta) + return 0; + + if (pktlen < contig_len) + return -EINVAL; + + ether_type = eh->ether_type; + src_mac = eh->ether_shost; + des_mac = eh->ether_dhost; + ismcast = IEEE80211_IS_MULTICAST(des_mac); + + if (ether_type == htons(ETH_P_PAE)) { + /* mark the pkt to allow local delivery on the device */ + buf->mark |= WRAP_MARK_ROUTE; + return 0; + } + + qwrap_trace("src mac:" QDF_MAC_ADDR_FMT " dst mac: " + " " QDF_MAC_ADDR_FMT " " + "ether type:%d", QDF_MAC_ADDR_REF(src_mac), + QDF_MAC_ADDR_REF(des_mac), ether_type); + + if (ether_type == htons(ETHERTYPE_ARP)) { + parp = (struct eth_arphdr *)p; + contig_len += sizeof(struct eth_arphdr); + + if (pktlen < contig_len) + return -EINVAL; + + if (parp->ar_hln == ETH_ALEN && + parp->ar_pro == htons(ETH_P_IP)) { + arp_smac = parp->ar_sha; + arp_dmac = parp->ar_tha; + } else { + parp = NULL; + } + } + + if (ismcast && wvdev->is_mpsta) { + struct wlan_objmgr_pdev *pdev; + struct wlan_objmgr_vdev *vdev = NULL; + struct wlan_objmgr_vdev *vdev_next = NULL; + struct wlan_objmgr_pdev_objmgr *objmgr; + qdf_list_t *vdev_list; + + pdev = wvdev->vdev->vdev_objmgr.wlan_pdev; + wlan_pdev_obj_lock(pdev); + + objmgr = &pdev->pdev_objmgr; + vdev_list = &objmgr->wlan_vdev_list; + /* Get first vdev */ + vdev = wlan_pdev_vdev_list_peek_head(vdev_list); + + while (vdev != NULL) { + if (qdf_is_macaddr_equal((struct qdf_mac_addr *)src_mac, + (struct qdf_mac_addr *)vdev->vdev_mlme.macaddr)) { + buf->mark |= WRAP_MARK_REFLECT; + if (dp_wrap_vdev_is_mat_set(vdev)) + IEEE80211_ADDR_COPY(src_mac, + vdev->vdev_mlme.mataddr); + wlan_pdev_obj_unlock(pdev); + return 0; + } + /* get next vdev */ + vdev_next = wlan_vdev_get_next_vdev_of_pdev(vdev_list, + vdev); + vdev = vdev_next; + } + + wlan_pdev_obj_unlock(pdev); + } + + if (parp) { + qwrap_debug("ARP %s\t" QDF_IPV4_ADDR_STR "\t" + " " QDF_MAC_ADDR_FMT " " + "\t" QDF_IPV4_ADDR_STR "\t" QDF_MAC_ADDR_FMT "\n", + arps[qdf_ntohs(parp->ar_op)], + QDF_IPV4_ADDR_ARRAY(parp->ar_sip), + QDF_MAC_ADDR_REF(parp->ar_sha), + QDF_IPV4_ADDR_ARRAY(parp->ar_tip), + QDF_MAC_ADDR_REF(parp->ar_tha)); + if (!ismcast) { + if (parp->ar_op == htons(ARPOP_REQUEST) || + parp->ar_op == htons(ARPOP_REPLY)) { + if (wvdev->mat_enabled) { + IEEE80211_ADDR_COPY(arp_dmac, + wvdev->wrap_dev_oma); + } + } + } else{ + struct wlan_objmgr_pdev *pdev; + struct wlan_objmgr_vdev *vdev = NULL, *n_vdev = NULL; + struct wlan_objmgr_vdev *vdev_next = NULL; + struct wlan_objmgr_pdev_objmgr *objmgr; + qdf_list_t *vdev_list; + + pdev = wvdev->vdev->vdev_objmgr.wlan_pdev; + + if (!qdf_is_macaddr_equal((struct qdf_mac_addr *)arp_dmac, + (struct qdf_mac_addr *)wvdev->wrap_dev_oma)) { + wlan_pdev_obj_lock(pdev); + + objmgr = &pdev->pdev_objmgr; + vdev_list = &objmgr->wlan_vdev_list; + /* Get first vdev */ + vdev = wlan_pdev_vdev_list_peek_head(vdev_list); + + while (vdev != NULL) { + if (dp_wrap_vdev_is_mat_set(vdev) && + qdf_is_macaddr_equal((struct qdf_mac_addr *)arp_dmac, + (struct qdf_mac_addr *)vdev->vdev_mlme.macaddr)) { + n_vdev = vdev; + break; + } + /* get next vdev */ + vdev_next = wlan_vdev_get_next_vdev_of_pdev(vdev_list, vdev); + vdev = vdev_next; + } + + wlan_pdev_obj_unlock(pdev); + if (n_vdev) { + wvdev = dp_wrap_get_vdev_handle(n_vdev); + } else { + return 0; + } + } + + if (wvdev->mat_enabled == 0) + return 0; + + IEEE80211_ADDR_COPY(arp_dmac, wvdev->wrap_dev_oma); + return 0; + } + } + + else if (ether_type == htons(ETHERTYPE_IPV6)) { + /* Add support later if needed */ + } + + if (!(ismcast) && !(wvdev->is_mpsta) && (wvdev->mat_enabled)) + IEEE80211_ADDR_COPY(des_mac, wvdev->wrap_dev_oma); + + return 0; +} +#endif +#endif