static int wcn36xx_dxe_request_irqs(struct wcn36xx *wcn) { int ret; ret = request_irq(wcn->tx_irq, wcn36xx_irq_tx_complete, IRQF_TRIGGER_HIGH, "wcn36xx_tx", wcn); if (ret) { wcn36xx_error("failed to alloc tx irq"); goto out_err; } ret = request_irq(wcn->rx_irq, wcn36xx_irq_rx_ready, IRQF_TRIGGER_HIGH, "wcn36xx_rx", wcn); if (ret) { wcn36xx_error("failed to alloc rx irq"); goto out_txirq; } enable_irq_wake(wcn->rx_irq); return 0; out_txirq: free_irq(wcn->tx_irq, wcn); out_err: return ret; }
static int wcn36xx_smd_send_and_wait(struct wcn36xx *wcn, size_t len) { int avail; init_completion(&wcn->smd_compl); avail = smd_write_avail(wcn->smd_ch); wcn36xx_dbg_dump(WCN36XX_DBG_SMD_DUMP, "SMD >>> ", wcn->smd_buf, len); if (avail >= len) { avail = smd_write(wcn->smd_ch, wcn->smd_buf, len); if (avail != len) { wcn36xx_error("Cannot write to SMD channel"); return -EAGAIN; } } else { wcn36xx_error("SMD channel can accept only %d bytes", avail); return -ENOMEM; } if (wait_for_completion_timeout(&wcn->smd_compl, msecs_to_jiffies(SMD_MSG_TIMEOUT)) <= 0) { wcn36xx_error("Timeout while waiting SMD response"); return -ETIME; } return 0; }
int wcn36xx_smd_send_beacon(struct wcn36xx *wcn, struct sk_buff *skb_beacon, u16 tim_off, u16 p2p_off) { struct wcn36xx_hal_send_beacon_req_msg msg_body; INIT_HAL_MSG(msg_body, WCN36XX_HAL_SEND_BEACON_REQ); /* TODO need to find out why this is needed? */ msg_body.beacon_length = skb_beacon->len + 6; /* TODO make this as a const */ if (BEACON_TEMPLATE_SIZE > msg_body.beacon_length) { memcpy(&msg_body.beacon, &skb_beacon->len, sizeof(u32)); memcpy(&(msg_body.beacon[4]), skb_beacon->data, skb_beacon->len); } else { wcn36xx_error("Beacon is to big: beacon size=%d", msg_body.beacon_length); return -ENOMEM; } memcpy(&msg_body.bssid, &wcn->addresses[0], ETH_ALEN); /* TODO need to find out why this is needed? */ msg_body.tim_ie_offset = tim_off+4; msg_body.p2p_ie_offset = p2p_off; PREPARE_HAL_BUF(wcn->smd_buf, msg_body); wcn36xx_dbg(WCN36XX_DBG_HAL, "hal send beacon beacon_length %d", msg_body.beacon_length); return wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); };
int wcn36xx_dxe_alloc_ctl_blks(struct wcn36xx *wcn) { int ret; wcn->dxe_tx_l_ch.ch_type = WCN36XX_DXE_CH_TX_L; wcn->dxe_tx_h_ch.ch_type = WCN36XX_DXE_CH_TX_H; wcn->dxe_rx_l_ch.ch_type = WCN36XX_DXE_CH_RX_L; wcn->dxe_rx_h_ch.ch_type = WCN36XX_DXE_CH_RX_H; wcn->dxe_tx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_TX_L; wcn->dxe_tx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_TX_H; wcn->dxe_rx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_L; wcn->dxe_rx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_H; wcn->dxe_tx_l_ch.dxe_wq = WCN36XX_DXE_WQ_TX_L; wcn->dxe_tx_h_ch.dxe_wq = WCN36XX_DXE_WQ_TX_H; wcn->dxe_tx_l_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_L_BD; wcn->dxe_tx_h_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_H_BD; wcn->dxe_tx_l_ch.ctrl_skb = WCN36XX_DXE_CTRL_TX_L_SKB; wcn->dxe_tx_h_ch.ctrl_skb = WCN36XX_DXE_CTRL_TX_H_SKB; wcn->dxe_tx_l_ch.reg_ctrl = WCN36XX_DXE_REG_CTL_TX_L; wcn->dxe_tx_h_ch.reg_ctrl = WCN36XX_DXE_REG_CTL_TX_H; wcn->dxe_tx_l_ch.def_ctrl = WCN36XX_DXE_CH_DEFAULT_CTL_TX_L; wcn->dxe_tx_h_ch.def_ctrl = WCN36XX_DXE_CH_DEFAULT_CTL_TX_H; /* DXE control block allocation */ ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_tx_l_ch); if (ret) goto out_err; ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_tx_h_ch); if (ret) goto out_err; ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_rx_l_ch); if (ret) goto out_err; ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_rx_h_ch); if (ret) goto out_err; /* TODO most probably do not need this */ /* Initialize SMSM state Clear TX Enable RING EMPTY STATE */ ret = smsm_change_state(SMSM_APPS_STATE, WCN36XX_SMSM_WLAN_TX_ENABLE, WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY); return 0; out_err: wcn36xx_error("Failed to allocate DXE control blocks"); wcn36xx_dxe_free_ctl_blks(wcn); return -ENOMEM; }
static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len) { struct wcn36xx_hal_msg_header *msg_header = buf; wcn36xx_dbg_dump(WCN36XX_DBG_SMD_DUMP, "SMD <<< ", buf, len); switch (msg_header->msg_type) { case WCN36XX_HAL_START_RSP: wcn36xx_smd_start_rsp(wcn, buf, len); break; case WCN36XX_HAL_CONFIG_STA_RSP: wcn36xx_smd_config_sta_rsp(wcn, buf, len); break; case WCN36XX_HAL_CONFIG_BSS_RSP: wcn36xx_smd_config_bss_rsp(wcn, buf, len); break; case WCN36XX_HAL_STOP_RSP: case WCN36XX_HAL_ADD_STA_SELF_RSP: wcn36xx_smd_add_sta_self_rsp(wcn, buf, len); break; case WCN36XX_HAL_DEL_STA_SELF_RSP: case WCN36XX_HAL_DELETE_STA_RSP: case WCN36XX_HAL_INIT_SCAN_RSP: case WCN36XX_HAL_START_SCAN_RSP: case WCN36XX_HAL_END_SCAN_RSP: case WCN36XX_HAL_FINISH_SCAN_RSP: case WCN36XX_HAL_DOWNLOAD_NV_RSP: case WCN36XX_HAL_DELETE_BSS_RSP: case WCN36XX_HAL_SEND_BEACON_RSP: case WCN36XX_HAL_SET_LINK_ST_RSP: case WCN36XX_HAL_UPDATE_PROBE_RSP_TEMPLATE_RSP: case WCN36XX_HAL_SET_BSSKEY_RSP: case WCN36XX_HAL_SET_STAKEY_RSP: if (wcn36xx_smd_rsp_status_check(buf, len)) { wcn36xx_warn("error response from hal request %d", msg_header->msg_type); } break; case WCN36XX_HAL_JOIN_RSP: wcn36xx_smd_join_rsp(buf, len); break; case WCN36XX_HAL_UPDATE_SCAN_PARAM_RSP: wcn36xx_smd_update_scan_params_rsp(buf, len); break; case WCN36XX_HAL_CH_SWITCH_RSP: wcn36xx_smd_switch_channel_rsp(buf, len); break; case WCN36XX_HAL_OTA_TX_COMPL_IND: wcn36xx_smd_tx_compl_ind(wcn, buf, len); break; default: wcn36xx_error("SMD_EVENT (%d) not supported", msg_header->msg_type); } }
int wcn36xx_smd_open(struct wcn36xx *wcn) { int ret, left; INIT_WORK(&wcn->smd_work, wcn36xx_smd_work); init_completion(&wcn->smd_compl); ret = smd_named_open_on_edge("WLAN_CTRL", SMD_APPS_WCNSS, &wcn->smd_ch, wcn, wcn36xx_smd_notify); if (ret) { wcn36xx_error("smd_named_open_on_edge failed: %d", ret); return ret; } left = wait_for_completion_interruptible_timeout(&wcn->smd_compl, msecs_to_jiffies(SMD_MSG_TIMEOUT)); if (left <= 0) { wcn36xx_error("timeout waiting for smd open: %d", ret); return left; } return 0; }
int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn) { size_t s; void *cpu_addr; /* Allocate BD headers for MGMT frames */ /* Where this come from ask QC */ wcn->mgmt_mem_pool.chunk_size = WCN36XX_BD_CHUNK_SIZE + 16 - (WCN36XX_BD_CHUNK_SIZE % 8); s = wcn->mgmt_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_H; cpu_addr = dma_alloc_coherent(NULL, s, &wcn->mgmt_mem_pool.phy_addr, GFP_KERNEL); if (!cpu_addr) goto out_err; wcn->mgmt_mem_pool.virt_addr = cpu_addr; memset(cpu_addr, 0, s); /* Allocate BD headers for DATA frames */ /* Where this come from ask QC */ wcn->data_mem_pool.chunk_size = WCN36XX_BD_CHUNK_SIZE + 16 - (WCN36XX_BD_CHUNK_SIZE % 8); s = wcn->data_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_L; cpu_addr = dma_alloc_coherent(NULL, s, &wcn->data_mem_pool.phy_addr, GFP_KERNEL); if (!cpu_addr) goto out_err; wcn->data_mem_pool.virt_addr = cpu_addr; memset(cpu_addr, 0, s); return 0; out_err: wcn36xx_dxe_free_mem_pools(wcn); wcn36xx_error("Failed to allocate BD mempool"); return -ENOMEM; }
static void wcn36xx_smd_notify(void *data, unsigned event) { struct wcn36xx *wcn = (struct wcn36xx *)data; switch (event) { case SMD_EVENT_OPEN: complete(&wcn->smd_compl); break; case SMD_EVENT_DATA: queue_work(wcn->wq, &wcn->smd_work); break; case SMD_EVENT_CLOSE: break; case SMD_EVENT_STATUS: break; case SMD_EVENT_REOPEN_READY: break; default: wcn36xx_error("SMD_EVENT (%d) not supported", event); break; } }
static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf) { struct wcn36xx *wcn = hw->priv; struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv; int ret = 0; u8 key[WLAN_MAX_KEY_LEN]; wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 set key"); wcn36xx_dbg(WCN36XX_DBG_MAC, "Key: cmd=0x%x algo:0x%x, id:%d, len:%d flags 0x%x", cmd, key_conf->cipher, key_conf->keyidx, key_conf->keylen, key_conf->flags); wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "KEY: ", key_conf->key, key_conf->keylen); switch (key_conf->cipher) { case WLAN_CIPHER_SUITE_CCMP: wcn->encrypt_type = WCN36XX_HAL_ED_CCMP; break; case WLAN_CIPHER_SUITE_TKIP: wcn->encrypt_type = WCN36XX_HAL_ED_TKIP; break; default: wcn36xx_error("Unsupported key type 0x%x", key_conf->cipher); ret = -EOPNOTSUPP; goto out; break; } switch (cmd) { case SET_KEY: if (WCN36XX_HAL_ED_TKIP == wcn->encrypt_type) { /* * Supplicant is sending key in the wrong order: * Temporal Key (16 b) - TX MIC (8 b) - RX MIC (8 b) * but HW expects it to be in the order as described in * IEEE 802.11 spec (see chapter 11.7) like this: * Temporal Key (16 b) - RX MIC (8 b) - TX MIC (8 b) */ memcpy(key, key_conf->key, 16); memcpy(key + 16, key_conf->key + 24, 8); memcpy(key + 24, key_conf->key + 16, 8); } else { memcpy(key, key_conf->key, key_conf->keylen); } if (IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags) { sta_priv->is_data_encrypted = true; /* Reconfigure bss with encrypt_type */ if (NL80211_IFTYPE_STATION == vif->type) wcn36xx_smd_config_bss(wcn, vif, sta, sta->addr, true); wcn36xx_smd_set_stakey(wcn, wcn->encrypt_type, key_conf->keyidx, key_conf->keylen, key, get_sta_index(vif, sta_priv)); } else { wcn36xx_smd_set_bsskey(wcn, wcn->encrypt_type, key_conf->keyidx, key_conf->keylen, key); } break; case DISABLE_KEY: if (!(IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags)) { wcn36xx_smd_remove_bsskey(wcn, wcn->encrypt_type, key_conf->keyidx); } else { sta_priv->is_data_encrypted = false; /* do not remove key if disassociated */ if (wcn->aid) wcn36xx_smd_remove_stakey(wcn, wcn->encrypt_type, key_conf->keyidx, get_sta_index(vif, sta_priv)); } break; default: wcn36xx_error("Unsupported key cmd 0x%x", cmd); ret = -EOPNOTSUPP; goto out; break; } out: return ret; }
static int wcn36xx_start(struct ieee80211_hw *hw) { struct wcn36xx *wcn = hw->priv; int ret; wcn36xx_dbg(WCN36XX_DBG_MAC, "mac start"); /* SMD initialization */ ret = wcn36xx_smd_open(wcn); if (ret) { wcn36xx_error("Failed to open smd channel: %d", ret); goto out_err; } /* Not to receive INT until the whole buf from SMD is read */ smd_disable_read_intr(wcn->smd_ch); /* Allocate memory pools for Mgmt BD headers and Data BD headers */ ret = wcn36xx_dxe_allocate_mem_pools(wcn); if (ret) { wcn36xx_error("Failed to alloc DXE mempool: %d", ret); goto out_smd_close; } wcn36xx_dxe_alloc_ctl_blks(wcn); if (ret) { wcn36xx_error("Failed to alloc DXE ctl blocks: %d", ret); goto out_free_dxe_pool; } INIT_WORK(&wcn->rx_ready_work, wcn36xx_rx_ready_work); /* Maximum SMD message size is 4k */ wcn->smd_buf = kmalloc(4096, GFP_KERNEL); if (!wcn->smd_buf) { wcn36xx_error("Failed to allocate smd buf"); ret = -ENOMEM; goto out_free_dxe_ctl; } /* TODO pass configuration to FW */ ret = wcn36xx_smd_load_nv(wcn); if (ret) { wcn36xx_error("Failed to push NV to chip"); goto out_free_smd_buf; } ret = wcn36xx_smd_start(wcn); if (ret) { wcn36xx_error("Failed to start chip"); goto out_free_smd_buf; } /* DMA channel initialization */ ret = wcn36xx_dxe_init(wcn); if (ret) { wcn36xx_error("DXE init failed"); goto out_smd_stop; } wcn36xx_pmc_init(wcn); wcn36xx_debugfs_init(wcn); if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) { ret = wcn36xx_smd_feature_caps_exchange(wcn); if (ret) wcn36xx_warn("Exchange feature caps failed"); } return 0; out_smd_stop: wcn36xx_smd_stop(wcn); out_free_smd_buf: kfree(wcn->smd_buf); out_free_dxe_pool: wcn36xx_dxe_free_mem_pools(wcn); out_free_dxe_ctl: wcn36xx_dxe_free_ctl_blks(wcn); out_smd_close: wcn36xx_smd_close(wcn); out_err: return ret; }
int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn, struct sk_buff *skb, bool is_low) { struct wcn36xx_dxe_ctl *ctl = NULL; struct wcn36xx_dxe_desc *desc = NULL; struct wcn36xx_dxe_ch *ch = NULL; ch = is_low ? &wcn->dxe_tx_l_ch : &wcn->dxe_tx_h_ch; ctl = ch->head_blk_ctl; ctl->skb = NULL; desc = ctl->desc; /* Set source address of the BD we send */ desc->src_addr_l = ctl->bd_phy_addr; desc->dst_addr_l = ch->dxe_wq; desc->fr_len = sizeof(struct wcn36xx_tx_bd); desc->ctrl = ch->ctrl_bd; wcn36xx_dbg(WCN36XX_DBG_DXE, "DXE TX"); wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC1 >>> ", (char *)desc, sizeof(*desc)); wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "BD >>> ", (char *)ctl->bd_cpu_addr, sizeof(struct wcn36xx_tx_bd)); /* Set source address of the SKB we send */ ctl = ctl->next; ctl->skb = skb; desc = ctl->desc; if (ctl->bd_cpu_addr) { /* TODO: Recover from this situation */ wcn36xx_error("bd_cpu_addr cannot be NULL for skb DXE"); return -EINVAL; } desc->src_addr_l = dma_map_single(NULL, ctl->skb->data, ctl->skb->len, DMA_TO_DEVICE); desc->dst_addr_l = ch->dxe_wq; desc->fr_len = ctl->skb->len; /* set dxe descriptor to VALID */ desc->ctrl = ch->ctrl_skb; wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC2 >>> ", (char *)desc, sizeof(*desc)); wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "SKB >>> ", (char *)ctl->skb->data, ctl->skb->len); /* Move the head of the ring to the next empty descriptor */ ch->head_blk_ctl = ctl->next; /* * When connected and trying to send data frame chip can be in sleep * mode and writing to the register will not wake up the chip. Instead * notify chip about new frame through SMSM bus. */ if (wcn->pw_state == WCN36XX_BMPS) { smsm_change_state(SMSM_APPS_STATE, 0, WCN36XX_SMSM_WLAN_TX_ENABLE); } else { /* indicate End Of Packet and generate interrupt on descriptor * done. */ wcn36xx_dxe_write_register(wcn, ch->reg_ctrl, ch->def_ctrl); } return 0; }