12da4366fSEmmanuel Grumbach // SPDX-License-Identifier: GPL-2.0-only
22da4366fSEmmanuel Grumbach /*
3ef030ab1SAvraham Stern * Copyright (C) 2021-2023 Intel Corporation
42da4366fSEmmanuel Grumbach */
52da4366fSEmmanuel Grumbach
62da4366fSEmmanuel Grumbach #include <linux/etherdevice.h>
72da4366fSEmmanuel Grumbach #include <linux/netdevice.h>
82da4366fSEmmanuel Grumbach #include <linux/ieee80211.h>
92da4366fSEmmanuel Grumbach #include <linux/rtnetlink.h>
102da4366fSEmmanuel Grumbach #include <linux/module.h>
112da4366fSEmmanuel Grumbach #include <linux/moduleparam.h>
122da4366fSEmmanuel Grumbach #include <linux/mei_cl_bus.h>
132da4366fSEmmanuel Grumbach #include <linux/rcupdate.h>
144ea7da5fSEmmanuel Grumbach #include <linux/debugfs.h>
152da4366fSEmmanuel Grumbach #include <linux/skbuff.h>
162da4366fSEmmanuel Grumbach #include <linux/wait.h>
172da4366fSEmmanuel Grumbach #include <linux/slab.h>
182da4366fSEmmanuel Grumbach #include <linux/mm.h>
192da4366fSEmmanuel Grumbach
202da4366fSEmmanuel Grumbach #include <net/cfg80211.h>
212da4366fSEmmanuel Grumbach
222da4366fSEmmanuel Grumbach #include "internal.h"
232da4366fSEmmanuel Grumbach #include "iwl-mei.h"
242da4366fSEmmanuel Grumbach #include "trace.h"
252da4366fSEmmanuel Grumbach #include "trace-data.h"
262da4366fSEmmanuel Grumbach #include "sap.h"
272da4366fSEmmanuel Grumbach
282da4366fSEmmanuel Grumbach MODULE_DESCRIPTION("The Intel(R) wireless / CSME firmware interface");
292da4366fSEmmanuel Grumbach MODULE_LICENSE("GPL");
302da4366fSEmmanuel Grumbach
312da4366fSEmmanuel Grumbach #define MEI_WLAN_UUID UUID_LE(0x13280904, 0x7792, 0x4fcb, \
322da4366fSEmmanuel Grumbach 0xa1, 0xaa, 0x5e, 0x70, 0xcb, 0xb1, 0xe8, 0x65)
332da4366fSEmmanuel Grumbach
3406ce23adSAvraham Stern /* After CSME takes ownership, it won't release it for 60 seconds to avoid
3506ce23adSAvraham Stern * frequent ownership transitions.
3606ce23adSAvraham Stern */
3706ce23adSAvraham Stern #define MEI_OWNERSHIP_RETAKE_TIMEOUT_MS msecs_to_jiffies(60000)
3806ce23adSAvraham Stern
392da4366fSEmmanuel Grumbach /*
402da4366fSEmmanuel Grumbach * Since iwlwifi calls iwlmei without any context, hold a pointer to the
412da4366fSEmmanuel Grumbach * mei_cl_device structure here.
422da4366fSEmmanuel Grumbach * Define a mutex that will synchronize all the flows between iwlwifi and
432da4366fSEmmanuel Grumbach * iwlmei.
442da4366fSEmmanuel Grumbach * Note that iwlmei can't have several instances, so it ok to have static
452da4366fSEmmanuel Grumbach * variables here.
462da4366fSEmmanuel Grumbach */
472da4366fSEmmanuel Grumbach static struct mei_cl_device *iwl_mei_global_cldev;
482da4366fSEmmanuel Grumbach static DEFINE_MUTEX(iwl_mei_mutex);
492da4366fSEmmanuel Grumbach static unsigned long iwl_mei_status;
502da4366fSEmmanuel Grumbach
512da4366fSEmmanuel Grumbach enum iwl_mei_status_bits {
522da4366fSEmmanuel Grumbach IWL_MEI_STATUS_SAP_CONNECTED,
532da4366fSEmmanuel Grumbach };
542da4366fSEmmanuel Grumbach
iwl_mei_is_connected(void)552da4366fSEmmanuel Grumbach bool iwl_mei_is_connected(void)
562da4366fSEmmanuel Grumbach {
572da4366fSEmmanuel Grumbach return test_bit(IWL_MEI_STATUS_SAP_CONNECTED, &iwl_mei_status);
582da4366fSEmmanuel Grumbach }
592da4366fSEmmanuel Grumbach EXPORT_SYMBOL_GPL(iwl_mei_is_connected);
602da4366fSEmmanuel Grumbach
612da4366fSEmmanuel Grumbach #define SAP_VERSION 3
622da4366fSEmmanuel Grumbach #define SAP_CONTROL_BLOCK_ID 0x21504153 /* SAP! in ASCII */
632da4366fSEmmanuel Grumbach
642da4366fSEmmanuel Grumbach struct iwl_sap_q_ctrl_blk {
652da4366fSEmmanuel Grumbach __le32 wr_ptr;
662da4366fSEmmanuel Grumbach __le32 rd_ptr;
672da4366fSEmmanuel Grumbach __le32 size;
682da4366fSEmmanuel Grumbach };
692da4366fSEmmanuel Grumbach
702da4366fSEmmanuel Grumbach enum iwl_sap_q_idx {
712da4366fSEmmanuel Grumbach SAP_QUEUE_IDX_NOTIF = 0,
722da4366fSEmmanuel Grumbach SAP_QUEUE_IDX_DATA,
732da4366fSEmmanuel Grumbach SAP_QUEUE_IDX_MAX,
742da4366fSEmmanuel Grumbach };
752da4366fSEmmanuel Grumbach
762da4366fSEmmanuel Grumbach struct iwl_sap_dir {
772da4366fSEmmanuel Grumbach __le32 reserved;
782da4366fSEmmanuel Grumbach struct iwl_sap_q_ctrl_blk q_ctrl_blk[SAP_QUEUE_IDX_MAX];
792da4366fSEmmanuel Grumbach };
802da4366fSEmmanuel Grumbach
812da4366fSEmmanuel Grumbach enum iwl_sap_dir_idx {
822da4366fSEmmanuel Grumbach SAP_DIRECTION_HOST_TO_ME = 0,
832da4366fSEmmanuel Grumbach SAP_DIRECTION_ME_TO_HOST,
842da4366fSEmmanuel Grumbach SAP_DIRECTION_MAX,
852da4366fSEmmanuel Grumbach };
862da4366fSEmmanuel Grumbach
872da4366fSEmmanuel Grumbach struct iwl_sap_shared_mem_ctrl_blk {
882da4366fSEmmanuel Grumbach __le32 sap_id;
892da4366fSEmmanuel Grumbach __le32 size;
902da4366fSEmmanuel Grumbach struct iwl_sap_dir dir[SAP_DIRECTION_MAX];
912da4366fSEmmanuel Grumbach };
922da4366fSEmmanuel Grumbach
932da4366fSEmmanuel Grumbach /*
942da4366fSEmmanuel Grumbach * The shared area has the following layout:
952da4366fSEmmanuel Grumbach *
962da4366fSEmmanuel Grumbach * +-----------------------------------+
972da4366fSEmmanuel Grumbach * |struct iwl_sap_shared_mem_ctrl_blk |
982da4366fSEmmanuel Grumbach * +-----------------------------------+
992da4366fSEmmanuel Grumbach * |Host -> ME data queue |
1002da4366fSEmmanuel Grumbach * +-----------------------------------+
1012da4366fSEmmanuel Grumbach * |Host -> ME notif queue |
1022da4366fSEmmanuel Grumbach * +-----------------------------------+
1032da4366fSEmmanuel Grumbach * |ME -> Host data queue |
1042da4366fSEmmanuel Grumbach * +-----------------------------------+
1052da4366fSEmmanuel Grumbach * |ME -> host notif queue |
1062da4366fSEmmanuel Grumbach * +-----------------------------------+
1072da4366fSEmmanuel Grumbach * |SAP control block id (SAP!) |
1082da4366fSEmmanuel Grumbach * +-----------------------------------+
1092da4366fSEmmanuel Grumbach */
1102da4366fSEmmanuel Grumbach
1112da4366fSEmmanuel Grumbach #define SAP_H2M_DATA_Q_SZ 48256
1122da4366fSEmmanuel Grumbach #define SAP_M2H_DATA_Q_SZ 24128
1132da4366fSEmmanuel Grumbach #define SAP_H2M_NOTIF_Q_SZ 2240
1142da4366fSEmmanuel Grumbach #define SAP_M2H_NOTIF_Q_SZ 62720
1152da4366fSEmmanuel Grumbach
1162da4366fSEmmanuel Grumbach #define _IWL_MEI_SAP_SHARED_MEM_SZ \
1172da4366fSEmmanuel Grumbach (sizeof(struct iwl_sap_shared_mem_ctrl_blk) + \
1182da4366fSEmmanuel Grumbach SAP_H2M_DATA_Q_SZ + SAP_H2M_NOTIF_Q_SZ + \
1192da4366fSEmmanuel Grumbach SAP_M2H_DATA_Q_SZ + SAP_M2H_NOTIF_Q_SZ + 4)
1202da4366fSEmmanuel Grumbach
1212da4366fSEmmanuel Grumbach #define IWL_MEI_SAP_SHARED_MEM_SZ \
1222da4366fSEmmanuel Grumbach (roundup(_IWL_MEI_SAP_SHARED_MEM_SZ, PAGE_SIZE))
1232da4366fSEmmanuel Grumbach
1242da4366fSEmmanuel Grumbach struct iwl_mei_shared_mem_ptrs {
1252da4366fSEmmanuel Grumbach struct iwl_sap_shared_mem_ctrl_blk *ctrl;
1262da4366fSEmmanuel Grumbach void *q_head[SAP_DIRECTION_MAX][SAP_QUEUE_IDX_MAX];
12765229160SEmmanuel Grumbach size_t q_size[SAP_DIRECTION_MAX][SAP_QUEUE_IDX_MAX];
1282da4366fSEmmanuel Grumbach };
1292da4366fSEmmanuel Grumbach
1302da4366fSEmmanuel Grumbach struct iwl_mei_filters {
1312da4366fSEmmanuel Grumbach struct rcu_head rcu_head;
1322da4366fSEmmanuel Grumbach struct iwl_sap_oob_filters filters;
1332da4366fSEmmanuel Grumbach };
1342da4366fSEmmanuel Grumbach
1352da4366fSEmmanuel Grumbach /**
1362da4366fSEmmanuel Grumbach * struct iwl_mei - holds the private date for iwl_mei
1372da4366fSEmmanuel Grumbach *
1382da4366fSEmmanuel Grumbach * @get_nvm_wq: the wait queue for the get_nvm flow
1392da4366fSEmmanuel Grumbach * @send_csa_msg_wk: used to defer the transmission of the CHECK_SHARED_AREA
1402da4366fSEmmanuel Grumbach * message. Used so that we can send CHECK_SHARED_AREA from atomic
1412da4366fSEmmanuel Grumbach * contexts.
1422da4366fSEmmanuel Grumbach * @get_ownership_wq: the wait queue for the get_ownership_flow
1432da4366fSEmmanuel Grumbach * @shared_mem: the memory that is shared between CSME and the host
1442da4366fSEmmanuel Grumbach * @cldev: the pointer to the MEI client device
1452da4366fSEmmanuel Grumbach * @nvm: the data returned by the CSME for the NVM
1462da4366fSEmmanuel Grumbach * @filters: the filters sent by CSME
1472da4366fSEmmanuel Grumbach * @got_ownership: true if we own the device
1482da4366fSEmmanuel Grumbach * @amt_enabled: true if CSME has wireless enabled
1492da4366fSEmmanuel Grumbach * @csa_throttled: when true, we can't send CHECK_SHARED_AREA over the MEI
1502da4366fSEmmanuel Grumbach * bus, but rather need to wait until send_csa_msg_wk runs
1512da4366fSEmmanuel Grumbach * @csme_taking_ownership: true when CSME is taking ownership. Used to remember
1522da4366fSEmmanuel Grumbach * to send CSME_OWNERSHIP_CONFIRMED when the driver completes its down
1532da4366fSEmmanuel Grumbach * flow.
15430de48b4SEmmanuel Grumbach * @link_prot_state: true when we are in link protection PASSIVE
1555aa7ce31SAvraham Stern * @device_down: true if the device is down. Used to remember to send
1565aa7ce31SAvraham Stern * CSME_OWNERSHIP_CONFIRMED when the driver is already down.
1572da4366fSEmmanuel Grumbach * @csa_throttle_end_wk: used when &csa_throttled is true
158733eb54fSAvraham Stern * @pldr_wq: the wait queue for PLDR flow
159733eb54fSAvraham Stern * @pldr_active: PLDR flow is in progress
1602da4366fSEmmanuel Grumbach * @data_q_lock: protects the access to the data queues which are
1612da4366fSEmmanuel Grumbach * accessed without the mutex.
162d288067eSAvraham Stern * @netdev_work: used to defer registering and unregistering of the netdev to
163d288067eSAvraham Stern * avoid taking the rtnl lock in the SAP messages handlers.
16406ce23adSAvraham Stern * @ownership_dwork: used to re-ask for NIC ownership after ownership was taken
16506ce23adSAvraham Stern * by CSME or when a previous ownership request failed.
1662da4366fSEmmanuel Grumbach * @sap_seq_no: the sequence number for the SAP messages
1672da4366fSEmmanuel Grumbach * @seq_no: the sequence number for the SAP messages
1684ea7da5fSEmmanuel Grumbach * @dbgfs_dir: the debugfs dir entry
1692da4366fSEmmanuel Grumbach */
1702da4366fSEmmanuel Grumbach struct iwl_mei {
1712da4366fSEmmanuel Grumbach wait_queue_head_t get_nvm_wq;
1722da4366fSEmmanuel Grumbach struct work_struct send_csa_msg_wk;
1732da4366fSEmmanuel Grumbach wait_queue_head_t get_ownership_wq;
1742da4366fSEmmanuel Grumbach struct iwl_mei_shared_mem_ptrs shared_mem;
1752da4366fSEmmanuel Grumbach struct mei_cl_device *cldev;
1762da4366fSEmmanuel Grumbach struct iwl_mei_nvm *nvm;
1772da4366fSEmmanuel Grumbach struct iwl_mei_filters __rcu *filters;
1782da4366fSEmmanuel Grumbach bool got_ownership;
1792da4366fSEmmanuel Grumbach bool amt_enabled;
1802da4366fSEmmanuel Grumbach bool csa_throttled;
1812da4366fSEmmanuel Grumbach bool csme_taking_ownership;
18230de48b4SEmmanuel Grumbach bool link_prot_state;
1835aa7ce31SAvraham Stern bool device_down;
1842da4366fSEmmanuel Grumbach struct delayed_work csa_throttle_end_wk;
185733eb54fSAvraham Stern wait_queue_head_t pldr_wq;
186733eb54fSAvraham Stern bool pldr_active;
1872da4366fSEmmanuel Grumbach spinlock_t data_q_lock;
188d288067eSAvraham Stern struct work_struct netdev_work;
18906ce23adSAvraham Stern struct delayed_work ownership_dwork;
1902da4366fSEmmanuel Grumbach
1912da4366fSEmmanuel Grumbach atomic_t sap_seq_no;
1922da4366fSEmmanuel Grumbach atomic_t seq_no;
1934ea7da5fSEmmanuel Grumbach
1944ea7da5fSEmmanuel Grumbach struct dentry *dbgfs_dir;
1952da4366fSEmmanuel Grumbach };
1962da4366fSEmmanuel Grumbach
1972da4366fSEmmanuel Grumbach /**
198991bbbecSJohannes Berg * struct iwl_mei_cache - cache for the parameters from iwlwifi
1992da4366fSEmmanuel Grumbach * @ops: Callbacks to iwlwifi.
2002da4366fSEmmanuel Grumbach * @netdev: The netdev that will be used to transmit / receive packets.
2012da4366fSEmmanuel Grumbach * @conn_info: The connection info message triggered by iwlwifi's association.
2022da4366fSEmmanuel Grumbach * @power_limit: pointer to an array of 10 elements (le16) represents the power
2032da4366fSEmmanuel Grumbach * restrictions per chain.
2042da4366fSEmmanuel Grumbach * @rf_kill: rf kill state.
2052da4366fSEmmanuel Grumbach * @mcc: MCC info
2062da4366fSEmmanuel Grumbach * @mac_address: interface MAC address.
2072da4366fSEmmanuel Grumbach * @nvm_address: NVM MAC address.
2082da4366fSEmmanuel Grumbach * @priv: A pointer to iwlwifi.
2092da4366fSEmmanuel Grumbach *
2102da4366fSEmmanuel Grumbach * This used to cache the configurations coming from iwlwifi's way. The data
2112da4366fSEmmanuel Grumbach * is cached here so that we can buffer the configuration even if we don't have
2122da4366fSEmmanuel Grumbach * a bind from the mei bus and hence, on iwl_mei structure.
2132da4366fSEmmanuel Grumbach */
214991bbbecSJohannes Berg struct iwl_mei_cache {
2152da4366fSEmmanuel Grumbach const struct iwl_mei_ops *ops;
2162da4366fSEmmanuel Grumbach struct net_device __rcu *netdev;
2172da4366fSEmmanuel Grumbach const struct iwl_sap_notif_connection_info *conn_info;
2182da4366fSEmmanuel Grumbach const __le16 *power_limit;
2192da4366fSEmmanuel Grumbach u32 rf_kill;
2202da4366fSEmmanuel Grumbach u16 mcc;
2212da4366fSEmmanuel Grumbach u8 mac_address[6];
2222da4366fSEmmanuel Grumbach u8 nvm_address[6];
2232da4366fSEmmanuel Grumbach void *priv;
224991bbbecSJohannes Berg };
225991bbbecSJohannes Berg
226991bbbecSJohannes Berg static struct iwl_mei_cache iwl_mei_cache = {
2272da4366fSEmmanuel Grumbach .rf_kill = SAP_HW_RFKILL_DEASSERTED | SAP_SW_RFKILL_DEASSERTED
2282da4366fSEmmanuel Grumbach };
2292da4366fSEmmanuel Grumbach
iwl_mei_free_shared_mem(struct mei_cl_device * cldev)2302da4366fSEmmanuel Grumbach static void iwl_mei_free_shared_mem(struct mei_cl_device *cldev)
2312da4366fSEmmanuel Grumbach {
2322da4366fSEmmanuel Grumbach struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
2332da4366fSEmmanuel Grumbach
2342da4366fSEmmanuel Grumbach if (mei_cldev_dma_unmap(cldev))
23565229160SEmmanuel Grumbach dev_err(&cldev->dev, "Couldn't unmap the shared mem properly\n");
2362da4366fSEmmanuel Grumbach memset(&mei->shared_mem, 0, sizeof(mei->shared_mem));
2372da4366fSEmmanuel Grumbach }
2382da4366fSEmmanuel Grumbach
2392da4366fSEmmanuel Grumbach #define HBM_DMA_BUF_ID_WLAN 1
2402da4366fSEmmanuel Grumbach
iwl_mei_alloc_shared_mem(struct mei_cl_device * cldev)2412da4366fSEmmanuel Grumbach static int iwl_mei_alloc_shared_mem(struct mei_cl_device *cldev)
2422da4366fSEmmanuel Grumbach {
2432da4366fSEmmanuel Grumbach struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
2442da4366fSEmmanuel Grumbach struct iwl_mei_shared_mem_ptrs *mem = &mei->shared_mem;
2452da4366fSEmmanuel Grumbach
2462da4366fSEmmanuel Grumbach mem->ctrl = mei_cldev_dma_map(cldev, HBM_DMA_BUF_ID_WLAN,
2472da4366fSEmmanuel Grumbach IWL_MEI_SAP_SHARED_MEM_SZ);
2482da4366fSEmmanuel Grumbach
2492da4366fSEmmanuel Grumbach if (IS_ERR(mem->ctrl)) {
2502da4366fSEmmanuel Grumbach int ret = PTR_ERR(mem->ctrl);
2512da4366fSEmmanuel Grumbach
2522da4366fSEmmanuel Grumbach mem->ctrl = NULL;
2532da4366fSEmmanuel Grumbach
2542da4366fSEmmanuel Grumbach return ret;
2552da4366fSEmmanuel Grumbach }
2562da4366fSEmmanuel Grumbach
2572da4366fSEmmanuel Grumbach memset(mem->ctrl, 0, IWL_MEI_SAP_SHARED_MEM_SZ);
2582da4366fSEmmanuel Grumbach
2592da4366fSEmmanuel Grumbach return 0;
2602da4366fSEmmanuel Grumbach }
2612da4366fSEmmanuel Grumbach
iwl_mei_init_shared_mem(struct iwl_mei * mei)2622da4366fSEmmanuel Grumbach static void iwl_mei_init_shared_mem(struct iwl_mei *mei)
2632da4366fSEmmanuel Grumbach {
2642da4366fSEmmanuel Grumbach struct iwl_mei_shared_mem_ptrs *mem = &mei->shared_mem;
2652da4366fSEmmanuel Grumbach struct iwl_sap_dir *h2m;
2662da4366fSEmmanuel Grumbach struct iwl_sap_dir *m2h;
2672da4366fSEmmanuel Grumbach int dir, queue;
2682da4366fSEmmanuel Grumbach u8 *q_head;
2692da4366fSEmmanuel Grumbach
2702da4366fSEmmanuel Grumbach mem->ctrl->sap_id = cpu_to_le32(SAP_CONTROL_BLOCK_ID);
2712da4366fSEmmanuel Grumbach
2722da4366fSEmmanuel Grumbach mem->ctrl->size = cpu_to_le32(sizeof(*mem->ctrl));
2732da4366fSEmmanuel Grumbach
2742da4366fSEmmanuel Grumbach h2m = &mem->ctrl->dir[SAP_DIRECTION_HOST_TO_ME];
2752da4366fSEmmanuel Grumbach m2h = &mem->ctrl->dir[SAP_DIRECTION_ME_TO_HOST];
2762da4366fSEmmanuel Grumbach
2772da4366fSEmmanuel Grumbach h2m->q_ctrl_blk[SAP_QUEUE_IDX_DATA].size =
2782da4366fSEmmanuel Grumbach cpu_to_le32(SAP_H2M_DATA_Q_SZ);
2792da4366fSEmmanuel Grumbach h2m->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF].size =
2802da4366fSEmmanuel Grumbach cpu_to_le32(SAP_H2M_NOTIF_Q_SZ);
2812da4366fSEmmanuel Grumbach m2h->q_ctrl_blk[SAP_QUEUE_IDX_DATA].size =
2822da4366fSEmmanuel Grumbach cpu_to_le32(SAP_M2H_DATA_Q_SZ);
2832da4366fSEmmanuel Grumbach m2h->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF].size =
2842da4366fSEmmanuel Grumbach cpu_to_le32(SAP_M2H_NOTIF_Q_SZ);
2852da4366fSEmmanuel Grumbach
2862da4366fSEmmanuel Grumbach /* q_head points to the start of the first queue */
2872da4366fSEmmanuel Grumbach q_head = (void *)(mem->ctrl + 1);
2882da4366fSEmmanuel Grumbach
2892da4366fSEmmanuel Grumbach /* Initialize the queue heads */
2902da4366fSEmmanuel Grumbach for (dir = 0; dir < SAP_DIRECTION_MAX; dir++) {
2912da4366fSEmmanuel Grumbach for (queue = 0; queue < SAP_QUEUE_IDX_MAX; queue++) {
2922da4366fSEmmanuel Grumbach mem->q_head[dir][queue] = q_head;
2932da4366fSEmmanuel Grumbach q_head +=
2942da4366fSEmmanuel Grumbach le32_to_cpu(mem->ctrl->dir[dir].q_ctrl_blk[queue].size);
29565229160SEmmanuel Grumbach mem->q_size[dir][queue] =
29665229160SEmmanuel Grumbach le32_to_cpu(mem->ctrl->dir[dir].q_ctrl_blk[queue].size);
2972da4366fSEmmanuel Grumbach }
2982da4366fSEmmanuel Grumbach }
2992da4366fSEmmanuel Grumbach
3002da4366fSEmmanuel Grumbach *(__le32 *)q_head = cpu_to_le32(SAP_CONTROL_BLOCK_ID);
3012da4366fSEmmanuel Grumbach }
3022da4366fSEmmanuel Grumbach
iwl_mei_write_cyclic_buf(struct mei_cl_device * cldev,struct iwl_sap_q_ctrl_blk * notif_q,u8 * q_head,const struct iwl_sap_hdr * hdr,u32 q_sz)3032da4366fSEmmanuel Grumbach static ssize_t iwl_mei_write_cyclic_buf(struct mei_cl_device *cldev,
3042da4366fSEmmanuel Grumbach struct iwl_sap_q_ctrl_blk *notif_q,
3052da4366fSEmmanuel Grumbach u8 *q_head,
30665229160SEmmanuel Grumbach const struct iwl_sap_hdr *hdr,
30765229160SEmmanuel Grumbach u32 q_sz)
3082da4366fSEmmanuel Grumbach {
3092da4366fSEmmanuel Grumbach u32 rd = le32_to_cpu(READ_ONCE(notif_q->rd_ptr));
3102da4366fSEmmanuel Grumbach u32 wr = le32_to_cpu(READ_ONCE(notif_q->wr_ptr));
3112da4366fSEmmanuel Grumbach size_t room_in_buf;
3122da4366fSEmmanuel Grumbach size_t tx_sz = sizeof(*hdr) + le16_to_cpu(hdr->len);
3132da4366fSEmmanuel Grumbach
3142da4366fSEmmanuel Grumbach if (rd > q_sz || wr > q_sz) {
3152da4366fSEmmanuel Grumbach dev_err(&cldev->dev,
3162da4366fSEmmanuel Grumbach "Pointers are past the end of the buffer\n");
3172da4366fSEmmanuel Grumbach return -EINVAL;
3182da4366fSEmmanuel Grumbach }
3192da4366fSEmmanuel Grumbach
3202da4366fSEmmanuel Grumbach room_in_buf = wr >= rd ? q_sz - wr + rd : rd - wr;
3212da4366fSEmmanuel Grumbach
3222da4366fSEmmanuel Grumbach /* we don't have enough room for the data to write */
3232da4366fSEmmanuel Grumbach if (room_in_buf < tx_sz) {
3242da4366fSEmmanuel Grumbach dev_err(&cldev->dev,
3252da4366fSEmmanuel Grumbach "Not enough room in the buffer\n");
3262da4366fSEmmanuel Grumbach return -ENOSPC;
3272da4366fSEmmanuel Grumbach }
3282da4366fSEmmanuel Grumbach
3292da4366fSEmmanuel Grumbach if (wr + tx_sz <= q_sz) {
3302da4366fSEmmanuel Grumbach memcpy(q_head + wr, hdr, tx_sz);
3312da4366fSEmmanuel Grumbach } else {
3322da4366fSEmmanuel Grumbach memcpy(q_head + wr, hdr, q_sz - wr);
333a31ec5faSJohannes Berg memcpy(q_head, (const u8 *)hdr + q_sz - wr, tx_sz - (q_sz - wr));
3342da4366fSEmmanuel Grumbach }
3352da4366fSEmmanuel Grumbach
3362da4366fSEmmanuel Grumbach WRITE_ONCE(notif_q->wr_ptr, cpu_to_le32((wr + tx_sz) % q_sz));
3372da4366fSEmmanuel Grumbach return 0;
3382da4366fSEmmanuel Grumbach }
3392da4366fSEmmanuel Grumbach
iwl_mei_host_to_me_data_pending(const struct iwl_mei * mei)3402da4366fSEmmanuel Grumbach static bool iwl_mei_host_to_me_data_pending(const struct iwl_mei *mei)
3412da4366fSEmmanuel Grumbach {
3422da4366fSEmmanuel Grumbach struct iwl_sap_q_ctrl_blk *notif_q;
3432da4366fSEmmanuel Grumbach struct iwl_sap_dir *dir;
3442da4366fSEmmanuel Grumbach
3452da4366fSEmmanuel Grumbach dir = &mei->shared_mem.ctrl->dir[SAP_DIRECTION_HOST_TO_ME];
3462da4366fSEmmanuel Grumbach notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_DATA];
3472da4366fSEmmanuel Grumbach
3482da4366fSEmmanuel Grumbach if (READ_ONCE(notif_q->wr_ptr) != READ_ONCE(notif_q->rd_ptr))
3492da4366fSEmmanuel Grumbach return true;
3502da4366fSEmmanuel Grumbach
3512da4366fSEmmanuel Grumbach notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF];
3522da4366fSEmmanuel Grumbach return READ_ONCE(notif_q->wr_ptr) != READ_ONCE(notif_q->rd_ptr);
3532da4366fSEmmanuel Grumbach }
3542da4366fSEmmanuel Grumbach
iwl_mei_send_check_shared_area(struct mei_cl_device * cldev)3552da4366fSEmmanuel Grumbach static int iwl_mei_send_check_shared_area(struct mei_cl_device *cldev)
3562da4366fSEmmanuel Grumbach {
3572da4366fSEmmanuel Grumbach struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
3582da4366fSEmmanuel Grumbach struct iwl_sap_me_msg_start msg = {
3592da4366fSEmmanuel Grumbach .hdr.type = cpu_to_le32(SAP_ME_MSG_CHECK_SHARED_AREA),
3602da4366fSEmmanuel Grumbach .hdr.seq_num = cpu_to_le32(atomic_inc_return(&mei->seq_no)),
3612da4366fSEmmanuel Grumbach };
3622da4366fSEmmanuel Grumbach int ret;
3632da4366fSEmmanuel Grumbach
3642da4366fSEmmanuel Grumbach lockdep_assert_held(&iwl_mei_mutex);
3652da4366fSEmmanuel Grumbach
3662da4366fSEmmanuel Grumbach if (mei->csa_throttled)
3672da4366fSEmmanuel Grumbach return 0;
3682da4366fSEmmanuel Grumbach
3692da4366fSEmmanuel Grumbach trace_iwlmei_me_msg(&msg.hdr, true);
3702da4366fSEmmanuel Grumbach ret = mei_cldev_send(cldev, (void *)&msg, sizeof(msg));
3712da4366fSEmmanuel Grumbach if (ret != sizeof(msg)) {
3722da4366fSEmmanuel Grumbach dev_err(&cldev->dev,
3732da4366fSEmmanuel Grumbach "failed to send the SAP_ME_MSG_CHECK_SHARED_AREA message %d\n",
3742da4366fSEmmanuel Grumbach ret);
3752da4366fSEmmanuel Grumbach return ret;
3762da4366fSEmmanuel Grumbach }
3772da4366fSEmmanuel Grumbach
3782da4366fSEmmanuel Grumbach mei->csa_throttled = true;
3792da4366fSEmmanuel Grumbach
3802da4366fSEmmanuel Grumbach schedule_delayed_work(&mei->csa_throttle_end_wk,
3812da4366fSEmmanuel Grumbach msecs_to_jiffies(100));
3822da4366fSEmmanuel Grumbach
3832da4366fSEmmanuel Grumbach return 0;
3842da4366fSEmmanuel Grumbach }
3852da4366fSEmmanuel Grumbach
iwl_mei_csa_throttle_end_wk(struct work_struct * wk)3862da4366fSEmmanuel Grumbach static void iwl_mei_csa_throttle_end_wk(struct work_struct *wk)
3872da4366fSEmmanuel Grumbach {
3882da4366fSEmmanuel Grumbach struct iwl_mei *mei =
3892da4366fSEmmanuel Grumbach container_of(wk, struct iwl_mei, csa_throttle_end_wk.work);
3902da4366fSEmmanuel Grumbach
3912da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
3922da4366fSEmmanuel Grumbach
3932da4366fSEmmanuel Grumbach mei->csa_throttled = false;
3942da4366fSEmmanuel Grumbach
3952da4366fSEmmanuel Grumbach if (iwl_mei_host_to_me_data_pending(mei))
3962da4366fSEmmanuel Grumbach iwl_mei_send_check_shared_area(mei->cldev);
3972da4366fSEmmanuel Grumbach
3982da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
3992da4366fSEmmanuel Grumbach }
4002da4366fSEmmanuel Grumbach
iwl_mei_send_sap_msg_payload(struct mei_cl_device * cldev,struct iwl_sap_hdr * hdr)4012da4366fSEmmanuel Grumbach static int iwl_mei_send_sap_msg_payload(struct mei_cl_device *cldev,
4022da4366fSEmmanuel Grumbach struct iwl_sap_hdr *hdr)
4032da4366fSEmmanuel Grumbach {
4042da4366fSEmmanuel Grumbach struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
4052da4366fSEmmanuel Grumbach struct iwl_sap_q_ctrl_blk *notif_q;
4062da4366fSEmmanuel Grumbach struct iwl_sap_dir *dir;
4072da4366fSEmmanuel Grumbach void *q_head;
40865229160SEmmanuel Grumbach u32 q_sz;
4092da4366fSEmmanuel Grumbach int ret;
4102da4366fSEmmanuel Grumbach
4112da4366fSEmmanuel Grumbach lockdep_assert_held(&iwl_mei_mutex);
4122da4366fSEmmanuel Grumbach
4132da4366fSEmmanuel Grumbach if (!mei->shared_mem.ctrl) {
4142da4366fSEmmanuel Grumbach dev_err(&cldev->dev,
4152da4366fSEmmanuel Grumbach "No shared memory, can't send any SAP message\n");
4162da4366fSEmmanuel Grumbach return -EINVAL;
4172da4366fSEmmanuel Grumbach }
4182da4366fSEmmanuel Grumbach
4192da4366fSEmmanuel Grumbach if (!iwl_mei_is_connected()) {
4202da4366fSEmmanuel Grumbach dev_err(&cldev->dev,
4212da4366fSEmmanuel Grumbach "Can't send a SAP message if we're not connected\n");
4222da4366fSEmmanuel Grumbach return -ENODEV;
4232da4366fSEmmanuel Grumbach }
4242da4366fSEmmanuel Grumbach
4252da4366fSEmmanuel Grumbach hdr->seq_num = cpu_to_le32(atomic_inc_return(&mei->sap_seq_no));
4262da4366fSEmmanuel Grumbach dev_dbg(&cldev->dev, "Sending %d\n", hdr->type);
4272da4366fSEmmanuel Grumbach
4282da4366fSEmmanuel Grumbach dir = &mei->shared_mem.ctrl->dir[SAP_DIRECTION_HOST_TO_ME];
4292da4366fSEmmanuel Grumbach notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF];
4302da4366fSEmmanuel Grumbach q_head = mei->shared_mem.q_head[SAP_DIRECTION_HOST_TO_ME][SAP_QUEUE_IDX_NOTIF];
43165229160SEmmanuel Grumbach q_sz = mei->shared_mem.q_size[SAP_DIRECTION_HOST_TO_ME][SAP_QUEUE_IDX_NOTIF];
43265229160SEmmanuel Grumbach ret = iwl_mei_write_cyclic_buf(q_head, notif_q, q_head, hdr, q_sz);
4332da4366fSEmmanuel Grumbach
4342da4366fSEmmanuel Grumbach if (ret < 0)
4352da4366fSEmmanuel Grumbach return ret;
4362da4366fSEmmanuel Grumbach
4372da4366fSEmmanuel Grumbach trace_iwlmei_sap_cmd(hdr, true);
4382da4366fSEmmanuel Grumbach
4392da4366fSEmmanuel Grumbach return iwl_mei_send_check_shared_area(cldev);
4402da4366fSEmmanuel Grumbach }
4412da4366fSEmmanuel Grumbach
iwl_mei_add_data_to_ring(struct sk_buff * skb,bool cb_tx)4422da4366fSEmmanuel Grumbach void iwl_mei_add_data_to_ring(struct sk_buff *skb, bool cb_tx)
4432da4366fSEmmanuel Grumbach {
4442da4366fSEmmanuel Grumbach struct iwl_sap_q_ctrl_blk *notif_q;
4452da4366fSEmmanuel Grumbach struct iwl_sap_dir *dir;
4462da4366fSEmmanuel Grumbach struct iwl_mei *mei;
4472da4366fSEmmanuel Grumbach size_t room_in_buf;
4482da4366fSEmmanuel Grumbach size_t tx_sz;
4492da4366fSEmmanuel Grumbach size_t hdr_sz;
4502da4366fSEmmanuel Grumbach u32 q_sz;
4512da4366fSEmmanuel Grumbach u32 rd;
4522da4366fSEmmanuel Grumbach u32 wr;
453a31ec5faSJohannes Berg u8 *q_head;
4542da4366fSEmmanuel Grumbach
4552da4366fSEmmanuel Grumbach if (!iwl_mei_global_cldev)
4562da4366fSEmmanuel Grumbach return;
4572da4366fSEmmanuel Grumbach
4582da4366fSEmmanuel Grumbach mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
4592da4366fSEmmanuel Grumbach
4602da4366fSEmmanuel Grumbach /*
4612da4366fSEmmanuel Grumbach * We access this path for Rx packets (the more common case)
4622da4366fSEmmanuel Grumbach * and from Tx path when we send DHCP packets, the latter is
4632da4366fSEmmanuel Grumbach * very unlikely.
4642da4366fSEmmanuel Grumbach * Take the lock already here to make sure we see that remove()
4652da4366fSEmmanuel Grumbach * might have cleared the IWL_MEI_STATUS_SAP_CONNECTED bit.
4662da4366fSEmmanuel Grumbach */
4672da4366fSEmmanuel Grumbach spin_lock_bh(&mei->data_q_lock);
4682da4366fSEmmanuel Grumbach
4692da4366fSEmmanuel Grumbach if (!iwl_mei_is_connected()) {
4702da4366fSEmmanuel Grumbach spin_unlock_bh(&mei->data_q_lock);
4712da4366fSEmmanuel Grumbach return;
4722da4366fSEmmanuel Grumbach }
4732da4366fSEmmanuel Grumbach
4742da4366fSEmmanuel Grumbach /*
4752da4366fSEmmanuel Grumbach * We are in a RCU critical section and the remove from the CSME bus
4762da4366fSEmmanuel Grumbach * which would free this memory waits for the readers to complete (this
4772da4366fSEmmanuel Grumbach * is done in netdev_rx_handler_unregister).
4782da4366fSEmmanuel Grumbach */
4792da4366fSEmmanuel Grumbach dir = &mei->shared_mem.ctrl->dir[SAP_DIRECTION_HOST_TO_ME];
4802da4366fSEmmanuel Grumbach notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_DATA];
4812da4366fSEmmanuel Grumbach q_head = mei->shared_mem.q_head[SAP_DIRECTION_HOST_TO_ME][SAP_QUEUE_IDX_DATA];
48265229160SEmmanuel Grumbach q_sz = mei->shared_mem.q_size[SAP_DIRECTION_HOST_TO_ME][SAP_QUEUE_IDX_DATA];
4832da4366fSEmmanuel Grumbach
4842da4366fSEmmanuel Grumbach rd = le32_to_cpu(READ_ONCE(notif_q->rd_ptr));
4852da4366fSEmmanuel Grumbach wr = le32_to_cpu(READ_ONCE(notif_q->wr_ptr));
4862da4366fSEmmanuel Grumbach hdr_sz = cb_tx ? sizeof(struct iwl_sap_cb_data) :
4872da4366fSEmmanuel Grumbach sizeof(struct iwl_sap_hdr);
4882da4366fSEmmanuel Grumbach tx_sz = skb->len + hdr_sz;
4892da4366fSEmmanuel Grumbach
4902da4366fSEmmanuel Grumbach if (rd > q_sz || wr > q_sz) {
4912da4366fSEmmanuel Grumbach dev_err(&mei->cldev->dev,
4922da4366fSEmmanuel Grumbach "can't write the data: pointers are past the end of the buffer\n");
4932da4366fSEmmanuel Grumbach goto out;
4942da4366fSEmmanuel Grumbach }
4952da4366fSEmmanuel Grumbach
4962da4366fSEmmanuel Grumbach room_in_buf = wr >= rd ? q_sz - wr + rd : rd - wr;
4972da4366fSEmmanuel Grumbach
4982da4366fSEmmanuel Grumbach /* we don't have enough room for the data to write */
4992da4366fSEmmanuel Grumbach if (room_in_buf < tx_sz) {
5002da4366fSEmmanuel Grumbach dev_err(&mei->cldev->dev,
5012da4366fSEmmanuel Grumbach "Not enough room in the buffer for this data\n");
5022da4366fSEmmanuel Grumbach goto out;
5032da4366fSEmmanuel Grumbach }
5042da4366fSEmmanuel Grumbach
5052da4366fSEmmanuel Grumbach if (skb_headroom(skb) < hdr_sz) {
5062da4366fSEmmanuel Grumbach dev_err(&mei->cldev->dev,
5072da4366fSEmmanuel Grumbach "Not enough headroom in the skb to write the SAP header\n");
5082da4366fSEmmanuel Grumbach goto out;
5092da4366fSEmmanuel Grumbach }
5102da4366fSEmmanuel Grumbach
5112da4366fSEmmanuel Grumbach if (cb_tx) {
5122da4366fSEmmanuel Grumbach struct iwl_sap_cb_data *cb_hdr = skb_push(skb, sizeof(*cb_hdr));
5132da4366fSEmmanuel Grumbach
51455cf1048SAvraham Stern memset(cb_hdr, 0, sizeof(*cb_hdr));
5152da4366fSEmmanuel Grumbach cb_hdr->hdr.type = cpu_to_le16(SAP_MSG_CB_DATA_PACKET);
5162da4366fSEmmanuel Grumbach cb_hdr->hdr.len = cpu_to_le16(skb->len - sizeof(cb_hdr->hdr));
5172da4366fSEmmanuel Grumbach cb_hdr->hdr.seq_num = cpu_to_le32(atomic_inc_return(&mei->sap_seq_no));
5182da4366fSEmmanuel Grumbach cb_hdr->to_me_filt_status = cpu_to_le32(BIT(CB_TX_DHCP_FILT_IDX));
5192da4366fSEmmanuel Grumbach cb_hdr->data_len = cpu_to_le32(skb->len - sizeof(*cb_hdr));
5202da4366fSEmmanuel Grumbach trace_iwlmei_sap_data(skb, IWL_SAP_TX_DHCP);
5212da4366fSEmmanuel Grumbach } else {
5222da4366fSEmmanuel Grumbach struct iwl_sap_hdr *hdr = skb_push(skb, sizeof(*hdr));
5232da4366fSEmmanuel Grumbach
5242da4366fSEmmanuel Grumbach hdr->type = cpu_to_le16(SAP_MSG_DATA_PACKET);
5252da4366fSEmmanuel Grumbach hdr->len = cpu_to_le16(skb->len - sizeof(*hdr));
5262da4366fSEmmanuel Grumbach hdr->seq_num = cpu_to_le32(atomic_inc_return(&mei->sap_seq_no));
5272da4366fSEmmanuel Grumbach trace_iwlmei_sap_data(skb, IWL_SAP_TX_DATA_FROM_AIR);
5282da4366fSEmmanuel Grumbach }
5292da4366fSEmmanuel Grumbach
5302da4366fSEmmanuel Grumbach if (wr + tx_sz <= q_sz) {
5312da4366fSEmmanuel Grumbach skb_copy_bits(skb, 0, q_head + wr, tx_sz);
5322da4366fSEmmanuel Grumbach } else {
5332da4366fSEmmanuel Grumbach skb_copy_bits(skb, 0, q_head + wr, q_sz - wr);
5342da4366fSEmmanuel Grumbach skb_copy_bits(skb, q_sz - wr, q_head, tx_sz - (q_sz - wr));
5352da4366fSEmmanuel Grumbach }
5362da4366fSEmmanuel Grumbach
5372da4366fSEmmanuel Grumbach WRITE_ONCE(notif_q->wr_ptr, cpu_to_le32((wr + tx_sz) % q_sz));
5382da4366fSEmmanuel Grumbach
5392da4366fSEmmanuel Grumbach out:
5402da4366fSEmmanuel Grumbach spin_unlock_bh(&mei->data_q_lock);
5412da4366fSEmmanuel Grumbach }
5422da4366fSEmmanuel Grumbach
5432da4366fSEmmanuel Grumbach static int
iwl_mei_send_sap_msg(struct mei_cl_device * cldev,u16 type)5442da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg(struct mei_cl_device *cldev, u16 type)
5452da4366fSEmmanuel Grumbach {
5462da4366fSEmmanuel Grumbach struct iwl_sap_hdr msg = {
5472da4366fSEmmanuel Grumbach .type = cpu_to_le16(type),
5482da4366fSEmmanuel Grumbach };
5492da4366fSEmmanuel Grumbach
5502da4366fSEmmanuel Grumbach return iwl_mei_send_sap_msg_payload(cldev, &msg);
5512da4366fSEmmanuel Grumbach }
5522da4366fSEmmanuel Grumbach
iwl_mei_send_csa_msg_wk(struct work_struct * wk)5532da4366fSEmmanuel Grumbach static void iwl_mei_send_csa_msg_wk(struct work_struct *wk)
5542da4366fSEmmanuel Grumbach {
5552da4366fSEmmanuel Grumbach struct iwl_mei *mei =
5562da4366fSEmmanuel Grumbach container_of(wk, struct iwl_mei, send_csa_msg_wk);
5572da4366fSEmmanuel Grumbach
5582da4366fSEmmanuel Grumbach if (!iwl_mei_is_connected())
5592da4366fSEmmanuel Grumbach return;
5602da4366fSEmmanuel Grumbach
5612da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
5622da4366fSEmmanuel Grumbach
5632da4366fSEmmanuel Grumbach iwl_mei_send_check_shared_area(mei->cldev);
5642da4366fSEmmanuel Grumbach
5652da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
5662da4366fSEmmanuel Grumbach }
5672da4366fSEmmanuel Grumbach
5682da4366fSEmmanuel Grumbach /* Called in a RCU read critical section from netif_receive_skb */
iwl_mei_rx_handler(struct sk_buff ** pskb)5692da4366fSEmmanuel Grumbach static rx_handler_result_t iwl_mei_rx_handler(struct sk_buff **pskb)
5702da4366fSEmmanuel Grumbach {
5712da4366fSEmmanuel Grumbach struct sk_buff *skb = *pskb;
5722da4366fSEmmanuel Grumbach struct iwl_mei *mei =
5732da4366fSEmmanuel Grumbach rcu_dereference(skb->dev->rx_handler_data);
5742da4366fSEmmanuel Grumbach struct iwl_mei_filters *filters = rcu_dereference(mei->filters);
5752da4366fSEmmanuel Grumbach bool rx_for_csme = false;
5762da4366fSEmmanuel Grumbach rx_handler_result_t res;
5772da4366fSEmmanuel Grumbach
5782da4366fSEmmanuel Grumbach /*
5792da4366fSEmmanuel Grumbach * remove() unregisters this handler and synchronize_net, so this
5802da4366fSEmmanuel Grumbach * should never happen.
5812da4366fSEmmanuel Grumbach */
5822da4366fSEmmanuel Grumbach if (!iwl_mei_is_connected()) {
5832da4366fSEmmanuel Grumbach dev_err(&mei->cldev->dev,
5842da4366fSEmmanuel Grumbach "Got an Rx packet, but we're not connected to SAP?\n");
5852da4366fSEmmanuel Grumbach return RX_HANDLER_PASS;
5862da4366fSEmmanuel Grumbach }
5872da4366fSEmmanuel Grumbach
5882da4366fSEmmanuel Grumbach if (filters)
5892da4366fSEmmanuel Grumbach res = iwl_mei_rx_filter(skb, &filters->filters, &rx_for_csme);
5902da4366fSEmmanuel Grumbach else
5912da4366fSEmmanuel Grumbach res = RX_HANDLER_PASS;
5922da4366fSEmmanuel Grumbach
5932da4366fSEmmanuel Grumbach /*
5942da4366fSEmmanuel Grumbach * The data is already on the ring of the shared area, all we
5952da4366fSEmmanuel Grumbach * need to do is to tell the CSME firmware to check what we have
5962da4366fSEmmanuel Grumbach * there.
5972da4366fSEmmanuel Grumbach */
5982da4366fSEmmanuel Grumbach if (rx_for_csme)
5992da4366fSEmmanuel Grumbach schedule_work(&mei->send_csa_msg_wk);
6002da4366fSEmmanuel Grumbach
6012da4366fSEmmanuel Grumbach if (res != RX_HANDLER_PASS) {
6022da4366fSEmmanuel Grumbach trace_iwlmei_sap_data(skb, IWL_SAP_RX_DATA_DROPPED_FROM_AIR);
6032da4366fSEmmanuel Grumbach dev_kfree_skb(skb);
6042da4366fSEmmanuel Grumbach }
6052da4366fSEmmanuel Grumbach
6062da4366fSEmmanuel Grumbach return res;
6072da4366fSEmmanuel Grumbach }
6082da4366fSEmmanuel Grumbach
iwl_mei_netdev_work(struct work_struct * wk)609d288067eSAvraham Stern static void iwl_mei_netdev_work(struct work_struct *wk)
610d288067eSAvraham Stern {
611d288067eSAvraham Stern struct iwl_mei *mei =
612d288067eSAvraham Stern container_of(wk, struct iwl_mei, netdev_work);
613d288067eSAvraham Stern struct net_device *netdev;
614d288067eSAvraham Stern
615d288067eSAvraham Stern /*
616d288067eSAvraham Stern * First take rtnl and only then the mutex to avoid an ABBA
617d288067eSAvraham Stern * with iwl_mei_set_netdev()
618d288067eSAvraham Stern */
619d288067eSAvraham Stern rtnl_lock();
620d288067eSAvraham Stern mutex_lock(&iwl_mei_mutex);
621d288067eSAvraham Stern
622d288067eSAvraham Stern netdev = rcu_dereference_protected(iwl_mei_cache.netdev,
623d288067eSAvraham Stern lockdep_is_held(&iwl_mei_mutex));
624d288067eSAvraham Stern if (netdev) {
625d288067eSAvraham Stern if (mei->amt_enabled)
626d288067eSAvraham Stern netdev_rx_handler_register(netdev, iwl_mei_rx_handler,
627d288067eSAvraham Stern mei);
628d288067eSAvraham Stern else
629d288067eSAvraham Stern netdev_rx_handler_unregister(netdev);
630d288067eSAvraham Stern }
631d288067eSAvraham Stern
632d288067eSAvraham Stern mutex_unlock(&iwl_mei_mutex);
633d288067eSAvraham Stern rtnl_unlock();
634d288067eSAvraham Stern }
635d288067eSAvraham Stern
6362da4366fSEmmanuel Grumbach static void
iwl_mei_handle_rx_start_ok(struct mei_cl_device * cldev,const struct iwl_sap_me_msg_start_ok * rsp,ssize_t len)6372da4366fSEmmanuel Grumbach iwl_mei_handle_rx_start_ok(struct mei_cl_device *cldev,
6382da4366fSEmmanuel Grumbach const struct iwl_sap_me_msg_start_ok *rsp,
6392da4366fSEmmanuel Grumbach ssize_t len)
6402da4366fSEmmanuel Grumbach {
6412da4366fSEmmanuel Grumbach if (len != sizeof(*rsp)) {
6422da4366fSEmmanuel Grumbach dev_err(&cldev->dev,
6432da4366fSEmmanuel Grumbach "got invalid SAP_ME_MSG_START_OK from CSME firmware\n");
6442da4366fSEmmanuel Grumbach dev_err(&cldev->dev,
6452da4366fSEmmanuel Grumbach "size is incorrect: %zd instead of %zu\n",
6462da4366fSEmmanuel Grumbach len, sizeof(*rsp));
6472da4366fSEmmanuel Grumbach return;
6482da4366fSEmmanuel Grumbach }
6492da4366fSEmmanuel Grumbach
6502da4366fSEmmanuel Grumbach if (rsp->supported_version != SAP_VERSION) {
6512da4366fSEmmanuel Grumbach dev_err(&cldev->dev,
6522da4366fSEmmanuel Grumbach "didn't get the expected version: got %d\n",
6532da4366fSEmmanuel Grumbach rsp->supported_version);
6542da4366fSEmmanuel Grumbach return;
6552da4366fSEmmanuel Grumbach }
6562da4366fSEmmanuel Grumbach
6572da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
6582da4366fSEmmanuel Grumbach set_bit(IWL_MEI_STATUS_SAP_CONNECTED, &iwl_mei_status);
65995170a46SEmmanuel Grumbach /*
66095170a46SEmmanuel Grumbach * We'll receive AMT_STATE SAP message in a bit and
66195170a46SEmmanuel Grumbach * that will continue the flow
66295170a46SEmmanuel Grumbach */
6632da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
6642da4366fSEmmanuel Grumbach }
6652da4366fSEmmanuel Grumbach
iwl_mei_handle_csme_filters(struct mei_cl_device * cldev,const struct iwl_sap_csme_filters * filters)6662da4366fSEmmanuel Grumbach static void iwl_mei_handle_csme_filters(struct mei_cl_device *cldev,
6672da4366fSEmmanuel Grumbach const struct iwl_sap_csme_filters *filters)
6682da4366fSEmmanuel Grumbach {
6692da4366fSEmmanuel Grumbach struct iwl_mei *mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
6702da4366fSEmmanuel Grumbach struct iwl_mei_filters *new_filters;
6712da4366fSEmmanuel Grumbach struct iwl_mei_filters *old_filters;
6722da4366fSEmmanuel Grumbach
6732da4366fSEmmanuel Grumbach old_filters =
6742da4366fSEmmanuel Grumbach rcu_dereference_protected(mei->filters,
6752da4366fSEmmanuel Grumbach lockdep_is_held(&iwl_mei_mutex));
6762da4366fSEmmanuel Grumbach
6772da4366fSEmmanuel Grumbach new_filters = kzalloc(sizeof(*new_filters), GFP_KERNEL);
67846c7b05aSEmmanuel Grumbach if (!new_filters)
67946c7b05aSEmmanuel Grumbach return;
6802da4366fSEmmanuel Grumbach
6812da4366fSEmmanuel Grumbach /* Copy the OOB filters */
6822da4366fSEmmanuel Grumbach new_filters->filters = filters->filters;
6832da4366fSEmmanuel Grumbach
6842da4366fSEmmanuel Grumbach rcu_assign_pointer(mei->filters, new_filters);
6852da4366fSEmmanuel Grumbach
6862da4366fSEmmanuel Grumbach if (old_filters)
6872da4366fSEmmanuel Grumbach kfree_rcu(old_filters, rcu_head);
6882da4366fSEmmanuel Grumbach }
6892da4366fSEmmanuel Grumbach
6902da4366fSEmmanuel Grumbach static void
iwl_mei_handle_conn_status(struct mei_cl_device * cldev,const struct iwl_sap_notif_conn_status * status)6912da4366fSEmmanuel Grumbach iwl_mei_handle_conn_status(struct mei_cl_device *cldev,
6922da4366fSEmmanuel Grumbach const struct iwl_sap_notif_conn_status *status)
6932da4366fSEmmanuel Grumbach {
6942da4366fSEmmanuel Grumbach struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
6952da4366fSEmmanuel Grumbach struct iwl_mei_conn_info conn_info = {
6962da4366fSEmmanuel Grumbach .lp_state = le32_to_cpu(status->link_prot_state),
6972da4366fSEmmanuel Grumbach .ssid_len = le32_to_cpu(status->conn_info.ssid_len),
6982da4366fSEmmanuel Grumbach .channel = status->conn_info.channel,
6992da4366fSEmmanuel Grumbach .band = status->conn_info.band,
7002da4366fSEmmanuel Grumbach .auth_mode = le32_to_cpu(status->conn_info.auth_mode),
7012da4366fSEmmanuel Grumbach .pairwise_cipher = le32_to_cpu(status->conn_info.pairwise_cipher),
7022da4366fSEmmanuel Grumbach };
7032da4366fSEmmanuel Grumbach
7042da4366fSEmmanuel Grumbach if (!iwl_mei_cache.ops ||
7052da4366fSEmmanuel Grumbach conn_info.ssid_len > ARRAY_SIZE(conn_info.ssid))
7062da4366fSEmmanuel Grumbach return;
7072da4366fSEmmanuel Grumbach
7082da4366fSEmmanuel Grumbach memcpy(conn_info.ssid, status->conn_info.ssid, conn_info.ssid_len);
7092da4366fSEmmanuel Grumbach ether_addr_copy(conn_info.bssid, status->conn_info.bssid);
7102da4366fSEmmanuel Grumbach
7112da4366fSEmmanuel Grumbach iwl_mei_cache.ops->me_conn_status(iwl_mei_cache.priv, &conn_info);
7122da4366fSEmmanuel Grumbach
71330de48b4SEmmanuel Grumbach mei->link_prot_state = status->link_prot_state;
71430de48b4SEmmanuel Grumbach
7152da4366fSEmmanuel Grumbach /*
7162da4366fSEmmanuel Grumbach * Update the Rfkill state in case the host does not own the device:
7172da4366fSEmmanuel Grumbach * if we are in Link Protection, ask to not touch the device, else,
7182da4366fSEmmanuel Grumbach * unblock rfkill.
7192da4366fSEmmanuel Grumbach * If the host owns the device, inform the user space whether it can
7202da4366fSEmmanuel Grumbach * roam.
7212da4366fSEmmanuel Grumbach */
7222da4366fSEmmanuel Grumbach if (mei->got_ownership)
7232da4366fSEmmanuel Grumbach iwl_mei_cache.ops->roaming_forbidden(iwl_mei_cache.priv,
7242da4366fSEmmanuel Grumbach status->link_prot_state);
7252da4366fSEmmanuel Grumbach else
7262da4366fSEmmanuel Grumbach iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv,
72722b68fc6SAvraham Stern status->link_prot_state, false);
7282da4366fSEmmanuel Grumbach }
7292da4366fSEmmanuel Grumbach
iwl_mei_set_init_conf(struct iwl_mei * mei)7302da4366fSEmmanuel Grumbach static void iwl_mei_set_init_conf(struct iwl_mei *mei)
7312da4366fSEmmanuel Grumbach {
7322da4366fSEmmanuel Grumbach struct iwl_sap_notif_host_link_up link_msg = {
7332da4366fSEmmanuel Grumbach .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_HOST_LINK_UP),
7342da4366fSEmmanuel Grumbach .hdr.len = cpu_to_le16(sizeof(link_msg) - sizeof(link_msg.hdr)),
7352da4366fSEmmanuel Grumbach };
7362da4366fSEmmanuel Grumbach struct iwl_sap_notif_country_code mcc_msg = {
7372da4366fSEmmanuel Grumbach .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_COUNTRY_CODE),
7382da4366fSEmmanuel Grumbach .hdr.len = cpu_to_le16(sizeof(mcc_msg) - sizeof(mcc_msg.hdr)),
7392da4366fSEmmanuel Grumbach .mcc = cpu_to_le16(iwl_mei_cache.mcc),
7402da4366fSEmmanuel Grumbach };
7412da4366fSEmmanuel Grumbach struct iwl_sap_notif_sar_limits sar_msg = {
7422da4366fSEmmanuel Grumbach .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_SAR_LIMITS),
7432da4366fSEmmanuel Grumbach .hdr.len = cpu_to_le16(sizeof(sar_msg) - sizeof(sar_msg.hdr)),
7442da4366fSEmmanuel Grumbach };
7452da4366fSEmmanuel Grumbach struct iwl_sap_notif_host_nic_info nic_info_msg = {
7462da4366fSEmmanuel Grumbach .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_NIC_INFO),
7472da4366fSEmmanuel Grumbach .hdr.len = cpu_to_le16(sizeof(nic_info_msg) - sizeof(nic_info_msg.hdr)),
7482da4366fSEmmanuel Grumbach };
7492da4366fSEmmanuel Grumbach struct iwl_sap_msg_dw rfkill_msg = {
7502da4366fSEmmanuel Grumbach .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_RADIO_STATE),
7512da4366fSEmmanuel Grumbach .hdr.len = cpu_to_le16(sizeof(rfkill_msg) - sizeof(rfkill_msg.hdr)),
7522da4366fSEmmanuel Grumbach .val = cpu_to_le32(iwl_mei_cache.rf_kill),
7532da4366fSEmmanuel Grumbach };
7542da4366fSEmmanuel Grumbach
75595170a46SEmmanuel Grumbach /* wifi driver has registered already */
75695170a46SEmmanuel Grumbach if (iwl_mei_cache.ops) {
75795170a46SEmmanuel Grumbach iwl_mei_send_sap_msg(mei->cldev,
75895170a46SEmmanuel Grumbach SAP_MSG_NOTIF_WIFIDR_UP);
75995170a46SEmmanuel Grumbach iwl_mei_cache.ops->sap_connected(iwl_mei_cache.priv);
76095170a46SEmmanuel Grumbach }
76195170a46SEmmanuel Grumbach
7622da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg(mei->cldev, SAP_MSG_NOTIF_WHO_OWNS_NIC);
7632da4366fSEmmanuel Grumbach
7642da4366fSEmmanuel Grumbach if (iwl_mei_cache.conn_info) {
7652da4366fSEmmanuel Grumbach link_msg.conn_info = *iwl_mei_cache.conn_info;
7662da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg_payload(mei->cldev, &link_msg.hdr);
7672da4366fSEmmanuel Grumbach }
7682da4366fSEmmanuel Grumbach
7692da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg_payload(mei->cldev, &mcc_msg.hdr);
7702da4366fSEmmanuel Grumbach
7712da4366fSEmmanuel Grumbach if (iwl_mei_cache.power_limit) {
7722da4366fSEmmanuel Grumbach memcpy(sar_msg.sar_chain_info_table, iwl_mei_cache.power_limit,
7732da4366fSEmmanuel Grumbach sizeof(sar_msg.sar_chain_info_table));
7742da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg_payload(mei->cldev, &sar_msg.hdr);
7752da4366fSEmmanuel Grumbach }
7762da4366fSEmmanuel Grumbach
777*6d2c360bSAvraham Stern if (is_valid_ether_addr(iwl_mei_cache.mac_address)) {
778*6d2c360bSAvraham Stern ether_addr_copy(nic_info_msg.mac_address,
779*6d2c360bSAvraham Stern iwl_mei_cache.mac_address);
780*6d2c360bSAvraham Stern ether_addr_copy(nic_info_msg.nvm_address,
781*6d2c360bSAvraham Stern iwl_mei_cache.nvm_address);
7822da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg_payload(mei->cldev, &nic_info_msg.hdr);
783*6d2c360bSAvraham Stern }
7842da4366fSEmmanuel Grumbach
7852da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg_payload(mei->cldev, &rfkill_msg.hdr);
7862da4366fSEmmanuel Grumbach }
7872da4366fSEmmanuel Grumbach
iwl_mei_handle_amt_state(struct mei_cl_device * cldev,const struct iwl_sap_msg_dw * dw)7882da4366fSEmmanuel Grumbach static void iwl_mei_handle_amt_state(struct mei_cl_device *cldev,
7892da4366fSEmmanuel Grumbach const struct iwl_sap_msg_dw *dw)
7902da4366fSEmmanuel Grumbach {
7912da4366fSEmmanuel Grumbach struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
7922da4366fSEmmanuel Grumbach
7932da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
7942da4366fSEmmanuel Grumbach
7952da4366fSEmmanuel Grumbach if (mei->amt_enabled == !!le32_to_cpu(dw->val))
7962da4366fSEmmanuel Grumbach goto out;
7972da4366fSEmmanuel Grumbach
7982da4366fSEmmanuel Grumbach mei->amt_enabled = dw->val;
7992da4366fSEmmanuel Grumbach
800d288067eSAvraham Stern if (mei->amt_enabled)
8012da4366fSEmmanuel Grumbach iwl_mei_set_init_conf(mei);
802d288067eSAvraham Stern else if (iwl_mei_cache.ops)
80322b68fc6SAvraham Stern iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false, false);
804d288067eSAvraham Stern
805d288067eSAvraham Stern schedule_work(&mei->netdev_work);
8062da4366fSEmmanuel Grumbach
8072da4366fSEmmanuel Grumbach out:
8082da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
8092da4366fSEmmanuel Grumbach }
8102da4366fSEmmanuel Grumbach
iwl_mei_handle_nic_owner(struct mei_cl_device * cldev,const struct iwl_sap_msg_dw * dw)8112da4366fSEmmanuel Grumbach static void iwl_mei_handle_nic_owner(struct mei_cl_device *cldev,
8122da4366fSEmmanuel Grumbach const struct iwl_sap_msg_dw *dw)
8132da4366fSEmmanuel Grumbach {
8142da4366fSEmmanuel Grumbach struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
8152da4366fSEmmanuel Grumbach
8162da4366fSEmmanuel Grumbach mei->got_ownership = dw->val != cpu_to_le32(SAP_NIC_OWNER_ME);
8172da4366fSEmmanuel Grumbach }
8182da4366fSEmmanuel Grumbach
iwl_mei_handle_can_release_ownership(struct mei_cl_device * cldev,const void * payload)8192da4366fSEmmanuel Grumbach static void iwl_mei_handle_can_release_ownership(struct mei_cl_device *cldev,
8202da4366fSEmmanuel Grumbach const void *payload)
8212da4366fSEmmanuel Grumbach {
8222da4366fSEmmanuel Grumbach /* We can get ownership and driver is registered, go ahead */
8232da4366fSEmmanuel Grumbach if (iwl_mei_cache.ops)
8242da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg(cldev,
8252da4366fSEmmanuel Grumbach SAP_MSG_NOTIF_HOST_ASKS_FOR_NIC_OWNERSHIP);
8262da4366fSEmmanuel Grumbach }
8272da4366fSEmmanuel Grumbach
iwl_mei_handle_csme_taking_ownership(struct mei_cl_device * cldev,const void * payload)8282da4366fSEmmanuel Grumbach static void iwl_mei_handle_csme_taking_ownership(struct mei_cl_device *cldev,
8292da4366fSEmmanuel Grumbach const void *payload)
8302da4366fSEmmanuel Grumbach {
8312da4366fSEmmanuel Grumbach struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
8322da4366fSEmmanuel Grumbach
8332da4366fSEmmanuel Grumbach dev_info(&cldev->dev, "CSME takes ownership\n");
8342da4366fSEmmanuel Grumbach
8352da4366fSEmmanuel Grumbach mei->got_ownership = false;
8362da4366fSEmmanuel Grumbach
8375aa7ce31SAvraham Stern if (iwl_mei_cache.ops && !mei->device_down) {
8382da4366fSEmmanuel Grumbach /*
8395aa7ce31SAvraham Stern * Remember to send CSME_OWNERSHIP_CONFIRMED when the wifi
8405aa7ce31SAvraham Stern * driver is finished taking the device down.
8412da4366fSEmmanuel Grumbach */
8422da4366fSEmmanuel Grumbach mei->csme_taking_ownership = true;
8432da4366fSEmmanuel Grumbach
84422b68fc6SAvraham Stern iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, true, true);
8455aa7ce31SAvraham Stern } else {
8465aa7ce31SAvraham Stern iwl_mei_send_sap_msg(cldev,
8475aa7ce31SAvraham Stern SAP_MSG_NOTIF_CSME_OWNERSHIP_CONFIRMED);
84806ce23adSAvraham Stern schedule_delayed_work(&mei->ownership_dwork,
84906ce23adSAvraham Stern MEI_OWNERSHIP_RETAKE_TIMEOUT_MS);
8505aa7ce31SAvraham Stern }
8512da4366fSEmmanuel Grumbach }
8522da4366fSEmmanuel Grumbach
iwl_mei_handle_nvm(struct mei_cl_device * cldev,const struct iwl_sap_nvm * sap_nvm)8532da4366fSEmmanuel Grumbach static void iwl_mei_handle_nvm(struct mei_cl_device *cldev,
8542da4366fSEmmanuel Grumbach const struct iwl_sap_nvm *sap_nvm)
8552da4366fSEmmanuel Grumbach {
8562da4366fSEmmanuel Grumbach struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
8572da4366fSEmmanuel Grumbach const struct iwl_mei_nvm *mei_nvm = (const void *)sap_nvm;
8582da4366fSEmmanuel Grumbach int i;
8592da4366fSEmmanuel Grumbach
8602da4366fSEmmanuel Grumbach kfree(mei->nvm);
8612da4366fSEmmanuel Grumbach mei->nvm = kzalloc(sizeof(*mei_nvm), GFP_KERNEL);
8622da4366fSEmmanuel Grumbach if (!mei->nvm)
8632da4366fSEmmanuel Grumbach return;
8642da4366fSEmmanuel Grumbach
8652da4366fSEmmanuel Grumbach ether_addr_copy(mei->nvm->hw_addr, sap_nvm->hw_addr);
8662da4366fSEmmanuel Grumbach mei->nvm->n_hw_addrs = sap_nvm->n_hw_addrs;
8672da4366fSEmmanuel Grumbach mei->nvm->radio_cfg = le32_to_cpu(sap_nvm->radio_cfg);
8682da4366fSEmmanuel Grumbach mei->nvm->caps = le32_to_cpu(sap_nvm->caps);
8692da4366fSEmmanuel Grumbach mei->nvm->nvm_version = le32_to_cpu(sap_nvm->nvm_version);
8702da4366fSEmmanuel Grumbach
8712da4366fSEmmanuel Grumbach for (i = 0; i < ARRAY_SIZE(mei->nvm->channels); i++)
8722da4366fSEmmanuel Grumbach mei->nvm->channels[i] = le32_to_cpu(sap_nvm->channels[i]);
8732da4366fSEmmanuel Grumbach
8742da4366fSEmmanuel Grumbach wake_up_all(&mei->get_nvm_wq);
8752da4366fSEmmanuel Grumbach }
8762da4366fSEmmanuel Grumbach
iwl_mei_handle_rx_host_own_req(struct mei_cl_device * cldev,const struct iwl_sap_msg_dw * dw)8772da4366fSEmmanuel Grumbach static void iwl_mei_handle_rx_host_own_req(struct mei_cl_device *cldev,
8782da4366fSEmmanuel Grumbach const struct iwl_sap_msg_dw *dw)
8792da4366fSEmmanuel Grumbach {
8802da4366fSEmmanuel Grumbach struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
8812da4366fSEmmanuel Grumbach
8822da4366fSEmmanuel Grumbach /*
8832da4366fSEmmanuel Grumbach * This means that we can't use the wifi device right now, CSME is not
8842da4366fSEmmanuel Grumbach * ready to let us use it.
8852da4366fSEmmanuel Grumbach */
8862da4366fSEmmanuel Grumbach if (!dw->val) {
8872da4366fSEmmanuel Grumbach dev_info(&cldev->dev, "Ownership req denied\n");
8882da4366fSEmmanuel Grumbach return;
8892da4366fSEmmanuel Grumbach }
8902da4366fSEmmanuel Grumbach
8912da4366fSEmmanuel Grumbach mei->got_ownership = true;
8922da4366fSEmmanuel Grumbach wake_up_all(&mei->get_ownership_wq);
8932da4366fSEmmanuel Grumbach
8942da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg(cldev,
8952da4366fSEmmanuel Grumbach SAP_MSG_NOTIF_HOST_OWNERSHIP_CONFIRMED);
8962da4366fSEmmanuel Grumbach
8972da4366fSEmmanuel Grumbach /* We can now start the connection, unblock rfkill */
8982da4366fSEmmanuel Grumbach if (iwl_mei_cache.ops)
89922b68fc6SAvraham Stern iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false, false);
9002da4366fSEmmanuel Grumbach }
9012da4366fSEmmanuel Grumbach
iwl_mei_handle_pldr_ack(struct mei_cl_device * cldev,const struct iwl_sap_pldr_ack_data * ack)902733eb54fSAvraham Stern static void iwl_mei_handle_pldr_ack(struct mei_cl_device *cldev,
903733eb54fSAvraham Stern const struct iwl_sap_pldr_ack_data *ack)
904733eb54fSAvraham Stern {
905733eb54fSAvraham Stern struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
906733eb54fSAvraham Stern
907733eb54fSAvraham Stern mei->pldr_active = le32_to_cpu(ack->status) == SAP_PLDR_STATUS_SUCCESS;
908733eb54fSAvraham Stern wake_up_all(&mei->pldr_wq);
909733eb54fSAvraham Stern }
910733eb54fSAvraham Stern
iwl_mei_handle_ping(struct mei_cl_device * cldev,const struct iwl_sap_hdr * hdr)9112da4366fSEmmanuel Grumbach static void iwl_mei_handle_ping(struct mei_cl_device *cldev,
9122da4366fSEmmanuel Grumbach const struct iwl_sap_hdr *hdr)
9132da4366fSEmmanuel Grumbach {
9142da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg(cldev, SAP_MSG_NOTIF_PONG);
9152da4366fSEmmanuel Grumbach }
9162da4366fSEmmanuel Grumbach
iwl_mei_handle_sap_msg(struct mei_cl_device * cldev,const struct iwl_sap_hdr * hdr)9172da4366fSEmmanuel Grumbach static void iwl_mei_handle_sap_msg(struct mei_cl_device *cldev,
9182da4366fSEmmanuel Grumbach const struct iwl_sap_hdr *hdr)
9192da4366fSEmmanuel Grumbach {
9202da4366fSEmmanuel Grumbach u16 len = le16_to_cpu(hdr->len) + sizeof(*hdr);
9212da4366fSEmmanuel Grumbach u16 type = le16_to_cpu(hdr->type);
9222da4366fSEmmanuel Grumbach
9232da4366fSEmmanuel Grumbach dev_dbg(&cldev->dev,
9242da4366fSEmmanuel Grumbach "Got a new SAP message: type %d, len %d, seq %d\n",
9252da4366fSEmmanuel Grumbach le16_to_cpu(hdr->type), len,
9262da4366fSEmmanuel Grumbach le32_to_cpu(hdr->seq_num));
9272da4366fSEmmanuel Grumbach
9282da4366fSEmmanuel Grumbach #define SAP_MSG_HANDLER(_cmd, _handler, _sz) \
9292da4366fSEmmanuel Grumbach case SAP_MSG_NOTIF_ ## _cmd: \
9302da4366fSEmmanuel Grumbach if (len < _sz) { \
9312da4366fSEmmanuel Grumbach dev_err(&cldev->dev, \
9322da4366fSEmmanuel Grumbach "Bad size for %d: %u < %u\n", \
9332da4366fSEmmanuel Grumbach le16_to_cpu(hdr->type), \
9342da4366fSEmmanuel Grumbach (unsigned int)len, \
9352da4366fSEmmanuel Grumbach (unsigned int)_sz); \
9362da4366fSEmmanuel Grumbach break; \
9372da4366fSEmmanuel Grumbach } \
9382da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex); \
9392da4366fSEmmanuel Grumbach _handler(cldev, (const void *)hdr); \
9402da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex); \
9412da4366fSEmmanuel Grumbach break
9422da4366fSEmmanuel Grumbach
9432da4366fSEmmanuel Grumbach #define SAP_MSG_HANDLER_NO_LOCK(_cmd, _handler, _sz) \
9442da4366fSEmmanuel Grumbach case SAP_MSG_NOTIF_ ## _cmd: \
9452da4366fSEmmanuel Grumbach if (len < _sz) { \
9462da4366fSEmmanuel Grumbach dev_err(&cldev->dev, \
9472da4366fSEmmanuel Grumbach "Bad size for %d: %u < %u\n", \
9482da4366fSEmmanuel Grumbach le16_to_cpu(hdr->type), \
9492da4366fSEmmanuel Grumbach (unsigned int)len, \
9502da4366fSEmmanuel Grumbach (unsigned int)_sz); \
9512da4366fSEmmanuel Grumbach break; \
9522da4366fSEmmanuel Grumbach } \
9532da4366fSEmmanuel Grumbach _handler(cldev, (const void *)hdr); \
9542da4366fSEmmanuel Grumbach break
9552da4366fSEmmanuel Grumbach
9562da4366fSEmmanuel Grumbach #define SAP_MSG_HANDLER_NO_HANDLER(_cmd, _sz) \
9572da4366fSEmmanuel Grumbach case SAP_MSG_NOTIF_ ## _cmd: \
9582da4366fSEmmanuel Grumbach if (len < _sz) { \
9592da4366fSEmmanuel Grumbach dev_err(&cldev->dev, \
9602da4366fSEmmanuel Grumbach "Bad size for %d: %u < %u\n", \
9612da4366fSEmmanuel Grumbach le16_to_cpu(hdr->type), \
9622da4366fSEmmanuel Grumbach (unsigned int)len, \
9632da4366fSEmmanuel Grumbach (unsigned int)_sz); \
9642da4366fSEmmanuel Grumbach break; \
9652da4366fSEmmanuel Grumbach } \
9662da4366fSEmmanuel Grumbach break
9672da4366fSEmmanuel Grumbach
9682da4366fSEmmanuel Grumbach switch (type) {
9692da4366fSEmmanuel Grumbach SAP_MSG_HANDLER(PING, iwl_mei_handle_ping, 0);
9702da4366fSEmmanuel Grumbach SAP_MSG_HANDLER(CSME_FILTERS,
9712da4366fSEmmanuel Grumbach iwl_mei_handle_csme_filters,
9722da4366fSEmmanuel Grumbach sizeof(struct iwl_sap_csme_filters));
9732da4366fSEmmanuel Grumbach SAP_MSG_HANDLER(CSME_CONN_STATUS,
9742da4366fSEmmanuel Grumbach iwl_mei_handle_conn_status,
9752da4366fSEmmanuel Grumbach sizeof(struct iwl_sap_notif_conn_status));
9762da4366fSEmmanuel Grumbach SAP_MSG_HANDLER_NO_LOCK(AMT_STATE,
9772da4366fSEmmanuel Grumbach iwl_mei_handle_amt_state,
9782da4366fSEmmanuel Grumbach sizeof(struct iwl_sap_msg_dw));
9792da4366fSEmmanuel Grumbach SAP_MSG_HANDLER_NO_HANDLER(PONG, 0);
9802da4366fSEmmanuel Grumbach SAP_MSG_HANDLER(NVM, iwl_mei_handle_nvm,
9812da4366fSEmmanuel Grumbach sizeof(struct iwl_sap_nvm));
9822da4366fSEmmanuel Grumbach SAP_MSG_HANDLER(CSME_REPLY_TO_HOST_OWNERSHIP_REQ,
9832da4366fSEmmanuel Grumbach iwl_mei_handle_rx_host_own_req,
9842da4366fSEmmanuel Grumbach sizeof(struct iwl_sap_msg_dw));
9852da4366fSEmmanuel Grumbach SAP_MSG_HANDLER(NIC_OWNER, iwl_mei_handle_nic_owner,
9862da4366fSEmmanuel Grumbach sizeof(struct iwl_sap_msg_dw));
9872da4366fSEmmanuel Grumbach SAP_MSG_HANDLER(CSME_CAN_RELEASE_OWNERSHIP,
9882da4366fSEmmanuel Grumbach iwl_mei_handle_can_release_ownership, 0);
9892da4366fSEmmanuel Grumbach SAP_MSG_HANDLER(CSME_TAKING_OWNERSHIP,
9902da4366fSEmmanuel Grumbach iwl_mei_handle_csme_taking_ownership, 0);
991733eb54fSAvraham Stern SAP_MSG_HANDLER(PLDR_ACK, iwl_mei_handle_pldr_ack,
992733eb54fSAvraham Stern sizeof(struct iwl_sap_pldr_ack_data));
9932da4366fSEmmanuel Grumbach default:
9942da4366fSEmmanuel Grumbach /*
9952da4366fSEmmanuel Grumbach * This is not really an error, there are message that we decided
9962da4366fSEmmanuel Grumbach * to ignore, yet, it is useful to be able to leave a note if debug
9972da4366fSEmmanuel Grumbach * is enabled.
9982da4366fSEmmanuel Grumbach */
9992da4366fSEmmanuel Grumbach dev_dbg(&cldev->dev, "Unsupported message: type %d, len %d\n",
10002da4366fSEmmanuel Grumbach le16_to_cpu(hdr->type), len);
10012da4366fSEmmanuel Grumbach }
10022da4366fSEmmanuel Grumbach
10032da4366fSEmmanuel Grumbach #undef SAP_MSG_HANDLER
10042da4366fSEmmanuel Grumbach #undef SAP_MSG_HANDLER_NO_LOCK
10052da4366fSEmmanuel Grumbach }
10062da4366fSEmmanuel Grumbach
iwl_mei_read_from_q(const u8 * q_head,u32 q_sz,u32 * _rd,u32 wr,void * _buf,u32 len)10072da4366fSEmmanuel Grumbach static void iwl_mei_read_from_q(const u8 *q_head, u32 q_sz,
10082da4366fSEmmanuel Grumbach u32 *_rd, u32 wr,
10092da4366fSEmmanuel Grumbach void *_buf, u32 len)
10102da4366fSEmmanuel Grumbach {
10112da4366fSEmmanuel Grumbach u8 *buf = _buf;
10122da4366fSEmmanuel Grumbach u32 rd = *_rd;
10132da4366fSEmmanuel Grumbach
10142da4366fSEmmanuel Grumbach if (rd + len <= q_sz) {
10152da4366fSEmmanuel Grumbach memcpy(buf, q_head + rd, len);
10162da4366fSEmmanuel Grumbach rd += len;
10172da4366fSEmmanuel Grumbach } else {
10182da4366fSEmmanuel Grumbach memcpy(buf, q_head + rd, q_sz - rd);
10192da4366fSEmmanuel Grumbach memcpy(buf + q_sz - rd, q_head, len - (q_sz - rd));
10202da4366fSEmmanuel Grumbach rd = len - (q_sz - rd);
10212da4366fSEmmanuel Grumbach }
10222da4366fSEmmanuel Grumbach
10232da4366fSEmmanuel Grumbach *_rd = rd;
10242da4366fSEmmanuel Grumbach }
10252da4366fSEmmanuel Grumbach
10262da4366fSEmmanuel Grumbach #define QOS_HDR_IV_SNAP_LEN (sizeof(struct ieee80211_qos_hdr) + \
10272da4366fSEmmanuel Grumbach IEEE80211_TKIP_IV_LEN + \
10282da4366fSEmmanuel Grumbach sizeof(rfc1042_header) + ETH_TLEN)
10292da4366fSEmmanuel Grumbach
iwl_mei_handle_sap_data(struct mei_cl_device * cldev,const u8 * q_head,u32 q_sz,u32 rd,u32 wr,ssize_t valid_rx_sz,struct sk_buff_head * tx_skbs)10302da4366fSEmmanuel Grumbach static void iwl_mei_handle_sap_data(struct mei_cl_device *cldev,
10312da4366fSEmmanuel Grumbach const u8 *q_head, u32 q_sz,
10322da4366fSEmmanuel Grumbach u32 rd, u32 wr, ssize_t valid_rx_sz,
10332da4366fSEmmanuel Grumbach struct sk_buff_head *tx_skbs)
10342da4366fSEmmanuel Grumbach {
10352da4366fSEmmanuel Grumbach struct iwl_sap_hdr hdr;
10362da4366fSEmmanuel Grumbach struct net_device *netdev =
10372da4366fSEmmanuel Grumbach rcu_dereference_protected(iwl_mei_cache.netdev,
10382da4366fSEmmanuel Grumbach lockdep_is_held(&iwl_mei_mutex));
10392da4366fSEmmanuel Grumbach
10402da4366fSEmmanuel Grumbach if (!netdev)
10412da4366fSEmmanuel Grumbach return;
10422da4366fSEmmanuel Grumbach
10432da4366fSEmmanuel Grumbach while (valid_rx_sz >= sizeof(hdr)) {
10442da4366fSEmmanuel Grumbach struct ethhdr *ethhdr;
10452da4366fSEmmanuel Grumbach unsigned char *data;
10462da4366fSEmmanuel Grumbach struct sk_buff *skb;
10472da4366fSEmmanuel Grumbach u16 len;
10482da4366fSEmmanuel Grumbach
10492da4366fSEmmanuel Grumbach iwl_mei_read_from_q(q_head, q_sz, &rd, wr, &hdr, sizeof(hdr));
10502da4366fSEmmanuel Grumbach valid_rx_sz -= sizeof(hdr);
10512da4366fSEmmanuel Grumbach len = le16_to_cpu(hdr.len);
10522da4366fSEmmanuel Grumbach
10532da4366fSEmmanuel Grumbach if (valid_rx_sz < len) {
10542da4366fSEmmanuel Grumbach dev_err(&cldev->dev,
10552da4366fSEmmanuel Grumbach "Data queue is corrupted: valid data len %zd, len %d\n",
10562da4366fSEmmanuel Grumbach valid_rx_sz, len);
10572da4366fSEmmanuel Grumbach break;
10582da4366fSEmmanuel Grumbach }
10592da4366fSEmmanuel Grumbach
10602da4366fSEmmanuel Grumbach if (len < sizeof(*ethhdr)) {
10612da4366fSEmmanuel Grumbach dev_err(&cldev->dev,
10622da4366fSEmmanuel Grumbach "Data len is smaller than an ethernet header? len = %d\n",
10632da4366fSEmmanuel Grumbach len);
10642da4366fSEmmanuel Grumbach }
10652da4366fSEmmanuel Grumbach
10662da4366fSEmmanuel Grumbach valid_rx_sz -= len;
10672da4366fSEmmanuel Grumbach
10682da4366fSEmmanuel Grumbach if (le16_to_cpu(hdr.type) != SAP_MSG_DATA_PACKET) {
10692da4366fSEmmanuel Grumbach dev_err(&cldev->dev, "Unsupported Rx data: type %d, len %d\n",
10702da4366fSEmmanuel Grumbach le16_to_cpu(hdr.type), len);
10712da4366fSEmmanuel Grumbach continue;
10722da4366fSEmmanuel Grumbach }
10732da4366fSEmmanuel Grumbach
10742da4366fSEmmanuel Grumbach /* We need enough room for the WiFi header + SNAP + IV */
10752da4366fSEmmanuel Grumbach skb = netdev_alloc_skb(netdev, len + QOS_HDR_IV_SNAP_LEN);
107678488a64SJohannes Berg if (!skb)
107778488a64SJohannes Berg continue;
10782da4366fSEmmanuel Grumbach
10792da4366fSEmmanuel Grumbach skb_reserve(skb, QOS_HDR_IV_SNAP_LEN);
10802da4366fSEmmanuel Grumbach ethhdr = skb_push(skb, sizeof(*ethhdr));
10812da4366fSEmmanuel Grumbach
10822da4366fSEmmanuel Grumbach iwl_mei_read_from_q(q_head, q_sz, &rd, wr,
10832da4366fSEmmanuel Grumbach ethhdr, sizeof(*ethhdr));
10842da4366fSEmmanuel Grumbach len -= sizeof(*ethhdr);
10852da4366fSEmmanuel Grumbach
10862da4366fSEmmanuel Grumbach skb_reset_mac_header(skb);
10872da4366fSEmmanuel Grumbach skb_reset_network_header(skb);
10882da4366fSEmmanuel Grumbach skb->protocol = ethhdr->h_proto;
10892da4366fSEmmanuel Grumbach
10902da4366fSEmmanuel Grumbach data = skb_put(skb, len);
10912da4366fSEmmanuel Grumbach iwl_mei_read_from_q(q_head, q_sz, &rd, wr, data, len);
10922da4366fSEmmanuel Grumbach
10932da4366fSEmmanuel Grumbach /*
10942da4366fSEmmanuel Grumbach * Enqueue the skb here so that it can be sent later when we
10952da4366fSEmmanuel Grumbach * do not hold the mutex. TX'ing a packet with a mutex held is
10962da4366fSEmmanuel Grumbach * possible, but it wouldn't be nice to forbid the TX path to
10972da4366fSEmmanuel Grumbach * call any of iwlmei's functions, since every API from iwlmei
10982da4366fSEmmanuel Grumbach * needs the mutex.
10992da4366fSEmmanuel Grumbach */
11002da4366fSEmmanuel Grumbach __skb_queue_tail(tx_skbs, skb);
11012da4366fSEmmanuel Grumbach }
11022da4366fSEmmanuel Grumbach }
11032da4366fSEmmanuel Grumbach
iwl_mei_handle_sap_rx_cmd(struct mei_cl_device * cldev,const u8 * q_head,u32 q_sz,u32 rd,u32 wr,ssize_t valid_rx_sz)11042da4366fSEmmanuel Grumbach static void iwl_mei_handle_sap_rx_cmd(struct mei_cl_device *cldev,
11052da4366fSEmmanuel Grumbach const u8 *q_head, u32 q_sz,
11062da4366fSEmmanuel Grumbach u32 rd, u32 wr, ssize_t valid_rx_sz)
11072da4366fSEmmanuel Grumbach {
11082da4366fSEmmanuel Grumbach struct page *p = alloc_page(GFP_KERNEL);
11092da4366fSEmmanuel Grumbach struct iwl_sap_hdr *hdr;
11102da4366fSEmmanuel Grumbach
11112da4366fSEmmanuel Grumbach if (!p)
11122da4366fSEmmanuel Grumbach return;
11132da4366fSEmmanuel Grumbach
11142da4366fSEmmanuel Grumbach hdr = page_address(p);
11152da4366fSEmmanuel Grumbach
11162da4366fSEmmanuel Grumbach while (valid_rx_sz >= sizeof(*hdr)) {
11172da4366fSEmmanuel Grumbach u16 len;
11182da4366fSEmmanuel Grumbach
11192da4366fSEmmanuel Grumbach iwl_mei_read_from_q(q_head, q_sz, &rd, wr, hdr, sizeof(*hdr));
11202da4366fSEmmanuel Grumbach valid_rx_sz -= sizeof(*hdr);
11212da4366fSEmmanuel Grumbach len = le16_to_cpu(hdr->len);
11222da4366fSEmmanuel Grumbach
11232da4366fSEmmanuel Grumbach if (valid_rx_sz < len)
11242da4366fSEmmanuel Grumbach break;
11252da4366fSEmmanuel Grumbach
11262da4366fSEmmanuel Grumbach iwl_mei_read_from_q(q_head, q_sz, &rd, wr, hdr + 1, len);
11272da4366fSEmmanuel Grumbach
11282da4366fSEmmanuel Grumbach trace_iwlmei_sap_cmd(hdr, false);
11292da4366fSEmmanuel Grumbach iwl_mei_handle_sap_msg(cldev, hdr);
11302da4366fSEmmanuel Grumbach valid_rx_sz -= len;
11312da4366fSEmmanuel Grumbach }
11322da4366fSEmmanuel Grumbach
11332da4366fSEmmanuel Grumbach /* valid_rx_sz must be 0 now... */
11342da4366fSEmmanuel Grumbach if (valid_rx_sz)
11352da4366fSEmmanuel Grumbach dev_err(&cldev->dev,
11362da4366fSEmmanuel Grumbach "More data in the buffer although we read it all\n");
11372da4366fSEmmanuel Grumbach
11382da4366fSEmmanuel Grumbach __free_page(p);
11392da4366fSEmmanuel Grumbach }
11402da4366fSEmmanuel Grumbach
iwl_mei_handle_sap_rx(struct mei_cl_device * cldev,struct iwl_sap_q_ctrl_blk * notif_q,const u8 * q_head,struct sk_buff_head * skbs,u32 q_sz)11412da4366fSEmmanuel Grumbach static void iwl_mei_handle_sap_rx(struct mei_cl_device *cldev,
11422da4366fSEmmanuel Grumbach struct iwl_sap_q_ctrl_blk *notif_q,
11432da4366fSEmmanuel Grumbach const u8 *q_head,
114465229160SEmmanuel Grumbach struct sk_buff_head *skbs,
114565229160SEmmanuel Grumbach u32 q_sz)
11462da4366fSEmmanuel Grumbach {
11472da4366fSEmmanuel Grumbach u32 rd = le32_to_cpu(READ_ONCE(notif_q->rd_ptr));
11482da4366fSEmmanuel Grumbach u32 wr = le32_to_cpu(READ_ONCE(notif_q->wr_ptr));
11492da4366fSEmmanuel Grumbach ssize_t valid_rx_sz;
11502da4366fSEmmanuel Grumbach
11512da4366fSEmmanuel Grumbach if (rd > q_sz || wr > q_sz) {
11522da4366fSEmmanuel Grumbach dev_err(&cldev->dev,
11532da4366fSEmmanuel Grumbach "Pointers are past the buffer limit\n");
11542da4366fSEmmanuel Grumbach return;
11552da4366fSEmmanuel Grumbach }
11562da4366fSEmmanuel Grumbach
11572da4366fSEmmanuel Grumbach if (rd == wr)
11582da4366fSEmmanuel Grumbach return;
11592da4366fSEmmanuel Grumbach
11602da4366fSEmmanuel Grumbach valid_rx_sz = wr > rd ? wr - rd : q_sz - rd + wr;
11612da4366fSEmmanuel Grumbach
11622da4366fSEmmanuel Grumbach if (skbs)
11632da4366fSEmmanuel Grumbach iwl_mei_handle_sap_data(cldev, q_head, q_sz, rd, wr,
11642da4366fSEmmanuel Grumbach valid_rx_sz, skbs);
11652da4366fSEmmanuel Grumbach else
11662da4366fSEmmanuel Grumbach iwl_mei_handle_sap_rx_cmd(cldev, q_head, q_sz, rd, wr,
11672da4366fSEmmanuel Grumbach valid_rx_sz);
11682da4366fSEmmanuel Grumbach
11692da4366fSEmmanuel Grumbach /* Increment the read pointer to point to the write pointer */
11702da4366fSEmmanuel Grumbach WRITE_ONCE(notif_q->rd_ptr, cpu_to_le32(wr));
11712da4366fSEmmanuel Grumbach }
11722da4366fSEmmanuel Grumbach
iwl_mei_handle_check_shared_area(struct mei_cl_device * cldev)11732da4366fSEmmanuel Grumbach static void iwl_mei_handle_check_shared_area(struct mei_cl_device *cldev)
11742da4366fSEmmanuel Grumbach {
11752da4366fSEmmanuel Grumbach struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
11762da4366fSEmmanuel Grumbach struct iwl_sap_q_ctrl_blk *notif_q;
11772da4366fSEmmanuel Grumbach struct sk_buff_head tx_skbs;
11782da4366fSEmmanuel Grumbach struct iwl_sap_dir *dir;
11792da4366fSEmmanuel Grumbach void *q_head;
118065229160SEmmanuel Grumbach u32 q_sz;
11812da4366fSEmmanuel Grumbach
11822da4366fSEmmanuel Grumbach if (!mei->shared_mem.ctrl)
11832da4366fSEmmanuel Grumbach return;
11842da4366fSEmmanuel Grumbach
11852da4366fSEmmanuel Grumbach dir = &mei->shared_mem.ctrl->dir[SAP_DIRECTION_ME_TO_HOST];
11862da4366fSEmmanuel Grumbach notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_NOTIF];
11872da4366fSEmmanuel Grumbach q_head = mei->shared_mem.q_head[SAP_DIRECTION_ME_TO_HOST][SAP_QUEUE_IDX_NOTIF];
118865229160SEmmanuel Grumbach q_sz = mei->shared_mem.q_size[SAP_DIRECTION_ME_TO_HOST][SAP_QUEUE_IDX_NOTIF];
11892da4366fSEmmanuel Grumbach
11902da4366fSEmmanuel Grumbach /*
11912da4366fSEmmanuel Grumbach * Do not hold the mutex here, but rather each and every message
11922da4366fSEmmanuel Grumbach * handler takes it.
11932da4366fSEmmanuel Grumbach * This allows message handlers to take it at a certain time.
11942da4366fSEmmanuel Grumbach */
119565229160SEmmanuel Grumbach iwl_mei_handle_sap_rx(cldev, notif_q, q_head, NULL, q_sz);
11962da4366fSEmmanuel Grumbach
11972da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
11982da4366fSEmmanuel Grumbach dir = &mei->shared_mem.ctrl->dir[SAP_DIRECTION_ME_TO_HOST];
11992da4366fSEmmanuel Grumbach notif_q = &dir->q_ctrl_blk[SAP_QUEUE_IDX_DATA];
12002da4366fSEmmanuel Grumbach q_head = mei->shared_mem.q_head[SAP_DIRECTION_ME_TO_HOST][SAP_QUEUE_IDX_DATA];
120165229160SEmmanuel Grumbach q_sz = mei->shared_mem.q_size[SAP_DIRECTION_ME_TO_HOST][SAP_QUEUE_IDX_DATA];
12022da4366fSEmmanuel Grumbach
12032da4366fSEmmanuel Grumbach __skb_queue_head_init(&tx_skbs);
12042da4366fSEmmanuel Grumbach
120565229160SEmmanuel Grumbach iwl_mei_handle_sap_rx(cldev, notif_q, q_head, &tx_skbs, q_sz);
12062da4366fSEmmanuel Grumbach
12072da4366fSEmmanuel Grumbach if (skb_queue_empty(&tx_skbs)) {
12082da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
12092da4366fSEmmanuel Grumbach return;
12102da4366fSEmmanuel Grumbach }
12112da4366fSEmmanuel Grumbach
12122da4366fSEmmanuel Grumbach /*
12132da4366fSEmmanuel Grumbach * Take the RCU read lock before we unlock the mutex to make sure that
12142da4366fSEmmanuel Grumbach * even if the netdev is replaced by another non-NULL netdev right after
12152da4366fSEmmanuel Grumbach * we unlock the mutex, the old netdev will still be valid when we
12162da4366fSEmmanuel Grumbach * transmit the frames. We can't allow to replace the netdev here because
12172da4366fSEmmanuel Grumbach * the skbs hold a pointer to the netdev.
12182da4366fSEmmanuel Grumbach */
12192da4366fSEmmanuel Grumbach rcu_read_lock();
12202da4366fSEmmanuel Grumbach
12212da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
12222da4366fSEmmanuel Grumbach
12232da4366fSEmmanuel Grumbach if (!rcu_access_pointer(iwl_mei_cache.netdev)) {
12242da4366fSEmmanuel Grumbach dev_err(&cldev->dev, "Can't Tx without a netdev\n");
12252da4366fSEmmanuel Grumbach skb_queue_purge(&tx_skbs);
12262da4366fSEmmanuel Grumbach goto out;
12272da4366fSEmmanuel Grumbach }
12282da4366fSEmmanuel Grumbach
12292da4366fSEmmanuel Grumbach while (!skb_queue_empty(&tx_skbs)) {
12302da4366fSEmmanuel Grumbach struct sk_buff *skb = __skb_dequeue(&tx_skbs);
12312da4366fSEmmanuel Grumbach
12322da4366fSEmmanuel Grumbach trace_iwlmei_sap_data(skb, IWL_SAP_RX_DATA_TO_AIR);
12332da4366fSEmmanuel Grumbach dev_queue_xmit(skb);
12342da4366fSEmmanuel Grumbach }
12352da4366fSEmmanuel Grumbach
12362da4366fSEmmanuel Grumbach out:
12372da4366fSEmmanuel Grumbach rcu_read_unlock();
12382da4366fSEmmanuel Grumbach }
12392da4366fSEmmanuel Grumbach
iwl_mei_rx(struct mei_cl_device * cldev)12402da4366fSEmmanuel Grumbach static void iwl_mei_rx(struct mei_cl_device *cldev)
12412da4366fSEmmanuel Grumbach {
12422da4366fSEmmanuel Grumbach struct iwl_sap_me_msg_hdr *hdr;
12432da4366fSEmmanuel Grumbach u8 msg[100];
12442da4366fSEmmanuel Grumbach ssize_t ret;
12452da4366fSEmmanuel Grumbach
12462da4366fSEmmanuel Grumbach ret = mei_cldev_recv(cldev, (u8 *)&msg, sizeof(msg));
12472da4366fSEmmanuel Grumbach if (ret < 0) {
12482da4366fSEmmanuel Grumbach dev_err(&cldev->dev, "failed to receive data: %zd\n", ret);
12492da4366fSEmmanuel Grumbach return;
12502da4366fSEmmanuel Grumbach }
12512da4366fSEmmanuel Grumbach
12522da4366fSEmmanuel Grumbach if (ret == 0) {
12532da4366fSEmmanuel Grumbach dev_err(&cldev->dev, "got an empty response\n");
12542da4366fSEmmanuel Grumbach return;
12552da4366fSEmmanuel Grumbach }
12562da4366fSEmmanuel Grumbach
12572da4366fSEmmanuel Grumbach hdr = (void *)msg;
12582da4366fSEmmanuel Grumbach trace_iwlmei_me_msg(hdr, false);
12592da4366fSEmmanuel Grumbach
12602da4366fSEmmanuel Grumbach switch (le32_to_cpu(hdr->type)) {
12612da4366fSEmmanuel Grumbach case SAP_ME_MSG_START_OK:
12622da4366fSEmmanuel Grumbach BUILD_BUG_ON(sizeof(struct iwl_sap_me_msg_start_ok) >
12632da4366fSEmmanuel Grumbach sizeof(msg));
12642da4366fSEmmanuel Grumbach
12652da4366fSEmmanuel Grumbach iwl_mei_handle_rx_start_ok(cldev, (void *)msg, ret);
12662da4366fSEmmanuel Grumbach break;
12672da4366fSEmmanuel Grumbach case SAP_ME_MSG_CHECK_SHARED_AREA:
12682da4366fSEmmanuel Grumbach iwl_mei_handle_check_shared_area(cldev);
12692da4366fSEmmanuel Grumbach break;
12702da4366fSEmmanuel Grumbach default:
12712da4366fSEmmanuel Grumbach dev_err(&cldev->dev, "got a RX notification: %d\n",
12722da4366fSEmmanuel Grumbach le32_to_cpu(hdr->type));
12732da4366fSEmmanuel Grumbach break;
12742da4366fSEmmanuel Grumbach }
12752da4366fSEmmanuel Grumbach }
12762da4366fSEmmanuel Grumbach
iwl_mei_send_start(struct mei_cl_device * cldev)12772da4366fSEmmanuel Grumbach static int iwl_mei_send_start(struct mei_cl_device *cldev)
12782da4366fSEmmanuel Grumbach {
12792da4366fSEmmanuel Grumbach struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
12802da4366fSEmmanuel Grumbach struct iwl_sap_me_msg_start msg = {
12812da4366fSEmmanuel Grumbach .hdr.type = cpu_to_le32(SAP_ME_MSG_START),
12822da4366fSEmmanuel Grumbach .hdr.seq_num = cpu_to_le32(atomic_inc_return(&mei->seq_no)),
12832da4366fSEmmanuel Grumbach .hdr.len = cpu_to_le32(sizeof(msg)),
12842da4366fSEmmanuel Grumbach .supported_versions[0] = SAP_VERSION,
12852da4366fSEmmanuel Grumbach .init_data_seq_num = cpu_to_le16(0x100),
12862da4366fSEmmanuel Grumbach .init_notif_seq_num = cpu_to_le16(0x800),
12872da4366fSEmmanuel Grumbach };
12882da4366fSEmmanuel Grumbach int ret;
12892da4366fSEmmanuel Grumbach
12902da4366fSEmmanuel Grumbach trace_iwlmei_me_msg(&msg.hdr, true);
12912da4366fSEmmanuel Grumbach ret = mei_cldev_send(cldev, (void *)&msg, sizeof(msg));
12922da4366fSEmmanuel Grumbach if (ret != sizeof(msg)) {
12932da4366fSEmmanuel Grumbach dev_err(&cldev->dev,
12942da4366fSEmmanuel Grumbach "failed to send the SAP_ME_MSG_START message %d\n",
12952da4366fSEmmanuel Grumbach ret);
12962da4366fSEmmanuel Grumbach return ret;
12972da4366fSEmmanuel Grumbach }
12982da4366fSEmmanuel Grumbach
12992da4366fSEmmanuel Grumbach return 0;
13002da4366fSEmmanuel Grumbach }
13012da4366fSEmmanuel Grumbach
iwl_mei_enable(struct mei_cl_device * cldev)13022da4366fSEmmanuel Grumbach static int iwl_mei_enable(struct mei_cl_device *cldev)
13032da4366fSEmmanuel Grumbach {
13042da4366fSEmmanuel Grumbach int ret;
13052da4366fSEmmanuel Grumbach
13062da4366fSEmmanuel Grumbach ret = mei_cldev_enable(cldev);
13072da4366fSEmmanuel Grumbach if (ret < 0) {
13082da4366fSEmmanuel Grumbach dev_err(&cldev->dev, "failed to enable the device: %d\n", ret);
13092da4366fSEmmanuel Grumbach return ret;
13102da4366fSEmmanuel Grumbach }
13112da4366fSEmmanuel Grumbach
13122da4366fSEmmanuel Grumbach ret = mei_cldev_register_rx_cb(cldev, iwl_mei_rx);
13132da4366fSEmmanuel Grumbach if (ret) {
13142da4366fSEmmanuel Grumbach dev_err(&cldev->dev,
13152da4366fSEmmanuel Grumbach "failed to register to the rx cb: %d\n", ret);
13162da4366fSEmmanuel Grumbach mei_cldev_disable(cldev);
13172da4366fSEmmanuel Grumbach return ret;
13182da4366fSEmmanuel Grumbach }
13192da4366fSEmmanuel Grumbach
13202da4366fSEmmanuel Grumbach return 0;
13212da4366fSEmmanuel Grumbach }
13222da4366fSEmmanuel Grumbach
iwl_mei_get_nvm(void)13232da4366fSEmmanuel Grumbach struct iwl_mei_nvm *iwl_mei_get_nvm(void)
13242da4366fSEmmanuel Grumbach {
13252da4366fSEmmanuel Grumbach struct iwl_mei_nvm *nvm = NULL;
13262da4366fSEmmanuel Grumbach struct iwl_mei *mei;
13272da4366fSEmmanuel Grumbach int ret;
13282da4366fSEmmanuel Grumbach
13292da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
13302da4366fSEmmanuel Grumbach
13312da4366fSEmmanuel Grumbach if (!iwl_mei_is_connected())
13322da4366fSEmmanuel Grumbach goto out;
13332da4366fSEmmanuel Grumbach
13342da4366fSEmmanuel Grumbach mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
13352da4366fSEmmanuel Grumbach
13362da4366fSEmmanuel Grumbach if (!mei)
13372da4366fSEmmanuel Grumbach goto out;
13382da4366fSEmmanuel Grumbach
13392da4366fSEmmanuel Grumbach ret = iwl_mei_send_sap_msg(iwl_mei_global_cldev,
13402da4366fSEmmanuel Grumbach SAP_MSG_NOTIF_GET_NVM);
13412da4366fSEmmanuel Grumbach if (ret)
13422da4366fSEmmanuel Grumbach goto out;
13432da4366fSEmmanuel Grumbach
13442da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
13452da4366fSEmmanuel Grumbach
13462da4366fSEmmanuel Grumbach ret = wait_event_timeout(mei->get_nvm_wq, mei->nvm, 2 * HZ);
13472da4366fSEmmanuel Grumbach if (!ret)
13482da4366fSEmmanuel Grumbach return NULL;
13492da4366fSEmmanuel Grumbach
13502da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
13512da4366fSEmmanuel Grumbach
13522da4366fSEmmanuel Grumbach if (!iwl_mei_is_connected())
13532da4366fSEmmanuel Grumbach goto out;
13542da4366fSEmmanuel Grumbach
13552da4366fSEmmanuel Grumbach mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
13562da4366fSEmmanuel Grumbach
13572da4366fSEmmanuel Grumbach if (!mei)
13582da4366fSEmmanuel Grumbach goto out;
13592da4366fSEmmanuel Grumbach
13602da4366fSEmmanuel Grumbach if (mei->nvm)
13612da4366fSEmmanuel Grumbach nvm = kmemdup(mei->nvm, sizeof(*mei->nvm), GFP_KERNEL);
13622da4366fSEmmanuel Grumbach
13632da4366fSEmmanuel Grumbach out:
13642da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
13652da4366fSEmmanuel Grumbach return nvm;
13662da4366fSEmmanuel Grumbach }
13672da4366fSEmmanuel Grumbach EXPORT_SYMBOL_GPL(iwl_mei_get_nvm);
13682da4366fSEmmanuel Grumbach
1369733eb54fSAvraham Stern #define IWL_MEI_PLDR_NUM_RETRIES 3
1370733eb54fSAvraham Stern
iwl_mei_pldr_req(void)1371733eb54fSAvraham Stern int iwl_mei_pldr_req(void)
1372733eb54fSAvraham Stern {
1373733eb54fSAvraham Stern struct iwl_mei *mei;
1374733eb54fSAvraham Stern int ret;
1375733eb54fSAvraham Stern struct iwl_sap_pldr_data msg = {
1376733eb54fSAvraham Stern .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_PLDR),
1377733eb54fSAvraham Stern .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
1378733eb54fSAvraham Stern };
1379733eb54fSAvraham Stern int i;
1380733eb54fSAvraham Stern
1381733eb54fSAvraham Stern mutex_lock(&iwl_mei_mutex);
1382733eb54fSAvraham Stern
1383733eb54fSAvraham Stern /* In case we didn't have a bind */
1384733eb54fSAvraham Stern if (!iwl_mei_is_connected()) {
1385733eb54fSAvraham Stern ret = 0;
1386733eb54fSAvraham Stern goto out;
1387733eb54fSAvraham Stern }
1388733eb54fSAvraham Stern
1389733eb54fSAvraham Stern mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
1390733eb54fSAvraham Stern
1391733eb54fSAvraham Stern if (!mei) {
1392733eb54fSAvraham Stern ret = -ENODEV;
1393733eb54fSAvraham Stern goto out;
1394733eb54fSAvraham Stern }
1395733eb54fSAvraham Stern
1396733eb54fSAvraham Stern if (!mei->amt_enabled) {
1397733eb54fSAvraham Stern ret = 0;
1398733eb54fSAvraham Stern goto out;
1399733eb54fSAvraham Stern }
1400733eb54fSAvraham Stern
1401733eb54fSAvraham Stern for (i = 0; i < IWL_MEI_PLDR_NUM_RETRIES; i++) {
1402733eb54fSAvraham Stern ret = iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
1403733eb54fSAvraham Stern mutex_unlock(&iwl_mei_mutex);
1404733eb54fSAvraham Stern if (ret)
1405733eb54fSAvraham Stern return ret;
1406733eb54fSAvraham Stern
1407733eb54fSAvraham Stern ret = wait_event_timeout(mei->pldr_wq, mei->pldr_active, HZ / 2);
1408733eb54fSAvraham Stern if (ret)
1409733eb54fSAvraham Stern break;
1410733eb54fSAvraham Stern
1411733eb54fSAvraham Stern /* Take the mutex for the next iteration */
1412733eb54fSAvraham Stern mutex_lock(&iwl_mei_mutex);
1413733eb54fSAvraham Stern }
1414733eb54fSAvraham Stern
1415733eb54fSAvraham Stern if (ret)
1416733eb54fSAvraham Stern return 0;
1417733eb54fSAvraham Stern
1418733eb54fSAvraham Stern ret = -ETIMEDOUT;
1419733eb54fSAvraham Stern out:
1420733eb54fSAvraham Stern mutex_unlock(&iwl_mei_mutex);
1421733eb54fSAvraham Stern return ret;
1422733eb54fSAvraham Stern }
1423733eb54fSAvraham Stern EXPORT_SYMBOL_GPL(iwl_mei_pldr_req);
1424733eb54fSAvraham Stern
iwl_mei_get_ownership(void)14252da4366fSEmmanuel Grumbach int iwl_mei_get_ownership(void)
14262da4366fSEmmanuel Grumbach {
14272da4366fSEmmanuel Grumbach struct iwl_mei *mei;
14282da4366fSEmmanuel Grumbach int ret;
14292da4366fSEmmanuel Grumbach
14302da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
14312da4366fSEmmanuel Grumbach
14322da4366fSEmmanuel Grumbach /* In case we didn't have a bind */
14332da4366fSEmmanuel Grumbach if (!iwl_mei_is_connected()) {
14342da4366fSEmmanuel Grumbach ret = 0;
14352da4366fSEmmanuel Grumbach goto out;
14362da4366fSEmmanuel Grumbach }
14372da4366fSEmmanuel Grumbach
14382da4366fSEmmanuel Grumbach mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
14392da4366fSEmmanuel Grumbach
14402da4366fSEmmanuel Grumbach if (!mei) {
14412da4366fSEmmanuel Grumbach ret = -ENODEV;
14422da4366fSEmmanuel Grumbach goto out;
14432da4366fSEmmanuel Grumbach }
14442da4366fSEmmanuel Grumbach
14452da4366fSEmmanuel Grumbach if (!mei->amt_enabled) {
14462da4366fSEmmanuel Grumbach ret = 0;
14472da4366fSEmmanuel Grumbach goto out;
14482da4366fSEmmanuel Grumbach }
14492da4366fSEmmanuel Grumbach
14502da4366fSEmmanuel Grumbach if (mei->got_ownership) {
14512da4366fSEmmanuel Grumbach ret = 0;
14522da4366fSEmmanuel Grumbach goto out;
14532da4366fSEmmanuel Grumbach }
14542da4366fSEmmanuel Grumbach
14552da4366fSEmmanuel Grumbach ret = iwl_mei_send_sap_msg(mei->cldev,
14562da4366fSEmmanuel Grumbach SAP_MSG_NOTIF_HOST_ASKS_FOR_NIC_OWNERSHIP);
14572da4366fSEmmanuel Grumbach if (ret)
14582da4366fSEmmanuel Grumbach goto out;
14592da4366fSEmmanuel Grumbach
14602da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
14612da4366fSEmmanuel Grumbach
14622da4366fSEmmanuel Grumbach ret = wait_event_timeout(mei->get_ownership_wq,
14632da4366fSEmmanuel Grumbach mei->got_ownership, HZ / 2);
146406ce23adSAvraham Stern if (!ret) {
146506ce23adSAvraham Stern schedule_delayed_work(&mei->ownership_dwork,
146606ce23adSAvraham Stern MEI_OWNERSHIP_RETAKE_TIMEOUT_MS);
146706ce23adSAvraham Stern return -ETIMEDOUT;
146806ce23adSAvraham Stern }
146906ce23adSAvraham Stern
147006ce23adSAvraham Stern return 0;
14712da4366fSEmmanuel Grumbach out:
14722da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
14732da4366fSEmmanuel Grumbach return ret;
14742da4366fSEmmanuel Grumbach }
14752da4366fSEmmanuel Grumbach EXPORT_SYMBOL_GPL(iwl_mei_get_ownership);
14762da4366fSEmmanuel Grumbach
iwl_mei_alive_notif(bool success)1477733eb54fSAvraham Stern void iwl_mei_alive_notif(bool success)
1478733eb54fSAvraham Stern {
1479733eb54fSAvraham Stern struct iwl_mei *mei;
1480733eb54fSAvraham Stern struct iwl_sap_pldr_end_data msg = {
1481733eb54fSAvraham Stern .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_PLDR_END),
1482733eb54fSAvraham Stern .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
1483733eb54fSAvraham Stern .status = success ? cpu_to_le32(SAP_PLDR_STATUS_SUCCESS) :
1484733eb54fSAvraham Stern cpu_to_le32(SAP_PLDR_STATUS_FAILURE),
1485733eb54fSAvraham Stern };
1486733eb54fSAvraham Stern
1487733eb54fSAvraham Stern mutex_lock(&iwl_mei_mutex);
1488733eb54fSAvraham Stern
1489733eb54fSAvraham Stern if (!iwl_mei_is_connected())
1490733eb54fSAvraham Stern goto out;
1491733eb54fSAvraham Stern
1492733eb54fSAvraham Stern mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
1493733eb54fSAvraham Stern if (!mei || !mei->pldr_active)
1494733eb54fSAvraham Stern goto out;
1495733eb54fSAvraham Stern
1496733eb54fSAvraham Stern mei->pldr_active = false;
1497733eb54fSAvraham Stern
1498733eb54fSAvraham Stern iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
1499733eb54fSAvraham Stern out:
1500733eb54fSAvraham Stern mutex_unlock(&iwl_mei_mutex);
1501733eb54fSAvraham Stern }
1502733eb54fSAvraham Stern EXPORT_SYMBOL_GPL(iwl_mei_alive_notif);
1503733eb54fSAvraham Stern
iwl_mei_host_associated(const struct iwl_mei_conn_info * conn_info,const struct iwl_mei_colloc_info * colloc_info)15042da4366fSEmmanuel Grumbach void iwl_mei_host_associated(const struct iwl_mei_conn_info *conn_info,
15052da4366fSEmmanuel Grumbach const struct iwl_mei_colloc_info *colloc_info)
15062da4366fSEmmanuel Grumbach {
15072da4366fSEmmanuel Grumbach struct iwl_sap_notif_host_link_up msg = {
15082da4366fSEmmanuel Grumbach .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_HOST_LINK_UP),
15092da4366fSEmmanuel Grumbach .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
15102da4366fSEmmanuel Grumbach .conn_info = {
15112da4366fSEmmanuel Grumbach .ssid_len = cpu_to_le32(conn_info->ssid_len),
15122da4366fSEmmanuel Grumbach .channel = conn_info->channel,
15132da4366fSEmmanuel Grumbach .band = conn_info->band,
15142da4366fSEmmanuel Grumbach .pairwise_cipher = cpu_to_le32(conn_info->pairwise_cipher),
15152da4366fSEmmanuel Grumbach .auth_mode = cpu_to_le32(conn_info->auth_mode),
15162da4366fSEmmanuel Grumbach },
15172da4366fSEmmanuel Grumbach };
15182da4366fSEmmanuel Grumbach struct iwl_mei *mei;
15192da4366fSEmmanuel Grumbach
15202da4366fSEmmanuel Grumbach if (conn_info->ssid_len > ARRAY_SIZE(msg.conn_info.ssid))
15212da4366fSEmmanuel Grumbach return;
15222da4366fSEmmanuel Grumbach
15232da4366fSEmmanuel Grumbach memcpy(msg.conn_info.ssid, conn_info->ssid, conn_info->ssid_len);
15242da4366fSEmmanuel Grumbach memcpy(msg.conn_info.bssid, conn_info->bssid, ETH_ALEN);
15252da4366fSEmmanuel Grumbach
15262da4366fSEmmanuel Grumbach if (colloc_info) {
15272da4366fSEmmanuel Grumbach msg.colloc_channel = colloc_info->channel;
15282da4366fSEmmanuel Grumbach msg.colloc_band = colloc_info->channel <= 14 ? 0 : 1;
15292da4366fSEmmanuel Grumbach memcpy(msg.colloc_bssid, colloc_info->bssid, ETH_ALEN);
15302da4366fSEmmanuel Grumbach }
15312da4366fSEmmanuel Grumbach
15322da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
15332da4366fSEmmanuel Grumbach
15342da4366fSEmmanuel Grumbach if (!iwl_mei_is_connected())
15352da4366fSEmmanuel Grumbach goto out;
15362da4366fSEmmanuel Grumbach
15372da4366fSEmmanuel Grumbach mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
15382da4366fSEmmanuel Grumbach
1539ef030ab1SAvraham Stern if (!mei || !mei->amt_enabled)
15402da4366fSEmmanuel Grumbach goto out;
15412da4366fSEmmanuel Grumbach
15422da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
15432da4366fSEmmanuel Grumbach
15442da4366fSEmmanuel Grumbach out:
15452da4366fSEmmanuel Grumbach kfree(iwl_mei_cache.conn_info);
15462da4366fSEmmanuel Grumbach iwl_mei_cache.conn_info =
15472da4366fSEmmanuel Grumbach kmemdup(&msg.conn_info, sizeof(msg.conn_info), GFP_KERNEL);
15482da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
15492da4366fSEmmanuel Grumbach }
15502da4366fSEmmanuel Grumbach EXPORT_SYMBOL_GPL(iwl_mei_host_associated);
15512da4366fSEmmanuel Grumbach
iwl_mei_host_disassociated(void)15522da4366fSEmmanuel Grumbach void iwl_mei_host_disassociated(void)
15532da4366fSEmmanuel Grumbach {
15542da4366fSEmmanuel Grumbach struct iwl_mei *mei;
15552da4366fSEmmanuel Grumbach struct iwl_sap_notif_host_link_down msg = {
15562da4366fSEmmanuel Grumbach .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_HOST_LINK_DOWN),
15572da4366fSEmmanuel Grumbach .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
1558c4bb943eSAvraham Stern .type = HOST_LINK_DOWN_TYPE_TEMPORARY,
15592da4366fSEmmanuel Grumbach };
15602da4366fSEmmanuel Grumbach
15612da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
15622da4366fSEmmanuel Grumbach
15632da4366fSEmmanuel Grumbach if (!iwl_mei_is_connected())
15642da4366fSEmmanuel Grumbach goto out;
15652da4366fSEmmanuel Grumbach
15662da4366fSEmmanuel Grumbach mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
15672da4366fSEmmanuel Grumbach
1568ef030ab1SAvraham Stern if (!mei || !mei->amt_enabled)
15692da4366fSEmmanuel Grumbach goto out;
15702da4366fSEmmanuel Grumbach
15712da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
15722da4366fSEmmanuel Grumbach
15732da4366fSEmmanuel Grumbach out:
15742da4366fSEmmanuel Grumbach kfree(iwl_mei_cache.conn_info);
15752da4366fSEmmanuel Grumbach iwl_mei_cache.conn_info = NULL;
15762da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
15772da4366fSEmmanuel Grumbach }
15782da4366fSEmmanuel Grumbach EXPORT_SYMBOL_GPL(iwl_mei_host_disassociated);
15792da4366fSEmmanuel Grumbach
iwl_mei_set_rfkill_state(bool hw_rfkill,bool sw_rfkill)15802da4366fSEmmanuel Grumbach void iwl_mei_set_rfkill_state(bool hw_rfkill, bool sw_rfkill)
15812da4366fSEmmanuel Grumbach {
15822da4366fSEmmanuel Grumbach struct iwl_mei *mei;
15832da4366fSEmmanuel Grumbach u32 rfkill_state = 0;
15842da4366fSEmmanuel Grumbach struct iwl_sap_msg_dw msg = {
15852da4366fSEmmanuel Grumbach .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_RADIO_STATE),
15862da4366fSEmmanuel Grumbach .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
15872da4366fSEmmanuel Grumbach };
15882da4366fSEmmanuel Grumbach
15892da4366fSEmmanuel Grumbach if (!sw_rfkill)
15902da4366fSEmmanuel Grumbach rfkill_state |= SAP_SW_RFKILL_DEASSERTED;
15912da4366fSEmmanuel Grumbach
15922da4366fSEmmanuel Grumbach if (!hw_rfkill)
15932da4366fSEmmanuel Grumbach rfkill_state |= SAP_HW_RFKILL_DEASSERTED;
15942da4366fSEmmanuel Grumbach
15952da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
15962da4366fSEmmanuel Grumbach
15972da4366fSEmmanuel Grumbach if (!iwl_mei_is_connected())
15982da4366fSEmmanuel Grumbach goto out;
15992da4366fSEmmanuel Grumbach
16002da4366fSEmmanuel Grumbach msg.val = cpu_to_le32(rfkill_state);
16012da4366fSEmmanuel Grumbach
16022da4366fSEmmanuel Grumbach mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
16032da4366fSEmmanuel Grumbach
1604ef030ab1SAvraham Stern if (!mei || !mei->amt_enabled)
16052da4366fSEmmanuel Grumbach goto out;
16062da4366fSEmmanuel Grumbach
16072da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
16082da4366fSEmmanuel Grumbach
16092da4366fSEmmanuel Grumbach out:
16102da4366fSEmmanuel Grumbach iwl_mei_cache.rf_kill = rfkill_state;
16112da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
16122da4366fSEmmanuel Grumbach }
16132da4366fSEmmanuel Grumbach EXPORT_SYMBOL_GPL(iwl_mei_set_rfkill_state);
16142da4366fSEmmanuel Grumbach
iwl_mei_set_nic_info(const u8 * mac_address,const u8 * nvm_address)16152da4366fSEmmanuel Grumbach void iwl_mei_set_nic_info(const u8 *mac_address, const u8 *nvm_address)
16162da4366fSEmmanuel Grumbach {
16172da4366fSEmmanuel Grumbach struct iwl_mei *mei;
16182da4366fSEmmanuel Grumbach struct iwl_sap_notif_host_nic_info msg = {
16192da4366fSEmmanuel Grumbach .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_NIC_INFO),
16202da4366fSEmmanuel Grumbach .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
16212da4366fSEmmanuel Grumbach };
16222da4366fSEmmanuel Grumbach
16232da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
16242da4366fSEmmanuel Grumbach
16252da4366fSEmmanuel Grumbach if (!iwl_mei_is_connected())
16262da4366fSEmmanuel Grumbach goto out;
16272da4366fSEmmanuel Grumbach
16282da4366fSEmmanuel Grumbach ether_addr_copy(msg.mac_address, mac_address);
16292da4366fSEmmanuel Grumbach ether_addr_copy(msg.nvm_address, nvm_address);
16302da4366fSEmmanuel Grumbach
16312da4366fSEmmanuel Grumbach mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
16322da4366fSEmmanuel Grumbach
1633ef030ab1SAvraham Stern if (!mei || !mei->amt_enabled)
16342da4366fSEmmanuel Grumbach goto out;
16352da4366fSEmmanuel Grumbach
16362da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
16372da4366fSEmmanuel Grumbach
16382da4366fSEmmanuel Grumbach out:
16392da4366fSEmmanuel Grumbach ether_addr_copy(iwl_mei_cache.mac_address, mac_address);
16402da4366fSEmmanuel Grumbach ether_addr_copy(iwl_mei_cache.nvm_address, nvm_address);
16412da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
16422da4366fSEmmanuel Grumbach }
16432da4366fSEmmanuel Grumbach EXPORT_SYMBOL_GPL(iwl_mei_set_nic_info);
16442da4366fSEmmanuel Grumbach
iwl_mei_set_country_code(u16 mcc)16452da4366fSEmmanuel Grumbach void iwl_mei_set_country_code(u16 mcc)
16462da4366fSEmmanuel Grumbach {
16472da4366fSEmmanuel Grumbach struct iwl_mei *mei;
16482da4366fSEmmanuel Grumbach struct iwl_sap_notif_country_code msg = {
16492da4366fSEmmanuel Grumbach .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_COUNTRY_CODE),
16502da4366fSEmmanuel Grumbach .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
16512da4366fSEmmanuel Grumbach .mcc = cpu_to_le16(mcc),
16522da4366fSEmmanuel Grumbach };
16532da4366fSEmmanuel Grumbach
16542da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
16552da4366fSEmmanuel Grumbach
16562da4366fSEmmanuel Grumbach if (!iwl_mei_is_connected())
16572da4366fSEmmanuel Grumbach goto out;
16582da4366fSEmmanuel Grumbach
16592da4366fSEmmanuel Grumbach mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
16602da4366fSEmmanuel Grumbach
1661ef030ab1SAvraham Stern if (!mei || !mei->amt_enabled)
16622da4366fSEmmanuel Grumbach goto out;
16632da4366fSEmmanuel Grumbach
16642da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
16652da4366fSEmmanuel Grumbach
16662da4366fSEmmanuel Grumbach out:
16672da4366fSEmmanuel Grumbach iwl_mei_cache.mcc = mcc;
16682da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
16692da4366fSEmmanuel Grumbach }
16702da4366fSEmmanuel Grumbach EXPORT_SYMBOL_GPL(iwl_mei_set_country_code);
16712da4366fSEmmanuel Grumbach
iwl_mei_set_power_limit(const __le16 * power_limit)16722da4366fSEmmanuel Grumbach void iwl_mei_set_power_limit(const __le16 *power_limit)
16732da4366fSEmmanuel Grumbach {
16742da4366fSEmmanuel Grumbach struct iwl_mei *mei;
16752da4366fSEmmanuel Grumbach struct iwl_sap_notif_sar_limits msg = {
16762da4366fSEmmanuel Grumbach .hdr.type = cpu_to_le16(SAP_MSG_NOTIF_SAR_LIMITS),
16772da4366fSEmmanuel Grumbach .hdr.len = cpu_to_le16(sizeof(msg) - sizeof(msg.hdr)),
16782da4366fSEmmanuel Grumbach };
16792da4366fSEmmanuel Grumbach
16802da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
16812da4366fSEmmanuel Grumbach
16822da4366fSEmmanuel Grumbach if (!iwl_mei_is_connected())
16832da4366fSEmmanuel Grumbach goto out;
16842da4366fSEmmanuel Grumbach
16852da4366fSEmmanuel Grumbach mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
16862da4366fSEmmanuel Grumbach
1687ef030ab1SAvraham Stern if (!mei || !mei->amt_enabled)
16882da4366fSEmmanuel Grumbach goto out;
16892da4366fSEmmanuel Grumbach
16902da4366fSEmmanuel Grumbach memcpy(msg.sar_chain_info_table, power_limit, sizeof(msg.sar_chain_info_table));
16912da4366fSEmmanuel Grumbach
16922da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg_payload(mei->cldev, &msg.hdr);
16932da4366fSEmmanuel Grumbach
16942da4366fSEmmanuel Grumbach out:
16952da4366fSEmmanuel Grumbach kfree(iwl_mei_cache.power_limit);
16962da4366fSEmmanuel Grumbach iwl_mei_cache.power_limit = kmemdup(power_limit,
16972da4366fSEmmanuel Grumbach sizeof(msg.sar_chain_info_table), GFP_KERNEL);
16982da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
16992da4366fSEmmanuel Grumbach }
17002da4366fSEmmanuel Grumbach EXPORT_SYMBOL_GPL(iwl_mei_set_power_limit);
17012da4366fSEmmanuel Grumbach
iwl_mei_set_netdev(struct net_device * netdev)17022da4366fSEmmanuel Grumbach void iwl_mei_set_netdev(struct net_device *netdev)
17032da4366fSEmmanuel Grumbach {
17042da4366fSEmmanuel Grumbach struct iwl_mei *mei;
17052da4366fSEmmanuel Grumbach
17062da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
17072da4366fSEmmanuel Grumbach
17082da4366fSEmmanuel Grumbach if (!iwl_mei_is_connected()) {
17092da4366fSEmmanuel Grumbach rcu_assign_pointer(iwl_mei_cache.netdev, netdev);
17102da4366fSEmmanuel Grumbach goto out;
17112da4366fSEmmanuel Grumbach }
17122da4366fSEmmanuel Grumbach
17132da4366fSEmmanuel Grumbach mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
17142da4366fSEmmanuel Grumbach
17152da4366fSEmmanuel Grumbach if (!mei)
17162da4366fSEmmanuel Grumbach goto out;
17172da4366fSEmmanuel Grumbach
17182da4366fSEmmanuel Grumbach if (!netdev) {
17192da4366fSEmmanuel Grumbach struct net_device *dev =
17202da4366fSEmmanuel Grumbach rcu_dereference_protected(iwl_mei_cache.netdev,
17212da4366fSEmmanuel Grumbach lockdep_is_held(&iwl_mei_mutex));
17222da4366fSEmmanuel Grumbach
17232da4366fSEmmanuel Grumbach if (!dev)
17242da4366fSEmmanuel Grumbach goto out;
17252da4366fSEmmanuel Grumbach
17262da4366fSEmmanuel Grumbach netdev_rx_handler_unregister(dev);
17272da4366fSEmmanuel Grumbach }
17282da4366fSEmmanuel Grumbach
17292da4366fSEmmanuel Grumbach rcu_assign_pointer(iwl_mei_cache.netdev, netdev);
17302da4366fSEmmanuel Grumbach
17312da4366fSEmmanuel Grumbach if (netdev && mei->amt_enabled)
17322da4366fSEmmanuel Grumbach netdev_rx_handler_register(netdev, iwl_mei_rx_handler, mei);
17332da4366fSEmmanuel Grumbach
17342da4366fSEmmanuel Grumbach out:
17352da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
17362da4366fSEmmanuel Grumbach }
17372da4366fSEmmanuel Grumbach EXPORT_SYMBOL_GPL(iwl_mei_set_netdev);
17382da4366fSEmmanuel Grumbach
iwl_mei_device_state(bool up)17395aa7ce31SAvraham Stern void iwl_mei_device_state(bool up)
17402da4366fSEmmanuel Grumbach {
17412da4366fSEmmanuel Grumbach struct iwl_mei *mei;
17422da4366fSEmmanuel Grumbach
17432da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
17442da4366fSEmmanuel Grumbach
17452da4366fSEmmanuel Grumbach if (!iwl_mei_is_connected())
17462da4366fSEmmanuel Grumbach goto out;
17472da4366fSEmmanuel Grumbach
17482da4366fSEmmanuel Grumbach mei = mei_cldev_get_drvdata(iwl_mei_global_cldev);
17492da4366fSEmmanuel Grumbach
17502da4366fSEmmanuel Grumbach if (!mei)
17512da4366fSEmmanuel Grumbach goto out;
17522da4366fSEmmanuel Grumbach
17535aa7ce31SAvraham Stern mei->device_down = !up;
17545aa7ce31SAvraham Stern
17555aa7ce31SAvraham Stern if (up || !mei->csme_taking_ownership)
17562da4366fSEmmanuel Grumbach goto out;
17572da4366fSEmmanuel Grumbach
17582da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg(mei->cldev,
17592da4366fSEmmanuel Grumbach SAP_MSG_NOTIF_CSME_OWNERSHIP_CONFIRMED);
17602da4366fSEmmanuel Grumbach mei->csme_taking_ownership = false;
176106ce23adSAvraham Stern schedule_delayed_work(&mei->ownership_dwork,
176206ce23adSAvraham Stern MEI_OWNERSHIP_RETAKE_TIMEOUT_MS);
17632da4366fSEmmanuel Grumbach out:
17642da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
17652da4366fSEmmanuel Grumbach }
17665aa7ce31SAvraham Stern EXPORT_SYMBOL_GPL(iwl_mei_device_state);
17672da4366fSEmmanuel Grumbach
iwl_mei_register(void * priv,const struct iwl_mei_ops * ops)17682da4366fSEmmanuel Grumbach int iwl_mei_register(void *priv, const struct iwl_mei_ops *ops)
17692da4366fSEmmanuel Grumbach {
17702da4366fSEmmanuel Grumbach int ret;
17712da4366fSEmmanuel Grumbach
17722da4366fSEmmanuel Grumbach /*
17732da4366fSEmmanuel Grumbach * We must have a non-NULL priv pointer to not crash when there are
17742da4366fSEmmanuel Grumbach * multiple WiFi devices.
17752da4366fSEmmanuel Grumbach */
17762da4366fSEmmanuel Grumbach if (!priv)
17772da4366fSEmmanuel Grumbach return -EINVAL;
17782da4366fSEmmanuel Grumbach
17792da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
17802da4366fSEmmanuel Grumbach
17812da4366fSEmmanuel Grumbach /* do not allow registration if someone else already registered */
17822da4366fSEmmanuel Grumbach if (iwl_mei_cache.priv || iwl_mei_cache.ops) {
17832da4366fSEmmanuel Grumbach ret = -EBUSY;
17842da4366fSEmmanuel Grumbach goto out;
17852da4366fSEmmanuel Grumbach }
17862da4366fSEmmanuel Grumbach
17872da4366fSEmmanuel Grumbach iwl_mei_cache.priv = priv;
17882da4366fSEmmanuel Grumbach iwl_mei_cache.ops = ops;
17892da4366fSEmmanuel Grumbach
17902da4366fSEmmanuel Grumbach if (iwl_mei_global_cldev) {
17912da4366fSEmmanuel Grumbach struct iwl_mei *mei =
17922da4366fSEmmanuel Grumbach mei_cldev_get_drvdata(iwl_mei_global_cldev);
17932da4366fSEmmanuel Grumbach
17942da4366fSEmmanuel Grumbach /* we have already a SAP connection */
179530de48b4SEmmanuel Grumbach if (iwl_mei_is_connected()) {
179695170a46SEmmanuel Grumbach if (mei->amt_enabled)
17972da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg(mei->cldev,
1798352d3ef4SAvraham Stern SAP_MSG_NOTIF_WIFIDR_UP);
1799352d3ef4SAvraham Stern ops->rfkill(priv, mei->link_prot_state, false);
180030de48b4SEmmanuel Grumbach }
18012da4366fSEmmanuel Grumbach }
18022da4366fSEmmanuel Grumbach ret = 0;
18032da4366fSEmmanuel Grumbach
18042da4366fSEmmanuel Grumbach out:
18052da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
18062da4366fSEmmanuel Grumbach return ret;
18072da4366fSEmmanuel Grumbach }
18082da4366fSEmmanuel Grumbach EXPORT_SYMBOL_GPL(iwl_mei_register);
18092da4366fSEmmanuel Grumbach
iwl_mei_start_unregister(void)18102da4366fSEmmanuel Grumbach void iwl_mei_start_unregister(void)
18112da4366fSEmmanuel Grumbach {
18122da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
18132da4366fSEmmanuel Grumbach
18142da4366fSEmmanuel Grumbach /* At this point, the wifi driver should have removed the netdev */
18152da4366fSEmmanuel Grumbach if (rcu_access_pointer(iwl_mei_cache.netdev))
18162da4366fSEmmanuel Grumbach pr_err("Still had a netdev pointer set upon unregister\n");
18172da4366fSEmmanuel Grumbach
18182da4366fSEmmanuel Grumbach kfree(iwl_mei_cache.conn_info);
18192da4366fSEmmanuel Grumbach iwl_mei_cache.conn_info = NULL;
18202da4366fSEmmanuel Grumbach kfree(iwl_mei_cache.power_limit);
18212da4366fSEmmanuel Grumbach iwl_mei_cache.power_limit = NULL;
18222da4366fSEmmanuel Grumbach iwl_mei_cache.ops = NULL;
18232da4366fSEmmanuel Grumbach /* leave iwl_mei_cache.priv non-NULL to prevent any new registration */
18242da4366fSEmmanuel Grumbach
18252da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
18262da4366fSEmmanuel Grumbach }
18272da4366fSEmmanuel Grumbach EXPORT_SYMBOL_GPL(iwl_mei_start_unregister);
18282da4366fSEmmanuel Grumbach
iwl_mei_unregister_complete(void)18292da4366fSEmmanuel Grumbach void iwl_mei_unregister_complete(void)
18302da4366fSEmmanuel Grumbach {
18312da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
18322da4366fSEmmanuel Grumbach
18332da4366fSEmmanuel Grumbach iwl_mei_cache.priv = NULL;
18342da4366fSEmmanuel Grumbach
18352da4366fSEmmanuel Grumbach if (iwl_mei_global_cldev) {
18362da4366fSEmmanuel Grumbach struct iwl_mei *mei =
18372da4366fSEmmanuel Grumbach mei_cldev_get_drvdata(iwl_mei_global_cldev);
18382da4366fSEmmanuel Grumbach
1839a7d9ac48SAvraham Stern if (mei->amt_enabled)
1840a7d9ac48SAvraham Stern iwl_mei_send_sap_msg(mei->cldev,
1841a7d9ac48SAvraham Stern SAP_MSG_NOTIF_WIFIDR_DOWN);
1842013f9e63SEmmanuel Grumbach mei->got_ownership = false;
18432da4366fSEmmanuel Grumbach }
18442da4366fSEmmanuel Grumbach
18452da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
18462da4366fSEmmanuel Grumbach }
18472da4366fSEmmanuel Grumbach EXPORT_SYMBOL_GPL(iwl_mei_unregister_complete);
18482da4366fSEmmanuel Grumbach
18494ea7da5fSEmmanuel Grumbach #if IS_ENABLED(CONFIG_DEBUG_FS)
18504ea7da5fSEmmanuel Grumbach
18514ea7da5fSEmmanuel Grumbach static ssize_t
iwl_mei_dbgfs_send_start_message_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)18524ea7da5fSEmmanuel Grumbach iwl_mei_dbgfs_send_start_message_write(struct file *file,
18534ea7da5fSEmmanuel Grumbach const char __user *user_buf,
18544ea7da5fSEmmanuel Grumbach size_t count, loff_t *ppos)
18554ea7da5fSEmmanuel Grumbach {
18564ea7da5fSEmmanuel Grumbach int ret;
18574ea7da5fSEmmanuel Grumbach
18584ea7da5fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
18594ea7da5fSEmmanuel Grumbach
18604ea7da5fSEmmanuel Grumbach if (!iwl_mei_global_cldev) {
18614ea7da5fSEmmanuel Grumbach ret = -ENODEV;
18624ea7da5fSEmmanuel Grumbach goto out;
18634ea7da5fSEmmanuel Grumbach }
18644ea7da5fSEmmanuel Grumbach
18654ea7da5fSEmmanuel Grumbach ret = iwl_mei_send_start(iwl_mei_global_cldev);
18664ea7da5fSEmmanuel Grumbach
18674ea7da5fSEmmanuel Grumbach out:
18684ea7da5fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
18694ea7da5fSEmmanuel Grumbach return ret ?: count;
18704ea7da5fSEmmanuel Grumbach }
18714ea7da5fSEmmanuel Grumbach
18724ea7da5fSEmmanuel Grumbach static const struct file_operations iwl_mei_dbgfs_send_start_message_ops = {
18734ea7da5fSEmmanuel Grumbach .write = iwl_mei_dbgfs_send_start_message_write,
18744ea7da5fSEmmanuel Grumbach .open = simple_open,
18754ea7da5fSEmmanuel Grumbach .llseek = default_llseek,
18764ea7da5fSEmmanuel Grumbach };
18774ea7da5fSEmmanuel Grumbach
iwl_mei_dbgfs_req_ownership_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)18784ea7da5fSEmmanuel Grumbach static ssize_t iwl_mei_dbgfs_req_ownership_write(struct file *file,
18794ea7da5fSEmmanuel Grumbach const char __user *user_buf,
18804ea7da5fSEmmanuel Grumbach size_t count, loff_t *ppos)
18814ea7da5fSEmmanuel Grumbach {
18824ea7da5fSEmmanuel Grumbach iwl_mei_get_ownership();
18834ea7da5fSEmmanuel Grumbach
18844ea7da5fSEmmanuel Grumbach return count;
18854ea7da5fSEmmanuel Grumbach }
18864ea7da5fSEmmanuel Grumbach
18874ea7da5fSEmmanuel Grumbach static const struct file_operations iwl_mei_dbgfs_req_ownership_ops = {
18884ea7da5fSEmmanuel Grumbach .write = iwl_mei_dbgfs_req_ownership_write,
18894ea7da5fSEmmanuel Grumbach .open = simple_open,
18904ea7da5fSEmmanuel Grumbach .llseek = default_llseek,
18914ea7da5fSEmmanuel Grumbach };
18924ea7da5fSEmmanuel Grumbach
iwl_mei_dbgfs_register(struct iwl_mei * mei)18934ea7da5fSEmmanuel Grumbach static void iwl_mei_dbgfs_register(struct iwl_mei *mei)
18944ea7da5fSEmmanuel Grumbach {
18954ea7da5fSEmmanuel Grumbach mei->dbgfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL);
18964ea7da5fSEmmanuel Grumbach
18974ea7da5fSEmmanuel Grumbach if (!mei->dbgfs_dir)
18984ea7da5fSEmmanuel Grumbach return;
18994ea7da5fSEmmanuel Grumbach
19004ea7da5fSEmmanuel Grumbach debugfs_create_ulong("status", S_IRUSR,
19014ea7da5fSEmmanuel Grumbach mei->dbgfs_dir, &iwl_mei_status);
19024ea7da5fSEmmanuel Grumbach debugfs_create_file("send_start_message", S_IWUSR, mei->dbgfs_dir,
19034ea7da5fSEmmanuel Grumbach mei, &iwl_mei_dbgfs_send_start_message_ops);
19041a4d5758SColin Ian King debugfs_create_file("req_ownership", S_IWUSR, mei->dbgfs_dir,
19054ea7da5fSEmmanuel Grumbach mei, &iwl_mei_dbgfs_req_ownership_ops);
19064ea7da5fSEmmanuel Grumbach }
19074ea7da5fSEmmanuel Grumbach
iwl_mei_dbgfs_unregister(struct iwl_mei * mei)19084ea7da5fSEmmanuel Grumbach static void iwl_mei_dbgfs_unregister(struct iwl_mei *mei)
19094ea7da5fSEmmanuel Grumbach {
19104ea7da5fSEmmanuel Grumbach debugfs_remove_recursive(mei->dbgfs_dir);
19114ea7da5fSEmmanuel Grumbach mei->dbgfs_dir = NULL;
19124ea7da5fSEmmanuel Grumbach }
19134ea7da5fSEmmanuel Grumbach
19144ea7da5fSEmmanuel Grumbach #else
19154ea7da5fSEmmanuel Grumbach
iwl_mei_dbgfs_register(struct iwl_mei * mei)19164ea7da5fSEmmanuel Grumbach static void iwl_mei_dbgfs_register(struct iwl_mei *mei) {}
iwl_mei_dbgfs_unregister(struct iwl_mei * mei)19174ea7da5fSEmmanuel Grumbach static void iwl_mei_dbgfs_unregister(struct iwl_mei *mei) {}
19184ea7da5fSEmmanuel Grumbach
19194ea7da5fSEmmanuel Grumbach #endif /* CONFIG_DEBUG_FS */
19204ea7da5fSEmmanuel Grumbach
iwl_mei_ownership_dwork(struct work_struct * wk)192106ce23adSAvraham Stern static void iwl_mei_ownership_dwork(struct work_struct *wk)
192206ce23adSAvraham Stern {
192306ce23adSAvraham Stern iwl_mei_get_ownership();
192406ce23adSAvraham Stern }
192506ce23adSAvraham Stern
192644bf7c46SEmmanuel Grumbach #define ALLOC_SHARED_MEM_RETRY_MAX_NUM 3
192744bf7c46SEmmanuel Grumbach
1928991bbbecSJohannes Berg /*
19292da4366fSEmmanuel Grumbach * iwl_mei_probe - the probe function called by the mei bus enumeration
19302da4366fSEmmanuel Grumbach *
19312da4366fSEmmanuel Grumbach * This allocates the data needed by iwlmei and sets a pointer to this data
19322da4366fSEmmanuel Grumbach * into the mei_cl_device's drvdata.
19332da4366fSEmmanuel Grumbach * It starts the SAP protocol by sending the SAP_ME_MSG_START without
19342da4366fSEmmanuel Grumbach * waiting for the answer. The answer will be caught later by the Rx callback.
19352da4366fSEmmanuel Grumbach */
iwl_mei_probe(struct mei_cl_device * cldev,const struct mei_cl_device_id * id)19362da4366fSEmmanuel Grumbach static int iwl_mei_probe(struct mei_cl_device *cldev,
19372da4366fSEmmanuel Grumbach const struct mei_cl_device_id *id)
19382da4366fSEmmanuel Grumbach {
193944bf7c46SEmmanuel Grumbach int alloc_retry = ALLOC_SHARED_MEM_RETRY_MAX_NUM;
19402da4366fSEmmanuel Grumbach struct iwl_mei *mei;
19412da4366fSEmmanuel Grumbach int ret;
19422da4366fSEmmanuel Grumbach
19432da4366fSEmmanuel Grumbach mei = devm_kzalloc(&cldev->dev, sizeof(*mei), GFP_KERNEL);
19442da4366fSEmmanuel Grumbach if (!mei)
19452da4366fSEmmanuel Grumbach return -ENOMEM;
19462da4366fSEmmanuel Grumbach
19472da4366fSEmmanuel Grumbach init_waitqueue_head(&mei->get_nvm_wq);
19482da4366fSEmmanuel Grumbach INIT_WORK(&mei->send_csa_msg_wk, iwl_mei_send_csa_msg_wk);
19492da4366fSEmmanuel Grumbach INIT_DELAYED_WORK(&mei->csa_throttle_end_wk,
19502da4366fSEmmanuel Grumbach iwl_mei_csa_throttle_end_wk);
19512da4366fSEmmanuel Grumbach init_waitqueue_head(&mei->get_ownership_wq);
1952733eb54fSAvraham Stern init_waitqueue_head(&mei->pldr_wq);
19532da4366fSEmmanuel Grumbach spin_lock_init(&mei->data_q_lock);
1954d288067eSAvraham Stern INIT_WORK(&mei->netdev_work, iwl_mei_netdev_work);
195506ce23adSAvraham Stern INIT_DELAYED_WORK(&mei->ownership_dwork, iwl_mei_ownership_dwork);
19562da4366fSEmmanuel Grumbach
19572da4366fSEmmanuel Grumbach mei_cldev_set_drvdata(cldev, mei);
19582da4366fSEmmanuel Grumbach mei->cldev = cldev;
19595aa7ce31SAvraham Stern mei->device_down = true;
19602da4366fSEmmanuel Grumbach
196144bf7c46SEmmanuel Grumbach do {
19622da4366fSEmmanuel Grumbach ret = iwl_mei_alloc_shared_mem(cldev);
196344bf7c46SEmmanuel Grumbach if (!ret)
196444bf7c46SEmmanuel Grumbach break;
196544bf7c46SEmmanuel Grumbach /*
196644bf7c46SEmmanuel Grumbach * The CSME firmware needs to boot the internal WLAN client.
196744bf7c46SEmmanuel Grumbach * This can take time in certain configurations (usually
196844bf7c46SEmmanuel Grumbach * upon resume and when the whole CSME firmware is shut down
196944bf7c46SEmmanuel Grumbach * during suspend).
197044bf7c46SEmmanuel Grumbach *
197144bf7c46SEmmanuel Grumbach * Wait a bit before retrying and hope we'll succeed next time.
197244bf7c46SEmmanuel Grumbach */
197344bf7c46SEmmanuel Grumbach
197444bf7c46SEmmanuel Grumbach dev_dbg(&cldev->dev,
197544bf7c46SEmmanuel Grumbach "Couldn't allocate the shared memory: %d, attempt %d / %d\n",
197644bf7c46SEmmanuel Grumbach ret, alloc_retry, ALLOC_SHARED_MEM_RETRY_MAX_NUM);
197744bf7c46SEmmanuel Grumbach msleep(100);
197844bf7c46SEmmanuel Grumbach alloc_retry--;
197944bf7c46SEmmanuel Grumbach } while (alloc_retry);
198044bf7c46SEmmanuel Grumbach
198144bf7c46SEmmanuel Grumbach if (ret) {
198244bf7c46SEmmanuel Grumbach dev_err(&cldev->dev, "Couldn't allocate the shared memory: %d\n",
198344bf7c46SEmmanuel Grumbach ret);
19842da4366fSEmmanuel Grumbach goto free;
198544bf7c46SEmmanuel Grumbach }
19862da4366fSEmmanuel Grumbach
19872da4366fSEmmanuel Grumbach iwl_mei_init_shared_mem(mei);
19882da4366fSEmmanuel Grumbach
19892da4366fSEmmanuel Grumbach ret = iwl_mei_enable(cldev);
19902da4366fSEmmanuel Grumbach if (ret)
19912da4366fSEmmanuel Grumbach goto free_shared_mem;
19922da4366fSEmmanuel Grumbach
19934ea7da5fSEmmanuel Grumbach iwl_mei_dbgfs_register(mei);
19944ea7da5fSEmmanuel Grumbach
19952da4366fSEmmanuel Grumbach /*
19969ad28ba1STom Rix * We now have a Rx function in place, start the SAP protocol
19972da4366fSEmmanuel Grumbach * we expect to get the SAP_ME_MSG_START_OK response later on.
19982da4366fSEmmanuel Grumbach */
19992da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
20002da4366fSEmmanuel Grumbach ret = iwl_mei_send_start(cldev);
20012da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
20022da4366fSEmmanuel Grumbach if (ret)
20034ea7da5fSEmmanuel Grumbach goto debugfs_unregister;
20042da4366fSEmmanuel Grumbach
20052da4366fSEmmanuel Grumbach /* must be last */
20062da4366fSEmmanuel Grumbach iwl_mei_global_cldev = cldev;
20072da4366fSEmmanuel Grumbach
20082da4366fSEmmanuel Grumbach return 0;
20092da4366fSEmmanuel Grumbach
20104ea7da5fSEmmanuel Grumbach debugfs_unregister:
20114ea7da5fSEmmanuel Grumbach iwl_mei_dbgfs_unregister(mei);
20122da4366fSEmmanuel Grumbach mei_cldev_disable(cldev);
20132da4366fSEmmanuel Grumbach free_shared_mem:
20142da4366fSEmmanuel Grumbach iwl_mei_free_shared_mem(cldev);
20152da4366fSEmmanuel Grumbach free:
20162da4366fSEmmanuel Grumbach mei_cldev_set_drvdata(cldev, NULL);
20172da4366fSEmmanuel Grumbach devm_kfree(&cldev->dev, mei);
20182da4366fSEmmanuel Grumbach
20192da4366fSEmmanuel Grumbach return ret;
20202da4366fSEmmanuel Grumbach }
20212da4366fSEmmanuel Grumbach
20222da4366fSEmmanuel Grumbach #define SEND_SAP_MAX_WAIT_ITERATION 10
2023b0b9b805SAvraham Stern #define IWLMEI_DEVICE_DOWN_WAIT_ITERATION 50
20242da4366fSEmmanuel Grumbach
iwl_mei_remove(struct mei_cl_device * cldev)20252da4366fSEmmanuel Grumbach static void iwl_mei_remove(struct mei_cl_device *cldev)
20262da4366fSEmmanuel Grumbach {
20272da4366fSEmmanuel Grumbach struct iwl_mei *mei = mei_cldev_get_drvdata(cldev);
20282da4366fSEmmanuel Grumbach int i;
20292da4366fSEmmanuel Grumbach
20302da4366fSEmmanuel Grumbach /*
20312da4366fSEmmanuel Grumbach * We are being removed while the bus is active, it means we are
20322da4366fSEmmanuel Grumbach * going to suspend/ shutdown, so the NIC will disappear.
20332da4366fSEmmanuel Grumbach */
2034b0b9b805SAvraham Stern if (mei_cldev_enabled(cldev) && iwl_mei_cache.ops) {
2035b0b9b805SAvraham Stern unsigned int iter = IWLMEI_DEVICE_DOWN_WAIT_ITERATION;
2036b0b9b805SAvraham Stern bool down = false;
2037b0b9b805SAvraham Stern
2038b0b9b805SAvraham Stern /*
2039b0b9b805SAvraham Stern * In case of suspend, wait for the mac to stop and don't remove
2040b0b9b805SAvraham Stern * the interface. This will allow the interface to come back
2041b0b9b805SAvraham Stern * on resume.
2042b0b9b805SAvraham Stern */
2043b0b9b805SAvraham Stern while (!down && iter--) {
2044b0b9b805SAvraham Stern mdelay(1);
2045b0b9b805SAvraham Stern
2046b0b9b805SAvraham Stern mutex_lock(&iwl_mei_mutex);
2047b0b9b805SAvraham Stern down = mei->device_down;
2048b0b9b805SAvraham Stern mutex_unlock(&iwl_mei_mutex);
2049b0b9b805SAvraham Stern }
2050b0b9b805SAvraham Stern
2051b0b9b805SAvraham Stern if (!down)
20522da4366fSEmmanuel Grumbach iwl_mei_cache.ops->nic_stolen(iwl_mei_cache.priv);
2053b0b9b805SAvraham Stern }
20542da4366fSEmmanuel Grumbach
20552da4366fSEmmanuel Grumbach if (rcu_access_pointer(iwl_mei_cache.netdev)) {
20562da4366fSEmmanuel Grumbach struct net_device *dev;
20572da4366fSEmmanuel Grumbach
20582da4366fSEmmanuel Grumbach /*
20592da4366fSEmmanuel Grumbach * First take rtnl and only then the mutex to avoid an ABBA
20602da4366fSEmmanuel Grumbach * with iwl_mei_set_netdev()
20612da4366fSEmmanuel Grumbach */
20622da4366fSEmmanuel Grumbach rtnl_lock();
20632da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
20642da4366fSEmmanuel Grumbach
20652da4366fSEmmanuel Grumbach /*
20662da4366fSEmmanuel Grumbach * If we are suspending and the wifi driver hasn't removed it's netdev
20672da4366fSEmmanuel Grumbach * yet, do it now. In any case, don't change the cache.netdev pointer.
20682da4366fSEmmanuel Grumbach */
20692da4366fSEmmanuel Grumbach dev = rcu_dereference_protected(iwl_mei_cache.netdev,
20702da4366fSEmmanuel Grumbach lockdep_is_held(&iwl_mei_mutex));
20712da4366fSEmmanuel Grumbach
20722da4366fSEmmanuel Grumbach netdev_rx_handler_unregister(dev);
20732da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
20742da4366fSEmmanuel Grumbach rtnl_unlock();
20752da4366fSEmmanuel Grumbach }
20762da4366fSEmmanuel Grumbach
20772da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
20782da4366fSEmmanuel Grumbach
20793243aee1SAvraham Stern /* Tell CSME that we are going down so that it won't access the
20802da4366fSEmmanuel Grumbach * memory anymore, make sure this message goes through immediately.
20812da4366fSEmmanuel Grumbach */
20822da4366fSEmmanuel Grumbach mei->csa_throttled = false;
20832da4366fSEmmanuel Grumbach iwl_mei_send_sap_msg(mei->cldev,
20842da4366fSEmmanuel Grumbach SAP_MSG_NOTIF_HOST_GOES_DOWN);
20852da4366fSEmmanuel Grumbach
20862da4366fSEmmanuel Grumbach for (i = 0; i < SEND_SAP_MAX_WAIT_ITERATION; i++) {
20872da4366fSEmmanuel Grumbach if (!iwl_mei_host_to_me_data_pending(mei))
20882da4366fSEmmanuel Grumbach break;
20892da4366fSEmmanuel Grumbach
209095170a46SEmmanuel Grumbach msleep(20);
20912da4366fSEmmanuel Grumbach }
20922da4366fSEmmanuel Grumbach
20933243aee1SAvraham Stern /* If we couldn't make sure that CSME saw the HOST_GOES_DOWN
209495170a46SEmmanuel Grumbach * message, it means that it will probably keep reading memory
209595170a46SEmmanuel Grumbach * that we are going to unmap and free, expect IOMMU error
209695170a46SEmmanuel Grumbach * messages.
20972da4366fSEmmanuel Grumbach */
20982da4366fSEmmanuel Grumbach if (i == SEND_SAP_MAX_WAIT_ITERATION)
20992da4366fSEmmanuel Grumbach dev_err(&mei->cldev->dev,
21002da4366fSEmmanuel Grumbach "Couldn't get ACK from CSME on HOST_GOES_DOWN message\n");
21012da4366fSEmmanuel Grumbach
21022da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
21032da4366fSEmmanuel Grumbach
21042da4366fSEmmanuel Grumbach /*
21052da4366fSEmmanuel Grumbach * This looks strange, but this lock is taken here to make sure that
21062da4366fSEmmanuel Grumbach * iwl_mei_add_data_to_ring called from the Tx path sees that we
21072da4366fSEmmanuel Grumbach * clear the IWL_MEI_STATUS_SAP_CONNECTED bit.
21082da4366fSEmmanuel Grumbach * Rx isn't a problem because the rx_handler can't be called after
21092da4366fSEmmanuel Grumbach * having been unregistered.
21102da4366fSEmmanuel Grumbach */
21112da4366fSEmmanuel Grumbach spin_lock_bh(&mei->data_q_lock);
21122da4366fSEmmanuel Grumbach clear_bit(IWL_MEI_STATUS_SAP_CONNECTED, &iwl_mei_status);
21132da4366fSEmmanuel Grumbach spin_unlock_bh(&mei->data_q_lock);
21142da4366fSEmmanuel Grumbach
21152da4366fSEmmanuel Grumbach if (iwl_mei_cache.ops)
211622b68fc6SAvraham Stern iwl_mei_cache.ops->rfkill(iwl_mei_cache.priv, false, false);
21172da4366fSEmmanuel Grumbach
21182da4366fSEmmanuel Grumbach /*
21192da4366fSEmmanuel Grumbach * mei_cldev_disable will return only after all the MEI Rx is done.
21202da4366fSEmmanuel Grumbach * It must be called when iwl_mei_mutex is *not* held, since it waits
21212da4366fSEmmanuel Grumbach * for our Rx handler to complete.
21222da4366fSEmmanuel Grumbach * After it returns, no new Rx will start.
21232da4366fSEmmanuel Grumbach */
21242da4366fSEmmanuel Grumbach mei_cldev_disable(cldev);
21252da4366fSEmmanuel Grumbach
21262da4366fSEmmanuel Grumbach /*
21272da4366fSEmmanuel Grumbach * Since the netdev was already removed and the netdev's removal
21282da4366fSEmmanuel Grumbach * includes a call to synchronize_net() so that we know there won't be
21292da4366fSEmmanuel Grumbach * any new Rx that will trigger the following workers.
21302da4366fSEmmanuel Grumbach */
21312da4366fSEmmanuel Grumbach cancel_work_sync(&mei->send_csa_msg_wk);
21322da4366fSEmmanuel Grumbach cancel_delayed_work_sync(&mei->csa_throttle_end_wk);
2133d288067eSAvraham Stern cancel_work_sync(&mei->netdev_work);
213406ce23adSAvraham Stern cancel_delayed_work_sync(&mei->ownership_dwork);
21352da4366fSEmmanuel Grumbach
21362da4366fSEmmanuel Grumbach /*
21372da4366fSEmmanuel Grumbach * If someone waits for the ownership, let him know that we are going
21382da4366fSEmmanuel Grumbach * down and that we are not connected anymore. He'll be able to take
21392da4366fSEmmanuel Grumbach * the device.
21402da4366fSEmmanuel Grumbach */
21412da4366fSEmmanuel Grumbach wake_up_all(&mei->get_ownership_wq);
2142733eb54fSAvraham Stern wake_up_all(&mei->pldr_wq);
21432da4366fSEmmanuel Grumbach
21442da4366fSEmmanuel Grumbach mutex_lock(&iwl_mei_mutex);
21452da4366fSEmmanuel Grumbach
21462da4366fSEmmanuel Grumbach iwl_mei_global_cldev = NULL;
21472da4366fSEmmanuel Grumbach
21482da4366fSEmmanuel Grumbach wake_up_all(&mei->get_nvm_wq);
21492da4366fSEmmanuel Grumbach
21502da4366fSEmmanuel Grumbach iwl_mei_free_shared_mem(cldev);
21512da4366fSEmmanuel Grumbach
21524ea7da5fSEmmanuel Grumbach iwl_mei_dbgfs_unregister(mei);
21534ea7da5fSEmmanuel Grumbach
21542da4366fSEmmanuel Grumbach mei_cldev_set_drvdata(cldev, NULL);
21552da4366fSEmmanuel Grumbach
21562da4366fSEmmanuel Grumbach kfree(mei->nvm);
21572da4366fSEmmanuel Grumbach
21582da4366fSEmmanuel Grumbach kfree(rcu_access_pointer(mei->filters));
21592da4366fSEmmanuel Grumbach
21602da4366fSEmmanuel Grumbach devm_kfree(&cldev->dev, mei);
21612da4366fSEmmanuel Grumbach
21622da4366fSEmmanuel Grumbach mutex_unlock(&iwl_mei_mutex);
21632da4366fSEmmanuel Grumbach }
21642da4366fSEmmanuel Grumbach
21652da4366fSEmmanuel Grumbach static const struct mei_cl_device_id iwl_mei_tbl[] = {
2166cdf3949fSJohannes Berg {
2167cdf3949fSJohannes Berg .name = KBUILD_MODNAME,
2168cdf3949fSJohannes Berg .uuid = MEI_WLAN_UUID,
2169cdf3949fSJohannes Berg .version = MEI_CL_VERSION_ANY,
2170cdf3949fSJohannes Berg },
21712da4366fSEmmanuel Grumbach
21722da4366fSEmmanuel Grumbach /* required last entry */
21732da4366fSEmmanuel Grumbach { }
21742da4366fSEmmanuel Grumbach };
21752da4366fSEmmanuel Grumbach
21762da4366fSEmmanuel Grumbach /*
21772da4366fSEmmanuel Grumbach * Do not export the device table because this module is loaded by
21782da4366fSEmmanuel Grumbach * iwlwifi's dependency.
21792da4366fSEmmanuel Grumbach */
21802da4366fSEmmanuel Grumbach
21812da4366fSEmmanuel Grumbach static struct mei_cl_driver iwl_mei_cl_driver = {
21822da4366fSEmmanuel Grumbach .id_table = iwl_mei_tbl,
21832da4366fSEmmanuel Grumbach .name = KBUILD_MODNAME,
21842da4366fSEmmanuel Grumbach .probe = iwl_mei_probe,
21852da4366fSEmmanuel Grumbach .remove = iwl_mei_remove,
21862da4366fSEmmanuel Grumbach };
21872da4366fSEmmanuel Grumbach
21882da4366fSEmmanuel Grumbach module_mei_cl_driver(iwl_mei_cl_driver);
2189