162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2021-2023 Intel Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/etherdevice.h> 762306a36Sopenharmony_ci#include <linux/netdevice.h> 862306a36Sopenharmony_ci#include <linux/ieee80211.h> 962306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/moduleparam.h> 1262306a36Sopenharmony_ci#include <linux/mei_cl_bus.h> 1362306a36Sopenharmony_ci#include <linux/rcupdate.h> 1462306a36Sopenharmony_ci#include <linux/debugfs.h> 1562306a36Sopenharmony_ci#include <linux/skbuff.h> 1662306a36Sopenharmony_ci#include <linux/wait.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/mm.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <net/cfg80211.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "internal.h" 2362306a36Sopenharmony_ci#include "iwl-mei.h" 2462306a36Sopenharmony_ci#include "trace.h" 2562306a36Sopenharmony_ci#include "trace-data.h" 2662306a36Sopenharmony_ci#include "sap.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ciMODULE_DESCRIPTION("The Intel(R) wireless / CSME firmware interface"); 2962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define MEI_WLAN_UUID UUID_LE(0x13280904, 0x7792, 0x4fcb, \ 3262306a36Sopenharmony_ci 0xa1, 0xaa, 0x5e, 0x70, 0xcb, 0xb1, 0xe8, 0x65) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* After CSME takes ownership, it won't release it for 60 seconds to avoid 3562306a36Sopenharmony_ci * frequent ownership transitions. 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_ci#define MEI_OWNERSHIP_RETAKE_TIMEOUT_MS msecs_to_jiffies(60000) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * Since iwlwifi calls iwlmei without any context, hold a pointer to the 4162306a36Sopenharmony_ci * mei_cl_device structure here. 4262306a36Sopenharmony_ci * Define a mutex that will synchronize all the flows between iwlwifi and 4362306a36Sopenharmony_ci * iwlmei. 4462306a36Sopenharmony_ci * Note that iwlmei can't have several instances, so it ok to have static 4562306a36Sopenharmony_ci * variables here. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_cistatic struct mei_cl_device *iwl_mei_global_cldev; 4862306a36Sopenharmony_cistatic DEFINE_MUTEX(iwl_mei_mutex); 4962306a36Sopenharmony_cistatic unsigned long iwl_mei_status; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cienum iwl_mei_status_bits { 5262306a36Sopenharmony_ci IWL_MEI_STATUS_SAP_CONNECTED, 5362306a36Sopenharmony_ci}; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cibool iwl_mei_is_connected(void) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci return test_bit(IWL_MEI_STATUS_SAP_CONNECTED, &iwl_mei_status); 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_is_connected); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#define SAP_VERSION 3 6262306a36Sopenharmony_ci#define SAP_CONTROL_BLOCK_ID 0x21504153 /* SAP! in ASCII */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistruct iwl_sap_q_ctrl_blk { 6562306a36Sopenharmony_ci __le32 wr_ptr; 6662306a36Sopenharmony_ci __le32 rd_ptr; 6762306a36Sopenharmony_ci __le32 size; 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cienum iwl_sap_q_idx { 7162306a36Sopenharmony_ci SAP_QUEUE_IDX_NOTIF = 0, 7262306a36Sopenharmony_ci SAP_QUEUE_IDX_DATA, 7362306a36Sopenharmony_ci SAP_QUEUE_IDX_MAX, 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistruct iwl_sap_dir { 7762306a36Sopenharmony_ci __le32 reserved; 7862306a36Sopenharmony_ci struct iwl_sap_q_ctrl_blk q_ctrl_blk[SAP_QUEUE_IDX_MAX]; 7962306a36Sopenharmony_ci}; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cienum iwl_sap_dir_idx { 8262306a36Sopenharmony_ci SAP_DIRECTION_HOST_TO_ME = 0, 8362306a36Sopenharmony_ci SAP_DIRECTION_ME_TO_HOST, 8462306a36Sopenharmony_ci SAP_DIRECTION_MAX, 8562306a36Sopenharmony_ci}; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistruct iwl_sap_shared_mem_ctrl_blk { 8862306a36Sopenharmony_ci __le32 sap_id; 8962306a36Sopenharmony_ci __le32 size; 9062306a36Sopenharmony_ci struct iwl_sap_dir dir[SAP_DIRECTION_MAX]; 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* 9462306a36Sopenharmony_ci * The shared area has the following layout: 9562306a36Sopenharmony_ci * 9662306a36Sopenharmony_ci * +-----------------------------------+ 9762306a36Sopenharmony_ci * |struct iwl_sap_shared_mem_ctrl_blk | 9862306a36Sopenharmony_ci * +-----------------------------------+ 9962306a36Sopenharmony_ci * |Host -> ME data queue | 10062306a36Sopenharmony_ci * +-----------------------------------+ 10162306a36Sopenharmony_ci * |Host -> ME notif queue | 10262306a36Sopenharmony_ci * +-----------------------------------+ 10362306a36Sopenharmony_ci * |ME -> Host data queue | 10462306a36Sopenharmony_ci * +-----------------------------------+ 10562306a36Sopenharmony_ci * |ME -> host notif queue | 10662306a36Sopenharmony_ci * +-----------------------------------+ 10762306a36Sopenharmony_ci * |SAP control block id (SAP!) | 10862306a36Sopenharmony_ci * +-----------------------------------+ 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#define SAP_H2M_DATA_Q_SZ 48256 11262306a36Sopenharmony_ci#define SAP_M2H_DATA_Q_SZ 24128 11362306a36Sopenharmony_ci#define SAP_H2M_NOTIF_Q_SZ 2240 11462306a36Sopenharmony_ci#define SAP_M2H_NOTIF_Q_SZ 62720 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define _IWL_MEI_SAP_SHARED_MEM_SZ \ 11762306a36Sopenharmony_ci (sizeof(struct iwl_sap_shared_mem_ctrl_blk) + \ 11862306a36Sopenharmony_ci SAP_H2M_DATA_Q_SZ + SAP_H2M_NOTIF_Q_SZ + \ 11962306a36Sopenharmony_ci SAP_M2H_DATA_Q_SZ + SAP_M2H_NOTIF_Q_SZ + 4) 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci#define IWL_MEI_SAP_SHARED_MEM_SZ \ 12262306a36Sopenharmony_ci (roundup(_IWL_MEI_SAP_SHARED_MEM_SZ, PAGE_SIZE)) 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistruct iwl_mei_shared_mem_ptrs { 12562306a36Sopenharmony_ci struct iwl_sap_shared_mem_ctrl_blk *ctrl; 12662306a36Sopenharmony_ci void *q_head[SAP_DIRECTION_MAX][SAP_QUEUE_IDX_MAX]; 12762306a36Sopenharmony_ci size_t q_size[SAP_DIRECTION_MAX][SAP_QUEUE_IDX_MAX]; 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistruct iwl_mei_filters { 13162306a36Sopenharmony_ci struct rcu_head rcu_head; 13262306a36Sopenharmony_ci struct iwl_sap_oob_filters filters; 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/** 13662306a36Sopenharmony_ci * struct iwl_mei - holds the private date for iwl_mei 13762306a36Sopenharmony_ci * 13862306a36Sopenharmony_ci * @get_nvm_wq: the wait queue for the get_nvm flow 13962306a36Sopenharmony_ci * @send_csa_msg_wk: used to defer the transmission of the CHECK_SHARED_AREA 14062306a36Sopenharmony_ci * message. Used so that we can send CHECK_SHARED_AREA from atomic 14162306a36Sopenharmony_ci * contexts. 14262306a36Sopenharmony_ci * @get_ownership_wq: the wait queue for the get_ownership_flow 14362306a36Sopenharmony_ci * @shared_mem: the memory that is shared between CSME and the host 14462306a36Sopenharmony_ci * @cldev: the pointer to the MEI client device 14562306a36Sopenharmony_ci * @nvm: the data returned by the CSME for the NVM 14662306a36Sopenharmony_ci * @filters: the filters sent by CSME 14762306a36Sopenharmony_ci * @got_ownership: true if we own the device 14862306a36Sopenharmony_ci * @amt_enabled: true if CSME has wireless enabled 14962306a36Sopenharmony_ci * @csa_throttled: when true, we can't send CHECK_SHARED_AREA over the MEI 15062306a36Sopenharmony_ci * bus, but rather need to wait until send_csa_msg_wk runs 15162306a36Sopenharmony_ci * @csme_taking_ownership: true when CSME is taking ownership. Used to remember 15262306a36Sopenharmony_ci * to send CSME_OWNERSHIP_CONFIRMED when the driver completes its down 15362306a36Sopenharmony_ci * flow. 15462306a36Sopenharmony_ci * @link_prot_state: true when we are in link protection PASSIVE 15562306a36Sopenharmony_ci * @device_down: true if the device is down. Used to remember to send 15662306a36Sopenharmony_ci * CSME_OWNERSHIP_CONFIRMED when the driver is already down. 15762306a36Sopenharmony_ci * @csa_throttle_end_wk: used when &csa_throttled is true 15862306a36Sopenharmony_ci * @pldr_wq: the wait queue for PLDR flow 15962306a36Sopenharmony_ci * @pldr_active: PLDR flow is in progress 16062306a36Sopenharmony_ci * @data_q_lock: protects the access to the data queues which are 16162306a36Sopenharmony_ci * accessed without the mutex. 16262306a36Sopenharmony_ci * @netdev_work: used to defer registering and unregistering of the netdev to 16362306a36Sopenharmony_ci * avoid taking the rtnl lock in the SAP messages handlers. 16462306a36Sopenharmony_ci * @ownership_dwork: used to re-ask for NIC ownership after ownership was taken 16562306a36Sopenharmony_ci * by CSME or when a previous ownership request failed. 16662306a36Sopenharmony_ci * @sap_seq_no: the sequence number for the SAP messages 16762306a36Sopenharmony_ci * @seq_no: the sequence number for the SAP messages 16862306a36Sopenharmony_ci * @dbgfs_dir: the debugfs dir entry 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_cistruct iwl_mei { 17162306a36Sopenharmony_ci wait_queue_head_t get_nvm_wq; 17262306a36Sopenharmony_ci struct work_struct send_csa_msg_wk; 17362306a36Sopenharmony_ci wait_queue_head_t get_ownership_wq; 17462306a36Sopenharmony_ci struct iwl_mei_shared_mem_ptrs shared_mem; 17562306a36Sopenharmony_ci struct mei_cl_device *cldev; 17662306a36Sopenharmony_ci struct iwl_mei_nvm *nvm; 17762306a36Sopenharmony_ci struct iwl_mei_filters __rcu *filters; 17862306a36Sopenharmony_ci bool got_ownership; 17962306a36Sopenharmony_ci bool amt_enabled; 18062306a36Sopenharmony_ci bool csa_throttled; 18162306a36Sopenharmony_ci bool csme_taking_ownership; 18262306a36Sopenharmony_ci bool link_prot_state; 18362306a36Sopenharmony_ci bool device_down; 18462306a36Sopenharmony_ci struct delayed_work csa_throttle_end_wk; 18562306a36Sopenharmony_ci wait_queue_head_t pldr_wq; 18662306a36Sopenharmony_ci bool pldr_active; 18762306a36Sopenharmony_ci spinlock_t data_q_lock; 18862306a36Sopenharmony_ci struct work_struct netdev_work; 18962306a36Sopenharmony_ci struct delayed_work ownership_dwork; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci atomic_t sap_seq_no; 19262306a36Sopenharmony_ci atomic_t seq_no; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci struct dentry *dbgfs_dir; 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/** 19862306a36Sopenharmony_ci * struct iwl_mei_cache - cache for the parameters from iwlwifi 19962306a36Sopenharmony_ci * @ops: Callbacks to iwlwifi. 20062306a36Sopenharmony_ci * @netdev: The netdev that will be used to transmit / receive packets. 20162306a36Sopenharmony_ci * @conn_info: The connection info message triggered by iwlwifi's association. 20262306a36Sopenharmony_ci * @power_limit: pointer to an array of 10 elements (le16) represents the power 20362306a36Sopenharmony_ci * restrictions per chain. 20462306a36Sopenharmony_ci * @rf_kill: rf kill state. 20562306a36Sopenharmony_ci * @mcc: MCC info 20662306a36Sopenharmony_ci * @mac_address: interface MAC address. 20762306a36Sopenharmony_ci * @nvm_address: NVM MAC address. 20862306a36Sopenharmony_ci * @priv: A pointer to iwlwifi. 20962306a36Sopenharmony_ci * 21062306a36Sopenharmony_ci * This used to cache the configurations coming from iwlwifi's way. The data 21162306a36Sopenharmony_ci * is cached here so that we can buffer the configuration even if we don't have 21262306a36Sopenharmony_ci * a bind from the mei bus and hence, on iwl_mei structure. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_cistruct iwl_mei_cache { 21562306a36Sopenharmony_ci const struct iwl_mei_ops *ops; 21662306a36Sopenharmony_ci struct net_device __rcu *netdev; 21762306a36Sopenharmony_ci const struct iwl_sap_notif_connection_info *conn_info; 21862306a36Sopenharmony_ci const __le16 *power_limit; 21962306a36Sopenharmony_ci u32 rf_kill; 22062306a36Sopenharmony_ci u16 mcc; 22162306a36Sopenharmony_ci u8 mac_address[6]; 22262306a36Sopenharmony_ci u8 nvm_address[6]; 22362306a36Sopenharmony_ci void *priv; 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic struct iwl_mei_cache iwl_mei_cache = { 22762306a36Sopenharmony_ci .rf_kill = SAP_HW_RFKILL_DEASSERTED | SAP_SW_RFKILL_DEASSERTED 22862306a36Sopenharmony_ci}; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic void iwl_mei_free_shared_mem(struct mei_cl_device *cldev) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct iwl_mei *mei = mei_cldev_get_drvdata(cldev); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (mei_cldev_dma_unmap(cldev)) 23562306a36Sopenharmony_ci dev_err(&cldev->dev, "Couldn't unmap the shared mem properly\n"); 23662306a36Sopenharmony_ci memset(&mei->shared_mem, 0, sizeof(mei->shared_mem)); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci#define HBM_DMA_BUF_ID_WLAN 1 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int iwl_mei_alloc_shared_mem(struct mei_cl_device *cldev) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct iwl_mei *mei = mei_cldev_get_drvdata(cldev); 24462306a36Sopenharmony_ci struct iwl_mei_shared_mem_ptrs *mem = &mei->shared_mem; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci mem->ctrl = mei_cldev_dma_map(cldev, HBM_DMA_BUF_ID_WLAN, 24762306a36Sopenharmony_ci IWL_MEI_SAP_SHARED_MEM_SZ); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (IS_ERR(mem->ctrl)) { 25062306a36Sopenharmony_ci int ret = PTR_ERR(mem->ctrl); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci mem->ctrl = NULL; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return ret; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci memset(mem->ctrl, 0, IWL_MEI_SAP_SHARED_MEM_SZ); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci return 0; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic void iwl_mei_init_shared_mem(struct iwl_mei *mei) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci struct iwl_mei_shared_mem_ptrs *mem = &mei->shared_mem; 26562306a36Sopenharmony_ci struct iwl_sap_dir *h2m; 26662306a36Sopenharmony_ci struct iwl_sap_dir *m2h; 26762306a36Sopenharmony_ci int dir, queue; 26862306a36Sopenharmony_ci u8 *q_head; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci mem->ctrl->sap_id = cpu_to_le32(SAP_CONTROL_BLOCK_ID); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci mem->ctrl->size = cpu_to_le32(sizeof(*mem->ctrl)); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci h2m = &mem->ctrl->dir[SAP_DIRECTION_HOST_TO_ME]; 27562306a36Sopenharmony_ci m2h = &mem->ctrl->dir[SAP_DIRECTION_ME_TO_HOST]; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci h2m->q_ctrl_blk[SAP_QUEUE_IDX_DATA].size = 27862306a36Sopenharmony_ci cpu_to_le32(SAP_H2M_DATA_Q_SZ); 27962306a36Sopenharmony_ci h2m->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF].size = 28062306a36Sopenharmony_ci cpu_to_le32(SAP_H2M_NOTIF_Q_SZ); 28162306a36Sopenharmony_ci m2h->q_ctrl_blk[SAP_QUEUE_IDX_DATA].size = 28262306a36Sopenharmony_ci cpu_to_le32(SAP_M2H_DATA_Q_SZ); 28362306a36Sopenharmony_ci m2h->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF].size = 28462306a36Sopenharmony_ci cpu_to_le32(SAP_M2H_NOTIF_Q_SZ); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* q_head points to the start of the first queue */ 28762306a36Sopenharmony_ci q_head = (void *)(mem->ctrl + 1); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* Initialize the queue heads */ 29062306a36Sopenharmony_ci for (dir = 0; dir < SAP_DIRECTION_MAX; dir++) { 29162306a36Sopenharmony_ci for (queue = 0; queue < SAP_QUEUE_IDX_MAX; queue++) { 29262306a36Sopenharmony_ci mem->q_head[dir][queue] = q_head; 29362306a36Sopenharmony_ci q_head += 29462306a36Sopenharmony_ci le32_to_cpu(mem->ctrl->dir[dir].q_ctrl_blk[queue].size); 29562306a36Sopenharmony_ci mem->q_size[dir][queue] = 29662306a36Sopenharmony_ci le32_to_cpu(mem->ctrl->dir[dir].q_ctrl_blk[queue].size); 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci *(__le32 *)q_head = cpu_to_le32(SAP_CONTROL_BLOCK_ID); 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic ssize_t iwl_mei_write_cyclic_buf(struct mei_cl_device *cldev, 30462306a36Sopenharmony_ci struct iwl_sap_q_ctrl_blk *notif_q, 30562306a36Sopenharmony_ci u8 *q_head, 30662306a36Sopenharmony_ci const struct iwl_sap_hdr *hdr, 30762306a36Sopenharmony_ci u32 q_sz) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci u32 rd = le32_to_cpu(READ_ONCE(notif_q->rd_ptr)); 31062306a36Sopenharmony_ci u32 wr = le32_to_cpu(READ_ONCE(notif_q->wr_ptr)); 31162306a36Sopenharmony_ci size_t room_in_buf; 31262306a36Sopenharmony_ci size_t tx_sz = sizeof(*hdr) + le16_to_cpu(hdr->len); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (rd > q_sz || wr > q_sz) { 31562306a36Sopenharmony_ci dev_err(&cldev->dev, 31662306a36Sopenharmony_ci "Pointers are past the end of the buffer\n"); 31762306a36Sopenharmony_ci return -EINVAL; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci room_in_buf = wr >= rd ? q_sz - wr + rd : rd - wr; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* we don't have enough room for the data to write */ 32362306a36Sopenharmony_ci if (room_in_buf < tx_sz) { 32462306a36Sopenharmony_ci dev_err(&cldev->dev, 32562306a36Sopenharmony_ci "Not enough room in the buffer\n"); 32662306a36Sopenharmony_ci return -ENOSPC; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (wr + tx_sz <= q_sz) { 33062306a36Sopenharmony_ci memcpy(q_head + wr, hdr, tx_sz); 33162306a36Sopenharmony_ci } else { 33262306a36Sopenharmony_ci memcpy(q_head + wr, hdr, q_sz - wr); 33362306a36Sopenharmony_ci memcpy(q_head, (const u8 *)hdr + q_sz - wr, tx_sz - (q_sz - wr)); 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci WRITE_ONCE(notif_q->wr_ptr, cpu_to_le32((wr + tx_sz) % q_sz)); 33762306a36Sopenharmony_ci return 0; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic bool iwl_mei_host_to_me_data_pending(const struct iwl_mei *mei) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct iwl_sap_q_ctrl_blk *notif_q; 34362306a36Sopenharmony_ci struct iwl_sap_dir *dir; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci dir = &mei->shared_mem.ctrl->dir[SAP_DIRECTION_HOST_TO_ME]; 34662306a36Sopenharmony_ci notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_DATA]; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (READ_ONCE(notif_q->wr_ptr) != READ_ONCE(notif_q->rd_ptr)) 34962306a36Sopenharmony_ci return true; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF]; 35262306a36Sopenharmony_ci return READ_ONCE(notif_q->wr_ptr) != READ_ONCE(notif_q->rd_ptr); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic int iwl_mei_send_check_shared_area(struct mei_cl_device *cldev) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct iwl_mei *mei = mei_cldev_get_drvdata(cldev); 35862306a36Sopenharmony_ci struct iwl_sap_me_msg_start msg = { 35962306a36Sopenharmony_ci .hdr.type = cpu_to_le32(SAP_ME_MSG_CHECK_SHARED_AREA), 36062306a36Sopenharmony_ci .hdr.seq_num = cpu_to_le32(atomic_inc_return(&mei->seq_no)), 36162306a36Sopenharmony_ci }; 36262306a36Sopenharmony_ci int ret; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci lockdep_assert_held(&iwl_mei_mutex); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (mei->csa_throttled) 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci trace_iwlmei_me_msg(&msg.hdr, true); 37062306a36Sopenharmony_ci ret = mei_cldev_send(cldev, (void *)&msg, sizeof(msg)); 37162306a36Sopenharmony_ci if (ret != sizeof(msg)) { 37262306a36Sopenharmony_ci dev_err(&cldev->dev, 37362306a36Sopenharmony_ci "failed to send the SAP_ME_MSG_CHECK_SHARED_AREA message %d\n", 37462306a36Sopenharmony_ci ret); 37562306a36Sopenharmony_ci return ret; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci mei->csa_throttled = true; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci schedule_delayed_work(&mei->csa_throttle_end_wk, 38162306a36Sopenharmony_ci msecs_to_jiffies(100)); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return 0; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic void iwl_mei_csa_throttle_end_wk(struct work_struct *wk) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct iwl_mei *mei = 38962306a36Sopenharmony_ci container_of(wk, struct iwl_mei, csa_throttle_end_wk.work); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci mei->csa_throttled = false; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (iwl_mei_host_to_me_data_pending(mei)) 39662306a36Sopenharmony_ci iwl_mei_send_check_shared_area(mei->cldev); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic int iwl_mei_send_sap_msg_payload(struct mei_cl_device *cldev, 40262306a36Sopenharmony_ci struct iwl_sap_hdr *hdr) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct iwl_mei *mei = mei_cldev_get_drvdata(cldev); 40562306a36Sopenharmony_ci struct iwl_sap_q_ctrl_blk *notif_q; 40662306a36Sopenharmony_ci struct iwl_sap_dir *dir; 40762306a36Sopenharmony_ci void *q_head; 40862306a36Sopenharmony_ci u32 q_sz; 40962306a36Sopenharmony_ci int ret; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci lockdep_assert_held(&iwl_mei_mutex); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (!mei->shared_mem.ctrl) { 41462306a36Sopenharmony_ci dev_err(&cldev->dev, 41562306a36Sopenharmony_ci "No shared memory, can't send any SAP message\n"); 41662306a36Sopenharmony_ci return -EINVAL; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (!iwl_mei_is_connected()) { 42062306a36Sopenharmony_ci dev_err(&cldev->dev, 42162306a36Sopenharmony_ci "Can't send a SAP message if we're not connected\n"); 42262306a36Sopenharmony_ci return -ENODEV; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci hdr->seq_num = cpu_to_le32(atomic_inc_return(&mei->sap_seq_no)); 42662306a36Sopenharmony_ci dev_dbg(&cldev->dev, "Sending %d\n", hdr->type); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci dir = &mei->shared_mem.ctrl->dir[SAP_DIRECTION_HOST_TO_ME]; 42962306a36Sopenharmony_ci notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF]; 43062306a36Sopenharmony_ci q_head = mei->shared_mem.q_head[SAP_DIRECTION_HOST_TO_ME][SAP_QUEUE_IDX_NOTIF]; 43162306a36Sopenharmony_ci q_sz = mei->shared_mem.q_size[SAP_DIRECTION_HOST_TO_ME][SAP_QUEUE_IDX_NOTIF]; 43262306a36Sopenharmony_ci ret = iwl_mei_write_cyclic_buf(q_head, notif_q, q_head, hdr, q_sz); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (ret < 0) 43562306a36Sopenharmony_ci return ret; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci trace_iwlmei_sap_cmd(hdr, true); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci return iwl_mei_send_check_shared_area(cldev); 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_civoid iwl_mei_add_data_to_ring(struct sk_buff *skb, bool cb_tx) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci struct iwl_sap_q_ctrl_blk *notif_q; 44562306a36Sopenharmony_ci struct iwl_sap_dir *dir; 44662306a36Sopenharmony_ci struct iwl_mei *mei; 44762306a36Sopenharmony_ci size_t room_in_buf; 44862306a36Sopenharmony_ci size_t tx_sz; 44962306a36Sopenharmony_ci size_t hdr_sz; 45062306a36Sopenharmony_ci u32 q_sz; 45162306a36Sopenharmony_ci u32 rd; 45262306a36Sopenharmony_ci u32 wr; 45362306a36Sopenharmony_ci u8 *q_head; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (!iwl_mei_global_cldev) 45662306a36Sopenharmony_ci return; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* 46162306a36Sopenharmony_ci * We access this path for Rx packets (the more common case) 46262306a36Sopenharmony_ci * and from Tx path when we send DHCP packets, the latter is 46362306a36Sopenharmony_ci * very unlikely. 46462306a36Sopenharmony_ci * Take the lock already here to make sure we see that remove() 46562306a36Sopenharmony_ci * might have cleared the IWL_MEI_STATUS_SAP_CONNECTED bit. 46662306a36Sopenharmony_ci */ 46762306a36Sopenharmony_ci spin_lock_bh(&mei->data_q_lock); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (!iwl_mei_is_connected()) { 47062306a36Sopenharmony_ci spin_unlock_bh(&mei->data_q_lock); 47162306a36Sopenharmony_ci return; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci /* 47562306a36Sopenharmony_ci * We are in a RCU critical section and the remove from the CSME bus 47662306a36Sopenharmony_ci * which would free this memory waits for the readers to complete (this 47762306a36Sopenharmony_ci * is done in netdev_rx_handler_unregister). 47862306a36Sopenharmony_ci */ 47962306a36Sopenharmony_ci dir = &mei->shared_mem.ctrl->dir[SAP_DIRECTION_HOST_TO_ME]; 48062306a36Sopenharmony_ci notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_DATA]; 48162306a36Sopenharmony_ci q_head = mei->shared_mem.q_head[SAP_DIRECTION_HOST_TO_ME][SAP_QUEUE_IDX_DATA]; 48262306a36Sopenharmony_ci q_sz = mei->shared_mem.q_size[SAP_DIRECTION_HOST_TO_ME][SAP_QUEUE_IDX_DATA]; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci rd = le32_to_cpu(READ_ONCE(notif_q->rd_ptr)); 48562306a36Sopenharmony_ci wr = le32_to_cpu(READ_ONCE(notif_q->wr_ptr)); 48662306a36Sopenharmony_ci hdr_sz = cb_tx ? sizeof(struct iwl_sap_cb_data) : 48762306a36Sopenharmony_ci sizeof(struct iwl_sap_hdr); 48862306a36Sopenharmony_ci tx_sz = skb->len + hdr_sz; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (rd > q_sz || wr > q_sz) { 49162306a36Sopenharmony_ci dev_err(&mei->cldev->dev, 49262306a36Sopenharmony_ci "can't write the data: pointers are past the end of the buffer\n"); 49362306a36Sopenharmony_ci goto out; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci room_in_buf = wr >= rd ? q_sz - wr + rd : rd - wr; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* we don't have enough room for the data to write */ 49962306a36Sopenharmony_ci if (room_in_buf < tx_sz) { 50062306a36Sopenharmony_ci dev_err(&mei->cldev->dev, 50162306a36Sopenharmony_ci "Not enough room in the buffer for this data\n"); 50262306a36Sopenharmony_ci goto out; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci if (skb_headroom(skb) < hdr_sz) { 50662306a36Sopenharmony_ci dev_err(&mei->cldev->dev, 50762306a36Sopenharmony_ci "Not enough headroom in the skb to write the SAP header\n"); 50862306a36Sopenharmony_ci goto out; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (cb_tx) { 51262306a36Sopenharmony_ci struct iwl_sap_cb_data *cb_hdr = skb_push(skb, sizeof(*cb_hdr)); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci memset(cb_hdr, 0, sizeof(*cb_hdr)); 51562306a36Sopenharmony_ci cb_hdr->hdr.type = cpu_to_le16(SAP_MSG_CB_DATA_PACKET); 51662306a36Sopenharmony_ci cb_hdr->hdr.len = cpu_to_le16(skb->len - sizeof(cb_hdr->hdr)); 51762306a36Sopenharmony_ci cb_hdr->hdr.seq_num = cpu_to_le32(atomic_inc_return(&mei->sap_seq_no)); 51862306a36Sopenharmony_ci cb_hdr->to_me_filt_status = cpu_to_le32(BIT(CB_TX_DHCP_FILT_IDX)); 51962306a36Sopenharmony_ci cb_hdr->data_len = cpu_to_le32(skb->len - sizeof(*cb_hdr)); 52062306a36Sopenharmony_ci trace_iwlmei_sap_data(skb, IWL_SAP_TX_DHCP); 52162306a36Sopenharmony_ci } else { 52262306a36Sopenharmony_ci struct iwl_sap_hdr *hdr = skb_push(skb, sizeof(*hdr)); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci hdr->type = cpu_to_le16(SAP_MSG_DATA_PACKET); 52562306a36Sopenharmony_ci hdr->len = cpu_to_le16(skb->len - sizeof(*hdr)); 52662306a36Sopenharmony_ci hdr->seq_num = cpu_to_le32(atomic_inc_return(&mei->sap_seq_no)); 52762306a36Sopenharmony_ci trace_iwlmei_sap_data(skb, IWL_SAP_TX_DATA_FROM_AIR); 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (wr + tx_sz <= q_sz) { 53162306a36Sopenharmony_ci skb_copy_bits(skb, 0, q_head + wr, tx_sz); 53262306a36Sopenharmony_ci } else { 53362306a36Sopenharmony_ci skb_copy_bits(skb, 0, q_head + wr, q_sz - wr); 53462306a36Sopenharmony_ci skb_copy_bits(skb, q_sz - wr, q_head, tx_sz - (q_sz - wr)); 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci WRITE_ONCE(notif_q->wr_ptr, cpu_to_le32((wr + tx_sz) % q_sz)); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ciout: 54062306a36Sopenharmony_ci spin_unlock_bh(&mei->data_q_lock); 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic int 54462306a36Sopenharmony_ciiwl_mei_send_sap_msg(struct mei_cl_device *cldev, u16 type) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci struct iwl_sap_hdr msg = { 54762306a36Sopenharmony_ci .type = cpu_to_le16(type), 54862306a36Sopenharmony_ci }; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci return iwl_mei_send_sap_msg_payload(cldev, &msg); 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic void iwl_mei_send_csa_msg_wk(struct work_struct *wk) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci struct iwl_mei *mei = 55662306a36Sopenharmony_ci container_of(wk, struct iwl_mei, send_csa_msg_wk); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (!iwl_mei_is_connected()) 55962306a36Sopenharmony_ci return; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci iwl_mei_send_check_shared_area(mei->cldev); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci/* Called in a RCU read critical section from netif_receive_skb */ 56962306a36Sopenharmony_cistatic rx_handler_result_t iwl_mei_rx_handler(struct sk_buff **pskb) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci struct sk_buff *skb = *pskb; 57262306a36Sopenharmony_ci struct iwl_mei *mei = 57362306a36Sopenharmony_ci rcu_dereference(skb->dev->rx_handler_data); 57462306a36Sopenharmony_ci struct iwl_mei_filters *filters = rcu_dereference(mei->filters); 57562306a36Sopenharmony_ci bool rx_for_csme = false; 57662306a36Sopenharmony_ci rx_handler_result_t res; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* 57962306a36Sopenharmony_ci * remove() unregisters this handler and synchronize_net, so this 58062306a36Sopenharmony_ci * should never happen. 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_ci if (!iwl_mei_is_connected()) { 58362306a36Sopenharmony_ci dev_err(&mei->cldev->dev, 58462306a36Sopenharmony_ci "Got an Rx packet, but we're not connected to SAP?\n"); 58562306a36Sopenharmony_ci return RX_HANDLER_PASS; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (filters) 58962306a36Sopenharmony_ci res = iwl_mei_rx_filter(skb, &filters->filters, &rx_for_csme); 59062306a36Sopenharmony_ci else 59162306a36Sopenharmony_ci res = RX_HANDLER_PASS; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci /* 59462306a36Sopenharmony_ci * The data is already on the ring of the shared area, all we 59562306a36Sopenharmony_ci * need to do is to tell the CSME firmware to check what we have 59662306a36Sopenharmony_ci * there. 59762306a36Sopenharmony_ci */ 59862306a36Sopenharmony_ci if (rx_for_csme) 59962306a36Sopenharmony_ci schedule_work(&mei->send_csa_msg_wk); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (res != RX_HANDLER_PASS) { 60262306a36Sopenharmony_ci trace_iwlmei_sap_data(skb, IWL_SAP_RX_DATA_DROPPED_FROM_AIR); 60362306a36Sopenharmony_ci dev_kfree_skb(skb); 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return res; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic void iwl_mei_netdev_work(struct work_struct *wk) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct iwl_mei *mei = 61262306a36Sopenharmony_ci container_of(wk, struct iwl_mei, netdev_work); 61362306a36Sopenharmony_ci struct net_device *netdev; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* 61662306a36Sopenharmony_ci * First take rtnl and only then the mutex to avoid an ABBA 61762306a36Sopenharmony_ci * with iwl_mei_set_netdev() 61862306a36Sopenharmony_ci */ 61962306a36Sopenharmony_ci rtnl_lock(); 62062306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci netdev = rcu_dereference_protected(iwl_mei_cache.netdev, 62362306a36Sopenharmony_ci lockdep_is_held(&iwl_mei_mutex)); 62462306a36Sopenharmony_ci if (netdev) { 62562306a36Sopenharmony_ci if (mei->amt_enabled) 62662306a36Sopenharmony_ci netdev_rx_handler_register(netdev, iwl_mei_rx_handler, 62762306a36Sopenharmony_ci mei); 62862306a36Sopenharmony_ci else 62962306a36Sopenharmony_ci netdev_rx_handler_unregister(netdev); 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 63362306a36Sopenharmony_ci rtnl_unlock(); 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic void 63762306a36Sopenharmony_ciiwl_mei_handle_rx_start_ok(struct mei_cl_device *cldev, 63862306a36Sopenharmony_ci const struct iwl_sap_me_msg_start_ok *rsp, 63962306a36Sopenharmony_ci ssize_t len) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci if (len != sizeof(*rsp)) { 64262306a36Sopenharmony_ci dev_err(&cldev->dev, 64362306a36Sopenharmony_ci "got invalid SAP_ME_MSG_START_OK from CSME firmware\n"); 64462306a36Sopenharmony_ci dev_err(&cldev->dev, 64562306a36Sopenharmony_ci "size is incorrect: %zd instead of %zu\n", 64662306a36Sopenharmony_ci len, sizeof(*rsp)); 64762306a36Sopenharmony_ci return; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (rsp->supported_version != SAP_VERSION) { 65162306a36Sopenharmony_ci dev_err(&cldev->dev, 65262306a36Sopenharmony_ci "didn't get the expected version: got %d\n", 65362306a36Sopenharmony_ci rsp->supported_version); 65462306a36Sopenharmony_ci return; 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 65862306a36Sopenharmony_ci set_bit(IWL_MEI_STATUS_SAP_CONNECTED, &iwl_mei_status); 65962306a36Sopenharmony_ci /* 66062306a36Sopenharmony_ci * We'll receive AMT_STATE SAP message in a bit and 66162306a36Sopenharmony_ci * that will continue the flow 66262306a36Sopenharmony_ci */ 66362306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic void iwl_mei_handle_csme_filters(struct mei_cl_device *cldev, 66762306a36Sopenharmony_ci const struct iwl_sap_csme_filters *filters) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci struct iwl_mei *mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); 67062306a36Sopenharmony_ci struct iwl_mei_filters *new_filters; 67162306a36Sopenharmony_ci struct iwl_mei_filters *old_filters; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci old_filters = 67462306a36Sopenharmony_ci rcu_dereference_protected(mei->filters, 67562306a36Sopenharmony_ci lockdep_is_held(&iwl_mei_mutex)); 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci new_filters = kzalloc(sizeof(*new_filters), GFP_KERNEL); 67862306a36Sopenharmony_ci if (!new_filters) 67962306a36Sopenharmony_ci return; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci /* Copy the OOB filters */ 68262306a36Sopenharmony_ci new_filters->filters = filters->filters; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci rcu_assign_pointer(mei->filters, new_filters); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (old_filters) 68762306a36Sopenharmony_ci kfree_rcu(old_filters, rcu_head); 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic void 69162306a36Sopenharmony_ciiwl_mei_handle_conn_status(struct mei_cl_device *cldev, 69262306a36Sopenharmony_ci const struct iwl_sap_notif_conn_status *status) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct iwl_mei *mei = mei_cldev_get_drvdata(cldev); 69562306a36Sopenharmony_ci struct iwl_mei_conn_info conn_info = { 69662306a36Sopenharmony_ci .lp_state = le32_to_cpu(status->link_prot_state), 69762306a36Sopenharmony_ci .ssid_len = le32_to_cpu(status->conn_info.ssid_len), 69862306a36Sopenharmony_ci .channel = status->conn_info.channel, 69962306a36Sopenharmony_ci .band = status->conn_info.band, 70062306a36Sopenharmony_ci .auth_mode = le32_to_cpu(status->conn_info.auth_mode), 70162306a36Sopenharmony_ci .pairwise_cipher = le32_to_cpu(status->conn_info.pairwise_cipher), 70262306a36Sopenharmony_ci }; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (!iwl_mei_cache.ops || 70562306a36Sopenharmony_ci conn_info.ssid_len > ARRAY_SIZE(conn_info.ssid)) 70662306a36Sopenharmony_ci return; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci memcpy(conn_info.ssid, status->conn_info.ssid, conn_info.ssid_len); 70962306a36Sopenharmony_ci ether_addr_copy(conn_info.bssid, status->conn_info.bssid); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci iwl_mei_cache.ops->me_conn_status(iwl_mei_cache.priv, &conn_info); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci mei->link_prot_state = status->link_prot_state; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci /* 71662306a36Sopenharmony_ci * Update the Rfkill state in case the host does not own the device: 71762306a36Sopenharmony_ci * if we are in Link Protection, ask to not touch the device, else, 71862306a36Sopenharmony_ci * unblock rfkill. 71962306a36Sopenharmony_ci * If the host owns the device, inform the user space whether it can 72062306a36Sopenharmony_ci * roam. 72162306a36Sopenharmony_ci */ 72262306a36Sopenharmony_ci if (mei->got_ownership) 72362306a36Sopenharmony_ci iwl_mei_cache.ops->roaming_forbidden(iwl_mei_cache.priv, 72462306a36Sopenharmony_ci status->link_prot_state); 72562306a36Sopenharmony_ci else 72662306a36Sopenharmony_ci iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, 72762306a36Sopenharmony_ci status->link_prot_state, false); 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic void iwl_mei_set_init_conf(struct iwl_mei *mei) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci struct iwl_sap_notif_host_link_up link_msg = { 73362306a36Sopenharmony_ci .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_HOST_LINK_UP), 73462306a36Sopenharmony_ci .hdr.len = cpu_to_le16(sizeof(link_msg) - sizeof(link_msg.hdr)), 73562306a36Sopenharmony_ci }; 73662306a36Sopenharmony_ci struct iwl_sap_notif_country_code mcc_msg = { 73762306a36Sopenharmony_ci .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_COUNTRY_CODE), 73862306a36Sopenharmony_ci .hdr.len = cpu_to_le16(sizeof(mcc_msg) - sizeof(mcc_msg.hdr)), 73962306a36Sopenharmony_ci .mcc = cpu_to_le16(iwl_mei_cache.mcc), 74062306a36Sopenharmony_ci }; 74162306a36Sopenharmony_ci struct iwl_sap_notif_sar_limits sar_msg = { 74262306a36Sopenharmony_ci .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_SAR_LIMITS), 74362306a36Sopenharmony_ci .hdr.len = cpu_to_le16(sizeof(sar_msg) - sizeof(sar_msg.hdr)), 74462306a36Sopenharmony_ci }; 74562306a36Sopenharmony_ci struct iwl_sap_notif_host_nic_info nic_info_msg = { 74662306a36Sopenharmony_ci .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_NIC_INFO), 74762306a36Sopenharmony_ci .hdr.len = cpu_to_le16(sizeof(nic_info_msg) - sizeof(nic_info_msg.hdr)), 74862306a36Sopenharmony_ci }; 74962306a36Sopenharmony_ci struct iwl_sap_msg_dw rfkill_msg = { 75062306a36Sopenharmony_ci .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_RADIO_STATE), 75162306a36Sopenharmony_ci .hdr.len = cpu_to_le16(sizeof(rfkill_msg) - sizeof(rfkill_msg.hdr)), 75262306a36Sopenharmony_ci .val = cpu_to_le32(iwl_mei_cache.rf_kill), 75362306a36Sopenharmony_ci }; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* wifi driver has registered already */ 75662306a36Sopenharmony_ci if (iwl_mei_cache.ops) { 75762306a36Sopenharmony_ci iwl_mei_send_sap_msg(mei->cldev, 75862306a36Sopenharmony_ci SAP_MSG_NOTIF_WIFIDR_UP); 75962306a36Sopenharmony_ci iwl_mei_cache.ops->sap_connected(iwl_mei_cache.priv); 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci iwl_mei_send_sap_msg(mei->cldev, SAP_MSG_NOTIF_WHO_OWNS_NIC); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (iwl_mei_cache.conn_info) { 76562306a36Sopenharmony_ci link_msg.conn_info = *iwl_mei_cache.conn_info; 76662306a36Sopenharmony_ci iwl_mei_send_sap_msg_payload(mei->cldev, &link_msg.hdr); 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci iwl_mei_send_sap_msg_payload(mei->cldev, &mcc_msg.hdr); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (iwl_mei_cache.power_limit) { 77262306a36Sopenharmony_ci memcpy(sar_msg.sar_chain_info_table, iwl_mei_cache.power_limit, 77362306a36Sopenharmony_ci sizeof(sar_msg.sar_chain_info_table)); 77462306a36Sopenharmony_ci iwl_mei_send_sap_msg_payload(mei->cldev, &sar_msg.hdr); 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (is_valid_ether_addr(iwl_mei_cache.mac_address)) { 77862306a36Sopenharmony_ci ether_addr_copy(nic_info_msg.mac_address, 77962306a36Sopenharmony_ci iwl_mei_cache.mac_address); 78062306a36Sopenharmony_ci ether_addr_copy(nic_info_msg.nvm_address, 78162306a36Sopenharmony_ci iwl_mei_cache.nvm_address); 78262306a36Sopenharmony_ci iwl_mei_send_sap_msg_payload(mei->cldev, &nic_info_msg.hdr); 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci iwl_mei_send_sap_msg_payload(mei->cldev, &rfkill_msg.hdr); 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic void iwl_mei_handle_amt_state(struct mei_cl_device *cldev, 78962306a36Sopenharmony_ci const struct iwl_sap_msg_dw *dw) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci struct iwl_mei *mei = mei_cldev_get_drvdata(cldev); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (mei->amt_enabled == !!le32_to_cpu(dw->val)) 79662306a36Sopenharmony_ci goto out; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci mei->amt_enabled = dw->val; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (mei->amt_enabled) 80162306a36Sopenharmony_ci iwl_mei_set_init_conf(mei); 80262306a36Sopenharmony_ci else if (iwl_mei_cache.ops) 80362306a36Sopenharmony_ci iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false, false); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci schedule_work(&mei->netdev_work); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ciout: 80862306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic void iwl_mei_handle_nic_owner(struct mei_cl_device *cldev, 81262306a36Sopenharmony_ci const struct iwl_sap_msg_dw *dw) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci struct iwl_mei *mei = mei_cldev_get_drvdata(cldev); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci mei->got_ownership = dw->val != cpu_to_le32(SAP_NIC_OWNER_ME); 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic void iwl_mei_handle_can_release_ownership(struct mei_cl_device *cldev, 82062306a36Sopenharmony_ci const void *payload) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci /* We can get ownership and driver is registered, go ahead */ 82362306a36Sopenharmony_ci if (iwl_mei_cache.ops) 82462306a36Sopenharmony_ci iwl_mei_send_sap_msg(cldev, 82562306a36Sopenharmony_ci SAP_MSG_NOTIF_HOST_ASKS_FOR_NIC_OWNERSHIP); 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic void iwl_mei_handle_csme_taking_ownership(struct mei_cl_device *cldev, 82962306a36Sopenharmony_ci const void *payload) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci struct iwl_mei *mei = mei_cldev_get_drvdata(cldev); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci dev_info(&cldev->dev, "CSME takes ownership\n"); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci mei->got_ownership = false; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci if (iwl_mei_cache.ops && !mei->device_down) { 83862306a36Sopenharmony_ci /* 83962306a36Sopenharmony_ci * Remember to send CSME_OWNERSHIP_CONFIRMED when the wifi 84062306a36Sopenharmony_ci * driver is finished taking the device down. 84162306a36Sopenharmony_ci */ 84262306a36Sopenharmony_ci mei->csme_taking_ownership = true; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, true, true); 84562306a36Sopenharmony_ci } else { 84662306a36Sopenharmony_ci iwl_mei_send_sap_msg(cldev, 84762306a36Sopenharmony_ci SAP_MSG_NOTIF_CSME_OWNERSHIP_CONFIRMED); 84862306a36Sopenharmony_ci schedule_delayed_work(&mei->ownership_dwork, 84962306a36Sopenharmony_ci MEI_OWNERSHIP_RETAKE_TIMEOUT_MS); 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_cistatic void iwl_mei_handle_nvm(struct mei_cl_device *cldev, 85462306a36Sopenharmony_ci const struct iwl_sap_nvm *sap_nvm) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct iwl_mei *mei = mei_cldev_get_drvdata(cldev); 85762306a36Sopenharmony_ci const struct iwl_mei_nvm *mei_nvm = (const void *)sap_nvm; 85862306a36Sopenharmony_ci int i; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci kfree(mei->nvm); 86162306a36Sopenharmony_ci mei->nvm = kzalloc(sizeof(*mei_nvm), GFP_KERNEL); 86262306a36Sopenharmony_ci if (!mei->nvm) 86362306a36Sopenharmony_ci return; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci ether_addr_copy(mei->nvm->hw_addr, sap_nvm->hw_addr); 86662306a36Sopenharmony_ci mei->nvm->n_hw_addrs = sap_nvm->n_hw_addrs; 86762306a36Sopenharmony_ci mei->nvm->radio_cfg = le32_to_cpu(sap_nvm->radio_cfg); 86862306a36Sopenharmony_ci mei->nvm->caps = le32_to_cpu(sap_nvm->caps); 86962306a36Sopenharmony_ci mei->nvm->nvm_version = le32_to_cpu(sap_nvm->nvm_version); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mei->nvm->channels); i++) 87262306a36Sopenharmony_ci mei->nvm->channels[i] = le32_to_cpu(sap_nvm->channels[i]); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci wake_up_all(&mei->get_nvm_wq); 87562306a36Sopenharmony_ci} 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_cistatic void iwl_mei_handle_rx_host_own_req(struct mei_cl_device *cldev, 87862306a36Sopenharmony_ci const struct iwl_sap_msg_dw *dw) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci struct iwl_mei *mei = mei_cldev_get_drvdata(cldev); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci /* 88362306a36Sopenharmony_ci * This means that we can't use the wifi device right now, CSME is not 88462306a36Sopenharmony_ci * ready to let us use it. 88562306a36Sopenharmony_ci */ 88662306a36Sopenharmony_ci if (!dw->val) { 88762306a36Sopenharmony_ci dev_info(&cldev->dev, "Ownership req denied\n"); 88862306a36Sopenharmony_ci return; 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci mei->got_ownership = true; 89262306a36Sopenharmony_ci wake_up_all(&mei->get_ownership_wq); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci iwl_mei_send_sap_msg(cldev, 89562306a36Sopenharmony_ci SAP_MSG_NOTIF_HOST_OWNERSHIP_CONFIRMED); 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci /* We can now start the connection, unblock rfkill */ 89862306a36Sopenharmony_ci if (iwl_mei_cache.ops) 89962306a36Sopenharmony_ci iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false, false); 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_cistatic void iwl_mei_handle_pldr_ack(struct mei_cl_device *cldev, 90362306a36Sopenharmony_ci const struct iwl_sap_pldr_ack_data *ack) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci struct iwl_mei *mei = mei_cldev_get_drvdata(cldev); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci mei->pldr_active = le32_to_cpu(ack->status) == SAP_PLDR_STATUS_SUCCESS; 90862306a36Sopenharmony_ci wake_up_all(&mei->pldr_wq); 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic void iwl_mei_handle_ping(struct mei_cl_device *cldev, 91262306a36Sopenharmony_ci const struct iwl_sap_hdr *hdr) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci iwl_mei_send_sap_msg(cldev, SAP_MSG_NOTIF_PONG); 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_cistatic void iwl_mei_handle_sap_msg(struct mei_cl_device *cldev, 91862306a36Sopenharmony_ci const struct iwl_sap_hdr *hdr) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci u16 len = le16_to_cpu(hdr->len) + sizeof(*hdr); 92162306a36Sopenharmony_ci u16 type = le16_to_cpu(hdr->type); 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci dev_dbg(&cldev->dev, 92462306a36Sopenharmony_ci "Got a new SAP message: type %d, len %d, seq %d\n", 92562306a36Sopenharmony_ci le16_to_cpu(hdr->type), len, 92662306a36Sopenharmony_ci le32_to_cpu(hdr->seq_num)); 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci#define SAP_MSG_HANDLER(_cmd, _handler, _sz) \ 92962306a36Sopenharmony_ci case SAP_MSG_NOTIF_ ## _cmd: \ 93062306a36Sopenharmony_ci if (len < _sz) { \ 93162306a36Sopenharmony_ci dev_err(&cldev->dev, \ 93262306a36Sopenharmony_ci "Bad size for %d: %u < %u\n", \ 93362306a36Sopenharmony_ci le16_to_cpu(hdr->type), \ 93462306a36Sopenharmony_ci (unsigned int)len, \ 93562306a36Sopenharmony_ci (unsigned int)_sz); \ 93662306a36Sopenharmony_ci break; \ 93762306a36Sopenharmony_ci } \ 93862306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); \ 93962306a36Sopenharmony_ci _handler(cldev, (const void *)hdr); \ 94062306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); \ 94162306a36Sopenharmony_ci break 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci#define SAP_MSG_HANDLER_NO_LOCK(_cmd, _handler, _sz) \ 94462306a36Sopenharmony_ci case SAP_MSG_NOTIF_ ## _cmd: \ 94562306a36Sopenharmony_ci if (len < _sz) { \ 94662306a36Sopenharmony_ci dev_err(&cldev->dev, \ 94762306a36Sopenharmony_ci "Bad size for %d: %u < %u\n", \ 94862306a36Sopenharmony_ci le16_to_cpu(hdr->type), \ 94962306a36Sopenharmony_ci (unsigned int)len, \ 95062306a36Sopenharmony_ci (unsigned int)_sz); \ 95162306a36Sopenharmony_ci break; \ 95262306a36Sopenharmony_ci } \ 95362306a36Sopenharmony_ci _handler(cldev, (const void *)hdr); \ 95462306a36Sopenharmony_ci break 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci#define SAP_MSG_HANDLER_NO_HANDLER(_cmd, _sz) \ 95762306a36Sopenharmony_ci case SAP_MSG_NOTIF_ ## _cmd: \ 95862306a36Sopenharmony_ci if (len < _sz) { \ 95962306a36Sopenharmony_ci dev_err(&cldev->dev, \ 96062306a36Sopenharmony_ci "Bad size for %d: %u < %u\n", \ 96162306a36Sopenharmony_ci le16_to_cpu(hdr->type), \ 96262306a36Sopenharmony_ci (unsigned int)len, \ 96362306a36Sopenharmony_ci (unsigned int)_sz); \ 96462306a36Sopenharmony_ci break; \ 96562306a36Sopenharmony_ci } \ 96662306a36Sopenharmony_ci break 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci switch (type) { 96962306a36Sopenharmony_ci SAP_MSG_HANDLER(PING, iwl_mei_handle_ping, 0); 97062306a36Sopenharmony_ci SAP_MSG_HANDLER(CSME_FILTERS, 97162306a36Sopenharmony_ci iwl_mei_handle_csme_filters, 97262306a36Sopenharmony_ci sizeof(struct iwl_sap_csme_filters)); 97362306a36Sopenharmony_ci SAP_MSG_HANDLER(CSME_CONN_STATUS, 97462306a36Sopenharmony_ci iwl_mei_handle_conn_status, 97562306a36Sopenharmony_ci sizeof(struct iwl_sap_notif_conn_status)); 97662306a36Sopenharmony_ci SAP_MSG_HANDLER_NO_LOCK(AMT_STATE, 97762306a36Sopenharmony_ci iwl_mei_handle_amt_state, 97862306a36Sopenharmony_ci sizeof(struct iwl_sap_msg_dw)); 97962306a36Sopenharmony_ci SAP_MSG_HANDLER_NO_HANDLER(PONG, 0); 98062306a36Sopenharmony_ci SAP_MSG_HANDLER(NVM, iwl_mei_handle_nvm, 98162306a36Sopenharmony_ci sizeof(struct iwl_sap_nvm)); 98262306a36Sopenharmony_ci SAP_MSG_HANDLER(CSME_REPLY_TO_HOST_OWNERSHIP_REQ, 98362306a36Sopenharmony_ci iwl_mei_handle_rx_host_own_req, 98462306a36Sopenharmony_ci sizeof(struct iwl_sap_msg_dw)); 98562306a36Sopenharmony_ci SAP_MSG_HANDLER(NIC_OWNER, iwl_mei_handle_nic_owner, 98662306a36Sopenharmony_ci sizeof(struct iwl_sap_msg_dw)); 98762306a36Sopenharmony_ci SAP_MSG_HANDLER(CSME_CAN_RELEASE_OWNERSHIP, 98862306a36Sopenharmony_ci iwl_mei_handle_can_release_ownership, 0); 98962306a36Sopenharmony_ci SAP_MSG_HANDLER(CSME_TAKING_OWNERSHIP, 99062306a36Sopenharmony_ci iwl_mei_handle_csme_taking_ownership, 0); 99162306a36Sopenharmony_ci SAP_MSG_HANDLER(PLDR_ACK, iwl_mei_handle_pldr_ack, 99262306a36Sopenharmony_ci sizeof(struct iwl_sap_pldr_ack_data)); 99362306a36Sopenharmony_ci default: 99462306a36Sopenharmony_ci /* 99562306a36Sopenharmony_ci * This is not really an error, there are message that we decided 99662306a36Sopenharmony_ci * to ignore, yet, it is useful to be able to leave a note if debug 99762306a36Sopenharmony_ci * is enabled. 99862306a36Sopenharmony_ci */ 99962306a36Sopenharmony_ci dev_dbg(&cldev->dev, "Unsupported message: type %d, len %d\n", 100062306a36Sopenharmony_ci le16_to_cpu(hdr->type), len); 100162306a36Sopenharmony_ci } 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci#undef SAP_MSG_HANDLER 100462306a36Sopenharmony_ci#undef SAP_MSG_HANDLER_NO_LOCK 100562306a36Sopenharmony_ci} 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_cistatic void iwl_mei_read_from_q(const u8 *q_head, u32 q_sz, 100862306a36Sopenharmony_ci u32 *_rd, u32 wr, 100962306a36Sopenharmony_ci void *_buf, u32 len) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci u8 *buf = _buf; 101262306a36Sopenharmony_ci u32 rd = *_rd; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci if (rd + len <= q_sz) { 101562306a36Sopenharmony_ci memcpy(buf, q_head + rd, len); 101662306a36Sopenharmony_ci rd += len; 101762306a36Sopenharmony_ci } else { 101862306a36Sopenharmony_ci memcpy(buf, q_head + rd, q_sz - rd); 101962306a36Sopenharmony_ci memcpy(buf + q_sz - rd, q_head, len - (q_sz - rd)); 102062306a36Sopenharmony_ci rd = len - (q_sz - rd); 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci *_rd = rd; 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci#define QOS_HDR_IV_SNAP_LEN (sizeof(struct ieee80211_qos_hdr) + \ 102762306a36Sopenharmony_ci IEEE80211_TKIP_IV_LEN + \ 102862306a36Sopenharmony_ci sizeof(rfc1042_header) + ETH_TLEN) 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_cistatic void iwl_mei_handle_sap_data(struct mei_cl_device *cldev, 103162306a36Sopenharmony_ci const u8 *q_head, u32 q_sz, 103262306a36Sopenharmony_ci u32 rd, u32 wr, ssize_t valid_rx_sz, 103362306a36Sopenharmony_ci struct sk_buff_head *tx_skbs) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci struct iwl_sap_hdr hdr; 103662306a36Sopenharmony_ci struct net_device *netdev = 103762306a36Sopenharmony_ci rcu_dereference_protected(iwl_mei_cache.netdev, 103862306a36Sopenharmony_ci lockdep_is_held(&iwl_mei_mutex)); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci if (!netdev) 104162306a36Sopenharmony_ci return; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci while (valid_rx_sz >= sizeof(hdr)) { 104462306a36Sopenharmony_ci struct ethhdr *ethhdr; 104562306a36Sopenharmony_ci unsigned char *data; 104662306a36Sopenharmony_ci struct sk_buff *skb; 104762306a36Sopenharmony_ci u16 len; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci iwl_mei_read_from_q(q_head, q_sz, &rd, wr, &hdr, sizeof(hdr)); 105062306a36Sopenharmony_ci valid_rx_sz -= sizeof(hdr); 105162306a36Sopenharmony_ci len = le16_to_cpu(hdr.len); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci if (valid_rx_sz < len) { 105462306a36Sopenharmony_ci dev_err(&cldev->dev, 105562306a36Sopenharmony_ci "Data queue is corrupted: valid data len %zd, len %d\n", 105662306a36Sopenharmony_ci valid_rx_sz, len); 105762306a36Sopenharmony_ci break; 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci if (len < sizeof(*ethhdr)) { 106162306a36Sopenharmony_ci dev_err(&cldev->dev, 106262306a36Sopenharmony_ci "Data len is smaller than an ethernet header? len = %d\n", 106362306a36Sopenharmony_ci len); 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci valid_rx_sz -= len; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci if (le16_to_cpu(hdr.type) != SAP_MSG_DATA_PACKET) { 106962306a36Sopenharmony_ci dev_err(&cldev->dev, "Unsupported Rx data: type %d, len %d\n", 107062306a36Sopenharmony_ci le16_to_cpu(hdr.type), len); 107162306a36Sopenharmony_ci continue; 107262306a36Sopenharmony_ci } 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci /* We need enough room for the WiFi header + SNAP + IV */ 107562306a36Sopenharmony_ci skb = netdev_alloc_skb(netdev, len + QOS_HDR_IV_SNAP_LEN); 107662306a36Sopenharmony_ci if (!skb) 107762306a36Sopenharmony_ci continue; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci skb_reserve(skb, QOS_HDR_IV_SNAP_LEN); 108062306a36Sopenharmony_ci ethhdr = skb_push(skb, sizeof(*ethhdr)); 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci iwl_mei_read_from_q(q_head, q_sz, &rd, wr, 108362306a36Sopenharmony_ci ethhdr, sizeof(*ethhdr)); 108462306a36Sopenharmony_ci len -= sizeof(*ethhdr); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci skb_reset_mac_header(skb); 108762306a36Sopenharmony_ci skb_reset_network_header(skb); 108862306a36Sopenharmony_ci skb->protocol = ethhdr->h_proto; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci data = skb_put(skb, len); 109162306a36Sopenharmony_ci iwl_mei_read_from_q(q_head, q_sz, &rd, wr, data, len); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci /* 109462306a36Sopenharmony_ci * Enqueue the skb here so that it can be sent later when we 109562306a36Sopenharmony_ci * do not hold the mutex. TX'ing a packet with a mutex held is 109662306a36Sopenharmony_ci * possible, but it wouldn't be nice to forbid the TX path to 109762306a36Sopenharmony_ci * call any of iwlmei's functions, since every API from iwlmei 109862306a36Sopenharmony_ci * needs the mutex. 109962306a36Sopenharmony_ci */ 110062306a36Sopenharmony_ci __skb_queue_tail(tx_skbs, skb); 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci} 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_cistatic void iwl_mei_handle_sap_rx_cmd(struct mei_cl_device *cldev, 110562306a36Sopenharmony_ci const u8 *q_head, u32 q_sz, 110662306a36Sopenharmony_ci u32 rd, u32 wr, ssize_t valid_rx_sz) 110762306a36Sopenharmony_ci{ 110862306a36Sopenharmony_ci struct page *p = alloc_page(GFP_KERNEL); 110962306a36Sopenharmony_ci struct iwl_sap_hdr *hdr; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci if (!p) 111262306a36Sopenharmony_ci return; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci hdr = page_address(p); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci while (valid_rx_sz >= sizeof(*hdr)) { 111762306a36Sopenharmony_ci u16 len; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci iwl_mei_read_from_q(q_head, q_sz, &rd, wr, hdr, sizeof(*hdr)); 112062306a36Sopenharmony_ci valid_rx_sz -= sizeof(*hdr); 112162306a36Sopenharmony_ci len = le16_to_cpu(hdr->len); 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci if (valid_rx_sz < len) 112462306a36Sopenharmony_ci break; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci iwl_mei_read_from_q(q_head, q_sz, &rd, wr, hdr + 1, len); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci trace_iwlmei_sap_cmd(hdr, false); 112962306a36Sopenharmony_ci iwl_mei_handle_sap_msg(cldev, hdr); 113062306a36Sopenharmony_ci valid_rx_sz -= len; 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci /* valid_rx_sz must be 0 now... */ 113462306a36Sopenharmony_ci if (valid_rx_sz) 113562306a36Sopenharmony_ci dev_err(&cldev->dev, 113662306a36Sopenharmony_ci "More data in the buffer although we read it all\n"); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci __free_page(p); 113962306a36Sopenharmony_ci} 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_cistatic void iwl_mei_handle_sap_rx(struct mei_cl_device *cldev, 114262306a36Sopenharmony_ci struct iwl_sap_q_ctrl_blk *notif_q, 114362306a36Sopenharmony_ci const u8 *q_head, 114462306a36Sopenharmony_ci struct sk_buff_head *skbs, 114562306a36Sopenharmony_ci u32 q_sz) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci u32 rd = le32_to_cpu(READ_ONCE(notif_q->rd_ptr)); 114862306a36Sopenharmony_ci u32 wr = le32_to_cpu(READ_ONCE(notif_q->wr_ptr)); 114962306a36Sopenharmony_ci ssize_t valid_rx_sz; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci if (rd > q_sz || wr > q_sz) { 115262306a36Sopenharmony_ci dev_err(&cldev->dev, 115362306a36Sopenharmony_ci "Pointers are past the buffer limit\n"); 115462306a36Sopenharmony_ci return; 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci if (rd == wr) 115862306a36Sopenharmony_ci return; 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci valid_rx_sz = wr > rd ? wr - rd : q_sz - rd + wr; 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci if (skbs) 116362306a36Sopenharmony_ci iwl_mei_handle_sap_data(cldev, q_head, q_sz, rd, wr, 116462306a36Sopenharmony_ci valid_rx_sz, skbs); 116562306a36Sopenharmony_ci else 116662306a36Sopenharmony_ci iwl_mei_handle_sap_rx_cmd(cldev, q_head, q_sz, rd, wr, 116762306a36Sopenharmony_ci valid_rx_sz); 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci /* Increment the read pointer to point to the write pointer */ 117062306a36Sopenharmony_ci WRITE_ONCE(notif_q->rd_ptr, cpu_to_le32(wr)); 117162306a36Sopenharmony_ci} 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_cistatic void iwl_mei_handle_check_shared_area(struct mei_cl_device *cldev) 117462306a36Sopenharmony_ci{ 117562306a36Sopenharmony_ci struct iwl_mei *mei = mei_cldev_get_drvdata(cldev); 117662306a36Sopenharmony_ci struct iwl_sap_q_ctrl_blk *notif_q; 117762306a36Sopenharmony_ci struct sk_buff_head tx_skbs; 117862306a36Sopenharmony_ci struct iwl_sap_dir *dir; 117962306a36Sopenharmony_ci void *q_head; 118062306a36Sopenharmony_ci u32 q_sz; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci if (!mei->shared_mem.ctrl) 118362306a36Sopenharmony_ci return; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci dir = &mei->shared_mem.ctrl->dir[SAP_DIRECTION_ME_TO_HOST]; 118662306a36Sopenharmony_ci notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF]; 118762306a36Sopenharmony_ci q_head = mei->shared_mem.q_head[SAP_DIRECTION_ME_TO_HOST][SAP_QUEUE_IDX_NOTIF]; 118862306a36Sopenharmony_ci q_sz = mei->shared_mem.q_size[SAP_DIRECTION_ME_TO_HOST][SAP_QUEUE_IDX_NOTIF]; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci /* 119162306a36Sopenharmony_ci * Do not hold the mutex here, but rather each and every message 119262306a36Sopenharmony_ci * handler takes it. 119362306a36Sopenharmony_ci * This allows message handlers to take it at a certain time. 119462306a36Sopenharmony_ci */ 119562306a36Sopenharmony_ci iwl_mei_handle_sap_rx(cldev, notif_q, q_head, NULL, q_sz); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 119862306a36Sopenharmony_ci dir = &mei->shared_mem.ctrl->dir[SAP_DIRECTION_ME_TO_HOST]; 119962306a36Sopenharmony_ci notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_DATA]; 120062306a36Sopenharmony_ci q_head = mei->shared_mem.q_head[SAP_DIRECTION_ME_TO_HOST][SAP_QUEUE_IDX_DATA]; 120162306a36Sopenharmony_ci q_sz = mei->shared_mem.q_size[SAP_DIRECTION_ME_TO_HOST][SAP_QUEUE_IDX_DATA]; 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci __skb_queue_head_init(&tx_skbs); 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci iwl_mei_handle_sap_rx(cldev, notif_q, q_head, &tx_skbs, q_sz); 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci if (skb_queue_empty(&tx_skbs)) { 120862306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 120962306a36Sopenharmony_ci return; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci /* 121362306a36Sopenharmony_ci * Take the RCU read lock before we unlock the mutex to make sure that 121462306a36Sopenharmony_ci * even if the netdev is replaced by another non-NULL netdev right after 121562306a36Sopenharmony_ci * we unlock the mutex, the old netdev will still be valid when we 121662306a36Sopenharmony_ci * transmit the frames. We can't allow to replace the netdev here because 121762306a36Sopenharmony_ci * the skbs hold a pointer to the netdev. 121862306a36Sopenharmony_ci */ 121962306a36Sopenharmony_ci rcu_read_lock(); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci if (!rcu_access_pointer(iwl_mei_cache.netdev)) { 122462306a36Sopenharmony_ci dev_err(&cldev->dev, "Can't Tx without a netdev\n"); 122562306a36Sopenharmony_ci skb_queue_purge(&tx_skbs); 122662306a36Sopenharmony_ci goto out; 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci while (!skb_queue_empty(&tx_skbs)) { 123062306a36Sopenharmony_ci struct sk_buff *skb = __skb_dequeue(&tx_skbs); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci trace_iwlmei_sap_data(skb, IWL_SAP_RX_DATA_TO_AIR); 123362306a36Sopenharmony_ci dev_queue_xmit(skb); 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ciout: 123762306a36Sopenharmony_ci rcu_read_unlock(); 123862306a36Sopenharmony_ci} 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_cistatic void iwl_mei_rx(struct mei_cl_device *cldev) 124162306a36Sopenharmony_ci{ 124262306a36Sopenharmony_ci struct iwl_sap_me_msg_hdr *hdr; 124362306a36Sopenharmony_ci u8 msg[100]; 124462306a36Sopenharmony_ci ssize_t ret; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci ret = mei_cldev_recv(cldev, (u8 *)&msg, sizeof(msg)); 124762306a36Sopenharmony_ci if (ret < 0) { 124862306a36Sopenharmony_ci dev_err(&cldev->dev, "failed to receive data: %zd\n", ret); 124962306a36Sopenharmony_ci return; 125062306a36Sopenharmony_ci } 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci if (ret == 0) { 125362306a36Sopenharmony_ci dev_err(&cldev->dev, "got an empty response\n"); 125462306a36Sopenharmony_ci return; 125562306a36Sopenharmony_ci } 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci hdr = (void *)msg; 125862306a36Sopenharmony_ci trace_iwlmei_me_msg(hdr, false); 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci switch (le32_to_cpu(hdr->type)) { 126162306a36Sopenharmony_ci case SAP_ME_MSG_START_OK: 126262306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(struct iwl_sap_me_msg_start_ok) > 126362306a36Sopenharmony_ci sizeof(msg)); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci iwl_mei_handle_rx_start_ok(cldev, (void *)msg, ret); 126662306a36Sopenharmony_ci break; 126762306a36Sopenharmony_ci case SAP_ME_MSG_CHECK_SHARED_AREA: 126862306a36Sopenharmony_ci iwl_mei_handle_check_shared_area(cldev); 126962306a36Sopenharmony_ci break; 127062306a36Sopenharmony_ci default: 127162306a36Sopenharmony_ci dev_err(&cldev->dev, "got a RX notification: %d\n", 127262306a36Sopenharmony_ci le32_to_cpu(hdr->type)); 127362306a36Sopenharmony_ci break; 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci} 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_cistatic int iwl_mei_send_start(struct mei_cl_device *cldev) 127862306a36Sopenharmony_ci{ 127962306a36Sopenharmony_ci struct iwl_mei *mei = mei_cldev_get_drvdata(cldev); 128062306a36Sopenharmony_ci struct iwl_sap_me_msg_start msg = { 128162306a36Sopenharmony_ci .hdr.type = cpu_to_le32(SAP_ME_MSG_START), 128262306a36Sopenharmony_ci .hdr.seq_num = cpu_to_le32(atomic_inc_return(&mei->seq_no)), 128362306a36Sopenharmony_ci .hdr.len = cpu_to_le32(sizeof(msg)), 128462306a36Sopenharmony_ci .supported_versions[0] = SAP_VERSION, 128562306a36Sopenharmony_ci .init_data_seq_num = cpu_to_le16(0x100), 128662306a36Sopenharmony_ci .init_notif_seq_num = cpu_to_le16(0x800), 128762306a36Sopenharmony_ci }; 128862306a36Sopenharmony_ci int ret; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci trace_iwlmei_me_msg(&msg.hdr, true); 129162306a36Sopenharmony_ci ret = mei_cldev_send(cldev, (void *)&msg, sizeof(msg)); 129262306a36Sopenharmony_ci if (ret != sizeof(msg)) { 129362306a36Sopenharmony_ci dev_err(&cldev->dev, 129462306a36Sopenharmony_ci "failed to send the SAP_ME_MSG_START message %d\n", 129562306a36Sopenharmony_ci ret); 129662306a36Sopenharmony_ci return ret; 129762306a36Sopenharmony_ci } 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci return 0; 130062306a36Sopenharmony_ci} 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_cistatic int iwl_mei_enable(struct mei_cl_device *cldev) 130362306a36Sopenharmony_ci{ 130462306a36Sopenharmony_ci int ret; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci ret = mei_cldev_enable(cldev); 130762306a36Sopenharmony_ci if (ret < 0) { 130862306a36Sopenharmony_ci dev_err(&cldev->dev, "failed to enable the device: %d\n", ret); 130962306a36Sopenharmony_ci return ret; 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci ret = mei_cldev_register_rx_cb(cldev, iwl_mei_rx); 131362306a36Sopenharmony_ci if (ret) { 131462306a36Sopenharmony_ci dev_err(&cldev->dev, 131562306a36Sopenharmony_ci "failed to register to the rx cb: %d\n", ret); 131662306a36Sopenharmony_ci mei_cldev_disable(cldev); 131762306a36Sopenharmony_ci return ret; 131862306a36Sopenharmony_ci } 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci return 0; 132162306a36Sopenharmony_ci} 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_cistruct iwl_mei_nvm *iwl_mei_get_nvm(void) 132462306a36Sopenharmony_ci{ 132562306a36Sopenharmony_ci struct iwl_mei_nvm *nvm = NULL; 132662306a36Sopenharmony_ci struct iwl_mei *mei; 132762306a36Sopenharmony_ci int ret; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 133062306a36Sopenharmony_ci 133162306a36Sopenharmony_ci if (!iwl_mei_is_connected()) 133262306a36Sopenharmony_ci goto out; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci if (!mei) 133762306a36Sopenharmony_ci goto out; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci ret = iwl_mei_send_sap_msg(iwl_mei_global_cldev, 134062306a36Sopenharmony_ci SAP_MSG_NOTIF_GET_NVM); 134162306a36Sopenharmony_ci if (ret) 134262306a36Sopenharmony_ci goto out; 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci ret = wait_event_timeout(mei->get_nvm_wq, mei->nvm, 2 * HZ); 134762306a36Sopenharmony_ci if (!ret) 134862306a36Sopenharmony_ci return NULL; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci if (!iwl_mei_is_connected()) 135362306a36Sopenharmony_ci goto out; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci if (!mei) 135862306a36Sopenharmony_ci goto out; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci if (mei->nvm) 136162306a36Sopenharmony_ci nvm = kmemdup(mei->nvm, sizeof(*mei->nvm), GFP_KERNEL); 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ciout: 136462306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 136562306a36Sopenharmony_ci return nvm; 136662306a36Sopenharmony_ci} 136762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_get_nvm); 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci#define IWL_MEI_PLDR_NUM_RETRIES 3 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ciint iwl_mei_pldr_req(void) 137262306a36Sopenharmony_ci{ 137362306a36Sopenharmony_ci struct iwl_mei *mei; 137462306a36Sopenharmony_ci int ret; 137562306a36Sopenharmony_ci struct iwl_sap_pldr_data msg = { 137662306a36Sopenharmony_ci .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_PLDR), 137762306a36Sopenharmony_ci .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)), 137862306a36Sopenharmony_ci }; 137962306a36Sopenharmony_ci int i; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci /* In case we didn't have a bind */ 138462306a36Sopenharmony_ci if (!iwl_mei_is_connected()) { 138562306a36Sopenharmony_ci ret = 0; 138662306a36Sopenharmony_ci goto out; 138762306a36Sopenharmony_ci } 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci if (!mei) { 139262306a36Sopenharmony_ci ret = -ENODEV; 139362306a36Sopenharmony_ci goto out; 139462306a36Sopenharmony_ci } 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci if (!mei->amt_enabled) { 139762306a36Sopenharmony_ci ret = 0; 139862306a36Sopenharmony_ci goto out; 139962306a36Sopenharmony_ci } 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci for (i = 0; i < IWL_MEI_PLDR_NUM_RETRIES; i++) { 140262306a36Sopenharmony_ci ret = iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr); 140362306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 140462306a36Sopenharmony_ci if (ret) 140562306a36Sopenharmony_ci return ret; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci ret = wait_event_timeout(mei->pldr_wq, mei->pldr_active, HZ / 2); 140862306a36Sopenharmony_ci if (ret) 140962306a36Sopenharmony_ci break; 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci /* Take the mutex for the next iteration */ 141262306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci if (ret) 141662306a36Sopenharmony_ci return 0; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci ret = -ETIMEDOUT; 141962306a36Sopenharmony_ciout: 142062306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 142162306a36Sopenharmony_ci return ret; 142262306a36Sopenharmony_ci} 142362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_pldr_req); 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ciint iwl_mei_get_ownership(void) 142662306a36Sopenharmony_ci{ 142762306a36Sopenharmony_ci struct iwl_mei *mei; 142862306a36Sopenharmony_ci int ret; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci /* In case we didn't have a bind */ 143362306a36Sopenharmony_ci if (!iwl_mei_is_connected()) { 143462306a36Sopenharmony_ci ret = 0; 143562306a36Sopenharmony_ci goto out; 143662306a36Sopenharmony_ci } 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci if (!mei) { 144162306a36Sopenharmony_ci ret = -ENODEV; 144262306a36Sopenharmony_ci goto out; 144362306a36Sopenharmony_ci } 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci if (!mei->amt_enabled) { 144662306a36Sopenharmony_ci ret = 0; 144762306a36Sopenharmony_ci goto out; 144862306a36Sopenharmony_ci } 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci if (mei->got_ownership) { 145162306a36Sopenharmony_ci ret = 0; 145262306a36Sopenharmony_ci goto out; 145362306a36Sopenharmony_ci } 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci ret = iwl_mei_send_sap_msg(mei->cldev, 145662306a36Sopenharmony_ci SAP_MSG_NOTIF_HOST_ASKS_FOR_NIC_OWNERSHIP); 145762306a36Sopenharmony_ci if (ret) 145862306a36Sopenharmony_ci goto out; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci ret = wait_event_timeout(mei->get_ownership_wq, 146362306a36Sopenharmony_ci mei->got_ownership, HZ / 2); 146462306a36Sopenharmony_ci if (!ret) { 146562306a36Sopenharmony_ci schedule_delayed_work(&mei->ownership_dwork, 146662306a36Sopenharmony_ci MEI_OWNERSHIP_RETAKE_TIMEOUT_MS); 146762306a36Sopenharmony_ci return -ETIMEDOUT; 146862306a36Sopenharmony_ci } 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci return 0; 147162306a36Sopenharmony_ciout: 147262306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 147362306a36Sopenharmony_ci return ret; 147462306a36Sopenharmony_ci} 147562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_get_ownership); 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_civoid iwl_mei_alive_notif(bool success) 147862306a36Sopenharmony_ci{ 147962306a36Sopenharmony_ci struct iwl_mei *mei; 148062306a36Sopenharmony_ci struct iwl_sap_pldr_end_data msg = { 148162306a36Sopenharmony_ci .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_PLDR_END), 148262306a36Sopenharmony_ci .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)), 148362306a36Sopenharmony_ci .status = success ? cpu_to_le32(SAP_PLDR_STATUS_SUCCESS) : 148462306a36Sopenharmony_ci cpu_to_le32(SAP_PLDR_STATUS_FAILURE), 148562306a36Sopenharmony_ci }; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci if (!iwl_mei_is_connected()) 149062306a36Sopenharmony_ci goto out; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); 149362306a36Sopenharmony_ci if (!mei || !mei->pldr_active) 149462306a36Sopenharmony_ci goto out; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci mei->pldr_active = false; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr); 149962306a36Sopenharmony_ciout: 150062306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 150162306a36Sopenharmony_ci} 150262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_alive_notif); 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_civoid iwl_mei_host_associated(const struct iwl_mei_conn_info *conn_info, 150562306a36Sopenharmony_ci const struct iwl_mei_colloc_info *colloc_info) 150662306a36Sopenharmony_ci{ 150762306a36Sopenharmony_ci struct iwl_sap_notif_host_link_up msg = { 150862306a36Sopenharmony_ci .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_HOST_LINK_UP), 150962306a36Sopenharmony_ci .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)), 151062306a36Sopenharmony_ci .conn_info = { 151162306a36Sopenharmony_ci .ssid_len = cpu_to_le32(conn_info->ssid_len), 151262306a36Sopenharmony_ci .channel = conn_info->channel, 151362306a36Sopenharmony_ci .band = conn_info->band, 151462306a36Sopenharmony_ci .pairwise_cipher = cpu_to_le32(conn_info->pairwise_cipher), 151562306a36Sopenharmony_ci .auth_mode = cpu_to_le32(conn_info->auth_mode), 151662306a36Sopenharmony_ci }, 151762306a36Sopenharmony_ci }; 151862306a36Sopenharmony_ci struct iwl_mei *mei; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci if (conn_info->ssid_len > ARRAY_SIZE(msg.conn_info.ssid)) 152162306a36Sopenharmony_ci return; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci memcpy(msg.conn_info.ssid, conn_info->ssid, conn_info->ssid_len); 152462306a36Sopenharmony_ci memcpy(msg.conn_info.bssid, conn_info->bssid, ETH_ALEN); 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci if (colloc_info) { 152762306a36Sopenharmony_ci msg.colloc_channel = colloc_info->channel; 152862306a36Sopenharmony_ci msg.colloc_band = colloc_info->channel <= 14 ? 0 : 1; 152962306a36Sopenharmony_ci memcpy(msg.colloc_bssid, colloc_info->bssid, ETH_ALEN); 153062306a36Sopenharmony_ci } 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci if (!iwl_mei_is_connected()) 153562306a36Sopenharmony_ci goto out; 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci if (!mei || !mei->amt_enabled) 154062306a36Sopenharmony_ci goto out; 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr); 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ciout: 154562306a36Sopenharmony_ci kfree(iwl_mei_cache.conn_info); 154662306a36Sopenharmony_ci iwl_mei_cache.conn_info = 154762306a36Sopenharmony_ci kmemdup(&msg.conn_info, sizeof(msg.conn_info), GFP_KERNEL); 154862306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 154962306a36Sopenharmony_ci} 155062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_host_associated); 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_civoid iwl_mei_host_disassociated(void) 155362306a36Sopenharmony_ci{ 155462306a36Sopenharmony_ci struct iwl_mei *mei; 155562306a36Sopenharmony_ci struct iwl_sap_notif_host_link_down msg = { 155662306a36Sopenharmony_ci .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_HOST_LINK_DOWN), 155762306a36Sopenharmony_ci .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)), 155862306a36Sopenharmony_ci .type = HOST_LINK_DOWN_TYPE_TEMPORARY, 155962306a36Sopenharmony_ci }; 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci if (!iwl_mei_is_connected()) 156462306a36Sopenharmony_ci goto out; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci if (!mei || !mei->amt_enabled) 156962306a36Sopenharmony_ci goto out; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr); 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ciout: 157462306a36Sopenharmony_ci kfree(iwl_mei_cache.conn_info); 157562306a36Sopenharmony_ci iwl_mei_cache.conn_info = NULL; 157662306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 157762306a36Sopenharmony_ci} 157862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_host_disassociated); 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_civoid iwl_mei_set_rfkill_state(bool hw_rfkill, bool sw_rfkill) 158162306a36Sopenharmony_ci{ 158262306a36Sopenharmony_ci struct iwl_mei *mei; 158362306a36Sopenharmony_ci u32 rfkill_state = 0; 158462306a36Sopenharmony_ci struct iwl_sap_msg_dw msg = { 158562306a36Sopenharmony_ci .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_RADIO_STATE), 158662306a36Sopenharmony_ci .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)), 158762306a36Sopenharmony_ci }; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci if (!sw_rfkill) 159062306a36Sopenharmony_ci rfkill_state |= SAP_SW_RFKILL_DEASSERTED; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci if (!hw_rfkill) 159362306a36Sopenharmony_ci rfkill_state |= SAP_HW_RFKILL_DEASSERTED; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ci if (!iwl_mei_is_connected()) 159862306a36Sopenharmony_ci goto out; 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci msg.val = cpu_to_le32(rfkill_state); 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci if (!mei || !mei->amt_enabled) 160562306a36Sopenharmony_ci goto out; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr); 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ciout: 161062306a36Sopenharmony_ci iwl_mei_cache.rf_kill = rfkill_state; 161162306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 161262306a36Sopenharmony_ci} 161362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_set_rfkill_state); 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_civoid iwl_mei_set_nic_info(const u8 *mac_address, const u8 *nvm_address) 161662306a36Sopenharmony_ci{ 161762306a36Sopenharmony_ci struct iwl_mei *mei; 161862306a36Sopenharmony_ci struct iwl_sap_notif_host_nic_info msg = { 161962306a36Sopenharmony_ci .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_NIC_INFO), 162062306a36Sopenharmony_ci .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)), 162162306a36Sopenharmony_ci }; 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci if (!iwl_mei_is_connected()) 162662306a36Sopenharmony_ci goto out; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci ether_addr_copy(msg.mac_address, mac_address); 162962306a36Sopenharmony_ci ether_addr_copy(msg.nvm_address, nvm_address); 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci if (!mei || !mei->amt_enabled) 163462306a36Sopenharmony_ci goto out; 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr); 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ciout: 163962306a36Sopenharmony_ci ether_addr_copy(iwl_mei_cache.mac_address, mac_address); 164062306a36Sopenharmony_ci ether_addr_copy(iwl_mei_cache.nvm_address, nvm_address); 164162306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 164262306a36Sopenharmony_ci} 164362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_set_nic_info); 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_civoid iwl_mei_set_country_code(u16 mcc) 164662306a36Sopenharmony_ci{ 164762306a36Sopenharmony_ci struct iwl_mei *mei; 164862306a36Sopenharmony_ci struct iwl_sap_notif_country_code msg = { 164962306a36Sopenharmony_ci .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_COUNTRY_CODE), 165062306a36Sopenharmony_ci .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)), 165162306a36Sopenharmony_ci .mcc = cpu_to_le16(mcc), 165262306a36Sopenharmony_ci }; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci if (!iwl_mei_is_connected()) 165762306a36Sopenharmony_ci goto out; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci if (!mei || !mei->amt_enabled) 166262306a36Sopenharmony_ci goto out; 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr); 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ciout: 166762306a36Sopenharmony_ci iwl_mei_cache.mcc = mcc; 166862306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 166962306a36Sopenharmony_ci} 167062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_set_country_code); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_civoid iwl_mei_set_power_limit(const __le16 *power_limit) 167362306a36Sopenharmony_ci{ 167462306a36Sopenharmony_ci struct iwl_mei *mei; 167562306a36Sopenharmony_ci struct iwl_sap_notif_sar_limits msg = { 167662306a36Sopenharmony_ci .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_SAR_LIMITS), 167762306a36Sopenharmony_ci .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)), 167862306a36Sopenharmony_ci }; 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci if (!iwl_mei_is_connected()) 168362306a36Sopenharmony_ci goto out; 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci if (!mei || !mei->amt_enabled) 168862306a36Sopenharmony_ci goto out; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci memcpy(msg.sar_chain_info_table, power_limit, sizeof(msg.sar_chain_info_table)); 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr); 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ciout: 169562306a36Sopenharmony_ci kfree(iwl_mei_cache.power_limit); 169662306a36Sopenharmony_ci iwl_mei_cache.power_limit = kmemdup(power_limit, 169762306a36Sopenharmony_ci sizeof(msg.sar_chain_info_table), GFP_KERNEL); 169862306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 169962306a36Sopenharmony_ci} 170062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_set_power_limit); 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_civoid iwl_mei_set_netdev(struct net_device *netdev) 170362306a36Sopenharmony_ci{ 170462306a36Sopenharmony_ci struct iwl_mei *mei; 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci if (!iwl_mei_is_connected()) { 170962306a36Sopenharmony_ci rcu_assign_pointer(iwl_mei_cache.netdev, netdev); 171062306a36Sopenharmony_ci goto out; 171162306a36Sopenharmony_ci } 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_ci mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci if (!mei) 171662306a36Sopenharmony_ci goto out; 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci if (!netdev) { 171962306a36Sopenharmony_ci struct net_device *dev = 172062306a36Sopenharmony_ci rcu_dereference_protected(iwl_mei_cache.netdev, 172162306a36Sopenharmony_ci lockdep_is_held(&iwl_mei_mutex)); 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci if (!dev) 172462306a36Sopenharmony_ci goto out; 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci netdev_rx_handler_unregister(dev); 172762306a36Sopenharmony_ci } 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci rcu_assign_pointer(iwl_mei_cache.netdev, netdev); 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci if (netdev && mei->amt_enabled) 173262306a36Sopenharmony_ci netdev_rx_handler_register(netdev, iwl_mei_rx_handler, mei); 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ciout: 173562306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 173662306a36Sopenharmony_ci} 173762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_set_netdev); 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_civoid iwl_mei_device_state(bool up) 174062306a36Sopenharmony_ci{ 174162306a36Sopenharmony_ci struct iwl_mei *mei; 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci if (!iwl_mei_is_connected()) 174662306a36Sopenharmony_ci goto out; 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci mei = mei_cldev_get_drvdata(iwl_mei_global_cldev); 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci if (!mei) 175162306a36Sopenharmony_ci goto out; 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci mei->device_down = !up; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ci if (up || !mei->csme_taking_ownership) 175662306a36Sopenharmony_ci goto out; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci iwl_mei_send_sap_msg(mei->cldev, 175962306a36Sopenharmony_ci SAP_MSG_NOTIF_CSME_OWNERSHIP_CONFIRMED); 176062306a36Sopenharmony_ci mei->csme_taking_ownership = false; 176162306a36Sopenharmony_ci schedule_delayed_work(&mei->ownership_dwork, 176262306a36Sopenharmony_ci MEI_OWNERSHIP_RETAKE_TIMEOUT_MS); 176362306a36Sopenharmony_ciout: 176462306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 176562306a36Sopenharmony_ci} 176662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_device_state); 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ciint iwl_mei_register(void *priv, const struct iwl_mei_ops *ops) 176962306a36Sopenharmony_ci{ 177062306a36Sopenharmony_ci int ret; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci /* 177362306a36Sopenharmony_ci * We must have a non-NULL priv pointer to not crash when there are 177462306a36Sopenharmony_ci * multiple WiFi devices. 177562306a36Sopenharmony_ci */ 177662306a36Sopenharmony_ci if (!priv) 177762306a36Sopenharmony_ci return -EINVAL; 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci /* do not allow registration if someone else already registered */ 178262306a36Sopenharmony_ci if (iwl_mei_cache.priv || iwl_mei_cache.ops) { 178362306a36Sopenharmony_ci ret = -EBUSY; 178462306a36Sopenharmony_ci goto out; 178562306a36Sopenharmony_ci } 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci iwl_mei_cache.priv = priv; 178862306a36Sopenharmony_ci iwl_mei_cache.ops = ops; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci if (iwl_mei_global_cldev) { 179162306a36Sopenharmony_ci struct iwl_mei *mei = 179262306a36Sopenharmony_ci mei_cldev_get_drvdata(iwl_mei_global_cldev); 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci /* we have already a SAP connection */ 179562306a36Sopenharmony_ci if (iwl_mei_is_connected()) { 179662306a36Sopenharmony_ci if (mei->amt_enabled) 179762306a36Sopenharmony_ci iwl_mei_send_sap_msg(mei->cldev, 179862306a36Sopenharmony_ci SAP_MSG_NOTIF_WIFIDR_UP); 179962306a36Sopenharmony_ci ops->rfkill(priv, mei->link_prot_state, false); 180062306a36Sopenharmony_ci } 180162306a36Sopenharmony_ci } 180262306a36Sopenharmony_ci ret = 0; 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ciout: 180562306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 180662306a36Sopenharmony_ci return ret; 180762306a36Sopenharmony_ci} 180862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_register); 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_civoid iwl_mei_start_unregister(void) 181162306a36Sopenharmony_ci{ 181262306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 181362306a36Sopenharmony_ci 181462306a36Sopenharmony_ci /* At this point, the wifi driver should have removed the netdev */ 181562306a36Sopenharmony_ci if (rcu_access_pointer(iwl_mei_cache.netdev)) 181662306a36Sopenharmony_ci pr_err("Still had a netdev pointer set upon unregister\n"); 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci kfree(iwl_mei_cache.conn_info); 181962306a36Sopenharmony_ci iwl_mei_cache.conn_info = NULL; 182062306a36Sopenharmony_ci kfree(iwl_mei_cache.power_limit); 182162306a36Sopenharmony_ci iwl_mei_cache.power_limit = NULL; 182262306a36Sopenharmony_ci iwl_mei_cache.ops = NULL; 182362306a36Sopenharmony_ci /* leave iwl_mei_cache.priv non-NULL to prevent any new registration */ 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 182662306a36Sopenharmony_ci} 182762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_start_unregister); 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_civoid iwl_mei_unregister_complete(void) 183062306a36Sopenharmony_ci{ 183162306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci iwl_mei_cache.priv = NULL; 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci if (iwl_mei_global_cldev) { 183662306a36Sopenharmony_ci struct iwl_mei *mei = 183762306a36Sopenharmony_ci mei_cldev_get_drvdata(iwl_mei_global_cldev); 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci if (mei->amt_enabled) 184062306a36Sopenharmony_ci iwl_mei_send_sap_msg(mei->cldev, 184162306a36Sopenharmony_ci SAP_MSG_NOTIF_WIFIDR_DOWN); 184262306a36Sopenharmony_ci mei->got_ownership = false; 184362306a36Sopenharmony_ci } 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 184662306a36Sopenharmony_ci} 184762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iwl_mei_unregister_complete); 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_DEBUG_FS) 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_cistatic ssize_t 185262306a36Sopenharmony_ciiwl_mei_dbgfs_send_start_message_write(struct file *file, 185362306a36Sopenharmony_ci const char __user *user_buf, 185462306a36Sopenharmony_ci size_t count, loff_t *ppos) 185562306a36Sopenharmony_ci{ 185662306a36Sopenharmony_ci int ret; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci if (!iwl_mei_global_cldev) { 186162306a36Sopenharmony_ci ret = -ENODEV; 186262306a36Sopenharmony_ci goto out; 186362306a36Sopenharmony_ci } 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci ret = iwl_mei_send_start(iwl_mei_global_cldev); 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ciout: 186862306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 186962306a36Sopenharmony_ci return ret ?: count; 187062306a36Sopenharmony_ci} 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_cistatic const struct file_operations iwl_mei_dbgfs_send_start_message_ops = { 187362306a36Sopenharmony_ci .write = iwl_mei_dbgfs_send_start_message_write, 187462306a36Sopenharmony_ci .open = simple_open, 187562306a36Sopenharmony_ci .llseek = default_llseek, 187662306a36Sopenharmony_ci}; 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_cistatic ssize_t iwl_mei_dbgfs_req_ownership_write(struct file *file, 187962306a36Sopenharmony_ci const char __user *user_buf, 188062306a36Sopenharmony_ci size_t count, loff_t *ppos) 188162306a36Sopenharmony_ci{ 188262306a36Sopenharmony_ci iwl_mei_get_ownership(); 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci return count; 188562306a36Sopenharmony_ci} 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_cistatic const struct file_operations iwl_mei_dbgfs_req_ownership_ops = { 188862306a36Sopenharmony_ci .write = iwl_mei_dbgfs_req_ownership_write, 188962306a36Sopenharmony_ci .open = simple_open, 189062306a36Sopenharmony_ci .llseek = default_llseek, 189162306a36Sopenharmony_ci}; 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_cistatic void iwl_mei_dbgfs_register(struct iwl_mei *mei) 189462306a36Sopenharmony_ci{ 189562306a36Sopenharmony_ci mei->dbgfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL); 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci if (!mei->dbgfs_dir) 189862306a36Sopenharmony_ci return; 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci debugfs_create_ulong("status", S_IRUSR, 190162306a36Sopenharmony_ci mei->dbgfs_dir, &iwl_mei_status); 190262306a36Sopenharmony_ci debugfs_create_file("send_start_message", S_IWUSR, mei->dbgfs_dir, 190362306a36Sopenharmony_ci mei, &iwl_mei_dbgfs_send_start_message_ops); 190462306a36Sopenharmony_ci debugfs_create_file("req_ownership", S_IWUSR, mei->dbgfs_dir, 190562306a36Sopenharmony_ci mei, &iwl_mei_dbgfs_req_ownership_ops); 190662306a36Sopenharmony_ci} 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_cistatic void iwl_mei_dbgfs_unregister(struct iwl_mei *mei) 190962306a36Sopenharmony_ci{ 191062306a36Sopenharmony_ci debugfs_remove_recursive(mei->dbgfs_dir); 191162306a36Sopenharmony_ci mei->dbgfs_dir = NULL; 191262306a36Sopenharmony_ci} 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci#else 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_cistatic void iwl_mei_dbgfs_register(struct iwl_mei *mei) {} 191762306a36Sopenharmony_cistatic void iwl_mei_dbgfs_unregister(struct iwl_mei *mei) {} 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci#endif /* CONFIG_DEBUG_FS */ 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_cistatic void iwl_mei_ownership_dwork(struct work_struct *wk) 192262306a36Sopenharmony_ci{ 192362306a36Sopenharmony_ci iwl_mei_get_ownership(); 192462306a36Sopenharmony_ci} 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci#define ALLOC_SHARED_MEM_RETRY_MAX_NUM 3 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci/* 192962306a36Sopenharmony_ci * iwl_mei_probe - the probe function called by the mei bus enumeration 193062306a36Sopenharmony_ci * 193162306a36Sopenharmony_ci * This allocates the data needed by iwlmei and sets a pointer to this data 193262306a36Sopenharmony_ci * into the mei_cl_device's drvdata. 193362306a36Sopenharmony_ci * It starts the SAP protocol by sending the SAP_ME_MSG_START without 193462306a36Sopenharmony_ci * waiting for the answer. The answer will be caught later by the Rx callback. 193562306a36Sopenharmony_ci */ 193662306a36Sopenharmony_cistatic int iwl_mei_probe(struct mei_cl_device *cldev, 193762306a36Sopenharmony_ci const struct mei_cl_device_id *id) 193862306a36Sopenharmony_ci{ 193962306a36Sopenharmony_ci int alloc_retry = ALLOC_SHARED_MEM_RETRY_MAX_NUM; 194062306a36Sopenharmony_ci struct iwl_mei *mei; 194162306a36Sopenharmony_ci int ret; 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci mei = devm_kzalloc(&cldev->dev, sizeof(*mei), GFP_KERNEL); 194462306a36Sopenharmony_ci if (!mei) 194562306a36Sopenharmony_ci return -ENOMEM; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci init_waitqueue_head(&mei->get_nvm_wq); 194862306a36Sopenharmony_ci INIT_WORK(&mei->send_csa_msg_wk, iwl_mei_send_csa_msg_wk); 194962306a36Sopenharmony_ci INIT_DELAYED_WORK(&mei->csa_throttle_end_wk, 195062306a36Sopenharmony_ci iwl_mei_csa_throttle_end_wk); 195162306a36Sopenharmony_ci init_waitqueue_head(&mei->get_ownership_wq); 195262306a36Sopenharmony_ci init_waitqueue_head(&mei->pldr_wq); 195362306a36Sopenharmony_ci spin_lock_init(&mei->data_q_lock); 195462306a36Sopenharmony_ci INIT_WORK(&mei->netdev_work, iwl_mei_netdev_work); 195562306a36Sopenharmony_ci INIT_DELAYED_WORK(&mei->ownership_dwork, iwl_mei_ownership_dwork); 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci mei_cldev_set_drvdata(cldev, mei); 195862306a36Sopenharmony_ci mei->cldev = cldev; 195962306a36Sopenharmony_ci mei->device_down = true; 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci do { 196262306a36Sopenharmony_ci ret = iwl_mei_alloc_shared_mem(cldev); 196362306a36Sopenharmony_ci if (!ret) 196462306a36Sopenharmony_ci break; 196562306a36Sopenharmony_ci /* 196662306a36Sopenharmony_ci * The CSME firmware needs to boot the internal WLAN client. 196762306a36Sopenharmony_ci * This can take time in certain configurations (usually 196862306a36Sopenharmony_ci * upon resume and when the whole CSME firmware is shut down 196962306a36Sopenharmony_ci * during suspend). 197062306a36Sopenharmony_ci * 197162306a36Sopenharmony_ci * Wait a bit before retrying and hope we'll succeed next time. 197262306a36Sopenharmony_ci */ 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci dev_dbg(&cldev->dev, 197562306a36Sopenharmony_ci "Couldn't allocate the shared memory: %d, attempt %d / %d\n", 197662306a36Sopenharmony_ci ret, alloc_retry, ALLOC_SHARED_MEM_RETRY_MAX_NUM); 197762306a36Sopenharmony_ci msleep(100); 197862306a36Sopenharmony_ci alloc_retry--; 197962306a36Sopenharmony_ci } while (alloc_retry); 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci if (ret) { 198262306a36Sopenharmony_ci dev_err(&cldev->dev, "Couldn't allocate the shared memory: %d\n", 198362306a36Sopenharmony_ci ret); 198462306a36Sopenharmony_ci goto free; 198562306a36Sopenharmony_ci } 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci iwl_mei_init_shared_mem(mei); 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_ci ret = iwl_mei_enable(cldev); 199062306a36Sopenharmony_ci if (ret) 199162306a36Sopenharmony_ci goto free_shared_mem; 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci iwl_mei_dbgfs_register(mei); 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci /* 199662306a36Sopenharmony_ci * We now have a Rx function in place, start the SAP protocol 199762306a36Sopenharmony_ci * we expect to get the SAP_ME_MSG_START_OK response later on. 199862306a36Sopenharmony_ci */ 199962306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 200062306a36Sopenharmony_ci ret = iwl_mei_send_start(cldev); 200162306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 200262306a36Sopenharmony_ci if (ret) 200362306a36Sopenharmony_ci goto debugfs_unregister; 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci /* must be last */ 200662306a36Sopenharmony_ci iwl_mei_global_cldev = cldev; 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci return 0; 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_cidebugfs_unregister: 201162306a36Sopenharmony_ci iwl_mei_dbgfs_unregister(mei); 201262306a36Sopenharmony_ci mei_cldev_disable(cldev); 201362306a36Sopenharmony_cifree_shared_mem: 201462306a36Sopenharmony_ci iwl_mei_free_shared_mem(cldev); 201562306a36Sopenharmony_cifree: 201662306a36Sopenharmony_ci mei_cldev_set_drvdata(cldev, NULL); 201762306a36Sopenharmony_ci devm_kfree(&cldev->dev, mei); 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci return ret; 202062306a36Sopenharmony_ci} 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci#define SEND_SAP_MAX_WAIT_ITERATION 10 202362306a36Sopenharmony_ci#define IWLMEI_DEVICE_DOWN_WAIT_ITERATION 50 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_cistatic void iwl_mei_remove(struct mei_cl_device *cldev) 202662306a36Sopenharmony_ci{ 202762306a36Sopenharmony_ci struct iwl_mei *mei = mei_cldev_get_drvdata(cldev); 202862306a36Sopenharmony_ci int i; 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci /* 203162306a36Sopenharmony_ci * We are being removed while the bus is active, it means we are 203262306a36Sopenharmony_ci * going to suspend/ shutdown, so the NIC will disappear. 203362306a36Sopenharmony_ci */ 203462306a36Sopenharmony_ci if (mei_cldev_enabled(cldev) && iwl_mei_cache.ops) { 203562306a36Sopenharmony_ci unsigned int iter = IWLMEI_DEVICE_DOWN_WAIT_ITERATION; 203662306a36Sopenharmony_ci bool down = false; 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci /* 203962306a36Sopenharmony_ci * In case of suspend, wait for the mac to stop and don't remove 204062306a36Sopenharmony_ci * the interface. This will allow the interface to come back 204162306a36Sopenharmony_ci * on resume. 204262306a36Sopenharmony_ci */ 204362306a36Sopenharmony_ci while (!down && iter--) { 204462306a36Sopenharmony_ci mdelay(1); 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 204762306a36Sopenharmony_ci down = mei->device_down; 204862306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 204962306a36Sopenharmony_ci } 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci if (!down) 205262306a36Sopenharmony_ci iwl_mei_cache.ops->nic_stolen(iwl_mei_cache.priv); 205362306a36Sopenharmony_ci } 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci if (rcu_access_pointer(iwl_mei_cache.netdev)) { 205662306a36Sopenharmony_ci struct net_device *dev; 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci /* 205962306a36Sopenharmony_ci * First take rtnl and only then the mutex to avoid an ABBA 206062306a36Sopenharmony_ci * with iwl_mei_set_netdev() 206162306a36Sopenharmony_ci */ 206262306a36Sopenharmony_ci rtnl_lock(); 206362306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci /* 206662306a36Sopenharmony_ci * If we are suspending and the wifi driver hasn't removed it's netdev 206762306a36Sopenharmony_ci * yet, do it now. In any case, don't change the cache.netdev pointer. 206862306a36Sopenharmony_ci */ 206962306a36Sopenharmony_ci dev = rcu_dereference_protected(iwl_mei_cache.netdev, 207062306a36Sopenharmony_ci lockdep_is_held(&iwl_mei_mutex)); 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci netdev_rx_handler_unregister(dev); 207362306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 207462306a36Sopenharmony_ci rtnl_unlock(); 207562306a36Sopenharmony_ci } 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci /* Tell CSME that we are going down so that it won't access the 208062306a36Sopenharmony_ci * memory anymore, make sure this message goes through immediately. 208162306a36Sopenharmony_ci */ 208262306a36Sopenharmony_ci mei->csa_throttled = false; 208362306a36Sopenharmony_ci iwl_mei_send_sap_msg(mei->cldev, 208462306a36Sopenharmony_ci SAP_MSG_NOTIF_HOST_GOES_DOWN); 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci for (i = 0; i < SEND_SAP_MAX_WAIT_ITERATION; i++) { 208762306a36Sopenharmony_ci if (!iwl_mei_host_to_me_data_pending(mei)) 208862306a36Sopenharmony_ci break; 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci msleep(20); 209162306a36Sopenharmony_ci } 209262306a36Sopenharmony_ci 209362306a36Sopenharmony_ci /* If we couldn't make sure that CSME saw the HOST_GOES_DOWN 209462306a36Sopenharmony_ci * message, it means that it will probably keep reading memory 209562306a36Sopenharmony_ci * that we are going to unmap and free, expect IOMMU error 209662306a36Sopenharmony_ci * messages. 209762306a36Sopenharmony_ci */ 209862306a36Sopenharmony_ci if (i == SEND_SAP_MAX_WAIT_ITERATION) 209962306a36Sopenharmony_ci dev_err(&mei->cldev->dev, 210062306a36Sopenharmony_ci "Couldn't get ACK from CSME on HOST_GOES_DOWN message\n"); 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci /* 210562306a36Sopenharmony_ci * This looks strange, but this lock is taken here to make sure that 210662306a36Sopenharmony_ci * iwl_mei_add_data_to_ring called from the Tx path sees that we 210762306a36Sopenharmony_ci * clear the IWL_MEI_STATUS_SAP_CONNECTED bit. 210862306a36Sopenharmony_ci * Rx isn't a problem because the rx_handler can't be called after 210962306a36Sopenharmony_ci * having been unregistered. 211062306a36Sopenharmony_ci */ 211162306a36Sopenharmony_ci spin_lock_bh(&mei->data_q_lock); 211262306a36Sopenharmony_ci clear_bit(IWL_MEI_STATUS_SAP_CONNECTED, &iwl_mei_status); 211362306a36Sopenharmony_ci spin_unlock_bh(&mei->data_q_lock); 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci if (iwl_mei_cache.ops) 211662306a36Sopenharmony_ci iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false, false); 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci /* 211962306a36Sopenharmony_ci * mei_cldev_disable will return only after all the MEI Rx is done. 212062306a36Sopenharmony_ci * It must be called when iwl_mei_mutex is *not* held, since it waits 212162306a36Sopenharmony_ci * for our Rx handler to complete. 212262306a36Sopenharmony_ci * After it returns, no new Rx will start. 212362306a36Sopenharmony_ci */ 212462306a36Sopenharmony_ci mei_cldev_disable(cldev); 212562306a36Sopenharmony_ci 212662306a36Sopenharmony_ci /* 212762306a36Sopenharmony_ci * Since the netdev was already removed and the netdev's removal 212862306a36Sopenharmony_ci * includes a call to synchronize_net() so that we know there won't be 212962306a36Sopenharmony_ci * any new Rx that will trigger the following workers. 213062306a36Sopenharmony_ci */ 213162306a36Sopenharmony_ci cancel_work_sync(&mei->send_csa_msg_wk); 213262306a36Sopenharmony_ci cancel_delayed_work_sync(&mei->csa_throttle_end_wk); 213362306a36Sopenharmony_ci cancel_work_sync(&mei->netdev_work); 213462306a36Sopenharmony_ci cancel_delayed_work_sync(&mei->ownership_dwork); 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci /* 213762306a36Sopenharmony_ci * If someone waits for the ownership, let him know that we are going 213862306a36Sopenharmony_ci * down and that we are not connected anymore. He'll be able to take 213962306a36Sopenharmony_ci * the device. 214062306a36Sopenharmony_ci */ 214162306a36Sopenharmony_ci wake_up_all(&mei->get_ownership_wq); 214262306a36Sopenharmony_ci wake_up_all(&mei->pldr_wq); 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci mutex_lock(&iwl_mei_mutex); 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci iwl_mei_global_cldev = NULL; 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci wake_up_all(&mei->get_nvm_wq); 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci iwl_mei_free_shared_mem(cldev); 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci iwl_mei_dbgfs_unregister(mei); 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci mei_cldev_set_drvdata(cldev, NULL); 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci kfree(mei->nvm); 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci kfree(rcu_access_pointer(mei->filters)); 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci devm_kfree(&cldev->dev, mei); 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci mutex_unlock(&iwl_mei_mutex); 216362306a36Sopenharmony_ci} 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_cistatic const struct mei_cl_device_id iwl_mei_tbl[] = { 216662306a36Sopenharmony_ci { 216762306a36Sopenharmony_ci .name = KBUILD_MODNAME, 216862306a36Sopenharmony_ci .uuid = MEI_WLAN_UUID, 216962306a36Sopenharmony_ci .version = MEI_CL_VERSION_ANY, 217062306a36Sopenharmony_ci }, 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci /* required last entry */ 217362306a36Sopenharmony_ci { } 217462306a36Sopenharmony_ci}; 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci/* 217762306a36Sopenharmony_ci * Do not export the device table because this module is loaded by 217862306a36Sopenharmony_ci * iwlwifi's dependency. 217962306a36Sopenharmony_ci */ 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_cistatic struct mei_cl_driver iwl_mei_cl_driver = { 218262306a36Sopenharmony_ci .id_table = iwl_mei_tbl, 218362306a36Sopenharmony_ci .name = KBUILD_MODNAME, 218462306a36Sopenharmony_ci .probe = iwl_mei_probe, 218562306a36Sopenharmony_ci .remove = iwl_mei_remove, 218662306a36Sopenharmony_ci}; 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_cimodule_mei_cl_driver(iwl_mei_cl_driver); 2189