wcn36xx: mac80211 driver for Qualcomm WCN3660/WCN3680 hardware
This is a mac80211 driver for Qualcomm WCN3660/WCN3680 devices. So far WCN3660/WCN3680 is available only on MSM platform. Firmware can be found here: https://www.codeaurora.org/cgit/external/hisense/platform/vendor/qcom-opensource/wlan/prima/tree/firmware_bin?h=8130_CS Wiki page is available here: http://wireless.kernel.org/en/users/Drivers/wcn36xx A lot people made a contribution to this driver. Here is the list in alphabetical order: Eugene Krasnikov <k.eugene.e@gmail.com> Kalle Valo <kvalo@qca.qualcomm.com> Olof Johansson <dev@skyshaper.net> Pontus Fuchs <pontus.fuchs@gmail.com> Yanbo Li <yanbol@qti.qualcomm.com> Signed-off-by: Eugene Krasnikov <k.eugene.e@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
		
					parent
					
						
							
								c856197d6e
							
						
					
				
			
			
				commit
				
					
						8e84c25821
					
				
			
		
					 18 changed files with 10082 additions and 0 deletions
				
			
		|  | @ -6816,6 +6816,14 @@ L:	linux-hexagon@vger.kernel.org | |||
| S:	Supported | ||||
| F:	arch/hexagon/ | ||||
| 
 | ||||
| QUALCOMM WCN36XX WIRELESS DRIVER | ||||
| M:	Eugene Krasnikov <k.eugene.e@gmail.com> | ||||
| L:	wcn36xx@lists.infradead.org | ||||
| W:	http://wireless.kernel.org/en/users/Drivers/wcn36xx | ||||
| T:	git git://github.com/KrasnikovEugene/wcn36xx.git | ||||
| S:	Supported | ||||
| F:	drivers/net/wireless/ath/wcn36xx/ | ||||
| 
 | ||||
| QUICKCAM PARALLEL PORT WEBCAMS | ||||
| M:	Hans Verkuil <hverkuil@xs4all.nl> | ||||
| L:	linux-media@vger.kernel.org | ||||
|  |  | |||
|  | @ -32,5 +32,6 @@ source "drivers/net/wireless/ath/ath6kl/Kconfig" | |||
| source "drivers/net/wireless/ath/ar5523/Kconfig" | ||||
| source "drivers/net/wireless/ath/wil6210/Kconfig" | ||||
| source "drivers/net/wireless/ath/ath10k/Kconfig" | ||||
| source "drivers/net/wireless/ath/wcn36xx/Kconfig" | ||||
| 
 | ||||
| endif | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ obj-$(CONFIG_ATH6KL)		+= ath6kl/ | |||
| obj-$(CONFIG_AR5523)		+= ar5523/ | ||||
| obj-$(CONFIG_WIL6210)		+= wil6210/ | ||||
| obj-$(CONFIG_ATH10K)		+= ath10k/ | ||||
| obj-$(CONFIG_WCN36XX)		+= wcn36xx/ | ||||
| 
 | ||||
| obj-$(CONFIG_ATH_COMMON)	+= ath.o | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										16
									
								
								drivers/net/wireless/ath/wcn36xx/Kconfig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								drivers/net/wireless/ath/wcn36xx/Kconfig
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| config WCN36XX | ||||
| 	tristate "Qualcomm Atheros WCN3660/3680 support" | ||||
| 	depends on MAC80211 && HAS_DMA | ||||
| 	---help--- | ||||
| 	  This module adds support for wireless adapters based on | ||||
| 	  Qualcomm Atheros WCN3660 and WCN3680 mobile chipsets. | ||||
| 
 | ||||
| 	  If you choose to build a module, it'll be called wcn36xx. | ||||
| 
 | ||||
| config WCN36XX_DEBUGFS | ||||
| 	bool "WCN36XX debugfs support" | ||||
| 	depends on WCN36XX | ||||
| 	---help--- | ||||
| 	  Enabled debugfs support | ||||
| 
 | ||||
| 	  If unsure, say Y to make it easier to debug problems. | ||||
							
								
								
									
										7
									
								
								drivers/net/wireless/ath/wcn36xx/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								drivers/net/wireless/ath/wcn36xx/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| obj-$(CONFIG_WCN36XX) := wcn36xx.o | ||||
| wcn36xx-y +=   main.o \
 | ||||
|                dxe.o \
 | ||||
|                txrx.o \
 | ||||
|                smd.o \
 | ||||
|                pmc.o \
 | ||||
|                debug.o | ||||
							
								
								
									
										188
									
								
								drivers/net/wireless/ath/wcn36xx/debug.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								drivers/net/wireless/ath/wcn36xx/debug.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,188 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||||
|  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||||
|  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||||
|  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||||
| 
 | ||||
| #include <linux/debugfs.h> | ||||
| #include <linux/uaccess.h> | ||||
| #include "wcn36xx.h" | ||||
| #include "debug.h" | ||||
| #include "pmc.h" | ||||
| 
 | ||||
| #ifdef CONFIG_WCN36XX_DEBUGFS | ||||
| 
 | ||||
| static int wcn36xx_debugfs_open(struct inode *inode, struct file *file) | ||||
| { | ||||
| 	file->private_data = inode->i_private; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static ssize_t read_file_bool_bmps(struct file *file, char __user *user_buf, | ||||
| 				   size_t count, loff_t *ppos) | ||||
| { | ||||
| 	struct wcn36xx *wcn = file->private_data; | ||||
| 	struct wcn36xx_vif *vif_priv = NULL; | ||||
| 	struct ieee80211_vif *vif = NULL; | ||||
| 	char buf[3]; | ||||
| 
 | ||||
| 	list_for_each_entry(vif_priv, &wcn->vif_list, list) { | ||||
| 			vif = container_of((void *)vif_priv, | ||||
| 				   struct ieee80211_vif, | ||||
| 				   drv_priv); | ||||
| 			if (NL80211_IFTYPE_STATION == vif->type) { | ||||
| 				if (vif_priv->pw_state == WCN36XX_BMPS) | ||||
| 					buf[0] = '1'; | ||||
| 				else | ||||
| 					buf[0] = '0'; | ||||
| 				break; | ||||
| 			} | ||||
| 	} | ||||
| 	buf[1] = '\n'; | ||||
| 	buf[2] = 0x00; | ||||
| 
 | ||||
| 	return simple_read_from_buffer(user_buf, count, ppos, buf, 2); | ||||
| } | ||||
| 
 | ||||
| static ssize_t write_file_bool_bmps(struct file *file, | ||||
| 				    const char __user *user_buf, | ||||
| 				    size_t count, loff_t *ppos) | ||||
| { | ||||
| 	struct wcn36xx *wcn = file->private_data; | ||||
| 	struct wcn36xx_vif *vif_priv = NULL; | ||||
| 	struct ieee80211_vif *vif = NULL; | ||||
| 
 | ||||
| 	char buf[32]; | ||||
| 	int buf_size; | ||||
| 
 | ||||
| 	buf_size = min(count, (sizeof(buf)-1)); | ||||
| 	if (copy_from_user(buf, user_buf, buf_size)) | ||||
| 		return -EFAULT; | ||||
| 
 | ||||
| 	switch (buf[0]) { | ||||
| 	case 'y': | ||||
| 	case 'Y': | ||||
| 	case '1': | ||||
| 		list_for_each_entry(vif_priv, &wcn->vif_list, list) { | ||||
| 			vif = container_of((void *)vif_priv, | ||||
| 				   struct ieee80211_vif, | ||||
| 				   drv_priv); | ||||
| 			if (NL80211_IFTYPE_STATION == vif->type) { | ||||
| 				wcn36xx_enable_keep_alive_null_packet(wcn, vif); | ||||
| 				wcn36xx_pmc_enter_bmps_state(wcn, vif); | ||||
| 			} | ||||
| 		} | ||||
| 		break; | ||||
| 	case 'n': | ||||
| 	case 'N': | ||||
| 	case '0': | ||||
| 		list_for_each_entry(vif_priv, &wcn->vif_list, list) { | ||||
| 			vif = container_of((void *)vif_priv, | ||||
| 				   struct ieee80211_vif, | ||||
| 				   drv_priv); | ||||
| 			if (NL80211_IFTYPE_STATION == vif->type) | ||||
| 				wcn36xx_pmc_exit_bmps_state(wcn, vif); | ||||
| 		} | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
| static const struct file_operations fops_wcn36xx_bmps = { | ||||
| 	.open  =       wcn36xx_debugfs_open, | ||||
| 	.read  =       read_file_bool_bmps, | ||||
| 	.write =       write_file_bool_bmps, | ||||
| }; | ||||
| 
 | ||||
| static ssize_t write_file_dump(struct file *file, | ||||
| 				    const char __user *user_buf, | ||||
| 				    size_t count, loff_t *ppos) | ||||
| { | ||||
| 	struct wcn36xx *wcn = file->private_data; | ||||
| 	char buf[255], *tmp; | ||||
| 	int buf_size; | ||||
| 	u32 arg[WCN36xx_MAX_DUMP_ARGS]; | ||||
| 	int i; | ||||
| 
 | ||||
| 	memset(buf, 0, sizeof(buf)); | ||||
| 	memset(arg, 0, sizeof(arg)); | ||||
| 
 | ||||
| 	buf_size = min(count, (sizeof(buf) - 1)); | ||||
| 	if (copy_from_user(buf, user_buf, buf_size)) | ||||
| 		return -EFAULT; | ||||
| 
 | ||||
| 	tmp = buf; | ||||
| 
 | ||||
| 	for (i = 0; i < WCN36xx_MAX_DUMP_ARGS; i++) { | ||||
| 		char *begin; | ||||
| 		begin = strsep(&tmp, " "); | ||||
| 		if (begin == NULL) | ||||
| 			break; | ||||
| 
 | ||||
| 		if (kstrtoul(begin, 0, (unsigned long *)(arg + i)) != 0) | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	wcn36xx_info("DUMP args is %d %d %d %d %d\n", arg[0], arg[1], arg[2], | ||||
| 		     arg[3], arg[4]); | ||||
| 	wcn36xx_smd_dump_cmd_req(wcn, arg[0], arg[1], arg[2], arg[3], arg[4]); | ||||
| 
 | ||||
| 	return count; | ||||
| } | ||||
| 
 | ||||
| static const struct file_operations fops_wcn36xx_dump = { | ||||
| 	.open  =       wcn36xx_debugfs_open, | ||||
| 	.write =       write_file_dump, | ||||
| }; | ||||
| 
 | ||||
| #define ADD_FILE(name, mode, fop, priv_data)		\ | ||||
| 	do {							\ | ||||
| 		struct dentry *d;				\ | ||||
| 		d = debugfs_create_file(__stringify(name),	\ | ||||
| 					mode, dfs->rootdir,	\ | ||||
| 					priv_data, fop);	\ | ||||
| 		dfs->file_##name.dentry = d;			\ | ||||
| 		if (IS_ERR(d)) {				\ | ||||
| 			wcn36xx_warn("Create the debugfs entry failed");\ | ||||
| 			dfs->file_##name.dentry = NULL;		\ | ||||
| 		}						\ | ||||
| 	} while (0) | ||||
| 
 | ||||
| 
 | ||||
| void wcn36xx_debugfs_init(struct wcn36xx *wcn) | ||||
| { | ||||
| 	struct wcn36xx_dfs_entry *dfs = &wcn->dfs; | ||||
| 
 | ||||
| 	dfs->rootdir = debugfs_create_dir(KBUILD_MODNAME, | ||||
| 					  wcn->hw->wiphy->debugfsdir); | ||||
| 	if (IS_ERR(dfs->rootdir)) { | ||||
| 		wcn36xx_warn("Create the debugfs failed\n"); | ||||
| 		dfs->rootdir = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	ADD_FILE(bmps_switcher, S_IRUSR | S_IWUSR, | ||||
| 		 &fops_wcn36xx_bmps, wcn); | ||||
| 	ADD_FILE(dump, S_IWUSR, &fops_wcn36xx_dump, wcn); | ||||
| } | ||||
| 
 | ||||
| void wcn36xx_debugfs_exit(struct wcn36xx *wcn) | ||||
| { | ||||
| 	struct wcn36xx_dfs_entry *dfs = &wcn->dfs; | ||||
| 	debugfs_remove_recursive(dfs->rootdir); | ||||
| } | ||||
| 
 | ||||
| #endif /* CONFIG_WCN36XX_DEBUGFS */ | ||||
							
								
								
									
										49
									
								
								drivers/net/wireless/ath/wcn36xx/debug.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								drivers/net/wireless/ath/wcn36xx/debug.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||||
|  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||||
|  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||||
|  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _WCN36XX_DEBUG_H_ | ||||
| #define _WCN36XX_DEBUG_H_ | ||||
| 
 | ||||
| #include <linux/kernel.h> | ||||
| 
 | ||||
| #define WCN36xx_MAX_DUMP_ARGS	5 | ||||
| 
 | ||||
| #ifdef CONFIG_WCN36XX_DEBUGFS | ||||
| struct wcn36xx_dfs_file { | ||||
| 	struct dentry *dentry; | ||||
| 	u32 value; | ||||
| }; | ||||
| 
 | ||||
| struct wcn36xx_dfs_entry { | ||||
| 	struct dentry *rootdir; | ||||
| 	struct wcn36xx_dfs_file file_bmps_switcher; | ||||
| 	struct wcn36xx_dfs_file file_dump; | ||||
| }; | ||||
| 
 | ||||
| void wcn36xx_debugfs_init(struct wcn36xx *wcn); | ||||
| void wcn36xx_debugfs_exit(struct wcn36xx *wcn); | ||||
| 
 | ||||
| #else | ||||
| static inline void wcn36xx_debugfs_init(struct wcn36xx *wcn) | ||||
| { | ||||
| } | ||||
| static inline void wcn36xx_debugfs_exit(struct wcn36xx *wcn) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| #endif /* CONFIG_WCN36XX_DEBUGFS */ | ||||
| 
 | ||||
| #endif	/* _WCN36XX_DEBUG_H_ */ | ||||
							
								
								
									
										805
									
								
								drivers/net/wireless/ath/wcn36xx/dxe.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										805
									
								
								drivers/net/wireless/ath/wcn36xx/dxe.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,805 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||||
|  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||||
|  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||||
|  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| /* DXE - DMA transfer engine
 | ||||
|  * we have 2 channels(High prio and Low prio) for TX and 2 channels for RX. | ||||
|  * through low channels data packets are transfered | ||||
|  * through high channels managment packets are transfered | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||||
| 
 | ||||
| #include <linux/interrupt.h> | ||||
| #include "wcn36xx.h" | ||||
| #include "txrx.h" | ||||
| 
 | ||||
| void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low) | ||||
| { | ||||
| 	struct wcn36xx_dxe_ch *ch = is_low ? | ||||
| 		&wcn->dxe_tx_l_ch : | ||||
| 		&wcn->dxe_tx_h_ch; | ||||
| 
 | ||||
| 	return ch->head_blk_ctl->bd_cpu_addr; | ||||
| } | ||||
| 
 | ||||
| static void wcn36xx_dxe_write_register(struct wcn36xx *wcn, int addr, int data) | ||||
| { | ||||
| 	wcn36xx_dbg(WCN36XX_DBG_DXE, | ||||
| 		    "wcn36xx_dxe_write_register: addr=%x, data=%x\n", | ||||
| 		    addr, data); | ||||
| 
 | ||||
| 	writel(data, wcn->mmio + addr); | ||||
| } | ||||
| 
 | ||||
| static void wcn36xx_dxe_read_register(struct wcn36xx *wcn, int addr, int *data) | ||||
| { | ||||
| 	*data = readl(wcn->mmio + addr); | ||||
| 
 | ||||
| 	wcn36xx_dbg(WCN36XX_DBG_DXE, | ||||
| 		    "wcn36xx_dxe_read_register: addr=%x, data=%x\n", | ||||
| 		    addr, *data); | ||||
| } | ||||
| 
 | ||||
| static void wcn36xx_dxe_free_ctl_block(struct wcn36xx_dxe_ch *ch) | ||||
| { | ||||
| 	struct wcn36xx_dxe_ctl *ctl = ch->head_blk_ctl, *next; | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < ch->desc_num && ctl; i++) { | ||||
| 		next = ctl->next; | ||||
| 		kfree(ctl); | ||||
| 		ctl = next; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int wcn36xx_dxe_allocate_ctl_block(struct wcn36xx_dxe_ch *ch) | ||||
| { | ||||
| 	struct wcn36xx_dxe_ctl *prev_ctl = NULL; | ||||
| 	struct wcn36xx_dxe_ctl *cur_ctl = NULL; | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < ch->desc_num; i++) { | ||||
| 		cur_ctl = kzalloc(sizeof(*cur_ctl), GFP_KERNEL); | ||||
| 		if (!cur_ctl) | ||||
| 			goto out_fail; | ||||
| 
 | ||||
| 		cur_ctl->ctl_blk_order = i; | ||||
| 		if (i == 0) { | ||||
| 			ch->head_blk_ctl = cur_ctl; | ||||
| 			ch->tail_blk_ctl = cur_ctl; | ||||
| 		} else if (ch->desc_num - 1 == i) { | ||||
| 			prev_ctl->next = cur_ctl; | ||||
| 			cur_ctl->next = ch->head_blk_ctl; | ||||
| 		} else { | ||||
| 			prev_ctl->next = cur_ctl; | ||||
| 		} | ||||
| 		prev_ctl = cur_ctl; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| out_fail: | ||||
| 	wcn36xx_dxe_free_ctl_block(ch); | ||||
| 	return -ENOMEM; | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| 
 | ||||
| 	/* Initialize SMSM state  Clear TX Enable RING EMPTY STATE */ | ||||
| 	ret = wcn->ctrl_ops->smsm_change_state( | ||||
| 		WCN36XX_SMSM_WLAN_TX_ENABLE, | ||||
| 		WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| out_err: | ||||
| 	wcn36xx_err("Failed to allocate DXE control blocks\n"); | ||||
| 	wcn36xx_dxe_free_ctl_blks(wcn); | ||||
| 	return -ENOMEM; | ||||
| } | ||||
| 
 | ||||
| void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn) | ||||
| { | ||||
| 	wcn36xx_dxe_free_ctl_block(&wcn->dxe_tx_l_ch); | ||||
| 	wcn36xx_dxe_free_ctl_block(&wcn->dxe_tx_h_ch); | ||||
| 	wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_l_ch); | ||||
| 	wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_h_ch); | ||||
| } | ||||
| 
 | ||||
| static int wcn36xx_dxe_init_descs(struct wcn36xx_dxe_ch *wcn_ch) | ||||
| { | ||||
| 	struct wcn36xx_dxe_desc *cur_dxe = NULL; | ||||
| 	struct wcn36xx_dxe_desc *prev_dxe = NULL; | ||||
| 	struct wcn36xx_dxe_ctl *cur_ctl = NULL; | ||||
| 	size_t size; | ||||
| 	int i; | ||||
| 
 | ||||
| 	size = wcn_ch->desc_num * sizeof(struct wcn36xx_dxe_desc); | ||||
| 	wcn_ch->cpu_addr = dma_alloc_coherent(NULL, size, &wcn_ch->dma_addr, | ||||
| 					      GFP_KERNEL); | ||||
| 	if (!wcn_ch->cpu_addr) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	memset(wcn_ch->cpu_addr, 0, size); | ||||
| 
 | ||||
| 	cur_dxe = (struct wcn36xx_dxe_desc *)wcn_ch->cpu_addr; | ||||
| 	cur_ctl = wcn_ch->head_blk_ctl; | ||||
| 
 | ||||
| 	for (i = 0; i < wcn_ch->desc_num; i++) { | ||||
| 		cur_ctl->desc = cur_dxe; | ||||
| 		cur_ctl->desc_phy_addr = wcn_ch->dma_addr + | ||||
| 			i * sizeof(struct wcn36xx_dxe_desc); | ||||
| 
 | ||||
| 		switch (wcn_ch->ch_type) { | ||||
| 		case WCN36XX_DXE_CH_TX_L: | ||||
| 			cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_L; | ||||
| 			cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_L; | ||||
| 			break; | ||||
| 		case WCN36XX_DXE_CH_TX_H: | ||||
| 			cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_H; | ||||
| 			cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_H; | ||||
| 			break; | ||||
| 		case WCN36XX_DXE_CH_RX_L: | ||||
| 			cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_L; | ||||
| 			cur_dxe->src_addr_l = WCN36XX_DXE_WQ_RX_L; | ||||
| 			break; | ||||
| 		case WCN36XX_DXE_CH_RX_H: | ||||
| 			cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_H; | ||||
| 			cur_dxe->src_addr_l = WCN36XX_DXE_WQ_RX_H; | ||||
| 			break; | ||||
| 		} | ||||
| 		if (0 == i) { | ||||
| 			cur_dxe->phy_next_l = 0; | ||||
| 		} else if ((0 < i) && (i < wcn_ch->desc_num - 1)) { | ||||
| 			prev_dxe->phy_next_l = | ||||
| 				cur_ctl->desc_phy_addr; | ||||
| 		} else if (i == (wcn_ch->desc_num - 1)) { | ||||
| 			prev_dxe->phy_next_l = | ||||
| 				cur_ctl->desc_phy_addr; | ||||
| 			cur_dxe->phy_next_l = | ||||
| 				wcn_ch->head_blk_ctl->desc_phy_addr; | ||||
| 		} | ||||
| 		cur_ctl = cur_ctl->next; | ||||
| 		prev_dxe = cur_dxe; | ||||
| 		cur_dxe++; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void wcn36xx_dxe_init_tx_bd(struct wcn36xx_dxe_ch *ch, | ||||
| 				   struct wcn36xx_dxe_mem_pool *pool) | ||||
| { | ||||
| 	int i, chunk_size = pool->chunk_size; | ||||
| 	dma_addr_t bd_phy_addr = pool->phy_addr; | ||||
| 	void *bd_cpu_addr = pool->virt_addr; | ||||
| 	struct wcn36xx_dxe_ctl *cur = ch->head_blk_ctl; | ||||
| 
 | ||||
| 	for (i = 0; i < ch->desc_num; i++) { | ||||
| 		/* Only every second dxe needs a bd pointer,
 | ||||
| 		   the other will point to the skb data */ | ||||
| 		if (!(i & 1)) { | ||||
| 			cur->bd_phy_addr = bd_phy_addr; | ||||
| 			cur->bd_cpu_addr = bd_cpu_addr; | ||||
| 			bd_phy_addr += chunk_size; | ||||
| 			bd_cpu_addr += chunk_size; | ||||
| 		} else { | ||||
| 			cur->bd_phy_addr = 0; | ||||
| 			cur->bd_cpu_addr = NULL; | ||||
| 		} | ||||
| 		cur = cur->next; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int wcn36xx_dxe_enable_ch_int(struct wcn36xx *wcn, u16 wcn_ch) | ||||
| { | ||||
| 	int reg_data = 0; | ||||
| 
 | ||||
| 	wcn36xx_dxe_read_register(wcn, | ||||
| 				  WCN36XX_DXE_INT_MASK_REG, | ||||
| 				  ®_data); | ||||
| 
 | ||||
| 	reg_data |= wcn_ch; | ||||
| 
 | ||||
| 	wcn36xx_dxe_write_register(wcn, | ||||
| 				   WCN36XX_DXE_INT_MASK_REG, | ||||
| 				   (int)reg_data); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int wcn36xx_dxe_fill_skb(struct wcn36xx_dxe_ctl *ctl) | ||||
| { | ||||
| 	struct wcn36xx_dxe_desc *dxe = ctl->desc; | ||||
| 	struct sk_buff *skb; | ||||
| 
 | ||||
| 	skb = alloc_skb(WCN36XX_PKT_SIZE, GFP_ATOMIC); | ||||
| 	if (skb == NULL) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	dxe->dst_addr_l = dma_map_single(NULL, | ||||
| 					 skb_tail_pointer(skb), | ||||
| 					 WCN36XX_PKT_SIZE, | ||||
| 					 DMA_FROM_DEVICE); | ||||
| 	ctl->skb = skb; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int wcn36xx_dxe_ch_alloc_skb(struct wcn36xx *wcn, | ||||
| 				    struct wcn36xx_dxe_ch *wcn_ch) | ||||
| { | ||||
| 	int i; | ||||
| 	struct wcn36xx_dxe_ctl *cur_ctl = NULL; | ||||
| 
 | ||||
| 	cur_ctl = wcn_ch->head_blk_ctl; | ||||
| 
 | ||||
| 	for (i = 0; i < wcn_ch->desc_num; i++) { | ||||
| 		wcn36xx_dxe_fill_skb(cur_ctl); | ||||
| 		cur_ctl = cur_ctl->next; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void wcn36xx_dxe_ch_free_skbs(struct wcn36xx *wcn, | ||||
| 				     struct wcn36xx_dxe_ch *wcn_ch) | ||||
| { | ||||
| 	struct wcn36xx_dxe_ctl *cur = wcn_ch->head_blk_ctl; | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < wcn_ch->desc_num; i++) { | ||||
| 		kfree_skb(cur->skb); | ||||
| 		cur = cur->next; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status) | ||||
| { | ||||
| 	struct ieee80211_tx_info *info; | ||||
| 	struct sk_buff *skb; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&wcn->dxe_lock, flags); | ||||
| 	skb = wcn->tx_ack_skb; | ||||
| 	wcn->tx_ack_skb = NULL; | ||||
| 	spin_unlock_irqrestore(&wcn->dxe_lock, flags); | ||||
| 
 | ||||
| 	if (!skb) { | ||||
| 		wcn36xx_warn("Spurious TX complete indication\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	info = IEEE80211_SKB_CB(skb); | ||||
| 
 | ||||
| 	if (status == 1) | ||||
| 		info->flags |= IEEE80211_TX_STAT_ACK; | ||||
| 
 | ||||
| 	wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ack status: %d\n", status); | ||||
| 
 | ||||
| 	ieee80211_tx_status_irqsafe(wcn->hw, skb); | ||||
| 	ieee80211_wake_queues(wcn->hw); | ||||
| } | ||||
| 
 | ||||
| static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch) | ||||
| { | ||||
| 	struct wcn36xx_dxe_ctl *ctl = ch->tail_blk_ctl; | ||||
| 	struct ieee80211_tx_info *info; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Make at least one loop of do-while because in case ring is | ||||
| 	 * completely full head and tail are pointing to the same element | ||||
| 	 * and while-do will not make any cycles. | ||||
| 	 */ | ||||
| 	do { | ||||
| 		if (ctl->skb) { | ||||
| 			dma_unmap_single(NULL, ctl->desc->src_addr_l, | ||||
| 					 ctl->skb->len, DMA_TO_DEVICE); | ||||
| 			info = IEEE80211_SKB_CB(ctl->skb); | ||||
| 			if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) { | ||||
| 				/* Keep frame until TX status comes */ | ||||
| 				ieee80211_free_txskb(wcn->hw, ctl->skb); | ||||
| 			} | ||||
| 			spin_lock_irqsave(&ctl->skb_lock, flags); | ||||
| 			if (wcn->queues_stopped) { | ||||
| 				wcn->queues_stopped = false; | ||||
| 				ieee80211_wake_queues(wcn->hw); | ||||
| 			} | ||||
| 			spin_unlock_irqrestore(&ctl->skb_lock, flags); | ||||
| 
 | ||||
| 			ctl->skb = NULL; | ||||
| 		} | ||||
| 		ctl = ctl->next; | ||||
| 	} while (ctl != ch->head_blk_ctl && | ||||
| 	       !(ctl->desc->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)); | ||||
| 
 | ||||
| 	ch->tail_blk_ctl = ctl; | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) | ||||
| { | ||||
| 	struct wcn36xx *wcn = (struct wcn36xx *)dev; | ||||
| 	int int_src, int_reason; | ||||
| 
 | ||||
| 	wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src); | ||||
| 
 | ||||
| 	if (int_src & WCN36XX_INT_MASK_CHAN_TX_H) { | ||||
| 		wcn36xx_dxe_read_register(wcn, | ||||
| 					  WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H, | ||||
| 					  &int_reason); | ||||
| 
 | ||||
| 		/* TODO: Check int_reason */ | ||||
| 
 | ||||
| 		wcn36xx_dxe_write_register(wcn, | ||||
| 					   WCN36XX_DXE_0_INT_CLR, | ||||
| 					   WCN36XX_INT_MASK_CHAN_TX_H); | ||||
| 
 | ||||
| 		wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR, | ||||
| 					   WCN36XX_INT_MASK_CHAN_TX_H); | ||||
| 		wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready high\n"); | ||||
| 		reap_tx_dxes(wcn, &wcn->dxe_tx_h_ch); | ||||
| 	} | ||||
| 
 | ||||
| 	if (int_src & WCN36XX_INT_MASK_CHAN_TX_L) { | ||||
| 		wcn36xx_dxe_read_register(wcn, | ||||
| 					  WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L, | ||||
| 					  &int_reason); | ||||
| 		/* TODO: Check int_reason */ | ||||
| 
 | ||||
| 		wcn36xx_dxe_write_register(wcn, | ||||
| 					   WCN36XX_DXE_0_INT_CLR, | ||||
| 					   WCN36XX_INT_MASK_CHAN_TX_L); | ||||
| 
 | ||||
| 		wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR, | ||||
| 					   WCN36XX_INT_MASK_CHAN_TX_L); | ||||
| 		wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready low\n"); | ||||
| 		reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch); | ||||
| 	} | ||||
| 
 | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t wcn36xx_irq_rx_ready(int irq, void *dev) | ||||
| { | ||||
| 	struct wcn36xx *wcn = (struct wcn36xx *)dev; | ||||
| 
 | ||||
| 	disable_irq_nosync(wcn->rx_irq); | ||||
| 	wcn36xx_dxe_rx_frame(wcn); | ||||
| 	enable_irq(wcn->rx_irq); | ||||
| 	return IRQ_HANDLED; | ||||
| } | ||||
| 
 | ||||
| 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_err("failed to alloc tx irq\n"); | ||||
| 		goto out_err; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = request_irq(wcn->rx_irq, wcn36xx_irq_rx_ready, IRQF_TRIGGER_HIGH, | ||||
| 			  "wcn36xx_rx", wcn); | ||||
| 	if (ret) { | ||||
| 		wcn36xx_err("failed to alloc rx irq\n"); | ||||
| 		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_rx_handle_packets(struct wcn36xx *wcn, | ||||
| 				     struct wcn36xx_dxe_ch *ch) | ||||
| { | ||||
| 	struct wcn36xx_dxe_ctl *ctl = ch->head_blk_ctl; | ||||
| 	struct wcn36xx_dxe_desc *dxe = ctl->desc; | ||||
| 	dma_addr_t  dma_addr; | ||||
| 	struct sk_buff *skb; | ||||
| 
 | ||||
| 	while (!(dxe->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)) { | ||||
| 		skb = ctl->skb; | ||||
| 		dma_addr = dxe->dst_addr_l; | ||||
| 		wcn36xx_dxe_fill_skb(ctl); | ||||
| 
 | ||||
| 		switch (ch->ch_type) { | ||||
| 		case WCN36XX_DXE_CH_RX_L: | ||||
| 			dxe->ctrl = WCN36XX_DXE_CTRL_RX_L; | ||||
| 			wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, | ||||
| 						   WCN36XX_DXE_INT_CH1_MASK); | ||||
| 			break; | ||||
| 		case WCN36XX_DXE_CH_RX_H: | ||||
| 			dxe->ctrl = WCN36XX_DXE_CTRL_RX_H; | ||||
| 			wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, | ||||
| 						   WCN36XX_DXE_INT_CH3_MASK); | ||||
| 			break; | ||||
| 		default: | ||||
| 			wcn36xx_warn("Unknown channel\n"); | ||||
| 		} | ||||
| 
 | ||||
| 		dma_unmap_single(NULL, dma_addr, WCN36XX_PKT_SIZE, | ||||
| 				 DMA_FROM_DEVICE); | ||||
| 		wcn36xx_rx_skb(wcn, skb); | ||||
| 		ctl = ctl->next; | ||||
| 		dxe = ctl->desc; | ||||
| 	} | ||||
| 
 | ||||
| 	ch->head_blk_ctl = ctl; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| void wcn36xx_dxe_rx_frame(struct wcn36xx *wcn) | ||||
| { | ||||
| 	int int_src; | ||||
| 
 | ||||
| 	wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src); | ||||
| 
 | ||||
| 	/* RX_LOW_PRI */ | ||||
| 	if (int_src & WCN36XX_DXE_INT_CH1_MASK) { | ||||
| 		wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR, | ||||
| 					   WCN36XX_DXE_INT_CH1_MASK); | ||||
| 		wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_l_ch)); | ||||
| 	} | ||||
| 
 | ||||
| 	/* RX_HIGH_PRI */ | ||||
| 	if (int_src & WCN36XX_DXE_INT_CH3_MASK) { | ||||
| 		/* Clean up all the INT within this channel */ | ||||
| 		wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR, | ||||
| 					   WCN36XX_DXE_INT_CH3_MASK); | ||||
| 		wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_h_ch)); | ||||
| 	} | ||||
| 
 | ||||
| 	if (!int_src) | ||||
| 		wcn36xx_warn("No DXE interrupt pending\n"); | ||||
| } | ||||
| 
 | ||||
| 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_err("Failed to allocate BD mempool\n"); | ||||
| 	return -ENOMEM; | ||||
| } | ||||
| 
 | ||||
| void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn) | ||||
| { | ||||
| 	if (wcn->mgmt_mem_pool.virt_addr) | ||||
| 		dma_free_coherent(NULL, wcn->mgmt_mem_pool.chunk_size * | ||||
| 				  WCN36XX_DXE_CH_DESC_NUMB_TX_H, | ||||
| 				  wcn->mgmt_mem_pool.virt_addr, | ||||
| 				  wcn->mgmt_mem_pool.phy_addr); | ||||
| 
 | ||||
| 	if (wcn->data_mem_pool.virt_addr) { | ||||
| 		dma_free_coherent(NULL, wcn->data_mem_pool.chunk_size * | ||||
| 				  WCN36XX_DXE_CH_DESC_NUMB_TX_L, | ||||
| 				  wcn->data_mem_pool.virt_addr, | ||||
| 				  wcn->data_mem_pool.phy_addr); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn, | ||||
| 			 struct wcn36xx_vif *vif_priv, | ||||
| 			 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; | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	ch = is_low ? &wcn->dxe_tx_l_ch : &wcn->dxe_tx_h_ch; | ||||
| 
 | ||||
| 	ctl = ch->head_blk_ctl; | ||||
| 
 | ||||
| 	spin_lock_irqsave(&ctl->next->skb_lock, flags); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If skb is not null that means that we reached the tail of the ring | ||||
| 	 * hence ring is full. Stop queues to let mac80211 back off until ring | ||||
| 	 * has an empty slot again. | ||||
| 	 */ | ||||
| 	if (NULL != ctl->next->skb) { | ||||
| 		ieee80211_stop_queues(wcn->hw); | ||||
| 		wcn->queues_stopped = true; | ||||
| 		spin_unlock_irqrestore(&ctl->next->skb_lock, flags); | ||||
| 		return -EBUSY; | ||||
| 	} | ||||
| 	spin_unlock_irqrestore(&ctl->next->skb_lock, flags); | ||||
| 
 | ||||
| 	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\n"); | ||||
| 
 | ||||
| 	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) { | ||||
| 		wcn36xx_err("bd_cpu_addr cannot be NULL for skb DXE\n"); | ||||
| 		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 (is_low &&  vif_priv->pw_state == WCN36XX_BMPS) { | ||||
| 		wcn->ctrl_ops->smsm_change_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; | ||||
| } | ||||
| 
 | ||||
| int wcn36xx_dxe_init(struct wcn36xx *wcn) | ||||
| { | ||||
| 	int reg_data = 0, ret; | ||||
| 
 | ||||
| 	reg_data = WCN36XX_DXE_REG_RESET; | ||||
| 	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CSR_RESET, reg_data); | ||||
| 
 | ||||
| 	/* Setting interrupt path */ | ||||
| 	reg_data = WCN36XX_DXE_CCU_INT; | ||||
| 	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CCU_INT, reg_data); | ||||
| 
 | ||||
| 	/***************************************/ | ||||
| 	/* Init descriptors for TX LOW channel */ | ||||
| 	/***************************************/ | ||||
| 	wcn36xx_dxe_init_descs(&wcn->dxe_tx_l_ch); | ||||
| 	wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_l_ch, &wcn->data_mem_pool); | ||||
| 
 | ||||
| 	/* Write channel head to a NEXT register */ | ||||
| 	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_L, | ||||
| 		wcn->dxe_tx_l_ch.head_blk_ctl->desc_phy_addr); | ||||
| 
 | ||||
| 	/* Program DMA destination addr for TX LOW */ | ||||
| 	wcn36xx_dxe_write_register(wcn, | ||||
| 		WCN36XX_DXE_CH_DEST_ADDR_TX_L, | ||||
| 		WCN36XX_DXE_WQ_TX_L); | ||||
| 
 | ||||
| 	wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, ®_data); | ||||
| 	wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_L); | ||||
| 
 | ||||
| 	/***************************************/ | ||||
| 	/* Init descriptors for TX HIGH channel */ | ||||
| 	/***************************************/ | ||||
| 	wcn36xx_dxe_init_descs(&wcn->dxe_tx_h_ch); | ||||
| 	wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_h_ch, &wcn->mgmt_mem_pool); | ||||
| 
 | ||||
| 	/* Write channel head to a NEXT register */ | ||||
| 	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_H, | ||||
| 		wcn->dxe_tx_h_ch.head_blk_ctl->desc_phy_addr); | ||||
| 
 | ||||
| 	/* Program DMA destination addr for TX HIGH */ | ||||
| 	wcn36xx_dxe_write_register(wcn, | ||||
| 		WCN36XX_DXE_CH_DEST_ADDR_TX_H, | ||||
| 		WCN36XX_DXE_WQ_TX_H); | ||||
| 
 | ||||
| 	wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, ®_data); | ||||
| 
 | ||||
| 	/* Enable channel interrupts */ | ||||
| 	wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_H); | ||||
| 
 | ||||
| 	/***************************************/ | ||||
| 	/* Init descriptors for RX LOW channel */ | ||||
| 	/***************************************/ | ||||
| 	wcn36xx_dxe_init_descs(&wcn->dxe_rx_l_ch); | ||||
| 
 | ||||
| 	/* For RX we need to preallocated buffers */ | ||||
| 	wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_l_ch); | ||||
| 
 | ||||
| 	/* Write channel head to a NEXT register */ | ||||
| 	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_L, | ||||
| 		wcn->dxe_rx_l_ch.head_blk_ctl->desc_phy_addr); | ||||
| 
 | ||||
| 	/* Write DMA source address */ | ||||
| 	wcn36xx_dxe_write_register(wcn, | ||||
| 		WCN36XX_DXE_CH_SRC_ADDR_RX_L, | ||||
| 		WCN36XX_DXE_WQ_RX_L); | ||||
| 
 | ||||
| 	/* Program preallocated destination address */ | ||||
| 	wcn36xx_dxe_write_register(wcn, | ||||
| 		WCN36XX_DXE_CH_DEST_ADDR_RX_L, | ||||
| 		wcn->dxe_rx_l_ch.head_blk_ctl->desc->phy_next_l); | ||||
| 
 | ||||
| 	/* Enable default control registers */ | ||||
| 	wcn36xx_dxe_write_register(wcn, | ||||
| 		WCN36XX_DXE_REG_CTL_RX_L, | ||||
| 		WCN36XX_DXE_CH_DEFAULT_CTL_RX_L); | ||||
| 
 | ||||
| 	/* Enable channel interrupts */ | ||||
| 	wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_L); | ||||
| 
 | ||||
| 	/***************************************/ | ||||
| 	/* Init descriptors for RX HIGH channel */ | ||||
| 	/***************************************/ | ||||
| 	wcn36xx_dxe_init_descs(&wcn->dxe_rx_h_ch); | ||||
| 
 | ||||
| 	/* For RX we need to prealocat buffers */ | ||||
| 	wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_h_ch); | ||||
| 
 | ||||
| 	/* Write chanel head to a NEXT register */ | ||||
| 	wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_H, | ||||
| 		wcn->dxe_rx_h_ch.head_blk_ctl->desc_phy_addr); | ||||
| 
 | ||||
| 	/* Write DMA source address */ | ||||
| 	wcn36xx_dxe_write_register(wcn, | ||||
| 		WCN36XX_DXE_CH_SRC_ADDR_RX_H, | ||||
| 		WCN36XX_DXE_WQ_RX_H); | ||||
| 
 | ||||
| 	/* Program preallocated destination address */ | ||||
| 	wcn36xx_dxe_write_register(wcn, | ||||
| 		WCN36XX_DXE_CH_DEST_ADDR_RX_H, | ||||
| 		 wcn->dxe_rx_h_ch.head_blk_ctl->desc->phy_next_l); | ||||
| 
 | ||||
| 	/* Enable default control registers */ | ||||
| 	wcn36xx_dxe_write_register(wcn, | ||||
| 		WCN36XX_DXE_REG_CTL_RX_H, | ||||
| 		WCN36XX_DXE_CH_DEFAULT_CTL_RX_H); | ||||
| 
 | ||||
| 	/* Enable channel interrupts */ | ||||
| 	wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_H); | ||||
| 
 | ||||
| 	ret = wcn36xx_dxe_request_irqs(wcn); | ||||
| 	if (ret < 0) | ||||
| 		goto out_err; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| out_err: | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| void wcn36xx_dxe_deinit(struct wcn36xx *wcn) | ||||
| { | ||||
| 	free_irq(wcn->tx_irq, wcn); | ||||
| 	free_irq(wcn->rx_irq, wcn); | ||||
| 
 | ||||
| 	if (wcn->tx_ack_skb) { | ||||
| 		ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb); | ||||
| 		wcn->tx_ack_skb = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_l_ch); | ||||
| 	wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_h_ch); | ||||
| } | ||||
							
								
								
									
										284
									
								
								drivers/net/wireless/ath/wcn36xx/dxe.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										284
									
								
								drivers/net/wireless/ath/wcn36xx/dxe.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,284 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||||
|  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||||
|  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||||
|  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _DXE_H_ | ||||
| #define _DXE_H_ | ||||
| 
 | ||||
| #include "wcn36xx.h" | ||||
| 
 | ||||
| /*
 | ||||
| TX_LOW	= DMA0 | ||||
| TX_HIGH	= DMA4 | ||||
| RX_LOW	= DMA1 | ||||
| RX_HIGH	= DMA3 | ||||
| H2H_TEST_RX_TX = DMA2 | ||||
| */ | ||||
| 
 | ||||
| /* DXE registers */ | ||||
| #define WCN36XX_DXE_MEM_BASE			0x03000000 | ||||
| #define WCN36XX_DXE_MEM_REG			0x202000 | ||||
| 
 | ||||
| #define WCN36XX_DXE_CCU_INT			0xA0011 | ||||
| #define WCN36XX_DXE_REG_CCU_INT			0x200b10 | ||||
| 
 | ||||
| /* TODO This must calculated properly but not hardcoded */ | ||||
| #define WCN36XX_DXE_CTRL_TX_L			0x328a44 | ||||
| #define WCN36XX_DXE_CTRL_TX_H			0x32ce44 | ||||
| #define WCN36XX_DXE_CTRL_RX_L			0x12ad2f | ||||
| #define WCN36XX_DXE_CTRL_RX_H			0x12d12f | ||||
| #define WCN36XX_DXE_CTRL_TX_H_BD		0x30ce45 | ||||
| #define WCN36XX_DXE_CTRL_TX_H_SKB		0x32ce4d | ||||
| #define WCN36XX_DXE_CTRL_TX_L_BD		0x308a45 | ||||
| #define WCN36XX_DXE_CTRL_TX_L_SKB		0x328a4d | ||||
| 
 | ||||
| /* TODO This must calculated properly but not hardcoded */ | ||||
| #define WCN36XX_DXE_WQ_TX_L			0x17 | ||||
| #define WCN36XX_DXE_WQ_TX_H			0x17 | ||||
| #define WCN36XX_DXE_WQ_RX_L			0xB | ||||
| #define WCN36XX_DXE_WQ_RX_H			0x4 | ||||
| 
 | ||||
| /* DXE descriptor control filed */ | ||||
| #define WCN36XX_DXE_CTRL_VALID_MASK (0x00000001) | ||||
| 
 | ||||
| /* TODO This must calculated properly but not hardcoded */ | ||||
| /* DXE default control register values */ | ||||
| #define WCN36XX_DXE_CH_DEFAULT_CTL_RX_L		0x847EAD2F | ||||
| #define WCN36XX_DXE_CH_DEFAULT_CTL_RX_H		0x84FED12F | ||||
| #define WCN36XX_DXE_CH_DEFAULT_CTL_TX_H		0x853ECF4D | ||||
| #define WCN36XX_DXE_CH_DEFAULT_CTL_TX_L		0x843e8b4d | ||||
| 
 | ||||
| /* Common DXE registers */ | ||||
| #define WCN36XX_DXE_MEM_CSR			(WCN36XX_DXE_MEM_REG + 0x00) | ||||
| #define WCN36XX_DXE_REG_CSR_RESET		(WCN36XX_DXE_MEM_REG + 0x00) | ||||
| #define WCN36XX_DXE_ENCH_ADDR			(WCN36XX_DXE_MEM_REG + 0x04) | ||||
| #define WCN36XX_DXE_REG_CH_EN			(WCN36XX_DXE_MEM_REG + 0x08) | ||||
| #define WCN36XX_DXE_REG_CH_DONE			(WCN36XX_DXE_MEM_REG + 0x0C) | ||||
| #define WCN36XX_DXE_REG_CH_ERR			(WCN36XX_DXE_MEM_REG + 0x10) | ||||
| #define WCN36XX_DXE_INT_MASK_REG		(WCN36XX_DXE_MEM_REG + 0x18) | ||||
| #define WCN36XX_DXE_INT_SRC_RAW_REG		(WCN36XX_DXE_MEM_REG + 0x20) | ||||
| 	/* #define WCN36XX_DXE_INT_CH6_MASK	0x00000040 */ | ||||
| 	/* #define WCN36XX_DXE_INT_CH5_MASK	0x00000020 */ | ||||
| 	#define WCN36XX_DXE_INT_CH4_MASK	0x00000010 | ||||
| 	#define WCN36XX_DXE_INT_CH3_MASK	0x00000008 | ||||
| 	/* #define WCN36XX_DXE_INT_CH2_MASK	0x00000004 */ | ||||
| 	#define WCN36XX_DXE_INT_CH1_MASK	0x00000002 | ||||
| 	#define WCN36XX_DXE_INT_CH0_MASK	0x00000001 | ||||
| #define WCN36XX_DXE_0_INT_CLR			(WCN36XX_DXE_MEM_REG + 0x30) | ||||
| #define WCN36XX_DXE_0_INT_ED_CLR		(WCN36XX_DXE_MEM_REG + 0x34) | ||||
| #define WCN36XX_DXE_0_INT_DONE_CLR		(WCN36XX_DXE_MEM_REG + 0x38) | ||||
| #define WCN36XX_DXE_0_INT_ERR_CLR		(WCN36XX_DXE_MEM_REG + 0x3C) | ||||
| 
 | ||||
| #define WCN36XX_DXE_0_CH0_STATUS		(WCN36XX_DXE_MEM_REG + 0x404) | ||||
| #define WCN36XX_DXE_0_CH1_STATUS		(WCN36XX_DXE_MEM_REG + 0x444) | ||||
| #define WCN36XX_DXE_0_CH2_STATUS		(WCN36XX_DXE_MEM_REG + 0x484) | ||||
| #define WCN36XX_DXE_0_CH3_STATUS		(WCN36XX_DXE_MEM_REG + 0x4C4) | ||||
| #define WCN36XX_DXE_0_CH4_STATUS		(WCN36XX_DXE_MEM_REG + 0x504) | ||||
| 
 | ||||
| #define WCN36XX_DXE_REG_RESET			0x5c89 | ||||
| 
 | ||||
| /* Temporary BMU Workqueue 4 */ | ||||
| #define WCN36XX_DXE_BMU_WQ_RX_LOW		0xB | ||||
| #define WCN36XX_DXE_BMU_WQ_RX_HIGH		0x4 | ||||
| /* DMA channel offset */ | ||||
| #define WCN36XX_DXE_TX_LOW_OFFSET		0x400 | ||||
| #define WCN36XX_DXE_TX_HIGH_OFFSET		0x500 | ||||
| #define WCN36XX_DXE_RX_LOW_OFFSET		0x440 | ||||
| #define WCN36XX_DXE_RX_HIGH_OFFSET		0x4C0 | ||||
| 
 | ||||
| /* Address of the next DXE descriptor */ | ||||
| #define WCN36XX_DXE_CH_NEXT_DESC_ADDR		0x001C | ||||
| #define WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_L	(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_TX_LOW_OFFSET + \ | ||||
| 						 WCN36XX_DXE_CH_NEXT_DESC_ADDR) | ||||
| #define WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_H	(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_TX_HIGH_OFFSET + \ | ||||
| 						 WCN36XX_DXE_CH_NEXT_DESC_ADDR) | ||||
| #define WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_L	(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_RX_LOW_OFFSET + \ | ||||
| 						 WCN36XX_DXE_CH_NEXT_DESC_ADDR) | ||||
| #define WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_H	(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_RX_HIGH_OFFSET + \ | ||||
| 						 WCN36XX_DXE_CH_NEXT_DESC_ADDR) | ||||
| 
 | ||||
| /* DXE Descriptor source address */ | ||||
| #define WCN36XX_DXE_CH_SRC_ADDR			0x000C | ||||
| #define WCN36XX_DXE_CH_SRC_ADDR_RX_L		(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_RX_LOW_OFFSET + \ | ||||
| 						 WCN36XX_DXE_CH_SRC_ADDR) | ||||
| #define WCN36XX_DXE_CH_SRC_ADDR_RX_H		(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_RX_HIGH_OFFSET + \ | ||||
| 						 WCN36XX_DXE_CH_SRC_ADDR) | ||||
| 
 | ||||
| /* DXE Descriptor address destination address */ | ||||
| #define WCN36XX_DXE_CH_DEST_ADDR		0x0014 | ||||
| #define WCN36XX_DXE_CH_DEST_ADDR_TX_L		(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_TX_LOW_OFFSET + \ | ||||
| 						 WCN36XX_DXE_CH_DEST_ADDR) | ||||
| #define WCN36XX_DXE_CH_DEST_ADDR_TX_H		(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_TX_HIGH_OFFSET + \ | ||||
| 						 WCN36XX_DXE_CH_DEST_ADDR) | ||||
| #define WCN36XX_DXE_CH_DEST_ADDR_RX_L		(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_RX_LOW_OFFSET + \ | ||||
| 						 WCN36XX_DXE_CH_DEST_ADDR) | ||||
| #define WCN36XX_DXE_CH_DEST_ADDR_RX_H		(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_RX_HIGH_OFFSET + \ | ||||
| 						 WCN36XX_DXE_CH_DEST_ADDR) | ||||
| 
 | ||||
| /* Interrupt status */ | ||||
| #define WCN36XX_DXE_CH_STATUS_REG_ADDR		0x0004 | ||||
| #define WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L	(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_TX_LOW_OFFSET + \ | ||||
| 						 WCN36XX_DXE_CH_STATUS_REG_ADDR) | ||||
| #define WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H	(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_TX_HIGH_OFFSET + \ | ||||
| 						 WCN36XX_DXE_CH_STATUS_REG_ADDR) | ||||
| #define WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_L	(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_RX_LOW_OFFSET + \ | ||||
| 						 WCN36XX_DXE_CH_STATUS_REG_ADDR) | ||||
| #define WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_H	(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_RX_HIGH_OFFSET + \ | ||||
| 						 WCN36XX_DXE_CH_STATUS_REG_ADDR) | ||||
| 
 | ||||
| 
 | ||||
| /* DXE default control register */ | ||||
| #define WCN36XX_DXE_REG_CTL_RX_L		(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_RX_LOW_OFFSET) | ||||
| #define WCN36XX_DXE_REG_CTL_RX_H		(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_RX_HIGH_OFFSET) | ||||
| #define WCN36XX_DXE_REG_CTL_TX_H		(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_TX_HIGH_OFFSET) | ||||
| #define WCN36XX_DXE_REG_CTL_TX_L		(WCN36XX_DXE_MEM_REG + \ | ||||
| 						 WCN36XX_DXE_TX_LOW_OFFSET) | ||||
| 
 | ||||
| #define WCN36XX_SMSM_WLAN_TX_ENABLE		0x00000400 | ||||
| #define WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY	0x00000200 | ||||
| 
 | ||||
| 
 | ||||
| /* Interrupt control channel mask */ | ||||
| #define WCN36XX_INT_MASK_CHAN_TX_L		0x00000001 | ||||
| #define WCN36XX_INT_MASK_CHAN_RX_L		0x00000002 | ||||
| #define WCN36XX_INT_MASK_CHAN_RX_H		0x00000008 | ||||
| #define WCN36XX_INT_MASK_CHAN_TX_H		0x00000010 | ||||
| 
 | ||||
| #define WCN36XX_BD_CHUNK_SIZE			128 | ||||
| 
 | ||||
| #define WCN36XX_PKT_SIZE			0xF20 | ||||
| enum wcn36xx_dxe_ch_type { | ||||
| 	WCN36XX_DXE_CH_TX_L, | ||||
| 	WCN36XX_DXE_CH_TX_H, | ||||
| 	WCN36XX_DXE_CH_RX_L, | ||||
| 	WCN36XX_DXE_CH_RX_H | ||||
| }; | ||||
| 
 | ||||
| /* amount of descriptors per channel */ | ||||
| enum wcn36xx_dxe_ch_desc_num { | ||||
| 	WCN36XX_DXE_CH_DESC_NUMB_TX_L		= 128, | ||||
| 	WCN36XX_DXE_CH_DESC_NUMB_TX_H		= 10, | ||||
| 	WCN36XX_DXE_CH_DESC_NUMB_RX_L		= 512, | ||||
| 	WCN36XX_DXE_CH_DESC_NUMB_RX_H		= 40 | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct wcn36xx_dxe_desc - describes descriptor of one DXE buffer | ||||
|  * | ||||
|  * @ctrl: is a union that consists of following bits: | ||||
|  * union { | ||||
|  *	u32	valid		:1; //0 = DMA stop, 1 = DMA continue with this
 | ||||
|  *				    //descriptor
 | ||||
|  *	u32	transfer_type	:2; //0 = Host to Host space
 | ||||
|  *	u32	eop		:1; //End of Packet
 | ||||
|  *	u32	bd_handling	:1; //if transferType = Host to BMU, then 0
 | ||||
|  *				    // means first 128 bytes contain BD, and 1
 | ||||
|  *				    // means create new empty BD
 | ||||
|  *	u32	siq		:1; // SIQ
 | ||||
|  *	u32	diq		:1; // DIQ
 | ||||
|  *	u32	pdu_rel		:1; //0 = don't release BD and PDUs when done,
 | ||||
|  *				    // 1 = release them
 | ||||
|  *	u32	bthld_sel	:4; //BMU Threshold Select
 | ||||
|  *	u32	prio		:3; //Specifies the priority level to use for
 | ||||
|  *				    // the transfer
 | ||||
|  *	u32	stop_channel	:1; //1 = DMA stops processing further, channel
 | ||||
|  *				    //requires re-enabling after this
 | ||||
|  *	u32	intr		:1; //Interrupt on Descriptor Done
 | ||||
|  *	u32	rsvd		:1; //reserved
 | ||||
|  *	u32	size		:14;//14 bits used - ignored for BMU transfers,
 | ||||
|  *				    //only used for host to host transfers?
 | ||||
|  * } ctrl; | ||||
|  */ | ||||
| struct wcn36xx_dxe_desc { | ||||
| 	u32	ctrl; | ||||
| 	u32	fr_len; | ||||
| 
 | ||||
| 	u32	src_addr_l; | ||||
| 	u32	dst_addr_l; | ||||
| 	u32	phy_next_l; | ||||
| 	u32	src_addr_h; | ||||
| 	u32	dst_addr_h; | ||||
| 	u32	phy_next_h; | ||||
| } __packed; | ||||
| 
 | ||||
| /* DXE Control block */ | ||||
| struct wcn36xx_dxe_ctl { | ||||
| 	struct wcn36xx_dxe_ctl	*next; | ||||
| 	struct wcn36xx_dxe_desc	*desc; | ||||
| 	unsigned int		desc_phy_addr; | ||||
| 	int			ctl_blk_order; | ||||
| 	struct sk_buff		*skb; | ||||
| 	spinlock_t              skb_lock; | ||||
| 	void			*bd_cpu_addr; | ||||
| 	dma_addr_t		bd_phy_addr; | ||||
| }; | ||||
| 
 | ||||
| struct wcn36xx_dxe_ch { | ||||
| 	enum wcn36xx_dxe_ch_type	ch_type; | ||||
| 	void				*cpu_addr; | ||||
| 	dma_addr_t			dma_addr; | ||||
| 	enum wcn36xx_dxe_ch_desc_num	desc_num; | ||||
| 	/* DXE control block ring */ | ||||
| 	struct wcn36xx_dxe_ctl		*head_blk_ctl; | ||||
| 	struct wcn36xx_dxe_ctl		*tail_blk_ctl; | ||||
| 
 | ||||
| 	/* DXE channel specific configs */ | ||||
| 	u32				dxe_wq; | ||||
| 	u32				ctrl_bd; | ||||
| 	u32				ctrl_skb; | ||||
| 	u32				reg_ctrl; | ||||
| 	u32				def_ctrl; | ||||
| }; | ||||
| 
 | ||||
| /* Memory Pool for BD headers */ | ||||
| struct wcn36xx_dxe_mem_pool { | ||||
| 	int		chunk_size; | ||||
| 	void		*virt_addr; | ||||
| 	dma_addr_t	phy_addr; | ||||
| }; | ||||
| 
 | ||||
| struct wcn36xx_vif; | ||||
| int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn); | ||||
| void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn); | ||||
| void wcn36xx_dxe_rx_frame(struct wcn36xx *wcn); | ||||
| int wcn36xx_dxe_alloc_ctl_blks(struct wcn36xx *wcn); | ||||
| void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn); | ||||
| int wcn36xx_dxe_init(struct wcn36xx *wcn); | ||||
| void wcn36xx_dxe_deinit(struct wcn36xx *wcn); | ||||
| int wcn36xx_dxe_init_channels(struct wcn36xx *wcn); | ||||
| int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn, | ||||
| 			 struct wcn36xx_vif *vif_priv, | ||||
| 			 struct sk_buff *skb, | ||||
| 			 bool is_low); | ||||
| void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status); | ||||
| void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low); | ||||
| #endif	/* _DXE_H_ */ | ||||
							
								
								
									
										4657
									
								
								drivers/net/wireless/ath/wcn36xx/hal.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4657
									
								
								drivers/net/wireless/ath/wcn36xx/hal.h
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										1036
									
								
								drivers/net/wireless/ath/wcn36xx/main.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1036
									
								
								drivers/net/wireless/ath/wcn36xx/main.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										62
									
								
								drivers/net/wireless/ath/wcn36xx/pmc.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								drivers/net/wireless/ath/wcn36xx/pmc.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,62 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||||
|  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||||
|  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||||
|  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||||
| 
 | ||||
| #include "wcn36xx.h" | ||||
| 
 | ||||
| int wcn36xx_pmc_enter_bmps_state(struct wcn36xx *wcn, | ||||
| 				 struct ieee80211_vif *vif) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; | ||||
| 	/* TODO: Make sure the TX chain clean */ | ||||
| 	ret = wcn36xx_smd_enter_bmps(wcn, vif); | ||||
| 	if (!ret) { | ||||
| 		wcn36xx_dbg(WCN36XX_DBG_PMC, "Entered BMPS\n"); | ||||
| 		vif_priv->pw_state = WCN36XX_BMPS; | ||||
| 	} else { | ||||
| 		/*
 | ||||
| 		 * One of the reasons why HW will not enter BMPS is because | ||||
| 		 * driver is trying to enter bmps before first beacon was | ||||
| 		 * received just after auth complete | ||||
| 		 */ | ||||
| 		wcn36xx_err("Can not enter BMPS!\n"); | ||||
| 	} | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int wcn36xx_pmc_exit_bmps_state(struct wcn36xx *wcn, | ||||
| 				struct ieee80211_vif *vif) | ||||
| { | ||||
| 	struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; | ||||
| 
 | ||||
| 	if (WCN36XX_BMPS != vif_priv->pw_state) { | ||||
| 		wcn36xx_err("Not in BMPS mode, no need to exit from BMPS mode!\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 	wcn36xx_smd_exit_bmps(wcn, vif); | ||||
| 	vif_priv->pw_state = WCN36XX_FULL_POWER; | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int wcn36xx_enable_keep_alive_null_packet(struct wcn36xx *wcn, | ||||
| 					  struct ieee80211_vif *vif) | ||||
| { | ||||
| 	wcn36xx_dbg(WCN36XX_DBG_PMC, "%s\n", __func__); | ||||
| 	return wcn36xx_smd_keep_alive_req(wcn, vif, | ||||
| 					  WCN36XX_HAL_KEEP_ALIVE_NULL_PKT); | ||||
| } | ||||
							
								
								
									
										33
									
								
								drivers/net/wireless/ath/wcn36xx/pmc.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								drivers/net/wireless/ath/wcn36xx/pmc.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||||
|  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||||
|  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||||
|  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _WCN36XX_PMC_H_ | ||||
| #define _WCN36XX_PMC_H_ | ||||
| 
 | ||||
| struct wcn36xx; | ||||
| 
 | ||||
| enum wcn36xx_power_state { | ||||
| 	WCN36XX_FULL_POWER, | ||||
| 	WCN36XX_BMPS | ||||
| }; | ||||
| 
 | ||||
| int wcn36xx_pmc_enter_bmps_state(struct wcn36xx *wcn, | ||||
| 				 struct ieee80211_vif *vif); | ||||
| int wcn36xx_pmc_exit_bmps_state(struct wcn36xx *wcn, | ||||
| 				struct ieee80211_vif *vif); | ||||
| int wcn36xx_enable_keep_alive_null_packet(struct wcn36xx *wcn, | ||||
| 					  struct ieee80211_vif *vif); | ||||
| #endif	/* _WCN36XX_PMC_H_ */ | ||||
							
								
								
									
										2126
									
								
								drivers/net/wireless/ath/wcn36xx/smd.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2126
									
								
								drivers/net/wireless/ath/wcn36xx/smd.c
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										127
									
								
								drivers/net/wireless/ath/wcn36xx/smd.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								drivers/net/wireless/ath/wcn36xx/smd.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,127 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||||
|  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||||
|  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||||
|  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _SMD_H_ | ||||
| #define _SMD_H_ | ||||
| 
 | ||||
| #include "wcn36xx.h" | ||||
| 
 | ||||
| /* Max shared size is 4k but we take less.*/ | ||||
| #define WCN36XX_NV_FRAGMENT_SIZE			3072 | ||||
| 
 | ||||
| #define WCN36XX_HAL_BUF_SIZE				4096 | ||||
| 
 | ||||
| #define HAL_MSG_TIMEOUT 200 | ||||
| #define WCN36XX_SMSM_WLAN_TX_ENABLE			0x00000400 | ||||
| #define WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY		0x00000200 | ||||
| /* The PNO version info be contained in the rsp msg */ | ||||
| #define WCN36XX_FW_MSG_PNO_VERSION_MASK			0x8000 | ||||
| 
 | ||||
| enum wcn36xx_fw_msg_result { | ||||
| 	WCN36XX_FW_MSG_RESULT_SUCCESS			= 0, | ||||
| 	WCN36XX_FW_MSG_RESULT_SUCCESS_SYNC		= 1, | ||||
| 
 | ||||
| 	WCN36XX_FW_MSG_RESULT_MEM_FAIL			= 5, | ||||
| }; | ||||
| 
 | ||||
| /******************************/ | ||||
| /* SMD requests and responses */ | ||||
| /******************************/ | ||||
| struct wcn36xx_fw_msg_status_rsp { | ||||
| 	u32	status; | ||||
| } __packed; | ||||
| 
 | ||||
| struct wcn36xx_hal_ind_msg { | ||||
| 	struct list_head list; | ||||
| 	u8 *msg; | ||||
| 	size_t msg_len; | ||||
| }; | ||||
| 
 | ||||
| struct wcn36xx; | ||||
| 
 | ||||
| int wcn36xx_smd_open(struct wcn36xx *wcn); | ||||
| void wcn36xx_smd_close(struct wcn36xx *wcn); | ||||
| 
 | ||||
| int wcn36xx_smd_load_nv(struct wcn36xx *wcn); | ||||
| int wcn36xx_smd_start(struct wcn36xx *wcn); | ||||
| int wcn36xx_smd_stop(struct wcn36xx *wcn); | ||||
| int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode); | ||||
| int wcn36xx_smd_start_scan(struct wcn36xx *wcn); | ||||
| int wcn36xx_smd_end_scan(struct wcn36xx *wcn); | ||||
| int wcn36xx_smd_finish_scan(struct wcn36xx *wcn, | ||||
| 			    enum wcn36xx_hal_sys_mode mode); | ||||
| int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn); | ||||
| int wcn36xx_smd_add_sta_self(struct wcn36xx *wcn, struct ieee80211_vif *vif); | ||||
| int wcn36xx_smd_delete_sta_self(struct wcn36xx *wcn, u8 *addr); | ||||
| int wcn36xx_smd_delete_sta(struct wcn36xx *wcn, u8 sta_index); | ||||
| int wcn36xx_smd_join(struct wcn36xx *wcn, const u8 *bssid, u8 *vif, u8 ch); | ||||
| int wcn36xx_smd_set_link_st(struct wcn36xx *wcn, const u8 *bssid, | ||||
| 			    const u8 *sta_mac, | ||||
| 			    enum wcn36xx_hal_link_state state); | ||||
| int wcn36xx_smd_config_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif, | ||||
| 			   struct ieee80211_sta *sta, const u8 *bssid, | ||||
| 			   bool update); | ||||
| int wcn36xx_smd_delete_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif); | ||||
| int wcn36xx_smd_config_sta(struct wcn36xx *wcn, struct ieee80211_vif *vif, | ||||
| 			   struct ieee80211_sta *sta); | ||||
| int wcn36xx_smd_send_beacon(struct wcn36xx *wcn, struct ieee80211_vif *vif, | ||||
| 			    struct sk_buff *skb_beacon, u16 tim_off, | ||||
| 			    u16 p2p_off); | ||||
| int wcn36xx_smd_switch_channel(struct wcn36xx *wcn, | ||||
| 			       struct ieee80211_vif *vif, int ch); | ||||
| int wcn36xx_smd_update_proberesp_tmpl(struct wcn36xx *wcn, | ||||
| 				      struct ieee80211_vif *vif, | ||||
| 				      struct sk_buff *skb); | ||||
| int wcn36xx_smd_set_stakey(struct wcn36xx *wcn, | ||||
| 			   enum ani_ed_type enc_type, | ||||
| 			   u8 keyidx, | ||||
| 			   u8 keylen, | ||||
| 			   u8 *key, | ||||
| 			   u8 sta_index); | ||||
| int wcn36xx_smd_set_bsskey(struct wcn36xx *wcn, | ||||
| 			   enum ani_ed_type enc_type, | ||||
| 			   u8 keyidx, | ||||
| 			   u8 keylen, | ||||
| 			   u8 *key); | ||||
| int wcn36xx_smd_remove_stakey(struct wcn36xx *wcn, | ||||
| 			      enum ani_ed_type enc_type, | ||||
| 			      u8 keyidx, | ||||
| 			      u8 sta_index); | ||||
| int wcn36xx_smd_remove_bsskey(struct wcn36xx *wcn, | ||||
| 			      enum ani_ed_type enc_type, | ||||
| 			      u8 keyidx); | ||||
| int wcn36xx_smd_enter_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif); | ||||
| int wcn36xx_smd_exit_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif); | ||||
| int wcn36xx_smd_set_power_params(struct wcn36xx *wcn, bool ignore_dtim); | ||||
| int wcn36xx_smd_keep_alive_req(struct wcn36xx *wcn, | ||||
| 			       struct ieee80211_vif *vif, | ||||
| 			       int packet_type); | ||||
| int wcn36xx_smd_dump_cmd_req(struct wcn36xx *wcn, u32 arg1, u32 arg2, | ||||
| 			     u32 arg3, u32 arg4, u32 arg5); | ||||
| int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn); | ||||
| 
 | ||||
| int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn, | ||||
| 		struct ieee80211_sta *sta, | ||||
| 		u16 tid, | ||||
| 		u16 *ssn, | ||||
| 		u8 direction, | ||||
| 		u8 sta_index); | ||||
| int wcn36xx_smd_add_ba(struct wcn36xx *wcn); | ||||
| int wcn36xx_smd_del_ba(struct wcn36xx *wcn, u16 tid, u8 sta_index); | ||||
| int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index); | ||||
| 
 | ||||
| int wcn36xx_smd_update_cfg(struct wcn36xx *wcn, u32 cfg_id, u32 value); | ||||
| #endif	/* _SMD_H_ */ | ||||
							
								
								
									
										284
									
								
								drivers/net/wireless/ath/wcn36xx/txrx.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										284
									
								
								drivers/net/wireless/ath/wcn36xx/txrx.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,284 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||||
|  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||||
|  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||||
|  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||||
| 
 | ||||
| #include "txrx.h" | ||||
| 
 | ||||
| static inline int get_rssi0(struct wcn36xx_rx_bd *bd) | ||||
| { | ||||
| 	return 100 - ((bd->phy_stat0 >> 24) & 0xff); | ||||
| } | ||||
| 
 | ||||
| int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb) | ||||
| { | ||||
| 	struct ieee80211_rx_status status; | ||||
| 	struct ieee80211_hdr *hdr; | ||||
| 	struct wcn36xx_rx_bd *bd; | ||||
| 	u16 fc, sn; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * All fields must be 0, otherwise it can lead to | ||||
| 	 * unexpected consequences. | ||||
| 	 */ | ||||
| 	memset(&status, 0, sizeof(status)); | ||||
| 
 | ||||
| 	bd = (struct wcn36xx_rx_bd *)skb->data; | ||||
| 	buff_to_be((u32 *)bd, sizeof(*bd)/sizeof(u32)); | ||||
| 	wcn36xx_dbg_dump(WCN36XX_DBG_RX_DUMP, | ||||
| 			 "BD   <<< ", (char *)bd, | ||||
| 			 sizeof(struct wcn36xx_rx_bd)); | ||||
| 
 | ||||
| 	skb_put(skb, bd->pdu.mpdu_header_off + bd->pdu.mpdu_len); | ||||
| 	skb_pull(skb, bd->pdu.mpdu_header_off); | ||||
| 
 | ||||
| 	status.mactime = 10; | ||||
| 	status.freq = WCN36XX_CENTER_FREQ(wcn); | ||||
| 	status.band = WCN36XX_BAND(wcn); | ||||
| 	status.signal = -get_rssi0(bd); | ||||
| 	status.antenna = 1; | ||||
| 	status.rate_idx = 1; | ||||
| 	status.flag = 0; | ||||
| 	status.rx_flags = 0; | ||||
| 	status.flag |= RX_FLAG_IV_STRIPPED | | ||||
| 		       RX_FLAG_MMIC_STRIPPED | | ||||
| 		       RX_FLAG_DECRYPTED; | ||||
| 
 | ||||
| 	wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x status->vendor_radiotap_len=%x\n", | ||||
| 		    status.flag,  status.vendor_radiotap_len); | ||||
| 
 | ||||
| 	memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); | ||||
| 
 | ||||
| 	hdr = (struct ieee80211_hdr *) skb->data; | ||||
| 	fc = __le16_to_cpu(hdr->frame_control); | ||||
| 	sn = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)); | ||||
| 
 | ||||
| 	if (ieee80211_is_beacon(hdr->frame_control)) { | ||||
| 		wcn36xx_dbg(WCN36XX_DBG_BEACON, "beacon skb %p len %d fc %04x sn %d\n", | ||||
| 			    skb, skb->len, fc, sn); | ||||
| 		wcn36xx_dbg_dump(WCN36XX_DBG_BEACON_DUMP, "SKB <<< ", | ||||
| 				 (char *)skb->data, skb->len); | ||||
| 	} else { | ||||
| 		wcn36xx_dbg(WCN36XX_DBG_RX, "rx skb %p len %d fc %04x sn %d\n", | ||||
| 			    skb, skb->len, fc, sn); | ||||
| 		wcn36xx_dbg_dump(WCN36XX_DBG_RX_DUMP, "SKB <<< ", | ||||
| 				 (char *)skb->data, skb->len); | ||||
| 	} | ||||
| 
 | ||||
| 	ieee80211_rx_irqsafe(wcn->hw, skb); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static void wcn36xx_set_tx_pdu(struct wcn36xx_tx_bd *bd, | ||||
| 			       u32 mpdu_header_len, | ||||
| 			       u32 len, | ||||
| 			       u16 tid) | ||||
| { | ||||
| 	bd->pdu.mpdu_header_len = mpdu_header_len; | ||||
| 	bd->pdu.mpdu_header_off = sizeof(*bd); | ||||
| 	bd->pdu.mpdu_data_off = bd->pdu.mpdu_header_len + | ||||
| 		bd->pdu.mpdu_header_off; | ||||
| 	bd->pdu.mpdu_len = len; | ||||
| 	bd->pdu.tid = tid; | ||||
| } | ||||
| 
 | ||||
| static inline struct wcn36xx_vif *get_vif_by_addr(struct wcn36xx *wcn, | ||||
| 						  u8 *addr) | ||||
| { | ||||
| 	struct wcn36xx_vif *vif_priv = NULL; | ||||
| 	struct ieee80211_vif *vif = NULL; | ||||
| 	list_for_each_entry(vif_priv, &wcn->vif_list, list) { | ||||
| 			vif = container_of((void *)vif_priv, | ||||
| 				   struct ieee80211_vif, | ||||
| 				   drv_priv); | ||||
| 			if (memcmp(vif->addr, addr, ETH_ALEN) == 0) | ||||
| 				return vif_priv; | ||||
| 	} | ||||
| 	wcn36xx_warn("vif %pM not found\n", addr); | ||||
| 	return NULL; | ||||
| } | ||||
| static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd, | ||||
| 				struct wcn36xx *wcn, | ||||
| 				struct wcn36xx_vif **vif_priv, | ||||
| 				struct wcn36xx_sta *sta_priv, | ||||
| 				struct ieee80211_hdr *hdr, | ||||
| 				bool bcast) | ||||
| { | ||||
| 	struct ieee80211_vif *vif = NULL; | ||||
| 	struct wcn36xx_vif *__vif_priv = NULL; | ||||
| 	bd->bd_rate = WCN36XX_BD_RATE_DATA; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * For not unicast frames mac80211 will not set sta pointer so use | ||||
| 	 * self_sta_index instead. | ||||
| 	 */ | ||||
| 	if (sta_priv) { | ||||
| 		__vif_priv = sta_priv->vif; | ||||
| 		vif = container_of((void *)__vif_priv, | ||||
| 				   struct ieee80211_vif, | ||||
| 				   drv_priv); | ||||
| 
 | ||||
| 		if (vif->type == NL80211_IFTYPE_STATION) { | ||||
| 			bd->sta_index = sta_priv->bss_sta_index; | ||||
| 			bd->dpu_desc_idx = sta_priv->bss_dpu_desc_index; | ||||
| 		} else if (vif->type == NL80211_IFTYPE_AP || | ||||
| 			   vif->type == NL80211_IFTYPE_ADHOC || | ||||
| 			   vif->type == NL80211_IFTYPE_MESH_POINT) { | ||||
| 			bd->sta_index = sta_priv->sta_index; | ||||
| 			bd->dpu_desc_idx = sta_priv->dpu_desc_index; | ||||
| 		} | ||||
| 	} else { | ||||
| 		__vif_priv = get_vif_by_addr(wcn, hdr->addr2); | ||||
| 		bd->sta_index = __vif_priv->self_sta_index; | ||||
| 		bd->dpu_desc_idx = __vif_priv->self_dpu_desc_index; | ||||
| 	} | ||||
| 
 | ||||
| 	bd->dpu_sign = __vif_priv->ucast_dpu_signature; | ||||
| 
 | ||||
| 	if (ieee80211_is_nullfunc(hdr->frame_control) || | ||||
| 	   (sta_priv && !sta_priv->is_data_encrypted)) | ||||
| 		bd->dpu_ne = 1; | ||||
| 
 | ||||
| 	if (bcast) { | ||||
| 		bd->ub = 1; | ||||
| 		bd->ack_policy = 1; | ||||
| 	} | ||||
| 	*vif_priv = __vif_priv; | ||||
| } | ||||
| 
 | ||||
| static void wcn36xx_set_tx_mgmt(struct wcn36xx_tx_bd *bd, | ||||
| 				struct wcn36xx *wcn, | ||||
| 				struct wcn36xx_vif **vif_priv, | ||||
| 				struct ieee80211_hdr *hdr, | ||||
| 				bool bcast) | ||||
| { | ||||
| 	struct wcn36xx_vif *__vif_priv = | ||||
| 		get_vif_by_addr(wcn, hdr->addr2); | ||||
| 	bd->sta_index = __vif_priv->self_sta_index; | ||||
| 	bd->dpu_desc_idx = __vif_priv->self_dpu_desc_index; | ||||
| 	bd->dpu_ne = 1; | ||||
| 
 | ||||
| 	/* default rate for unicast */ | ||||
| 	if (ieee80211_is_mgmt(hdr->frame_control)) | ||||
| 		bd->bd_rate = (WCN36XX_BAND(wcn) == IEEE80211_BAND_5GHZ) ? | ||||
| 			WCN36XX_BD_RATE_CTRL : | ||||
| 			WCN36XX_BD_RATE_MGMT; | ||||
| 	else if (ieee80211_is_ctl(hdr->frame_control)) | ||||
| 		bd->bd_rate = WCN36XX_BD_RATE_CTRL; | ||||
| 	else | ||||
| 		wcn36xx_warn("frame control type unknown\n"); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * In joining state trick hardware that probe is sent as | ||||
| 	 * unicast even if address is broadcast. | ||||
| 	 */ | ||||
| 	if (__vif_priv->is_joining && | ||||
| 	    ieee80211_is_probe_req(hdr->frame_control)) | ||||
| 		bcast = false; | ||||
| 
 | ||||
| 	if (bcast) { | ||||
| 		/* broadcast */ | ||||
| 		bd->ub = 1; | ||||
| 		/* No ack needed not unicast */ | ||||
| 		bd->ack_policy = 1; | ||||
| 		bd->queue_id = WCN36XX_TX_B_WQ_ID; | ||||
| 	} else | ||||
| 		bd->queue_id = WCN36XX_TX_U_WQ_ID; | ||||
| 	*vif_priv = __vif_priv; | ||||
| } | ||||
| 
 | ||||
| int wcn36xx_start_tx(struct wcn36xx *wcn, | ||||
| 		     struct wcn36xx_sta *sta_priv, | ||||
| 		     struct sk_buff *skb) | ||||
| { | ||||
| 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; | ||||
| 	struct wcn36xx_vif *vif_priv = NULL; | ||||
| 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); | ||||
| 	unsigned long flags; | ||||
| 	bool is_low = ieee80211_is_data(hdr->frame_control); | ||||
| 	bool bcast = is_broadcast_ether_addr(hdr->addr1) || | ||||
| 		is_multicast_ether_addr(hdr->addr1); | ||||
| 	struct wcn36xx_tx_bd *bd = wcn36xx_dxe_get_next_bd(wcn, is_low); | ||||
| 
 | ||||
| 	if (!bd) { | ||||
| 		/*
 | ||||
| 		 * TX DXE are used in pairs. One for the BD and one for the | ||||
| 		 * actual frame. The BD DXE's has a preallocated buffer while | ||||
| 		 * the skb ones does not. If this isn't true something is really | ||||
| 		 * wierd. TODO: Recover from this situation | ||||
| 		 */ | ||||
| 
 | ||||
| 		wcn36xx_err("bd address may not be NULL for BD DXE\n"); | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	memset(bd, 0, sizeof(*bd)); | ||||
| 
 | ||||
| 	wcn36xx_dbg(WCN36XX_DBG_TX, | ||||
| 		    "tx skb %p len %d fc %04x sn %d %s %s\n", | ||||
| 		    skb, skb->len, __le16_to_cpu(hdr->frame_control), | ||||
| 		    IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)), | ||||
| 		    is_low ? "low" : "high", bcast ? "bcast" : "ucast"); | ||||
| 
 | ||||
| 	wcn36xx_dbg_dump(WCN36XX_DBG_TX_DUMP, "", skb->data, skb->len); | ||||
| 
 | ||||
| 	bd->dpu_rf = WCN36XX_BMU_WQ_TX; | ||||
| 
 | ||||
| 	bd->tx_comp = info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS; | ||||
| 	if (bd->tx_comp) { | ||||
| 		wcn36xx_dbg(WCN36XX_DBG_DXE, "TX_ACK status requested\n"); | ||||
| 		spin_lock_irqsave(&wcn->dxe_lock, flags); | ||||
| 		if (wcn->tx_ack_skb) { | ||||
| 			spin_unlock_irqrestore(&wcn->dxe_lock, flags); | ||||
| 			wcn36xx_warn("tx_ack_skb already set\n"); | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
| 
 | ||||
| 		wcn->tx_ack_skb = skb; | ||||
| 		spin_unlock_irqrestore(&wcn->dxe_lock, flags); | ||||
| 
 | ||||
| 		/* Only one at a time is supported by fw. Stop the TX queues
 | ||||
| 		 * until the ack status gets back. | ||||
| 		 * | ||||
| 		 * TODO: Add watchdog in case FW does not answer | ||||
| 		 */ | ||||
| 		ieee80211_stop_queues(wcn->hw); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Data frames served first*/ | ||||
| 	if (is_low) { | ||||
| 		wcn36xx_set_tx_data(bd, wcn, &vif_priv, sta_priv, hdr, bcast); | ||||
| 		wcn36xx_set_tx_pdu(bd, | ||||
| 			   ieee80211_is_data_qos(hdr->frame_control) ? | ||||
| 			   sizeof(struct ieee80211_qos_hdr) : | ||||
| 			   sizeof(struct ieee80211_hdr_3addr), | ||||
| 			   skb->len, sta_priv ? sta_priv->tid : 0); | ||||
| 	} else { | ||||
| 		/* MGMT and CTRL frames are handeld here*/ | ||||
| 		wcn36xx_set_tx_mgmt(bd, wcn, &vif_priv, hdr, bcast); | ||||
| 		wcn36xx_set_tx_pdu(bd, | ||||
| 			   ieee80211_is_data_qos(hdr->frame_control) ? | ||||
| 			   sizeof(struct ieee80211_qos_hdr) : | ||||
| 			   sizeof(struct ieee80211_hdr_3addr), | ||||
| 			   skb->len, WCN36XX_TID); | ||||
| 	} | ||||
| 
 | ||||
| 	buff_to_be((u32 *)bd, sizeof(*bd)/sizeof(u32)); | ||||
| 	bd->tx_bd_sign = 0xbdbdbdbd; | ||||
| 
 | ||||
| 	return wcn36xx_dxe_tx_frame(wcn, vif_priv, skb, is_low); | ||||
| } | ||||
							
								
								
									
										160
									
								
								drivers/net/wireless/ath/wcn36xx/txrx.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								drivers/net/wireless/ath/wcn36xx/txrx.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,160 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||||
|  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||||
|  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||||
|  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _TXRX_H_ | ||||
| #define _TXRX_H_ | ||||
| 
 | ||||
| #include <linux/etherdevice.h> | ||||
| #include "wcn36xx.h" | ||||
| 
 | ||||
| /* TODO describe all properties */ | ||||
| #define WCN36XX_802_11_HEADER_LEN	24 | ||||
| #define WCN36XX_BMU_WQ_TX		25 | ||||
| #define WCN36XX_TID			7 | ||||
| /* broadcast wq ID */ | ||||
| #define WCN36XX_TX_B_WQ_ID		0xA | ||||
| #define WCN36XX_TX_U_WQ_ID		0x9 | ||||
| /* bd_rate */ | ||||
| #define WCN36XX_BD_RATE_DATA 0 | ||||
| #define WCN36XX_BD_RATE_MGMT 2 | ||||
| #define WCN36XX_BD_RATE_CTRL 3 | ||||
| 
 | ||||
| struct wcn36xx_pdu { | ||||
| 	u32	dpu_fb:8; | ||||
| 	u32	adu_fb:8; | ||||
| 	u32	pdu_id:16; | ||||
| 
 | ||||
| 	/* 0x04*/ | ||||
| 	u32	tail_pdu_idx:16; | ||||
| 	u32	head_pdu_idx:16; | ||||
| 
 | ||||
| 	/* 0x08*/ | ||||
| 	u32	pdu_count:7; | ||||
| 	u32	mpdu_data_off:9; | ||||
| 	u32	mpdu_header_off:8; | ||||
| 	u32	mpdu_header_len:8; | ||||
| 
 | ||||
| 	/* 0x0c*/ | ||||
| 	u32	reserved4:8; | ||||
| 	u32	tid:4; | ||||
| 	u32	reserved3:4; | ||||
| 	u32	mpdu_len:16; | ||||
| }; | ||||
| 
 | ||||
| struct wcn36xx_rx_bd { | ||||
| 	u32	bdt:2; | ||||
| 	u32	ft:1; | ||||
| 	u32	dpu_ne:1; | ||||
| 	u32	rx_key_id:3; | ||||
| 	u32	ub:1; | ||||
| 	u32	rmf:1; | ||||
| 	u32	uma_bypass:1; | ||||
| 	u32	csr11:1; | ||||
| 	u32	reserved0:1; | ||||
| 	u32	scan_learn:1; | ||||
| 	u32	rx_ch:4; | ||||
| 	u32	rtsf:1; | ||||
| 	u32	bsf:1; | ||||
| 	u32	a2hf:1; | ||||
| 	u32	st_auf:1; | ||||
| 	u32	dpu_sign:3; | ||||
| 	u32	dpu_rf:8; | ||||
| 
 | ||||
| 	struct wcn36xx_pdu pdu; | ||||
| 
 | ||||
| 	/* 0x14*/ | ||||
| 	u32	addr3:8; | ||||
| 	u32	addr2:8; | ||||
| 	u32	addr1:8; | ||||
| 	u32	dpu_desc_idx:8; | ||||
| 
 | ||||
| 	/* 0x18*/ | ||||
| 	u32	rxp_flags:23; | ||||
| 	u32	rate_id:9; | ||||
| 
 | ||||
| 	u32	phy_stat0; | ||||
| 	u32	phy_stat1; | ||||
| 
 | ||||
| 	/* 0x24 */ | ||||
| 	u32	rx_times; | ||||
| 
 | ||||
| 	u32	pmi_cmd[6]; | ||||
| 
 | ||||
| 	/* 0x40 */ | ||||
| 	u32	reserved7:4; | ||||
| 	u32	reorder_slot_id:6; | ||||
| 	u32	reorder_fwd_id:6; | ||||
| 	u32	reserved6:12; | ||||
| 	u32	reorder_code:4; | ||||
| 
 | ||||
| 	/* 0x44 */ | ||||
| 	u32	exp_seq_num:12; | ||||
| 	u32	cur_seq_num:12; | ||||
| 	u32	fr_type_subtype:8; | ||||
| 
 | ||||
| 	/* 0x48 */ | ||||
| 	u32	msdu_size:16; | ||||
| 	u32	sub_fr_id:4; | ||||
| 	u32	proc_order:4; | ||||
| 	u32	reserved9:4; | ||||
| 	u32	aef:1; | ||||
| 	u32	lsf:1; | ||||
| 	u32	esf:1; | ||||
| 	u32	asf:1; | ||||
| }; | ||||
| 
 | ||||
| struct wcn36xx_tx_bd { | ||||
| 	u32	bdt:2; | ||||
| 	u32	ft:1; | ||||
| 	u32	dpu_ne:1; | ||||
| 	u32	fw_tx_comp:1; | ||||
| 	u32	tx_comp:1; | ||||
| 	u32	reserved1:1; | ||||
| 	u32	ub:1; | ||||
| 	u32	rmf:1; | ||||
| 	u32	reserved0:12; | ||||
| 	u32	dpu_sign:3; | ||||
| 	u32	dpu_rf:8; | ||||
| 
 | ||||
| 	struct wcn36xx_pdu pdu; | ||||
| 
 | ||||
| 	/* 0x14*/ | ||||
| 	u32	reserved5:7; | ||||
| 	u32	queue_id:5; | ||||
| 	u32	bd_rate:2; | ||||
| 	u32	ack_policy:2; | ||||
| 	u32	sta_index:8; | ||||
| 	u32	dpu_desc_idx:8; | ||||
| 
 | ||||
| 	u32	tx_bd_sign; | ||||
| 	u32	reserved6; | ||||
| 	u32	dxe_start_time; | ||||
| 	u32	dxe_end_time; | ||||
| 
 | ||||
| 	/*u32	tcp_udp_start_off:10;
 | ||||
| 	u32	header_cks:16; | ||||
| 	u32	reserved7:6;*/ | ||||
| }; | ||||
| 
 | ||||
| struct wcn36xx_sta; | ||||
| struct wcn36xx; | ||||
| 
 | ||||
| int  wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb); | ||||
| int wcn36xx_start_tx(struct wcn36xx *wcn, | ||||
| 		     struct wcn36xx_sta *sta_priv, | ||||
| 		     struct sk_buff *skb); | ||||
| 
 | ||||
| #endif	/* _TXRX_H_ */ | ||||
							
								
								
									
										238
									
								
								drivers/net/wireless/ath/wcn36xx/wcn36xx.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								drivers/net/wireless/ath/wcn36xx/wcn36xx.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,238 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com> | ||||
|  * | ||||
|  * Permission to use, copy, modify, and/or distribute this software for any | ||||
|  * purpose with or without fee is hereby granted, provided that the above | ||||
|  * copyright notice and this permission notice appear in all copies. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
|  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
|  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||||
|  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
|  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||||
|  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||||
|  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _WCN36XX_H_ | ||||
| #define _WCN36XX_H_ | ||||
| 
 | ||||
| #include <linux/completion.h> | ||||
| #include <linux/printk.h> | ||||
| #include <linux/spinlock.h> | ||||
| #include <net/mac80211.h> | ||||
| 
 | ||||
| #include "hal.h" | ||||
| #include "smd.h" | ||||
| #include "txrx.h" | ||||
| #include "dxe.h" | ||||
| #include "pmc.h" | ||||
| #include "debug.h" | ||||
| 
 | ||||
| #define WLAN_NV_FILE               "wlan/prima/WCNSS_qcom_wlan_nv.bin" | ||||
| #define WCN36XX_AGGR_BUFFER_SIZE 64 | ||||
| 
 | ||||
| extern unsigned int wcn36xx_dbg_mask; | ||||
| 
 | ||||
| enum wcn36xx_debug_mask { | ||||
| 	WCN36XX_DBG_DXE		= 0x00000001, | ||||
| 	WCN36XX_DBG_DXE_DUMP	= 0x00000002, | ||||
| 	WCN36XX_DBG_SMD		= 0x00000004, | ||||
| 	WCN36XX_DBG_SMD_DUMP	= 0x00000008, | ||||
| 	WCN36XX_DBG_RX		= 0x00000010, | ||||
| 	WCN36XX_DBG_RX_DUMP	= 0x00000020, | ||||
| 	WCN36XX_DBG_TX		= 0x00000040, | ||||
| 	WCN36XX_DBG_TX_DUMP	= 0x00000080, | ||||
| 	WCN36XX_DBG_HAL		= 0x00000100, | ||||
| 	WCN36XX_DBG_HAL_DUMP	= 0x00000200, | ||||
| 	WCN36XX_DBG_MAC		= 0x00000400, | ||||
| 	WCN36XX_DBG_BEACON	= 0x00000800, | ||||
| 	WCN36XX_DBG_BEACON_DUMP	= 0x00001000, | ||||
| 	WCN36XX_DBG_PMC		= 0x00002000, | ||||
| 	WCN36XX_DBG_PMC_DUMP	= 0x00004000, | ||||
| 	WCN36XX_DBG_ANY		= 0xffffffff, | ||||
| }; | ||||
| 
 | ||||
| #define wcn36xx_err(fmt, arg...)				\ | ||||
| 	printk(KERN_ERR pr_fmt("ERROR " fmt), ##arg); | ||||
| 
 | ||||
| #define wcn36xx_warn(fmt, arg...)				\ | ||||
| 	printk(KERN_WARNING pr_fmt("WARNING " fmt), ##arg) | ||||
| 
 | ||||
| #define wcn36xx_info(fmt, arg...)		\ | ||||
| 	printk(KERN_INFO pr_fmt(fmt), ##arg) | ||||
| 
 | ||||
| #define wcn36xx_dbg(mask, fmt, arg...) do {			\ | ||||
| 	if (wcn36xx_dbg_mask & mask)					\ | ||||
| 		printk(KERN_DEBUG pr_fmt(fmt), ##arg);	\ | ||||
| } while (0) | ||||
| 
 | ||||
| #define wcn36xx_dbg_dump(mask, prefix_str, buf, len) do {	\ | ||||
| 	if (wcn36xx_dbg_mask & mask)					\ | ||||
| 		print_hex_dump(KERN_DEBUG, pr_fmt(prefix_str),	\ | ||||
| 			       DUMP_PREFIX_OFFSET, 32, 1,	\ | ||||
| 			       buf, len, false);		\ | ||||
| } while (0) | ||||
| 
 | ||||
| #define WCN36XX_HW_CHANNEL(__wcn) (__wcn->hw->conf.chandef.chan->hw_value) | ||||
| #define WCN36XX_BAND(__wcn) (__wcn->hw->conf.chandef.chan->band) | ||||
| #define WCN36XX_CENTER_FREQ(__wcn) (__wcn->hw->conf.chandef.chan->center_freq) | ||||
| #define WCN36XX_LISTEN_INTERVAL(__wcn) (__wcn->hw->conf.listen_interval) | ||||
| #define WCN36XX_FLAGS(__wcn) (__wcn->hw->flags) | ||||
| #define WCN36XX_MAX_POWER(__wcn) (__wcn->hw->conf.chandef.chan->max_power) | ||||
| 
 | ||||
| static inline void buff_to_be(u32 *buf, size_t len) | ||||
| { | ||||
| 	int i; | ||||
| 	for (i = 0; i < len; i++) | ||||
| 		buf[i] = cpu_to_be32(buf[i]); | ||||
| } | ||||
| 
 | ||||
| struct nv_data { | ||||
| 	int	is_valid; | ||||
| 	u8	table; | ||||
| }; | ||||
| 
 | ||||
| /* Interface for platform control path
 | ||||
|  * | ||||
|  * @open: hook must be called when wcn36xx wants to open control channel. | ||||
|  * @tx: sends a buffer. | ||||
|  */ | ||||
| struct wcn36xx_platform_ctrl_ops { | ||||
| 	int (*open)(void *drv_priv, void *rsp_cb); | ||||
| 	void (*close)(void); | ||||
| 	int (*tx)(char *buf, size_t len); | ||||
| 	int (*get_hw_mac)(u8 *addr); | ||||
| 	int (*smsm_change_state)(u32 clear_mask, u32 set_mask); | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct wcn36xx_vif - holds VIF related fields | ||||
|  * | ||||
|  * @bss_index: bss_index is initially set to 0xFF. bss_index is received from | ||||
|  * HW after first config_bss call and must be used in delete_bss and | ||||
|  * enter/exit_bmps. | ||||
|  */ | ||||
| struct wcn36xx_vif { | ||||
| 	struct list_head list; | ||||
| 	struct wcn36xx_sta *sta; | ||||
| 	u8 dtim_period; | ||||
| 	enum ani_ed_type encrypt_type; | ||||
| 	bool is_joining; | ||||
| 	struct wcn36xx_hal_mac_ssid ssid; | ||||
| 
 | ||||
| 	/* Power management */ | ||||
| 	enum wcn36xx_power_state pw_state; | ||||
| 
 | ||||
| 	u8 bss_index; | ||||
| 	u8 ucast_dpu_signature; | ||||
| 	/* Returned from WCN36XX_HAL_ADD_STA_SELF_RSP */ | ||||
| 	u8 self_sta_index; | ||||
| 	u8 self_dpu_desc_index; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * struct wcn36xx_sta - holds STA related fields | ||||
|  * | ||||
|  * @tid: traffic ID that is used during AMPDU and in TX BD. | ||||
|  * @sta_index: STA index is returned from HW after config_sta call and is | ||||
|  * used in both SMD channel and TX BD. | ||||
|  * @dpu_desc_index: DPU descriptor index is returned from HW after config_sta | ||||
|  * call and is used in TX BD. | ||||
|  * @bss_sta_index: STA index is returned from HW after config_bss call and is | ||||
|  * used in both SMD channel and TX BD. See table bellow when it is used. | ||||
|  * @bss_dpu_desc_index: DPU descriptor index is returned from HW after | ||||
|  * config_bss call and is used in TX BD. | ||||
|  * ______________________________________________ | ||||
|  * |		  |	STA	|	AP	| | ||||
|  * |______________|_____________|_______________| | ||||
|  * |    TX BD     |bss_sta_index|   sta_index   | | ||||
|  * |______________|_____________|_______________| | ||||
|  * |all SMD calls |bss_sta_index|   sta_index	| | ||||
|  * |______________|_____________|_______________| | ||||
|  * |smd_delete_sta|  sta_index  |   sta_index	| | ||||
|  * |______________|_____________|_______________| | ||||
|  */ | ||||
| struct wcn36xx_sta { | ||||
| 	struct wcn36xx_vif *vif; | ||||
| 	u16 aid; | ||||
| 	u16 tid; | ||||
| 	u8 sta_index; | ||||
| 	u8 dpu_desc_index; | ||||
| 	u8 bss_sta_index; | ||||
| 	u8 bss_dpu_desc_index; | ||||
| 	bool is_data_encrypted; | ||||
| 	/* Rates */ | ||||
| 	struct wcn36xx_hal_supported_rates supported_rates; | ||||
| }; | ||||
| struct wcn36xx_dxe_ch; | ||||
| struct wcn36xx { | ||||
| 	struct ieee80211_hw	*hw; | ||||
| 	struct device		*dev; | ||||
| 	struct list_head	vif_list; | ||||
| 
 | ||||
| 	u8			fw_revision; | ||||
| 	u8			fw_version; | ||||
| 	u8			fw_minor; | ||||
| 	u8			fw_major; | ||||
| 
 | ||||
| 	/* extra byte for the NULL termination */ | ||||
| 	u8			crm_version[WCN36XX_HAL_VERSION_LENGTH + 1]; | ||||
| 	u8			wlan_version[WCN36XX_HAL_VERSION_LENGTH + 1]; | ||||
| 
 | ||||
| 	/* IRQs */ | ||||
| 	int			tx_irq; | ||||
| 	int			rx_irq; | ||||
| 	void __iomem		*mmio; | ||||
| 
 | ||||
| 	struct wcn36xx_platform_ctrl_ops *ctrl_ops; | ||||
| 	/*
 | ||||
| 	 * smd_buf must be protected with smd_mutex to garantee | ||||
| 	 * that all messages are sent one after another | ||||
| 	 */ | ||||
| 	u8			*hal_buf; | ||||
| 	size_t			hal_rsp_len; | ||||
| 	struct mutex		hal_mutex; | ||||
| 	struct completion	hal_rsp_compl; | ||||
| 	struct workqueue_struct	*hal_ind_wq; | ||||
| 	struct work_struct	hal_ind_work; | ||||
| 	struct mutex		hal_ind_mutex; | ||||
| 	struct list_head	hal_ind_queue; | ||||
| 
 | ||||
| 	/* DXE channels */ | ||||
| 	struct wcn36xx_dxe_ch	dxe_tx_l_ch;	/* TX low */ | ||||
| 	struct wcn36xx_dxe_ch	dxe_tx_h_ch;	/* TX high */ | ||||
| 	struct wcn36xx_dxe_ch	dxe_rx_l_ch;	/* RX low */ | ||||
| 	struct wcn36xx_dxe_ch	dxe_rx_h_ch;	/* RX high */ | ||||
| 
 | ||||
| 	/* For synchronization of DXE resources from BH, IRQ and WQ contexts */ | ||||
| 	spinlock_t	dxe_lock; | ||||
| 	bool                    queues_stopped; | ||||
| 
 | ||||
| 	/* Memory pools */ | ||||
| 	struct wcn36xx_dxe_mem_pool mgmt_mem_pool; | ||||
| 	struct wcn36xx_dxe_mem_pool data_mem_pool; | ||||
| 
 | ||||
| 	struct sk_buff		*tx_ack_skb; | ||||
| 
 | ||||
| #ifdef CONFIG_WCN36XX_DEBUGFS | ||||
| 	/* Debug file system entry */ | ||||
| 	struct wcn36xx_dfs_entry    dfs; | ||||
| #endif /* CONFIG_WCN36XX_DEBUGFS */ | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| static inline bool wcn36xx_is_fw_version(struct wcn36xx *wcn, | ||||
| 					 u8 major, | ||||
| 					 u8 minor, | ||||
| 					 u8 version, | ||||
| 					 u8 revision) | ||||
| { | ||||
| 	return (wcn->fw_major == major && | ||||
| 		wcn->fw_minor == minor && | ||||
| 		wcn->fw_version == version && | ||||
| 		wcn->fw_revision == revision); | ||||
| } | ||||
| void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates); | ||||
| 
 | ||||
| #endif	/* _WCN36XX_H_ */ | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Eugene Krasnikov
				Eugene Krasnikov