Path: blob/main/sys/contrib/dev/iwlwifi/mld/notif.c
48285 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2024-2025 Intel Corporation3*/45#include "mld.h"6#include "notif.h"7#include "scan.h"8#include "iface.h"9#include "mlo.h"10#include "iwl-trans.h"11#include "fw/file.h"12#include "fw/dbg.h"13#include "fw/api/cmdhdr.h"14#include "fw/api/mac-cfg.h"15#include "session-protect.h"16#include "fw/api/time-event.h"17#include "fw/api/tx.h"18#include "fw/api/rs.h"19#include "fw/api/offload.h"20#include "fw/api/stats.h"21#include "fw/api/rfi.h"22#include "fw/api/coex.h"2324#include "mcc.h"25#include "link.h"26#include "tx.h"27#include "rx.h"28#include "tlc.h"29#include "agg.h"30#include "mac80211.h"31#include "thermal.h"32#include "roc.h"33#include "stats.h"34#include "coex.h"35#include "time_sync.h"36#include "ftm-initiator.h"3738/* Please use this in an increasing order of the versions */39#define CMD_VER_ENTRY(_ver, _struct) \40{ .size = sizeof(struct _struct), .ver = _ver },41#define CMD_VERSIONS(name, ...) \42static const struct iwl_notif_struct_size \43iwl_notif_struct_sizes_##name[] = { __VA_ARGS__ };4445#define RX_HANDLER_NO_OBJECT(_grp, _cmd, _name, _context) \46{.cmd_id = WIDE_ID(_grp, _cmd), \47.context = _context, \48.fn = iwl_mld_handle_##_name, \49.sizes = iwl_notif_struct_sizes_##_name, \50.n_sizes = ARRAY_SIZE(iwl_notif_struct_sizes_##_name), \51},5253/* Use this for Rx handlers that do not need notification validation */54#define RX_HANDLER_NO_VAL(_grp, _cmd, _name, _context) \55{.cmd_id = WIDE_ID(_grp, _cmd), \56.context = _context, \57.fn = iwl_mld_handle_##_name, \58},5960#define RX_HANDLER_VAL_FN(_grp, _cmd, _name, _context) \61{ .cmd_id = WIDE_ID(_grp, _cmd), \62.context = _context, \63.fn = iwl_mld_handle_##_name, \64.val_fn = iwl_mld_validate_##_name, \65},6667#define DEFINE_SIMPLE_CANCELLATION(name, notif_struct, id_member) \68static bool iwl_mld_cancel_##name##_notif(struct iwl_mld *mld, \69struct iwl_rx_packet *pkt, \70u32 obj_id) \71{ \72const struct notif_struct *notif = (const void *)pkt->data; \73\74return obj_id == _Generic((notif)->id_member, \75__le32: le32_to_cpu((notif)->id_member), \76__le16: le16_to_cpu((notif)->id_member), \77u8: (notif)->id_member); \78}7980/* Currently only defined for the RX_HANDLER_SIZES options. Use this for81* notifications that belong to a specific object, and that should be82* canceled when the object is removed83*/84#define RX_HANDLER_OF_OBJ(_grp, _cmd, _name, _obj_type) \85{.cmd_id = WIDE_ID(_grp, _cmd), \86/* Only async handlers can be canceled */ \87.context = RX_HANDLER_ASYNC, \88.fn = iwl_mld_handle_##_name, \89.sizes = iwl_notif_struct_sizes_##_name, \90.n_sizes = ARRAY_SIZE(iwl_notif_struct_sizes_##_name), \91.obj_type = IWL_MLD_OBJECT_TYPE_##_obj_type, \92.cancel = iwl_mld_cancel_##_name, \93},9495#define RX_HANDLER_OF_LINK(_grp, _cmd, _name) \96RX_HANDLER_OF_OBJ(_grp, _cmd, _name, LINK) \9798#define RX_HANDLER_OF_VIF(_grp, _cmd, _name) \99RX_HANDLER_OF_OBJ(_grp, _cmd, _name, VIF) \100101#define RX_HANDLER_OF_STA(_grp, _cmd, _name) \102RX_HANDLER_OF_OBJ(_grp, _cmd, _name, STA) \103104#define RX_HANDLER_OF_ROC(_grp, _cmd, _name) \105RX_HANDLER_OF_OBJ(_grp, _cmd, _name, ROC)106107#define RX_HANDLER_OF_SCAN(_grp, _cmd, _name) \108RX_HANDLER_OF_OBJ(_grp, _cmd, _name, SCAN)109110#define RX_HANDLER_OF_FTM_REQ(_grp, _cmd, _name) \111RX_HANDLER_OF_OBJ(_grp, _cmd, _name, FTM_REQ)112113static void iwl_mld_handle_mfuart_notif(struct iwl_mld *mld,114struct iwl_rx_packet *pkt)115{116struct iwl_mfuart_load_notif *mfuart_notif = (void *)pkt->data;117118IWL_DEBUG_INFO(mld,119"MFUART: installed ver: 0x%08x, external ver: 0x%08x\n",120le32_to_cpu(mfuart_notif->installed_ver),121le32_to_cpu(mfuart_notif->external_ver));122IWL_DEBUG_INFO(mld,123"MFUART: status: 0x%08x, duration: 0x%08x image size: 0x%08x\n",124le32_to_cpu(mfuart_notif->status),125le32_to_cpu(mfuart_notif->duration),126le32_to_cpu(mfuart_notif->image_size));127}128129static void iwl_mld_mu_mimo_iface_iterator(void *_data, u8 *mac,130struct ieee80211_vif *vif)131{132struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;133unsigned int link_id = 0;134135if (WARN(hweight16(vif->active_links) > 1,136"no support for this notif while in EMLSR 0x%x\n",137vif->active_links))138return;139140if (ieee80211_vif_is_mld(vif)) {141link_id = __ffs(vif->active_links);142bss_conf = link_conf_dereference_check(vif, link_id);143}144145if (!WARN_ON(!bss_conf) && bss_conf->mu_mimo_owner) {146const struct iwl_mu_group_mgmt_notif *notif = _data;147148BUILD_BUG_ON(sizeof(notif->membership_status) !=149WLAN_MEMBERSHIP_LEN);150BUILD_BUG_ON(sizeof(notif->user_position) !=151WLAN_USER_POSITION_LEN);152153/* MU-MIMO Group Id action frame is little endian. We treat154* the data received from firmware as if it came from the155* action frame, so no conversion is needed.156*/157ieee80211_update_mu_groups(vif, link_id,158#if defined(__linux__)159(u8 *)¬if->membership_status,160(u8 *)¬if->user_position);161#elif defined(__FreeBSD__)162(const u8 *)¬if->membership_status,163(const u8 *)¬if->user_position);164#endif165}166}167168/* This handler is called in SYNC mode because it needs to be serialized with169* Rx as specified in ieee80211_update_mu_groups()'s documentation.170*/171static void iwl_mld_handle_mu_mimo_grp_notif(struct iwl_mld *mld,172struct iwl_rx_packet *pkt)173{174struct iwl_mu_group_mgmt_notif *notif = (void *)pkt->data;175176ieee80211_iterate_active_interfaces_atomic(mld->hw,177IEEE80211_IFACE_ITER_NORMAL,178iwl_mld_mu_mimo_iface_iterator,179notif);180}181182static void183iwl_mld_handle_channel_switch_start_notif(struct iwl_mld *mld,184struct iwl_rx_packet *pkt)185{186struct iwl_channel_switch_start_notif *notif = (void *)pkt->data;187u32 link_id = le32_to_cpu(notif->link_id);188struct ieee80211_bss_conf *link_conf =189iwl_mld_fw_id_to_link_conf(mld, link_id);190struct ieee80211_vif *vif;191192if (WARN_ON(!link_conf))193return;194195vif = link_conf->vif;196197IWL_DEBUG_INFO(mld,198"CSA Start Notification with vif type: %d, link_id: %d\n",199vif->type,200link_conf->link_id);201202switch (vif->type) {203case NL80211_IFTYPE_AP:204/* We don't support canceling a CSA as it was advertised205* by the AP itself206*/207if (!link_conf->csa_active)208return;209210ieee80211_csa_finish(vif, link_conf->link_id);211break;212case NL80211_IFTYPE_STATION:213if (!link_conf->csa_active) {214/* Either unexpected cs notif or mac80211 chose to215* ignore, for example in channel switch to same channel216*/217struct iwl_cancel_channel_switch_cmd cmd = {218.id = cpu_to_le32(link_id),219};220221if (iwl_mld_send_cmd_pdu(mld,222WIDE_ID(MAC_CONF_GROUP,223CANCEL_CHANNEL_SWITCH_CMD),224&cmd))225IWL_ERR(mld,226"Failed to cancel the channel switch\n");227return;228}229230ieee80211_chswitch_done(vif, true, link_conf->link_id);231break;232233default:234WARN(1, "CSA on invalid vif type: %d", vif->type);235}236}237238static void239iwl_mld_handle_channel_switch_error_notif(struct iwl_mld *mld,240struct iwl_rx_packet *pkt)241{242struct iwl_channel_switch_error_notif *notif = (void *)pkt->data;243struct ieee80211_bss_conf *link_conf;244struct ieee80211_vif *vif;245u32 link_id = le32_to_cpu(notif->link_id);246u32 csa_err_mask = le32_to_cpu(notif->csa_err_mask);247248link_conf = iwl_mld_fw_id_to_link_conf(mld, link_id);249if (WARN_ON(!link_conf))250return;251252vif = link_conf->vif;253254IWL_DEBUG_INFO(mld, "FW reports CSA error: id=%u, csa_err_mask=%u\n",255link_id, csa_err_mask);256257if (csa_err_mask & (CS_ERR_COUNT_ERROR |258CS_ERR_LONG_DELAY_AFTER_CS |259CS_ERR_TX_BLOCK_TIMER_EXPIRED))260ieee80211_channel_switch_disconnect(vif);261}262263static void iwl_mld_handle_beacon_notification(struct iwl_mld *mld,264struct iwl_rx_packet *pkt)265{266struct iwl_extended_beacon_notif *beacon = (void *)pkt->data;267268mld->ibss_manager = !!beacon->ibss_mgr_status;269}270271/**272* DOC: Notification versioning273*274* The firmware's notifications change from time to time. In order to275* differentiate between different versions of the same notification, the276* firmware advertises the version of each notification.277* Here are listed all the notifications that are supported. Several versions278* of the same notification can be allowed at the same time:279*280* CMD_VERSION(my_multi_version_notif,281* CMD_VER_ENTRY(1, iwl_my_multi_version_notif_ver1)282* CMD_VER_ENTRY(2, iwl_my_multi_version_notif_ver2)283*284* etc...285*286* The driver will enforce that the notification coming from the firmware287* has its version listed here and it'll also enforce that the firmware sent288* at least enough bytes to cover the structure listed in the CMD_VER_ENTRY.289*/290291CMD_VERSIONS(scan_complete_notif,292CMD_VER_ENTRY(1, iwl_umac_scan_complete))293CMD_VERSIONS(scan_iter_complete_notif,294CMD_VER_ENTRY(2, iwl_umac_scan_iter_complete_notif))295CMD_VERSIONS(channel_survey_notif,296CMD_VER_ENTRY(1, iwl_umac_scan_channel_survey_notif))297CMD_VERSIONS(mfuart_notif,298CMD_VER_ENTRY(2, iwl_mfuart_load_notif))299CMD_VERSIONS(update_mcc,300CMD_VER_ENTRY(1, iwl_mcc_chub_notif))301CMD_VERSIONS(session_prot_notif,302CMD_VER_ENTRY(3, iwl_session_prot_notif))303CMD_VERSIONS(missed_beacon_notif,304CMD_VER_ENTRY(5, iwl_missed_beacons_notif))305CMD_VERSIONS(tx_resp_notif,306CMD_VER_ENTRY(8, iwl_tx_resp)307CMD_VER_ENTRY(9, iwl_tx_resp))308CMD_VERSIONS(compressed_ba_notif,309CMD_VER_ENTRY(5, iwl_compressed_ba_notif)310CMD_VER_ENTRY(6, iwl_compressed_ba_notif)311CMD_VER_ENTRY(7, iwl_compressed_ba_notif))312CMD_VERSIONS(tlc_notif,313CMD_VER_ENTRY(3, iwl_tlc_update_notif)314CMD_VER_ENTRY(4, iwl_tlc_update_notif))315CMD_VERSIONS(mu_mimo_grp_notif,316CMD_VER_ENTRY(1, iwl_mu_group_mgmt_notif))317CMD_VERSIONS(channel_switch_start_notif,318CMD_VER_ENTRY(3, iwl_channel_switch_start_notif))319CMD_VERSIONS(channel_switch_error_notif,320CMD_VER_ENTRY(2, iwl_channel_switch_error_notif))321CMD_VERSIONS(ct_kill_notif,322CMD_VER_ENTRY(2, ct_kill_notif))323CMD_VERSIONS(temp_notif,324CMD_VER_ENTRY(2, iwl_dts_measurement_notif))325CMD_VERSIONS(roc_notif,326CMD_VER_ENTRY(1, iwl_roc_notif))327CMD_VERSIONS(probe_resp_data_notif,328CMD_VER_ENTRY(1, iwl_probe_resp_data_notif))329CMD_VERSIONS(datapath_monitor_notif,330CMD_VER_ENTRY(1, iwl_datapath_monitor_notif))331CMD_VERSIONS(stats_oper_notif,332CMD_VER_ENTRY(3, iwl_system_statistics_notif_oper))333CMD_VERSIONS(stats_oper_part1_notif,334CMD_VER_ENTRY(4, iwl_system_statistics_part1_notif_oper))335CMD_VERSIONS(bt_coex_notif,336CMD_VER_ENTRY(1, iwl_bt_coex_profile_notif))337CMD_VERSIONS(beacon_notification,338CMD_VER_ENTRY(6, iwl_extended_beacon_notif))339CMD_VERSIONS(emlsr_mode_notif,340CMD_VER_ENTRY(1, iwl_esr_mode_notif_v1)341CMD_VER_ENTRY(2, iwl_esr_mode_notif))342CMD_VERSIONS(emlsr_trans_fail_notif,343CMD_VER_ENTRY(1, iwl_esr_trans_fail_notif))344CMD_VERSIONS(uapsd_misbehaving_ap_notif,345CMD_VER_ENTRY(1, iwl_uapsd_misbehaving_ap_notif))346CMD_VERSIONS(time_msmt_notif,347CMD_VER_ENTRY(1, iwl_time_msmt_notify))348CMD_VERSIONS(time_sync_confirm_notif,349CMD_VER_ENTRY(1, iwl_time_msmt_cfm_notify))350CMD_VERSIONS(ftm_resp_notif, CMD_VER_ENTRY(10, iwl_tof_range_rsp_ntfy))351CMD_VERSIONS(beacon_filter_notif, CMD_VER_ENTRY(2, iwl_beacon_filter_notif))352353DEFINE_SIMPLE_CANCELLATION(session_prot, iwl_session_prot_notif, mac_link_id)354DEFINE_SIMPLE_CANCELLATION(tlc, iwl_tlc_update_notif, sta_id)355DEFINE_SIMPLE_CANCELLATION(channel_switch_start,356iwl_channel_switch_start_notif, link_id)357DEFINE_SIMPLE_CANCELLATION(channel_switch_error,358iwl_channel_switch_error_notif, link_id)359DEFINE_SIMPLE_CANCELLATION(datapath_monitor, iwl_datapath_monitor_notif,360link_id)361DEFINE_SIMPLE_CANCELLATION(roc, iwl_roc_notif, activity)362DEFINE_SIMPLE_CANCELLATION(scan_complete, iwl_umac_scan_complete, uid)363DEFINE_SIMPLE_CANCELLATION(probe_resp_data, iwl_probe_resp_data_notif,364mac_id)365DEFINE_SIMPLE_CANCELLATION(uapsd_misbehaving_ap, iwl_uapsd_misbehaving_ap_notif,366mac_id)367DEFINE_SIMPLE_CANCELLATION(ftm_resp, iwl_tof_range_rsp_ntfy, request_id)368DEFINE_SIMPLE_CANCELLATION(beacon_filter, iwl_beacon_filter_notif, link_id)369370/**371* DOC: Handlers for fw notifications372*373* Here are listed the notifications IDs (including the group ID), the handler374* of the notification and how it should be called:375*376* - RX_HANDLER_SYNC: will be called as part of the Rx path377* - RX_HANDLER_ASYNC: will be handled in a working with the wiphy_lock held378*379* This means that if the firmware sends two notifications A and B in that380* order and notification A is RX_HANDLER_ASYNC and notification is381* RX_HANDLER_SYNC, the handler of B will likely be called before the handler382* of A.383*384* This list should be in order of frequency for performance purposes.385* The handler can be one from two contexts, see &iwl_rx_handler_context386*387* A handler can declare that it relies on a specific object in which case it388* can be cancelled in case the object is deleted. In order to use this389* mechanism, a cancellation function is needed. The cancellation function must390* receive an object id (the index of that object in the firmware) and a391* notification payload. It'll return true if that specific notification should392* be cancelled upon the obliteration of the specific instance of the object.393*394* DEFINE_SIMPLE_CANCELLATION allows to easily create a cancellation function395* that wills simply return true if a given object id matches the object id in396* the firmware notification.397*/398399VISIBLE_IF_IWLWIFI_KUNIT400const struct iwl_rx_handler iwl_mld_rx_handlers[] = {401RX_HANDLER_NO_OBJECT(LEGACY_GROUP, TX_CMD, tx_resp_notif,402RX_HANDLER_SYNC)403RX_HANDLER_NO_OBJECT(LEGACY_GROUP, BA_NOTIF, compressed_ba_notif,404RX_HANDLER_SYNC)405RX_HANDLER_OF_SCAN(LEGACY_GROUP, SCAN_COMPLETE_UMAC,406scan_complete_notif)407RX_HANDLER_NO_OBJECT(LEGACY_GROUP, SCAN_ITERATION_COMPLETE_UMAC,408scan_iter_complete_notif,409RX_HANDLER_SYNC)410RX_HANDLER_NO_VAL(LEGACY_GROUP, MATCH_FOUND_NOTIFICATION,411match_found_notif, RX_HANDLER_SYNC)412413RX_HANDLER_NO_OBJECT(SCAN_GROUP, CHANNEL_SURVEY_NOTIF,414channel_survey_notif,415RX_HANDLER_ASYNC)416417RX_HANDLER_NO_OBJECT(STATISTICS_GROUP, STATISTICS_OPER_NOTIF,418stats_oper_notif, RX_HANDLER_ASYNC)419RX_HANDLER_NO_OBJECT(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF,420stats_oper_part1_notif, RX_HANDLER_ASYNC)421422RX_HANDLER_NO_OBJECT(LEGACY_GROUP, MFUART_LOAD_NOTIFICATION,423mfuart_notif, RX_HANDLER_SYNC)424425RX_HANDLER_NO_OBJECT(PHY_OPS_GROUP, DTS_MEASUREMENT_NOTIF_WIDE,426temp_notif, RX_HANDLER_ASYNC)427RX_HANDLER_OF_LINK(MAC_CONF_GROUP, SESSION_PROTECTION_NOTIF,428session_prot_notif)429RX_HANDLER_OF_LINK(MAC_CONF_GROUP, MISSED_BEACONS_NOTIF,430missed_beacon_notif)431RX_HANDLER_OF_STA(DATA_PATH_GROUP, TLC_MNG_UPDATE_NOTIF, tlc_notif)432RX_HANDLER_OF_LINK(MAC_CONF_GROUP, CHANNEL_SWITCH_START_NOTIF,433channel_switch_start_notif)434RX_HANDLER_OF_LINK(MAC_CONF_GROUP, CHANNEL_SWITCH_ERROR_NOTIF,435channel_switch_error_notif)436RX_HANDLER_OF_ROC(MAC_CONF_GROUP, ROC_NOTIF, roc_notif)437RX_HANDLER_NO_OBJECT(DATA_PATH_GROUP, MU_GROUP_MGMT_NOTIF,438mu_mimo_grp_notif, RX_HANDLER_SYNC)439RX_HANDLER_OF_VIF(MAC_CONF_GROUP, PROBE_RESPONSE_DATA_NOTIF,440probe_resp_data_notif)441RX_HANDLER_NO_OBJECT(PHY_OPS_GROUP, CT_KILL_NOTIFICATION,442ct_kill_notif, RX_HANDLER_ASYNC)443RX_HANDLER_OF_LINK(DATA_PATH_GROUP, MONITOR_NOTIF,444datapath_monitor_notif)445RX_HANDLER_NO_OBJECT(LEGACY_GROUP, MCC_CHUB_UPDATE_CMD, update_mcc,446RX_HANDLER_ASYNC)447RX_HANDLER_NO_OBJECT(BT_COEX_GROUP, PROFILE_NOTIF,448bt_coex_notif, RX_HANDLER_ASYNC)449RX_HANDLER_NO_OBJECT(LEGACY_GROUP, BEACON_NOTIFICATION,450beacon_notification, RX_HANDLER_ASYNC)451RX_HANDLER_NO_OBJECT(DATA_PATH_GROUP, ESR_MODE_NOTIF,452emlsr_mode_notif, RX_HANDLER_ASYNC)453RX_HANDLER_NO_OBJECT(MAC_CONF_GROUP, EMLSR_TRANS_FAIL_NOTIF,454emlsr_trans_fail_notif, RX_HANDLER_ASYNC)455RX_HANDLER_OF_VIF(LEGACY_GROUP, PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION,456uapsd_misbehaving_ap_notif)457RX_HANDLER_NO_OBJECT(LEGACY_GROUP,458WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION,459time_msmt_notif, RX_HANDLER_SYNC)460RX_HANDLER_NO_OBJECT(LEGACY_GROUP,461WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NOTIFICATION,462time_sync_confirm_notif, RX_HANDLER_ASYNC)463RX_HANDLER_OF_LINK(DATA_PATH_GROUP, BEACON_FILTER_IN_NOTIF,464beacon_filter_notif)465RX_HANDLER_OF_FTM_REQ(LOCATION_GROUP, TOF_RANGE_RESPONSE_NOTIF,466ftm_resp_notif)467};468EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_rx_handlers);469470#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)471const unsigned int iwl_mld_rx_handlers_num = ARRAY_SIZE(iwl_mld_rx_handlers);472EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_rx_handlers_num);473#endif474475static bool476iwl_mld_notif_is_valid(struct iwl_mld *mld, struct iwl_rx_packet *pkt,477const struct iwl_rx_handler *handler)478{479unsigned int size = iwl_rx_packet_payload_len(pkt);480size_t notif_ver;481482/* If n_sizes == 0, it indicates that a validation function may be used483* or that no validation is required.484*/485if (!handler->n_sizes) {486if (handler->val_fn)487return handler->val_fn(mld, pkt);488return true;489}490491notif_ver = iwl_fw_lookup_notif_ver(mld->fw,492iwl_cmd_groupid(handler->cmd_id),493iwl_cmd_opcode(handler->cmd_id),494IWL_FW_CMD_VER_UNKNOWN);495496for (int i = 0; i < handler->n_sizes; i++) {497if (handler->sizes[i].ver != notif_ver)498continue;499500if (IWL_FW_CHECK(mld, size < handler->sizes[i].size,501"unexpected notification 0x%04x size %d, need %d\n",502handler->cmd_id, size, handler->sizes[i].size))503return false;504return true;505}506507IWL_FW_CHECK_FAILED(mld,508"notif 0x%04x ver %zu missing expected size, use version %u size\n",509handler->cmd_id, notif_ver,510handler->sizes[handler->n_sizes - 1].ver);511512return size < handler->sizes[handler->n_sizes - 1].size;513}514515struct iwl_async_handler_entry {516struct list_head list;517struct iwl_rx_cmd_buffer rxb;518const struct iwl_rx_handler *rx_h;519};520521static void522iwl_mld_log_async_handler_op(struct iwl_mld *mld, const char *op,523struct iwl_rx_cmd_buffer *rxb)524{525struct iwl_rx_packet *pkt = rxb_addr(rxb);526527IWL_DEBUG_HC(mld,528"%s async handler for notif %s (%.2x.%2x, seq 0x%x)\n",529op, iwl_get_cmd_string(mld->trans,530WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)),531pkt->hdr.group_id, pkt->hdr.cmd,532le16_to_cpu(pkt->hdr.sequence));533}534535static void iwl_mld_rx_notif(struct iwl_mld *mld,536struct iwl_rx_cmd_buffer *rxb,537struct iwl_rx_packet *pkt)538{539for (int i = 0; i < ARRAY_SIZE(iwl_mld_rx_handlers); i++) {540const struct iwl_rx_handler *rx_h = &iwl_mld_rx_handlers[i];541struct iwl_async_handler_entry *entry;542543if (rx_h->cmd_id != WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd))544continue;545546if (!iwl_mld_notif_is_valid(mld, pkt, rx_h))547return;548549if (rx_h->context == RX_HANDLER_SYNC) {550rx_h->fn(mld, pkt);551break;552}553554entry = kzalloc(sizeof(*entry), GFP_ATOMIC);555/* we can't do much... */556if (!entry)557return;558559/* Set the async handler entry */560entry->rxb._page = rxb_steal_page(rxb);561entry->rxb._offset = rxb->_offset;562entry->rxb._rx_page_order = rxb->_rx_page_order;563564entry->rx_h = rx_h;565566/* Add it to the list and queue the work */567spin_lock(&mld->async_handlers_lock);568list_add_tail(&entry->list, &mld->async_handlers_list);569spin_unlock(&mld->async_handlers_lock);570571wiphy_work_queue(mld->hw->wiphy,572&mld->async_handlers_wk);573574iwl_mld_log_async_handler_op(mld, "Queued", rxb);575break;576}577578iwl_notification_wait_notify(&mld->notif_wait, pkt);579}580581void iwl_mld_rx(struct iwl_op_mode *op_mode, struct napi_struct *napi,582struct iwl_rx_cmd_buffer *rxb)583{584struct iwl_rx_packet *pkt = rxb_addr(rxb);585struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);586u16 cmd_id = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd);587588if (likely(cmd_id == WIDE_ID(LEGACY_GROUP, REPLY_RX_MPDU_CMD)))589iwl_mld_rx_mpdu(mld, napi, rxb, 0);590else if (cmd_id == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE))591iwl_mld_handle_frame_release_notif(mld, napi, pkt, 0);592else if (cmd_id == WIDE_ID(LEGACY_GROUP, BAR_FRAME_RELEASE))593iwl_mld_handle_bar_frame_release_notif(mld, napi, pkt, 0);594else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP,595RX_QUEUES_NOTIFICATION)))596iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, 0);597else if (cmd_id == WIDE_ID(DATA_PATH_GROUP, RX_NO_DATA_NOTIF))598iwl_mld_rx_monitor_no_data(mld, napi, pkt, 0);599else600iwl_mld_rx_notif(mld, rxb, pkt);601}602603void iwl_mld_rx_rss(struct iwl_op_mode *op_mode, struct napi_struct *napi,604struct iwl_rx_cmd_buffer *rxb, unsigned int queue)605{606struct iwl_rx_packet *pkt = rxb_addr(rxb);607struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);608u16 cmd_id = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd);609610if (unlikely(queue >= mld->trans->info.num_rxqs))611return;612613if (likely(cmd_id == WIDE_ID(LEGACY_GROUP, REPLY_RX_MPDU_CMD)))614iwl_mld_rx_mpdu(mld, napi, rxb, queue);615else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP,616RX_QUEUES_NOTIFICATION)))617iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, queue);618else if (unlikely(cmd_id == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE)))619iwl_mld_handle_frame_release_notif(mld, napi, pkt, queue);620}621622void iwl_mld_delete_handlers(struct iwl_mld *mld, const u16 *cmds, int n_cmds)623{624struct iwl_async_handler_entry *entry, *tmp;625626spin_lock_bh(&mld->async_handlers_lock);627list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) {628bool match = false;629630for (int i = 0; i < n_cmds; i++) {631if (entry->rx_h->cmd_id == cmds[i]) {632match = true;633break;634}635}636637if (!match)638continue;639640iwl_mld_log_async_handler_op(mld, "Delete", &entry->rxb);641iwl_free_rxb(&entry->rxb);642list_del(&entry->list);643kfree(entry);644}645spin_unlock_bh(&mld->async_handlers_lock);646}647648void iwl_mld_async_handlers_wk(struct wiphy *wiphy, struct wiphy_work *wk)649{650struct iwl_mld *mld =651container_of(wk, struct iwl_mld, async_handlers_wk);652struct iwl_async_handler_entry *entry, *tmp;653LIST_HEAD(local_list);654655/* Sync with Rx path with a lock. Remove all the entries from this656* list, add them to a local one (lock free), and then handle them.657*/658spin_lock_bh(&mld->async_handlers_lock);659list_splice_init(&mld->async_handlers_list, &local_list);660spin_unlock_bh(&mld->async_handlers_lock);661662list_for_each_entry_safe(entry, tmp, &local_list, list) {663iwl_mld_log_async_handler_op(mld, "Handle", &entry->rxb);664entry->rx_h->fn(mld, rxb_addr(&entry->rxb));665iwl_free_rxb(&entry->rxb);666list_del(&entry->list);667kfree(entry);668}669}670671void iwl_mld_cancel_async_notifications(struct iwl_mld *mld)672{673struct iwl_async_handler_entry *entry, *tmp;674675lockdep_assert_wiphy(mld->wiphy);676677wiphy_work_cancel(mld->wiphy, &mld->async_handlers_wk);678679spin_lock_bh(&mld->async_handlers_lock);680list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) {681iwl_mld_log_async_handler_op(mld, "Purged", &entry->rxb);682iwl_free_rxb(&entry->rxb);683list_del(&entry->list);684kfree(entry);685}686spin_unlock_bh(&mld->async_handlers_lock);687}688689void iwl_mld_cancel_notifications_of_object(struct iwl_mld *mld,690enum iwl_mld_object_type obj_type,691u32 obj_id)692{693struct iwl_async_handler_entry *entry, *tmp;694LIST_HEAD(cancel_list);695696lockdep_assert_wiphy(mld->wiphy);697698if (WARN_ON(obj_type == IWL_MLD_OBJECT_TYPE_NONE))699return;700701/* Sync with RX path and remove matching entries from the async list */702spin_lock_bh(&mld->async_handlers_lock);703list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) {704const struct iwl_rx_handler *rx_h = entry->rx_h;705706if (rx_h->obj_type != obj_type || WARN_ON(!rx_h->cancel))707continue;708709if (rx_h->cancel(mld, rxb_addr(&entry->rxb), obj_id)) {710iwl_mld_log_async_handler_op(mld, "Cancel", &entry->rxb);711list_del(&entry->list);712list_add_tail(&entry->list, &cancel_list);713}714}715716spin_unlock_bh(&mld->async_handlers_lock);717718/* Free the matching entries outside of the spinlock */719list_for_each_entry_safe(entry, tmp, &cancel_list, list) {720iwl_free_rxb(&entry->rxb);721list_del(&entry->list);722kfree(entry);723}724}725726727