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 | S:	Supported | ||||||
| F:	arch/hexagon/ | 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 | QUICKCAM PARALLEL PORT WEBCAMS | ||||||
| M:	Hans Verkuil <hverkuil@xs4all.nl> | M:	Hans Verkuil <hverkuil@xs4all.nl> | ||||||
| L:	linux-media@vger.kernel.org | 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/ar5523/Kconfig" | ||||||
| source "drivers/net/wireless/ath/wil6210/Kconfig" | source "drivers/net/wireless/ath/wil6210/Kconfig" | ||||||
| source "drivers/net/wireless/ath/ath10k/Kconfig" | source "drivers/net/wireless/ath/ath10k/Kconfig" | ||||||
|  | source "drivers/net/wireless/ath/wcn36xx/Kconfig" | ||||||
| 
 | 
 | ||||||
| endif | endif | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ obj-$(CONFIG_ATH6KL)		+= ath6kl/ | ||||||
| obj-$(CONFIG_AR5523)		+= ar5523/ | obj-$(CONFIG_AR5523)		+= ar5523/ | ||||||
| obj-$(CONFIG_WIL6210)		+= wil6210/ | obj-$(CONFIG_WIL6210)		+= wil6210/ | ||||||
| obj-$(CONFIG_ATH10K)		+= ath10k/ | obj-$(CONFIG_ATH10K)		+= ath10k/ | ||||||
|  | obj-$(CONFIG_WCN36XX)		+= wcn36xx/ | ||||||
| 
 | 
 | ||||||
| obj-$(CONFIG_ATH_COMMON)	+= ath.o | 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