Beispiel #1
0
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;

}
Beispiel #2
0
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;
}
Beispiel #3
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);
};
Beispiel #4
0
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;
}
Beispiel #5
0
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);
	}
}
Beispiel #6
0
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;
}
Beispiel #7
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;
}
Beispiel #8
0
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;
	}
}
Beispiel #9
0
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;
}
Beispiel #10
0
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;
}
Beispiel #11
0
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;
}